mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-24 06:14:01 +08:00
- scr_string_to_number: ECMA-262 StringNumericLiteral exactly — StrWhiteSpace trim (empty → +0), signed decimals through strtod over the validated span, unsigned 0x/0o/0b through the exact bignum rounding, trailing garbage → NaN - oracle suite mirrors the formatting precedent: gen-tonumber-cases.mjs (Node is the oracle), 50k committed cases asserting bit-equal doubles under ASan + RC audit - fuzzed once against Node on 1,000,000 mixed-shape strings: 1000000/1000000 bit-exact
232 lines
8.9 KiB
JavaScript
232 lines
8.9 KiB
JavaScript
// Generates ToNumber(string) test cases with Node as the oracle — the
|
|
// scr_string_to_number counterpart of gen-number-cases.mjs (formatting).
|
|
// Each line: <UTF-8 bytes of the input string as hex, "-" when empty>\t
|
|
// <16 hex digits of Number(input)'s bit pattern>
|
|
//
|
|
// node gen-tonumber-cases.mjs cases # curated + 50k seeded cases → stdout
|
|
// node gen-tonumber-cases.mjs fuzz <count> # <count> random cases → stdout
|
|
import { argv, stdout } from "node:process";
|
|
|
|
const f64 = new Float64Array(1);
|
|
const u64 = new BigUint64Array(f64.buffer);
|
|
|
|
function line(s) {
|
|
f64[0] = Number(s);
|
|
const hex = Buffer.from(s, "utf8").toString("hex");
|
|
return `${hex === "" ? "-" : hex}\t${u64[0].toString(16).padStart(16, "0")}\n`;
|
|
}
|
|
|
|
// The complete StrWhiteSpace set: WhiteSpace (TAB VT FF SP NBSP ZWNBSP +
|
|
// the Zs category) and LineTerminator (LF CR LS PS).
|
|
const WS = [
|
|
"\t", "\n", "\v", "\f", "\r", " ", "\u00a0", "\u1680",
|
|
"\u2000", "\u2001", "\u2002", "\u2003", "\u2004", "\u2005", "\u2006",
|
|
"\u2007", "\u2008", "\u2009", "\u200a", "\u2028", "\u2029", "\u202f",
|
|
"\u205f", "\u3000", "\ufeff",
|
|
];
|
|
|
|
function* curated() {
|
|
// Whitespace: empty, each StrWhiteSpaceChar alone, all of them joined,
|
|
// and NON-whitespace lookalikes (ZWSP, ZWNJ, MVS) that must be NaN.
|
|
yield "";
|
|
yield* WS;
|
|
yield WS.join("");
|
|
yield* ["\u200b", "\u200c", "\u200d", "\u180e", "\u0085"];
|
|
// Core decimals, signed and whitespace-decorated.
|
|
const core = [
|
|
"0", "-0", "+0", "1", "42", "-1", "0.5", ".5", "5.", "1.", "+.5", "-.5",
|
|
"0.1", "0.2", "0.3", "123.456", "3.141592653589793", "1e3", "1E3",
|
|
"1e+3", "1e-3", "5.e3", ".5e-3", "2e308", "1e309", "-1e309", "1e-309",
|
|
"1e-324", "5e-324", "2.5e-324", "2e-324", "7.5e-324", "1e-400", "1e400",
|
|
"9007199254740991", "9007199254740992", "9007199254740993",
|
|
"9007199254740993.0", "9007199254740994", "9007199254740995",
|
|
"1.7976931348623157e308", "1.7976931348623158e308",
|
|
"1.7976931348623159e308", "-1.7976931348623157e308",
|
|
"2.2250738585072014e-308", "2.2250738585072011e-308",
|
|
"4.9406564584124654e-324", "2.4703282292062327e-324",
|
|
"2.4703282292062328e-324", "1.0000000000000002", "1.0000000000000001",
|
|
"0.000001", "0.0000001", "1e21", "1e-7", "123456789012345678901",
|
|
"Infinity", "+Infinity", "-Infinity",
|
|
];
|
|
yield* core;
|
|
for (const s of core) {
|
|
yield ` ${s} `;
|
|
yield `\t\n${s}\u00a0\ufeff`;
|
|
yield `\u3000${s}\u2028`;
|
|
}
|
|
// Non-decimal integer literals: every prefix case, digit sweeps, the
|
|
// 2^53 neighborhood (rounding), giant exact-integer inputs (bignum
|
|
// rounding, half-even ties), and overflow past 2^1024 → Infinity.
|
|
const radixes = [
|
|
["0x", "0X"], ["0o", "0O"], ["0b", "0B"],
|
|
];
|
|
const digits = {
|
|
"0x": "0123456789abcdefABCDEF", "0o": "01234567", "0b": "01",
|
|
};
|
|
for (const [lo, up] of radixes) {
|
|
const d = digits[lo];
|
|
yield* [`${lo}0`, `${up}0`, `${lo}1`, `${up}1`, `${lo}${d}`, `${lo}00007`];
|
|
yield ` ${lo}11 `;
|
|
yield* [lo, up, `${lo} 1`, `-${lo}1`, `+${lo}1`, `${lo}1.5`, `${lo}1e2x`];
|
|
}
|
|
yield* [
|
|
"0x1fffffffffffff", "0x20000000000000", "0x20000000000001",
|
|
"0x20000000000002", "0x20000000000003", "0x20000000000004",
|
|
"0x3fffffffffffff", "0x40000000000001", "0x40000000000002",
|
|
"0x7fffffffffffffff", "0x8000000000000000", "0xffffffffffffffff",
|
|
"0x10000000000000000", "0x100000000000000001",
|
|
"0xfffffffffffff800000000000000000000", // half-even tie territory
|
|
"0xfffffffffffffbffffffffffffffffffff",
|
|
"0xfffffffffffffc00000000000000000000",
|
|
"0x" + "f".repeat(100), "0x" + "1" + "0".repeat(255), // < 2^1024
|
|
"0x" + "1" + "0".repeat(256), // = 2^1024 → Infinity
|
|
"0x" + "f".repeat(300), // way past → Infinity
|
|
"0b" + "1".repeat(53), "0b" + "1".repeat(54), "0b" + "1".repeat(55),
|
|
"0b1" + "0".repeat(52) + "1", "0b1" + "0".repeat(53) + "1",
|
|
"0b" + "1".repeat(1024), "0b1" + "0".repeat(1023),
|
|
"0b1" + "0".repeat(1024), "0o" + "7".repeat(400),
|
|
"0o1" + "0".repeat(340), "0x0000000000000000000000000000",
|
|
];
|
|
// Long decimal digit strings: exactness and overflow through strtod.
|
|
for (const k of [17, 20, 25, 50, 100, 308, 309, 310, 400, 800]) {
|
|
yield "9".repeat(k);
|
|
yield "1" + "0".repeat(k);
|
|
yield "0." + "0".repeat(k) + "1";
|
|
yield "0." + "1".repeat(k);
|
|
yield "1." + "9".repeat(k) + "e-100";
|
|
}
|
|
// Garbage shapes: every one must be NaN (asserted against Node either
|
|
// way — the oracle decides).
|
|
yield* [
|
|
"12px", "px12", "1.2.3", "1..2", "..5", "1_000", "1,000", "+-1", "-+1",
|
|
"--1", "++1", "1 2", "+ 1", "- 1", "NaN", "nan", "-NaN", "infinity",
|
|
"INFINITY", "Infinit", "Infinityy", "+Infinity8", "- Infinity",
|
|
"e5", ".e5", "+.", "-.", ".", "+", "-", "0x", "0xg", "0o8", "0b2",
|
|
"1e", "1e+", "1e-", "1e1.5", "1e+1e", "0x1_0", "0b1_0", "\u0661\u0662\u0663", "12\u0663",
|
|
"1\u200b", "\u200b1", "1 |