mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-08 08:44:37 +08:00
String.fromCharCode, String.fromCodePoint, the global isNaN and isFinite, DataView offsets and values, and typed-array slice/subarray bounds took a `number | undefined` value (an out-of-range read the checker types as number) through a checked narrow, which threw "undefined is not representable" or refused to compile. Node converts undefined with ToNumber: code unit 0, a RangeError for fromCodePoint, isNaN true, offset 0; an undefined slice end is the length. The Math argument conversion becomes a shared helper that also handles field reads and any other value lowered as a number-or-undefined union.
96 lines
3.4 KiB
TypeScript
96 lines
3.4 KiB
TypeScript
// Builtins that convert a number argument with ToNumber see a missing
|
|
// read (an out-of-range typed-array or array read the checker types as
|
|
// `number`) as NaN, ToNumber's answer for undefined: String.fromCharCode
|
|
// yields code unit 0, fromCodePoint throws a RangeError, Math answers NaN,
|
|
// isNaN is true. The missing value reaches them from a module binding, a
|
|
// local, a class field and a record field.
|
|
const base: number = process.argv.length;
|
|
const chars = new Uint16Array(4);
|
|
chars[0] = 108;
|
|
chars[1] = 66;
|
|
const nums: number[] = [7];
|
|
const far = base + 100;
|
|
|
|
const missing = chars[far];
|
|
const present = chars[1];
|
|
const missingNum = nums[far];
|
|
|
|
function show(label: string, value: unknown): void {
|
|
console.log(label, typeof value === "string" ? JSON.stringify(value) : value);
|
|
}
|
|
|
|
function attempt(label: string, run: () => unknown): void {
|
|
try {
|
|
show(label, run());
|
|
} catch (e) {
|
|
console.log(label, "threw", (e as Error).name, (e as Error).message);
|
|
}
|
|
}
|
|
|
|
// String.fromCharCode / fromCodePoint.
|
|
show("fcc module", String.fromCharCode(chars[0], missing));
|
|
show("fcc single", String.fromCharCode(missing));
|
|
show("fcc present", String.fromCharCode(chars[0], present));
|
|
show("fcc array", String.fromCharCode(missingNum, 65));
|
|
attempt("fcp module", () => String.fromCodePoint(missing));
|
|
show("fcp present", String.fromCodePoint(present));
|
|
|
|
// Math functions.
|
|
show("abs", Math.abs(missing));
|
|
show("floor", Math.floor(missingNum));
|
|
show("max", Math.max(missing, 1));
|
|
show("min3", Math.min(1, 2, missing));
|
|
show("imul", Math.imul(missing, 3));
|
|
show("pow", Math.pow(present, 2));
|
|
show("hypot", Math.hypot(missing, 3));
|
|
|
|
// The global number predicates convert; the Number statics do not.
|
|
show("isNaN", isNaN(missing));
|
|
show("isNaN present", isNaN(present));
|
|
show("isFinite", isFinite(missing));
|
|
show("isFinite present", isFinite(present));
|
|
show("Number.isNaN", Number.isNaN(missing));
|
|
show("Number.isFinite", Number.isFinite(missing));
|
|
|
|
// DataView offsets (ToIndex) and values (ToNumber).
|
|
const view = new DataView(new ArrayBuffer(4));
|
|
view.setUint8(1, 9);
|
|
show("dv get", view.getUint8(missing));
|
|
view.setUint8(missing, 5);
|
|
show("dv set offset", view.getUint8(0));
|
|
view.setUint8(2, missing);
|
|
show("dv set value", view.getUint8(2));
|
|
|
|
// Typed-array ranges: a missing start is 0, a missing end is the length.
|
|
show("slice start", chars.slice(missing).length);
|
|
show("slice end", chars.slice(1, missing).length);
|
|
show("subarray start", chars.subarray(missing).length);
|
|
show("subarray end", chars.subarray(2, missing).length);
|
|
|
|
// The same reads held in a local, a class field and a record field.
|
|
function local(i: number, label: string): void {
|
|
let code: number | undefined = 0;
|
|
code = chars[i];
|
|
show(`local fcc ${label}`, String.fromCharCode(97, code));
|
|
show(`local max ${label}`, Math.max(code, 1));
|
|
show(`local isNaN ${label}`, isNaN(code));
|
|
}
|
|
local(far, "missing");
|
|
local(1, "present");
|
|
|
|
class Cursor {
|
|
code: number | undefined = 0;
|
|
}
|
|
const cursor = new Cursor();
|
|
cursor.code = chars[far];
|
|
show("field fcc", String.fromCharCode(97, cursor.code));
|
|
show("field abs", Math.abs(cursor.code));
|
|
cursor.code = chars[1];
|
|
show("field fcc present", String.fromCharCode(97, cursor.code));
|
|
|
|
const record: { code: number | undefined } = { code: 0 };
|
|
record.code = chars[far];
|
|
show("record fcc", String.fromCharCode(97, record.code));
|
|
show("record max", Math.max(record.code, 1));
|
|
show("record isNaN", isNaN(record.code));
|