Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion quest/m3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ generic std gap is planned here even when moq never calls it.
- [Writes through iter_mut without a Place per item](/quest/m3/ts-iter-mut-places.md) - a loop over `iter_mut()` of copy values writes the slice's array by index
- [Throw only where Rust panics](/quest/m3/ts-runtime-throws.md) - no runtime `rust2-ts:` error where native Rust goes on: an exact slower path, or a refusal at translation
- [rust2-ts with no ts.toml](/quest/m3/ts-configless.md) - a crate's whole `pub` API translates with no config and no bound items
- [std's panic messages hold std's capacity](/quest/m3/ts-panic-payload-capacity.md) - a hook reads native's `String` capacity from std's own formatted panics
- [Browser APIs through wasm-bindgen](/quest/m3/ts-wasm-bindgen/README.md) - web-sys and js-sys imports become direct JS calls, so idiomatic crates such as web-rs's web-codecs translate as stock crates
- [BufMut on a byte slice](/quest/m3/ts-bufmut-slices.md) - `bytes`' `BufMut` for `&mut [u8]` and `&mut [MaybeUninit<u8>]` translates
- [Fields typed by an associated type](/quest/m3/ts-assoc-type-fields.md) - a generic struct's field of `<T as Trait>::Item` translates at each instance
16 changes: 0 additions & 16 deletions quest/m3/ts-panic-payload-capacity.md

This file was deleted.

7 changes: 0 additions & 7 deletions ts/fixtures/lang/panics/hook/expected/core/option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ export function map$18ftgyp(self: rs.char | undefined, _: (a0: rs.char) => rs.u3
return self as rs.u32;
}

export function map$0ded99z(self: rs.String | undefined, _: (a0: rs.String) => rs.usize): rs.usize | undefined {
if (self === undefined) {
return undefined;
}
return self.cap;
}

export function map$04ry0e1(self: rs.u32 | undefined, f: (a0: rs.u32) => rs.u8): rs.u8 | undefined {
if (self === undefined) {
return undefined;
Expand Down
2 changes: 1 addition & 1 deletion ts/fixtures/lang/panics/hook/expected/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function text(data: rs.ByteSlice): rs.String {
}
const _13: rs.usize = rs.u8Rem(_5, 16, { file: "fixtures/lang/panics/src/../../harness.rs", line: 62, col: 25 }) as rs.usize;
if (!(_13 < 16)) {
rs.panic("index out of bounds: the len is " + 16 + " but the index is " + _13, { file: "fixtures/lang/panics/src/../../harness.rs", line: 62, col: 16 }, true);
rs.panicSizes(["index out of bounds: the len is ", 16, " but the index is ", _13], { file: "fixtures/lang/panics/src/../../harness.rs", line: 62, col: 16 });
}
rs.stringPush(s, [47, 92, 97, 48, 57, 127, 128, 233, 2047, 2048, 55295, 57344, 65535, 65536, 128512, 1114111][_13]);
}
Expand Down
16 changes: 16 additions & 0 deletions ts/fixtures/lang/panics/hook/expected/hooked.Failing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Generated by rust2-ts. Do not edit.

import * as rs from "./rs.ts";

/**
* A `Display` that fails though the writer did not, which `format!` and `to_string`
* panic on.
*/
export interface Failing {
}

export function fmt$fmt_Display(_self: Failing, _2: rs.Formatter): rs.Result<undefined, rs.FmtError> {
return rs.Err(new rs.FmtError());
}

//# sourceMappingURL=hooked.Failing.ts.map
2 changes: 1 addition & 1 deletion ts/fixtures/lang/panics/hook/expected/hooked.Shown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function fmt$fmt_Display(self: Shown, f: rs.Formatter): rs.Result<undefin
if (!self.again) {
return rs.fmtWriteStr(f, "shown");
}
rs.panicFmt(["shown"], { file: "fixtures/lang/panics/src/hooked.rs", line: 159, col: 13 });
rs.panicFmt(["shown"], { file: "fixtures/lang/panics/src/hooked.rs", line: 142, col: 13 });
}

//# sourceMappingURL=hooked.Shown.ts.map
416 changes: 212 additions & 204 deletions ts/fixtures/lang/panics/hook/expected/hooked.ts

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions ts/fixtures/lang/panics/hook/expected/located.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export function called(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): void {
const _75: rs.Location = h(first);
const _78: rs.usize = rs.u8Rem(first, 2, { file: "fixtures/lang/panics/src/located.rs", line: 170, col: 61 }) as rs.usize;
if (!(_78 < 2)) {
rs.panic("index out of bounds: the len is " + 2 + " but the index is " + _78, { file: "fixtures/lang/panics/src/located.rs", line: 170, col: 46 }, true);
rs.panicSizes(["index out of bounds: the len is ", 2, " but the index is ", _78], { file: "fixtures/lang/panics/src/located.rs", line: 170, col: 46 });
}
lines(out, rs.sliceFrom([_73, _74, _75, fs[_78](), own2({})]));
} else if (_7 === 4) {
Expand Down Expand Up @@ -204,7 +204,7 @@ export function panicked(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): void
}) as (a0: boolean) => undefined;
const _18: rs.usize = rs.u8Rem(first, 2, { file: "fixtures/lang/panics/src/located.rs", line: 261, col: 28 }) as rs.usize;
if (!(_18 < 2)) {
rs.panic("index out of bounds: the len is " + 2 + " but the index is " + _18, { file: "fixtures/lang/panics/src/located.rs", line: 261, col: 13 }, true);
rs.panicSizes(["index out of bounds: the len is ", 2, " but the index is ", _18], { file: "fixtures/lang/panics/src/located.rs", line: 261, col: 13 });
}
[_15, f][_18](rs.u8Rem(first, 3, { file: "fixtures/lang/panics/src/located.rs", line: 261, col: 40 }) !== 1);
}
Expand Down
52 changes: 28 additions & 24 deletions ts/fixtures/lang/panics/src/hooked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ use bytes::{Buf, BufMut, Bytes, BytesMut};

use crate::NOTE;

/// Write what `info` says: its `Display`, its payload's type and text, and its location's
/// parts, and whether the thread is panicking.
/// Write what `info` says: its `Display`, its payload's type and text, a `String` payload's
/// capacity, its location's parts, and whether the thread is panicking.
fn record(info: &PanicHookInfo<'_>) {
let payload = info.payload();
let kind = if payload.type_id() == std::any::TypeId::of::<&'static str>() {
"&str".to_string()
} else if payload.is::<String>() {
"String".to_string()
} else if let Some(s) = payload.downcast_ref::<String>() {
format!("String {}", s.capacity())
} else if let Some(n) = payload.downcast_ref::<u32>() {
format!("u32 {n}")
} else if let Some(n) = payload.downcast_ref::<u8>() {
Expand Down Expand Up @@ -74,17 +74,6 @@ fn hook() {
}));
}

/// Have the hook also write a `String` payload's capacity, which `panic!` formats into one
/// from empty and `panic_any` keeps.
fn sized() {
let prev = panic::take_hook();
panic::set_hook(Box::new(move |info| {
let capacity = info.payload().downcast_ref::<String>().map(String::capacity);
NOTE.with_borrow_mut(|note| note.push_str(&format!("capacity {capacity:?}\n")));
prev(info);
}));
}

#[track_caller]
fn tracked(n: u8) -> u8 {
panic!("tracked {n}")
Expand All @@ -97,10 +86,7 @@ pub fn panicked(kind: u8, data: &[u8], out: &mut Vec<u8>) {
let v = vec![n, n];
match kind % 32 {
0 => panic!("literal"),
1 => {
sized();
panic!("formatted {n}")
}
1 => panic!("formatted {n}"),
2 => out.push(v[usize::from(n) + 2]),
3 => out.push(n.max(200) + n.max(100)),
4 => out.push(Some(n).filter(|_| false).unwrap()),
Expand All @@ -119,10 +105,7 @@ pub fn panicked(kind: u8, data: &[u8], out: &mut Vec<u8>) {
13 => v.clone().rotate_left(usize::from(n) + 3),
14 => out.push(HashMap::<u8, u8>::new()[&n]),
15 => panic::panic_any(u32::from(n)),
16 => {
sized();
panic::panic_any(format!("any {n}"))
}
16 => panic::panic_any(format!("any {n}")),
17 => assert_eq!(n, n.wrapping_add(1), "differ"),
18 => out.push(tracked(n)),
19 => out.push(VecDeque::from(v.clone())[usize::from(n) + 2]),
Expand Down Expand Up @@ -200,7 +183,7 @@ pub fn sited(kind: u8, data: &[u8], out: &mut Vec<u8>) {
let deque = VecDeque::from(v.clone());
let map = BTreeMap::from([(u16::from(n), n)]);
let set = BTreeSet::from([u16::from(n)]);
match kind % 75 {
match kind % 86 {
0 => out.push(Vec::<u8>::with_capacity(usize::MAX - at).len() as u8),
1 => v.clone().rotate_right(at + 3),
2 => out.push(n.clamp(n.max(1), n.max(1) - 1)),
Expand Down Expand Up @@ -346,10 +329,31 @@ pub fn sited(kind: u8, data: &[u8], out: &mut Vec<u8>) {
std::io::IoSlice::advance_slices(&mut left, at % 2 + 3);
out.push(left.len() as u8);
}
74 => v.clone().insert(at + 3, n),
75 => out.extend(v.clone().split_off(at + 3)),
76 => out.push(vec![u16::from(n)].remove(at + 1) as u8),
77 => out.push(vec![u16::from(n)].swap_remove(at + 1) as u8),
78 => vec![u16::from(n)].insert(at + 2, 0),
79 => out.extend(vec![u16::from(n)].split_off(at + 2).iter().map(|&w| w as u8)),
80 => v.clone().copy_within(at + 1..at + 3, 0),
81 => out.push(u64::from(n).clamp(u64::from(n) + 1, u64::from(n)) as u8),
82 => out.push((-i32::from(n)).clamp(0, -1) as u8),
83 => out.extend_from_slice(format!("{Failing}").as_bytes()),
84 => out.extend_from_slice(Failing.to_string().as_bytes()),
_ => out.push(Lying(n).len() as u8),
}
}

/// A `Display` that fails though the writer did not, which `format!` and `to_string`
/// panic on.
struct Failing;

impl fmt::Display for Failing {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
Err(fmt::Error)
}
}

/// A `concat` or `join` whose length overflows, by `kind`: its sum of the parts' lengths,
/// or the capacity the sum reserves.
fn joined(kind: u8, n: u8, out: &mut Vec<u8>) {
Expand Down
54 changes: 31 additions & 23 deletions ts/runtime/rs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ export function panic(message: string, at?: Location | string, owned?: boolean |
throw new Panic(message);
}

/**
* Panic at `at` with `message`: literal pieces, and the `usize`s between them, which a
* hook reads as the `String` std's `panic!` writes each piece and number into.
*/
export function panicSizes(message: readonly (string | number)[], at?: Location | string): never {
panicFmt(message.map((part) => (typeof part === "string" ? part : fmtOwned(part as usize, usizeDisplay))), at);
}

/** `resume_unwind`, which aborts as `panic=abort`'s runtime does, running no panic hook. */
export function resumeUnwind(): never {
throw new Abort("resume_unwind");
Expand All @@ -80,8 +88,8 @@ export function unreachable(_value: unknown): never {

/** `assert_eq!` or `assert_ne!` failing: `op` is `==` or `!=`. */
export function assertFailed(op: string, left: Fmt, right: Fmt, args?: Arguments, at?: Location | string): never {
const failed: Arguments = args === undefined ? [" failed"] : [" failed: ", (f) => fmtWriteFmt(f, args)];
panicFmt(["assertion `left ", op, " right`", ...failed, "\n left: ", left, "\n right: ", right], at);
const failed: Arguments = args === undefined ? [" right` failed\n left: "] : [" right` failed: ", (f) => fmtWriteFmt(f, args), "\n left: "];
panicFmt(["assertion `left ", op, ...failed, left, "\n right: ", right], at);
}

// Places
Expand Down Expand Up @@ -677,8 +685,8 @@ export const OK: Result<undefined, never> = /* @__PURE__ */ Object.freeze({ ok:
export const ERR: Result<never, undefined> = /* @__PURE__ */ Object.freeze({ ok: false, error: undefined });

/** Panic with `message` and `value`'s `Debug`. */
function unwrapFailed<T>(message: string, value: T, debug: Debug<T>, at?: Location): never {
panicFmt([message, ": ", (f) => debug(value, f)], at);
function unwrapFailed<T>(message: string, value: T, debug: Debug<T>, at?: Location | string): never {
panicFmt([message, ": ", fmtOwned(value, debug)], at);
}

// A function that may drop an operand std's would, rather than return it, takes what
Expand Down Expand Up @@ -819,7 +827,7 @@ export function sliceSame<T>(a: Slice<T>, b: Slice<T>): boolean {

/** A bounds-checked index into a slice. */
export function idx(slice: { readonly length: number }, index: number, at?: Location): number {
if (index >= slice.length) panic(`index out of bounds: the len is ${slice.length} but the index is ${index}`, at, true);
if (index >= slice.length) panicSizes(["index out of bounds: the len is ", slice.length, " but the index is ", index], at);
return index;
}

Expand All @@ -830,9 +838,9 @@ export function at<T>(slice: ArrayLike<T>, index: number, at?: Location): T {

/** Panic unless `start..end` is in a slice of `length`, as std's range checks do. */
export function rangeCheck(length: number, start: number, end: number, at?: Location): void {
if (start > length) panic(`range start index ${start} out of range for slice of length ${length}`, at, true);
if (end > length) panic(`range end index ${end} out of range for slice of length ${length}`, at, true);
if (start > end) panic(`slice index starts at ${start} but ends at ${end}`, at, true);
if (start > length) panicSizes(["range start index ", start, " out of range for slice of length ", length], at);
if (end > length) panicSizes(["range end index ", end, " out of range for slice of length ", length], at);
if (start > end) panicSizes(["slice index starts at ", start, " but ends at ", end], at);
}

/** `&s[start..end]` and `&mut s[start..end]`, with `end` defaulting to the length. */
Expand Down Expand Up @@ -1455,7 +1463,7 @@ export function sliceCopyWithin<T>(s: Slice<T>, start: number, end: number, dest
/** `copy_within`'s checks of `start..end` and `dest` in `length` elements. */
function copyWithinCheck(length: number, start: number, end: number, dest: number, at?: Location): void {
// `slice::range` checks the end first, then `range` reports the rest.
if (end > length) panic(`range end index ${end} out of range for slice of length ${length}`, at, true);
if (end > length) panicSizes(["range end index ", end, " out of range for slice of length ", length], at);
rangeCheck(length, start, end, at);
if (dest > length - (end - start)) panic("dest is out of bounds", at);
}
Expand Down Expand Up @@ -1493,7 +1501,7 @@ export function sliceWriteCloneOfSlice<T>(dst: Slice<T>, src: Slice<T>, clone: (
}

function writeCloneLengthFail(dst: number, src: number, at?: Location): never {
panic(`assertion \`left == right\` failed: destination and source slices have different lengths\n left: ${dst}\n right: ${src}`, at, true);
assertFailed("==", fmtOwned(dst as usize, usizeDebug), fmtOwned(src as usize, usizeDebug), ["destination and source slices have different lengths"], at);
}

/** `Box::new_uninit_slice` of `size`-byte elements: `n` of what `uninit` makes, panicking past what a `Vec` of them may hold. */
Expand Down Expand Up @@ -1589,7 +1597,7 @@ export function byteBoxSliceCloneFrom(dst: ByteSlice, src: ByteSlice, at?: Locat
}

function copyLengthFail(dst: number, src: number, at?: Location): never {
panic(`copy_from_slice: source slice length (${src}) does not match destination slice length (${dst})`, at, true);
panicSizes(["copy_from_slice: source slice length (", src, ") does not match destination slice length (", dst, ")"], at);
}

/**
Expand Down Expand Up @@ -1685,7 +1693,7 @@ export function arrayAssign<T>(p: T[], value: T[]): void {

/** `Vec::insert`: `item` at `i`, with those from `i` on moved up. */
export function vecInsert<T>(v: Vec<T>, i: number, item: T, at?: Location): void {
if (i > v.items.length) panic(`insertion index (is ${i}) should be <= len (is ${v.items.length})`, at, true);
if (i > v.items.length) panicSizes(["insertion index (is ", i, ") should be <= len (is ", v.items.length, ")"], at);
v.items.splice(i, 0, item);
}

Expand Down Expand Up @@ -1742,7 +1750,7 @@ export function vecPopIfMut<T>(

/** `Vec::remove`: the element at `i`, with those after it moved down. */
export function vecRemove<T>(v: Vec<T>, i: number, at?: Location): T {
if (i >= v.items.length) panic(`removal index (is ${i}) should be < len (is ${v.items.length})`, at, true);
if (i >= v.items.length) panicSizes(["removal index (is ", i, ") should be < len (is ", v.items.length, ")"], at);
vecKeep(v);
return v.items.splice(i, 1)[0];
}
Expand All @@ -1758,7 +1766,7 @@ export function vecTruncate<T>(v: Vec<T>, len: number, drop?: (item: T) => void)
/** `Vec::swap_remove`: the element at `i`, with the last moved into its place. */
export function vecSwapRemove<T>(v: Vec<T>, i: number): T {
const d = v.items;
if (i >= d.length) panic(`swap_remove index (is ${i}) should be < len (is ${d.length})`, "swap_remove", true);
if (i >= d.length) panicSizes(["swap_remove index (is ", i, ") should be < len (is ", d.length, ")"], "swap_remove");
vecKeep(v);
const item = d[i];
d[i] = d[d.length - 1];
Expand Down Expand Up @@ -1831,7 +1839,7 @@ export function vecResizeWith<T>(v: Vec<T>, len: number, f: () => T, size: numbe

/** `Vec::split_off`: those from `mid` on, moved to a new vector with room for exactly them. */
export function vecSplitOff<T>(v: Vec<T>, mid: number, at?: Location): Vec<T> {
if (mid > v.items.length) panic(`\`at\` split index (is ${mid}) should be <= len (is ${v.items.length})`, at, true);
if (mid > v.items.length) panicSizes(["`at` split index (is ", mid, ") should be <= len (is ", v.items.length, ")"], at);
vecKeep(v);
return vecFrom(v.items.splice(mid));
}
Expand Down Expand Up @@ -2309,7 +2317,7 @@ export function byteVecClear(v: ByteVec): void {

/** `Vec::insert`: `byte` at `i`, with those from `i` on moved up. */
export function byteVecInsert(v: ByteVec, i: number, byte: number, at?: Location): void {
if (i > v.length) panic(`insertion index (is ${i}) should be <= len (is ${v.length})`, at, true);
if (i > v.length) panicSizes(["insertion index (is ", i, ") should be <= len (is ", v.length, ")"], at);
reserve(v, 1);
v.data.copyWithin(i + 1, i, v.length);
v.data[i] = byte;
Expand All @@ -2318,7 +2326,7 @@ export function byteVecInsert(v: ByteVec, i: number, byte: number, at?: Location

/** `Vec::remove`: the byte at `i`, with those after it moved down. */
export function byteVecRemove(v: ByteVec, i: number, at?: Location): number {
if (i >= v.length) panic(`removal index (is ${i}) should be < len (is ${v.length})`, at, true);
if (i >= v.length) panicSizes(["removal index (is ", i, ") should be < len (is ", v.length, ")"], at);
const byte = v.data[i];
v.data.copyWithin(i, i + 1, v.length);
v.length--;
Expand All @@ -2327,7 +2335,7 @@ export function byteVecRemove(v: ByteVec, i: number, at?: Location): number {

/** `Vec::swap_remove`: the byte at `i`, with the last moved into its place. */
export function byteVecSwapRemove(v: ByteVec, i: number): number {
if (i >= v.length) panic(`swap_remove index (is ${i}) should be < len (is ${v.length})`, "swap_remove", true);
if (i >= v.length) panicSizes(["swap_remove index (is ", i, ") should be < len (is ", v.length, ")"], "swap_remove");
const byte = v.data[i];
v.data[i] = v.data[--v.length];
return byte;
Expand Down Expand Up @@ -2742,7 +2750,7 @@ export function byteVecResizeWith(v: ByteVec, len: number, f: () => number): voi

/** `Vec::split_off` of bytes. */
export function byteVecSplitOff(v: ByteVec, mid: number, at?: Location): ByteVec {
if (mid > v.length) panic(`\`at\` split index (is ${mid}) should be <= len (is ${v.length})`, at, true);
if (mid > v.length) panicSizes(["`at` split index (is ", mid, ") should be <= len (is ", v.length, ")"], at);
const other = new ByteVec(v.length - mid);
copyBytes(other.data, 0, v.data, mid, v.length - mid);
other.length = v.length - mid;
Expand Down Expand Up @@ -3531,9 +3539,8 @@ function closePretty(f: Formatter, close: string): FmtResult {
*/
export function fmtFormat(args: Arguments): RsString {
const s = stringWithCapacity(estimate(args) as usize);
if (!write(new StringWriter(s), args).ok) {
panic("a formatting trait implementation returned an error when the underlying stream did not: Error", "format", true);
}
const r = write(new StringWriter(s), args);
if (!r.ok) unwrapFailed("a formatting trait implementation returned an error when the underlying stream did not", r.error, fmtErrorDebug, "format");
return s;
}

Expand Down Expand Up @@ -3563,7 +3570,8 @@ function estimate(args: Arguments): number {
*/
export function fmtToString(value: Fmt, size?: number): RsString {
const s = stringNew();
if (!value({ out: new StringWriter(s), spec: {} }).ok) panic("a Display implementation returned an error unexpectedly: Error", "to_string", true);
const r = value({ out: new StringWriter(s), spec: {} });
if (!r.ok) unwrapFailed("a Display implementation returned an error unexpectedly", r.error, fmtErrorDebug, "to_string");
if (size !== undefined) s.cap = (size === 0 ? s.len : size + (s.v.startsWith("-") ? 1 : 0)) as usize;
return s;
}
Expand Down
Loading