diff --git a/AGENTS.md b/AGENTS.md index 4d66cb42fa..b94c900676 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,6 +48,12 @@ Corpus programs are differential tests against Node: every program runs under No Before opening a PR that adds or changes corpus, diagnostic, or frontend fixtures, run `pnpm test:ts7 --baselines-only`. This fast completeness check uses the same fixture discovery as the parity suite; it does not type-check or replace behavioral parity. Generate missing preflight/order baselines, review their diagnostics and module order, and run the affected `order-parity.test.ts` batches before pushing. Keep existing baseline entries unchanged unless the corresponding behavior intentionally changes. The full `pnpm test:ts7` command also checks completeness before starting its sweep. +A change that must not cost good code anything, such as a compatibility fix that replaces a refusal, proves it with the zero-cost gate. The gate compiles the benchmark workloads, the self-hosted compiler, and the TypeScript corpus with two built checkouts. It requires byte-identical IR, LLVM, and release executables, and compile time within tolerance. `--allow=` names the change's pattern signature: programs whose sources match may differ, and the PR lists them. Any other difference fails the gate, which names the first differing function. The gate also enforces the two-tier runtime separation: no executable may gain symbols from `DYNAMIC_TIER_PREFIXES` in `scripts/zero-cost-compare.mjs`, and fully typed benchmarks may link none; extend that list as dynamic-tier units land. `--quick` checks only the benchmarks; `--cachegrind` adds per-function instruction counts for executables that differ. Run it in a Sandbox with both checkouts prepared: + +```bash +node scripts/zero-cost-gate.mjs --baseline=/vercel/sandbox/prev --candidate=. --allow='\barguments\b' +``` + Test location follows scope: - Co-locate white-box unit tests with implementation files under diff --git a/benchmarks/regions/README.md b/benchmarks/regions/README.md index 457f059bea..0cc450abc1 100644 --- a/benchmarks/regions/README.md +++ b/benchmarks/regions/README.md @@ -7,6 +7,7 @@ Programs that build generations of `ProgramLifetime` objects and free each gener | `generations.ts` | Per-file syntax trees and binder symbols in one region per file, checker state (types, signatures, node and symbol links in paged tables, literal and union caches) in one region per generation. `edit` re-parses one file per generation, `all` re-parses every file. | | `compiler.ts` | A tokenizer, recursive-descent parser, binder and checker over generated sources, rebuilt from scratch in one region per build, like a watch mode that does not reuse syntax trees. | | `emit.ts` | A large live graph of ordinary objects with parent links, standing in for a type checker, and a per-file emit that builds its output in a region released right after. | +| `requests.ts` | Batches of concurrent async request handlers, each in its own region across its awaits: tokenize a JSON body, parse it into a syntax tree that points at its tokens, validate it into issues and an index, render a response, release the region. | ```console $ node benchmarks/regions/run.mjs --baseline=../scriptc-main --runs=7 diff --git a/benchmarks/regions/emit.ts b/benchmarks/regions/emit.ts index 630708361e..9fd15b8605 100644 --- a/benchmarks/regions/emit.ts +++ b/benchmarks/regions/emit.ts @@ -54,7 +54,7 @@ let level: Type[] = [new Type(id++, undefined)]; while (id < typeCount) { const next: Type[] = []; for (const t of level) for (let k = 0; k < 4 && id < typeCount; k++) next.push(new Type(id++, t)); - roots.push(...next); + for (const t of next) roots.push(t); level = next; } diff --git a/benchmarks/regions/requests.ts b/benchmarks/regions/requests.ts new file mode 100644 index 0000000000..6148eb25df --- /dev/null +++ b/benchmarks/regions/requests.ts @@ -0,0 +1,221 @@ +// An async request handler: each request tokenizes a JSON body, awaits (as +// if reading more of it), parses the tokens into a syntax tree whose nodes +// point back at their tokens, awaits again (as if loading reference data), +// validates the tree into issues held in an index, and renders a response. +// Requests run in batches of concurrent handlers, so the handlers of one +// batch interleave at every await. +// +// requests [plain] +// +// Each request runs in its own LifetimeRegion, released when its response +// is rendered; `plain` never constructs a region. +import { readFileSync } from "node:fs"; +import type { ProgramLifetime } from "@scriptc/types"; +import { LifetimeRegion } from "@scriptc/experimental"; + +const T_PUNCT = 0; +const T_STR = 1; +const T_NUM = 2; +const T_WORD = 3; + +class Token implements ProgramLifetime { + readonly kind: number; + readonly text: string; + readonly pos: number; + constructor(kind: number, text: string, pos: number) { + this.kind = kind; + this.text = text; + this.pos = pos; + } +} + +const N_OBJECT = 0; +const N_ARRAY = 1; +const N_STRING = 2; +const N_NUMBER = 3; +const N_LITERAL = 4; + +class JsonNode implements ProgramLifetime { + readonly kind: number; + readonly token: Token; + parent: JsonNode | undefined = undefined; + readonly keys: string[] = []; + readonly children: JsonNode[] = []; + constructor(kind: number, token: Token) { + this.kind = kind; + this.token = token; + } +} + +class Issue implements ProgramLifetime { + readonly path: string; + readonly node: JsonNode; + readonly message: string; + constructor(path: string, node: JsonNode, message: string) { + this.path = path; + this.node = node; + this.message = message; + } +} + +// ---- request bodies (deterministic) ---- + +function body(id: number, items: number): string { + let seed = 7 + id * 7919; + const rand = (n: number): number => { + seed = (seed * 1103515245 + 12345) % 2147483648; + return seed % n; + }; + const parts: string[] = []; + for (let i = 0; i < items; i++) { + const qty = rand(12) - 1; + const sku = rand(9) === 0 ? "" : `sku-${rand(5000)}`; + const tags = Array.from({ length: rand(4) }, (_, k) => `"t${k}${rand(10)}"`).join(","); + parts.push(`{"sku":"${sku}","qty":${qty},"price":${rand(10000) / 100},"gift":${rand(2) === 0},"tags":[${tags}]}`); + } + return `{"order":${id},"customer":"c${id % 97}","items":[${parts.join(",")}]}`; +} + +// ---- handler ---- + +function tokenize(text: string): Token[] { + const tokens: Token[] = []; + let i = 0; + while (i < text.length) { + const c = text.charCodeAt(i); + const start = i; + if (c === 34) { + i++; + while (text.charCodeAt(i) !== 34) i++; + i++; + tokens.push(new Token(T_STR, text.slice(start + 1, i - 1), start)); + } else if (c === 45 || (c >= 48 && c <= 57)) { + i++; + while (i < text.length && (text.charCodeAt(i) === 46 || (text.charCodeAt(i) >= 48 && text.charCodeAt(i) <= 57))) i++; + tokens.push(new Token(T_NUM, text.slice(start, i), start)); + } else if (c >= 97 && c <= 122) { + while (i < text.length && text.charCodeAt(i) >= 97 && text.charCodeAt(i) <= 122) i++; + tokens.push(new Token(T_WORD, text.slice(start, i), start)); + } else { + i++; + tokens.push(new Token(T_PUNCT, text[start]!, start)); + } + } + return tokens; +} + +class Parser { + private at = 0; + private readonly tokens: Token[]; + constructor(tokens: Token[]) { + this.tokens = tokens; + } + value(): JsonNode { + const token = this.tokens[this.at++]!; + if (token.kind === T_STR) return new JsonNode(N_STRING, token); + if (token.kind === T_NUM) return new JsonNode(N_NUMBER, token); + if (token.kind === T_WORD) return new JsonNode(N_LITERAL, token); + const node = new JsonNode(token.text === "{" ? N_OBJECT : N_ARRAY, token); + const close = token.text === "{" ? "}" : "]"; + while (this.tokens[this.at]!.text !== close) { + if (node.kind === N_OBJECT) { + node.keys.push(this.tokens[this.at]!.text); + this.at += 2; // the key and the colon + } + const child = this.value(); + child.parent = node; + node.children.push(child); + if (this.tokens[this.at]!.text === ",") this.at++; + } + this.at++; + return node; + } +} + +function member(node: JsonNode, key: string): JsonNode | undefined { + const i = node.keys.indexOf(key); + return i < 0 ? undefined : node.children[i]; +} + +function validate(root: JsonNode, issues: Issue[], index: Map): number { + let total = 0; + const items = member(root, "items")!; + for (let i = 0; i < items.children.length; i++) { + const item = items.children[i]!; + const path = `items[${i}]`; + const sku = member(item, "sku")!; + if (sku.token.text === "") issues.push(new Issue(`${path}.sku`, sku, "empty sku")); + else if (index.has(sku.token.text)) issues.push(new Issue(`${path}.sku`, sku, `duplicate of ${index.get(sku.token.text)!.token.pos}`)); + else index.set(sku.token.text, item); + const qty = Number(member(item, "qty")!.token.text); + if (qty <= 0) issues.push(new Issue(`${path}.qty`, member(item, "qty")!, "quantity must be positive")); + else total += qty * Number(member(item, "price")!.token.text); + const tags = member(item, "tags")!; + for (const tag of tags.children) if (tag.token.text.endsWith("0")) issues.push(new Issue(`${path}.tags`, tag, "reserved tag")); + } + return total; +} + +const hop = (): Promise => new Promise((resolve) => setImmediate(resolve)); + +async function handle(text: string): Promise { + const tokens = tokenize(text); + await hop(); + const root = new Parser(tokens).value(); + await hop(); + const issues: Issue[] = []; + const index = new Map(); + const total = validate(root, issues, index); + await hop(); + const first = issues[0]; + return `${member(root, "order")!.token.text} ${tokens.length} ${index.size} ${issues.length} ${total.toFixed(2)}` + + (first === undefined ? "" : ` ${first.path}@${first.node.token.pos}`); +} + +// ---- driver ---- + +function rssMb(key: string): number { + const m = new RegExp(`${key}:\\s*(\\d+) kB`).exec(readFileSync("/proc/self/status", "utf8")); + return m === null ? 0 : Number(m[1]) / 1024; +} + +const args = process.argv.slice(2); +const requests = Number(args[0] ?? "4000"); +const concurrency = Number(args[1] ?? "16"); +const items = Number(args[2] ?? "100"); +const useRegions = args[3] !== "plain"; + +async function serve(id: number): Promise { + const text = body(id, items); + if (!useRegions) return await handle(text); + const region = new LifetimeRegion(); + const response = await region.run(() => handle(text)); + region.release(); + return response; +} + +async function main(): Promise { + let hash = 0; + let lines = 0; + let sample = ""; + let rssFirst = 0; + const t0 = performance.now(); + for (let next = 0; next < requests; next += concurrency) { + const batch: Promise[] = []; + for (let k = next; k < next + concurrency && k < requests; k++) batch.push(serve(k)); + for (const response of await Promise.all(batch)) { + for (let i = 0; i < response.length; i++) hash = (hash * 31 + response.charCodeAt(i)) % 1000000007; + lines++; + if (lines === 1) sample = response; + } + if (next === 0) rssFirst = rssMb("VmRSS"); + } + const ms = performance.now() - t0; + console.log(`${lines} responses, hash ${hash}, first ${sample}`); + console.log( + `total ${ms.toFixed(1)} ms per-request ${((ms * 1000) / requests).toFixed(1)} us` + + ` rss-first ${rssFirst.toFixed(1)} MB rss-last ${rssMb("VmRSS").toFixed(1)} MB peak ${rssMb("VmHWM").toFixed(1)} MB`, + ); +} + +main(); diff --git a/benchmarks/regions/run.mjs b/benchmarks/regions/run.mjs index 1f1c4609f4..e698dc47d4 100644 --- a/benchmarks/regions/run.mjs +++ b/benchmarks/regions/run.mjs @@ -39,6 +39,8 @@ const CONFIGS = [ { name: "compiler-one-shot", program: "compiler.ts", args: ["400", "60", "1"] }, // Per-file emit regions next to a large live heap of ordinary objects. { name: "emit-files", program: "emit.ts", args: ["400000", "200", "2000"] }, + // Concurrent async request handlers, one region per request across its awaits. + { name: "requests-async", program: "requests.ts", args: ["4000", "16", "100"] }, ]; const work = join(tmpdir(), `scriptc-region-bench-${process.pid}`); @@ -93,7 +95,7 @@ for (const config of CONFIGS) { const lines = r.stdout.trim().split("\n"); outputs.set(name, lines[0].replace(/ gen0 .*/, "")); const metrics = {}; - for (const [, key, value] of lines.at(-1).matchAll(/([a-z0-9-]+) ([0-9.]+) (?:ms|MB)/g)) + for (const [, key, value] of lines.at(-1).matchAll(/([a-z0-9-]+) ([0-9.]+) (?:ms|us|MB)/g)) metrics[key] = Number(value); samples[name].push(metrics); } diff --git a/benchmarks/runtime/README.md b/benchmarks/runtime/README.md index ed9ee0532b..9635ee1f7a 100644 --- a/benchmarks/runtime/README.md +++ b/benchmarks/runtime/README.md @@ -19,6 +19,7 @@ Application-shaped workloads for measuring the speed of compiled executables. Ea | `intern-table`, `intern-table-map`, `intern-table-1`, `intern-table-map-1` | cpu | worker threads interning the same type-like objects under string keys and records under number keys, then hits and misses: one shared `InternTable` per kind of key, or a module-level `Map` per thread (the `-map` entries), with 8 threads or 1 (the `-1` entries); under Node.js every thread has its own tables | | `atomics-contended` | cpu | four worker threads updating `SharedArrayBuffer` counters with `Atomics`: one shared counter, one counter per worker, a `compareExchange` loop, and `Atomics.wait`/`notify` handoffs | | `value-objects` | cpu | small immutable records and class instances: vector helpers, method chains, particle updates | +| `value-records` | cpu | records that hold strings, spread state updates and destructured tuples: token helpers, an immutable reducer, tuple returns | | `region-builds` | cpu | a watch-mode build loop over `LifetimeRegion`: each build binds a program in a new region and releases the previous one, which a cache keeps referenced for two builds | | `optional-fields` | cpu | records and class instances with `number \| undefined` and other scalar-union fields | | `record-boundaries` | cpu | records built from unchecked array reads passed to and returned from annotated functions | diff --git a/benchmarks/runtime/workloads.json b/benchmarks/runtime/workloads.json index c0d643a583..b95791a1d1 100644 --- a/benchmarks/runtime/workloads.json +++ b/benchmarks/runtime/workloads.json @@ -141,6 +141,13 @@ "2" ] }, + { + "name": "value-records", + "entry": "workloads/value-records.ts", + "args": [ + "1" + ] + }, { "name": "region-builds", "entry": "workloads/region-builds.ts", diff --git a/benchmarks/runtime/workloads/value-records.ts b/benchmarks/runtime/workloads/value-records.ts new file mode 100644 index 0000000000..922fb8e439 --- /dev/null +++ b/benchmarks/runtime/workloads/value-records.ts @@ -0,0 +1,93 @@ +// Records that hold references: a tokenizer that builds token records with +// string fields and passes them to helpers, an immutable reducer that +// updates its state with object spread, and tuple-returning helpers whose +// results are destructured at once. None of these temporaries outlives +// the statement or loop iteration that builds it. +interface Token { + kind: string; + text: string; + pos: number; +} +const KEYWORDS = ["let", "const", "if", "else", "for", "while", "return", "function"]; +function classify(text: string): string { + return KEYWORDS.includes(text) ? "keyword" : "ident"; +} +function token(kind: string, text: string, pos: number): Token { + return { kind, text, pos }; +} +function weight(t: Token): number { + return t.kind === "keyword" ? t.text.length * 3 : t.text.length + (t.pos & 7); +} +function tokenize(source: string): number { + let sum = 0; + let start = -1; + for (let i = 0; i <= source.length; i++) { + const c = i < source.length ? source.charCodeAt(i) : 32; + const word = (c >= 97 && c <= 122) || (c >= 48 && c <= 57) || c === 95; + if (word) { + if (start < 0) start = i; + continue; + } + if (start >= 0) { + const text = source.slice(start, i); + const t = token(classify(text), text, start); + sum += weight(t); + start = -1; + } + if (c !== 32 && c !== 10) { + const punct = { kind: "punct", text: source[i]!, pos: i }; + sum += weight(punct) + (punct.text === ";" ? 1 : 0); + } + } + return sum; +} + +interface State { + mode: string; + depth: number; + count: number; + last: string; +} +function step(s: State, text: string): State { + if (text === "{") return { ...s, depth: s.depth + 1, last: text }; + if (text === "}") return { ...s, depth: s.depth - 1, last: text }; + return s.mode === "code" ? { ...s, count: s.count + 1 } : { ...s, mode: "code", last: text }; +} +function reduce(texts: string[], rounds: number): number { + let total = 0; + for (let r = 0; r < rounds; r++) { + let state: State = { mode: "start", depth: 0, count: 0, last: "" }; + for (const text of texts) { + state = step(state, text); + if (state.depth > 3) state = { ...state, depth: 3 }; + } + total += state.count + state.depth + state.last.length + state.mode.length; + } + return total; +} + +function divmod(a: number, b: number): [number, number] { + return [Math.floor(a / b), a % b]; +} +function split(text: string, at: number): [string, string] { + return [text.slice(0, at), text.slice(at)]; +} +function tuples(n: number, texts: string[]): number { + let sum = 0; + for (let i = 0; i < n; i++) { + const [q, r] = divmod(i * 31, 17); + const [head, tail] = split(texts[i % texts.length]!, r % 3); + sum += q + r + head.length * 2 + tail.length; + } + return sum; +} + +const scaleArg = Number(process.argv[2] ?? "1"); +const line = "function step ( s , text ) { if ( text ) { return s ; } let x = s ; while ( x ) { x = x ; } }\n"; +const source = line.repeat(Math.floor(400 * scaleArg)); +const texts = source.split(" ").filter((t) => t.length > 0); +let lexed = 0; +for (let i = 0; i < 240; i++) lexed += tokenize(source); +const reduced = reduce(texts, Math.floor(600 * scaleArg)); +const pairs = tuples(Math.floor(2_000_000 * scaleArg), texts); +console.log("tokens", lexed, "reduce", reduced, "tuples", pairs); diff --git a/docs/content/docs/dependencies.mdx b/docs/content/docs/dependencies.mdx index f939522217..5ecc144c60 100644 --- a/docs/content/docs/dependencies.mdx +++ b/docs/content/docs/dependencies.mdx @@ -69,6 +69,10 @@ $ scriptc build app.ts --npm-static=three -o app >/dev/null Naming a package skips the declaration requirement above, but the package still has to compile. `--npm-static=auto` uses the default automatic selection. The option accepts a comma-separated list and can be repeated. +## CommonJS packages + +CommonJS packages compile with Node.js's module semantics. Each `require()` evaluates its module at the call, once, and later calls return the cached exports. A package can assign `module.exports` and run code before it requires its dependencies. If that code reads a `const` or `let` require binding before its `require` runs, the read throws the same `ReferenceError` as Node.js. A `var` binding that earlier code can reach is reported at build time. A `require()` of a JSON file, such as a package's own `package.json`, returns one object parsed at build time, shared by every module that requires it. A require cycle compiles when each of its modules assigns its exports before requiring anything, as semver's classes do, and uses the other modules only inside functions that its top-level code can't call. Other require cycles are reported at build time. The [limitations](/docs/limitations) page lists the supported `require` forms. + ## Unsupported code inside a package A JavaScript package can contain operations scriptc can't compile, often in functions your program never calls. These don't fail the build: each one compiles to code that throws a runtime error if it ever runs. Coverage reports list them as deferred sites, so review the list and test the parts of the package your program uses. diff --git a/docs/content/docs/experimental.mdx b/docs/content/docs/experimental.mdx index ac5891ec97..6a0fb53efe 100644 --- a/docs/content/docs/experimental.mdx +++ b/docs/content/docs/experimental.mdx @@ -73,9 +73,9 @@ scriptc region heap passes: 0
new LifetimeRegion()
Creates a region. Creating a region also retries the released regions that could not be freed yet.
region.run(fn)
-
Calls fn with the region as the current region and returns its result. ProgramLifetime instances created during the call belong to the region. Calls nest: a run inside the callback makes another region current until it returns, and the previous region is current again however the callback finishes, including when it throws.
+
Calls fn with the region as the current region and returns its result. ProgramLifetime instances created during the call belong to the region, and so do those that async functions called during it create after an await (see Async work). Calls nest: a run inside the callback makes another region current until it returns, and the previous region is current again however the callback finishes, including when it throws.
region.release()
-
Requests that the region be freed. scriptc frees it as soon as nothing outside the region refers to any of its instances, which may be at once, at a later release or region creation, or never. A region is never freed while a run call on it is active. After release(), run still calls its callback, and the instances it creates belong to the default region.
+
Requests that the region be freed. scriptc frees it as soon as nothing outside the region refers to any of its instances, which may be at once, at a later release or region creation, or never. A region is never freed while a run call on it is active, or while async work started inside it has not finished. After release(), run still calls its callback, and the instances it creates belong to the default region.
`ProgramLifetime` instances created outside every `run` call belong to the default region, which is never freed. Ordinary objects (instances of classes without the contract, arrays, maps, closures) are reference counted as always; a region frees the ones that only its instances refer to. @@ -92,7 +92,62 @@ A released region stays allocated, and its instances stay usable, while anything - an instance of the default region, which is never freed; - a promise, generator or `Error` that holds an instance, until it is no longer referenced. -Work that resumes after an `await` inside the callback runs outside the region, so the instances it creates belong to the default region and keep the instances they refer to allocated. +### Async work + +An async function called inside `run` keeps the region for its whole run: the instances it creates after each `await` belong to the region too, as do those of the async functions it calls. A server can give every request its own region, even while requests interleave at their awaits: + +```ts:requests.ts +import type { ProgramLifetime } from "@scriptc/types"; +import { LifetimeRegion } from "@scriptc/experimental"; + +class Part implements ProgramLifetime { + readonly text: string; + previous: Part | undefined = undefined; + constructor(text: string) { + this.text = text; + } +} + +const read = (): Promise => new Promise((resolve) => setImmediate(resolve)); + +async function handle(id: number): Promise { + let last: Part | undefined; + for (let i = 0; i < 1000; i++) { + if (i % 100 === 0) await read(); // the next chunk of the request body + const part = new Part(`${id}.${i}`); + part.previous = last; + last = part; + } + return `request ${id}: last part ${last!.text}`; +} + +async function serve(id: number): Promise { + const region = new LifetimeRegion(); + try { + return await region.run(() => handle(id)); + } finally { + region.release(); + } +} + +Promise.all([1, 2, 3].map(serve)).then((responses) => { + for (const response of responses) console.log(response); +}); +``` + +Each request's 1000 parts are freed when the request releases its region: + +```console +$ scriptc build requests.ts -o requests >/dev/null +$ SCRIPTC_REGION_STATS=1 ./requests >/dev/null +scriptc region 1: freed 1000 objects, 0 owned objects, 1 chunks (2.0 MiB); cycles 0.0 ms, walk 0.0 ms, free 0.0 ms +scriptc region 2: freed 1000 objects, 0 owned objects, 1 chunks (2.0 MiB); cycles 0.0 ms, walk 0.0 ms, free 0.0 ms +scriptc region 3: freed 1000 objects, 0 owned objects, 1 chunks (2.0 MiB); cycles 0.0 ms, walk 0.0 ms, free 0.0 ms +scriptc regions: 3 created, 3 released, 3 freed, 0 pinned, 0 kept +scriptc region heap passes: 0 +``` + +A region released while async work started inside it is still pending stays allocated until that work has finished, and the work keeps creating its instances in the region. Callbacks that the event loop calls (timers, `queueMicrotask`, event listeners) and the bodies of async generators run outside the region; synchronous generators run in the region of the code that resumes them. ### Threads @@ -102,7 +157,7 @@ Each thread has its own regions and its own default region. A region belongs to Programs that never construct a `LifetimeRegion` compile exactly as they would without the package. In programs that do, every retain and release of a `ProgramLifetime` instance that belongs to a region updates a count of the region chunk that holds it. Instances of the default region are not counted, and until the program creates its first region, a retain or release only tests whether counting has started. -Freeing a region visits each of its instances and the ordinary objects they own. A region that the check would keep may be kept only by unreachable cycles of ordinary objects that refer into it, so before keeping it scriptc runs the cycle collector over the thread's heap and checks again. +Freeing a region visits each of its instances and the ordinary objects they own. Each thread keeps up to 8 MiB of the memory of freed regions for its next regions, so a program that frees a region per request or per batch does not map and fault in new pages every time. A region that the check would keep may be kept only by unreachable cycles of ordinary objects that refer into it, so before keeping it scriptc runs the cycle collector over the thread's heap and checks again. `SCRIPTC_REGION_STATS=1` prints one line per release attempt, freed, `kept` (with the number of references the check could not explain) or `pinned` (published), and at exit a summary per thread and the number of those cycle-collector passes over the thread's heap. diff --git a/docs/content/docs/limitations.mdx b/docs/content/docs/limitations.mdx index ed2c5b1ec3..544bd2b2dd 100644 --- a/docs/content/docs/limitations.mdx +++ b/docs/content/docs/limitations.mdx @@ -32,6 +32,8 @@ title: "Limitations" ../limitations/language-features/dynamic-imports.mdx +../limitations/language-features/commonjs-require.mdx + ### Types and class definitions ../limitations/types-and-class-definitions/bigint.mdx diff --git a/docs/content/limitations/array-bounds-and-runtime-errors.mdx b/docs/content/limitations/array-bounds-and-runtime-errors.mdx index 431e60efe3..8eb664a249 100644 --- a/docs/content/limitations/array-bounds-and-runtime-errors.mdx +++ b/docs/content/limitations/array-bounds-and-runtime-errors.mdx @@ -2,6 +2,8 @@ Typed-array reads at an index that is not an integer in the array's range return Ordinary arrays track holes separately from present `undefined` values. Missing reads and empty `pop()`/`shift()` return `undefined`, indexed writes can grow the array, and length growth creates holes. +Keyed reads of a `Record` or an index signature return `undefined` for a missing key, as in Node.js, with or without `noUncheckedIndexedAccess`. A missing array element or key that reaches storage that cannot hold `undefined`, such as a class field typed `number`, throws a `TypeError`. + Runtime traps cannot be caught. User-thrown values and supported exceptions, including JSON parse, checked-cast, filesystem, regex, and `String.prototype.repeat` count errors, can be caught; hard traps such as out-of-range reads of statically typed ordinary arrays terminate the process. Deep recursion in Linux and macOS executables throws Node's catchable `RangeError: Maximum call stack size exceeded`, on the main thread, in worker threads, and inside async functions and generators. The depth at which it is thrown differs from Node: the main thread uses the process stack limit (usually 8 MiB), worker threads get an 8 MiB stack, and async functions and generators run on 512 KiB stacks, so native code usually recurses deeper than Node on threads and somewhat less deeply inside async bodies. Recursion inside runtime library operations, such as serializing deeply nested data, is not checked. Windows executables, WASI builds and library builds do not check the stack, and exhausting it there terminates the process. diff --git a/docs/content/limitations/builtin-apis/eventemitter.mdx b/docs/content/limitations/builtin-apis/eventemitter.mdx index 0b44cd5493..c4435f4c70 100644 --- a/docs/content/limitations/builtin-apis/eventemitter.mdx +++ b/docs/content/limitations/builtin-apis/eventemitter.mdx @@ -1 +1 @@ -- **EventEmitter:** Literal event names use fixed argument tuples. Computed string names require a constant prefix or suffix that excludes `error`, meta events, and stream-internal events; their arguments must match the exact arity and support checked conversion. Unrestricted names, symbols, names that may reach internal events, and typed `listeners()`/`rawListeners()` results for names with possible computed registrations are unsupported. +- **EventEmitter:** Literal event names use fixed argument tuples. Computed string names require a constant prefix or suffix that excludes `error`, meta events, and stream-internal events; their arguments must match the exact arity and support checked conversion. Unrestricted names, symbols, names that may reach internal events, and typed `listeners()`/`rawListeners()` results for names with possible computed registrations are unsupported. Reading `defaultMaxListeners` through a CommonJS destructured or member binding (`const { defaultMaxListeners } = require("events")`) is unsupported. diff --git a/docs/content/limitations/builtin-apis/regular-expressions.mdx b/docs/content/limitations/builtin-apis/regular-expressions.mdx index a765bbe3cf..d4287396d6 100644 --- a/docs/content/limitations/builtin-apis/regular-expressions.mdx +++ b/docs/content/limitations/builtin-apis/regular-expressions.mdx @@ -1 +1 @@ -- **Regular expressions:** Native checked storage preserves identity, `source`, `flags`, flag properties, and stateful `test`, and the flag getters (`global`, `unicodeSets`, and the rest) compile on statically typed regexes. Static `exec`, global `match`, and numeric `lastIndex` access use the native matcher. Runtime constructors accept checked strings and native regex copies; `replace` and `replaceAll` accept native regex values with string templates or callbacks, including captures, named groups, and UTF-16 offsets. Non-numeric `lastIndex`, detached methods, and `exec` through an untyped receiver are unsupported. The `v` (unicode sets) flag supports class set operations (`--`, `&&`), nested classes, `\q{...}` string literals, and properties of strings such as `\p{RGI_Emoji}`. `new RegExp` with an invalid pattern throws a `SyntaxError` whose message comes from the native engine and can differ from Node's. The `d` (match indices) flag is unsupported: regex literals and `new RegExp` calls with a literal flag string containing it fail to compile with SC1120, and `new RegExp` with a computed flag string containing it throws an error naming the flag. +- **Regular expressions:** Native checked storage preserves identity, `source`, `flags`, flag properties, and stateful `test`, and the flag getters (`global`, `unicodeSets`, and the rest) compile on statically typed regexes. Static `exec`, global `match`, and numeric `lastIndex` access use the native matcher. Runtime constructors accept checked strings and native regex copies; `replace` and `replaceAll` accept native regex values with string templates or callbacks, including captures, named groups, and UTF-16 offsets. Named groups (`groups`) read on matches of a statically known regex: from `exec` and `match`, through optional chains (`m?.groups?.name`), in a `for...of` over `matchAll`, and on the element parameter of an array-method callback over `matchAll` rows. The groups object has a null prototype, as in Node. Non-numeric `lastIndex`, detached methods, and `exec` through an untyped receiver are unsupported. The `v` (unicode sets) flag supports class set operations (`--`, `&&`), nested classes, `\q{...}` string literals, and properties of strings such as `\p{RGI_Emoji}`. `new RegExp` with an invalid pattern throws a `SyntaxError` whose message comes from the native engine and can differ from Node's. The `d` (match indices) flag is unsupported: regex literals and `new RegExp` calls with a literal flag string containing it fail to compile with SC1120, and `new RegExp` with a computed flag string containing it throws an error naming the flag. diff --git a/docs/content/limitations/builtin-apis/test-runner.mdx b/docs/content/limitations/builtin-apis/test-runner.mdx index 7a50901db2..6583a28739 100644 --- a/docs/content/limitations/builtin-apis/test-runner.mdx +++ b/docs/content/limitations/builtin-apis/test-runner.mdx @@ -1,2 +1,5 @@ - **Concurrent tests:** with `{ concurrency }` above 1, subtests run on their own fibers, and results are reported in declaration order, as in Node. Concurrent bodies resume in Node's order around `beforeEach` hooks and nested suites. Other runner steps, such as `afterEach` and `after` hooks, take no microtask turns between bodies, so code that observes the interleaving of concurrent bodies across those steps can see a different order than Node. - **Snapshots:** `t.assert.snapshot` and `t.assert.fileSnapshot` compile, with Node's default serializer, `JSON.stringify(value, null, 2)`, or an `options.serializers` array literal. A compiled test reads the snapshot file Node writes beside the entry file, `.snapshot`, at the entry file's path when the program was compiled. Running the binary with Node's flag `--test-update-snapshots` as its first argument writes the snapshot files instead; as in Node, the flag is kept out of `process.argv` and appears in `process.execArgv`. Snapshot files are read in the form Node writes them, `` exports[`key`] = `value`; `` statements. The `snapshot.setDefaultSnapshotSerializers` and `snapshot.setResolveSnapshotPath` module functions throw `SC2020`. +- **TestContext values:** `t.filePath` is the entry file's path when the program was compiled. `t.attempt` is `0`, since a test runs once. `t.passed` is `false` while the body runs and the test's outcome in its `afterEach` and `t.after` hooks. `t.workerId` is `Number(process.env.NODE_TEST_WORKER_ID) || undefined`, as in Node. `t.tags` is `[]`, since the `tags` test option throws `SC2020`; the array is not frozen as Node's is. +- **Suite and hook contexts:** a `describe` body may take its `SuiteContext`, which has `name`, `fullName`, `filePath`, `attempt`, `passed` and `diagnostic`. Hooks may take their context, as in Node: the suite's for `before` and `after`, the test's for `beforeEach`, `afterEach`, `t.before` and `t.after`. The `SuiteContext` `signal` and `log` members, and hook `done` callbacks, throw `SC2020`. +- **Module-level hooks:** `before` hooks outside any suite run at their `before()` call, and the first top-level test waits for the async ones; `after` hooks outside any suite run after the last top-level test. Both get the root's context, named ``. As in Node, a throwing root `before` fails each top-level test with its error, without running its body, and cancels the tests and suites inside each top-level suite. A throwing root `after` reports the entry file as a failed result, and the exit code is unchanged. diff --git a/docs/content/limitations/builtin-function-values.mdx b/docs/content/limitations/builtin-function-values.mdx index 69a8ff1b18..4fd093e471 100644 --- a/docs/content/limitations/builtin-function-values.mdx +++ b/docs/content/limitations/builtin-function-values.mdx @@ -5,7 +5,7 @@ Some builtins can be stored and passed as functions: - `util.isDeepStrictEqual`, `styleText`, `stripVTControlCharacters`, `toUSVString`, `inherits`, `deprecate`, `callbackify`, `debuglog`, and `debug`. - `fs.existsSync`, `unlinkSync`, `chmodSync`, `chownSync`, `renameSync`, and `closeSync`, and `fs/promises.unlink`, `chmod`, and `rename`. -Other immutable aliases of table-backed Node builtins support direct calls, but cannot be passed elsewhere. `util.promisify` supports compile-time conversions of `child_process.execFile`, `child_process.exec`, `setTimeout` and `setImmediate` (to the `timers/promises` functions, as in Node), `stream.finished` and `stream.pipeline` (to the `stream/promises` functions, as in Node), the `zlib` callback one-shots `deflate`, `inflate`, `deflateRaw`, `inflateRaw`, `gzip`, `gunzip` and `unzip` (with or without options), and the `fs` callback functions that have an `fs/promises` counterpart (`access`, `appendFile`, `chmod`, `chown`, `copyFile`, `lchown`, `link`, `lstat`, `lutimes`, `mkdir`, `mkdtemp`, `readFile`, `readdir`, `readlink`, `realpath`, `rename`, `rm`, `rmdir`, `stat`, `statfs`, `symlink`, `truncate`, `unlink`, `utimes`, and `writeFile`); calls through the converted function lower as the `fs/promises` call. +Other immutable aliases of table-backed Node builtins support direct calls, but cannot be passed elsewhere. `util.promisify` supports compile-time conversions of `child_process.execFile`, `child_process.exec`, `setTimeout` and `setImmediate` (to the `timers/promises` functions, as in Node), `stream.finished` and `stream.pipeline` (to the `stream/promises` functions, as in Node), the `zlib` callback one-shots `deflate`, `inflate`, `deflateRaw`, `inflateRaw`, `gzip`, `gunzip` and `unzip` (with or without options), the `crypto` callback functions `randomBytes`, `pbkdf2`, `scrypt`, `hkdf`, `randomFill`, `randomInt` and `generateKey`, `dns.lookup` (settling with `{ address, family }`, or the address array with `all: true`) and `dns.lookupService` (settling with `{ hostname, service }`), and the `fs` callback functions that have an `fs/promises` counterpart (`access`, `appendFile`, `chmod`, `chown`, `copyFile`, `lchown`, `link`, `lstat`, `lutimes`, `mkdir`, `mkdtemp`, `readFile`, `readdir`, `readlink`, `realpath`, `rename`, `rm`, `rmdir`, `stat`, `statfs`, `symlink`, `truncate`, `unlink`, `utimes`, and `writeFile`); calls through the converted function lower as the `fs/promises` call. `util.styleText` supports standard formats and aliases, `#RGB` and `#RRGGBB` hex colors, arrays of formats, nested ANSI resets, and explicit color enabling with `{ validateStream: false }`. Native calls accept standard process streams; custom native streams and mutations to `util.inspect.colors` remain unsupported. diff --git a/docs/content/limitations/language-features/commonjs-require.mdx b/docs/content/limitations/language-features/commonjs-require.mdx new file mode 100644 index 0000000000..5784994103 --- /dev/null +++ b/docs/content/limitations/language-features/commonjs-require.mdx @@ -0,0 +1 @@ +- **CommonJS `require`:** Literal `require()` calls of compiled modules, supported builtins, and JSON documents compile. Reading a top-level `const` or `let` require binding before its `require` runs throws the same `ReferenceError` as Node.js. A `var` binding that code above its `require` can reach is unsupported, and so is a read that compiles to no checkable expression, such as exporting the binding before the `require`. JSON documents are parsed at build time, and every `require()` of a document returns the same object. Destructuring the exports of a module supports plain and renamed properties, plus nested patterns for builtin sub-modules such as `util.types`. Any pattern works on a JSON document. Defaults, rest elements, and nested patterns over other modules are unsupported. A require cycle compiles when each of its modules assigns all of its exports before its first `require` and uses the other modules of the cycle only inside functions that its top-level code can't call. Other require cycles are rejected with `SC1016`. diff --git a/docs/content/limitations/language-features/error-causes.mdx b/docs/content/limitations/language-features/error-causes.mdx index 2238a4e5bf..c4691a3d22 100644 --- a/docs/content/limitations/language-features/error-causes.mdx +++ b/docs/content/limitations/language-features/error-causes.mdx @@ -1 +1 @@ -- **Error causes:** Accessor constructor options and explicit subclass redeclarations of `cause` are unsupported. `Error`, `TypeError`, `RangeError`, and `SyntaxError` otherwise accept `{ cause }`, including through inherited constructors. Reads, writes, deletion, and `"cause" in error` distinguish an absent cause from an explicitly undefined cause. Constructor causes are non-enumerable; assignment to an absent cause creates an enumerable property. Property descriptors and checked-value aliases share the cause, including getters and setters installed after construction. +- **Error causes:** Accessor constructor options are unsupported. Subclasses may redeclare `cause` as `unknown` or `object` (for example `cause?: unknown`), which defines the own property after `super()` returns as Node does; redeclarations with other types, parameter properties, and accessors named `cause` are unsupported. `Error`, `TypeError`, `RangeError`, and `SyntaxError` otherwise accept `{ cause }`, including through inherited constructors. Reads, writes, deletion, and `"cause" in error` distinguish an absent cause from an explicitly undefined cause. Constructor causes are non-enumerable; assignment to an absent cause creates an enumerable property. Property descriptors and checked-value aliases share the cause, including getters and setters installed after construction. diff --git a/docs/content/limitations/runtime-type-checks.mdx b/docs/content/limitations/runtime-type-checks.mdx index 5514fb41ee..ee7b953ced 100644 --- a/docs/content/limitations/runtime-type-checks.mdx +++ b/docs/content/limitations/runtime-type-checks.mdx @@ -3,3 +3,5 @@ Casts from values whose type is only known at runtime validate the value. For ex Casts between program classes are checked too: casting an instance to a class it does not extend throws a `TypeError`, where Node.js reads the members structurally. `SC6004` reports calls that pass such an instance to a function that casts it. A Promise converted from `unknown` to a typed result validates its fulfilled value. A mismatch rejects with a `TypeError`. Rejection reasons and reference equality are preserved across conversions. + +A non-null assertion (`value!`) checks the value when it extracts an object, array or other non-nullable member from a union. If the value holds `null` or `undefined`, the assertion throws a `TypeError`, even when the result is never used; Node.js continues until a property of the value is read. diff --git a/docs/src/generated/node-v24-compatibility-meta.json b/docs/src/generated/node-v24-compatibility-meta.json index 500876593d..a641699336 100644 --- a/docs/src/generated/node-v24-compatibility-meta.json +++ b/docs/src/generated/node-v24-compatibility-meta.json @@ -2,6 +2,6 @@ "schemaVersion": 3, "nodeVersion": "24.22.0", "nodeCommit": "f14cd3aa7bf9ca8df2cc5b8bb14beb0f335b1d93", - "artifactVersion": "56f660f5e2eb7391a048", + "artifactVersion": "a610bdd1ae56e1c4cd38", "rowCount": 3880 } diff --git a/docs/src/generated/node-v24-compatibility.json b/docs/src/generated/node-v24-compatibility.json index 568cdb4c9e..d4b6839e70 100644 --- a/docs/src/generated/node-v24-compatibility.json +++ b/docs/src/generated/node-v24-compatibility.json @@ -164,9 +164,9 @@ "apiEntries": 39, "static": { "supported": 0, - "partial": 17, + "partial": 18, "refused": 0, - "not-implemented": 22, + "not-implemented": 21, "by-design": 0, "unreviewed": 0, "not-applicable": 0 @@ -569,9 +569,9 @@ "apiEntries": 120, "static": { "supported": 0, - "partial": 50, + "partial": 51, "refused": 0, - "not-implemented": 70, + "not-implemented": 69, "by-design": 0, "unreviewed": 0, "not-applicable": 0 @@ -689,9 +689,9 @@ "apiEntries": 117, "static": { "supported": 0, - "partial": 53, + "partial": 64, "refused": 0, - "not-implemented": 64, + "not-implemented": 53, "by-design": 0, "unreviewed": 0, "not-applicable": 0 @@ -9312,9 +9312,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -57593,9 +57593,9 @@ "anchor": "", "nodeStability": null, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the call shapes accepted by the compiler lowering.", + "verification": "test-backed" } }, { @@ -66447,9 +66447,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -66516,9 +66516,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -66562,9 +66562,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -66585,9 +66585,9 @@ "inherited": false }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -66608,9 +66608,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -66677,9 +66677,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -66723,9 +66723,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -66746,9 +66746,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -66769,9 +66769,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -66815,9 +66815,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { @@ -66838,9 +66838,9 @@ "inherited": true }, "static": { - "status": "not-implemented", - "detail": "No static compiler lowering is registered for this API yet.", - "verification": "registry-gap" + "status": "partial", + "detail": "Implemented for the documented scriptc module-loader subset.", + "verification": "test-backed" } }, { diff --git a/docs/test/fixtures/migration-baseline.json b/docs/test/fixtures/migration-baseline.json index 6aaab5f70c..dd4320e03a 100644 --- a/docs/test/fixtures/migration-baseline.json +++ b/docs/test/fixtures/migration-baseline.json @@ -273,6 +273,10 @@ "text": "When a region is freed", "id": "when-a-region-is-freed" }, + { + "text": "Async work", + "id": "async-work" + }, { "text": "Threads", "id": "threads" diff --git a/internal/compatibility/generated/node-v24-backlog.json b/internal/compatibility/generated/node-v24-backlog.json index 683d477a5f..f36c865cf6 100644 --- a/internal/compatibility/generated/node-v24-backlog.json +++ b/internal/compatibility/generated/node-v24-backlog.json @@ -7,8 +7,8 @@ "tierItemCount": 3456, "tiers": { "static": { - "audit-partial": 1142, - "verify-gap": 2187, + "audit-partial": 1155, + "verify-gap": 2174, "replace-refusal": 40, "classify": 2, "implement": 85 @@ -3941,12 +3941,16 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:child_process", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:child_process.child_process.exec", + "tests": [ + "tests/corpus/5627-exec-callback.ts", + "tests/fixtures/node-types/child-exec.ts", + "tests/harness/project-config.test.ts" + ] } } }, @@ -25009,7 +25013,9 @@ "confidence": "high", "source": "compiler-dedicated:events.getEventListeners", "tests": [ - "tests/corpus/5885-events-max-listener-helpers.ts" + "tests/corpus/5885-events-max-listener-helpers.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs" ] } } @@ -25036,7 +25042,9 @@ "confidence": "high", "source": "compiler-dedicated:events.getMaxListeners", "tests": [ - "tests/corpus/5885-events-max-listener-helpers.ts" + "tests/corpus/5885-events-max-listener-helpers.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs" ] } } @@ -25091,7 +25099,9 @@ "confidence": "high", "source": "compiler-dedicated:events.listenerCount", "tests": [ - "tests/corpus/5885-events-max-listener-helpers.ts" + "tests/corpus/5885-events-max-listener-helpers.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs" ] } } @@ -25143,7 +25153,9 @@ "confidence": "high", "source": "compiler-dedicated:events.setMaxListeners", "tests": [ - "tests/corpus/5885-events-max-listener-helpers.ts" + "tests/corpus/5885-events-max-listener-helpers.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs" ] } } @@ -25197,7 +25209,9 @@ "confidence": "high", "source": "compiler-dedicated:events.defaultMaxListeners", "tests": [ - "tests/corpus/5885-events-max-listener-helpers.ts" + "tests/corpus/5885-events-max-listener-helpers.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs" ] } } @@ -48430,7 +48444,8 @@ "confidence": "high", "source": "compiler-feature:module.module.isBuiltin", "tests": [ - "tests/corpus/2932-create-require-program/main.ts" + "tests/corpus/2932-create-require-program/main.ts", + "tests/corpus/5846-events-esm-default-max.ts" ] } } @@ -48527,7 +48542,8 @@ "confidence": "high", "source": "compiler-feature:module.module.syncBuiltinESMExports", "tests": [ - "tests/corpus/2932-create-require-program/main.ts" + "tests/corpus/2932-create-require-program/main.ts", + "tests/corpus/5846-events-esm-default-max.ts" ] } } @@ -57507,12 +57523,15 @@ "priority": "normal", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:process", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "surface-manifest:node-builtin.process.execArgv", + "tests": [ + "tests/corpus/990-process-basics.ts", + "tests/corpus/2314-process-introspection.ts" + ] } } }, @@ -67723,12 +67742,15 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.context.filePath", + "tests": [ + "tests/fixtures/node-test/cases/ctx-members.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -67804,12 +67826,15 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.passed", + "tests": [ + "tests/fixtures/node-test/cases/ctx-members.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -67854,12 +67879,15 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.attempt", + "tests": [ + "tests/fixtures/node-test/cases/ctx-members.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -67879,12 +67907,15 @@ "priority": "low", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.tags", + "tests": [ + "tests/fixtures/node-test/cases/ctx-members.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -67904,12 +67935,15 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.workerId", + "tests": [ + "tests/fixtures/node-test/cases/ctx-members.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -67979,12 +68013,15 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.context.diagnostic", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -68029,12 +68066,15 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.context.filePath", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -68054,12 +68094,15 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.context.fullName", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -68079,12 +68122,15 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.context.name", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -68129,12 +68175,15 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.passed", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -68154,12 +68203,15 @@ "priority": "high", "tiers": { "static": { - "status": "not-implemented", - "action": "verify-gap", - "verification": "registry-gap", - "confidence": "low", - "source": "compiler-unmatched:test", - "tests": [] + "status": "partial", + "action": "audit-partial", + "verification": "test-backed", + "confidence": "high", + "source": "compiler-feature:test.attempt", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] } } }, @@ -76074,6 +76126,10 @@ "tests/corpus/5629-promisify-stream.ts", "tests/corpus/5630-promisify-zlib.ts", "tests/fixtures/node-types/promisify-zlib.ts", + "tests/corpus/5631-promisify-crypto.ts", + "tests/fixtures/node-types/promisify-crypto.ts", + "tests/corpus/5632-promisify-dns.ts", + "tests/fixtures/node-types/promisify-dns.ts", "tests/harness/project-config.test.ts" ] } diff --git a/internal/compatibility/generated/node-v24-internal.json b/internal/compatibility/generated/node-v24-internal.json index 393955bc01..514282a0a1 100644 --- a/internal/compatibility/generated/node-v24-internal.json +++ b/internal/compatibility/generated/node-v24-internal.json @@ -131,9 +131,9 @@ "apiEntries": 39, "static": { "supported": 0, - "partial": 17, + "partial": 18, "refused": 0, - "not-implemented": 22, + "not-implemented": 21, "by-design": 0, "unreviewed": 0, "not-applicable": 0 @@ -596,9 +596,9 @@ "apiEntries": 120, "static": { "supported": 0, - "partial": 50, + "partial": 51, "refused": 0, - "not-implemented": 70, + "not-implemented": 69, "by-design": 0, "unreviewed": 0, "not-applicable": 0 @@ -746,9 +746,9 @@ "apiEntries": 117, "static": { "supported": 0, - "partial": 53, + "partial": 64, "refused": 0, - "not-implemented": 64, + "not-implemented": 53, "by-design": 0, "unreviewed": 0, "not-applicable": 0 @@ -11663,7 +11663,10 @@ "tests/corpus/3127-spawn-runtime-options.ts", "tests/corpus/3130-child-generic-streams.ts", "tests/corpus/3131-child-rest-listeners.js", - "tests/corpus/3132-child-generic-stream-completion.ts" + "tests/corpus/3132-child-generic-stream-completion.ts", + "tests/corpus/5627-exec-callback.ts", + "tests/fixtures/node-types/child-exec.ts", + "tests/harness/project-config.test.ts" ] }, "anchorSource": "chapter" @@ -11732,8 +11735,13 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:child_process" + "status": "partial", + "evidence": "compiler-feature:child_process.child_process.exec", + "tests": [ + "tests/corpus/5627-exec-callback.ts", + "tests/fixtures/node-types/child-exec.ts", + "tests/harness/project-config.test.ts" + ] }, "anchorSource": "exact" }, @@ -41819,6 +41827,8 @@ "tests/corpus/5245-event-target-subclass-signals.ts", "tests/corpus/5247-event-target-subclass-lifetimes.ts", "tests/corpus/5248-event-target-subclass-warnings.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs", "tests/corpus/5885-events-max-listener-helpers.ts" ] }, @@ -43739,7 +43749,9 @@ "status": "partial", "evidence": "compiler-dedicated:events.getEventListeners", "tests": [ - "tests/corpus/5885-events-max-listener-helpers.ts" + "tests/corpus/5885-events-max-listener-helpers.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs" ] }, "anchorSource": "exact" @@ -43765,7 +43777,9 @@ "status": "partial", "evidence": "compiler-dedicated:events.getMaxListeners", "tests": [ - "tests/corpus/5885-events-max-listener-helpers.ts" + "tests/corpus/5885-events-max-listener-helpers.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs" ] }, "anchorSource": "exact" @@ -43841,7 +43855,9 @@ "status": "partial", "evidence": "compiler-dedicated:events.listenerCount", "tests": [ - "tests/corpus/5885-events-max-listener-helpers.ts" + "tests/corpus/5885-events-max-listener-helpers.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs" ] }, "anchorSource": "exact" @@ -43890,7 +43906,9 @@ "status": "partial", "evidence": "compiler-dedicated:events.setMaxListeners", "tests": [ - "tests/corpus/5885-events-max-listener-helpers.ts" + "tests/corpus/5885-events-max-listener-helpers.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs" ] }, "anchorSource": "exact" @@ -43942,7 +43960,9 @@ "status": "partial", "evidence": "compiler-dedicated:events.defaultMaxListeners", "tests": [ - "tests/corpus/5885-events-max-listener-helpers.ts" + "tests/corpus/5885-events-max-listener-helpers.ts", + "tests/corpus/5846-events-esm-default-max.ts", + "tests/corpus/5847-events-cjs-default-max.cjs" ] }, "anchorSource": "exact" @@ -69416,6 +69436,7 @@ "tests/corpus/2637-create-require-bare/main.ts", "tests/corpus/2675-create-require-nested-esm.js", "tests/corpus/2932-create-require-program/main.ts", + "tests/corpus/5846-events-esm-default-max.ts", "tests/harness/npm-static.test.ts" ] }, @@ -69503,7 +69524,8 @@ "status": "partial", "evidence": "compiler-feature:module.module.isBuiltin", "tests": [ - "tests/corpus/2932-create-require-program/main.ts" + "tests/corpus/2932-create-require-program/main.ts", + "tests/corpus/5846-events-esm-default-max.ts" ] }, "anchorSource": "exact" @@ -69593,7 +69615,8 @@ "status": "partial", "evidence": "compiler-feature:module.module.syncBuiltinESMExports", "tests": [ - "tests/corpus/2932-create-require-program/main.ts" + "tests/corpus/2932-create-require-program/main.ts", + "tests/corpus/5846-events-esm-default-max.ts" ] }, "anchorSource": "exact" @@ -80949,8 +80972,12 @@ "anchor": "", "nodeStability": null, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:process" + "status": "partial", + "evidence": "surface-manifest:node-builtin.process.execArgv", + "tests": [ + "tests/corpus/990-process-basics.ts", + "tests/corpus/2314-process-introspection.ts" + ] }, "anchorSource": "chapter" }, @@ -90945,6 +90972,7 @@ "tests/corpus/5813-mock-timers-module.ts", "tests/corpus/5814-mock-timers-date.ts", "tests/fixtures/node-test/cases/ctx-hooks.ts", + "tests/fixtures/node-test/cases/ctx-members.ts", "tests/fixtures/node-test/cases/ctx-plan.ts", "tests/fixtures/node-test/cases/mock-fn.ts", "tests/fixtures/node-test/cases/mock-method.ts", @@ -90952,6 +90980,7 @@ "tests/fixtures/node-test/cases/mock-timers.ts", "tests/fixtures/node-test/cases/only-nested.ts", "tests/fixtures/node-test/cases/snapshot.ts", + "tests/fixtures/node-test/cases/suite-context.ts", "tests/fixtures/node-test/cases/suite-variants.ts", "tests/fixtures/node-test/cases/todo-suites.ts", "tests/harness/node-test.test.ts" @@ -93657,8 +93686,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.context.filePath", + "tests": [ + "tests/fixtures/node-test/cases/ctx-members.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "exact" }, @@ -93734,8 +93767,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.passed", + "tests": [ + "tests/fixtures/node-test/cases/ctx-members.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "ancestor" }, @@ -93780,8 +93817,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.attempt", + "tests": [ + "tests/fixtures/node-test/cases/ctx-members.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "ancestor" }, @@ -93803,8 +93844,12 @@ "inherited": false }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.tags", + "tests": [ + "tests/fixtures/node-test/cases/ctx-members.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "ancestor" }, @@ -93826,8 +93871,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.workerId", + "tests": [ + "tests/fixtures/node-test/cases/ctx-members.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "ancestor" }, @@ -93895,8 +93944,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.context.diagnostic", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "exact" }, @@ -93941,8 +93994,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.context.filePath", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "exact" }, @@ -93964,8 +94021,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.context.fullName", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "exact" }, @@ -93987,8 +94048,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.context.name", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "exact" }, @@ -94033,8 +94098,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.passed", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "ancestor" }, @@ -94056,8 +94125,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:test" + "status": "partial", + "evidence": "compiler-feature:test.attempt", + "tests": [ + "tests/fixtures/node-test/cases/suite-context.ts", + "tests/harness/node-test.test.ts" + ] }, "anchorSource": "ancestor" }, @@ -100982,6 +101055,8 @@ "tests/corpus/5628-promisify-timers.ts", "tests/corpus/5629-promisify-stream.ts", "tests/corpus/5630-promisify-zlib.ts", + "tests/corpus/5631-promisify-crypto.ts", + "tests/corpus/5632-promisify-dns.ts", "tests/corpus/5831-util-types-brands.ts", "tests/corpus/5832-util-types-brands-dynamic.js", "tests/corpus/5835-util-format-integer-specifiers.ts", @@ -101012,6 +101087,8 @@ "tests/corpus/text-codec-values/main.ts", "tests/fixtures/node-types/deep-equality.ts", "tests/fixtures/node-types/env-files.ts", + "tests/fixtures/node-types/promisify-crypto.ts", + "tests/fixtures/node-types/promisify-dns.ts", "tests/fixtures/node-types/promisify-fs.ts", "tests/fixtures/node-types/promisify-timers.ts", "tests/fixtures/node-types/promisify-zlib.ts", @@ -102664,6 +102741,10 @@ "tests/corpus/5629-promisify-stream.ts", "tests/corpus/5630-promisify-zlib.ts", "tests/fixtures/node-types/promisify-zlib.ts", + "tests/corpus/5631-promisify-crypto.ts", + "tests/fixtures/node-types/promisify-crypto.ts", + "tests/corpus/5632-promisify-dns.ts", + "tests/fixtures/node-types/promisify-dns.ts", "tests/harness/project-config.test.ts" ] }, diff --git a/internal/compatibility/static-support.json b/internal/compatibility/static-support.json index cdd025b72a..45a9469252 100644 --- a/internal/compatibility/static-support.json +++ b/internal/compatibility/static-support.json @@ -311,7 +311,7 @@ { "chapter": "modules", "symbols": ["cache", "children", "id", "isPreloading", "loaded", "parent", "path", "paths", "Module"], "status": "partial", "evidence": ["tests/corpus/2880-cjs-module-graph/main.cjs"] }, { "chapter": "modules", "symbols": ["extensions"], "status": "not-implemented" }, { "chapter": "module", "symbols": ["module.createRequire"], "status": "partial", "evidence": ["tests/corpus/2631-create-require/main.ts", "tests/corpus/2637-create-require-bare/main.ts", "tests/corpus/2675-create-require-nested-esm.js", "tests/corpus/2932-create-require-program/main.ts", "tests/harness/npm-static.test.ts"] }, - { "chapter": "module", "symbols": ["module.isBuiltin", "module.syncBuiltinESMExports"], "status": "partial", "evidence": ["tests/corpus/2932-create-require-program/main.ts"] }, + { "chapter": "module", "symbols": ["module.isBuiltin", "module.syncBuiltinESMExports"], "status": "partial", "evidence": ["tests/corpus/2932-create-require-program/main.ts", "tests/corpus/5846-events-esm-default-max.ts"] }, { "chapter": "module", "symbols": ["builtinModules"], "status": "partial", "evidence": ["tests/corpus/2631-create-require/main.ts", "tests/corpus/2637-create-require-bare/main.ts", "tests/corpus/2932-create-require-program/main.ts"] }, { "chapter": "modules", "symbols": ["node:", "node_modules"], "status": "partial", "evidence": ["tests/corpus/1598-cjs-builtin-require/main.js", "tests/corpus/1596-cjs-modules/main.js"] }, { "chapter": "esm", "symbols": ["import", "file:", "node:", "await"], "status": "partial", "evidence": ["tests/corpus/950-modules-basic/main.ts", "tests/corpus/2646-top-level-await.ts", "tests/corpus/2933-static-dynamic-import/main.ts", "tests/corpus/2934-static-import-stored.ts", "tests/corpus/2935-module-namespace-values/main.ts"] }, @@ -322,6 +322,7 @@ { "chapter": "packages", "symbols": ["--input-type"], "status": "not-applicable" }, { "chapter": "child_process", "signatures": ["`subprocess[Symbol.dispose]()`"], "status": "partial", "evidence": ["tests/corpus/2844-node-disposable-resources.ts"] }, { "chapter": "child_process", "signatures": ["`child_process.execFile(file[, args][, options][, callback])`"], "status": "partial", "evidence": ["tests/corpus/2911-execfile-callback.ts"] }, + { "chapter": "child_process", "signatures": ["`child_process.exec(command[, options][, callback])`"], "status": "partial", "evidence": ["tests/corpus/5627-exec-callback.ts", "tests/fixtures/node-types/child-exec.ts", "tests/harness/project-config.test.ts"] }, { "chapter": "child_process", "signatures": ["`child_process.fork(modulePath[, args][, options])`", "`subprocess.send(message[, sendHandle[, options]][, callback])`", "`subprocess.disconnect()`", "`connected` Type: {boolean} Set to `false` after `subprocess.disconnect()` is called.", "Event: `'message'`", "Event: `'disconnect'`"], "status": "partial", "evidence": ["tests/corpus/2946-child-fork-ipc/main.ts"] }, { "chapter": "child_process", "signatures": ["Event: `'close'`"], "status": "partial", "evidence": ["tests/corpus/2939-child-shell-text.ts", "tests/corpus/3131-child-rest-listeners.js"] }, { "chapter": "child_process", "signatures": ["`stdout` Type: {stream.Readable|null|undefined}", "`stderr` Type: {stream.Readable|null|undefined}"], "status": "partial", "evidence": ["tests/corpus/1565-spawn-pipe-streams.ts", "tests/corpus/2939-child-shell-text.ts"] }, @@ -355,6 +356,8 @@ { "chapter": "url", "signatures": ["`port` Type: {string}"], "status": "partial", "evidence": ["tests/corpus/2946-url-port-hash.ts"] }, { "chapter": "url", "signatures": ["Legacy `urlObject`", "`urlObject.auth`", "`urlObject.hash`", "`urlObject.host`", "`urlObject.hostname`", "`urlObject.href`", "`urlObject.path`", "`urlObject.pathname`", "`urlObject.port`", "`urlObject.protocol`", "`urlObject.query`", "`urlObject.search`", "`urlObject.slashes`"], "status": "partial", "evidence": ["tests/corpus/5221-url-parse-shapes.ts", "tests/corpus/5222-url-parse-hosts.ts", "tests/corpus/5223-url-parse-query.ts", "tests/corpus/5224-url-format-objects.ts", "tests/fixtures/node-types/url-legacy.ts"] }, { "chapter": "test", "parent": "`context.assert`", "symbols": ["context.assert.snapshot", "context.assert.fileSnapshot"], "status": "partial", "evidence": ["tests/fixtures/node-test/cases/snapshot.ts", "tests/harness/node-test.test.ts"] }, + { "chapter": "test", "parent": "Class: `TestContext`", "symbols": ["context.filePath", "passed", "attempt", "tags", "workerId"], "status": "partial", "evidence": ["tests/fixtures/node-test/cases/ctx-members.ts", "tests/harness/node-test.test.ts"] }, + { "chapter": "test", "parent": "Class: `SuiteContext`", "symbols": ["context.name", "context.fullName", "context.filePath", "context.diagnostic", "passed", "attempt"], "status": "partial", "evidence": ["tests/fixtures/node-test/cases/suite-context.ts", "tests/harness/node-test.test.ts"] }, { "chapter": "test", "parent": "Class: `TestContext`", "symbols": ["context.diagnostic", "context.name"], "status": "partial", "evidence": ["tests/harness/node-test.test.ts"] }, { "chapter": "test", "parent": "Class: `MockTracker`", "symbols": ["mock.fn"], "status": "partial", "evidence": ["tests/fixtures/node-test/cases/mock-fn.ts", "tests/harness/node-test.test.ts"] }, { "chapter": "test", "parent": "Class: `MockTracker`", "symbols": ["mock.method", "mock.reset", "mock.restoreAll"], "status": "partial", "evidence": ["tests/fixtures/node-test/cases/mock-method.ts", "tests/corpus/5810-mock-method-records.ts", "tests/corpus/5811-mock-tracker-restore.ts", "tests/harness/node-test.test.ts"] }, @@ -363,7 +366,7 @@ { "chapter": "test", "parent": "Class: `MockFunctionContext`", "symbols": ["calls", "ctx.callCount", "ctx.mockImplementation", "ctx.mockImplementationOnce", "ctx.resetCalls", "ctx.restore"], "status": "partial", "evidence": ["tests/fixtures/node-test/cases/mock-fn.ts", "tests/fixtures/node-test/cases/mock-method.ts", "tests/harness/node-test.test.ts"] }, { "chapter": "test", "parent": "Class: `TestContext`", "symbols": ["context.before", "context.after", "context.beforeEach", "context.afterEach", "context.plan", "context.fullName"], "status": "partial", "evidence": ["tests/fixtures/node-test/cases/ctx-hooks.ts", "tests/fixtures/node-test/cases/ctx-plan.ts", "tests/harness/node-test.test.ts"] }, { "chapter": "test", "symbols": ["describe.skip", "describe.todo", "describe.only", "suite.skip", "suite.todo", "suite.only", "it.skip", "it.todo", "it.only"], "status": "partial", "evidence": ["tests/fixtures/node-test/cases/suite-variants.ts", "tests/fixtures/node-test/cases/only-nested.ts", "tests/fixtures/node-test/cases/todo-suites.ts", "tests/harness/node-test.test.ts"] }, - { "chapter": "util", "signatures": ["`util.promisify(original)`"], "status": "partial", "evidence": ["tests/corpus/1473-promisify-execfile.ts", "tests/corpus/1573-promisify-execfile-env-spread.ts", "tests/corpus/2912-promisify-fs-readfile.ts", "tests/corpus/5625-promisify-exec.ts", "tests/corpus/5626-promisify-forms.ts", "tests/fixtures/node-types/promisify-fs.ts", "tests/corpus/5628-promisify-timers.ts", "tests/fixtures/node-types/promisify-timers.ts", "tests/corpus/5629-promisify-stream.ts", "tests/corpus/5630-promisify-zlib.ts", "tests/fixtures/node-types/promisify-zlib.ts", "tests/harness/project-config.test.ts"] }, + { "chapter": "util", "signatures": ["`util.promisify(original)`"], "status": "partial", "evidence": ["tests/corpus/1473-promisify-execfile.ts", "tests/corpus/1573-promisify-execfile-env-spread.ts", "tests/corpus/2912-promisify-fs-readfile.ts", "tests/corpus/5625-promisify-exec.ts", "tests/corpus/5626-promisify-forms.ts", "tests/fixtures/node-types/promisify-fs.ts", "tests/corpus/5628-promisify-timers.ts", "tests/fixtures/node-types/promisify-timers.ts", "tests/corpus/5629-promisify-stream.ts", "tests/corpus/5630-promisify-zlib.ts", "tests/fixtures/node-types/promisify-zlib.ts", "tests/corpus/5631-promisify-crypto.ts", "tests/fixtures/node-types/promisify-crypto.ts", "tests/corpus/5632-promisify-dns.ts", "tests/fixtures/node-types/promisify-dns.ts", "tests/harness/project-config.test.ts"] }, { "chapter": "typescript", "symbols": ["type"], "status": "supported", "evidence": ["tests/corpus/2702-type-import-link-static/main.ts", "tests/corpus/2703-type-import-link-static-reexport/main.ts", "tests/corpus/2704-type-import-link-static-success/main.ts", "tests/corpus/2707-type-import-link-static-default-reexport/main.ts", "tests/harness/errors.test.ts"] }, { "chapter": "zlib", "signatures": ["Class: `zlib.Deflate`", "Class: `zlib.Inflate`", "Class: `zlib.Gzip`", "Class: `zlib.Gunzip`", "Class: `zlib.DeflateRaw`", "Class: `zlib.InflateRaw`", "Class: `zlib.Unzip`", "Class: `zlib.ZlibBase`", "`zlib.close([callback])`", "`zlib.flush([kind, ]callback)`", "`zlib.params(level, strategy, callback)`", "`zlib.reset()`", "`bytesWritten` Type: {number}", "Class: `Options`"], "status": "partial", "evidence": ["tests/corpus/5370-zlib-stream-roundtrip.ts", "tests/corpus/5371-zlib-stream-chunking.cjs", "tests/corpus/5372-zlib-stream-errors.cjs", "tests/corpus/5373-zlib-stream-options.cjs", "tests/corpus/5374-zlib-stream-flush-params.ts", "tests/corpus/5375-zlib-stream-promises.ts", "tests/corpus/5377-zlib-stream-backpressure.cjs"] }, { "chapter": "zlib", "signatures": ["`zlib.constants`"], "status": "partial", "evidence": ["tests/corpus/5376-zlib-constants.cjs", "tests/corpus/5374-zlib-stream-flush-params.ts"] } @@ -390,7 +393,7 @@ { "symbols": ["crypto.randomInt"], "status": "partial", "evidence": ["tests/corpus/5442-crypto-random-values.ts", "tests/corpus/2890-crypto-static-utility.ts"] }, { "symbols": ["crypto.hkdf", "crypto.hkdfSync", "crypto.scrypt", "crypto.scryptSync"], "status": "partial", "evidence": ["tests/corpus/node24-crypto-kdf.ts", "tests/corpus/node24-crypto-kdf-errors.ts", "tests/corpus/node24-crypto-kdf-options.ts", "tests/corpus/5438-crypto-kdf-digests.ts"] }, { "symbols": ["EventEmitter", "emitter.on", "emitter.addListener", "emitter.once", "emitter.prependListener", "emitter.prependOnceListener", "emitter.off", "emitter.removeListener", "emitter.removeAllListeners", "emitter.emit", "emitter.listenerCount", "emitter.listeners", "emitter.rawListeners", "emitter.eventNames", "emitter.setMaxListeners", "emitter.getMaxListeners", "events.once"], "status": "partial", "evidence": ["tests/corpus/1677-emitter-listeners.ts", "tests/corpus/2620-ee-override-once-order.ts"] }, - { "symbols": ["events.getEventListeners", "events.getMaxListeners", "events.listenerCount", "events.setMaxListeners", "events.defaultMaxListeners"], "status": "partial", "evidence": ["tests/corpus/5885-events-max-listener-helpers.ts"] }, + { "symbols": ["events.getEventListeners", "events.getMaxListeners", "events.listenerCount", "events.setMaxListeners", "events.defaultMaxListeners"], "status": "partial", "evidence": ["tests/corpus/5885-events-max-listener-helpers.ts", "tests/corpus/5846-events-esm-default-max.ts", "tests/corpus/5847-events-cjs-default-max.cjs"] }, { "symbols": ["AsyncLocalStorage", "asyncLocalStorage.run", "asyncLocalStorage.exit", "asyncLocalStorage.getStore", "asyncLocalStorage.enterWith", "asyncLocalStorage.disable"], "status": "partial", "evidence": ["tests/corpus/2213-async-local-storage.cjs"] }, { "symbols": ["StringDecoder", "stringDecoder.write", "stringDecoder.end"], "status": "partial", "evidence": ["tests/corpus/1662-string-decoder-encodings.ts", "tests/corpus/node24-string-decoder.ts", "tests/corpus/5882-string-decoder-runtime-encoding.ts"] }, { "symbols": ["setImmediate", "setInterval", "setTimeout", "clearImmediate", "clearInterval", "clearTimeout", "timersPromises.setTimeout", "timersPromises.setImmediate", "timersPromises.setInterval", "immediate.hasRef", "immediate.ref", "immediate.unref", "timeout.hasRef", "timeout.ref", "timeout.unref", "timeout.refresh"], "status": "partial", "evidence": ["tests/corpus/1804-timers-module.ts", "tests/corpus/1805-timer-callback-args.ts", "tests/corpus/2093-timers-promises.ts", "tests/corpus/2852-timers-promises-interval.ts", "tests/corpus/5830-timers-promises-signal.ts"] }, diff --git a/packages/compiler/ambient/scriptc-node-fallback.d.ts b/packages/compiler/ambient/scriptc-node-fallback.d.ts index 8cb8c972f0..d72ca2dcdd 100644 --- a/packages/compiler/ambient/scriptc-node-fallback.d.ts +++ b/packages/compiler/ambient/scriptc-node-fallback.d.ts @@ -3033,10 +3033,10 @@ declare module "child_process" { args?: string[] | null, options?: ExecSyncOptions, ): unknown; - /* The async shell form — declared surface without full lowering (the - * type-level tolerance story): harness code on paths tests don't reach - * must typecheck; a reached call fences at its site. util.promisify - * recognizes it statically, as it does execFile. */ + /* The async shell form. The callback forms lower like execFile's, with + * the command line run by /bin/sh -c, and util.promisify recognizes it + * statically, as it does execFile; reached calls without a callback + * fence at their site. */ export function exec( command: string, options?: ExecSyncOptions | ((error: Error | null, stdout: string, stderr: string) => void), @@ -3174,6 +3174,69 @@ declare module "util" { export function promisify( fn: typeof import("node:stream").finished, ): typeof import("node:stream/promises").finished; + /* The crypto callback functions: the promise settles with the callback's + * value. */ + export function promisify( + fn: typeof import("node:crypto").randomBytes, + ): (size: number) => Promise; + export function promisify( + fn: typeof import("node:crypto").pbkdf2, + ): ( + password: string | Uint8Array, + salt: string | Uint8Array, + iterations: number, + keylen: number, + digest: string, + ) => Promise; + export function promisify( + fn: typeof import("node:crypto").scrypt, + ): ( + password: import("node:crypto").BinaryLike, + salt: import("node:crypto").BinaryLike, + keylen: number, + options?: import("node:crypto").ScryptOptions, + ) => Promise; + export function promisify( + fn: typeof import("node:crypto").hkdf, + ): ( + digest: string, + ikm: import("node:crypto").BinaryLike | import("node:crypto").KeyObject, + salt: import("node:crypto").BinaryLike, + info: import("node:crypto").BinaryLike, + keylen: number, + ) => Promise; + export function promisify( + fn: typeof import("node:crypto").randomFill, + ): ( + buffer: T, + offset?: number, + size?: number, + ) => Promise; + export function promisify(fn: typeof import("node:crypto").randomInt): { + (max: number): Promise; + (min: number, max: number): Promise; + }; + export function promisify( + fn: typeof import("node:crypto").generateKey, + ): ( + type: "hmac" | "aes", + options: { length: number }, + ) => Promise; + /* dns.lookup's promise settles with { address, family } (the address + * array with options.all), and lookupService's with { hostname, service }. */ + export function promisify(fn: typeof import("node:dns").lookup): { + ( + hostname: string, + options: import("node:dns").LookupAllOptions, + ): Promise; + ( + hostname: string, + options?: number | import("node:dns").LookupOneOptions, + ): Promise; + }; + export function promisify( + fn: typeof import("node:dns").lookupService, + ): (address: string, port: number) => Promise<{ hostname: string; service: string }>; /* Declared surface without full lowering (the type-level tolerance * story: guarded/diagnostic-path code must TYPECHECK; a reached use * without a lowering fences at its site). inspect's options carry the @@ -3312,8 +3375,18 @@ declare module "node:util/types" { * suites typecheck but fence at their use sites (no lowering). */ declare module "node:test" { type TestFn = (t: TestContext) => void | Promise; - type SuiteFn = () => void; - type HookFn = () => void | Promise; + type SuiteFn = (s: SuiteContext) => void; + type HookFn = (c: TestContext | SuiteContext) => void | Promise; + /* The describe/suite callback's context, and the before/after hook + * context of a suite. */ + interface SuiteContext { + readonly name: string; + readonly fullName: string; + readonly filePath: string | undefined; + readonly attempt: number; + readonly passed: boolean; + diagnostic(message: string): void; + } interface TestOptions { skip?: boolean | string; todo?: boolean | string; @@ -3325,6 +3398,11 @@ declare module "node:test" { interface TestContext { readonly name: string; readonly fullName: string; + readonly filePath: string | undefined; + readonly attempt: number; + readonly passed: boolean; + readonly workerId: number | undefined; + readonly tags: readonly string[]; readonly assert: TestContextAssert; readonly mock: MockTracker; test(name: string, fn?: TestFn): Promise; @@ -3469,6 +3547,7 @@ declare module "node:test" { run, snapshot, TestContext, + SuiteContext, TestOptions, Mock, MockFunctionContext, diff --git a/packages/compiler/src/backend/llvm/blocks.test.ts b/packages/compiler/src/backend/llvm/blocks.test.ts index c2ee90b5f8..b96fc851ee 100644 --- a/packages/compiler/src/backend/llvm/blocks.test.ts +++ b/packages/compiler/src/backend/llvm/blocks.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "vitest"; -import { BlockBuilder } from "./blocks.js"; +import { BlockBuilder, volatileSlotAccess } from "./blocks.js"; import { f64Lit } from "./common.js"; test("entry allocations precede instructions and temporaries stay unique", () => { @@ -106,3 +106,34 @@ test("inclusive loops and nested loops keep labels separate", () => { expect(text).toContain("fcmp ole double"); expect(text.match(/alloca double/g)).toHaveLength(2); }); + +test("volatile slots render their loads and stores volatile and nothing else", () => { + const slots = new Set(["%sc_l_a_0"]); + expect(volatileSlotAccess(" %t1 = load ptr, ptr %sc_l_a_0 ; a", slots)).toBe( + " %t1 = load volatile ptr, ptr %sc_l_a_0 ; a", + ); + expect(volatileSlotAccess(" store ptr %t1, ptr %sc_l_a_0, !dbg !7", slots)).toBe( + " store volatile ptr %t1, ptr %sc_l_a_0, !dbg !7", + ); + expect(volatileSlotAccess(" store ptr null, ptr %sc_l_a_0", slots)).toBe( + " store volatile ptr null, ptr %sc_l_a_0", + ); + // Other addresses, a slot stored as a value, prefixes and other opcodes. + for (const line of [ + " %t2 = load ptr, ptr %sc_l_a_01", + " store ptr %sc_l_a_0, ptr %sc_l_b_0", + " %t3 = getelementptr inbounds ptr, ptr %sc_l_a_0, i64 1", + " call void @scr_box_release(ptr %sc_l_a_0)", + " %t4 = load volatile ptr, ptr %sc_l_a_0", + ]) + expect(volatileSlotAccess(line, slots)).toBe(line); + + const blocks = new BlockBuilder(); + blocks.volatileSlots = slots; + blocks.entryAllocas.push("%sc_l_a_0 = alloca ptr"); + blocks.line("store ptr null, ptr %sc_l_a_0"); + blocks.terminate("ret void"); + expect(blocks.render()).toBe( + "entry:\n %sc_l_a_0 = alloca ptr\n store volatile ptr null, ptr %sc_l_a_0\n ret void", + ); +}); diff --git a/packages/compiler/src/backend/llvm/blocks.ts b/packages/compiler/src/backend/llvm/blocks.ts index 5be4ef8801..6d22bf3bf6 100644 --- a/packages/compiler/src/backend/llvm/blocks.ts +++ b/packages/compiler/src/backend/llvm/blocks.ts @@ -31,6 +31,9 @@ export class BlockBuilder { /** Temps known to hold immortal values (temp names are only unique * within one builder). */ readonly immortalTemps = new Set(); + /** Entry slots whose loads and stores render volatile, so no LLVM pass + * promotes them to SSA values (see volatileSlotAccess). */ + volatileSlots: ReadonlySet | null = null; constructor() { this.cur = { label: "entry", lines: [], term: null }; @@ -130,9 +133,14 @@ export class BlockBuilder { } render(): string { + const slots = this.volatileSlots; return this.blocks .map((b, i) => { - const lines = i === 0 ? [...this.entryAllocas.map((l) => ` ${l}`), ...b.lines] : b.lines; + const body = + slots === null || slots.size === 0 + ? b.lines + : b.lines.map((line) => volatileSlotAccess(line, slots)); + const lines = i === 0 ? [...this.entryAllocas.map((l) => ` ${l}`), ...body] : body; // A block left unterminated is a structurally unreachable join // (every predecessor jumped elsewhere); the verifier still wants a // terminator. @@ -142,3 +150,36 @@ export class BlockBuilder { .join("\n"); } } + +/** `line` with its load or store made volatile when the address operand is + * one of `slots`. Lines are this builder's own instruction text, where the + * address is the first `ptr %name` operand after the opcode (a stored value + * precedes it without a comma). */ +export function volatileSlotAccess(line: string, slots: ReadonlySet): string { + let op = 0; + while (line.charCodeAt(op) === 32) op++; + if (line.charCodeAt(op) === 37) { + const assign = line.indexOf(" = ", op); + if (assign < 0) return line; + op = assign + 3; + } + const kind = line.startsWith("load ", op) ? 5 : line.startsWith("store ", op) ? 6 : 0; + if (kind === 0 || line.startsWith("volatile ", op + kind)) return line; + const address = line.indexOf(", ptr %", op + kind); + if (address < 0) return line; + const start = address + 6; + let end = start + 1; + while (end < line.length && isNameCode(line.charCodeAt(end))) end++; + if (!slots.has(line.slice(start, end))) return line; + return `${line.slice(0, op + kind)}volatile ${line.slice(op + kind)}`; +} + +function isNameCode(code: number): boolean { + return ( + (code >= 48 && code <= 57) || + (code >= 65 && code <= 90) || + (code >= 97 && code <= 122) || + code === 95 || + code === 46 + ); +} diff --git a/packages/compiler/src/backend/llvm/call-lifetimes.ts b/packages/compiler/src/backend/llvm/call-lifetimes.ts index 65dd76de7e..db5bdb5741 100644 --- a/packages/compiler/src/backend/llvm/call-lifetimes.ts +++ b/packages/compiler/src/backend/llvm/call-lifetimes.ts @@ -178,6 +178,12 @@ function collectUses( ) return true; break; + case "recordClone": + // A same-shape spread copies the source's fields (retaining the + // references) and keeps no reference to the source itself. + if (node.source.kind !== "varRef") break; + for (const field of node.overrides) expr(field.value); + return true; case "recordLit": if (node.type.kind !== "record") break; for (const field of node.fields) diff --git a/packages/compiler/src/backend/llvm/dense-array-access.test.ts b/packages/compiler/src/backend/llvm/dense-array-access.test.ts index dc23ceae21..31680a5183 100644 --- a/packages/compiler/src/backend/llvm/dense-array-access.test.ts +++ b/packages/compiler/src/backend/llvm/dense-array-access.test.ts @@ -3,6 +3,7 @@ import { BOOL, F64, STRING, + UNDEFINED_T, VOID, arrayOf, type IrExpr, @@ -23,11 +24,17 @@ const ref = (localId: string, type: IrExpr["type"]): IrExpr => ({ }); const index = ref("i", F64); -function module(name: string, returnType: IrExpr["type"], body: IrStmt[]): IrModule { +function module( + name: string, + returnType: IrExpr["type"], + body: IrStmt[], + unions: IrModule["unions"] = [], +): IrModule { return { irVersion: 16, sourceFile: loc.file, entry: "__main", + unions, functions: [ { name: "__main", params: [], returnType: VOID, locals: [], body: [], loc }, { @@ -174,6 +181,76 @@ test.each([32, 64] as const)( }, ); +test.each([32, 64] as const)( + "pop removes the last element of a packed array inline and keeps a cold runtime pop (%i-bit)", + (bits) => { + const maybe = { kind: "union", unionId: "maybe" } as const; + const pop: IrExpr = { + kind: "arrIntrinsic", + method: "pop", + receiver: ref("a", nums), + args: [], + type: maybe, + loc, + }; + const llvm = emitLlvmModule( + module( + "take", + maybe, + [{ kind: "return", value: pop, loc }], + [{ id: "maybe", arms: [F64, UNDEFINED_T] }], + ), + { pointerBits: bits }, + ); + const fn = body(llvm, "take"); + const size = bits === 32 ? "i32" : "i64"; + // An empty array answers undefined without a call. + expect(fn).toMatch(new RegExp(`icmp eq ${size} %t\\d+, 0\\n br i1 %t\\d+, label %remove\\.a`)); + expect(fn).toMatch(new RegExp(`sub nuw ${size} %t\\d+, 1`)); + expect(fn).toMatch(/store i64 0, ptr /); + expect(fn).toMatch(/store i8 0, ptr /); + expect(fn).toMatch(/phi i64 /); + expect(fn).toMatch(/call zeroext i8 @scr_arr_pop_state\(.*\) cold$/m); + }, +); + +test("a discarded pop releases the removed element inline", () => { + const words = ref("s", arrayOf(STRING)); + const length: IrExpr = { + kind: "arrIntrinsic", + method: "length", + receiver: words, + args: [], + type: F64, + loc, + }; + const llvm = emitLlvmModule( + module("drop", VOID, [ + { + kind: "arraySetLength", + arr: words, + length: { + kind: "bin", + op: "-", + left: length, + right: { kind: "numLit", value: 1, type: F64, loc }, + type: F64, + loc, + }, + loc, + pop: true, + }, + ]), + ); + const fn = body(llvm, "drop"); + expect(fn).toMatch(/fsub double %t\d+, 1\.0/); + expect(fn).toMatch(/load i64, ptr .*\n(?:.*\n)*? store i64 0, ptr /); + expect(fn).toMatch(/inttoptr i64 %t\d+ to ptr/); + expect(fn).toContain("call void @scr_str_release("); + // Holes, sparse storage, and every other length keep the runtime call. + expect(fn).toContain("call void @scr_arr_set_len("); +}); + test.each([32, 64] as const)( "indices below the hole-free prefix bound skip the state bytes (%i-bit)", (bits) => { diff --git a/packages/compiler/src/backend/llvm/dense-array-access.ts b/packages/compiler/src/backend/llvm/dense-array-access.ts index aeaf3e70f8..f19073b75f 100644 --- a/packages/compiler/src/backend/llvm/dense-array-access.ts +++ b/packages/compiler/src/backend/llvm/dense-array-access.ts @@ -47,7 +47,8 @@ interface DenseGuard { * then extends the length over hole states exactly the same way). An index * below the hole-free prefix bound branches to `filledLabel` first; both * that block and the returned (state-checked) dense block may use the - * guard's values. */ + * guard's values. A `proven` index is known to lie below the bound (a dense + * array loop's fast copy, dense-array-loops.ts) and always takes it. */ function emitDenseGuard( host: LlvmEmitterContext, array: string, @@ -56,6 +57,7 @@ function emitDenseGuard( slow: string, requireBelowLen: boolean, filledLabel: string, + proven = false, ): DenseGuard { const B = host.B; const size = host.sizeType; @@ -137,8 +139,10 @@ function emitDenseGuard( B.line(`${offset} = trunc i64 ${wide} to i32`); } // The bound never exceeds the length or the dense capacity. - let inFilled = B.tmp(); - B.line(`${inFilled} = icmp ult i64 ${wide}, ${filledWide}`); + // The proof covers the counter's integer value only. + const below = proven && integral === null; + let inFilled = below ? "true" : B.tmp(); + if (!below) B.line(`${inFilled} = icmp ult i64 ${wide}, ${filledWide}`); if (integral) { const t = B.tmp(); B.line(`${t} = and i1 ${integral}, ${inFilled}`); @@ -272,6 +276,7 @@ export function emitDenseArrayGet( element: IrType, getter: string, retain: boolean, + proven = false, ): string { const B = host.B; const ty = acc === "f64" ? "double" : acc === "bool" ? "i1" : "ptr"; @@ -285,7 +290,7 @@ export function emitDenseArrayGet( const slow = B.newLabel("arr.get.slow"), join = B.newLabel("arr.get.join"), valueLabel = B.newLabel("arr.get.value"); - const guard = emitDenseGuard(host, array, index, indexExpr, slow, true, valueLabel); + const guard = emitDenseGuard(host, array, index, indexExpr, slow, true, valueLabel, proven); const state = loadState(host, guard); const present = B.tmp(); B.line(`${present} = icmp eq i8 ${state}, ${SCR_ARR_VALUE}`); @@ -331,6 +336,7 @@ export function emitDenseArrayNumber( array: string, index: LlValue, indexExpr: IrExpr | undefined, + proven = false, ): string { return emitDenseArrayGet( host, @@ -341,6 +347,7 @@ export function emitDenseArrayNumber( { kind: "f64" }, "scr_arr_get_number", false, + proven, ); } @@ -399,6 +406,7 @@ export function emitDenseArraySet( acc: ArrayAccess, element: IrType, value: string, + proven = false, ): void { const B = host.B; const ty = acc === "f64" ? "double" : acc === "bool" ? "i1" : "ptr"; @@ -413,7 +421,7 @@ export function emitDenseArraySet( const slow = B.newLabel("arr.set.slow"), join = B.newLabel("arr.set.join"), filledLabel = B.newLabel("arr.set.filled"); - const guard = emitDenseGuard(host, array, index, indexExpr, slow, false, filledLabel); + const guard = emitDenseGuard(host, array, index, indexExpr, slow, false, filledLabel, proven); const { offset, len } = guard; const storeValue = (valuePtr: string): void => { if (acc === "f64") { @@ -508,6 +516,75 @@ export function emitDenseArraySet( B.startBlock(join); } +/** The dense half of `pop`. An empty array branches to `empty` (the + * runtime's pop returns a hole state there and changes nothing). When the + * hole-free prefix bound equals the length, the last element is a present + * value in dense storage, and the runtime's pop is exactly: clear its slot + * and state, lower the bound and the length to its index, and move the + * slot out. That block is left open and its slot bits are returned as an + * i64 (a boolean's 0 or 1, zero-extended); every other array branches to + * `slow`, which keeps the runtime call. */ +export function emitDenseArrayPop( + host: LlvmEmitterContext, + array: string, + acc: ArrayAccess, + empty: string, + slow: string, +): string { + const B = host.B; + const size = host.sizeType; + const header = (field: number): string => { + const ptr = B.tmp(); + B.line(`${ptr} = getelementptr inbounds %ScrArr, ptr ${array}, i32 0, i32 ${field}`); + host.markMemoryPointer(ptr, "array:header"); + return ptr; + }; + const lenPtr = header(SCR_ARR_FIELD.len), + len = B.tmp(), + none = B.tmp(); + B.line(`${len} = load ${size}, ptr ${lenPtr}${host.fieldAliasAttachment(lenPtr)}`); + B.line(`${none} = icmp eq ${size} ${len}, 0`); + const check = B.newLabel("arr.pop.check"), + dense = B.newLabel("arr.pop.dense"); + B.condBr(none, empty, check); + B.startBlock(check); + const filledPtr = header(SCR_ARR_FIELD.filled), + filled = B.tmp(), + packed = B.tmp(); + B.line(`${filled} = load i32, ptr ${filledPtr}${host.fieldAliasAttachment(filledPtr)}`); + B.line(`${packed} = icmp eq ${size} ${sizedFilled(host, filled)}, ${len}`); + B.condBr(packed, dense, slow); + B.startBlock(dense); + const last = B.tmp(); + B.line(`${last} = sub nuw ${size} ${len}, 1`); + const dataPtr = header(SCR_ARR_FIELD.data), + data = B.tmp(), + statesPtr = header(SCR_ARR_FIELD.present), + states = B.tmp(); + B.line(`${data} = load ptr, ptr ${dataPtr}${host.fieldAliasAttachment(dataPtr)}`); + B.line(`${states} = load ptr, ptr ${statesPtr}${host.fieldAliasAttachment(statesPtr)}`); + const valuePtr = valuePointer(host, { offset: last, data }, acc); + const statePtr = statePointer(host, { offset: last, states }); + const slotTy = slotType(acc); + let raw = B.tmp(); + B.line(`${raw} = load ${slotTy}, ptr ${valuePtr}${host.fieldAliasAttachment(valuePtr)}`); + if (slotTy === "i8") { + const wide = B.tmp(); + B.line(`${wide} = zext i8 ${raw} to i64`); + raw = wide; + } + B.line(`store ${slotTy} 0, ptr ${valuePtr}${host.fieldAliasAttachment(valuePtr)}`); + B.line(`store i8 0, ptr ${statePtr}${host.fieldAliasAttachment(statePtr)}`); + let last32 = last; + if (size === "i64") { + last32 = B.tmp(); + B.line(`${last32} = trunc i64 ${last} to i32`); + } + B.line(`store i32 ${last32}, ptr ${filledPtr}${host.fieldAliasAttachment(filledPtr)}`); + B.line(`store ${size} ${last}, ptr ${lenPtr}${host.fieldAliasAttachment(lenPtr)}`); + return raw; +} + /** `scr_arr_push_*` (the array takes the value's reference) returning the * new length as a double. When `len < cap` the runtime's append is exactly * a dense store at `len` plus `len + 1`: no sparse entry can exist past the diff --git a/packages/compiler/src/backend/llvm/dense-array-loops.test.ts b/packages/compiler/src/backend/llvm/dense-array-loops.test.ts new file mode 100644 index 0000000000..405ad67e7e --- /dev/null +++ b/packages/compiler/src/backend/llvm/dense-array-loops.test.ts @@ -0,0 +1,147 @@ +import { expect, test } from "vitest"; +import { BOOL, F64, arrayOf, type IrExpr, type IrLocal, type IrStmt } from "../../ir/ir.js"; +import { + countedDenseShape, + matchDenseArrayLoop, + type DenseLoopShape, +} from "./dense-array-loops.js"; + +const loc = { file: "dense-array-loops.ts", start: 0, end: 0 }; +const ARR = arrayOf(F64); +const num = (value: number): IrExpr => ({ kind: "numLit", value, type: F64, loc }); +const ref = (localId: string): IrExpr => ({ kind: "varRef", localId, type: F64, loc }); +const arr = (localId: string): IrExpr & { kind: "varRef" } => ({ + kind: "varRef", + localId, + type: ARR, + loc, +}); +const method = ( + receiver: string, + name: Extract["method"], + args: IrExpr[] = [], +): IrExpr => ({ + kind: "arrIntrinsic", + method: name, + receiver: arr(receiver), + args, + type: F64, + loc, +}); +const read = (receiver: string, index = ref("i")): IrExpr => method(receiver, "getNumber", [index]); +const write = (receiver: string, value: IrExpr, index = ref("i")): IrStmt => ({ + kind: "arraySet", + arr: arr(receiver), + index, + value, + loc, +}); +const locals = new Map([ + ["i", { id: "i", name: "i", type: F64, mutable: true }], + ["a", { id: "a", name: "a", type: ARR, mutable: false }], + ["b", { id: "b", name: "b", type: ARR, mutable: true }], + ["c", { id: "c", name: "c", type: ARR, mutable: false }], +]); + +/** for (let i = 0; i < a.length; i++) body */ +function loop(body: IrStmt[]): IrStmt & { kind: "for" } { + return { + kind: "for", + loc, + init: { kind: "varDecl", localId: "i", init: num(0), loc }, + cond: { kind: "bin", op: "<", left: ref("i"), right: method("a", "length"), type: BOOL, loc }, + update: { + kind: "assign", + localId: "i", + value: { kind: "bin", op: "+", left: ref("i"), right: num(1), type: F64, loc }, + loc, + }, + body, + }; +} +const shape: DenseLoopShape = { + counter: "i", + length: arr("a"), + range: { min: 0, max: 4294967294 }, +}; +const match = (body: IrStmt[], initialized = new Set(["a", "b"])) => + matchDenseArrayLoop(loop(body), shape, locals, new Set(), initialized); + +test("arrays indexed at the counter of a length-preserving loop get an int32 fast copy", () => { + const scale = write("b", { + kind: "bin", + op: "*", + left: read("a"), + right: num(2), + type: F64, + loc, + }); + const dense = match([scale]); + expect([...dense!.arrays.keys()].sort()).toEqual(["a", "b"]); + expect(dense!.range).toEqual({ min: 0, max: 2147483646 }); + // A binding not definitely assigned before the loop is not read by the guard. + expect([...match([scale], new Set())!.arrays.keys()]).toEqual(["a"]); +}); + +test("loops that may resize, rebind or move storage keep the ordinary copy only", () => { + expect(match([{ kind: "exprStmt", expr: method("a", "push", [num(1)]), loc }])).toBeNull(); + expect(match([write("a", num(0), ref("unknown"))])).toBeNull(); + expect( + match([ + { kind: "exprStmt", expr: { kind: "call", callee: "f", args: [], type: F64, loc }, loc }, + ]), + ).toBeNull(); + // A nested loop would be duplicated too. + expect(match([{ kind: "exprStmt", expr: read("a"), loc }, loop([])])).toBeNull(); + // No access at the counter: nothing to prove. + expect(match([{ kind: "exprStmt", expr: read("a", num(0)), loc }])).toBeNull(); + // Copying the bounding array into another binding lets it escape. + expect( + match([ + { kind: "exprStmt", expr: read("a"), loc }, + { kind: "assign", localId: "b", value: arr("a"), loc }, + ]), + ).toBeNull(); + // A rebound array is not covered, but the bounding array still is. + const rebound = match([ + { kind: "exprStmt", expr: read("b"), loc }, + { kind: "exprStmt", expr: read("a"), loc }, + { kind: "assign", localId: "b", value: arr("c"), loc }, + ]); + expect([...rebound!.arrays.keys()]).toEqual(["a"]); +}); + +test("counted shapes need a non-negative counter strictly below the length", () => { + const ascending = (limit: IrExpr, inclusive: boolean, min = 0) => + countedDenseShape(loop([]), { + localId: "i", + start: num(min), + limit, + step: 1, + inclusive, + range: { min, max: 100 }, + }); + const minus = (offset: number): IrExpr => ({ + kind: "bin", + op: "-", + left: method("a", "length"), + right: num(offset), + type: F64, + loc, + }); + expect(ascending(method("a", "length"), false)?.length.localId).toBe("a"); + expect(ascending(minus(1), true)?.length.localId).toBe("a"); + expect(ascending(method("a", "length"), true)).toBeNull(); + expect(ascending(minus(1), false, -1)).toBeNull(); + const descending = (offset: number) => + countedDenseShape(loop([]), { + localId: "i", + start: minus(offset), + limit: num(0), + step: -1, + inclusive: true, + range: { min: 0, max: 100 }, + }); + expect(descending(1)?.length.localId).toBe("a"); + expect(descending(0)).toBeNull(); +}); diff --git a/packages/compiler/src/backend/llvm/dense-array-loops.ts b/packages/compiler/src/backend/llvm/dense-array-loops.ts new file mode 100644 index 0000000000..271a5ddf83 --- /dev/null +++ b/packages/compiler/src/backend/llvm/dense-array-loops.ts @@ -0,0 +1,215 @@ +/* Dense array loops: a versioned copy of an index loop over an array whose + * length the loop never changes. + * + * `for (let i = 0; i < a.length; i++) b[i] = a[i] * 2` reads its bound from + * `a`, and every element access at the counter normally tests the index + * against the array's hole-free prefix bound (`filled`, see + * dense-array-access.ts) before it touches the slot. When the loop provably + * never changes `a.length` (ir/array-length-stability.ts) and never moves or + * cuts the storage of the arrays it indexes, one test before the loop covers + * every pass: if `a.length` is at most each indexed array's `filled` bound, + * every counter value the loop can reach (at least 0, below `a.length`) + * names a present value in dense storage. The fast copy then reads and + * replaces those slots directly, which leaves LLVM a loop without + * per-element branches (vectorizable where the body allows it). The same + * test bounds `a.length` by 2^31 - 1, so the fast copy's counter is an int32. + * Arrays longer than that are sparse in practice (a dense one would need + * 16 GB of slots) and take the ordinary copy, as does every array with a + * hole, present undefined or sparse storage below `a.length`. */ + +import type { IrExpr, IrLocal, IrStmt } from "../../ir/ir.js"; +import type { IntegerRange } from "../../ir/integer-ranges.js"; +import { preservesArrayLength } from "../../ir/array-length-stability.js"; +import { everyExprChild, everyStmtChild } from "../../ir/traverse.js"; +import type { LlvmEmitterContext } from "./expr-context.js"; +import { SCR_ARR_FIELD } from "./common.js"; + +export const INT32_MAX = 2147483647; + +export interface DenseArrayLoop { + /** The loop's counter binding. */ + counter: string; + /** The array whose length bounds the counter. */ + length: IrExpr & { kind: "varRef" }; + /** Arrays whose accesses at the counter are proven to be inside the + * hole-free prefix in the fast copy, with a read of each binding. */ + arrays: ReadonlyMap; + /** The counter's range on every pass of the fast copy. */ + range: IntegerRange; +} + +/** The fast copy duplicates the loop; keep the duplicated code small. */ +const MAX_LOOP_NODES = 120; + +/** How a matched integer loop bounds its counter below `length.length`. */ +export interface DenseLoopShape { + counter: string; + length: IrExpr & { kind: "varRef" }; + /** The counter's proven range on every pass of the ordinary loop; the + * fast copy keeps its minimum, which must be at least 0. */ + range: IntegerRange; +} + +/** `length.length - c` (c an integer literal, 0 when absent). */ +function lengthOffset(e: IrExpr): { receiver: IrExpr & { kind: "varRef" }; offset: number } | null { + let offset = 0; + if ( + e.kind === "bin" && + e.op === "-" && + e.right.kind === "numLit" && + Number.isSafeInteger(e.right.value) + ) { + offset = e.right.value; + e = e.left; + } + return e.kind === "arrIntrinsic" && + e.method === "length" && + e.args.length === 0 && + e.receiver.kind === "varRef" && + e.receiver.type.kind === "array" + ? { receiver: e.receiver, offset } + : null; +} + +/** The array bounding a counted loop's counter below its length: an + * ascending loop compared below `a.length - c`, or a descending one starting + * at `a.length - c` (c >= 1). */ +export function countedDenseShape( + loop: IrStmt & { kind: "for" }, + counted: { + localId: string; + start: IrExpr; + limit: IrExpr; + step: number; + inclusive: boolean; + range: IntegerRange; + }, +): DenseLoopShape | null { + if (counted.range.min < 0) return null; + if (counted.step > 0) { + const bound = lengthOffset(counted.limit); + if (!bound || bound.offset < (counted.inclusive ? 1 : 0)) return null; + return { counter: counted.localId, length: bound.receiver, range: counted.range }; + } + // The start is evaluated once, before the loop; the counter only falls. + const start = lengthOffset(counted.start); + if (!start || start.offset < 1 || loop.init?.kind !== "varDecl") return null; + return { counter: counted.localId, length: start.receiver, range: counted.range }; +} + +/** Match the fast copy of a loop of the given shape. `initialized` holds the + * bindings definitely assigned when the loop starts; `unaliased` the local + * arrays no other binding holds. */ +export function matchDenseArrayLoop( + loop: IrStmt & { kind: "for" }, + shape: DenseLoopShape, + locals: ReadonlyMap, + unaliased: ReadonlySet, + initialized: ReadonlySet, +): DenseArrayLoop | null { + const { counter, length } = shape; + if (shape.range.min < 0 || !loop.cond) return null; + let nodes = 0, + nested = false; + const candidates = new Map(); + // Arrays written, resized, or indexed other than at the counter by an + // element write. + const disturbed = new Set(); + const access = (arr: IrExpr, index: IrExpr): void => { + if (arr.kind === "varRef" && index.kind === "varRef" && index.localId === counter) + candidates.set(arr.localId, arr); + }; + const expr = (e: IrExpr): boolean => { + if (++nodes > MAX_LOOP_NODES) return false; + if (e.kind === "assignExpr" || e.kind === "incDec") disturbed.add(e.localId); + if (e.kind === "arrayGet") access(e.arr, e.index); + if (e.kind === "arrIntrinsic") { + if (e.method === "getNumber" && e.args[0]) access(e.receiver, e.args[0]); + else if (e.method !== "length" && e.receiver.kind === "varRef") + disturbed.add(e.receiver.localId); + } + return everyExprChild(e, expr, stmt); + }; + const stmt = (s: IrStmt): boolean => { + if (++nodes > MAX_LOOP_NODES) return false; + if (s.kind === "for" || s.kind === "while" || s.kind === "doWhile" || s.kind === "forOf") + nested = true; + if (s.kind === "assign" || s.kind === "varDecl") disturbed.add(s.localId); + if (s.kind === "arraySet") { + access(s.arr, s.index); + if (s.arr.kind === "varRef" && (s.index.kind !== "varRef" || s.index.localId !== counter)) + disturbed.add(s.arr.localId); + } + return everyStmtChild(s, expr, stmt); + }; + if (!loop.body.every(stmt) || !expr(loop.cond) || nested) return null; + if ( + !preservesArrayLength( + [loop.cond, ...loop.body], + { receiver: length.localId, inRange: counter }, + locals, + unaliased, + ) + ) + return null; + const arrays = new Map(); + for (const [id, ref] of candidates) { + const local = locals.get(id); + if ( + !local || + local.type.kind !== "array" || + local.boxed || + local.tdz || + disturbed.has(id) || + (id !== length.localId && !initialized.has(id)) + ) + continue; + arrays.set(id, ref); + } + if (arrays.size === 0) return null; + return { + counter, + length, + arrays, + range: { min: shape.range.min, max: Math.min(shape.range.max, INT32_MAX - 1) }, + }; +} + +/** The fast copy's entry test: the bounding length is at most 2^31 - 1 and + * at most every indexed array's hole-free prefix bound. */ +export function emitDenseLoopGuard(host: LlvmEmitterContext, loop: DenseArrayLoop): string { + const B = host.B; + const size = host.sizeType; + const header = (array: string, field: number): string => { + const ptr = B.tmp(); + B.line(`${ptr} = getelementptr inbounds %ScrArr, ptr ${array}, i32 0, i32 ${field}`); + host.markMemoryPointer(ptr, "array:header"); + return ptr; + }; + const receiver = host.emitStableReceiver(loop.length, []); + const lenPtr = header(receiver.name, 1), + len = B.tmp(), + small = B.tmp(); + B.line( + `${len} = load ${size}, ptr ${lenPtr}${host.fieldAliasAttachment(lenPtr)}${host.arrayLengthRange}`, + ); + B.line(`${small} = icmp ule ${size} ${len}, ${INT32_MAX}`); + let valid = small; + for (const [id, ref] of loop.arrays) { + const array = id === loop.length.localId ? receiver : host.emitStableReceiver(ref, []); + const filledPtr = header(array.name, SCR_ARR_FIELD.filled), + filled = B.tmp(), + covered = B.tmp(), + both = B.tmp(); + B.line(`${filled} = load i32, ptr ${filledPtr}${host.fieldAliasAttachment(filledPtr)}`); + let bound = filled; + if (size === "i64") { + bound = B.tmp(); + B.line(`${bound} = zext i32 ${filled} to i64`); + } + B.line(`${covered} = icmp ule ${size} ${len}, ${bound}`); + B.line(`${both} = and i1 ${valid}, ${covered}`); + valid = both; + } + return valid; +} diff --git a/packages/compiler/src/backend/llvm/emitter.test.ts b/packages/compiler/src/backend/llvm/emitter.test.ts index 4fda0f7e0c..74dc8f8598 100644 --- a/packages/compiler/src/backend/llvm/emitter.test.ts +++ b/packages/compiler/src/backend/llvm/emitter.test.ts @@ -126,6 +126,61 @@ test("the shared indexed-read helpers carry an inline hint", () => { expect(define("plain")).not.toContain("inlinehint"); }); +test("bodies holding thousands of capture boxes compile unoptimized with boxes in memory", () => { + const number = (value: number): IrExpr => ({ kind: "numLit", value, type: F64, loc }); + const read = (localId: string): IrExpr => ({ kind: "varRef", localId, type: F64, loc }); + // A declaring body with `count` boxed bindings, and a closure body that + // receives `count` of them through its environment. + const declaring = (name: string, count: number): IrFunction => { + const ids = Array.from({ length: count }, (_, i) => `${name}${i}`); + return { + name, + params: [], + returnType: F64, + locals: ids.map((id) => ({ id, name: id, type: F64, mutable: true, boxed: true as const })), + body: [ + ...ids.map((id, i): IrStmt => ({ kind: "varDecl", localId: id, init: number(i), loc })), + { kind: "return", value: read(ids[count - 1]!), loc }, + ], + loc, + }; + }; + const receiving = (name: string, count: number): IrFunction => { + const ids = Array.from({ length: count }, (_, i) => `${name}${i}`); + return { + name, + params: [], + returnType: F64, + captures: ids.map((id) => ({ localId: id, name: id, type: F64 })), + locals: ids.map((id) => ({ id, name: id, type: F64, mutable: true, boxed: true as const })), + body: [{ kind: "return", value: read(ids[0]!), loc }], + loc, + }; + }; + const module = moduleFor([]); + const llvm = emitLlvmModule({ + ...module, + functions: [ + ...module.functions, + declaring("wide", 512), + declaring("narrow", 511), + receiving("env", 512), + ], + }); + const body = (name: string): string => { + const start = llvm.split("\n").findIndex((line) => line.endsWith(`{ ; ${name}`)); + const lines = llvm.split("\n").slice(start); + return lines.slice(0, lines.indexOf("}")).join("\n"); + }; + expect(body("wide")).toMatch(/\) noinline optnone #0 \{/); + expect(body("wide")).toMatch(/store volatile ptr %[st]\d+, ptr %sc_l_wide0\b/); + expect(body("wide")).toMatch(/= load volatile ptr, ptr %sc_l_wide511\b/); + expect(body("env")).toMatch(/\) noinline optnone #0 \{/); + expect(body("env")).toMatch(/store volatile ptr %t\d+, ptr %sc_l_env0 ; captured env0/); + expect(body("narrow")).not.toContain("optnone"); + expect(body("narrow")).not.toContain("volatile"); +}); + test("throwing-call count does not multiply identical scope cleanup", () => { for (const boxed of [false, true]) { const small = emitLlvmModule(moduleFor(Array.from({ length: 4 }, call), { boxed })); diff --git a/packages/compiler/src/backend/llvm/emitter.ts b/packages/compiler/src/backend/llvm/emitter.ts index 8bae1abaf2..1157cfb30f 100644 --- a/packages/compiler/src/backend/llvm/emitter.ts +++ b/packages/compiler/src/backend/llvm/emitter.ts @@ -55,6 +55,13 @@ import { matchIntegerBytesForLoop, matchIntegerCountedForLoop, } from "../../ir/integer-loops.js"; +import { findUnaliasedLocalArrays } from "../../ir/array-length-stability.js"; +import { + countedDenseShape, + emitDenseLoopGuard, + matchDenseArrayLoop, + type DenseArrayLoop, +} from "./dense-array-loops.js"; import { emitCountedLoopLimit } from "./counted-loops.js"; import { emitSafeIntegerCheck, @@ -88,7 +95,7 @@ import { type StringSplit, } from "./split-loops.js"; import { scalarizeNumericRecords } from "../../ir/scalar-records.js"; -import { RESULT_ENTRY_PREFIX, addRecordResultEntries } from "./record-results.js"; +import { RESULT_DST_ID, RESULT_ENTRY_PREFIX, addRecordResultEntries } from "./record-results.js"; import { specializeCallbackCalls } from "../../ir/callback-specialization.js"; import { specializeNumericCalls } from "../../ir/numeric-call-specialization.js"; import { everyExpr, everyExprChild, everyStmtChild, everyStmtList } from "../../ir/traverse.js"; @@ -166,7 +173,9 @@ import { emitStackObject, findLocalStackObjects, findReassignedStackObjects, + stackObjectFields, stackObjectStruct, + type StackObjectField, type StackObjectSource, } from "./stack-objects.js"; import { @@ -199,7 +208,12 @@ import { type LocalArrayRead, } from "./local-array-reads.js"; import { CheckedNarrows, emitCheckedNarrowReceiver } from "./checked-narrows.js"; -import { emitDenseArrayGet, emitDenseArraySet, emitDenseSlotUnlink } from "./dense-array-access.js"; +import { + emitDenseArrayGet, + emitDenseArrayPop, + emitDenseArraySet, + emitDenseSlotUnlink, +} from "./dense-array-access.js"; import { emitStackMapRead, findMapReadLifetimes, @@ -222,7 +236,10 @@ import { findAppendedArrayLiterals } from "./appended-array-literals.js"; import { AmbientReceiverReaders, findConstantCallbacks } from "./constant-callbacks.js"; import { findScalarStringSlices, + findScratchStringSlices, + findLentStringParameters, emitStringSliceSnapshot, + type LentStringParameters, type StringSliceSnapshot, } from "./string-slices.js"; import { LazyCaptures } from "./lazy-captures.js"; @@ -391,6 +408,7 @@ import { } from "./shapes.js"; import type { ExprOf, LibCallExpr, LlStreamTypedRefAdapter, LlValue } from "./expr-context.js"; import { INLINE_LITERAL_BYTES, literalEqualityDefinition } from "./string-literal-equality.js"; +import { emitDowncastGuard, emitUnionNarrowGuard } from "./narrow-guards.js"; export { LlvmUnsupportedError } from "./unsupported.js"; @@ -548,10 +566,35 @@ const AWAIT_SUSPENSIONS = /@(?:scr_await_(?:void|f64|bool|str|ref|hop)\b|sc_a[wd * own fiber and promise. */ const COROUTINE_SUSPENDS = /@(?:scr_(?:await_|module_await\b|promise_await_settled\b|test_sub\b|gen_yield_|async_gen_|fiber_(?!self\b|promise\b))|sc_a[wd]_)/; +/** Captured bindings (boxes a body declares or receives) from which a + * function compiles without LLVM optimization (see boxSlotCount). */ +const UNOPTIMIZED_BOX_SLOTS = 512; /** ScrExcKind tags of a generator slot holding a number or a boolean. */ const SCR_EXC_F64 = 1; const SCR_EXC_BOOL = 2; +/** The capture boxes `fn` holds: its boxed bindings and the bindings its + * environment brings in. Each box pointer is a value from its binding (or + * function entry) to its last use, and every one of them crosses the calls + * in between. With thousands of them in one body, as in a factory whose + * returned methods share a large environment, LLVM's inliner, instruction + * selection and register allocation grow quadratically and take minutes. + * Past UNOPTIMIZED_BOX_SLOTS, which ordinary functions stay far below, the + * body is compiled `optnone` (nothing inlines into it and code generation + * takes its fast paths) and its box slots are accessed volatile, so no + * pass promotes them to values even where a pipeline runs IR passes over + * optnone bodies, as the native helper's does. Each use reloads its box + * pointer and code generation stays linear. Semantics, boxes and + * environments are unchanged; only that body's machine code is slower. */ +function boxSlotCount(fn: IrFunction): number { + const captured = new Set(); + for (const capture of fn.captures ?? []) captured.add(capture.localId); + for (const capture of fn.classCaptures ?? []) captured.add(capture.localId); + let count = 0; + for (const local of fn.locals) if (local.boxed === true || captured.has(local.id)) count++; + return count; +} + /** Whether any node of `e` names the local (reads, writes, declarations, * captures). */ function mentionsLocal(e: IrExpr, localId: string): boolean { @@ -590,11 +633,18 @@ export class LlEmitter { private loopArrayBorrows: ReadonlySet = new Set(); private scalarStringSlices = new Map(); readonly stringSlices = new Map(); + /** Slices written into stack buffers (findScratchStringSlices). */ + scratchStringSlices: ReadonlySet = new Set(); + /** String parameters a caller may lend scratch slices to. */ + private lentStringParameters: LentStringParameters = new Map(); private localStackUnions = new Map(); private localStackObjects = new Map(); private reassignedStackObjects = new Set(); /** The binding slot of each reassigned stack-object local. */ private stackObjectSlots = new Map(); + /** Entry-block pointers to the reference fields of each frame object + * slot (stack-objects.ts stackObjectFields). */ + stackObjectFieldPtrs = new Map(); private localUnionStorageProofs = new Map(); private localUnionStorage = new Map(); integerArrayBindings = new Set(); @@ -638,6 +688,21 @@ export class LlEmitter { return this.sizeType === "i64" ? ", !range !{i64 0, i64 4294967296}" : ""; } + /** Whether an element access at `index` of `arr` lies in the hole-free + * prefix: the counter of a dense array loop's fast copy indexing one of + * the arrays its entry test covered. */ + provenDenseIndex(arr: IrExpr, index: IrExpr): boolean { + const loop = this.denseLoop; + return ( + loop !== null && + arr.kind === "varRef" && + loop.arrays.has(arr.localId) && + index.kind === "varRef" && + index.localId === loop.counter && + this.integerLoopBindings.has(loop.counter) + ); + } + fieldAliasAttachment(ptr: string): string { const tag = this.fieldPointerTags.get(ptr); return tag === undefined ? "" : `, !tbaa !${tag}`; @@ -994,6 +1059,14 @@ export class LlEmitter { * immortal from construction (classes.ts programLifetimeClass). */ readonly programLifetime: ReadonlySet; private numericLocals = new Map(); + /** Local arrays of the current function that no other binding can hold + * (ir/array-length-stability.ts). */ + private unaliasedArrays: ReadonlySet = new Set(); + /** Dense array loops (dense-array-loops.ts) run in this function. */ + private denseLoopsEnabled = false; + private denseLoopEntries: ReturnType = new Map(); + /** The fast copy of a dense array loop being emitted. */ + private denseLoop: DenseArrayLoop | null = null; private captureIds = new Set(); /** Active induction bindings retain their width independently of size_t. */ integerLoopBindings = new Map< @@ -1289,6 +1362,8 @@ export class LlEmitter { return shape !== undefined && !recordDynKeepsIdentity(shape); }, ); + this.lentStringParameters = + this.debug === null ? findLentStringParameters(this.fnByName, this.callLifetimes) : new Map(); this.constantCallbacks = findConstantCallbacks(mod, this.callLifetimes); // Integer clones signal a stack overflow through their result, which // needs the single-threaded guard; worker programs also poll for @@ -4761,6 +4836,30 @@ export class LlEmitter { if (isRefCounted(type)) this.currentFrame().push({ name: slot, type, slot: true }); } + /** The innermost lexical scope releases a frame object's reference + * fields when it exits, as it releases its bindings. */ + ownScopeFields(fields: readonly StackObjectField[]): void { + const scope = this.scopes[this.scopes.length - 1]!; + for (const field of fields) scope.push({ slot: field.ptr, type: field.type }); + } + + /** Strike frame-owned slots (ownSlot) from their statement frames: their + * contents are moving to another owner. */ + disownSlots(slots: readonly { ptr: string }[]): void { + for (const { ptr } of slots) { + let found = false; + for (let i = this.frames.length - 1; i >= 0 && !found; i--) { + const idx = this.frames[i]!.findIndex((e) => e.slot === true && e.name === ptr); + if (idx >= 0) { + this.frames[i]!.splice(idx, 1); + found = true; + } + } + if (!found) + throw new InternalCompilerError(`llvm emitter bug: disowned slot ${ptr} not in a frame`); + } + } + /** Strike a refcounted temp from its frame: ownership is being moved. */ moveTemp(v: LlValue): void { if (!isRefCounted(v.type) || this.isImmortal(v.name)) return; @@ -6615,6 +6714,7 @@ export class LlEmitter { const initializerBindings = this.initializerBindings.get(fn.name) ?? []; const numericFn = withInitializerBindings(fn, initializerBindings); this.numericLocals = new Map(numericFn.locals.map((l) => [l.id, l])); + this.unaliasedArrays = findUnaliasedLocalArrays(fn); this.borrowedParameters.clear(); this.referenceWrites?.enter(fn); this.reboundOwnerSlots.clear(); @@ -6653,6 +6753,12 @@ export class LlEmitter { this.finalFieldBorrows = !fn.async && !fn.generator && constructorClass(fn.name) === null && this.finalFields.size > 0; this.byteWindowsEnabled = this.countedLoopsEnabled; + this.denseLoopsEnabled = this.countedLoopsEnabled; + this.denseLoop = null; + this.denseLoopEntries = + this.denseLoopsEnabled && numericFn.locals.some((l) => l.type.kind === "array") + ? findInitializedByteLoopBindings(fn) + : new Map(); this.byteWindowEntries = this.byteWindowsEnabled && numericFn.locals.some((l) => l.type.kind === "bytes") ? findInitializedByteLoopBindings(fn) @@ -6690,6 +6796,15 @@ export class LlEmitter { ); this.scalarStringSlices = this.debug === null ? findScalarStringSlices(fn, this.callLifetimes) : new Map(); + this.scratchStringSlices = + this.debug === null + ? findScratchStringSlices( + fn, + this.callLifetimes, + this.scalarStringSlices, + this.lentStringParameters, + ) + : new Set(); this.mapReadLifetimes = findMapReadLifetimes(fn, this.boxedUnionsById, this.callLifetimes); this.readNumberMapWrites = findReadNumberMapWrites(fn); this.mapEntryFusion = this.debug === null ? findMapEntryFusions(fn) : null; @@ -6701,6 +6816,7 @@ export class LlEmitter { ? findReassignedStackObjects(this, fn, this.callLifetimes) : new Set(); this.stackObjectSlots = new Map(); + this.stackObjectFieldPtrs = new Map(); this.localUnionStorageProofs = findLocalUnionStorage(fn, this.callLifetimes, this.unionSources); this.localUnionStorage = new Map(); this.walkBorrows = this.walkBorrowsByFunction.get(fn.name) ?? new Set(); @@ -6879,6 +6995,15 @@ export class LlEmitter { B.line(`store ptr null, ptr %${mangleLocal(local.id)}`); } } + // A body holding thousands of capture boxes compiles unoptimized, its + // box pointers kept in their slots (see boxSlotCount). + const unoptimized = this.currentCoro === null && boxSlotCount(fn) >= UNOPTIMIZED_BOX_SLOTS; + if (unoptimized) { + const slots = new Set(); + for (const local of this.currentLocals.values()) + if (local.boxed || this.captureIds.has(local.id)) slots.add(`%${mangleLocal(local.id)}`); + B.volatileSlots = slots; + } // Captured bindings come in through the environment — borrowed for the // whole call (the closure owns them): bound here, never released here. (fn.captures ?? []).forEach((c, i) => { @@ -7082,6 +7207,7 @@ export class LlEmitter { if (fn.name.startsWith("%dyn.class.protocol.fallback.")) attrs = `noinline ${attrs}`; else if (fn.name.startsWith("%dyn.class.protocol.")) attrs = `inlinehint ${attrs}`; if (fn.name.startsWith(RESULT_ENTRY_PREFIX)) attrs = `inlinehint ${attrs}`; + if (unoptimized && attrs === FN_ATTRS) attrs = `noinline optnone ${attrs}`; const debug = this.debugScope === null ? "" : ` !dbg ${this.debugScope}`; const symbol = borrowed ? mangleBorrowedFunction(fn.name) : mangleFunction(fn.name); this.privateGlobalSlots.clear(); @@ -7826,12 +7952,18 @@ export class LlEmitter { } const stackObject = this.localStackObjects.get(s.localId); if (stackObject) { - B.line(`store ptr ${emitStackObject(this, stackObject).name}, ptr ${b.slot}`); + const value = emitStackObject(this, stackObject, (fields) => this.ownScopeFields(fields)); + B.line(`store ptr ${value.name}, ptr ${b.slot}`); break; } if (s.init && this.reassignedStackObjects.has(s.localId)) { const slot = this.stackObjectSlot(s.localId, s.init.type); - emitStackObject(this, s.init as StackObjectSource, slot); + emitStackObject( + this, + s.init as StackObjectSource, + (fields) => this.ownScopeFields(fields), + slot, + ); B.line(`store ptr ${slot}, ptr ${b.slot}`); break; } @@ -8004,12 +8136,28 @@ export class LlEmitter { break; } if (this.reassignedStackObjects.has(s.localId)) { - // The new value may read the old one: build it apart, then copy. + // The new value may read the old one: build it apart (its + // reference fields owned by this statement until it completes), + // then copy it over the binding's slot. The binding takes over the + // new fields; the old ones are released after the slot no longer + // holds them. const struct = stackObjectStruct(s.value.type); - const value = emitStackObject(this, s.value as StackObjectSource); + let owned: readonly StackObjectField[] = []; + const value = emitStackObject(this, s.value as StackObjectSource, (fields) => { + owned = fields; + for (const field of fields) this.ownSlot(field.ptr, field.type); + }); + const target = this.stackObjectSlot(s.localId, s.value.type); + const old = stackObjectFields(this, s.value.type, target).map((field) => { + const t = B.tmp(); + B.line(`${t} = load ptr, ptr ${field.ptr}`); + return { name: t, type: field.type }; + }); const bits = B.tmp(); B.line(`${bits} = load ${struct}, ptr ${value.name}`); - B.line(`store ${struct} ${bits}, ptr ${this.stackObjectSlot(s.localId, s.value.type)}`); + B.line(`store ${struct} ${bits}, ptr ${target}`); + this.disownSlots(owned); + for (const field of old) this.releaseValue(field.name, field.type); break; } const localStorage = this.localUnionStorage.get(s.localId); @@ -8092,7 +8240,16 @@ export class LlEmitter { const acc = elemAccess(s.arr.type.elem); this.emitPublishedArrayGuard(arr.name, s.arr.type, 0, idx.name); if (acc === "ref") this.moveTemp(v); - emitDenseArraySet(this, arr.name, idx, s.index, acc, s.arr.type.elem, v.name); + emitDenseArraySet( + this, + arr.name, + idx, + s.index, + acc, + s.arr.type.elem, + v.name, + this.provenDenseIndex(s.arr, s.index), + ); break; } case "arraySetLength": { @@ -8130,6 +8287,28 @@ export class LlEmitter { B.line(`${same} = fcmp oeq double ${length.name}, ${lenNumber}`); B.condBr(same, done, resize); B.startBlock(resize); + if (s.pop === true) { + // A discarded pop's `length - 1` on a packed array removes the + // last element inline, then releases it. + const call = B.newLabel("arr.len.call"), + pop = B.newLabel("arr.len.pop"); + const lastNumber = B.tmp(), + dropsLast = B.tmp(); + B.line(`${lastNumber} = fsub double ${lenNumber}, 1.0`); + B.line(`${dropsLast} = fcmp oeq double ${length.name}, ${lastNumber}`); + B.condBr(dropsLast, pop, call); + B.startBlock(pop); + const elem = s.arr.type.elem; + const acc = elemAccess(elem); + const raw = emitDenseArrayPop(this, arr.name, acc, call, call); + if (acc === "ref" && isRefCounted(elem)) { + const old = B.tmp(); + B.line(`${old} = inttoptr i64 ${raw} to ptr`); + this.releaseValue(old, elem); + } + B.br(done); + B.startBlock(call); + } B.line(`call void @scr_arr_set_len(ptr ${arr.name}, double ${length.name})`); this.emitPendingCheck(); B.br(done); @@ -8219,7 +8398,15 @@ export class LlEmitter { s.kind === "fieldSet" ? this.classFieldPtr(obj.name, s.className, s.field) : this.recordFieldPtr(obj.name, s.shapeId, s.field); - this.replaceField(ptr, type, inline, v); + // A result-slot entry fills zeroed caller storage, each field once. + if ( + s.kind === "recordSet" && + s.obj.kind === "varRef" && + s.obj.localId === RESULT_DST_ID && + this.currentFunctionName.startsWith(RESULT_ENTRY_PREFIX) + ) + this.initField(ptr, type, inline, v); + else this.replaceField(ptr, type, inline, v); if ( s.kind === "fieldSet" && s.field === "name" && @@ -8509,7 +8696,12 @@ export class LlEmitter { (this.debug === null ? matchIntegerBytesForLoop(s, this.numericLocals) : null); const countedLoop = !integerLoop && this.countedLoopsEnabled - ? matchIntegerCountedForLoop(s, this.numericLocals, this.integerRanges) + ? matchIntegerCountedForLoop( + s, + this.numericLocals, + this.integerRanges, + this.unaliasedArrays, + ) : null; const window = countedLoop && this.byteWindowsEnabled @@ -8522,6 +8714,49 @@ export class LlEmitter { this.byteWindowEntries.get(s) ?? new Set(), ) : null; + const denseShape = !this.denseLoopsEnabled + ? null + : integerArrayLoop + ? { + counter: integerArrayLoop.localId, + length: integerArrayLoop.limitReceiver as IrExpr & { kind: "varRef" }, + range: { min: 0, max: 4294967294 }, + } + : countedLoop && !countedLoop.guarded + ? countedDenseShape(s, countedLoop) + : null; + const dense = + denseShape && !window + ? matchDenseArrayLoop( + s, + denseShape, + this.numericLocals, + this.unaliasedArrays, + this.denseLoopEntries.get(s) ?? new Set(), + ) + : null; + if (dense) { + const valid = emitDenseLoopGuard(this, dense); + const fast = B.newLabel("dense.loop.fast"), + slow = B.newLabel("dense.loop.slow"), + done = B.newLabel("dense.loop.done"); + B.condBr(valid, fast, slow); + this.denseLoopsEnabled = false; + try { + B.startBlock(slow); + this.emitStmt(s); + B.br(done); + B.startBlock(fast); + this.denseLoop = dense; + this.emitStmt(s); + B.br(done); + } finally { + this.denseLoop = null; + this.denseLoopsEnabled = true; + } + B.startBlock(done); + break; + } if (window) { const valid = emitByteWindowGuard(this, window); const fast = B.newLabel("bytes.window.fast"), @@ -8624,7 +8859,10 @@ export class LlEmitter { this.integerLoopBindings.set(integerLoop.localId, { slot: integerSlot, type: this.sizeType, - range: { min: 0, max: integerArrayLoop ? 4294967294 : Number.MAX_SAFE_INTEGER - 1 }, + range: + this.denseLoop?.counter === integerLoop.localId + ? this.denseLoop.range + : { min: 0, max: integerArrayLoop ? 4294967294 : Number.MAX_SAFE_INTEGER - 1 }, }); } else if (countedLoop) { integerSlot = B.slot(); @@ -8639,7 +8877,10 @@ export class LlEmitter { slot: integerSlot, type: "i64", signed: true, - range: countedLoop.range, + range: + this.denseLoop?.counter === countedLoop.localId + ? this.denseLoop.range + : countedLoop.range, }); countedLimit = emitCountedLoopLimit(this, countedLoop); } else if (s.init) { @@ -10234,11 +10475,16 @@ export class LlEmitter { if (canStackUnion(e, this.boxedUnionsById)) return emitStackUnion(this, e).value; if (e.kind === "unionNarrow") { const union = this.emitUnionProjection(e.value); + emitUnionNarrowGuard(this, union.name, e.unionId, e.tag); return { name: this.unionPeek(union.name, e.unionId), type: e.type }; } if ((e.kind === "downcast" || e.kind === "upcast") && e.value.type.kind === "object") { - // Prefix layouts: a class cast reads the operand's own pointer. - return { name: this.emitReadReceiver(e.value).name, type: e.type }; + // Prefix layouts: a class cast reads the operand's own pointer; a + // downcast verifies its target class (narrow-guards.ts). + const receiver = this.emitReadReceiver(e.value); + if (e.kind === "downcast" && e.type.kind === "object") + emitDowncastGuard(this, receiver.name, e.value.type.className, e.type.className); + return { name: receiver.name, type: e.type }; } // A nullable-pointer field has no box to borrow. Projections use // emitUnionProjection; other borrowers get an owned heap box. @@ -10983,6 +11229,7 @@ export class LlEmitter { resultType: IrType, overflowOnly: boolean, loc?: SrcLoc, + missNaN = false, ): void { return keyedRecordReadInto( this, @@ -10994,6 +11241,7 @@ export class LlEmitter { resultType, overflowOnly, loc, + missNaN, ); } diff --git a/packages/compiler/src/backend/llvm/expr-calls.ts b/packages/compiler/src/backend/llvm/expr-calls.ts index 6e8b8caf4d..b1d810a3f4 100644 --- a/packages/compiler/src/backend/llvm/expr-calls.ts +++ b/packages/compiler/src/backend/llvm/expr-calls.ts @@ -32,7 +32,6 @@ import { mangleVtStruct, } from "../mangle.js"; import { - classMembershipIntervals, classEnvironmentIndex, classStructSym, vtEntriesFor, @@ -52,6 +51,7 @@ import { inputSuffixWrites, } from "./borrowed-inputs.js"; import type { Writes } from "./reference-writes.js"; +import { emitClassMembership, emitDowncastGuard } from "./narrow-guards.js"; export function emitCallExpr( host: LlvmEmitterContext, @@ -147,7 +147,10 @@ export function emitCallExpr( return emitScalarOptional(host, a); if (a.kind === "call" && host.scalarResultCall(a)) return emitCallExpr(host, a, false, true); - if (host.stackObjects && canStackObject(host, a)) return emitStackObject(host, a); + if (host.stackObjects && canStackObject(host, a)) + return emitStackObject(host, a, (fields) => { + for (const field of fields) host.ownSlot(field.ptr, field.type); + }); if (host.nullableFieldGet(a)) return { name: host.emitOwnedNullableStack(a).box, type: a.type }; // An inline union field read copies its tag and bits. @@ -1126,8 +1129,11 @@ export function emitCallExpr( // Prefix layout: both directions are reinterprets of the SAME // pointer — no RC traffic, ownership transfers from the operand // temp to the result temp (struck so the one +1 releases exactly - // once, under the RESULT type's release). + // once, under the RESULT type's release). A downcast verifies the + // class tsc's narrowing proved (narrow-guards.ts). const v = host.emitExpr(e.value); + if (e.kind === "downcast" && e.type.kind === "object" && v.type.kind === "object") + emitDowncastGuard(host, v.name, v.type.className, e.type.className); if (isRefCounted(v.type)) host.moveTemp(v); return host.own({ name: v.name, type: e.type }); } @@ -1139,24 +1145,14 @@ export function emitCallExpr( // The test only reads the vtable word: a borrowable operand needs no // retain/release pair around it. const v = emitBorrowedInputs(host, [e.value])[0]!; - const target = host.classMetaOf(e.className); - const pre = host.loadVtPre(v.name, e.value.type.className); - let result: string | undefined; - for (const interval of classMembershipIntervals(host.classMeta, target.def.name)) { - const ge = B.tmp(), - le = B.tmp(), - test = B.tmp(); - B.line(`${ge} = icmp uge ${host.sizeType} ${pre}, ${interval.pre}`); - B.line(`${le} = icmp ule ${host.sizeType} ${pre}, ${interval.post}`); - B.line(`${test} = and i1 ${ge}, ${le} ; instanceof ${e.className}`); - if (result === undefined) result = test; - else { - const combined = B.tmp(); - B.line(`${combined} = or i1 ${result}, ${test}`); - result = combined; - } - } - return { name: result!, type: e.type }; + const result = emitClassMembership( + host, + v.name, + e.value.type.className, + e.className, + `instanceof ${e.className}`, + ); + return { name: result, type: e.type }; } case "virtualCall": { // Dispatch through the receiver's vtable: the slot lives on the diff --git a/packages/compiler/src/backend/llvm/expr-containers.ts b/packages/compiler/src/backend/llvm/expr-containers.ts index a2d5fd35aa..22a0b4df5b 100644 --- a/packages/compiler/src/backend/llvm/expr-containers.ts +++ b/packages/compiler/src/backend/llvm/expr-containers.ts @@ -42,14 +42,18 @@ import { } from "./map-number-lookup.js"; import { emitBorrowedInput } from "./borrowed-inputs.js"; import { emitFusedMapSet, rememberMapEntry } from "./map-entry-fusion.js"; -import { emitStringSliceRead } from "./string-slices.js"; +import { emitScratchStringSlice, emitStringSliceRead } from "./string-slices.js"; import { STRING_CHAR_CODE_AT_DECL, STRING_CHAR_CODE_AT_INT_MARKER, STRING_UTF16_LEN_DECL, } from "./string-reads.js"; import { exactInteger, widenInteger } from "./integer-values.js"; -import { emitDenseArrayNumber, emitDenseArrayPush } from "./dense-array-access.js"; +import { + emitDenseArrayNumber, + emitDenseArrayPop, + emitDenseArrayPush, +} from "./dense-array-access.js"; import { emitUnionWiden } from "./expr-records.js"; export function resolveThunkFor(host: LlvmEmitterContext, inner: IrType): string { @@ -391,6 +395,7 @@ export function emitStrIntrinsic( false, ); case "slice": + if (host.scratchStringSlices.has(e)) return emitScratchStringSlice(host, e, r, args); return call( "scr_str_slice", "ptr (ptr, double, double)", @@ -399,6 +404,7 @@ export function emitStrIntrinsic( true, ); case "substring": + if (host.scratchStringSlices.has(e)) return emitScratchStringSlice(host, e, r, args); return call( "scr_str_substring", "ptr (ptr, double, double)", @@ -571,7 +577,16 @@ export function emitArrIntrinsic( ? host.constantNumericTables.get(e.receiver.localId) : undefined; if (!table && elem.kind === "f64") - return { name: emitDenseArrayNumber(host, r.name, index, e.args[0]), type: e.type }; + return { + name: emitDenseArrayNumber( + host, + r.name, + index, + e.args[0], + host.provenDenseIndex(e.receiver, e.args[0]!), + ), + type: e.type, + }; host.declare(`declare double @scr_arr_get_number(ptr, double)`); const t = B.tmp(); B.line( @@ -667,19 +682,38 @@ export function emitArrIntrinsic( const rawSlot = B.slot(); const resultSlot = B.slot(); B.entryAllocas.push(`${rawSlot} = alloca i64`, `${resultSlot} = alloca ptr`); - B.line(`store i64 0, ptr ${rawSlot}`); - const state = B.tmp(); - const has = B.tmp(); - B.line(`${state} = call zeroext i8 @scr_arr_${method}_state(ptr ${r.name}, ptr ${rawSlot})`); - B.line(`${has} = icmp eq i8 ${state}, 1`); const lp = B.newLabel("remove.p"); const la = B.newLabel("remove.a"); const lj = B.newLabel("remove.j"); + // pop answers an empty or packed array inline; shift and every other + // pop take the runtime call. + let denseRaw: string | null = null; + let denseLabel = ""; + let slowLabel = ""; + if (method === "pop") { + slowLabel = B.newLabel("remove.slow"); + denseRaw = emitDenseArrayPop(host, r.name, acc, la, slowLabel); + denseLabel = B.currentLabel(); + B.br(lp); + B.startBlock(slowLabel); + } + B.line(`store i64 0, ptr ${rawSlot}`); + const state = B.tmp(); + const has = B.tmp(); + const slowRaw = B.tmp(); + B.line( + `${state} = call zeroext i8 @scr_arr_${method}_state(ptr ${r.name}, ptr ${rawSlot})${denseRaw === null ? "" : " cold"}`, + ); + B.line(`${slowRaw} = load i64, ptr ${rawSlot}`); + B.line(`${has} = icmp eq i8 ${state}, 1`); B.condBr(has, lp, la); B.startBlock(lp); - const raw = B.tmp(); + let raw = slowRaw; const value = B.tmp(); - B.line(`${raw} = load i64, ptr ${rawSlot}`); + if (denseRaw !== null) { + raw = B.tmp(); + B.line(`${raw} = phi i64 [ ${denseRaw}, %${denseLabel} ], [ ${slowRaw}, %${slowLabel} ]`); + } if (into) { // Slot bits for f64; a canonical 0/1 for bool, like scr_union_new_bool. let bits = raw; diff --git a/packages/compiler/src/backend/llvm/expr-dynamic.ts b/packages/compiler/src/backend/llvm/expr-dynamic.ts index 9a8ef28fb5..76df9795d1 100644 --- a/packages/compiler/src/backend/llvm/expr-dynamic.ts +++ b/packages/compiler/src/backend/llvm/expr-dynamic.ts @@ -1,4 +1,9 @@ import { classMembershipIntervals } from "./classes.js"; +import { + emitCaughtInstanceof, + emitCaughtNarrowGuard, + emitUnionNarrowGuard, +} from "./narrow-guards.js"; import { borrowableInputs, emitBorrowedInput, emitBorrowedInputs } from "./borrowed-inputs.js"; import { preservesDynTest } from "./checked-value-lifetimes.js"; import { typedRefConstructor } from "./shapes.js"; @@ -295,13 +300,14 @@ export function emitDynamicExpr( return host.own({ name: host.unionNewOwned(e.unionId, e.tag, v), type: e.type }); } case "unionNarrow": { - // Tag-UNCHECKED payload extraction: the frontend emits this only - // where tsc's control-flow narrowing proved the tag. Ref payloads - // come out +1. The receiver is consumed before any later expression. + // Payload extraction where tsc's control-flow narrowing proved the + // tag; the guard verifies it (narrow-guards.ts). Ref payloads come + // out +1. The receiver is consumed before any later expression. const u = host.emitUnionProjection(e.value); const arm = e.type; if (isUnitType(arm)) throw new InternalCompilerError(`llvm emitter bug: unionNarrow to unit arm ${arm.kind}`); + emitUnionNarrowGuard(host, u.name, e.unionId, e.tag); const v = host.unionExtract(u.name, e.unionId, arm); return host.own({ name: v, type: arm }); } @@ -974,56 +980,7 @@ export function emitDynamicExpr( // borrowed. SCR_EXC_STR = 3, SCR_EXC_F64 = 1, SCR_EXC_BOOL = 2. const c = host.emitReadReceiver(e.value); if (e.test === "instanceof") { - const target = host.classMetaOf(e.className!); - if (!target.hierarchy) { - // Standalone class payloads have no vtable word. Their retain - // adapter identifies the exact native layout in the snapshot. - const adapterSlot = B.tmp(); - const adapter = B.tmp(); - B.line(`${adapterSlot} = getelementptr inbounds %ScrCaught, ptr ${c.name}, i64 0, i32 5`); - B.line(`${adapter} = load ptr, ptr ${adapterSlot}`); - let result: string | undefined; - const intervals = classMembershipIntervals(host.classMeta, target.def.name); - for (const candidate of host.classMeta.values()) { - if ( - candidate.hierarchy || - !intervals.some((range) => range.pre <= candidate.pre && candidate.pre <= range.post) - ) - continue; - const rc = vAdapters(host.shapeHost, { kind: "object", className: candidate.def.name }); - const matches = B.tmp(); - B.line(`${matches} = icmp eq ptr ${adapter}, ${rc.retain}`); - if (result === undefined) result = matches; - else { - const joined = B.tmp(); - B.line(`${joined} = or i1 ${result}, ${matches}`); - result = joined; - } - } - if (result === undefined) - throw new InternalCompilerError("empty standalone class membership"); - if (e.negated !== true) return { name: result, type: e.type }; - const negated = B.tmp(); - B.line(`${negated} = xor i1 ${result}, true`); - return { name: negated, type: e.type }; - } - host.declare( - `declare zeroext i1 @scr_caught_instanceof(ptr, ${host.sizeType}, ${host.sizeType})`, - ); - let t: string | undefined; - for (const interval of classMembershipIntervals(host.classMeta, target.def.name)) { - const test = B.tmp(); - B.line( - `${test} = call zeroext i1 @scr_caught_instanceof(ptr ${c.name}, ${host.sizeType} ${interval.pre}, ${host.sizeType} ${interval.post})`, - ); - if (t === undefined) t = test; - else { - const combined = B.tmp(); - B.line(`${combined} = or i1 ${t}, ${test}`); - t = combined; - } - } - if (t === undefined) throw new InternalCompilerError("empty class membership"); + const t = emitCaughtInstanceof(host, c.name, e.className!); if (e.negated !== true) return { name: t, type: e.type }; const n = B.tmp(); B.line(`${n} = xor i1 ${t}, true`); @@ -1097,10 +1054,18 @@ export function emitDynamicExpr( return out; } case "caughtNarrow": { - // Checker-trusted extraction (the matching caughtTest was proven - // by tsc's narrowing): scalars read the snapshot's slots, - // refcounted payloads come out retained (+1). Box borrowed. + // Extraction where tsc's narrowing proved the matching caughtTest; + // the guard verifies it (narrow-guards.ts). Scalars read the + // snapshot's slots, refcounted payloads come out retained (+1). Box + // borrowed. const c = host.emitReadReceiver(e.value); + if ( + e.type.kind === "f64" || + e.type.kind === "bool" || + e.type.kind === "string" || + e.type.kind === "object" + ) + emitCaughtNarrowGuard(host, c.name, e.type); if (e.type.kind === "f64") { const p = B.tmp(); const v = B.tmp(); diff --git a/packages/compiler/src/backend/llvm/expr-primitives.ts b/packages/compiler/src/backend/llvm/expr-primitives.ts index aaae4c35cc..e237f8f13c 100644 --- a/packages/compiler/src/backend/llvm/expr-primitives.ts +++ b/packages/compiler/src/backend/llvm/expr-primitives.ts @@ -64,16 +64,25 @@ export function emitLiteralExpr( const integer = B.tmp(); const number = B.tmp(); B.line(`${integer} = load ${integerSlot.type}, ptr ${integerSlot.slot}`); - // A 64-bit induction value is a safe integer, so its sign bit is - // clear and the signed conversion is exact: one cvtsi2sd instead of - // x86-64's multi-instruction unsigned 64-bit conversion. - const signedConversion = integerSlot.signed === true || integerSlot.type === "i64"; - B.line( - `${number} = ${signedConversion ? "sitofp" : "uitofp"} ${integerSlot.type} ${integer} to double`, - ); const uint32 = integerSlot.type === "i32" ? integer : B.tmp(); if (integerSlot.type !== "i32") B.line(`${uint32} = trunc ${integerSlot.type} ${integer} to i32`); + // An int32 counter (a dense array loop's) converts from its low half, + // so LLVM folds a later `fptosi` of the double (an inlined callee's + // index) back to the integer. Otherwise a 64-bit induction value is + // a safe integer, so its sign bit is clear and the signed conversion + // is exact: one cvtsi2sd instead of x86-64's multi-instruction + // unsigned 64-bit conversion. (A uint32 counter would fold through + // `uitofp i32` too, but the zero extension it adds on x86-64 cost + // more in loops that never convert the double than it saved.) + const range = integerSlot.range; + const signedConversion = integerSlot.signed === true || integerSlot.type === "i64"; + if (range && range.min >= -2147483648 && range.max <= 2147483647) + B.line(`${number} = sitofp i32 ${uint32} to double`); + else + B.line( + `${number} = ${signedConversion ? "sitofp" : "uitofp"} ${integerSlot.type} ${integer} to double`, + ); return { name: number, type: e.type, @@ -851,6 +860,7 @@ export function emitContainerExpr( elem, `scr_arr_get_${acc}`, true, + host.provenDenseIndex(e.arr, e.index), ); return host.own({ name: t, type: e.type }); } diff --git a/packages/compiler/src/backend/llvm/expr-records.ts b/packages/compiler/src/backend/llvm/expr-records.ts index 4b03dbad24..fc9df6daff 100644 --- a/packages/compiler/src/backend/llvm/expr-records.ts +++ b/packages/compiler/src/backend/llvm/expr-records.ts @@ -339,6 +339,7 @@ export function emitRecordKeyGet( e.type, e.overflowOnly === true, e.loc, + e.missNaN === true, ); B.startBlock(join); const t = B.tmp(); @@ -356,6 +357,7 @@ export function keyedRecordReadInto( resultType: IrType, overflowOnly: boolean, loc?: SrcLoc, + missNaN = false, ): void { const B = host.B; const shape = host.recordShape(shapeId); @@ -563,6 +565,12 @@ export function keyedRecordReadInto( B.br(join); return; } + if (missNaN && resultType.kind === "f64") { + // A NaN-coded read: every consumer treats undefined like NaN. + B.line(`store double ${f64Lit(NaN)}, ptr ${slot}`); + B.br(join); + return; + } // A direct result cannot represent a miss. This is unreachable for // programs whose behavior matches Node, but preserve the missing-key trap. host.needsBadKey = true; diff --git a/packages/compiler/src/backend/llvm/lazy-captures.test.ts b/packages/compiler/src/backend/llvm/lazy-captures.test.ts index b0a47d0814..b0e5c55c55 100644 --- a/packages/compiler/src/backend/llvm/lazy-captures.test.ts +++ b/packages/compiler/src/backend/llvm/lazy-captures.test.ts @@ -47,7 +47,7 @@ function lifted(name: string, body: IrStmt[], captured = "options"): IrFunction loc, }; } -function lazy(...functions: IrFunction[]): Set { +function lazy(...functions: IrFunction[]): ReadonlySet { const map = new Map(functions.map((fn) => [fn.name, fn])); return new LazyCaptures(map).parameters(functions[0]!); } diff --git a/packages/compiler/src/backend/llvm/lazy-captures.ts b/packages/compiler/src/backend/llvm/lazy-captures.ts index 12fae0bf46..2014869af3 100644 --- a/packages/compiler/src/backend/llvm/lazy-captures.ts +++ b/packages/compiler/src/backend/llvm/lazy-captures.ts @@ -26,9 +26,15 @@ function rebound(node: IrStmt | IrExpr): string | null { * closure then allocate no box. The proof follows each capture into nested * function bodies; class environments keep the eager box. */ export class LazyCaptures { - private readonly unchanged = new Map(); private readonly stable = new Map>(); private readonly once = new Map>(); + private readonly unchangedParameters = new Map>(); + /** Per function, every local rebound there or through nested captures; + * the second map counts declarations separately (see rebindings). Each + * body is walked once per map, so the queries stay linear in the IR. */ + private readonly rebound = new Map>(); + private readonly reboundAfterDeclaration = new Map>(); + private readonly pending = new Set(); private sites: Map | null = null; constructor(private readonly functions: ReadonlyMap) {} @@ -112,8 +118,11 @@ export class LazyCaptures { return sites; } - parameters(fn: IrFunction): Set { + parameters(fn: IrFunction): ReadonlySet { + const known = this.unchangedParameters.get(fn); + if (known !== undefined) return known; const result = new Set(); + this.unchangedParameters.set(fn, result); if (fn.async || fn.generator) return result; const locals = new Map(fn.locals.map((local) => [local.id, local])); for (const param of fn.params) { @@ -132,18 +141,33 @@ export class LazyCaptures { ids: ReadonlySet, declarationsBind = false, ): Set { + const all = this.reboundIn(fn, declarationsBind); const changed = new Set(); + for (const id of ids) if (all.has(id)) changed.add(id); + return changed; + } + + /** Every local rebound in `fn` or, through the closures it creates, in + * any function that captures it, directly or through further nesting. + * With `declarationsBind`, declarations are counted separately. */ + private reboundIn(fn: IrFunction, declarationsBind: boolean): ReadonlySet { + const memo = declarationsBind ? this.reboundAfterDeclaration : this.rebound; + const known = memo.get(fn); + if (known !== undefined) return known; + const changed = new Set(); + // Capture chains follow lexical nesting and cannot cycle; on malformed + // input a function reached again while its walk is pending reports + // every capture as rebound (see isUnchanged). + if (!declarationsBind) this.pending.add(fn); const nested: { target: string; local: string; source: string }[] = []; const visit = (node: IrStmt | IrExpr): boolean => { - // With `declarationsBind`, declarations are counted separately. const target = declarationsBind && node.kind === "varDecl" ? null : rebound(node); - if (target !== null && ids.has(target)) changed.add(target); + if (target !== null) changed.add(target); if (node.kind === "classRef") { - for (const id of node.captures ?? []) if (ids.has(id)) changed.add(id); + for (const id of node.captures ?? []) changed.add(id); } else if (node.kind === "closure") { const callee = this.functions.get(node.fnName); node.captures.forEach((id, index) => { - if (!ids.has(id)) return; const capture = callee?.captures?.[index]; if (!capture) changed.add(id); else nested.push({ target: node.fnName, local: capture.localId, source: id }); @@ -156,19 +180,14 @@ export class LazyCaptures { if (!changed.has(edge.source) && !this.isUnchanged(edge.target, edge.local)) changed.add(edge.source); } + if (!declarationsBind) this.pending.delete(fn); + memo.set(fn, changed); return changed; } private isUnchanged(fnName: string, localId: string): boolean { - const key = `${fnName}\0${localId}`; - const known = this.unchanged.get(key); - if (known !== undefined) return known; - // Capture chains follow lexical nesting and cannot cycle; the - // provisional entry only bounds work on malformed input. - this.unchanged.set(key, false); const fn = this.functions.get(fnName); - const result = fn !== undefined && this.rebindings(fn, new Set([localId])).size === 0; - this.unchanged.set(key, result); - return result; + if (fn === undefined || this.pending.has(fn)) return false; + return !this.reboundIn(fn, false).has(localId); } } diff --git a/packages/compiler/src/backend/llvm/lib-datagram.ts b/packages/compiler/src/backend/llvm/lib-datagram.ts index 1553ecf4f1..c97744f640 100644 --- a/packages/compiler/src/backend/llvm/lib-datagram.ts +++ b/packages/compiler/src/backend/llvm/lib-datagram.ts @@ -121,6 +121,8 @@ export function emitDatagramLibCall(host: LlvmEmitterContext, e: LibCallExpr): L "dgram.getSendQueueSize": "scr_dgram_get_send_queue_size", "dgram.getSendQueueCount": "scr_dgram_get_send_queue_count", "dgram.disconnect": "scr_dgram_disconnect", + "dns.lookupPromise": "scr_dns_lookup_promise", + "dns.lookupServicePromise": "scr_dns_lookup_service_promise", }; const symbol = direct[e.fn]; if (symbol !== undefined) return finishRuntimeCall(host, e, symbol, a); diff --git a/packages/compiler/src/backend/llvm/lib-dispatch.ts b/packages/compiler/src/backend/llvm/lib-dispatch.ts index 4a9f4017a1..fdfe723d2c 100644 --- a/packages/compiler/src/backend/llvm/lib-dispatch.ts +++ b/packages/compiler/src/backend/llvm/lib-dispatch.ts @@ -327,6 +327,94 @@ export function emitGenericLibCall(host: LlvmEmitterContext, e: LibCallExpr): Ll host.emitPendingCheck(); return { name: "", type: e.type }; } + if (e.fn.startsWith("crypto.") && e.fn.includes("Promise")) { + // util.promisify over the callback forms: the callback form runs with + // a runtime callback that owns the +1 promise and settles it in the + // promise's value representation; an argument error the callback form + // threw moves into the promise as its rejection. + host.usesTimers = true; + if (e.type.kind !== "promise") + throw new InternalCompilerError("llvm emitter bug: crypto promise result not a promise"); + const inner = e.type.inner; + const dyn = inner.kind === "dyn"; + const args = e.args.map((arg) => host.emitExpr(arg)); + const settle = + e.fn === "crypto.randomIntPromise" + ? dyn + ? "scr_crypto_promise_settle_number_dyn" + : "scr_crypto_promise_settle_number" + : e.fn === "crypto.hkdfPromise" + ? "scr_crypto_promise_settle_array_buffer" + : e.fn === "crypto.generateKeyPromise" || e.fn === "crypto.randomFillPromiseDyn" + ? "scr_crypto_promise_settle_dyn" + : !dyn + ? "scr_crypto_promise_settle_bytes" + : e.fn === "crypto.randomFillPromise" + ? "scr_crypto_promise_settle_view_dyn" + : "scr_crypto_promise_settle_buffer_dyn"; + host.declare(`declare ptr @scr_promise_new()`); + host.declare(`declare ptr @scr_crypto_promise_callback(ptr)`); + host.declare(`declare void @scr_crypto_promise_reject_thrown(ptr)`); + host.declare( + e.fn === "crypto.randomIntPromise" + ? `declare void @${settle}(ptr, double)` + : `declare void @${settle}(ptr, ptr, ptr)`, + ); + const promise = B.tmp(); + const cb = B.tmp(); + B.line(`${promise} = call ptr @scr_promise_new()`); + B.line(`${cb} = call ptr @scr_crypto_promise_callback(ptr ${promise})`); + const a = (i: number, type = "ptr") => `${type} ${args[i]!.name}`; + const call = (symbol: string, params: string[], values: string[]) => { + host.declare(`declare void @${symbol}(${[...params, "ptr", "ptr"].join(", ")})`); + B.line(`call void @${symbol}(${[...values, `ptr ${cb}`, `ptr @${settle}`].join(", ")})`); + }; + if (e.fn === "crypto.randomBytesPromise") { + call("scr_crypto_random_bytes_async", ["double"], [a(0, "double")]); + } else if (e.fn === "crypto.pbkdf2Promise") { + call( + "scr_crypto_pbkdf2_async", + ["ptr", "ptr", "double", "double", "ptr"], + [a(0), a(1), a(2, "double"), a(3, "double"), a(4)], + ); + } else if (e.fn === "crypto.scryptPromise") { + call( + "scr_crypto_scrypt_async", + ["ptr", "ptr", "double", "ptr"], + [a(0), a(1), a(2, "double"), a(3)], + ); + } else if (e.fn === "crypto.hkdfPromise") { + call( + "scr_crypto_hkdf_async", + ["ptr", "ptr", "ptr", "ptr", "double"], + [a(0), a(1), a(2), a(3), a(4, "double")], + ); + } else if (e.fn === "crypto.randomFillPromise") { + call( + "scr_crypto_random_fill_async", + ["ptr", "double", "double", "double"], + [a(0), a(1, "double"), a(2, "double"), a(3, "double")], + ); + } else if (e.fn === "crypto.randomFillPromiseDyn") { + call( + "scr_crypto_random_fill_async_dyn", + ["ptr", "ptr", "ptr", "double"], + [a(0), a(1), a(2), a(3, "double")], + ); + } else if (e.fn === "crypto.randomIntPromise") { + call( + "scr_crypto_random_int_async", + ["double", "double", "double"], + [a(0, "double"), a(1, "double"), a(2, "double")], + ); + } else if (e.fn === "crypto.generateKeyPromise") { + call("scr_crypto_generate_key_async", ["ptr", "ptr"], [a(0), a(1)]); + } else { + throw new InternalCompilerError(`llvm emitter bug: unknown crypto promise ${e.fn}`); + } + B.line(`call void @scr_crypto_promise_reject_thrown(ptr ${promise})`); + return host.own({ name: promise, type: e.type }); + } if ( e.fn === "crypto.randomBytesCb" || e.fn === "crypto.pbkdf2Cb" || diff --git a/packages/compiler/src/backend/llvm/lib-process.ts b/packages/compiler/src/backend/llvm/lib-process.ts index b30c65b8df..546c9a209a 100644 --- a/packages/compiler/src/backend/llvm/lib-process.ts +++ b/packages/compiler/src/backend/llvm/lib-process.ts @@ -19,12 +19,13 @@ export function emitChildProcessLibCall(host: LlvmEmitterContext, e: LibCallExpr const argv = host.emitExpr(e.args[1]!); const cb = host.emitExpr(callbackArg); host.moveTemp(cb); + const shell = host.emitExpr(e.args[3]!); const adapter = host.execFileThunkFor(cbT); host.usesTimers = true; - host.declare(`declare ptr @scr_exec_file(ptr, ptr, ptr, ptr)`); + host.declare(`declare ptr @scr_exec_file(ptr, ptr, ptr, ptr, i1)`); const out = B.tmp(); B.line( - `${out} = call ptr @scr_exec_file(ptr ${cmd.name}, ptr ${argv.name}, ptr ${cb.name}, ptr @${adapter})`, + `${out} = call ptr @scr_exec_file(ptr ${cmd.name}, ptr ${argv.name}, ptr ${cb.name}, ptr @${adapter}, i1 ${shell.name})`, ); return host.own({ name: out, type: e.type }); } diff --git a/packages/compiler/src/backend/llvm/lib-shared.ts b/packages/compiler/src/backend/llvm/lib-shared.ts index 6793504391..f83896ad4f 100644 --- a/packages/compiler/src/backend/llvm/lib-shared.ts +++ b/packages/compiler/src/backend/llvm/lib-shared.ts @@ -850,6 +850,7 @@ export const LIB_FN_SYMS: Record = { "fetch.abortAny": "scr_fetch_abort_any", "fetch.webIs": "scr_fetch_web_is", "fetch.streamIs": "scr_fetch_stream_is", + "zlib.streamIs": "scr_zlib_stream_is", "fetch.streamNew": "scr_fetch_stream_new", "fetch.streamFrom": "scr_fetch_stream_from", "fetch.readerRead": "scr_fetch_reader_read", @@ -980,6 +981,8 @@ export const LIB_FN_SYMS: Record = { "emitter.getMax": "scr_emitter_get_max", "emitter.setDefaultMax": "scr_emitter_set_default_max", "emitter.getDefaultMax": "scr_emitter_get_default_max", + "emitter.esmDefaultMax": "scr_emitter_esm_default_max_get", + "emitter.esmSync": "scr_emitter_esm_sync", "emitter.errorMonitor": "scr_emitter_error_monitor", // node:stream (scr_stream.c): receivers borrowed, chunks borrowed, // chaining forms answer the receiver +1. The throwing members diff --git a/packages/compiler/src/backend/llvm/lib-test.ts b/packages/compiler/src/backend/llvm/lib-test.ts index d0d9a9bfc2..37e642d882 100644 --- a/packages/compiler/src/backend/llvm/lib-test.ts +++ b/packages/compiler/src/backend/llvm/lib-test.ts @@ -45,6 +45,10 @@ export function emitTestLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlVal return finishRuntimeCall(host, e, "scr_test_ctx_name", a); case "test.ctxFullName": return finishRuntimeCall(host, e, "scr_test_ctx_full_name", a); + case "test.ctxPassed": + return finishRuntimeCall(host, e, "scr_test_ctx_passed", a); + case "test.workerId": + return finishRuntimeCall(host, e, "scr_test_worker_id", a); case "test.ctxHook": host.moveTemp(args[2]!); return finishRuntimeCall(host, e, "scr_test_ctx_hook", a); diff --git a/packages/compiler/src/backend/llvm/narrow-guards.ts b/packages/compiler/src/backend/llvm/narrow-guards.ts new file mode 100644 index 0000000000..910af6f79e --- /dev/null +++ b/packages/compiler/src/backend/llvm/narrow-guards.ts @@ -0,0 +1,195 @@ +/* Runtime guards for checker-proven narrowings. + * + * `unionNarrow`, `downcast` and `caughtNarrow` extract a payload whose arm, + * class or kind TypeScript's control-flow narrowing proved. Emitted code + * verifies that proof once at the narrowing site: the tag, class interval or + * caught kind is compared and a mismatch stops the program through the + * runtime trap funnel instead of reading the payload as the wrong type. The + * frontend lowers every narrowing it knows it cannot prove through checked + * extraction, which throws the catchable TypeError, so a failing guard means + * either a compiler bug or a program whose hand-written type predicate or + * assertion function (`x is T`, `asserts x is T`) lied about a value's type: + * TypeScript trusts those claims without checking them. The guard aborts + * rather than unwinding through code that assumed the extraction could not + * throw. The guards are cheap: one load and compare per site, which LLVM + * folds where an identical test already dominates. */ +import { InternalCompilerError } from "../../errors.js"; +import { classMembershipIntervals } from "./classes.js"; +import type { LlvmEmitterContext } from "./expr-context.js"; +import { vAdapters } from "./shapes.js"; + +const NARROW_GUARD_HINT = " (a type predicate or assertion may be wrong)\n"; + +export const NARROW_GUARD_MESSAGES = { + union: `scriptc: a narrowed value does not have the type its narrowing proved${NARROW_GUARD_HINT}`, + downcast: `scriptc: a narrowed object is not an instance of the class its narrowing proved${NARROW_GUARD_HINT}`, + caught: `scriptc: a narrowed catch binding does not have the type its narrowing proved${NARROW_GUARD_HINT}`, +} as const; + +/** Continues in a fresh block where `ok` holds; otherwise traps with the + * message for `kind`. */ +export function emitNarrowGuard( + host: LlvmEmitterContext, + ok: string, + kind: keyof typeof NARROW_GUARD_MESSAGES, +): void { + const B = host.B; + host.declare(`declare i1 @llvm.expect.i1(i1, i1)`); + host.declare(`declare void @scr_trap(ptr)`); + const expected = B.tmp(); + B.line(`${expected} = call i1 @llvm.expect.i1(i1 ${ok}, i1 true)`); + const pass = B.newLabel("narrow.ok"); + const fail = B.newLabel("narrow.bad"); + B.condBr(expected, pass, fail); + B.startBlock(fail); + B.line(`call void @scr_trap(ptr ${host.cstr(NARROW_GUARD_MESSAGES[kind])})`); + B.terminate("unreachable"); + B.startBlock(pass); +} + +/** Verifies that the union value `unionName` holds arm `tag`. A two-arm + * union holds it exactly when it does not hold the other arm; testing the + * other arm lets LLVM fold the guard under a dominating `!== other` test as + * well as under `=== tag` (a tag outside the arms is not a union value). */ +export function emitUnionNarrowGuard( + host: LlvmEmitterContext, + unionName: string, + unionId: string, + tag: number, +): void { + const ok = host.B.tmp(); + const actual = host.unionTag(unionName, unionId); + if (host.unionsById.get(unionId)?.arms.length === 2) + host.B.line(`${ok} = icmp ne i32 ${actual}, ${1 - tag}`); + else host.B.line(`${ok} = icmp eq i32 ${actual}, ${tag}`); + emitNarrowGuard(host, ok, "union"); +} + +/** The i1 `instanceof` test of the borrowed object `objName`, statically a + * `staticClassName`, against `targetClassName`'s preorder intervals. */ +export function emitClassMembership( + host: LlvmEmitterContext, + objName: string, + staticClassName: string, + targetClassName: string, + comment: string, +): string { + const B = host.B; + const target = host.classMetaOf(targetClassName); + const pre = host.loadVtPre(objName, staticClassName); + let result: string | undefined; + for (const interval of classMembershipIntervals(host.classMeta, target.def.name)) { + const ge = B.tmp(), + le = B.tmp(), + test = B.tmp(); + B.line(`${ge} = icmp uge ${host.sizeType} ${pre}, ${interval.pre}`); + B.line(`${le} = icmp ule ${host.sizeType} ${pre}, ${interval.post}`); + B.line(`${test} = and i1 ${ge}, ${le} ; ${comment}`); + if (result === undefined) result = test; + else { + const combined = B.tmp(); + B.line(`${combined} = or i1 ${result}, ${test}`); + result = combined; + } + } + return result!; +} + +/** Verifies that the borrowed object `objName` is a `targetClassName`. */ +export function emitDowncastGuard( + host: LlvmEmitterContext, + objName: string, + staticClassName: string, + targetClassName: string, +): void { + const ok = emitClassMembership( + host, + objName, + staticClassName, + targetClassName, + `downcast ${targetClassName}`, + ); + emitNarrowGuard(host, ok, "downcast"); +} + +/** The i1 `e instanceof C` test of the borrowed catch snapshot `caught`. + * Hierarchy classes compare the OBJ payload's vtable preorder; standalone + * class payloads have no vtable word, so their retain adapter identifies + * the exact native layout. */ +export function emitCaughtInstanceof( + host: LlvmEmitterContext, + caught: string, + className: string, +): string { + const B = host.B; + const target = host.classMetaOf(className); + const intervals = classMembershipIntervals(host.classMeta, target.def.name); + if (!target.hierarchy) { + const adapterSlot = B.tmp(); + const adapter = B.tmp(); + B.line(`${adapterSlot} = getelementptr inbounds %ScrCaught, ptr ${caught}, i64 0, i32 5`); + B.line(`${adapter} = load ptr, ptr ${adapterSlot}`); + let result: string | undefined; + for (const candidate of host.classMeta.values()) { + if ( + candidate.hierarchy || + !intervals.some((range) => range.pre <= candidate.pre && candidate.pre <= range.post) + ) + continue; + const rc = vAdapters(host.shapeHost, { kind: "object", className: candidate.def.name }); + const matches = B.tmp(); + B.line(`${matches} = icmp eq ptr ${adapter}, ${rc.retain}`); + if (result === undefined) result = matches; + else { + const joined = B.tmp(); + B.line(`${joined} = or i1 ${result}, ${matches}`); + result = joined; + } + } + if (result === undefined) throw new InternalCompilerError("empty standalone class membership"); + return result; + } + host.declare( + `declare zeroext i1 @scr_caught_instanceof(ptr, ${host.sizeType}, ${host.sizeType})`, + ); + let result: string | undefined; + for (const interval of intervals) { + const test = B.tmp(); + B.line( + `${test} = call zeroext i1 @scr_caught_instanceof(ptr ${caught}, ${host.sizeType} ${interval.pre}, ${host.sizeType} ${interval.post})`, + ); + if (result === undefined) result = test; + else { + const combined = B.tmp(); + B.line(`${combined} = or i1 ${result}, ${test}`); + result = combined; + } + } + if (result === undefined) throw new InternalCompilerError("empty class membership"); + return result; +} + +/** SCR_EXC_F64 = 1, SCR_EXC_BOOL = 2, SCR_EXC_STR = 3 (scr_exception.h). */ +export const CAUGHT_SCALAR_KINDS = { f64: 1, bool: 2, string: 3 } as const; + +/** Verifies that the borrowed catch snapshot `caught` holds a value of the + * narrowed type: a scalar's kind tag, or an object's class. */ +export function emitCaughtNarrowGuard( + host: LlvmEmitterContext, + caught: string, + type: { kind: "f64" | "bool" | "string" } | { kind: "object"; className: string }, +): void { + const B = host.B; + let ok: string; + if (type.kind === "object") { + ok = emitCaughtInstanceof(host, caught, type.className); + } else { + const kp = B.tmp(); + const k = B.tmp(); + ok = B.tmp(); + B.line(`${kp} = getelementptr inbounds %ScrCaught, ptr ${caught}, i64 0, i32 1`); + B.line(`${k} = load i32, ptr ${kp}`); + B.line(`${ok} = icmp eq i32 ${k}, ${CAUGHT_SCALAR_KINDS[type.kind]}`); + } + emitNarrowGuard(host, ok, "caught"); +} diff --git a/packages/compiler/src/backend/llvm/record-results.ts b/packages/compiler/src/backend/llvm/record-results.ts index 41d76564e3..47a7983d2d 100644 --- a/packages/compiler/src/backend/llvm/record-results.ts +++ b/packages/compiler/src/backend/llvm/record-results.ts @@ -21,6 +21,16 @@ export function resultEntryName(name: string): string { return `${RESULT_ENTRY_PREFIX}${name}`; } +/** The local id of a result-slot entry's destination parameter. The + * caller passes zeroed frame storage, and on every path the entry stores + * each of its fields at most once, so a store into it is an + * initialization: there is no old value to release. */ +export const RESULT_DST_ID = "%into.dst"; + +function distinctNames(names: readonly string[]): boolean { + return new Set(names).size === names.length; +} + /** A plain record shape whose fields are numbers, booleans and inline * scalar unions (inline-unions.ts): its fields own nothing. */ export function scalarRecordShape( @@ -38,6 +48,15 @@ export function scalarRecordShape( ); } +/** A closed record or tuple shape: declared fields only. Its instance can + * live in caller storage; fields that own references are released by the + * frame that holds the storage (stack-objects.ts). Tuples are element + * records: destructuring and element reads project them, and every + * array-like use of one is a whole-value use that keeps it on the heap. */ +export function frameRecordShape(shape: IrRecordShape | undefined): boolean { + return shape !== undefined && !shape.indexValue; +} + /** A literal that stores every field of its shape, none of them through * the overflow map, and that sets no overflow-map flag. */ export function completeRecordLiteral( @@ -110,7 +129,7 @@ export function constructorInitializesFields(cls: IrClassDef, ctor: IrFunction): } /** Add a result-slot entry for every function whose returns all build a - * fresh scalar record or standalone scalar object, return another such + * fresh closed record or standalone scalar object, return another such * producer's result (forwarding the slot), or return a parameter of a * record shape no store can change (copied into the slot). The original * function stays as it is; the backend calls the entry only where the @@ -155,8 +174,12 @@ export function addRecordResultEntries(mod: IrModule): IrModule { }, }); const dynamicStores = dynamicFieldStores(mod); + // Copies are limited to scalar shapes: a copy of a reference-owning + // record would not carry the undeclared source keys a checked conversion + // may keep beside the original (record-sources.ts). const copyable = (type: IrType): boolean => type.kind === "record" && + scalarRecordShape(shapes.get(type.shapeId), inline) && mod.lib === undefined && !written.has(type.shapeId) && !dynamicStores.all && @@ -165,9 +188,11 @@ export function addRecordResultEntries(mod: IrModule): IrModule { const fillable = (value: IrExpr, params: ReadonlySet): boolean => { switch (value.kind) { case "recordLit": - return completeRecordLiteral( - value, - shapes.get((value.type as IrType & { kind: "record" }).shapeId), + return ( + completeRecordLiteral( + value, + shapes.get((value.type as IrType & { kind: "record" }).shapeId), + ) && distinctNames(value.fields.map((field) => field.name)) ); case "new": return stackClass(value.className); @@ -175,6 +200,15 @@ export function addRecordResultEntries(mod: IrModule): IrModule { return fillable(value.then, params) && fillable(value.else_, params); case "varRef": return params.has(value.localId) && copyable(value.type); + case "recordClone": + // A spread of a binding: the copies read it before any override + // evaluates, as the heap clone does. + return ( + value.source.kind === "varRef" && + value.type.kind === "record" && + frameRecordShape(shapes.get(value.type.shapeId)) && + distinctNames(value.overrides.map((override) => override.name)) + ); case "call": return producers.has(value.callee); default: @@ -210,6 +244,39 @@ export function addRecordResultEntries(mod: IrModule): IrModule { loc: value.loc, })); } + case "recordClone": { + const shapeId = (value.type as IrType & { kind: "record" }).shapeId; + const overridden = new Set(value.overrides.map((override) => override.name)); + const copies: IrStmt[] = shapes + .get(shapeId)! + .fields.filter((field) => !overridden.has(field.name)) + .map((field): IrStmt => ({ + kind: "recordSet", + obj: dst, + shapeId, + field: field.name, + value: { + kind: "recordGet", + obj: value.source, + shapeId, + field: field.name, + type: field.type, + loc: value.loc, + }, + loc: value.loc, + })); + return [ + ...copies, + ...value.overrides.map((override): IrStmt => ({ + kind: "recordSet", + obj: dst, + shapeId, + field: override.name, + value: override.value, + loc: override.value.loc, + })), + ]; + } case "call": return [ { @@ -327,7 +394,7 @@ export function addRecordResultEntries(mod: IrModule): IrModule { return false; const ret = fn.returnType; return ( - (ret.kind === "record" && scalarRecordShape(shapes.get(ret.shapeId), inline)) || + (ret.kind === "record" && frameRecordShape(shapes.get(ret.shapeId))) || (ret.kind === "object" && stackClass(ret.className)) ); }); @@ -360,7 +427,7 @@ export function addRecordResultEntries(mod: IrModule): IrModule { for (const fn of candidates) { if (!producers.has(fn.name)) continue; const ret = fn.returnType; - const dstId = "%into.dst"; + const dstId = RESULT_DST_ID; const dst: IrExpr = { kind: "varRef", localId: dstId, type: ret, loc: fn.loc }; const body = transformStmtList(fn.body, { expr: (e) => e, diff --git a/packages/compiler/src/backend/llvm/stack-objects.test.ts b/packages/compiler/src/backend/llvm/stack-objects.test.ts index 845d6ae5ea..a413db333d 100644 --- a/packages/compiler/src/backend/llvm/stack-objects.test.ts +++ b/packages/compiler/src/backend/llvm/stack-objects.test.ts @@ -275,3 +275,64 @@ console.log(pick({ x: 1, y: 2 }, { x: 3, y: 4 }, true).y, forward({ x: 1, y: 1 } .sort(); expect(entries).toEqual([`${RESULT_ENTRY_PREFIX}forward`, `${RESULT_ENTRY_PREFIX}pick`]); }); + +const TOKENS = ` +interface Token { kind: string; text: string; pos: number } +function make(kind: string, text: string, pos: number): Token { return { kind, text, pos }; } +function width(t: Token): number { return t.text.length + t.kind.length; } +function divmod(a: number, b: number): [number, number] { return [Math.floor(a / b), a % b]; } +interface State { name: string; count: number } +function bump(s: State): State { return { ...s, count: s.count + 1 }; } +export function tokens(n: number, w: string): number { + const t = { kind: "id", text: w, pos: n }; + const u = make("num", w, n); + const [q, r] = divmod(n, 7); + return t.pos + width(u) + width({ kind: w, text: w, pos: 0 }) + q + r; +} +export function states(n: number, w: string): number { + let s: State = { name: w, count: 0 }; + for (let i = 0; i < n; i++) { + s = { ...s, count: s.count + 1 }; + s = bump(s); + } + return s.count + s.name.length; +} +console.log(tokens(3, "ab"), states(4, "cd")); +`; + +test("records and tuples that own references live in frame storage", async () => { + const ir = emitLlvmModule(await moduleFor(TOKENS)); + const tokens = body(ir, "tokens"); + expect(tokens).not.toMatch(/@sc_rnew_/); + expect(tokens).toMatch(/call void @sc_\w*into_make\(/); + expect(tokens).toMatch(/call void @sc_\w*into_divmod\(/); + // Reference fields start empty in entry-addressed slots, and the frame + // releases them; an interned literal field (`kind: "id"`) needs neither. + const fields = [...tokens.matchAll(/(%s\d+) = getelementptr inbounds %sc_rs_\w+, ptr %s\d+/g)]; + const owned = fields.filter(([, ptr]) => tokens.includes(`store ptr null, ptr ${ptr}\n`)); + expect(owned.length).toBe(5); + expect(fields.length).toBe(6); + for (const [, ptr] of owned) + expect(tokens).toMatch(new RegExp(`load ptr, ptr ${ptr}\\n\\s+call void @\\w+\\(`)); +}); + +test("spread updates copy into the binding's slot and release the replaced fields", async () => { + const mod = await moduleFor(TOKENS); + const entries = addRecordResultEntries(mod) + .functions.map((fn) => fn.name) + .filter((name) => name.startsWith(RESULT_ENTRY_PREFIX)); + expect(entries).toEqual( + expect.arrayContaining([ + `${RESULT_ENTRY_PREFIX}make`, + `${RESULT_ENTRY_PREFIX}divmod`, + `${RESULT_ENTRY_PREFIX}bump`, + ]), + ); + const states = body(emitLlvmModule(mod), "states"); + expect(states).not.toMatch(/@sc_rnew_|@sc_rclone_/); + expect(states).toMatch(/call void @sc_\w*into_bump\(/); + // The old name is loaded before the copy and released after it. + expect(states).toMatch( + /(%t\d+) = load ptr, ptr %s\d+\n\s+(%t\d+) = load %sc_rs_\w+, ptr %s\d+\n\s+store %sc_rs_\w+ \2, ptr %s\d+\n(?:.*\n)*?\s+call void @\w+\(ptr \1\)/, + ); +}); diff --git a/packages/compiler/src/backend/llvm/stack-objects.ts b/packages/compiler/src/backend/llvm/stack-objects.ts index c03c7fb442..e9f437f149 100644 --- a/packages/compiler/src/backend/llvm/stack-objects.ts +++ b/packages/compiler/src/backend/llvm/stack-objects.ts @@ -1,9 +1,16 @@ -import { VOID, typeEquals, type IrExpr, type IrFunction, type IrType } from "../../ir/ir.js"; +import { + VOID, + isRefCounted, + typeEquals, + type IrExpr, + type IrFunction, + type IrType, +} from "../../ir/ir.js"; import { completeRecordLiteral, constructorInitializesFields, + frameRecordShape, resultEntryName, - scalarRecordShape, scalarStandaloneClass, } from "./record-results.js"; import { everyStmtList } from "../../ir/traverse.js"; @@ -14,23 +21,45 @@ import type { CallLifetimes } from "./call-lifetimes.js"; import { emitCallExpr, emitConstruction } from "./expr-calls.js"; import type { LlValue, LlvmEmitterContext } from "./expr-context.js"; import { emitRecordLitFields } from "./expr-primitives.js"; +import { llFieldType } from "./shapes.js"; -export type StackObjectSource = IrExpr & { kind: "recordLit" | "new" | "call" }; +export type StackObjectSource = IrExpr & { kind: "recordLit" | "recordClone" | "new" | "call" }; -/** A record literal, `new` or result-slot producer call whose object can - * live in the frame's own storage: a plain scalar record shape, or a +/** A field of a frame object that owns a reference: an entry-block pointer + * to its slot, and the field's type for the release. */ +export interface StackObjectField { + name: string; + ptr: string; + type: IrType; +} + +/** Records the owners of a frame object's reference fields before anything + * stores them: a lexical scope for bindings, a statement frame for + * temporaries. */ +export type StackObjectFieldOwner = (fields: readonly StackObjectField[]) => void; + +/** A record literal, spread copy, `new` or result-slot producer call whose + * object can live in the frame's own storage: a closed record shape, or a * standalone scalar class whose constructor never lets `this` escape * (record-results.ts). The caller proves that every use of the value only - * projects it. */ + * projects it. Record fields may own references; the frame releases them + * (stackObjectFields). Published shapes keep heap storage: their field + * guards treat the immortal header as a frozen object. */ export function canStackObject(host: LlvmEmitterContext, e: IrExpr): e is StackObjectSource { + if (e.type.kind === "record" && host.publishedTypes?.recordGuarded(e.type.shapeId) === true) + return false; // A producer with a result-slot entry builds its fresh result in the // caller's storage. if (e.kind === "call") return host.fnByName.has(resultEntryName(e.callee)); if (e.kind === "recordLit") { if (e.type.kind !== "record") return false; const shape = host.recordsById.get(e.type.shapeId); - return scalarRecordShape(shape, host.inlineUnions) && completeRecordLiteral(e, shape); + return frameRecordShape(shape) && completeRecordLiteral(e, shape); } + // A same-shape spread (`{ ...source, field: value }`) copies the source's + // fields; the frontend builds it only for plain declared-field records. + if (e.kind === "recordClone") + return e.type.kind === "record" && frameRecordShape(host.recordsById.get(e.type.shapeId)); if (e.kind !== "new") return false; const meta = host.classMeta.get(e.className); return ( @@ -45,6 +74,33 @@ export function canStackObject(host: LlvmEmitterContext, e: IrExpr): e is StackO ); } +/** Whether a frame object of this type has fields that own references. */ +function ownsReferences(host: LlvmEmitterContext, type: IrType): boolean { + return ( + type.kind === "record" && + host + .recordShape(type.shapeId) + .fields.some((field) => isRefCounted(field.type) && !host.inlineUnions.record(field.type)) + ); +} + +/** Locals declared directly in a switch case. The cases share one scope + * whose exit runs whichever case was entered, so a scope-owned field would + * be released without its declaration having run. */ +function caseScopedLocals(fn: IrFunction): Set { + const ids = new Set(); + everyStmtList(fn.body, { + expr: () => true, + stmt: (stmt) => { + if (stmt.kind === "switch") + for (const c of stmt.cases) + for (const s of c.body) if (s.kind === "varDecl") ids.add(s.localId); + return true; + }, + }); + return ids; +} + /** Locals initialized once by a stack-eligible object and only projected * afterwards (CallLifetimes.locals): field reads, and arguments to * parameters that are themselves only projected. */ @@ -56,6 +112,7 @@ export function findLocalStackObjects( const result = new Map(); const safe = lifetimes.locals.get(fn.name); if (!safe?.size || fn.async || fn.generator) return result; + const caseScoped = caseScopedLocals(fn); everyStmtList(fn.body, { expr: () => true, stmt: (stmt) => { @@ -63,7 +120,8 @@ export function findLocalStackObjects( stmt.kind === "varDecl" && safe.has(stmt.localId) && stmt.init && - canStackObject(host, stmt.init) + canStackObject(host, stmt.init) && + !(caseScoped.has(stmt.localId) && ownsReferences(host, stmt.init.type)) ) result.set(stmt.localId, stmt.init); return true; @@ -112,7 +170,10 @@ export function findReassignedStackObjects( return true; }, }); - for (const id of result) if (!initialized.has(id)) result.delete(id); + const caseScoped = caseScopedLocals(fn); + for (const id of result) + if (!initialized.has(id) || (caseScoped.has(id) && ownsReferences(host, types.get(id)!))) + result.delete(id); return result; } @@ -122,13 +183,77 @@ export function stackObjectStruct(type: IrType): string { : `%${mangleClassStruct((type as IrType & { kind: "object" }).className)}`; } +/** The reference fields of the frame object in `slot`, addressed from the + * entry block so that every cleanup path can reach them. Inline union + * fields copy bits and own nothing; class objects in frame storage are + * scalar. */ +export function stackObjectFields( + host: LlvmEmitterContext, + type: IrType, + slot: string, +): readonly StackObjectField[] { + if (type.kind !== "record") return []; + const cached = host.stackObjectFieldPtrs.get(slot); + if (cached) return cached; + const struct = stackObjectStruct(type); + const fields: StackObjectField[] = []; + host.recordShape(type.shapeId).fields.forEach((field, index) => { + if (!isRefCounted(field.type) || host.inlineUnions.record(field.type)) return; + const ptr = host.B.slot(); + host.B.entryAllocas.push( + `${ptr} = getelementptr inbounds ${struct}, ptr ${slot}, i64 0, i32 ${index + 1}`, + ); + fields.push({ name: field.name, ptr, type: field.type }); + }); + host.stackObjectFieldPtrs.set(slot, fields); + return fields; +} + +/** Copy a same-shape spread's source fields into `slot`, retaining the + * references, then store the overrides in source order. The source is + * read once, before any override evaluates, as the heap clone helper + * reads it; an overridden field is never copied, so its slot stays empty + * until the override initializes it. */ +function emitRecordCloneFields( + host: LlvmEmitterContext, + e: IrExpr & { kind: "recordClone" }, + slot: string, +): void { + const B = host.B; + const shapeId = (e.type as IrType & { kind: "record" }).shapeId; + const overridden = new Set(e.overrides.map((override) => override.name)); + const source = host.emitReadReceiver(e.source); + for (const field of host.recordShape(shapeId).fields) { + if (overridden.has(field.name)) continue; + const from = host.recordFieldPtr(source.name, shapeId, field.name); + const to = host.recordFieldPtr(slot, shapeId, field.name); + const fieldTy = llFieldType(from.type, from.inline); + const v = B.tmp(); + B.line(`${v} = load ${fieldTy}, ptr ${from.ptr}`); + const stored = isRefCounted(from.type) && !from.inline ? host.retainValue(v, from.type) : v; + B.line(`store ${fieldTy} ${stored}, ptr ${to.ptr}`); + } + const initialized = new Set(); + for (const override of e.overrides) { + const v = host.emitFieldValue(override.value, host.recordFieldInline(shapeId, override.name)); + const { ptr, type, inline } = host.recordFieldPtr(slot, shapeId, override.name); + // The first store fills an empty slot; a repeated name replaces it. + if (initialized.has(override.name)) host.replaceField(ptr, type, inline, v); + else host.initField(ptr, type, inline, v); + initialized.add(override.name); + } +} + /** Build the object in an entry-block slot (or `into`, an existing one). * The immortal reference count makes every retain and release a no-op; * the proof that the value is only projected keeps the pointer from - * outliving the frame. */ + * outliving the frame. Reference fields start empty and are handed to + * `owner` before anything is stored, so an exception while the object is + * being built releases exactly the fields stored so far. */ export function emitStackObject( host: LlvmEmitterContext, e: StackObjectSource, + owner: StackObjectFieldOwner, into?: string, ): LlValue { const B = host.B; @@ -139,6 +264,18 @@ export function emitStackObject( slot = B.slot(); B.entryAllocas.push(`${slot} = alloca ${struct}, align 8`); } + // A literal's interned string fields are immortal and need no owner. A + // binding's own slot (`into`) keeps an owner for every field: later + // assignments copy arbitrary values into it. + const literals = + into === undefined && e.kind === "recordLit" + ? new Set(e.fields.filter((f) => f.value.kind === "strLit").map((f) => f.name)) + : new Set(); + const fields = stackObjectFields(host, type, slot).filter((f) => !literals.has(f.name)); + if (fields.length > 0) { + for (const field of fields) B.line(`store ptr null, ptr ${field.ptr}`); + owner(fields); + } // Literals store every field; constructors that store every field // before reading one need no zeroed storage either. if (type.kind === "object") { @@ -169,6 +306,7 @@ export function emitStackObject( host.markImmortal({ name: slot, type }), ); } else if (e.kind === "recordLit") emitRecordLitFields(host, e, slot); + else if (e.kind === "recordClone") emitRecordCloneFields(host, e, slot); else emitConstruction(host, e, slot, host.fnByName.get(`%${e.className}.constructor`)!); return { name: slot, type: e.type }; } diff --git a/packages/compiler/src/backend/llvm/string-slices.test.ts b/packages/compiler/src/backend/llvm/string-slices.test.ts index 53cca64179..b2656e560a 100644 --- a/packages/compiler/src/backend/llvm/string-slices.test.ts +++ b/packages/compiler/src/backend/llvm/string-slices.test.ts @@ -1,7 +1,11 @@ import { expect, test } from "vitest"; -import { F64, STRING, VOID, type IrExpr, type IrFunction, type IrStmt } from "../../ir/ir.js"; +import { BOOL, F64, STRING, VOID, type IrExpr, type IrFunction, type IrStmt } from "../../ir/ir.js"; import { analyzeCallLifetimes } from "./call-lifetimes.js"; -import { findScalarStringSlices } from "./string-slices.js"; +import { + findLentStringParameters, + findScalarStringSlices, + findScratchStringSlices, +} from "./string-slices.js"; const loc = { file: "slices.ts", start: 0, end: 0 }; const ref = (id: string): IrExpr => ({ kind: "varRef", localId: id, type: STRING, loc }); function fixture(): IrFunction { @@ -95,3 +99,146 @@ test.each(["escape", "capture", "write", "other-consumer", "suspend"])( expect(facts(fn).size).toBe(0); }, ); + +const slice = (receiver: string, start: number, end: number): IrExpr => ({ + kind: "strIntrinsic", + method: "slice", + receiver: ref(receiver), + args: [ + { kind: "numLit", value: start, type: F64, loc }, + { kind: "numLit", value: end, type: F64, loc }, + ], + type: STRING, + loc, +}); +const equals = (left: IrExpr): IrExpr => ({ + kind: "strEq", + negated: false, + left, + right: { kind: "strLit", value: "GET", type: STRING, loc }, + type: BOOL, + loc, +}); +const parse = (value: IrExpr): IrExpr => ({ + kind: "libCall", + fn: "num.fromString", + args: [value], + type: F64, + loc, +}); +function lending(body: IrStmt[]): IrFunction { + return { + name: "scan", + params: [{ localId: "source", name: "source", type: STRING }], + locals: [ + { id: "source", name: "source", type: STRING, mutable: false }, + { id: "word", name: "word", type: STRING, mutable: false }, + ], + returnType: VOID, + loc, + body, + }; +} +function scratch(fn: IrFunction, extra: IrFunction[] = []): Set { + const functions = new Map([fn, ...extra].map((f) => [f.name, f])); + const lifetimes = analyzeCallLifetimes(functions); + return findScratchStringSlices( + fn, + lifetimes, + findScalarStringSlices(fn, lifetimes), + findLentStringParameters(functions, lifetimes), + ); +} +test("slices read only by lending consumers use a stack buffer", () => { + const inline = slice("source", 0, 3); + const parsed = slice("source", 4, 6); + const init = slice("source", 1, 5); + const fn = lending([ + { kind: "exprStmt", expr: equals(inline), loc }, + { kind: "exprStmt", expr: parse(parsed), loc }, + { kind: "varDecl", localId: "word", init, loc }, + { kind: "exprStmt", expr: equals(ref("word")), loc }, + { kind: "exprStmt", expr: parse(ref("word")), loc }, + ]); + expect(scratch(fn)).toEqual(new Set([inline, parsed, init])); +}); +test.each(["return", "concat", "slice-of-slice", "capture", "async"])( + "a slice local that may be kept (%s) stays owned", + (reason) => { + const init = slice("source", 1, 5); + const fn = lending([ + { kind: "varDecl", localId: "word", init, loc }, + { kind: "exprStmt", expr: equals(ref("word")), loc }, + ]); + if (reason === "return") fn.body.push({ kind: "return", value: ref("word"), loc }); + if (reason === "concat") + fn.body.push({ + kind: "exprStmt", + expr: { + kind: "strConcat", + left: { kind: "strLit", value: "", type: STRING, loc }, + right: ref("word"), + type: STRING, + loc, + }, + loc, + }); + if (reason === "slice-of-slice") + fn.body.push({ kind: "exprStmt", expr: equals(slice("word", 0, 2)), loc }); + if (reason === "capture") fn.locals[1]!.boxed = true; + if (reason === "async") fn.async = true; + expect(scratch(fn).has(init)).toBe(false); + }, +); +function helper(name: string, body: IrStmt[]): IrFunction { + return { + name, + params: [{ localId: "text", name: "text", type: STRING }], + locals: [{ id: "text", name: "text", type: STRING, mutable: false }], + returnType: BOOL, + loc, + body, + }; +} +const callOf = (callee: string, arg: IrExpr): IrExpr => ({ + kind: "call", + callee, + args: [arg], + type: BOOL, + loc, +}); +test("helper parameters read only by lending consumers accept stack slices", () => { + const recursive = helper("isGet", [ + { + kind: "return", + value: { + kind: "logical", + op: "||", + left: equals(ref("text")), + right: callOf("isGet", ref("text")), + type: BOOL, + loc, + }, + loc, + }, + ]); + const keeps = helper("keeps", [{ kind: "return", value: callOf("stores", ref("text")), loc }]); + const stores = helper("stores", [ + { + kind: "exprStmt", + expr: { kind: "arrayLit", elems: [ref("text")], type: { kind: "array", elem: STRING }, loc }, + loc, + }, + { kind: "return", value: { kind: "boolLit", value: true, type: BOOL, loc }, loc }, + ]); + const lent = slice("source", 0, 3); + const kept = slice("source", 0, 4); + const fn = lending([ + { kind: "exprStmt", expr: callOf("isGet", lent), loc }, + { kind: "exprStmt", expr: callOf("keeps", kept), loc }, + ]); + const functions = [recursive, keeps, stores]; + expect(scratch(fn, functions)).toEqual(new Set([lent])); + const all = new Map([fn, ...functions].map((f) => [f.name, f])); + expect([...findLentStringParameters(all, analyzeCallLifetimes(all)).keys()]).toEqual(["isGet"]); +}); diff --git a/packages/compiler/src/backend/llvm/string-slices.ts b/packages/compiler/src/backend/llvm/string-slices.ts index 71a26e2f39..ae73f1ea1e 100644 --- a/packages/compiler/src/backend/llvm/string-slices.ts +++ b/packages/compiler/src/backend/llvm/string-slices.ts @@ -1,4 +1,9 @@ -import { type IrExpr, type IrFunction, type IrStmt } from "../../ir/ir.js"; +import { + type IrExpr, + type IrFunction, + type IrStmt, + type IrStrIntrinsicMethod, +} from "../../ir/ir.js"; import { everyExprChild, everyStmtChild, everyStmtList } from "../../ir/traverse.js"; import { F64_INF, f64Lit, sizeToDouble } from "./common.js"; import type { CallLifetimes } from "./call-lifetimes.js"; @@ -6,6 +11,12 @@ import type { LlValue, LlvmEmitterContext } from "./expr-context.js"; type Slice = IrExpr & { kind: "strIntrinsic" }; +function isSlice(value: IrExpr): value is Slice { + return ( + value.kind === "strIntrinsic" && (value.method === "slice" || value.method === "substring") + ); +} + /** A local used exclusively for scalar indexing needs no copied bytes. * Snapshot the receiver and normalized bounds at its initializer, including * side effects in the bounds. Escapes, captures, writes, suspension, and all @@ -111,3 +122,234 @@ export function emitStringSliceRead(host: LlvmEmitterContext, value: Slice): LlV ); return { name: result, type: value.type }; } + +/** Bytes of the stack buffer behind one scratch slice, header included. */ +export const SCRATCH_SLICE_BYTES = 96; + +/** String methods that read a lent receiver or argument only during their + * runtime call and never return it. Methods returning a string can return + * their unchanged receiver, so they stay on the owned path. */ +const LENDING_METHODS: ReadonlySet = new Set([ + "length", + "charCodeAt", + "indexOf", + "includes", + "startsWith", + "endsWith", +]); + +/** Numeric parsers read their text during the call. */ +const LENDING_LIB_FNS: ReadonlySet = new Set([ + "num.fromString", + "num.parseInt", + "num.parseFloat", +]); + +/** Typed string-keyed collection operations: reads probe with the key, and + * an insertion stores scr_str_scratch_keep's copy (scr_map_append). */ +const LENDING_KEY_METHODS: ReadonlySet = new Set(["get", "has", "set", "add", "delete"]); + +function stringKeyed(value: IrExpr & { kind: "mapIntrinsic" | "setIntrinsic" }): boolean { + const receiver = value.receiver.type; + if (receiver.kind === "map") + return receiver.key.kind === "string" && receiver.value.kind !== "dyn"; + if (receiver.kind === "set") return receiver.elem.kind === "string"; + return false; +} + +/** String parameters, by function and index, that the body reads only in + * lending positions (findLentStringParameters). */ +export type LentStringParameters = ReadonlyMap>; + +/** The operands `value` reads only while it runs, keeping no reference. */ +function lentOperands(value: IrExpr, parameters: LentStringParameters): readonly IrExpr[] { + switch (value.kind) { + case "strEq": + case "strCmp": + return [value.left, value.right]; + case "strIntrinsic": + return LENDING_METHODS.has(value.method) ? [value.receiver, ...value.args] : []; + case "libCall": + return LENDING_LIB_FNS.has(value.fn) ? value.args.slice(0, 1) : []; + case "mapIntrinsic": + case "setIntrinsic": + return LENDING_KEY_METHODS.has(value.method) && stringKeyed(value) + ? value.args.slice(0, 1) + : []; + case "call": { + const lent = parameters.get(value.callee); + return lent ? value.args.filter((_, index) => lent.has(index)) : []; + } + default: + return []; + } +} + +interface LendingScan { + /** Slice operands of lending consumers, excluding any node also reached + * in an owning position. */ + scratch: Set; + /** Candidates read anywhere other than a lending position. */ + invalid: Set; + /** Direct callees, for the parameter fixpoint. */ + callees: Set; +} + +/** One pass over `fn`: which candidate bindings only lending consumers read + * (a slice initializer of a candidate local is not itself a read), and + * which slice operands those consumers lend. A switch discriminant is only + * compared with the case tests. */ +function scanLending( + fn: IrFunction, + candidates: ReadonlySet, + initializers: ReadonlyMap, + parameters: LentStringParameters, +): LendingScan { + const scan: LendingScan = { scratch: new Set(), invalid: new Set(), callees: new Set() }; + const owned = new Set(); + function lent(value: IrExpr): boolean { + if (value.kind === "varRef" && candidates.has(value.localId)) return true; + if (!isSlice(value)) return expr(value); + scan.scratch.add(value); + return everyExprChild(value, expr, stmt); + } + function expr(value: IrExpr): boolean { + if (isSlice(value)) owned.add(value); + if (value.kind === "varRef" && candidates.has(value.localId)) scan.invalid.add(value.localId); + if (value.kind === "closure" || value.kind === "classRef") + for (const id of value.captures ?? []) scan.invalid.add(id); + if (value.kind === "call") scan.callees.add(value.callee); + const operands = lentOperands(value, parameters); + if (operands.length === 0) return everyExprChild(value, expr, stmt); + return everyExprChild( + value, + (child) => (operands.includes(child) ? lent(child) : expr(child)), + stmt, + ); + } + function stmt(value: IrStmt): boolean { + // A candidate's single declaration initializes it with its slice. + if (value.kind === "varDecl" && value.init !== null && initializers.has(value.localId)) + return everyExprChild(value.init, expr, stmt); + if (value.kind === "switch" && value.disc.type.kind === "string") { + lent(value.disc); + for (const entry of value.cases) { + if (entry.test) expr(entry.test); + entry.body.forEach(stmt); + } + return true; + } + return everyStmtChild(value, expr, stmt); + } + fn.body.forEach(stmt); + for (const node of owned) scan.scratch.delete(node); + return scan; +} + +/** String parameters that every body reads only in lending positions, + * including lent arguments of other such parameters: a caller may pass a + * scratch slice to them. Start from every borrowed (unchanged) string + * parameter of a synchronous function and drop violations until nothing changes, so + * recursion through a lent parameter stays lent. A coroutine can keep its + * parameters past the caller's frame, so it never lends. */ +export function findLentStringParameters( + functions: ReadonlyMap, + lifetimes: CallLifetimes, +): Map> { + const lent = new Map>(); + for (const fn of functions.values()) { + if (fn.async || fn.generator) continue; + // Borrowed parameters are the unchanged ones nothing redeclares. + const stable = lifetimes.borrowed.get(fn.name); + const indexes = new Set(); + fn.params.forEach((param, index) => { + if (param.type.kind === "string" && stable?.has(index)) indexes.add(index); + }); + if (indexes.size > 0) lent.set(fn.name, indexes); + } + const callers = new Map>(); + const pending = new Set(lent.keys()); + while (pending.size > 0) { + const name = pending.values().next().value as string; + pending.delete(name); + const indexes = lent.get(name); + const fn = functions.get(name); + if (!indexes || !fn) continue; + const ids = new Set([...indexes].map((index) => fn.params[index]!.localId)); + const scan = scanLending(fn, ids, new Map(), lent); + for (const callee of scan.callees) { + let set = callers.get(callee); + if (!set) callers.set(callee, (set = new Set())); + set.add(name); + } + let changed = false; + for (const index of [...indexes]) { + if (!scan.invalid.has(fn.params[index]!.localId)) continue; + indexes.delete(index); + changed = true; + } + if (!changed) continue; + if (indexes.size === 0) lent.delete(name); + const affected = callers.get(name); + if (affected) for (const caller of affected) if (lent.has(caller)) pending.add(caller); + } + return lent; +} + +/** Slices whose every reader borrows them are written into a stack buffer + * instead of a heap allocation (scr_str_slice_scratch): a slice operand of + * a lending consumer, and the initializer of a stable local read only by + * lending consumers. The copy is ASCII, bounded, and immortal for retain and + * release, so it neither pins its source nor needs to be freed; longer or + * non-ASCII results keep the ordinary owned string. Each site owns its + * buffer: a site runs again only after its previous value is dead, since + * nothing can keep it. Coroutine frames do not keep stack buffers across a + * suspension, so they are left alone. */ +export function findScratchStringSlices( + fn: IrFunction, + lifetimes: CallLifetimes, + scalar: ReadonlyMap, + parameters: LentStringParameters = new Map(), +): Set { + if (fn.async || fn.generator) return new Set(); + const bindings = lifetimes.bindings.get(fn.name); + const locals = new Map(); + everyStmtList(fn.body, { + expr: () => true, + stmt: (stmt) => { + if ( + stmt.kind === "varDecl" && + bindings?.has(stmt.localId) && + !scalar.has(stmt.localId) && + stmt.init !== null && + isSlice(stmt.init) + ) + locals.set(stmt.localId, stmt.init); + return true; + }, + }); + const scan = scanLending(fn, new Set(locals.keys()), locals, parameters); + for (const [id, init] of locals) if (!scan.invalid.has(id)) scan.scratch.add(init); + return scan.scratch; +} + +/** Write a scratch slice into its own stack buffer. The result is owned: + * releasing the scratch copy does nothing, and a heap fallback is freed. */ +export function emitScratchStringSlice( + host: LlvmEmitterContext, + slice: Slice, + receiver: LlValue, + args: readonly LlValue[], +): LlValue { + const B = host.B; + const buffer = B.slot(); + B.entryAllocas.push(`${buffer} = alloca [${SCRATCH_SLICE_BYTES} x i8], align 8`); + host.declare( + `declare ptr @scr_str_slice_scratch(ptr, double, double, i1 zeroext, ptr, ${host.sizeType})`, + ); + const result = B.tmp(); + B.line( + `${result} = call ptr @scr_str_slice_scratch(ptr ${receiver.name}, double ${args[0]?.name ?? f64Lit(0)}, double ${args[1]?.name ?? F64_INF}, i1 ${slice.method === "substring" ? "true" : "false"}, ptr ${buffer}, ${host.sizeType} ${SCRATCH_SLICE_BYTES})`, + ); + return host.own({ name: result, type: slice.type }); +} diff --git a/packages/compiler/src/frontend/lowering/builtin-calls.ts b/packages/compiler/src/frontend/lowering/builtin-calls.ts index 037b3e5a7f..7bb5f3e624 100644 --- a/packages/compiler/src/frontend/lowering/builtin-calls.ts +++ b/packages/compiler/src/frontend/lowering/builtin-calls.ts @@ -62,8 +62,8 @@ export function lowerBuiltinCall( loc: SrcLoc, namespaceMember?: ts.MemberName, ): IrExpr { - if (bi.module === "child_process" && bi.member === "execFile") { - return lowerer.lowerExecFileCall(expr, loc); + if (bi.module === "child_process" && (bi.member === "execFile" || bi.member === "exec")) { + return lowerer.lowerExecFileCall(expr, loc, bi.member === "exec"); } if (bi.module === "events" && bi.member === "addAbortListener") { return lowerAddAbortListener(lowerer, expr, loc); diff --git a/packages/compiler/src/frontend/lowering/builtins/callable-aliases.ts b/packages/compiler/src/frontend/lowering/builtins/callable-aliases.ts index d4574b41ce..cf19b40030 100644 --- a/packages/compiler/src/frontend/lowering/builtins/callable-aliases.ts +++ b/packages/compiler/src/frontend/lowering/builtins/callable-aliases.ts @@ -4,6 +4,8 @@ import { isJsSourceFile } from "../../program.js"; import { builtinModuleFnOf } from "../surfaces.js"; import { registerHttpClientFnBinding } from "../lower-server.js"; import { isPromisifiedZlibMember } from "./compression.js"; +import { isPromisifiedCryptoMember } from "./crypto.js"; +import { isPromisifiedDnsMember } from "../lower-dgram.js"; /** Compile-time util.promisify projections. The immutable result binding * owns no runtime slot: calls rewrite to the target's promise lowering, @@ -157,12 +159,16 @@ export function registerPromisifiedBuiltinDecl( } : target?.module === "zlib" && isPromisifiedZlibMember(target.member) ? { kind: "promisified-zlib" as const, member: target.member } - : null; + : target?.module === "crypto" && isPromisifiedCryptoMember(target.member) + ? { kind: "promisified-crypto" as const, member: target.member } + : target?.module === "dns" && isPromisifiedDnsMember(target.member) + ? { kind: "promisified-dns" as const, member: target.member } + : null; if (!projection) { lowerer.noLowering( "util.promisify of this target", argNode ?? e, - "child_process.exec, child_process.execFile, setTimeout, setImmediate, stream.finished, stream.pipeline, the zlib callback one-shots and the fs callback functions with an fs/promises counterpart are the supported targets", + "child_process.exec, child_process.execFile, setTimeout, setImmediate, stream.finished, stream.pipeline, the zlib callback one-shots, the crypto callback functions (randomBytes, pbkdf2, scrypt, hkdf, randomFill, randomInt, generateKey), dns.lookup, dns.lookupService and the fs callback functions with an fs/promises counterpart are the supported targets", ); } const symbol = lowerer.checker.getSymbolAtLocation(nameNode); diff --git a/packages/compiler/src/frontend/lowering/builtins/child-process.ts b/packages/compiler/src/frontend/lowering/builtins/child-process.ts index 8c06a28df6..ecec14fc5b 100644 --- a/packages/compiler/src/frontend/lowering/builtins/child-process.ts +++ b/packages/compiler/src/frontend/lowering/builtins/child-process.ts @@ -725,35 +725,57 @@ export function lowerForkCall(lowerer: Lowerer, expr: ts.CallExpression, loc: Sr } /** `execFile(file[, args][, options], callback)` — the asynchronous callback - * slice. The child starts immediately with stdin/stdout/stderr piped; the - * runtime captures both outputs and invokes the error-first callback after - * settlement. The callback may ignore a suffix of `(error, stdout, + * slice. With `shell`, `exec(command[, options], callback)`: the command + * line runs with /bin/sh -c, as execSync runs it, and failure messages show + * the command line as Node's do. The child starts immediately with + * stdin/stdout/stderr piped; the runtime captures both outputs and invokes + * the error-first callback after settlement. The callback may ignore a suffix of `(error, stdout, * stderr)`, but every declared parameter must have the Node shape. Inline * `encoding: "utf8"` and a pure numeric `maxBuffer` are accepted; the latter * is not enforced by the growing native capture, as * with the synchronous and promisified capture forms. Other options remain * fenced until their lifecycle can share this asynchronous core. */ -export function lowerExecFileCall(lowerer: Lowerer, expr: ts.CallExpression, loc: SrcLoc): IrExpr { +export function lowerExecFileCall( + lowerer: Lowerer, + expr: ts.CallExpression, + loc: SrcLoc, + shell = false, +): IrExpr { + const what = shell ? "exec" : "execFile"; if ( expr.arguments.some(ts.isSpreadElement) || expr.arguments.length < 2 || - expr.arguments.length > 4 + expr.arguments.length > (shell ? 3 : 4) ) { lowerer.noLowering( - `execFile with ${expr.arguments.length} arguments`, + `${what} with ${expr.arguments.length} arguments`, expr, - "the supported callback forms are execFile(file, callback), execFile(file, args, callback), and execFile(file, args, { encoding: 'utf8', maxBuffer: N }, callback)", + shell + ? "the supported callback forms are exec(command, callback) and exec(command, { encoding: 'utf8', maxBuffer: N }, callback)" + : "the supported callback forms are execFile(file, callback), execFile(file, args, callback), and execFile(file, args, { encoding: 'utf8', maxBuffer: N }, callback)", ); } - const cmd = lowerer.lowerExprExpecting(expr.arguments[0]!, STRING); - const argsNode = expr.arguments.length >= 3 ? expr.arguments[1] : undefined; + const first = lowerer.lowerExprExpecting(expr.arguments[0]!, STRING); const callbackNode = expr.arguments[expr.arguments.length - 1]!; - const argv = lowerChildArgsArg(lowerer, argsNode, loc); - if (expr.arguments.length === 4) { - const options = expr.arguments[2]!; + // exec: /bin/sh -c , execSync's lowering (the runtime reads the + // command line back from argv[1] for failure messages); execFile: the + // file and its args list. + const cmd: IrExpr = shell ? { kind: "strLit", value: "/bin/sh", type: STRING, loc } : first; + const argv: IrExpr = shell + ? { + kind: "arrayLit", + elems: [{ kind: "strLit", value: "-c", type: STRING, loc }, first], + type: arrayOf(STRING), + loc, + } + : lowerChildArgsArg(lowerer, expr.arguments.length >= 3 ? expr.arguments[1] : undefined, loc); + const optionsNode = + expr.arguments.length === (shell ? 3 : 4) ? expr.arguments[shell ? 1 : 2] : undefined; + if (optionsNode) { + const options = optionsNode; if (!ts.isObjectLiteralExpression(options)) { lowerer.noLowering( - "execFile with a non-literal options argument", + `${what} with a non-literal options argument`, options, "pass encoding and maxBuffer in an inline object literal", ); @@ -774,7 +796,7 @@ export function lowerExecFileCall(lowerer: Lowerer, expr: ts.CallExpression, loc const member = optionMember(property); if (!member) lowerer.noLowering( - "execFile with this options shape", + `${what} with this options shape`, property, "use plain encoding and maxBuffer properties without spreads or computed keys", ); @@ -784,7 +806,7 @@ export function lowerExecFileCall(lowerer: Lowerer, expr: ts.CallExpression, loc (member.value.text !== "utf8" && member.value.text !== "utf-8") ) { lowerer.noLowering( - "execFile with a non-literal utf8 encoding", + `${what} with a non-literal utf8 encoding`, member.value, "pass the literal 'utf8' or 'utf-8' (other expressions might have side effects)", ); @@ -792,14 +814,14 @@ export function lowerExecFileCall(lowerer: Lowerer, expr: ts.CallExpression, loc } else if (member.name === "maxBuffer") { if (!pureNumber(member.value)) { lowerer.noLowering( - "execFile with a non-literal maxBuffer", + `${what} with a non-literal maxBuffer`, member.value, "pass a numeric literal or literal arithmetic (the native capture grows without enforcing this limit)", ); } } else { lowerer.noLowering( - `execFile option '${member.name}'`, + `${what} option '${member.name}'`, property, "only encoding: 'utf8' and a pure numeric maxBuffer are supported in the callback form", ); @@ -815,14 +837,14 @@ export function lowerExecFileCall(lowerer: Lowerer, expr: ts.CallExpression, loc lowerer.unsupported( "SC1090", callbackNode, - "execFile callbacks take (error), (error, stdout), (error, stdout, stderr), or no parameters", + `${what} callbacks take (error), (error, stdout), (error, stdout, stderr), or no parameters`, ); } if (callback.type.ret.kind !== "void") { lowerer.unsupported( "SC1090", callbackNode, - "execFile callbacks returning a value (make the callback body a block, or return nothing)", + `${what} callbacks returning a value (make the callback body a block, or return nothing)`, ); } const errorParam = callback.type.params[0]; @@ -837,7 +859,7 @@ export function lowerExecFileCall(lowerer: Lowerer, expr: ts.CallExpression, loc lowerer.unsupported( "SC1090", callbackNode, - `execFile callbacks whose first parameter is not 'Error | null' (got '${lowerer.fmt(errorParam)}')`, + `${what} callbacks whose first parameter is not 'Error | null' (got '${lowerer.fmt(errorParam)}')`, ); } } @@ -846,9 +868,15 @@ export function lowerExecFileCall(lowerer: Lowerer, expr: ts.CallExpression, loc lowerer.unsupported( "SC1090", callbackNode, - `execFile callbacks whose ${i === 1 ? "stdout" : "stderr"} parameter is not 'string'`, + `${what} callbacks whose ${i === 1 ? "stdout" : "stderr"} parameter is not 'string'`, ); } } - return { kind: "libCall", fn: "cp.execFile", args: [cmd, argv, callback], type: CHILD_T, loc }; + return { + kind: "libCall", + fn: "cp.execFile", + args: [cmd, argv, callback, boolLit(shell, loc)], + type: CHILD_T, + loc, + }; } diff --git a/packages/compiler/src/frontend/lowering/builtins/compression.ts b/packages/compiler/src/frontend/lowering/builtins/compression.ts index 9c47711c14..c9553c2f97 100644 --- a/packages/compiler/src/frontend/lowering/builtins/compression.ts +++ b/packages/compiler/src/frontend/lowering/builtins/compression.ts @@ -1,11 +1,12 @@ import { lowerBuiltinByteInput, errorFirstBytesCallback } from "./arguments.js"; -import { dynUndefinedExpr, numLit } from "../../../ir/build.js"; +import { dynUndefinedExpr, numLit, varRef } from "../../../ir/build.js"; import { InternalCompilerError } from "../../../errors.js"; import * as ts from "../../ts7/adapter.js"; import { type Lowerer } from "../lowerer.js"; import { locOf } from "../../program.js"; import { isNodeTypesPath } from "../../dts-paths.js"; import { + BOOL, BYTES_U8, DYN, F64, @@ -39,7 +40,8 @@ const ZLIB_CALLBACK_FNS: Readonly> = { unzip: "zlib.unzipCb", }; -const ZLIB_STREAM_T: IrType = { kind: "object", className: "%Transform" }; +const ZLIB_STREAM_CLASS = "%Transform"; +const ZLIB_STREAM_T: IrType = { kind: "object", className: ZLIB_STREAM_CLASS }; /** A zlib data argument, lowered once: Buffer/Uint8Array bytes, a string * (UTF-8 encoded here), or a checked-dynamic value (the JS lane's untyped @@ -130,6 +132,8 @@ export function zlibStreamModeOf( symbol: ts.Symbol | null | undefined, ): number | null { if (!symbol) return null; + const aliasMode = zlibClassAliasModeOf(lowerer, symbol, 0); + if (aliasMode !== undefined) return aliasMode; const target = symbol.flags & ts.SymbolFlags.Alias ? lowerer.checker.getAliasedSymbol(symbol) : symbol; const mode = ZLIB_STREAM_MODES.get(target.name); @@ -494,3 +498,145 @@ export function lowerZlibModuleCall( } throw new InternalCompilerError(`unhandled lowered zlib member ${bi.member}`); } + +/** The zlib stream class an expression names (`zlib.Gzip`, an imported + * `Gzip`, a `const C = zlib.Gzip` alias), as its node_zlib_mode, or null. */ +export function zlibClassModeOfExpr(lowerer: Lowerer, expr: ts.Expression): number | null { + let e = expr; + while (ts.isParenthesizedExpression(e)) e = e.expression; + if (ts.isIdentifier(e)) return zlibStreamModeOf(lowerer, lowerer.checker.getSymbolAtLocation(e)); + if (ts.isPropertyAccessExpression(e) && !e.questionDotToken) { + return zlibStreamModeOf(lowerer, lowerer.checker.getSymbolAtLocation(e.name)); + } + return null; +} + +/** The class name of a zlib stream mode (Node's constructor name). */ +export function zlibClassName(mode: number): string { + for (const [name, m] of ZLIB_STREAM_MODES) if (m === mode) return name; + return "Zlib"; +} + +/** The mode of a `const C = zlib.Gzip` binding (or an alias of such a + * binding), null when the symbol is a const binding of something else, + * undefined when it is not a const binding. Resolved from the declaration, + * so uses lowered before the declaration statement see the same answer. */ +function zlibClassAliasModeOf( + lowerer: Lowerer, + symbol: ts.Symbol, + depth: number, +): number | null | undefined { + const cached = lowerer.zlibClassAliases.get(symbol); + if (cached !== undefined) return cached; + if ((symbol.flags & ts.SymbolFlags.BlockScopedVariable) === 0) return undefined; + const decl = lowerer.checker.valueDeclarationOf(symbol); + if (decl === undefined || !ts.isVariableDeclaration(decl) || !ts.isIdentifier(decl.name)) { + return undefined; + } + // const only: a var or let binding could be reassigned + const list = decl.parent; + if (!ts.isVariableDeclarationList(list) || (list.flags & ts.NodeFlags.Const) === 0) { + return undefined; + } + let init = decl.initializer; + while (init !== undefined && ts.isParenthesizedExpression(init)) init = init.expression; + if (init === undefined || depth > 16) return null; + let mode: number | null = null; + if (ts.isIdentifier(init)) { + const next = lowerer.checker.getSymbolAtLocation(init); + if (next !== undefined) { + const nested = zlibClassAliasModeOf(lowerer, next, depth + 1); + mode = nested === undefined ? zlibStreamModeOf(lowerer, next) : nested; + } + } else if (ts.isPropertyAccessExpression(init) && !init.questionDotToken) { + mode = zlibStreamModeOf(lowerer, lowerer.checker.getSymbolAtLocation(init.name)); + } + if (mode !== null) lowerer.zlibClassAliases.set(symbol, mode); + return mode; +} + +/** True for `const C = zlib.Gzip` (or an alias of an alias): a + * compile-time zlib class alias that owns no runtime slot, so the + * statement lowering skips it. */ +export function registerZlibClassAlias( + lowerer: Lowerer, + nameNode: ts.Node, + init: ts.Expression | undefined, +): boolean { + if (!ts.isIdentifier(nameNode) || init === undefined) return false; + const symbol = lowerer.checker.getSymbolAtLocation(nameNode); + return symbol !== undefined && zlibClassAliasOf(lowerer, symbol) !== null; +} + +/** The mode of a `const C = zlib.Gzip` alias symbol, or null. */ +export function zlibClassAliasOf(lowerer: Lowerer, symbol: ts.Symbol): number | null { + return zlibClassAliasModeOf(lowerer, symbol, 0) ?? null; +} + +/** `value instanceof zlib.Gzip` (any spelling of the class, aliases + * included). zlib streams are runtime Transforms: a Transform operand asks + * the runtime for the stream's mode, and an operand typed as one of + * Transform's bases (Duplex, Readable, EventEmitter) is first tested for a + * Transform. Any other static type answers false after the operand + * evaluates. */ +export function lowerZlibInstanceOf( + lowerer: Lowerer, + expr: ts.BinaryExpression, + mode: number, + loc: SrcLoc, +): IrExpr { + const value = lowerer.lowerExpr(expr.left); + const type = value.type; + const holdsStream = (className: string): boolean => + className === ZLIB_STREAM_CLASS || lowerer.isSubclassOf(ZLIB_STREAM_CLASS, className); + const arms = type.kind === "union" ? lowerer.unions.get(type.unionId)?.arms : undefined; + if ( + type.kind === "dyn" || + arms?.some((arm) => arm.kind === "object" && holdsStream(arm.className)) + ) { + lowerer.unsupported( + "SC1090", + expr, + `'instanceof zlib.${zlibClassName(mode)}' on an untyped or union value (give the operand a stream type first)`, + ); + } + if (type.kind === "object" && holdsStream(type.className)) { + const isMode = (stream: IrExpr): IrExpr => ({ + kind: "libCall", + fn: "zlib.streamIs", + args: [stream, numLit(mode, loc)], + type: BOOL, + loc, + }); + if (type.className === ZLIB_STREAM_CLASS) return isMode(value); + const local = lowerer.declareHiddenLocal("%zlibTest", type); + const ref = varRef(local.id, type, loc); + return { + kind: "seqExpr", + stmts: [{ kind: "varDecl", localId: local.id, init: value, loc }], + result: { + kind: "logical", + op: "&&", + left: { + kind: "instanceOf", + value: ref, + className: ZLIB_STREAM_CLASS, + type: BOOL, + loc, + }, + right: isMode({ kind: "downcast", value: ref, type: ZLIB_STREAM_T, loc }), + type: BOOL, + loc, + }, + type: BOOL, + loc, + }; + } + return { + kind: "seqExpr", + stmts: [{ kind: "exprStmt", expr: value, loc }], + result: { kind: "boolLit", value: false, type: BOOL, loc }, + type: BOOL, + loc, + }; +} diff --git a/packages/compiler/src/frontend/lowering/builtins/crypto.ts b/packages/compiler/src/frontend/lowering/builtins/crypto.ts index 2202088fae..a492a0da73 100644 --- a/packages/compiler/src/frontend/lowering/builtins/crypto.ts +++ b/packages/compiler/src/frontend/lowering/builtins/crypto.ts @@ -15,6 +15,7 @@ import { F64, type IrExpr, type IrLibFn, + type IrType, type IrStmt, STRING, type SrcLoc, @@ -1090,3 +1091,176 @@ export function lowerCryptoHashMethodCall( lowerer.checker.getSymbolAtLocation(access.name), ); } + +/** The crypto callback functions util.promisify converts. */ +const PROMISIFIED_CRYPTO_ARITY: Readonly> = { + randomBytes: [1, 1], + pbkdf2: [5, 5], + scrypt: [3, 4], + hkdf: [5, 5], + randomFill: [1, 3], + randomInt: [1, 2], + generateKey: [1, 2], +}; + +export function isPromisifiedCryptoMember(member: string): boolean { + return PROMISIFIED_CRYPTO_ARITY[member] !== undefined; +} + +/** A call through `promisify(crypto.randomBytes)` and the other callback + * functions: the callback form runs with a callback that settles the + * returned promise, so the value or error arrives where the callback + * would run. An argument error rejects the promise instead of throwing, + * as the throw inside Node's promisify wrapper does. The promise's value + * type follows the declared result: a Buffer or the filled view, or a + * checked-dynamic value where the declarations give `unknown`. */ +export function lowerPromisifiedCryptoCall( + lowerer: Lowerer, + expr: ts.CallExpression, + member: string, + loc: SrcLoc, +): IrExpr { + const args = expr.arguments; + if (args.some(ts.isSpreadElement)) { + lowerer.noLowering(`the promisified crypto.${member} with spread arguments`, expr); + } + const [min, max] = PROMISIFIED_CRYPTO_ARITY[member]!; + if (args.length < min || args.length > max) { + lowerer.noLowering( + `the promisified crypto.${member} with ${args.length} arguments`, + expr, + `the promisified form takes the callback form's arguments without the callback`, + ); + } + const declared = lowerer.mapTypeOf(lowerer.typeOf(expr)); + const inner = declared?.kind === "promise" ? declared.inner : null; + const promise = ( + fn: IrLibFn, + values: IrExpr[], + accepts: (t: IrType) => boolean, + what: string, + ): IrExpr => { + if (inner === null || !accepts(inner)) { + lowerer.noLowering( + `the promisified crypto.${member} with a '${declared ? lowerer.fmt(declared) : "?"}' result`, + expr, + `the promise's value type must be ${what} or unknown`, + ); + } + return { kind: "libCall", fn, args: values, type: { kind: "promise", inner }, loc }; + }; + const bufferOrDyn = (t: IrType) => t.kind === "dyn" || (t.kind === "bytes" && t.elem === "u8"); + if (member === "randomBytes") { + return promise( + "crypto.randomBytesPromise", + [lowerer.lowerExprExpecting(args[0]!, F64)], + bufferOrDyn, + "Buffer", + ); + } + if (member === "pbkdf2") { + return promise( + "crypto.pbkdf2Promise", + [ + lowerBuiltinByteInput(lowerer, args[0]!, locOf(args[0]!), "crypto"), + lowerBuiltinByteInput(lowerer, args[1]!, locOf(args[1]!), "crypto"), + lowerer.lowerExprExpecting(args[2]!, F64), + lowerer.lowerExprExpecting(args[3]!, F64), + cryptoAlgorithm(lowerer, args[4]!), + ], + bufferOrDyn, + "Buffer", + ); + } + if (member === "scrypt") { + return promise( + "crypto.scryptPromise", + [ + cryptoKdfInput(lowerer, args[0]!), + cryptoKdfInput(lowerer, args[1]!), + lowerer.lowerExprExpecting(args[2]!, F64), + args[3] + ? lowerer.coerceInto(args[3], lowerer.lowerExpr(args[3]), DYN) + : dynUndefinedExpr(loc), + ], + bufferOrDyn, + "Buffer", + ); + } + if (member === "hkdf") { + return promise( + "crypto.hkdfPromise", + [ + cryptoAlgorithm(lowerer, args[0]!), + isKeyObjectValue(lowerer, args[1]!) + ? { + kind: "libCall", + fn: "crypto.keyObjectInput", + args: [lowerer.lowerExprExpecting(args[1]!, DYN)], + type: BYTES_U8, + loc, + } + : cryptoKdfInput(lowerer, args[1]!), + cryptoKdfInput(lowerer, args[2]!), + cryptoKdfInput(lowerer, args[3]!), + lowerer.lowerExprExpecting(args[4]!, F64), + ], + (t) => t.kind === "dyn", + "ArrayBuffer", + ); + } + if (member === "randomFill") { + // Node's randomFill counts the arguments before the callback. + const form: IrExpr = { kind: "numLit", value: args.length, type: F64, loc }; + const buffer = lowerer.lowerExpr(args[0]!); + if (buffer.type.kind === "bytes" && cryptoNumberArgs(lowerer, args.slice(1))) { + const view = buffer.type; + return promise( + "crypto.randomFillPromise", + [ + buffer, + args.length >= 2 + ? lowerer.lowerExprExpecting(args[1]!, F64) + : { kind: "numLit", value: 0, type: F64, loc }, + args.length === 3 + ? lowerer.lowerExprExpecting(args[2]!, F64) + : { kind: "numLit", value: 0, type: F64, loc }, + form, + ], + (t) => t.kind === "dyn" || (t.kind === "bytes" && t.elem === view.elem), + "the buffer's own view", + ); + } + return promise( + "crypto.randomFillPromiseDyn", + [ + lowerer.coerceInto(args[0]!, buffer, DYN), + dynArg(lowerer, args[1], loc), + dynArg(lowerer, args[2], loc), + form, + ], + (t) => t.kind === "dyn", + "the buffer's own type", + ); + } + if (member === "randomInt") { + return promise( + "crypto.randomIntPromise", + [ + args.length === 2 + ? lowerer.lowerExprExpecting(args[0]!, F64) + : { kind: "numLit", value: 0, type: F64, loc }, + lowerer.lowerExprExpecting(args[args.length - 1]!, F64), + { kind: "numLit", value: args.length === 2 ? 1 : 0, type: F64, loc }, + ], + (t) => t.kind === "dyn" || t.kind === "f64", + "number", + ); + } + return promise( + "crypto.generateKeyPromise", + [lowerer.lowerExprExpecting(args[0]!, DYN), dynArg(lowerer, args[1], loc)], + (t) => t.kind === "dyn", + "KeyObject", + ); +} diff --git a/packages/compiler/src/frontend/lowering/builtins/errors.ts b/packages/compiler/src/frontend/lowering/builtins/errors.ts index 5947f37148..50a97cead7 100644 --- a/packages/compiler/src/frontend/lowering/builtins/errors.ts +++ b/packages/compiler/src/frontend/lowering/builtins/errors.ts @@ -11,6 +11,28 @@ const ASSERTION_ERROR_PROPERTIES: ReadonlySet = new Set([ "diff", ]); +/** True for child_process.ExecException, exec's callback error. Its + * `code` is the child's numeric exit status (null after a signal), which + * the runtime error's string code slot does not hold, so reads, `in` tests + * and typeof tests of `code` refuse instead of answering from the slot. */ +export function isExecExceptionType(lowerer: Lowerer, t: ts.Type): boolean { + const sym = t.getSymbol(); + return ( + sym?.name === "ExecException" && + lowerer.checker + .declarationsOf(sym) + .some((d) => ts.isInterfaceDeclaration(d) && lowerer.isStdlibFile(d.getSourceFile())) + ); +} + +export function refuseExecExceptionCode(lowerer: Lowerer, node: ts.Node): never { + return lowerer.noLowering( + "ExecException.code", + node, + "spawn reports the exit status through its 'close' event", + ); +} + /** `.code` and `.cause` on an error-hierarchy receiver. The latter reads * the hidden dyn slot installed by constructor options. The former is NodeJS.ErrnoException's * member (the fallback declares the same shape): the runtime Error's @@ -122,6 +144,9 @@ export function lowerErrorCodeProperty( !(recvT.className === "%Error" && lowerer.typeOf(expr.expression).isIntersectionType()) ) return null; + if (expr.name.text === "code" && isExecExceptionType(lowerer, lowerer.typeOf(expr.expression))) { + refuseExecExceptionCode(lowerer, expr); + } const rawReceiver = lowerer.lowerExpr(expr.expression); const receiver = rawReceiver.type.kind === "dyn" diff --git a/packages/compiler/src/frontend/lowering/builtins/module-bindings.ts b/packages/compiler/src/frontend/lowering/builtins/module-bindings.ts index 3fa6eb0c9d..9c3a8c0cfc 100644 --- a/packages/compiler/src/frontend/lowering/builtins/module-bindings.ts +++ b/packages/compiler/src/frontend/lowering/builtins/module-bindings.ts @@ -34,21 +34,48 @@ export function builtinImportOf( // binding (`const { readFileSync } = require("fs")`, renames via // `{ readFileSync: rf }`) keys the same tables. if (ts.isBindingElement(decl) && decl.name !== undefined && ts.isIdentifier(decl.name)) { - const varDecl = decl.parent?.parent; + // A NESTED pattern names a member of a sub-module (`const { types: { + // isPromise } } = require("util")`): util.types IS the util/types + // module object, so each level composes the module name. + const path: string[] = []; + let pattern: ts.Node | undefined = decl.parent; + let varDecl = pattern?.parent; + while ( + pattern !== undefined && + ts.isObjectBindingPattern(pattern) && + varDecl !== undefined && + ts.isBindingElement(varDecl) + ) { + const key = varDecl.propertyName; + if (varDecl.initializer !== undefined || key === undefined || !ts.isIdentifier(key)) { + return null; + } + path.unshift(key.text); + pattern = varDecl.parent; + varDecl = pattern?.parent; + } if ( - ts.isObjectBindingPattern(decl.parent) && + pattern !== undefined && + ts.isObjectBindingPattern(pattern) && + varDecl !== undefined && ts.isVariableDeclaration(varDecl) && varDecl.initializer !== undefined ) { const spec = requireSpecOf(varDecl.initializer); - const module = + let module = spec !== null ? canonicalBuiltinModule(spec) - : // The one-hop alias twin: `const crypto = require('crypto'); - // const { createSign } = crypto;` — test/common's idiom. The - // destructure re-binds module members under local names, so the - // bindings key the same tables as the direct-require form. - builtinNamespaceDestructureModuleOf(lowerer, varDecl); + : path.length > 0 + ? null + : // The one-hop alias twin: `const crypto = require('crypto'); + // const { createSign } = crypto;` — test/common's idiom. The + // destructure re-binds module members under local names, so the + // bindings key the same tables as the direct-require form. + builtinNamespaceDestructureModuleOf(lowerer, varDecl); + for (const key of path) { + if (module === null) break; + module = canonicalBuiltinModule(`${module}/${key}`); + } if (module === null) return null; const member = decl.propertyName && ts.isIdentifier(decl.propertyName) @@ -114,6 +141,24 @@ export function builtinImportOf( return null; } +/** The ES module binding kind of `ident`: "namespace" for a namespace + * import (`import * as events from "node:events"`), "named" for a named + * import (`import { defaultMaxListeners } from "node:events"`, also through + * a re-exporting module), null otherwise. Namespace members and named + * imports of a builtin read its ES module namespace, which holds copies of + * the module's exports (module.syncBuiltinESMExports refreshes them), where + * default imports and CommonJS bindings read the module object itself. */ +export function esmImportBindingKind( + lowerer: Lowerer, + ident: ts.Identifier, +): "namespace" | "named" | null { + const symbol = lowerer.checker.getSymbolAtLocation(ident); + const decl = symbol ? lowerer.checker.declarationsOf(symbol)[0] : undefined; + if (decl === undefined) return null; + if (ts.isNamespaceImport(decl)) return "namespace"; + return ts.isImportSpecifier(decl) ? "named" : null; +} + /** True for `const = require("").` — the * member-binding require import builtinImportOf resolves. Both * declaration walks (lowerVarDecl, collectGlobals) skip these: like a diff --git a/packages/compiler/src/frontend/lowering/builtins/module-resolution.ts b/packages/compiler/src/frontend/lowering/builtins/module-resolution.ts index f1b36e3c63..5fcf355418 100644 --- a/packages/compiler/src/frontend/lowering/builtins/module-resolution.ts +++ b/packages/compiler/src/frontend/lowering/builtins/module-resolution.ts @@ -42,9 +42,10 @@ import { stripTypeCasts } from "./arguments.js"; /** node:module's two compiler-only calls. isBuiltin compares one evaluated * string against the pinned Node 24 list (bare builtins also accept their * node: spelling; prefix-only entries do not gain a bare alias). - * syncBuiltinESMExports is observably a no-op inside the static surface: - * builtin exports cannot be mutated, so its only supported behavior is - * evaluating no arguments and returning undefined. */ + * syncBuiltinESMExports takes no arguments and returns undefined; as a + * statement it refreshes the events module's ES module namespace copy of + * defaultMaxListeners (lowerExprStatement), the one builtin export a program + * can change. */ export function lowerNodeModuleCall( lowerer: Lowerer, expr: ts.CallExpression, @@ -64,7 +65,7 @@ export function lowerNodeModuleCall( lowerer.noLowering( "module.syncBuiltinESMExports used as a value", expr, - "call syncBuiltinESMExports() as its own statement; the supported static effect is a no-op", + "call syncBuiltinESMExports() as its own statement", ); } return { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc }; diff --git a/packages/compiler/src/frontend/lowering/class-method-mutations.ts b/packages/compiler/src/frontend/lowering/class-method-mutations.ts index 8e7b1f1525..75abeaf57f 100644 --- a/packages/compiler/src/frontend/lowering/class-method-mutations.ts +++ b/packages/compiler/src/frontend/lowering/class-method-mutations.ts @@ -288,7 +288,12 @@ export function collectClassMethodMutations( ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node)) && - ts.isComputedPropertyName(node.name) + ts.isComputedPropertyName(node.name) && + // A computed key in an object literal defines a property of the new + // object, which is not a class instance. Literals passed to + // Object.assign or Object.defineProperties are judged with their + // receiver below. + !ts.isObjectLiteralExpression(node.parent) ) { markKey(node.name.expression, node); } diff --git a/packages/compiler/src/frontend/lowering/construction-escapes.ts b/packages/compiler/src/frontend/lowering/construction-escapes.ts index 484920a1c2..a1626f12d5 100644 --- a/packages/compiler/src/frontend/lowering/construction-escapes.ts +++ b/packages/compiler/src/frontend/lowering/construction-escapes.ts @@ -124,6 +124,15 @@ export function constructionEscape(inputs: ConstructionInputs): ConstructionEsca continue; } if (ts.isExpressionStatement(stmt) && !exposes(stmt.expression, depth)) continue; + // A check that only reads assigned fields and throws or does nothing + // with the receiver (`if (this.size < 0) throw new RangeError()`) + // does not expose the instance; assignments in its branches are not + // definite and still count as exposures. + if ( + (ts.isIfStatement(stmt) || ts.isThrowStatement(stmt) || ts.isBlock(stmt)) && + !exposes(stmt, depth) + ) + continue; return false; } return true; diff --git a/packages/compiler/src/frontend/lowering/containers/indexed-objects.ts b/packages/compiler/src/frontend/lowering/containers/indexed-objects.ts index e917ee75f4..cca4bd51b4 100644 --- a/packages/compiler/src/frontend/lowering/containers/indexed-objects.ts +++ b/packages/compiler/src/frontend/lowering/containers/indexed-objects.ts @@ -1,3 +1,4 @@ +import { nullProtoRecordShape, recordNullProtoTest } from "../null-prototype.js"; import { countedFor, numLit, varRef } from "../../../ir/build.js"; import { objectEnumerationReceiver } from "../object-enumeration-receiver.js"; import { InternalCompilerError } from "../../../errors.js"; @@ -852,50 +853,57 @@ export function lowerRecordOvfCaptureHelper( lift === "dyn" ? { kind: "dynFrom", value: v, type: DYN, loc } : lowerer.applyWidthLift(lift, v, tIv, loc); - const body: IrStmt[] = [ - { - kind: "varDecl", - localId: "out.0", - init: { - kind: "recordLit", - fields: inits.map((d) => ({ - name: d.name, - value: - d.kind === "direct" - ? lowerer.presenceKeepingCopy( - sRef, - fromId, - d.name, - lowerer.applyWidthLift( - d.lift, - { - kind: "recordGet", - obj: sRef, - shapeId: fromId, - field: d.name, - type: d.src, - loc, - }, - to.fields.find((f) => f.name === d.name)!.type, - loc, - ), + const fresh: IrExpr & { kind: "recordLit" } = { + kind: "recordLit", + fields: inits.map((d) => ({ + name: d.name, + value: + d.kind === "direct" + ? lowerer.presenceKeepingCopy( + sRef, + fromId, + d.name, + lowerer.applyWidthLift( + d.lift, + { + kind: "recordGet", + obj: sRef, + shapeId: fromId, + field: d.name, + type: d.src, loc, - ) - : ({ - // No source property of this name: the field starts - // absent (an overflow write below can supply it). - kind: "fieldAbsent", - unionId: d.unionId, - type: to.fields.find((f) => f.name === d.name)!.type, - loc, - } satisfies IrExpr), - })), - type: toT, - loc, - }, - loc, - }, - ]; + }, + to.fields.find((f) => f.name === d.name)!.type, + loc, + ), + loc, + ) + : ({ + // No source property of this name: the field starts + // absent (an overflow write below can supply it). + kind: "fieldAbsent", + unionId: d.unionId, + type: to.fields.find((f) => f.name === d.name)!.type, + loc, + } satisfies IrExpr), + })), + type: toT, + loc, + }; + // The source and its reshape are one object in JavaScript, so a + // null-prototype source (a match's groups, Object.groupBy) keeps it. + const init: IrExpr = + nullProtoRecordShape(lowerer, fromT) && nullProtoRecordShape(lowerer, toT) + ? { + kind: "ternary", + cond: recordNullProtoTest(sRef, fromId, loc), + then: { ...fresh, nullProto: true }, + else_: fresh, + type: toT, + loc, + } + : fresh; + const body: IrStmt[] = [{ kind: "varDecl", localId: "out.0", init, loc }]; const fieldStmts = new Map(); // Source declared fields in declaration order, skipping unset // optionals (stance 37) and the direct-initialized (consumed) names. diff --git a/packages/compiler/src/frontend/lowering/expressions/evaluation-safety.ts b/packages/compiler/src/frontend/lowering/expressions/evaluation-safety.ts index 791df1340d..0c4da61be0 100644 --- a/packages/compiler/src/frontend/lowering/expressions/evaluation-safety.ts +++ b/packages/compiler/src/frontend/lowering/expressions/evaluation-safety.ts @@ -6,9 +6,10 @@ import type { IrExpr } from "../../../ir/ir.js"; export function isSafeToRepeat(expr: IrExpr): boolean { if (expr.kind === "varRef") return true; if (expr.kind === "recordGet" || expr.kind === "fieldGet") return isSafeToRepeat(expr.obj); - // The trust-the-checker narrowing bridges over a pure read: extraction - // and downcast are reads too (the +1 on ref payloads is RC bookkeeping, - // not an observable effect — each emission owns its own copy). + // The narrowing bridges over a pure read: extraction and downcast are + // reads too (their guards only compare, and the +1 on ref payloads is RC + // bookkeeping, not an observable effect — each emission owns its own + // copy). if (expr.kind === "unionNarrow" || expr.kind === "downcast") return isSafeToRepeat(expr.value); return false; } diff --git a/packages/compiler/src/frontend/lowering/json-require.ts b/packages/compiler/src/frontend/lowering/json-require.ts new file mode 100644 index 0000000000..7c688dcda0 --- /dev/null +++ b/packages/compiler/src/frontend/lowering/json-require.ts @@ -0,0 +1,140 @@ +/* CommonJS require() of JSON modules. Node parses the document at the + * first require and caches the resulting object: every later require, from + * any module, answers that same object, so a mutation through one requirer + * is visible to all of them. The document is data known at build time, so + * the object bakes from the parsed text (comptimeValueToIr, as JSON import + * declarations do) into one global per document. The parse has no + * observable effect, so each requiring module creates the object in its + * %init prelude unless an earlier module already did, and every require + * call of the document reads the shared global. A require binding of a + * JSON module is ordinary storage holding that object — destructuring + * copies its properties at the require, exactly like Node. ES-module JSON + * imports keep their own record (Node's ESM loader caches separately). */ +import * as ts from "../ts7/adapter.js"; +import type { Lowerer } from "./lowerer.js"; +import { PoisonError } from "./lowerer.js"; +import type { FileParts } from "./lower-modules.js"; +import { isCjsJsFile, locOf, requireSpecOf, resolveImport } from "../program.js"; +import { createRequireCalleeFileOf } from "./builtins/module-bindings.js"; +import { invalidJsonModuleDiag } from "../../diagnostics/diagnostic.js"; +import { BOOL, type IrExpr, type IrGlobal, type IrType } from "../../ir/ir.js"; + +/** One required JSON document: its shared global, the run-once flag, and + * the parsed value with the type it bakes to. */ +export interface JsonRequireModule { + global: IrGlobal; + loadedId: string; + parsed: unknown; + type: IrType; +} + +/** The JSON document a CommonJS `require("…")` call loads, or null. */ +export function jsonRequireTarget( + lowerer: Lowerer, + call: ts.Expression | undefined, +): ts.SourceFile | null { + if (call === undefined || !ts.isCallExpression(call)) return null; + const spec = requireSpecOf(call); + if (spec === null || !ts.isIdentifier(call.expression)) return null; + const sf = call.getSourceFile(); + if (!isCjsJsFile(sf, lowerer.program)) return null; + if (createRequireCalleeFileOf(lowerer, call.expression) !== null) return null; + const target = resolveImport(lowerer.program, sf, spec, "require"); + return target !== null && target.fileName.endsWith(".json") ? target : null; +} + +/** A declaration whose initializer is a require() of a JSON document. */ +export function isJsonRequireDecl(lowerer: Lowerer, decl: ts.VariableDeclaration): boolean { + return jsonRequireTarget(lowerer, decl.initializer) !== null; +} + +/** Registers the shared global of every JSON document a CommonJS module of + * the program requires, and the guarded creation in each requiring + * module's %init prelude. */ +export function collectJsonRequires(lowerer: Lowerer, parts: FileParts[]): void { + for (const fp of parts) { + const sf = fp.sf; + if (sf.isDeclarationFile || !isCjsJsFile(sf, lowerer.program)) continue; + if (!sf.text.includes(".json")) continue; + const calls: ts.CallExpression[] = []; + ts.walkPreorder(sf, (node) => { + if (ts.isCallExpression(node) && requireSpecOf(node) !== null) calls.push(node); + return undefined; + }); + const created = new Set(); + for (const call of calls) { + const jsonSf = jsonRequireTarget(lowerer, call); + if (jsonSf === null) continue; + try { + let mod = lowerer.jsonRequireModules.get(jsonSf.fileName); + if (mod === undefined) { + const tsType = lowerer.typeOf(call); + const mapped = lowerer.mapTypeOf(tsType); + if (!mapped || !lowerer.comptimeBakeable(mapped)) lowerer.badType(call, tsType); + let parsed: unknown; + try { + parsed = JSON.parse(jsonSf.text); + } catch (e) { + lowerer.pushDiag( + invalidJsonModuleDiag( + jsonSf.fileName, + e instanceof Error ? e.message : String(e), + locOf(call), + ), + ); + throw new PoisonError(); + } + const id = `%g.jsonRequire.${lowerer.jsonRequireModules.size}`; + const global: IrGlobal = { id, name: "%json", type: mapped, mutable: true }; + const loadedId = `${id}%loaded`; + lowerer.globalsList.push(global, { + id: loadedId, + name: "%loaded", + type: BOOL, + mutable: true, + }); + mod = { global, loadedId, parsed, type: mapped }; + lowerer.jsonRequireModules.set(jsonSf.fileName, mod); + } + lowerer.jsonRequireCalls.set(call, mod); + if (created.has(mod)) continue; + created.add(mod); + const loc = locOf(call); + const value = lowerer.comptimeValueToIr(mod.parsed, mod.type, "$", call); + const actions = lowerer.jsonInitActions.get(sf) ?? []; + lowerer.jsonInitActions.set(sf, actions); + actions.push({ + kind: "if", + cond: { + kind: "unary", + op: "!", + operand: { kind: "varRef", localId: mod.loadedId, type: BOOL, loc }, + type: BOOL, + loc, + }, + then: [ + { + kind: "assign", + localId: mod.loadedId, + value: { kind: "boolLit", value: true, type: BOOL, loc }, + loc, + }, + { kind: "assign", localId: mod.global.id, value, loc }, + ], + else_: null, + loc, + }); + } catch (e) { + if (!(e instanceof PoisonError)) throw e; + // diagnostic already recorded + } + } + } +} + +/** A require() call of a JSON document: the shared object. */ +export function lowerJsonRequireCall(lowerer: Lowerer, call: ts.CallExpression): IrExpr | null { + const mod = lowerer.jsonRequireCalls.get(call); + if (mod === undefined) return null; + return { kind: "varRef", localId: mod.global.id, type: mod.global.type, loc: locOf(call) }; +} diff --git a/packages/compiler/src/frontend/lowering/lower-calls.ts b/packages/compiler/src/frontend/lowering/lower-calls.ts index 56c639397b..3260c0484c 100644 --- a/packages/compiler/src/frontend/lowering/lower-calls.ts +++ b/packages/compiler/src/frontend/lowering/lower-calls.ts @@ -59,6 +59,7 @@ import { } from "../../ir/ir.js"; import { lowerProtocolMethodCall } from "./class-protocol-dispatch.js"; import { isJsSourceFile, locOf, requireSpecOf } from "../program.js"; +import { lowerJsonRequireCall } from "./json-require.js"; import { lowerAssertInstanceCall } from "./assert-instances.js"; import { isGenericCallableMemberType, typeKey } from "../type-mapper.js"; import { dynFallbackType, jsFuncNameOf, newFnCtx, staticImportNamespaceType } from "./lowerer.js"; @@ -105,8 +106,13 @@ import { lowerImportMetaResolveCall, lowerRequireResolveCall, } from "./builtins/module-resolution.js"; -import { lowerCipherMethodCall, lowerCryptoHashMethodCall } from "./builtins/crypto.js"; +import { + lowerCipherMethodCall, + lowerCryptoHashMethodCall, + lowerPromisifiedCryptoCall, +} from "./builtins/crypto.js"; import { lowerErrorIsErrorCall } from "./builtins/errors.js"; +import { lowerPromisifiedDnsCall } from "./lower-dgram.js"; import { lowerDisposableStackUseCall } from "./builtins/disposable-stack.js"; import { lowerDirentMethodCall, @@ -125,6 +131,7 @@ import { recordMayKeepSource, throughRecordSource } from "./record-sources.js"; import { lowerReflectCall } from "./builtins/reflection.js"; import { lowerAbsenceProbe, + v8CalleeText, lowerOptionalNumber, lowerPromiseAllTupleCall, lowerPromiseRejectCall, @@ -223,6 +230,10 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr { if (object) return object; } + // require() of a JSON document: the shared parsed object. + const json = lowerJsonRequireCall(lowerer, expr); + if (json) return json; + // A call whose chain ROOTS at an ambient-undefined name (`declare // const value: Y | undefined; value?.foo("a")`, `declare function // chain...; chain(o).mapValues(f).value()`, a trap binding's read): @@ -801,6 +812,12 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr { if (projection?.kind === "promisified-zlib") { return lowerPromisifiedZlibCall(lowerer, expr, projection.member, loc); } + if (projection?.kind === "promisified-crypto") { + return lowerPromisifiedCryptoCall(lowerer, expr, projection.member, loc); + } + if (projection?.kind === "promisified-dns") { + return lowerPromisifiedDnsCall(lowerer, expr, projection.member, loc); + } if (projection?.kind === "promisified-builtin") { return lowerBuiltinCall( lowerer, @@ -1433,7 +1450,7 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr { kind: "dynCall", callee: staticField, receiver: lowerer.lowerExprExpecting(expr.expression.expression, DYN), - calleeName: expr.expression.getText(), + calleeName: v8CalleeText(expr.expression), args: expr.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), type: DYN, loc: locOf(expr), @@ -1452,7 +1469,7 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr { return { kind: "dynCall", callee, - calleeName: expr.expression.getText(), + calleeName: v8CalleeText(expr.expression), args, type: DYN, loc: locOf(expr), @@ -1705,7 +1722,7 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr { return { kind: "dynCall", callee: lowerer.coerceToExpected(callee, DYN), - calleeName: expr.expression.getText(), + calleeName: v8CalleeText(expr.expression), args: expr.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), type: DYN, loc, @@ -1787,7 +1804,7 @@ function lowerCheckedCallableValue( return { kind: "dynCall", callee, - calleeName: call.expression.getText(), + calleeName: v8CalleeText(call.expression), args: call.arguments.map((arg) => lowerer.lowerExprExpecting(arg, DYN)), type: DYN, loc, diff --git a/packages/compiler/src/frontend/lowering/lower-classes.ts b/packages/compiler/src/frontend/lowering/lower-classes.ts index 8856ae9b5c..7b2d176c3a 100644 --- a/packages/compiler/src/frontend/lowering/lower-classes.ts +++ b/packages/compiler/src/frontend/lowering/lower-classes.ts @@ -10,7 +10,7 @@ import { InternalCompilerError } from "../../errors.js"; * hierarchy registration. */ import { fsStreamClassOf, lowerFsStreamNew } from "./builtins/fs-streams.js"; import * as ts from "../ts7/adapter.js"; -import { lowerZlibStreamNew, zlibStreamModeOf } from "./builtins/compression.js"; +import { lowerZlibStreamNew, zlibClassName, zlibStreamModeOf } from "./builtins/compression.js"; import type { FnCtx, Lowerer } from "./lowerer.js"; import { BOOL, @@ -143,7 +143,7 @@ import { lowerObjectFactoryNew } from "./object-factory-new.js"; import { tryLowerExpression } from "./expressions/try-lower-expression.js"; import { lowerInstanceConstructorNew } from "./class-instance-constructor.js"; import { classPrototypeData } from "./class-prototypes.js"; -import { constructionEscape } from "./construction-escapes.js"; +import { constructionEscape, mentionsReceiver } from "./construction-escapes.js"; import { checkedClassConstruction, checkedClassConstructionPacked } from "./class-construction.js"; import { initializeRuntimeStatics, hasRuntimeStatics } from "./class-runtime-statics.js"; import { reflectedClassStaticMethodValue } from "./class-method-values.js"; @@ -468,6 +468,11 @@ export interface ClassInfo { * unassigned read throws the catchable TypeError instead of yielding * an undefined the declared type cannot hold (SEMANTICS.md). */ deferredInitFields?: Set; + /** Fields (inherited included) that code construction exposes the + * instance to can read before their assignment: the slot is the + * undefined-armed union, allocation writes undefined, and reads yield the + * union value, so an early read sees undefined as in Node. */ + earlyReadFields?: Set; /** True when construction may hand the instance to other code (a method * call, an argument, a callback, or a base constructor that does) — a * subclass's own fields can then be read before their assignment. */ @@ -2344,7 +2349,11 @@ export function collectClassShapeInner( (member.type !== undefined || (lowerer.typeOf(member.name).flags & ts.TypeFlags.Any) === 0) ) { - lowerer.unsupported("SC1090", member.name, "'unknown'-typed static fields"); + lowerer.unsupported( + "SC1090", + member.name, + `${dynFieldTypeSpelling(lowerer, member.name)}-typed static fields`, + ); } staticFields.push({ name: member.name.text, @@ -2419,12 +2428,29 @@ export function collectClassShapeInner( ts.isPropertyDeclaration(member) && !member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword) ) { - if (!isJsSourceFile(member.getSourceFile())) - lowerer.unsupported( - "SC1090", - memberName, - "redeclaring Error.cause (pass the cause in the Error constructor options)", - ); + // A TypeScript redeclaration is the JavaScript field: Node + // defines the Error's own `cause` (undefined without an + // initializer) after super() returns. `declare` and `abstract` + // declarations are erased and define nothing. Every `.cause` + // read of an Error-rooted class is the checked-dynamic property + // (fieldTarget's errorCause container), so only types that read + // back unchanged from it are accepted. + if (!isJsSourceFile(member.getSourceFile())) { + if ( + modifiers?.some( + (m) => + m.kind === ts.SyntaxKind.DeclareKeyword || + m.kind === ts.SyntaxKind.AbstractKeyword, + ) + ) + continue; + if (!lowerer.classFieldStoresDyn(lowerer.typeOf(member.name))) + lowerer.unsupported( + "SC1090", + memberName, + "redeclaring Error.cause with a type other than 'unknown' or 'object' (declare it 'cause?: unknown' and narrow at the read)", + ); + } fieldOrder.push({ name: "cause", type: DYN, @@ -2788,7 +2814,11 @@ export function collectClassShapeInner( !isJsSourceFile(member.getSourceFile()) && !lowerer.classFieldStoresDyn(lowerer.typeOf(member.name)) ) { - lowerer.unsupported("SC1090", member.name, "'unknown'-typed class fields"); + lowerer.unsupported( + "SC1090", + member.name, + `${dynFieldTypeSpelling(lowerer, member.name)}-typed class fields`, + ); } const undefinedInitializer = type.kind === "dyn" && !member.initializer ? locOf(member) : undefined; @@ -2914,7 +2944,7 @@ export function collectClassShapeInner( lowerer.unsupported( "SC1090", p.name, - "redeclaring Error.cause (pass the cause in the Error constructor options)", + "redeclaring Error.cause as a parameter property (declare 'cause?: unknown' and assign it in the constructor)", ); } const shape = lowerer.paramShape(p); @@ -2922,7 +2952,11 @@ export function collectClassShapeInner( if (type.kind === "void") lowerer.badType(p.name, lowerer.typeOf(p.name)); // The class-field dyn rule verbatim (KEEP NARROW). if (type.kind === "dyn" && !lowerer.classFieldStoresDyn(lowerer.typeOf(p))) { - lowerer.unsupported("SC1090", p.name, "'unknown'-typed class fields"); + lowerer.unsupported( + "SC1090", + p.name, + `${dynFieldTypeSpelling(lowerer, p)}-typed class fields`, + ); } // `override x` (and any same-named inherited member) would // redeclare a base slot — the declared-field rule verbatim. @@ -3473,55 +3507,54 @@ export function collectClassShapeInner( const derived = decl.heritageClauses?.some((clause) => clause.token === ts.SyntaxKind.ExtendsKeyword) ?? false; - // Generic instances and mixins share layouts across declarations, so - // they only report whether construction exposes the instance. - const escape: { escapes: boolean; assigned: Set | null } = - familyMode || mixin - ? { escapes: derived, assigned: null } - : constructionEscape({ - ctor, - fields: fieldOrder.flatMap((f) => - paramProps.some((pp) => pp.name === f.name) - ? [] - : [{ name: f.name, initializer: f.initializer }], - ), - paramProps: paramProps.map((pp) => pp.name), - derived, - baseEscapes: - base === null || - callableBase !== undefined || - factoryBaseExpression !== undefined || - base.constructionEscapes === true, - isField: (name) => - fields.has(name) && - !methods.has(`set:${name}`) && - !lowerer.findMethodOn(base, `set:${name}`), - privateMethodBody: (call) => { - const callee = call.expression; - if (!ts.isPropertyAccessExpression(callee)) return null; - const name = callee.name.text; - const declaration = decl.members.find( - (member): member is ts.MethodDeclaration => - ts.isMethodDeclaration(member) && - member.body !== undefined && - (ts.isIdentifier(member.name) || ts.isPrivateIdentifier(member.name)) && - member.name.text === name, - ); - if (!declaration?.body || declaration.asteriskToken) return null; - const modifiers = ts.getModifiers(declaration) ?? []; - if ( - modifiers.some( - (m) => - m.kind === ts.SyntaxKind.StaticKeyword || m.kind === ts.SyntaxKind.AsyncKeyword, - ) + // A generic family is not emitted; its instances and each mixin + // application analyze their own construction with their actual base. + const escape: { escapes: boolean; assigned: Set | null } = familyMode + ? { escapes: derived, assigned: null } + : constructionEscape({ + ctor, + fields: fieldOrder.flatMap((f) => + paramProps.some((pp) => pp.name === f.name) + ? [] + : [{ name: f.name, initializer: f.initializer }], + ), + paramProps: paramProps.map((pp) => pp.name), + derived, + baseEscapes: + base === null || + callableBase !== undefined || + factoryBaseExpression !== undefined || + base.constructionEscapes === true, + isField: (name) => + fields.has(name) && + !methods.has(`set:${name}`) && + !lowerer.findMethodOn(base, `set:${name}`), + privateMethodBody: (call) => { + const callee = call.expression; + if (!ts.isPropertyAccessExpression(callee)) return null; + const name = callee.name.text; + const declaration = decl.members.find( + (member): member is ts.MethodDeclaration => + ts.isMethodDeclaration(member) && + member.body !== undefined && + (ts.isIdentifier(member.name) || ts.isPrivateIdentifier(member.name)) && + member.name.text === name, + ); + if (!declaration?.body || declaration.asteriskToken) return null; + const modifiers = ts.getModifiers(declaration) ?? []; + if ( + modifiers.some( + (m) => + m.kind === ts.SyntaxKind.StaticKeyword || m.kind === ts.SyntaxKind.AsyncKeyword, ) - return null; - const isPrivate = - ts.isPrivateIdentifier(declaration.name) || - modifiers.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword); - return isPrivate ? declaration.body : null; - }, - }); + ) + return null; + const isPrivate = + ts.isPrivateIdentifier(declaration.name) || + modifiers.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword); + return isPrivate ? declaration.body : null; + }, + }); // The deferred definite-assignment check: a field on the unguarded // list passes only with an unconditional `this.x = ...` at the // constructor's TOP LEVEL — the same standard the JS-class path @@ -3577,18 +3610,30 @@ export function collectClassShapeInner( } } - if (escape.assigned && !inst) { + // Fields assigned after construction can expose the instance (a method + // call, a base constructor that calls an override) can be read while + // Node still holds undefined. Their slots admit undefined, allocation + // writes it, and reads yield it, as in Node. + const earlyReadFields = new Set(base?.earlyReadFields ?? []); + if (escape.assigned && escape.escapes) { for (const f of fieldOrder) { if (f.redeclared || escape.assigned.has(f.name) || deferredInitFields.has(f.name)) continue; - if (paramProps.some((pp) => pp.name === f.name)) continue; const declared = fields.get(f.name); - if (declared?.kind !== "object" || lowerer.classes.get(declared.className)?.def.runtime) + if (declared === undefined || !earlyReadable(lowerer, declared)) continue; + // A declared `T | undefined` field already starts undefined. + if (declared.kind === "union" && lowerer.armTag(declared.unionId, UNDEFINED_T) >= 0) continue; - const armed = lowerer.withUndefinedArm(declared); - if (armed === null || armed.kind !== "union") continue; + const armed = lowerer.runtimeOptionalType(declared); + if (armed.kind !== "union") continue; fields.set(f.name, armed); f.type = armed; - deferredInitFields.add(f.name); + // Primitive and union slots yield the undefined value itself. + // Reference slots keep the deferred-init reads: consumers that + // observe undefined (printing, equality, typeof, nullish defaults) + // see it, and member access on it throws a TypeError, as in Node. + if (declared.kind === "union" || PRIMITIVE_EARLY_READS.has(declared.kind)) + earlyReadFields.add(f.name); + else deferredInitFields.add(f.name); } } @@ -3693,6 +3738,43 @@ export function collectClassShapeInner( (name) => !hasAccessor(name) && !methods.has(name) && !lowerer.findMethodOn(base, name), ) : null; + // Constructor statements that run after the instance may have been + // exposed (a method call, `this` passed along, a base constructor + // that exposes it): a field they create can be read before its + // assignment, while Node holds undefined. + const exposedBefore = new Set(); + { + let exposed = false; + for (const stmt of ctor?.body?.statements ?? []) { + const expression = ts.isExpressionStatement(stmt) ? stmt.expression : null; + if ( + expression && + ts.isCallExpression(expression) && + expression.expression.kind === ts.SyntaxKind.SuperKeyword + ) { + if (base?.constructionEscapes === true || expression.arguments.some(mentionsReceiver)) + exposed = true; + continue; + } + const assigned = + expression && + ts.isBinaryExpression(expression) && + expression.operatorToken.kind === ts.SyntaxKind.EqualsToken && + ts.isPropertyAccessExpression(expression.left) && + expression.left.expression.kind === ts.SyntaxKind.ThisKeyword + ? expression + : null; + if (exposed || (assigned && mentionsReceiver(assigned.right))) exposedBefore.add(stmt); + const plainAssignment = + assigned !== null && + ts.isPropertyAccessExpression(assigned.left) && + !mentionsReceiver(assigned.right) && + !hasAccessor(assigned.left.name.text) && + !methods.has(assigned.left.name.text) && + !lowerer.findMethodOn(base, assigned.left.name.text); + if (!plainAssignment && mentionsReceiver(stmt)) exposed = true; + } + } if (ctor?.body) { for (const stmt of ctor.body.statements) { const lhs = @@ -3763,8 +3845,15 @@ export function collectClassShapeInner( type = inheritedJsFieldType(lowerer, decl, name, type); // super() can invoke a subclass setter or method before its // constructor assignments run. These slots start undefined. - if (base && lowerer.dynConvertible(type) && !sealedPrefix?.created.has(name)) - type = DYN; + const sealed = sealedPrefix?.created.has(name) === true; + if (base && lowerer.dynConvertible(type) && !sealed) type = DYN; + if (exposedBefore.has(stmt) && !sealed && earlyReadable(lowerer, type)) { + const armed = lowerer.runtimeOptionalType(type); + if (armed.kind === "union") { + type = armed; + earlyReadFields.add(name); + } + } fields.set(name, type); fieldOrder.push({ name, type, initializer: undefined }); continue; @@ -4025,6 +4114,7 @@ export function collectClassShapeInner( ? { classDecorators: { nodes: classDecoratorNodes } } : {}), ...(deferredInitFields.size > 0 ? { deferredInitFields } : {}), + ...(earlyReadFields.size > 0 ? { earlyReadFields } : {}), ...(escape.escapes ? { constructionEscapes: true as const } : {}), }; // GENERIC members get their declaring-class backlink now that the @@ -6199,6 +6289,33 @@ function overrideReturnAbi( return declared; } +/** Field kinds whose early reads yield the undefined value (earlyReadFields). */ +const PRIMITIVE_EARLY_READS: ReadonlySet = new Set([ + "f64", + "bool", + "string", + "bigint", +]); + +/** Field types whose slot can admit undefined for reads before the field's + * assignment. Date values are scalars, so their unassigned slot is memory + * safe; checked-dynamic slots already hold undefined. */ +function earlyReadable(lowerer: Lowerer, type: IrType): boolean { + if (isUnitType(type)) return false; + switch (type.kind) { + case "dyn": + case "void": + case "date": + case "generator": + case "caught": + return false; + case "object": + return lowerer.classes.get(type.className)?.def.runtime !== true; + default: + return true; + } +} + /** The nearest declaration of `name` at or above `info` — the method a * receiver of that static class runs when nothing below overrides it. */ export function findMethodOn( @@ -7527,6 +7644,14 @@ export function throwingSetterFn(lowerer: Lowerer, info: ClassInfo, prop: string }; } +/** The declared type a refused checked-dynamic class field names in its + * diagnostic (`'any'`, `'WeakMap'`): `unknown` and + * `object` fields store checked-dynamic values, so the refusal is about + * whatever else the declaration spells. */ +function dynFieldTypeSpelling(lowerer: Lowerer, node: ts.Node): string { + return `'${lowerer.checker.typeToString(lowerer.typeOf(node))}'`; +} + /** The base a constructor chain actually CALLS into: generic FAMILIES are * never constructed (no `%.constructor` exists), so an * instantiation's construction-relevant base is the family's own base — @@ -7628,7 +7753,11 @@ function paramPropInitStmts(lowerer: Lowerer, info: ClassInfo, thisLocal: IrLoca obj: { kind: "varRef", localId: thisLocal.id, type: thisType, loc }, className: info.def.name, field: pp.name, - value: { kind: "varRef", localId: local!.id, type: local!.type, loc }, + // An early-readable field's slot admits undefined (earlyReadFields). + value: lowerer.coerceToExpected( + { kind: "varRef", localId: local!.id, type: local!.type, loc }, + info.fields.get(pp.name) ?? local!.type, + ), loc, }); } @@ -7861,7 +7990,6 @@ export function superCallStmt( args: IrExpr[], loc: SrcLoc, ): IrStmt { - const base = superBaseOf(info)!; const thisRef: IrExpr = { kind: "varRef", localId: thisLocal.id, @@ -7906,6 +8034,8 @@ export function superCallStmt( }, loc, }; + // A callable base has no class info, so the base is read after that case. + const base = superBaseOf(info)!; if (base.builtinError) { // super(message) into the runtime-provided Error constructor: stamps // name/message on the (already-allocated) object. Receiver + message @@ -8571,7 +8701,7 @@ export function lowerNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr { // `new zlib.Gzip(options?)` — the zlib stream classes. const zlibModeNs = zlibStreamModeOf(lowerer, resolved); if (zlibModeNs !== null) { - const what = `new zlib.${expr.expression.name.text}`; + const what = `new zlib.${zlibClassName(zlibModeNs)}`; return lowerZlibStreamNew(lowerer, expr.arguments ?? [], zlibModeNs, what, expr, loc); } // `new N.C(...)` / `new a.Point(...)` — construction through a @@ -8829,7 +8959,7 @@ export function lowerNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr { if (streamInfo) return lowerStreamNew(lowerer, expr, streamInfo); const zlibMode = zlibStreamModeOf(lowerer, symbol); if (zlibMode !== null) { - const what = `new zlib.${symbol!.name}`; + const what = `new zlib.${zlibClassName(zlibMode)}`; return lowerZlibStreamNew(lowerer, expr.arguments ?? [], zlibMode, what, expr, loc); } // `new URL(input)`: the WHATWG URL class (stdlib/@types provenance — diff --git a/packages/compiler/src/frontend/lowering/lower-dgram.ts b/packages/compiler/src/frontend/lowering/lower-dgram.ts index 17dab01abe..06c83da44a 100644 --- a/packages/compiler/src/frontend/lowering/lower-dgram.ts +++ b/packages/compiler/src/frontend/lowering/lower-dgram.ts @@ -14,6 +14,7 @@ import { isJsSourceFile, locOf } from "../program.js"; import { BOOL, canBoxFuncIntoDyn, + canDynCheckTo, DGRAMSOCK_T, DYN, F64, @@ -655,3 +656,98 @@ export function lowerDgramMethodCall( lowerer.checker.getSymbolAtLocation(access.name), ); } + +/** True for a dns callback function util.promisify converts. */ +export function isPromisifiedDnsMember(member: string): boolean { + return member === "lookup" || member === "lookupService"; +} + +/** A call through `promisify(dns.lookup)` or `promisify(dns.lookupService)`: + * the callback form runs with a callback that settles a promise of the + * checked-dynamic `{ address, family }` (the address array with + * options.all) or `{ hostname, service }`, which Node's custom promisified + * forms produce. An argument error rejects the promise instead of + * throwing. A declared result type other than `unknown` is reached through + * an async helper that awaits that promise and checks the value. */ +export function lowerPromisifiedDnsCall( + lowerer: Lowerer, + expr: ts.CallExpression, + member: string, + loc: SrcLoc, +): IrExpr { + const args = expr.arguments; + if (args.some(ts.isSpreadElement)) { + lowerer.noLowering(`the promisified dns.${member} with spread arguments`, expr); + } + const service = member === "lookupService"; + if (service ? args.length !== 2 : args.length < 1 || args.length > 2) { + lowerer.noLowering( + `the promisified dns.${member} with ${args.length} arguments`, + expr, + service + ? "the form is lookupService(address, port)" + : "the supported forms are lookup(hostname) and lookup(hostname, options)", + ); + } + const dyn = (node: ts.Expression | undefined): IrExpr => + node ? lowerer.coerceToExpected(lowerer.lowerExpr(node), DYN) : dynUndefinedExpr(loc); + const promiseDyn: IrType = { kind: "promise", inner: DYN }; + const call: IrExpr = { + kind: "libCall", + fn: service ? "dns.lookupServicePromise" : "dns.lookupPromise", + args: [dyn(args[0]), dyn(args[1])], + type: promiseDyn, + loc, + }; + const declared = lowerer.mapTypeOf(lowerer.typeOf(expr)); + if (declared?.kind !== "promise") { + lowerer.noLowering(`the promisified dns.${member} with this result type`, expr); + } + const value = declared.inner; + if (value.kind === "dyn") return call; + if ( + !canDynCheckTo( + value, + (id) => lowerer.shapes.get(id), + (id) => lowerer.unions.get(id), + ) + ) { + lowerer.noLowering( + `the promisified dns.${member} with a '${lowerer.fmt(declared)}' result`, + expr, + "the result type must have a checked conversion from the lookup's value", + ); + } + const key = `dnsPromise:${JSON.stringify(value)}`; + let name = lowerer.valueHelpers.get(key); + if (!name) { + name = `%dnsPromise.${lowerer.valueHelpers.size}`; + lowerer.valueHelpers.set(key, name); + lowerer.liftedFns.push({ + name, + params: [{ localId: "p.0", name: "p", type: promiseDyn }], + returnType: value, + async: true, + locals: [{ id: "p.0", name: "p", type: promiseDyn, mutable: false }], + body: [ + { + kind: "return", + value: { + kind: "dynCheck", + value: { + kind: "awaitExpr", + value: { kind: "varRef", localId: "p.0", type: promiseDyn, loc }, + type: DYN, + loc, + }, + type: value, + loc, + }, + loc, + }, + ], + loc, + }); + } + return { kind: "call", callee: name, args: [call], type: declared, loc }; +} diff --git a/packages/compiler/src/frontend/lowering/lower-exprs.ts b/packages/compiler/src/frontend/lowering/lower-exprs.ts index f0538bb71e..4ef1392935 100644 --- a/packages/compiler/src/frontend/lowering/lower-exprs.ts +++ b/packages/compiler/src/frontend/lowering/lower-exprs.ts @@ -8,10 +8,12 @@ import { lowerEventStaticProperty } from "./builtins/numbers.js"; import { lowerUnionEquality, tagEqualityMayMissAlias } from "./strict-equality.js"; import { isOptionalProcessStreamProperty } from "./builtins/process.js"; import { lowerWorkerMetadata } from "./builtins/workers.js"; +import { esmImportBindingKind } from "./builtins/module-bindings.js"; import { lowerReadlineProperty } from "./builtins/readline.js"; import { lowerBlobInstanceof } from "./builtins/blob.js"; import { lowerKeyObjectInstanceof } from "./builtins/key-object.js"; import { lowerNetAddressInstanceof, lowerNetAddressStaticRead } from "./builtins/net-address.js"; +import { isExecExceptionType, refuseExecExceptionCode } from "./builtins/errors.js"; import { STATS_TYPE_TESTS } from "./builtins/filesystem.js"; import { threadsImportOf } from "../threads-import.js"; import { experimentalImportOf, lifetimeRegionMemberOf } from "../experimental-import.js"; @@ -29,7 +31,14 @@ import { literalUnionArm } from "../union-discriminants.js"; * (FieldTarget). */ import { isFsStreamMember, lowerFsStreamProperty } from "./builtins/fs-streams.js"; import * as ts from "../ts7/adapter.js"; -import { isZlibStreamMember, lowerZlibStreamBytesWritten } from "./builtins/compression.js"; +import { + isZlibStreamMember, + lowerZlibInstanceOf, + lowerZlibStreamBytesWritten, + zlibClassAliasOf, + zlibClassModeOfExpr, + zlibClassName, +} from "./builtins/compression.js"; import { constituentTypes } from "../ts7/checker.js"; import { dirname } from "node:path"; import * as posix from "node:path/posix"; @@ -129,7 +138,7 @@ import { lowerSafeBytesRead, } from "./array-values.js"; import { strCharsCall } from "./containers/array-construction.js"; -import { lowerOptionalStringIndex } from "./string-index.js"; +import { lowerOptionalStringIndex, lowerUnionStringIndex } from "./string-index.js"; import { tryLowerIndexedComparison } from "./indexed-comparison.js"; import { npmStaticPackageOfPath } from "../npm-static.js"; import { unsupportedModuleFeatureOf } from "../builtin-modules.js"; @@ -606,6 +615,10 @@ function lowerExprInner(lowerer: Lowerer, expr: ts.Expression): IrExpr { if (isNodeModuleValue(lowerer, expr.expression)) { return { kind: "strLit", value: "object", type: STRING, loc }; } + // `typeof zlib.Gzip`, `typeof C` over a zlib class alias. + if (zlibClassModeOfExpr(lowerer, expr.expression) !== null) { + return { kind: "strLit", value: "function", type: STRING, loc }; + } if (ts.isPropertyAccessExpression(expr.expression)) { const member = expr.expression; const prototype = member.expression; @@ -840,7 +853,9 @@ function lowerExprInner(lowerer: Lowerer, expr: ts.Expression): IrExpr { projection?.kind === "promisified-exec-file" || projection?.kind === "promisified-exec" || projection?.kind === "promisified-builtin" || - projection?.kind === "promisified-zlib" + projection?.kind === "promisified-zlib" || + projection?.kind === "promisified-crypto" || + projection?.kind === "promisified-dns" ) { lowerer.unsupported( "SC1090", @@ -862,6 +877,15 @@ function lowerExprInner(lowerer: Lowerer, expr: ts.Expression): IrExpr { `a builtin function alias as an escaping value (call '${expr.text}' directly)`, ); } + // `const C = zlib.Gzip` owns no runtime slot: construction, + // instanceof, typeof and .name lower at their use sites. + if (sym && zlibClassAliasOf(lowerer, sym) !== null) { + lowerer.unsupported( + "SC1090", + expr, + `a zlib class alias as an escaping value (use '${expr.text}' with new, instanceof, typeof or .name)`, + ); + } } // Union-typed bindings read through tsc's control-flow narrowing: // when the checker types this USE as a single arm, maybeNarrow @@ -1174,6 +1198,20 @@ function lowerExprInner(lowerer: Lowerer, expr: ts.Expression): IrExpr { const value = lowerWorkerMetadata(bi.member, loc); if (value) return value; } + // A named import of defaultMaxListeners reads the ES module + // namespace's copy. A CommonJS destructured or member binding holds + // the value read when its declaration ran, which these bindings do + // not store. + if (bi.module === "events" && bi.member === "defaultMaxListeners") { + if (esmImportBindingKind(lowerer, expr) === "named") { + return { kind: "libCall", fn: "emitter.esmDefaultMax", args: [], type: F64, loc }; + } + lowerer.noLowering( + "events.defaultMaxListeners through a CommonJS destructured or member binding", + expr, + "read require('events').defaultMaxListeners or EventEmitter.defaultMaxListeners where the value is needed", + ); + } const c = builtinModuleConstOf(lowerer, bi.module, bi.member); if (c !== undefined) return builtinConstLit(c, loc); // module.builtinModules — the baked Node v24 list, a fresh @@ -1783,6 +1821,11 @@ function lowerExprInner(lowerer: Lowerer, expr: ts.Expression): IrExpr { const table = lowerInternTableProperty(lowerer, expr); if (table) return table; } + // `zlib.Gzip.name`, `C.name` over a zlib class alias. + if (expr.name.text === "name" && !expr.questionDotToken) { + const mode = zlibClassModeOfExpr(lowerer, expr.expression); + if (mode !== null) return { kind: "strLit", value: zlibClassName(mode), type: STRING, loc }; + } // Expando statics may still be absent even when JS inference sees // the eventual assignment's scalar type. Preserve the checked value. const staticField = lowerStaticFieldRead(lowerer, expr); @@ -3112,12 +3155,57 @@ export function lowerTernary( * union-typed reference at use sites (`if (r.kind === "ok") { ...r... }` * types `r` as the ok-arm inside the branch); the IR value is still the * tagged union, so the read is bridged with a `unionNarrow` extracting - * the arm's payload. The extraction is tag-UNCHECKED at runtime — - * soundness rests entirely on tsc having proven the tag (the project's - * trust-the-checker thesis; see docs/ir.md). A checker type that maps to + * the arm's payload. The extraction relies on tsc having proven the tag; + * backends verify it once and trap on a mismatch, so a wrong proof stops + * the program instead of reading another arm. A checker type that maps to * the same union (unnarrowed use), to a SUB-union (partial narrowing — * unrepresentable without a re-tag), or to nothing (`never` in an * exhaustive default) leaves the expression union-typed. */ +/** Uses that read an early-read field's value as Node does when it is + * undefined: printing, templates, operators, tests, member access, calls of + * program functions (whose parameters the optional-value analysis widens), + * and stores into bindings, fields and returns. Other uses, such as library + * arguments, extract the declared type with a check. */ +function observesEarlyRead(node: ts.Node): boolean { + let child = node; + let parent = node.parent; + while (parent && ts.isParenthesizedExpression(parent)) { + child = parent; + parent = parent.parent; + } + if (!parent) return false; + if ( + ts.isTemplateSpan(parent) || + ts.isBinaryExpression(parent) || + ts.isPrefixUnaryExpression(parent) || + ts.isPostfixUnaryExpression(parent) || + ts.isTypeOfExpression(parent) || + ts.isConditionalExpression(parent) || + ts.isIfStatement(parent) || + ts.isWhileStatement(parent) || + ts.isReturnStatement(parent) || + ts.isVariableDeclaration(parent) || + ts.isPropertyAssignment(parent) || + ts.isArrayLiteralExpression(parent) || + (ts.isPropertyAccessExpression(parent) && parent.expression === child) || + (ts.isElementAccessExpression(parent) && parent.expression === child) + ) + return true; + if (ts.isCallExpression(parent) && parent.arguments.includes(child as ts.Expression)) { + const callee = parent.expression; + if ( + ts.isPropertyAccessExpression(callee) && + ts.isIdentifier(callee.expression) && + callee.expression.text === "console" + ) + return true; + return ( + !ts.isPropertyAccessExpression(callee) || callee.expression.kind === ts.SyntaxKind.ThisKeyword + ); + } + return false; +} + export function maybeNarrow(lowerer: Lowerer, expr: IrExpr, node: ts.Node): IrExpr { if ( (expr.type.kind === "union" || expr.type.kind === "dyn") && @@ -3304,8 +3392,8 @@ export function maybeNarrow(lowerer: Lowerer, expr: IrExpr, node: ts.Node): IrEx } // instanceof narrowing for classes: tsc types this USE as a subclass of // the IR value's class (only a dynamic instanceof test narrows a class - // type), so the read bridges with an unchecked static downcast — the - // same trust-the-checker contract as the union bridge below. + // type), so the read bridges with a static downcast that backends verify + // at run time, like the union bridge below. if (expr.type.kind === "object") { const narrowed = lowerer.mapTypeOf(lowerer.typeOf(node)); if (narrowed?.kind === "union") { @@ -3328,6 +3416,25 @@ export function maybeNarrow(lowerer: Lowerer, expr: IrExpr, node: ts.Node): IrEx if (expr.type.kind !== "union") return expr; if (ts.isPropertyAccessExpression(node) && isOptionalProcessStreamProperty(lowerer, node)) return expr; + // A field read before its assignment holds undefined although its + // declared type omits it; the declared type is no narrowing proof. A flow + // narrowing past the declared type (`if (this.x !== null)`) extracts the + // narrowed arm with a check, since the slot may still hold undefined. + if ( + expr.kind === "fieldGet" && + lowerer.classes.get(expr.className)?.earlyReadFields?.has(expr.field) === true + ) { + const symbol = ts.isPropertyAccessExpression(node) + ? lowerer.checker.getSymbolAtLocation(node.name) + : undefined; + const declared = symbol ? lowerer.mapTypeOf(lowerer.checker.getTypeOfSymbol(symbol)) : null; + const narrowedArm = lowerer.mapTypeOf(lowerer.typeOf(node)); + if (narrowedArm === null || narrowedArm.kind === "union") return expr; + if (declared !== null && typeEquals(declared, narrowedArm) && observesEarlyRead(node)) + return expr; + const helper = lowerer.narrowedArmHelper(expr.type.unionId, narrowedArm, expr.loc); + return helper ? lowerer.checkedNarrowCall(helper, expr, narrowedArm, expr.loc) : expr; + } // Represented fields and index values can include undefined even when // checker inference or lib.d.ts declares strings (RegExp captures). @@ -3555,6 +3662,22 @@ function checkedStaleArm( return helper ? lowerer.checkedNarrowCall(helper, value, arm, value.loc) : null; } +/** V8's spelling of a callee in "is not a function": a string-literal + * element key reads as a property name (`o["a"]` is `o.a`), other keys + * keep their brackets. */ +export function v8CalleeText(node: ts.Node): string { + if (ts.isElementAccessExpression(node)) { + const key = node.argumentExpression; + const object = v8CalleeText(node.expression); + return ts.isStringLiteral(key) || ts.isNoSubstitutionTemplateLiteral(key) + ? `${object}.${key.text}` + : `${object}[${key.getText()}]`; + } + if (ts.isPropertyAccessExpression(node) && !node.questionDotToken) + return `${v8CalleeText(node.expression)}.${node.name.text}`; + return node.getText(); +} + /** Node's TypeError for the operation consuming `node` when it holds a * value of type `found`, or null where Node would not throw. */ function staleReadMessage(node: ts.Node, found: IrType): string | null { @@ -3565,7 +3688,7 @@ function staleReadMessage(node: ts.Node, found: IrType): string | null { parent = parent.parent; } if (ts.isCallExpression(parent) && parent.expression === child && !parent.questionDotToken) { - return found.kind === "func" ? null : `${node.getText()} is not a function`; + return found.kind === "func" ? null : `${v8CalleeText(node)} is not a function`; } const unit = found.kind === "undefinedT" ? "undefined" : found.kind === "nullT" ? "null" : null; if (unit === null) return null; @@ -3956,7 +4079,7 @@ function runtimeOptionalAssertionErases( * (`w = 5; if (w !== null)`), the comparison is statically decided and * folds to a bool literal — that drops only a side-effect-free read * (flow narrowing applies to references, never to calls), and soundness - * is the same trust-the-checker bet unionNarrow already makes. Null when + * rests on the same narrowing proof unionNarrow relies on. Null when * neither side is a unit literal (not a unit comparison). */ export function lowerUnitComparison( lowerer: Lowerer, @@ -6864,6 +6987,19 @@ export function lowerElementAccess(lowerer: Lowerer, expr: ts.ElementAccessExpre locOf(expr), ); } + // The checker narrowed the receiver to string, but its stored value + // is still the union (an array-derived binding, a typeof guard). + if (index.type.kind === "f64") { + const read = lowerUnionStringIndex( + lowerer, + recv, + index, + expr.argumentExpression, + lowerer.withUndefinedArm(STRING), + locOf(expr), + ); + if (read) return read; + } lowerer.unsupported( "SC1090", expr, @@ -7042,15 +7178,173 @@ export function lowerElementAccess(lowerer: Lowerer, expr: ts.ElementAccessExpre return lowerer.maybeNarrow({ kind: "arrayGet", arr, index, type: elemT, loc: locOf(expr) }, expr); } +/** A record receiver that an outer keyed read left optional + * (`nested[a][b]`, where `nested[a]` can miss). The receiver evaluates + * once into `stmts`; `present` is its record arm, valid only once the + * access has tested `stable` against the undefined arm. */ +interface OptionalRecordReceiver { + stmts: IrStmt[]; + stable: IrExpr; + present: IrExpr; + unionId: string; + undefTag: number; +} + +function optionalRecordReceiver( + lowerer: Lowerer, + value: IrExpr, + loc: SrcLoc, +): OptionalRecordReceiver | null { + if (value.type.kind !== "union") return null; + const unionId = value.type.unionId; + const undefTag = lowerer.armTag(unionId, UNDEFINED_T); + if (undefTag < 0 || lowerer.unions.get(unionId)?.arms.length !== 2) return null; + const record = lowerer.stripUndefinedArm(value.type); + if (record.kind !== "record") return null; + const stmts: IrStmt[] = []; + const stable = stabilized(lowerer, value, "%recordReceiver", stmts); + const present: IrExpr = { + kind: "unionNarrow", + unionId, + tag: lowerer.armTag(unionId, record), + value: stable, + type: record, + loc, + }; + return { stmts, stable, present, unionId, undefTag }; +} + +/** `value`, evaluated once at this point of `out`. */ +function stabilized(lowerer: Lowerer, value: IrExpr, name: string, out: IrStmt[]): IrExpr { + if (isSafeToRepeat(value)) return value; + const tmp = lowerer.declareHiddenLocal(name, value.type); + out.push({ kind: "varDecl", localId: tmp.id, init: value, loc: value.loc }); + return varRef(tmp.id, value.type, value.loc); +} + +function undefinedReceiver(receiver: OptionalRecordReceiver, loc: SrcLoc): IrExpr { + return { + kind: "unionIsTag", + unionId: receiver.unionId, + tag: receiver.undefTag, + negated: false, + value: receiver.stable, + type: BOOL, + loc, + }; +} + +/** Node's TypeError for a keyed read or write on an undefined receiver. */ +function undefinedReceiverThrow( + key: IrExpr, + action: "read" | "set", + type: IrType, + loc: SrcLoc, +): IrExpr { + const head = `Cannot ${action} properties of undefined (${action === "read" ? "reading" : "setting"} '`; + const message: IrExpr = { + kind: "strConcat", + left: { + kind: "strConcat", + left: { kind: "strLit", value: head, type: STRING, loc }, + right: key, + type: STRING, + loc, + }, + right: { kind: "strLit", value: "')", type: STRING, loc }, + type: STRING, + loc, + }; + return { + kind: "libCall", + fn: "error.nodeThrow", + args: [numLit(1, loc), { kind: "strLit", value: "", type: STRING, loc }, message], + type, + loc, + }; +} + +/** The receiver and key of a keyed access whose receiver can be undefined, + * evaluated in order into `stmts`, with the receiver's record arm and the + * key's property-name string. Null when the receiver is not optional. */ +function guardedRecordAccess( + lowerer: Lowerer, + access: ts.ElementAccessExpression, + rawObj: IrExpr, +): { receiver: OptionalRecordReceiver; key: IrExpr; name: IrExpr } | null { + const receiver = optionalRecordReceiver(lowerer, rawObj, locOf(access.expression)); + if (!receiver) return null; + const keyNode = access.argumentExpression; + const litKey = recordKeyLiteralText(keyNode) ?? recordKeyTypeLiteralText(lowerer, keyNode); + const key = + litKey !== null + ? ({ kind: "strLit", value: litKey, type: STRING, loc: locOf(keyNode) } satisfies IrExpr) + : stabilized(lowerer, lowerer.lowerExpr(keyNode), "%recordKey", receiver.stmts); + // A symbol key names no string property; its message spelling is not + // modeled. + const name = + key.type.kind === "symbol" + ? ({ kind: "strLit", value: "Symbol()", type: STRING, loc: locOf(keyNode) } satisfies IrExpr) + : lowerRecordPropertyKey(lowerer, key, keyNode); + return { receiver, key, name }; +} + +/** `nested[a][b] = v` where `nested[a]` can miss: the receiver, the key and + * the value evaluate in order, then an undefined receiver throws Node's + * TypeError and a present one takes the write. Only member and identifier + * receivers are probed, since a probe that finds no optional receiver + * lowers the receiver again. */ +function lowerGuardedRecordWrite( + lowerer: Lowerer, + expr: ts.BinaryExpression, + target: ts.ElementAccessExpression, +): IrStmt | null { + let receiverNode: ts.Expression = target.expression; + while (ts.isParenthesizedExpression(receiverNode)) receiverNode = receiverNode.expression; + if ( + lowerer.chainRecvByNode.has(target.expression) || + !( + ts.isElementAccessExpression(receiverNode) || + ts.isPropertyAccessExpression(receiverNode) || + ts.isIdentifier(receiverNode) + ) + ) + return null; + const guarded = guardedRecordAccess(lowerer, target, lowerer.lowerExpr(target.expression)); + if (!guarded) return null; + const { receiver, key, name } = guarded; + const loc = locOf(expr); + const value = stabilized(lowerer, lowerer.lowerExpr(expr.right), "%recordValue", receiver.stmts); + const write = lowerer.withExpressionOverride(target.expression, receiver.present, () => + lowerer.withExpressionOverride(target.argumentExpression, key, () => + lowerer.withExpressionOverride(expr.right, value, () => lowerElementWrite(lowerer, expr)), + ), + ); + return { + kind: "block", + body: [ + ...receiver.stmts, + { + kind: "if", + cond: undefinedReceiver(receiver, loc), + then: [{ kind: "exprStmt", expr: undefinedReceiverThrow(name, "set", VOID, loc), loc }], + else_: [write], + loc, + }, + ], + loc, + }; +} + /** `r[k]` over a (non-tuple) record shape. A LITERAL key naming a declared * field is the bracket spelling of field access (the ONLY spelling tsc * permits for signature-declared fields under * noPropertyAccessFromIndexSignature) — an ordinary recordGet. Everything * else is a runtime-keyed read (recordKeyGet): declared fields answer * first via an emitted string-switch, index-signature shapes fall through - * to the overflow map. The result type is the CHECKER's for the access — - * the index signature's value type (dyn for `unknown`), or its - * undefined-armed union under noUncheckedIndexedAccess. Every declared + * to the overflow map. The result type is the index signature's value type + * armed with undefined (dyn for `unknown`): a missing key reads undefined, + * as in JavaScript, whatever noUncheckedIndexedAccess says. Every declared * field must be able to SURFACE as that type (equal, an arm of it, or — * for dyn results — JSON-safe for the dyn conversion); shapes mixing in * fields outside that stay fenced. Declared-only shapes support reads @@ -7074,9 +7368,39 @@ export function lowerRecordKeyRead( // a generic instance) is the same static read — identifier evaluation // is pure, so skipping it matches JS exactly. const litKey = recordKeyLiteralText(keyNode) ?? recordKeyTypeLiteralText(lowerer, keyNode); + // An outer keyed read can leave the receiver undefined: evaluate the + // receiver and the key, then throw Node's TypeError or read the present + // record. // The receiver lowers FIRST (both branches below read it, and JS // evaluates the receiver before the key). const obj = lowerer.lowerExpr(expr.expression); + { + const guarded = guardedRecordAccess(lowerer, expr, obj); + if (guarded) { + const { receiver, key, name } = guarded; + const read = lowerer.withExpressionOverride(expr.expression, receiver.present, () => + litKey !== null + ? lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefined) + : lowerer.withExpressionOverride(keyNode, key, () => + lowerRecordKeyRead(lowerer, expr, shapeId, shape, includeUndefined), + ), + ); + return { + kind: "seqExpr", + stmts: receiver.stmts, + result: { + kind: "ternary", + cond: undefinedReceiver(receiver, loc), + then: undefinedReceiverThrow(name, "read", read.type, loc), + else_: read, + type: read.type, + loc, + }, + type: read.type, + loc, + }; + } + } // A record-mapped CHECKER type over a VALUE living in the checked-dynamic tree (a JS // file-scope object-literal global): the checked-dynamic keyed read — // dynKeyGet against the runtime keys (a missing key answers the checked-dynamic tree @@ -7190,21 +7514,29 @@ export function lowerRecordKeyRead( loc, }; } - // The DECLARED result type of the access — the index signature's value - // type (armed with undefined under noUncheckedIndexedAccess), or the - // declared fields' one common type on signature-free shapes (tsc's - // keyof check proved membership; a smuggled miss traps). The checker - // may have NARROWED this occurrence (assignment CFA on literal keys) — - // maybeNarrow bridges to the narrowed arm exactly like a field read. + // The result type of the access — the index signature's value type + // armed with undefined, or the declared fields' one common type on + // signature-free shapes (tsc's keyof check proved membership; a + // smuggled miss traps). On signature-free shapes the checker may have + // NARROWED this occurrence (assignment CFA on literal keys) — maybeNarrow + // bridges to the narrowed arm exactly like a field read. let declared: IrType | null = null; + // An index-signature read is a JavaScript property read: a missing key + // answers undefined even without noUncheckedIndexedAccess. The read keeps + // that explicit optional union, as an ordinary array read does. + let optionalRead = false; + // A read whose every consumer treats undefined like NaN (see + // nan-coded-reads.ts) is a plain number that is NaN for a missing key. + const nanCoded = + !includeUndefined && + shape.indexValue?.kind === "f64" && + lowerer.nanCodedReads?.isNumericRead(expr) === true; if (shape.indexValue) { declared = shape.indexValue; - if ( - declared.kind !== "dyn" && - (includeUndefined || lowerer.program.getCompilerOptions().noUncheckedIndexedAccess) - ) { + if (declared.kind !== "dyn" && !nanCoded) { declared = lowerer.withUndefinedArmOf(declared); if (!declared) lowerer.badType(expr, lowerer.typeOf(expr)); + optionalRead = true; } } else if ( shape.fields.length > 0 && @@ -7237,10 +7569,12 @@ export function lowerRecordKeyRead( shapeId, key, ...(overflowOnly ? { overflowOnly: true as const } : {}), + ...(nanCoded ? { missNaN: true as const } : {}), type: declared, loc, }; - return includeUndefined ? read : lowerer.maybeNarrow(read, expr); + if (nanCoded) return read; + return includeUndefined || optionalRead ? read : lowerer.maybeNarrow(read, expr); } /** ToPropertyKey for represented record keys. Missing array-derived string @@ -7348,7 +7682,8 @@ export function lowerPresenceOperand(lowerer: Lowerer, node: ts.Expression): IrE * side of `||`/`??`). JavaScript answers undefined for missing record keys * and array indices even when noUncheckedIndexedAccess is disabled. Keep * that value in an explicit union until the surrounding guard/default has - * consumed it; ordinary unchecked reads retain the typed trap. Assertions + * consumed it, including class-field and declared-only reads that otherwise + * keep their typed storage. Assertions * around the read are erased here because this context handles the missing * arm instead of consuming it as the asserted type. */ export function lowerAbsenceProbe(lowerer: Lowerer, node: ts.Expression): IrExpr | null { @@ -8146,6 +8481,8 @@ export function lowerElementWrite(lowerer: Lowerer, expr: ts.BinaryExpression): // the field's type through the emitted helper (a mismatch throws the // catchable TypeError — SEMANTICS.md). if (shape && !shape.tuple) { + const guarded = lowerGuardedRecordWrite(lowerer, expr, target); + if (guarded) return guarded; // Literal keys — string literals AND numeric literals in their // canonical string spelling (`b[1] = v` writes field/key "1", JS's // own key derivation) — name declared fields directly. A key @@ -9937,6 +10274,19 @@ export function lowerBinary(lowerer: Lowerer, expr: ts.BinaryExpression): IrExpr lowerUnionTypeofTest(lowerer, expr, loc); if (test) return test; } + // `C === zlib.Gzip` between two zlib class references: the classes + // are compile-time values, so the answer folds. + if ( + op === ts.SyntaxKind.EqualsEqualsEqualsToken || + op === ts.SyntaxKind.ExclamationEqualsEqualsToken + ) { + const leftClass = zlibClassModeOfExpr(lowerer, expr.left); + const rightClass = leftClass === null ? null : zlibClassModeOfExpr(lowerer, expr.right); + if (leftClass !== null && rightClass !== null) { + const negated = op === ts.SyntaxKind.ExclamationEqualsEqualsToken; + return { kind: "boolLit", value: (leftClass === rightClass) !== negated, type: BOOL, loc }; + } + } const strictEquality = op === ts.SyntaxKind.EqualsEqualsEqualsToken || @@ -11311,6 +11661,7 @@ function lowerErrorCodeTypeofTest( recv = recv.expression; const errT = errorRootedObjectOf(lowerer, lowerer.typeOf(recv)); if (!errT) continue; + if (isExecExceptionType(lowerer, lowerer.typeOf(recv))) refuseExecExceptionCode(lowerer, prop); const caught = lowerer.caughtLocalOf(recv); let receiver: IrExpr; if (caught) { @@ -11887,8 +12238,8 @@ function lowerCjsExportTableThisMember( /** A read of a catch binding — the caught analog of maybeNarrow. Where * tsc's control-flow narrowing has proven a supported test (`instanceof` * over a hierarchy class, `typeof` over a primitive), the read bridges - * with a kind-unchecked `caughtNarrow` extraction (trust-the-checker, - * exactly like unionNarrow). Every OTHER read is the narrowness fence: + * with a `caughtNarrow` extraction whose kind backends verify at run time, + * exactly like unionNarrow. Every OTHER read is the narrowness fence: * the binding's payload is a typed exception snapshot, not a dyn, so * un-narrowed uses have nothing sound to lower to. */ export function caughtRead( @@ -11981,6 +12332,8 @@ export function lowerInstanceOf(lowerer: Lowerer, expr: ts.BinaryExpression, loc // h2 compat 'connect' narrowing (lower-server.ts): a union tag test. const sockTest = lowerSocketInstanceOf(lowerer, expr, loc); if (sockTest !== null) return sockTest; + const zlibMode = zlibClassModeOfExpr(lowerer, expr.right); + if (zlibMode !== null) return lowerZlibInstanceOf(lowerer, expr, zlibMode, loc); if (lowerer.isStdlibGlobal(expr.right, "Set") || lowerer.isStdlibGlobal(expr.right, "Map")) { const collection = lowerer.isStdlibGlobal(expr.right, "Map") ? "map" : "set"; @@ -12913,6 +13266,9 @@ function lowerInExpression(lowerer: Lowerer, expr: ts.BinaryExpression, loc: Src loc, }; } + if (key === "code" && isExecExceptionType(lowerer, lowerer.typeOf(expr.right))) { + refuseExecExceptionCode(lowerer, expr); + } let recv = lowerer.lowerExpr(expr.right); const classInfo = recv.type.kind === "classval" ? lowerer.classes.get(recv.type.className) : undefined; @@ -13600,6 +13956,8 @@ const MATCH_ROW_CALLBACK_METHODS = new Set([ "filter", "find", "findLast", + "findIndex", + "findLastIndex", "some", "every", "flatMap", @@ -13713,7 +14071,10 @@ function matchAllRegexOf(lowerer: Lowerer, e: ts.Expression): ts.Expression | nu * receiver isn't a match slice or the regex isn't traceable — the * caller's member fence (with the groups hint) names the gap. */ function lowerMatchGroupsRead(lowerer: Lowerer, expr: ts.PropertyAccessExpression): IrExpr | null { - if (expr.name.text !== "groups" || expr.questionDotToken !== undefined) return null; + if (expr.name.text !== "groups") return null; + // `m?.groups` re-dispatches through the optional-chain machinery, which + // narrows the receiver to its match arm first. + if (expr.questionDotToken !== undefined && !lowerer.chainHandled.has(expr)) return null; const loc = locOf(expr); const strArr = arrayOf(STRING); const recvT = lowerer.mapTypeOf(lowerer.typeOf(expr.expression)); @@ -14664,19 +15025,35 @@ export function fieldTarget( // all-unknown-fields cast — `(err as { code?: unknown }).code`): // decline, and the dyn keyed-read fallback answers. if (obj.type.kind === "union" && lowerer.armTag(obj.type.unionId, UNDEFINED_T) >= 0) { - // A nested property read such as `rows[i].value` has the same - // unchecked outer array-read issue as tuple indexing. Validate - // the record arm before fieldGet/recordGet so a missing outer - // value raises a catchable TypeError rather than reaching the - // backend with a union receiver. + // A nested property read such as `rows[i].value` or + // `table[k].value`: the outer read can miss. Validate the record + // arm before fieldGet/recordGet so a missing outer value raises + // Node's TypeError rather than reaching the backend with a union + // receiver. const present = lowerer.stripUndefinedArm(obj.type); + const parent = access.parent; + const written = + parent !== undefined && + ts.isBinaryExpression(parent) && + parent.left === access && + parent.operatorToken.kind === ts.SyntaxKind.EqualsToken; const helper = present.kind === "record" ? lowerer.narrowedArmHelper(obj.type.unionId, present, locOf(access.expression)) : null; - obj = helper - ? lowerer.checkedNarrowCall(helper, obj, present, locOf(access.expression)) - : lowerer.maybeNarrow(obj, access.expression); + obj = + (present.kind === "record" + ? lowerer.runtimeOptionalPropertyReceiver( + access.expression, + obj, + present, + access.name.text, + written ? "set" : "read", + ) + : null) ?? + (helper + ? lowerer.checkedNarrowCall(helper, obj, present, locOf(access.expression)) + : lowerer.maybeNarrow(obj, access.expression)); } if (obj.type.kind !== "record") return null; const actualShape = lowerer.shapes.get(obj.type.shapeId); @@ -14994,8 +15371,8 @@ export function fieldGetExpr( } // Overflow dot reads: exactly the bracket read with a literal key — // recordKeyGet, overflowOnly (the name declares no field by - // construction), typed as the index value armed with undefined under - // noUncheckedIndexedAccess (mirroring lowerRecordKeyRead). + // construction), typed as the index value armed with undefined + // (mirroring lowerRecordKeyRead). if (target.container === "recordOvf") { let obj = target.obj; if (obj.type.kind === "union" && ts.isPropertyAccessExpression(blame)) { @@ -15021,7 +15398,7 @@ export function fieldGetExpr( `reading '${target.field}' on a record without an index signature`, ); let t: IrType = shape.indexValue; - if (lowerer.program.getCompilerOptions().noUncheckedIndexedAccess) { + if (t.kind !== "dyn") { const armed = lowerer.withUndefinedArmOf(t); if (!armed) lowerer.badType(blame, lowerer.typeOf(blame)); t = armed; @@ -15088,6 +15465,20 @@ export function fieldGetExpr( const helper = lowerer.deferredReadHelper(target.fieldType.unionId, inner, loc); if (helper) return { kind: "call", callee: helper, args: [read], type: inner, loc }; } + // An early-read field (ClassInfo.earlyReadFields) yields its undefined + // where the use reads it as Node does; elsewhere the declared type is + // extracted with a check. + if ( + target.fieldType.kind === "union" && + lowerer.classes.get(target.className)?.earlyReadFields?.has(target.field) === true && + !observesEarlyRead(blame) + ) { + const declared = lowerer.mapTypeOf(lowerer.typeOf(blame)); + if (declared && declared.kind !== "union") { + const helper = lowerer.narrowedArmHelper(target.fieldType.unionId, declared, loc); + if (helper) return lowerer.checkedNarrowCall(helper, read, declared, loc); + } + } return read; } return { @@ -15628,6 +16019,22 @@ function lowerFieldCompoundValue( if (op === "+" && read.type.kind === "string") { appended = lowerer.ensureString(rhs, rhsNode ?? access); value = { kind: "strConcat", left: read, right: appended, type: STRING, loc }; + } else if ( + op === "+" && + read.type.kind === "union" && + lowerer.stripUndefinedArm(read.type).kind === "string" && + target.container === "class" && + lowerer.classes.get(target.className)?.earlyReadFields?.has(target.field) === true + ) { + // A string field read before its assignment holds undefined, which + // concatenates as "undefined". + value = { + kind: "strConcat", + left: lowerer.ensureString(read, access), + right: lowerer.ensureString(rhs, rhsNode ?? access), + type: STRING, + loc, + }; } else if (numericRead.type.kind === "f64" && numericRhs.type.kind === "f64") { value = { kind: "bin", op, left: numericRead, right: numericRhs, type: F64, loc }; } else if (bigintValue) { diff --git a/packages/compiler/src/frontend/lowering/lower-modules.ts b/packages/compiler/src/frontend/lowering/lower-modules.ts index d18c4f547b..c5802b07ed 100644 --- a/packages/compiler/src/frontend/lowering/lower-modules.ts +++ b/packages/compiler/src/frontend/lowering/lower-modules.ts @@ -35,6 +35,8 @@ import { withClassInitFlag, withCycleInitFlags, } from "./cycle-initialization.js"; +import { markRequireEarlyBindings } from "./require-initialization.js"; +import { collectJsonRequires, isJsonRequireDecl } from "./json-require.js"; import { invalidJsonModuleDiag, unsupportedPackageImportDiag, @@ -78,6 +80,7 @@ import { createRequireProgramModuleDecl, } from "./builtins/module-bindings.js"; import { isPromisifyCall, registerBuiltinCallableAlias } from "./builtins/callable-aliases.js"; +import { registerZlibClassAlias } from "./builtins/compression.js"; import { stripTypeCasts } from "./builtins/arguments.js"; import { bindingContextualGenericFnNodeOf, @@ -409,6 +412,7 @@ export function collectProgram(lowerer: Lowerer, parts: FileParts[]): void { for (const fp of parts) lowerer.collectGlobals(fp.sf, fp.topStmts); lowerer.collectNpmImports(parts); lowerer.collectJsonImports(parts); + collectJsonRequires(lowerer, parts); } finally { lowerer.diagSink = null; } @@ -416,6 +420,7 @@ export function collectProgram(lowerer: Lowerer, parts: FileParts[]): void { for (const fp of parts) lowerer.collectGlobals(fp.sf, fp.topStmts); lowerer.collectNpmImports(parts); lowerer.collectJsonImports(parts); + collectJsonRequires(lowerer, parts); } for (const decl of computed) { const symbol = declSymbolOf(lowerer, decl); @@ -493,6 +498,7 @@ export function collectProgram(lowerer: Lowerer, parts: FileParts[]): void { lowerer.collecting = wasCollecting; } markCycleEarlyBindings(lowerer); + markRequireEarlyBindings(lowerer); } export function collectNpmImports(lowerer: Lowerer, parts: FileParts[]): void { @@ -1532,8 +1538,9 @@ export function collectGlobals( isRequireStatement(stmt) && !( ts.isVariableStatement(stmt) && - stmt.declarationList.declarations.some((decl) => - isNativeFfiRequire(lowerer, decl.initializer), + stmt.declarationList.declarations.some( + (decl) => + isNativeFfiRequire(lowerer, decl.initializer) || isJsonRequireDecl(lowerer, decl), ) ) ) { @@ -1694,6 +1701,7 @@ export function collectGlobals( // resolution and value reads reuse the source's interned closure. if (isConst && registerOverloadedCallableAlias(lowerer, decl)) continue; if (isConst && registerBuiltinCallableAlias(lowerer, decl.name, decl.initializer)) continue; + if (registerZlibClassAlias(lowerer, decl.name, decl.initializer)) continue; // `const f = (x) => ...` at file scope of an npm-static JS file // whose params are implicit-any: the implicit-monomorphization // twin of the generic binding above — no global exists; calls and diff --git a/packages/compiler/src/frontend/lowering/lower-stmts.ts b/packages/compiler/src/frontend/lowering/lower-stmts.ts index ad4fd53123..93f00596c4 100644 --- a/packages/compiler/src/frontend/lowering/lower-stmts.ts +++ b/packages/compiler/src/frontend/lowering/lower-stmts.ts @@ -10,6 +10,8 @@ import { InternalCompilerError } from "../../errors.js"; * flow (if/while/for/for-of/do, switch, try/catch, abrupt completions), * explicit resource management, and blocked-binding poisoning. */ import * as ts from "../ts7/adapter.js"; +import { requireInitFlagStmt } from "./require-initialization.js"; +import { isJsonRequireDecl } from "./json-require.js"; import { bindingSource } from "../binding-source.js"; import type { Lowerer } from "./lowerer.js"; import { @@ -145,6 +147,7 @@ import { } from "./builtins/module-bindings.js"; import { lowerNodeModuleCall } from "./builtins/module-resolution.js"; import { registerBuiltinCallableAlias } from "./builtins/callable-aliases.js"; +import { registerZlibClassAlias } from "./builtins/compression.js"; import { lowerEnumDeclaration } from "./lower-enums.js"; import { abstractPropertyDeclOf, @@ -1512,81 +1515,96 @@ export function lowerVarStatement(lowerer: Lowerer, stmt: ts.VariableStatement): const isLet = (list.flags & ts.NodeFlags.Let) !== 0 || (list.flags & ts.NodeFlags.BlockScoped) === 0; return list.declarations.flatMap((decl) => { - // The canonical ESM bridge to a compiled program module. Like a - // top-level CommonJS require declaration, the binding is alias - // plumbing while the dependency evaluates at this statement's - // exact position through its run-once initializer. - if (isConst && createRequireProgramModuleDecl(lowerer, decl.name, decl.initializer)) { - if (!ts.isSourceFile(stmt.parent)) { - lowerer.unsupported( - "SC1090", - decl, - "createRequire program-module bindings outside the module's top level (move the binding to the top of the file; side-effect-only requires may remain nested)", - ); - } - const target = createRequireProgramModuleOf(lowerer, decl.initializer)!; - const init = lowerer.requireInitStmt(target.spec, decl); - return init ? [init] : []; - } - // CommonJS require declarations (JS files): at the module's top - // level the BINDINGS are alias plumbing (no storage; - // resolveValueSymbol routes the reads), but the require itself is - // Node's inline module evaluation — a relative require lowers to the - // required module's guarded %init call at exactly this position - // (first require runs the body, later ones are cache hits; builtins - // load nothing). Binding requires anywhere else would need lazily- - // initialized alias storage this model doesn't represent; named - // fence. - if ( - decl.initializer !== undefined && - requireSpecOf(decl.initializer) !== null && - !isNativeFfiRequire(lowerer, decl.initializer) && - isJsSourceFile(decl.getSourceFile()) - ) { - const spec = requireSpecOf(decl.initializer)!; - if (lowerer.externalTypes.has(spec)) lowerer.externalHostFence(spec, decl, false); - if (ts.isSourceFile(stmt.parent)) { - const init = lowerer.requireInitStmt(spec, decl); - return init ? [init] : []; - } - // A nested BUILTIN require (`get inFreeBSDJail() { const { execSync } - // = require('child_process'); ... }` — test/common's lazy-getter - // idiom): builtins load nothing, so the statement is PURE alias - // plumbing wherever it sits — the bindings resolve through the - // same routing top-level requires use, and no statement remains. - // Relative requires keep the fence: their module-init-at-position - // semantics needs storage this model doesn't represent. - if (canonicalBuiltinModule(spec) !== null) { - return []; - } + const lowered = lowerVarDeclarator(lowerer, stmt, decl, isConst, isLet); + // A require binding earlier code can read is initialized from here on + // (require-initialization.ts). + const initialized = requireInitFlagStmt(lowerer, decl); + return initialized ? [...lowered, initialized] : lowered; + }); +} + +function lowerVarDeclarator( + lowerer: Lowerer, + stmt: ts.VariableStatement, + decl: ts.VariableDeclaration, + isConst: boolean, + isLet: boolean, +): IrStmt[] { + // The canonical ESM bridge to a compiled program module. Like a + // top-level CommonJS require declaration, the binding is alias + // plumbing while the dependency evaluates at this statement's + // exact position through its run-once initializer. + if (isConst && createRequireProgramModuleDecl(lowerer, decl.name, decl.initializer)) { + if (!ts.isSourceFile(stmt.parent)) { lowerer.unsupported( "SC1090", decl, - "require() with bindings outside the module's top level (move it to the top of the file)", + "createRequire program-module bindings outside the module's top level (move the binding to the top of the file; side-effect-only requires may remain nested)", ); } - // `const { NGHTTP2_CANCEL } = http2.constants` — a destructure over - // the baked constants table: alias plumbing, no storage (uses read - // their literals via builtinConstantBindingOf; collectGlobals skipped - // the globals by the same test). - if (lowerer.builtinConstantsDestructureDecl(decl.name, decl.initializer)) return []; - if (ts.isArrayBindingPattern(decl.name) || ts.isObjectBindingPattern(decl.name)) { - // `const { createSign } = crypto` over a builtin NAMESPACE binding: - // alias plumbing (builtinImportOf routes the reads) — no statement. - if (builtinNamespaceDestructureModuleOf(lowerer, decl) !== null) return []; - return lowerer.lowerDestructuringDecl(decl, isLet); + const target = createRequireProgramModuleOf(lowerer, decl.initializer)!; + const init = lowerer.requireInitStmt(target.spec, decl); + return init ? [init] : []; + } + // CommonJS require declarations (JS files): at the module's top + // level the BINDINGS are alias plumbing (no storage; + // resolveValueSymbol routes the reads), but the require itself is + // Node's inline module evaluation — a relative require lowers to the + // required module's guarded %init call at exactly this position + // (first require runs the body, later ones are cache hits; builtins + // load nothing). Binding requires anywhere else would need lazily- + // initialized alias storage this model doesn't represent; named + // fence. + if ( + decl.initializer !== undefined && + requireSpecOf(decl.initializer) !== null && + !isNativeFfiRequire(lowerer, decl.initializer) && + !isJsonRequireDecl(lowerer, decl) && + isJsSourceFile(decl.getSourceFile()) + ) { + const spec = requireSpecOf(decl.initializer)!; + if (lowerer.externalTypes.has(spec)) lowerer.externalHostFence(spec, decl, false); + if (ts.isSourceFile(stmt.parent)) { + const init = lowerer.requireInitStmt(spec, decl); + return init ? [init] : []; } - // STORED protocol values with statically closed representations: - // numeric value iterators retain source/cursor/done hidden locals; a - // matchAll drain retains its companion index array so a later - // for-of can serve `m.index`. Both are const-only interceptions. - const numericIterator = lowerNumericIteratorDecl(lowerer, decl, isConst); - if (numericIterator) return numericIterator; - const drained = lowerMatchAllDrainDecl(lowerer, decl, isConst); - if (drained) return drained; - const lowered = lowerer.lowerVarDecl(decl, isLet); - return lowered ? [lowered] : []; - }); + // A nested BUILTIN require (`get inFreeBSDJail() { const { execSync } + // = require('child_process'); ... }` — test/common's lazy-getter + // idiom): builtins load nothing, so the statement is PURE alias + // plumbing wherever it sits — the bindings resolve through the + // same routing top-level requires use, and no statement remains. + // Relative requires keep the fence: their module-init-at-position + // semantics needs storage this model doesn't represent. + if (canonicalBuiltinModule(spec) !== null) { + return []; + } + lowerer.unsupported( + "SC1090", + decl, + "require() with bindings outside the module's top level (move it to the top of the file)", + ); + } + // `const { NGHTTP2_CANCEL } = http2.constants` — a destructure over + // the baked constants table: alias plumbing, no storage (uses read + // their literals via builtinConstantBindingOf; collectGlobals skipped + // the globals by the same test). + if (lowerer.builtinConstantsDestructureDecl(decl.name, decl.initializer)) return []; + if (ts.isArrayBindingPattern(decl.name) || ts.isObjectBindingPattern(decl.name)) { + // `const { createSign } = crypto` over a builtin NAMESPACE binding: + // alias plumbing (builtinImportOf routes the reads) — no statement. + if (builtinNamespaceDestructureModuleOf(lowerer, decl) !== null) return []; + return lowerer.lowerDestructuringDecl(decl, isLet); + } + // STORED protocol values with statically closed representations: + // numeric value iterators retain source/cursor/done hidden locals; a + // matchAll drain retains its companion index array so a later + // for-of can serve `m.index`. Both are const-only interceptions. + const numericIterator = lowerNumericIteratorDecl(lowerer, decl, isConst); + if (numericIterator) return numericIterator; + const drained = lowerMatchAllDrainDecl(lowerer, decl, isConst); + if (drained) return drained; + const lowered = lowerer.lowerVarDecl(decl, isLet); + return lowered ? [lowered] : []; } /** The numeric indexed source of a built-in value-iterator expression: @@ -2700,6 +2718,14 @@ export function lowerBindingPattern( } return; } + if ( + srcType.kind === "array" && + isMatchSliceType(lowerer, srcType) && + isRegexMatchResultPattern(lowerer, pattern) + ) { + lowerMatchResultObjectPattern(lowerer, pattern, srcRef, srcType, isLet, out); + return; + } if (srcType.kind !== "record") { lowerer.unsupported( "SC1031", @@ -2799,18 +2825,20 @@ export function lowerBindingPattern( // An UNDECLARED key of an INDEX-SIGNATURE shape (`const { m, n, p } // = ....groups` over `{ [key: string]: string }` — the groups // record, `Record` tables generally): the overflow-map - // read, exactly the dot access's recordKeyGet — a missing key - // traps when the value type carries no undefined arm (the checker - // claimed V), or binds the undefined arm (where a default applies - // like any field's). + // read, exactly the dot access's recordKeyGet — a missing key binds + // the undefined arm (where a default applies like any field's). const loc = locOf(el); + const readType = + shape.indexValue.kind === "dyn" + ? shape.indexValue + : (lowerer.withUndefinedArmOf(shape.indexValue) ?? shape.indexValue); let value: IrExpr = { kind: "recordKeyGet", obj: srcRef(), shapeId: srcType.shapeId, key: { kind: "strLit", value: propName, type: STRING, loc }, overflowOnly: true, - type: shape.indexValue, + type: readType, loc, }; if (el.initializer) value = applyBindingDefault(lowerer, el, value); @@ -2968,6 +2996,124 @@ function patternKeyNameOf(lowerer: Lowerer, prop: ts.PropertyName): string | nul return null; } +/** True when an object pattern destructures a regex match result: the + * checker types its source as the standard library's RegExpExecArray or + * RegExpMatchArray. */ +function isRegexMatchResultPattern(lowerer: Lowerer, pattern: ts.ObjectBindingPattern): boolean { + const parent = pattern.parent; + const source = + ts.isVariableDeclaration(parent) && parent.initializer !== undefined + ? lowerer.typeOf(parent.initializer) + : lowerer.typeOf(pattern); + const type = lowerer.checker.getNonNullableType(source); + const symbol = type.getAliasSymbol() ?? type.getSymbol(); + return ( + symbol !== undefined && + (symbol.name === "RegExpExecArray" || symbol.name === "RegExpMatchArray") && + lowerer.isStdlibSymbol(symbol) + ); +} + +/** `const { 0: whole, 1: first, index, input, groups } = re.exec(s)!`: an + * object pattern over a regex match result. The result is an honest + * string[] slice whose `index` and `input` are the special-form reads that + * `m.index` and `m.input` lower to; numeric keys are the positional reads + * of the equivalent array pattern, and `groups` is the projection `m.groups` + * builds when the regex is statically known. Rest elements, defaults on + * the special properties, and every other key keep a named fence. */ +function lowerMatchResultObjectPattern( + lowerer: Lowerer, + pattern: ts.ObjectBindingPattern, + srcRef: () => IrExpr, + srcType: IrType & { kind: "array" }, + isLet: boolean, + out: IrStmt[], +): void { + let groups: { name: string; index: number }[] | null | undefined; + for (const el of pattern.elements) { + if (el.name === undefined) continue; + const loc = locOf(el); + if (el.dotDotDotToken) { + lowerer.unsupported( + "SC1031", + el, + "rest bindings over regex match results (the rest copies the positions and the index, input and groups properties — destructure them by name)", + ); + } + const prop = el.propertyName ?? el.name; + const propName = + ts.isIdentifier(prop) || + ts.isComputedPropertyName(prop) || + ts.isStringLiteralLike(prop) || + ts.isNumericLiteral(prop) + ? patternKeyNameOf(lowerer, prop as ts.PropertyName) + : null; + if (propName === null) { + lowerer.unsupported( + "SC1031", + el, + "destructuring with computed keys that do not fold to one property name", + ); + } + let value: IrExpr; + if (/^(?:0|[1-9]\d{0,8})$/.test(propName)) { + const index = Number(propName); + const position: IrExpr = { kind: "numLit", value: index, type: F64, loc }; + value = lowerSafeIndexRead(lowerer, srcRef(), position, loc) ?? { + kind: "arrayGet", + arr: srcRef(), + index: position, + type: srcType.elem, + loc, + }; + if (el.initializer) { + value = arrayPositionDefault(lowerer, el, value, srcRef, index, srcType.elem, out); + } + } else if (propName === "index" || propName === "input") { + if (el.initializer) { + lowerer.unsupported( + "SC1031", + el, + `binding defaults on the '${propName}' of a regex match result`, + ); + } + value = lowerer.maybeNarrow( + { + kind: "dynKeyGet", + value: lowerer.coerceToExpected(srcRef(), DYN), + key: { kind: "strLit", value: propName, type: STRING, loc }, + type: DYN, + loc, + }, + el.name, + ); + } else if (propName === "groups") { + if (groups === undefined) { + const parent = pattern.parent; + groups = + ts.isVariableDeclaration(parent) && parent.initializer !== undefined + ? matchResultNamedGroupsOf(lowerer, parent.initializer) + : null; + } + if (groups === null || groups.length === 0 || el.initializer) { + lowerer.unsupported( + "SC1031", + el, + "destructuring 'groups' from this regex match result (it lowers when the regex is statically known and has named groups, without a default)", + ); + } + value = lowerGroupsProjection(lowerer, srcRef(), groups, loc); + } else { + lowerer.unsupported( + "SC1031", + el, + `destructuring the property '${propName}' of a regex match result (numeric positions, index, input and groups compile)`, + ); + } + lowerer.bindPatternTarget(el.name, value, isLet, out); + } +} + /** Decode one non-rest object-assignment property into its static source * field, destination expression, and optional default initializer. */ function destructuringPropertyOf( @@ -4246,6 +4392,7 @@ export function lowerVarDecl( // reads reuse its interned closure identity. if (!isLet && registerOverloadedCallableAlias(lowerer, decl)) return null; if (!isLet && registerBuiltinCallableAlias(lowerer, decl.name, decl.initializer)) return null; + if (registerZlibClassAlias(lowerer, decl.name, decl.initializer)) return null; // `const knownBy = (cmd) => ...` — an IMPLICIT-ANY function-value // binding in npm-static JS (function-scope bindings register here, in @@ -5388,7 +5535,7 @@ function lowerBranchSwitch( * cell, deliberately narrower than dyn: the supported uses are the * narrowing tests (`e instanceof C` over hierarchy classes, `typeof e * === "string"/"number"/"boolean"`), reads under a proven narrow - * (caughtRead bridges them with caughtNarrow, trust-the-checker like + * (caughtRead bridges them with caughtNarrow, verified at run time like * unionNarrow), and rethrow (`throw e`). Unsupported raw uses and * assignments are fenced (SC1063 and friends); destructuring patterns * are SC1062. Captures retain the snapshot beyond the catch scope. @@ -6055,16 +6202,22 @@ export function lowerExprStatement(lowerer: Lowerer, expr: ts.Expression): IrStm ); } // syncBuiltinESMExports() is the other compiler-only statement: the - // call validates its zero-argument shape, then emits no code because - // the static builtin surface is immutable. + // call validates its zero-argument shape, then refreshes the events + // module's ES module namespace copy of defaultMaxListeners, the one + // builtin export a program can change. const bi = ts.isIdentifier(expr.expression) ? lowerer.builtinImportOf(expr.expression) : ts.isPropertyAccessExpression(expr.expression) ? lowerer.builtinMemberOf(expr.expression) : null; if (bi?.module === "module" && bi.member === "syncBuiltinESMExports") { - lowerNodeModuleCall(lowerer, expr, bi, locOf(expr)); - return { kind: "block", body: [], loc: locOf(expr) }; + const loc = locOf(expr); + lowerNodeModuleCall(lowerer, expr, bi, loc); + return { + kind: "exprStmt", + expr: { kind: "libCall", fn: "emitter.esmSync", args: [], type: VOID, loc }, + loc, + }; } } if ( diff --git a/packages/compiler/src/frontend/lowering/lower-test.ts b/packages/compiler/src/frontend/lowering/lower-test.ts index d18aec3055..e36518559c 100644 --- a/packages/compiler/src/frontend/lowering/lower-test.ts +++ b/packages/compiler/src/frontend/lowering/lower-test.ts @@ -66,7 +66,7 @@ function lowerMockTrackerMember( const TEST_FN_HINT = "test bodies take () or (t: TestContext) and return void or Promise"; const TESTCTX_SURFACE_HINT = - "t.test, t.skip, t.todo, t.diagnostic, t.plan, t.before, t.after, t.beforeEach, t.afterEach, t.name, t.fullName, and t.assert.* are the supported TestContext members"; + "t.test, t.skip, t.todo, t.diagnostic, t.plan, t.before, t.after, t.beforeEach, t.afterEach, t.name, t.fullName, t.filePath, t.attempt, t.passed, t.workerId, t.tags, and t.assert.* are the supported TestContext members"; /** Registration calls return Node's Promise, but the promise only * resolves through the runner — consuming it outside `await t.test(...)` @@ -409,11 +409,16 @@ function lowerSuite( } if (methodMode === 1 || methodMode === 2) opts.mode = methodMode; const cb = lowerer.lowerExpr(fnNode); - if (cb.type.kind !== "func" || cb.type.params.length !== 0) { + // The body may take the SuiteContext (flag 2, as test bodies take theirs). + if ( + cb.type.kind !== "func" || + cb.type.params.length > 1 || + (cb.type.params.length === 1 && cb.type.params[0]!.kind !== "testCtx") + ) { lowerer.noLowering( - `${what} whose body is not a zero-parameter function`, + `${what} whose body is not a function of the SuiteContext`, fnNode, - "suite bodies take no parameters (they run at registration)", + "suite bodies take no parameters or the SuiteContext (they run at registration)", ); } if (cb.type.ret.kind !== "void" && !isUnitType(cb.type.ret)) { @@ -427,7 +432,10 @@ function lowerSuite( const mode = numLit(opts.mode, loc); const msg = opts.msg ?? strLit("", loc); // describe.only and { only: true } mark the suite (flag 4, as for tests). - const flags = numLit(optionFlags(opts) | (methodMode === 3 ? 4 : 0), loc); + const flags = numLit( + optionFlags(opts) | (methodMode === 3 ? 4 : 0) | (cb.type.params.length === 1 ? 2 : 0), + loc, + ); return withOptionValues( { kind: "libCall", @@ -456,14 +464,16 @@ function lowerHook( lowerer.noLowering( `${what} with ${args.length} arguments`, expr, - "the supported form is one zero-parameter function (hook options have no lowering)", + "the supported form is one function (hook options have no lowering)", ); } - const { cb, flags } = lowerBodyArg(lowerer, args[0]!, what, false); + // A hook may take its context: the suite's for before/after, the test's + // for beforeEach/afterEach. + const { cb, flags } = lowerBodyArg(lowerer, args[0]!, what, true); return { kind: "libCall", fn: "test.hook", - args: [numLit(which, loc), cb, numLit(flags, loc)], + args: [numLit(which, loc), cb, numLit(flags, loc), strLit(atStringOf(expr), loc)], type: VOID, loc, }; @@ -919,7 +929,8 @@ export function lowerTestMethodCall( ); } const receiver = lowerer.lowerExpr(access.expression); - const { cb, flags } = lowerBodyArg(lowerer, args[0]!, what, false); + // The hook may take the test's context. + const { cb, flags } = lowerBodyArg(lowerer, args[0]!, what, true); return { kind: "libCall", fn: "test.ctxHook", @@ -962,6 +973,78 @@ export function lowerTestMethodCall( ); } +/** t.filePath (the entry file, as Node's entryFile), t.attempt (0: tests + * run once), t.passed, t.workerId (Number(NODE_TEST_WORKER_ID) || + * undefined) and t.tags ([]: the tags option has no lowering), each + * coerced to the member's declared type. */ +function lowerTestCtxValue( + lowerer: Lowerer, + expr: ts.PropertyAccessExpression, + member: "filePath" | "attempt" | "passed" | "workerId" | "tags", +): IrExpr { + const loc = locOf(expr); + const receiver = lowerer.lowerExpr(expr.expression); + const declared = lowerer.mapTypeOf(lowerer.typeOf(expr)); + if (!declared) { + lowerer.noLowering(`TestContext.${member} of this type`, expr, TESTCTX_SURFACE_HINT); + } + const after = (value: IrExpr): IrExpr => ({ + kind: "seqExpr", + stmts: [{ kind: "exprStmt", expr: receiver, loc }], + result: lowerer.coerceToExpected(value, declared), + type: declared, + loc, + }); + switch (member) { + case "filePath": + return after(strLit(lowerer.entry.fileName, loc)); + case "attempt": + return after(numLit(0, loc)); + case "tags": + return after({ kind: "arrayLit", elems: [], type: declared, loc }); + case "passed": + return lowerer.coerceToExpected( + { kind: "libCall", fn: "test.ctxPassed", args: [receiver], type: BOOL, loc }, + declared, + ); + case "workerId": { + const id = lowerer.declareHiddenLocal("%workerId", F64); + const missing = lowerer.wrappedUndefined(declared, loc); + if (!missing) + lowerer.noLowering("TestContext.workerId of this type", expr, TESTCTX_SURFACE_HINT); + return { + kind: "seqExpr", + stmts: [ + { kind: "exprStmt", expr: receiver, loc }, + { + kind: "varDecl", + localId: id.id, + init: { kind: "libCall", fn: "test.workerId", args: [], type: F64, loc }, + loc, + }, + ], + result: { + kind: "ternary", + cond: { + kind: "bin", + op: "!==", + left: varRef(id.id, F64, loc), + right: numLit(0, loc), + type: BOOL, + loc, + }, + then: lowerer.coerceToExpected(varRef(id.id, F64, loc), declared), + else_: missing, + type: declared, + loc, + }, + type: declared, + loc, + }; + } + } +} + /** `t.name` as a VALUE — one entry in lower-exprs' property chain. Null * for other receivers/members (the chain keeps trying); unlowered * TestContext members fence member-qualified. */ @@ -985,7 +1068,17 @@ export function lowerTestCtxProperty( loc, }; } - if (expr.name.text === "assert") return null; // claimed by the call path + const member = expr.name.text; + if ( + member === "filePath" || + member === "attempt" || + member === "passed" || + member === "workerId" || + member === "tags" + ) { + return lowerTestCtxValue(lowerer, expr, member); + } + if (member === "assert") return null; // claimed by the call path lowerer.noLowering( `TestContext.${expr.name.text}`, expr, diff --git a/packages/compiler/src/frontend/lowering/lowerer.ts b/packages/compiler/src/frontend/lowering/lowerer.ts index 12deec7ad0..b77768c283 100644 --- a/packages/compiler/src/frontend/lowering/lowerer.ts +++ b/packages/compiler/src/frontend/lowering/lowerer.ts @@ -10,6 +10,12 @@ import { refuseUncheckedCycleReads, type CycleInitFlag, } from "./cycle-initialization.js"; +import { isJsonRequireDecl, type JsonRequireModule } from "./json-require.js"; +import { + checkRequireBindingAccesses, + type EarlyReference, + type RequireInitFlag, +} from "./require-initialization.js"; import { identityPreservingWidening } from "./coercions/identity.js"; import { scanDivergences } from "./divergences.js"; import { checkedClassAssertion } from "./class-assertions.js"; @@ -388,6 +394,7 @@ import { createRequireProgramModuleOf, createRequireSpecOf, fencedBuiltinImportOf, + esmImportBindingKind, } from "./builtins/module-bindings.js"; import { stripTypeCasts } from "./builtins/arguments.js"; import { lowerBuiltinModuleCall } from "./builtins/module-calls.js"; @@ -601,7 +608,9 @@ export type StaticCallableProjection = | { kind: "promisified-exec-file" } | { kind: "promisified-exec" } | { kind: "promisified-builtin"; module: string; member: string } - | { kind: "promisified-zlib"; member: string }; + | { kind: "promisified-zlib"; member: string } + | { kind: "promisified-crypto"; member: string } + | { kind: "promisified-dns"; member: string }; /** Own-property lookup for the surface tables. They are plain object * literals, so a bare `table[name]` would also find Object.prototype @@ -1795,6 +1804,11 @@ export class Lowerer { * typeParamBindings — the checker has no `T` to substitute, so the * binding rides the node-type accessor instead of mapType. */ implicitParamTypes: Map | null = null; + /** Immutable bindings that name a zlib stream class (`const C = + * zlib.Gzip`), keyed by the binding's symbol: the class's + * node_zlib_mode. No runtime slot exists: new, typeof, .name and === + * resolve at compile time, and instanceof checks the stream's mode. */ + readonly zlibClassAliases = new Map(); /** Whether some net.Socket in this program may get an encoding: a * setEncoding call whose receiver is a socket or an untyped value. * Computed once on first use (null until then). */ @@ -1958,6 +1972,26 @@ export class Lowerer { return fallback ?? null; } + /** The index value of a keyed read of a record's index signature that + * can answer undefined for a missing key while its checker type does not + * admit undefined, or null. A literal key naming a declared field is a + * field read. */ + optionalIndexSignatureRead(e: ts.ElementAccessExpression): IrType | null { + const receiver = panicSafe(() => this.mapTypeOf(this.typeOf(e.expression)), null); + if (receiver?.kind !== "record") return null; + const shape = this.shapes.get(receiver.shapeId); + if (!shape || shape.tuple || !shape.indexValue || shape.indexValue.kind === "dyn") return null; + const key = e.argumentExpression; + if ( + (ts.isStringLiteral(key) || ts.isNumericLiteral(key)) && + shape.fields.some((field) => field.name === key.text) + ) + return null; + const read = panicSafe(() => this.mapTypeOf(this.typeOf(e)), null); + if (read?.kind === "union" && this.armTag(read.unionId, UNDEFINED_T) >= 0) return null; + return shape.indexValue; + } + runtimeOptionalIdentifierValue( node: ts.Expression, ): { value: IrExpr; present: IrType; unionId: string } | null { @@ -2041,6 +2075,7 @@ export class Lowerer { value: IrExpr, expected: IrType, member: string, + action: "read" | "set" = "read", ): IrExpr | null { const source = this.runtimeOptionalSourceValue(node, value); if (source?.type.kind !== "union") return null; @@ -2083,7 +2118,7 @@ export class Lowerer { then: nodeThrowExpr( 1, "", - `Cannot read properties of ${unit} (reading '${member}')`, + `Cannot ${action} properties of ${unit} (${action === "read" ? "reading" : "setting"} '${member}')`, expected, loc, ), @@ -2442,6 +2477,10 @@ export class Lowerer { * prepended by lowerFileInit so the bindings are live before any * top-level statement runs. */ readonly jsonInitActions = new Map(); + /** CommonJS require() of JSON documents: the shared object per document + * and the calls reading it (json-require.ts). */ + readonly jsonRequireModules = new Map(); + readonly jsonRequireCalls = new Map(); /** Dynamic `import("literal")` resolutions, keyed * `fileName\u0000specifier` (collectDynamicImports fills it during npm * collection; lowerDynamicImportCall reads it per site). */ @@ -2609,6 +2648,10 @@ export class Lowerer { * by class name (cycle-initialization.ts). */ readonly classInitFlags = new Map(); readonly cycleInitDeclarations = new Map(); + /** CommonJS require declarations whose bindings code running before the + * require can read, and those references (require-initialization.ts). */ + readonly requireInitFlags = new Map(); + readonly requireEarlyReferences: EarlyReference[] = []; get ctx(): FnCtx { const top = this.fnStack[this.fnStack.length - 1]; @@ -3449,6 +3492,7 @@ export class Lowerer { return null; const directSpec = requireSpecOf(decl.initializer); if (directSpec !== null) { + if (isJsonRequireDecl(this, decl)) return null; return ( resolveImport(this.program, decl.getSourceFile(), directSpec, "require") ?? npmStaticDepSf7(this.program, decl.getSourceFile(), directSpec, "require") @@ -3932,6 +3976,18 @@ export class Lowerer { if (!s) return null; return s.flags & ts.SymbolFlags.Alias ? this.checker.getAliasedSymbol(s) : s; }; + // Fields that can be read before their assignment hold undefined then; + // their reads are runtime-optional values like missing array reads. + for (const info of this.classes.values()) { + if (!info.earlyReadFields || !info.decl?.name) continue; + const classSymbol = symbolOf(info.decl.name); + if (!classSymbol) continue; + const instance = this.checker.getDeclaredTypeOfSymbol(classSymbol); + for (const name of info.earlyReadFields) { + const property = this.checker.getPropertyOfType(instance, name); + if (property) optionalSymbols.add(property); + } + } const signatureOf = (node: ts.Identifier): FnSig | null => { const symbol = symbolOf(node); if (!symbol) return null; @@ -4077,6 +4133,7 @@ export class Lowerer { const t = mappedOrNull(receiver); return t?.kind === "bytes" ? t.elem : null; }, + isNumberRecordRead: (read) => this.optionalIndexSignatureRead(read)?.kind === "f64", isNumber: (node) => mappedOrNull(node)?.kind === "f64", isString: (node) => mappedOrNull(node)?.kind === "string", hasLength: (node) => { @@ -4212,8 +4269,26 @@ export class Lowerer { // Typed-array reads answer undefined for an invalid index too, unless // every consumer treats undefined like NaN. if (kind === "bytes") return !nanCoded.isNumericRead(e); + if (kind === "record") + return this.optionalIndexSignatureRead(e) !== null && !nanCoded.isNumericRead(e); return kind === "array"; }; + /** `r.name` where the name resolves to the receiver's index signature. */ + const isRecordIndexPropertyRead = (e: ts.PropertyAccessExpression): boolean => { + // A name that resolves to a declared member is not an index read, and + // only a receiver with an index signature maps to an indexed record. + if (symbolOf(e.name)) return false; + const receiverType = this.typeOf(e.expression); + if (this.checker.getIndexInfosOfType(receiverType).length === 0) return false; + const receiver = this.mapTypeOf(receiverType); + if (receiver?.kind !== "record") return false; + const shape = this.shapes.get(receiver.shapeId); + if (!shape || shape.tuple || !shape.indexValue || shape.indexValue.kind === "dyn") + return false; + if (shape.fields.some((field) => field.name === e.name.text)) return false; + const read = this.mapTypeOf(this.typeOf(e)); + return !(read?.kind === "union" && this.armTag(read.unionId, UNDEFINED_T) >= 0); + }; const isDynamicObjectEntryRead = (node: ts.Expression): boolean => { const read = peel(node); if (!ts.isElementAccessExpression(read) || !ts.isIdentifier(read.expression)) return false; @@ -4302,6 +4377,7 @@ export class Lowerer { if (assertsDefined(node)) return false; const e = peel(node); if (isIndexedRead(e)) return true; + if (ts.isPropertyAccessExpression(e) && isRecordIndexPropertyRead(e)) return true; if (ts.isPropertyAccessExpression(e) && isOptionalProcessStreamProperty(this, e)) return true; if (ts.isPropertyAccessExpression(e) && isSettledIteratorValueRead(this, e)) return true; if (ts.isPropertyAccessExpression(e) && isOsConstantsSymbol(symbolOf(e.expression), this)) @@ -4526,6 +4602,7 @@ export class Lowerer { return out; }; const scanPattern = (name: ts.BindingName, init: ts.Expression): boolean => { + if (ts.isObjectBindingPattern(name)) return scanRecordPattern(name, init); if (!ts.isArrayBindingPattern(name)) return false; const sourceType = this.mapTypeOf(this.typeOf(init)); if (sourceType?.kind !== "array") return false; @@ -4561,6 +4638,40 @@ export class Lowerer { }); return changed; }; + /** `const { a, b } = table` over an index-signature record: a name that + * is not a declared field reads the index signature and can be + * missing. */ + const scanRecordPattern = (name: ts.ObjectBindingPattern, init: ts.Expression): boolean => { + const sourceType = this.mapTypeOf(this.typeOf(init)); + if (sourceType?.kind !== "record") return false; + const shape = this.shapes.get(sourceType.shapeId); + const indexValue = shape?.indexValue; + if (!shape || shape.tuple || !indexValue || indexValue.kind === "dyn") return false; + let changed = false; + for (const el of name.elements) { + if (el.dotDotDotToken || el.initializer || !ts.isIdentifier(el.name)) continue; + const key = el.propertyName ?? el.name; + if (!ts.isIdentifier(key) && !ts.isStringLiteral(key) && !ts.isNumericLiteral(key)) + continue; + if (shape.fields.some((field) => field.name === key.text)) continue; + const symbol = symbolOf(el.name); + if (!symbol || optionalSymbols.has(symbol)) continue; + const read = this.mapTypeOf(this.typeOf(el.name)); + if (read?.kind === "union" && this.armTag(read.unionId, UNDEFINED_T) >= 0) continue; + const widened = this.withUndefinedArmOf(indexValue); + if (!widened) continue; + optionalSymbols.add(symbol); + provenance?.note("pattern-binding", el.name, init); + this.runtimeOptionalBindingTypes.set(symbol, widened); + const global = this.globalsBySymbol.get(symbol); + if (global) { + global.type = widened; + this.runtimeOptionalGlobals.add(global); + } + changed = true; + } + return changed; + }; const hofCallbackIndices = (name: string, hasInitialValue: boolean): number[] | null => { if (name === "reduce" || name === "reduceRight") return hasInitialValue ? [1] : [0, 1]; if ( @@ -5850,25 +5961,61 @@ export class Lowerer { addClassFields(recordFlows.bindingClass(symbol), fields); for (const [symbol, fields] of returnFields) addClassFields(recordFlows.returnClass(symbol), fields); + // A record field that flows into other slots (`fill(args, state.parsed)`) + // widens with its owner's class: every member takes the field's widened + // layout, so the field and the slots it reaches share one record. + const nestedFields = new Map>(); + for (let changed = true; changed;) { + changed = false; + for (const { owner, name, classId } of recordFlows.fields()) { + if (nestedFields.get(owner)?.has(name)) continue; + if (!(classFields.has(classId) || nestedFields.has(classId))) continue; + if (!recordFlows.closed(classId)) continue; + const byName = nestedFields.get(owner) ?? new Map(); + byName.set(name, classId); + nestedFields.set(owner, byName); + changed = true; + } + } const settledFields = ( id: number | null, own: ReadonlySet | undefined, ): ReadonlySet | undefined => id === null ? own - : classFields.has(id) && recordFlows.closed(id) - ? classFields.get(id) + : (classFields.has(id) || nestedFields.has(id)) && recordFlows.closed(id) + ? (classFields.get(id) ?? new Set()) : undefined; - const widenRecordFields = (type: IrType, fields: ReadonlySet): IrType => { + const widenRecordFields = ( + type: IrType, + fields: ReadonlySet, + id: number | null = null, + seen: ReadonlySet = new Set(), + ): IrType => { let promoted = type; + const fieldOf = (name: string) => + promoted.kind === "record" + ? this.shapes.get(promoted.shapeId)?.fields.find((f) => f.name === name) + : undefined; for (const field of fields) { - const source = - promoted.kind === "record" - ? this.shapes.get(promoted.shapeId)?.fields.find((f) => f.name === field) - : undefined; + const source = fieldOf(field); if (source) promoted = this.runtimeOptionalRecordField(promoted, field, addUndefined(source.type)); } + if (id === null || seen.has(id)) return promoted; + const nested = nestedFields.get(id); + if (!nested) return promoted; + for (const [field, inner] of nested) { + const source = fieldOf(field); + if (source?.type.kind !== "record") continue; + const widened = widenRecordFields( + source.type, + classFields.get(inner) ?? new Set(), + inner, + new Set([...seen, id]), + ); + promoted = this.runtimeOptionalRecordField(promoted, field, widened); + } return promoted; }; for (const [symbol, id] of recordFlows.bindings()) @@ -5883,9 +6030,10 @@ export class Lowerer { for (const [bound, { target, index }] of recordParameterSlots) { const shape = signatureBySymbol.get(target)?.params[index]; if (!shape || shape.type.kind !== "record") continue; - const fields = settledFields(recordFlows.bindingClass(bound), optionalFields.get(bound)); + const id = recordFlows.bindingClass(bound); + const fields = settledFields(id, optionalFields.get(bound)); if (!fields) continue; - const promoted = widenRecordFields(shape.type, fields); + const promoted = widenRecordFields(shape.type, fields, id); if (typeEquals(promoted, shape.type)) continue; shape.type = promoted; this.runtimeOptionalBindingTypes.set(bound, promoted); @@ -5894,7 +6042,7 @@ export class Lowerer { const id = recordFlows.returnClass(symbol); const fields = id === null ? undefined : settledFields(id, undefined); if (fields && sig.returnType.kind === "record") { - const next = widenRecordFields(sig.returnType, fields); + const next = widenRecordFields(sig.returnType, fields, id); sig.returnType = next; if (sig.top) sig.top.returnType = next; if (sig.method) sig.method.ret = next; @@ -5980,7 +6128,8 @@ export class Lowerer { if (global) global.type = promoted; } } - const fields = settledFields(recordFlows.bindingClass(symbol), optionalFields.get(symbol)); + const flowClass = recordFlows.bindingClass(symbol); + const fields = settledFields(flowClass, optionalFields.get(symbol)); if (!dynamicObjectEntryRows.has(symbol) && !optionalSymbols.has(symbol) && !fields) return; const current = this.mapTypeOf(this.typeOf(node.name)); if (!current) return; @@ -5997,7 +6146,8 @@ export class Lowerer { }; } } - if (fields && promoted.kind === "record") promoted = widenRecordFields(promoted, fields); + if (fields && promoted.kind === "record") + promoted = widenRecordFields(promoted, fields, flowClass); if (!typeEquals(promoted, current)) { this.runtimeOptionalBindingTypes.set(symbol, promoted); const global = ts.isIdentifier(node.name) @@ -6288,6 +6438,7 @@ export class Lowerer { // slots), uniformly across params/locals/globals/fields/body types so // every producer and consumer agrees. Programs with no unregistered // reference are untouched — byte-stability holds. + checkRequireBindingAccesses(this, functions); const module: IrModule | null = this.diags.length > 0 ? null @@ -8506,9 +8657,18 @@ export class Lowerer { // initial contextual layout was chosen. Its known discriminator still // selects the destination arm; validate that payload before wrapping. // Work with the already lowered value so property effects run once. + // A type assertion keeps the literal's own record layout, which can + // equal another arm's layout (`{ kind: "return", value, loc } as Stmt` + // when a `throw` arm has the same fields), so assertions are looked + // through as well. if (expr.type.kind === "record" && expected.kind === "union") { let literal = node; - while (ts.isParenthesizedExpression(literal) || ts.isSatisfiesExpression(literal)) + while ( + ts.isParenthesizedExpression(literal) || + ts.isSatisfiesExpression(literal) || + ts.isAsExpression(literal) || + ts.isTypeAssertion(literal) + ) literal = literal.expression; const def = this.unions.get(expected.unionId); if (def?.discriminant && ts.isObjectLiteralExpression(literal)) { @@ -11462,9 +11622,19 @@ export class Lowerer { return pathModuleValue(this, `path/${bi.member}`, loc); } // events.defaultMaxListeners READS the process-wide default (its - // write twin routes through emitter.setDefaultMaxChk). + // write twin routes through emitter.setDefaultMaxChk). Through an ES + // module namespace it reads the namespace's copy instead. if (bi.module === "events" && bi.member === "defaultMaxListeners") { - return { kind: "libCall", fn: "emitter.getDefaultMax", args: [], type: F64, loc }; + const esm = + ts.isIdentifier(expr.expression) && + esmImportBindingKind(this, expr.expression) === "namespace"; + return { + kind: "libCall", + fn: esm ? "emitter.esmDefaultMax" : "emitter.getDefaultMax", + args: [], + type: F64, + loc, + }; } if (bi.module === "worker_threads") { const value = lowerWorkerMetadata(bi.member, loc); @@ -11709,8 +11879,8 @@ export class Lowerer { return lowerSpawnCall(this, expr, loc); } - lowerExecFileCall(expr: ts.CallExpression, loc: SrcLoc): IrExpr { - return lowerExecFileCall(this, expr, loc); + lowerExecFileCall(expr: ts.CallExpression, loc: SrcLoc, shell = false): IrExpr { + return lowerExecFileCall(this, expr, loc, shell); } lowerExecSyncCall(expr: ts.CallExpression, shell: boolean, loc: SrcLoc): IrExpr { diff --git a/packages/compiler/src/frontend/lowering/nan-coded-reads.ts b/packages/compiler/src/frontend/lowering/nan-coded-reads.ts index f7023cc52d..b84672406e 100644 --- a/packages/compiler/src/frontend/lowering/nan-coded-reads.ts +++ b/packages/compiler/src/frontend/lowering/nan-coded-reads.ts @@ -43,6 +43,9 @@ export interface NanCodedHost { declarationOf(symbol: ts.Symbol): ts.Node | undefined; /** The element kind of a typed-array receiver, or null. */ bytesElemOf(receiver: ts.Expression): IrBytesElem | null; + /** The read is a keyed read of a record's `number` index signature, + * which answers undefined for a missing key. */ + isNumberRecordRead(read: ts.ElementAccessExpression): boolean; /** The static IR type of the node is a plain number. */ isNumber(node: ts.Node): boolean; isString(node: ts.Expression): boolean; @@ -315,7 +318,10 @@ export class NanCodedReads { return; } if (ts.isElementAccessExpression(node) && this.isReadCandidate(node)) { - const elem = this.host.bytesElemOf(node.expression); + // A record's number values may hold a genuine NaN, like f64 elements. + const elem = + this.host.bytesElemOf(node.expression) ?? + (this.host.isNumberRecordRead(node) ? "f64" : null); if (elem) candidates.set(node, elem); } }); @@ -393,7 +399,13 @@ export class NanCodedReads { // native build of the compiler. const cached = this.symbolCache.get(node); if (cached !== undefined) return cached; - const symbol = this.host.symbolOf(node); + let symbol = this.host.symbolOf(node); + // A method read through an instance (`h.get`) can resolve to another + // symbol than its declaration name; slots and call sites key on the + // declaration's own symbol. + const decl = symbol ? this.host.declarationOf(symbol) : undefined; + if (decl && ts.isMethodDeclaration(decl) && decl.name !== node && ts.isIdentifier(decl.name)) + symbol = this.symbolOf(decl.name) ?? symbol; this.symbolCache.set(node, symbol); return symbol; } diff --git a/packages/compiler/src/frontend/lowering/narrowing-stability.ts b/packages/compiler/src/frontend/lowering/narrowing-stability.ts index 4ae8eec675..5bd563d9d3 100644 --- a/packages/compiler/src/frontend/lowering/narrowing-stability.ts +++ b/packages/compiler/src/frontend/lowering/narrowing-stability.ts @@ -11,9 +11,10 @@ import type { Lowerer } from "./lowerer.js"; * after assignments and tests, and keeps that narrowing across calls: a * callback, method or getter that clears or replaces the property (or a * closure that reassigns the binding) leaves the reference typed by its old - * value. Lowering bridges a narrowed union read with an UNCHECKED payload - * extraction and folds `?.`, truthiness and `typeof` by the narrowed type, - * so such a read would interpret the wrong arm. This analysis decides when + * value. Lowering bridges a narrowed union read with a payload extraction + * that traps on the wrong arm and folds `?.`, truthiness and `typeof` by the + * narrowed type, so such a read would abort or fold to the old value instead + * of throwing the catchable TypeError. This analysis decides when * the narrowing is provably current: * * - every property in the reference is construction-only (no store to its @@ -30,6 +31,10 @@ import type { Lowerer } from "./lowerer.js"; * A loop around the use that starts inside that span contributes its * whole body, since its back edge reaches the use again. * + * A binding initialized or assigned from a read whose narrowing may be + * stale inherits that narrowing (`const held: Expr = current` keeps the arm + * `current` was typed with), so its narrowed reads are never trusted. + * * TypeScript never carries a property narrowing into a nested function, * and every narrowing (an assignment, test, predicate or assertion of the * reference) is an occurrence of the reference, so a narrowed use always @@ -42,6 +47,7 @@ export class NarrowingStability { private readonly verdicts = new Map(); private readonly effects = new Map(); private readonly capturedMutable = new Map(); + private readonly staleBindings = new Map(); private readonly identifierWrites = new Map>(); private readonly widened = new Map(); private readonly plainContainers = new Map(); @@ -90,7 +96,7 @@ export class NarrowingStability { } if (ts.isIdentifier(node)) { const symbol = this.bindingOf(node); - if (!symbol || !this.isCapturedMutable(symbol)) return false; + if (!symbol || !this.mayHoldStaleNarrowing(symbol)) return false; return !this.trusted(node); } return false; @@ -113,7 +119,8 @@ export class NarrowingStability { declared = this.declaredPropertyType(node); } else { const symbol = this.bindingOf(node); - declared = symbol && this.isCapturedMutable(symbol) ? checker.getTypeOfSymbol(symbol) : null; + declared = + symbol && this.mayHoldStaleNarrowing(symbol) ? checker.getTypeOfSymbol(symbol) : null; } if (declared === null) return null; const narrowedTs = checker.getTypeAtLocation(node); @@ -245,6 +252,10 @@ export class NarrowingStability { ) return false; } + if (ts.isIdentifier(chain.root)) { + const root = this.bindingOf(chain.root); + if (root !== null && this.inheritsStaleNarrowing(root)) return false; + } const rootStable = chain.root.kind === ts.SyntaxKind.ThisKeyword || (ts.isIdentifier(chain.root) && !this.rootCapturedMutable(chain.root)); @@ -346,6 +357,63 @@ export class NarrowingStability { return !program.wildcard && !program.mutable.has(name) && !program.accessors.has(name); } + /** A binding whose narrowed reads may observe a narrowing that no longer + * holds: another function assigns it, or it inherits a stale narrowing. */ + private mayHoldStaleNarrowing(symbol: ts.Symbol): boolean { + return this.isCapturedMutable(symbol) || this.inheritsStaleNarrowing(symbol); + } + + /** The binding is initialized or assigned from a narrowed read that may be + * stale, so TypeScript's narrowing of the binding may be stale too. */ + private inheritsStaleNarrowing(symbol: ts.Symbol): boolean { + const cached = this.staleBindings.get(symbol); + if (cached !== undefined) return cached; + // A cycle of bindings assigned from each other contributes nothing. + this.staleBindings.set(symbol, false); + let result = false; + try { + const decl = this.lowerer.checker.valueDeclarationOf(symbol); + if (decl !== undefined && ts.isVariableDeclaration(decl)) { + if (decl.initializer !== undefined && this.mayBeStale(decl.initializer)) result = true; + else { + for (const write of this.writesIn(decl.getSourceFile()).get(symbol.name) ?? []) { + const assignment = write.parent; + if ( + ts.isBinaryExpression(assignment) && + assignment.left === write && + assignment.operatorToken.kind === ts.SyntaxKind.EqualsToken && + this.lowerer.resolveValueSymbol(write) === symbol && + this.mayBeStale(assignment.right) + ) { + result = true; + break; + } + } + } + } + } finally { + this.staleBindings.set(symbol, result); + } + return result; + } + + /** A reference TypeScript narrows below its declared type where the + * narrowing may no longer hold. */ + private mayBeStale(expression: ts.Expression): boolean { + let e = expression; + while (ts.isParenthesizedExpression(e)) e = e.expression; + if (!ts.isIdentifier(e) && !ts.isPropertyAccessExpression(e)) return false; + const checker = this.lowerer.checker; + let declared: ts.Type | null; + if (ts.isPropertyAccessExpression(e)) declared = this.declaredPropertyType(e); + else { + const symbol = this.bindingOf(e); + declared = symbol === null ? null : checker.getTypeOfSymbol(symbol); + } + if (declared === null || checker.getTypeAtLocation(e) === declared) return false; + return !this.trusted(e); + } + /** A `let`/`var`/parameter binding assigned from a function other than * the one that declares it: any call may change it. */ private isCapturedMutable(symbol: ts.Symbol): boolean { diff --git a/packages/compiler/src/frontend/lowering/require-initialization.ts b/packages/compiler/src/frontend/lowering/require-initialization.ts new file mode 100644 index 0000000000..5375f3792e --- /dev/null +++ b/packages/compiler/src/frontend/lowering/require-initialization.ts @@ -0,0 +1,262 @@ +/* CommonJS require bindings read before their require ran. A top-level + * `const x = require("./dep")` binds x AT the require: Node throws "Cannot + * access 'x' before initialization" when code that runs earlier (a hoisted + * function called above the require, say) reads it. The lowering keeps + * require bindings as module aliases, so the reads themselves cannot + * observe the order. When preflight finds code that can reach a binding + * before its require (requireEarlyReference), the declaration gets a BOOL + * initialization flag, set right after the require evaluated the module, + * and every reference that can run earlier checks it first — the same + * module.tdzCheck the import-cycle bindings use. A reference whose lowering + * leaves no expression to check keeps the SC1013 fence. */ +import * as ts from "../ts7/adapter.js"; +import type { Lowerer } from "./lowerer.js"; +import { + inDeferredPosition, + isCjsJsFile, + locOf, + requireEarlyReference, + requireSpecOf, +} from "../program.js"; +import { unsupportedDiag } from "../../diagnostics/diagnostic.js"; +import { + BOOL, + STRING, + VOID, + type IrExpr, + type IrFunction, + type IrStmt, + type SrcLoc, +} from "../../ir/ir.js"; +import { mapExprChildren, mapStmtChildren } from "../../ir/traverse.js"; + +/** One flagged require declaration. */ +export interface RequireInitFlag { + flagId: string; + /** The binding names, by symbol. */ + names: Map; +} + +/** A reference that can run before its require: the identifier, the + * outermost expression whose evaluation starts with reading it, and the + * body of the innermost function holding it (null at the top level). */ +export interface EarlyReference { + flag: RequireInitFlag; + name: string; + ref: SrcLoc; + chain: SrcLoc; + body: SrcLoc | null; + node: ts.Identifier; +} + +function boundNames(name: ts.BindingName): ts.Identifier[] { + if (ts.isIdentifier(name)) return [name]; + const out: ts.Identifier[] = []; + for (const el of name.elements) { + if (ts.isOmittedExpression(el) || el.name === undefined) continue; + out.push(...boundNames(el.name)); + } + return out; +} + +/** The outermost node whose evaluation begins by evaluating `ref`: member + * reads, calls and constructions through it, and the transparent wrappers + * around them. */ +function chainOf(ref: ts.Identifier): ts.Node { + let node: ts.Node = ref; + for (;;) { + const parent = node.parent; + if (parent === undefined) return node; + if ( + ((ts.isPropertyAccessExpression(parent) || + ts.isElementAccessExpression(parent) || + ts.isCallExpression(parent) || + ts.isNewExpression(parent)) && + parent.expression === node) || + (ts.isTaggedTemplateExpression(parent) && parent.tag === node) || + ts.isParenthesizedExpression(parent) || + ts.isNonNullExpression(parent) || + (ts.isAsExpression(parent) && parent.expression === node) || + (ts.isSatisfiesExpression(parent) && parent.expression === node) + ) { + node = parent; + continue; + } + return node; + } +} + +function enclosingBody(node: ts.Node): ts.Node | null { + for (let p = node.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) { + if (ts.isFunctionLike(p)) { + const body = (p as { body?: ts.Node }).body; + if (body !== undefined) return body; + } + if (ts.isPropertyDeclaration(p) && p.initializer !== undefined) return p.initializer; + } + return null; +} + +/** Registers a flag for every top-level const/let require declaration of a + * CommonJS module whose bindings earlier code can reach, and collects the + * references that can run before the require. Runs once per lowering pass, + * after global collection. */ +export function markRequireEarlyBindings(lowerer: Lowerer): void { + for (const sf of lowerer.moduleOrder) { + if (sf.isDeclarationFile || !isCjsJsFile(sf, lowerer.program)) continue; + const declared: { decl: ts.VariableDeclaration; stmt: ts.VariableStatement }[] = []; + for (const stmt of sf.statements) { + if (!ts.isVariableStatement(stmt)) continue; + if ((stmt.declarationList.flags & (ts.NodeFlags.Const | ts.NodeFlags.Let)) === 0) continue; + for (const decl of stmt.declarationList.declarations) { + if (decl.initializer === undefined || requireSpecOf(decl.initializer) === null) continue; + if (requireEarlyReference(lowerer.program, decl) === null) continue; + declared.push({ decl, stmt }); + } + } + if (declared.length === 0) continue; + const rawTag = lowerer.fileTag.get(sf) ?? ""; + const tag = rawTag === "" ? "e." : rawTag.replace(/^%/, ""); + const bySymbol = new Map(); + for (const { decl } of declared) { + const flagId = `%g.${tag}%required.${decl.getStart(sf)}`; + lowerer.globalsList.push({ id: flagId, name: "%initialized", type: BOOL, mutable: true }); + const flag: RequireInitFlag = { flagId, names: new Map() }; + for (const id of boundNames(decl.name)) { + const symbol = lowerer.checker.getSymbolAtLocation(id); + if (symbol === undefined) continue; + flag.names.set(symbol, id.text); + bySymbol.set(symbol, { flag, decl }); + } + lowerer.requireInitFlags.set(decl, flag); + } + const names = new Set(); + for (const { flag } of bySymbol.values()) for (const n of flag.names.values()) names.add(n); + const idents: ts.Identifier[] = []; + ts.walkPreorder(sf, (node) => { + if (ts.isIdentifier(node) && names.has(node.text)) idents.push(node); + return undefined; + }); + lowerer.checker.prefetchSymbolNodesExact(idents); + for (const id of idents) { + const parent = id.parent; + const symbol = + parent !== undefined && ts.isShorthandPropertyAssignment(parent) && parent.name === id + ? lowerer.checker.getShorthandAssignmentValueSymbol(parent) + : lowerer.checker.getSymbolAtLocation(id); + const owner = symbol === undefined ? undefined : bySymbol.get(symbol); + if (owner === undefined) continue; + if (boundNames(owner.decl.name).includes(id)) continue; + // Code after the require runs after it, except inside functions. + if (id.getStart(sf) >= owner.decl.getEnd() && !inDeferredPosition(id)) continue; + const body = enclosingBody(id); + lowerer.requireEarlyReferences.push({ + flag: owner.flag, + name: owner.flag.names.get(symbol!)!, + ref: locOf(id), + chain: locOf(chainOf(id)), + body: body === null ? null : locOf(body), + node: id, + }); + } + } +} + +/** The statement marking a flagged require declaration initialized, placed + * right after its require evaluated the module. */ +export function requireInitFlagStmt(lowerer: Lowerer, decl: ts.VariableDeclaration): IrStmt | null { + const flag = lowerer.requireInitFlags.get(decl); + if (flag === undefined) return null; + const loc = locOf(decl); + return { + kind: "assign", + localId: flag.flagId, + value: { kind: "boolLit", value: true, type: BOOL, loc }, + loc, + }; +} + +const within = (inner: SrcLoc, outer: SrcLoc): boolean => + inner.file === outer.file && outer.start <= inner.start && inner.end <= outer.end; + +/** Inserts the initialization check before the outermost expression built + * for each early reference, and refuses a reference whose lowered code + * holds no expression for it. */ +export function checkRequireBindingAccesses(lowerer: Lowerer, functions: IrFunction[]): void { + const refs = lowerer.requireEarlyReferences; + if (refs.length === 0) return; + const byFile = new Map(); + for (const r of refs) { + const list = byFile.get(r.ref.file); + if (list) list.push(r); + else byFile.set(r.ref.file, [r]); + } + const covered = new Set(); + const loweredBodies = new Set(); + const claimed = new Set(); + const check = (r: EarlyReference, loc: SrcLoc): IrStmt => ({ + kind: "exprStmt", + expr: { + kind: "intrinsic", + name: "module.tdzCheck", + args: [ + { kind: "varRef", localId: r.flag.flagId, type: BOOL, loc }, + { kind: "strLit", value: r.name, type: STRING, loc }, + ], + type: VOID, + loc, + }, + loc, + }); + const note = (loc: SrcLoc | undefined): EarlyReference[] => { + if (loc === undefined) return []; + const list = byFile.get(loc.file); + if (list === undefined) return []; + const mine: EarlyReference[] = []; + for (const r of list) { + if (r.body !== null && !loweredBodies.has(r) && within(loc, r.body)) loweredBodies.add(r); + if (!claimed.has(r) && within(r.ref, loc) && within(loc, r.chain)) mine.push(r); + } + return mine; + }; + const rewriteExpr = (e: IrExpr): IrExpr => { + const mine = note(e.loc); + for (const r of mine) claimed.add(r); + const out = mapExprChildren(e, rewriteExpr, rewriteStmt); + for (const r of mine) { + claimed.delete(r); + covered.add(r); + } + if (mine.length === 0) return out; + const flags = new Set(); + const stmts: IrStmt[] = []; + for (const r of mine) { + if (flags.has(r.flag.flagId)) continue; + flags.add(r.flag.flagId); + stmts.push(check(r, e.loc)); + } + return { kind: "seqExpr", stmts, result: out, type: out.type, loc: out.loc }; + }; + const rewriteStmt = (s: IrStmt): IrStmt => { + note(s.loc); + return mapStmtChildren(s, rewriteExpr, rewriteStmt); + }; + for (const fn of functions) fn.body = fn.body.map(rewriteStmt); + if (lowerer.remainder) return; + const reported = new Set(); + for (const r of refs) { + if (covered.has(r)) continue; + // A function body that never lowered never runs. + if (r.body !== null && !loweredBodies.has(r)) continue; + const key = `${r.ref.file}:${r.ref.start}`; + if (reported.has(key)) continue; + reported.add(key); + lowerer.pushDiag( + unsupportedDiag( + "SC1013", + r.ref, + `reading require() binding '${r.name}' where it can run before the require (this use has no checked form for Node's ReferenceError — move the require above the code that can run first)`, + ), + ); + } +} diff --git a/packages/compiler/src/frontend/lowering/runtime-optional-records.test.ts b/packages/compiler/src/frontend/lowering/runtime-optional-records.test.ts index 2ca59739dd..f956946241 100644 --- a/packages/compiler/src/frontend/lowering/runtime-optional-records.test.ts +++ b/packages/compiler/src/frontend/lowering/runtime-optional-records.test.ts @@ -53,6 +53,8 @@ function classes(source: string): { : null, declarationOf: (symbol) => declarations.get(symbol), recordShape: (node) => { + // Field slots: a binding's field read where a record is expected. + if (ts.isPropertyAccessExpression(node)) return "R"; const declaration = node.parent; if (!declaration) return null; if (ts.isParameter(declaration)) return annotated(declaration.type); @@ -183,3 +185,19 @@ test("assignments join their target and a foreign value opens it", () => { expect(flows.closed("target")).toBe(true); expect(flows.closed("foreign")).toBe(false); }); + +test("a binding's record field joins the slots it reaches and widens with its owner", () => { + const flows = classes(` + function fill(into: R): void { into.v = 1; } + const state = { parsed: { v: 0 } }; + fill(state.parsed); + function mark(m: R): void { m.v = 2; } + const spread = { parsed: { v: 0 } }; + mark(spread.parsed); + console.log({ ...spread }); + `); + expect(flows.closed("into")).toBe(true); + expect(flows.closed("state")).toBe(true); + // A spread copies the field out of its owner, so the record keeps its layout. + expect(flows.closed("m")).toBe(false); +}); diff --git a/packages/compiler/src/frontend/lowering/runtime-optional-records.ts b/packages/compiler/src/frontend/lowering/runtime-optional-records.ts index 0ecdd62b34..334f94d4fc 100644 --- a/packages/compiler/src/frontend/lowering/runtime-optional-records.ts +++ b/packages/compiler/src/frontend/lowering/runtime-optional-records.ts @@ -85,6 +85,20 @@ export class RecordFlowClasses { /** Names written as shorthand properties (`{ p }`): the checker resolves * those to the property, so a binding of that name stays open. */ private readonly shorthandNames = new Set(); + /** Record fields of tracked bindings that flow between slots + * (`fill(args, state.parsed)`), by owner symbol and field name. A field + * slot widens only with its owner, whose layout holds it. */ + private readonly fieldSlots = new Map>(); + /** The owner binding slot of each field slot. */ + private readonly fieldOwners = new Map(); + /** Object literals entering each slot; their property values enter the + * class's field slots. */ + private readonly literalSources: { slot: number; literal: ts.ObjectLiteralExpression }[] = []; + /** Slots whose field values are copied out whole (a spread, a + * destructuring, `Object.values`), so a nested record would convert. */ + private readonly copiedOut = new Set(); + /** Counts merges and openings, so field joins run until stable. */ + private changes = 0; constructor( private readonly files: readonly ts.SourceFile[], @@ -94,6 +108,7 @@ export class RecordFlowClasses { this.collectFunctions(); this.collectSlots(); this.connect(); + this.connectFields(); } /** The class id of a binding or parameter slot, or null when the symbol @@ -160,9 +175,27 @@ export class RecordFlowClasses { shape = own; } this.shapeChecked.set(root, uniform); + // A field slot takes a widened layout only with its owner's class. + for (const [slot, owner] of this.fieldOwners) { + if (!uniform) break; + if (this.find(slot) === root && !this.closed(owner)) uniform = false; + } + this.shapeChecked.set(root, uniform); return uniform; } + /** Every field slot: its owner's class, the field name and its class. */ + fields(): { owner: number; name: string; classId: number }[] { + const out: { owner: number; name: string; classId: number }[] = []; + for (const byName of this.fieldSlots.values()) { + for (const [name, slot] of byName) { + const owner = this.fieldOwners.get(slot)!; + out.push({ owner: this.find(owner), name, classId: this.find(slot) }); + } + } + return out; + } + private newSlot(node: ts.Node, isReturn = false): number { const id = this.parent.length; this.parent.push(id); @@ -189,10 +222,109 @@ export class RecordFlowClasses { if (ra === rb) return; this.parent[rb] = ra; this.open[ra] = this.open[ra]! || this.open[rb]!; + this.changes++; } private markOpen(slot: number): void { - this.open[this.find(slot)] = true; + const root = this.find(slot); + if (!this.open[root]) this.changes++; + this.open[root] = true; + } + + /** The slot of a tracked binding's record field, created on first use. */ + private fieldSlot(owner: ts.Symbol, name: string, node: ts.Node): number | undefined { + const ownerSlot = this.bindingSlots.get(owner); + if (ownerSlot === undefined) return undefined; + let byName = this.fieldSlots.get(owner); + if (!byName) { + byName = new Map(); + this.fieldSlots.set(owner, byName); + } + let slot = byName.get(name); + if (slot === undefined) { + slot = this.newSlot(node); + byName.set(name, slot); + this.fieldOwners.set(slot, ownerSlot); + this.changes++; + } + return slot; + } + + /** `binding.name` where the binding is a tracked slot. */ + private fieldAccess(node: ts.Node): { owner: ts.Symbol; name: string } | null { + if (!ts.isPropertyAccessExpression(node) || !ts.isIdentifier(node.expression)) return null; + if (!ts.isIdentifier(node.name)) return null; + const owner = this.host.symbolOf(node.expression); + return owner && this.bindingSlots.has(owner) ? { owner, name: node.name.text } : null; + } + + /** Joins each field slot with the same field of every member of its + * owner's class, gives it the values that literals and assignments store + * in the field, and opens it with its owner, until no class changes. */ + private connectFields(): void { + const visitedUses = new Set(); + const visitedLiterals = new Set(); + for (let before = -1; before !== this.changes;) { + before = this.changes; + // Grouped once per pass; joins made during a pass show in the next. + const members = new Map(); + for (const [symbol, slot] of this.bindingSlots) addTo(members, this.find(slot), symbol); + const literals = new Map(); + this.literalSources.forEach(({ slot }, i) => addTo(literals, this.find(slot), i)); + const copied = new Set([...this.copiedOut].map((slot) => this.find(slot))); + for (const [symbol, byName] of [...this.fieldSlots]) { + const root = this.find(this.bindingSlots.get(symbol)!); + for (const [name, field] of [...byName]) { + for (const member of members.get(root) ?? []) { + const peer = this.fieldSlot(member, name, this.slotNodes[field]!)!; + this.union(field, peer); + const key = `${this.bindingSlots.get(member)}:${name}`; + if (visitedUses.has(key)) continue; + visitedUses.add(key); + for (const reference of this.referencesOf(member)) this.fieldUse(reference, name, peer); + } + for (const i of literals.get(root) ?? []) { + const key = `${i}:${name}`; + if (visitedLiterals.has(key)) continue; + visitedLiterals.add(key); + for (const property of this.literalSources[i]!.literal.properties) { + if (ts.isSpreadAssignment(property)) { + this.markOpen(field); + continue; + } + const propertyName = property.name; + if ( + !propertyName || + !(ts.isIdentifier(propertyName) || ts.isStringLiteral(propertyName)) || + propertyName.text !== name + ) + continue; + if (ts.isPropertyAssignment(property)) this.source(property.initializer, field); + else if (ts.isShorthandPropertyAssignment(property)) + this.source(property.name as ts.Identifier, field); + else this.markOpen(field); + } + } + if (this.open[this.find(root)] || copied.has(root)) this.markOpen(field); + } + } + } + } + + /** Checks one reference to a field slot's owner: a read of the field + * (`owner.name`, `owner["name"]`) must be a member use, and a computed + * key can reach the field too. */ + private fieldUse(reference: ts.Identifier, name: string, field: number): void { + const access = reference.parent; + if (!access || (access as { expression?: ts.Node }).expression !== reference) return; + if (ts.isPropertyAccessExpression(access)) { + if (access.name.text === name && !this.use(access, field)) this.markOpen(field); + return; + } + if (!ts.isElementAccessExpression(access)) return; + const key = peel(access.argumentExpression); + if (!ts.isStringLiteral(key) && !ts.isNoSubstitutionTemplateLiteral(key)) this.markOpen(field); + else if (key.text === name && !this.use(access, field)) this.markOpen(field); } private index(): void { @@ -473,7 +605,15 @@ export class RecordFlowClasses { * when the value can be an object some other storage already holds. */ private source(node: ts.Expression, slot: number): void { const e = peel(node); - if (ts.isObjectLiteralExpression(e)) return; + if (ts.isObjectLiteralExpression(e)) { + this.literalSources.push({ slot, literal: e }); + return; + } + const access = this.fieldAccess(e); + if (access) { + this.union(slot, this.fieldSlot(access.owner, access.name, e)!); + return; + } if (ts.isIdentifier(e)) { const symbol = this.host.symbolOf(e); const other = symbol ? this.bindingSlots.get(symbol) : undefined; @@ -519,7 +659,7 @@ export class RecordFlowClasses { /** True when this reference to a member only reads or writes its fields, * passes the record to another member (a flow `source` joins), or * inspects it without keeping or converting it. */ - private use(reference: ts.Identifier, slot: number): boolean { + private use(reference: ts.Expression, slot: number): boolean { let value: ts.Node = reference; for (;;) { const user = value.parent; @@ -536,7 +676,10 @@ export class RecordFlowClasses { } if (ts.isPropertyAccessExpression(user) && user.expression === value) return true; if (ts.isElementAccessExpression(user) && user.expression === value) return true; - if (ts.isSpreadAssignment(user)) return true; + if (ts.isSpreadAssignment(user)) { + this.copiedOut.add(slot); + return true; + } if (ts.isTemplateSpan(user) || ts.isTypeOfExpression(user)) return true; if (ts.isExpressionStatement(user)) return true; if (ts.isPrefixUnaryExpression(user) && user.operator === ts.SyntaxKind.ExclamationToken) @@ -588,7 +731,8 @@ export class RecordFlowClasses { op === ts.SyntaxKind.AmpersandAmpersandEqualsToken; // A record assigned to a member joins it when the member's own // reference is visited, below. - if (assigns && user.right === value) return this.isMember(user.left); + if (assigns && user.right === value) + return this.isMember(user.left) || this.isFieldMember(user.left); if (assigns && user.left === value && value === reference) { this.source(user.right, slot); return true; @@ -597,7 +741,10 @@ export class RecordFlowClasses { return false; } if (ts.isVariableDeclaration(user) && user.initializer === value) { - if (ts.isObjectBindingPattern(user.name)) return true; + if (ts.isObjectBindingPattern(user.name)) { + this.copiedOut.add(slot); + return true; + } return this.isMember(user.name); } if (ts.isReturnStatement(user)) { @@ -629,11 +776,13 @@ export class RecordFlowClasses { } if (ts.isPropertyAccessExpression(callee) && ts.isIdentifier(callee.expression)) { const methods = READING_CALLS.get(callee.expression.text); - return ( + const reads = !!methods && methods.has(callee.name.text) && - this.host.isGlobal(callee.expression, callee.expression.text) - ); + this.host.isGlobal(callee.expression, callee.expression.text); + if (reads && (callee.name.text === "values" || callee.name.text === "entries")) + this.copiedOut.add(slot); + return reads; } return false; } @@ -646,6 +795,19 @@ export class RecordFlowClasses { const symbol = this.host.symbolOf(node); return !!symbol && this.bindingSlots.has(symbol); } + + /** A tracked binding's field as an assignment target: the stored record + * joins the field slot when the field's uses are visited. */ + private isFieldMember(node: ts.Node): boolean { + const access = this.fieldAccess(node); + return !!access && this.fieldSlot(access.owner, access.name, node) !== undefined; + } +} + +function addTo(map: Map, key: number, value: T): void { + const list = map.get(key); + if (list) list.push(value); + else map.set(key, [value]); } /** The return expressions of a function body, excluding nested functions. */ diff --git a/packages/compiler/src/frontend/lowering/string-index.ts b/packages/compiler/src/frontend/lowering/string-index.ts index e9755742a4..dd086cd73c 100644 --- a/packages/compiler/src/frontend/lowering/string-index.ts +++ b/packages/compiler/src/frontend/lowering/string-index.ts @@ -1,4 +1,15 @@ -import { BOOL, F64, STRING, type IrExpr, type IrType, type SrcLoc } from "../../ir/ir.js"; +import type * as ts from "../ts7/adapter.js"; +import { + BOOL, + DYN, + F64, + STRING, + isUnitType, + type IrExpr, + type IrStmt, + type IrType, + type SrcLoc, +} from "../../ir/ir.js"; import { numLit, varRef } from "../../ir/build.js"; import type { Lowerer } from "./lowerer.js"; @@ -66,3 +77,77 @@ export function lowerOptionalStringIndex( loc, }; } + +/** A number-keyed read of a receiver the checker narrowed to string whose + * stored value is still a union: an array-derived binding keeps its + * undefined arm (`const t = tokens[i]`), and a `typeof t === "string"` + * guard narrows the checker's type without retagging the storage. Both + * operands are captured first, as for a plain string receiver. A unit arm + * performs the checked-dynamic keyed read on that unit, which throws Node's + * "Cannot read properties of undefined (reading '')"; another value + * arm, which the checker has excluded, fails the checked extraction; the + * string arm reads like any string receiver. Null when the union has no + * string arm. */ +export function lowerUnionStringIndex( + lowerer: Lowerer, + receiver: IrExpr, + index: IrExpr, + keyNode: ts.Node, + type: IrType, + loc: SrcLoc, +): IrExpr | null { + if (receiver.type.kind !== "union") return null; + const unionType = receiver.type; + const unionId = unionType.unionId; + const def = lowerer.unions.get(unionId); + const stringTag = lowerer.armTag(unionId, STRING); + if (!def || stringTag < 0) return null; + const receiverLocal = lowerer.declareHiddenLocal("%indexedUnion", unionType); + const indexLocal = lowerer.declareHiddenLocal("%unionStringIndex", F64); + const stored = (): IrExpr => varRef(receiverLocal.id, unionType, loc); + const key = (): IrExpr => varRef(indexLocal.id, F64, loc); + const stmts: IrStmt[] = [ + { kind: "varDecl", localId: receiverLocal.id, init: receiver, loc }, + { kind: "varDecl", localId: indexLocal.id, init: index, loc }, + ]; + let text: IrExpr; + if (def.arms.every((arm, tag) => tag === stringTag || isUnitType(arm))) { + text = { kind: "unionNarrow", unionId, tag: stringTag, value: stored(), type: STRING, loc }; + } else { + const helper = lowerer.narrowedArmHelper(unionId, STRING, loc); + if (helper === null) return null; + text = lowerer.checkedNarrowCall(helper, stored(), STRING, loc); + } + let result = lowerOptionalStringIndex(lowerer, text, key(), type, loc); + for (let tag = def.arms.length - 1; tag >= 0; tag--) { + const arm = def.arms[tag]!; + if (!isUnitType(arm)) continue; + const unit = arm.kind === "nullT" ? "null" : "undefined"; + const thrown: IrExpr = { + kind: "dynCheck", + value: { + kind: "dynKeyGet", + value: { + kind: "dynFrom", + value: { kind: "unitLit", unit, type: arm, loc }, + type: DYN, + loc, + }, + key: lowerer.ensureString(key(), keyNode), + type: DYN, + loc, + }, + type: STRING, + loc, + }; + result = { + kind: "ternary", + cond: { kind: "unionIsTag", unionId, tag, negated: false, value: stored(), type: BOOL, loc }, + then: lowerer.coerceToExpected(thrown, type), + else_: result, + type, + loc, + }; + } + return { kind: "seqExpr", stmts, result, type, loc }; +} diff --git a/packages/compiler/src/frontend/lowering/surfaces.ts b/packages/compiler/src/frontend/lowering/surfaces.ts index 848b34afe9..d394c1960a 100644 --- a/packages/compiler/src/frontend/lowering/surfaces.ts +++ b/packages/compiler/src/frontend/lowering/surfaces.ts @@ -2047,8 +2047,13 @@ export const BUILTIN_MODULE_FN_ALIASES: Record< crypto: { // The composed randomBytes(n).toString(enc) chain keeps its one-libCall // lowering (lowerCryptoComposedCall), while the callback overload has - // its own adapter spelling. Both remain the randomBytes surface. - randomBytes: ["crypto.randomBytesToString", "crypto.randomBytesCb"], + // its own adapter spelling. Both remain the randomBytes surface, as does + // the util.promisify form. + randomBytes: [ + "crypto.randomBytesToString", + "crypto.randomBytesCb", + "crypto.randomBytesPromise", + ], // Omitting size fills from offset through the rest of the Buffer and // uses a separate runtime entry point for Node's exact bounds checks. // Views of every kind return themselves; other buffers take the Dyn form. @@ -2058,9 +2063,13 @@ export const BUILTIN_MODULE_FN_ALIASES: Record< "crypto.randomFillViewRest", "crypto.randomFillSyncDyn", ], - randomFill: ["crypto.randomFillCbDyn"], + randomFill: [ + "crypto.randomFillCbDyn", + "crypto.randomFillPromise", + "crypto.randomFillPromiseDyn", + ], timingSafeEqual: ["crypto.timingSafeEqualDyn"], - randomInt: ["crypto.randomIntMax", "crypto.randomIntCb"], + randomInt: ["crypto.randomIntMax", "crypto.randomIntCb", "crypto.randomIntPromise"], randomUUID: ["crypto.randomUUIDOpts"], }, os: { @@ -2874,7 +2883,7 @@ export const BUILTIN_MODULE_FENCE_HINTS: Record< }, util: { promisify: - "the lowered shapes are const bindings over child_process.execFile, child_process.exec, setTimeout, setImmediate, stream.finished, stream.pipeline, the zlib callback one-shots and the fs callback functions with an fs/promises counterpart; call the bound function directly", + "the lowered shapes are const bindings over child_process.execFile, child_process.exec, setTimeout, setImmediate, stream.finished, stream.pipeline, the zlib callback one-shots, the crypto callback functions, dns.lookup, dns.lookupService and the fs callback functions with an fs/promises counterpart; call the bound function directly", }, "stream/consumers": { blob: @@ -2890,7 +2899,7 @@ export const BUILTIN_MODULE_FENCE_HINTS: Record< isBuiltin: "the lowered call checks a string against the baked Node v24 list, including bare/node: aliases and prefix-only builtins", syncBuiltinESMExports: - "the direct zero-argument call is a no-op because compiled builtin exports are immutable and cannot become stale", + "the direct zero-argument call statement refreshes the ES module namespace copy of events.defaultMaxListeners, the one builtin export a program can change", }, child_process: { execFile: @@ -3037,8 +3046,8 @@ export function stdlibMemberFence(lowerer: Lowerer, access: ts.PropertyAccessExp hint = "'.groups' lowers when the match's regex is STATICALLY known — a regex literal, or a " + "const initialized with one (the group-name table is built at compile time) — and the " + - "read is not an optional-chain step: narrow the match instead (if (m) { m.groups } or " + - "m!.groups)"; + "match comes from exec, match, a for-of over matchAll, or the element parameter of an " + + "array-method callback over matchAll rows"; } else if (recvIr?.kind === "array" && member === "flat") { hint = "flat has no lowering (flatMap does) — flatten into an accumulator instead: " + diff --git a/packages/compiler/src/frontend/program-analysis.ts b/packages/compiler/src/frontend/program-analysis.ts index 0ecf09e6a7..e49b9307e4 100644 --- a/packages/compiler/src/frontend/program-analysis.ts +++ b/packages/compiler/src/frontend/program-analysis.ts @@ -1,4 +1,9 @@ -import type { ExportDeclaration, SourceFile, VariableDeclaration } from "./ts7/ast-types.js"; +import type { + ExportDeclaration, + Identifier, + SourceFile, + VariableDeclaration, +} from "./ts7/ast-types.js"; import type { ModuleSourceCandidates } from "./module-source-candidates.js"; import type { CycleEarlyBinding } from "./program.js"; @@ -20,6 +25,11 @@ export class ProgramAnalysis { readonly exactCycleMembers = new Set(); cycleEarlyBindings: EarlyBindingsMemo | null = null; moduleEarlyBindings: EarlyBindingsMemo | null = null; + /** Top-level require declarations: the first reference to their bindings + * that code running before the require can reach (requireEarlyReference). */ + readonly requireEarlyReferences = new Map(); + /** Per file: the first top-level statement that can run user code. */ + readonly requireFirstRunnable = new Map(); clear(): void { this.createRequireReasons.clear(); @@ -29,5 +39,7 @@ export class ProgramAnalysis { this.exactCycleMembers.clear(); this.cycleEarlyBindings = null; this.moduleEarlyBindings = null; + this.requireEarlyReferences.clear(); + this.requireFirstRunnable.clear(); } } diff --git a/packages/compiler/src/frontend/program.test.ts b/packages/compiler/src/frontend/program.test.ts index 33abf6d679..8fec2f486f 100644 --- a/packages/compiler/src/frontend/program.test.ts +++ b/packages/compiler/src/frontend/program.test.ts @@ -126,10 +126,12 @@ test.for([ ["replacement export property", "module.exports = read; module.exports.name = read;"], ["non-export assignment chain", "const box = {}; exports.alias = box.read = read;"], ])("require still refuses an early read through %s", ([, early]) => { + // A var binding keeps the fence (Node reads undefined there); a const + // compiles with initialization checks instead, from the same analysis. const diagnostics = requireOrderDiagnostics(` exports.read = read; ${early} -const dep = require('./dep.cjs'); +var dep = require('./dep.cjs'); function read() { return dep.value; } `); expect( @@ -140,13 +142,43 @@ function read() { return dep.value; } test("publishing a require binding itself still reads it before initialization", () => { const diagnostics = requireOrderDiagnostics(` exports.read = read; -const { read } = require('./dep.cjs'); +var { read } = require('./dep.cjs'); `); expect( diagnostics.some((diag) => diag.code === "SC1013" && diag.message.includes("binding 'read'")), ).toBe(true); }); +test("an early-reachable const require binding compiles with initialization checks", () => { + // Preflight admits it; the lowering checks every reference that can run + // before the require (require-initialization.ts). + expect( + requireOrderDiagnostics(` +exports.read = read; +read(); +const dep = require('./dep.cjs'); +function read() { return dep.value; } +`), + ).toEqual([]); +}); + +test.for([ + ["a class whose methods read it", "class Reader { read() { return dep.value; } }"], + ["a stored class expression", "var Reader = class { read() { return dep.value; } };"], + ["a later-job callback", "setTimeout(() => dep.value, 0);"], + ["a published getter", "Object.defineProperty(exports, 'value', { get: () => dep.value });"], + ["a property stored on a published function", "module.exports = read; read.again = again;"], +])("code that cannot run before the require does not reach it: %s", ([, early]) => { + expect( + requireOrderDiagnostics(` +${early} +var dep = require('./dep.cjs'); +function read() { return dep.value; } +function again() { return dep.value; } +`), + ).toEqual([]); +}); + test.for([ "var Object = null; Object.defineProperty(exports, '__esModule', { value: true }); exports.read = read;", "var exports = null; exports.read = read;", @@ -154,7 +186,7 @@ test.for([ ])("source bindings cannot masquerade as a CommonJS prologue: %s", (prefix) => { const diagnostics = requireOrderDiagnostics(` ${prefix} -const dep = require('./dep.cjs'); +var dep = require('./dep.cjs'); function read() { return dep.value; } `); expect( @@ -165,7 +197,7 @@ function read() { return dep.value; } test("an earlier declarator can call an export before the require initializes", () => { const diagnostics = requireOrderDiagnostics(` exports.read = read; -const before = read(), dep = require('./dep.cjs'); +var before = read(), dep = require('./dep.cjs'); function read() { return dep.value; } `); expect( @@ -183,6 +215,63 @@ function read() { return dep.value; } ).toEqual([]); }); +test("a member added to a replaced export does not run the replacement (on-finished)", () => { + expect( + requireOrderDiagnostics(` +module.exports = read; +module.exports.check = check; +var hooks = probe(); +var dep = require('./dep.cjs'); +function read() { return dep.value; } +function check() { return 1; } +function probe() { return check(); } +`), + ).toEqual([]); +}); + +test("a require cycle whose modules export first and use each other only later is admitted", () => { + expect( + requireOrderDiagnostics( + ` +const ANY = Symbol('ANY'); +class Main { dep() { return new Dep(); } } +module.exports = Main; +const Dep = require('./dep.cjs'); +`, + "class Dep { main() { return new Main(); } }\nmodule.exports = Dep;\nconst Main = require('./main.cjs');\n", + ), + ).toEqual([]); +}); + +test.for([ + [ + "an export after the require", + "const Main = require('./main.cjs');\nmodule.exports = { Dep: 1 };\n", + ], + [ + "a top-level use of the cycle binding", + "module.exports = 1;\nconst Main = require('./main.cjs');\nnew Main();\n", + ], + [ + "an escaping export object", + "module.exports = 1;\nconst Main = require('./main.cjs');\nObject.assign(exports, {});\n", + ], + [ + "a call before the export", + "setup();\nmodule.exports = 1;\nconst Main = require('./main.cjs');\nfunction setup() {}\n", + ], +])("a require cycle keeps SC1016 for %s", ([, dependency]) => { + const diagnostics = requireOrderDiagnostics( + ` +class Main {} +module.exports = Main; +const dep = require('./dep.cjs'); +`, + dependency, + ); + expect(diagnostics.some((diag) => diag.code === "SC1016")).toBe(true); +}); + test("function publication does not admit CommonJS cycles", () => { const diagnostics = requireOrderDiagnostics( ` diff --git a/packages/compiler/src/frontend/program.ts b/packages/compiler/src/frontend/program.ts index 3a4ec16479..df0cb673e2 100644 --- a/packages/compiler/src/frontend/program.ts +++ b/packages/compiler/src/frontend/program.ts @@ -1312,6 +1312,510 @@ export function bindingEarlyUse7( return hit; } +/** The first reference to a top-level require declaration's bindings that + * code running before the require completes can reach, or null when no + * earlier code can run or reach one. Memoized per program: preflight + * decides the fence and the lowering places the initialization checks + * from the same answer. */ +function requireEarlyReference7( + program: ts.Program, + decl: ts.VariableDeclaration, +): ts.Identifier | null { + const memo = program.analysis.requireEarlyReferences; + const cached = memo.get(decl); + if (cached !== undefined) return cached; + let found: ts.Identifier | null = null; + const stmt = decl.parent?.parent; + const sf = decl.getSourceFile(); + if (stmt !== undefined && ts.isVariableStatement(stmt) && ts.isSourceFile(stmt.parent)) { + const stmts = sf.statements; + const k = stmts.indexOf(stmt); + const declarations = stmt.declarationList.declarations; + // Earlier declarators run too: `const x = read(), dep = require('./dep')`. + const precedingDecls = declarations.slice(0, declarations.indexOf(decl)); + let firstRunnable = program.analysis.requireFirstRunnable.get(sf); + if (firstRunnable === undefined) { + firstRunnable = stmts.findIndex((s) => !purePrefixStmt7(program, s)); + program.analysis.requireFirstRunnable.set(sf, firstRunnable); + } + const prefixCanRun = + (firstRunnable >= 0 && firstRunnable < k) || precedingDecls.some((d) => !purePrefixDecl7(d)); + if (prefixCanRun) found = requireBindingEarlyUse7(program, sf, k, decl, precedingDecls); + } + memo.set(decl, found); + return found; +} + +/** The CommonJS twin of bindingEarlyUse7 for a top-level require binding: + * the first reference to one of `decl`'s bindings that code running before + * the require completes can reach, or null. It follows Node's evaluation + * order instead of the conservative "every earlier statement" scan: + * - hoisted functions and classes, and the function and class values a + * var stored before the require, run only when reachable code names + * them; a class statement runs only its heritage, computed keys, + * decorators and static initializers; + * - callbacks handed to timers, microtasks and promise reactions run after + * the module body, hence after the require; + * - publishing a value (`module.exports = f`, `exports.n = f`, + * `Object.defineProperty(exports, "n", { get })`, `F.prototype.m = f`) + * runs nothing. The published value becomes reachable only when + * reachable code reads it back: an `exports.n` read reaches the values + * published as `n`, any other read of the export object reaches all of + * them, and naming `F` reaches what was stored on `F`. No other module + * can read this module's exports before its body completes: require + * cycles keep their SC1016 fence and a compiled module graph has no + * reflective exports object. + * Every other function-like met in reachable code counts as reachable + * because it may be handed to a callee. */ +export function requireBindingEarlyUse7( + program: ts.Program, + sf: ts.SourceFile, + k: number, + decl: ts.VariableDeclaration, + precedingDecls: readonly ts.VariableDeclaration[], +): ts.Identifier | null { + const checker = program.getTypeChecker(); + const bindings = new Set(); + for (const id of boundNames7(decl.name)) { + const sym = checker.getSymbolAtLocation(id); + if (sym) bindings.add(sym); + } + if (bindings.size === 0) return null; + const stmts = sf.statements; + const cjs = isCjsJsFile7(sf, program); + const symbolOf = (id: ts.Identifier): ts.Symbol | undefined => { + const parent = id.parent; + if (parent !== undefined && ts.isShorthandPropertyAssignment(parent) && parent.name === id) { + return checker.getShorthandAssignmentValueSymbol(parent); + } + return checker.getSymbolAtLocation(id); + }; + // Stored callables and the scans that skip them. + const units = new Map(); + const stored = new Set(); + const addUnit = (name: ts.Identifier, node: ts.Node): void => { + const sym = checker.getSymbolAtLocation(name); + if (sym === undefined) return; + stored.add(node); + const list = units.get(sym); + if (list) list.push(node); + else units.set(sym, [node]); + }; + const storedCallables = (e: ts.Expression, out: ts.Node[]): void => { + while (ts.isParenthesizedExpression(e)) e = e.expression; + if (ts.isArrowFunction(e) || ts.isFunctionExpression(e) || ts.isClassExpression(e)) { + out.push(e); + } else if (ts.isObjectLiteralExpression(e)) { + for (const p of e.properties) { + if ( + (ts.isMethodDeclaration(p) || + ts.isGetAccessorDeclaration(p) || + ts.isSetAccessorDeclaration(p)) && + !ts.isComputedPropertyName(p.name) + ) { + out.push(p); + } else if (ts.isPropertyAssignment(p) && !ts.isComputedPropertyName(p.name)) { + storedCallables(p.initializer, out); + } + } + } + }; + stmts.forEach((s, i) => { + if ((ts.isFunctionDeclaration(s) || ts.isClassDeclaration(s)) && s.name !== undefined) { + addUnit(s.name, s); + } else if (i < k && ts.isVariableStatement(s)) { + for (const d of s.declarationList.declarations) { + if (!ts.isIdentifier(d.name) || d.initializer === undefined) continue; + const out: ts.Node[] = []; + storedCallables(d.initializer, out); + for (const node of out) addUnit(d.name, node); + } + } + }); + // Callbacks that only run as later jobs never run before the require: + // the timers, microtasks and promise reactions. (Event listeners can run + // synchronously from an emit, so they keep the conservative rule.) + const laterJob = (callee: ts.Expression): boolean => { + while (ts.isParenthesizedExpression(callee)) callee = callee.expression; + const name = ts.isIdentifier(callee) + ? callee.text + : ts.isPropertyAccessExpression(callee) + ? callee.name.text + : null; + return ( + name !== null && + (LATER_JOB_BUILTINS7.has(name) || name === "nextTick") && + laterJobCallee7(checker, callee) + ); + }; + if (!hasTopLevelAwait7(sf)) { + const callbacks = (e: ts.Expression): void => { + while (ts.isParenthesizedExpression(e)) e = e.expression; + if (ts.isArrowFunction(e) || ts.isFunctionExpression(e)) stored.add(e); + }; + for (const s of stmts.slice(0, k)) { + ts.walkPreorder(s, (node) => { + if ((ts.isCallExpression(node) || ts.isNewExpression(node)) && laterJob(node.expression)) { + for (const arg of node.arguments ?? []) callbacks(arg); + } + return undefined; + }); + } + } + const isWrapperName = (e: ts.Node, name: string): e is ts.Identifier => + ts.isIdentifier(e) && e.text === name && !sourceBinding7(program, e); + /** `this` at the module's top level: the original export object. */ + const isModuleThis = (e: ts.Node): boolean => { + if (e.kind !== ts.SyntaxKind.ThisKeyword) return false; + for (let p = e.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) { + if (ts.isFunctionLike(p) && !ts.isArrowFunction(p)) return false; + if (ts.isClassExpression(p) || ts.isClassDeclaration(p)) return false; + } + return true; + }; + const isExportObject = (e: ts.Expression): boolean => + isWrapperName(e, "exports") || + isModuleThis(e) || + (isModuleExportsAccess(e) && isWrapperName(e.expression, "module")); + const replacesExports = stmts.some((st) => cjsExportAssignmentOf(st)?.kind === "table"); + // Published values: by export name ("*" for a whole replacement), and by + // the stored callable whose properties hold them. + const exportLinks = new Map(); + const ownerLinks = new Map(); + const reachedKeys = new Set(); + const writtenKeys = new Set(); + const reachedUnits = new Set(); + const work: ts.Node[] = []; + let hit: ts.Identifier | null = null; + const reachUnit = (node: ts.Node): void => { + if (reachedUnits.has(node)) return; + reachedUnits.add(node); + work.push(node); + }; + const reachSymbol = (sym: ts.Symbol): void => { + for (const node of units.get(sym) ?? []) reachUnit(node); + for (const node of ownerLinks.get(sym) ?? []) reachUnit(node); + }; + let allReached = false; + const reachKey = (key: string | null): void => { + if (key === null) { + allReached = true; + for (const list of exportLinks.values()) for (const node of list) reachUnit(node); + return; + } + reachedKeys.add(key); + for (const name of [key, "*"]) for (const node of exportLinks.get(name) ?? []) reachUnit(node); + }; + const keyReached = (key: string): boolean => + allReached || reachedKeys.has(key) || (key === "*" && reachedKeys.size > 0); + /** A read of the export object: which published names it can observe. */ + const exportRead = (node: ts.Expression): void => { + let use: ts.Node = node; + while (ts.isParenthesizedExpression(use.parent)) use = use.parent; + const parent = use.parent; + // A truthiness test reads no member (tsc's `this && this.__helper`). + if ( + ts.isBinaryExpression(parent) && + parent.left === use && + parent.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken + ) { + return; + } + if (ts.isTypeOfExpression(parent)) return; + if (ts.isPropertyAccessExpression(parent) && parent.expression === use) { + let target: ts.Node = parent; + while (ts.isParenthesizedExpression(target.parent)) target = target.parent; + const assignment = target.parent; + if ( + ts.isBinaryExpression(assignment) && + assignment.left === target && + assignment.operatorToken.kind === ts.SyntaxKind.EqualsToken + ) { + // A plain write reads no member. It can still run a setter: one + // published for that name (now or later), or a static one of a + // replacing class. + writtenKeys.add(parent.name.text); + for (const node of exportLinks.get(parent.name.text) ?? []) reachUnit(node); + for (const node of exportLinks.get("*") ?? []) { + if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) reachUnit(node); + } + return; + } + reachKey(parent.name.text); + return; + } + if ( + ts.isElementAccessExpression(parent) && + parent.expression === use && + ts.isStringLiteralLike(parent.argumentExpression) + ) { + reachKey(parent.argumentExpression.text); + return; + } + reachKey(null); + }; + const scan = (root: ts.Node): void => { + ts.walkPreorder(root, (n) => { + if (hit !== null) return "stop"; + if (n !== root && stored.has(n)) { + if (ts.isClassExpression(n) || ts.isClassDeclaration(n)) { + for (const part of classSyncParts7(n)) scan(part); + } + return "skip"; + } + // A self-require answers this module's partially built exports. + if (cjs && ts.isCallExpression(n)) { + const spec = requireSpecOf7(n); + if (spec !== null && resolveImport7(program, sf, spec, "require") === sf) reachKey(null); + } + if (cjs && n.kind === ts.SyntaxKind.ThisKeyword && isModuleThis(n)) { + exportRead(n as ts.Expression); + return undefined; + } + if (!ts.isIdentifier(n)) return undefined; + if (inTypePosition7(n)) return undefined; + const named = n.parent; + if ( + (ts.isPropertyAccessExpression(named) || + ts.isPropertyAssignment(named) || + ts.isMethodDeclaration(named) || + ts.isGetAccessorDeclaration(named) || + ts.isSetAccessorDeclaration(named) || + ts.isPropertyDeclaration(named)) && + named.name === n + ) { + return undefined; + } + // A declaration's own name reads nothing. + if (named !== undefined && declaredName7(named) === n) return undefined; + if (cjs && isWrapperName(n, "exports")) { + exportRead(n); + return undefined; + } + if (cjs && isWrapperName(n, "module")) { + const parent = n.parent; + if (ts.isPropertyAccessExpression(parent) && parent.expression === n) { + if (parent.name.text === "exports") exportRead(parent); + return "skip"; + } + reachKey(null); + return undefined; + } + const sym = symbolOf(n); + if (sym === undefined) return undefined; + if (bindings.has(sym)) { + hit = n; + return "stop"; + } + reachSymbol(sym); + return undefined; + }); + }; + /** Links a published value: evaluating it reads identifiers, but the + * callables it names run only once they are read back. */ + const publish = (value: ts.Expression, link: (node: ts.Node) => void): void => { + while (ts.isParenthesizedExpression(value)) value = value.expression; + if (ts.isIdentifier(value)) { + const sym = symbolOf(value); + if (sym !== undefined && bindings.has(sym)) { + hit = value; + return; + } + if (sym !== undefined) { + // What was stored on the value stays reachable through it. + for (const node of [...(units.get(sym) ?? []), ...(ownerLinks.get(sym) ?? [])]) link(node); + } + return; + } + if (ts.isFunctionExpression(value) || ts.isArrowFunction(value)) { + link(value); + return; + } + if (ts.isClassExpression(value)) { + for (const part of classSyncParts7(value)) scan(part); + link(value); + return; + } + if (ts.isObjectLiteralExpression(value)) { + for (const p of value.properties) { + if (ts.isShorthandPropertyAssignment(p)) { + const sym = checker.getShorthandAssignmentValueSymbol(p); + if (sym !== undefined && bindings.has(sym)) hit = p.name as ts.Identifier; + else if (sym !== undefined) { + for (const node of [...(units.get(sym) ?? []), ...(ownerLinks.get(sym) ?? [])]) { + link(node); + } + } + } else if (ts.isPropertyAssignment(p) && !ts.isComputedPropertyName(p.name)) { + publish(p.initializer, link); + } else if ( + (ts.isMethodDeclaration(p) || + ts.isGetAccessorDeclaration(p) || + ts.isSetAccessorDeclaration(p)) && + !ts.isComputedPropertyName(p.name) + ) { + link(p); + } else scan(p); + } + return; + } + if ( + ts.isStringLiteralLike(value) || + ts.isNumericLiteral(value) || + value.kind === ts.SyntaxKind.TrueKeyword || + value.kind === ts.SyntaxKind.FalseKeyword || + value.kind === ts.SyntaxKind.NullKeyword || + (ts.isVoidExpression(value) && ts.isNumericLiteral(value.expression)) + ) { + return; + } + scan(value); + // Whatever an arbitrary expression evaluates to is opaque: treat it as + // reachable through the target too (scan already reached its parts). + }; + /** Where a publication stores: an export name, or a stored callable. */ + type Target = { kind: "key"; key: string } | { kind: "owner"; owner: ts.Symbol }; + const targetOf = (left: ts.Expression): Target | null => { + if (isModuleExportsAccess(left) && isWrapperName(left.expression, "module")) { + return { kind: "key", key: "*" }; + } + const names: string[] = []; + let e: ts.Expression = left; + for (;;) { + if (ts.isPropertyAccessExpression(e) && !e.questionDotToken) { + if (isExportObject(e)) break; + names.unshift(e.name.text); + e = e.expression; + } else if ( + ts.isElementAccessExpression(e) && + !e.questionDotToken && + ts.isStringLiteralLike(e.argumentExpression) + ) { + names.unshift(e.argumentExpression.text); + e = e.expression; + } else break; + } + if (names.length === 0) return null; + if (isExportObject(e)) { + // __proto__ and the interop marker are not plain data slots, and a + // member of a replaced export may be a read-only function property: + // those writes keep the conservative scan. + if (names[0] === "__proto__" || names[0] === "__esModule" || replacesExports) return null; + return { kind: "key", key: names[0]! }; + } + if (ts.isIdentifier(e)) { + const sym = symbolOf(e); + if (sym !== undefined && units.has(sym) && !bindings.has(sym)) + return { kind: "owner", owner: sym }; + } + return null; + }; + const linkTo = (target: Target, node: ts.Node): void => { + if (target.kind === "key") { + const replacingClass = + target.key === "*" && + writtenKeys.size > 0 && + (ts.isClassDeclaration(node) || ts.isClassExpression(node)); + if (keyReached(target.key) || writtenKeys.has(target.key) || replacingClass) { + reachUnit(node); + } + const list = exportLinks.get(target.key); + if (list) list.push(node); + else exportLinks.set(target.key, [node]); + } else { + if ((units.get(target.owner) ?? []).some((u) => reachedUnits.has(u))) reachUnit(node); + if ((units.get(target.owner) ?? []).includes(node)) return; + const list = ownerLinks.get(target.owner); + if (list === undefined) ownerLinks.set(target.owner, [node]); + else if (!list.includes(node)) list.push(node); + } + }; + /** A top-level publication statement, handled in place; false for any + * other statement. */ + const publication = (s: ts.Statement): boolean => { + if (!cjs || !ts.isExpressionStatement(s)) return false; + const e = s.expression; + if (ts.isBinaryExpression(e) && e.operatorToken.kind === ts.SyntaxKind.EqualsToken) { + const targets: Target[] = []; + let value: ts.Expression = e; + while ( + ts.isBinaryExpression(value) && + value.operatorToken.kind === ts.SyntaxKind.EqualsToken + ) { + const target = targetOf(value.left); + if (target === null) return false; + targets.push(target); + value = value.right; + } + publish(value, (node) => { + for (const t of targets) linkTo(t, node); + }); + return true; + } + // Object.defineProperty(exports, "name", { get() {...}, value: f, ... }) + if ( + ts.isCallExpression(e) && + ts.isPropertyAccessExpression(e.expression) && + isWrapperName(e.expression.expression, "Object") && + e.expression.name.text === "defineProperty" && + e.arguments.length === 3 + ) { + const [recv, key, desc] = e.arguments as unknown as [ + ts.Expression, + ts.Expression, + ts.Expression, + ]; + if (!isExportObject(recv) || !ts.isStringLiteralLike(key)) return false; + if (!ts.isObjectLiteralExpression(desc)) return false; + const keyTarget: Target = { kind: "key", key: key.text }; + const link = (node: ts.Node): void => linkTo(keyTarget, node); + for (const p of desc.properties) { + if ( + (ts.isMethodDeclaration(p) || ts.isPropertyAssignment(p)) && + ts.isIdentifier(p.name) && + (p.name.text === "get" || p.name.text === "set") + ) { + if (ts.isMethodDeclaration(p)) link(p); + else publish(p.initializer, link); + } else if ( + ts.isPropertyAssignment(p) && + ts.isIdentifier(p.name) && + p.name.text === "value" + ) { + publish(p.initializer, link); + } else scan(p); + } + return true; + } + return false; + }; + const drain = (): void => { + while (hit === null && work.length > 0) { + const unit = work.pop()!; + checker.prefetchSymbolRoots([unit]); + scan(unit); + } + }; + const roots = stmts.slice(0, k); + checker.prefetchSymbolRoots([...roots, ...precedingDecls]); + for (const s of roots) { + if (hit !== null) break; + if (ts.isFunctionDeclaration(s)) continue; + if (ts.isClassDeclaration(s)) { + for (const part of classSyncParts7(s)) scan(part); + } else if (!publication(s)) { + scan(s); + } + drain(); + } + for (const d of precedingDecls) { + if (hit !== null) break; + scan(d); + drain(); + } + return hit; +} + /** Bare `require("...")` EXPRESSION STATEMENTS anywhere below the top * level of a file — inside top-level ifs/blocks/loops and inside function * bodies (the lazy-require idiom). No bindings, pure side-effect loads. */ @@ -2307,8 +2811,19 @@ export function makeCycleAdmission( bindingUses.set(sf, uses); return uses; }; + // The CommonJS cluster verdict, same keying (cjsCycleRefusal7). + const cjsVerdict = new Map(); return (importer: ts.SourceFile, e: CycleEdge): string | null => { - if (e.stmt === undefined) return "the cycle closes through a require() edge"; + if (e.stmt === undefined) { + if (!index.has(importer)) strongconnect(importer); + const comp = sccOf.get(e.dep); + if (comp === undefined || comp.length < 2 || !comp.includes(importer)) { + return "the cycle closes through a require() edge"; + } + if (!cjsVerdict.has(comp)) cjsVerdict.set(comp, cjsCycleRefusal7(program, comp, lineOf)); + const reason = cjsVerdict.get(comp) ?? null; + return reason === null ? null : `the cycle closes through a require() edge: ${reason}`; + } // Cheap per-edge admission: nothing readable binds through the edge. if (ts.isImportDeclaration(e.stmt)) { const clause = e.stmt.importClause; @@ -2354,6 +2869,211 @@ export function makeCycleAdmission( }; } +/** The reason a strongly-connected cluster of CommonJS modules cannot be + * initialized exactly through its require() cycle, or null when it can. + * Node answers a require of a module that is still evaluating with that + * module's exports object as it stands, while the compiled bindings alias + * the exporter's final declarations. The two agree when: + * - every member settles its exports before anything can enter the + * cycle: all of its export statements come before its first top-level + * statement that can run another module's code (a require, or anything + * but literals, plain reads, function and class values, and + * standard-library globals called with literal arguments), and every + * other use of its export object is a plain member read; and + * - no member's top-level code can reach its binding of another member, + * so no member runs another member's code while the cluster is still + * initializing. Each module then initializes exactly as it would + * outside the cycle, and its own require bindings keep the acyclic + * analysis (requireEarlyReference). + * Other cycles keep the SC1016 fence. */ +function cjsCycleRefusal7( + program: ts.Program, + comp: readonly ts.SourceFile[], + lineOf: (node: ts.Node) => string, +): string | null { + const members = new Set(comp); + const builtinRoot = (e: ts.Expression): boolean => { + while (ts.isPropertyAccessExpression(e)) e = e.expression; + return ts.isIdentifier(e) && !sourceBinding7(program, e); + }; + const literal = (e: ts.Node): boolean => + ts.isStringLiteralLike(e) || + ts.isNumericLiteral(e) || + ts.isRegularExpressionLiteral(e) || + e.kind === ts.SyntaxKind.TrueKeyword || + e.kind === ts.SyntaxKind.FalseKeyword || + e.kind === ts.SyntaxKind.NullKeyword; + /** An expression whose evaluation runs no user code and requires + * nothing: literals, plain reads, function and class values, literal + * containers of those, and standard-library globals called with + * literal arguments. */ + const inertExpr = (e: ts.Node): boolean => { + while (ts.isParenthesizedExpression(e)) e = e.expression; + if (literal(e) || ts.isIdentifier(e)) return true; + if (ts.isFunctionExpression(e) || ts.isArrowFunction(e)) return true; + if (ts.isClassExpression(e)) return classSyncParts7(e).every(inertPart); + if (ts.isObjectLiteralExpression(e)) { + return e.properties.every((p) => + ts.isShorthandPropertyAssignment(p) + ? true + : ts.isPropertyAssignment(p) + ? !ts.isComputedPropertyName(p.name) && inertExpr(p.initializer) + : (ts.isMethodDeclaration(p) || + ts.isGetAccessorDeclaration(p) || + ts.isSetAccessorDeclaration(p)) && + !ts.isComputedPropertyName(p.name), + ); + } + if (ts.isArrayLiteralExpression(e)) { + return e.elements.every((el) => !ts.isSpreadElement(el) && inertExpr(el)); + } + if (ts.isPropertyAccessExpression(e)) return builtinRoot(e); + if (ts.isVoidExpression(e)) return literal(e.expression); + if (ts.isCallExpression(e) || ts.isNewExpression(e)) { + return ( + requireSpecOf7(e) === null && + builtinRoot(e.expression) && + (e.arguments ?? []).every(literal) + ); + } + return false; + }; + const inertPart = (part: ts.Node): boolean => { + if (ts.isHeritageClause(part)) return part.types.every((t) => inertExpr(t.expression)); + if (ts.isComputedPropertyName(part)) return inertExpr(part.expression); + return ts.isExpression(part) && inertExpr(part); + }; + const exportObject = (e: ts.Expression): boolean => + (ts.isIdentifier(e) && e.text === "exports" && !sourceBinding7(program, e)) || + (isModuleExportsAccess(e) && + ts.isIdentifier(e.expression) && + !sourceBinding7(program, e.expression)); + /** A top-level statement that only publishes inert values. */ + const exportStatement = (stmt: ts.Statement): boolean => { + if (!ts.isExpressionStatement(stmt)) return false; + if (isEsModuleStamp(stmt.expression)) return true; + const cjs = cjsExportAssignmentOf(stmt); + if (cjs !== null) { + let value: ts.Expression = cjs.expr; + while ( + ts.isBinaryExpression(value) && + value.operatorToken.kind === ts.SyntaxKind.EqualsToken + ) { + const left = value.left; + if ( + !isModuleExportsAccess(left) && + !(ts.isPropertyAccessExpression(left) && exportObject(left.expression)) + ) + return false; + value = value.right; + } + return inertExpr(value); + } + const call = stmt.expression; + return ( + ts.isCallExpression(call) && + ts.isPropertyAccessExpression(call.expression) && + ts.isIdentifier(call.expression.expression) && + call.expression.expression.text === "Object" && + !sourceBinding7(program, call.expression.expression) && + call.expression.name.text === "defineProperty" && + call.arguments.length === 3 && + exportObject(call.arguments[0]!) && + ts.isStringLiteralLike(call.arguments[1]!) && + inertExpr(call.arguments[2]!) + ); + }; + for (const m of comp) { + if (!isCjsJsFile7(m, program)) { + return `${m.fileName} is not a CommonJS module — require cycles are admitted between CommonJS modules only`; + } + const stmts = m.statements; + // The first statement that can run another module's code. + let firstEntry = stmts.length; + for (let i = 0; i < stmts.length; i++) { + const st = stmts[i]!; + const inert = + ts.isFunctionDeclaration(st) || + ts.isEmptyStatement(st) || + (ts.isExpressionStatement(st) && ts.isStringLiteral(st.expression)) || + (ts.isClassDeclaration(st) && classSyncParts7(st).every(inertPart)) || + (ts.isVariableStatement(st) && + st.declarationList.declarations.every( + (d) => + ts.isIdentifier(d.name) && (d.initializer === undefined || inertExpr(d.initializer)), + )) || + exportStatement(st); + if (!inert) { + firstEntry = i; + break; + } + } + // Every other use of the export object must be a plain member read: + // anything else can change what a module of the cycle observes. + let stray: ts.Node | null = null; + const settled = new Set(stmts.slice(0, firstEntry).filter(exportStatement)); + ts.walkPreorder(m, (n) => { + if (stray !== null) return "stop"; + if (ts.isStatement(n) && settled.has(n)) return "skip"; + let use: ts.Expression | null = null; + if (n.kind === ts.SyntaxKind.ThisKeyword) { + let moduleThis = true; + for (let p = n.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) { + if ( + (ts.isFunctionLike(p) && !ts.isArrowFunction(p)) || + ts.isClassDeclaration(p) || + ts.isClassExpression(p) + ) { + moduleThis = false; + break; + } + } + if (moduleThis) use = n as ts.Expression; + } else if ( + ts.isIdentifier(n) && + (n.text === "exports" || n.text === "module") && + !(ts.isPropertyAccessExpression(n.parent) && n.parent.name === n) && + !sourceBinding7(program, n) + ) { + if (n.text === "exports") use = n; + else if (!ts.isPropertyAccessExpression(n.parent) || n.parent.expression !== n) stray = n; + else if (n.parent.name.text === "exports") use = n.parent; + } + if (use === null) return undefined; + const parent = use.parent; + const read = + ts.isPropertyAccessExpression(parent) && + parent.expression === use && + !( + ts.isBinaryExpression(parent.parent) && + parent.parent.left === parent && + parent.parent.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && + parent.parent.operatorToken.kind <= ts.SyntaxKind.LastAssignment + ) && + !(ts.isCallExpression(parent.parent) && parent.parent.expression === parent) && + !ts.isDeleteExpression(parent.parent); + if (!read) stray = use; + return undefined; + }); + if (stray !== null) { + return `${m.fileName} uses its exports at ${lineOf(stray)} outside the export statements that precede its first require, so a module of the cycle can observe them before they settle`; + } + // Bindings of other members must stay unreachable from top-level code. + for (const st of stmts) { + for (const req of requiresOf7(st)) { + if (req.decl === null) continue; + const dep = resolveImport7(program, m, req.spec, "require"); + if (dep === null || !members.has(dep)) continue; + const reached = requireBindingEarlyUse7(program, m, stmts.length, req.decl, []); + if (reached !== null) { + return `the top-level code of ${m.fileName} can use its binding '${reached.text}' of a module of the cycle (${lineOf(reached)}) while the cycle initializes`; + } + } + } + } + return null; +} + /* ── exact cyclic initialization ───────────────────────────────────────── * Cycles that fail the declaration-only rule still evaluate exactly like * Node when their shape is representable: the guarded %init calls already @@ -4049,10 +4769,37 @@ function preflight7(load: LoadResult): { { const jsRequireFile = isJsSourceFileName(sf.fileName); const stmts = sf.statements; - let firstRunnable = -1; - stmts.forEach((s, i) => { - if (firstRunnable < 0 && !purePrefixStmt7(program, s)) firstRunnable = i; - }); + // A binding that code running before the require can reach. Node + // initializes it AT the require: a const/let read earlier throws + // ReferenceError, which the lowering reproduces with an + // initialization check at every reference that can run early + // (require-initialization.ts). A var reads undefined there, which + // module aliases cannot represent; createRequire bindings have no + // checked form either. + const earlyRequireFence = ( + req: { decl: ts.VariableDeclaration | null }, + bindingKind: RequireCallBindingKind7, + loc: { file: string; start: number; end: number }, + ): boolean => { + const early = req.decl !== null ? requireEarlyReference7(program, req.decl) : null; + if ( + early === null || + (bindingKind !== "createRequire" && + ((req.decl!.parent?.flags ?? 0) & (ts.NodeFlags.Const | ts.NodeFlags.Let)) !== 0) + ) { + return false; + } + diags.push( + unsupportedDiag( + "SC1013", + loc, + bindingKind === "createRequire" + ? `createRequire() binding '${early.text}' below code that can reach it (Node throws ReferenceError on any earlier access, which is not modeled — move the require above the code that can run first)` + : `var require() binding '${early.text}' below code that can reach it (Node reads undefined before the require runs, which is not modeled — declare it with const or let, or move the require above the code that can run first)`, + ), + ); + return true; + }; for (let k = 0; k < stmts.length; k++) { const stmt = stmts[k]!; for (const req of requiresOf7(stmt)) { @@ -4072,24 +4819,62 @@ function preflight7(load: LoadResult): { if (load.externalTypes.has(req.spec)) { continue; } + // A JSON document: the require answers its cached parsed object + // (json-require.ts), an ordinary value the binding stores, so + // every destructuring form lowers as usual. No module edge. + const jsonDep = resolveImport7(program, sf, req.spec, "require"); + if (jsonDep !== null && jsonDep.fileName.endsWith(".json")) { + if (bindingKind === "createRequire") continue; + if (ts.isVariableStatement(stmt) && requiresOf7(stmt).length > 1) { + const others = requiresOf7(stmt).some((r) => { + const d = resolveImport7(program, sf, r.spec, "require"); + return d === null || !d.fileName.endsWith(".json"); + }); + if (others) { + diags.push( + unsupportedDiag( + "SC1012", + loc, + "require() of a JSON module declared together with other require() bindings", + ), + ); + continue; + } + } + earlyRequireFence(req, bindingKind, loc); + continue; + } if (req.decl && ts.isArrayBindingPattern(req.decl.name)) { diags.push(unsupportedDiag("SC1012", loc, "array-destructuring require() bindings")); continue; } if (req.decl && ts.isObjectBindingPattern(req.decl.name)) { - const bad = req.decl.name.elements.find( - (e) => - e.name === undefined || - !ts.isIdentifier(e.name) || - e.initializer !== undefined || - e.dotDotDotToken !== undefined, - ); - if (bad) { + // A nested pattern of a builtin binds members of the sub-module + // its key names (`const { types: { isPromise } } = + // require("util")` — util.types IS util/types). + const builtin = canonicalBuiltinModule(req.spec); + const plainElements = ( + pattern: ts.ObjectBindingPattern, + module: string | null, + ): boolean => + pattern.elements.every((e) => { + if (e.initializer !== undefined || e.dotDotDotToken !== undefined) return false; + if (e.name !== undefined && ts.isIdentifier(e.name)) return true; + if (module === null || e.name === undefined || !ts.isObjectBindingPattern(e.name)) + return false; + const key = e.propertyName; + if (key === undefined || !ts.isIdentifier(key)) return false; + const sub = canonicalBuiltinModule(`${module}/${key.text}`); + return sub !== null && plainElements(e.name, sub); + }); + if (!plainElements(req.decl.name, builtin)) { diags.push( unsupportedDiag( "SC1012", loc, - "require() destructuring with defaults, rest, or nested patterns", + builtin !== null + ? "require() destructuring with defaults, rest, or nested patterns other than builtin sub-modules" + : "require() destructuring with defaults, rest, or nested patterns of program modules (JSON documents and builtin sub-modules destructure fully)", ), ); continue; @@ -4132,38 +4917,7 @@ function preflight7(load: LoadResult): { continue; } } - if (dep && dep.fileName.endsWith(".json")) { - if (bindingKind === "createRequire") continue; - diags.push(unsupportedDiag("SC1012", loc, "require() of JSON modules")); - continue; - } - // Earlier declarators run too: `const x = read(), dep = - // require('./dep')` must not lose the guard merely because both - // initializers share one VariableStatement. - const precedingDecls = - req.decl && ts.isVariableStatement(stmt) - ? stmt.declarationList.declarations.slice( - 0, - stmt.declarationList.declarations.indexOf(req.decl), - ) - : []; - const prefixCanRun = - (firstRunnable >= 0 && firstRunnable < k) || - precedingDecls.some((decl) => !purePrefixDecl7(decl)); - const tdzName = - prefixCanRun && req.decl - ? bindingEarlyUse7(program, sf, k, req.decl, precedingDecls) - : null; - if (tdzName !== null) { - diags.push( - unsupportedDiag( - "SC1013", - loc, - `require() below code that can reach its binding '${tdzName}' (Node initializes the binding AT the require and throws ReferenceError on any earlier access, which is not modeled — move the require above the code that can run first)`, - ), - ); - continue; - } + if (earlyRequireFence(req, bindingKind, loc)) continue; if (dep) deps.push({ dep }); } } @@ -4191,11 +4945,9 @@ function preflight7(load: LoadResult): { } const projectDep = resolveImport7(program, sf, spec, "require"); if (projectDep !== null) { - if (projectDep.fileName.endsWith(".json")) { - if (bindingKind === "createRequire") continue; - diags.push(unsupportedDiag("SC1012", loc, "require() of JSON modules")); - continue; - } + // A bare require of a JSON document only parses it: nothing to + // evaluate (json-require.ts creates the cached object). + if (projectDep.fileName.endsWith(".json")) continue; deps.push({ dep: projectDep }); continue; } @@ -4930,6 +5682,8 @@ export { resolveImport7 as resolveImport, resolveNpmImport7 as resolveNpmImport, locOf7 as locOf, + requireEarlyReference7 as requireEarlyReference, + inDeferredPosition7 as inDeferredPosition, }; /** True for JavaScript source files (.js/.mjs/.cjs) — the files whose diff --git a/packages/compiler/src/frontend/type-mapper.ts b/packages/compiler/src/frontend/type-mapper.ts index 52964e1bde..5ecfc60aea 100644 --- a/packages/compiler/src/frontend/type-mapper.ts +++ b/packages/compiler/src/frontend/type-mapper.ts @@ -1386,6 +1386,23 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { ) { return { kind: "object", className: "%Error" }; } + // child_process.ExecException, exec's callback error, is an interface + // extending Error with optional metadata members; the value is one + // ordinary Error as well. + const execException = type.getSymbol(); + if ( + execException?.name === "ExecException" && + checker + .declarationsOf(execException) + .some( + (decl) => + ts.isInterfaceDeclaration(decl) && + ctx.isStdlibFile(decl.getSourceFile()) && + isDeclaredInAmbientModule(decl, "child_process"), + ) + ) { + return { kind: "object", className: "%Error" }; + } if (isOsConstantsType(type, ctx) || isLegacyUrlType(type, ctx)) return DYN; // Immutable (@scriptc/types) is a deep-readonly view: the value is // still the T it maps. @@ -2935,11 +2952,13 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { return { kind: "dgramSocket" }; } // node:test's TestContext — the test-body parameter (`test('x', (t) => - // ...)`). @types/node's `class TestContext` and the fallback - // declarations' interface both live inside `declare module "node:test"` - // (isDeclaredInAmbientModule answers for both spellings). + // ...)`) — and SuiteContext, the describe-body and suite-hook parameter: + // both are the runner's node for the test or suite. @types/node's + // classes and the fallback declarations' interfaces all live inside + // `declare module "node:test"` (isDeclaredInAmbientModule answers for + // both spellings). if ( - psym?.name === "TestContext" && + (psym?.name === "TestContext" || psym?.name === "SuiteContext") && checker .declarationsOf(psym) .some( @@ -3543,12 +3562,14 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { ), }; } - // The promisified exec/execFile result `{ stdout: string; stderr: string }`: - // an anonymous type literal (@types/node's __promisify__ overloads in - // child_process, the shipped fallback's promisify overloads in util), - // so it maps by provenance and exact shape, interned as the - // %execFileAsync/%execAsync helpers build it. Node builds the object as - // { stdout, stderr }. The Buffer-typed literals do not match and stay + // The promisified exec/execFile result `{ stdout: string; stderr: string }` + // and the promisified dns.lookupService result `{ hostname: string; + // service: string }`: anonymous type literals (@types/node's + // __promisify__ overloads in child_process and dns, the shipped + // fallback's promisify overloads in util), so they map by provenance and + // exact shape. The exec result is interned as the %execFileAsync/ + // %execAsync helpers build it; Node builds { stdout, stderr } and + // { hostname, service }. The Buffer-typed literals do not match and stay // unmapped. if ( psym && @@ -3558,21 +3579,38 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { (d) => ts.isTypeLiteralNode(d) && ctx.isStdlibFile(d.getSourceFile()) && - (isDeclaredInAmbientModule(d, "child_process") || isDeclaredInAmbientModule(d, "util")), + (isDeclaredInAmbientModule(d, "child_process") || + isDeclaredInAmbientModule(d, "util") || + isDeclaredInAmbientModule(d, "dns")), ) ) { const props = checker.getPropertiesOfType(widened); - if ( + const names = props.length === 2 && props.every( (p) => - (p.name === "stdout" || p.name === "stderr") && (p.flags & ts.SymbolFlags.Optional) === 0 && (checker.getTypeOfSymbol(p).flags & ts.TypeFlags.String) !== 0, - ) && - props[0]!.name !== props[1]!.name - ) { - return { kind: "record", shapeId: execResultShape(ctx.shapes) }; + ) + ? props + .map((p) => p.name) + .sort() + .join(",") + : ""; + if (names === "stderr,stdout") return { kind: "record", shapeId: execResultShape(ctx.shapes) }; + if (names === "hostname,service") { + return { + kind: "record", + shapeId: ctx.shapes.intern( + [ + { name: "hostname", type: STRING }, + { name: "service", type: STRING }, + ], + false, + undefined, + ["hostname", "service"], + ), + }; } } // crypto.X509Certificate — the Dirent-style data record: THREE visible diff --git a/packages/compiler/src/ir/array-length-stability.test.ts b/packages/compiler/src/ir/array-length-stability.test.ts new file mode 100644 index 0000000000..44e461ca2d --- /dev/null +++ b/packages/compiler/src/ir/array-length-stability.test.ts @@ -0,0 +1,141 @@ +import { expect, test } from "vitest"; +import { findUnaliasedLocalArrays, preservesArrayLength } from "./array-length-stability.js"; +import { + BOOL, + F64, + arrayOf, + type IrExpr, + type IrFunction, + type IrLocal, + type IrStmt, +} from "./ir.js"; + +const loc = { file: "array-length-stability.ts", start: 0, end: 0 }; +const ARR = arrayOf(F64); +const num = (value: number): IrExpr => ({ kind: "numLit", value, type: F64, loc }); +const ref = (localId: string): IrExpr => ({ kind: "varRef", localId, type: F64, loc }); +const arr = (localId: string): IrExpr => ({ kind: "varRef", localId, type: ARR, loc }); +const method = ( + receiver: string, + name: Extract["method"], + args: IrExpr[] = [], +): IrExpr => ({ + kind: "arrIntrinsic", + method: name, + receiver: arr(receiver), + args, + type: F64, + loc, +}); +const length = (receiver: string): IrExpr => method(receiver, "length"); +const read = (receiver: string, index: IrExpr): IrExpr => method(receiver, "getNumber", [index]); +const plus = (left: IrExpr, right: IrExpr): IrExpr => ({ + kind: "bin", + op: "+", + left, + right, + type: F64, + loc, +}); +const stmt = (expr: IrExpr): IrStmt => ({ kind: "exprStmt", expr, loc }); +const set = (receiver: string, index: IrExpr, value: IrExpr): IrStmt => ({ + kind: "arraySet", + arr: arr(receiver), + index, + value, + loc, +}); +const cond: IrExpr = { kind: "bin", op: "<", left: ref("i"), right: length("a"), type: BOOL, loc }; +const sum: IrStmt = { + kind: "assign", + localId: "s", + value: plus(ref("s"), read("a", ref("i"))), + loc, +}; +const locals = new Map([ + ["i", { id: "i", name: "i", type: F64, mutable: true }], + ["s", { id: "s", name: "s", type: F64, mutable: true }], + ["a", { id: "a", name: "a", type: ARR, mutable: false }], + ["b", { id: "b", name: "b", type: ARR, mutable: false }], + ["out", { id: "out", name: "out", type: ARR, mutable: false }], +]); +const preserves = ( + body: IrStmt[], + inRange: string | null = "i", + unaliased: ReadonlySet = new Set(), + scope = locals, +): boolean => preservesArrayLength([cond, ...body], { receiver: "a", inRange }, scope, unaliased); + +test("reads, local arithmetic and element writes at the in-range counter keep the length", () => { + expect(preserves([sum])).toBe(true); + expect(preserves([set("a", ref("i"), num(1)), set("b", ref("i"), read("a", ref("i")))])).toBe( + true, + ); + expect(preserves([stmt(method("a", "indexOf", [num(3)]))])).toBe(true); +}); + +test("writes past the counter, without an in-range counter, or to the counter can grow it", () => { + expect(preserves([set("a", plus(ref("i"), num(1)), num(1))])).toBe(false); + expect(preserves([set("b", ref("s"), num(1))])).toBe(false); + expect(preserves([set("a", ref("i"), num(1))], null)).toBe(false); + expect(preserves([{ kind: "assign", localId: "i", value: num(0), loc }, sum])).toBe(false); +}); + +test("resizing methods, length stores, calls and escapes refuse the proof", () => { + for (const name of ["push", "pop", "shift", "unshift", "splice", "fill"] as const) + expect(preserves([stmt(method("a", name, [num(1)]))])).toBe(false); + // An unrelated array may alias the receiver. + expect(preserves([stmt(method("b", "push", [num(1)]))])).toBe(false); + expect(preserves([{ kind: "arraySetLength", arr: arr("a"), length: num(0), loc }])).toBe(false); + expect(preserves([{ kind: "arrayDelete", arr: arr("a"), index: ref("i"), loc }])).toBe(false); + expect(preserves([stmt({ kind: "call", callee: "f", args: [], type: F64, loc })])).toBe(false); + expect(preserves([stmt({ kind: "call", callee: "f", args: [arr("a")], type: F64, loc })])).toBe( + false, + ); + expect(preserves([{ kind: "varDecl", localId: "b", init: arr("a"), loc }])).toBe(false); + expect(preserves([{ kind: "assign", localId: "a", value: arr("b"), loc }])).toBe(false); +}); + +test("an unaliased local array other than the receiver may grow", () => { + const push = stmt(method("out", "push", [read("a", ref("i"))])); + expect(preserves([push], "i", new Set(["out"]))).toBe(true); + expect(preserves([set("out", ref("s"), num(1))], "i", new Set(["out"]))).toBe(true); + expect(preserves([push])).toBe(false); + // The receiver itself is never resized, even when no alias exists. + expect(preserves([stmt(method("a", "push", [num(1)]))], "i", new Set(["a"]))).toBe(false); +}); + +test("captured receivers have no local proof", () => { + const boxed = new Map(locals); + boxed.set("a", { ...locals.get("a")!, boxed: true }); + expect(preserves([sum], "i", new Set(), boxed)).toBe(false); +}); + +function fn(body: IrStmt[], params: string[] = []): IrFunction { + return { + loc, + name: "f", + params: params.map((localId) => ({ localId, type: ARR })), + returnType: ARR, + locals: [...locals.values()], + body, + } as IrFunction; +} +const fresh: IrExpr = { kind: "arrayLit", elems: [], type: ARR, loc }; + +test("fresh local arrays read only as receivers have no alias", () => { + const body: IrStmt[] = [ + { kind: "varDecl", localId: "out", init: fresh, loc }, + stmt(method("out", "push", [num(1)])), + { kind: "return", value: arr("out"), loc }, + ]; + expect([...findUnaliasedLocalArrays(fn(body))]).toEqual(["out"]); + // Parameters, copies into another binding, and arguments escape. + expect(findUnaliasedLocalArrays(fn(body, ["out"])).size).toBe(0); + const copied = [...body, { kind: "varDecl", localId: "b", init: arr("out"), loc } as IrStmt]; + expect(findUnaliasedLocalArrays(fn(copied)).has("out")).toBe(false); + const passed = [...body, stmt({ kind: "call", callee: "g", args: [arr("out")], type: F64, loc })]; + expect(findUnaliasedLocalArrays(fn(passed)).has("out")).toBe(false); + const rebound = [...body, { kind: "assign", localId: "out", value: arr("a"), loc } as IrStmt]; + expect(findUnaliasedLocalArrays(fn(rebound)).has("out")).toBe(false); +}); diff --git a/packages/compiler/src/ir/array-length-stability.ts b/packages/compiler/src/ir/array-length-stability.ts new file mode 100644 index 0000000000..2ac196d30b --- /dev/null +++ b/packages/compiler/src/ir/array-length-stability.ts @@ -0,0 +1,259 @@ +import type { IrExpr, IrFunction, IrLocal, IrStmt } from "./ir.js"; +import { isBorrowSafeMath } from "./analysis.js"; +import { byteNumberAccess } from "./byte-numbers.js"; +import { everyExprChild, everyStmtChild } from "./traverse.js"; + +/** Array methods that read the receiver without changing its length or its + * slot states and run no user code. */ +const READING_ARRAY_METHODS = new Set([ + "length", + "getNumber", + "indexEq", + "indexOf", + "includes", + "nextPresent", +]); + +/** String methods that read an immutable string and run no user code. */ +const READING_STRING_METHODS = new Set(["length", "charCodeAt", "codePointAt"]); + +/** Expressions that compute from their operands and run no user code. Their + * children are checked separately. */ +const PLAIN_EXPRESSIONS = new Set([ + "numLit", + "strLit", + "boolLit", + "unitLit", + "varRef", + "bin", + "unary", + "incDec", + "toBool", + "logical", + "ternary", + "strEq", + "strCmp", + "fieldGet", + "recordGet", + "unionNarrow", + "unionIsTag", + "unionDisc", + "unionWrap", + "upcast", + "downcast", + "instanceOf", + "nullish", + "seqExpr", +]); + +/** Statements whose own effect is a local write or control flow inside the + * loop. Their children are checked separately. */ +const PLAIN_STATEMENTS = new Set([ + "varDecl", + "assign", + "exprStmt", + "if", + "block", + "break", + "continue", + "for", + "while", + "doWhile", +]); + +/** Array methods whose result is never the receiver itself (unlike + * `reverse`, `fill` or `copyWithin`, which return it). */ +const CONTAINED_RECEIVER_METHODS = new Set([ + ...READING_ARRAY_METHODS, + "push", + "pop", + "shift", + "unshift", + "splice", + "join", + "slice", +]); + +/** Local arrays that no other binding can hold: every write of the local + * stores a fresh array literal and every read of it is the receiver of an + * element access or array method (or the value of a `return`, which ends the + * activation). No alias of such an array exists, so mutating it cannot change + * the length of an array held by any other binding. */ +export function findUnaliasedLocalArrays(fn: IrFunction): ReadonlySet { + const candidates = new Set(); + const captured = new Set( + [...(fn.captures ?? []), ...(fn.classCaptures ?? [])].map((c) => c.localId), + ); + for (const local of fn.locals) + if (local.type.kind === "array" && !local.boxed && !local.tdz && !captured.has(local.id)) + candidates.add(local.id); + for (const param of fn.params) candidates.delete(param.localId); + const written = new Set(); + const fresh = (value: IrExpr | null): boolean => + value !== null && (value.kind === "arrayLit" || value.kind === "arrayNewLen"); + const receiver = (e: IrExpr): void => { + if (e.kind !== "varRef") expr(e); + }; + const expr = (e: IrExpr): boolean => { + switch (e.kind) { + case "varRef": + candidates.delete(e.localId); + return true; + case "assignExpr": + candidates.delete(e.localId); + break; + case "arrIntrinsic": + if (!CONTAINED_RECEIVER_METHODS.has(e.method)) break; + receiver(e.receiver); + e.args.forEach(expr); + return true; + case "arrayGet": + case "arrayHas": + case "arrayState": + receiver(e.arr); + expr(e.index); + return true; + } + return everyExprChild(e, expr, stmt); + }; + const stmt = (s: IrStmt): boolean => { + switch (s.kind) { + case "varDecl": + case "assign": { + const value = s.kind === "varDecl" ? s.init : s.value; + if (fresh(value)) written.add(s.localId); + else if (value) candidates.delete(s.localId); + if (value) expr(value); + return true; + } + case "arraySet": + receiver(s.arr); + expr(s.index); + expr(s.value); + return true; + case "return": + if (s.value) receiver(s.value); + return true; + case "forOf": + case "tryCatch": + if (s.kind === "forOf") candidates.delete(s.localId); + if (s.kind === "tryCatch" && s.catchLocalId) candidates.delete(s.catchLocalId); + break; + } + return everyStmtChild(s, expr, stmt); + }; + fn.body.every(stmt); + // A binding with no visible write takes its value some other way. + for (const id of candidates) if (!written.has(id)) candidates.delete(id); + return candidates; +} + +export interface ArrayLengthLoop { + /** The local holding the array whose length must not change. */ + receiver: string; + /** A binding that holds a value below the array's length wherever the + * loop's condition, body or update reads it (the induction variable of + * `for (let i = 0; i < a.length; i++)` when the body never writes it, as + * long as the length itself has not changed). An element write at this + * index replaces an existing element or sets a non-index property. */ + inRange: string | null; +} + +/** Whether running `nodes` (a loop's condition, body and update) can never + * change the length of the array held by local `loop.receiver`. The proof is + * syntactic and conservative: every expression and statement must be one + * that runs no user code (no calls, callbacks, closures, iterators, + * suspension or exception regions); the binding is never written and only + * ever read as the receiver of an element access or a reading array method, + * so the array cannot escape to code that resizes it; and nothing in the + * nodes resizes any array that may be the same one. Element writes are + * accepted at the in-range binding (no array grows by them: an alias of the + * receiver has the same length) and anywhere in an unaliased local array. + * Array methods that resize an array are accepted only on unaliased local + * arrays other than the receiver. */ +export function preservesArrayLength( + nodes: readonly (IrExpr | IrStmt)[], + loop: ArrayLengthLoop, + locals: ReadonlyMap, + unaliased: ReadonlySet, +): boolean { + const local = locals.get(loop.receiver); + if (!local || local.type.kind !== "array" || local.boxed || local.tdz) return false; + const separate = (e: IrExpr): boolean => + e.kind === "varRef" && e.localId !== loop.receiver && unaliased.has(e.localId); + // A receiver position: the receiver binding itself is read there without + // escaping. + const receiver = (e: IrExpr): boolean => + (e.kind === "varRef" && e.localId === loop.receiver) || expr(e); + const written = (id: string): boolean => id === loop.receiver || id === loop.inRange; + const expr = (e: IrExpr): boolean => { + switch (e.kind) { + case "varRef": + return e.localId !== loop.receiver; + case "assignExpr": + case "incDec": + return !written(e.localId) && everyExprChild(e, expr, stmt); + case "arrayGet": + case "arrayHas": + case "arrayState": + return receiver(e.arr) && expr(e.index); + case "arrIntrinsic": + if (READING_ARRAY_METHODS.has(e.method)) return receiver(e.receiver) && e.args.every(expr); + return separate(e.receiver) && e.args.every(expr); + case "strIntrinsic": + return ( + READING_STRING_METHODS.has(e.method) && + e.receiver.type.kind === "string" && + expr(e.receiver) && + e.args.every(expr) + ); + case "bytesIntrinsic": { + const numeric = byteNumberAccess(e); + return ( + (numeric ? !numeric.write : e.method === "get" || e.method === "length") && + expr(e.receiver) && + e.args.every(expr) + ); + } + case "libCall": + return ( + isBorrowSafeMath(e.fn) && + e.args.every((arg) => arg.type.kind === "f64") && + e.args.every(expr) + ); + case "strConcat": + return ( + e.left.type.kind === "string" && + e.right.type.kind === "string" && + expr(e.left) && + expr(e.right) + ); + default: + return PLAIN_EXPRESSIONS.has(e.kind) && everyExprChild(e, expr, stmt); + } + }; + const stmt = (s: IrStmt): boolean => { + switch (s.kind) { + case "varDecl": + case "assign": + if (written(s.localId)) return false; + break; + case "arraySet": + return ( + (separate(s.arr) || + (s.index.kind === "varRef" && + loop.inRange !== null && + s.index.localId === loop.inRange)) && + receiver(s.arr) && + expr(s.index) && + expr(s.value) + ); + case "return": + return s.value === null || expr(s.value); + default: + if (!PLAIN_STATEMENTS.has(s.kind)) return false; + } + return everyStmtChild(s, expr, stmt); + }; + return nodes.every((node) => ("type" in node ? expr(node as IrExpr) : stmt(node as IrStmt))); +} diff --git a/packages/compiler/src/ir/builtin-signatures.ts b/packages/compiler/src/ir/builtin-signatures.ts index d3ad4f26ff..f673ebd0c9 100644 --- a/packages/compiler/src/ir/builtin-signatures.ts +++ b/packages/compiler/src/ir/builtin-signatures.ts @@ -1436,11 +1436,10 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ argTypes: [STRING, F64, STRING, F64, STRING, F64, F64, F64], result: VOID, }, - "test.suite": { - argTypes: [STRING, F64, STRING, { kind: "func", params: [], ret: VOID }, STRING, F64, F64], - result: VOID, - }, - "test.hook": { argTypes: [F64, null, F64], result: VOID }, + // The suite body takes no parameters or its SuiteContext (flag 2). + "test.suite": { argTypes: [STRING, F64, STRING, null, STRING, F64, F64], result: VOID }, + // The trailing string is the hook call's "file:line:col". + "test.hook": { argTypes: [F64, null, F64, STRING], result: VOID }, "test.sub": { argTypes: [TESTCTX_T, STRING, F64, STRING, null, F64, STRING, F64, F64, F64], result: { kind: "promise", inner: VOID }, @@ -1454,6 +1453,10 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "test.ctxDiagnostic": { argTypes: [TESTCTX_T, STRING], result: VOID }, "test.ctxName": { argTypes: [TESTCTX_T], result: STRING }, "test.ctxFullName": { argTypes: [TESTCTX_T], result: STRING }, + // t.passed, and t.workerId's Number(NODE_TEST_WORKER_ID) with 0 for + // undefined. + "test.ctxPassed": { argTypes: [TESTCTX_T], result: BOOL }, + "test.workerId": { argTypes: [], result: F64 }, // t.before/t.after/t.beforeEach/t.afterEach (which: 0 before, 1 after, // 2 beforeEach, 3 afterEach); a throwing t.before is captured on the test. "test.ctxHook": { argTypes: [TESTCTX_T, F64, null, F64], result: VOID }, @@ -2343,8 +2346,9 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ /** child_process.execFile's callback slice: starts an all-piped child, * captures stdout/stderr, and moves the error-first callback into the * child registry. The callback shape is program-dependent and checked - * by the validator/backends. */ - "cp.execFile": { argTypes: [STRING, arrayOf(STRING), null], result: CHILD_T }, + * by the validator/backends. The last argument is exec's shell flag: + * the argv is ["-c", command] and failure messages show the command. */ + "cp.execFile": { argTypes: [STRING, arrayOf(STRING), null, BOOL], result: CHILD_T }, // spawn's options form: per-slot stdio modes (0 ignore / 1 inherit / // 2 fd) with the out/err fds for mode 2, detached, env replacement // pairs, cwd ("" = inherit). @@ -2583,6 +2587,25 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "crypto.hkdfCb": { argTypes: [STRING, BYTES_U8, BYTES_U8, BYTES_U8, F64, null], result: VOID }, "crypto.scrypt": { argTypes: [BYTES_U8, BYTES_U8, F64, DYN], result: BYTES_U8 }, "crypto.scryptCb": { argTypes: [BYTES_U8, BYTES_U8, F64, DYN, null], result: VOID }, + /** util.promisify over the callback forms: the callback form's + * arguments without the callback → a promise the callback form settles, + * or rejected with an argument error; never throws. The result's value + * type is the declared one (a Buffer, the filled view, a number, or a + * checked-dynamic value); the validator checks it. */ + "crypto.randomBytesPromise": { argTypes: [F64], result: VOID }, + "crypto.pbkdf2Promise": { argTypes: [BYTES_U8, BYTES_U8, F64, F64, STRING], result: VOID }, + "crypto.scryptPromise": { argTypes: [BYTES_U8, BYTES_U8, F64, DYN], result: VOID }, + "crypto.hkdfPromise": { argTypes: [STRING, BYTES_U8, BYTES_U8, BYTES_U8, F64], result: VOID }, + "crypto.randomFillPromise": { argTypes: [null, F64, F64, F64], result: VOID }, + "crypto.randomFillPromiseDyn": { argTypes: [DYN, DYN, DYN, F64], result: VOID }, + "crypto.randomIntPromise": { argTypes: [F64, F64, F64], result: VOID }, + "crypto.generateKeyPromise": { argTypes: [DYN, DYN], result: VOID }, + /** util.promisify(dns.lookup) and util.promisify(dns.lookupService): + * [hostname, options] and [address, port] → a promise of the + * checked-dynamic result object (the address array with options.all), + * or rejected with the error; never throws. */ + "dns.lookupPromise": { argTypes: [DYN, DYN], result: { kind: "promise", inner: DYN } }, + "dns.lookupServicePromise": { argTypes: [DYN, DYN], result: { kind: "promise", inner: DYN } }, // The Buffer statics and the fs/zlib Buffer forms: fixed always-u8 // signatures (Buffer IS a Uint8Array — one bytes kind). /** The Buffer statics with fixed (always-u8) signatures. fromStr is @@ -2772,6 +2795,9 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ * receivers are borrowed. */ "zlib.streamNew": { argTypes: [F64, DYN], result: VOID }, "zlib.streamBytesWritten": { argTypes: [null], result: F64 }, + /** `stream instanceof zlib.Gzip`: the borrowed stream and the class's + * node_zlib_mode; true when the stream is a zlib stream of that mode. */ + "zlib.streamIs": { argTypes: [null, F64], result: BOOL }, "zlib.streamFlush": { argTypes: [null, DYN], result: VOID }, "zlib.streamFlushCb": { argTypes: [null, DYN, null], result: VOID }, "zlib.streamParams": { argTypes: [null, DYN, DYN], result: VOID }, @@ -3413,6 +3439,10 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ "emitter.getMax": { argTypes: [null], result: F64 }, "emitter.setDefaultMax": { argTypes: [F64], result: VOID }, "emitter.getDefaultMax": { argTypes: [], result: F64 }, + // The ES module namespace's copy of defaultMaxListeners, and + // module.syncBuiltinESMExports() refreshing it. + "emitter.esmDefaultMax": { argTypes: [], result: F64 }, + "emitter.esmSync": { argTypes: [], result: VOID }, /** The events.errorMonitor event key: an immortal string the runtime * registry matches by identity, so no string event name reaches it. */ "emitter.errorMonitor": { argTypes: [], result: STRING }, diff --git a/packages/compiler/src/ir/capture-values.ts b/packages/compiler/src/ir/capture-values.ts index 94cff053bb..d7a1516bb1 100644 --- a/packages/compiler/src/ir/capture-values.ts +++ b/packages/compiler/src/ir/capture-values.ts @@ -244,8 +244,13 @@ interface FunctionFacts { classCaptured: Set; reads: Map; /** Closure capture edges: the creator's binding `source` flows into - * capture `local` of `target`. */ - edges: { target: string; index: number; source: string }[]; + * capture `local` of `target`, grouped by `source`. */ + edges: Map; + /** The function's own captures (first index of each id), class + * captures, and parameters, for constant-time membership tests. */ + captureIndex: Map; + classCaptures: Set; + params: Set; } interface CreationSite { @@ -317,7 +322,7 @@ export class CaptureFlow { ? value.fnName : null; } - const index = fn.captures?.findIndex((c) => c.localId === id) ?? -1; + const index = this.factsOf(fn).captureIndex.get(id) ?? -1; if (index < 0 || !this.stableCapture(fnName, id)) return null; let result: string | null = null; for (const site of this.sites.get(fnName) ?? []) { @@ -399,7 +404,7 @@ export class CaptureFlow { return true; }; everyStmtList(body.slice(declared + 1), { stmt: () => true, expr: count }); - const edges = facts.edges.filter((edge) => edge.source === id).length; + const edges = facts.edges.get(id)?.length ?? 0; return nodes === edges && !facts.classCaptured.has(id); } @@ -410,7 +415,7 @@ export class CaptureFlow { const local = facts.locals.get(id); const declaration = facts.declarations.get(id); if (local === undefined || local.boxed === true || local.tdz === true) return false; - if (fn.params.some((p) => p.localId === id) || declaration?.count !== 1) return false; + if (facts.params.has(id) || declaration?.count !== 1) return false; const writes = facts.writes.get(id) ?? 0; return declaration.initialized ? writes === 0 : facts.hoisted.has(id) && writes === 1; } @@ -450,8 +455,14 @@ export class CaptureFlow { tdzInits: new Map(), classCaptured: new Set(), reads: new Map(), - edges: [], + edges: new Map(), + captureIndex: new Map(), + classCaptures: new Set((fn.classCaptures ?? []).map((c) => c.localId)), + params: new Set(fn.params.map((p) => p.localId)), }; + (fn.captures ?? []).forEach((c, index) => { + if (!facts.captureIndex.has(c.localId)) facts.captureIndex.set(c.localId, index); + }); this.facts.set(fn.name, facts); const capturesIn = (e: IrExpr): Set => { const ids = new Set(); @@ -464,19 +475,17 @@ export class CaptureFlow { visitExpr(e); return ids; }; + // One pass over runs of empty declarations and closure assignments: + // each empty declaration pairs with the first later assignment of its + // binding in the same run. const findHoisted = (body: readonly IrStmt[]): void => { - body.forEach((s, i) => { - if (s.kind !== "varDecl" || s.init !== null) return; - for (let j = i + 1; j < body.length; j++) { - const next = body[j]!; - if (next.kind === "assign" && next.value.kind === "closure") { - if (next.localId === s.localId) { - facts.hoisted.set(s.localId, next); - return; - } - } else if (next.kind !== "varDecl" || next.init !== null) return; - } - }); + const pending = new Set(); + for (const s of body) { + if (s.kind === "assign" && s.value.kind === "closure") { + if (pending.delete(s.localId)) facts.hoisted.set(s.localId, s); + } else if (s.kind === "varDecl" && s.init === null) pending.add(s.localId); + else pending.clear(); + } }; const declare = (s: IrStmt & { kind: "varDecl" }, splittable: boolean): void => { const entry = facts.declarations.get(s.localId); @@ -514,9 +523,11 @@ export class CaptureFlow { let list = this.sites.get(e.fnName); if (list === undefined) this.sites.set(e.fnName, (list = [])); list.push({ owner: fn.name, captures: e.captures, adapts: e.adapts === true }); - e.captures.forEach((source, index) => - facts.edges.push({ target: e.fnName, index, source }), - ); + e.captures.forEach((source, index) => { + let edges = facts.edges.get(source); + if (edges === undefined) facts.edges.set(source, (edges = [])); + edges.push({ target: e.fnName, index, source }); + }); break; } case "classRef": @@ -535,12 +546,15 @@ export class CaptureFlow { // iteration, and statement lists inside expressions release their // locals with the enclosing frame; declarations there stay as they are. const findTdzInits = (body: readonly IrStmt[]): void => { + // The first empty declaration of each binding in this list. + const empty = new Map(); body.forEach((s, j) => { + if (s.kind === "varDecl" && s.init === null && !empty.has(s.localId)) + empty.set(s.localId, j); if (s.kind !== "assign" || s.initializes !== true) return; - const declared = body.findIndex( - (d, i) => i < j && d.kind === "varDecl" && d.localId === s.localId && d.init === null, - ); - if (declared >= 0) facts.tdzInits.set(s.localId, { body, declared, initialized: j }); + const declared = empty.get(s.localId); + if (declared !== undefined && declared < j) + facts.tdzInits.set(s.localId, { body, declared, initialized: j }); }); }; const list = (body: readonly IrStmt[], split: boolean): boolean => { @@ -591,10 +605,6 @@ export class CaptureFlow { return facts; } - private local(fn: IrFunction, id: string): IrLocal | undefined { - return this.factsOf(fn).locals.get(id); - } - /** Nothing rebinds `id` in `fnName` (declarations aside, and `ownWrites` * writes of its own body) or in any body that shares its box through * closure captures. */ @@ -610,8 +620,7 @@ export class CaptureFlow { result = (facts.writes.get(id) ?? 0) === ownWrites && !facts.classCaptured.has(id) && - facts.edges.every((edge) => { - if (edge.source !== id) return true; + (facts.edges.get(id) ?? []).every((edge) => { const capture = this.functions.get(edge.target)?.captures?.[edge.index]; return capture !== undefined && this.unchanged(edge.target, capture.localId); }); @@ -623,19 +632,18 @@ export class CaptureFlow { /** A boxed binding of `fn` itself (not a capture) whose box is filled * once, before any environment observes it, and never written again. */ stableOrigin(fn: IrFunction, id: string): boolean { - const local = this.local(fn, id); + const facts = this.factsOf(fn); + const local = facts.locals.get(id); if (local?.boxed !== true) return false; if (local.tdz === true) { - if (fn.captures?.some((c) => c.localId === id)) return false; - if (this.factsOf(fn).declarations.get(id)?.count !== 1) return false; + if (facts.captureIndex.has(id)) return false; + if (facts.declarations.get(id)?.count !== 1) return false; return this.orderedTdz(fn, id) && this.unchanged(fn.name, id, 1); } - if (fn.captures?.some((c) => c.localId === id)) return false; - if (fn.classCaptures?.some((c) => c.localId === id)) return false; - const facts = this.factsOf(fn); + if (facts.captureIndex.has(id)) return false; + if (facts.classCaptures.has(id)) return false; const declaration = facts.declarations.get(id); - if (fn.params.some((p) => p.localId === id)) - return declaration === undefined && this.unchanged(fn.name, id); + if (facts.params.has(id)) return declaration === undefined && this.unchanged(fn.name, id); if (declaration?.count !== 1) return false; if (declaration.initialized) return this.unchanged(fn.name, id); return facts.hoisted.has(id) && this.unchanged(fn.name, id, 1); @@ -654,8 +662,8 @@ export class CaptureFlow { private computeStableCapture(fnName: string, id: string): boolean { const fn = this.functions.get(fnName); - const index = fn?.captures?.findIndex((c) => c.localId === id) ?? -1; - if (fn === undefined || index < 0) return false; + const index = fn === undefined ? undefined : this.factsOf(fn).captureIndex.get(id); + if (fn === undefined || index === undefined) return false; const sites = this.sites.get(fnName); if (sites === undefined || sites.length === 0) return false; for (const site of sites) { @@ -679,7 +687,7 @@ export class CaptureFlow { const assign = init?.body[init.initialized]; return assign?.kind === "assign" ? plainLiteral(constantNumber(assign.value)) : own.literal; } - const index = fn.captures?.findIndex((c) => c.localId === id) ?? -1; + const index = this.factsOf(fn).captureIndex.get(id) ?? -1; if (index < 0 || !this.stableCapture(fnName, id)) return null; let value: number | null = null; for (const site of this.sites.get(fnName) ?? []) { @@ -715,8 +723,8 @@ export class CaptureFlow { const immutable = new Set(); for (const local of fn.locals) { if (local.boxed !== true) continue; - const capture = fn.captures?.some((c) => c.localId === local.id) === true; - const param = fn.params.some((p) => p.localId === local.id); + const capture = facts.captureIndex.has(local.id); + const param = facts.params.has(local.id); if (capture && local.mutable && this.stableCapture(fn.name, local.id)) immutable.add(local.id); if (!facts.reads.has(local.id)) continue; @@ -871,10 +879,8 @@ export class CaptureFlow { ); const pruned = dead.size === 0 ? body : prune(body, dead); const liveIds = new Set(live.map((s) => (s.kind === "varDecl" ? s.localId : ""))); - const added = locals.filter( - (local) => - liveIds.has(local.id) || !entry.some((s) => s.kind === "varDecl" && s.localId === local.id), - ); + const entryIds = new Set(entry.map((s) => (s.kind === "varDecl" ? s.localId : ""))); + const added = locals.filter((local) => liveIds.has(local.id) || !entryIds.has(local.id)); return { ...fn, locals: [...kept, ...added], body: [...live, ...pruned] }; } } diff --git a/packages/compiler/src/ir/int32-slots.ts b/packages/compiler/src/ir/int32-slots.ts index a6a10eaa71..d7d06db7d1 100644 --- a/packages/compiler/src/ir/int32-slots.ts +++ b/packages/compiler/src/ir/int32-slots.ts @@ -890,27 +890,44 @@ export function analyzeInt32Slots(mod: IrModule): Int32Slots { else groups.set(root, [key]); } const boxOrigin = new Map(); + // Per function, computed on first use: its capture ids and the locals an + // initialized declaration binds (one body walk each, not one per box). + const captureIds = new Map>(); + const capturedIn = (fnName: string, localId: string): boolean => { + let ids = captureIds.get(fnName); + if (ids === undefined) { + ids = new Set((functions.get(fnName)?.captures ?? []).map((c) => c.localId)); + captureIds.set(fnName, ids); + } + return ids.has(localId); + }; + const initializedDeclarations = new Map>(); + const initializedIn = (fn: IrFunction, localId: string): boolean => { + let ids = initializedDeclarations.get(fn.name); + if (ids === undefined) { + const found = new Set(); + walkFunction(fn, { + stmt: (s) => { + if (s.kind === "varDecl" && s.init) found.add(s.localId); + }, + }); + initializedDeclarations.set(fn.name, (ids = found)); + } + return ids.has(localId); + }; for (const [group, members] of groups) { if (members.some((key) => unstableBoxes.has(key))) continue; // The origin is the one member that is not a capture of its function. const origins = members.filter((key) => { const [fnName, localId] = key.split("\0") as [string, string]; - return !functions.get(fnName)?.captures?.some((c) => c.localId === localId); + return !capturedIn(fnName, localId); }); if (origins.length !== 1) continue; const [fnName, localId] = origins[0]!.split("\0") as [string, string]; const origin = functions.get(fnName)!; if (origin.params.some((p) => p.localId === localId)) { if (!slots.hasParam(fnName, localId)) continue; - } else { - let initialized = false; - walkFunction(origin, { - stmt: (s) => { - if (s.kind === "varDecl" && s.localId === localId && s.init) initialized = true; - }, - }); - if (!initialized) continue; - } + } else if (!initializedIn(origin, localId)) continue; boxOrigin.set(`param:${fnName}\0${localId}`, group); slots.addBox(group, members); // A box starts from its origin parameter's value: it can only be diff --git a/packages/compiler/src/ir/integer-loop-facts.ts b/packages/compiler/src/ir/integer-loop-facts.ts index 4c1a1ef1f2..84fabf9563 100644 --- a/packages/compiler/src/ir/integer-loop-facts.ts +++ b/packages/compiler/src/ir/integer-loop-facts.ts @@ -118,6 +118,9 @@ export function boundedIntegerLoopFacts( stable(e.receiver.localId) ) return { min: 0, max: Number.MAX_SAFE_INTEGER }; + // Every evaluation of an array length lies in its range, so the + // condition bounds each pass even when the body resizes the array. + if (isArrayLength(e)) return ARRAY_LENGTH_RANGE; return null; } let step: IntegerRange; @@ -134,12 +137,15 @@ export function boundedIntegerLoopFacts( if (step.min <= 0 || step.max > 2147483647) return result; } const limit = bound(loop.cond.right); - if (!limit || limit.max < start.min) return result; - const distance = limit.max - start.min; + // The counter is an integer, so a strict bound admits passes up to one + // below the limit. + const last = limit && (loop.cond.op === "<" ? limit.max - 1 : limit.max); + if (!limit || last === null || last < start.min) return result; + const distance = last - start.min; const iterations = Math.floor(distance / step.min) + 1; if (!Number.isSafeInteger(distance) || !Number.isSafeInteger(iterations)) return result; // Header facts also cover the failing final condition and zero iterations. - const maximum = Math.max(start.max, limit.max + step.max); + const maximum = Math.max(start.max, last + step.max); if (Number.isSafeInteger(maximum)) result.set(counter, { min: start.min, max: maximum }); for (const [id, hi] of upper) { const range = initial.get(id); @@ -154,6 +160,20 @@ export function boundedIntegerLoopFacts( return result; } +/** Every length an array can have: SCR_ARR_MAX_LENGTH (scr_array.c). */ +export const ARRAY_LENGTH_RANGE: IntegerRange = { min: 0, max: 4294967295 }; + +/** `a.length` of a typed array value (an ordinary JS array). */ +function isArrayLength(e: IrExpr): boolean { + return ( + e.kind === "arrIntrinsic" && + e.method === "length" && + e.args.length === 0 && + e.type.kind === "f64" && + e.receiver.type.kind === "array" + ); +} + /** The most UTF-16 code units a runtime string holds: SCR_STR_MAX_CAP * (scr_runtime.h) caps a string's UTF-8 bytes, and a UTF-16 length never * exceeds the UTF-8 byte length. */ @@ -241,6 +261,9 @@ export function guardedLoopCounterFacts( stable(e.receiver.localId) ) return { min: 0, max: STRING_MAX_UNITS }; + // An array length can change on any pass, but never leaves its range, + // which is all a ceiling needs. + if (isArrayLength(e)) return ARRAY_LENGTH_RANGE; return null; }; // The bounds a condition's conjuncts put on counters while it holds. diff --git a/packages/compiler/src/ir/integer-loops.test.ts b/packages/compiler/src/ir/integer-loops.test.ts index 0f7b9ae134..75968b2623 100644 --- a/packages/compiler/src/ir/integer-loops.test.ts +++ b/packages/compiler/src/ir/integer-loops.test.ts @@ -208,3 +208,111 @@ test("a string read at the counter is an integer consumer", () => { s.body = [read(ref("limit"))]; expect(matchIntegerCountedForLoop(s, withText)).toBeNull(); }); + +test("a stable array length bounds a counted loop; a resizing body keeps it a condition", () => { + const ARR = arrayOf(F64); + const array = (localId: string): IrExpr => ({ kind: "varRef", localId, type: ARR, loc }); + const length: IrExpr = { + kind: "arrIntrinsic", + method: "length", + receiver: array("a"), + args: [], + type: F64, + loc, + }; + const read = (index: IrExpr): IrExpr => ({ + kind: "arrIntrinsic", + method: "getNumber", + receiver: array("a"), + args: [index], + type: F64, + loc, + }); + // for (let i = 0; i < a.length; i += 2) a[i]; + const strided = (body: IrStmt[]): IrStmt & { kind: "for" } => { + const s = counted(length); + s.update = { + kind: "assign", + localId: "i", + value: { kind: "bin", op: "+", left: ref("i"), right: number(2), type: F64, loc }, + loc, + }; + s.body = [{ kind: "exprStmt", expr: read(ref("i")), loc }, ...body]; + return s; + }; + const ranges = new Map([[length, { min: 0, max: 4294967295 }]]); + expect(matchIntegerCountedForLoop(strided([]), locals, ranges, new Set())).toMatchObject({ + localId: "i", + guarded: false, + range: { min: 0, max: 4294967294 }, + }); + // Without the alias facts there is no stability proof. + expect(matchIntegerCountedForLoop(strided([]), locals, ranges)).toBeNull(); + const push: IrStmt = { + kind: "exprStmt", + expr: { + kind: "arrIntrinsic", + method: "push", + receiver: array("a"), + args: [number(1)], + type: F64, + loc, + }, + loc, + }; + expect(matchIntegerCountedForLoop(strided([push]), locals, ranges, new Set())).toBeNull(); + // An element write at the counter stays below the length; one past it can grow the array. + const write = (index: IrExpr): IrStmt => ({ + kind: "arraySet", + arr: array("a"), + index, + value: number(0), + loc, + }); + expect( + matchIntegerCountedForLoop(strided([write(ref("i"))]), locals, ranges, new Set()), + ).not.toBeNull(); + const past = { kind: "bin", op: "+", left: ref("i"), right: number(1), type: F64, loc } as const; + expect(matchIntegerCountedForLoop(strided([write(past)]), locals, ranges, new Set())).toBeNull(); +}); + +test("a descending loop from an array's last index takes an integer counter for its reads", () => { + const ARR = arrayOf(F64); + const length: IrExpr = { + kind: "arrIntrinsic", + method: "length", + receiver: { kind: "varRef", localId: "a", type: ARR, loc }, + args: [], + type: F64, + loc, + }; + const start: IrExpr = { kind: "bin", op: "-", left: length, right: number(1), type: F64, loc }; + const s = loop(start); + s.cond = { kind: "bin", op: ">=", left: ref("i"), right: number(0), type: BOOL, loc }; + s.update = { + kind: "assign", + localId: "i", + value: { kind: "bin", op: "-", left: ref("i"), right: number(1), type: F64, loc }, + loc, + }; + s.body = [ + { + kind: "exprStmt", + expr: { + kind: "arrIntrinsic", + method: "getNumber", + receiver: { kind: "varRef", localId: "a", type: ARR, loc }, + args: [ref("i")], + type: F64, + loc, + }, + loc, + }, + ]; + const ranges = new Map([[start, { min: -1, max: 4294967294 }]]); + expect(matchIntegerCountedForLoop(s, locals, ranges)).toMatchObject({ + step: -1, + guarded: false, + range: { min: 0, max: 4294967294 }, + }); +}); diff --git a/packages/compiler/src/ir/integer-loops.ts b/packages/compiler/src/ir/integer-loops.ts index c854a99c56..3ba1863760 100644 --- a/packages/compiler/src/ir/integer-loops.ts +++ b/packages/compiler/src/ir/integer-loops.ts @@ -3,6 +3,7 @@ import type { IntegerRange, IntegerRanges } from "./integer-ranges.js"; import { everyExprChild, everyStmtChild, everyStmtList } from "./traverse.js"; import { byteNumberAccess } from "./byte-numbers.js"; +import { preservesArrayLength } from "./array-length-stability.js"; /** A canonical byte loop whose induction variable is mathematically an * unsigned integer for every body entry. Backends may keep this binding in @@ -47,15 +48,17 @@ function constantStep(update: IrStmt | null, id: string): number | null { } /** Pure initial values may be evaluated again on a fallback path. Bounds - * additionally have to remain invariant throughout the loop. Array length - * is allowed only in the initializer; its mutable value is never hoisted - * from a condition. Typed-array lengths are fixed for a given receiver. */ + * additionally have to remain invariant throughout the loop. An array length + * is hoisted from a condition only when `stableLength` proves the loop never + * changes it; otherwise it is allowed only in the initializer. Typed-array + * lengths are fixed for a given receiver. */ function stableLoopValue( value: IrExpr, locals: ReadonlyMap, body: IrStmt[], invariant: boolean, counter: string, + stableLength: (receiver: string) => boolean = () => false, ): boolean { switch (value.kind) { case "numLit": @@ -77,16 +80,16 @@ function stableLoopValue( value.op === "&" || value.op === ">>>" || value.op === "|") && - stableLoopValue(value.left, locals, body, invariant, counter) && - stableLoopValue(value.right, locals, body, invariant, counter) + stableLoopValue(value.left, locals, body, invariant, counter, stableLength) && + stableLoopValue(value.right, locals, body, invariant, counter, stableLength) ); case "bytesIntrinsic": case "arrIntrinsic": return ( (value.method === "length" || (value.kind === "bytesIntrinsic" && value.method === "byteLength")) && - (value.kind === "bytesIntrinsic" || !invariant) && value.receiver.kind === "varRef" && + (value.kind === "bytesIntrinsic" || !invariant || stableLength(value.receiver.localId)) && stableLoopValue(value.receiver, locals, body, invariant, counter) ); default: @@ -103,6 +106,7 @@ export function matchIntegerCountedForLoop( stmt: IrStmt & { kind: "for" }, locals: ReadonlyMap, ranges: IntegerRanges = new Map(), + unaliased: ReadonlySet | null = null, ): IntegerCountedForLoop | null { const init = stmt.init; if (init?.kind !== "varDecl" || !init.init) return null; @@ -135,7 +139,38 @@ export function matchIntegerCountedForLoop( op = ({ "<": ">", "<=": ">=", ">": "<", ">=": "<=" } as Record)[op] ?? ""; } if (step > 0 ? op !== "<" && op !== "<=" : op !== ">" && op !== ">=") return null; - if (!stableLoopValue(limit, locals, stmt.body, true, local.id)) return null; + // An ascending counter compared below `a.length - c` holds an index below + // the length on every pass while that length is unchanged. + const belowLength = (receiver: string): boolean => { + let offset = 0, + length = limit; + if ( + length.kind === "bin" && + length.op === "-" && + length.right.kind === "numLit" && + Number.isFinite(length.right.value) + ) { + offset = length.right.value; + length = length.left; + } + return ( + step > 0 && + length.kind === "arrIntrinsic" && + length.method === "length" && + length.receiver.kind === "varRef" && + length.receiver.localId === receiver && + (op === "<" ? offset >= 0 : offset >= 1) + ); + }; + const stableLength = (receiver: string): boolean => + unaliased !== null && + preservesArrayLength( + [cond, ...stmt.body], + { receiver, inRange: belowLength(receiver) ? local.id : null }, + locals, + unaliased, + ); + if (!stableLoopValue(limit, locals, stmt.body, true, local.id, stableLength)) return null; const inclusive = op === "<=" || op === ">="; const guardLimit = step > 0 ? 2 ** 53 - (inclusive ? 1 : 0) : -(2 ** 53) + (inclusive ? 1 : 0); const boundRange = @@ -178,10 +213,19 @@ export function matchIntegerCountedForLoop( // A string read at the counter indexes with the integer directly. if (e.kind === "strIntrinsic" && e.method === "charCodeAt" && e.args[0] && counter(e.args[0])) useful = true; + // So does an array element read; only a loop that needs no versioned + // copy takes the integer counter for it. + if ( + !guarded && + ((e.kind === "arrayGet" && counter(e.index)) || + (e.kind === "arrIntrinsic" && e.method === "getNumber" && e.args[0] && counter(e.args[0]))) + ) + useful = true; return (!guarded || ++cost <= 80) && everyExprChild(e, expr, bodyStmt); }; const bodyStmt = (s: IrStmt): boolean => { if (s.kind === "bytesSet" && counter(s.index)) useful = true; + if (!guarded && s.kind === "arraySet" && counter(s.index)) useful = true; if ( guarded && (s.kind === "for" || s.kind === "forOf" || s.kind === "while" || s.kind === "doWhile") diff --git a/packages/compiler/src/ir/integer-ranges.test.ts b/packages/compiler/src/ir/integer-ranges.test.ts index e9d1797d14..8e1c97f66d 100644 --- a/packages/compiler/src/ir/integer-ranges.test.ts +++ b/packages/compiler/src/ir/integer-ranges.test.ts @@ -334,7 +334,8 @@ test("filtered strided loops retain decoded values and bound secondary cursors", const ranges = analyzeIntegerRanges(f); expect(ranges.get(cursor)).toEqual({ min: 0, max: 80 }); expect(ranges.get(decoded)).toEqual({ min: 0, max: 4294967295 }); - expect(ranges.get(conditionRead)).toEqual({ min: 0, max: 124 }); + // The last pass enters at most at 99, so the failing test reads at most 123. + expect(ranges.get(conditionRead)).toEqual({ min: 0, max: 123 }); for (const mutation of ["fraction", "repeat", "overflow", "counter"] as const) { const changed = structuredClone(f); const body = (changed.body[1] as IrStmt & { kind: "for" }).body; @@ -795,6 +796,77 @@ test("a while condition on a string's length bounds a counter, and guarded incre expect(run((inside) => [observe(inside), bump, rebound]).after).toBeNull(); }); +test("an array length bounds while and for counters even when the body resizes the array", () => { + // let i = 0; while (i < items.length) { observe(i); items.push(i); i++; } observe(i); + const ARR = { kind: "array", elem: F64 } as const; + const items: IrExpr = { loc, kind: "varRef", localId: "items", type: ARR }; + const length: IrExpr = { + loc, + kind: "arrIntrinsic", + method: "length", + receiver: items, + args: [], + type: F64, + }; + const push: IrStmt = { + loc, + kind: "exprStmt", + expr: { + loc, + kind: "arrIntrinsic", + method: "push", + receiver: items, + args: [ref("i")], + type: F64, + }, + }; + const bump: IrStmt = { + loc, + kind: "exprStmt", + expr: { loc, kind: "incDec", op: "+", prefix: false, localId: "i", type: F64 }, + }; + const inside = ref("i"), + after = ref("i"); + const f = fn([ + { loc, kind: "assign", localId: "i", value: num(0) }, + { + loc, + kind: "while", + cond: condition("<", ref("i"), length), + body: [observe(inside), push, bump], + }, + observe(after), + ]); + f.locals.push( + { id: "i", name: "i", type: F64, mutable: true }, + { id: "items", name: "items", type: ARR, mutable: false }, + ); + const ranges = analyzeIntegerRanges(f); + expect(ranges.get(inside)).toEqual({ min: 0, max: 4294967294 }); + expect(ranges.get(after)).toEqual({ min: 0, max: 4294967295 }); + // A secondary cursor of `for (let j = 0; j < items.length; j++) k++` stays a uint32. + const cursor = ref("k"); + const loop: IrStmt = { + loc, + kind: "for", + init: { loc, kind: "varDecl", localId: "j", init: num(0) }, + cond: condition("<", ref("j"), length), + update: { loc, kind: "assign", localId: "j", value: bin("+", ref("j"), num(1)) }, + body: [ + push, + observe(cursor), + { loc, kind: "assign", localId: "k", value: bin("+", ref("k"), num(1)) }, + ], + }; + const g = fn([{ loc, kind: "assign", localId: "k", value: num(0) }, loop]); + g.locals.push( + { id: "j", name: "j", type: F64, mutable: true }, + { id: "k", name: "k", type: F64, mutable: true }, + { id: "items", name: "items", type: ARR, mutable: false }, + ); + expect(analyzeIntegerRanges(g).get(cursor)).toEqual({ min: 0, max: 4294967295 }); +}); + test("whole-program slot facts seed parameters, field reads and call results", () => { const field: IrExpr = { loc, diff --git a/packages/compiler/src/ir/ir.ts b/packages/compiler/src/ir/ir.ts index 9591c684a3..5382a67357 100644 --- a/packages/compiler/src/ir/ir.ts +++ b/packages/compiler/src/ir/ir.ts @@ -344,8 +344,8 @@ export type IrType = * Runtime representation is a refcounted snapshot box (ScrCaught) holding * the taken exception: a kind tag plus the payload. Even NARROWER than * dyn: the only expressions a caught value may appear in are `caughtTest` - * (kind/instanceof tests), `caughtNarrow` (checker-trusted extraction - * under a proven test), and the `rethrow` statement — the frontend + * (kind/instanceof tests), `caughtNarrow` (extraction under a proven + * test, verified at run time), and the `rethrow` statement — the frontend * rejects every other use with the narrowing hint. */ | { kind: "caught" } | { kind: "promise"; inner: IrType } // heap, refcounted, settled-once @@ -2953,11 +2953,12 @@ export type IrExpr = * fulfillment payload (scr_await_void) and rejections flow untyped. * Ownership of the operand transfers to the result, like upcast. */ | { kind: "promiseVoidWiden"; value: IrExpr; type: IrType; loc: SrcLoc } - /** Checker-trusted narrowing of a base-class value to a subclass (`type` - * is the subclass). The frontend emits this only where tsc's control-flow - * narrowing has already proven the dynamic class (an `instanceof` guard) - * — the same trust-the-checker contract as unionNarrow: no runtime check, - * a pointer reinterpret with ownership transferring like upcast. */ + /** Narrowing of a base-class value to a subclass (`type` is the + * subclass). The frontend emits this only where tsc's control-flow + * narrowing has already proven the dynamic class (an `instanceof` guard), + * like unionNarrow. Backends verify the class once and trap on a mismatch + * (a compiler bug or a lying type predicate or assertion function), then + * reinterpret the pointer with ownership transferring like upcast. */ | { kind: "downcast"; value: IrExpr; type: IrType; loc: SrcLoc } /** `x instanceof C` where x's static class and C are both in extends- * hierarchies: an O(1) preorder-interval check against the vtable the @@ -3034,26 +3035,30 @@ export type IrExpr = * Declared fields are tried FIRST (an emitted string-switch — field * access exactness is preserved: a declared name always answers from the * struct slot), then the overflow map on index-signature shapes. `type` - * is the CHECKER's type for the access: the index signature's value type - * (dyn for `unknown`; with noUncheckedIndexedAccess, its - * `V | undefined` union). A MISSING key produces: the undefined dyn - * singleton when `type` is dyn; the undefined arm when `type` is an - * undefined-armed union; otherwise a TRAP — the checker claimed V and no - * undefined is representable (the array OOB policy; on declared-only - * shapes tsc's keyof check makes the trap unreachable without an `as` - * smuggle). Declared-field values surface as `type`: V-typed fields read - * directly, dyn results build a dyn COPY of the field value (the dynFrom - * conversion — deep for composites, documented), union results wrap. + * is the index signature's value type armed with undefined (dyn for + * `unknown`), so a missing key reads undefined as in JavaScript, or the + * declared fields' common type on signature-free shapes. A MISSING key + * produces: the undefined dyn singleton when `type` is dyn; the undefined + * arm when `type` is an undefined-armed union; NaN with `missNaN`; + * otherwise a TRAP (declared-only shapes, where tsc's keyof check makes + * the trap unreachable without an `as` smuggle). Declared-field values + * surface as `type`: V-typed fields read directly, dyn results build a + * dyn COPY of the field value (the dynFrom conversion — deep for + * composites, documented), union results wrap. * The key and object are borrowed; refcounted results are owned (+1). * `overflowOnly` (set when the key is a LITERAL that names no declared * field): the read touches only the overflow map — declared fields need - * not surface as `type`, and the emitted helper skips the string-switch. */ + * not surface as `type`, and the emitted helper skips the string-switch. + * `missNaN` (with an f64 `type`): a missing key produces NaN instead of + * trapping, for reads whose every consumer treats undefined like NaN + * (nan-coded-reads.ts). */ | { kind: "recordKeyGet"; obj: IrExpr; shapeId: string; key: IrExpr; overflowOnly?: true; + missNaN?: true; type: IrType; loc: SrcLoc; } @@ -3386,12 +3391,13 @@ export type IrExpr = type: IrType; loc: SrcLoc; } - /** Checker-trusted extraction of a catch binding's payload as `type` — - * the caught analog of unionNarrow: the frontend emits this only where - * tsc's control-flow narrowing has already proven the matching test - * (`e instanceof C` / `typeof e === "string"`), so the read is - * kind-UNCHECKED at runtime. `type` is f64, bool, string, or a - * class object; refcounted results come out retained (+1). */ + /** Extraction of a catch binding's payload as `type` — the caught + * analog of unionNarrow: the frontend emits this only where tsc's + * control-flow narrowing has already proven the matching test + * (`e instanceof C` / `typeof e === "string"`). Backends verify the kind + * or class once and trap on a mismatch (a compiler bug or a lying type + * predicate or assertion function). `type` is f64, bool, string, or a class object; refcounted results come + * out retained (+1). */ | { kind: "caughtNarrow"; value: IrExpr; type: IrType; loc: SrcLoc } /** CHECKED extraction of a catch binding's payload as a hierarchy-class * instance — the caught analog of dynCheck, emitted for `e as C` casts @@ -3417,11 +3423,13 @@ export type IrExpr = * varRef (borrowed); `type` is dyn; the result is a fresh tree (+1), * never aliasing the payload. Never throws. */ | { kind: "caughtToDyn"; value: IrExpr; type: IrType; loc: SrcLoc } - /** Union payload extraction, tag-UNCHECKED: `value` is union-typed, - * `type` is arms[tag], and the backend reads the payload assuming the tag - * — SOUNDNESS RESTS ON tsc's control-flow narrowing (the frontend emits - * this only where the checker has already narrowed the expression to that - * arm; see docs/ir.md). Refcounted payloads come out retained (+1). */ + /** Union payload extraction: `value` is union-typed and `type` is + * arms[tag]. The frontend emits this only where tsc's control-flow + * narrowing has already narrowed the expression to that arm; backends + * verify the tag once and trap on a mismatch (a compiler bug or a lying + * type predicate or assertion function), so a wrong proof cannot read the + * payload as another arm. + * Refcounted payloads come out retained (+1). */ | { kind: "unionNarrow"; unionId: string; tag: number; value: IrExpr; type: IrType; loc: SrcLoc } /** Discriminant read `r.kind` on a union receiver: every arm is a * record/class possessing field `field` with the SAME primitive IR type diff --git a/packages/compiler/src/ir/validate.test.ts b/packages/compiler/src/ir/validate.test.ts index d44a187f44..b7caf8c41b 100644 --- a/packages/compiler/src/ir/validate.test.ts +++ b/packages/compiler/src/ir/validate.test.ts @@ -951,6 +951,7 @@ test("library callbacks retain child, specialized, and generic result diagnostic { kind: "strLit", value: "tool", type: STRING, loc }, { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc }, { kind: "boolLit", value: true, type: F64, loc }, + { kind: "boolLit", value: false, type: BOOL, loc }, ], }; expect(validateModule(expressionModule(expr, [])).map((error) => error.message)).toEqual([ diff --git a/packages/compiler/src/ir/validate.ts b/packages/compiler/src/ir/validate.ts index 388abf0a74..d101601155 100644 --- a/packages/compiler/src/ir/validate.ts +++ b/packages/compiler/src/ir/validate.ts @@ -60,6 +60,18 @@ import { DYN_CLASS_PROPERTIES } from "./ir.js"; import { unionWideningTags } from "./analysis.js"; import { alwaysReturns } from "./control-flow.js"; import { LIB_FN_SIGS } from "./builtin-signatures.js"; + +/** The promisified crypto calls' value kinds besides checked-dynamic. */ +const CRYPTO_PROMISE_VALUES: Readonly> = { + "crypto.randomBytesPromise": "buffer", + "crypto.pbkdf2Promise": "buffer", + "crypto.scryptPromise": "buffer", + "crypto.hkdfPromise": "dyn", + "crypto.randomFillPromise": "view", + "crypto.randomFillPromiseDyn": "dyn", + "crypto.randomIntPromise": "number", + "crypto.generateKeyPromise": "dyn", +}; import { REGEX_INTRINSIC_SIGS, STR_INTRINSIC_SIGS } from "./intrinsic-signatures.js"; /** True for an object type naming a runtime-provided error class. */ @@ -2907,7 +2919,7 @@ function validateFunction( case "downcast": { // A pointer reinterpret is sound exactly between hierarchy // relatives: upcast widens a STRICT descendant to an ancestor, - // downcast (checker-trusted) narrows an ancestor to a STRICT + // downcast (verified at run time) narrows an ancestor to a STRICT // descendant. Upcast additionally widens CLASS VALUES // (classval:D → classval:C): the same pointer, type-only — legal // exactly when D strictly descends from C AND the two completed @@ -4097,6 +4109,19 @@ function validateFunction( } function checkLibValueCall(e: IrExpr & { kind: "libCall" }): boolean { + const cryptoPromise = CRYPTO_PROMISE_VALUES[e.fn]; + if (cryptoPromise) { + const inner = e.type.kind === "promise" ? e.type.inner : undefined; + const view = e.fn === "crypto.randomFillPromise" ? e.args[0]?.type : undefined; + const ok = + inner !== undefined && + (inner.kind === "dyn" || + (cryptoPromise === "buffer" && inner.kind === "bytes" && inner.elem === "u8") || + (cryptoPromise === "view" && view !== undefined && typeEquals(inner, view)) || + (cryptoPromise === "number" && inner.kind === "f64")); + if (!ok) err(`libCall ${e.fn} must be a promise of its ${cryptoPromise} value`, e.loc); + return true; + } if (e.fn === "events.once") { const [recv, , boxer, errorBoxer] = e.args; const arrayT = e.type.kind === "promise" ? e.type.inner : null; @@ -5695,6 +5720,8 @@ function validateFunction( e.fn !== "emitter.setDefaultMax" && e.fn !== "emitter.setDefaultMaxChk" && e.fn !== "emitter.getDefaultMax" && + e.fn !== "emitter.esmDefaultMax" && + e.fn !== "emitter.esmSync" && e.fn !== "emitter.errorMonitor" && e.fn !== "emitter.checkListener" ) { diff --git a/packages/compiler/surface-manifest.json b/packages/compiler/surface-manifest.json index 219095bc0a..f5313f12df 100644 --- a/packages/compiler/surface-manifest.json +++ b/packages/compiler/surface-manifest.json @@ -1367,7 +1367,7 @@ "name": "module.syncBuiltinESMExports", "status": "unsupported", "code": "SC2020", - "note": "the direct zero-argument call is a no-op because compiled builtin exports are immutable and cannot become stale" + "note": "the direct zero-argument call statement refreshes the ES module namespace copy of events.defaultMaxListeners, the one builtin export a program can change" }, { "id": "node-builtin.net", @@ -2462,7 +2462,7 @@ "name": "util.promisify", "status": "unsupported", "code": "SC2020", - "note": "the lowered shapes are const bindings over child_process.execFile, child_process.exec, setTimeout, setImmediate, stream.finished, stream.pipeline, the zlib callback one-shots and the fs callback functions with an fs/promises counterpart; call the bound function directly" + "note": "the lowered shapes are const bindings over child_process.execFile, child_process.exec, setTimeout, setImmediate, stream.finished, stream.pipeline, the zlib callback one-shots, the crypto callback functions, dns.lookup, dns.lookupService and the fs callback functions with an fs/promises counterpart; call the bound function directly" }, { "id": "node-builtin.util.stripVTControlCharacters", diff --git a/packages/compiler/test/class-early-field-reads.test.ts b/packages/compiler/test/class-early-field-reads.test.ts new file mode 100644 index 0000000000..c602a2f42a --- /dev/null +++ b/packages/compiler/test/class-early-field-reads.test.ts @@ -0,0 +1,93 @@ +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { expect, test } from "vitest"; +import { compile, deserializeModule, validateModule } from "../src/index.js"; +import type { IrModule } from "../src/ir/ir.js"; + +async function lower(source: string, file = "main.ts"): Promise { + const dir = await mkdtemp(join(tmpdir(), "scriptc-early-field-reads-")); + try { + const entry = join(dir, file), + output = join(dir, "main.ir.json"); + await writeFile(entry, source); + const result = await compile(entry, { outDir: dir, outPath: output, outputKind: "ir" }); + if (!result.ok) + throw new Error(result.diagnostics.map((item) => `${item.code}: ${item.message}`).join("\n")); + const module = deserializeModule(await readFile(output, "utf8")); + expect(validateModule(module)).toEqual([]); + return module; + } finally { + await rm(dir, { recursive: true, force: true }); + } +} + +/** Field name → IR kind for a class. */ +function fields(module: IrModule, name: string): string { + const cls = module.classes.find((candidate) => candidate.name === name); + expect(cls, name).toBeDefined(); + return cls!.fields + .filter((field) => !field.name.startsWith("%")) + .map((field) => `${field.name}:${field.type.kind}`) + .join(" "); +} + +test("fields assigned after construction can call an override admit undefined", async () => { + const module = await lower(` +abstract class Base { + constructor() { + this.describe(); + } + abstract describe(): void; +} +class Panel extends Base { + label: string; + width: number; + count = 0; + constructor() { + super(); + this.label = "panel"; + this.width = 2; + } + describe(): void { + console.log(this.label, this.width, this.count); + } +} +class Plain { + label: string; + width: number; + constructor() { + this.label = "plain"; + this.width = 1; + this.describe(); + } + describe(): void { + console.log(this.label, this.width); + } +} +new Panel(); +new Plain(); +`); + expect(fields(module, "Panel")).toBe("label:union width:union count:union"); + expect(fields(module, "Plain")).toBe("label:string width:f64"); +}); + +test("JavaScript fields created after the constructor calls a method admit undefined", async () => { + const module = await lower( + ` +class Widget { + constructor() { + this.ready = true; + this.render(); + this.title = "widget"; + } + render() { + console.log(this.ready, this.title); + } +} +new Widget(); +`, + "main.js", + ); + expect(fields(module, "Widget")).toBe("ready:bool title:union"); +}); diff --git a/packages/compiler/test/class-method-mutations.test.ts b/packages/compiler/test/class-method-mutations.test.ts index 50c8a2ae2e..2258bd54a5 100644 --- a/packages/compiler/test/class-method-mutations.test.ts +++ b/packages/compiler/test/class-method-mutations.test.ts @@ -87,3 +87,30 @@ test("an asserted string key keeps checked dispatch", async () => { ); expect(await callersOf(source, "%dyn.class.properties")).not.toEqual([]); }); + +const tally = ` +class Tally { + n = 0; + add(): void { this.n++; } +} +function entry(target: object, key: string, value: number): TYPE { + return LITERAL; +} +const tally = new Tally(); +tally.add(); +console.log(tally.n, Object.keys(entry(tally, String(tally.n), 2))); +`; + +test("a computed key in an object literal leaves method calls direct", async () => { + const source = tally + .replace("TYPE", "Record") + .replace("LITERAL", "{ [key]: value }"); + expect(await callersOf(source, "%dyn.class.properties")).toEqual([]); +}); + +test("a computed key in an Object.assign source keeps checked dispatch", async () => { + const source = tally + .replace("TYPE", "object") + .replace("LITERAL", "Object.assign(target, { [key]: value })"); + expect(await callersOf(source, "%dyn.class.properties")).not.toEqual([]); +}); diff --git a/packages/compiler/test/narrow-guard-emission.test.ts b/packages/compiler/test/narrow-guard-emission.test.ts new file mode 100644 index 0000000000..026762caea --- /dev/null +++ b/packages/compiler/test/narrow-guard-emission.test.ts @@ -0,0 +1,118 @@ +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { expect, test } from "vitest"; +import { compile } from "../src/index.js"; + +async function emitLlvm(source: string): Promise { + const dir = await mkdtemp(join(tmpdir(), "scriptc-narrow-guards-")); + try { + const entry = join(dir, "main.ts"); + const outPath = join(dir, "main.ll"); + await writeFile(entry, source); + const result = await compile(entry, { outDir: dir, outPath, outputKind: "llvm" }); + expect(result.ok, JSON.stringify(result.diagnostics)).toBe(true); + return await readFile(outPath, "utf8"); + } finally { + await rm(dir, { recursive: true, force: true }); + } +} + +/** The trap call guarding narrowings of `kind`, after its message constant. */ +function guardCount(llvm: string, message: string): number { + const constant = [...llvm.matchAll(/^(@[\w.]+) = [^\n]*c"([^"]*)"/gm)].find(([, , text]) => + text!.startsWith(message), + ); + if (constant === undefined) return 0; + return llvm.split(`call void @scr_trap(ptr ${constant[1]})`).length - 1; +} + +test("typeof narrowing verifies the union tag before reading the payload", async () => { + const llvm = await emitLlvm(` +function width(v: string | number): number { + if (typeof v === "string") return v.length; + return v; +} +console.log(width("abc") + width(4)); +`); + expect(guardCount(llvm, "scriptc: a narrowed value does not")).toBeGreaterThan(0); +}); + +/** The tag compares that feed narrowing guards, as `eq K` / `ne K`. */ +function guardCompares(llvm: string): string[] { + const compares: string[] = []; + for (const fn of llvm.split("\ndefine ")) { + for (const [, expected, ok] of fn.matchAll( + /(%[\w.]+) = call i1 @llvm\.expect\.i1\(i1 (%[\w.]+), i1 true\)/g, + )) { + if (!fn.includes(`br i1 ${expected}, label %narrow.ok`)) continue; + const def = new RegExp(`^\\s*${ok} = icmp (eq|ne) i32 %[\\w.]+, (\\d+)$`, "m").exec(fn); + if (def) compares.push(`${def[1]} ${def[2]}`); + } + } + return compares; +} + +test("two-arm union guards test the other arm; wider unions test the arm", async () => { + const two = await emitLlvm(` +function width(v: string | number): number { + if (typeof v === "string") return v.length; + return v; +} +console.log(width("abc") + width(4)); +`); + const twoCompares = guardCompares(two); + expect(twoCompares.length).toBeGreaterThan(0); + expect(twoCompares.every((c) => c.startsWith("ne "))).toBe(true); + const three = await emitLlvm(` +function width(v: string | number | boolean): number { + if (typeof v === "string") return v.length; + if (typeof v === "number") return v; + return v ? 1 : 0; +} +console.log(width("abc") + width(4) + width(true)); +`); + const threeCompares = guardCompares(three); + expect(threeCompares.length).toBeGreaterThan(0); + expect(threeCompares.every((c) => c.startsWith("eq "))).toBe(true); +}); + +test("instanceof narrowing verifies the downcast class interval", async () => { + const llvm = await emitLlvm(` +class Base { id = 1; } +class Child extends Base { values = [2, 4]; } +function count(value: Base): number { + if (value instanceof Child) return value.values.length; + return value.id; +} +console.log(count(new Child()), count(new Base())); +`); + expect(guardCount(llvm, "scriptc: a narrowed object is not")).toBeGreaterThan(0); +}); + +test("catch-binding narrowing verifies the caught kind and class", async () => { + const llvm = await emitLlvm(` +class Failure extends Error { code = 7; } +function run(which: number): string { + try { + if (which === 0) throw "text"; + throw new Failure("bad"); + } catch (e) { + if (typeof e === "string") return e; + if (e instanceof Failure) return String(e.code); + return "other"; + } +} +console.log(run(0), run(1)); +`); + expect(guardCount(llvm, "scriptc: a narrowed catch binding")).toBeGreaterThan(1); +}); + +test("programs without narrowings carry no guards", async () => { + const llvm = await emitLlvm(` +let total = 0; +for (let i = 0; i < 10; i++) total += i; +console.log(total); +`); + expect(llvm).not.toContain("scriptc: a narrowed"); +}); diff --git a/packages/compiler/test/narrow-guard-traps.test.ts b/packages/compiler/test/narrow-guard-traps.test.ts new file mode 100644 index 0000000000..be89ad3415 --- /dev/null +++ b/packages/compiler/test/narrow-guard-traps.test.ts @@ -0,0 +1,72 @@ +/* A hand-written type predicate or assertion function is a claim TypeScript + * does not check. When it lies, the narrowing guard stops the program with + * its trap message instead of reading the payload as the claimed type (which + * crashed before the guards existed). Node throws or reads `undefined` + * there, so these programs intentionally diverge and are not corpus cases. */ +import { spawnSync } from "node:child_process"; +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { expect, test } from "vitest"; +import { compile } from "../src/index.js"; + +const UNION_TRAP = + "scriptc: a narrowed value does not have the type its narrowing proved " + + "(a type predicate or assertion may be wrong)\n"; + +async function run(source: string): Promise> { + const dir = mkdtempSync(join(tmpdir(), "scriptc-narrow-traps-")); + try { + const entry = join(dir, "main.ts"); + writeFileSync(entry, source); + const built = await compile(entry, { + outDir: dir, + outPath: join(dir, "program"), + sanitize: process.env["SCRIPTC_SAN"] === "1", + }); + if (!built.ok) throw new Error(built.diagnostics.map((d) => d.message).join("\n")); + return spawnSync(built.binaryPath, [], { timeout: 30_000 }); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +} + +test.skipIf(process.platform === "win32")( + "a lying type predicate stops at the narrowing guard", + async () => { + const result = await run(` +function isText(x: string | number): x is string { + return x !== ""; +} +function shout(v: string | number): string { + if (isText(v)) return v.toUpperCase(); + return "other"; +} +const inputs: (string | number)[] = ["a", 42]; +for (const v of inputs) console.log(shout(v)); +`); + expect(result.error).toBeUndefined(); + expect(result.signal).toBe("SIGABRT"); + expect(result.stdout.toString()).toBe("A\n"); + expect(result.stderr.toString()).toBe(UNION_TRAP); + }, +); + +test.skipIf(process.platform === "win32")( + "a lying assertion function stops at the narrowing guard", + async () => { + const result = await run(` +function assertText(x: string | number): asserts x is string {} +function width(v: string | number): number { + assertText(v); + return v.length; +} +const inputs: (string | number)[] = ["abc", 7]; +for (const v of inputs) console.log(width(v)); +`); + expect(result.error).toBeUndefined(); + expect(result.signal).toBe("SIGABRT"); + expect(result.stdout.toString()).toBe("3\n"); + expect(result.stderr.toString()).toBe(UNION_TRAP); + }, +); diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5023-lifetime-region-await.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5023-lifetime-region-await.ts.json new file mode 100644 index 0000000000..9e7d481300 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5023-lifetime-region-await.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5023-lifetime-region-await.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5024-lifetime-region-await-forms.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5024-lifetime-region-await-forms.ts.json new file mode 100644 index 0000000000..87cb98c435 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5024-lifetime-region-await-forms.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5024-lifetime-region-await-forms.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5627-exec-callback.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5627-exec-callback.ts.json new file mode 100644 index 0000000000..cbefe8acca --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5627-exec-callback.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5627-exec-callback.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5631-promisify-crypto.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5631-promisify-crypto.ts.json new file mode 100644 index 0000000000..cf5c255589 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5631-promisify-crypto.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5631-promisify-crypto.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5632-promisify-dns.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5632-promisify-dns.ts.json new file mode 100644 index 0000000000..b96c90a824 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5632-promisify-dns.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5632-promisify-dns.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5681-array-pop-and-length.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5681-array-pop-and-length.ts.json new file mode 100644 index 0000000000..d20701cae3 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5681-array-pop-and-length.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5681-array-pop-and-length.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5770-guarded-union-narrows.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5770-guarded-union-narrows.ts.json new file mode 100644 index 0000000000..18a2666e2f --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5770-guarded-union-narrows.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5770-guarded-union-narrows.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5771-guarded-downcasts.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5771-guarded-downcasts.ts.json new file mode 100644 index 0000000000..c2570951b6 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5771-guarded-downcasts.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5771-guarded-downcasts.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5772-guarded-catch-narrows.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5772-guarded-catch-narrows.ts.json new file mode 100644 index 0000000000..6de65b7315 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5772-guarded-catch-narrows.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5772-guarded-catch-narrows.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5773-stale-narrowing-inherited.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5773-stale-narrowing-inherited.ts.json new file mode 100644 index 0000000000..7a82f68e41 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5773-stale-narrowing-inherited.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5773-stale-narrowing-inherited.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5846-events-esm-default-max.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5846-events-esm-default-max.ts.json new file mode 100644 index 0000000000..f1867c914b --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5846-events-esm-default-max.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5846-events-esm-default-max.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5847-events-cjs-default-max.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5847-events-cjs-default-max.cjs.json new file mode 100644 index 0000000000..1464ab2cc4 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5847-events-cjs-default-max.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5847-events-cjs-default-max.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5907-regex-groups-match-rows.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5907-regex-groups-match-rows.ts.json new file mode 100644 index 0000000000..ec7d629458 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5907-regex-groups-match-rows.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5907-regex-groups-match-rows.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5938-object-literal-computed-keys-keep-methods.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5938-object-literal-computed-keys-keep-methods.ts.json new file mode 100644 index 0000000000..356382374f --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5938-object-literal-computed-keys-keep-methods.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5938-object-literal-computed-keys-keep-methods.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5942-local-class-observed-methods.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5942-local-class-observed-methods.ts.json deleted file mode 100644 index 89502dd27b..0000000000 --- a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5942-local-class-observed-methods.ts.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "order": [ - "/tests/corpus/5942-local-class-observed-methods.ts" - ], - "diags": [] -} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5943-fields-read-before-assignment.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5943-fields-read-before-assignment.ts.json new file mode 100644 index 0000000000..d2dcdcc242 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5943-fields-read-before-assignment.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5943-fields-read-before-assignment.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5944-js-fields-read-before-assignment.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5944-js-fields-read-before-assignment.js.json new file mode 100644 index 0000000000..e9800b71de --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5944-js-fields-read-before-assignment.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5944-js-fields-read-before-assignment.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5945-local-class-observed-methods.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5945-local-class-observed-methods.ts.json new file mode 100644 index 0000000000..f1de4d6086 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5945-local-class-observed-methods.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5945-local-class-observed-methods.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5946-nested-record-field-optional-writes.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5946-nested-record-field-optional-writes.ts.json new file mode 100644 index 0000000000..df296d33f5 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5946-nested-record-field-optional-writes.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5946-nested-record-field-optional-writes.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5952-record-missing-keys.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5952-record-missing-keys.ts.json new file mode 100644 index 0000000000..06b0e81068 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5952-record-missing-keys.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5952-record-missing-keys.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5953-nan-coded-method-returns.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5953-nan-coded-method-returns.ts.json new file mode 100644 index 0000000000..7a84896f80 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5953-nan-coded-method-returns.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5953-nan-coded-method-returns.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5960-frame-records-own-references.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5960-frame-records-own-references.ts.json new file mode 100644 index 0000000000..afe039025d --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5960-frame-records-own-references.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5960-frame-records-own-references.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5961-frame-spread-updates.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5961-frame-spread-updates.ts.json new file mode 100644 index 0000000000..255b1238e3 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5961-frame-spread-updates.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5961-frame-spread-updates.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5962-frame-tuples.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5962-frame-tuples.ts.json new file mode 100644 index 0000000000..ab8d80fafb --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5962-frame-tuples.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5962-frame-tuples.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5973-large-closure-environment.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5973-large-closure-environment.ts.json new file mode 100644 index 0000000000..5455362850 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5973-large-closure-environment.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5973-large-closure-environment.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5975-zlib-class-values.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5975-zlib-class-values.ts.json new file mode 100644 index 0000000000..561bd1e922 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5975-zlib-class-values.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5975-zlib-class-values.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5993-array-length-loop-resizing.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5993-array-length-loop-resizing.ts.json new file mode 100644 index 0000000000..e4cf027ce2 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5993-array-length-loop-resizing.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5993-array-length-loop-resizing.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5994-array-length-sparse-loops.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5994-array-length-sparse-loops.ts.json new file mode 100644 index 0000000000..6289e8a21f --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5994-array-length-sparse-loops.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5994-array-length-sparse-loops.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5995-array-dense-loop-boundaries.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5995-array-dense-loop-boundaries.ts.json new file mode 100644 index 0000000000..d0afcf5051 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5995-array-dense-loop-boundaries.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5995-array-dense-loop-boundaries.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6020-class-extends-function-constructor.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6020-class-extends-function-constructor.js.json new file mode 100644 index 0000000000..b2fa4c986f --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6020-class-extends-function-constructor.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6020-class-extends-function-constructor.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6021-asserted-literal-union-arm.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6021-asserted-literal-union-arm.ts.json new file mode 100644 index 0000000000..e3b27d23c4 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6021-asserted-literal-union-arm.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6021-asserted-literal-union-arm.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6041-error-cause-redeclared.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6041-error-cause-redeclared.ts.json new file mode 100644 index 0000000000..c8ffa4fc7a --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6041-error-cause-redeclared.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6041-error-cause-redeclared.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6042-regex-match-object-destructuring.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6042-regex-match-object-destructuring.ts.json new file mode 100644 index 0000000000..bbe177602d --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6042-regex-match-object-destructuring.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6042-regex-match-object-destructuring.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6043-string-index-narrowed-union.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6043-string-index-narrowed-union.ts.json new file mode 100644 index 0000000000..3ff98a72a8 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6043-string-index-narrowed-union.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6043-string-index-narrowed-union.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6140-cjs-require-tdz-throws/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6140-cjs-require-tdz-throws/main.js.json new file mode 100644 index 0000000000..db768b31be --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6140-cjs-require-tdz-throws/main.js.json @@ -0,0 +1,7 @@ +{ + "order": [ + "/tests/corpus/6140-cjs-require-tdz-throws/lib.js", + "/tests/corpus/6140-cjs-require-tdz-throws/main.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6141-cjs-require-tdz-checked/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6141-cjs-require-tdz-checked/main.js.json new file mode 100644 index 0000000000..b074bf18d6 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6141-cjs-require-tdz-checked/main.js.json @@ -0,0 +1,8 @@ +{ + "order": [ + "/tests/corpus/6141-cjs-require-tdz-checked/lib.js", + "/tests/corpus/6141-cjs-require-tdz-checked/counter.js", + "/tests/corpus/6141-cjs-require-tdz-checked/main.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6142-cjs-require-published-early/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6142-cjs-require-published-early/main.js.json new file mode 100644 index 0000000000..55720ef1a4 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6142-cjs-require-published-early/main.js.json @@ -0,0 +1,13 @@ +{ + "order": [ + "/tests/corpus/6142-cjs-require-published-early/parse-options.js", + "/tests/corpus/6142-cjs-require-published-early/debug.js", + "/tests/corpus/6142-cjs-require-published-early/comparator.js", + "/tests/corpus/6142-cjs-require-published-early/helpers.js", + "/tests/corpus/6142-cjs-require-published-early/glob.js", + "/tests/corpus/6142-cjs-require-published-early/core.js", + "/tests/corpus/6142-cjs-require-published-early/bundle.js", + "/tests/corpus/6142-cjs-require-published-early/main.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6143-cjs-require-json/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6143-cjs-require-json/main.js.json new file mode 100644 index 0000000000..92fe169c31 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6143-cjs-require-json/main.js.json @@ -0,0 +1,7 @@ +{ + "order": [ + "/tests/corpus/6143-cjs-require-json/counter.js", + "/tests/corpus/6143-cjs-require-json/main.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6144-cjs-require-builtin-nested/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6144-cjs-require-builtin-nested/main.js.json new file mode 100644 index 0000000000..df78ca9ed9 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6144-cjs-require-builtin-nested/main.js.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6144-cjs-require-builtin-nested/main.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6145-cjs-require-cycle/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6145-cjs-require-cycle/main.js.json new file mode 100644 index 0000000000..3e8122d1f2 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6145-cjs-require-cycle/main.js.json @@ -0,0 +1,9 @@ +{ + "order": [ + "/tests/corpus/6145-cjs-require-cycle/range.js", + "/tests/corpus/6145-cjs-require-cycle/util.js", + "/tests/corpus/6145-cjs-require-cycle/comparator.js", + "/tests/corpus/6145-cjs-require-cycle/main.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/scratch-string-slices.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/scratch-string-slices.ts.json new file mode 100644 index 0000000000..5145c33d18 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/scratch-string-slices.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/scratch-string-slices.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-cycle/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-cycle/main.js.json index a54923bcfe..67d01f47e2 100644 --- a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-cycle/main.js.json +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-cycle/main.js.json @@ -7,7 +7,7 @@ "diags": [ { "code": "SC1016", - "message": "circular imports (/tests/diagnostics/cjs-require-cycle/a.js → /tests/diagnostics/cjs-require-cycle/b.js → /tests/diagnostics/cjs-require-cycle/a.js; the cycle closes through a require() edge) are not supported yet", + "message": "circular imports (/tests/diagnostics/cjs-require-cycle/a.js → /tests/diagnostics/cjs-require-cycle/b.js → /tests/diagnostics/cjs-require-cycle/a.js; the cycle closes through a require() edge: /tests/diagnostics/cjs-require-cycle/a.js uses its exports at /tests/diagnostics/cjs-require-cycle/a.js:6 outside the export statements that precede its first require, so a module of the cycle can observe them before they settle) are not supported yet", "loc": { "file": "/tests/diagnostics/cjs-require-cycle/a.js", "start": 0, diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-tdz-unchecked/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-tdz-unchecked/main.js.json new file mode 100644 index 0000000000..aaeea4cf27 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-tdz-unchecked/main.js.json @@ -0,0 +1,7 @@ +{ + "order": [ + "/tests/diagnostics/cjs-require-tdz-unchecked/lib.js", + "/tests/diagnostics/cjs-require-tdz-unchecked/main.js" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-tdz/main.js.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-tdz/main.js.json index d15d404ae9..205a8cd6ee 100644 --- a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-tdz/main.js.json +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/cjs-require-tdz/main.js.json @@ -5,11 +5,11 @@ "diags": [ { "code": "SC1013", - "message": "require() below code that can reach its binding 'greet' (Node initializes the binding AT the require and throws ReferenceError on any earlier access, which is not modeled — move the require above the code that can run first) is not supported yet", + "message": "var require() binding 'lib' below code that can reach it (Node reads undefined before the require runs, which is not modeled — declare it with const or let, or move the require above the code that can run first) is not supported yet", "loc": { "file": "/tests/diagnostics/cjs-require-tdz/main.js", - "start": 336, - "end": 367 + "start": 402, + "end": 427 }, "milestone": "later" } diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/error-cause-redeclared-fences.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/error-cause-redeclared-fences.ts.json new file mode 100644 index 0000000000..437c1da660 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/error-cause-redeclared-fences.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/diagnostics/error-cause-redeclared-fences.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/regex-match-destructuring-fences.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/regex-match-destructuring-fences.ts.json new file mode 100644 index 0000000000..1f104f489e --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/regex-match-destructuring-fences.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/diagnostics/regex-match-destructuring-fences.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/zlib-class-alias-escape.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/zlib-class-alias-escape.ts.json new file mode 100644 index 0000000000..cab5c0ba0d --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/zlib-class-alias-escape.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/diagnostics/zlib-class-alias-escape.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec-code-fences.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec-code-fences.ts.json new file mode 100644 index 0000000000..8f8a2a4b89 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec-code-fences.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/fixtures/node-types/child-exec-code-fences.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec.ts.json new file mode 100644 index 0000000000..73db22acae --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/fixtures/node-types/child-exec.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/promisify-crypto.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/promisify-crypto.ts.json new file mode 100644 index 0000000000..c340af28b1 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/promisify-crypto.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/fixtures/node-types/promisify-crypto.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/promisify-dns.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/promisify-dns.ts.json new file mode 100644 index 0000000000..08a2faf823 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/promisify-dns.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/fixtures/node-types/promisify-dns.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/program-adapter.test.ts b/packages/compiler/test/ts7/program-adapter.test.ts index 660394a6fd..e7479c7498 100644 --- a/packages/compiler/test/ts7/program-adapter.test.ts +++ b/packages/compiler/test/ts7/program-adapter.test.ts @@ -119,7 +119,7 @@ ${uses} return required.value + value; } before(); -const required = require("./dep.cjs"); +var required = require("./dep.cjs"); console.log(required.value); `, ); @@ -144,6 +144,8 @@ console.log(required.value); }) as Checker; program.checkerFacade = new CheckerFacade(proxy, { project: program.project.checker.project }); + // A var binding keeps the SC1013 fence (Node reads undefined there); a + // const would compile with initialization checks from the same scan. expect(checkPreflight(load).map((diag) => diag.code)).toContain("SC1013"); expect(calls.some(([arg]) => Array.isArray(arg) && arg.length > 24)).toBe(true); expect(calls.some(([arg]) => !Array.isArray(arg))).toBe(false); diff --git a/packages/compiler/test/tuple-forof.test.ts b/packages/compiler/test/tuple-forof.test.ts index 3a66d9c099..78cc9376de 100644 --- a/packages/compiler/test/tuple-forof.test.ts +++ b/packages/compiler/test/tuple-forof.test.ts @@ -25,7 +25,12 @@ console.log(total, calls); const mod = deserializeModule(await readFile(outPath, "utf8")); expect(validateModule(mod)).toEqual([]); const llvm = emitLlvmModule(mod); - expect(llvm.match(/call ptr @sc_f_pair\(/g)).toHaveLength(1); + // The receiver is computed once: an ordinary call, or (when the loop + // only reads elements) the result-slot entry filling frame storage. + const calls = + (llvm.match(/call ptr @sc_f_pair\(/g) ?? []).length + + (llvm.match(/call void @sc_\w*into_pair\(ptr %s\d+\)/g) ?? []).length; + expect(calls).toBe(1); expect(llvm).not.toContain("@scr_arr_new"); } finally { await rm(dir, { recursive: true, force: true }); diff --git a/packages/experimental/README.md b/packages/experimental/README.md index 413deeff24..8a44053b0b 100644 --- a/packages/experimental/README.md +++ b/packages/experimental/README.md @@ -21,7 +21,7 @@ function rebuild(): Program { - `run(fn)` calls `fn` with the region as the current region and returns its result. `ProgramLifetime` instances created during the call belong to the region; calls nest. - Instances created outside every `run` call belong to the default region, which is never freed. -- Work that resumes after an `await` inside `fn` runs outside the region. +- Async work started inside `fn` keeps its region: what it creates after an `await` belongs to the region too, and the region is not freed before that work has finished. - `release()` requests that the region be freed. scriptc frees it once nothing outside the region refers to any of its instances; until then the region stays allocated. Later `run` calls on a released region allocate in the default region. - A region that holds a published object (`@scriptc/threads`) is never freed. diff --git a/packages/experimental/index.d.ts b/packages/experimental/index.d.ts index d831cbff96..057dcde09f 100644 --- a/packages/experimental/index.d.ts +++ b/packages/experimental/index.d.ts @@ -7,13 +7,14 @@ export declare class LifetimeRegion { constructor(); /** Calls `fn` with this region as the current region and returns its * result. ProgramLifetime instances created during the call, outside - * nested `run` calls of other regions, belong to this region. Work that - * resumes after an `await` inside `fn` runs outside the region. */ + * nested `run` calls of other regions, belong to this region, and so do + * those that async functions called during it create after an `await`. */ run(fn: () => T): T; /** Requests that the region be freed. Its instances are freed once - * nothing outside the region refers to any of them; until then the - * region stays allocated. Later `run` calls allocate outside the region. - * Never observable except through memory use. */ + * nothing outside the region refers to any of them and the async work + * started inside it has finished; until then the region stays allocated. + * Later `run` calls allocate outside the region. Never observable except + * through memory use. */ release(): void; } diff --git a/packages/runtime/src/scr_async.c b/packages/runtime/src/scr_async.c index 259f2fa7a6..308c9198d6 100644 --- a/packages/runtime/src/scr_async.c +++ b/packages/runtime/src/scr_async.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -594,6 +595,11 @@ struct ScrFiber { * SPAWNER at spawn (Node's init-time capture), swapped active with the * fiber (the exc-cell pattern) so run()'s window rides awaits. */ ScrAlsCtx *als; + /* The lifetime region the body was spawned in (scr_region_fiber_hooks; + * NULL = the default region): the loop makes it current again whenever + * it resumes the fiber, stackful or stackless. Async bodies only; + * generators run in their resumer's region. */ + void *region; bool done; /* A native synchronous generator compiled as an LLVM coroutine (see coro * above): no stack or saved context; resumes call into the frame on the @@ -682,6 +688,12 @@ void scr_als_ctx_release(ScrAlsCtx *c) { free(c); } +/* The lifetime region a new async fiber carries (ScrFiber.region). */ +static inline void *scr_fiber_region_capture(void) { + const ScrRgnFiberHooks *regions = atomic_load_explicit(&scr_region_fiber_hooks, memory_order_relaxed); + return regions != NULL ? regions->capture() : NULL; +} + static SCR_TL ScrCtx scr_loop_ctx; /* the main stack (scheduler home) */ static SCR_TL ScrFiber *scr_current = NULL; /* A finished fiber whose last switch went to the loop (scr_resume_fiber). */ @@ -1903,6 +1915,7 @@ static void scr_fiber_destroy(ScrFiber *f) { #endif #endif scr_als_ctx_release(f->als); + if (f->region != NULL) atomic_load_explicit(&scr_region_fiber_hooks, memory_order_relaxed)->drop(f->region); #if !defined(_WIN32) && !defined(__wasi__) scr_fiber_stack_free(f->stack); #else @@ -1934,6 +1947,7 @@ static ScrPromise *scr_async_spawn_impl(void (*entry)(ScrFiber *, void *), void /* AsyncLocalStorage: the child runs in the SPAWNER's context (Node's * init-time capture); snapshots are immutable, so a retain suffices. */ f->als = scr_als_ctx_retain(*SCR_ALS_SLOT()); + f->region = scr_fiber_region_capture(); scr_fibers_live++; #ifdef _WIN32 @@ -2075,6 +2089,9 @@ ScrPromise *scr_async_spawn_stackless(void (*entry)(ScrFiber *, void *), void *a f->promise = scr_promise_new(); f->stackless = true; f->als = scr_als_ctx_retain(*SCR_ALS_SLOT()); + /* The lifetime region the eager prefix runs in, which every later step + * resumes in (scr_resume_fiber), as a fiber's body does. */ + f->region = scr_fiber_region_capture(); scr_fibers_live++; #ifdef SCR_ASAN_FIBERS f->stackless_next = scr_stackless_fibers; @@ -2248,6 +2265,7 @@ ScrPromise *scr_async_spawn_after(ScrPromise *dependency, f->entry = entry; f->argpack = argpack; f->als = scr_als_ctx_retain(*SCR_ALS_SLOT()); + f->region = scr_fiber_region_capture(); scr_fibers_live++; if (dependency->state == SCR_PROM_PENDING) { @@ -3796,6 +3814,9 @@ static void scr_resume_fiber(ScrFiber *f) { (void)scr_win_self(); #endif f->return_to = &scr_loop_ctx; + /* The body continues in the lifetime region it was spawned in. */ + const ScrRgnFiberHooks *regions = atomic_load_explicit(&scr_region_fiber_hooks, memory_order_relaxed); + void *region_before = regions != NULL ? regions->resume(f->region) : NULL; #ifdef __wasi__ scr_current = f; scr_exc_swap_cell(&f->exc); @@ -3812,6 +3833,7 @@ static void scr_resume_fiber(ScrFiber *f) { scr_current = NULL; scr_exc_swap_cell(NULL); scr_als_active = &scr_als_main_slot; + if (regions != NULL) regions->suspend(region_before); #else if (f->stackless) { /* A stackless async body continues on the loop's stack. */ @@ -3822,8 +3844,10 @@ static void scr_resume_fiber(ScrFiber *f) { scr_current = NULL; scr_exc_swap_cell(NULL); scr_als_active = &scr_als_main_slot; + if (regions != NULL) regions->suspend(region_before); } else { scr_switch(&scr_loop_ctx, &f->ctx, f); + if (regions != NULL) regions->suspend(region_before); f = scr_loop_reap; if (f == NULL) return; scr_loop_reap = NULL; diff --git a/packages/runtime/src/scr_child.c b/packages/runtime/src/scr_child.c index 2d72c21d2a..1235001ba7 100644 --- a/packages/runtime/src/scr_child.c +++ b/packages/runtime/src/scr_child.c @@ -2266,14 +2266,14 @@ static void scr_exec_file_fire(ScrChild *c) { } ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb, - ScrExecFileFn fn) { + ScrExecFileFn fn, bool shell) { ScrStr *empty = scr_str_new("", 0); ScrChild *c = scr_spawn_opts(cmd, args, 3, 3, 3, 0, 0, false, false, false, NULL, empty); scr_str_release(empty); c->exec_cb = cb; c->exec_fn = fn; - char *display = scr_exec_display(cmd, args, false); + char *display = scr_exec_display(cmd, args, shell); c->exec_display = scr_str_new(display, strlen(display)); free(display); scr_child_stream_capture_begin(c->out_stream); @@ -4911,14 +4911,14 @@ static void scr_exec_file_fire(ScrChild *c) { } ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb, - ScrExecFileFn fn) { + ScrExecFileFn fn, bool shell) { ScrStr *empty = scr_str_new("", 0); ScrChild *c = scr_spawn_opts(cmd, args, 3, 3, 3, 0, 0, false, false, false, NULL, empty); scr_str_release(empty); c->exec_cb = cb; c->exec_fn = fn; - char *display = scr_exec_display(cmd, args, false); + char *display = scr_exec_display(cmd, args, shell); c->exec_display = scr_str_new(display, strlen(display)); free(display); scr_child_stream_capture_begin(c->out_stream); diff --git a/packages/runtime/src/scr_child.h b/packages/runtime/src/scr_child.h index 350995e315..1819b9c538 100644 --- a/packages/runtime/src/scr_child.h +++ b/packages/runtime/src/scr_child.h @@ -155,9 +155,11 @@ ScrChild *scr_spawn_dynamic(ScrStr *cmd, ScrArr *args, const ScrDyn *options); void scr_child_on_spawn(ScrChild *child, ScrClosure *cb); /* moves zero-argument callback */ /* The callback slice: file + args, default options. The returned child has * all three stdio slots piped; stdout/stderr are captured internally and - * the callback moves into the child registry until settlement. */ + * the callback moves into the child registry until settlement. With shell + * (exec), args is ["-c", command] for /bin/sh and failure messages show + * the command, as for scr_exec_sync. */ ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb /*moves*/, - ScrExecFileFn fn); + ScrExecFileFn fn, bool shell); ScrChild *scr_child_retain(ScrChild *c); void scr_child_release(ScrChild *c); void *scr_child_retain_v(void *p); diff --git a/packages/runtime/src/scr_crypto.h b/packages/runtime/src/scr_crypto.h index 6b70a7fab6..5abfeaa480 100644 --- a/packages/runtime/src/scr_crypto.h +++ b/packages/runtime/src/scr_crypto.h @@ -114,6 +114,23 @@ void scr_crypto_hkdf_async(ScrStr *digest, ScrBytes *ikm, ScrBytes *salt, ScrBytes *info, double keylen, ScrClosure *cb, ScrCryptoBytesFn fn); void scr_crypto_scrypt_async(ScrBytes *password, ScrBytes *salt, double keylen, ScrDyn *options, ScrClosure *cb, ScrCryptoBytesFn fn); +/* util.promisify over the callback forms: scr_crypto_promise_callback + * returns a callback closure (+1) that owns the promise, and the settle + * adapters fulfill it with the value as a Buffer or view (_bytes), a + * checked-dynamic Buffer, view or ArrayBuffer, a checked-dynamic value + * (_dyn), or a number, or reject it with the callback's error. After the + * callback form returns, scr_crypto_promise_reject_thrown moves a pending + * argument error into the promise as its rejection. */ +typedef struct ScrPromise ScrPromise; +ScrClosure *scr_crypto_promise_callback(ScrPromise *promise); +void scr_crypto_promise_reject_thrown(ScrPromise *promise); +void scr_crypto_promise_settle_bytes(ScrClosure *cb, ScrError *error, ScrBytes *value); +void scr_crypto_promise_settle_buffer_dyn(ScrClosure *cb, ScrError *error, ScrBytes *value); +void scr_crypto_promise_settle_view_dyn(ScrClosure *cb, ScrError *error, ScrBytes *value); +void scr_crypto_promise_settle_array_buffer(ScrClosure *cb, ScrError *error, ScrBytes *value); +void scr_crypto_promise_settle_dyn(ScrClosure *cb, ScrError *error, ScrBytes *value); +void scr_crypto_promise_settle_number(ScrClosure *cb, double value); +void scr_crypto_promise_settle_number_dyn(ScrClosure *cb, double value); /* The digest primitives beyond MD5/SHA-1/SHA-2 (scr_hash.c). Each final * reads a copy of its context. */ diff --git a/packages/runtime/src/scr_crypto_async.c b/packages/runtime/src/scr_crypto_async.c index bae5cf8111..02aca73236 100644 --- a/packages/runtime/src/scr_crypto_async.c +++ b/packages/runtime/src/scr_crypto_async.c @@ -230,3 +230,83 @@ void scr_crypto_generate_key_async(ScrDyn *type, ScrDyn *options, ScrClosure *cb } scr_crypto_defer_dyn(key, NULL, cb, fn); } + +/* util.promisify over the callback forms above. The callback is a closure + * that owns the pending promise in caps[0], and each adapter below settles + * that promise where the callback would run: an error rejects it, and the + * value fulfills it in the representation the promise's type uses. */ +static ScrPromise *scr_crypto_promise_of(ScrClosure *cb) { + ScrPromise *p; + memcpy(&p, &cb->caps[0]->slot, sizeof p); + return p; +} + +ScrClosure *scr_crypto_promise_callback(ScrPromise *promise) { + ScrClosure *cb = scr_closure_new(NULL, 1); + cb->caps[0] = scr_box_new_obj(scr_promise_retain_v, scr_promise_release_v, NULL); + scr_box_set_ref(cb->caps[0], scr_promise_retain(promise)); + return cb; +} + +/* An argument error thrown before the operation started rejects the + * promise, as the throw inside Node's promisify wrapper does. */ +void scr_crypto_promise_reject_thrown(ScrPromise *promise) { + if (scr_exc_pending()) scr_promise_reject_pending(promise); +} + +static bool scr_crypto_promise_rejected(ScrPromise *p, ScrError *error) { + if (!error) return false; + scr_throw_obj(scr_error_retain(error), &scr_error_retain_v, &scr_error_release_v, + scr_error_trace_arg()); + scr_promise_reject_pending(p); + return true; +} + +static void scr_crypto_promise_fulfill_dyn(ScrPromise *p, ScrDyn *value /* moves */) { + scr_promise_fulfill_ref(p, value, scr_dyn_retain_v, scr_dyn_release_v, NULL); +} + +void scr_crypto_promise_settle_bytes(ScrClosure *cb, ScrError *error, ScrBytes *value) { + ScrPromise *p = scr_crypto_promise_of(cb); + if (scr_crypto_promise_rejected(p, error)) return; + scr_promise_fulfill_ref(p, value, scr_bytes_retain_v, scr_bytes_release_v, NULL); +} + +void scr_crypto_promise_settle_buffer_dyn(ScrClosure *cb, ScrError *error, ScrBytes *value) { + ScrPromise *p = scr_crypto_promise_of(cb); + if (scr_crypto_promise_rejected(p, error)) return; + ScrDyn *boxed = scr_dyn_new_buffer(value); + scr_bytes_release(value); + scr_crypto_promise_fulfill_dyn(p, boxed); +} + +void scr_crypto_promise_settle_view_dyn(ScrClosure *cb, ScrError *error, ScrBytes *value) { + ScrPromise *p = scr_crypto_promise_of(cb); + if (scr_crypto_promise_rejected(p, error)) return; + ScrDyn *boxed = scr_dyn_new_bytes(value); + scr_bytes_release(value); + scr_crypto_promise_fulfill_dyn(p, boxed); +} + +void scr_crypto_promise_settle_array_buffer(ScrClosure *cb, ScrError *error, ScrBytes *value) { + ScrPromise *p = scr_crypto_promise_of(cb); + if (scr_crypto_promise_rejected(p, error)) return; + ScrDyn *buffer = scr_array_buffer_from_bytes(value); + scr_bytes_release(value); + scr_crypto_promise_fulfill_dyn(p, buffer); +} + +/* generateKey and the checked-dynamic randomFill pass a ScrDyn value. */ +void scr_crypto_promise_settle_dyn(ScrClosure *cb, ScrError *error, ScrBytes *value) { + ScrPromise *p = scr_crypto_promise_of(cb); + if (scr_crypto_promise_rejected(p, error)) return; + scr_crypto_promise_fulfill_dyn(p, (ScrDyn *)value); +} + +void scr_crypto_promise_settle_number(ScrClosure *cb, double value) { + scr_promise_fulfill_f64(scr_crypto_promise_of(cb), value); +} + +void scr_crypto_promise_settle_number_dyn(ScrClosure *cb, double value) { + scr_crypto_promise_fulfill_dyn(scr_crypto_promise_of(cb), scr_dyn_new_num(value)); +} diff --git a/packages/runtime/src/scr_dgram.c b/packages/runtime/src/scr_dgram.c index 13650a35d3..b02c41c03d 100644 --- a/packages/runtime/src/scr_dgram.c +++ b/packages/runtime/src/scr_dgram.c @@ -1636,6 +1636,66 @@ void scr_dns_lookup_service(const ScrDyn *address, const ScrDyn *port, const Scr scr_dns_call_later(callback, args, 3, false); } +/* util.promisify(dns.lookup) and util.promisify(dns.lookupService): the + * callback forms with a callback that settles the returned promise. An + * error rejects it; otherwise it fulfills with { address, family } (the + * address array with options.all) or { hostname, service }, the objects + * Node's custom promisified forms build. An argument error rejects the + * promise instead of throwing. */ +static ScrDyn *scr_dns_promise_settle(ScrClosure *clo, ScrDyn *const *args, size_t argc, + const char *first, const char *second) { + ScrPromise *p; + memcpy(&p, &clo->caps[0]->slot, sizeof p); + if (argc >= 1 && !scr_dyn_is_nullish(args[0])) { + scr_dyn_throw(scr_dyn_retain(args[0])); + scr_promise_reject_pending(p); + return scr_dyn_undefined(); + } + ScrDyn *value; + if (argc == 2) { + value = scr_dyn_retain(args[1]); + } else { + value = scr_dyn_new_obj(); + scr_dyn_obj_set(value, first, strlen(first), argc >= 2 ? scr_dyn_retain(args[1]) : scr_dyn_undefined()); + scr_dyn_obj_set(value, second, strlen(second), argc >= 3 ? scr_dyn_retain(args[2]) : scr_dyn_undefined()); + } + scr_promise_fulfill_ref(p, value, scr_dyn_retain_v, scr_dyn_release_v, NULL); + return scr_dyn_undefined(); +} + +static ScrDyn *scr_dns_lookup_settle(ScrClosure *clo, ScrDyn *const *args, size_t argc) { + return scr_dns_promise_settle(clo, args, argc, "address", "family"); +} + +static ScrDyn *scr_dns_service_settle(ScrClosure *clo, ScrDyn *const *args, size_t argc) { + return scr_dns_promise_settle(clo, args, argc, "hostname", "service"); +} + +static ScrDyn *scr_dns_promise_callback(ScrPromise *p, ScrDynThunk thunk) { + ScrClosure *clo = scr_closure_new((void *)thunk, 1); + clo->caps[0] = scr_box_new_obj(scr_promise_retain_v, scr_promise_release_v, NULL); + scr_box_set_ref(clo->caps[0], scr_promise_retain(p)); + return scr_dyn_new_func(clo, thunk, 3, "(err,a,b)", "callback"); +} + +ScrPromise *scr_dns_lookup_promise(const ScrDyn *hostname, const ScrDyn *options) { + ScrPromise *p = scr_promise_new(); + ScrDyn *callback = scr_dns_promise_callback(p, scr_dns_lookup_settle); + scr_dns_lookup_dyn(hostname, options, callback, false); + scr_dyn_release(callback); + if (scr_exc_pending()) scr_promise_reject_pending(p); + return p; +} + +ScrPromise *scr_dns_lookup_service_promise(const ScrDyn *address, const ScrDyn *port) { + ScrPromise *p = scr_promise_new(); + ScrDyn *callback = scr_dns_promise_callback(p, scr_dns_service_settle); + scr_dns_lookup_service(address, port, callback, false); + scr_dyn_release(callback); + if (scr_exc_pending()) scr_promise_reject_pending(p); + return p; +} + /* dns.getServers(): the nameserver lines of /etc/resolv.conf, as c-ares * reads them ("[addr]:port" only for a port other than 53). +1. */ ScrDyn *scr_dns_get_servers(void) { diff --git a/packages/runtime/src/scr_dgram.h b/packages/runtime/src/scr_dgram.h index ccdfcbf777..6c8782da08 100644 --- a/packages/runtime/src/scr_dgram.h +++ b/packages/runtime/src/scr_dgram.h @@ -83,6 +83,11 @@ void scr_dns_lookup_dyn(const ScrDyn *hostname, const ScrDyn *options, const Scr bool typed); void scr_dns_lookup_service(const ScrDyn *address, const ScrDyn *port, const ScrDyn *callback, bool typed); +/* util.promisify(dns.lookup) and util.promisify(dns.lookupService): a +1 + * promise of the checked-dynamic result; never throws. */ +typedef struct ScrPromise ScrPromise; +ScrPromise *scr_dns_lookup_promise(const ScrDyn *hostname, const ScrDyn *options); +ScrPromise *scr_dns_lookup_service_promise(const ScrDyn *address, const ScrDyn *port); ScrDyn *scr_dns_get_servers(void); /* +1 */ ScrStr *scr_dns_get_default_result_order(void); /* +1 */ void scr_dns_set_default_result_order(const ScrDyn *order); diff --git a/packages/runtime/src/scr_events_emitter.c b/packages/runtime/src/scr_events_emitter.c index 4f934ce246..ceecb15e3d 100644 --- a/packages/runtime/src/scr_events_emitter.c +++ b/packages/runtime/src/scr_events_emitter.c @@ -1115,3 +1115,11 @@ void scr_emitter_set_default_max(double n) { scr_emitter_default_max = n; } double scr_emitter_get_default_max(void) { return scr_emitter_default_max; } + +/* The events module's ES module namespace holds a copy of + * defaultMaxListeners, taken when the program starts; ESM namespace + * and named-import reads answer it. module.syncBuiltinESMExports() copies + * the current value into it. */ +static SCR_TL double scr_emitter_esm_default_max = 10; +double scr_emitter_esm_default_max_get(void) { return scr_emitter_esm_default_max; } +void scr_emitter_esm_sync(void) { scr_emitter_esm_default_max = scr_emitter_default_max; } diff --git a/packages/runtime/src/scr_events_emitter.h b/packages/runtime/src/scr_events_emitter.h index c7edcf1070..99da57a504 100644 --- a/packages/runtime/src/scr_events_emitter.h +++ b/packages/runtime/src/scr_events_emitter.h @@ -143,6 +143,8 @@ ScrEmitter *scr_emitter_set_max(ScrEmitter *em, double n); /* returns em +1 */ double scr_emitter_get_max(ScrEmitter *em); void scr_emitter_set_default_max(double n); double scr_emitter_get_default_max(void); +double scr_emitter_esm_default_max_get(void); +void scr_emitter_esm_sync(void); /* True when the emitter has at least one listener for `name` — the * stream unit's registration probe (and anyone else's). */ bool scr_emitter_has(ScrEmitter *em, const char *name); diff --git a/packages/runtime/src/scr_map.c b/packages/runtime/src/scr_map.c index 8e34533add..14181968ce 100644 --- a/packages/runtime/src/scr_map.c +++ b/packages/runtime/src/scr_map.c @@ -77,7 +77,8 @@ static inline uint64_t scr_map_hash_str(const ScrStr *k) { uint32_t h = k->hash; if (__builtin_expect(h != 0, 1)) return scr_map_hash_widen(h); h = scr_map_hash_fold(k->data, k->len); - if (k->rc != SIZE_MAX) ((ScrStr *)k)->hash = h; + /* A scratch slice is a writable stack copy (scr_str_is_scratch). */ + if (k->rc != SIZE_MAX || scr_str_is_scratch(k)) ((ScrStr *)k)->hash = h; return scr_map_hash_widen(h); #else return scr_map_hash_bytes(k->data, k->len); @@ -931,7 +932,11 @@ static void scr_map_append(ScrMap *m, uint64_t hash, uint64_t key, uint64_t val, m->filter |= scr_map_filter_bits(hash); m->nlive++; if (m->key_kind == SCR_MAP_KEY_STR) { - scr_str_retain((ScrStr *)scr_map_slot_to_ptr(key)); /* key is borrowed */ + ScrStr *k = scr_map_slot_to_ptr(key); /* key is borrowed */ + if (__builtin_expect(scr_str_is_scratch(k), 0)) + m->entries[idx].key = scr_map_slot_from_ptr(scr_str_scratch_keep(k)); + else + scr_str_retain(k); } else if (m->key_kind == SCR_MAP_KEY_DYN) { const ScrDyn *d = (ScrDyn *)scr_map_slot_to_ptr(key); if (d->kind == SCR_DYN_NUM && d->v.num == 0) { diff --git a/packages/runtime/src/scr_object.c b/packages/runtime/src/scr_object.c index a533a409ce..7cafbeca3d 100644 --- a/packages/runtime/src/scr_object.c +++ b/packages/runtime/src/scr_object.c @@ -2,6 +2,11 @@ * one runtime layout, including captured bindings for local classes. */ #include "scr_runtime.h" +/* The lifetime-region fiber hooks (scr_region.h) live in this always-linked + * unit: scr_async.c reads them, and some runtime test links take + * scr_async.c without scr_region.c. scr_region_create installs them. */ +_Atomic(const ScrRgnFiberHooks *) scr_region_fiber_hooks = NULL; + void scr_classobj_trace_v(void *object, ScrTraceVisit visit, void *ctx) { ScrClassObj *c = object; for (size_t i = 0; i < c->ncaps; i++) visit(c->caps[i], ctx); diff --git a/packages/runtime/src/scr_region.c b/packages/runtime/src/scr_region.c index c1a125e8bc..31d01296e3 100644 --- a/packages/runtime/src/scr_region.c +++ b/packages/runtime/src/scr_region.c @@ -70,6 +70,18 @@ void __lsan_unregister_root_region(const void *p, size_t size) __attribute__((we #define SCR_RGN_AHEAD_START ((size_t)1 << 20) /* see scr_rgn_ahead */ _Static_assert(sizeof(ScrRgnChunk) <= SCR_RGN_HDR, "the chunk header fits before the first segment"); +/* Freed chunks a thread keeps for its next regions: a program that frees + * a region per request or per batch would otherwise map, fault in and + * unmap its chunks every time. A cached chunk keeps at most its first + * SCR_RGN_CACHE_WARM bytes resident (cleared), and gets fresh pages past + * them, so a small region that reuses the chunk of a large one does not + * hold the large one's memory. The cache holds at most SCR_RGN_CACHE_BYTES + * of resident chunk memory (whole chunks where chunks are not mapped + * pages); chunks beyond it, and chunks huge pages may back, are unmapped + * as before. */ +#define SCR_RGN_CACHE_BYTES ((size_t)8 << 20) +#define SCR_RGN_CACHE_WARM ((size_t)256 << 10) + /* Regions start with small pages and switch to huge pages once they span * this many chunks (a default region at once): small regions then do not * pay a 2 MiB fault, large ones do not pay 512 small ones. */ @@ -99,6 +111,7 @@ typedef struct ScrRegion { uint64_t id; /* 0: the thread's default region */ int state; uint32_t depth; /* run() frames active on this region */ + size_t fibers; /* async fibers that carry it (scr_region_fiber_hooks) */ ScrRgnChunk *chunks; size_t nchunks; ScrRgnSeg *segs; @@ -210,7 +223,10 @@ typedef struct { size_t allocated; /* bytes of region segments this thread has allocated */ size_t checked; /* `allocated` at the last retry */ ScrRgnWalk walk; + ScrRgnChunk *cache; /* freed chunks, linked through `next` (SCR_RGN_CACHE_BYTES) */ + size_t cache_bytes; /* their resident bytes, as recorded in each one's `found` */ bool exit_registered; + bool exited; /* the exit cleanup ran: the fiber hooks do nothing */ double cycle_ms; /* the cycle pass before the next attempt (stats) */ struct { size_t created, released, freed, pinned, leaked, attempts; @@ -329,18 +345,39 @@ static bool scr_rgn_huge(const ScrRegion *r) { return r->id == 0 || r->nchunks + /* A chunk for `r`'s next one, with its page policy. */ static ScrRgnChunk *scr_rgn_chunk_reserve(const ScrRegion *r) { ScrRgnChunk *c = scr_rgn_chunk_map(); + c->huge = false; +#if SCR_RGN_MMAP + c->touched = (char *)c + SCR_RGN_HDR; +#else + c->touched = (char *)c + SCR_RGN_CHUNK; /* allocated zeroed */ +#endif #if SCR_RGN_MMAP && defined(MADV_HUGEPAGE) - if (scr_rgn_huge(r)) (void)madvise(c, SCR_RGN_CHUNK, MADV_HUGEPAGE); + if (scr_rgn_huge(r)) { + (void)madvise(c, SCR_RGN_CHUNK, MADV_HUGEPAGE); + c->huge = true; + } #else (void)r; #endif return c; } +/* Faults [from, to) of chunk `c` in ahead of use. */ +static void scr_rgn_prefault(ScrRgnChunk *c, char *from, char *to) { + if (to > c->touched) c->touched = to; + scr_mem_prefault(from, to); +} + static ScrRgnChunk *scr_rgn_chunk_new(ScrRegion *r) { - ScrRgnChunk *c = r->spare ? r->spare : scr_rgn_chunk_reserve(r); + ScrRgnChunk *c = r->spare; + if (!c && scr_rgn.cache) { + c = scr_rgn.cache; + scr_rgn.cache = c->next; + scr_rgn.cache_bytes -= (size_t)c->found; + } + if (!c) c = scr_rgn_chunk_reserve(r); r->spare = NULL; - if (r->bytes >= SCR_RGN_AHEAD_START) scr_mem_prefault(c, (char *)c + 2 * SCR_RGN_AHEAD); + if (r->bytes >= SCR_RGN_AHEAD_START) scr_rgn_prefault(c, (char *)c, (char *)c + 2 * SCR_RGN_AHEAD); #ifdef SCR_LSAN_IMMORTAL if (__lsan_register_root_region) __lsan_register_root_region(c, SCR_RGN_CHUNK); #endif @@ -369,28 +406,76 @@ static void scr_rgn_ahead(ScrRegion *r, ScrRgnChunk *c, char *from) { size_t before = (size_t)(from - (char *)c), after = (size_t)(c->bump - (char *)c); if (after / SCR_RGN_AHEAD != before / SCR_RGN_AHEAD) { char *lo = (char *)c + (after / SCR_RGN_AHEAD + 1) * SCR_RGN_AHEAD; - if (lo < (char *)c + SCR_RGN_CHUNK) scr_mem_prefault(lo, lo + SCR_RGN_AHEAD); + if (lo < (char *)c + SCR_RGN_CHUNK) scr_rgn_prefault(c, lo, lo + SCR_RGN_AHEAD); } if (!r->spare && after >= SCR_RGN_CHUNK / 2 && before < SCR_RGN_CHUNK / 2) { bool huge = scr_rgn_huge(r); r->spare = scr_rgn_chunk_reserve(r); - scr_mem_prefault(r->spare, (char *)r->spare + (huge ? SCR_RGN_CHUNK : SCR_RGN_AHEAD)); + scr_rgn_prefault(r->spare, (char *)r->spare, (char *)r->spare + (huge ? SCR_RGN_CHUNK : SCR_RGN_AHEAD)); } } +/* Returns a chunk that holds no object to the system. */ +static void scr_rgn_chunk_drop(ScrRgnChunk *c) { +#if SCR_RGN_MMAP + munmap(c, SCR_RGN_CHUNK); +#elif defined(_WIN32) + _aligned_free(c); +#else + free(c); +#endif +} + /* A spare chunk never held objects: it goes straight back. */ static void scr_rgn_spare_drop(ScrRegion *r) { if (!r->spare) return; -#if SCR_RGN_MMAP - munmap(r->spare, SCR_RGN_CHUNK); -#elif defined(_WIN32) - _aligned_free(r->spare); -#else - free(r->spare); -#endif + scr_rgn_chunk_drop(r->spare); r->spare = NULL; } +/* A freed region's chunk: kept for the thread's next regions while the + * cache has room, cleared (a fresh chunk is zero, and allocation relies on + * that), else unmapped (SCR_RGN_CACHE_BYTES). Sanitized builds never reuse + * a chunk (scr_rgn_chunk_unmap). */ +static void scr_rgn_chunk_retire(ScrRgnThread *t, ScrRgnChunk *c) { +#ifndef SCR_RGN_POISON + char *first = (char *)c + SCR_RGN_HDR; + if (c->bump > c->touched) c->touched = c->bump; +#if SCR_RGN_MMAP + char *warm = (char *)c + SCR_RGN_CACHE_WARM; + size_t resident = (size_t)((c->touched < warm ? c->touched : warm) - (char *)c); +#else + size_t resident = SCR_RGN_CHUNK; +#endif + if (!c->huge && t->cache_bytes + resident <= SCR_RGN_CACHE_BYTES) { +#if SCR_RGN_MMAP + if (c->touched > warm) { + /* Fresh zero pages past the warm part (64 KiB steps cover every + * page size). */ + size_t tail = ((size_t)(c->touched - warm) + 0xffff) & ~(size_t)0xffff; + if (tail > SCR_RGN_CHUNK - SCR_RGN_CACHE_WARM) tail = SCR_RGN_CHUNK - SCR_RGN_CACHE_WARM; + if (mmap(warm, tail, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == MAP_FAILED) { + scr_rgn_chunk_unmap(c); + return; + } + c->touched = warm; + } + memset(first, 0, (size_t)((c->bump < warm ? c->bump : warm) - first)); +#else + memset(first, 0, (size_t)(c->bump - first)); +#endif + c->found = (int64_t)resident; + c->next = t->cache; + t->cache = c; + t->cache_bytes += resident; + return; + } +#else + (void)t; +#endif + scr_rgn_chunk_unmap(c); +} + /* The instance stride: the emitted fast path rounds the size the same way. */ static inline size_t scr_rgn_stride(const ScrRgnClass *cls) { return (cls->size + 7) & ~(size_t)7; } @@ -875,7 +960,7 @@ static void scr_rgn_free_region(ScrRgnThread *t, ScrRegion *r) { scr_rgn_spare_drop(r); for (ScrRgnChunk *c = r->chunks, *next; c; c = next) { next = c->next; - scr_rgn_chunk_unmap(c); + scr_rgn_chunk_retire(t, c); } for (ScrRgnSeg *s = r->segs, *next; s; s = next) { next = s->next; @@ -1118,7 +1203,7 @@ static bool scr_rgn_try_free(ScrRgnThread *t, ScrRegion *r, bool tentative) { /* A condemned region not in use, never attempted, or kept by an attempt * whose outcome may have changed since (see SCR_RGN_RETRY_BYTES). */ static bool scr_rgn_due(const ScrRegion *r, const ScrRgnThread *t) { - if (r->state != SCR_RGN_CONDEMNED || r->depth != 0 || r == t->current) return false; + if (r->state != SCR_RGN_CONDEMNED || r->depth != 0 || r->fibers != 0 || r == t->current) return false; return !r->kept || t->allocated >= r->retry_at || scr_rgn_counted(r) != r->kept_counted; } @@ -1162,11 +1247,53 @@ static void scr_rgn_retry(ScrRgnThread *t) { scr_rgn_try_due(t, false); } +/* ── async continuations ─────────────────────────────────────────────── */ + +/* A fiber spawned while a region is current carries it: the region + * cannot be freed until the fiber is gone, so work that resumes after an + * await can keep allocating in it, condemned or not (scr_rgn_due). The + * fiber's own references to the region's objects are counted like any + * others. Fibers of the default region carry nothing. */ +static void *scr_rgn_fiber_capture(void) { + ScrRgnThread *t = &scr_rgn; + ScrRegion *r = t->current; + if (t->exited || !r || r->id == 0) return NULL; + r->fibers++; + return r; +} + +static void scr_rgn_fiber_drop(void *region) { + if (region && !scr_rgn.exited) ((ScrRegion *)region)->fibers--; +} + +/* The event loop resumes a fiber: its region becomes current until the + * fiber suspends or finishes. Run frames never span a suspension, so the + * fiber leaves the region current that it found. */ +static void *scr_rgn_fiber_resume(void *region) { + ScrRgnThread *t = &scr_rgn; + if (t->exited || !t->dflt) return NULL; /* no region on this thread yet */ + ScrRegion *previous = t->current; + scr_rgn_switch(t, region ? (ScrRegion *)region : t->dflt); + return previous; +} + +static void scr_rgn_fiber_suspend(void *previous) { + if (previous && !scr_rgn.exited) scr_rgn_switch(&scr_rgn, previous); +} + +static const ScrRgnFiberHooks scr_rgn_fiber_hooks = { + scr_rgn_fiber_capture, + scr_rgn_fiber_drop, + scr_rgn_fiber_resume, + scr_rgn_fiber_suspend, +}; + /* ── program-facing entry points ─────────────────────────────────────── */ double scr_region_create(const ScrRgnType *types) { const ScrRgnType *expected = NULL; atomic_compare_exchange_strong(&scr_rgn_types, &expected, types); + atomic_store_explicit(&scr_region_fiber_hooks, &scr_rgn_fiber_hooks, memory_order_relaxed); /* Counting starts before the region's first object exists. */ atomic_store_explicit(&scr_region_counting, 1, memory_order_relaxed); ScrRgnThread *t = scr_rgn_thread(); @@ -1230,6 +1357,10 @@ static void scr_rgn_thread_exit(void) { free(r->cur); free(r); } + for (ScrRgnChunk *c = t->cache, *next; c; c = next) { + next = c->next; + scr_rgn_chunk_drop(c); + } free(t->regions); free(t->cursors); free(t->stack); @@ -1243,4 +1374,5 @@ static void scr_rgn_thread_exit(void) { #endif memset(t, 0, sizeof *t); t->exit_registered = true; /* the cleanup ran; a late allocation keeps its memory */ + t->exited = true; /* fibers destroyed later must not touch the freed regions */ } diff --git a/packages/runtime/src/scr_region.h b/packages/runtime/src/scr_region.h index 14eae6951a..aa07f90527 100644 --- a/packages/runtime/src/scr_region.h +++ b/packages/runtime/src/scr_region.h @@ -77,6 +77,11 @@ typedef struct ScrRgnChunk { struct ScrRgnChunk *next; char *bump; /* the next free segment byte */ int64_t found; /* references the last walk saw into this chunk */ + /* What stays resident across reuse (scr_region.c's chunk cache): the + * highest byte any region carved or faulted in, and whether the chunk + * may be backed by huge pages. */ + char *touched; + bool huge; } ScrRgnChunk; _Static_assert(offsetof(ScrRgnChunk, count) == 0 && offsetof(ScrRgnChunk, owner) == sizeof(int64_t), "llvm/regions.ts addresses the chunk count at 0 and the owner at 8"); @@ -111,4 +116,21 @@ void scr_region_release(double id); void scr_region_ref(ScrRgnWalk *w, void *obj); void scr_region_slot(ScrRgnWalk *w, void *obj, void (*release)(void *)); +/* Async continuations. A fiber that runs an async function body, on its + * own stack or as a stackless coroutine, carries the region that was + * current when it was spawned (`capture`; NULL is the default region), and + * the event loop makes that region current while the fiber runs after an + * await (`resume` answers the region to restore with + * `suspend`). A region is never freed while a fiber that carries it + * exists; `drop` ends the fiber's hold. scr_region_create installs the + * hooks (process-wide, once); until then the fiber machinery (scr_async.c) + * calls nothing. Every hook acts on the calling thread's regions. */ +typedef struct ScrRgnFiberHooks { + void *(*capture)(void); + void (*drop)(void *region); + void *(*resume)(void *region); + void (*suspend)(void *previous); +} ScrRgnFiberHooks; +extern _Atomic(const ScrRgnFiberHooks *) scr_region_fiber_hooks; + #endif /* SCR_REGION_H */ diff --git a/packages/runtime/src/scr_region.test.c b/packages/runtime/src/scr_region.test.c index b79c818cd3..31e98942a8 100644 --- a/packages/runtime/src/scr_region.test.c +++ b/packages/runtime/src/scr_region.test.c @@ -381,6 +381,30 @@ static void check_debug_report(void) { puts("the debug report names what keeps a region"); } +/* A freed region's chunk serves the thread's next region, cleared where + * the freed objects were. Sanitized builds never reuse a chunk. */ +static void check_chunk_reuse(void) { +#ifndef SCR_RC_AUDIT + double r = region(); + scr_region_enter(r); + Node *first = node_new(7); + first->other = node_retain(first); + scr_region_leave(); + ScrRgnChunk *chunk = scr_rgn_chunk_of(first); + node_release(first); + scr_region_release(r); + double s = region(); + scr_region_enter(s); + Node *again = node_new(8); + scr_region_leave(); + assert(scr_rgn_chunk_of(again) == chunk); + assert(again->other == NULL && again->parent == NULL && again->kids == NULL); + node_release(again); + scr_region_release(s); + puts("a freed region's chunk serves the next region, cleared"); +#endif +} + static void check_poison(void) { #if defined(SCR_RC_AUDIT) && !defined(_WIN32) double r = region(); @@ -499,6 +523,7 @@ int main(int argc, char **argv) { check_in_use(); check_cursors(); check_retry_schedule(); + check_chunk_reuse(); check_poison(); } else if (!strcmp(mode, "debug")) { check_debug_report(); diff --git a/packages/runtime/src/scr_region.test.ts b/packages/runtime/src/scr_region.test.ts index cfadac91eb..28e44e09af 100644 --- a/packages/runtime/src/scr_region.test.ts +++ b/packages/runtime/src/scr_region.test.ts @@ -122,11 +122,16 @@ describe.skipIf(process.platform === "win32")("counted lifetime regions", () => const sanitized = lane === "sanitized"; expect(owned.stdout.trim().split("\n")).toEqual([ ...OWNED_STDOUT, - ...(sanitized ? ["sanitized builds fault on any access to a freed region"] : []), + sanitized + ? "sanitized builds fault on any access to a freed region" + : "a freed region's chunk serves the next region, cleared", ]); const ownedStats = attempts(owned.stderr); - expect(ownedStats.attempts).toEqual([...OWNED_ATTEMPTS, ...(sanitized ? ["18 freed"] : [])]); - const n = sanitized ? 18 : 17; + expect(ownedStats.attempts).toEqual([ + ...OWNED_ATTEMPTS, + ...(sanitized ? ["18 freed"] : ["18 freed", "19 freed"]), + ]); + const n = sanitized ? 18 : 19; expect(ownedStats.summaries).toEqual([ `scriptc regions: ${n} created, ${n} released, ${n} freed, 0 pinned, 0 kept`, ]); diff --git a/packages/runtime/src/scr_string.c b/packages/runtime/src/scr_string.c index 6f35297779..43cf21b0c2 100644 --- a/packages/runtime/src/scr_string.c +++ b/packages/runtime/src/scr_string.c @@ -1999,20 +1999,34 @@ ScrStr *scr_str_slice(ScrStr *s, double start, double end) { return scr_str_slice_units(s, e, from, to); } +/* One slice() or substring() boundary in [0, length]: ToIntegerOrInfinity, + * then slice's end-relative negative or substring's clamp. Conversion to an + * integer truncates toward zero exactly like ToIntegerOrInfinity, so no + * libm call is needed; NaN fails both comparisons and lands on 0, and a + * negative fraction above -1 is -0, which slice treats as 0. */ +static inline size_t scr_slice_unit(double v, size_t length, bool substring) { + if (v >= 0) return v >= (double)length ? length : (size_t)v; + if (substring || !(v > -(double)length)) return 0; + long long unit = (long long)v; /* in (-length, 0] */ + return unit == 0 ? 0 : (size_t)((long long)length + unit); +} + +/* slice()/substring() boundaries in UTF-16 units over the receiver's length. */ +static void scr_str_slice_bounds(ScrStr *s, ScrSidx *e, double start, double end, bool substring, + size_t *from_out, size_t *to_out) { + size_t length = scr_sidx_len(s, e); + size_t from = scr_slice_unit(start, length, substring); + size_t to = scr_slice_unit(end, length, substring); + if (substring && from > to) { size_t swap = from; from = to; to = swap; } + *from_out = from; + *to_out = to; +} + void scr_str_slice_range(ScrStr *s, double start, double end, bool substring, ScrStringSlice *out) { ScrSidx *e = scr_sidx_for(s); - size_t length = scr_sidx_len(s, e); - double a = scr_to_integer_or_infinity(start), b = scr_to_integer_or_infinity(end); size_t from, to; - if (substring) { - from = a <= 0 ? 0 : a >= (double)length ? length : (size_t)a; - to = b <= 0 ? 0 : b >= (double)length ? length : (size_t)b; - if (from > to) { size_t swap = from; from = to; to = swap; } - } else { - from = scr_slice_boundary(a, length); - to = scr_slice_boundary(b, length); - } + scr_str_slice_bounds(s, e, start, end, substring, &from, &to); out->start = from; out->length = to > from ? to - from : 0; out->split = 0; @@ -2024,6 +2038,56 @@ void scr_str_slice_range(ScrStr *s, double start, double end, bool substring, } } +/* Fill a scratch buffer with `units` ASCII bytes (scr_str_is_scratch). */ +static inline ScrStr *scr_str_scratch_fill(ScrStr *scratch, const char *data, size_t units) { + scratch->rc = SIZE_MAX; + scratch->len = units; + scratch->cap = 0; /* below len: the scratch marker */ + scratch->ascii = 1; +#if SCR_STR_HASH_CACHE + scratch->hash = 0; +#endif + scr_copy_bytes(scratch->data, data, units); + scratch->data[units] = '\0'; + return scratch; +} + +ScrStr *scr_str_slice_scratch(ScrStr *s, double start, double end, bool substring, + ScrStr *scratch, size_t bytes) { + size_t capacity = bytes - sizeof(ScrStr) - 1; + if (__builtin_expect(s->ascii, 1)) { + /* Units are bytes: no index entry, and the bounds are byte offsets. */ + size_t length = s->len; + size_t from = scr_slice_unit(start, length, substring); + size_t to = scr_slice_unit(end, length, substring); + if (substring && from > to) { size_t swap = from; from = to; to = swap; } + size_t units = to > from ? to - from : 0; + /* Shorter results are interned statics. */ + if (units >= 2 && units <= capacity) return scr_str_scratch_fill(scratch, s->data + from, units); + return scr_str_slice_units(s, NULL, from, to); + } + ScrSidx *e = scr_sidx_for(s); + size_t from, to; + scr_str_slice_bounds(s, e, start, end, substring, &from, &to); + size_t units = to > from ? to - from : 0; + if (units >= 2 && units <= capacity) { + /* Equal unit and byte counts between two character boundaries prove + * the span is ASCII: every other character takes more bytes than + * UTF-16 units. Non-ASCII results keep the owned copy and its index. */ + bool first, last; + size_t from_b = scr_u16_to_byte_c(s, e, from, &first); + size_t to_b = scr_u16_to_byte_c(s, e, to, &last); + if (!first && !last && to_b - from_b == units) + return scr_str_scratch_fill(scratch, s->data + from_b, units); + } + return scr_str_slice_units(s, e, from, to); +} + +ScrStr *scr_str_scratch_keep(ScrStr *s) { + if (!scr_str_is_scratch(s)) return scr_str_retain(s); + return scr_str_from_span_ascii(s->data, s->len); +} + double scr_str_slice_char_code_at(ScrStr *s, const ScrStringSlice *range, double index) { double unit = scr_to_integer_or_infinity(index); if (!(unit >= 0) || unit >= (double)range->length) return NAN; diff --git a/packages/runtime/src/scr_string.h b/packages/runtime/src/scr_string.h index e4bf099958..425dfaabfe 100644 --- a/packages/runtime/src/scr_string.h +++ b/packages/runtime/src/scr_string.h @@ -184,6 +184,14 @@ static inline ScrStr *scr_str_retain(ScrStr *s) { void scr_str_release(ScrStr *s); /* NULL-tolerant (uninitialized locals) */ +/* A transient stack copy of a slice (scr_str_slice_scratch): immortal for + * retain and release, and valid only while its borrowing consumer runs. No + * other string has a capacity below its length. Containers that keep a + * string they were lent store scr_str_scratch_keep's copy instead. */ +static inline bool scr_str_is_scratch(const ScrStr *s) { + return s->rc == SIZE_MAX && s->cap < s->len; +} + /* ── lone surrogates: canonical WTF-8 storage ───────────────────────── * String storage is UTF-8 extended to lone UTF-16 surrogates: an unpaired * surrogate is its generalized 3-byte encoding ED A0..BF 80..BF, while a diff --git a/packages/runtime/src/scr_string_methods.h b/packages/runtime/src/scr_string_methods.h index 5377475306..62354142e2 100644 --- a/packages/runtime/src/scr_string_methods.h +++ b/packages/runtime/src/scr_string_methods.h @@ -83,6 +83,16 @@ typedef struct ScrStringSlice { void scr_str_slice_range(ScrStr *s, double start, double end, bool substring, ScrStringSlice *out); double scr_str_slice_char_code_at(ScrStr *s, const ScrStringSlice *range, double index); +/* A slice that only borrowing consumers read (string-slices.ts) is written + * into the caller's `bytes`-sized stack buffer as an immortal ASCII string, + * whose header marks it as a scratch copy (scr_str_is_scratch). Results + * that do not fit, are not ASCII, or are interned statics are returned as + * the ordinary +1 slice. Releasing either result is correct. */ +ScrStr *scr_str_slice_scratch(ScrStr *s, double start, double end, bool substring, + ScrStr *scratch, size_t bytes); +/* The owning reference a container stores for `s`: a heap copy of a scratch + * string, else `s` retained. */ +ScrStr *scr_str_scratch_keep(ScrStr *s); /* repeat(count): count < 0 or Infinity is a JS RangeError; scriptc has no * exceptions, so it prints "scriptc: RangeError: Invalid count value" to diff --git a/packages/runtime/src/scr_test.c b/packages/runtime/src/scr_test.c index 9e6da5939f..cc52513a61 100644 --- a/packages/runtime/src/scr_test.c +++ b/packages/runtime/src/scr_test.c @@ -80,6 +80,7 @@ enum { typedef struct { ScrClosure *cb; int flags; + ScrStr *at; /* "file:line:col" of the hook call, owned; NULL for t.* hooks */ } ScrTestHook; typedef struct { @@ -122,6 +123,8 @@ struct ScrTestCtx { ScrPromise *done; /* owned; settles when a concurrent subtest finishes */ ScrPromise *slot; /* owned; the parent waiting for its next subtest to finish */ struct ScrMockTimers *mock_timers; /* t.mock.timers, made on first use; freed at exit */ + bool passed; /* t.passed: set when the body passes, as Node's pass() does */ + bool listed; /* a suite listed in the failing section (a failing root before) */ }; static SCR_TL ScrTestCtx *scr_test_root = NULL; @@ -135,6 +138,18 @@ static SCR_TL long scr_test_ct_tests = 0, scr_test_ct_suites = 0, scr_test_ct_pa scr_test_ct_todo = 0; static SCR_TL double scr_test_t0 = 0; static SCR_TL bool scr_test_suite_hook_failed = false; +/* The promises of async module-level before hooks, which the runner waits + * for before the first top-level test. */ +static SCR_TL ScrPromise **scr_test_root_pending = NULL; +static SCR_TL size_t scr_test_root_npending = 0, scr_test_root_pending_cap = 0; + +/* A module-level before hook threw or rejected: the first failure is kept + * for the top-level tests and suites, later ones are dropped. */ +static void scr_test_root_before_failed(ScrTestCtx *root) { + if (!scr_exc_pending()) return; + if (!root->before_err) root->before_err = scr_exc_take(); + else scr_exc_clear(); +} /* Above 0 while subtests run concurrently: result lines are held, and the * outermost concurrent parent prints its subtrees in declaration order, * as Node's reporter does, once they all finish. */ @@ -163,7 +178,10 @@ ScrTestCtx *scr_testctx_retain(ScrTestCtx *t) { } static void scr_test_hooks_free(ScrTestHooks *h) { - for (size_t i = 0; i < h->n; i++) scr_closure_release(h->v[i].cb); + for (size_t i = 0; i < h->n; i++) { + scr_closure_release(h->v[i].cb); + scr_str_release(h->v[i].at); + } free(h->v); h->v = NULL; h->n = h->cap = 0; @@ -276,7 +294,9 @@ static void scr_test_indent(int depth) { /* "(1.234567ms)" — durations print microsecond-rounded (Node's hrtime * granularity), not with double noise. */ +/* NaN prints nothing: a test cancelled before it started has no duration. */ static void scr_test_print_dur(double ms) { + if (isnan(ms)) return; char num[32]; fputs(" (", stdout); size_t n = scr_f64_to_str(round(ms * 1e6) / 1e6, num); @@ -405,10 +425,17 @@ static void scr_test_mocks_restore(ScrTestHooks *mocks, bool forget) { /* Run `which` hooks up (beforeEach: outermost suite first) or down * (afterEach: innermost first) the ancestor chain. A hook throw fails the * test with the hook's error (Node cancels harder — SEMANTICS.md). */ -static void scr_test_run_hooks_of(ScrTestCtx *owner, int which) { - for (size_t i = 0; i < owner->hooks[which].n && !scr_exc_pending(); i++) { - scr_test_call(owner->hooks[which].v[i].cb, owner->hooks[which].v[i].flags, NULL, NULL); +/* `ctx` is the context a hook that takes one receives: the suite or test + * for its own before/after hooks, the test for the beforeEach/afterEach + * chain. */ +static ScrTestHook *scr_test_run_hooks_of(ScrTestCtx *owner, int which, ScrTestCtx *ctx) { + if (scr_exc_pending()) return NULL; /* an earlier hook in the chain threw */ + for (size_t i = 0; i < owner->hooks[which].n; i++) { + ScrTestHook *h = &owner->hooks[which].v[i]; + scr_test_call(h->cb, h->flags, ctx, NULL); + if (scr_exc_pending()) return h; /* the hook that threw */ } + return NULL; } /* Returns whether the chain has any `which` hooks. */ @@ -421,9 +448,9 @@ static bool scr_test_run_each_hooks(ScrTestCtx *t, int which, bool outermost_fir if (p->hooks[which].n > 0) any = true; } if (outermost_first) { - for (int i = n - 1; i >= 0; i--) scr_test_run_hooks_of(chain[i], which); + for (int i = n - 1; i >= 0; i--) scr_test_run_hooks_of(chain[i], which, t); } else { - for (int i = 0; i < n; i++) scr_test_run_hooks_of(chain[i], which); + for (int i = 0; i < n; i++) scr_test_run_hooks_of(chain[i], which, t); } return any; } @@ -555,6 +582,36 @@ static void scr_test_finish_concurrent(ScrTestCtx *t) { for (size_t i = 0; i < t->nchildren; i++) scr_test_report_tree(t->children[i]); } +/* A failing root before hook (Node): each top-level test fails with its + * error without running its body or beforeEach hooks, though its + * afterEach hooks run; a top-level suite fails with it, and the tests and + * suites in it are cancelled before they start. */ +static void scr_test_cancel_unrun(ScrTestCtx *c) { + for (size_t i = 0; i < c->nchildren; i++) scr_test_cancel_unrun(c->children[i]); + static const char msg[] = "test did not finish before its parent and was cancelled"; + scr_throw_str(scr_str_new(msg, sizeof msg - 1)); + c->err = scr_exc_take(); + c->listed = true; + c->dur_ms = NAN; + c->state = SCR_TS_CANCELLED; + scr_test_report_result(c); +} + +static void scr_test_fail_unrun(ScrTestCtx *c, ScrCaught *err) { + double start = scr_now_ms(); + c->err = scr_caught_retain(err); + c->listed = true; + if (c->is_suite) { + for (size_t i = 0; i < c->nchildren; i++) scr_test_cancel_unrun(c->children[i]); + } else { + scr_test_run_each_hooks(c, SCR_HOOK_AFTER_EACH, false); + if (scr_exc_pending()) scr_exc_clear(); + } + c->dur_ms = scr_now_ms() - start; + c->state = SCR_TS_FAIL; + scr_test_report_result(c); +} + static void scr_test_run_children(ScrTestCtx *t) { /* `only` at this level: when a child is marked, or contains a marked * test or suite, only those children run. The others are dropped entirely @@ -568,6 +625,10 @@ static void scr_test_run_children(ScrTestCtx *t) { /* Subtests t.test already ran INLINE at their call site, or started on * their own fiber. */ if (c->state != SCR_TS_PENDING || c->done) continue; + if (t == scr_test_root && t->before_err) { + scr_test_fail_unrun(c, t->before_err); + continue; + } if (t->concurrency > 1) scr_test_start(c); else scr_test_run_node(c); } @@ -586,6 +647,7 @@ static void scr_test_run_node(ScrTestCtx *t) { if (t->mode == SCR_TEST_SKIP) { t->dur_ms = scr_now_ms() - start; t->state = SCR_TS_SKIPPED; + t->passed = true; scr_test_report_result(t); return; } @@ -593,7 +655,7 @@ static void scr_test_run_node(ScrTestCtx *t) { /* The body already ran at registration (children collected). A hook * throw fails the SUITE with that error — before-failures skip the * children (Node cancels them; SEMANTICS.md numbers the difference). */ - scr_test_run_hooks_of(t, SCR_HOOK_BEFORE); + scr_test_run_hooks_of(t, SCR_HOOK_BEFORE, t); if (scr_exc_pending()) { t->err = scr_exc_take(); } else { @@ -604,7 +666,7 @@ static void scr_test_run_node(ScrTestCtx *t) { } else { scr_test_run_children(t); } - scr_test_run_hooks_of(t, SCR_HOOK_AFTER); + scr_test_run_hooks_of(t, SCR_HOOK_AFTER, t); if (scr_exc_pending()) t->err = scr_exc_take(); } t->dur_ms = scr_now_ms() - start; @@ -617,6 +679,8 @@ static void scr_test_run_node(ScrTestCtx *t) { : scr_test_subtree_failed(t) ? SCR_TS_CHILDFAIL : SCR_TS_PASS; } + /* Node's suite pass() comes after its after hooks. */ + t->passed = t->state == SCR_TS_PASS || t->state == SCR_TS_TODO_PASS; scr_test_report_result(t); return; } @@ -664,6 +728,8 @@ static void scr_test_run_node(ScrTestCtx *t) { t->err = scr_exc_take(); failed = true; } + /* Node's pass()/fail(): afterEach hooks already see the outcome. */ + t->passed = !failed; scr_test_run_each_hooks(t, SCR_HOOK_AFTER_EACH, false); if (scr_exc_pending()) { if (!failed && !t->err) { @@ -674,7 +740,7 @@ static void scr_test_run_node(ScrTestCtx *t) { } } /* t.after hooks run once the test and its afterEach chain finish. */ - scr_test_run_hooks_of(t, SCR_HOOK_AFTER); + scr_test_run_hooks_of(t, SCR_HOOK_AFTER, t); if (scr_exc_pending()) { if (!failed && !t->err) { t->err = scr_exc_take(); @@ -686,6 +752,10 @@ static void scr_test_run_node(ScrTestCtx *t) { /* Then t.mock is reset: its mocks are restored and its timers reset. */ scr_test_mocks_restore(&t->mocks, true); scr_mt_reset(t->mock_timers); + /* Its t.before/t.after/t.beforeEach/t.afterEach hooks have all run, and + * nothing runs them again: dropping them releases closures that capture + * the context, which would otherwise keep it alive in a cycle. */ + for (int k = 0; k < 4; k++) scr_test_hooks_free(&t->hooks[k]); scr_test_parent_set(prev_parent); t->dur_ms = scr_now_ms() - start; if (t->mode == SCR_TEST_SKIP) { @@ -800,7 +870,7 @@ static void scr_test_print_failures(ScrTestCtx *t, bool *any) { for (size_t i = 0; i < t->nchildren; i++) { ScrTestCtx *c = t->children[i]; scr_test_print_failures(c, any); - if (!c->is_suite && + if ((!c->is_suite || c->listed) && (c->state == SCR_TS_FAIL || c->state == SCR_TS_TODO_FAIL || c->state == SCR_TS_CANCELLED) && c->err) { @@ -1391,7 +1461,41 @@ static void scr_test_runner_entry(ScrFiber *self, void *argpack) { * the first test only after the file's synchronous body completes. */ scr_await_hop(); ScrTestCtx *root = scr_test_root; + /* The root's before hooks ran at their before() calls; the first test + * waits for the async ones. */ + for (size_t i = 0; i < scr_test_root_npending; i++) { + scr_await_void(scr_test_root_pending[i]); + scr_test_root_before_failed(root); + scr_promise_release(scr_test_root_pending[i]); + } + free(scr_test_root_pending); + scr_test_root_pending = NULL; + scr_test_root_npending = scr_test_root_pending_cap = 0; scr_test_run_children(root); + /* Its after hooks: a throw reports the root as "✖ " with the hook's + * location, after the top-level results, without counting it or changing + * the exit code, as Node does. */ + double after_start = scr_now_ms(); + ScrTestHook *after_failed = scr_test_run_hooks_of(root, SCR_HOOK_AFTER, root); + if (scr_exc_pending()) { + root->err = scr_exc_take(); + root->dur_ms = scr_now_ms() - after_start; + root->state = SCR_TS_FAIL; + if (after_failed && after_failed->at) { + root->at = scr_str_retain(after_failed->at); + /* The name is the file: the location without its ":line:col". */ + size_t end = root->at->len; + for (int colons = 0; end > 0 && colons < 2; end--) { + if (root->at->data[end - 1] == ':') colons++; + } + scr_str_release(root->name); + root->name = scr_str_new(root->at->data, end); + } + fputs("\xE2\x9C\x96 ", stdout); + scr_test_print_str(root->name); + scr_test_print_dur(root->dur_ms); + fputc('\n', stdout); + } scr_test_run_done = true; scr_snap_write_all(); scr_test_count(root); @@ -1405,6 +1509,10 @@ static void scr_test_runner_entry(ScrFiber *self, void *argpack) { scr_test_info_line("duration_ms", round((scr_now_ms() - scr_test_t0) * 1e6) / 1e6); bool any = false; scr_test_print_failures(root, &any); + if (root->err) { + if (!any) fputs("\n\xE2\x9C\x96 failing tests:\n", stdout); + scr_test_print_failure(root); + } /* The tree is done reporting: release it (test bodies may still hold * their TestContext refs — the boxes stay alive through those). */ scr_test_reg_parent = NULL; @@ -1511,13 +1619,51 @@ void scr_test_suite(ScrStr *name /*borrowed*/, double mode, ScrStr *msg /*borrow if (cb && (int)mode != SCR_TEST_SKIP) { ScrTestCtx *prev = scr_test_parent_get(); scr_test_parent_set(t); - ((void (*)(ScrClosure *))cb->fn)(cb); + /* flag 2: the body takes its SuiteContext (+1, as test bodies do). */ + if ((int)flags & SCR_TEST_F_CTX) + ((void (*)(ScrClosure *, ScrTestCtx *))cb->fn)(cb, scr_testctx_retain(t)); + else + ((void (*)(ScrClosure *))cb->fn)(cb); scr_test_parent_set(prev); } scr_closure_release(cb); } -void scr_test_hook(double which, ScrClosure *cb /*moves*/, double flags) { +/* A module-level before hook runs at its before() call, as Node runs the + * hooks of its already-started root at once: the synchronous part runs + * now, on the caller's stack, and the runner waits for an async hook's + * promise before the first top-level test. The first failure fails the + * top-level tests and suites (scr_test_fail_unrun). */ +static void scr_test_root_before_now(ScrTestCtx *root, ScrClosure *cb, int flags) { + bool takes_ctx = (flags & SCR_TEST_F_CTX) != 0; + if (flags & SCR_TEST_F_ASYNC) { + ScrPromise *p = + takes_ctx + ? ((ScrPromise * (*)(ScrClosure *, ScrTestCtx *)) cb->fn)(cb, scr_testctx_retain(root)) + : ((ScrPromise * (*)(ScrClosure *)) cb->fn)(cb); + if (p && !scr_exc_pending()) { + if (scr_test_root_npending == scr_test_root_pending_cap) { + scr_test_root_pending_cap = scr_test_root_pending_cap ? scr_test_root_pending_cap * 2 : 2; + scr_test_root_pending = realloc(scr_test_root_pending, + scr_test_root_pending_cap * sizeof *scr_test_root_pending); + if (!scr_test_root_pending) scr_test_oom(); + } + /* The runner observes it: a rejection is the hook's failure, not an + * unhandled rejection. */ + scr_promise_mark_handled(p); + scr_test_root_pending[scr_test_root_npending++] = p; + return; + } + scr_promise_release(p); + } else if (takes_ctx) { + ((void (*)(ScrClosure *, ScrTestCtx *))cb->fn)(cb, scr_testctx_retain(root)); + } else { + ((void (*)(ScrClosure *))cb->fn)(cb); + } + scr_test_root_before_failed(root); +} + +void scr_test_hook(double which, ScrClosure *cb /*moves*/, double flags, ScrStr *at) { scr_test_ensure_root(); ScrTestCtx *owner = scr_test_parent_get(); ScrTestHooks *h = &owner->hooks[(int)which]; @@ -1528,7 +1674,10 @@ void scr_test_hook(double which, ScrClosure *cb /*moves*/, double flags) { } h->v[h->n].cb = cb; h->v[h->n].flags = (int)flags; + h->v[h->n].at = at && at->len > 0 ? scr_str_retain(at) : NULL; h->n++; + if ((int)which == SCR_HOOK_BEFORE && owner == scr_test_root) + scr_test_root_before_now(owner, cb, (int)flags); } /* t.test(...): registers under `t` and — because subtests run inline on @@ -1589,7 +1738,7 @@ void scr_test_ctx_todo(ScrTestCtx *t, ScrStr *msg /*borrowed*/) { * each descendant subtest, and after runs once the test finishes. */ void scr_test_ctx_hook(ScrTestCtx *t, double which, ScrClosure *cb /*moves*/, double flags) { if ((int)which == SCR_HOOK_BEFORE) { - scr_test_call(cb, (int)flags, NULL, NULL); + scr_test_call(cb, (int)flags, t, NULL); scr_closure_release(cb); if (scr_exc_pending() && !t->before_err) t->before_err = scr_exc_take(); else if (scr_exc_pending()) scr_exc_clear(); @@ -1603,6 +1752,7 @@ void scr_test_ctx_hook(ScrTestCtx *t, double which, ScrClosure *cb /*moves*/, do } h->v[h->n].cb = cb; h->v[h->n].flags = (int)flags; + h->v[h->n].at = NULL; h->n++; } @@ -1614,6 +1764,7 @@ static void scr_test_mocks_add(ScrTestHooks *h, ScrClosure *restore /*moves*/) { } h->v[h->n].cb = restore; h->v[h->n].flags = 0; + h->v[h->n].at = NULL; h->n++; } @@ -2155,11 +2306,25 @@ void scr_test_ctx_diagnostic(ScrTestCtx *t, ScrStr *msg /*borrowed*/) { } ScrStr *scr_test_ctx_name(ScrTestCtx *t) { + if (!t->name && !t->parent) return scr_str_new("", 6); /* Node's root test */ return t->name ? scr_str_retain(t->name) : scr_str_new("", 0); } /* t.fullName: the names from the outermost suite or test down to this one, * joined with " > ". */ +bool scr_test_ctx_passed(ScrTestCtx *t) { return t->passed; } + +/* t.workerId: Number(process.env.NODE_TEST_WORKER_ID) || undefined, with 0 + * standing for undefined. */ +double scr_test_worker_id(void) { + const char *env = getenv("NODE_TEST_WORKER_ID"); + if (!env) return 0; + ScrStr *s = scr_str_new(env, strlen(env)); + double n = scr_string_to_number(s); + scr_str_release(s); + return isnan(n) ? 0 : n; +} + ScrStr *scr_test_ctx_full_name(ScrTestCtx *t) { const ScrTestCtx *chain[64]; int n = 0; diff --git a/packages/runtime/src/scr_test.h b/packages/runtime/src/scr_test.h index b1078cccd6..64a280bf7b 100644 --- a/packages/runtime/src/scr_test.h +++ b/packages/runtime/src/scr_test.h @@ -33,8 +33,10 @@ void scr_test_register(ScrStr *name, double mode, ScrStr *msg, ScrClosure *cb, d void scr_test_suite(ScrStr *name, double mode, ScrStr *msg, ScrClosure *cb, ScrStr *at, double flags, double concurrency); /* before/after/beforeEach/afterEach on the CURRENT registration parent - * (`which`: 0 before, 1 after, 2 beforeEach, 3 afterEach). */ -void scr_test_hook(double which, ScrClosure *cb, double flags); + * (`which`: 0 before, 1 after, 2 beforeEach, 3 afterEach); `at` is the + * hook call's "file:line:col". The root's before and after hooks run + * around the top-level tests. */ +void scr_test_hook(double which, ScrClosure *cb, double flags, ScrStr *at); /* t.test(...): registers under `t`, runs the subtest INLINE on the * runner fiber, and returns the settled promise the `await` consumes (+1). */ ScrPromise *scr_test_sub(ScrTestCtx *t, ScrStr *name, double mode, ScrStr *msg, ScrClosure *cb, double flags, ScrStr *at, @@ -46,6 +48,9 @@ void scr_test_ctx_hook(ScrTestCtx *t, double which, ScrClosure *cb, double flags void scr_test_ctx_plan(ScrTestCtx *t, double count); void scr_test_ctx_count(ScrTestCtx *t); ScrStr *scr_test_ctx_full_name(ScrTestCtx *t); +/* t.passed, and t.workerId as a number with 0 for undefined. */ +bool scr_test_ctx_passed(ScrTestCtx *t); +double scr_test_worker_id(void); ScrStr *scr_test_ctx_name(ScrTestCtx *t); /* +1 */ /* Mock trackers: the module's `mock` and each test's t.mock. track keeps * the closure that restores one mock; restore_all calls them in creation diff --git a/packages/runtime/src/scr_zlib.h b/packages/runtime/src/scr_zlib.h index 3ac07d8441..124e157abf 100644 --- a/packages/runtime/src/scr_zlib.h +++ b/packages/runtime/src/scr_zlib.h @@ -90,6 +90,9 @@ ScrPromise *scr_zlib_codec_promise_dyn(ScrDyn *data, double mode, ScrDyn *opts); * with Node's ladder. The callbacks MOVE in and may be NULL. */ ScrStream *scr_zlib_stream_new(double mode, ScrDyn *opts); double scr_zlib_stream_bytes_written(ScrStream *s); +/* `s instanceof zlib.Gzip` (and the other classes): true when s is a zlib + * stream built with that mode. s is borrowed. */ +bool scr_zlib_stream_is(ScrStream *s, double mode); void scr_zlib_stream_flush(ScrStream *s, ScrDyn *kind, ScrClosure *cb); void scr_zlib_stream_params(ScrStream *s, ScrDyn *level, ScrDyn *strategy, ScrClosure *cb); void scr_zlib_stream_close(ScrStream *s, ScrClosure *cb); diff --git a/packages/runtime/src/scr_zlib_stream.c b/packages/runtime/src/scr_zlib_stream.c index 55dcd6302a..14d8747e83 100644 --- a/packages/runtime/src/scr_zlib_stream.c +++ b/packages/runtime/src/scr_zlib_stream.c @@ -496,6 +496,11 @@ static ScrZlibStream *scr_zs_from_stream(ScrStream *s) { return zs; } +bool scr_zlib_stream_is(ScrStream *s, double mode) { + const ScrZlibStream *zs = scr_stream_native(s, &scr_zs_release_v); + return zs != NULL && zs->mode == (int)mode; +} + double scr_zlib_stream_bytes_written(ScrStream *s) { ScrZlibStream *zs = scr_zs_from_stream(s); return zs ? zs->bytes_written : 0; diff --git a/packages/runtime/test/test_map.c b/packages/runtime/test/test_map.c index 6eee2d6bb4..8899974a83 100644 --- a/packages/runtime/test/test_map.c +++ b/packages/runtime/test/test_map.c @@ -284,6 +284,36 @@ static void test_span_keys(void) { scr_map_release(numbers); } +/* A scratch slice probes like any key; an insertion stores a heap copy, so + * the stored key survives the buffer's next contents. */ +static void test_scratch_keys(void) { + ScrMap *m = scr_map_new(SCR_MAP_KEY_STR, SCR_MAP_VAL_F64, NULL); + ScrStr *line = S("alpha beta alpha"); + _Alignas(16) unsigned char buffer[96]; + ScrStr *alpha = scr_str_slice_scratch(line, 0, 5, false, (ScrStr *)buffer, sizeof buffer); + check(alpha == (ScrStr *)buffer && scr_str_is_scratch(alpha), "short ASCII slice uses the buffer"); + scr_map_set_str_f64(m, alpha, 1); + double out = 0; + check(scr_map_get_str_f64(m, alpha, &out) && out == 1, "scratch key probes its entry"); + ScrStr *beta = scr_str_slice_scratch(line, 6, 10, false, (ScrStr *)buffer, sizeof buffer); + check(!scr_map_has_str(m, beta), "refilled buffer probes its new text"); + scr_map_set_str_f64(m, beta, 2); + ScrStr *again = scr_str_slice_scratch(line, 11, 16, false, (ScrStr *)buffer, sizeof buffer); + scr_map_set_str_f64(m, again, 3); + check(scr_map_size(m) == 2, "scratch overwrite keeps one entry"); + ScrStr *first = scr_map_iter_key_str(m, 0), *second = scr_map_iter_key_str(m, 1); + ScrStr *a = S("alpha"), *b = S("beta"); + check(!scr_str_is_scratch(first) && scr_str_eq(first, a), "stored key is an owned copy"); + check(!scr_str_is_scratch(second) && scr_str_eq(second, b), "second stored key is a copy"); + check(scr_map_get_str_f64(m, a, &out) && out == 3, "overwrite through scratch key"); + scr_str_release(a); + scr_str_release(b); + scr_str_release(first); + scr_str_release(second); + scr_str_release(line); + scr_map_release(m); +} + static void test_same_value_zero(void) { ScrMap *m = scr_map_new(SCR_MAP_KEY_F64, SCR_MAP_VAL_BOOL, NULL); scr_map_set_f64_bool(m, 0.0 / 0.0, true); /* NaN key */ @@ -793,6 +823,7 @@ int main(void) { test_string_entries(); test_same_value_zero(); test_span_keys(); + test_scratch_keys(); test_string_hash_cache(); #ifdef SCR_RC_AUDIT test_rc_accounting(); diff --git a/packages/runtime/test/test_string.c b/packages/runtime/test/test_string.c index 55c49af7e7..e49d8eb8b4 100644 --- a/packages/runtime/test/test_string.c +++ b/packages/runtime/test/test_string.c @@ -205,6 +205,38 @@ static void surrogate_asserts(void) { } } } + /* Scratch slices hold the materialized text. Only ASCII results of at + * least two units use the caller's buffer, marked as scratch. */ + { + ScrStr *sources[] = {two, s, scr_str_new("GET /api/users 200", 18)}; + const double marks[] = {-INFINITY, -7.5, -3, -0.5, -0.0, 0, 0.5, 1, + 2, 3.9, 5, 9, 40, INFINITY, NAN}; + const size_t nmarks = sizeof marks / sizeof marks[0]; + _Alignas(16) unsigned char buffer[96]; + for (size_t k = 0; k < 3; k++) { + for (size_t a = 0; a < nmarks; a++) { + for (size_t b = 0; b < nmarks; b++) { + for (unsigned substring = 0; substring < 2; substring++) { + ScrStr *src = sources[k]; + ScrStr *scratch = scr_str_slice_scratch(src, marks[a], marks[b], substring != 0, + (ScrStr *)buffer, sizeof buffer); + ScrStr *copy = substring ? scr_str_substring(src, marks[a], marks[b]) + : scr_str_slice(src, marks[a], marks[b]); + bool used = scratch == (ScrStr *)buffer; + bool expected = copy->len >= 2 && scr_bytes_all_ascii(copy->data, copy->len); + total++; + if (scratch->len != copy->len || memcmp(scratch->data, copy->data, copy->len) != 0 || + used != expected || used != scr_str_is_scratch(scratch) || + scr_str_utf16_len(scratch) != scr_str_utf16_len(copy)) + failed++; + scr_str_release(scratch); + scr_str_release(copy); + } + } + } + } + scr_str_release(sources[2]); + } scr_str_release(two); scr_str_release(s); } diff --git a/scripts/zero-cost-compare.mjs b/scripts/zero-cost-compare.mjs new file mode 100644 index 0000000000..a89fe46500 --- /dev/null +++ b/scripts/zero-cost-compare.mjs @@ -0,0 +1,490 @@ +/* Comparison logic for scripts/zero-cost-gate.mjs, kept free of process + * orchestration so tests/harness/zero-cost-gate.test.ts can pin it. + * + * Artifacts are compared as bytes first. Only when they differ are they split + * into named units (IR functions, LLVM functions, executable symbols) to name + * the first unit that differs. The self-hosted compiler's IR and LLVM exceed + * V8's maximum string length, so splitting works on Buffers and hashes each + * unit instead of decoding the artifact. */ +import { createHash } from "node:crypto"; + +const hash = (bytes) => createHash("sha1").update(bytes).digest("hex"); + +/** Named units of an artifact: `order` lists unit keys in artifact order, + * `hashes` maps each key to a content hash, and `module` holds the + * module-level remainder (IR header, LLVM globals and declarations) as a list + * of comparable pieces with their labels. Repeated names get `#n` suffixes. */ +function unitsBuilder() { + const order = []; + const hashes = new Map(); + const seen = new Map(); + return { + order, + hashes, + module: [], + add(name, bytes) { + const count = seen.get(name) ?? 0; + seen.set(name, count + 1); + const key = count === 0 ? name : `${name}#${count + 1}`; + order.push(key); + hashes.set(key, hash(bytes)); + }, + }; +} + +/* ── IR (compact or readable JSON) ─────────────────────────────────────── */ + +function skipString(bytes, index) { + for (index++; index < bytes.length; index++) { + if (bytes[index] === 0x5c) index++; + else if (bytes[index] === 0x22) return index + 1; + } + return bytes.length; +} + +/** Byte ranges of the root object's `"functions"` array elements, or null. */ +export function irFunctionSpans(bytes) { + const key = Buffer.from('"functions"'); + let depth = 0; + for (let index = 0; index < bytes.length;) { + const byte = bytes[index]; + if (byte === 0x22) { + const end = skipString(bytes, index); + if (depth === 1 && end - index === key.length && bytes.subarray(index, end).equals(key)) { + let next = end; + while (next < bytes.length && bytes[next] !== 0x3a) next++; + next++; + while (next < bytes.length && bytes[next] !== 0x5b && bytes[next] <= 0x20) next++; + if (bytes[next] === 0x5b) return elementSpans(bytes, next); + } + index = end; + continue; + } + if (byte === 0x7b || byte === 0x5b) depth++; + else if (byte === 0x7d || byte === 0x5d) depth--; + index++; + } + return null; +} + +function elementSpans(bytes, open) { + const spans = []; + let depth = 0; + let start = -1; + for (let index = open + 1; index < bytes.length;) { + const byte = bytes[index]; + if (byte === 0x22) { + if (start < 0) start = index; + index = skipString(bytes, index); + continue; + } + if (byte === 0x7b || byte === 0x5b) { + if (start < 0) start = index; + depth++; + } else if (byte === 0x7d || byte === 0x5d) { + if (depth === 0) { + if (start >= 0) spans.push([start, trimEnd(bytes, index)]); + return { open, close: index, spans }; + } + depth--; + } else if (byte === 0x2c && depth === 0) { + spans.push([start, trimEnd(bytes, index)]); + start = -1; + } else if (start < 0 && byte > 0x20) start = index; + index++; + } + return null; +} + +function trimEnd(bytes, end) { + while (end > 0 && bytes[end - 1] <= 0x20) end--; + return end; +} + +/** The `name` of one serialized IR function. Functions serialize `name` + * first; anything else is parsed (a single function is small). */ +function irFunctionName(bytes, [start, end]) { + const head = bytes.toString("utf8", start, Math.min(end, start + 4096)); + const match = /^\{\s*"name"\s*:\s*("(?:[^"\\]|\\.)*")/.exec(head); + if (match) return JSON.parse(match[1]); + const fn = JSON.parse(bytes.toString("utf8", start, end)); + return typeof fn?.name === "string" ? fn.name : ""; +} + +/** IR units: one per function, plus the module header split by top-level + * key (and, for arrays, by element) so header differences are named too. */ +export function irUnits(bytes) { + const units = unitsBuilder(); + const located = irFunctionSpans(bytes); + let header; + if (located === null) header = JSON.parse(bytes.toString("utf8")); + else { + for (const span of located.spans) + units.add(irFunctionName(bytes, span), bytes.subarray(span[0], span[1])); + header = JSON.parse( + bytes.toString("utf8", 0, located.open + 1) + bytes.toString("utf8", located.close), + ); + } + for (const [key, value] of Object.entries(header)) { + if (key === "functions" && located !== null) continue; + if (Array.isArray(value)) + value.forEach((item, index) => { + const name = item && typeof item === "object" && "name" in item ? `:${item.name}` : ""; + units.module.push({ label: `${key}[${index}${name}]`, text: JSON.stringify(item) }); + }); + else units.module.push({ label: key, text: JSON.stringify(value) }); + } + return units; +} + +/* ── LLVM text ─────────────────────────────────────────────────────────── */ + +const DEFINE_NAME = /@("(?:[^"\\]|\\.)*"|[-\w$.]+)\s*\(/; + +/** LLVM units: one per `define … { … }` body; every other line is + * module-level text compared line by line. */ +export function llvmUnits(bytes) { + const units = unitsBuilder(); + let fnStart = -1; + let fnName = ""; + let lineNumber = 0; + for (let start = 0; start < bytes.length;) { + let end = bytes.indexOf(10, start); + if (end < 0) end = bytes.length; + lineNumber++; + if (fnStart >= 0) { + if (end - start === 1 && bytes[start] === 0x7d) { + units.add(fnName, bytes.subarray(fnStart, end)); + fnStart = -1; + } + } else if (bytes[start] === 0x64 && bytes.toString("latin1", start, start + 7) === "define ") { + const line = bytes.toString("utf8", start, end); + fnName = DEFINE_NAME.exec(line)?.[1] ?? ``; + fnStart = start; + if (line.endsWith("}")) { + units.add(fnName, bytes.subarray(fnStart, end)); + fnStart = -1; + } + } else if (end > start) { + units.module.push({ label: `line ${lineNumber}`, text: bytes.toString("utf8", start, end) }); + } + start = end + 1; + } + return units; +} + +/* ── unit comparison ───────────────────────────────────────────────────── */ + +const clip = (text, width = 160) => (text.length > width ? `${text.slice(0, width)}…` : text); + +/** Compare two unit sets. `first` names the first difference in baseline + * order: a changed or removed function, then an added one, then the first + * differing module-level piece. */ +export function compareUnits(baseline, candidate) { + const changed = []; + const removed = []; + for (const key of baseline.order) { + const other = candidate.hashes.get(key); + if (other === undefined) removed.push(key); + else if (other !== baseline.hashes.get(key)) changed.push(key); + } + const added = candidate.order.filter((key) => !baseline.hashes.has(key)); + let module = null; + const count = Math.max(baseline.module.length, candidate.module.length); + for (let index = 0; index < count && module === null; index++) { + const a = baseline.module[index]; + const b = candidate.module[index]; + if (a?.text !== b?.text) + module = { + label: (a ?? b).label, + baseline: a === undefined ? null : clip(a.text), + candidate: b === undefined ? null : clip(b.text), + }; + } + const firstFunction = baseline.order.find( + (key) => !candidate.hashes.has(key) || candidate.hashes.get(key) !== baseline.hashes.get(key), + ); + let first = null; + if (firstFunction !== undefined) + first = `${changed.includes(firstFunction) ? "changed" : "removed"} ${firstFunction}`; + else if (added.length > 0) first = `added ${added[0]}`; + else if (module !== null) first = `module ${module.label}`; + else if (baseline.order.join("\n") !== candidate.order.join("\n")) first = "function order"; + return { + identical: first === null, + first, + changed, + added, + removed, + module, + functions: { baseline: baseline.order.length, candidate: candidate.order.length }, + }; +} + +/** Byte-compare an artifact and, when it differs, name what changed. */ +export function compareArtifact(kind, baseline, candidate) { + if (baseline.equals(candidate)) return { identical: true }; + const split = kind === "ir" ? irUnits : llvmUnits; + const result = compareUnits(split(baseline), split(candidate)); + // Equal units with unequal bytes means formatting outside the units. + return { ...result, identical: false, first: result.first ?? "bytes outside named units" }; +} + +/* ── executables ───────────────────────────────────────────────────────── */ + +/** The output of `nm` for `binary`, from its spawnSync result. Throws when + * nm could not start, was killed by a signal, or exited with a non-zero + * status, because the symbol checks would otherwise read an empty list. */ +export function nmOutput(binary, result) { + if (result.error) throw new Error(`nm ${binary}: ${result.error.message}`); + if (result.signal) throw new Error(`nm ${binary}: killed by ${result.signal}`); + if (result.status !== 0) { + const detail = typeof result.stderr === "string" ? result.stderr.trim() : ""; + throw new Error( + `nm ${binary}: exited with status ${result.status}${detail ? `: ${detail}` : ""}`, + ); + } + return result.stdout; +} + +/** Defined symbols from `nm -S` output (GNU/LLVM nm, address size type + * name) or `nm` output without sizes (Apple, address type name). */ +export function parseNm(text) { + const symbols = []; + for (const line of text.split("\n")) { + const fields = line.trim().split(/\s+/); + if (fields.length === 4 && /^[0-9a-f]+$/i.test(fields[1])) + symbols.push({ + address: BigInt(`0x${fields[0]}`), + size: BigInt(`0x${fields[1]}`), + type: fields[2], + name: fields[3], + }); + else if (fields.length === 3 && /^[0-9a-f]+$/i.test(fields[0])) + symbols.push({ + address: BigInt(`0x${fields[0]}`), + size: null, + type: fields[1], + name: fields[2], + }); + } + return symbols; +} + +/** The virtual address of file offset `offset` in an ELF64 little-endian + * executable, from its PT_LOAD program headers, or null. */ +export function elfOffsetToAddress(bytes, offset) { + if (bytes.length < 64 || bytes.readUInt32BE(0) !== 0x7f454c46 || bytes[4] !== 2) return null; + const phoff = Number(bytes.readBigUInt64LE(0x20)); + const phentsize = bytes.readUInt16LE(0x36); + const phnum = bytes.readUInt16LE(0x38); + for (let index = 0; index < phnum; index++) { + const at = phoff + index * phentsize; + if (bytes.readUInt32LE(at) !== 1) continue; + const fileOffset = bytes.readBigUInt64LE(at + 8); + const address = bytes.readBigUInt64LE(at + 16); + const fileSize = bytes.readBigUInt64LE(at + 32); + const target = BigInt(offset); + if (target >= fileOffset && target < fileOffset + fileSize) + return address + (target - fileOffset); + } + return null; +} + +/** Name what differs between two executables: symbols added, removed or + * resized, then the symbol holding the first differing byte. */ +export function compareExecutables(baseline, candidate, baselineNm, candidateNm) { + if (baseline.equals(candidate)) return { identical: true }; + const before = new Map(parseNm(baselineNm).map((s) => [s.name, s])); + const after = new Map(parseNm(candidateNm).map((s) => [s.name, s])); + const removed = [...before.keys()].filter((name) => !after.has(name)); + const added = [...after.keys()].filter((name) => !before.has(name)); + const resized = [...before.values()] + .filter((s) => after.has(s.name) && s.size !== null && after.get(s.name).size !== s.size) + .map((s) => ({ + name: s.name, + baseline: Number(s.size), + candidate: Number(after.get(s.name).size), + })); + let offset = 0; + const limit = Math.min(baseline.length, candidate.length); + while (offset < limit && baseline[offset] === candidate[offset]) offset++; + const address = elfOffsetToAddress(baseline, offset); + let holder = null; + if (address !== null) + for (const s of before.values()) + if (s.size !== null && address >= s.address && address < s.address + s.size) holder = s.name; + const first = + resized[0] !== undefined + ? `resized ${resized[0].name}` + : removed[0] !== undefined + ? `removed ${removed[0]}` + : added[0] !== undefined + ? `added ${added[0]}` + : holder !== null + ? `bytes in ${holder}` + : `byte offset ${offset}`; + return { identical: false, first, offset, holder, added, removed, resized }; +} + +/* ── dynamic-tier symbols (two-tier design, invariant I3) ────────────────── */ + +/** Runtime symbol prefixes that belong to the dynamic tier: code a fully + * typed program has no reason to reach. A typed program's release binary + * must define none of them. Extend this list as dynamic-tier units land + * (S3/S4 add their scr_js_* families) and as legacy ScrDyn families move. + * The ScrDyn prefixes are the object-model entry points the two-tier audit + * (results/design-two-tier/DESIGN.md §3.2-3.3) lists as dynamic-only. */ +export const DYNAMIC_TIER_PREFIXES = [ + // Dynamic-tier runtime units (scr_js_value, scr_js_object, ...), from S3. + "scr_js_", + // ScrDyn property bags: reads, writes, lookup and key indexing. + "scr_dyn_obj_", + "scr_dyn_find_", + "scr_dyn_index_", + "scr_dyn_key_", + "sc_dyn_key_", + // Descriptors, own-property reflection, prototypes and proxies. + "scr_dyn_property_", + "scr_dyn_define_", + "scr_dyn_descriptor_", + "scr_dyn_own_", + "scr_dyn_get_own_", + "scr_dyn_prototype", + "scr_dyn_proxy_", + // Dynamic calls and the dynamic receiver read. + "scr_dyn_call", + "scr_dyn_apply", + "scr_dyn_invoke", + "scr_dyn_construct", + "scr_dyn_function_apply", + "scr_dyn_fn_get", + "scr_dyn_this_get", + // Dynamic operators and coercions. + "scr_dyn_add_", + "scr_dyn_arithmetic", + "scr_dyn_to_numeric", + "scr_dyn_number_coerce", + "scr_dyn_string_coerce", +]; + +/** Symbols under those prefixes that typed binaries reach today through a + * leak the two-tier design schedules for removal (S1). Remove an entry when + * its leak closes. */ +export const DYNAMIC_TIER_EXEMPT = new Map([ + // §3.3 L10: scr_dyn_release's dispose switch, linked whenever any dyn + // value can be released. + ["scr_dyn_obj_drop_keys", "L10"], +]); + +/** The dynamic-tier symbols among `names`, sorted. */ +export function dynamicTierSymbols(names) { + return [...new Set(names)] + .filter( + (name) => + DYNAMIC_TIER_PREFIXES.some((prefix) => name.startsWith(prefix)) && + !DYNAMIC_TIER_EXEMPT.has(name), + ) + .sort(); +} + +/* ── cachegrind ────────────────────────────────────────────────────────── */ + +/** Background threads whose instruction counts depend on scheduling: the + * allocator's prefault thread. */ +export const CACHEGRIND_EXCLUDED = new Set(["scr_sa_pf_main", "scr_sa_prefault"]); + +/** Per-function instruction counts (Ir) from a cachegrind output file, + * restricted to `keep` (the executable's own defined symbols). Dynamic + * linker and libc/pthread functions are outside `keep`; their counts vary + * run to run with lazy binding and thread wakeups. */ +export function cachegrindCounts(text, keep) { + const counts = new Map(); + let events = null; + let current = null; + for (const line of text.split("\n")) { + if (line.startsWith("events:")) events = line.slice(7).trim().split(/\s+/); + else if (line.startsWith("fn=")) { + const name = line.slice(3).trim(); + current = keep.has(name) && !CACHEGRIND_EXCLUDED.has(name) ? name : null; + } else if (line.startsWith("fl=") || line.startsWith("fi=") || line.startsWith("fe=")) continue; + else if (current !== null && /^\d/.test(line)) { + const fields = line.trim().split(/\s+/); + const column = events === null ? 0 : Math.max(0, events.indexOf("Ir")); + const ir = Number(fields[column + 1] ?? 0); + counts.set(current, (counts.get(current) ?? 0) + ir); + } + } + return counts; +} + +/** Per-function instruction-count deltas, largest absolute first. */ +export function compareCounts(baseline, candidate) { + const names = new Set([...baseline.keys(), ...candidate.keys()]); + const deltas = []; + let totalBaseline = 0; + let totalCandidate = 0; + for (const name of names) { + const a = baseline.get(name) ?? 0; + const b = candidate.get(name) ?? 0; + totalBaseline += a; + totalCandidate += b; + if (a !== b) deltas.push({ name, baseline: a, candidate: b, delta: b - a }); + } + deltas.sort((x, y) => Math.abs(y.delta) - Math.abs(x.delta) || (x.name < y.name ? -1 : 1)); + return { + identical: deltas.length === 0, + total: { baseline: totalBaseline, candidate: totalCandidate }, + deltas, + }; +} + +/* ── verdicts ──────────────────────────────────────────────────────────── */ + +/** One program's verdict from its build results and artifact comparisons. + * `allowed` marks programs that match the change's pattern signature. */ +export function programVerdict({ baseline, candidate, artifacts, allowed }) { + if (!baseline.ok || !candidate.ok) { + if (!baseline.ok && !candidate.ok && baseline.error === candidate.error) + return { verdict: "both-fail", fails: false }; + return { verdict: "build-error", fails: true }; + } + const differing = Object.entries(artifacts).filter(([, r]) => !r.identical); + if (differing.length === 0) return { verdict: "identical", fails: false }; + return allowed ? { verdict: "allowed", fails: false } : { verdict: "differs", fails: true }; +} + +export function median(values) { + const sorted = [...values].sort((a, b) => a - b); + const middle = sorted.length >> 1; + return sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2; +} + +export function geomean(values) { + return values.length === 0 + ? 1 + : Math.exp(values.reduce((sum, value) => sum + Math.log(value), 0) / values.length); +} + +/** Compile-time ratio (candidate / baseline) for a set of programs. Each + * row holds paired samples: one baseline and one candidate build that ran + * side by side, so both saw the same machine load. The estimate is the + * geometric mean of each program's median pair ratio, with a 95% bootstrap + * interval over the pairs. Builds on a shared VM vary by 10-20% run to run, + * so the gate judges the interval, not the point estimate. */ +export function timeRatio(rows, resamples = 2000) { + let state = 0x5eed; + const random = () => { + state = (Math.imul(state, 1664525) + 1013904223) >>> 0; + return state / 4294967296; + }; + const pick = (xs) => + Array.from({ length: xs.length }, () => xs[Math.floor(random() * xs.length)]); + const estimate = (draw) => geomean(rows.map((r) => median(draw(r.ratios)))); + const ratios = Array.from({ length: resamples }, () => estimate(pick)).sort((a, b) => a - b); + return { + ratio: estimate((xs) => xs), + low: ratios[Math.floor(resamples * 0.025)], + high: ratios[Math.floor(resamples * 0.975)], + }; +} diff --git a/scripts/zero-cost-gate.mjs b/scripts/zero-cost-gate.mjs new file mode 100644 index 0000000000..f4272ac6a1 --- /dev/null +++ b/scripts/zero-cost-gate.mjs @@ -0,0 +1,652 @@ +/* Zero-cost gate: prove that a compiler change leaves good code untouched. + * + * Compiles a fixed good-code set with a baseline and a candidate checkout and + * compares IR, LLVM and release executables byte for byte, plus compile time + * and binary size, and optionally per-function cachegrind instruction counts. + * Both checkouts compile the same program files (from --programs, default the + * baseline), so program paths embedded in artifacts match; checkout paths and + * timestamps do not reach the artifacts. Builds bypass the native cache. + * + * The good-code set: the runtime benchmark workloads (and benchmarks/builds + * applications), the self-hosted compiler (IR and LLVM), and the TypeScript + * corpus programs. A program whose sources match --allow (the change's + * pattern signature) may differ; any other difference fails the gate. */ +import assert from "node:assert/strict"; +import { spawn, spawnSync } from "node:child_process"; +import { + existsSync, + mkdirSync, + mkdtempSync, + readFileSync, + readdirSync, + rmSync, + statSync, + writeFileSync, +} from "node:fs"; +import { availableParallelism } from "node:os"; +import { dirname, join, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { parseArgs } from "node:util"; +import { + cachegrindCounts, + compareArtifact, + compareCounts, + compareExecutables, + dynamicTierSymbols, + median, + nmOutput, + parseNm, + programVerdict, + timeRatio, +} from "./zero-cost-compare.mjs"; + +const repoRoot = fileURLToPath(new URL("..", import.meta.url)); +const { values } = parseArgs({ + options: { + baseline: { type: "string" }, + candidate: { type: "string", default: repoRoot }, + programs: { type: "string", default: "baseline" }, + set: { type: "string", default: "bench,self,corpus" }, + quick: { type: "boolean", default: false }, + corpus: { type: "string", default: "ts" }, + shard: { type: "string" }, + only: { type: "string" }, + allow: { type: "string" }, + "timing-runs": { type: "string" }, + "self-timing-runs": { type: "string", default: "0" }, + "max-time-growth": { type: "string", default: "2" }, + "max-size-growth": { type: "string", default: "0" }, + cachegrind: { type: "boolean", default: false }, + jobs: { type: "string", default: String(Math.max(1, availableParallelism())) }, + json: { type: "string" }, + keep: { type: "boolean", default: false }, + help: { type: "boolean", default: false }, + }, +}); +if (values.help || values.baseline === undefined) { + console.log(`Usage: node scripts/zero-cost-gate.mjs --baseline= [--candidate=] + [--programs=baseline|candidate|] [--set=bench,self,corpus] [--quick] + [--corpus=ts|all] [--shard=i/n] [--only=] [--allow=] + [--timing-runs=N] [--self-timing-runs=N] [--max-time-growth=2] + [--max-size-growth=0] + [--cachegrind] [--jobs=N] [--json=] [--keep] + +Each checkout must be built (packages/cli/dist/bootstrap.js and this host's +runtime pack). Both compile the same program files, taken from --programs +(default: the baseline checkout). + +--set picks the good-code set: bench (benchmarks/runtime workloads and the +benchmarks/builds applications), self (packages/compiler/src/native/cli.ts, +LLVM only), corpus (tests/corpus TypeScript programs; --corpus=all adds +JavaScript entries). --quick is --set=bench --timing-runs=0, for CI. +--shard=i/n keeps every n-th corpus program; --only filters program ids. + +--allow= is the change's pattern signature: programs whose source +files match it may differ (reported as "allowed"). Any other difference in +IR, LLVM or the release executable fails the gate. + +--timing-runs=N (default 5) times N paired --emit=llvm builds per bench +program, baseline and candidate side by side. The self-hosted compiler's +comparison build is its first pair; --self-timing-runs adds more. The gate +fails when the 95% bootstrap interval of the bench geomean ratio, or of the +self-hosted ratio, lies entirely above --max-time-growth percent; an interval +that straddles it is reported as inconclusive. +--max-size-growth bounds executable growth (percent) of allowed programs. + +Dynamic-tier symbols (two-tier invariant I3): no executable may gain +symbols under DYNAMIC_TIER_PREFIXES (scripts/zero-cost-compare.mjs) unless +it matches --allow, and fully typed bench workloads (all but +DYNAMIC_WORKLOADS in this script) may define none. + +--cachegrind (Linux, valgrind on PATH) runs differing executables under +cachegrind (bench workloads with their arguments, corpus programs without) +and compares instruction counts per function of the executable's own +symbols, excluding the allocator's prefault thread. + +Exit status: 0 when the gate passes, 1 when it fails.`); + process.exit(values.help ? 0 : 2); +} + +const trees = { baseline: resolve(values.baseline), candidate: resolve(values.candidate) }; +for (const [label, root] of Object.entries(trees)) + assert.ok( + existsSync(join(root, "packages/cli/dist/bootstrap.js")), + `${label} checkout ${root} has no built CLI (packages/cli/dist/bootstrap.js)`, + ); +const programsRoot = + values.programs === "baseline" || values.programs === "candidate" + ? trees[values.programs] + : resolve(values.programs); +const sets = new Set((values.quick ? "bench" : values.set).split(",")); +for (const set of sets) assert.ok(["bench", "self", "corpus"].includes(set), `unknown set ${set}`); +const timingRuns = Number(values["timing-runs"] ?? (values.quick ? 0 : 5)); +const selfTimingRuns = Number(values["self-timing-runs"]); +const maxTimeGrowth = Number(values["max-time-growth"]) / 100; +const maxSizeGrowth = Number(values["max-size-growth"]) / 100; +const jobs = Number(values.jobs); +assert.ok(Number.isInteger(timingRuns) && timingRuns >= 0, "--timing-runs must be >= 0"); +assert.ok(Number.isInteger(jobs) && jobs >= 1, "--jobs must be >= 1"); +assert.ok(["ts", "all"].includes(values.corpus), "--corpus must be ts or all"); +const only = values.only ? new RegExp(values.only) : null; +const allow = values.allow ? new RegExp(values.allow, "m") : null; +if (values.cachegrind) + assert.equal( + spawnSync("valgrind", ["--version"]).status, + 0, + "--cachegrind needs valgrind on PATH", + ); + +const work = mkdtempSync("/tmp/scriptc-zero-cost-"); + +/* ── the good-code set ─────────────────────────────────────────────────── */ + +/** Every file a program reads: the entry and its relative imports. */ +function programInputs(entry) { + const files = new Set(); + const pending = [entry]; + while (pending.length > 0) { + const file = pending.pop(); + if (files.has(file)) continue; + files.add(file); + const text = readFileSync(file, "utf8"); + for (const match of text.matchAll( + /(?:from|import|require)\s*\(?\s*["'](\.{1,2}\/[^"']+)["']/g, + )) { + const base = resolve(dirname(file), match[1]); + const stem = base.replace(/\.(m|c)?js$/, ""); + const found = [ + base, + `${stem}.ts`, + `${stem}.mts`, + `${stem}.cts`, + `${base}.ts`, + join(base, "index.ts"), + ].find((candidate) => existsSync(candidate) && statSync(candidate).isFile()); + if (found !== undefined) pending.push(found); + } + } + return [...files]; +} + +function goodCodeSet() { + const programs = []; + if (sets.has("bench")) { + const suite = join(programsRoot, "benchmarks/runtime"); + const manifest = JSON.parse(readFileSync(join(suite, "workloads.json"), "utf8")); + const seen = new Set(); + for (const w of manifest.workloads) { + const entry = join(suite, w.entry); + if (seen.has(entry)) continue; + seen.add(entry); + programs.push({ id: `bench/${w.name}`, entry, kind: "exe", workload: w }); + } + } + if (sets.has("self")) + programs.push({ + id: "self/cli", + entry: join(programsRoot, "packages/compiler/src/native/cli.ts"), + kind: "source", + }); + if (sets.has("corpus")) { + const corpus = join(programsRoot, "tests/corpus"); + const exts = values.corpus === "all" ? ["ts", "js", "mjs", "cjs"] : ["ts"]; + const entries = []; + for (const name of readdirSync(corpus).sort()) { + const path = join(corpus, name); + if (statSync(path).isDirectory()) { + const main = exts.map((ext) => join(path, `main.${ext}`)).find((p) => existsSync(p)); + if (main !== undefined) entries.push(main); + } else if (exts.some((ext) => name.endsWith(`.${ext}`))) entries.push(path); + } + let selected = entries; + if (values.shard) { + const [index, count] = values.shard.split("/").map(Number); + assert.ok(index >= 1 && index <= count, "--shard must be i/n with 1 <= i <= n"); + selected = entries.filter((_, k) => k % count === index - 1); + } + for (const entry of selected) { + const head = readFileSync(entry, "utf8").split("\n", 2); + const speed = head.some((line) => /^\/\/ @optimization:\s*speed\s*$/.test(line)); + programs.push({ + id: `corpus/${relative(corpus, entry)}`, + entry, + kind: "exe", + optimization: speed ? "speed" : "release", + }); + } + } + return programs.filter((p) => only === null || only.test(p.id)); +} + +/* ── builds ────────────────────────────────────────────────────────────── */ + +function exec(command, args, options = {}) { + return new Promise((resolvePromise) => { + const started = process.hrtime.bigint(); + const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"], ...options }); + let stdout = ""; + let stderr = ""; + child.stdout.setEncoding("utf8").on("data", (c) => (stdout += c)); + child.stderr.setEncoding("utf8").on("data", (c) => (stderr += c)); + child.on("close", (status, signal) => + resolvePromise({ + status, + signal, + stdout, + stderr, + ms: Number(process.hrtime.bigint() - started) / 1e6, + }), + ); + }); +} + +function buildEnv() { + const env = { ...process.env, SCRIPTC_NO_CACHE: "1" }; + delete env.SCRIPTC_TIMING; + delete env.SCRIPTC_CACHE_DIR; + return env; +} + +function compile(tree, args) { + return exec( + process.execPath, + ["--max-old-space-size=8192", join(tree, "packages/cli/dist/bootstrap.js"), "build", ...args], + { cwd: programsRoot, env: buildEnv() }, + ); +} + +/** Build one program with one checkout. Executable programs build once, + * keeping the LLVM and IR beside the release executable. The self-hosted + * compiler emits LLVM only: each of its frontend runs takes minutes, and its + * LLVM names the first differing function as well as its IR would. */ +async function buildProgram(program, label) { + const dir = join(work, label, program.id.replace(/[^\w.-]+/g, "_")); + mkdirSync(dir, { recursive: true }); + const tree = trees[label]; + const failure = (result) => ({ + ok: false, + error: (result.stderr || result.stdout || `exit ${result.status ?? result.signal}`) + .replaceAll(dir, "") + .trim() + .slice(0, 4000), + }); + if (program.kind === "source") { + const llvm = join(dir, "program.ll"); + const result = await compile(tree, [program.entry, "--emit=llvm", "-o", llvm]); + if (result.status !== 0) return failure(result); + return { ok: true, dir, llvm, ms: result.ms }; + } + const exe = join(dir, "program"); + const result = await compile(tree, [ + program.entry, + `--optimization=${program.optimization ?? "release"}`, + "--keep-llvm", + "--emit-ir", + "-o", + exe, + ]); + if (result.status !== 0) return failure(result); + const files = readdirSync(dir); + const pick = (suffix) => { + const name = files.find((f) => f.endsWith(suffix)); + return name === undefined ? null : join(dir, name); + }; + return { ok: true, dir, exe, ir: pick(".ir.json"), llvm: pick(".ll"), ms: result.ms }; +} + +const nmCache = new Map(); +function nm(binary) { + if (nmCache.has(binary)) return nmCache.get(binary); + const result = spawnSync("nm", process.platform === "darwin" ? [binary] : ["-S", binary], { + encoding: "utf8", + maxBuffer: 256 * 1024 * 1024, + }); + const text = nmOutput(binary, result); + nmCache.set(binary, text); + return text; +} + +/** Names of the functions an executable defines. */ +const definedFunctions = (binary) => + parseNm(nm(binary)) + .filter((s) => /^[tTwW]$/.test(s.type)) + .map((s) => s.name); + +/* Bench workloads whose release binaries link dynamic-tier symbols on + * b14377864 (DYNAMIC_TIER_PREFIXES in zero-cost-compare.mjs): JSON.parse + * trees, worker messages, regex exec metadata (two-tier §3.3 L5), and a + * shared scr_dyn_call/scr_dyn_define_property core not yet traced to its + * typed root. Every other bench workload is fully typed and must link none. + * Remove a workload here when a leak fix makes its binary clean. */ +const DYNAMIC_WORKLOADS = new Set([ + "bench/json-records", + "bench/cli-config", + "bench/http-api", + "bench/validate-errors", + "bench/regex-logs", + "bench/atomics-contended", + "bench/intern-table", + "bench/typed-array-scan-workers", + "bench/log-summary", + "bench/inventory-report", +]); + +/** The I3 symbol check. Every executable may not gain dynamic-tier symbols + * over the baseline (unless the program matches --allow); a fully typed + * bench workload may contain none at all. */ +function dynamicTierCheck(program, baseline, candidate, artifacts, allowed) { + if (!baseline.exe || !artifacts.exe) return null; + const typed = program.id.startsWith("bench/") && !DYNAMIC_WORKLOADS.has(program.id); + if (artifacts.exe.identical && !typed) return null; + const after = dynamicTierSymbols(definedFunctions(candidate.exe)); + const before = artifacts.exe.identical + ? after + : dynamicTierSymbols(definedFunctions(baseline.exe)); + const added = after.filter((name) => !before.includes(name)); + const failures = []; + if (typed && after.length > 0) + failures.push( + `fully typed program links dynamic-tier symbols: ${after.slice(0, 5).join(", ")}`, + ); + if (added.length > 0 && !allowed) + failures.push(`gained dynamic-tier symbols: ${added.slice(0, 5).join(", ")}`); + return { typed, count: after.length, added, failures }; +} + +function compareBuilds(baseline, candidate) { + const artifacts = {}; + for (const kind of ["ir", "llvm"]) { + if (!(kind in baseline)) continue; + if (baseline[kind] === null || candidate[kind] === null) { + artifacts[kind] = { identical: false, first: `missing ${kind} artifact` }; + continue; + } + artifacts[kind] = compareArtifact( + kind, + readFileSync(baseline[kind]), + readFileSync(candidate[kind]), + ); + } + if (baseline.exe) { + const a = readFileSync(baseline.exe); + const b = readFileSync(candidate.exe); + artifacts.exe = a.equals(b) + ? { identical: true } + : compareExecutables(a, b, nm(baseline.exe), nm(candidate.exe)); + artifacts.exe.bytes = { baseline: a.length, candidate: b.length }; + } + return artifacts; +} + +/** Run tasks with at most `jobs` at a time; heavy tasks run alone. */ +async function pool(tasks) { + const results = new Array(tasks.length); + let next = 0; + let running = 0; + let heavy = false; + await new Promise((done) => { + const pump = () => { + if (next === tasks.length && running === 0) return done(); + while (next < tasks.length && running < jobs && !heavy) { + const task = tasks[next]; + if (task.heavy && running > 0) break; + const index = next++; + running++; + if (task.heavy) heavy = true; + task.run().then((value) => { + results[index] = value; + running--; + if (task.heavy) heavy = false; + pump(); + }); + } + }; + pump(); + }); + return results; +} + +/* ── cachegrind ────────────────────────────────────────────────────────── */ + +async function instructionCounts(binary, args, out) { + const result = await exec("valgrind", [ + "--tool=cachegrind", + "--cache-sim=no", + `--cachegrind-out-file=${out}`, + binary, + ...args, + ]); + // Corpus programs may exit non-zero on purpose (`// @exit: 1`). + if (result.signal !== null || !existsSync(out)) return null; + return cachegrindCounts(readFileSync(out, "utf8"), new Set(definedFunctions(binary))); +} + +/* ── main ──────────────────────────────────────────────────────────────── */ + +function revision(root) { + const r = spawnSync("git", ["-C", root, "rev-parse", "--short", "HEAD"], { encoding: "utf8" }); + if (r.status !== 0) return null; + const dirty = spawnSync("git", ["-C", root, "status", "--porcelain", "--untracked-files=no"], { + encoding: "utf8", + }); + return r.stdout.trim() + (dirty.stdout.trim() ? "+dirty" : ""); +} + +const fmtMs = (ms) => `${(ms / 1000).toFixed(2)}s`; +const pct = (ratio) => `${ratio >= 1 ? "+" : ""}${((ratio - 1) * 100).toFixed(2)}%`; + +const programs = goodCodeSet(); +assert.ok(programs.length > 0, "the selected good-code set is empty"); +const report = { + schema: 1, + date: new Date().toISOString(), + host: { platform: process.platform, arch: process.arch, node: process.version }, + trees: Object.fromEntries( + Object.entries(trees).map(([label, root]) => [label, { root, revision: revision(root) }]), + ), + programsRoot, + allow: values.allow ?? null, + programs: [], + timing: null, + failures: [], +}; +console.log( + `zero-cost gate: ${programs.length} programs (${[...sets].join(", ")}), ` + + `baseline ${trees.baseline}, candidate ${trees.candidate}, programs from ${programsRoot}`, +); + +let failed = false; +const started = Date.now(); +await pool( + programs.map((program) => ({ + heavy: program.kind === "source", + run: async () => { + const [baseline, candidate] = await Promise.all([ + buildProgram(program, "baseline"), + buildProgram(program, "candidate"), + ]); + const allowed = + allow !== null && + programInputs(program.entry).some((f) => allow.test(readFileSync(f, "utf8"))); + const artifacts = baseline.ok && candidate.ok ? compareBuilds(baseline, candidate) : {}; + const { verdict, fails } = programVerdict({ baseline, candidate, artifacts, allowed }); + const entry = { + id: program.id, + verdict, + allowed, + buildMs: { baseline: baseline.ms ?? null, candidate: candidate.ms ?? null }, + artifacts, + }; + if (!baseline.ok) entry.baselineError = baseline.error; + if (!candidate.ok) entry.candidateError = candidate.error; + const size = artifacts.exe?.bytes; + if (verdict === "allowed" && size && size.candidate > size.baseline * (1 + maxSizeGrowth)) { + entry.sizeFailure = true; + report.failures.push( + `${program.id}: executable grew ${size.baseline} -> ${size.candidate} bytes`, + ); + } + if (fails) report.failures.push(`${program.id}: ${verdict}`); + const symbols = + baseline.ok && candidate.ok + ? dynamicTierCheck(program, baseline, candidate, artifacts, allowed) + : null; + if (symbols !== null) entry.dynamicTier = symbols; + for (const failure of symbols?.failures ?? []) + report.failures.push(`${program.id}: ${failure}`); + if (fails || entry.sizeFailure || symbols?.failures.length) failed = true; + if (values.cachegrind && artifacts.exe && !artifacts.exe.identical) { + // Bench workloads run with their benchmark arguments; corpus + // programs take none. + const w = program.workload ?? { args: [] }; + if (w.kind === "server" || w.args.includes("{input}")) + entry.cachegrind = { skipped: "needs a server client or generated input" }; + else { + const counts = await Promise.all([ + instructionCounts(baseline.exe, w.args, join(baseline.dir, "cg.out")), + instructionCounts(candidate.exe, w.args, join(candidate.dir, "cg.out")), + ]); + entry.cachegrind = counts.includes(null) + ? { skipped: "valgrind run failed" } + : (({ deltas, ...rest }) => ({ ...rest, deltas: deltas.slice(0, 20) }))( + compareCounts(counts[0], counts[1]), + ); + } + } + printProgram(entry); + report.programs.push(entry); + nmCache.delete(baseline.exe); + nmCache.delete(candidate.exe); + if (!values.keep) { + if (baseline.dir) rmSync(baseline.dir, { recursive: true, force: true }); + if (candidate.dir) rmSync(candidate.dir, { recursive: true, force: true }); + } + }, + })), +); + +function printProgram(entry) { + const label = { + identical: "identical", + allowed: "allowed ", + differs: "DIFFERS ", + "build-error": "ERROR ", + "both-fail": "both-fail", + }[entry.verdict]; + const notes = []; + for (const [kind, result] of Object.entries(entry.artifacts)) + if (!result.identical) { + const counts = result.changed + ? ` (${result.changed.length} changed, ${result.added.length} added, ${result.removed.length} removed)` + : ""; + notes.push(`${kind}: ${result.first}${counts}`); + } + const size = entry.artifacts.exe?.bytes; + if (size && size.baseline !== size.candidate) + notes.push( + `size ${size.baseline} -> ${size.candidate} B (${pct(size.candidate / size.baseline)})`, + ); + if (entry.cachegrind?.total) { + const { total, deltas } = entry.cachegrind; + notes.push( + `Ir ${total.baseline} -> ${total.candidate} (${pct(total.candidate / Math.max(1, total.baseline))})` + + (deltas[0] + ? `, largest ${deltas[0].name} ${deltas[0].delta > 0 ? "+" : ""}${deltas[0].delta}` + : ""), + ); + } else if (entry.cachegrind?.skipped) + notes.push(`cachegrind skipped: ${entry.cachegrind.skipped}`); + for (const failure of entry.dynamicTier?.failures ?? []) notes.push(`SYMBOLS: ${failure}`); + if (entry.baselineError) notes.push(`baseline: ${entry.baselineError.split("\n")[0]}`); + if (entry.candidateError) notes.push(`candidate: ${entry.candidateError.split("\n")[0]}`); + console.log(`${label} ${entry.id}${notes.length ? ` ${notes.join("; ")}` : ""}`); +} + +/* Compile time: paired --emit=llvm builds, the baseline and the candidate + * side by side, one program at a time. Identical LLVM means everything after + * emission (LLVM optimization, linking) costs the same, so the timed part is + * the compiler's own work. The self-hosted compiler takes minutes per build; + * its main comparison build is its first pair. */ +if (timingRuns > 0) { + const rows = []; + const pair = async (program) => { + const [baseline, candidate] = await Promise.all( + ["baseline", "candidate"].map((label) => + compile(trees[label], [ + program.entry, + "--emit=llvm", + "-o", + join(work, `timing-${label}.ll`), + ]), + ), + ); + return baseline.status === 0 && candidate.status === 0 ? [baseline.ms, candidate.ms] : null; + }; + for (const program of programs) { + if (program.id.startsWith("corpus/")) continue; + const row = { id: program.id, baselineMs: [], candidateMs: [], ratios: [] }; + const add = (sample) => { + if (sample === null) return; + row.baselineMs.push(Math.round(sample[0])); + row.candidateMs.push(Math.round(sample[1])); + row.ratios.push(sample[1] / sample[0]); + }; + const runs = program.kind === "source" ? selfTimingRuns : timingRuns; + if (program.kind === "source") { + const built = report.programs.find((p) => p.id === program.id)?.buildMs; + if (built?.baseline && built?.candidate) add([built.baseline, built.candidate]); + } + for (let run = 0; run < runs; run++) add(await pair(program)); + if (row.ratios.length === 0) continue; + rows.push(row); + console.log( + `time ${program.id} ${fmtMs(median(row.baselineMs))} -> ${fmtMs(median(row.candidateMs))} ` + + `(${pct(median(row.ratios))}, ${row.ratios.length} pairs)`, + ); + } + report.timing = { runs: timingRuns, maxGrowth: maxTimeGrowth, rows, groups: {} }; + const groups = { + bench: rows.filter((r) => r.id.startsWith("bench/")), + self: rows.filter((r) => r.id === "self/cli"), + }; + for (const [name, group] of Object.entries(groups)) { + if (group.length === 0) continue; + const result = timeRatio(group); + report.timing.groups[name] = result; + const verdict = + result.low > 1 + maxTimeGrowth + ? "SLOWER" + : result.high > 1 + maxTimeGrowth + ? "inconclusive (rerun with more runs)" + : "within tolerance"; + console.log( + `time ${name} ${group.length > 1 ? "geomean " : ""}${pct(result.ratio)} ` + + `[95% CI ${pct(result.low)}, ${pct(result.high)}]: ${verdict}`, + ); + if (result.low > 1 + maxTimeGrowth) { + failed = true; + report.failures.push( + `${name} compile time grew ${pct(result.ratio)} (CI low ${pct(result.low)})`, + ); + } + } +} + +const counts = {}; +for (const p of report.programs) counts[p.verdict] = (counts[p.verdict] ?? 0) + 1; +report.counts = counts; +report.verdict = failed ? "fail" : "pass"; +report.elapsedMs = Date.now() - started; +if (values.json) { + mkdirSync(dirname(resolve(values.json)), { recursive: true }); + writeFileSync(values.json, JSON.stringify(report, null, 2) + "\n"); +} +if (!values.keep) rmSync(work, { recursive: true, force: true }); +else console.log(`artifacts kept in ${work}`); +console.log( + `zero-cost gate: ${failed ? "FAIL" : "PASS"} — ${Object.entries(counts) + .map(([k, v]) => `${v} ${k}`) + .join(", ")} in ${fmtMs(report.elapsedMs)}`, +); +for (const failure of report.failures) console.log(` ${failure}`); +process.exit(failed ? 1 : 0); diff --git a/tests/corpus/5013-lifetime-region-async.ts b/tests/corpus/5013-lifetime-region-async.ts index 683d93d6d7..930c6af677 100644 --- a/tests/corpus/5013-lifetime-region-async.ts +++ b/tests/corpus/5013-lifetime-region-async.ts @@ -1,10 +1,9 @@ // LifetimeRegion (@scriptc/experimental) with async callbacks: run(fn) // returns fn's promise once fn first awaits, and the work that resumes -// after an await runs outside the region (it allocates in the default -// region, which is never freed). Releasing the region while the async work -// is still pending is safe: everything the pending work refers to stays -// allocated, and objects of the default region that refer into a released -// region keep it allocated for good. Node.js prints the same. +// after an await still allocates in the region. Releasing the region while +// the async work is still pending is safe: the region is not freed before +// that work has finished and nothing outside refers to its objects. +// Node.js prints the same. import type { ProgramLifetime } from "@scriptc/types"; import { LifetimeRegion } from "@scriptc/experimental"; diff --git a/tests/corpus/5023-lifetime-region-await.ts b/tests/corpus/5023-lifetime-region-await.ts new file mode 100644 index 0000000000..b17ef07aa1 --- /dev/null +++ b/tests/corpus/5023-lifetime-region-await.ts @@ -0,0 +1,200 @@ +// LifetimeRegion (@scriptc/experimental) across await: an async callback +// keeps allocating in its region after it resumes, so a region per request +// holds the whole request's graph and frees it once released. Requests run +// concurrently and interleave at every await; continuations nest other +// regions, reject, release their region while still pending, spawn work +// that outlives the request, and return objects that escape into a cache. +// Node.js prints the same. +import type { ProgramLifetime } from "@scriptc/types"; +import { LifetimeRegion } from "@scriptc/experimental"; + +class Token implements ProgramLifetime { + readonly text: string; + readonly pos: number; + constructor(text: string, pos: number) { + this.text = text; + this.pos = pos; + } +} + +class Node implements ProgramLifetime { + readonly token: Token; + readonly kids: Node[] = []; + parent: Node | undefined = undefined; + readonly attrs = new Map(); + constructor(token: Token) { + this.token = token; + } +} + +const tick = (): Promise => new Promise((resolve) => setTimeout(resolve, 1)); +const turn = (): Promise => new Promise((resolve) => setImmediate(resolve)); + +function tokenize(source: string): Token[] { + return source.split(" ").map((text, i) => new Token(text, i)); +} + +async function parse(tokens: Token[]): Promise { + const root = new Node(tokens[0]!); + for (let i = 1; i < tokens.length; i++) { + if (i % 3 === 0) await turn(); + const node = new Node(tokens[i]!); + node.parent = root; + node.attrs.set("pos", `${tokens[i]!.pos}`); + root.kids.push(node); + } + return root; +} + +function render(root: Node): string { + return `${root.token.text}(${root.kids.map((k) => `${k.token.text}@${k.attrs.get("pos")}`).join(" ")})`; +} + +async function handle(id: number, source: string): Promise { + const tokens = tokenize(source); + await tick(); + const root = await parse(tokens); + await Promise.resolve(); + // Children spawned after a resume run in the region too. + const sizes = await Promise.all(root.kids.map(async (kid) => { + await turn(); + const copy = new Node(kid.token); + copy.parent = kid; + return copy.token.text.length; + })); + return `#${id} ${render(root)} sizes=${sizes.join(",")}`; +} + +async function serve(id: number, source: string): Promise { + const region = new LifetimeRegion(); + try { + return await region.run(() => handle(id, source)); + } finally { + region.release(); + } +} + +// A sync generator driven by a resumed continuation. +function* chain(tokens: Token[]): Generator { + let prev: Node | undefined; + for (const token of tokens) { + const node = new Node(token); + node.parent = prev; + prev = node; + yield node; + } +} + +async function nested(): Promise { + const outer = new LifetimeRegion(); + const inner = new LifetimeRegion(); + const result = await outer.run(async () => { + const before = new Node(new Token("outer", 0)); + await tick(); + const local = inner.run(() => new Node(new Token("inner-sync", 1))); + const later = await inner.run(async () => { + await turn(); + const n = new Node(new Token("inner-async", 2)); + n.parent = local; + return n; + }); + const after = new Node(new Token("after", 3)); + after.parent = before; + after.kids.push(local, later); + const gen = [...chain(tokenize("g0 g1 g2"))]; + return `${after.parent.token.text} ${after.kids.map((k) => k.token.text).join("+")} ${later.parent!.token.text} ${gen.at(-1)!.parent!.token.text}`; + }); + inner.release(); + outer.release(); + return result; +} + +async function rejects(): Promise { + const region = new LifetimeRegion(); + try { + await region.run(async () => { + const node = new Node(new Token("doomed", 0)); + await tick(); + node.kids.push(new Node(new Token("child", 1))); + throw new Error(`failed after ${node.kids.length} child`); + }); + return "no error"; + } catch (e) { + return e instanceof Error ? e.message : String(e); + } finally { + region.release(); + } +} + +async function releasedWhilePending(): Promise { + const region = new LifetimeRegion(); + const pending = region.run(async () => { + const first = new Node(new Token("first", 0)); + await tick(); + // The region was released meanwhile; the work still allocates in it. + const second = new Node(new Token("second", 1)); + second.parent = first; + await turn(); + return `${second.parent.token.text}>${second.token.text}`; + }); + region.release(); + return await pending; +} + +// Background work started in a request outlives it. +let background: Promise | undefined; +async function outlives(): Promise { + const region = new LifetimeRegion(); + const text = await region.run(async () => { + const node = new Node(new Token("request", 0)); + background = (async (): Promise => { + await tick(); + await tick(); + const late = new Node(new Token("late", 1)); + late.parent = node; + return `${late.parent.token.text}<-${late.token.text}`; + })(); + await turn(); + return node.token.text; + }); + region.release(); + return text; +} + +// A result that escapes the request into a module-level cache keeps the +// region allocated until the cache lets go of it. +const cache = new Map(); +async function escapes(key: string): Promise { + const region = new LifetimeRegion(); + const node = await region.run(async () => { + await turn(); + const n = new Node(new Token(key, 0)); + n.kids.push(new Node(new Token(`${key}-kid`, 1))); + return n; + }); + region.release(); + cache.set(key, node); + return `${node.token.text}/${node.kids[0]!.token.text}`; +} + +async function main(): Promise { + const sources = ["let a = b + c ;", "fn f ( x ) { x }", "1 2 3 4 5 6 7", "if ( x ) y else z"]; + for (let round = 0; round < 2; round++) { + const responses = await Promise.all(sources.map((s, i) => serve(round * 10 + i, s))); + for (const r of responses) console.log(r); + } + console.log(await nested()); + console.log(await rejects()); + console.log(await releasedWhilePending()); + console.log(await outlives()); + console.log(await background); + console.log(await escapes("cached")); + console.log(cache.get("cached")!.kids[0]!.token.pos); + cache.clear(); + // A later region, after the cache let go. + console.log(await serve(99, "done .")); +} + +main().catch((e) => { + console.log("unexpected", String(e)); +}); diff --git a/tests/corpus/5024-lifetime-region-await-forms.ts b/tests/corpus/5024-lifetime-region-await-forms.ts new file mode 100644 index 0000000000..e2c9d4622e --- /dev/null +++ b/tests/corpus/5024-lifetime-region-await-forms.ts @@ -0,0 +1,114 @@ +// LifetimeRegion (@scriptc/experimental) across await in both forms an +// async body compiles to: a body that awaits only at its own awaits runs as +// a stackless coroutine, and one that drives an async generator with +// for await keeps a fiber. Each form spawns and awaits the other inside a +// region, requests interleave, and a region is released while its work is +// still pending; everything allocated after a resume stays in the region, +// so every region is freed (tests/harness/lifetime-regions.test.ts). +// Node.js prints the same. +import type { ProgramLifetime } from "@scriptc/types"; +import { LifetimeRegion } from "@scriptc/experimental"; + +class Item implements ProgramLifetime { + readonly name: string; + next: Item | undefined = undefined; + readonly tags: string[] = []; + constructor(name: string) { + this.name = name; + } +} + +const tick = (): Promise => new Promise((resolve) => setTimeout(resolve, 1)); +const turn = (): Promise => new Promise((resolve) => setImmediate(resolve)); + +function names(head: Item | undefined): string { + const out: string[] = []; + for (let at = head; at !== undefined; at = at.next) out.push(`${at.name}[${at.tags.join("/")}]`); + return out.join(","); +} + +// Stackless: suspends only at its own awaits. +async function decorate(name: string, round: number): Promise { + const item = new Item(name); + await turn(); + item.tags.push(`r${round}`); + await Promise.resolve(); + item.tags.push(`${name.length}`); + return item; +} + +async function* produce(prefix: string, n: number): AsyncGenerator { + for (let i = 0; i < n; i++) { + await tick(); + yield `${prefix}${i}`; + } +} + +// Fiber: drives an async generator, and awaits stackless work per step. +async function streamed(prefix: string, n: number): Promise { + let head: Item | undefined; + let round = 0; + for await (const name of produce(prefix, n)) { + const item = await decorate(name, round++); + item.next = head; + head = item; + } + return head; +} + +// Stackless again: awaits the fiber form, then keeps allocating. +async function request(id: number): Promise { + const first = await decorate(`q${id}`, 0); + const chain = await streamed(`s${id}.`, 2 + (id % 2)); + first.next = chain; + await tick(); + const last = new Item(`end${id}`); + last.next = first; + last.tags.push("done"); + return `#${id} ${names(last)}`; +} + +async function serve(id: number): Promise { + const region = new LifetimeRegion(); + try { + return await region.run(() => request(id)); + } finally { + region.release(); + } +} + +async function releasedWhilePending(): Promise { + const region = new LifetimeRegion(); + const pending = region.run(() => streamed("late.", 3)); + region.release(); + const head = await pending; + return `released early: ${names(head)}`; +} + +async function failing(): Promise { + const region = new LifetimeRegion(); + try { + await region.run(async () => { + const head = await streamed("f.", 2); + await turn(); + throw new Error(`failed after ${names(head)}`); + }); + return "no error"; + } catch (e) { + return e instanceof Error ? e.message : String(e); + } finally { + region.release(); + } +} + +async function main(): Promise { + const responses = await Promise.all([1, 2, 3, 4].map((id) => serve(id))); + for (const r of responses) console.log(r); + console.log(await releasedWhilePending()); + console.log(await failing()); + console.log(await serve(9)); +} + +main().catch((e) => { + console.log("unexpected", String(e)); +}); diff --git a/tests/corpus/5627-exec-callback.ts b/tests/corpus/5627-exec-callback.ts new file mode 100644 index 0000000000..eed79e738c --- /dev/null +++ b/tests/corpus/5627-exec-callback.ts @@ -0,0 +1,36 @@ +// child_process.exec's callback forms: the command line runs with +// /bin/sh -c, the call returns a live ChildProcess immediately, utf8 +// stdout/stderr are captured, and the error-first callback fires after the +// current turn. A non-zero exit (a missing command is the shell's 127) +// reports "Command failed: \n". +import { exec } from "node:child_process"; +import * as childProcess from "node:child_process"; + +const child = exec("printf out; printf err >&2", (error, stdout, stderr) => { + console.log("ok", error === null, JSON.stringify(stdout), JSON.stringify(stderr)); + exec("printf 'a\\nb\\nc\\n' | wc -l", (pipeError, lines) => { + console.log("pipe", pipeError === null, lines.trim()); + exec(`printf '%s|' "a b" 'c d' e`, { encoding: "utf8", maxBuffer: 1024 * 1024 }, (_, quoted) => { + console.log("quoting", quoted); + exec("printf partial; printf failed >&2; exit 3", (failed, failedOut, failedErr) => { + console.log("fail", failed !== null, JSON.stringify(failedOut), JSON.stringify(failedErr)); + console.log("fail-message", JSON.stringify(failed === null ? "" : failed.message)); + exec("exit 1", (silent) => { + console.log("silent", JSON.stringify(silent === null ? "" : silent.message)); + exec("scriptc-definitely-not-a-command", (missing, missingOut) => { + const message = missing === null ? "" : missing.message; + console.log("missing", missing !== null, JSON.stringify(missingOut)); + console.log("missing-message", message.split("\n")[0]); + exec("true", () => { + console.log("zero-parameter callback"); + childProcess.exec("printf namespace", (_, out) => console.log(out)); + }); + }); + }); + }); + }); + }); +}); + +console.log("returned", child.pid !== undefined, child.stdout !== null, child.stderr !== null); +console.log("scheduled"); diff --git a/tests/corpus/5631-promisify-crypto.ts b/tests/corpus/5631-promisify-crypto.ts new file mode 100644 index 0000000000..bd5aaf00c9 --- /dev/null +++ b/tests/corpus/5631-promisify-crypto.ts @@ -0,0 +1,118 @@ +// util.promisify over the crypto callback functions: the promise settles +// with the value the callback form receives, or rejects with its error, +// after the call returns. An argument error rejects the promise instead of +// throwing. Random values are checked by length and range only. +import * as crypto from "node:crypto"; +import { pbkdf2, randomBytes, scrypt } from "node:crypto"; +import { promisify } from "node:util"; + +const randomBytesAsync = promisify(randomBytes); +const pbkdf2Async = promisify(pbkdf2); +const scryptAsync = promisify(scrypt); +const hkdfAsync = promisify(crypto.hkdf); +const randomFillAsync = promisify(crypto.randomFill); +const randomIntAsync = promisify(crypto.randomInt); +const generateKeyAsync = promisify(crypto.generateKey); + +async function rejects(label: string, run: () => Promise): Promise { + try { + await run(); + console.log(label, "fulfilled"); + } catch (e) { + const err = e as NodeJS.ErrnoException; + console.log(label, "rejected", err.name, err.code, err.message); + } +} + +async function main(): Promise { + let returned = false; + const pending = randomBytesAsync(16); + returned = true; + const bytes = await pending; + console.log( + "randomBytes", + returned, + bytes.length, + bytes.toString("hex").length, + ); + console.log("randomBytes 0", (await randomBytesAsync(0)).length); + + const derived = await pbkdf2Async("password", "salt", 1000, 32, "sha256"); + console.log("pbkdf2", derived.toString("hex")); + console.log( + "pbkdf2 bytes", + ( + await pbkdf2Async(Buffer.from("pw"), new Uint8Array([1, 2]), 1, 8, "sha1") + ).toString("hex"), + ); + + console.log( + "scrypt", + (await scryptAsync("password", "salt", 16)).toString("hex"), + ); + console.log( + "scrypt options", + (await scryptAsync("password", "salt", 16, { N: 1024 })).toString("hex"), + ); + + const key = await hkdfAsync("sha256", "key", "salt", "info", 16); + console.log( + "hkdf", + key instanceof ArrayBuffer, + Buffer.from(key).toString("hex"), + ); + + const buf = Buffer.alloc(8); + const filled = await randomFillAsync(buf); + console.log("randomFill", filled === buf, filled.length); + const view = new Uint32Array(4); + const tail = await randomFillAsync(view, 2); + console.log("randomFill offset", tail === view, view[0], view[1]); + const middle = new Uint8Array(6); + await randomFillAsync(middle, 1, 2); + console.log("randomFill size", middle[0], middle[3], middle[4], middle[5]); + + const n = await randomIntAsync(10); + console.log("randomInt", Number.isInteger(n), n >= 0 && n < 10); + const m = await randomIntAsync(5, 7); + console.log("randomInt range", m === 5 || m === 6); + + const secret = await generateKeyAsync("hmac", { length: 64 }); + console.log("generateKey", secret.type, secret.symmetricKeySize); + + await rejects("randomBytes size", async () => { + await randomBytesAsync(-1); + }); + await rejects("pbkdf2 digest", async () => { + await pbkdf2Async("pw", "salt", 1, 8, "no-such-digest"); + }); + await rejects("pbkdf2 iterations", async () => { + await pbkdf2Async("pw", "salt", 0, 8, "sha256"); + }); + await rejects("scrypt keylen", async () => { + await scryptAsync("pw", "salt", -1); + }); + await rejects("hkdf digest", async () => { + await hkdfAsync("nope", "k", "s", "i", 8); + }); + await rejects("randomFill offset", async () => { + await randomFillAsync(Buffer.alloc(4), 9); + }); + await rejects("randomInt range", async () => { + await randomIntAsync(3, 3); + }); + await rejects("generateKey length", async () => { + await generateKeyAsync("hmac", { length: 3 }); + }); + + const order: string[] = []; + const first = randomBytesAsync(4).then(() => order.push("randomBytes")); + const second = pbkdf2Async("a", "b", 1, 4, "sha256").then(() => + order.push("pbkdf2"), + ); + order.push("sync"); + await Promise.all([first, second]); + console.log("order", order[0]); +} + +main().catch((e) => console.log("unexpected", String(e))); diff --git a/tests/corpus/5632-promisify-dns.ts b/tests/corpus/5632-promisify-dns.ts new file mode 100644 index 0000000000..2c0a1adbcd --- /dev/null +++ b/tests/corpus/5632-promisify-dns.ts @@ -0,0 +1,57 @@ +// util.promisify over dns.lookup and dns.lookupService: lookup's promise +// settles with { address, family }, or the address array with options.all, +// and lookupService's with { hostname, service }, after the call returns. A +// failed lookup rejects with the callback's error, and an argument error +// rejects the promise instead of throwing. +import * as dns from "node:dns"; +import { lookup } from "node:dns"; +import { promisify } from "node:util"; + +const lookupAsync = promisify(lookup); +const lookupServiceAsync = promisify(dns.lookupService); + +async function rejects(label: string, run: () => Promise): Promise { + try { + await run(); + console.log(label, "fulfilled"); + } catch (e) { + const err = e as NodeJS.ErrnoException; + console.log(label, "rejected", err.name, err.code, err.message); + } +} + +async function main(): Promise { + let returned = false; + const pending = lookupAsync("127.0.0.1"); + returned = true; + const one = await pending; + console.log("literal", returned, one.address, one.family); + const six = await lookupAsync("::1", 6); + console.log("family", six.address, six.family); + const all = await lookupAsync("::1", { all: true }); + console.log("all", all.length, all[0]!.address, all[0]!.family); + const local = await lookupAsync("localhost", { family: 4 }); + console.log("localhost", local.address, local.family); + const local4 = await lookupAsync("localhost", { family: 4, all: true }); + console.log("localhost all", JSON.stringify(local4)); + + await rejects("missing host", async () => { + await lookupAsync("no-such-host.invalid"); + }); + await rejects("bad family", async () => { + await lookupAsync("localhost", 5); + }); + await rejects("bad hostname", async () => { + await lookupAsync(42 as unknown as string); + }); + await rejects("bad address", async () => { + await lookupServiceAsync("not-an-address", 22); + }); + await rejects("bad port", async () => { + await lookupServiceAsync("127.0.0.1", 70000); + }); + const service = await lookupServiceAsync("127.0.0.1", 22); + console.log("service", typeof service.hostname, typeof service.service); +} + +main().catch((e) => console.log("unexpected", String(e))); diff --git a/tests/corpus/5681-array-pop-and-length.ts b/tests/corpus/5681-array-pop-and-length.ts new file mode 100644 index 0000000000..451b32576b --- /dev/null +++ b/tests/corpus/5681-array-pop-and-length.ts @@ -0,0 +1,143 @@ +// Removing the last element with a statement-level pop() or `length = n`, +// for every element kind, on dense arrays, arrays with holes and present +// undefined values, sparse arrays and arrays shared between bindings. +// Removed references are released; the remaining elements, the length and +// later appends match Node. + +class Item { + readonly id: number; + constructor(id: number) { + this.id = id; + } +} + +function show(label: string, values: unknown[]): void { + const parts: string[] = []; + for (let i = 0; i < values.length; i++) { + if (!(i in values)) parts.push(""); + else { + const v = values[i]; + parts.push(v instanceof Item ? `I${v.id}` : (JSON.stringify(v) ?? "undefined")); + } + } + console.log(label, values.length, parts.join(" ")); +} + +const numbers = [1.5, 2.5, 3.5, 4.5]; +numbers.pop(); +numbers.length = numbers.length - 1; +numbers.push(9); +show("numbers", numbers); + +const flags = [true, false, true]; +flags.pop(); +flags.length = 1; +flags.push(false); +show("flags", flags); + +const words = ["a", "b", "c", "d"]; +const alias = words; +words.pop(); +alias.length = alias.length - 1; +words.push("z"); +show("words", words); +show("alias", alias); + +const items = [new Item(1), new Item(2), new Item(3)]; +const kept = items[2]!; +items.pop(); +items.length = 1; +items.push(new Item(4)); +show("items", items); +console.log("kept", kept.id); + +// Holes and present undefined at the end. +const holes: (number | undefined)[] = [1, 2]; +holes[4] = 5; +holes.length = 4; +show("holes trimmed", holes); +holes.length = 3; +show("holes trimmed again", holes); +const undef: (string | undefined)[] = ["x", undefined, undefined]; +undef.pop(); +undef.length = undef.length - 1; +show("undefined", undef); + +// A sparse array: a far index then shrinking past it and back by one. +const sparse: number[] = [1, 2, 3]; +sparse[2000000] = 4; +sparse.length = 2000000; +show("sparse cut", sparse.slice(0, 4)); +console.log("sparse length", sparse.length, 1999999 in sparse); +sparse.length = sparse.length - 1; +console.log("sparse length", sparse.length); + +// A stack used the way a checker pushes and pops contextual entries. +const stack: Item[] = []; +const top: number[] = []; +let checksum = 0; +for (let i = 0; i < 20000; i++) { + stack.push(new Item(i)); + top.push(i); + if (i % 3 === 2) { + stack.pop(); + top.length = top.length - 1; + } + if (stack.length > 50) { + stack.length = stack.length - 1; + top.pop(); + } + checksum = (checksum + stack[stack.length - 1]!.id + top[top.length - 1]!) % 1000003; +} +console.log("stack", stack.length, top.length, checksum); + +// Lengths that are not one less: unchanged, longer, invalid. +const other = [1, 2, 3]; +other.length = 3; +other.length = 5; +show("longer", other); +other.length = 1; +show("shorter by many", other); +try { + other.length = -1; +} catch (e) { + console.log("invalid", (e as Error).name, other.length); +} +try { + other.length = 0.5; +} catch (e) { + console.log("fraction", (e as Error).name, other.length); +} +const empty: number[] = []; +empty.pop(); +empty.length = 0; +show("empty", empty); + +// pop() results: the last element, undefined for an empty array, a hole or a +// present undefined, and values taken from sparse storage. +const nums = [0.5, -0, 7]; +console.log("pop number", nums.pop(), Object.is(nums.pop(), -0), nums.pop(), nums.pop(), nums.length); +const bools = [false, true]; +console.log("pop bool", bools.pop(), bools.pop(), bools.pop(), bools.length); +const strs = ["p", "q"]; +const strAlias = strs; +console.log("pop string", strs.pop(), strAlias.pop(), strs.pop(), strAlias.length); +const objs = [new Item(7), new Item(8)]; +const popped = objs.pop(); +console.log("pop item", popped?.id, objs.pop()?.id, objs.pop(), objs.length); +const gaps: number[] = [1]; +gaps[3] = 4; +console.log("pop gaps", gaps.pop(), gaps.pop(), gaps.pop(), gaps.pop(), gaps.pop(), gaps.length); +const maybe: (number | undefined)[] = [1, undefined]; +console.log("pop present undefined", maybe.pop(), maybe.length, maybe.pop(), maybe.length); +const far: string[] = ["a"]; +far[100000] = "b"; +console.log("pop sparse", far.pop(), far.length, far.pop(), far.length); +let popTotal = 0; +const work: number[] = []; +for (let i = 0; i < 10000; i++) { + work.push(i, i + 1); + popTotal += work.pop()!; + if (i % 5 === 0) popTotal += work.pop() ?? -1; +} +console.log("pop loop", work.length, popTotal); diff --git a/tests/corpus/5770-guarded-union-narrows.ts b/tests/corpus/5770-guarded-union-narrows.ts new file mode 100644 index 0000000000..2673fb048b --- /dev/null +++ b/tests/corpus/5770-guarded-union-narrows.ts @@ -0,0 +1,88 @@ +// Union narrowings that TypeScript proves are verified at run time: every +// narrowing below reads its payload through a tag guard. Correct programs +// never trip it, so output matches Node across typeof, nullable, tagged, +// switch, early-return, loop and closure narrowings, including payloads +// borrowed for field reads and owned ones stored into arrays. +type Shape = + | { kind: "circle"; r: number } + | { kind: "rect"; w: number; h: number } + | { kind: "label"; text: string }; + +class Node2 { + value: number; + next: Node2 | null; + constructor(value: number, next: Node2 | null) { + this.value = value; + this.next = next; + } +} + +function describe(v: string | number | boolean): string { + if (typeof v === "string") return `string:${v.length}:${v.toUpperCase()}`; + if (typeof v === "number") return `number:${(v * 2).toFixed(1)}`; + return `boolean:${v ? "yes" : "no"}`; +} + +function area(s: Shape): number { + switch (s.kind) { + case "circle": + return Math.round(Math.PI * s.r * s.r); + case "rect": + return s.w * s.h; + case "label": + return s.text.length; + } +} + +function sumList(head: Node2 | null): number { + let total = 0; + let cur = head; + while (cur !== null) { + total += cur.value; + cur = cur.next; + } + return total; +} + +function firstWord(text: string | undefined): string { + if (text === undefined) return ""; + const words = text.split(" "); + return words[0] ?? ""; +} + +function pickNames(items: (string | number)[]): string[] { + const names: string[] = []; + for (const item of items) { + if (typeof item !== "string") continue; + names.push(item); + } + return names; +} + +function makeFormatter(prefix: string | null): (n: number) => string { + const p = prefix ?? "#"; + return (n) => `${p}${n}`; +} + +const values: (string | number | boolean)[] = ["abc", 4.25, true, "", 0, false]; +for (const v of values) console.log(describe(v)); + +const shapes: Shape[] = [ + { kind: "circle", r: 2 }, + { kind: "rect", w: 3, h: 5 }, + { kind: "label", text: "hello" }, +]; +let totalArea = 0; +for (let round = 0; round < 1000; round++) for (const s of shapes) totalArea += area(s); +console.log("area", totalArea); + +let list: Node2 | null = null; +for (let i = 1; i <= 100; i++) list = new Node2(i, list); +console.log("list", sumList(list), sumList(null)); + +console.log(firstWord("guarded narrows"), firstWord(undefined), firstWord("")); +console.log(pickNames(["a", 1, "b", 2, "c"]).join(",")); +const fmt = makeFormatter(null); +const fmt2 = makeFormatter("n="); +console.log(fmt(1), fmt2(2)); + diff --git a/tests/corpus/5771-guarded-downcasts.ts b/tests/corpus/5771-guarded-downcasts.ts new file mode 100644 index 0000000000..a85b2e08f8 --- /dev/null +++ b/tests/corpus/5771-guarded-downcasts.ts @@ -0,0 +1,81 @@ +// instanceof narrowing downcasts objects to subclasses; the downcast is +// verified against the target's preorder intervals at run time. Covers +// multi-level hierarchies, abstract bases, generic classes, and receivers +// borrowed for field reads as well as owned values passed on. +abstract class Animal { + name: string; + constructor(name: string) { + this.name = name; + } + abstract sound(): string; +} +class Dog extends Animal { + tricks = ["sit", "roll"]; + sound(): string { + return "woof"; + } +} +class Puppy extends Dog { + age = 1; + sound(): string { + return "yip"; + } +} +class Cat extends Animal { + lives = 9; + sound(): string { + return "meow"; + } +} + +class Box { + value: T; + constructor(value: T) { + this.value = value; + } +} +class LabeledBox extends Box { + label: string; + constructor(value: T, label: string) { + super(value); + this.label = label; + } +} + +function describe(a: Animal): string { + if (a instanceof Puppy) return `${a.name} puppy age ${a.age} ${a.tricks.length} tricks`; + if (a instanceof Dog) return `${a.name} dog ${a.tricks.join("+")}`; + if (a instanceof Cat) return `${a.name} cat ${a.lives} lives`; + return `${a.name} unknown`; +} + +function keepDogs(all: Animal[]): Dog[] { + const dogs: Dog[] = []; + for (const a of all) if (a instanceof Dog) dogs.push(a); + return dogs; +} + +function labelOf(b: Box): string { + return b instanceof LabeledBox ? `${b.label}=${b.value}` : `box=${b.value}`; +} + +function labelOfText(b: Box): string { + return b instanceof LabeledBox ? `${b.label}:${b.value}` : `text:${b.value}`; +} + +const zoo: Animal[] = [new Dog("rex"), new Cat("tom"), new Puppy("bit"), new Cat("kit")]; +for (const a of zoo) console.log(describe(a), a.sound()); +console.log( + "dogs", + keepDogs(zoo) + .map((d) => d.name) + .join(","), +); + +let tricks = 0; +for (let round = 0; round < 2000; round++) + for (const a of zoo) if (a instanceof Dog) tricks += a.tricks.length; +console.log("tricks", tricks); + +console.log(labelOf(new Box(1)), labelOf(new LabeledBox(2, "two"))); +console.log(labelOfText(new Box("a")), labelOfText(new LabeledBox("b", "bee"))); diff --git a/tests/corpus/5772-guarded-catch-narrows.ts b/tests/corpus/5772-guarded-catch-narrows.ts new file mode 100644 index 0000000000..a29bda9a07 --- /dev/null +++ b/tests/corpus/5772-guarded-catch-narrows.ts @@ -0,0 +1,63 @@ +// Catch bindings narrowed by typeof or instanceof extract their payload +// through a kind or class guard. Covers string, number and boolean throws, +// Error subclass hierarchies, and a thrown standalone class. +class AppError extends Error { + code: number; + constructor(message: string, code: number) { + super(message); + this.name = "AppError"; + this.code = code; + } +} +class NotFound extends AppError { + path: string; + constructor(path: string) { + super(`missing ${path}`, 404); + this.name = "NotFound"; + this.path = path; + } +} +class Signal { + reason: string; + constructor(reason: string) { + this.reason = reason; + } +} + +function fail(which: number): void { + if (which === 0) throw "text"; + if (which === 1) throw 7.5; + if (which === 2) throw false; + if (which === 3) throw new NotFound("/etc/x"); + if (which === 4) throw new AppError("bad", 500); + if (which === 5) throw new RangeError("range"); + if (which === 6) throw new Signal("stop"); +} + +function classify(which: number): string { + try { + fail(which); + return "ok"; + } catch (e) { + if (typeof e === "string") return `string ${e.length} ${e}`; + if (typeof e === "number") return `number ${e * 2}`; + if (typeof e === "boolean") return `boolean ${!e}`; + if (e instanceof NotFound) return `notfound ${e.path} ${e.code}`; + if (e instanceof AppError) return `app ${e.code} ${e.message}`; + if (e instanceof Error) return `error ${e.name}: ${e.message}`; + if (e instanceof Signal) return `signal ${e.reason}`; + return "unknown"; + } +} + +for (let which = 0; which <= 7; which++) console.log(which, classify(which)); + +let codes = 0; +for (let i = 0; i < 500; i++) { + try { + fail(3 + (i % 2)); + } catch (e) { + if (e instanceof AppError) codes += e.code; + } +} +console.log("codes", codes); diff --git a/tests/corpus/5773-stale-narrowing-inherited.ts b/tests/corpus/5773-stale-narrowing-inherited.ts new file mode 100644 index 0000000000..a63ed46146 --- /dev/null +++ b/tests/corpus/5773-stale-narrowing-inherited.ts @@ -0,0 +1,74 @@ +// A binding initialized or assigned from a stale narrowed read inherits the +// stale narrowing: after reset() stores a Lit, TypeScript still types +// `current` as Neg, and `const held: Expr = current` narrows `held` to Neg +// too. Reads of such bindings observe the value stored now, as Node does: +// members every arm has read the current arm, and an arm-only member of +// the wrong arm throws the catchable TypeError instead of reading another +// record's slot. +type Lit = { kind: "lit"; value: number }; +type Neg = { kind: "neg"; operand: Expr }; +type Expr = Lit | Neg; + +function show(e: Expr): string { + return e.kind === "lit" ? String(e.value) : `-${show(e.operand)}`; +} + +function fromInitializer(replace: boolean): string { + let current: Expr | null = null; + const reset = (): void => { + current = { kind: "lit", value: 7 }; + }; + current = { kind: "neg", operand: { kind: "lit", value: 1 } }; + if (replace) reset(); + if (current === null) return "null"; + const held: Expr = current; + const kind = held.kind; + try { + return `${kind} ${show(held.operand)}`; + } catch (e) { + return `${kind} caught ${(e as Error).name}`; + } +} + +function fromAssignment(replace: boolean): string { + let current: Expr | null = null; + const reset = (): void => { + current = { kind: "lit", value: 8 }; + }; + current = { kind: "neg", operand: { kind: "lit", value: 2 } }; + if (replace) reset(); + let held: Expr = { kind: "lit", value: 0 }; + if (current === null) return show(held); + held = current; + try { + return `${held.kind} ${show(held.operand)}`; + } catch (e) { + return `${held.kind} caught ${(e as Error).name}`; + } +} + +class Holder { + expr: Expr | null = null; + replace(): void { + this.expr = { kind: "lit", value: 9 }; + } +} + +function fromProperty(replace: boolean): string { + const h = new Holder(); + h.expr = { kind: "neg", operand: { kind: "lit", value: 3 } }; + if (replace) h.replace(); + if (h.expr === null) return "null"; + const held: Expr = h.expr; + try { + return `${held.kind} ${show(held.operand)}`; + } catch (e) { + return `${held.kind} caught ${(e as Error).name}`; + } +} + +for (const replace of [false, true]) { + console.log("initializer:", fromInitializer(replace)); + console.log("assignment:", fromAssignment(replace)); + console.log("property:", fromProperty(replace)); +} diff --git a/tests/corpus/5846-events-esm-default-max.ts b/tests/corpus/5846-events-esm-default-max.ts new file mode 100644 index 0000000000..acc57328ad --- /dev/null +++ b/tests/corpus/5846-events-esm-default-max.ts @@ -0,0 +1,28 @@ +// events.defaultMaxListeners through the module's ES module namespace and +// named imports reads the namespace's copy, which keeps the value from +// program start until module.syncBuiltinESMExports() refreshes it. The +// default import and the EventEmitter class read the current default. +import EventEmitterDefault, { defaultMaxListeners, EventEmitter, setMaxListeners } from "node:events"; +import * as events from "node:events"; +import { syncBuiltinESMExports } from "node:module"; + +const show = (label: string): void => { + console.log( + label, + events.defaultMaxListeners, + defaultMaxListeners, + EventEmitterDefault.defaultMaxListeners, + EventEmitter.defaultMaxListeners, + ); +}; +show("start"); +setMaxListeners(20); +show("after setMaxListeners"); +EventEmitter.defaultMaxListeners = 7; +show("after assignment"); +syncBuiltinESMExports(); +show("after sync"); +events.setMaxListeners(3); +show("after namespace setMaxListeners"); +const emitter = new EventEmitter(); +console.log(emitter.getMaxListeners(), events.getMaxListeners(emitter)); diff --git a/tests/corpus/5847-events-cjs-default-max.cjs b/tests/corpus/5847-events-cjs-default-max.cjs new file mode 100644 index 0000000000..9edbe811d3 --- /dev/null +++ b/tests/corpus/5847-events-cjs-default-max.cjs @@ -0,0 +1,10 @@ +// CommonJS bindings of node:events read defaultMaxListeners from the module +// object, which is the EventEmitter class: every read sees the current +// default. +const events = require("node:events"); +const { EventEmitter, setMaxListeners } = require("node:events"); +console.log(events.defaultMaxListeners, EventEmitter.defaultMaxListeners); +setMaxListeners(15); +console.log(events.defaultMaxListeners, EventEmitter.defaultMaxListeners, require("events").defaultMaxListeners); +events.defaultMaxListeners = 4; +console.log(events.defaultMaxListeners, EventEmitter.defaultMaxListeners, new EventEmitter().getMaxListeners()); diff --git a/tests/corpus/5907-regex-groups-match-rows.ts b/tests/corpus/5907-regex-groups-match-rows.ts new file mode 100644 index 0000000000..22334bad6f --- /dev/null +++ b/tests/corpus/5907-regex-groups-match-rows.ts @@ -0,0 +1,33 @@ +// Named groups on matchAll rows (array-method callbacks over spread rows, +// duplicate names), through optional chains on exec and match results +// (including null matches), and the groups object's null prototype through +// inspection, declared types and unions. Node.js prints the same. +const text = "a1 b22 c333"; +const re = /(?[a-z])(?\d+)/g; +const rows = [...text.matchAll(re)]; +console.log(rows.map((m) => `${m.groups!.word}=${m.groups!.num}`).join(" ")); +rows.forEach((m) => console.log("each", m.groups?.word, m[0])); +console.log(rows.filter((m) => m.groups!.num.length > 1).length, rows.find((m) => m.groups!.word === "c")?.[0]); +console.log([..."x9y8".matchAll(/(?[a-z])|(?\d)/g)].map((m) => `${m.groups!.l ?? "-"}${m.groups!.d ?? "-"}`).join(",")); +console.log(rows.some((m) => m.groups!.word === "b"), rows.every(function (m) { return m.groups!.num !== ""; })); +for (const s of ["2024-05", "nope", "07/1999"]) { + const m = /(?\d{4})-(?\d{2})|(?\d{2})\/(?\d{4})/.exec(s); + console.log(s, m?.groups?.year, m?.groups?.month ?? "none", m?.groups); +} +const mm = "id=42".match(/id=(?\d+)/); +console.log(mm?.groups?.id, "nothing".match(/(?z)/)?.groups?.z); +const none = /(?q)/.exec("abc"); +console.log(none?.groups, none?.groups?.q === undefined); +const parts = /(?\d)-(?\w)/.exec("1-a")!; +const g = parts.groups!; +console.log(parts.groups, JSON.stringify(g), Object.keys(g), Object.getPrototypeOf(g) === null); +try { console.log(String(g)); } catch (e) { console.log((e as Error).name, (e as Error).message); } +const { y, z } = parts.groups!; +console.log(y, z); +const one = /(?\d)/.exec("1"); +console.log(one?.groups); +const maybeGroups = one?.groups; +console.log(maybeGroups); +console.log(one!.groups); +const declared: { [k: string]: string } | undefined = one!.groups; +console.log(declared); diff --git a/tests/corpus/5938-object-literal-computed-keys-keep-methods.ts b/tests/corpus/5938-object-literal-computed-keys-keep-methods.ts new file mode 100644 index 0000000000..bd324210a9 --- /dev/null +++ b/tests/corpus/5938-object-literal-computed-keys-keep-methods.ts @@ -0,0 +1,39 @@ +// An object literal with a computed key that is only known at runtime +// defines a property of that new object. Class methods, including methods +// of generic classes, keep their calls. +class Ctx { + v: number; + constructor() { + this.v = 0; + } + json(o: T): string { + return JSON.stringify(o); + } + status(n: number): Ctx { + this.v = n; + return this; + } +} + +class Counter { + count = 0; + bump(): number { + return ++this.count; + } +} + +function headers(names: string[]): Record { + const out: Record = {}; + for (const name of names) Object.assign(out, { [name.toLowerCase()]: name.length }); + return out; +} + +const prefix = process.argv.length > 99 ? "never" : "x-"; +const key = `${prefix}id`; +const literal: Record = { [key]: 1, [`${key}-2`]: 2 }; +const ctx = new Ctx<{ ok: boolean }>(); +const counter = new Counter(); +counter.bump(); +console.log(literal, headers(["Accept", "Host"])); +console.log(typeof ctx.json, typeof ctx.status, ctx.status(200).json({ ok: true }), ctx.v); +console.log(counter.bump(), counter.count); diff --git a/tests/corpus/5943-fields-read-before-assignment.ts b/tests/corpus/5943-fields-read-before-assignment.ts new file mode 100644 index 0000000000..922b3c34d4 --- /dev/null +++ b/tests/corpus/5943-fields-read-before-assignment.ts @@ -0,0 +1,181 @@ +// @transform-types +// Fields that a base constructor (or the constructor itself) can read +// through an override or a method call before they are assigned hold +// undefined then, as in Node, for every field kind. After construction +// the fields read and update normally. +class Point { + x: number; + constructor(x: number) { + this.x = x; + } +} +function show(label: string, value: number): string { + return `${label}=${value}`; +} +abstract class Widget { + log: string[] = []; + constructor() { + this.describe("base"); + } + abstract describe(when: string): void; +} +class Panel extends Widget { + label: string; + width: number; + visible: boolean; + origin: Point; + items: number[]; + options: { depth: number }; + key: string | number; + index: Map; + tags: Set; + count = 5; + constructor(label: string, readonly id: string) { + super(); + this.label = label; + this.width = 10; + this.visible = true; + this.origin = new Point(1); + this.items = [1, 2]; + this.options = { depth: 2 }; + this.key = 7; + this.index = new Map([["a", 1]]); + this.tags = new Set(["t"]); + this.describe("ctor"); + } + describe(when: string): void { + console.log(when, this.label, this.width, this.visible, this.origin, this.items, this.options, this.key, this.count, this.id); + console.log(when, `${this.label}:${this.width}`, this.width + 1, this.count * 2, this.label === undefined, typeof this.items, this.origin?.x, this.options?.depth ?? -1, this.index?.size, this.tags?.has("t")); + console.log(when, show(this.label, this.width), String(this.key), [this.label].length); + try { + console.log(this.label.length, this.items.length); + } catch (error) { + console.log(when, (error as Error).message); + } + } + grow(): number { + this.count++; + this.width += 5; + this.label += "!"; + this.items.push(this.width); + return this.count + this.width + this.items.length; + } +} +const panel = new Panel("main", "p1"); +console.log(panel.grow(), panel.label, panel.items, panel.count, panel.width); + +class Plain { + total: number; + name: string; + constructor() { + this.total = 1; + this.name = "plain"; + this.bump(); + } + bump(): void { + this.total++; + } +} +const plain = new Plain(); +console.log(plain.total, plain.name); + +class Toucher { + constructor() { + this.touch(); + } + touch(): void {} +} +class Counter extends Toucher { + count = 0; + label: string; + items: string[]; + constructor() { + super(); + this.label = "c"; + this.items = []; + } + override touch(): void { + this.count++; + this.label += "x"; + console.log(this.count, this.label, Number.isNaN(this.count)); + try { + this.items.push("a"); + } catch (error) { + console.log(error instanceof TypeError); + } + } +} +const c = new Counter(); +c.touch(); +console.log(c.count, c.label, c.items); + +class Shower { + constructor() { + this.show(); + } + show(): void {} +} +class Box extends Shower { + label: string; + value: T; + constructor(value: T) { + super(); + this.label = "box"; + this.value = value; + } + override show(): void { + console.log("show", this.label, this.value); + } +} +new Box(3).show(); +new Box("s").show(); + +type Ctor = new (...args: any[]) => T; +class Viewer { + constructor() { + this.show(); + } + show(): void {} +} +function Labeled>(Parent: B) { + return class extends Parent { + label = "mixed"; + override show(): void { + console.log("show", this.label); + } + }; +} +const L = Labeled(Viewer); +new L().show(); + +interface Crash { loc: number; message: string } +class Setupper { + constructor() { + this.setup(); + } + setup(): void {} +} +class Host extends Setupper { + crash: Crash | null; + name: string | null; + constructor(c: Crash | null) { + super(); + this.crash = c; + this.name = "host"; + } + override setup(): void { + console.log("setup", this.crash, this.name); + if (this.crash !== null) { + try { + console.log(this.crash.loc); + } catch (error) { + console.log(error instanceof TypeError); + } + } + } + report(): string { + if (this.crash) return `${this.crash.message} at ${this.crash.loc}`; + return this.name === null ? "none" : this.name.toUpperCase(); + } +} +console.log(new Host({ loc: 3, message: "boom" }).report(), new Host(null).report()); diff --git a/tests/corpus/5944-js-fields-read-before-assignment.js b/tests/corpus/5944-js-fields-read-before-assignment.js new file mode 100644 index 0000000000..e313a12b00 --- /dev/null +++ b/tests/corpus/5944-js-fields-read-before-assignment.js @@ -0,0 +1,28 @@ +// JavaScript class fields that the constructor creates after it calls a +// method, or after a base constructor that does, hold undefined until +// they are assigned, as in Node. +class Widget { + constructor(name) { + this.render(); + this.name = name; + this.size = 3; + this.title = "widget"; + this.parts = ["a"]; + this.meta = { id: 1 }; + } + render() { + console.log("render", this.name, this.title, this.size, this.parts, this.meta, this.size + 1, `${this.name}!`); + } +} +class Big extends Widget { + constructor() { + super("big"); + this.extra = 2; + } + render() { + super.render(); + console.log("big", this.extra); + } +} +new Widget("w").render(); +new Big().render(); diff --git a/tests/corpus/5942-local-class-observed-methods.ts b/tests/corpus/5945-local-class-observed-methods.ts similarity index 100% rename from tests/corpus/5942-local-class-observed-methods.ts rename to tests/corpus/5945-local-class-observed-methods.ts diff --git a/tests/corpus/5946-nested-record-field-optional-writes.ts b/tests/corpus/5946-nested-record-field-optional-writes.ts new file mode 100644 index 0000000000..ff1b2d25ac --- /dev/null +++ b/tests/corpus/5946-nested-record-field-optional-writes.ts @@ -0,0 +1,43 @@ +// A record held in another record's field, passed to a function that stores +// a possibly missing array element in it, keeps the write and its identity, +// as in Node; the outer record's layout holds the widened field. +type Parsed = { command: string; rest: string[] }; +type Holder = { parsed: Parsed; label: string }; + +function fill(args: string[], into: Parsed): Parsed { + into.command = args[0]; + into.rest = args.slice(1); + return into; +} + +const state = { parsed: { command: "", rest: [] as string[] } }; +fill(["deep", "x"], state.parsed); +console.log(state.parsed.command, state.parsed.rest); +const alias = state.parsed; +fill([], state.parsed); +console.log(state.parsed.command, alias.command, alias === state.parsed, typeof alias.command); +console.log(state, JSON.stringify(state)); + +state.parsed = { command: "fresh", rest: [] }; +fill(["again"], state.parsed); +console.log(state.parsed.command, alias.command); + +function run(holder: Holder, args: string[]): string { + fill(args, holder.parsed); + return `${holder.label}:${holder.parsed.command ?? "none"}`; +} +console.log(run({ parsed: { command: "", rest: [] }, label: "a" }, ["go"])); +console.log(run({ parsed: { command: "", rest: [] }, label: "b" }, [])); + +function makeHolder(label: string): Holder { + return { parsed: { command: "init", rest: [] }, label }; +} +const made = makeHolder("made"); +fill([], made.parsed); +console.log(made.parsed.command === undefined, made.label); +fill(["set"], made.parsed); +console.log(made.parsed.command.toUpperCase()); + +const empty: Parsed = { command: "x", rest: [] }; +fill([], empty); +console.log(empty.command, empty.rest.length); diff --git a/tests/corpus/5952-record-missing-keys.ts b/tests/corpus/5952-record-missing-keys.ts new file mode 100644 index 0000000000..69cc78d60d --- /dev/null +++ b/tests/corpus/5952-record-missing-keys.ts @@ -0,0 +1,127 @@ +// A keyed read of a Record or index signature that misses answers +// undefined, as in Node, at module top level and inside functions, without +// noUncheckedIndexedAccess. Static types stay what TypeScript says. +export {}; + +const kp = String(Date.now()); +const r: Record = { [kp]: 1, a: 2 }; +console.log(r["k"], r["a"], r[kp] === 1); +console.log("done"); + +function inside(): void { + const local: Record = { [kp]: 1 }; + console.log(local["k"], local[kp]); +} +inside(); + +// Bindings, reassignment, arithmetic, templates and conversions. +const v = r["missing"]; +console.log(v, typeof v, v === undefined); +let w = r["nope"]; +console.log(w); +w = 5; +console.log(w + 1); +console.log(r["x"] + 1, r["a"] + 1, `${r["x"]}`, String(r["x"]), r["x"] > 1, r["a"] > 1); +console.log(Math.max(r["a"], r["nope"]), isNaN(r["nope"]), r["nope"] == null); + +// Parameters, returns, record fields and array elements. +function take(n: number): string { + return String(n); +} +console.log(take(r["q"]), take(r["a"])); +function give(k: string): number { + return r[k]; +} +console.log(give("a"), give("zz")); +const o = { v: r["zz"] }; +console.log(JSON.stringify(o), o.v); +const arr: number[] = [r["zz"], r["a"]]; +console.log(arr.length, arr[0], arr[1]); + +// Dot reads through the index signature and destructuring. +console.log(r.zz, r.a, r.zz === undefined); +const { a, missing } = r; +console.log(a, missing); + +// Strings, booleans, arrays and unions as index values. +const s: Record = { a: "x" }; +console.log(s["b"], s["a"].toUpperCase(), s["b"]?.length); +try { + console.log(s["b"].length); +} catch (error) { + console.log((error as Error).message); +} +const b: Record = { t: true }; +console.log(b["f"], b["t"], !b["f"], b["f"] ? "y" : "n"); +const nested: Record = { a: [1] }; +console.log(nested["b"], nested["a"].length, nested["b"]?.length); +const mixed: Record = { n: 1, s: "x" }; +const m2 = mixed["q"]; +console.log(mixed["q"], typeof mixed["q"], mixed["n"], m2 === undefined); + +// Interfaces with declared fields and an index signature. +interface Bag { + name: string; + [key: string]: string; +} +const bag: Bag = { name: "n" }; +console.log(bag.name, bag["other"], bag.other); + +// Methods returning a keyed read, and loops over present and missing keys. +class Holder { + table: Record = { x: 1 }; + get(k: string): number { + return this.table[k]; + } +} +const h = new Holder(); +console.log(h.get("x"), h.get("y"), h.table["z"]); +const counts: Record = {}; +for (const word of ["a", "b", "a"]) counts[word] = (counts[word] ?? 0) + 1; +console.log(JSON.stringify(counts), counts["c"]); +let total = 0; +for (const k of ["a", "zz"]) { + console.log(k, r[k], r[k] !== undefined); + total += r[k]; +} +console.log(total); +if (r["zz"] === undefined) console.log("missing zz"); + +// Nested tables: a missing outer entry is undefined, and a member access +// on it throws Node's TypeError after the key evaluates. +const tables: Record> = { m: { one: 1 } }; +console.log(tables["m"]["one"], tables.m.one, tables["m"]["two"]); +tables["m"]["three"] = 3; +tables.m.four = 4; +console.log(JSON.stringify(tables)); +const objs: Record = { a: { b: 1 } }; +objs["a"].b = 5; +console.log(objs["a"].b, objs.a.b); +const dynamicKey = `z${kp.length > 0 ? "z" : ""}`; +for (const attempt of [ + () => objs["zz"].b, + () => tables["zz"]["q"], + () => tables[dynamicKey]["q"], + () => { + tables["zz"]["q"] = 1; + }, + () => { + objs[dynamicKey].b = 2; + }, +]) { + try { + attempt(); + } catch (error) { + console.log(error instanceof TypeError, (error as Error).message); + } +} + +// Calling a function stored under a missing key throws Node's TypeError. +const handlers: Record number> = { double: (n) => n * 2 }; +const handlerName = "double"; +console.log(handlers["double"](4), handlers[handlerName](5)); +try { + console.log(handlers["triple"](4)); +} catch (error) { + console.log(error instanceof TypeError, (error as Error).message); +} diff --git a/tests/corpus/5953-nan-coded-method-returns.ts b/tests/corpus/5953-nan-coded-method-returns.ts new file mode 100644 index 0000000000..823079f034 --- /dev/null +++ b/tests/corpus/5953-nan-coded-method-returns.ts @@ -0,0 +1,44 @@ +// A method that returns a typed-array element or a numeric record entry +// answers undefined for a missing one when its caller can observe it, and +// a plain number when every caller only uses it numerically. +export {}; + +class Samples { + values = new Float64Array([1.5, 2.5]); + at(index: number): number { + return this.values[index]; + } + sum(count: number): number { + let total = 0; + for (let i = 0; i < count; i++) total += this.at(i); + return total; + } +} +const samples = new Samples(); +console.log(samples.at(0), samples.at(9)); +const missing = samples.at(9); +console.log(missing === undefined, typeof missing); +console.log(samples.sum(2), samples.sum(3)); + +class Scores { + table: Record = { ada: 3 }; + score(name: string): number { + return this.table[name]; + } + doubled(name: string): number { + return this.score(name) * 2; + } +} +const scores = new Scores(); +console.log(scores.score("ada"), scores.score("bob")); +console.log(scores.doubled("ada"), scores.doubled("bob")); + +class Readings { + data = new Float64Array(1); + read(index: number) { + return this.data[index]; + } +} +const readings = new Readings(); +const late = readings.read(5); +console.log(late, readings.read(0)); diff --git a/tests/corpus/5960-frame-records-own-references.ts b/tests/corpus/5960-frame-records-own-references.ts new file mode 100644 index 0000000000..894037ff70 --- /dev/null +++ b/tests/corpus/5960-frame-records-own-references.ts @@ -0,0 +1,164 @@ +// Records whose fields hold strings, arrays, other records and class +// instances, and that only flow into field reads, release exactly the +// references they hold: on normal scope exit, per loop iteration, on +// break/continue/return, and when a field expression or a producer +// throws while the record is half built. Records that are stored, +// returned, captured, compared by identity or printed stay observable +// exactly as in Node. +interface Token { + kind: string; + text: string; + pos: number; +} +interface Span { + label: string; + parts: string[]; + token: Token; +} +class Counter { + hits = 0; + name: string; + constructor(name: string) { + this.name = name; + } +} +interface Owned { + owner: Counter; + tag: string | undefined; + note?: string; +} + +const words = ["alpha", "beta", "gamma", "delta"]; +function word(i: number): string { + return words[i % words.length]! + i; +} +function makeToken(kind: string, text: string, pos: number): Token { + return { kind, text, pos }; +} +function pick(flag: boolean, a: string, b: string): Token { + return flag ? { kind: "a", text: a, pos: 1 } : makeToken("b", b, 2); +} +function width(t: Token): number { + return t.text.length + t.kind.length; +} +function spanWidth(s: Span): number { + return s.label.length + s.parts.length + width(s.token); +} +function failing(text: string, n: number): string { + if (n % 4 === 3) throw new Error(`bad ${text}`); + return text.toUpperCase(); +} +function strict(kind: string, n: number): Token { + return { kind, text: failing(kind + n, n), pos: n }; +} +const kept: Token[] = []; +function keep(t: Token): number { + kept.push(t); + return t.pos; +} + +let total = 0; +let log = ""; +for (let i = 0; i < 12; i++) { + const w = word(i); + const t = { kind: "id", text: w, pos: i }; + total += t.pos + t.text.length; + const u = makeToken("num", w + "!", i); + if (u.kind === "num") total += width(u); + total += width({ kind: w, text: w + w, pos: 0 }); + const s: Span = { label: "s" + i, parts: [w, t.text, u.text], token: makeToken("x", w, i) }; + total += spanWidth(s) + s.parts.join("").length; + const p = pick(i % 2 === 0, w, t.kind); + log += p.text[0]; + if (i % 5 === 4) total += keep(t); + try { + const bad = { kind: "k", text: failing(w, i), pos: i }; + log += bad.text.length; + } catch (e) { + log += "!"; + } + try { + const half = strict(w, i); + log += half.text.length; + } catch (e) { + log += "?"; + } + if (i === 6) continue; + const late = { kind: w, text: String(i), pos: -i }; + if (late.text === "9") break; + total += late.kind.length; +} +console.log(total, log, kept.map((t) => t.text).join(",")); + +function find(names: string[], target: string): number { + for (let i = 0; i < names.length; i++) { + const entry = { name: names[i]!, index: i, upper: names[i]!.toUpperCase() }; + if (entry.name === target) return entry.index * 10 + entry.upper.length; + } + return -1; +} +console.log(find(words, "gamma"), find(words, "omega")); + +function owners(n: number): string { + const counters = [new Counter("a"), new Counter("b")]; + let out = ""; + for (let i = 0; i < n; i++) { + const o: Owned = { owner: counters[i % 2]!, tag: i % 3 === 0 ? undefined : "t" + i }; + o.owner.hits++; + out += o.tag ?? "-"; + const optional: Owned = { owner: counters[0]!, tag: "x", note: i % 2 ? "odd" : undefined }; + out += optional.note === undefined ? "." : optional.note[0]; + } + return `${out} ${counters[0]!.hits} ${counters[1]!.hits}`; +} +console.log(owners(7)); + +function tally(lines: string[]): string { + let out = ""; + for (const line of lines) { + const parsed = { head: line.slice(0, 1), rest: line.slice(1), size: line.length }; + switch (parsed.head) { + case "a": { + const scoped = { head: parsed.rest, rest: parsed.head, size: 1 }; + out += scoped.head; + break; + } + case "b": + out += parsed.rest.toUpperCase(); + break; + default: + out += parsed.size; + } + } + return out; +} +console.log(tally(["abc", "bcd", "cde", "a"])); + +function guarded(n: number): string { + let out = ""; + for (let i = 0; i < n; i++) { + try { + const r = { name: word(i), list: [i, i + 1] }; + if (i % 2 === 0) throw new Error(r.name); + out += r.list.length; + } catch (e) { + out += (e as Error).message.length; + } finally { + const f = { name: "f" + i, list: [i] }; + out += f.name.length; + } + } + return out; +} +console.log(guarded(5)); + +// Identity, storage, capture and printing keep heap records. +const a = { kind: "same", text: "x", pos: 1 }; +const b = { kind: "same", text: "x", pos: 1 }; +const readers: (() => string)[] = []; +for (let i = 0; i < 3; i++) { + const r = { kind: "r", text: word(i), pos: i }; + readers.push(() => r.text); +} +console.log(a === b, readers.map((read) => read()).join(" ")); +console.log({ kind: "shown", text: word(2), pos: 3 }, JSON.stringify(makeToken("j", "s", 1))); diff --git a/tests/corpus/5961-frame-spread-updates.ts b/tests/corpus/5961-frame-spread-updates.ts new file mode 100644 index 0000000000..ef2f79cd9a --- /dev/null +++ b/tests/corpus/5961-frame-spread-updates.ts @@ -0,0 +1,96 @@ +// Same-shape spread updates (`{ ...state, field: value }`) of records that +// hold strings and arrays: each update reads the whole old value before +// any override evaluates, releases the replaced fields exactly once, and +// keeps the previous value when an override throws. Aliases, identity, +// storage and printing keep the heap object Node would have. +interface State { + name: string; + count: number; + items: string[]; + last: string | undefined; +} +interface Num { + count: number; + total: number; +} +function bump(s: State, item: string): State { + return { ...s, count: s.count + 1, last: item }; +} +function rename(s: State, name: string): State { + return name.length > 3 ? { ...s, name } : s.count > 2 ? { ...s, name: name + "!" } : bump(s, name); +} +function check(n: number): number { + if (n === 3) throw new Error("three"); + return n * 2; +} +function describe(s: State): string { + return `${s.name}:${s.count}:${s.items.length}:${s.last ?? "-"}`; +} + +function run(n: number): string { + let state: State = { name: "s", count: 0, items: ["a"], last: undefined }; + let out = ""; + for (let i = 0; i < n; i++) { + state = { ...state, count: state.count + 1 }; + if (i % 2 === 0) state = { ...state, last: "i" + i, name: state.name + i }; + state = bump(state, "b" + i); + state = rename(state, i % 3 === 0 ? "long" + i : "x"); + try { + state = { ...state, count: check(i) }; + } catch (e) { + out += "!"; + } + out += describe(state) + " "; + } + return out + state.items.join(""); +} +console.log(run(6)); + +function numeric(n: number): number { + let state: Num = { count: 0, total: 0 }; + for (let i = 0; i < n; i++) { + state = { ...state, count: state.count + 1 }; + if (i % 3 === 0) state = { ...state, total: state.total + i }; + } + return state.count * 1000 + state.total; +} +console.log(numeric(10)); + +// Overrides see the old value and run after the copy. +function order(): string { + let s: State = { name: "n", count: 1, items: [], last: "l" }; + let log = ""; + s = { ...s, name: (log += s.name + s.count, s.name + "2"), count: s.count + 10 }; + s = { ...s, last: s.name }; + return `${log} ${describe(s)}`; +} +console.log(order()); + +// Spread of a temporary and of a producer result. +function fresh(i: number): State { + return { name: "f" + i, count: i, items: [String(i)], last: undefined }; +} +function temporaries(n: number): string { + let out = ""; + for (let i = 0; i < n; i++) { + const s = { ...fresh(i), count: 100 + i }; + const t = { ...s, items: [s.name, s.name] }; + out += describe(s) + "/" + describe(t) + " "; + } + return out; +} +console.log(temporaries(3)); + +// Aliases and identity are observable: these stay heap objects. +let current: State = { name: "c", count: 0, items: [], last: undefined }; +const before = current; +current = { ...current, count: 5 }; +console.log(before === current, before.count, current.count); +const history: State[] = []; +let tracked: State = { name: "h", count: 0, items: [], last: undefined }; +for (let i = 0; i < 3; i++) { + tracked = { ...tracked, count: tracked.count + 1 }; + history.push(tracked); +} +console.log(history.map((s) => s.count).join(","), history[0] === history[1]); +console.log({ ...tracked, last: "shown" }); diff --git a/tests/corpus/5962-frame-tuples.ts b/tests/corpus/5962-frame-tuples.ts new file mode 100644 index 0000000000..b03cbc79d4 --- /dev/null +++ b/tests/corpus/5962-frame-tuples.ts @@ -0,0 +1,57 @@ +// Tuples that are only destructured or indexed: number and string +// elements, producer functions returning fresh tuples, literals passed to +// functions that only read their elements, and tuples that escape into +// arrays, closures, identity comparisons and printing. +function divmod(a: number, b: number): [number, number] { + return [Math.floor(a / b), a % b]; +} +const names = ["a", "bb", "ccc"]; +function entry(i: number): [string, number] { + return [names[i % 3]! + i, i]; +} +function flip(p: [string, number]): [number, string] { + return [p[1] * 2, p[0].toUpperCase()]; +} +function area(r: [number, number]): number { + return r[0] * r[1]; +} +function risky(i: number): [string, string] { + if (i % 4 === 2) throw new Error("risky " + i); + return ["r" + i, String(i * i)]; +} +const kept: [string, number][] = []; + +let acc = 0; +let log = ""; +for (let i = 0; i < 10; i++) { + const [q, r] = divmod(i * 13, 7); + acc += q + r; + const [k, v] = entry(i); + acc += k.length + v; + const pair: [number, number] = [i, i + 1]; + acc += pair[0] * pair[1] + area([i, 2]) + area(pair); + const [n, s] = flip(entry(i + 1)); + log += s + n; + try { + const [x, y] = risky(i); + log += x + y.length; + } catch (e) { + log += "!"; + } + if (i % 3 === 0) kept.push(entry(i)); +} +console.log(acc, log); +console.log(kept.map(([name, i]) => name + "=" + i).join(" ")); + +let [left, right] = ["l", "r"]; +for (let i = 0; i < 3; i++) [left, right] = [right, left + i]; +console.log(left, right); + +const t1: [number, string] = [1, "x"]; +const t2: [number, string] = [1, "x"]; +const getters: (() => string)[] = []; +for (let i = 0; i < 2; i++) { + const t: [string, number] = [names[i]!, i]; + getters.push(() => t[0] + t[1]); +} +console.log(t1 === t2, getters.map((get) => get()).join(","), entry(4), JSON.stringify(divmod(9, 4))); diff --git a/tests/corpus/5973-large-closure-environment.ts b/tests/corpus/5973-large-closure-environment.ts new file mode 100644 index 0000000000..1ade2570c9 --- /dev/null +++ b/tests/corpus/5973-large-closure-environment.ts @@ -0,0 +1,1859 @@ +// One factory with 600 captured bindings (more than any ordinary function holds), +// shared by methods that read and rebind them, closures created per loop +// iteration, a nested closure, and a function declared before the bindings +// it reads. Every environment observes the same boxes: writes through one +// method are visible through the others and in later calls, and each +// factory call gets its own bindings. +function total(list: number[]): number { + let t = 0; + for (const n of list) t += n; + return t; +} +const one = Number("1"); +function make(seed: number) { + function early(): number { + return n0 + n539 + s541.length; + } + let before = "unset"; + try { + before = String(early()); + } catch (e) { + before = (e as Error).name + ": " + (e as Error).message; + } + let n0 = seed + 0; + let n1 = seed + 1; + let n2 = seed + 2; + let n3 = seed + 3; + let n4 = seed + 4; + let n5 = seed + 5; + let n6 = seed + 6; + let n7 = seed + 7; + let n8 = seed + 8; + let n9 = seed + 9; + let n10 = seed + 10; + let n11 = seed + 11; + let n12 = seed + 12; + let n13 = seed + 13; + let n14 = seed + 14; + let n15 = seed + 15; + let n16 = seed + 16; + let n17 = seed + 17; + let n18 = seed + 18; + let n19 = seed + 19; + let n20 = seed + 20; + let n21 = seed + 21; + let n22 = seed + 22; + let n23 = seed + 23; + let n24 = seed + 24; + let n25 = seed + 25; + let n26 = seed + 26; + let n27 = seed + 27; + let n28 = seed + 28; + let n29 = seed + 29; + let n30 = seed + 30; + let n31 = seed + 31; + let n32 = seed + 32; + let n33 = seed + 33; + let n34 = seed + 34; + let n35 = seed + 35; + let n36 = seed + 36; + let n37 = seed + 37; + let n38 = seed + 38; + let n39 = seed + 39; + let n40 = seed + 40; + let n41 = seed + 41; + let n42 = seed + 42; + let n43 = seed + 43; + let n44 = seed + 44; + let n45 = seed + 45; + let n46 = seed + 46; + let n47 = seed + 47; + let n48 = seed + 48; + let n49 = seed + 49; + let n50 = seed + 50; + let n51 = seed + 51; + let n52 = seed + 52; + let n53 = seed + 53; + let n54 = seed + 54; + let n55 = seed + 55; + let n56 = seed + 56; + let n57 = seed + 57; + let n58 = seed + 58; + let n59 = seed + 59; + let n60 = seed + 60; + let n61 = seed + 61; + let n62 = seed + 62; + let n63 = seed + 63; + let n64 = seed + 64; + let n65 = seed + 65; + let n66 = seed + 66; + let n67 = seed + 67; + let n68 = seed + 68; + let n69 = seed + 69; + let n70 = seed + 70; + let n71 = seed + 71; + let n72 = seed + 72; + let n73 = seed + 73; + let n74 = seed + 74; + let n75 = seed + 75; + let n76 = seed + 76; + let n77 = seed + 77; + let n78 = seed + 78; + let n79 = seed + 79; + let n80 = seed + 80; + let n81 = seed + 81; + let n82 = seed + 82; + let n83 = seed + 83; + let n84 = seed + 84; + let n85 = seed + 85; + let n86 = seed + 86; + let n87 = seed + 87; + let n88 = seed + 88; + let n89 = seed + 89; + let n90 = seed + 90; + let n91 = seed + 91; + let n92 = seed + 92; + let n93 = seed + 93; + let n94 = seed + 94; + let n95 = seed + 95; + let n96 = seed + 96; + let n97 = seed + 97; + let n98 = seed + 98; + let n99 = seed + 99; + let n100 = seed + 100; + let n101 = seed + 101; + let n102 = seed + 102; + let n103 = seed + 103; + let n104 = seed + 104; + let n105 = seed + 105; + let n106 = seed + 106; + let n107 = seed + 107; + let n108 = seed + 108; + let n109 = seed + 109; + let n110 = seed + 110; + let n111 = seed + 111; + let n112 = seed + 112; + let n113 = seed + 113; + let n114 = seed + 114; + let n115 = seed + 115; + let n116 = seed + 116; + let n117 = seed + 117; + let n118 = seed + 118; + let n119 = seed + 119; + let n120 = seed + 120; + let n121 = seed + 121; + let n122 = seed + 122; + let n123 = seed + 123; + let n124 = seed + 124; + let n125 = seed + 125; + let n126 = seed + 126; + let n127 = seed + 127; + let n128 = seed + 128; + let n129 = seed + 129; + let n130 = seed + 130; + let n131 = seed + 131; + let n132 = seed + 132; + let n133 = seed + 133; + let n134 = seed + 134; + let n135 = seed + 135; + let n136 = seed + 136; + let n137 = seed + 137; + let n138 = seed + 138; + let n139 = seed + 139; + let n140 = seed + 140; + let n141 = seed + 141; + let n142 = seed + 142; + let n143 = seed + 143; + let n144 = seed + 144; + let n145 = seed + 145; + let n146 = seed + 146; + let n147 = seed + 147; + let n148 = seed + 148; + let n149 = seed + 149; + let n150 = seed + 150; + let n151 = seed + 151; + let n152 = seed + 152; + let n153 = seed + 153; + let n154 = seed + 154; + let n155 = seed + 155; + let n156 = seed + 156; + let n157 = seed + 157; + let n158 = seed + 158; + let n159 = seed + 159; + let n160 = seed + 160; + let n161 = seed + 161; + let n162 = seed + 162; + let n163 = seed + 163; + let n164 = seed + 164; + let n165 = seed + 165; + let n166 = seed + 166; + let n167 = seed + 167; + let n168 = seed + 168; + let n169 = seed + 169; + let n170 = seed + 170; + let n171 = seed + 171; + let n172 = seed + 172; + let n173 = seed + 173; + let n174 = seed + 174; + let n175 = seed + 175; + let n176 = seed + 176; + let n177 = seed + 177; + let n178 = seed + 178; + let n179 = seed + 179; + let n180 = seed + 180; + let n181 = seed + 181; + let n182 = seed + 182; + let n183 = seed + 183; + let n184 = seed + 184; + let n185 = seed + 185; + let n186 = seed + 186; + let n187 = seed + 187; + let n188 = seed + 188; + let n189 = seed + 189; + let n190 = seed + 190; + let n191 = seed + 191; + let n192 = seed + 192; + let n193 = seed + 193; + let n194 = seed + 194; + let n195 = seed + 195; + let n196 = seed + 196; + let n197 = seed + 197; + let n198 = seed + 198; + let n199 = seed + 199; + let n200 = seed + 200; + let n201 = seed + 201; + let n202 = seed + 202; + let n203 = seed + 203; + let n204 = seed + 204; + let n205 = seed + 205; + let n206 = seed + 206; + let n207 = seed + 207; + let n208 = seed + 208; + let n209 = seed + 209; + let n210 = seed + 210; + let n211 = seed + 211; + let n212 = seed + 212; + let n213 = seed + 213; + let n214 = seed + 214; + let n215 = seed + 215; + let n216 = seed + 216; + let n217 = seed + 217; + let n218 = seed + 218; + let n219 = seed + 219; + let n220 = seed + 220; + let n221 = seed + 221; + let n222 = seed + 222; + let n223 = seed + 223; + let n224 = seed + 224; + let n225 = seed + 225; + let n226 = seed + 226; + let n227 = seed + 227; + let n228 = seed + 228; + let n229 = seed + 229; + let n230 = seed + 230; + let n231 = seed + 231; + let n232 = seed + 232; + let n233 = seed + 233; + let n234 = seed + 234; + let n235 = seed + 235; + let n236 = seed + 236; + let n237 = seed + 237; + let n238 = seed + 238; + let n239 = seed + 239; + let n240 = seed + 240; + let n241 = seed + 241; + let n242 = seed + 242; + let n243 = seed + 243; + let n244 = seed + 244; + let n245 = seed + 245; + let n246 = seed + 246; + let n247 = seed + 247; + let n248 = seed + 248; + let n249 = seed + 249; + let n250 = seed + 250; + let n251 = seed + 251; + let n252 = seed + 252; + let n253 = seed + 253; + let n254 = seed + 254; + let n255 = seed + 255; + let n256 = seed + 256; + let n257 = seed + 257; + let n258 = seed + 258; + let n259 = seed + 259; + let n260 = seed + 260; + let n261 = seed + 261; + let n262 = seed + 262; + let n263 = seed + 263; + let n264 = seed + 264; + let n265 = seed + 265; + let n266 = seed + 266; + let n267 = seed + 267; + let n268 = seed + 268; + let n269 = seed + 269; + let n270 = seed + 270; + let n271 = seed + 271; + let n272 = seed + 272; + let n273 = seed + 273; + let n274 = seed + 274; + let n275 = seed + 275; + let n276 = seed + 276; + let n277 = seed + 277; + let n278 = seed + 278; + let n279 = seed + 279; + let n280 = seed + 280; + let n281 = seed + 281; + let n282 = seed + 282; + let n283 = seed + 283; + let n284 = seed + 284; + let n285 = seed + 285; + let n286 = seed + 286; + let n287 = seed + 287; + let n288 = seed + 288; + let n289 = seed + 289; + let n290 = seed + 290; + let n291 = seed + 291; + let n292 = seed + 292; + let n293 = seed + 293; + let n294 = seed + 294; + let n295 = seed + 295; + let n296 = seed + 296; + let n297 = seed + 297; + let n298 = seed + 298; + let n299 = seed + 299; + let n300 = seed + 300; + let n301 = seed + 301; + let n302 = seed + 302; + let n303 = seed + 303; + let n304 = seed + 304; + let n305 = seed + 305; + let n306 = seed + 306; + let n307 = seed + 307; + let n308 = seed + 308; + let n309 = seed + 309; + let n310 = seed + 310; + let n311 = seed + 311; + let n312 = seed + 312; + let n313 = seed + 313; + let n314 = seed + 314; + let n315 = seed + 315; + let n316 = seed + 316; + let n317 = seed + 317; + let n318 = seed + 318; + let n319 = seed + 319; + let n320 = seed + 320; + let n321 = seed + 321; + let n322 = seed + 322; + let n323 = seed + 323; + let n324 = seed + 324; + let n325 = seed + 325; + let n326 = seed + 326; + let n327 = seed + 327; + let n328 = seed + 328; + let n329 = seed + 329; + let n330 = seed + 330; + let n331 = seed + 331; + let n332 = seed + 332; + let n333 = seed + 333; + let n334 = seed + 334; + let n335 = seed + 335; + let n336 = seed + 336; + let n337 = seed + 337; + let n338 = seed + 338; + let n339 = seed + 339; + let n340 = seed + 340; + let n341 = seed + 341; + let n342 = seed + 342; + let n343 = seed + 343; + let n344 = seed + 344; + let n345 = seed + 345; + let n346 = seed + 346; + let n347 = seed + 347; + let n348 = seed + 348; + let n349 = seed + 349; + let n350 = seed + 350; + let n351 = seed + 351; + let n352 = seed + 352; + let n353 = seed + 353; + let n354 = seed + 354; + let n355 = seed + 355; + let n356 = seed + 356; + let n357 = seed + 357; + let n358 = seed + 358; + let n359 = seed + 359; + let n360 = seed + 360; + let n361 = seed + 361; + let n362 = seed + 362; + let n363 = seed + 363; + let n364 = seed + 364; + let n365 = seed + 365; + let n366 = seed + 366; + let n367 = seed + 367; + let n368 = seed + 368; + let n369 = seed + 369; + let n370 = seed + 370; + let n371 = seed + 371; + let n372 = seed + 372; + let n373 = seed + 373; + let n374 = seed + 374; + let n375 = seed + 375; + let n376 = seed + 376; + let n377 = seed + 377; + let n378 = seed + 378; + let n379 = seed + 379; + let n380 = seed + 380; + let n381 = seed + 381; + let n382 = seed + 382; + let n383 = seed + 383; + let n384 = seed + 384; + let n385 = seed + 385; + let n386 = seed + 386; + let n387 = seed + 387; + let n388 = seed + 388; + let n389 = seed + 389; + let n390 = seed + 390; + let n391 = seed + 391; + let n392 = seed + 392; + let n393 = seed + 393; + let n394 = seed + 394; + let n395 = seed + 395; + let n396 = seed + 396; + let n397 = seed + 397; + let n398 = seed + 398; + let n399 = seed + 399; + let n400 = seed + 400; + let n401 = seed + 401; + let n402 = seed + 402; + let n403 = seed + 403; + let n404 = seed + 404; + let n405 = seed + 405; + let n406 = seed + 406; + let n407 = seed + 407; + let n408 = seed + 408; + let n409 = seed + 409; + let n410 = seed + 410; + let n411 = seed + 411; + let n412 = seed + 412; + let n413 = seed + 413; + let n414 = seed + 414; + let n415 = seed + 415; + let n416 = seed + 416; + let n417 = seed + 417; + let n418 = seed + 418; + let n419 = seed + 419; + let n420 = seed + 420; + let n421 = seed + 421; + let n422 = seed + 422; + let n423 = seed + 423; + let n424 = seed + 424; + let n425 = seed + 425; + let n426 = seed + 426; + let n427 = seed + 427; + let n428 = seed + 428; + let n429 = seed + 429; + let n430 = seed + 430; + let n431 = seed + 431; + let n432 = seed + 432; + let n433 = seed + 433; + let n434 = seed + 434; + let n435 = seed + 435; + let n436 = seed + 436; + let n437 = seed + 437; + let n438 = seed + 438; + let n439 = seed + 439; + let n440 = seed + 440; + let n441 = seed + 441; + let n442 = seed + 442; + let n443 = seed + 443; + let n444 = seed + 444; + let n445 = seed + 445; + let n446 = seed + 446; + let n447 = seed + 447; + let n448 = seed + 448; + let n449 = seed + 449; + let n450 = seed + 450; + let n451 = seed + 451; + let n452 = seed + 452; + let n453 = seed + 453; + let n454 = seed + 454; + let n455 = seed + 455; + let n456 = seed + 456; + let n457 = seed + 457; + let n458 = seed + 458; + let n459 = seed + 459; + let n460 = seed + 460; + let n461 = seed + 461; + let n462 = seed + 462; + let n463 = seed + 463; + let n464 = seed + 464; + let n465 = seed + 465; + let n466 = seed + 466; + let n467 = seed + 467; + let n468 = seed + 468; + let n469 = seed + 469; + let n470 = seed + 470; + let n471 = seed + 471; + let n472 = seed + 472; + let n473 = seed + 473; + let n474 = seed + 474; + let n475 = seed + 475; + let n476 = seed + 476; + let n477 = seed + 477; + let n478 = seed + 478; + let n479 = seed + 479; + let n480 = seed + 480; + let n481 = seed + 481; + let n482 = seed + 482; + let n483 = seed + 483; + let n484 = seed + 484; + let n485 = seed + 485; + let n486 = seed + 486; + let n487 = seed + 487; + let n488 = seed + 488; + let n489 = seed + 489; + let n490 = seed + 490; + let n491 = seed + 491; + let n492 = seed + 492; + let n493 = seed + 493; + let n494 = seed + 494; + let n495 = seed + 495; + let n496 = seed + 496; + let n497 = seed + 497; + let n498 = seed + 498; + let n499 = seed + 499; + let n500 = seed + 500; + let n501 = seed + 501; + let n502 = seed + 502; + let n503 = seed + 503; + let n504 = seed + 504; + let n505 = seed + 505; + let n506 = seed + 506; + let n507 = seed + 507; + let n508 = seed + 508; + let n509 = seed + 509; + let n510 = seed + 510; + let n511 = seed + 511; + let n512 = seed + 512; + let n513 = seed + 513; + let n514 = seed + 514; + let n515 = seed + 515; + let n516 = seed + 516; + let n517 = seed + 517; + let n518 = seed + 518; + let n519 = seed + 519; + let n520 = seed + 520; + let n521 = seed + 521; + let n522 = seed + 522; + let n523 = seed + 523; + let n524 = seed + 524; + let n525 = seed + 525; + let n526 = seed + 526; + let n527 = seed + 527; + let n528 = seed + 528; + let n529 = seed + 529; + let n530 = seed + 530; + let n531 = seed + 531; + let n532 = seed + 532; + let n533 = seed + 533; + let n534 = seed + 534; + let n535 = seed + 535; + let n536 = seed + 536; + let n537 = seed + 537; + let n538 = seed + 538; + let n539 = seed + 539; + let o540 = { x: 540 }; + let s541 = "s" + (541 + one); + let o542 = { x: 542 }; + let s543 = "s" + (543 + one); + let o544 = { x: 544 }; + let s545 = "s" + (545 + one); + let o546 = { x: 546 }; + let s547 = "s" + (547 + one); + let o548 = { x: 548 }; + let s549 = "s" + (549 + one); + let o550 = { x: 550 }; + let s551 = "s" + (551 + one); + let o552 = { x: 552 }; + let s553 = "s" + (553 + one); + let o554 = { x: 554 }; + let s555 = "s" + (555 + one); + let o556 = { x: 556 }; + let s557 = "s" + (557 + one); + let o558 = { x: 558 }; + let s559 = "s" + (559 + one); + let o560 = { x: 560 }; + let s561 = "s" + (561 + one); + let o562 = { x: 562 }; + let s563 = "s" + (563 + one); + let o564 = { x: 564 }; + let s565 = "s" + (565 + one); + let o566 = { x: 566 }; + let s567 = "s" + (567 + one); + let o568 = { x: 568 }; + let s569 = "s" + (569 + one); + let o570 = { x: 570 }; + let s571 = "s" + (571 + one); + let o572 = { x: 572 }; + let s573 = "s" + (573 + one); + let o574 = { x: 574 }; + let s575 = "s" + (575 + one); + let o576 = { x: 576 }; + let s577 = "s" + (577 + one); + let o578 = { x: 578 }; + let s579 = "s" + (579 + one); + let o580 = { x: 580 }; + let s581 = "s" + (581 + one); + let o582 = { x: 582 }; + let s583 = "s" + (583 + one); + let o584 = { x: 584 }; + let s585 = "s" + (585 + one); + let o586 = { x: 586 }; + let s587 = "s" + (587 + one); + let o588 = { x: 588 }; + let s589 = "s" + (589 + one); + let o590 = { x: 590 }; + let s591 = "s" + (591 + one); + let o592 = { x: 592 }; + let s593 = "s" + (593 + one); + let o594 = { x: 594 }; + let s595 = "s" + (595 + one); + let o596 = { x: 596 }; + let s597 = "s" + (597 + one); + let o598 = { x: 598 }; + let s599 = "s" + (599 + one); + const readers: (() => string)[] = []; + for (let i = 0; i < 3; i++) { + const label = "r" + i; + readers.push(() => label + ":" + (n539 + i) + ":" + o540.x + ":" + s541); + } + return { + before, + early, + sum(): number { + return total([ + n0, + n1, + n2, + n3, + n4, + n5, + n6, + n7, + n8, + n9, + n10, + n11, + n12, + n13, + n14, + n15, + n16, + n17, + n18, + n19, + n20, + n21, + n22, + n23, + n24, + n25, + n26, + n27, + n28, + n29, + n30, + n31, + n32, + n33, + n34, + n35, + n36, + n37, + n38, + n39, + n40, + n41, + n42, + n43, + n44, + n45, + n46, + n47, + n48, + n49, + n50, + n51, + n52, + n53, + n54, + n55, + n56, + n57, + n58, + n59, + n60, + n61, + n62, + n63, + n64, + n65, + n66, + n67, + n68, + n69, + n70, + n71, + n72, + n73, + n74, + n75, + n76, + n77, + n78, + n79, + n80, + n81, + n82, + n83, + n84, + n85, + n86, + n87, + n88, + n89, + n90, + n91, + n92, + n93, + n94, + n95, + n96, + n97, + n98, + n99, + n100, + n101, + n102, + n103, + n104, + n105, + n106, + n107, + n108, + n109, + n110, + n111, + n112, + n113, + n114, + n115, + n116, + n117, + n118, + n119, + n120, + n121, + n122, + n123, + n124, + n125, + n126, + n127, + n128, + n129, + n130, + n131, + n132, + n133, + n134, + n135, + n136, + n137, + n138, + n139, + n140, + n141, + n142, + n143, + n144, + n145, + n146, + n147, + n148, + n149, + n150, + n151, + n152, + n153, + n154, + n155, + n156, + n157, + n158, + n159, + n160, + n161, + n162, + n163, + n164, + n165, + n166, + n167, + n168, + n169, + n170, + n171, + n172, + n173, + n174, + n175, + n176, + n177, + n178, + n179, + n180, + n181, + n182, + n183, + n184, + n185, + n186, + n187, + n188, + n189, + n190, + n191, + n192, + n193, + n194, + n195, + n196, + n197, + n198, + n199, + n200, + n201, + n202, + n203, + n204, + n205, + n206, + n207, + n208, + n209, + n210, + n211, + n212, + n213, + n214, + n215, + n216, + n217, + n218, + n219, + n220, + n221, + n222, + n223, + n224, + n225, + n226, + n227, + n228, + n229, + n230, + n231, + n232, + n233, + n234, + n235, + n236, + n237, + n238, + n239, + n240, + n241, + n242, + n243, + n244, + n245, + n246, + n247, + n248, + n249, + n250, + n251, + n252, + n253, + n254, + n255, + n256, + n257, + n258, + n259, + n260, + n261, + n262, + n263, + n264, + n265, + n266, + n267, + n268, + n269, + n270, + n271, + n272, + n273, + n274, + n275, + n276, + n277, + n278, + n279, + n280, + n281, + n282, + n283, + n284, + n285, + n286, + n287, + n288, + n289, + n290, + n291, + n292, + n293, + n294, + n295, + n296, + n297, + n298, + n299, + n300, + n301, + n302, + n303, + n304, + n305, + n306, + n307, + n308, + n309, + n310, + n311, + n312, + n313, + n314, + n315, + n316, + n317, + n318, + n319, + n320, + n321, + n322, + n323, + n324, + n325, + n326, + n327, + n328, + n329, + n330, + n331, + n332, + n333, + n334, + n335, + n336, + n337, + n338, + n339, + n340, + n341, + n342, + n343, + n344, + n345, + n346, + n347, + n348, + n349, + n350, + n351, + n352, + n353, + n354, + n355, + n356, + n357, + n358, + n359, + n360, + n361, + n362, + n363, + n364, + n365, + n366, + n367, + n368, + n369, + n370, + n371, + n372, + n373, + n374, + n375, + n376, + n377, + n378, + n379, + n380, + n381, + n382, + n383, + n384, + n385, + n386, + n387, + n388, + n389, + n390, + n391, + n392, + n393, + n394, + n395, + n396, + n397, + n398, + n399, + n400, + n401, + n402, + n403, + n404, + n405, + n406, + n407, + n408, + n409, + n410, + n411, + n412, + n413, + n414, + n415, + n416, + n417, + n418, + n419, + n420, + n421, + n422, + n423, + n424, + n425, + n426, + n427, + n428, + n429, + n430, + n431, + n432, + n433, + n434, + n435, + n436, + n437, + n438, + n439, + n440, + n441, + n442, + n443, + n444, + n445, + n446, + n447, + n448, + n449, + n450, + n451, + n452, + n453, + n454, + n455, + n456, + n457, + n458, + n459, + n460, + n461, + n462, + n463, + n464, + n465, + n466, + n467, + n468, + n469, + n470, + n471, + n472, + n473, + n474, + n475, + n476, + n477, + n478, + n479, + n480, + n481, + n482, + n483, + n484, + n485, + n486, + n487, + n488, + n489, + n490, + n491, + n492, + n493, + n494, + n495, + n496, + n497, + n498, + n499, + n500, + n501, + n502, + n503, + n504, + n505, + n506, + n507, + n508, + n509, + n510, + n511, + n512, + n513, + n514, + n515, + n516, + n517, + n518, + n519, + n520, + n521, + n522, + n523, + n524, + n525, + n526, + n527, + n528, + n529, + n530, + n531, + n532, + n533, + n534, + n535, + n536, + n537, + n538, + n539, + o540.x, + s541.length, + o542.x, + s543.length, + o544.x, + s545.length, + o546.x, + s547.length, + o548.x, + s549.length, + o550.x, + s551.length, + o552.x, + s553.length, + o554.x, + s555.length, + o556.x, + s557.length, + o558.x, + s559.length, + o560.x, + s561.length, + o562.x, + s563.length, + o564.x, + s565.length, + o566.x, + s567.length, + o568.x, + s569.length, + o570.x, + s571.length, + o572.x, + s573.length, + o574.x, + s575.length, + o576.x, + s577.length, + o578.x, + s579.length, + o580.x, + s581.length, + o582.x, + s583.length, + o584.x, + s585.length, + o586.x, + s587.length, + o588.x, + s589.length, + o590.x, + s591.length, + o592.x, + s593.length, + o594.x, + s595.length, + o596.x, + s597.length, + o598.x, + s599.length, + ]); + }, + bump(): void { + n0 += 1; + n1 += 1; + n2 += 1; + n3 += 1; + n4 += 1; + n5 += 1; + n6 += 1; + n7 += 1; + n8 += 1; + n9 += 1; + n10 += 1; + n11 += 1; + n12 += 1; + n13 += 1; + n14 += 1; + n15 += 1; + n16 += 1; + n17 += 1; + n18 += 1; + n19 += 1; + n20 += 1; + n21 += 1; + n22 += 1; + n23 += 1; + n24 += 1; + n25 += 1; + n26 += 1; + n27 += 1; + n28 += 1; + n29 += 1; + n30 += 1; + n31 += 1; + n32 += 1; + n33 += 1; + n34 += 1; + n35 += 1; + n36 += 1; + n37 += 1; + n38 += 1; + n39 += 1; + n40 += 1; + n41 += 1; + n42 += 1; + n43 += 1; + n44 += 1; + n45 += 1; + n46 += 1; + n47 += 1; + n48 += 1; + n49 += 1; + n50 += 1; + n51 += 1; + n52 += 1; + n53 += 1; + n54 += 1; + n55 += 1; + n56 += 1; + n57 += 1; + n58 += 1; + n59 += 1; + n60 += 1; + n61 += 1; + n62 += 1; + n63 += 1; + n64 += 1; + n65 += 1; + n66 += 1; + n67 += 1; + n68 += 1; + n69 += 1; + n70 += 1; + n71 += 1; + n72 += 1; + n73 += 1; + n74 += 1; + n75 += 1; + n76 += 1; + n77 += 1; + n78 += 1; + n79 += 1; + n80 += 1; + n81 += 1; + n82 += 1; + n83 += 1; + n84 += 1; + n85 += 1; + n86 += 1; + n87 += 1; + n88 += 1; + n89 += 1; + n90 += 1; + n91 += 1; + n92 += 1; + n93 += 1; + n94 += 1; + n95 += 1; + n96 += 1; + n97 += 1; + n98 += 1; + n99 += 1; + n100 += 1; + n101 += 1; + n102 += 1; + n103 += 1; + n104 += 1; + n105 += 1; + n106 += 1; + n107 += 1; + n108 += 1; + n109 += 1; + n110 += 1; + n111 += 1; + n112 += 1; + n113 += 1; + n114 += 1; + n115 += 1; + n116 += 1; + n117 += 1; + n118 += 1; + n119 += 1; + n120 += 1; + n121 += 1; + n122 += 1; + n123 += 1; + n124 += 1; + n125 += 1; + n126 += 1; + n127 += 1; + n128 += 1; + n129 += 1; + n130 += 1; + n131 += 1; + n132 += 1; + n133 += 1; + n134 += 1; + n135 += 1; + n136 += 1; + n137 += 1; + n138 += 1; + n139 += 1; + n140 += 1; + n141 += 1; + n142 += 1; + n143 += 1; + n144 += 1; + n145 += 1; + n146 += 1; + n147 += 1; + n148 += 1; + n149 += 1; + n150 += 1; + n151 += 1; + n152 += 1; + n153 += 1; + n154 += 1; + n155 += 1; + n156 += 1; + n157 += 1; + n158 += 1; + n159 += 1; + n160 += 1; + n161 += 1; + n162 += 1; + n163 += 1; + n164 += 1; + n165 += 1; + n166 += 1; + n167 += 1; + n168 += 1; + n169 += 1; + n170 += 1; + n171 += 1; + n172 += 1; + n173 += 1; + n174 += 1; + n175 += 1; + n176 += 1; + n177 += 1; + n178 += 1; + n179 += 1; + n180 += 1; + n181 += 1; + n182 += 1; + n183 += 1; + n184 += 1; + n185 += 1; + n186 += 1; + n187 += 1; + n188 += 1; + n189 += 1; + n190 += 1; + n191 += 1; + n192 += 1; + n193 += 1; + n194 += 1; + n195 += 1; + n196 += 1; + n197 += 1; + n198 += 1; + n199 += 1; + n200 += 1; + n201 += 1; + n202 += 1; + n203 += 1; + n204 += 1; + n205 += 1; + n206 += 1; + n207 += 1; + n208 += 1; + n209 += 1; + n210 += 1; + n211 += 1; + n212 += 1; + n213 += 1; + n214 += 1; + n215 += 1; + n216 += 1; + n217 += 1; + n218 += 1; + n219 += 1; + n220 += 1; + n221 += 1; + n222 += 1; + n223 += 1; + n224 += 1; + n225 += 1; + n226 += 1; + n227 += 1; + n228 += 1; + n229 += 1; + n230 += 1; + n231 += 1; + n232 += 1; + n233 += 1; + n234 += 1; + n235 += 1; + n236 += 1; + n237 += 1; + n238 += 1; + n239 += 1; + n240 += 1; + n241 += 1; + n242 += 1; + n243 += 1; + n244 += 1; + n245 += 1; + n246 += 1; + n247 += 1; + n248 += 1; + n249 += 1; + n250 += 1; + n251 += 1; + n252 += 1; + n253 += 1; + n254 += 1; + n255 += 1; + n256 += 1; + n257 += 1; + n258 += 1; + n259 += 1; + n260 += 1; + n261 += 1; + n262 += 1; + n263 += 1; + n264 += 1; + n265 += 1; + n266 += 1; + n267 += 1; + n268 += 1; + n269 += 1; + n270 += 1; + n271 += 1; + n272 += 1; + n273 += 1; + n274 += 1; + n275 += 1; + n276 += 1; + n277 += 1; + n278 += 1; + n279 += 1; + n280 += 1; + n281 += 1; + n282 += 1; + n283 += 1; + n284 += 1; + n285 += 1; + n286 += 1; + n287 += 1; + n288 += 1; + n289 += 1; + n290 += 1; + n291 += 1; + n292 += 1; + n293 += 1; + n294 += 1; + n295 += 1; + n296 += 1; + n297 += 1; + n298 += 1; + n299 += 1; + n300 += 1; + n301 += 1; + n302 += 1; + n303 += 1; + n304 += 1; + n305 += 1; + n306 += 1; + n307 += 1; + n308 += 1; + n309 += 1; + n310 += 1; + n311 += 1; + n312 += 1; + n313 += 1; + n314 += 1; + n315 += 1; + n316 += 1; + n317 += 1; + n318 += 1; + n319 += 1; + n320 += 1; + n321 += 1; + n322 += 1; + n323 += 1; + n324 += 1; + n325 += 1; + n326 += 1; + n327 += 1; + n328 += 1; + n329 += 1; + n330 += 1; + n331 += 1; + n332 += 1; + n333 += 1; + n334 += 1; + n335 += 1; + n336 += 1; + n337 += 1; + n338 += 1; + n339 += 1; + n340 += 1; + n341 += 1; + n342 += 1; + n343 += 1; + n344 += 1; + n345 += 1; + n346 += 1; + n347 += 1; + n348 += 1; + n349 += 1; + n350 += 1; + n351 += 1; + n352 += 1; + n353 += 1; + n354 += 1; + n355 += 1; + n356 += 1; + n357 += 1; + n358 += 1; + n359 += 1; + n360 += 1; + n361 += 1; + n362 += 1; + n363 += 1; + n364 += 1; + n365 += 1; + n366 += 1; + n367 += 1; + n368 += 1; + n369 += 1; + n370 += 1; + n371 += 1; + n372 += 1; + n373 += 1; + n374 += 1; + n375 += 1; + n376 += 1; + n377 += 1; + n378 += 1; + n379 += 1; + n380 += 1; + n381 += 1; + n382 += 1; + n383 += 1; + n384 += 1; + n385 += 1; + n386 += 1; + n387 += 1; + n388 += 1; + n389 += 1; + n390 += 1; + n391 += 1; + n392 += 1; + n393 += 1; + n394 += 1; + n395 += 1; + n396 += 1; + n397 += 1; + n398 += 1; + n399 += 1; + n400 += 1; + n401 += 1; + n402 += 1; + n403 += 1; + n404 += 1; + n405 += 1; + n406 += 1; + n407 += 1; + n408 += 1; + n409 += 1; + n410 += 1; + n411 += 1; + n412 += 1; + n413 += 1; + n414 += 1; + n415 += 1; + n416 += 1; + n417 += 1; + n418 += 1; + n419 += 1; + n420 += 1; + n421 += 1; + n422 += 1; + n423 += 1; + n424 += 1; + n425 += 1; + n426 += 1; + n427 += 1; + n428 += 1; + n429 += 1; + n430 += 1; + n431 += 1; + n432 += 1; + n433 += 1; + n434 += 1; + n435 += 1; + n436 += 1; + n437 += 1; + n438 += 1; + n439 += 1; + n440 += 1; + n441 += 1; + n442 += 1; + n443 += 1; + n444 += 1; + n445 += 1; + n446 += 1; + n447 += 1; + n448 += 1; + n449 += 1; + n450 += 1; + n451 += 1; + n452 += 1; + n453 += 1; + n454 += 1; + n455 += 1; + n456 += 1; + n457 += 1; + n458 += 1; + n459 += 1; + n460 += 1; + n461 += 1; + n462 += 1; + n463 += 1; + n464 += 1; + n465 += 1; + n466 += 1; + n467 += 1; + n468 += 1; + n469 += 1; + n470 += 1; + n471 += 1; + n472 += 1; + n473 += 1; + n474 += 1; + n475 += 1; + n476 += 1; + n477 += 1; + n478 += 1; + n479 += 1; + n480 += 1; + n481 += 1; + n482 += 1; + n483 += 1; + n484 += 1; + n485 += 1; + n486 += 1; + n487 += 1; + n488 += 1; + n489 += 1; + n490 += 1; + n491 += 1; + n492 += 1; + n493 += 1; + n494 += 1; + n495 += 1; + n496 += 1; + n497 += 1; + n498 += 1; + n499 += 1; + n500 += 1; + n501 += 1; + n502 += 1; + n503 += 1; + n504 += 1; + n505 += 1; + n506 += 1; + n507 += 1; + n508 += 1; + n509 += 1; + n510 += 1; + n511 += 1; + n512 += 1; + n513 += 1; + n514 += 1; + n515 += 1; + n516 += 1; + n517 += 1; + n518 += 1; + n519 += 1; + n520 += 1; + n521 += 1; + n522 += 1; + n523 += 1; + n524 += 1; + n525 += 1; + n526 += 1; + n527 += 1; + n528 += 1; + n529 += 1; + n530 += 1; + n531 += 1; + n532 += 1; + n533 += 1; + n534 += 1; + n535 += 1; + n536 += 1; + n537 += 1; + n538 += 1; + n539 += 1; + o540 = { x: o540.x + 1 }; + s541 += "!"; + o542 = { x: o542.x + 1 }; + s543 += "!"; + o544 = { x: o544.x + 1 }; + s545 += "!"; + o546 = { x: o546.x + 1 }; + s547 += "!"; + o548 = { x: o548.x + 1 }; + s549 += "!"; + o550 = { x: o550.x + 1 }; + s551 += "!"; + o552 = { x: o552.x + 1 }; + s553 += "!"; + o554 = { x: o554.x + 1 }; + s555 += "!"; + o556 = { x: o556.x + 1 }; + s557 += "!"; + o558 = { x: o558.x + 1 }; + s559 += "!"; + o560 = { x: o560.x + 1 }; + s561 += "!"; + o562 = { x: o562.x + 1 }; + s563 += "!"; + o564 = { x: o564.x + 1 }; + s565 += "!"; + o566 = { x: o566.x + 1 }; + s567 += "!"; + o568 = { x: o568.x + 1 }; + s569 += "!"; + o570 = { x: o570.x + 1 }; + s571 += "!"; + o572 = { x: o572.x + 1 }; + s573 += "!"; + o574 = { x: o574.x + 1 }; + s575 += "!"; + o576 = { x: o576.x + 1 }; + s577 += "!"; + o578 = { x: o578.x + 1 }; + s579 += "!"; + o580 = { x: o580.x + 1 }; + s581 += "!"; + o582 = { x: o582.x + 1 }; + s583 += "!"; + o584 = { x: o584.x + 1 }; + s585 += "!"; + o586 = { x: o586.x + 1 }; + s587 += "!"; + o588 = { x: o588.x + 1 }; + s589 += "!"; + o590 = { x: o590.x + 1 }; + s591 += "!"; + o592 = { x: o592.x + 1 }; + s593 += "!"; + o594 = { x: o594.x + 1 }; + s595 += "!"; + o596 = { x: o596.x + 1 }; + s597 += "!"; + o598 = { x: o598.x + 1 }; + s599 += "!"; + }, + readers, + nested(): () => string { + let calls = 0; + return () => { + calls++; + n0 += 10; + return calls + "/" + n0 + "/" + s599; + }; + }, + }; +} +const a = make(0); +const b = make(100); +console.log(a.before); +console.log(a.sum(), b.sum(), a.early()); +a.bump(); +a.bump(); +console.log(a.sum(), b.sum(), a.early()); +console.log(a.readers.map((r) => r()).join(" ")); +const next = a.nested(); +console.log(next(), next(), a.sum()); +b.bump(); +console.log(b.readers.map((r) => r()).join(" "), b.early()); diff --git a/tests/corpus/5975-zlib-class-values.ts b/tests/corpus/5975-zlib-class-values.ts new file mode 100644 index 0000000000..f46d7d5d4d --- /dev/null +++ b/tests/corpus/5975-zlib-class-values.ts @@ -0,0 +1,52 @@ +// The zlib stream classes as values: const aliases construct, instanceof +// tests each class against stream, EventEmitter and plain operands, and +// typeof, .name and === answer as in Node. +import * as zlib from "node:zlib"; +import { Gunzip, Unzip } from "node:zlib"; +import { Readable, Transform } from "node:stream"; +import { EventEmitter } from "node:events"; + +function makeLate() { + return new Late(); +} + +const Gz = zlib.Gzip; +const Gun = Gunzip; +const Again = Gz; +const Late = zlib.Inflate; + +const gz = new Gz(); +console.log("new alias", gz instanceof zlib.Gzip, gz instanceof Gz, gz instanceof Again); +console.log("other classes", gz instanceof Gun, gz instanceof Gunzip, gz instanceof zlib.Deflate); +console.log("factory", zlib.createGzip() instanceof Gz, zlib.createGunzip() instanceof Gz); +console.log("hoisted use", makeLate() instanceof zlib.Inflate, makeLate() instanceof Gun); + +const asTransform: Transform = new zlib.DeflateRaw(); +console.log("transform-typed", asTransform instanceof zlib.DeflateRaw, asTransform instanceof Gz); +const plain = new Transform({ + transform(chunk, _encoding, callback) { + callback(null, chunk); + }, +}); +console.log("plain transform", plain instanceof Gz, plain instanceof Transform); +const readable = new Readable({ read() {} }); +console.log("readable", readable instanceof zlib.Unzip); + +const emitter: EventEmitter = zlib.createDeflate(); +console.log("emitter-typed", emitter instanceof zlib.Deflate, emitter instanceof zlib.Inflate); +const bare = new EventEmitter(); +console.log("bare emitter", bare instanceof zlib.Deflate); +const record = { kind: "gzip" }; +console.log("object", record instanceof Gz); + +console.log("typeof", typeof Gz, typeof zlib.Inflate, typeof Unzip); +console.log("name", Gz.name, Again.name, Gun.name, zlib.DeflateRaw.name, Unzip.name); +console.log("identity", Gz === zlib.Gzip, Again === Gz, Gz === Gun, Gz !== zlib.Gunzip); + +const chunks: Buffer[] = []; +const roundTrip = new Again(); +roundTrip.on("data", (chunk: Buffer) => chunks.push(chunk)); +roundTrip.on("end", () => { + console.log("round trip", zlib.gunzipSync(Buffer.concat(chunks)).toString()); +}); +roundTrip.end("class values"); diff --git a/tests/corpus/5993-array-length-loop-resizing.ts b/tests/corpus/5993-array-length-loop-resizing.ts new file mode 100644 index 0000000000..a1d5f6d114 --- /dev/null +++ b/tests/corpus/5993-array-length-loop-resizing.ts @@ -0,0 +1,167 @@ +// Index loops bounded by an array's length whose bodies resize that array, +// directly, through an alias, a helper or a closure: the bound is re-read on +// every pass, exactly like Node. + +function pushInBody(): string { + const a = [1, 2, 3]; + const seen: number[] = []; + for (let i = 0; i < a.length; i++) { + seen.push(a[i]); + if (a.length < 6) a.push(a[i] * 10); + } + return `${seen.join(",")} | ${a.length}`; +} + +function stridedPush(): string { + const a = [0, 1, 2, 3]; + let visits = 0; + for (let i = 0; i < a.length; i += 2) { + visits++; + if (a.length < 9) a.push(i); + } + return `${visits} ${a.join(",")}`; +} + +function truncateInBody(): string { + const a = [5, 6, 7, 8, 9, 10]; + let sum = 0; + for (let i = 0; i < a.length; i++) { + sum += a[i]; + if (i === 2) a.length = 4; + } + return `${sum} ${a.length}`; +} + +function growLengthInBody(): string { + const a = [1, 2]; + let holes = 0; + for (let i = 0; i < a.length; i++) { + if (a[i] === undefined) holes++; + if (i === 0) a.length = 5; + } + return `${holes} ${a.length}`; +} + +function helperPush(xs: number[], v: number): void { + if (xs.length < 7) xs.push(v); +} + +function pushThroughHelper(): string { + const a = [1, 2, 3]; + let sum = 0; + for (let i = 0; i < a.length; i++) { + sum += a[i]; + helperPush(a, i + 100); + } + return `${sum} ${a.length}`; +} + +function pushThroughClosure(): string { + const a = [1, 2, 3]; + const grow = (v: number): void => { + if (a.length < 6) a.push(v); + }; + let sum = 0; + for (let i = 0; i < a.length; i++) { + sum += a[i]; + grow(i); + } + return `${sum} ${a.length}`; +} + +function pushThroughAlias(): string { + const a = [1, 2, 3]; + const alias = a; + let sum = 0; + for (let i = 0; i < a.length; i++) { + sum += a[i]; + if (alias.length < 8) alias.push(i * 2); + } + return `${sum} ${a.length}`; +} + +function aliasedParameters(a: number[], out: number[]): string { + // `out` may be `a` itself. + let n = 0; + for (let i = 0; i < a.length; i++) { + n++; + if (out.length < 10) out.push(a[i]); + } + return `${n} ${a.length} ${out.length}`; +} + +function writePastEnd(): string { + const a = [1, 2, 3]; + let passes = 0; + for (let i = 0; i < a.length; i++) { + passes++; + if (i + 1 === a.length && a.length < 6) a[i + 1] = i; + } + return `${passes} ${a.join(",")}`; +} + +function shiftInBody(): string { + const a = [1, 2, 3, 4, 5, 6]; + const seen: number[] = []; + for (let i = 0; i < a.length; i++) { + seen.push(a[i]); + a.shift(); + } + return `${seen.join(",")} | ${a.join(",")}`; +} + +function spliceInDescendingLoop(): string { + const a = [1, 2, 3, 4, 5, 6, 7]; + for (let i = a.length - 1; i >= 0; i--) if (a[i] % 2 === 0) a.splice(i, 1); + return a.join(","); +} + +function whilePush(): string { + const a = [3, 1]; + let i = 0; + let sum = 0; + while (i < a.length) { + sum += a[i]; + if (a.length < 5) a.push(sum); + i++; + } + return `${sum} ${i} ${a.join(",")}`; +} + +function nestedResize(): string { + const rows = [[1], [2, 3], [4, 5, 6]]; + let total = 0; + for (let r = 0; r < rows.length; r++) { + const row = rows[r]; + for (let c = 0; c < row.length; c++) { + total += row[c]; + if (row.length < 3) row.push(c); + } + } + return `${total} ${rows.map((row) => row.length).join(",")}`; +} + +function pushIntoOther(): string { + const a = [2, 4, 6]; + const out: number[] = []; + for (let i = 0; i < a.length; i++) out.push(a[i] + 1); + for (let i = 0; i < out.length; i += 2) out.push(-1); + return `${out.join(",")}`; +} + +console.log(pushInBody()); +console.log(stridedPush()); +console.log(truncateInBody()); +console.log(growLengthInBody()); +console.log(pushThroughHelper()); +console.log(pushThroughClosure()); +console.log(pushThroughAlias()); +const shared = [7, 8, 9]; +console.log(aliasedParameters(shared, shared)); +console.log(aliasedParameters([7, 8, 9], [])); +console.log(writePastEnd()); +console.log(shiftInBody()); +console.log(spliceInDescendingLoop()); +console.log(whilePush()); +console.log(nestedResize()); +console.log(pushIntoOther()); diff --git a/tests/corpus/5994-array-length-sparse-loops.ts b/tests/corpus/5994-array-length-sparse-loops.ts new file mode 100644 index 0000000000..589c1df60c --- /dev/null +++ b/tests/corpus/5994-array-length-sparse-loops.ts @@ -0,0 +1,133 @@ +// Index loops over sparse arrays with lengths beyond 2^31, holes, and arrays +// that become sparse while a loop runs. Dense-loop fast paths must leave all +// of these on the general path. + +function partialAscending(a: number[], limit: number): string { + let sum = 0; + let passes = 0; + for (let i = 0; i < a.length; i++) { + if (passes++ === limit) break; + sum += a[i] === undefined ? -1 : a[i]; + } + return `${passes} ${sum}`; +} + +function partialDescending(a: number[], limit: number): string { + let last = -1; + let passes = 0; + for (let i = a.length - 1; i >= 0; i--) { + if (passes++ === limit) break; + last = i; + } + return `${passes} ${last}`; +} + +function stridedFromEnd(a: number[]): string { + const hits: number[] = []; + for (let i = a.length - 1; i >= 0 && hits.length < 3; i -= 1000000000) hits.push(i); + return hits.join(","); +} + +function whileWithLimit(a: number[], limit: number): number { + let i = 0; + let present = 0; + while (i < a.length && i < limit) { + if (a[i] !== undefined) present++; + i++; + } + return present * 1000 + i; +} + +function writeAtCounter(a: number[], limit: number): string { + for (let i = 0; i < a.length && i < limit; i++) a[i] = i * 2; + return `${a.length} ${a[0]} ${a[limit - 1]} ${a[limit]}`; +} + +function holeSum(a: number[]): string { + let sum = 0; + let nan = 0; + for (let i = 0; i < a.length; i++) { + const v = a[i]; + if (Number.isNaN(v + 0)) nan++; + else sum += v; + } + return `${sum} ${nan}`; +} + +function firstValues(a: number[]): string { + let s = 0; + for (let i = 0; i < a.length; i++) { + if (i === 4) break; + s += a[i]; + } + let t = 0; + for (let i = a.length - 1; i >= 0; i--) { + if (i < a.length - 2) break; + t += a[i]; + } + let u = 0; + for (let i = 0; i < a.length; i += 2) { + if (i > 2) break; + u += a[i]; + } + return `${s} ${t} ${u}`; +} + +const huge: number[] = [1, 2, 3]; +huge.length = 3e9; +console.log(huge.length); +console.log(partialAscending(huge, 5)); +console.log(firstValues(huge), firstValues([1, 2, 3, 4, 5])); +console.log(partialDescending(huge, 4)); +console.log(stridedFromEnd(huge)); +console.log(whileWithLimit(huge, 6)); +console.log(writeAtCounter(huge, 4)); +console.log(huge.length); + +const edge: number[] = []; +edge[4294967294] = 7; +console.log(edge.length, partialDescending(edge, 2), partialAscending(edge, 3)); + +const holey = [1, 2]; +holey[3] = 4; +holey[5] = 6; +console.log(holeSum(holey)); +const sparse: number[] = [1, 2]; +sparse[10] = 5; +console.log(holeSum(sparse)); + +// Becomes sparse mid-loop: a write far past the end through another binding. +function sparseMidLoop(): string { + const a = [1, 2, 3, 4]; + const b = a; + let sum = 0; + for (let i = 0; i < a.length; i++) { + if (i === 1) b[3000000000] = 9; + if (i > 6) break; + sum += a[i] === undefined ? 100 : a[i]; + } + return `${sum} ${a.length}`; +} +console.log(sparseMidLoop()); + +function holeMidLoop(): string { + const a = [1, 2, 3, 4, 5]; + let sum = 0; + for (let i = 0; i < a.length; i++) { + if (i === 1) a.length = 3; + if (i === 2) a.length = 5; + sum += a[i] === undefined ? 100 : a[i]; + } + return `${sum}`; +} +console.log(holeMidLoop()); + +const objects: { v: number }[] = [{ v: 1 }, { v: 2 }, { v: 3 }]; +objects.length = 5; +try { + let total = 0; + for (let i = 0; i < objects.length; i++) total += objects[i].v; + console.log(total); +} catch (e) { + console.log(e instanceof TypeError ? "TypeError" : "other"); +} diff --git a/tests/corpus/5995-array-dense-loop-boundaries.ts b/tests/corpus/5995-array-dense-loop-boundaries.ts new file mode 100644 index 0000000000..5efad78ec1 --- /dev/null +++ b/tests/corpus/5995-array-dense-loop-boundaries.ts @@ -0,0 +1,173 @@ +// Index loops whose array length the loop never changes, run over dense, +// holey, partially filled, aliased and short arrays, with fractional, +// negative and offset bounds, ascending, strided and descending counters. + +interface Point { + x: number; + y: number; +} + +function sum(a: number[]): number { + let s = 0; + for (let i = 0; i < a.length; i++) s += a[i]; + return s; +} + +function scale(a: number[], b: number[]): string { + for (let i = 0; i < a.length; i++) b[i] = a[i] * 2; + return `${b.length}:${b.join(",")}`; +} + +function inPlace(a: number[]): string { + for (let i = 0; i < a.length; i++) a[i] = a[i] + 1; + return a.join(","); +} + +function strided(a: number[], step: number): string { + let s = 0; + if (step === 2) for (let i = 0; i < a.length; i += 2) s += a[i]; + else for (let i = 1; i < a.length; i += 3) s += a[i]; + return `${s}`; +} + +function descending(a: number[]): string { + const out: number[] = []; + for (let i = a.length - 1; i >= 0; i--) out.push(a[i]); + return out.join(","); +} + +function descendingPairs(a: number[]): number { + let s = 0; + for (let i = a.length - 2; i > 0; i -= 2) s += a[i] - a[i + 1]; + return s; +} + +function offsetBounds(a: number[]): string { + let inner = 0; + for (let i = 1; i < a.length - 1; i++) inner += a[i]; + let inclusive = 0; + for (let i = 0; i <= a.length - 1; i++) inclusive += a[i]; + let diffs = 0; + for (let i = 0; i < a.length - 1; i++) diffs += a[i + 1] - a[i]; + return `${inner} ${inclusive} ${diffs}`; +} + +function fractionalBounds(a: number[]): string { + const seen: number[] = []; + for (let i = 0.5; i < a.length; i++) seen.push(a[i] === undefined ? -1 : 1); + for (let i = -2; i < a.length - 3; i++) seen.push(a[i] === undefined ? -2 : a[i]); + for (let i = 0; i < a.length - 0.5; i++) seen.push(i); + return seen.join(","); +} + +function whileLoop(a: number[]): number { + let i = 0; + let s = 0; + while (i < a.length) { + s += a[i] * i; + i++; + } + return s; +} + +function constLength(a: number[]): number { + const n = a.length; + let s = 0; + for (let i = 0; i < n; i++) s += a[i]; + return s; +} + +function points(ps: Point[]): string { + let s = 0; + for (let i = 0; i < ps.length; i++) s += ps[i].x * ps[i].y; + return `${s}`; +} + +function flags(bs: boolean[]): number { + let n = 0; + for (let i = 0; i < bs.length; i++) if (bs[i]) n++; + return n; +} + +function names(ss: string[]): string { + let out = ""; + for (let i = ss.length - 1; i >= 0; i--) out += ss[i]; + return out; +} + +function maybe(xs: (number | undefined)[]): string { + let s = 0; + let missing = 0; + for (let i = 0; i < xs.length; i++) { + const v = xs[i]; + if (v === undefined) missing++; + else s += v; + } + return `${s} ${missing}`; +} + +function nested(g: number[][]): number { + let s = 0; + for (let r = 0; r < g.length; r++) { + const row = g[r]; + for (let c = 0; c < row.length; c++) s += row[c] * (r + 1); + } + return s; +} + +function counterAsNumber(a: number[]): string { + let mixed = 0; + for (let i = 0; i < a.length; i++) mixed += i / 2 + (i | 0) + a[i] * -0; + return `${mixed} ${Object.is(mixed, -0)}`; +} + +function earlyExit(a: number[], target: number): number { + for (let i = 0; i < a.length; i++) if (a[i] === target) return i; + return -1; +} + +const dense = [1, 2, 3, 4, 5, 6, 7, 8]; +console.log(sum(dense), sum([]), sum([0.5, -0.25])); +console.log(scale(dense, new Array(8).fill(0))); +console.log(scale(dense, [9, 9])); +console.log(scale([1, 2, 3], [])); +const self = [1, 2, 3, 4]; +console.log(scale(self, self)); +console.log(inPlace([10, 20, 30])); +console.log(strided(dense, 2), strided(dense, 3)); +console.log(descending(dense), descending([])); +console.log(descendingPairs(dense), descendingPairs([1])); +console.log(offsetBounds(dense), offsetBounds([4]), offsetBounds([])); +console.log(fractionalBounds([1, 2, 3, 4, 5])); +console.log(whileLoop(dense)); +console.log(constLength(dense)); +console.log(points([{ x: 1, y: 2 }, { x: 3, y: 4 }])); +console.log(flags([true, false, true, true])); +console.log(names(["a", "b", "c"])); +console.log(maybe([1, undefined, 3])); +console.log(nested([[1, 2], [3], [], [4, 5, 6]])); +console.log(counterAsNumber([1, 2, 3])); +console.log(earlyExit(dense, 5), earlyExit(dense, 50)); + +// Holes and partially filled storage take the checked path. +const holey = [1]; +holey[2] = 3; +holey[4] = 5; +console.log(sum(holey), descending(holey), whileLoop(holey)); +const target = [0, 0, 0]; +target[4] = 0; +console.log(scale(dense.slice(0, 5), target)); +const gap: number[] = []; +gap[3] = 1; +console.log(sum(gap), scale([1, 2, 3, 4], gap)); +const shortened = [1, 2, 3, 4, 5, 6]; +shortened.length = 3; +shortened.length = 6; +console.log(sum(shortened), constLength(shortened)); +const holeyPoints: Point[] = [{ x: 1, y: 1 }]; +holeyPoints[2] = { x: 2, y: 2 }; +try { + console.log(points(holeyPoints)); +} catch (e) { + console.log(e instanceof TypeError ? "TypeError" : "other"); +} diff --git a/tests/corpus/6020-class-extends-function-constructor.js b/tests/corpus/6020-class-extends-function-constructor.js new file mode 100644 index 0000000000..d98d22a551 --- /dev/null +++ b/tests/corpus/6020-class-extends-function-constructor.js @@ -0,0 +1,27 @@ +// Classes whose base is an ordinary function constructor, with a declared +// constructor calling super and with the implicit derived constructor. +function Shape(name, sides) { + this.name = name; + this.sides = sides; +} +Shape.prototype.describe = function () { + return `${this.name} with ${this.sides} sides`; +}; + +class Polygon extends Shape { + constructor(name, sides, length) { + super(name, sides); + this.length = length; + } + perimeter() { + return this.sides * this.length; + } +} + +class Unnamed extends Shape {} + +const square = new Polygon("square", 4, 3); +console.log(square.describe(), square.perimeter()); +console.log(square instanceof Polygon); +const shape = new Unnamed("triangle", 3); +console.log(shape.describe(), Object.keys(shape).join(",")); diff --git a/tests/corpus/6021-asserted-literal-union-arm.ts b/tests/corpus/6021-asserted-literal-union-arm.ts new file mode 100644 index 0000000000..cbe8b74f7f --- /dev/null +++ b/tests/corpus/6021-asserted-literal-union-arm.ts @@ -0,0 +1,42 @@ +// Object literals asserted to a discriminated union whose own fields match +// another member's fields: the discriminant selects the member. +interface Loc { + start: number; +} +type Expr = { kind: "num"; value: number; loc: Loc } | { kind: "str"; text: string; loc: Loc }; +type Stmt = + | { kind: "return"; value: Expr | null; awaited?: true; loc: Loc } + | { kind: "throw"; value: Expr; loc: Loc } + | { kind: "if"; cond: Expr; then: Stmt[]; loc: Loc }; + +function show(stmt: Stmt): string { + switch (stmt.kind) { + case "return": + return `return${stmt.awaited ? " awaited" : ""} ${stmt.value === null ? "-" : stmt.value.kind}`; + case "throw": + return `throw ${stmt.value.kind}`; + case "if": + return `if ${stmt.cond.kind} { ${stmt.then.map(show).join("; ")} }`; + } +} + +function guarded(value: Expr, loc: Loc): Stmt { + return { kind: "if", cond: value, then: [{ kind: "return", value, loc } as Stmt], loc }; +} + +function branches(reflect: boolean, value: Expr, loc: Loc): Stmt[] { + return [ + ...(reflect + ? [{ kind: "return", value, loc } as Stmt] + : [{ kind: "throw", value, loc } as Stmt, { kind: "return", value: null, loc } as Stmt]), + ]; +} + +const loc: Loc = { start: 0 }; +const num: Expr = { kind: "num", value: 1, loc }; +const str: Expr = { kind: "str", text: "x", loc }; +console.log(show(guarded(num, loc))); +console.log(branches(true, str, loc).map(show).join(" | ")); +console.log(branches(false, num, loc).map(show).join(" | ")); +const listed: Stmt[] = [{ kind: "return", value: str, loc } as Stmt, { kind: "throw", value: num, loc }]; +console.log(listed.map(show).join(" | "), listed.map((stmt) => stmt.kind).join(",")); diff --git a/tests/corpus/6041-error-cause-redeclared.ts b/tests/corpus/6041-error-cause-redeclared.ts new file mode 100644 index 0000000000..862006414b --- /dev/null +++ b/tests/corpus/6041-error-cause-redeclared.ts @@ -0,0 +1,64 @@ +// TypeScript subclasses of Error may redeclare `cause` as `unknown` or +// `object`. The declaration defines the error's own enumerable `cause` after +// super() returns, exactly as Node does: without an initializer it replaces a +// cause passed through the options with undefined. `declare` and `abstract` +// declarations are erased and define nothing. +class Base extends Error { + cause?: unknown; + constructor(message?: string, options?: { cause?: unknown }) { + super(message, options); + } +} +const viaOptions = new Base("a", { cause: 1 }); +console.log("cause" in viaOptions, viaOptions.cause, Object.keys(viaOptions)); +console.log(Object.getOwnPropertyDescriptor(viaOptions, "cause")); +class Init extends Error { + override cause: unknown = "init"; +} +const init = new Init("b", { cause: "options" }); +console.log(init.cause, Object.getOwnPropertyDescriptor(init, "cause")); +class Declared extends Error { + declare cause: unknown; +} +const d1 = new Declared("c"); +const d2 = new Declared("c", { cause: "kept" }); +console.log("cause" in d1, d1.cause, "cause" in d2, d2.cause); +abstract class Abstract extends Error { + abstract override cause: unknown; +} +class Concrete extends Abstract { + override cause: object | null = null; +} +console.log(new Concrete("d").cause); +class Assign extends Error { + cause?: unknown; + constructor(message: string, cause?: unknown) { + super(message); + this.cause = cause; + } +} +const a = new Assign("e", new Error("inner")); +const inner = a.cause; +if (inner instanceof Error) console.log(inner.message); +delete a.cause; +console.log("cause" in a, a.cause); +class Sub extends Assign { + override cause: unknown = "sub"; +} +const s = new Sub("f", "x"); +console.log("cause" in s, s.cause); +try { + throw new Base("thrown", { cause: { code: 7 } }); +} catch (e) { + if (e instanceof Base) console.log(e.message, e.cause); +} +class OpenFeatureStyle extends Error { + cause?: unknown; + constructor(message?: string, options?: { cause?: unknown }) { + super(message); + this.name = "OpenFeatureStyle"; + this.cause = options?.cause; + } +} +const o = new OpenFeatureStyle("flag", { cause: "network" }); +console.log(o.name, o.message, o.cause, o instanceof OpenFeatureStyle, o instanceof Error); diff --git a/tests/corpus/6042-regex-match-object-destructuring.ts b/tests/corpus/6042-regex-match-object-destructuring.ts new file mode 100644 index 0000000000..160ce027aa --- /dev/null +++ b/tests/corpus/6042-regex-match-object-destructuring.ts @@ -0,0 +1,51 @@ +// Object patterns over regex match results: numeric keys read the match +// positions (an unmatched optional group is undefined, a position past the +// end takes its default), `index` and `input` are the result's own +// properties, and `groups` is the named-group object when the regex is known. +const re = /(\d+)-(\d+)?/; +function first(text: string): string { + const m = re.exec(text); + if (!m) return "none"; + const { 0: whole, 1: a, 2: b, index, input } = m; + return `${whole}|${a}|${b}|${index}|${input}`; +} +console.log(first("x 12-34 y"), first("ab 7- c"), first("none")); + +const matched = "key=value".match(/(?\w+)=(?\w+)/); +if (matched) { + const { index, input, groups } = matched; + console.log(index, input, groups?.k, groups?.v); + const { 1: key, groups: named } = matched; + console.log(key, named?.v); +} + +const exec = /b/.exec("abc")!; +const { index: at, input: text, 0: hit, 3: missing = "default" } = exec; +console.log(at, text, hit, missing); + +function viaParam({ index, 0: s }: RegExpExecArray): string { + return `${index}:${s}`; +} +console.log(viaParam(/c/.exec("abc")!)); + +let total = 0; +for (const word of ["aa1", "b22", "333"]) { + const r = /\d+/.exec(word); + if (r) { + const { index, 0: digits } = r; + total += index + digits.length; + } +} +console.log(total); + +// path-to-regexp's regexpToFunction shape. +function matcher(pattern: RegExp): (pathname: string) => false | { path: string; index: number } { + return function (pathname: string) { + const m = pattern.exec(pathname); + if (!m) return false; + const { 0: path, index } = m; + return { path, index }; + }; +} +const match = matcher(/\/users\/(\d+)/); +console.log(match("/api/users/42"), match("/other")); diff --git a/tests/corpus/6043-string-index-narrowed-union.ts b/tests/corpus/6043-string-index-narrowed-union.ts new file mode 100644 index 0000000000..6fee639eb1 --- /dev/null +++ b/tests/corpus/6043-string-index-narrowed-union.ts @@ -0,0 +1,59 @@ +// Indexing a string whose binding was read from an array or narrowed from a +// union with typeof. The receiver's stored value keeps its other arms, so an +// out-of-bounds array read throws Node's TypeError at the index, naming the +// key. +interface Key { + name: string; +} +type Token = string | Key; + +// path-to-regexp's tokensToRegexp shape. +function endDelimited(tokens: Token[], delimiters: string): boolean { + const endToken = tokens[tokens.length - 1]; + return typeof endToken === "string" + ? delimiters.indexOf(endToken[endToken.length - 1]) > -1 + : endToken === undefined; +} +console.log( + endDelimited(["a/", { name: "x" }], "/"), + endDelimited(["a/"], "/"), + endDelimited([], "/"), + endDelimited(["ab"], "/"), +); + +function lastChar(tokens: Token[]): string { + const t = tokens[0]; + if (typeof t === "string") return t[t.length - 1]; + return "-"; +} +console.log(lastChar(["xyz"]), lastChar([{ name: "k" }])); + +function lastLetter(words: string[], i: number): string { + const w = words[i]; + return w[w.length - 1]; +} +console.log(lastLetter(["ab", "cd"], 1)); + +function at(words: string[], i: number, j: number): string | undefined { + const w = words[i]; + return w[j]; +} +console.log(at(["abc"], 0, 1), at(["abc"], 0, 7), at(["abc"], 0, 1.5), at(["abc"], 0, -1)); +for (const j of [2, -0.5, 1e21]) { + try { + at(["abc"], 3, j); + } catch (e) { + console.log(e instanceof TypeError, (e as Error).message); + } +} + +let calls = 0; +function key(): number { + calls++; + return 0; +} +function viaCall(words: string[]): string { + const w = words[0]; + return w[key()]; +} +console.log(viaCall(["q"]), calls); diff --git a/tests/corpus/6140-cjs-require-tdz-throws/lib.js b/tests/corpus/6140-cjs-require-tdz-throws/lib.js new file mode 100644 index 0000000000..2a9669e123 --- /dev/null +++ b/tests/corpus/6140-cjs-require-tdz-throws/lib.js @@ -0,0 +1,8 @@ +'use strict'; + +/** @param {string} who */ +function greet(who) { + return `hello ${who}`; +} + +module.exports = { greet }; diff --git a/tests/corpus/6140-cjs-require-tdz-throws/main.js b/tests/corpus/6140-cjs-require-tdz-throws/main.js new file mode 100644 index 0000000000..13163137e2 --- /dev/null +++ b/tests/corpus/6140-cjs-require-tdz-throws/main.js @@ -0,0 +1,15 @@ +// @exit: 1 +// A hoisted function called above the require reads the binding before +// the require ran: Node throws "Cannot access 'greet' before +// initialization" there, uncaught, and nothing after it runs. +'use strict'; + +console.log('start'); +console.log('boot', helper()); +const { greet } = require('./lib.js'); + +function helper() { + return greet('early'); +} + +console.log('unreachable', helper()); diff --git a/tests/corpus/6140-cjs-require-tdz-throws/package.json b/tests/corpus/6140-cjs-require-tdz-throws/package.json new file mode 100644 index 0000000000..a3c15a7a63 --- /dev/null +++ b/tests/corpus/6140-cjs-require-tdz-throws/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/tests/corpus/6141-cjs-require-tdz-checked/counter.js b/tests/corpus/6141-cjs-require-tdz-checked/counter.js new file mode 100644 index 0000000000..24ef13e5de --- /dev/null +++ b/tests/corpus/6141-cjs-require-tdz-checked/counter.js @@ -0,0 +1,14 @@ +'use strict'; + +class Counter { + /** @param {number} start */ + constructor(start) { + this.value = start; + } + next() { + this.value += 1; + return this.value; + } +} + +module.exports = Counter; diff --git a/tests/corpus/6141-cjs-require-tdz-checked/lib.js b/tests/corpus/6141-cjs-require-tdz-checked/lib.js new file mode 100644 index 0000000000..aa15c97d6a --- /dev/null +++ b/tests/corpus/6141-cjs-require-tdz-checked/lib.js @@ -0,0 +1,8 @@ +'use strict'; + +/** @param {string} who */ +function greet(who) { + return `hello ${who}`; +} + +module.exports = { greet, answer: 42 }; diff --git a/tests/corpus/6141-cjs-require-tdz-checked/main.js b/tests/corpus/6141-cjs-require-tdz-checked/main.js new file mode 100644 index 0000000000..917eb78743 --- /dev/null +++ b/tests/corpus/6141-cjs-require-tdz-checked/main.js @@ -0,0 +1,49 @@ +// Require bindings that code running before the require can read. Every +// read that happens before the require throws Node's ReferenceError for +// the local binding name, before any argument of the access evaluates; +// the same reads succeed once the require ran. +'use strict'; + +/** + * @param {string} label + * @param {() => unknown} read + */ +function attempt(label, read) { + try { + console.log(label, 'ok', read()); + } catch (e) { + if (!(e instanceof ReferenceError)) throw e; + console.log(label, e.name, e.message); + } +} + +/** @param {string} s */ +function note(s) { + console.log('evaluated', s); + return s; +} + +const later = [() => greet('later'), () => new Counter(5).next()]; + +attempt('call', () => greet('a')); +attempt('call-arguments', () => greet(note('argument'))); +attempt('member', () => lib.greet('b')); +attempt('construct', () => new Counter(1).next()); +attempt('builtin', () => path.join('a', 'b')); +attempt('value', () => answer + 1); +attempt('renamed', () => hello('c')); + +const { greet } = require('./lib.js'); +const lib = require('./lib.js'); +const Counter = require('./counter.js'); +const path = require('path'); +const { answer, greet: hello } = require('./lib.js'); + +attempt('call', () => greet('a')); +attempt('call-arguments', () => greet(note('argument'))); +attempt('member', () => lib.greet('b')); +attempt('construct', () => new Counter(1).next()); +attempt('builtin', () => path.join('a', 'b')); +attempt('value', () => answer + 1); +attempt('renamed', () => hello('c')); +for (const read of later) console.log('later', read()); diff --git a/tests/corpus/6141-cjs-require-tdz-checked/package.json b/tests/corpus/6141-cjs-require-tdz-checked/package.json new file mode 100644 index 0000000000..a3c15a7a63 --- /dev/null +++ b/tests/corpus/6141-cjs-require-tdz-checked/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/tests/corpus/6142-cjs-require-published-early/bundle.js b/tests/corpus/6142-cjs-require-published-early/bundle.js new file mode 100644 index 0000000000..c123872197 --- /dev/null +++ b/tests/corpus/6142-cjs-require-published-early/bundle.js @@ -0,0 +1,17 @@ +'use strict'; + +// The esbuild CommonJS layout: stored class expressions and the export +// table come before the var requires their methods use. Publishing the +// classes runs none of their methods. +const Provider = class { + /** @param {string} name */ + constructor(name) { + this.name = name; + } + describe() { + return core.label(this.name); + } +}; +const REASON = 'default'; +module.exports = { Provider, REASON }; +var core = require('./core.js'); diff --git a/tests/corpus/6142-cjs-require-published-early/comparator.js b/tests/corpus/6142-cjs-require-published-early/comparator.js new file mode 100644 index 0000000000..2b2ea6146d --- /dev/null +++ b/tests/corpus/6142-cjs-require-published-early/comparator.js @@ -0,0 +1,37 @@ +'use strict'; + +// The semver layout: the class and its export come first, the requires its +// methods use come after. Publishing the class runs none of its methods, so +// no read can happen before the requires ran. +const ANY = Symbol('ANY'); + +class Comparator { + static get ANY() { + return ANY; + } + + static describe() { + return `Comparator(${typeof parseOptions})`; + } + + /** + * @param {string} text + * @param {{ loose?: boolean }} [options] + */ + constructor(text, options) { + this.mode = parseOptions(options); + this.text = text.trim(); + debug('comparator', this.text, this.mode); + } + + /** @param {string} other */ + test(other) { + return path.basename(other) === this.text; + } +} + +module.exports = Comparator; + +const parseOptions = require('./parse-options.js'); +const { debug } = require('./debug.js'); +const path = require('path'); diff --git a/tests/corpus/6142-cjs-require-published-early/core.js b/tests/corpus/6142-cjs-require-published-early/core.js new file mode 100644 index 0000000000..9e3a9a565c --- /dev/null +++ b/tests/corpus/6142-cjs-require-published-early/core.js @@ -0,0 +1,10 @@ +'use strict'; + +console.log('core: init'); + +/** @param {string} name */ +function label(name) { + return `<${name}>`; +} + +module.exports = { label }; diff --git a/tests/corpus/6142-cjs-require-published-early/debug.js b/tests/corpus/6142-cjs-require-published-early/debug.js new file mode 100644 index 0000000000..30451569b1 --- /dev/null +++ b/tests/corpus/6142-cjs-require-published-early/debug.js @@ -0,0 +1,10 @@ +'use strict'; + +console.log('debug: init'); + +/** @param {...unknown} args */ +function debug(...args) { + console.log('debug:', args.join(' ')); +} + +module.exports = { debug }; diff --git a/tests/corpus/6142-cjs-require-published-early/glob.js b/tests/corpus/6142-cjs-require-published-early/glob.js new file mode 100644 index 0000000000..3d2907f005 --- /dev/null +++ b/tests/corpus/6142-cjs-require-published-early/glob.js @@ -0,0 +1,20 @@ +'use strict'; + +// The glob@7 layout with var: export first, then var requires, a runnable +// statement between them, and properties stored on the exported function. +module.exports = glob; +glob.sync = globSync; + +var parseOptions = require('./parse-options.js'); +var mode = parseOptions({ loose: true }); +var helpers = require('./helpers.js'); + +/** @param {string} pattern */ +function glob(pattern) { + return `${mode}:${helpers.normalize(pattern)}`; +} + +/** @param {string} pattern */ +function globSync(pattern) { + return helpers.normalize(pattern).toUpperCase(); +} diff --git a/tests/corpus/6142-cjs-require-published-early/helpers.js b/tests/corpus/6142-cjs-require-published-early/helpers.js new file mode 100644 index 0000000000..206ca77851 --- /dev/null +++ b/tests/corpus/6142-cjs-require-published-early/helpers.js @@ -0,0 +1,10 @@ +'use strict'; + +console.log('helpers: init'); + +/** @param {string} s */ +function normalize(s) { + return s.replace(/\/+/g, '/'); +} + +module.exports = { normalize }; diff --git a/tests/corpus/6142-cjs-require-published-early/main.js b/tests/corpus/6142-cjs-require-published-early/main.js new file mode 100644 index 0000000000..310dacdb01 --- /dev/null +++ b/tests/corpus/6142-cjs-require-published-early/main.js @@ -0,0 +1,15 @@ +// CommonJS modules that export first and require afterwards: code that can +// reach the bindings only runs after the module finished, so they compile +// without a fence and behave exactly as under Node. +'use strict'; + +console.log('main: start'); +const Comparator = require('./comparator.js'); +const glob = require('./glob.js'); +const { Provider, REASON } = require('./bundle.js'); + +const c = new Comparator(' a.txt ', { loose: true }); +console.log(c.mode, c.text, c.test('/x/a.txt'), Comparator.ANY.toString()); +console.log(new Comparator('b').mode, Comparator.describe()); +console.log(glob('a//b'), glob.sync('c//d')); +console.log(new Provider('custom').describe(), REASON); diff --git a/tests/corpus/6142-cjs-require-published-early/package.json b/tests/corpus/6142-cjs-require-published-early/package.json new file mode 100644 index 0000000000..a3c15a7a63 --- /dev/null +++ b/tests/corpus/6142-cjs-require-published-early/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/tests/corpus/6142-cjs-require-published-early/parse-options.js b/tests/corpus/6142-cjs-require-published-early/parse-options.js new file mode 100644 index 0000000000..0872049ea3 --- /dev/null +++ b/tests/corpus/6142-cjs-require-published-early/parse-options.js @@ -0,0 +1,8 @@ +'use strict'; + +/** @param {{ loose?: boolean } | undefined} options */ +function parseOptions(options) { + return options && options.loose ? 'loose' : 'strict'; +} + +module.exports = parseOptions; diff --git a/tests/corpus/6143-cjs-require-json/counter.js b/tests/corpus/6143-cjs-require-json/counter.js new file mode 100644 index 0000000000..b794d1d4c8 --- /dev/null +++ b/tests/corpus/6143-cjs-require-json/counter.js @@ -0,0 +1,14 @@ +'use strict'; + +// A second requirer: it receives the same cached object and mutates it. +const data = require('./data.json'); + +console.log('counter: sees', data.count); +data.count += 1; + +function bump() { + data.count += 100; + data.tags.push('bumped'); +} + +module.exports = { bump }; diff --git a/tests/corpus/6143-cjs-require-json/data.json b/tests/corpus/6143-cjs-require-json/data.json new file mode 100644 index 0000000000..b1cf3d3bc9 --- /dev/null +++ b/tests/corpus/6143-cjs-require-json/data.json @@ -0,0 +1,7 @@ +{ + "name": "fixture-data", + "version": "1.2.3", + "count": 41, + "tags": ["native", "json"], + "nested": { "flag": true, "ratio": 0.5 } +} diff --git a/tests/corpus/6143-cjs-require-json/list.json b/tests/corpus/6143-cjs-require-json/list.json new file mode 100644 index 0000000000..98ace19c09 --- /dev/null +++ b/tests/corpus/6143-cjs-require-json/list.json @@ -0,0 +1 @@ +[3, 1, 2] diff --git a/tests/corpus/6143-cjs-require-json/main.js b/tests/corpus/6143-cjs-require-json/main.js new file mode 100644 index 0000000000..b67c39f3eb --- /dev/null +++ b/tests/corpus/6143-cjs-require-json/main.js @@ -0,0 +1,27 @@ +// require() of a JSON document: Node parses it at the first require and +// caches the object, so every require of the document, from any module, +// returns the same object and a mutation is visible to all requirers. +// Destructuring copies the properties at the require. +'use strict'; + +const data = require('./data.json'); +const { count, nested: { flag, ratio }, tags: [firstTag] } = require('./data.json'); +const version = require('./data.json').version; + +console.log(data.name, version, data.count, count, flag, ratio, firstTag); +console.log('same object', require('./data.json') === data); + +const { bump } = require('./counter.js'); +console.log('after counter', data.count, count); +bump(); +console.log('after bump', data.count, data.tags.join(','), count); + +function load() { + return require('./data.json'); +} +console.log('lazy same object', load() === data, load().count); +load().nested.flag = false; +console.log('flag', data.nested.flag, flag); + +const list = require('./list.json'); +console.log(list.length, list.slice().sort().join(','), require('./list.json') === list); diff --git a/tests/corpus/6143-cjs-require-json/package.json b/tests/corpus/6143-cjs-require-json/package.json new file mode 100644 index 0000000000..a3c15a7a63 --- /dev/null +++ b/tests/corpus/6143-cjs-require-json/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/tests/corpus/6144-cjs-require-builtin-nested/main.js b/tests/corpus/6144-cjs-require-builtin-nested/main.js new file mode 100644 index 0000000000..3058767623 --- /dev/null +++ b/tests/corpus/6144-cjs-require-builtin-nested/main.js @@ -0,0 +1,15 @@ +// Nested destructuring of a builtin require (undici's `const { types: { +// isPromise } } = require('util')`): util.types IS the util/types module, +// so the nested pattern binds that module's members. +'use strict'; + +const { + types: { isPromise, isPromise: promiseCheck }, + format, +} = require('util'); +const { promises: { readFile } } = require('fs'); +const { posix: { join } } = require('path'); + +console.log(isPromise(Promise.resolve(1)), isPromise({}), promiseCheck(Promise.reject(new Error('x')).catch(() => {}))); +console.log(format('%s=%d', 'a', 1), join('a', 'b', '..', 'c')); +readFile(__filename, 'utf8').then((text) => console.log('read', text.includes('isPromise'))); diff --git a/tests/corpus/6144-cjs-require-builtin-nested/package.json b/tests/corpus/6144-cjs-require-builtin-nested/package.json new file mode 100644 index 0000000000..a3c15a7a63 --- /dev/null +++ b/tests/corpus/6144-cjs-require-builtin-nested/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/tests/corpus/6145-cjs-require-cycle/comparator.js b/tests/corpus/6145-cjs-require-cycle/comparator.js new file mode 100644 index 0000000000..1f0d4eb9d2 --- /dev/null +++ b/tests/corpus/6145-cjs-require-cycle/comparator.js @@ -0,0 +1,29 @@ +'use strict'; + +// semver's layout: the class and its export come first ("hoisted class for +// cyclic dependency"), the requires — one of them closing the cycle with +// range.js — after it. When range.js requires this module mid-evaluation, +// Node hands it the exports as they stand: already the class. +const ANY = Symbol('ANY'); + +class Comparator { + /** @param {string} value */ + constructor(value) { + this.value = value; + } + + /** @param {string} other */ + within(other) { + return new Range(other).has(this.value); + } + + static get ANY() { + return ANY; + } +} + +module.exports = Comparator; + +const Range = require('./range.js'); +const { trim } = require('./util.js'); +console.log('comparator: loaded', trim(' done ')); diff --git a/tests/corpus/6145-cjs-require-cycle/main.js b/tests/corpus/6145-cjs-require-cycle/main.js new file mode 100644 index 0000000000..90cbc88247 --- /dev/null +++ b/tests/corpus/6145-cjs-require-cycle/main.js @@ -0,0 +1,15 @@ +// A require cycle (comparator.js ↔ range.js) whose modules settle their +// exports before they require anything, and use each other only inside +// methods: the cycle evaluates in Node's order and each binding is the +// other module's class. +'use strict'; + +console.log('main: start'); +const Comparator = require('./comparator.js'); +const Range = require('./range.js'); + +const range = new Range('1.0.0 2.0.0'); +console.log(range.has('2.0.0'), range.has('3.0.0')); +const first = range.first(); +console.log(first instanceof Comparator, first.value, first.within('2.0.0 1.0.0')); +console.log(new Comparator('9').within('1 2'), String(Comparator.ANY)); diff --git a/tests/corpus/6145-cjs-require-cycle/package.json b/tests/corpus/6145-cjs-require-cycle/package.json new file mode 100644 index 0000000000..a3c15a7a63 --- /dev/null +++ b/tests/corpus/6145-cjs-require-cycle/package.json @@ -0,0 +1 @@ +{ "type": "commonjs" } diff --git a/tests/corpus/6145-cjs-require-cycle/range.js b/tests/corpus/6145-cjs-require-cycle/range.js new file mode 100644 index 0000000000..ab71e4a52d --- /dev/null +++ b/tests/corpus/6145-cjs-require-cycle/range.js @@ -0,0 +1,24 @@ +'use strict'; + +const SEPARATOR = /\s+/; + +class Range { + /** @param {string} spec */ + constructor(spec) { + this.parts = spec.split(SEPARATOR).map((part) => new Comparator(part)); + } + + /** @param {string} value */ + has(value) { + return this.parts.some((part) => part.value === value); + } + + first() { + return this.parts[0]; + } +} + +module.exports = Range; + +const Comparator = require('./comparator.js'); +console.log('range: loaded'); diff --git a/tests/corpus/6145-cjs-require-cycle/util.js b/tests/corpus/6145-cjs-require-cycle/util.js new file mode 100644 index 0000000000..4e13760cd4 --- /dev/null +++ b/tests/corpus/6145-cjs-require-cycle/util.js @@ -0,0 +1,10 @@ +'use strict'; + +console.log('util: loaded'); + +/** @param {string} s */ +function trim(s) { + return s.trim(); +} + +module.exports = { trim }; diff --git a/tests/corpus/scratch-string-slices.ts b/tests/corpus/scratch-string-slices.ts new file mode 100644 index 0000000000..487883d6e5 --- /dev/null +++ b/tests/corpus/scratch-string-slices.ts @@ -0,0 +1,116 @@ +// Slices read only by comparisons, numeric parsers, collection probes, +// switch discriminants and lent helper parameters, including the keys a +// map or set keeps after the slice that produced them is gone. +const ascii = "GET /api/users?id=42 200 17.5ms"; +const mixed = "Aé水x😀yz-12.5e1 ok"; +const bounds = [-Infinity, -40, -8, -2.5, -1, -0.5, -0, 0, 0.5, 1, 2, 3, 4.9, 5, 7, 10, 40, Infinity, NaN]; + +function compare(source: string, start: number, end: number): string { + const s = source.slice(start, end); + const t = source.substring(start, end); + const parts = [ + String(s === "GET"), + String(t === "GET"), + String(source.slice(start, end) === source.substring(start, end)), + String(source.slice(start, end) < "M"), + String(source.substring(start, end) >= "a"), + String(s.length), + String(t.length), + String(s.charCodeAt(0)), + String(t.charCodeAt(t.length - 1)), + String(s.indexOf("a")), + String(t.includes("😀")), + String(s.startsWith("A")), + String(t.endsWith("z")), + ]; + return parts.join(","); +} +for (const source of [ascii, mixed]) { + for (const start of bounds) { + const row: string[] = []; + for (const end of bounds) row.push(compare(source, start, end)); + console.log(row.join(" | ")); + } +} + +// Numeric parsing of slices. +const numbers = " 42|-0|0x1F|1e3|.5|Infinity|-Infinity|12px|abc|+7.25| 9 |0b11|1_000|"; +let offset = 0; +while (offset < numbers.length) { + const bar = numbers.indexOf("|", offset); + const field = numbers.slice(offset, bar); + console.log( + JSON.stringify(field), + Number(field), + +numbers.substring(offset, bar), + parseInt(field), + parseInt(numbers.slice(offset, bar), 16), + parseFloat(field), + Object.is(Number(numbers.slice(offset, bar)), -0), + ); + offset = bar + 1; +} +console.log(Number(mixed.slice(9, 16)), parseFloat(mixed.slice(9)), parseInt(mixed.slice(10, 12))); + +// Collection probes and insertions keyed by slices. The stored keys must +// outlive every later slice written to the same site. +const counts = new Map(); +const seen = new Set(); +const words = "alpha beta gamma alpha delta beta alpha epsilon-with-a-much-longer-key-than-the-stack-buffer-holds-alpha zeta é-key é-key x x"; +for (let round = 0; round < 3; round++) { + let start = 0; + while (start < words.length) { + let end = words.indexOf(" ", start); + if (end < 0) end = words.length; + const word = words.slice(start, end); + counts.set(word, (counts.get(word) ?? 0) + 1); + if (!seen.has(word)) seen.add(word); + if (counts.get(words.substring(start, end)) === 2) seen.delete(word); + start = end + 1; + } +} +console.log([...counts.entries()].map(([key, value]) => `${key}=${value}`).join(",")); +console.log([...seen].join(",")); +console.log(counts.has(ascii.slice(0, 3)), counts.has(words.slice(0, 5)), seen.has(words.slice(6, 10))); +const index = new Map(); +for (let i = 0; i + 3 <= ascii.length; i++) index.set(ascii.slice(i, i + 3), i); +console.log(index.size, index.get("api"), index.get(ascii.substring(5, 8)), [...index.keys()].slice(0, 6).join("/")); +const reads = new Map([["api", "first"], ["users", "second"]]); +console.log(reads.get(ascii.slice(5, 8)), reads.get(ascii.slice(9, 14)) ?? "none", reads.get(ascii.slice(0, 1)) ?? "none"); + +// Switch discriminants. +function method(line: string): number { + switch (line.slice(0, line.indexOf(" "))) { + case "GET": + return 1; + case "POST": + return 2; + case "é": + return 3; + default: + return 0; + } +} +console.log(method(ascii), method("POST /x"), method("é y"), method("PATCH /x"), method("nospace")); + +// Lent helper parameters, including recursion through a lent parameter. +function isKeyword(text: string): boolean { + return text === "let" || text === "const" || text === "return"; +} +function depth(text: string, n: number): number { + if (n === 0) return text.length; + return depth(text, n - 1) + (text === "abc" ? 1 : 0); +} +function keep(text: string): string[] { + return [text]; +} +const program = "let x = 1; const y = 2; return x + y; letter"; +let keywords = 0; +const kept: string[][] = []; +for (let i = 0; i < program.length; i++) { + for (let j = i + 2; j <= Math.min(program.length, i + 6); j++) { + if (isKeyword(program.slice(i, j))) keywords++; + } + if (i % 9 === 0) kept.push(keep(program.slice(i, i + 4))); +} +console.log(keywords, depth("abcdef".slice(0, 3), 4), kept.map((k) => k[0]).join("|")); diff --git a/tests/diagnostics/cjs-require-cycle/main.js b/tests/diagnostics/cjs-require-cycle/main.js index 619117b910..376aa0a363 100644 --- a/tests/diagnostics/cjs-require-cycle/main.js +++ b/tests/diagnostics/cjs-require-cycle/main.js @@ -1,6 +1,8 @@ -// A require cycle: Node hands B the PARTIAL exports of A mid-init, a +// A require cycle whose module exports only after requiring into the +// cycle: Node hands B the PARTIAL exports of A mid-init, a // partially-initialized-module observation the export-global model cannot -// represent — cycles keep the named SC1016 fence. +// represent — the cycle keeps the named SC1016 fence. (A cycle whose +// modules export first compiles: tests/corpus/6145-cjs-require-cycle.) 'use strict'; console.log('main: start'); diff --git a/tests/diagnostics/cjs-require-tdz-unchecked/lib.js b/tests/diagnostics/cjs-require-tdz-unchecked/lib.js new file mode 100644 index 0000000000..2a9669e123 --- /dev/null +++ b/tests/diagnostics/cjs-require-tdz-unchecked/lib.js @@ -0,0 +1,8 @@ +'use strict'; + +/** @param {string} who */ +function greet(who) { + return `hello ${who}`; +} + +module.exports = { greet }; diff --git a/tests/diagnostics/cjs-require-tdz-unchecked/main.js b/tests/diagnostics/cjs-require-tdz-unchecked/main.js new file mode 100644 index 0000000000..52b12fcb47 --- /dev/null +++ b/tests/diagnostics/cjs-require-tdz-unchecked/main.js @@ -0,0 +1,8 @@ +// A const require binding read by a statement above the require that +// lowers to no expression: publishing the binding as an export is module +// plumbing, so no initialization check can run where Node throws +// "Cannot access 'greet' before initialization". The read keeps SC1013. +'use strict'; + +module.exports = { greet }; +const { greet } = require('./lib.js'); diff --git a/tests/diagnostics/cjs-require-tdz-unchecked/package.json b/tests/diagnostics/cjs-require-tdz-unchecked/package.json new file mode 100644 index 0000000000..5bbefffbab --- /dev/null +++ b/tests/diagnostics/cjs-require-tdz-unchecked/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/tests/diagnostics/cjs-require-tdz/main.js b/tests/diagnostics/cjs-require-tdz/main.js index a571b45242..c2b5a945ce 100644 --- a/tests/diagnostics/cjs-require-tdz/main.js +++ b/tests/diagnostics/cjs-require-tdz/main.js @@ -1,14 +1,15 @@ -// A require whose binding IS reachable from code above it: Node would -// throw "Cannot access 'greet' before initialization" at the helper() -// call (the const initializes AT the require), an ordering the compiled -// aliasing does not model — the require keeps a named SC1013 fence. +// A var require binding that code above the require can reach: Node reads +// undefined there (the var is hoisted, the require has not run), a value +// the compiled module alias cannot hold — the require keeps a named SC1013 +// fence. A const or let binding compiles with an initialization check +// instead (tests/corpus/6141-cjs-require-tdz-checked). 'use strict'; console.log('boot', helper()); -const { greet } = require('./lib.js'); +var lib = require('./lib.js'); function helper() { - return greet('early'); + return lib.greet('early'); } console.log(helper()); diff --git a/tests/diagnostics/error-cause-redeclared-fences.ts b/tests/diagnostics/error-cause-redeclared-fences.ts new file mode 100644 index 0000000000..38309b6199 --- /dev/null +++ b/tests/diagnostics/error-cause-redeclared-fences.ts @@ -0,0 +1,20 @@ +// Redeclared Error causes compile when declared `unknown` or `object`. A +// narrower declared type would claim more than the checked-dynamic `cause` +// property proves, and a parameter property or an accessor named `cause` +// has no lowering. +class Typed extends Error { + override cause?: Error; +} +class FromParameter extends Error { + constructor(public override cause: unknown) { + super("parameter"); + } +} +class Accessor extends Error { + override get cause(): unknown { + return "accessor"; + } +} +console.log(new Typed("typed").cause); +console.log(new FromParameter(1).cause); +console.log(new Accessor("a").cause); diff --git a/tests/diagnostics/regex-match-destructuring-fences.ts b/tests/diagnostics/regex-match-destructuring-fences.ts new file mode 100644 index 0000000000..dc8671b894 --- /dev/null +++ b/tests/diagnostics/regex-match-destructuring-fences.ts @@ -0,0 +1,9 @@ +// Object patterns over regex match results compile for numeric positions, +// index, input and groups. A rest element would copy the result's own +// properties, a default on index or input has no lowering, and other array +// members are not data on the result. +const m = /(a)(b)?/.exec("ab")!; +const { index, ...rest } = m; +const { input = "none" } = m; +const { length } = m; +console.log(index, rest, input, length); diff --git a/tests/diagnostics/zlib-class-alias-escape.ts b/tests/diagnostics/zlib-class-alias-escape.ts new file mode 100644 index 0000000000..8c8e17452f --- /dev/null +++ b/tests/diagnostics/zlib-class-alias-escape.ts @@ -0,0 +1,11 @@ +// A zlib class alias owns no runtime value: construction, instanceof, +// typeof and .name compile, other uses report at the use. instanceof on +// an untyped operand reports as well. +import * as zlib from "node:zlib"; + +const Gz = zlib.Gzip; +const classes = [Gz]; +console.log(classes.length); + +const parsed: unknown = JSON.parse("{}"); +console.log(parsed instanceof Gz); diff --git a/tests/fixtures/node-test/cases/ctx-members.ts b/tests/fixtures/node-test/cases/ctx-members.ts new file mode 100644 index 0000000000..b9df422b3c --- /dev/null +++ b/tests/fixtures/node-test/cases/ctx-members.ts @@ -0,0 +1,33 @@ +// @exit: 1 +// TestContext members read as values: t.filePath (the entry file), +// t.attempt (0), t.passed (false while the body runs, the outcome in the +// test's t.after hooks), t.workerId (undefined outside a worker) and +// t.tags ([] without the tags option). +import { describe, it, test } from "node:test"; +import * as assert from "node:assert"; + +const seen: string[] = []; + +describe("passed", () => { + it("passes", (t) => { + t.after(() => { + seen.push(`after passes: ${t.passed}`); + }); + seen.push(`body: ${t.passed}`); + }); + it("fails", (t) => { + t.after(() => { + seen.push(`after fails: ${t.passed}`); + }); + throw new Error("expected failure"); + }); +}); + +test("members", (t) => { + const file = t.filePath ?? ""; + assert.strictEqual(file.endsWith("tests/fixtures/node-test/cases/ctx-members.ts"), true); + assert.strictEqual(t.attempt, 0); + assert.strictEqual(t.workerId === undefined, true); + assert.strictEqual(t.tags.length, 0); + assert.deepStrictEqual(seen, ["body: false", "after passes: true", "after fails: false"]); +}); diff --git a/tests/fixtures/node-test/cases/root-after-throws.ts b/tests/fixtures/node-test/cases/root-after-throws.ts new file mode 100644 index 0000000000..f7c20b9b00 --- /dev/null +++ b/tests/fixtures/node-test/cases/root-after-throws.ts @@ -0,0 +1,10 @@ +// A throwing module-level after hook: the run reports the entry file as a +// failed result with the hook's location, after the top-level results, +// without counting it, and the exit code stays 0, as in Node. +import { after, test } from "node:test"; + +after(() => { + throw new Error("root after failed"); +}); + +test("t", () => {}); diff --git a/tests/fixtures/node-test/cases/root-before-throws.ts b/tests/fixtures/node-test/cases/root-before-throws.ts new file mode 100644 index 0000000000..b12c50060e --- /dev/null +++ b/tests/fixtures/node-test/cases/root-before-throws.ts @@ -0,0 +1,30 @@ +// @exit: 1 +// A throwing module-level before hook: each top-level test fails with its +// error without running its body or beforeEach hooks, and each top-level +// suite fails with it while the tests and suites in it are cancelled +// before they start. Later module-level before hooks still run at their +// before() calls, and the first failure is the one reported. +import { before, beforeEach, describe, it, test } from "node:test"; + +before(() => { + throw new Error("root before failed"); +}); +before(async () => { + await Promise.resolve(); + throw new Error("a later failure"); +}); +beforeEach(() => { + throw new Error("beforeEach never runs"); +}); + +describe("s", () => { + it("x", () => {}); + describe("inner", () => { + it("y", () => {}); + }); + it("z", () => {}); +}); + +test("t", () => { + throw new Error("the body never runs"); +}); diff --git a/tests/fixtures/node-test/cases/root-hooks.ts b/tests/fixtures/node-test/cases/root-hooks.ts new file mode 100644 index 0000000000..8159aa7bb7 --- /dev/null +++ b/tests/fixtures/node-test/cases/root-hooks.ts @@ -0,0 +1,44 @@ +// Module-level before hooks run at their before() calls, with the root's +// context (named ""), and the first top-level test waits for the +// async ones; module-level after hooks run after the last top-level test +// or suite, and module-level beforeEach hooks run before each test, with +// that test's context. +import { after, before, beforeEach, describe, it, test } from "node:test"; +import * as assert from "node:assert"; + +const seen: string[] = []; + +before((c) => { + seen.push(`before: ${c.name}`); +}); +seen.push("after the before() call"); +before(async () => { + seen.push("async before starts"); + await new Promise((resolve) => setTimeout(resolve, 5)); + seen.push("async before ends"); +}); +beforeEach((c) => { + seen.push(`beforeEach: ${c.name}`); +}); +after(() => { + // Throws, and so fails the run visibly, unless every hook ran in order. + assert.deepStrictEqual(seen, [ + "before: ", + "after the before() call", + "async before starts", + "suite body", + "async before ends", + "beforeEach: x", + "beforeEach: t", + "report", + ]); +}); + +describe("s", () => { + seen.push("suite body"); + it("x", () => {}); +}); + +test("t", () => { + seen.push("report"); +}); diff --git a/tests/fixtures/node-test/cases/suite-context.ts b/tests/fixtures/node-test/cases/suite-context.ts new file mode 100644 index 0000000000..5a13136fff --- /dev/null +++ b/tests/fixtures/node-test/cases/suite-context.ts @@ -0,0 +1,57 @@ +// The SuiteContext a describe body receives (name, fullName, filePath, +// attempt, passed, diagnostic), and the context hooks receive: before and +// after get the suite's, beforeEach and afterEach the test's, and a test's +// t.before and t.after hooks the test's. +import { after, afterEach, before, beforeEach, describe, it, test } from "node:test"; +import * as assert from "node:assert"; + +const seen: string[] = []; +let outerPassed = (): boolean => false; + +describe("outer", (s) => { + const file = (s.filePath ?? "").endsWith("tests/fixtures/node-test/cases/suite-context.ts"); + seen.push(`body: ${s.name} | ${s.fullName} | ${s.passed} | ${s.attempt} | ${file}`); + s.diagnostic("suite diagnostic"); + outerPassed = () => s.passed; + before((c) => { + seen.push(`before: ${c.name}`); + }); + after((c) => { + seen.push(`after: ${c.name} ${c.passed}`); + }); + beforeEach((c) => { + seen.push(`beforeEach: ${c.name}`); + }); + afterEach((c) => { + seen.push(`afterEach: ${c.name} ${c.passed}`); + }); + describe("inner", (s2) => { + seen.push(`inner: ${s2.fullName}`); + it("x", () => {}); + }); + it("y", (t) => { + t.before((c) => { + seen.push(`t.before: ${c.name}`); + }); + t.after((c) => { + seen.push(`t.after: ${c.name} ${c.passed}`); + }); + }); +}); + +test("report", () => { + seen.push(`outer afterwards: ${outerPassed()}`); + assert.deepStrictEqual(seen, [ + "body: outer | outer | false | 0 | true", + "inner: outer > inner", + "before: outer", + "beforeEach: x", + "afterEach: x true", + "beforeEach: y", + "t.before: y", + "afterEach: y true", + "t.after: y true", + "after: outer false", + "outer afterwards: true", + ]); +}); diff --git a/tests/fixtures/node-types/child-exec-code-fences.ts b/tests/fixtures/node-types/child-exec-code-fences.ts new file mode 100644 index 0000000000..71f627df46 --- /dev/null +++ b/tests/fixtures/node-types/child-exec-code-fences.ts @@ -0,0 +1,8 @@ +import { exec } from "node:child_process"; + +exec("exit 3", (error) => { + if (error === null) return; + console.log(error.code); + console.log("code" in error); + console.log(typeof error.code === "number"); +}); diff --git a/tests/fixtures/node-types/child-exec.ts b/tests/fixtures/node-types/child-exec.ts new file mode 100644 index 0000000000..97e9c546df --- /dev/null +++ b/tests/fixtures/node-types/child-exec.ts @@ -0,0 +1,9 @@ +import { exec } from "node:child_process"; + +exec("printf typed-exec; printf err >&2", (error, stdout, stderr) => { + if (error !== null) throw error; + console.log(stdout, stderr); + exec("exit 2", { encoding: "utf8" }, (failed) => { + console.log(JSON.stringify(failed === null ? "" : failed.message)); + }); +}); diff --git a/tests/fixtures/node-types/promisify-crypto.ts b/tests/fixtures/node-types/promisify-crypto.ts new file mode 100644 index 0000000000..f854b02780 --- /dev/null +++ b/tests/fixtures/node-types/promisify-crypto.ts @@ -0,0 +1,22 @@ +import * as crypto from "node:crypto"; +import { promisify } from "node:util"; + +const randomBytes = promisify(crypto.randomBytes); +const pbkdf2 = promisify(crypto.pbkdf2); +const scrypt = promisify(crypto.scrypt); +const hkdf = promisify(crypto.hkdf); +const randomInt = promisify(crypto.randomInt); +const generateKey = promisify(crypto.generateKey); + +console.log((await randomBytes(12)).length); +console.log((await pbkdf2("pw", "salt", 10, 16, "sha512")).toString("hex")); +console.log(((await scrypt("pw", "salt", 16)) as Buffer).toString("base64")); +console.log(Buffer.from(await hkdf("sha256", "key", "salt", "info", 12)).toString("hex")); +const n = (await randomInt(4)) as number; +console.log(n >= 0 && n < 4); +console.log((await generateKey("aes", { length: 128 })).symmetricKeySize); +try { + await pbkdf2("pw", "salt", 1, 8, "md-none"); +} catch (e) { + if (e instanceof Error) console.log(e.name, e.message); +} diff --git a/tests/fixtures/node-types/promisify-dns.ts b/tests/fixtures/node-types/promisify-dns.ts new file mode 100644 index 0000000000..9e4fe9c199 --- /dev/null +++ b/tests/fixtures/node-types/promisify-dns.ts @@ -0,0 +1,18 @@ +import * as dns from "node:dns"; +import { promisify } from "node:util"; + +const lookup = promisify(dns.lookup); +const lookupService = promisify(dns.lookupService); + +const one = await lookup("127.0.0.1"); +console.log(one.address, one.family); +const all = await lookup("::1", { all: true }); +console.log(all.map((entry) => `${entry.address}/${entry.family}`).join(",")); +const service = await lookupService("127.0.0.1", 80); +console.log(typeof service.hostname, typeof service.service); +try { + await lookup("no-such-host.invalid"); +} catch (e) { + const err = e as NodeJS.ErrnoException; + console.log(err.code, err.message); +} diff --git a/tests/fixtures/npm-static/cjs-interop-cli.ts b/tests/fixtures/npm-static/cjs-interop-cli.ts new file mode 100644 index 0000000000..2b9a88f635 --- /dev/null +++ b/tests/fixtures/npm-static/cjs-interop-cli.ts @@ -0,0 +1,9 @@ +// npm-static pilots: real CommonJS packages that preflight refused before. +// fs.realpath@1.0.0 and on-finished@2.4.1 assign module.exports and run +// code before they require their dependencies (each binding is reachable +// only after its require ran); statuses@2.0.1 requires its codes.json. +import realpath from "fs.realpath"; +import onFinished from "on-finished"; +import status from "statuses"; + +console.log(typeof realpath.realpathSync, typeof onFinished, typeof status); diff --git a/tests/fixtures/npm-static/node_modules/ee-first/LICENSE b/tests/fixtures/npm-static/node_modules/ee-first/LICENSE new file mode 100644 index 0000000000..a7ae8ee9b8 --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/ee-first/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/tests/fixtures/npm-static/node_modules/ee-first/index.js b/tests/fixtures/npm-static/node_modules/ee-first/index.js new file mode 100644 index 0000000000..501287cd3b --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/ee-first/index.js @@ -0,0 +1,95 @@ +/*! + * ee-first + * Copyright(c) 2014 Jonathan Ong + * MIT Licensed + */ + +'use strict' + +/** + * Module exports. + * @public + */ + +module.exports = first + +/** + * Get the first event in a set of event emitters and event pairs. + * + * @param {array} stuff + * @param {function} done + * @public + */ + +function first(stuff, done) { + if (!Array.isArray(stuff)) + throw new TypeError('arg must be an array of [ee, events...] arrays') + + var cleanups = [] + + for (var i = 0; i < stuff.length; i++) { + var arr = stuff[i] + + if (!Array.isArray(arr) || arr.length < 2) + throw new TypeError('each array member must be [ee, events...]') + + var ee = arr[0] + + for (var j = 1; j < arr.length; j++) { + var event = arr[j] + var fn = listener(event, callback) + + // listen to the event + ee.on(event, fn) + // push this listener to the list of cleanups + cleanups.push({ + ee: ee, + event: event, + fn: fn, + }) + } + } + + function callback() { + cleanup() + done.apply(null, arguments) + } + + function cleanup() { + var x + for (var i = 0; i < cleanups.length; i++) { + x = cleanups[i] + x.ee.removeListener(x.event, x.fn) + } + } + + function thunk(fn) { + done = fn + } + + thunk.cancel = cleanup + + return thunk +} + +/** + * Create the event listener. + * @private + */ + +function listener(event, done) { + return function onevent(arg1) { + var args = new Array(arguments.length) + var ee = this + var err = event === 'error' + ? arg1 + : null + + // copy args to prevent arguments escaping scope + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + + done(err, ee, event, args) + } +} diff --git a/tests/fixtures/npm-static/node_modules/ee-first/package.json b/tests/fixtures/npm-static/node_modules/ee-first/package.json new file mode 100644 index 0000000000..b6d0b7d675 --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/ee-first/package.json @@ -0,0 +1,29 @@ +{ + "name": "ee-first", + "description": "return the first event in a set of ee/event pairs", + "version": "1.1.1", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com", + "twitter": "https://twitter.com/jongleberry" + }, + "contributors": [ + "Douglas Christopher Wilson " + ], + "license": "MIT", + "repository": "jonathanong/ee-first", + "devDependencies": { + "istanbul": "0.3.9", + "mocha": "2.2.5" + }, + "files": [ + "index.js", + "LICENSE" + ], + "scripts": { + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", + "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" + } +} diff --git a/tests/fixtures/npm-static/node_modules/fs.realpath/LICENSE b/tests/fixtures/npm-static/node_modules/fs.realpath/LICENSE new file mode 100644 index 0000000000..5bd884c252 --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/fs.realpath/LICENSE @@ -0,0 +1,43 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +---- + +This library bundles a version of the `fs.realpath` and `fs.realpathSync` +methods from Node.js v0.10 under the terms of the Node.js MIT license. + +Node's license follows, also included at the header of `old.js` which contains +the licensed code: + + Copyright Joyent, Inc. and other Node contributors. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. diff --git a/tests/fixtures/npm-static/node_modules/fs.realpath/index.js b/tests/fixtures/npm-static/node_modules/fs.realpath/index.js new file mode 100644 index 0000000000..b09c7c7e63 --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/fs.realpath/index.js @@ -0,0 +1,66 @@ +module.exports = realpath +realpath.realpath = realpath +realpath.sync = realpathSync +realpath.realpathSync = realpathSync +realpath.monkeypatch = monkeypatch +realpath.unmonkeypatch = unmonkeypatch + +var fs = require('fs') +var origRealpath = fs.realpath +var origRealpathSync = fs.realpathSync + +var version = process.version +var ok = /^v[0-5]\./.test(version) +var old = require('./old.js') + +function newError (er) { + return er && er.syscall === 'realpath' && ( + er.code === 'ELOOP' || + er.code === 'ENOMEM' || + er.code === 'ENAMETOOLONG' + ) +} + +function realpath (p, cache, cb) { + if (ok) { + return origRealpath(p, cache, cb) + } + + if (typeof cache === 'function') { + cb = cache + cache = null + } + origRealpath(p, cache, function (er, result) { + if (newError(er)) { + old.realpath(p, cache, cb) + } else { + cb(er, result) + } + }) +} + +function realpathSync (p, cache) { + if (ok) { + return origRealpathSync(p, cache) + } + + try { + return origRealpathSync(p, cache) + } catch (er) { + if (newError(er)) { + return old.realpathSync(p, cache) + } else { + throw er + } + } +} + +function monkeypatch () { + fs.realpath = realpath + fs.realpathSync = realpathSync +} + +function unmonkeypatch () { + fs.realpath = origRealpath + fs.realpathSync = origRealpathSync +} diff --git a/tests/fixtures/npm-static/node_modules/fs.realpath/old.js b/tests/fixtures/npm-static/node_modules/fs.realpath/old.js new file mode 100644 index 0000000000..b40305e73f --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/fs.realpath/old.js @@ -0,0 +1,303 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var pathModule = require('path'); +var isWindows = process.platform === 'win32'; +var fs = require('fs'); + +// JavaScript implementation of realpath, ported from node pre-v6 + +var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); + +function rethrow() { + // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and + // is fairly slow to generate. + var callback; + if (DEBUG) { + var backtrace = new Error; + callback = debugCallback; + } else + callback = missingCallback; + + return callback; + + function debugCallback(err) { + if (err) { + backtrace.message = err.message; + err = backtrace; + missingCallback(err); + } + } + + function missingCallback(err) { + if (err) { + if (process.throwDeprecation) + throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs + else if (!process.noDeprecation) { + var msg = 'fs: missing callback ' + (err.stack || err.message); + if (process.traceDeprecation) + console.trace(msg); + else + console.error(msg); + } + } + } +} + +function maybeCallback(cb) { + return typeof cb === 'function' ? cb : rethrow(); +} + +var normalize = pathModule.normalize; + +// Regexp that finds the next partion of a (partial) path +// result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] +if (isWindows) { + var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; +} else { + var nextPartRe = /(.*?)(?:[\/]+|$)/g; +} + +// Regex to find the device root, including trailing slash. E.g. 'c:\\'. +if (isWindows) { + var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; +} else { + var splitRootRe = /^[\/]*/; +} + +exports.realpathSync = function realpathSync(p, cache) { + // make p is absolute + p = pathModule.resolve(p); + + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return cache[p]; + } + + var original = p, + seenLinks = {}, + knownHard = {}; + + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; + + start(); + + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; + + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstatSync(base); + knownHard[base] = true; + } + } + + // walk down the path, swapping out linked pathparts for their real + // values + // NB: p.length changes. + while (pos < p.length) { + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; + + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + continue; + } + + var resolvedLink; + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // some known symbolic link. no need to stat again. + resolvedLink = cache[base]; + } else { + var stat = fs.lstatSync(base); + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + continue; + } + + // read the link if it wasn't read before + // dev/ino always return 0 on windows, so skip the check. + var linkTarget = null; + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + linkTarget = seenLinks[id]; + } + } + if (linkTarget === null) { + fs.statSync(base); + linkTarget = fs.readlinkSync(base); + } + resolvedLink = pathModule.resolve(previous, linkTarget); + // track this, if given a cache. + if (cache) cache[base] = resolvedLink; + if (!isWindows) seenLinks[id] = linkTarget; + } + + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); + } + + if (cache) cache[original] = p; + + return p; +}; + + +exports.realpath = function realpath(p, cache, cb) { + if (typeof cb !== 'function') { + cb = maybeCallback(cache); + cache = null; + } + + // make p is absolute + p = pathModule.resolve(p); + + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return process.nextTick(cb.bind(null, null, cache[p])); + } + + var original = p, + seenLinks = {}, + knownHard = {}; + + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; + + start(); + + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; + + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstat(base, function(err) { + if (err) return cb(err); + knownHard[base] = true; + LOOP(); + }); + } else { + process.nextTick(LOOP); + } + } + + // walk down the path, swapping out linked pathparts for their real + // values + function LOOP() { + // stop if scanned past end of path + if (pos >= p.length) { + if (cache) cache[original] = p; + return cb(null, p); + } + + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; + + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + return process.nextTick(LOOP); + } + + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // known symbolic link. no need to stat again. + return gotResolvedLink(cache[base]); + } + + return fs.lstat(base, gotStat); + } + + function gotStat(err, stat) { + if (err) return cb(err); + + // if not a symlink, skip to the next path part + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + return process.nextTick(LOOP); + } + + // stat & read the link if not read before + // call gotTarget as soon as the link target is known + // dev/ino always return 0 on windows, so skip the check. + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + return gotTarget(null, seenLinks[id], base); + } + } + fs.stat(base, function(err) { + if (err) return cb(err); + + fs.readlink(base, function(err, target) { + if (!isWindows) seenLinks[id] = target; + gotTarget(err, target); + }); + }); + } + + function gotTarget(err, target, base) { + if (err) return cb(err); + + var resolvedLink = pathModule.resolve(previous, target); + if (cache) cache[base] = resolvedLink; + gotResolvedLink(resolvedLink); + } + + function gotResolvedLink(resolvedLink) { + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); + } +}; diff --git a/tests/fixtures/npm-static/node_modules/fs.realpath/package.json b/tests/fixtures/npm-static/node_modules/fs.realpath/package.json new file mode 100644 index 0000000000..3edc57d21c --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/fs.realpath/package.json @@ -0,0 +1,26 @@ +{ + "name": "fs.realpath", + "version": "1.0.0", + "description": "Use node's fs.realpath, but fall back to the JS implementation if the native one fails", + "main": "index.js", + "dependencies": {}, + "devDependencies": {}, + "scripts": { + "test": "tap test/*.js --cov" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/isaacs/fs.realpath.git" + }, + "keywords": [ + "realpath", + "fs", + "polyfill" + ], + "author": "Isaac Z. Schlueter (http://blog.izs.me/)", + "license": "ISC", + "files": [ + "old.js", + "index.js" + ] +} diff --git a/tests/fixtures/npm-static/node_modules/on-finished/LICENSE b/tests/fixtures/npm-static/node_modules/on-finished/LICENSE new file mode 100644 index 0000000000..5931fd23ea --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/on-finished/LICENSE @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2013 Jonathan Ong +Copyright (c) 2014 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/fixtures/npm-static/node_modules/on-finished/index.js b/tests/fixtures/npm-static/node_modules/on-finished/index.js new file mode 100644 index 0000000000..e68df7bde3 --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/on-finished/index.js @@ -0,0 +1,234 @@ +/*! + * on-finished + * Copyright(c) 2013 Jonathan Ong + * Copyright(c) 2014 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module exports. + * @public + */ + +module.exports = onFinished +module.exports.isFinished = isFinished + +/** + * Module dependencies. + * @private + */ + +var asyncHooks = tryRequireAsyncHooks() +var first = require('ee-first') + +/** + * Variables. + * @private + */ + +/* istanbul ignore next */ +var defer = typeof setImmediate === 'function' + ? setImmediate + : function (fn) { process.nextTick(fn.bind.apply(fn, arguments)) } + +/** + * Invoke callback when the response has finished, useful for + * cleaning up resources afterwards. + * + * @param {object} msg + * @param {function} listener + * @return {object} + * @public + */ + +function onFinished (msg, listener) { + if (isFinished(msg) !== false) { + defer(listener, null, msg) + return msg + } + + // attach the listener to the message + attachListener(msg, wrap(listener)) + + return msg +} + +/** + * Determine if message is already finished. + * + * @param {object} msg + * @return {boolean} + * @public + */ + +function isFinished (msg) { + var socket = msg.socket + + if (typeof msg.finished === 'boolean') { + // OutgoingMessage + return Boolean(msg.finished || (socket && !socket.writable)) + } + + if (typeof msg.complete === 'boolean') { + // IncomingMessage + return Boolean(msg.upgrade || !socket || !socket.readable || (msg.complete && !msg.readable)) + } + + // don't know + return undefined +} + +/** + * Attach a finished listener to the message. + * + * @param {object} msg + * @param {function} callback + * @private + */ + +function attachFinishedListener (msg, callback) { + var eeMsg + var eeSocket + var finished = false + + function onFinish (error) { + eeMsg.cancel() + eeSocket.cancel() + + finished = true + callback(error) + } + + // finished on first message event + eeMsg = eeSocket = first([[msg, 'end', 'finish']], onFinish) + + function onSocket (socket) { + // remove listener + msg.removeListener('socket', onSocket) + + if (finished) return + if (eeMsg !== eeSocket) return + + // finished on first socket event + eeSocket = first([[socket, 'error', 'close']], onFinish) + } + + if (msg.socket) { + // socket already assigned + onSocket(msg.socket) + return + } + + // wait for socket to be assigned + msg.on('socket', onSocket) + + if (msg.socket === undefined) { + // istanbul ignore next: node.js 0.8 patch + patchAssignSocket(msg, onSocket) + } +} + +/** + * Attach the listener to the message. + * + * @param {object} msg + * @return {function} + * @private + */ + +function attachListener (msg, listener) { + var attached = msg.__onFinished + + // create a private single listener with queue + if (!attached || !attached.queue) { + attached = msg.__onFinished = createListener(msg) + attachFinishedListener(msg, attached) + } + + attached.queue.push(listener) +} + +/** + * Create listener on message. + * + * @param {object} msg + * @return {function} + * @private + */ + +function createListener (msg) { + function listener (err) { + if (msg.__onFinished === listener) msg.__onFinished = null + if (!listener.queue) return + + var queue = listener.queue + listener.queue = null + + for (var i = 0; i < queue.length; i++) { + queue[i](err, msg) + } + } + + listener.queue = [] + + return listener +} + +/** + * Patch ServerResponse.prototype.assignSocket for node.js 0.8. + * + * @param {ServerResponse} res + * @param {function} callback + * @private + */ + +// istanbul ignore next: node.js 0.8 patch +function patchAssignSocket (res, callback) { + var assignSocket = res.assignSocket + + if (typeof assignSocket !== 'function') return + + // res.on('socket', callback) is broken in 0.8 + res.assignSocket = function _assignSocket (socket) { + assignSocket.call(this, socket) + callback(socket) + } +} + +/** + * Try to require async_hooks + * @private + */ + +function tryRequireAsyncHooks () { + try { + return require('async_hooks') + } catch (e) { + return {} + } +} + +/** + * Wrap function with async resource, if possible. + * AsyncResource.bind static method backported. + * @private + */ + +function wrap (fn) { + var res + + // create anonymous resource + if (asyncHooks.AsyncResource) { + res = new asyncHooks.AsyncResource(fn.name || 'bound-anonymous-fn') + } + + // incompatible node.js + if (!res || !res.runInAsyncScope) { + return fn + } + + // return bound function + return res.runInAsyncScope.bind(res, fn, null) +} diff --git a/tests/fixtures/npm-static/node_modules/on-finished/package.json b/tests/fixtures/npm-static/node_modules/on-finished/package.json new file mode 100644 index 0000000000..644cd814b9 --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/on-finished/package.json @@ -0,0 +1,39 @@ +{ + "name": "on-finished", + "description": "Execute a callback when a request closes, finishes, or errors", + "version": "2.4.1", + "contributors": [ + "Douglas Christopher Wilson ", + "Jonathan Ong (http://jongleberry.com)" + ], + "license": "MIT", + "repository": "jshttp/on-finished", + "dependencies": { + "ee-first": "1.1.1" + }, + "devDependencies": { + "eslint": "7.32.0", + "eslint-config-standard": "14.1.1", + "eslint-plugin-import": "2.25.4", + "eslint-plugin-markdown": "2.2.1", + "eslint-plugin-node": "11.1.0", + "eslint-plugin-promise": "5.2.0", + "eslint-plugin-standard": "4.1.0", + "mocha": "9.2.1", + "nyc": "15.1.0" + }, + "engines": { + "node": ">= 0.8" + }, + "files": [ + "HISTORY.md", + "LICENSE", + "index.js" + ], + "scripts": { + "lint": "eslint .", + "test": "mocha --reporter spec --bail --check-leaks test/", + "test-ci": "nyc --reporter=lcovonly --reporter=text npm test", + "test-cov": "nyc --reporter=html --reporter=text npm test" + } +} diff --git a/tests/fixtures/npm-static/node_modules/statuses/LICENSE b/tests/fixtures/npm-static/node_modules/statuses/LICENSE new file mode 100644 index 0000000000..28a3161820 --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/statuses/LICENSE @@ -0,0 +1,23 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2016 Douglas Christopher Wilson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/tests/fixtures/npm-static/node_modules/statuses/codes.json b/tests/fixtures/npm-static/node_modules/statuses/codes.json new file mode 100644 index 0000000000..1333ed10ba --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/statuses/codes.json @@ -0,0 +1,65 @@ +{ + "100": "Continue", + "101": "Switching Protocols", + "102": "Processing", + "103": "Early Hints", + "200": "OK", + "201": "Created", + "202": "Accepted", + "203": "Non-Authoritative Information", + "204": "No Content", + "205": "Reset Content", + "206": "Partial Content", + "207": "Multi-Status", + "208": "Already Reported", + "226": "IM Used", + "300": "Multiple Choices", + "301": "Moved Permanently", + "302": "Found", + "303": "See Other", + "304": "Not Modified", + "305": "Use Proxy", + "307": "Temporary Redirect", + "308": "Permanent Redirect", + "400": "Bad Request", + "401": "Unauthorized", + "402": "Payment Required", + "403": "Forbidden", + "404": "Not Found", + "405": "Method Not Allowed", + "406": "Not Acceptable", + "407": "Proxy Authentication Required", + "408": "Request Timeout", + "409": "Conflict", + "410": "Gone", + "411": "Length Required", + "412": "Precondition Failed", + "413": "Payload Too Large", + "414": "URI Too Long", + "415": "Unsupported Media Type", + "416": "Range Not Satisfiable", + "417": "Expectation Failed", + "418": "I'm a Teapot", + "421": "Misdirected Request", + "422": "Unprocessable Entity", + "423": "Locked", + "424": "Failed Dependency", + "425": "Too Early", + "426": "Upgrade Required", + "428": "Precondition Required", + "429": "Too Many Requests", + "431": "Request Header Fields Too Large", + "451": "Unavailable For Legal Reasons", + "500": "Internal Server Error", + "501": "Not Implemented", + "502": "Bad Gateway", + "503": "Service Unavailable", + "504": "Gateway Timeout", + "505": "HTTP Version Not Supported", + "506": "Variant Also Negotiates", + "507": "Insufficient Storage", + "508": "Loop Detected", + "509": "Bandwidth Limit Exceeded", + "510": "Not Extended", + "511": "Network Authentication Required" +} diff --git a/tests/fixtures/npm-static/node_modules/statuses/index.js b/tests/fixtures/npm-static/node_modules/statuses/index.js new file mode 100644 index 0000000000..ea351c5535 --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/statuses/index.js @@ -0,0 +1,146 @@ +/*! + * statuses + * Copyright(c) 2014 Jonathan Ong + * Copyright(c) 2016 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var codes = require('./codes.json') + +/** + * Module exports. + * @public + */ + +module.exports = status + +// status code to message map +status.message = codes + +// status message (lower-case) to code map +status.code = createMessageToStatusCodeMap(codes) + +// array of status codes +status.codes = createStatusCodeList(codes) + +// status codes for redirects +status.redirect = { + 300: true, + 301: true, + 302: true, + 303: true, + 305: true, + 307: true, + 308: true +} + +// status codes for empty bodies +status.empty = { + 204: true, + 205: true, + 304: true +} + +// status codes for when you should retry the request +status.retry = { + 502: true, + 503: true, + 504: true +} + +/** + * Create a map of message to status code. + * @private + */ + +function createMessageToStatusCodeMap (codes) { + var map = {} + + Object.keys(codes).forEach(function forEachCode (code) { + var message = codes[code] + var status = Number(code) + + // populate map + map[message.toLowerCase()] = status + }) + + return map +} + +/** + * Create a list of all status codes. + * @private + */ + +function createStatusCodeList (codes) { + return Object.keys(codes).map(function mapCode (code) { + return Number(code) + }) +} + +/** + * Get the status code for given message. + * @private + */ + +function getStatusCode (message) { + var msg = message.toLowerCase() + + if (!Object.prototype.hasOwnProperty.call(status.code, msg)) { + throw new Error('invalid status message: "' + message + '"') + } + + return status.code[msg] +} + +/** + * Get the status message for given code. + * @private + */ + +function getStatusMessage (code) { + if (!Object.prototype.hasOwnProperty.call(status.message, code)) { + throw new Error('invalid status code: ' + code) + } + + return status.message[code] +} + +/** + * Get the status code. + * + * Given a number, this will throw if it is not a known status + * code, otherwise the code will be returned. Given a string, + * the string will be parsed for a number and return the code + * if valid, otherwise will lookup the code assuming this is + * the status message. + * + * @param {string|number} code + * @returns {number} + * @public + */ + +function status (code) { + if (typeof code === 'number') { + return getStatusMessage(code) + } + + if (typeof code !== 'string') { + throw new TypeError('code must be a number or string') + } + + // '403' + var n = parseInt(code, 10) + if (!isNaN(n)) { + return getStatusMessage(n) + } + + return getStatusCode(code) +} diff --git a/tests/fixtures/npm-static/node_modules/statuses/package.json b/tests/fixtures/npm-static/node_modules/statuses/package.json new file mode 100644 index 0000000000..8c3e719b50 --- /dev/null +++ b/tests/fixtures/npm-static/node_modules/statuses/package.json @@ -0,0 +1,49 @@ +{ + "name": "statuses", + "description": "HTTP status utility", + "version": "2.0.1", + "contributors": [ + "Douglas Christopher Wilson ", + "Jonathan Ong (http://jongleberry.com)" + ], + "repository": "jshttp/statuses", + "license": "MIT", + "keywords": [ + "http", + "status", + "code" + ], + "files": [ + "HISTORY.md", + "index.js", + "codes.json", + "LICENSE" + ], + "devDependencies": { + "csv-parse": "4.14.2", + "eslint": "7.17.0", + "eslint-config-standard": "14.1.1", + "eslint-plugin-import": "2.22.1", + "eslint-plugin-markdown": "1.0.2", + "eslint-plugin-node": "11.1.0", + "eslint-plugin-promise": "4.2.1", + "eslint-plugin-standard": "4.1.0", + "mocha": "8.2.1", + "nyc": "15.1.0", + "raw-body": "2.4.1", + "stream-to-array": "2.3.0" + }, + "engines": { + "node": ">= 0.8" + }, + "scripts": { + "build": "node scripts/build.js", + "fetch": "node scripts/fetch-apache.js && node scripts/fetch-iana.js && node scripts/fetch-nginx.js && node scripts/fetch-node.js", + "lint": "eslint --plugin markdown --ext js,md .", + "test": "mocha --reporter spec --check-leaks --bail test/", + "test-ci": "nyc --reporter=lcov --reporter=text npm test", + "test-cov": "nyc --reporter=html --reporter=text npm test", + "update": "npm run fetch && npm run build", + "version": "node scripts/version-history.js && git add HISTORY.md" + } +} diff --git a/tests/harness/__snapshots__/cjs-require-cycle/main.js.txt b/tests/harness/__snapshots__/cjs-require-cycle/main.js.txt index ad55be5244..8916284c3a 100644 --- a/tests/harness/__snapshots__/cjs-require-cycle/main.js.txt +++ b/tests/harness/__snapshots__/cjs-require-cycle/main.js.txt @@ -1,4 +1,4 @@ -cjs-require-cycle/a.js:1:1 - error SC1016: circular imports (cjs-require-cycle/a.js → cjs-require-cycle/b.js → cjs-require-cycle/a.js; the cycle closes through a require() edge) are not supported yet +cjs-require-cycle/a.js:1:1 - error SC1016: circular imports (cjs-require-cycle/a.js → cjs-require-cycle/b.js → cjs-require-cycle/a.js; the cycle closes through a require() edge: cjs-require-cycle/a.js uses its exports at cjs-require-cycle/a.js:6 outside the export statements that precede its first require, so a module of the cycle can observe them before they settle) are not supported yet 1 | 'use strict'; | ^ diff --git a/tests/harness/__snapshots__/cjs-require-tdz-unchecked/main.js.txt b/tests/harness/__snapshots__/cjs-require-tdz-unchecked/main.js.txt new file mode 100644 index 0000000000..eda869e9a6 --- /dev/null +++ b/tests/harness/__snapshots__/cjs-require-tdz-unchecked/main.js.txt @@ -0,0 +1,6 @@ +cjs-require-tdz-unchecked/main.js:7:20 - error SC1013: reading require() binding 'greet' where it can run before the require (this use has no checked form for Node's ReferenceError — move the require above the code that can run first) is not supported yet + + 6 | + 7 | module.exports = { greet }; + | ^~~~~ + 8 | const { greet } = require('./lib.js'); \ No newline at end of file diff --git a/tests/harness/__snapshots__/cjs-require-tdz/main.js.txt b/tests/harness/__snapshots__/cjs-require-tdz/main.js.txt index 6316ce98b4..295f7cd0c1 100644 --- a/tests/harness/__snapshots__/cjs-require-tdz/main.js.txt +++ b/tests/harness/__snapshots__/cjs-require-tdz/main.js.txt @@ -1,6 +1,6 @@ -cjs-require-tdz/main.js:8:7 - error SC1013: require() below code that can reach its binding 'greet' (Node initializes the binding AT the require and throws ReferenceError on any earlier access, which is not modeled — move the require above the code that can run first) is not supported yet +cjs-require-tdz/main.js:9:5 - error SC1013: var require() binding 'lib' below code that can reach it (Node reads undefined before the require runs, which is not modeled — declare it with const or let, or move the require above the code that can run first) is not supported yet - 7 | console.log('boot', helper()); - 8 | const { greet } = require('./lib.js'); - | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 9 | \ No newline at end of file + 8 | console.log('boot', helper()); + 9 | var lib = require('./lib.js'); + | ^~~~~~~~~~~~~~~~~~~~~~~~~ + 10 | \ No newline at end of file diff --git a/tests/harness/__snapshots__/deferred-class-binding/main.ts.txt b/tests/harness/__snapshots__/deferred-class-binding/main.ts.txt index c80ae94e9b..381d1a0a48 100644 --- a/tests/harness/__snapshots__/deferred-class-binding/main.ts.txt +++ b/tests/harness/__snapshots__/deferred-class-binding/main.ts.txt @@ -1,4 +1,4 @@ -deferred-class-binding/crate.ts:3:3 - error SC1090: 'unknown'-typed class fields are not supported yet +deferred-class-binding/crate.ts:3:3 - error SC1090: 'any'-typed class fields are not supported yet 2 | readonly label: string; 3 | contents: any; diff --git a/tests/harness/__snapshots__/deferred-class-receiver/main.ts.txt b/tests/harness/__snapshots__/deferred-class-receiver/main.ts.txt index 4431445ce5..0e9cab090c 100644 --- a/tests/harness/__snapshots__/deferred-class-receiver/main.ts.txt +++ b/tests/harness/__snapshots__/deferred-class-receiver/main.ts.txt @@ -5,7 +5,7 @@ deferred-class-receiver/main.ts:4:10 - error SC1090: constructing through a clas | ^~~~~~~~~~~~~~~ 5 | } -deferred-class-receiver/shelf.ts:3:20 - error SC1090: 'unknown'-typed class fields are not supported yet +deferred-class-receiver/shelf.ts:3:20 - error SC1090: 'WeakMap'-typed class fields are not supported yet 2 | readonly name: string; 3 | private readonly notes = new WeakMap(); diff --git a/tests/harness/__snapshots__/error-cause-redeclared-fences.ts.txt b/tests/harness/__snapshots__/error-cause-redeclared-fences.ts.txt new file mode 100644 index 0000000000..67b26e2edf --- /dev/null +++ b/tests/harness/__snapshots__/error-cause-redeclared-fences.ts.txt @@ -0,0 +1,41 @@ +error-cause-redeclared-fences.ts:6:12 - error SC1090: redeclaring Error.cause with a type other than 'unknown' or 'object' (declare it 'cause?: unknown' and narrow at the read) is not supported yet + + 5 | class Typed extends Error { + 6 | override cause?: Error; + | ^~~~~ + 7 | } + +error-cause-redeclared-fences.ts:9:31 - error SC1090: redeclaring Error.cause as a parameter property (declare 'cause?: unknown' and assign it in the constructor) is not supported yet + + 8 | class FromParameter extends Error { + 9 | constructor(public override cause: unknown) { + | ^~~~~ + 10 | super("parameter"); + +error-cause-redeclared-fences.ts:14:16 - error SC1090: Error.cause accessors are not supported yet + + 13 | class Accessor extends Error { + 14 | override get cause(): unknown { + | ^~~~~ + 15 | return "accessor"; + +error-cause-redeclared-fences.ts:18:13 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet + + 17 | } + 18 | console.log(new Typed("typed").cause); + | ^~~~~~~~~~~~~~~~~~ + 19 | console.log(new FromParameter(1).cause); + +error-cause-redeclared-fences.ts:19:13 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet + + 18 | console.log(new Typed("typed").cause); + 19 | console.log(new FromParameter(1).cause); + | ^~~~~~~~~~~~~~~~~~~~ + 20 | console.log(new Accessor("a").cause); + +error-cause-redeclared-fences.ts:20:13 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet + + 19 | console.log(new FromParameter(1).cause); + 20 | console.log(new Accessor("a").cause); + | ^~~~~~~~~~~~~~~~~ + 21 | \ No newline at end of file diff --git a/tests/harness/__snapshots__/regex-match-destructuring-fences.ts.txt b/tests/harness/__snapshots__/regex-match-destructuring-fences.ts.txt new file mode 100644 index 0000000000..d4eb59168c --- /dev/null +++ b/tests/harness/__snapshots__/regex-match-destructuring-fences.ts.txt @@ -0,0 +1,36 @@ +regex-match-destructuring-fences.ts:6:16 - error SC1031: rest bindings over regex match results (the rest copies the positions and the index, input and groups properties — destructure them by name) are not supported yet + + 5 | const m = /(a)(b)?/.exec("ab")!; + 6 | const { index, ...rest } = m; + | ^~~~~~~ + 7 | const { input = "none" } = m; + +regex-match-destructuring-fences.ts:6:19 - error SC2001: values of type '{ [n: number]: string; input: string; 0: string; length: number; toString(): string; toLocaleString(): string; toLocaleString(locales: string | string[], options?: (NumberFormatOptions & DateTimeFormatOptions) | undefined): string; ... 39 more ...; with(index: number, value: string): string[]; }' cannot be compiled yet (supported: number, string, boolean, arrays, Maps, Sets, RegExp, read-only Date values, functions, classes, records, unions of those, and 'unknown') + + 5 | const m = /(a)(b)?/.exec("ab")!; + 6 | const { index, ...rest } = m; + | ^~~~ + 7 | const { input = "none" } = m; + +regex-match-destructuring-fences.ts:7:9 - error SC1031: binding defaults on the 'input' of a regex match result is not supported yet + + 6 | const { index, ...rest } = m; + 7 | const { input = "none" } = m; + | ^~~~~~~~~~~~~~ + 8 | const { length } = m; + +regex-match-destructuring-fences.ts:8:9 - error SC1031: destructuring the property 'length' of a regex match result (numeric positions, index, input and groups compile) is not supported yet + + 7 | const { input = "none" } = m; + 8 | const { length } = m; + | ^~~~~~ + 9 | console.log(index, rest, input, length); + +regex-match-destructuring-fences.ts:9:20 - error SC2004: uses of 'rest' inherit the blocker on its declaration + + 8 | const { length } = m; + 9 | console.log(index, rest, input, length); + | ^~~~ + 10 | + + hint: the declaration of 'rest' did not compile — fix the diagnostic reported there and these sites clear with it \ No newline at end of file diff --git a/tests/harness/__snapshots__/regex.ts.txt b/tests/harness/__snapshots__/regex.ts.txt index 22bdbef90f..899633aed7 100644 --- a/tests/harness/__snapshots__/regex.ts.txt +++ b/tests/harness/__snapshots__/regex.ts.txt @@ -21,4 +21,4 @@ regex.ts:9:22 - error SC2020: 'RegExpExecArray.groups' is part of the standard l | ^~~~~~~~ 10 | } - hint: '.groups' lowers when the match's regex is STATICALLY known — a regex literal, or a const initialized with one (the group-name table is built at compile time) — and the read is not an optional-chain step: narrow the match instead (if (m) { m.groups } or m!.groups) \ No newline at end of file + hint: '.groups' lowers when the match's regex is STATICALLY known — a regex literal, or a const initialized with one (the group-name table is built at compile time) — and the match comes from exec, match, a for-of over matchAll, or the element parameter of an array-method callback over matchAll rows \ No newline at end of file diff --git a/tests/harness/__snapshots__/retired-engine-1587-any-field-unassigned.ts.txt b/tests/harness/__snapshots__/retired-engine-1587-any-field-unassigned.ts.txt index f5d79bffb6..fd5bb3ecc6 100644 --- a/tests/harness/__snapshots__/retired-engine-1587-any-field-unassigned.ts.txt +++ b/tests/harness/__snapshots__/retired-engine-1587-any-field-unassigned.ts.txt @@ -1,4 +1,4 @@ -retired-engine-1587-any-field-unassigned.ts:5:3 - error SC1090: 'unknown'-typed class fields are not supported yet +retired-engine-1587-any-field-unassigned.ts:5:3 - error SC1090: 'any'-typed class fields are not supported yet 4 | class Holder { 5 | x: any; diff --git a/tests/harness/__snapshots__/zlib-class-alias-escape.ts.txt b/tests/harness/__snapshots__/zlib-class-alias-escape.ts.txt new file mode 100644 index 0000000000..ea9d760895 --- /dev/null +++ b/tests/harness/__snapshots__/zlib-class-alias-escape.ts.txt @@ -0,0 +1,13 @@ +zlib-class-alias-escape.ts:7:18 - error SC1090: a zlib class alias as an escaping value (use 'Gz' with new, instanceof, typeof or .name) is not supported yet + + 6 | const Gz = zlib.Gzip; + 7 | const classes = [Gz]; + | ^~ + 8 | console.log(classes.length); + +zlib-class-alias-escape.ts:11:13 - error SC1090: 'instanceof zlib.Gzip' on an untyped or union value (give the operand a stream type first) is not supported yet + + 10 | const parsed: unknown = JSON.parse("{}"); + 11 | console.log(parsed instanceof Gz); + | ^~~~~~~~~~~~~~~~~~~~ + 12 | \ No newline at end of file diff --git a/tests/harness/closure-environment-scaling.test.ts b/tests/harness/closure-environment-scaling.test.ts new file mode 100644 index 0000000000..785f073492 --- /dev/null +++ b/tests/harness/closure-environment-scaling.test.ts @@ -0,0 +1,127 @@ +/* Compile time stays linear in the size of a closure environment. A factory + * whose methods share thousands of captured bindings (the shape of Bun's + * large-environment AOT program) used to take minutes: capture analyses + * walked the whole body once per binding, and LLVM received every box + * pointer as a value live across the entire function. Behavior of such a + * program is pinned by corpus 5973; this test bounds how compile time grows + * between two generated sizes. */ +import { execFile } from "node:child_process"; +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { promisify } from "node:util"; +import { expect, test } from "vitest"; + +const execFileAsync = promisify(execFile); +const repoRoot = join(import.meta.dirname, "../.."); +// Sanitized builds compile the runtime with each uncached build, which +// dwarfs both sizes; the growth this test bounds is the same in every lane. +const sanitize = process.env["SCRIPTC_SAN"] === "1"; + +/** `n` number bindings plus n/20 objects and strings, all captured by a + * reader and a writer method, and the output the program prints. */ +function program(n: number): { source: string; expected: string } { + const lines = [ + `function total(list: number[]): number {`, + ` let t = 0;`, + ` for (const v of list) t += v;`, + ` return t;`, + `}`, + `const one = Number("1");`, + `function make() {`, + ]; + const reads: string[] = []; + const writes: string[] = []; + let sum = 0; + let bumped = 0; + for (let i = 0; i < n; i++) { + lines.push(` let n${i} = ${i} + 0.5;`); + reads.push(`n${i}`); + writes.push(`n${i} += 1;`); + sum += i + 0.5; + bumped += i + 1.5; + } + for (let i = n; i < n + n / 10; i++) { + if (i % 2 === 0) { + lines.push(` let o${i} = { x: ${i} };`); + reads.push(`o${i}.x`); + writes.push(`o${i} = { x: o${i}.x + 1 };`); + sum += i; + bumped += i + 1; + } else { + lines.push(` let s${i} = "s" + (${i} + one);`); + reads.push(`s${i}.length`); + writes.push(`s${i} += "!";`); + const length = `s${i + 1}`.length; + sum += length; + bumped += length + 1; + } + } + lines.push( + ` return {`, + ` sum(): number {`, + ` return total([${reads.join(", ")}]);`, + ` },`, + ` bump(): void {`, + ...writes.map((write) => ` ${write}`), + ` },`, + ` };`, + `}`, + `const a = make();`, + `const b = make();`, + `a.bump();`, + `console.log(a.sum(), b.sum());`, + ); + return { source: lines.join("\n") + "\n", expected: `${bumped} ${sum}\n` }; +} + +/** Milliseconds from the end of the frontend load to the finished + * executable (SCRIPTC_TIMING phases): lowering, IR passes, LLVM emission, + * native code generation and the link, without process and tsgo startup. */ +async function compileTime(dir: string, n: number): Promise { + const { source, expected } = program(n); + const entry = join(dir, `env${n}.ts`); + const binary = join(dir, `env${n}`); + writeFileSync(entry, source); + const { stderr } = await execFileAsync( + process.execPath, + [join(repoRoot, "packages/cli/dist/main.js"), "build", entry, "-o", binary], + { + env: { ...process.env, SCRIPTC_TIMING: "1", SCRIPTC_NO_CACHE: "1" }, + maxBuffer: 16 * 1024 * 1024, + }, + ); + const phases = new Map(); + for (const line of stderr.split("\n")) { + if (!line.startsWith("scriptc timing ")) continue; + const { phase, total_ms } = JSON.parse(line.slice("scriptc timing ".length)) as { + phase: string; + total_ms: number; + }; + phases.set(phase, total_ms); + } + const loaded = phases.get("frontend-load"); + const complete = phases.get("complete"); + if (loaded === undefined || complete === undefined) throw new Error(stderr); + const run = await execFileAsync(binary, [], { encoding: "utf8" }); + expect(run.stdout).toBe(expected); + return complete - loaded; +} + +test.skipIf(sanitize)( + "compile time grows linearly with a shared closure environment", + async () => { + const dir = mkdtempSync(join(tmpdir(), "scriptc-closure-scaling-")); + try { + const small = await compileTime(dir, 500); + const large = await compileTime(dir, 4000); + // Eight times the bindings. Linear growth stays at or below 8x (fixed + // costs keep it near 5x); the quadratic analyses and code generation + // measured 24x at these sizes, and every doubling beyond adds 4x. + expect(large / small, `${small.toFixed(0)} ms -> ${large.toFixed(0)} ms`).toBeLessThan(16); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }, + 300_000, +); diff --git a/tests/harness/corpus-costs.json b/tests/harness/corpus-costs.json index 3db67e6de4..bf60ad889b 100644 --- a/tests/harness/corpus-costs.json +++ b/tests/harness/corpus-costs.json @@ -1,5397 +1,6033 @@ { "note": "Milliseconds per corpus program on a GitHub-hosted ubuntu-24.04 runner: its differential plus llvm-differential case, per flavor (medians of green main runs). scripts/ci-corpus-costs.mjs refreshes it from CI logs.", "plain": { - "001-hello.ts": 3069, - "002-log-args.ts": 2728, - "100-number-format.ts": 3733, - "1000-json-stringify-basics.ts": 4746, - "1001-json-escapes-unicode.ts": 3114, - "1002-json-parse-cast.ts": 4816, - "1003-json-parse-unions.ts": 3373, - "1004-json-parse-errors.ts": 2257, - "1005-json-nested.ts": 3430, - "1006-json-fs-config.ts": 3508, - "1007-json-rc-stress.ts": 4738, - "1008-json-null-arms.ts": 3237, - "1009-json-optional-fields.ts": 3598, - "101-arithmetic.ts": 2904, - "1010-json-stringify-space.ts": 4663, - "1011-json-unknown-typeof.ts": 2914, - "102-comparisons.ts": 2408, - "1020-async-basics.ts": 2514, - "1021-async-ordering.ts": 2150, - "1022-async-exceptions.ts": 2155, - "1023-async-rc-stress.ts": 3249, - "1024-async-pending-exit.ts": 3248, - "1025-async-promise-capture.ts": 4134, - "1026-throw-promise.ts": 2862, - "1027-async-return-promise.ts": 3195, - "1028-async-return-record-literals.ts": 2979, - "1029-async-eager-chains.ts": 3200, - "103-ternary.ts": 2586, - "104-ternary-empty-arrays.ts": 3925, - "1050-comptime-tables.ts": 3449, - "1051-comptime-strings.ts": 4033, - "1052-comptime-records.ts": 4539, - "1053-comptime-json.ts": 2953, - "1054-comptime-modules/main.ts": 2477, - "110-number-radix.ts": 2253, - "111-record-entries.ts": 4124, - "1110-math-members.ts": 3859, - "1111-math-random.ts": 2956, - "1113-string-methods.ts": 3292, - "1114-string-unicode-island.ts": 2830, - "1115-parse-globals.ts": 2990, - "1116-island-static-mix.ts": 3032, - "1117-typeof-static-union.ts": 2965, - "1118-object-spread-conditional.ts": 2667, - "1119-switch-union.ts": 3067, - "112-class-descriptor-key.js": 2522, - "1121-infinity-number-tostring.ts": 3370, - "1122-any-captures.ts": 3176, - "1124-union-narrowed-retag.ts": 2492, - "113-promise-checked-exits.ts": 2859, - "114-live-record-delete.ts": 2500, - "115-array-compound.js": 3135, - "116-text-decoder-runtime.js": 2862, - "117-weak-cache-cycle.js": 2266, - "118-fs-stored-callbacks.cjs": 3485, - "119-native-patterns.js": 3500, - "1200-regex-test-basics.ts": 3223, - "1201-regex-replace.ts": 3086, - "1202-regex-substitutions.ts": 3684, - "1203-regex-split.ts": 3320, - "1204-regex-empty-unicode.ts": 2968, - "1205-regex-rc-stress.ts": 2448, - "1300-errors-basics.ts": 2709, - "1301-errors-subclass.ts": 3447, - "1302-errors-typed-catch.ts": 3128, - "1303-errors-rc-stress.ts": 2776, - "1304-errors-uncaught.ts": 2907, - "1305-errors-async-rejections.ts": 2863, - "1306-errors-runtime-instances.ts": 2803, - "1350-path-normalize-join.ts": 2394, - "1351-path-parts.ts": 2228, - "1352-path-resolve-relative.ts": 3019, - "1353-os-basics.ts": 2682, - "1354-builtin-bare-specifiers.ts": 2284, - "1355-url-parse.ts": 2467, - "1356-url-file-bridge.ts": 3063, - "1357-fsp-roundtrip.ts": 3440, - "1358-crypto-random.ts": 2124, - "1359-json-module/main.ts": 2889, - "1360-spawn-sync.ts": 3158, - "1361-spawn-events.ts": 3000, - "1362-spawn-timers.ts": 8505, - "1363-spawn-unhandled-error.ts": 2724, - "1364-union-truthiness.ts": 2131, - "1365-union-logical.ts": 2948, - "1366-union-equality.ts": 2233, - "1367-destructuring.ts": 3492, - "1368-constructor-functions.ts": 2941, - "1369-promise-union.ts": 2048, - "1370-spread.ts": 2658, - "1371-union-template-tostring.ts": 2681, - "1372-loose-null-tests.ts": 2819, - "1373-union-array-arms.ts": 2414, - "140-bitwise-operators.ts": 3378, - "1400-typedarray-basics.ts": 3056, - "1401-typedarray-slice-set.ts": 2697, - "1402-buffer-encodings.ts": 3146, - "1403-buffer-fs.ts": 3327, - "1404-zlib-crypto-bytes.ts": 3318, - "1405-bytes-unions-arrays.ts": 2463, - "1406-dataview-mp4.ts": 2536, - "1407-dataview-bounds.ts": 3301, - "1408-string-indexing.ts": 2027, - "1409-typedarray-integer-loops.ts": 2940, - "1420-number-statics.ts": 2894, - "1421-number-parse-strings.ts": 2645, - "1422-date-iso.ts": 2686, - "1423-text-codec.ts": 4188, - "1424-fs-options.ts": 2662, - "1425-stdin-tty.ts": 2638, - "1426-stdin-read.ts": 2379, - "1427-fromcharcode-lastindexof.ts": 2109, - "1428-settled-await-order.ts": 2663, - "1429-promise-catch-finally.ts": 3486, - "1430-promise-race.ts": 4032, - "1431-caught-tostring.ts": 2082, - "1432-destructured-params.ts": 3251, - "1433-filter-narrow.ts": 3353, - "1434-async-never.ts": 2711, - "1435-math-spread.ts": 2665, - "1436-default-undef-param.ts": 2896, - "1437-pad-default.ts": 3003, - "1438-promise-all.ts": 6347, - "1440-interval-basics.ts": 3004, - "1441-interval-clear-cross.ts": 5524, - "1442-interval-throw.ts": 3148, - "1443-signal-handlers.ts": 5739, - "1444-exit-listeners.ts": 3270, - "1445-exit-process-exit.ts": 2515, - "1446-exit-uncaught.ts": 2709, - "1447-stdin-closed-events.ts": 2034, - "1448-stream-columns.ts": 2668, - "1450-incdec-expression.ts": 3189, - "1451-instanceof-uint8array-unknown.ts": 3003, - "1452-return-through-finally.ts": 2097, - "1453-pmap-settled.ts": 3863, - "1454-nonnull-narrow-and-fcc-bytes.ts": 2192, - "1455-lambda-union-return-adoption.ts": 3144, - "1460-console-error-warn.ts": 2229, - "1461-process-pid-getuid-kill.ts": 2853, - "1462-exec-sync.ts": 3145, - "1463-timeout-unref.ts": 2935, - "1464-env-writes.ts": 2594, - "1465-env-value.ts": 3233, - "1466-child-containers.ts": 2674, - "1467-string-match.ts": 2677, - "1468-exec-path.ts": 2974, - "1469-remove-listener.ts": 4038, - "1470-child-lifecycle.ts": 1948, - "1471-child-unref.ts": 6913, - "1472-errno-code.ts": 2768, - "1473-promisify-execfile.ts": 3155, - "1474-string-decoder.ts": 3057, - "1475-readline-closed-stdin.ts": 2826, - "1476-assign-expression.ts": 2557, - "1477-in-expressions.ts": 2740, - "1478-promise-reject.ts": 3143, - "1479-destructuring-assign.ts": 2933, - "1480-os-network-interfaces.ts": 3749, - "1481-string-case-static.ts": 1949, - "1482-spawnsync-error.ts": 2332, - "1483-array-slice.ts": 3329, - "1520-fs-wider-surface.ts": 3206, - "1520-string-split-static.ts": 2787, - "1521-fs-lstat-lock.ts": 2868, - "1521-string-trim-pad-static.ts": 2727, - "1522-parseint-static.ts": 2059, - "1522-spawnsync-options.ts": 3990, - "1523-isnan-floor-static.ts": 2552, - "1523-spawn-options.ts": 2955, - "1524-atomics-sleep-static.ts": 3221, - "1524-catch-narrowing.ts": 2221, - "1525-child-exit-signal.ts": 2022, - "1525-unknown-typeof-validation.ts": 2311, - "1526-set-spread.ts": 3277, - "1527-logical-mixed-operands.ts": 2920, - "1528-delete-records-env.ts": 3318, - "1529-class-with-statics.ts": 2626, - "1530-array-push-variadic.ts": 2272, - "1530-spread-override-completion.ts": 2333, - "1531-delete-optional-fields.ts": 2822, - "1531-process-arch-versions.ts": 2409, - "1532-array-splice-shift.ts": 2440, - "1532-union-shared-field-read.ts": 2449, - "1533-bool-equality.ts": 3130, - "1533-path-platform-namespaces.ts": 2472, - "1534-crypto-hash-chain.ts": 3303, - "1534-union-as-arm-cast.ts": 2773, - "1535-spawn-fd-stdio.ts": 3617, - "1535-union-param-defaults.ts": 2848, - "1536-destructuring-defaults.ts": 2783, - "1536-string-array-sweep.ts": 2930, - "1537-chalk-hybrid/main.ts": 3161, - "1537-os-release-spawnsync-stdio.ts": 2411, - "1538-await-promiselike-return.ts": 2433, - "1538-math-static-scalar.ts": 2674, - "1539-readonly-set-map.ts": 2573, - "1539-unknown-truthiness.ts": 2629, - "1540-os-userinfo.ts": 2636, - "1540-void-statement.ts": 2658, - "1541-fs-readdir-dirent.ts": 3093, - "1541-union-keyed-reads.ts": 4144, - "1542-map-promise-values.ts": 2657, - "1542-record-literal-into-union.ts": 3004, - "1543-rest-destructuring.ts": 2897, - "1543-set-server-handles.ts": 2927, - "1544-dyn-json-reads.ts": 3602, - "1544-string-matchall.ts": 2940, - "1545-spread-order-and-optional.ts": 2963, - "1546-union-element-reads.ts": 2716, - "1547-computed-key-fold.ts": 4334, - "1548-boolean-condition-forms.ts": 4675, - "1549-array-isarray-unions.ts": 3420, - "1550-param-defaults-func-set.ts": 3314, - "1551-dyn-receiver-methods.ts": 4271, - "1552-exec-options-record.ts": 3353, - "1553-truthy-hof-predicates.ts": 2475, - "1554-caught-into-unknown.ts": 2996, - "1555-index-spread-into-declared.ts": 3100, - "1556-union-retag-width-arms.ts": 2198, - "1557-url-host.ts": 2745, - "1558-any-joins-and-dyn-validation.ts": 2600, - "1559-conditional-spread-index-merge.ts": 3037, - "1560-forof-homogeneous-tuples.ts": 2478, - "1561-forof-strings.ts": 2503, - "1561-promise-then.ts": 3156, - "1562-optional-chain-tails.ts": 3390, - "1562-spawn-conditional-spread.ts": 3006, - "1563-forof-iterator-projections.ts": 3054, - "1563-string-raw-fold.ts": 2666, - "1564-fs-watch.ts": 3415, - "1564-string-raw.ts": 2651, - "1565-spawn-pipe-streams.ts": 3154, - "1565-union-element-join.ts": 2733, - "1566-child-duck-interface.ts": 2463, - "1567-server-close-override.ts": 2863, - "1568-x509-fingerprint.ts": 2419, - "1569-fsp-mkdir-unlink-chmod.ts": 2704, - "1570-child-unref-kill-reffed.ts": 8818, - "1571-optional-call-tostring-tail.ts": 4280, - "1571-stdin-set-raw-mode-non-tty.ts": 2435, - "1572-promise-reject-all-tuple.ts": 3046, - "1572-x509-validity-date-gettime.ts": 2933, - "1573-promisify-execfile-env-spread.ts": 3140, - "1573-tdz-scalar-forward-capture.ts": 2068, - "1574-dyn-optional-method-number-keys.ts": 3318, - "1574-promise-all-tuple-literal.ts": 3025, - "1575-unknown-assert-into-record.ts": 3248, - "1576-width-coercions.ts": 2888, - "1577-url-hostname.ts": 2352, - "1578-exec-input-optional.ts": 3178, - "1579-matchall-index.ts": 3025, - "1580-exec-env-conditional-spread.ts": 3275, - "1581-declare-const-read.ts": 2341, - "1582-timer-sleep-resolve.ts": 2315, - "1583-loops-and-stream-captures.ts": 3154, - "1584-union-field-unassigned.ts": 2858, - "1585-union-field-conditional-ctor.ts": 2073, - "1586-derived-field-before-super-init.ts": 2823, - "1590-js-unannotated.js": 2965, - "1591-js-closures.js": 2113, - "1592-js-classes.js": 4260, - "1593-js-jsdoc-records.js": 3366, - "1594-js-async.js": 2772, - "1595-js-island-gap/main.js": 2168, - "1596-cjs-modules/main.js": 2751, - "1597-mjs-graph/main.mjs": 2858, - "1598-cjs-builtin-require/main.js": 2109, - "1599-js-uncaught-throw.js": 1870, - "1600-assert-passing.ts": 3321, - "1601-assert-fail-exit.ts": 2842, - "1602-assert-caught-error.ts": 2041, - "1603-assert-scalar-messages.ts": 2650, - "1604-assert-deep-structures.ts": 3035, - "1605-assert-import-forms.ts": 3128, - "1606-assert-strict-module.ts": 2927, - "1607-assert-throws-match.ts": 2731, - "1608-cjs-assert/main.js": 3193, - "1609-assert-async.ts": 1990, - "1610-path-win32-full.ts": 1939, - "1611-url-file-bridge-neutral.ts": 2575, - "1612-cjs-module-globals.cjs": 2846, - "1613-cjs-esm-proxy-named/main.mjs": 2282, - "1614-js-fence-dodges.cjs": 2910, - "1615-cjs-export-shapes/main.cjs": 3349, - "1616-cjs-esm-lexer-invisible/main.mjs": 2499, - "1617-cjs-esm-lexer-visible/main.mjs": 2355, - "1618-cjs-esm-poisoned-tail/main.mjs": 2404, - "1619-cjs-esm-reexport-invisible/main.mjs": 2675, - "1620-cjs-require-effects-order/main.js": 2586, - "1621-cjs-require-cache-hit/main.js": 2101, - "1622-cjs-require-conditional/main.js": 2194, - "1623-cjs-require-diamond/main.js": 2241, - "1624-cjs-require-dir-mid-file/main.js": 2559, - "1625-cjs-require-lazy-fn/main.js": 2333, - "1626-cjs-require-mjs/main.js": 2430, - "1627-mjs-import-cjs-requires/main.mjs": 2803, - "1628-checked-dynamic-builtin-args.cjs": 2085, - "1629-require-main-filename.cjs": 2419, - "1630-inspect-scalars.ts": 2701, - "1630-rmsync-retry-options.cjs": 2427, - "1631-inspect-arrays.ts": 3949, - "1632-inspect-records.ts": 3013, - "1633-inspect-map-set.ts": 2316, - "1634-inspect-classes.ts": 3062, - "1635-inspect-buffer.ts": 2271, - "1636-util-format.ts": 2132, - "1637-inspect-dyn.ts": 3164, - "1638-inspect-cjs.cjs": 2023, - "1639-versions-openssl-probe.ts": 2845, - "1640-fd-read-decode.ts": 3219, - "1641-string-search.ts": 3172, - "1642-object-fromentries-rows.ts": 2713, - "1643-metadata-dyn-return.cjs": 3460, - "1644-ee-basics.ts": 2495, - "1645-ee-extends.ts": 2603, - "1646-ee-once-remove.ts": 2849, - "1647-ee-meta-events.ts": 2040, - "1648-ee-error-event.ts": 2703, - "1649-ee-names-counts.ts": 2487, - "1650-ee-prepend.ts": 2672, - "1651-ee-max-listeners.ts": 2331, - "1652-ee-snapshot.ts": 2031, - "1653-ee-cjs.cjs": 2472, - "1654-ee-namespace.ts": 2390, - "1655-spawnsync-neutral.ts": 4056, - "1656-execsync-neutral.ts": 2824, - "1657-spawn-async-neutral.ts": 3268, - "1660-buffer-read-write-num.ts": 3727, - "1661-buffer-encodings-full.ts": 2670, - "1662-string-decoder-encodings.ts": 2362, - "1663-buffer-compare-search-fill.ts": 3956, - "1664-dyn-fn-boundary.cjs": 2792, - "1665-dyn-fn-mustcall/main.cjs": 2827, - "1666-dyn-fn-identity.ts": 2623, - "1667-dyn-fn-not-callable.cjs": 2618, - "1668-dyn-fn-throws.cjs": 2216, - "1669-symbol-identity.ts": 2335, - "1670-symbol-registry.ts": 1852, - "1671-symbol-typeof.ts": 2343, - "1672-symbol-containers.ts": 2800, - "1673-symbol-cjs.cjs": 2407, - "1674-symbol-inspect.ts": 1818, - "1676-func-array-surface.ts": 2784, - "1677-emitter-listeners.ts": 2400, - "1678-emitter-dyn-listeners.cjs": 2358, - "1679-console-dyn.js": 2880, - "1680-assert-bytes.ts": 3465, - "1681-assert-funcs.ts": 2454, - "1685-stream-readable-basics.ts": 2506, - "1686-stream-readable-paused.ts": 2711, - "1687-stream-readable-flow-control.ts": 2359, - "1688-stream-writable-basics.ts": 2613, - "1689-stream-writable-async-drain.ts": 2671, - "1690-stream-duplex.ts": 2112, - "1691-stream-transform.ts": 1780, - "1692-stream-pipe.ts": 3345, - "1693-stream-pipe-backpressure.ts": 2344, - "1694-stream-destroy.ts": 1870, - "1695-stream-props.ts": 2766, - "1696-stream-read-inside-read.ts": 3023, - "1697-stream-tick-vs-timer.ts": 2275, - "1698-stream-uncaught-error.ts": 2523, - "1699-stream-callback-shapes.ts": 1972, - "1700-mustcall-common/main.cjs": 3401, - "1701-mustcall-exit-report/main.cjs": 3316, - "1702-dyn-proto-dispatch.cjs": 2744, - "1703-arguments-rest-props.cjs": 2431, - "1710-cjs-export-class/main.js": 2997, - "1711-cjs-export-single-values/main.js": 2866, - "1712-field-incdec.ts": 1828, - "1713-js-dyn-fields.js": 2492, - "1720-assert-throws-shape.ts": 2372, - "1721-assert-throws-regex-class.ts": 2125, - "1722-assert-rejects.ts": 2761, - "1723-assert-does-not-reject.ts": 2536, - "1724-assert-iferror.ts": 2883, - "1725-assert-symbols.ts": 2626, - "1726-promise-with-resolvers.ts": 2320, - "1727-assert-throws-shape-exit.ts": 2580, - "1730-countdown-symbol-keys/main.js": 2657, - "1731-symbol-field-shapes.cjs": 3005, - "1732-symbol-field-cross-module/main.js": 2509, - "1740-stream-extends-readable.ts": 2167, - "1741-stream-extends-writable.ts": 2479, - "1742-stream-extends-duplex-transform.ts": 2640, - "1743-stream-extends-vt-dispatch.ts": 2388, - "1744-stream-set-encoding.ts": 2886, - "1745-stream-encoding-js.cjs": 2861, - "1746-stream-for-await.ts": 2864, - "1747-stream-for-await-js.cjs": 2685, - "1750-http-agent-options.ts": 2435, - "1751-options-record-stance.ts": 3010, - "1760-surplus-args-drop.cjs": 2845, - "1761-emitter-special-event-names.cjs": 2672, - "1770-assert-dyn-strict.ts": 2841, - "1771-assert-dyn-deep.ts": 2098, - "1772-assert-dyn-js.cjs": 2493, - "1773-assert-dyn-exit.ts": 2809, - "1780-http-res-surface.ts": 3019, - "1781-http-server-shapes.cjs": 2697, - "1790-computed-key-folds.ts": 2567, - "1791-searchparams-core.ts": 2632, - "1792-searchparams-encoding.ts": 2548, - "1793-searchparams-iteration.ts": 2977, - "1794-searchparams-url-live.ts": 2027, - "1795-dyn-handle-request.cjs": 2079, - "1796-dyn-handle-body.cjs": 3120, - "1797-dyn-handle-socket.cjs": 2724, - "1798-dyn-handle-typed-box.ts": 2104, - "1800-immediate-basics.ts": 2313, - "1801-immediate-handle.ts": 2272, - "1802-immediate-unref-exit.ts": 2368, - "1803-timeout-refresh.ts": 2346, - "1804-timers-module.ts": 2379, - "1805-timer-callback-args.ts": 2475, - "1806-timer-delay-trunc.ts": 2847, - "1810-stream-options-const-record.ts": 2576, - "1811-stream-option-value-callbacks.cjs": 2945, - "1812-stream-super-options-forwarding.cjs": 3075, - "1813-stream-finished.ts": 2644, - "1814-stream-pipeline.cjs": 2915, - "1815-stream-state-reads.cjs": 2499, - "1820-static-block-basics.ts": 2475, - "1821-static-block-order.ts": 2605, - "1822-static-block-js.js": 1831, - "1823-static-instance-method-names.ts": 2522, - "1824-for-of-destructuring-defaults.ts": 2238, - "1825-exhaustive-typeof-switch.ts": 1814, - "1826-settimeout-arguments-callback.js": 2356, - "1830-enum-numeric-basics.ts": 2431, - "1831-enum-string-const-reverse.ts": 2780, - "1832-enum-modules/main.ts": 2343, - "1835-var-basics.ts": 2924, - "1836-var-loop-capture.ts": 2857, - "1837-var-undefined-hoisting.ts": 2785, - "1838-var-modules/main.ts": 2572, - "1839-var-js/main.cjs": 2766, - "1840-jsdoc-import-type-only/main.js": 2328, - "1850-overload-basics.ts": 2354, - "1851-overload-return-narrowing.ts": 2268, - "1852-overload-class-members.ts": 2874, - "1853-overload-modules/main.ts": 2339, - "1854-ambient-declare-fn.ts": 2384, - "1855-ambient-declare-fn-uncaught.ts": 2441, - "1870-unit-type-values.ts": 2730, - "1871-empty-array-never.ts": 2177, - "1872-template-tostring-composite.ts": 2526, - "1880-json-declared-field-order.ts": 2276, - "1881-default-anon/main.ts": 2233, - "1882-default-cjs-interop/main.ts": 2407, - "1883-default-exports/main.ts": 2358, - "1890-ns-imports/main.ts": 2428, - "1891-ns-reexport/main.ts": 2422, - "1940-class-values-basics.ts": 2483, - "1941-class-values-registry.ts": 2632, - "1942-class-expressions.ts": 2765, - "1943-class-statics-expanded.ts": 2459, - "1944-class-values-modules/main.ts": 2440, - "1950-generic-fn-values.ts": 2561, - "1951-generic-classes-basics.ts": 2197, - "1952-generic-class-statics-values.ts": 2662, - "1953-generic-class-hierarchy.ts": 3001, - "1954-generic-modules/main.ts": 2756, - "1960-namespace-basics.ts": 2647, - "1961-namespace-nested.ts": 2257, - "1962-namespace-classes.ts": 2660, - "1963-namespace-aliases.ts": 2070, - "1964-namespace-type-only.ts": 2445, - "1965-namespace-ambient.ts": 2332, - "1966-namespace-modules/main.ts": 2434, - "1967-namespace-alias-typeonly.ts": 2455, - "1968-namespace-import-eq-snapshot.ts": 2251, - "1970-decorators-basics.ts": 2885, - "1971-decorators-rebinding.ts": 2022, - "1972-decorators-throwing.ts": 2312, - "1973-decorators-ambient.ts": 2390, - "1980-primitive-proto-statics.ts": 3122, - "1981-date-utc.ts": 3440, - "1982-freeze-resolve-passthrough.ts": 2302, - "1983-array-tuple-surfaces.ts": 3057, - "1990-labels-basics.ts": 2548, - "1991-for-in-loops.ts": 2966, - "1992-small-syntax.ts": 1927, - "200-strings.ts": 2314, - "2000-generic-methods-basic.ts": 2743, - "2001-generic-methods-generic-class.ts": 2632, - "2002-generic-methods-inheritance.ts": 3264, - "2003-generic-methods-object-literal.ts": 2644, - "2004-generic-methods-statics.ts": 2578, - "201-templates.ts": 2516, - "2010-generators-basics.ts": 1908, - "2011-generators-forof.ts": 2574, - "2012-generators-return-throw.ts": 2779, - "2013-generators-sent-values.ts": 2065, - "2014-generators-values.ts": 2940, - "2015-generators-yieldstar.ts": 1953, - "2016-generators-rc-stress.ts": 2595, - "2017-generators-async.ts": 2715, - "2018-generators-uncaught.ts": 2356, - "2019-generators-loops.ts": 2496, - "2020-generic-value-bindings.ts": 2351, - "2021-generic-value-binding-modules/main.ts": 2687, - "2022-width-index-capture.ts": 2751, - "2023-width-readonly.ts": 2800, - "2024-width-array-elems.ts": 2726, - "2025-width-union-compose.ts": 2851, - "2026-width-spread.ts": 2485, - "2030-width-nested.ts": 2774, - "2031-width-tuples.ts": 2693, - "2032-cjs-export-assigned-class-expression/main.cjs": 2477, - "2033-property-assigned-class-extends.cjs": 2618, - "2035-ternary-array-retag.cjs": 2404, - "2036-evolving-array-decl.cjs": 2542, - "2037-fn-decl-hoisting.cjs": 2645, - "2038-evolving-array-js.cjs": 3054, - "2040-any-bindings.ts": 2984, - "2040-mixin-heritage.ts": 2131, - "2041-any-params-returns.ts": 2687, - "2041-mixin-values.ts": 2744, - "2042-any-flow-loops.ts": 2769, - "2042-mixin-modules/main.ts": 1924, - "2043-any-dom-values.ts": 2778, - "2043-mixin-rc-stress.ts": 2775, - "2044-vtable-sibling-slots.ts": 2571, - "2045-objlit-accessors-basic.ts": 2640, - "2045-parameter-properties.ts": 2113, - "2046-abstract-classes.ts": 3000, - "2046-objlit-accessors-effects.ts": 2795, - "2047-objlit-accessors-shapes.ts": 2983, - "2047-optional-class-fields.ts": 2949, - "2050-dynamic-import-own-module/main.ts": 2608, - "2051-dynamic-import-then/main.ts": 2467, - "2052-dynamic-import-self.ts": 2449, - "2053-typeof-static-fold.ts": 2181, - "2054-destructuring-island-source.ts": 2707, - "2060-empty-tuple.ts": 2373, - "2070-computed-key-folds.ts": 2645, - "2071-integer-key-enumeration-order.ts": 3006, - "2072-expando-function-members.ts": 2512, - "2073-destructuring-assignment.ts": 2535, - "2074-island-destructuring.ts": 2609, - "2075-index-signature-runtime-keys.ts": 1981, - "2080-destructuring-defaults.ts": 2417, - "2081-destructuring-rest.ts": 2774, - "2082-destructuring-assignment.ts": 2489, - "2085-destructuring-heads-rest-params.ts": 2668, - "2087-destructuring-for-heads.ts": 2521, - "2090-index-signature-number-keys.ts": 1958, - "2091-index-signature-string-intersection.ts": 2567, - "2092-package-imports/main.ts": 2340, - "2093-timers-promises.ts": 2743, - "2094-diagnostics-channel.ts": 1983, - "2095-js-catch-unknown.js": 2499, - "2096-os-type.ts": 2709, - "2097-js-evolving-globals.js": 2560, - "210-string-methods.ts": 2593, - "2100-stream-default-hwm.ts": 2961, - "2101-dyn-param-defaults.ts": 2737, - "2102-empty-pattern-decls.ts": 2787, - "2105-computed-key-static-folds.ts": 2507, - "2106-comma-expressions.ts": 2500, - "211-string-unicode.ts": 1823, - "2110-top-object-types.ts": 1971, - "2111-json-stringify-unknown.ts": 2830, - "2112-array-at-findlast.ts": 2001, - "2113-string-index-methods.js": 2989, - "212-string-aliasing-append.ts": 1909, - "2120-package-self-import/main.ts": 1825, - "2121-esm-cycle-inert-backedge/main.ts": 1748, - "2123-ambient-import-crash/main.ts": 1870, - "2124-imports-field-wildcard/main.ts": 2082, - "2140-uri-component-codecs.ts": 1955, - "2160-dc-tracing-channel.cjs": 2212, - "2161-js-object-literal-identity.cjs": 2587, - "2162-js-dyn-destructure.cjs": 2628, - "2163-js-selfref-const.cjs": 2533, - "2164-js-then-dyn-handler.cjs": 2503, - "2165-js-throw-dyn.cjs": 2509, - "2166-cjs-getter-this/main.cjs": 2420, - "2170-island-array-exits.ts": 3096, - "2171-island-json-stringify.ts": 2652, - "2172-island-regexp-boundary.ts": 2763, - "2190-new-object-and-string-wrappers.ts": 1860, - "2191-uri-encoders.ts": 2586, - "2192-object-default-tostring.ts": 2607, - "2193-discarded-stdlib-reads.ts": 2445, - "2194-ambient-undef-chains.ts": 2022, - "2195-require-missing-package/main.mjs": 2412, - "2196-island-computed-key-chain.ts": 2831, - "2210-dyn-promise-crossing.cjs": 1918, - "2211-dc-trace-promise.cjs": 2793, - "2212-unhandled-rejection-listener.cjs": 2511, - "2213-async-local-storage.cjs": 2825, - "2214-dc-bind-store.cjs": 2651, - "2215-process-emit-warning-exit.cjs": 2666, - "2240-width-class-into-record.ts": 2573, - "2241-width-record-into-class.ts": 2686, - "2242-width-statics-into-record.ts": 2654, - "2243-width-assert-satisfies-shapes.ts": 2411, - "2250-tagged-templates.ts": 2617, - "2252-class-iterators.ts": 3448, - "2253-union-coercions.ts": 2899, - "2260-http2-constants.cjs": 2471, - "2280-builtin-namespace-destructure.cjs": 2645, - "2281-domexception-atob-btoa.cjs": 2853, - "2282-queue-microtask.cjs": 2526, - "2283-structured-clone.cjs": 3050, - "2284-regexp-constructor.cjs": 1945, - "2285-iferror-dyn.cjs": 2665, - "2286-dyn-object-walks.cjs": 2662, - "2300-dyn-record-spread-and-keyed-access.cjs": 2261, - "2301-cjs-export-table-dom-attach.cjs": 2384, - "2310-process-next-tick.ts": 2843, - "2311-next-tick-js-callbacks.js": 2254, - "2312-stream-underscore-assign.ts": 2790, - "2313-stream-underscore-assign-js.cjs": 2842, - "2314-process-introspection.ts": 2971, - "2320-await-unit.ts": 2748, - "2321-emitter-static-setmax.cjs": 2272, - "2322-dyn-array-sort.cjs": 1886, - "2323-timer-callback-returns.ts": 2523, - "2330-tls-server-runtime-options.cjs": 2677, - "2331-tls-server-options-typed.ts": 2790, - "2350-nan-global.ts": 2734, - "2351-async-methods.ts": 2622, - "2352-void-coercions.ts": 1788, - "2353-decorators-member-ambient.ts": 2547, - "2354-decorators-expression-ambient.ts": 2465, - "2360-tuple-to-array.ts": 2471, - "2361-assign-index-record.ts": 3021, - "2362-execsync-inherit.ts": 2693, - "2363-nullish-retag.ts": 2801, - "2364-in-runtime-key.ts": 2572, - "2365-dyn-into-island.ts": 1996, - "2366-string-well-formed.ts": 2147, - "2367-regexp-escape.ts": 2470, - "2368-set-methods.ts": 2846, - "2369-promise-try.ts": 2170, - "2370-group-by.ts": 2298, - "2371-iterator-helpers.ts": 2730, - "2380-buffer-module-named-import.ts": 2400, - "2381-builtin-default-imports.js": 2365, - "2382-cycle-two-decl/main.ts": 1682, - "2383-cycle-assertdoc/main.js": 2760, - "2384-cycle-mixed-bindings/main.ts": 1815, - "2385-builtin-reexport-facade/main.ts": 1834, - "2386-string-to-chars.ts": 2748, - "2387-tonumber-argv.ts": 2345, - "2388-tonumber-grammar-zoo.ts": 2619, - "2389-tonumber-unary-plus.ts": 2473, - "2390-dot-requires/main.cjs": 2365, - "2391-default-builtin-imports/main.ts": 3036, - "2392-block-guard-alias-scope.ts": 2477, - "2393-loop-exit-guard-narrow.ts": 2791, - "2394-loop-narrow-containment.ts": 2749, - "2395-while-cond-guard.ts": 2574, - "2396-born-narrowed-decl.ts": 2435, - "2397-branch-kill-merge.ts": 2334, - "2398-switch-arm-kill.ts": 2438, - "2399-reassign-kill-polarity.ts": 2238, - "240-stdout-write.ts": 2820, - "2400-kill-return-edge.ts": 2315, - "2401-kill-break-staging.ts": 2347, - "2402-labeled-break-kill.ts": 2610, - "2403-kill-continue-backedge.ts": 2391, - "2404-kill-throw-catch.ts": 2611, - "2405-sibling-arm-narrow.ts": 2300, - "2406-switch-clause-sibling-narrow.ts": 1885, - "2407-infinite-loop-seal.ts": 2596, - "2408-loop-terminality-reopen.ts": 2452, - "2409-loop-body-return-not-terminal.ts": 1860, - "2410-stacked-cases-terminality.ts": 2588, - "2411-exhaustive-switch-terminality.ts": 2704, - "2412-dowhile-guard-trailing-test.ts": 2401, - "2413-dowhile-kill-renarrow.ts": 2389, - "2414-dowhile-continue-kill.ts": 2546, - "2415-dowhile-break-trailing-test.ts": 2461, - "2416-spread-ternary-positions.ts": 2790, - "2417-ternary-local-inference.ts": 2737, - "2418-wrapped-ternary-arms.ts": 2485, - "2419-selector-ternary-union.ts": 2554, - "2420-nullish-null-identity.ts": 2418, - "2421-float-lane-inference.ts": 2337, - "2422-ieee-div-rem-corners.ts": 2498, - "2423-param-null-reassign.ts": 2447, - "2424-ternary-miss-first-narrowing.ts": 2373, - "2425-elseif-head-exit-narrow.ts": 2194, - "2426-default-snapshot-mutable/main.ts": 2194, - "2427-perf-hooks-now.ts": 2489, - "2428-field-redeclare-inherited.ts": 2204, - "2429-destructure-class-instance.ts": 2362, - "2430-empty-literal-unknown-field.js": 2633, - "2431-primitive-ctor-values.ts": 2702, - "2432-generic-member-fields-async.ts": 2679, - "2433-as-const-command-tables.ts": 2271, - "2434-deferred-init-fields.ts": 2673, - "2435-loose-same-kind-equality.ts": 2441, - "2440-console-inspect-args.ts": 3150, - "2441-console-process-argv.ts": 2027, - "2442-union-literal-arm-widening.ts": 2300, - "2443-union-literal-shadow-narrowing.ts": 2799, - "2444-union-literal-reducer-spread.ts": 2545, - "2445-math-minmax-nary.ts": 2355, - "2446-performance-global.ts": 2453, - "2447-island-boundary-slots/main.ts": 3241, - "2448-entries-null-arms-regex-arrays.ts": 3215, - "2450-private-instance-methods.ts": 2581, - "2451-private-fields.ts": 2918, - "2452-private-statics.ts": 2431, - "2453-private-accessors.ts": 2589, - "2454-private-generator-methods.ts": 2809, - "2455-private-brand-checks.ts": 2369, - "2456-private-async-methods.ts": 2625, - "2457-private-members.js": 2014, - "2460-qs-parse-grammar.ts": 2706, - "2461-qs-parse-options.ts": 2139, - "2462-qs-stringify.ts": 2756, - "2463-qs-escape-unescape.ts": 2677, - "2464-qs-require-forms.cjs": 2768, - "2470-mockable-module-shape.js": 3152, - "2471-record-keyed-write-hasown.js": 2699, - "2472-string-regexp-union.ts": 2532, - "2473-option-table-widths.js": 2095, - "2474-island-literal-forms.ts": 2881, - "2475-variadic-wrapper-island.js": 2240, - "2476-map-regex-values.ts": 2431, - "2480-recursive-record-tree.ts": 2075, - "2481-mutual-recursive-records.ts": 2525, - "2482-recursive-union-tree.ts": 2633, - "2483-recursive-record-cycles.ts": 2479, - "2484-json-stringify-circular.ts": 2869, - "2485-inspect-circular-refs.ts": 2957, - "2486-recursive-record-boundaries.ts": 2857, - "2487-recursive-deep-equal.ts": 2516, - "2488-ast-walker.ts": 2925, - "2490-find-miss-null-compare.ts": 2051, - "2491-strict-unit-compare-no-arm.ts": 2381, - "2492-loose-null-compare-unions.ts": 2451, - "2493-switch-unit-cases.ts": 2503, - "2494-no-arm-compare-effects.ts": 2501, - "2500-net-autosel-timeout.ts": 2202, - "2510-dyn-evolving-array-map.ts": 2611, - "2511-dyn-evolving-array-filter-foreach.ts": 2729, - "2512-dyn-evolving-array-mixed-push.ts": 2656, - "2520-private-statics-class-name-calls.ts": 2568, - "2521-private-statics-class-expression.ts": 2429, - "2522-private-statics-decorated.ts": 2492, - "2523-private-statics-aliased-class.ts": 2618, - "2530-object-destructuring-decl.ts": 2513, - "2531-array-tuple-destructuring-decl.ts": 2504, - "2532-param-destructuring-options.ts": 2714, - "2533-forof-destructuring.ts": 2583, - "2534-object-rest-decl.ts": 2277, - "2535-destructuring-assign-basics.ts": 2758, - "2536-destructuring-assign-nested.ts": 2761, - "2537-destructuring-assign-member-targets.ts": 2552, - "2538-destructuring-assign-class-source.ts": 2658, - "2539-class-instance-rest.ts": 2852, - "2540-accessor-destructuring-defaults.ts": 2732, - "2541-destructuring-eval-order.ts": 2121, - "2550-generics-keyof-pick.ts": 2790, - "2551-generics-value-aliases.ts": 2489, - "2552-generics-iface-methods.ts": 2118, - "2553-generics-signature-bindings.ts": 2838, - "2554-generics-frontier-mix.ts": 2857, - "2555-generics-keyof-writes.ts": 2600, - "2556-crypto-introspection.ts": 2362, - "2556-width-hybrid-shapes.ts": 3063, - "2557-tls-ca-store.ts": 2877, - "2557-width-field-lifts.ts": 2899, - "2558-index-signature-func-values.ts": 2980, - "2558-rejection-events.cjs": 2741, - "2559-index-signature-container-values.ts": 2921, - "2560-arraybuffer-dataview-set.ts": 2985, - "2561-object-is.ts": 3314, - "2562-intl-numberformat-en-us.ts": 1713, - "2563-stream-promises-pipeline.ts": 2673, - "2564-stream-promises-finished.ts": 2596, - "2565-stream-promises-js.cjs": 2121, - "2566-promise-reject-dyn-reason.js": 2527, - "2567-rest-spread-forward.js": 3263, - "2568-rest-spread-forward-dynamic.js": 2990, - "2569-upcast-identity.ts": 1878, - "2570-buffer-arg-validation.cjs": 4190, - "2571-searchparams-arg-validation.cjs": 2897, - "2572-readable-emitted-readable-flag.cjs": 2583, - "2573-fs-url-arg-ladders.cjs": 2789, - "2574-emitter-max-listeners-ladders.cjs": 2728, - "2575-string-destructuring-decl.ts": 2831, - "2576-string-destructuring-assign.ts": 2718, - "2577-builtin-global-destructuring.js": 2482, - "2578-jsval-into-unknown-rows.ts": 2821, - "2579-jsval-object-param-crossing.js": 2723, - "2580-jsval-routed-keyed-ops.js": 2125, - "2581-jsval-routed-calls.js": 3090, - "2582-jsval-object-statics.js": 2972, - "2582-jsval-routed-keyed-ops.js": 2851, - "2583-dyn-nullish-coalesce.js": 2185, - "2583-jsval-routed-calls.js": 2972, - "2584-jsval-object-statics.js": 2832, - "2584-union-dyn-collapse.ts": 2220, - "2585-dyn-nullish-coalesce.js": 2398, - "2585-unknown-array.ts": 2751, - "2586-object-assign-spread.js": 3004, - "2587-array-entries-forof.ts": 2555, - "2588-array-entries-chains.ts": 3001, - "2589-object-create-null.cjs": 2994, - "2590-object-create-dynamic.js": 3188, - "2591-ambient-generic-traps.ts": 2461, - "2592-ambient-trap-uncaught.ts": 2508, - "2593-generic-inert-bindings.ts": 2003, - "2594-nullish-generic-bindings.ts": 2473, - "2595-fs-arg-ladders.cjs": 3590, - "2596-net-arg-ladders.cjs": 2708, - "2597-dgram-send-ladders.cjs": 3185, - "2598-tls-arg-ladders.cjs": 3457, - "2599-stream-arg-ladders.cjs": 2639, - "2600-dyn-keyed-write-harness.js": 2645, - "2601-dyn-keyed-write-ops.js": 2850, - "2602-dyn-array-destructure.js": 3140, - "2603-island-keyed-write-dyn.js": 2307, - "2604-cycle-classes-mutual/main.ts": 2453, - "2605-cycle-three-module/main.ts": 2707, - "2606-dynamic-import-cycle/main.ts": 2783, - "2608-regex-named-groups.ts": 3177, - "2609-regex-named-replace.ts": 2707, - "2610-regex-named-matchall.ts": 2590, - "2611-regex-named-groups-js.cjs": 2302, - "2613-for-init-uninitialized-let.ts": 2413, - "2614-trap-binding-later-writes.ts": 2362, - "2615-nullish-field-record-binding.ts": 2480, - "2616-js-collect-probe-defers.js": 2756, - "2617-enum-static-field-import/main.ts": 2551, - "2618-ee-emit-override.ts": 2613, - "2619-ee-override-chain.ts": 2034, - "2620-ee-override-once-order.ts": 2930, - "2621-ee-override-error-throw.ts": 2533, - "2622-ee-override-filter.ts": 2677, - "2623-ee-job-queue.ts": 2500, - "2624-ee-override-js.cjs": 2183, - "2625-bytes-views.ts": 2152, - "2626-stream-event-names.ts": 2499, - "2627-stream-ticks-are-nextticks.ts": 2708, - "2628-stream-push-encodings.cjs": 2440, - "2629-stream-consumers.ts": 2913, - "2630-stream-consumers-errors.ts": 2991, - "2631-create-require/main.ts": 3044, - "2632-dyn-jsval-iterate.js": 2878, - "2633-island-promise-crossing.js": 3370, - "2634-stream-pipeline-arg-ladders.cjs": 2714, - "2635-trap-binding-unmappable-written.ts": 2160, - "2636-trap-binding-ambient-rooted-rewrite.ts": 2374, - "2637-create-require-bare/main.ts": 2239, - "2638-http-dyn-res-compat.cjs": 3131, - "2639-http-dyn-req-pause.cjs": 2921, - "2640-net-dyn-socket-compat.cjs": 2968, - "2641-http-agent-surface.cjs": 3429, - "2642-http-agent-maxsockets.cjs": 2699, - "2643-or-default-retag.ts": 2845, - "2644-http-client-url-argument.ts": 2453, - "2645-https-client-url-argument.ts": 2836, - "2646-top-level-await.ts": 2403, - "2647-top-level-await-modules/main.ts": 2235, - "2648-top-level-await-rejection.ts": 1959, - "2649-top-level-await-pending.ts": 2384, - "2650-top-level-await-self-import.ts": 1987, - "2651-top-level-await-pending-unhandled.ts": 2002, - "2652-top-level-await-dynamic/main.ts": 2598, - "2653-top-level-await-rejection-stops-loop.ts": 2523, - "2654-top-level-await-unrelated-rejection.ts": 2015, - "2655-top-level-await-cycle/main.ts": 2308, - "2656-top-level-await-cycle-order/main.ts": 4054, - "2657-top-level-await-cycle-dynamic/main.ts": 2392, - "2658-top-level-await-sync-completion/main.ts": 2314, - "2659-top-level-await-dynamic-cycle/main.ts": 2467, - "2660-top-level-await-cycle-rejection/main.ts": 2494, - "2661-top-level-await-dynamic-runtime-root/main.ts": 2327, - "2662-top-level-await-cycle-external-wait/main.ts": 2397, - "2663-top-level-await-dependency-errors/main.ts": 2080, - "2664-top-level-await-same-checkpoint-rejection.ts": 2543, - "2665-top-level-await-unhandled-listener-liveness.ts": 2570, - "2666-array-to-sorted.ts": 6667, - "2668-stored-number-array-iterator.ts": 2876, - "2669-array-copying-methods.ts": 3089, - "2670-uint8array-copy-iterate.ts": 2885, - "2671-array-copying-js.cjs": 2315, - "2672-http-request-response-callback.ts": 2962, - "2673-top-level-await-implicit-module.ts": 2333, - "2674-top-level-for-await-implicit-module.ts": 2353, - "2675-create-require-nested-esm.js": 2188, - "2675-process-self-reexec.ts": 2836, - "2676-indexed-read-strict-equality.ts": 2408, - "2676-record-clone-overrides.ts": 2543, - "2676-top-level-await-ambiguous/main.ts": 2576, - "2677-runtime-optional-closure.ts": 4255, - "2677-top-level-await-module-scope/main.ts": 2463, - "2678-date-instances.ts": 2194, - "2678-large-string-sparse-index.ts": 2586, - "2678-util-parseargs.ts": 4462, - "2679-util-parseargs-cjs.cjs": 4905, - "2680-util-parseargs-default.ts": 1889, - "2681-util-parseargs-dynamic.js": 2618, - "2682-fs-rename.ts": 3414, - "2683-filter-truthy-predicate.ts": 3055, - "2683-fs-rename-js.cjs": 4058, - "2684-filter-no-predicate-dyn.cjs": 2608, - "2684-fs-rename-abort.ts": 2639, - "2685-array-unshift-reverse.ts": 3540, - "2685-fs-write-sync.ts": 2779, - "2686-fs-file-handle.ts": 3096, - "2687-fsp-write-file-options.ts": 2864, - "2687-process-write-args.ts": 2396, - "2688-fs-stats-disk-fields.ts": 3953, - "2689-http-server-timeout-properties.ts": 2717, - "2690-https-server-timeout-option.ts": 2702, - "2691-retained-lowering-record-order.ts": 2988, - "2692-inline-record-assert-index.ts": 2526, - "2692-string-accumulation.ts": 2081, - "2693-math-constants.ts": 1991, - "2700-array-sort-complexity.ts": 2921, - "2700-http-static-trailers.ts": 2864, - "2700-wasi-core.ts": 2423, - "2701-array-sort-snapshot.ts": 2953, - "2701-http-static-trailer-wire.ts": 4541, - "2701-import-meta/main.mjs": 2520, - "2702-http-static-trailer-end.ts": 2827, - "2702-type-import-link-static/main.ts": 2535, - "2702-uint8array-to-sorted.ts": 2284, - "2703-array-sparse-sort.ts": 4504, - "2703-type-import-link-static-reexport/main.ts": 3626, - "2704-array-sparse-to-sorted.ts": 4339, - "2704-type-import-link-static-success/main.ts": 2363, - "2705-array-sparse-sort-append.ts": 4401, - "2705-type-import-link-dynamic/main.ts": 1851, - "2706-array-search-missing-needle.ts": 2603, - "2706-type-import-link-dynamic-success/main.ts": 2413, - "2707-array-search-huge-sparse.ts": 45742, - "2707-type-import-link-static-default-reexport/main.ts": 2437, - "2708-type-import-link-dynamic/main.ts": 2892, - "2720-array-read-parity.ts": 3049, - "2721-array-nested-missing-receiver.ts": 2315, - "2750-array-sparse-foundation.ts": 3742, - "2751-buffer-concat-sparse-list.ts": 4185, - "2752-array-optional-stores.ts": 2347, - "2753-array-optional-method-values.ts": 3965, - "2754-array-property-undefined.ts": 2255, - "2755-array-optional-literal-forwarding.ts": 2583, - "2780-array-parity-composition.ts": 6251, - "2781-array-missing-value-escape.ts": 2554, - "2782-array-sparse-callbacks.ts": 3284, - "2783-array-defaults-rest.ts": 2724, - "2784-array-reduce-missing-result.ts": 2691, - "2785-array-missing-destructuring.ts": 2806, - "2786-array-sparse-serialization.ts": 2888, - "2787-array-missing-string-arithmetic.ts": 1877, - "2788-array-missing-builtin-conversions.ts": 1930, - "2789-array-missing-optional-tostring.ts": 1731, - "2790-dynamic-import-self-name/main.ts": 2308, - "2791-record-optional-keys.ts": 2478, - "2792-record-optional-spread-entries.ts": 2703, - "2793-union-conversion-identity-capture.ts": 1753, - "2794-array-generic-callback-values.ts": 2420, - "2795-array-optional-typedarray-source.ts": 1737, - "2796-array-sparse-class-value-instanceof.ts": 2296, - "2800-array-expression-consumers.ts": 2565, - "2801-array-switch-consumers.ts": 2471, - "2820-array-missing-method-receivers.ts": 2123, - "2821-array-optional-numeric-comparisons.ts": 2408, - "2830-cjs-require-self-name/main.cjs": 2633, - "2840-finally-completions.ts": 2505, - "2841-using-sync.ts": 2632, - "2842-await-using.ts": 1790, - "2843-using-suppressed-error.ts": 2235, - "2844-node-disposable-resources.ts": 2658, - "2845-for-using-of.ts": 4410, - "2846-generator-using.ts": 2392, - "2850-async-generators-protocol.ts": 3002, - "2851-async-generators-values.ts": 4379, - "2852-timers-promises-interval.ts": 2511, - "2854-leading-bom-literals.ts": 2528, - "2855-searchparams-sort-complexity.ts": 2416, - "2856-dynamic-any-local-operators.ts": 1839, - "2857-branded-primitives.ts": 1736, - "2858-import-meta-resolve/main.mjs": 2679, - "2859-import-meta-resolve-dynamic/main.mjs": 2295, + "001-hello.ts": 4256, + "002-log-args.ts": 4431, + "100-number-format.ts": 4288, + "1000-json-stringify-basics.ts": 5132, + "1001-json-escapes-unicode.ts": 3267, + "1002-json-parse-cast.ts": 5058, + "1003-json-parse-unions.ts": 4949, + "1004-json-parse-errors.ts": 3893, + "1005-json-nested.ts": 4163, + "1006-json-fs-config.ts": 3643, + "1007-json-rc-stress.ts": 4310, + "1008-json-null-arms.ts": 3701, + "1009-json-optional-fields.ts": 5156, + "101-arithmetic.ts": 3674, + "1010-json-stringify-space.ts": 4817, + "1011-json-unknown-typeof.ts": 3898, + "102-comparisons.ts": 4171, + "1020-async-basics.ts": 2636, + "1021-async-ordering.ts": 3263, + "1022-async-exceptions.ts": 3483, + "1023-async-rc-stress.ts": 4371, + "1024-async-pending-exit.ts": 3071, + "1025-async-promise-capture.ts": 3271, + "1026-throw-promise.ts": 3956, + "1027-async-return-promise.ts": 2933, + "1028-async-return-record-literals.ts": 3384, + "1029-async-eager-chains.ts": 2879, + "103-ternary.ts": 2774, + "104-ternary-empty-arrays.ts": 3724, + "1050-comptime-tables.ts": 3691, + "1051-comptime-strings.ts": 2389, + "1052-comptime-records.ts": 3840, + "1053-comptime-json.ts": 4536, + "1054-comptime-modules/main.ts": 3656, + "110-number-radix.ts": 2382, + "111-record-entries.ts": 4655, + "1110-math-members.ts": 4248, + "1111-math-random.ts": 3457, + "1113-string-methods.ts": 3364, + "1114-string-unicode-island.ts": 3366, + "1115-parse-globals.ts": 2591, + "1116-island-static-mix.ts": 3058, + "1117-typeof-static-union.ts": 3667, + "1118-object-spread-conditional.ts": 3693, + "1119-switch-union.ts": 3739, + "112-class-descriptor-key.js": 3492, + "1121-infinity-number-tostring.ts": 3539, + "1122-any-captures.ts": 3596, + "1124-union-narrowed-retag.ts": 3745, + "113-promise-checked-exits.ts": 2180, + "114-live-record-delete.ts": 3506, + "115-array-compound.js": 2722, + "116-text-decoder-runtime.js": 3978, + "117-weak-cache-cycle.js": 2417, + "118-fs-stored-callbacks.cjs": 4105, + "119-native-patterns.js": 4047, + "1200-regex-test-basics.ts": 2828, + "1201-regex-replace.ts": 3118, + "1202-regex-substitutions.ts": 2460, + "1203-regex-split.ts": 3529, + "1204-regex-empty-unicode.ts": 2746, + "1205-regex-rc-stress.ts": 3472, + "1300-errors-basics.ts": 3642, + "1301-errors-subclass.ts": 3871, + "1302-errors-typed-catch.ts": 3598, + "1303-errors-rc-stress.ts": 2806, + "1304-errors-uncaught.ts": 2053, + "1305-errors-async-rejections.ts": 3710, + "1306-errors-runtime-instances.ts": 2540, + "1350-path-normalize-join.ts": 2427, + "1351-path-parts.ts": 2680, + "1352-path-resolve-relative.ts": 3325, + "1353-os-basics.ts": 3345, + "1354-builtin-bare-specifiers.ts": 3282, + "1355-url-parse.ts": 2902, + "1356-url-file-bridge.ts": 2530, + "1357-fsp-roundtrip.ts": 2551, + "1358-crypto-random.ts": 3328, + "1359-json-module/main.ts": 3223, + "1360-spawn-sync.ts": 3535, + "1361-spawn-events.ts": 2778, + "1362-spawn-timers.ts": 8421, + "1363-spawn-unhandled-error.ts": 2559, + "1364-union-truthiness.ts": 2999, + "1365-union-logical.ts": 3464, + "1366-union-equality.ts": 3482, + "1367-destructuring.ts": 3491, + "1368-constructor-functions.ts": 3392, + "1369-promise-union.ts": 3326, + "1370-spread.ts": 2384, + "1371-union-template-tostring.ts": 3754, + "1372-loose-null-tests.ts": 3218, + "1373-union-array-arms.ts": 4066, + "140-bitwise-operators.ts": 2496, + "1400-typedarray-basics.ts": 3520, + "1401-typedarray-slice-set.ts": 3865, + "1402-buffer-encodings.ts": 2493, + "1403-buffer-fs.ts": 2658, + "1404-zlib-crypto-bytes.ts": 2380, + "1405-bytes-unions-arrays.ts": 2532, + "1406-dataview-mp4.ts": 3915, + "1407-dataview-bounds.ts": 3752, + "1408-string-indexing.ts": 3470, + "1409-typedarray-integer-loops.ts": 2071, + "1420-number-statics.ts": 3136, + "1421-number-parse-strings.ts": 2476, + "1422-date-iso.ts": 2600, + "1423-text-codec.ts": 4597, + "1424-fs-options.ts": 3300, + "1425-stdin-tty.ts": 3063, + "1426-stdin-read.ts": 2925, + "1427-fromcharcode-lastindexof.ts": 3413, + "1428-settled-await-order.ts": 2724, + "1429-promise-catch-finally.ts": 2634, + "1430-promise-race.ts": 4554, + "1431-caught-tostring.ts": 2091, + "1432-destructured-params.ts": 3338, + "1433-filter-narrow.ts": 3904, + "1434-async-never.ts": 3174, + "1435-math-spread.ts": 3092, + "1436-default-undef-param.ts": 3956, + "1437-pad-default.ts": 3383, + "1438-promise-all.ts": 6011, + "1440-interval-basics.ts": 4575, + "1441-interval-clear-cross.ts": 6244, + "1442-interval-throw.ts": 3389, + "1443-signal-handlers.ts": 6258, + "1444-exit-listeners.ts": 3211, + "1445-exit-process-exit.ts": 3320, + "1446-exit-uncaught.ts": 2360, + "1447-stdin-closed-events.ts": 2750, + "1448-stream-columns.ts": 2209, + "1450-incdec-expression.ts": 3228, + "1451-instanceof-uint8array-unknown.ts": 2957, + "1452-return-through-finally.ts": 3660, + "1453-pmap-settled.ts": 3618, + "1454-nonnull-narrow-and-fcc-bytes.ts": 3317, + "1455-lambda-union-return-adoption.ts": 2774, + "1460-console-error-warn.ts": 3320, + "1461-process-pid-getuid-kill.ts": 3247, + "1462-exec-sync.ts": 3282, + "1463-timeout-unref.ts": 3251, + "1464-env-writes.ts": 2987, + "1465-env-value.ts": 3024, + "1466-child-containers.ts": 3422, + "1467-string-match.ts": 3346, + "1468-exec-path.ts": 2301, + "1469-remove-listener.ts": 3606, + "1470-child-lifecycle.ts": 3256, + "1471-child-unref.ts": 7451, + "1472-errno-code.ts": 3295, + "1473-promisify-execfile.ts": 2595, + "1474-string-decoder.ts": 3611, + "1475-readline-closed-stdin.ts": 3112, + "1476-assign-expression.ts": 3030, + "1477-in-expressions.ts": 2996, + "1478-promise-reject.ts": 3812, + "1479-destructuring-assign.ts": 3356, + "1480-os-network-interfaces.ts": 4196, + "1481-string-case-static.ts": 3245, + "1482-spawnsync-error.ts": 3565, + "1483-array-slice.ts": 3674, + "1520-fs-wider-surface.ts": 3471, + "1520-string-split-static.ts": 2773, + "1521-fs-lstat-lock.ts": 3726, + "1521-string-trim-pad-static.ts": 3379, + "1522-parseint-static.ts": 3352, + "1522-spawnsync-options.ts": 5275, + "1523-isnan-floor-static.ts": 3491, + "1523-spawn-options.ts": 3565, + "1524-atomics-sleep-static.ts": 3559, + "1524-catch-narrowing.ts": 3222, + "1525-child-exit-signal.ts": 3428, + "1525-unknown-typeof-validation.ts": 3170, + "1526-set-spread.ts": 3383, + "1527-logical-mixed-operands.ts": 3407, + "1528-delete-records-env.ts": 3353, + "1529-class-with-statics.ts": 2988, + "1530-array-push-variadic.ts": 3122, + "1530-spread-override-completion.ts": 2849, + "1531-delete-optional-fields.ts": 3199, + "1531-process-arch-versions.ts": 3354, + "1532-array-splice-shift.ts": 2767, + "1532-union-shared-field-read.ts": 3248, + "1533-bool-equality.ts": 3299, + "1533-path-platform-namespaces.ts": 3213, + "1534-crypto-hash-chain.ts": 3265, + "1534-union-as-arm-cast.ts": 2201, + "1535-spawn-fd-stdio.ts": 3518, + "1535-union-param-defaults.ts": 2934, + "1536-destructuring-defaults.ts": 3325, + "1536-string-array-sweep.ts": 3492, + "1537-chalk-hybrid/main.ts": 2721, + "1537-os-release-spawnsync-stdio.ts": 3013, + "1538-await-promiselike-return.ts": 3514, + "1538-math-static-scalar.ts": 2646, + "1539-readonly-set-map.ts": 2440, + "1539-unknown-truthiness.ts": 3020, + "1540-os-userinfo.ts": 2759, + "1540-void-statement.ts": 3146, + "1541-fs-readdir-dirent.ts": 3308, + "1541-union-keyed-reads.ts": 2673, + "1542-map-promise-values.ts": 3263, + "1542-record-literal-into-union.ts": 3421, + "1543-rest-destructuring.ts": 2387, + "1543-set-server-handles.ts": 3383, + "1544-dyn-json-reads.ts": 4211, + "1544-string-matchall.ts": 3589, + "1545-spread-order-and-optional.ts": 3635, + "1546-union-element-reads.ts": 3551, + "1547-computed-key-fold.ts": 3124, + "1548-boolean-condition-forms.ts": 3319, + "1549-array-isarray-unions.ts": 3931, + "1550-param-defaults-func-set.ts": 3236, + "1551-dyn-receiver-methods.ts": 4427, + "1552-exec-options-record.ts": 3644, + "1553-truthy-hof-predicates.ts": 3271, + "1554-caught-into-unknown.ts": 3523, + "1555-index-spread-into-declared.ts": 4108, + "1556-union-retag-width-arms.ts": 2796, + "1557-url-host.ts": 3260, + "1558-any-joins-and-dyn-validation.ts": 2875, + "1559-conditional-spread-index-merge.ts": 2528, + "1560-forof-homogeneous-tuples.ts": 2537, + "1561-forof-strings.ts": 2246, + "1561-promise-then.ts": 3068, + "1562-optional-chain-tails.ts": 2705, + "1562-spawn-conditional-spread.ts": 3287, + "1563-forof-iterator-projections.ts": 2707, + "1563-string-raw-fold.ts": 2255, + "1564-fs-watch.ts": 3802, + "1564-string-raw.ts": 2918, + "1565-spawn-pipe-streams.ts": 3630, + "1565-union-element-join.ts": 3683, + "1566-child-duck-interface.ts": 3169, + "1567-server-close-override.ts": 2292, + "1568-x509-fingerprint.ts": 3260, + "1569-fsp-mkdir-unlink-chmod.ts": 2151, + "1570-child-unref-kill-reffed.ts": 8153, + "1571-optional-call-tostring-tail.ts": 2977, + "1571-stdin-set-raw-mode-non-tty.ts": 2223, + "1572-promise-reject-all-tuple.ts": 3566, + "1572-x509-validity-date-gettime.ts": 2517, + "1573-promisify-execfile-env-spread.ts": 3520, + "1573-tdz-scalar-forward-capture.ts": 2526, + "1574-dyn-optional-method-number-keys.ts": 3924, + "1574-promise-all-tuple-literal.ts": 2676, + "1575-unknown-assert-into-record.ts": 3519, + "1576-width-coercions.ts": 2683, + "1577-url-hostname.ts": 2337, + "1578-exec-input-optional.ts": 3059, + "1579-matchall-index.ts": 2856, + "1580-exec-env-conditional-spread.ts": 2426, + "1581-declare-const-read.ts": 3033, + "1582-timer-sleep-resolve.ts": 3454, + "1583-loops-and-stream-captures.ts": 2185, + "1584-union-field-unassigned.ts": 2354, + "1585-union-field-conditional-ctor.ts": 2702, + "1586-derived-field-before-super-init.ts": 2376, + "1590-js-unannotated.js": 2299, + "1591-js-closures.js": 3459, + "1592-js-classes.js": 3000, + "1593-js-jsdoc-records.js": 2837, + "1594-js-async.js": 3183, + "1595-js-island-gap/main.js": 3402, + "1596-cjs-modules/main.js": 2196, + "1597-mjs-graph/main.mjs": 3071, + "1598-cjs-builtin-require/main.js": 2199, + "1599-js-uncaught-throw.js": 2702, + "1600-assert-passing.ts": 3435, + "1601-assert-fail-exit.ts": 2059, + "1602-assert-caught-error.ts": 3377, + "1603-assert-scalar-messages.ts": 3468, + "1604-assert-deep-structures.ts": 3981, + "1605-assert-import-forms.ts": 3304, + "1606-assert-strict-module.ts": 3605, + "1607-assert-throws-match.ts": 2614, + "1608-cjs-assert/main.js": 3331, + "1609-assert-async.ts": 3158, + "1610-path-win32-full.ts": 3354, + "1611-url-file-bridge-neutral.ts": 2896, + "1612-cjs-module-globals.cjs": 1926, + "1613-cjs-esm-proxy-named/main.mjs": 2400, + "1614-js-fence-dodges.cjs": 2746, + "1615-cjs-export-shapes/main.cjs": 3057, + "1616-cjs-esm-lexer-invisible/main.mjs": 2969, + "1617-cjs-esm-lexer-visible/main.mjs": 3072, + "1618-cjs-esm-poisoned-tail/main.mjs": 2562, + "1619-cjs-esm-reexport-invisible/main.mjs": 2897, + "1620-cjs-require-effects-order/main.js": 3000, + "1621-cjs-require-cache-hit/main.js": 2851, + "1622-cjs-require-conditional/main.js": 3013, + "1623-cjs-require-diamond/main.js": 2407, + "1624-cjs-require-dir-mid-file/main.js": 2600, + "1625-cjs-require-lazy-fn/main.js": 2150, + "1626-cjs-require-mjs/main.js": 2871, + "1627-mjs-import-cjs-requires/main.mjs": 1644, + "1628-checked-dynamic-builtin-args.cjs": 3084, + "1629-require-main-filename.cjs": 2697, + "1630-inspect-scalars.ts": 2908, + "1630-rmsync-retry-options.cjs": 2250, + "1631-inspect-arrays.ts": 3807, + "1632-inspect-records.ts": 3072, + "1633-inspect-map-set.ts": 3749, + "1634-inspect-classes.ts": 3647, + "1635-inspect-buffer.ts": 2966, + "1636-util-format.ts": 2790, + "1637-inspect-dyn.ts": 3304, + "1638-inspect-cjs.cjs": 2542, + "1639-versions-openssl-probe.ts": 2867, + "1640-fd-read-decode.ts": 2932, + "1641-string-search.ts": 2902, + "1642-object-fromentries-rows.ts": 3493, + "1643-metadata-dyn-return.cjs": 3745, + "1644-ee-basics.ts": 2848, + "1645-ee-extends.ts": 2459, + "1646-ee-once-remove.ts": 3027, + "1647-ee-meta-events.ts": 2585, + "1648-ee-error-event.ts": 2283, + "1649-ee-names-counts.ts": 2675, + "1650-ee-prepend.ts": 2768, + "1651-ee-max-listeners.ts": 2226, + "1652-ee-snapshot.ts": 2979, + "1653-ee-cjs.cjs": 2570, + "1654-ee-namespace.ts": 2090, + "1655-spawnsync-neutral.ts": 4974, + "1656-execsync-neutral.ts": 2907, + "1657-spawn-async-neutral.ts": 3429, + "1660-buffer-read-write-num.ts": 3576, + "1661-buffer-encodings-full.ts": 3754, + "1662-string-decoder-encodings.ts": 3115, + "1663-buffer-compare-search-fill.ts": 3954, + "1664-dyn-fn-boundary.cjs": 3077, + "1665-dyn-fn-mustcall/main.cjs": 2296, + "1666-dyn-fn-identity.ts": 1815, + "1667-dyn-fn-not-callable.cjs": 3076, + "1668-dyn-fn-throws.cjs": 2849, + "1669-symbol-identity.ts": 2663, + "1670-symbol-registry.ts": 2947, + "1671-symbol-typeof.ts": 3427, + "1672-symbol-containers.ts": 2913, + "1673-symbol-cjs.cjs": 2504, + "1674-symbol-inspect.ts": 2841, + "1676-func-array-surface.ts": 3070, + "1677-emitter-listeners.ts": 3184, + "1678-emitter-dyn-listeners.cjs": 4068, + "1679-console-dyn.js": 3572, + "1680-assert-bytes.ts": 2927, + "1681-assert-funcs.ts": 3461, + "1685-stream-readable-basics.ts": 3135, + "1686-stream-readable-paused.ts": 2443, + "1687-stream-readable-flow-control.ts": 3237, + "1688-stream-writable-basics.ts": 2329, + "1689-stream-writable-async-drain.ts": 2789, + "1690-stream-duplex.ts": 2966, + "1691-stream-transform.ts": 2618, + "1692-stream-pipe.ts": 1917, + "1693-stream-pipe-backpressure.ts": 2276, + "1694-stream-destroy.ts": 3398, + "1695-stream-props.ts": 3260, + "1696-stream-read-inside-read.ts": 2732, + "1697-stream-tick-vs-timer.ts": 2767, + "1698-stream-uncaught-error.ts": 2710, + "1699-stream-callback-shapes.ts": 5108, + "1700-mustcall-common/main.cjs": 2603, + "1701-mustcall-exit-report/main.cjs": 3638, + "1702-dyn-proto-dispatch.cjs": 3719, + "1703-arguments-rest-props.cjs": 3420, + "1710-cjs-export-class/main.js": 3197, + "1711-cjs-export-single-values/main.js": 1720, + "1712-field-incdec.ts": 2250, + "1713-js-dyn-fields.js": 2301, + "1720-assert-throws-shape.ts": 3820, + "1721-assert-throws-regex-class.ts": 3123, + "1722-assert-rejects.ts": 3400, + "1723-assert-does-not-reject.ts": 2181, + "1724-assert-iferror.ts": 2290, + "1725-assert-symbols.ts": 2728, + "1726-promise-with-resolvers.ts": 3835, + "1727-assert-throws-shape-exit.ts": 2906, + "1730-countdown-symbol-keys/main.js": 3476, + "1731-symbol-field-shapes.cjs": 2330, + "1732-symbol-field-cross-module/main.js": 2189, + "1740-stream-extends-readable.ts": 2897, + "1741-stream-extends-writable.ts": 2368, + "1742-stream-extends-duplex-transform.ts": 2283, + "1743-stream-extends-vt-dispatch.ts": 3339, + "1744-stream-set-encoding.ts": 3916, + "1745-stream-encoding-js.cjs": 2742, + "1746-stream-for-await.ts": 3433, + "1747-stream-for-await-js.cjs": 2649, + "1750-http-agent-options.ts": 3028, + "1751-options-record-stance.ts": 3136, + "1760-surplus-args-drop.cjs": 2857, + "1761-emitter-special-event-names.cjs": 2907, + "1770-assert-dyn-strict.ts": 2622, + "1771-assert-dyn-deep.ts": 3046, + "1772-assert-dyn-js.cjs": 2864, + "1773-assert-dyn-exit.ts": 3097, + "1780-http-res-surface.ts": 3598, + "1781-http-server-shapes.cjs": 3736, + "1790-computed-key-folds.ts": 2845, + "1791-searchparams-core.ts": 3126, + "1792-searchparams-encoding.ts": 2170, + "1793-searchparams-iteration.ts": 2956, + "1794-searchparams-url-live.ts": 2603, + "1795-dyn-handle-request.cjs": 2396, + "1796-dyn-handle-body.cjs": 3535, + "1797-dyn-handle-socket.cjs": 3460, + "1798-dyn-handle-typed-box.ts": 2566, + "1800-immediate-basics.ts": 2566, + "1801-immediate-handle.ts": 2028, + "1802-immediate-unref-exit.ts": 3806, + "1803-timeout-refresh.ts": 2342, + "1804-timers-module.ts": 2401, + "1805-timer-callback-args.ts": 2781, + "1806-timer-delay-trunc.ts": 2824, + "1810-stream-options-const-record.ts": 2399, + "1811-stream-option-value-callbacks.cjs": 2961, + "1812-stream-super-options-forwarding.cjs": 3737, + "1813-stream-finished.ts": 3060, + "1814-stream-pipeline.cjs": 3701, + "1815-stream-state-reads.cjs": 2427, + "1820-static-block-basics.ts": 2518, + "1821-static-block-order.ts": 1976, + "1822-static-block-js.js": 3002, + "1823-static-instance-method-names.ts": 2691, + "1824-for-of-destructuring-defaults.ts": 2461, + "1825-exhaustive-typeof-switch.ts": 2514, + "1826-settimeout-arguments-callback.js": 2251, + "1830-enum-numeric-basics.ts": 2064, + "1831-enum-string-const-reverse.ts": 2264, + "1832-enum-modules/main.ts": 1833, + "1835-var-basics.ts": 2710, + "1836-var-loop-capture.ts": 3374, + "1837-var-undefined-hoisting.ts": 2917, + "1838-var-modules/main.ts": 2984, + "1839-var-js/main.cjs": 2891, + "1840-jsdoc-import-type-only/main.js": 1993, + "1850-overload-basics.ts": 2300, + "1851-overload-return-narrowing.ts": 2471, + "1852-overload-class-members.ts": 3006, + "1853-overload-modules/main.ts": 2503, + "1854-ambient-declare-fn.ts": 2630, + "1855-ambient-declare-fn-uncaught.ts": 3114, + "1870-unit-type-values.ts": 3109, + "1871-empty-array-never.ts": 2887, + "1872-template-tostring-composite.ts": 3584, + "1880-json-declared-field-order.ts": 3331, + "1881-default-anon/main.ts": 2353, + "1882-default-cjs-interop/main.ts": 2739, + "1883-default-exports/main.ts": 2650, + "1890-ns-imports/main.ts": 2779, + "1891-ns-reexport/main.ts": 2711, + "1940-class-values-basics.ts": 2460, + "1941-class-values-registry.ts": 3025, + "1942-class-expressions.ts": 2872, + "1943-class-statics-expanded.ts": 2580, + "1944-class-values-modules/main.ts": 2749, + "1950-generic-fn-values.ts": 2970, + "1951-generic-classes-basics.ts": 2980, + "1952-generic-class-statics-values.ts": 3062, + "1953-generic-class-hierarchy.ts": 2835, + "1954-generic-modules/main.ts": 1880, + "1960-namespace-basics.ts": 2265, + "1961-namespace-nested.ts": 2468, + "1962-namespace-classes.ts": 2516, + "1963-namespace-aliases.ts": 2375, + "1964-namespace-type-only.ts": 2816, + "1965-namespace-ambient.ts": 2803, + "1966-namespace-modules/main.ts": 3021, + "1967-namespace-alias-typeonly.ts": 2847, + "1968-namespace-import-eq-snapshot.ts": 2494, + "1970-decorators-basics.ts": 2822, + "1971-decorators-rebinding.ts": 2744, + "1972-decorators-throwing.ts": 2170, + "1973-decorators-ambient.ts": 2056, + "1980-primitive-proto-statics.ts": 2953, + "1981-date-utc.ts": 3276, + "1982-freeze-resolve-passthrough.ts": 2464, + "1983-array-tuple-surfaces.ts": 2644, + "1990-labels-basics.ts": 2635, + "1991-for-in-loops.ts": 3295, + "1992-small-syntax.ts": 3144, + "200-strings.ts": 2045, + "2000-generic-methods-basic.ts": 2851, + "2001-generic-methods-generic-class.ts": 2303, + "2002-generic-methods-inheritance.ts": 2257, + "2003-generic-methods-object-literal.ts": 3223, + "2004-generic-methods-statics.ts": 2748, + "201-templates.ts": 2400, + "2010-generators-basics.ts": 2273, + "2011-generators-forof.ts": 2704, + "2012-generators-return-throw.ts": 3247, + "2013-generators-sent-values.ts": 2662, + "2014-generators-values.ts": 3279, + "2015-generators-yieldstar.ts": 2320, + "2016-generators-rc-stress.ts": 2649, + "2017-generators-async.ts": 1806, + "2018-generators-uncaught.ts": 2418, + "2019-generators-loops.ts": 2694, + "2020-generic-value-bindings.ts": 3269, + "2021-generic-value-binding-modules/main.ts": 2143, + "2022-width-index-capture.ts": 3286, + "2023-width-readonly.ts": 2938, + "2024-width-array-elems.ts": 3159, + "2025-width-union-compose.ts": 3090, + "2026-width-spread.ts": 2628, + "2030-width-nested.ts": 3229, + "2031-width-tuples.ts": 2339, + "2032-cjs-export-assigned-class-expression/main.cjs": 2543, + "2033-property-assigned-class-extends.cjs": 1959, + "2035-ternary-array-retag.cjs": 2840, + "2036-evolving-array-decl.cjs": 2905, + "2037-fn-decl-hoisting.cjs": 2710, + "2038-evolving-array-js.cjs": 2879, + "2040-any-bindings.ts": 1964, + "2040-mixin-heritage.ts": 3294, + "2041-any-params-returns.ts": 2494, + "2041-mixin-values.ts": 3171, + "2042-any-flow-loops.ts": 2765, + "2042-mixin-modules/main.ts": 2821, + "2043-any-dom-values.ts": 1989, + "2043-mixin-rc-stress.ts": 2763, + "2044-vtable-sibling-slots.ts": 3156, + "2045-objlit-accessors-basic.ts": 2839, + "2045-parameter-properties.ts": 2262, + "2046-abstract-classes.ts": 2943, + "2046-objlit-accessors-effects.ts": 3238, + "2047-objlit-accessors-shapes.ts": 2962, + "2047-optional-class-fields.ts": 2352, + "2050-dynamic-import-own-module/main.ts": 2258, + "2051-dynamic-import-then/main.ts": 2628, + "2052-dynamic-import-self.ts": 2111, + "2053-typeof-static-fold.ts": 2331, + "2054-destructuring-island-source.ts": 2721, + "2060-empty-tuple.ts": 2638, + "2070-computed-key-folds.ts": 2482, + "2071-integer-key-enumeration-order.ts": 1999, + "2072-expando-function-members.ts": 2249, + "2073-destructuring-assignment.ts": 3060, + "2074-island-destructuring.ts": 3490, + "2075-index-signature-runtime-keys.ts": 3003, + "2080-destructuring-defaults.ts": 2782, + "2081-destructuring-rest.ts": 2626, + "2082-destructuring-assignment.ts": 2395, + "2085-destructuring-heads-rest-params.ts": 3137, + "2087-destructuring-for-heads.ts": 1934, + "2090-index-signature-number-keys.ts": 3257, + "2091-index-signature-string-intersection.ts": 2929, + "2092-package-imports/main.ts": 2114, + "2093-timers-promises.ts": 2678, + "2094-diagnostics-channel.ts": 3068, + "2095-js-catch-unknown.js": 2369, + "2096-os-type.ts": 2451, + "2097-js-evolving-globals.js": 2097, + "210-string-methods.ts": 2359, + "2100-stream-default-hwm.ts": 3022, + "2101-dyn-param-defaults.ts": 3172, + "2102-empty-pattern-decls.ts": 2881, + "2105-computed-key-static-folds.ts": 2838, + "2106-comma-expressions.ts": 1834, + "211-string-unicode.ts": 2972, + "2110-top-object-types.ts": 2238, + "2111-json-stringify-unknown.ts": 3135, + "2112-array-at-findlast.ts": 3283, + "2113-string-index-methods.js": 2879, + "212-string-aliasing-append.ts": 2677, + "2120-package-self-import/main.ts": 2837, + "2121-esm-cycle-inert-backedge/main.ts": 2507, + "2123-ambient-import-crash/main.ts": 2934, + "2124-imports-field-wildcard/main.ts": 2541, + "2140-uri-component-codecs.ts": 2133, + "2160-dc-tracing-channel.cjs": 2230, + "2161-js-object-literal-identity.cjs": 2537, + "2162-js-dyn-destructure.cjs": 2853, + "2163-js-selfref-const.cjs": 2693, + "2164-js-then-dyn-handler.cjs": 3004, + "2165-js-throw-dyn.cjs": 2225, + "2166-cjs-getter-this/main.cjs": 2804, + "2170-island-array-exits.ts": 2101, + "2171-island-json-stringify.ts": 2821, + "2172-island-regexp-boundary.ts": 2351, + "2190-new-object-and-string-wrappers.ts": 1908, + "2191-uri-encoders.ts": 3002, + "2192-object-default-tostring.ts": 2457, + "2193-discarded-stdlib-reads.ts": 2611, + "2194-ambient-undef-chains.ts": 1952, + "2195-require-missing-package/main.mjs": 3141, + "2196-island-computed-key-chain.ts": 2827, + "2210-dyn-promise-crossing.cjs": 3139, + "2211-dc-trace-promise.cjs": 2409, + "2212-unhandled-rejection-listener.cjs": 3057, + "2213-async-local-storage.cjs": 3299, + "2214-dc-bind-store.cjs": 3410, + "2215-process-emit-warning-exit.cjs": 3115, + "2240-width-class-into-record.ts": 2461, + "2241-width-record-into-class.ts": 2711, + "2242-width-statics-into-record.ts": 2854, + "2243-width-assert-satisfies-shapes.ts": 2928, + "2250-tagged-templates.ts": 2744, + "2252-class-iterators.ts": 3258, + "2253-union-coercions.ts": 3096, + "2260-http2-constants.cjs": 2383, + "2280-builtin-namespace-destructure.cjs": 2801, + "2281-domexception-atob-btoa.cjs": 2886, + "2282-queue-microtask.cjs": 2614, + "2283-structured-clone.cjs": 3700, + "2284-regexp-constructor.cjs": 2429, + "2285-iferror-dyn.cjs": 2807, + "2286-dyn-object-walks.cjs": 2926, + "2300-dyn-record-spread-and-keyed-access.cjs": 2890, + "2301-cjs-export-table-dom-attach.cjs": 2512, + "2310-process-next-tick.ts": 3216, + "2311-next-tick-js-callbacks.js": 2966, + "2312-stream-underscore-assign.ts": 3290, + "2313-stream-underscore-assign-js.cjs": 2122, + "2314-process-introspection.ts": 2917, + "2320-await-unit.ts": 2292, + "2321-emitter-static-setmax.cjs": 2256, + "2322-dyn-array-sort.cjs": 2432, + "2323-timer-callback-returns.ts": 2842, + "2330-tls-server-runtime-options.cjs": 2815, + "2331-tls-server-options-typed.ts": 3087, + "2350-nan-global.ts": 3034, + "2351-async-methods.ts": 2783, + "2352-void-coercions.ts": 3060, + "2353-decorators-member-ambient.ts": 2052, + "2354-decorators-expression-ambient.ts": 1987, + "2360-tuple-to-array.ts": 3001, + "2361-assign-index-record.ts": 2918, + "2362-execsync-inherit.ts": 2884, + "2363-nullish-retag.ts": 3048, + "2364-in-runtime-key.ts": 2311, + "2365-dyn-into-island.ts": 2070, + "2366-string-well-formed.ts": 2331, + "2367-regexp-escape.ts": 3110, + "2368-set-methods.ts": 2456, + "2369-promise-try.ts": 2986, + "2370-group-by.ts": 3578, + "2371-iterator-helpers.ts": 4030, + "2380-buffer-module-named-import.ts": 2871, + "2381-builtin-default-imports.js": 2605, + "2382-cycle-two-decl/main.ts": 2659, + "2383-cycle-assertdoc/main.js": 2644, + "2384-cycle-mixed-bindings/main.ts": 2274, + "2385-builtin-reexport-facade/main.ts": 2047, + "2386-string-to-chars.ts": 2950, + "2387-tonumber-argv.ts": 2189, + "2388-tonumber-grammar-zoo.ts": 2227, + "2389-tonumber-unary-plus.ts": 2546, + "2390-dot-requires/main.cjs": 2258, + "2391-default-builtin-imports/main.ts": 3224, + "2392-block-guard-alias-scope.ts": 2115, + "2393-loop-exit-guard-narrow.ts": 2686, + "2394-loop-narrow-containment.ts": 2464, + "2395-while-cond-guard.ts": 2793, + "2396-born-narrowed-decl.ts": 2989, + "2397-branch-kill-merge.ts": 2557, + "2398-switch-arm-kill.ts": 2776, + "2399-reassign-kill-polarity.ts": 2005, + "240-stdout-write.ts": 2170, + "2400-kill-return-edge.ts": 2417, + "2401-kill-break-staging.ts": 2710, + "2402-labeled-break-kill.ts": 2048, + "2403-kill-continue-backedge.ts": 2629, + "2404-kill-throw-catch.ts": 1738, + "2405-sibling-arm-narrow.ts": 2542, + "2406-switch-clause-sibling-narrow.ts": 1729, + "2407-infinite-loop-seal.ts": 2732, + "2408-loop-terminality-reopen.ts": 2758, + "2409-loop-body-return-not-terminal.ts": 2649, + "2410-stacked-cases-terminality.ts": 2587, + "2411-exhaustive-switch-terminality.ts": 2117, + "2412-dowhile-guard-trailing-test.ts": 2829, + "2413-dowhile-kill-renarrow.ts": 2634, + "2414-dowhile-continue-kill.ts": 2056, + "2415-dowhile-break-trailing-test.ts": 2838, + "2416-spread-ternary-positions.ts": 2794, + "2417-ternary-local-inference.ts": 1836, + "2418-wrapped-ternary-arms.ts": 2376, + "2419-selector-ternary-union.ts": 1886, + "2420-nullish-null-identity.ts": 2382, + "2421-float-lane-inference.ts": 2567, + "2422-ieee-div-rem-corners.ts": 2214, + "2423-param-null-reassign.ts": 2589, + "2424-ternary-miss-first-narrowing.ts": 2704, + "2425-elseif-head-exit-narrow.ts": 2085, + "2426-default-snapshot-mutable/main.ts": 2501, + "2427-perf-hooks-now.ts": 2322, + "2428-field-redeclare-inherited.ts": 2811, + "2429-destructure-class-instance.ts": 2113, + "2430-empty-literal-unknown-field.js": 2775, + "2431-primitive-ctor-values.ts": 2709, + "2432-generic-member-fields-async.ts": 3359, + "2433-as-const-command-tables.ts": 3152, + "2434-deferred-init-fields.ts": 2675, + "2435-loose-same-kind-equality.ts": 2929, + "2440-console-inspect-args.ts": 3616, + "2441-console-process-argv.ts": 2729, + "2442-union-literal-arm-widening.ts": 2394, + "2443-union-literal-shadow-narrowing.ts": 2878, + "2444-union-literal-reducer-spread.ts": 2611, + "2445-math-minmax-nary.ts": 2928, + "2446-performance-global.ts": 1959, + "2447-island-boundary-slots/main.ts": 3526, + "2448-entries-null-arms-regex-arrays.ts": 3606, + "2450-private-instance-methods.ts": 3040, + "2451-private-fields.ts": 2458, + "2452-private-statics.ts": 2875, + "2453-private-accessors.ts": 2477, + "2454-private-generator-methods.ts": 3086, + "2455-private-brand-checks.ts": 2830, + "2456-private-async-methods.ts": 3027, + "2457-private-members.js": 2823, + "2460-qs-parse-grammar.ts": 2369, + "2461-qs-parse-options.ts": 2633, + "2462-qs-stringify.ts": 2904, + "2463-qs-escape-unescape.ts": 2627, + "2464-qs-require-forms.cjs": 2308, + "2470-mockable-module-shape.js": 3113, + "2471-record-keyed-write-hasown.js": 3326, + "2472-string-regexp-union.ts": 2731, + "2473-option-table-widths.js": 3152, + "2474-island-literal-forms.ts": 2479, + "2475-variadic-wrapper-island.js": 3198, + "2476-map-regex-values.ts": 2520, + "2480-recursive-record-tree.ts": 2079, + "2481-mutual-recursive-records.ts": 2753, + "2482-recursive-union-tree.ts": 2627, + "2483-recursive-record-cycles.ts": 3056, + "2484-json-stringify-circular.ts": 3122, + "2485-inspect-circular-refs.ts": 2852, + "2486-recursive-record-boundaries.ts": 3348, + "2487-recursive-deep-equal.ts": 2661, + "2488-ast-walker.ts": 3060, + "2490-find-miss-null-compare.ts": 1733, + "2491-strict-unit-compare-no-arm.ts": 2221, + "2492-loose-null-compare-unions.ts": 2888, + "2493-switch-unit-cases.ts": 2663, + "2494-no-arm-compare-effects.ts": 2604, + "2500-net-autosel-timeout.ts": 2518, + "2510-dyn-evolving-array-map.ts": 2547, + "2511-dyn-evolving-array-filter-foreach.ts": 2095, + "2512-dyn-evolving-array-mixed-push.ts": 1858, + "2520-private-statics-class-name-calls.ts": 2236, + "2521-private-statics-class-expression.ts": 2886, + "2522-private-statics-decorated.ts": 2242, + "2523-private-statics-aliased-class.ts": 2547, + "2530-object-destructuring-decl.ts": 2810, + "2531-array-tuple-destructuring-decl.ts": 2637, + "2532-param-destructuring-options.ts": 1880, + "2533-forof-destructuring.ts": 2933, + "2534-object-rest-decl.ts": 2924, + "2535-destructuring-assign-basics.ts": 2850, + "2536-destructuring-assign-nested.ts": 3192, + "2537-destructuring-assign-member-targets.ts": 2938, + "2538-destructuring-assign-class-source.ts": 2800, + "2539-class-instance-rest.ts": 3105, + "2540-accessor-destructuring-defaults.ts": 2832, + "2541-destructuring-eval-order.ts": 2813, + "2550-generics-keyof-pick.ts": 2927, + "2551-generics-value-aliases.ts": 2674, + "2552-generics-iface-methods.ts": 2402, + "2553-generics-signature-bindings.ts": 3014, + "2554-generics-frontier-mix.ts": 2966, + "2555-generics-keyof-writes.ts": 2779, + "2556-crypto-introspection.ts": 3064, + "2556-width-hybrid-shapes.ts": 1985, + "2557-tls-ca-store.ts": 3012, + "2557-width-field-lifts.ts": 3351, + "2558-index-signature-func-values.ts": 3392, + "2558-rejection-events.cjs": 2929, + "2559-index-signature-container-values.ts": 3437, + "2560-arraybuffer-dataview-set.ts": 3189, + "2561-object-is.ts": 2016, + "2562-intl-numberformat-en-us.ts": 2324, + "2563-stream-promises-pipeline.ts": 2778, + "2564-stream-promises-finished.ts": 2282, + "2565-stream-promises-js.cjs": 2174, + "2566-promise-reject-dyn-reason.js": 2837, + "2567-rest-spread-forward.js": 3053, + "2568-rest-spread-forward-dynamic.js": 3336, + "2569-upcast-identity.ts": 2549, + "2570-buffer-arg-validation.cjs": 3495, + "2571-searchparams-arg-validation.cjs": 3289, + "2572-readable-emitted-readable-flag.cjs": 2702, + "2573-fs-url-arg-ladders.cjs": 2908, + "2574-emitter-max-listeners-ladders.cjs": 2125, + "2575-string-destructuring-decl.ts": 3056, + "2576-string-destructuring-assign.ts": 2769, + "2577-builtin-global-destructuring.js": 2592, + "2578-jsval-into-unknown-rows.ts": 3507, + "2579-jsval-object-param-crossing.js": 2689, + "2580-jsval-routed-keyed-ops.js": 3049, + "2581-jsval-routed-calls.js": 2613, + "2582-jsval-object-statics.js": 3028, + "2582-jsval-routed-keyed-ops.js": 2927, + "2583-dyn-nullish-coalesce.js": 2319, + "2583-jsval-routed-calls.js": 3227, + "2584-jsval-object-statics.js": 2921, + "2584-union-dyn-collapse.ts": 2532, + "2585-dyn-nullish-coalesce.js": 3154, + "2585-unknown-array.ts": 3043, + "2586-object-assign-spread.js": 3635, + "2587-array-entries-forof.ts": 2696, + "2588-array-entries-chains.ts": 2632, + "2589-object-create-null.cjs": 3471, + "2590-object-create-dynamic.js": 2856, + "2591-ambient-generic-traps.ts": 2325, + "2592-ambient-trap-uncaught.ts": 2711, + "2593-generic-inert-bindings.ts": 2516, + "2594-nullish-generic-bindings.ts": 2446, + "2595-fs-arg-ladders.cjs": 3442, + "2596-net-arg-ladders.cjs": 3131, + "2597-dgram-send-ladders.cjs": 3331, + "2598-tls-arg-ladders.cjs": 3733, + "2599-stream-arg-ladders.cjs": 2976, + "2600-dyn-keyed-write-harness.js": 3406, + "2601-dyn-keyed-write-ops.js": 2727, + "2602-dyn-array-destructure.js": 2959, + "2603-island-keyed-write-dyn.js": 2708, + "2604-cycle-classes-mutual/main.ts": 2723, + "2605-cycle-three-module/main.ts": 1625, + "2606-dynamic-import-cycle/main.ts": 2126, + "2608-regex-named-groups.ts": 2860, + "2609-regex-named-replace.ts": 2148, + "2610-regex-named-matchall.ts": 2297, + "2611-regex-named-groups-js.cjs": 2862, + "2613-for-init-uninitialized-let.ts": 2631, + "2614-trap-binding-later-writes.ts": 2032, + "2615-nullish-field-record-binding.ts": 2520, + "2616-js-collect-probe-defers.js": 2218, + "2617-enum-static-field-import/main.ts": 2916, + "2618-ee-emit-override.ts": 2896, + "2619-ee-override-chain.ts": 2889, + "2620-ee-override-once-order.ts": 2166, + "2621-ee-override-error-throw.ts": 2609, + "2622-ee-override-filter.ts": 3169, + "2623-ee-job-queue.ts": 3061, + "2624-ee-override-js.cjs": 2641, + "2625-bytes-views.ts": 2414, + "2626-stream-event-names.ts": 3336, + "2627-stream-ticks-are-nextticks.ts": 2921, + "2628-stream-push-encodings.cjs": 2936, + "2629-stream-consumers.ts": 2548, + "2630-stream-consumers-errors.ts": 2549, + "2631-create-require/main.ts": 3179, + "2632-dyn-jsval-iterate.js": 2810, + "2633-island-promise-crossing.js": 3789, + "2634-stream-pipeline-arg-ladders.cjs": 3204, + "2635-trap-binding-unmappable-written.ts": 2573, + "2636-trap-binding-ambient-rooted-rewrite.ts": 2836, + "2637-create-require-bare/main.ts": 3077, + "2638-http-dyn-res-compat.cjs": 3513, + "2639-http-dyn-req-pause.cjs": 3242, + "2640-net-dyn-socket-compat.cjs": 3276, + "2641-http-agent-surface.cjs": 2546, + "2642-http-agent-maxsockets.cjs": 2399, + "2643-or-default-retag.ts": 2038, + "2644-http-client-url-argument.ts": 3141, + "2645-https-client-url-argument.ts": 3327, + "2646-top-level-await.ts": 1674, + "2647-top-level-await-modules/main.ts": 2735, + "2648-top-level-await-rejection.ts": 2839, + "2649-top-level-await-pending.ts": 2632, + "2650-top-level-await-self-import.ts": 2130, + "2651-top-level-await-pending-unhandled.ts": 2661, + "2652-top-level-await-dynamic/main.ts": 2738, + "2653-top-level-await-rejection-stops-loop.ts": 2112, + "2654-top-level-await-unrelated-rejection.ts": 2041, + "2655-top-level-await-cycle/main.ts": 1850, + "2656-top-level-await-cycle-order/main.ts": 2731, + "2657-top-level-await-cycle-dynamic/main.ts": 2538, + "2658-top-level-await-sync-completion/main.ts": 2977, + "2659-top-level-await-dynamic-cycle/main.ts": 2760, + "2660-top-level-await-cycle-rejection/main.ts": 2805, + "2661-top-level-await-dynamic-runtime-root/main.ts": 2535, + "2662-top-level-await-cycle-external-wait/main.ts": 2399, + "2663-top-level-await-dependency-errors/main.ts": 2147, + "2664-top-level-await-same-checkpoint-rejection.ts": 2912, + "2665-top-level-await-unhandled-listener-liveness.ts": 2595, + "2666-array-to-sorted.ts": 5539, + "2668-stored-number-array-iterator.ts": 3042, + "2669-array-copying-methods.ts": 2580, + "2670-uint8array-copy-iterate.ts": 3366, + "2671-array-copying-js.cjs": 2250, + "2672-http-request-response-callback.ts": 2816, + "2673-top-level-await-implicit-module.ts": 2785, + "2674-top-level-for-await-implicit-module.ts": 1961, + "2675-create-require-nested-esm.js": 2652, + "2675-process-self-reexec.ts": 2948, + "2676-indexed-read-strict-equality.ts": 2688, + "2676-record-clone-overrides.ts": 2029, + "2676-top-level-await-ambiguous/main.ts": 2537, + "2677-runtime-optional-closure.ts": 3484, + "2677-top-level-await-module-scope/main.ts": 2041, + "2678-date-instances.ts": 2275, + "2678-large-string-sparse-index.ts": 2301, + "2678-util-parseargs.ts": 5332, + "2679-util-parseargs-cjs.cjs": 2235, + "2680-util-parseargs-default.ts": 2688, + "2681-util-parseargs-dynamic.js": 3031, + "2682-fs-rename.ts": 3590, + "2683-filter-truthy-predicate.ts": 3261, + "2683-fs-rename-js.cjs": 2894, + "2684-filter-no-predicate-dyn.cjs": 2218, + "2684-fs-rename-abort.ts": 2755, + "2685-array-unshift-reverse.ts": 2996, + "2685-fs-write-sync.ts": 3069, + "2686-fs-file-handle.ts": 4210, + "2687-fsp-write-file-options.ts": 2069, + "2687-process-write-args.ts": 2527, + "2688-fs-stats-disk-fields.ts": 2259, + "2689-http-server-timeout-properties.ts": 2541, + "2690-https-server-timeout-option.ts": 1835, + "2691-retained-lowering-record-order.ts": 3261, + "2692-inline-record-assert-index.ts": 2994, + "2692-string-accumulation.ts": 2802, + "2693-math-constants.ts": 2159, + "2700-array-sort-complexity.ts": 3020, + "2700-http-static-trailers.ts": 2315, + "2700-wasi-core.ts": 2595, + "2701-array-sort-snapshot.ts": 2502, + "2701-http-static-trailer-wire.ts": 2176, + "2701-import-meta/main.mjs": 3392, + "2702-http-static-trailer-end.ts": 2071, + "2702-type-import-link-static/main.ts": 2443, + "2702-uint8array-to-sorted.ts": 2965, + "2703-array-sparse-sort.ts": 3909, + "2703-type-import-link-static-reexport/main.ts": 2704, + "2704-array-sparse-to-sorted.ts": 3697, + "2704-type-import-link-static-success/main.ts": 2487, + "2705-array-sparse-sort-append.ts": 4843, + "2705-type-import-link-dynamic/main.ts": 2701, + "2706-array-search-missing-needle.ts": 2539, + "2706-type-import-link-dynamic-success/main.ts": 2462, + "2707-array-search-huge-sparse.ts": 45317, + "2707-type-import-link-static-default-reexport/main.ts": 2670, + "2708-type-import-link-dynamic/main.ts": 2350, + "2720-array-read-parity.ts": 1959, + "2721-array-nested-missing-receiver.ts": 2561, + "2750-array-sparse-foundation.ts": 2117, + "2751-buffer-concat-sparse-list.ts": 1676, + "2752-array-optional-stores.ts": 2649, + "2753-array-optional-method-values.ts": 2199, + "2754-array-property-undefined.ts": 2439, + "2755-array-optional-literal-forwarding.ts": 2745, + "2780-array-parity-composition.ts": 7200, + "2781-array-missing-value-escape.ts": 2902, + "2782-array-sparse-callbacks.ts": 3115, + "2783-array-defaults-rest.ts": 2837, + "2784-array-reduce-missing-result.ts": 2800, + "2785-array-missing-destructuring.ts": 3065, + "2786-array-sparse-serialization.ts": 2409, + "2787-array-missing-string-arithmetic.ts": 2854, + "2788-array-missing-builtin-conversions.ts": 2132, + "2789-array-missing-optional-tostring.ts": 1804, + "2790-dynamic-import-self-name/main.ts": 2534, + "2791-record-optional-keys.ts": 2401, + "2792-record-optional-spread-entries.ts": 2956, + "2793-union-conversion-identity-capture.ts": 3189, + "2794-array-generic-callback-values.ts": 2738, + "2795-array-optional-typedarray-source.ts": 3311, + "2796-array-sparse-class-value-instanceof.ts": 2925, + "2800-array-expression-consumers.ts": 2649, + "2801-array-switch-consumers.ts": 2865, + "2820-array-missing-method-receivers.ts": 1887, + "2821-array-optional-numeric-comparisons.ts": 2197, + "2830-cjs-require-self-name/main.cjs": 2632, + "2840-finally-completions.ts": 3271, + "2841-using-sync.ts": 2134, + "2842-await-using.ts": 1855, + "2843-using-suppressed-error.ts": 1631, + "2844-node-disposable-resources.ts": 2377, + "2845-for-using-of.ts": 2775, + "2846-generator-using.ts": 2854, + "2850-async-generators-protocol.ts": 2671, + "2851-async-generators-values.ts": 2378, + "2852-timers-promises-interval.ts": 2688, + "2854-leading-bom-literals.ts": 2637, + "2855-searchparams-sort-complexity.ts": 1784, + "2856-dynamic-any-local-operators.ts": 2953, + "2857-branded-primitives.ts": 2640, + "2858-import-meta-resolve/main.mjs": 2725, + "2859-import-meta-resolve-dynamic/main.mjs": 1820, "2860-require-resolve/main.cjs": 2780, - "2861-readable-iterator-close.ts": 3207, - "2862-web-readable-async-iteration.ts": 3375, - "2863-class-async-iterator.ts": 2531, - "2864-array-class-method-receivers.ts": 1837, - "2865-cjs-process-alias.cjs": 2734, - "2866-dyn-switch.ts": 1772, - "2867-array-shift-length.js": 2411, - "2868-empty-record-promise-presence.js": 2531, - "2869-nullish-mixed-lazy.ts": 2433, - "2870-and-dropped-truthy-arm.ts": 2606, - "2871-optional-promise-to-unknown.ts": 2387, - "2872-unobserved-dynamic-event.ts": 2191, - "2873-optional-receiver-loop-lifetime.js": 2453, - "2874-nullable-callback-field-cycle.js": 2707, - "2875-evolving-let-read-before-write.js": 3668, - "2876-class-unknown-roundtrip.ts": 2817, - "2877-child-unknown-roundtrip.ts": 2660, - "2878-class-private-unknown.cjs": 4040, - "2880-cjs-module-graph/main.cjs": 2924, - "2890-crypto-static-utility.ts": 2261, - "2891-json-record-key-escapes.ts": 2705, - "2892-windows-console-unicode.ts": 2266, - "2893-child-stdin-duplex.ts": 3340, - "2894-child-stdin-backpressure.ts": 3131, - "2895-child-stdin-write-after-end.ts": 2384, - "2896-child-stdin-write-after-finish.ts": 2871, - "2897-child-stdin-destroy.ts": 2871, - "2898-child-stdin-spawn-failure.ts": 2549, - "2899-nested-mutual-recursion.ts": 2721, - "2900-float64array-static.ts": 2799, - "2900-void-conditional-arrow.ts": 2537, - "2901-bigint-core.ts": 2424, - "2902-bigint-bytes.ts": 2498, - "2903-bigint-storage.ts": 2708, - "2904-bigint-errors.ts": 2577, - "2905-bigint-edges.ts": 2439, - "2906-bigint-wasi-width.ts": 1786, - "2910-overloaded-callable-alias.ts": 2648, - "2911-execfile-callback.ts": 2191, - "2912-execfile-callback-options.ts": 2127, - "2912-promisify-fs-readfile.ts": 2460, - "2913-builtin-callable-alias.ts": 2481, - "2914-fixed-tuple-call-spread.ts": 2974, - "2915-path-function-values.ts": 2177, - "2916-path-function-values-cjs.ts": 2451, - "2917-builtin-callable-widths.ts": 2149, - "2918-builtin-callable-widths-cjs.ts": 2749, - "2919-fs-function-values.ts": 2635, - "2920-fs-promises-function-values.ts": 1999, - "2921-zlib-callbacks.ts": 2825, - "2922-dyn-bitwise-not.cjs": 2337, - "2923-cjs-factory-export/main.cjs": 2557, - "2930-union-receiver-calls.ts": 3403, - "2931-union-dynamic-field-call.js": 3002, - "2932-create-require-program/main.ts": 2467, - "2933-static-dynamic-import/main.ts": 2896, - "2934-static-import-stored.ts": 2404, - "2935-module-namespace-values/main.ts": 2608, - "2936-static-loose-equality.ts": 3140, - "2937-bitwise-hot-loop.ts": 2268, - "2938-fs-sync-overloads.cjs": 1857, - "2939-child-shell-text.ts": 3041, - "2940-date-parse.ts": 2483, - "2940-fsp-realpath.ts": 2587, - "2941-fsp-lstat-identity.ts": 2657, - "2941-process-exit-code.ts": 1743, - "2942-fsp-readdir-dirent.ts": 2911, - "2942-process-exit-code-override.ts": 1751, - "2943-process-exit-code-invalid.ts": 2583, - "2944-process-exit-code-wrap.ts": 2435, - "2945-url-origin-username.ts": 1863, - "2946-child-fork-ipc/main.ts": 3271, - "2946-url-port-hash.ts": 2587, - "2947-set-readonly-seeds.ts": 2456, - "2948-child-fork-eof-callback/main.ts": 3043, - "2948-stdio-callback-record.ts": 2421, - "2949-child-fork-default/main.mjs": 2736, - "2949-url-password.ts": 2744, - "2950-fs-descriptor-publication.ts": 2681, - "2951-math-static-analytic.ts": 3121, - "2952-math-static-remaining.ts": 2862, - "2953-http-header-validation.ts": 2956, - "2954-http-outgoing-headers.ts": 2822, - "2955-http-static-state.ts": 2955, - "2956-http-static-informational.ts": 3085, - "2957-http-static-response-controls.ts": 2273, - "2961-emitter-computed-names.cjs": 2632, - "2962-emitter-computed-optional.ts": 2051, - "2963-child-fork-dispatch/main.ts": 2809, - "2964-indexed-compound.ts": 2982, - "2966-numeric-array-read-fusion.ts": 2045, - "2967-numeric-array-read-lifetime.ts": 3048, - "2968-numeric-array-borrow.ts": 2914, - "2969-scalar-record-results.ts": 2620, - "2970-constant-numeric-tables.ts": 2078, - "2971-constant-table-modules/main.ts": 2587, - "2972-http-static-distinct-snapshots.ts": 2220, - "2972-math-receiver-borrow.ts": 3503, - "2973-http-static-agent.ts": 3163, - "2973-integer-bitwise-arithmetic.ts": 2756, - "2974-array-search-from-index.ts": 120636, - "2974-http-static-module-values.ts": 2696, - "2974-numeric-remainder.ts": 2784, - "2975-bigint-default-radix.ts": 1741, - "2975-http-static-client-controls.ts": 2204, - "2976-http-static-incoming-state.ts": 2949, - "2976-json-stringify-no-arguments.js": 2583, - "2977-http-static-incoming-timeout.ts": 2724, - "2977-nested-array-binding-defaults.js": 2760, - "2978-http-static-client-timeout.ts": 3060, - "2978-regexp-optional-subject.js": 2540, - "2979-array-search-mixed-needles.js": 2011, - "2979-http-static-server-timeout.ts": 2740, - "2980-http-static-server-timeout-default.ts": 2671, - "2980-string-position-arguments.js": 2246, - "2981-http-static-events.ts": 1817, - "2981-string-optional-positions.ts": 1836, - "2982-http-static-close-all.ts": 2421, - "2982-string-position-coercion-order.js": 3081, - "2983-http-static-close-idle.ts": 2006, - "2984-http-static-close-upgrade.ts": 2129, - "2985-http-static-message-destroy.ts": 2630, - "2986-http-static-client-socket-finish.ts": 1940, - "2987-http-static-timeout-errors.ts": 2737, - "2988-http-static-client-abort.ts": 2025, - "2989-http-static-response-timeout-handled.ts": 2928, - "2990-http-static-request-timeout-handled.ts": 2599, - "2991-array-indexed-equality.ts": 2993, - "2992-stream-rows.ts": 2556, - "2993-fiber-stack-lifetimes.ts": 3202, - "2994-fromcharcode-scalar.ts": 2833, - "2995-math-hyperbolic.ts": 2687, - "2996-math-near-one.ts": 2252, - "2997-math-integer-float.ts": 3190, - "300-if-else.ts": 2219, - "3001-caught-typeof-object.js": 2450, - "3002-promise-then-two.ts": 2484, - "301-while.ts": 2153, - "302-for.ts": 1918, - "3020-string-last-index-position.ts": 2434, - "3021-buffer-union-narrow-callback.ts": 2866, - "3022-string-affix-positions.ts": 2229, - "3023-string-affix-position-coercion.js": 3333, - "3024-string-number-narrow-for-in.ts": 2650, - "3025-string-search-position-coercion.js": 2447, - "3026-array-last-index-of.js": 2579, - "3027-array-last-index-of-records.ts": 2304, - "3028-string-search-dynamic-needle.js": 2564, - "3029-string-search-union-needle.js": 1657, - "303-break-continue.ts": 2442, - "3030-array-search-no-arg.js": 3027, - "3031-array-relative-index-coercion.js": 2940, - "3031-string-last-index-search-value.js": 2407, - "3032-string-search-value.js": 2579, - "3033-string-search-needle-coercion.js": 2232, - "3034-arguments-with-parameters.js": 2126, - "3035-string-positioned-search-value.js": 2449, - "3036-string-object-search-value.js": 1849, - "3037-number-nullish-conversion.js": 2478, - "3038-number-scalar-unions.ts": 1881, - "3039-string-repeat-count.js": 2372, - "304-compound-incdec.ts": 2266, - "3040-string-prototype-call.js": 2389, - "3041-string-prototype-object-call.js": 2679, - "3042-string-padding-coercion.js": 2923, - "3043-string-split-arguments.js": 3247, - "3044-array-slice-splice-positions.js": 2966, - "3045-array-indexed-mutation.js": 2960, - "3046-array-indexed-ref-identity.ts": 2358, - "3047-object-prototype-tag.js": 2265, - "3048-object-own-prototype.ts": 3162, - "3049-array-flat-depth.ts": 2904, - "305-truthiness.ts": 2588, - "3050-array-splice-insert.ts": 2779, - "3051-array-join-default.ts": 2220, - "3052-array-construction.ts": 2520, - "3053-array-construction-js.js": 2821, - "3054-dynamic-abort-record.ts": 2818, - "3055-default-builtin-nodenext/main.ts": 2044, - "3056-default-builtin-bundler/main.ts": 2874, - "3057-default-builtin-nodenext-dynamic/main.ts": 1912, - "3058-optional-array-chain-join.ts": 2986, - "3059-arguments-property-names.ts": 1856, - "3061-concurrent-fiber-stacks.ts": 2766, - "3062-runtime-optional-record-guards.ts": 3210, - "3063-runtime-optional-class-guards.ts": 2677, - "3064-installed-entry-imports/main.ts": 1867, - "3065-installed-entry-js/main.js": 2294, - "3066-source-debugging/main.ts": 2276, - "3067-cjs-function-export-require/main.cjs": 1963, - "3068-debug-variables/main.ts": 2578, - "3068-url-checked-identity.ts": 2207, - "3069-set-checked-seed.js": 2608, - "3069-tuple-spread.ts": 2969, - "3070-checked-object-accessors.js": 2496, - "3070-collection-views.ts": 2799, - "3071-checked-specialization-narrowing.js": 2715, - "3071-map-contextual-record-seeds.ts": 3095, - "3072-optional-union-receivers.ts": 2303, - "3072-weak-process-streams.js": 2885, - "3073-optional-callable-module.js": 2520, - "3073-union-keyed-reads.ts": 2905, - "3074-discriminated-intersections.ts": 2460, - "3075-recursive-contextual-literals.ts": 3335, - "3076-nullish-record-defaults.ts": 2894, - "3077-optional-union-array-reads.ts": 2500, - "3078-forward-mutable-captures.ts": 3097, - "3079-forward-mutable-tdz.ts": 2796, - "3080-exhaustive-never-bindings.ts": 2704, - "3081-recursive-array-destinations.ts": 2667, - "3082-forward-write-unwind.ts": 2538, - "3083-set-string-seed.ts": 2595, - "3084-record-own-membership.ts": 2879, - "3085-conditional-record-construction.ts": 2874, - "3086-error-constructor-options.ts": 2970, - "3087-error-options-lifetime.ts": 2675, - "3088-array-wide-callbacks.ts": 2690, - "3089-array-find-narrowing.ts": 2833, - "3090-json-discriminated-records.ts": 3066, - "3091-json-recursive-discriminants.ts": 2220, - "3092-number-union-predicates.ts": 2324, - "3093-error-option-data.ts": 2104, - "3094-json-replacer-traversal.ts": 2818, - "3095-json-reviver-traversal.ts": 2804, - "3096-json-callback-nonfinite.ts": 2753, - "3097-json-callback-reentrancy.ts": 2198, - "3098-json-callback-space.ts": 2839, - "3099-json-callback-functions.ts": 3102, - "3100-json-callback-holder.js": 2748, - "3101-json-callback-bytes.ts": 2087, - "3102-json-unknown-fields.ts": 2238, - "3103-scalar-record-nested-control-flow.ts": 2806, - "3104-native-analysis-tables-loops.ts": 2680, - "3105-nullable-collections.ts": 2913, - "3106-mixed-insertion-spreads.ts": 3159, - "3107-nullable-collection-lifetimes.ts": 3025, - "3108-for-await-sync-arrays.js": 3537, - "3108-identity-map-keys.ts": 3249, - "3109-destructuring-unknown-defaults.js": 2565, - "3109-identity-union-collections.ts": 3588, - "3110-builtin-error-families.ts": 2812, - "3110-identity-collection-mutation.ts": 2831, - "3111-identity-collection-cycles.ts": 2915, - "3112-union-switch-control-flow.ts": 2075, - "3113-union-array-removal.ts": 3111, - "3114-union-record-field-write.ts": 2886, - "3115-zlib-static-compression-levels.ts": 2667, - "3116-sequence-expression-lifetimes.ts": 2953, - "3117-unknown-switch-control-flow.ts": 2388, - "3118-recursive-union-spread.ts": 3091, - "3119-runtime-optional-spread.ts": 3563, - "3120-dynamic-property-descriptors.js": 2806, - "3120-generator-method-loop-bindings.ts": 2970, - "3120-ir-nonfinite-numbers.ts": 2756, - "3121-compound-assignment-values.js": 2784, - "3121-dynamic-accessor-descriptors.js": 2889, - "3122-dynamic-delete-descriptors.cjs": 2435, - "3122-dynamic-delete-descriptors.js": 2554, - "3122-js-async-class-methods.js": 2294, - "3123-destructured-global-tdz.js": 2755, - "3124-array-prototype-borrow.js": 2322, - "3125-regex-global-match.ts": 2783, - "3126-js-number-radix/main.ts": 2820, - "3127-spawn-runtime-options.ts": 3072, - "3128-regex-last-index.ts": 1823, - "3129-js-token-union/main.ts": 2769, - "3130-child-generic-streams.ts": 2981, - "3131-child-rest-listeners.js": 2627, - "3132-child-generic-stream-completion.ts": 5669, - "3133-text-decoder-streaming.ts": 2916, - "3134-fs-vectors.ts": 2782, - "3135-fs-file-handle-controls.ts": 3077, - "3136-fs-file-handle-options.ts": 3203, - "3137-fs-vector-callbacks.cjs": 3063, - "3138-array-large-storage.ts": 4112, - "3139-regex-native-deep-alternatives.ts": 26910, - "3140-array-dense-access.ts": 3431, - "3141-map-string-key-hash.ts": 3132, - "3150-integer-remainder.ts": 3203, - "3151-array-push-writes.ts": 3025, - "3170-number-conversion-fast-paths.ts": 2101, - "3171-method-census-data-writes.ts": 3216, - "400-fib.ts": 2634, - "401-mutual-recursion.ts": 2541, - "402-string-functions.ts": 2183, - "403-void-and-params.ts": 2239, - "4030-runtime-object-truthiness.ts": 2407, - "4031-event-emitter-long-tuples.ts": 2624, - "4032-inline-rc-fast-paths.ts": 3924, - "404-rc-stress.ts": 2608, - "4040-adapted-function-identity.ts": 3296, - "4041-adapted-function-properties.ts": 2613, - "4042-object-is-union-subclass.ts": 2694, - "4043-nonnull-missing-element.ts": 2672, - "4044-adapted-callback-modules/main.ts": 2828, - "4045-optional-element-projections.ts": 2963, - "4046-captured-parameter-values.ts": 3070, - "4047-captured-parameter-rebinding.ts": 2848, - "405-params-optional.ts": 1924, - "406-params-defaults.ts": 1978, - "4060-cycle-shared-instance/main.ts": 2198, - "4060-stack-union-reads.ts": 3362, - "4061-cycle-evaluation-order/main.ts": 2399, - "4061-nullable-ref-fields.ts": 4246, - "4062-cycle-tdz-read/main.ts": 2254, - "4063-cycle-hoisted-calls/main.ts": 2402, - "4064-cycle-classes-after-init/main.ts": 2048, - "4065-cycle-init-throws/main.ts": 2640, - "4066-cycle-top-level-tdz/main.ts": 2245, - "4067-dynamic-import-cycle-tdz/main.ts": 2515, - "407-params-rest.ts": 2698, - "408-params-ctors-inheritance.ts": 2092, - "4080-optional-read-presence-tests.ts": 2936, - "4080-string-index-bounds.ts": 2863, - "4081-hoisted-function-tdz.ts": 2788, - "4081-string-index-unproven.ts": 2335, - "4082-hoisted-function-tdz-uncaught.ts": 1949, - "4083-dynamic-import-failure/main.ts": 2717, - "4084-record-optional-field-copies.ts": 2527, - "4085-generator-loop-close.ts": 2591, - "4086-optional-capture-defaults.ts": 2395, - "4088-class-static-tdz.ts": 2363, - "4089-class-wrapper-tdz.js": 2402, - "409-params-async-generics.ts": 2098, - "4090-computed-key-tdz.js": 2605, - "4091-class-evaluation-deferred.ts": 2199, - "410-params-rc-stress.ts": 2565, - "4140-array-callback-split-visit.ts": 4386, - "4141-array-callback-ambient-receiver/main.ts": 2299, - "4142-array-callback-factories.ts": 3208, - "4143-union-refinement-intersection-arms.ts": 2685, - "420-dead-strip-modules/main.ts": 2527, - "4290-acyclic-immutable-trees.ts": 3246, - "4291-acyclic-constructor-escapes.ts": 2909, - "4292-acyclic-readonly-bypass.ts": 3024, - "4293-acyclic-dynamic-writes.ts": 2956, - "4294-console-line-sizes.ts": 5873, - "4295-console-template-parts.ts": 2597, - "4296-console-glued-long-parts.ts": 2868, - "4410-inline-object-allocation.ts": 3093, - "4411-nullable-ref-values.ts": 4434, - "4412-nullable-record-values.ts": 3635, - "4413-cycle-old-late-death.ts": 2102, - "4413-nullable-null-and-undefined.ts": 2783, - "4414-cycle-old-weak-keys.ts": 2960, - "4414-nullable-string-values.ts": 3874, - "4415-identity-borrowed-operands.ts": 2087, - "4415-nullable-container-values.ts": 3581, - "4431-exception-pending-contexts.ts": 3035, - "4432-checked-downcast-failures.ts": 3246, - "4451-int32-field-storage.ts": 3457, - "4452-int32-field-dynamic-writes.ts": 3861, - "4453-int32-closed-locals.ts": 2280, - "4471-unassigned-field-reads.ts": 3309, - "4476-string-ascii-header-fact.ts": 2846, - "4477-buffer-write-ascii-runs.ts": 2125, - "4478-string-large-index-reads.ts": 3334, - "4481-array-optional-push.ts": 3059, - "4482-instanceof-static-class-optional.ts": 2994, - "4483-array-splice-statement.ts": 3331, - "4484-array-length-reset.ts": 2664, - "4485-array-splice-optional-item.ts": 2526, - "4486-array-field-element-borrows.ts": 2341, - "4487-array-literal-optional-values.ts": 2248, - "4520-optional-guard-narrowing.ts": 2281, - "4521-array-callback-element-presence.ts": 4131, - "4522-captured-parameter-lifetimes.ts": 2903, - "4523-array-pop-removal.ts": 2536, - "4524-array-callback-supertype-params.ts": 3251, - "4541-nullable-wrap-borrows.ts": 2882, - "4542-preserving-call-borrows.ts": 3130, - "4543-walk-borrowed-locals.ts": 3034, - "4544-checked-cast-aliases.ts": 2574, - "4545-borrowed-returns.ts": 3551, - "4546-rebound-borrowed-params.ts": 2811, - "4560-field-store-borrowed-receivers.ts": 3116, - "4561-virtual-borrowed-parameters.ts": 3237, - "4562-string-self-concat-move.ts": 2061, - "4563-array-read-projection-receivers.ts": 2512, - "4564-literal-choice-strings.ts": 2795, - "4568-for-of-hole-elements.ts": 2661, - "4569-shorthand-missing-elements.ts": 3064, - "4570-map-lookup-default-record.ts": 2901, - "4591-weak-keys-union-records.ts": 2778, - "4592-cycle-const-from-import/main.ts": 2529, - "4593-weak-keys-empty-objects.ts": 2806, - "4594-union-class-field-mixed-types.ts": 3153, - "4595-union-class-field-optional-receiver.ts": 1997, - "4596-error-subclass-int-fields.ts": 2606, - "4596-tdz-nullable-forward-capture.ts": 3433, - "4597-callback-field-method-name.ts": 2519, - "4598-live-array-ref-element-access.ts": 4363, - "4598-map-set-clear-reuse.ts": 4118, - "4598-os-available-parallelism.ts": 2474, - "4598-publish-constructor-stores.ts": 2635, - "4599-construction-only-field-borrows.ts": 3147, - "4600-construction-only-fields-dynamic-writes.ts": 2319, - "4601-cycle-holders.ts": 2904, - "4602-constructor-escape-order.ts": 2703, - "4610-array-ext-dyn-refresh.ts": 3049, - "4620-optional-chain-borrowed-receivers.ts": 3006, - "4621-edge-preserving-callees/main.ts": 2464, - "4630-contract-int32.ts": 2586, - "4630-reference-write-call-arguments.ts": 2892, - "4631-contract-nothrow.ts": 2842, - "4631-reference-write-aliases.ts": 2922, - "4632-contract-immutable.ts": 2582, - "4632-reference-write-loops-and-walks.ts": 2399, - "4633-contract-program-lifetime.ts": 3449, - "4633-reference-write-callbacks-returns.ts": 2678, - "4634-contract-program-lifetime-publish.ts": 4573, - "4634-optional-element-borrowed-returns.ts": 2111, - "4640-bitwise-identity-ranges.ts": 2671, - "4641-nothrow-try-catch.ts": 2141, - "4642-acyclic-contract.ts": 2859, - "4650-stale-property-narrowing.ts": 2759, - "4651-map-string-get-entry.ts": 3114, - "4652-stale-closure-binding-narrowing.ts": 3092, - "4660-typed-rest-spread-forwarding.ts": 2331, - "4671-scoped-array-read-borrows.ts": 2575, - "4690-cycle-bypass-long-lived.ts": 4228, - "4691-publish-constructor-self-alias.ts": 2964, - "4692-borrowed-result-mutating-callees.ts": 2766, - "4720-cold-throw-paths.ts": 3228, - "4721-integer-induction-numbers.ts": 2974, - "4730-touint32-large-magnitudes.ts": 2198, - "4731-string-mixed-ascii-slices.ts": 3160, - "4732-buffer-write-utf16le-runs.ts": 3120, - "4733-typed-array-read-compares.ts": 2977, - "4750-map-string-absent-filter.ts": 3464, - "4751-string-search-multibyte-needles.ts": 2454, - "4795-published-untyped-member-reads.ts": 3340, - "4806-uint32-loop-accumulators.ts": 2740, - "4810-union-identity-retags.ts": 4045, - "4811-union-retag-checked-cast.ts": 2819, - "4812-int32-wrapping-sums.ts": 3339, - "4813-typed-array-read-equality-bindings.ts": 3997, - "4820-sort-comparator-lent-arguments.ts": 2782, - "4821-callback-lent-arguments.ts": 3485, - "4822-stable-capture-reads.ts": 2483, - "4823-array-store-receivers.ts": 2858, - "4830-typed-teardown-chains.ts": 3292, - "4831-cycle-bypass-probe-windows.ts": 3738, - "4840-array-hole-free-prefix.ts": 3550, - "4860-int32-local-storage.ts": 3699, - "4870-array-pipeline-fusion.ts": 6694, - "4871-array-pipeline-analytics.ts": 4274, - "4872-array-pipeline-temporaries.ts": 7763, - "4875-stream-consumers-array-buffer.ts": 3017, - "4876-array-buffer-inspect.ts": 2569, - "4880-await-call-turn-order.ts": 2784, - "4881-await-call-exceptions.ts": 2757, - "4882-await-call-request-chain.ts": 2151, - "4883-await-values-and-hops.ts": 2520, - "4884-await-call-unhandled-sibling.ts": 2379, - "4885-await-call-checkpoints.ts": 2128, - "4890-regex-literal-sites.ts": 3484, - "4891-regex-plain-text-patterns.ts": 2502, - "4892-regex-exec-read-captures.ts": 3120, - "4900-bigint-division-cross-check.ts": 3532, - "4900-console-instances.ts": 3723, - "4901-bigint-radix-text.ts": 3090, - "4901-console-instance-errors.ts": 3055, - "4902-bigint-compound-assign.ts": 2449, - "4903-bigint-fixed-width.ts": 2818, - "4904-bigint-large-products.ts": 2953, - "4905-bigint-fixed-width-fusion.ts": 3047, - "4906-bigint-small-value-trees.ts": 3132, - "4907-bigint-random-trees.ts": 4068, - "4910-hole-free-prefix-upkeep.ts": 3149, - "4920-string-literal-switch.ts": 4588, - "4930-typed-record-reflection.ts": 4183, - "4931-typed-data-generic-walks.ts": 4062, - "4932-typed-json-unknown.ts": 3239, - "4940-async-return-adoption.ts": 2845, - "4941-async-return-adoption-try.ts": 3156, - "4942-async-return-adoption-values.ts": 3620, - "4943-async-return-adoption-interleaved.ts": 2831, - "4950-regex-compiled-captures.ts": 5723, - "4951-regex-compiled-last-index.ts": 3059, - "4952-regex-compiled-anchors-classes.ts": 2426, - "4953-regex-compiled-backtracking.ts": 3365, - "4954-regex-log-report.ts": 3193, - "4955-regex-compiled-differential.ts": 24862, - "4970-int32-call-abi.ts": 2508, - "4980-generator-early-exit-finally.ts": 2836, - "4981-generator-delegation-iterables.ts": 4874, - "4982-generator-sent-values.ts": 2242, - "4983-generator-reuse-iterables.ts": 3066, - "4984-generator-exceptions.ts": 2959, - "4985-generator-lazy-pipeline.ts": 2708, - "4986-generator-default-channels.ts": 3119, - "4987-generator-closures-methods.ts": 2675, - "4988-generator-async-interplay.ts": 2141, - "4989-generator-step-drives.ts": 4185, - "4990-lone-surrogates.ts": 3319, - "4991-lone-surrogate-halves.ts": 2978, - "4992-lone-surrogate-search.ts": 2759, - "4993-lone-surrogate-json.ts": 2994, - "4994-lone-surrogate-uri.ts": 2786, - "4995-lone-surrogate-regex.ts": 3169, - "4996-lone-surrogate-stdio.ts": 2201, - "4997-lone-surrogate-stdout-patch.js": 2754, - "4998-lone-surrogate-buffers.ts": 3060, - "4999-lone-surrogate-io.ts": 4219, - "500-array-basics.ts": 2646, - "501-array-push-pop.ts": 2128, - "5010-lifetime-region-generations.ts": 2950, - "5011-lifetime-region-outside-references.ts": 2816, - "5012-lifetime-region-shapes.ts": 3847, - "5013-lifetime-region-async.ts": 3198, - "5014-lifetime-region-nesting.ts": 2741, - "5015-lifetime-region-publish.ts": 2749, - "5016-lifetime-region-weak-keys.ts": 2541, - "5017-lifetime-region-cycles.ts": 2989, - "5018-lifetime-region-hierarchies.ts": 3642, - "5019-lifetime-region-values.ts": 2516, - "502-array-for-of.ts": 2614, - "5020-lifetime-region-large.ts": 3840, - "5021-lifetime-region-opaque-holders.ts": 2702, - "5022-lifetime-region-workers.ts": 3127, - "503-array-functions.ts": 2610, - "504-array-rc-stress.ts": 2247, - "5040-promise-all-race-turns.ts": 3415, - "5041-promise-all-turns.js": 5156, - "5042-async-generator-return-promise.ts": 3112, - "5043-async-generator-return-forms.ts": 3029, - "5045-generator-delegation-typed-next.ts": 3463, - "5046-generator-delegation-return-throw.ts": 2606, - "5047-generator-delegation-iterables-typed.ts": 4378, - "5048-generator-delegation-async.ts": 4885, - "5049-generator-delegation-edges.ts": 2538, - "5050-inherited-in-membership.ts": 3142, - "5051-typed-record-prototypes.ts": 3140, - "5052-class-for-in-unknown.ts": 2653, - "5053-json-replacer-arrays.ts": 4713, - "5054-required-field-delete.ts": 2632, - "5055-object-create-record-literal.ts": 2065, - "5056-generator-in-unknown.ts": 3903, - "5060-child-fork-exit-after-messages/main.ts": 2572, - "5061-child-fork-local-disconnect/main.ts": 3248, - "5062-child-fork-message-before-eof/main.ts": 4732, - "5065-global-increment-initializers.ts": 3985, - "5066-global-compound-updates.ts": 2635, - "5067-global-updates-in-functions.ts": 2589, - "5068-global-var-let-const-updates.ts": 2726, - "5069-global-update-modules/main.ts": 2372, - "5070-optional-read-bindings.ts": 2144, - "5071-optional-chain-scalars.ts": 2192, - "5072-optional-nullish-ternary.ts": 2587, - "5073-optional-results.ts": 2727, - "5074-optional-pop-shift.ts": 2568, - "5075-optional-reads-unchecked/main.ts": 4996, - "5076-optional-escapes.ts": 2735, - "5080-boolean-array-storage.ts": 5236, - "5081-boolean-array-dynamic-views.ts": 2815, - "5085-promise-allsettled-turns.ts": 3332, - "5086-promise-any-turns.ts": 3297, - "5087-promise-allsettled-any-dyn.js": 3767, - "5088-promise-combinator-thenables.js": 4248, - "5089-promise-settled-records.ts": 3528, - "5090-generator-finished-result-value.ts": 2964, - "5091-generator-delegation-arrays-strings.ts": 2768, - "5092-generator-delegation-sets-maps.ts": 3565, - "5095-console-count-group.ts": 2753, - "5096-console-assert-dir.ts": 4648, - "5097-console-table.ts": 2620, - "5098-console-time-warnings.ts": 6302, - "5099-console-methods-dynamic.cjs": 2644, - "510-array-map-filter-foreach.ts": 2991, - "511-array-indexof-includes.ts": 2657, - "5110-var-hoisting-early-reads.ts": 2806, - "5111-var-hoisting-closures-classes.ts": 2117, - "5112-var-hoisting-function-scope.ts": 2614, - "5113-tdz-let-const-class.ts": 3154, - "5114-var-hoisting-js.js": 4536, - "5115-regex-replace-compiled.ts": 2608, - "5116-regex-capture-reads.ts": 3064, - "5117-regex-log-report-spans.ts": 3404, - "5118-regex-replace-char-chains.ts": 2444, - "512-array-join-chains.ts": 2376, - "5120-frame-records.ts": 2615, - "5121-frame-objects.ts": 2616, - "5122-frame-result-slots.ts": 2622, - "5123-frame-reassigned.ts": 2564, - "5124-frame-objects-control.ts": 3061, - "5125-frame-mutating-methods.ts": 2527, - "5126-frame-returned-params.ts": 2697, - "5127-frame-loop-bindings.ts": 2824, - "513-array-methods-rc-stress.ts": 2641, - "5130-event-target-dispatch.js": 3752, - "5131-event-target-listener-errors.js": 3003, - "5132-event-target-typed.ts": 3341, - "5133-abort-signal-event-target.ts": 3302, - "5134-events-error-monitor.ts": 2664, - "5135-event-target-warnings.js": 2890, - "5136-events-error-monitor-string-name.ts": 2380, - "5137-events-error-monitor-computed-name.js": 3077, - "5138-events-error-monitor-shared-listener.ts": 2001, - "5139-event-target-listener-throw.ts": 1965, - "514-array-find-some-every.ts": 2998, - "5140-os-host-facts.ts": 3248, - "5141-os-constants.ts": 2402, - "5142-path-parse-format.ts": 2850, - "5145-path-matches-glob-basics.ts": 2904, - "5146-path-matches-glob-braces.ts": 4866, - "5147-path-matches-glob-extglob.ts": 2653, - "5148-path-matches-glob-classes.ts": 2765, - "5149-path-matches-glob-win32.ts": 2754, - "515-array-flatmap.ts": 3015, - "5150-small-array-inline-slots.ts": 3276, - "5155-cycle-header-types.ts": 3561, - "516-array-reduce.ts": 3293, - "5160-field-callback-receiver.ts": 3482, - "5161-field-callback-receiver-escape.ts": 2665, - "5162-field-method-reference-receiver.ts": 2779, - "5163-field-callback-receiver.cjs": 2875, - "5164-worker-field-callback-receiver.ts": 3147, - "5165-interface-method-dispatch.ts": 2641, - "5166-interface-method-values.ts": 4036, - "5167-interface-method-order.ts": 3679, - "5168-interface-method-hierarchy.ts": 4400, - "5169-interface-method-chains.ts": 2934, - "517-array-hof-index-args.ts": 2918, - "5170-string-scan-eviction.ts": 2885, - "5171-lone-surrogate-replace-seams.ts": 3463, - "5175-forin-typed-as-unknown.ts": 3333, - "5176-object-keys-typed-as-unknown.ts": 3148, - "5177-forin-dynamic-objects.ts": 2926, - "5178-forin-keys-js.js": 2135, - "5179-forin-typed-shapes.ts": 3197, - "518-array-sort.ts": 2699, - "518-promise-void-union-callbacks.ts": 1929, - "5180-interface-arrays.ts": 3267, - "5181-interface-array-methods.ts": 7443, - "5182-interface-array-boundaries.ts": 3225, - "5183-interface-property-reads.ts": 3018, - "5184-interface-property-objects.ts": 3161, - "5185-util-inherits.cjs": 3120, - "5186-util-inherits.ts": 3012, - "5187-util-deprecate.cjs": 3705, - "5188-util-deprecate-flags.cjs": 2866, - "5189-util-deprecate.ts": 2361, - "519-array-from-length.ts": 3817, - "519-promise-union-await-values.ts": 2296, - "5190-util-callbackify.cjs": 3512, - "5191-util-callbackify.ts": 2893, - "5192-util-debuglog.cjs": 3224, - "5193-util-debuglog.ts": 4346, - "5194-process-emit-warning-tick.cjs": 2199, - "5195-object-entries-typed-as-unknown.ts": 2787, - "5196-object-values-typed-as-unknown.ts": 2984, - "5197-object-entries-dynamic-objects.ts": 3371, - "5198-object-entries-values-js.js": 2819, - "5199-object-entries-identity.ts": 3133, - "520-map-basics.ts": 2629, - "5200-array-sort-keyed-numbers.ts": 3636, - "5201-array-sort-keyed-records.ts": 3559, - "5202-array-sort-keyed-lookalikes.ts": 3148, - "5203-typed-array-sort-default.ts": 6169, - "5204-typed-array-sort-comparators.ts": 4152, - "521-map-number-keys.ts": 2529, - "5210-process-memory-usage.ts": 3044, - "5211-process-identity.ts": 2815, - "5212-perf-marks-measures.ts": 2925, - "5213-perf-timing-errors.ts": 3203, - "5214-perf-entry-inspect.ts": 3164, - "5215-perf-timing-validation.js": 4384, - "5216-perf-spread-arguments.js": 3711, - "5217-perf-spread-typed.ts": 3324, - "522-map-foreach.ts": 2813, - "5220-url-parse-warnings.ts": 2906, - "5221-url-parse-shapes.ts": 2400, - "5222-url-parse-hosts.ts": 2972, - "5223-url-parse-query.ts": 2190, - "5224-url-format-objects.ts": 2681, - "5225-url-format-whatwg.ts": 2885, - "5226-url-resolve.ts": 2184, - "5227-url-legacy-dependency/main.ts": 3291, - "5228-url-domain-conversion.ts": 2624, - "5229-url-legacy-errors.cjs": 3698, - "523-map-ref-values.ts": 2904, - "5230-inline-union-fields.ts": 2318, - "5231-inline-union-field-kinds.ts": 3614, - "5232-inline-union-fields-dynamic.ts": 3502, - "5233-inline-union-fields-shapes.ts": 3338, - "5234-inline-union-class-fields.ts": 4285, - "5235-inline-union-fields-publish.ts": 2840, - "5236-inline-union-unchecked-reads.ts": 1998, - "5237-inline-union-frame-objects.ts": 2284, - "524-map-cycles.ts": 3272, - "5240-event-target-subclass.ts": 3668, - "5241-event-subclass.ts": 4201, - "5242-event-target-subclass-hierarchy.ts": 2218, - "5243-event-target-subclass-dynamic.ts": 2613, - "5244-event-target-subclass.js": 3715, - "5245-event-target-subclass-signals.ts": 3077, - "5246-event-target-subclass-listener-throw.ts": 2770, - "5247-event-target-subclass-lifetimes.ts": 3603, - "5248-event-target-subclass-warnings.ts": 2743, - "5249-event-target-subclass-unconstructed.ts": 1632, - "525-map-rc-stress.ts": 3853, - "526-set-basics.ts": 1919, - "5260-record-unknown-identity.ts": 2937, - "5261-optional-field-identity.ts": 2860, - "5262-record-unknown-many.ts": 2984, - "527-set-foreach.ts": 3210, - "5270-url-parse-no-deprecation.cjs": 3158, - "5271-url-parse-throw-deprecation.cjs": 2975, - "5275-http-end-callback-order.ts": 2389, - "5276-style-text-hex-colors.cjs": 2820, - "5277-fs-read-file-buffer-option.cjs": 2710, - "5278-host-null-bytes.cjs": 2114, - "5279-search-params-null-init.cjs": 1781, - "528-set-seeded.ts": 2610, - "5285-blob-construct.ts": 2681, - "5286-blob-readers.ts": 3079, - "5287-blob-inspect.ts": 3817, - "5288-blob-validation.js": 3803, - "5289-blob-read-order.ts": 2829, - "529-map-seeded.ts": 2574, - "5290-blob-dynamic.js": 2354, - "5295-readline-promises-question.ts": 3709, - "5296-readline-lines.ts": 2760, - "5297-readline-callback-events.cjs": 2472, - "5298-readline-abort.cjs": 2757, - "5299-readline-stdin.ts": 3087, - "530-record-arrays.ts": 2787, - "5300-stream-add-abort-signal.ts": 3027, - "5301-stream-add-abort-signal-errors.cjs": 2717, - "5302-stream-promises-options.ts": 3120, - "5303-stream-finished-callback-opts.cjs": 2705, - "5305-json-stringify-typed-arrays.ts": 3153, - "531-record-array-hofs.ts": 2832, - "5315-map-iterate-after-front-deletes.ts": 3911, - "5316-map-lru-eviction.ts": 2782, - "5317-map-iterate-while-mutating.ts": 2769, - "5318-set-iterate-after-deletes.ts": 2291, - "5319-map-iteration-values.ts": 2689, - "532-record-arrays-rc-stress.ts": 2437, - "5320-map-iterate-across-compaction.ts": 3526, - "5325-record-boundary-returns.ts": 1763, - "5326-record-boundary-params.ts": 2634, - "5327-record-boundary-identity.ts": 2323, - "5328-record-boundary-open.ts": 2561, - "5329-record-boundary-recursion.ts": 2680, - "533-array-element-cycles.ts": 2932, - "5330-record-boundary-fields.ts": 1977, - "5331-record-boundary-methods.ts": 3130, - "5333-record-boundary-nullable-argument.ts": 3012, - "5334-fetch-mentions-without-calls.ts": 2532, - "5335-top-level-await-pending-exit-code.ts": 1919, - "5336-top-level-await-pending-exit-listener.ts": 3734, - "5337-dc-subscriber-throw-next-tick.cjs": 2604, - "5338-js-tdz-function-element.js": 2562, - "5339-array-buffer-constructor.js": 2502, - "534-record-width-subtyping.ts": 2615, - "5345-object-values-for-of-unknown.ts": 3008, - "5346-object-values-length-dynamic.ts": 3055, - "5347-object-values-loop-control.ts": 3030, - "5348-object-values-for-of-js.js": 2732, - "5349-object-entries-values-optional.ts": 2083, - "535-object-statics.ts": 2312, - "5350-object-values-wide-record.ts": 2790, - "5351-object-values-methods-index.ts": 3275, - "536-map-seed-array.ts": 2712, - "5360-array-sort-comparator-calls.ts": 3175, - "5361-array-sort-inconsistent-comparators.ts": 3161, - "5362-array-sort-comparator-throws.ts": 2896, - "5363-array-sort-comparator-writes.ts": 6813, - "5364-array-sort-union-and-boolean-elements.ts": 8514, - "5365-array-sort-dynamic-values.js": 2402, - "537-map-iter-drains.ts": 2867, - "538-map-set-for-of.ts": 2756, - "5390-interface-loop-element-lifetimes.ts": 2999, - "5391-interface-dispatch-hierarchies.ts": 3168, - "5392-interface-receiver-bindings.ts": 2015, - "5393-interface-array-holes.ts": 2530, - "5394-interface-implementers.ts": 2992, - "5395-locale-compare-ascii.ts": 3026, - "5396-locale-compare-latin.ts": 3119, - "5397-locale-compare-sorting.ts": 3179, - "540-tuples-basics.ts": 2776, - "5400-json-parse-typed-keys.ts": 3131, - "5405-buffer-arraybuffer-window.ts": 2729, - "5406-util-types-keys.ts": 2747, - "5407-http-header-field-limit.ts": 2785, - "5408-http-unread-request-end.ts": 3226, - "5409-http-held-request-body.ts": 3218, - "541-ref-array-json.ts": 3099, - "5410-inspect-class-values.ts": 1875, - "5411-inspect-class-hierarchies.ts": 2712, - "5412-inspect-dynamic-collections.ts": 2792, - "5413-inspect-class-values.cjs": 3020, - "542-union-element-arrays.ts": 2846, - "5430-dyn-number-string-boxes.ts": 3059, - "5435-crypto-hash-algorithms.ts": 2030, - "5436-crypto-hash-names.js": 2098, - "5437-crypto-hmac-algorithms.ts": 2699, - "5438-crypto-kdf-digests.ts": 2741, - "5439-crypto-kdf-callbacks.js": 2685, - "5440-crypto-hash-encodings.js": 4004, - "5441-crypto-hash-output-length.ts": 3671, - "5442-crypto-random-values.ts": 3947, - "5443-crypto-random-arguments.js": 3548, - "5444-crypto-timing-safe-equal.js": 3213, - "5445-crypto-secret-key-objects.ts": 3869, - "5445-stream-buffer-chunks.ts": 2881, - "5446-crypto-secret-key-errors.js": 3035, - "5446-stream-buffer-chunks-dynamic.js": 2859, - "5447-fs-readdir-order.ts": 2639, - "5448-fs-glob-patterns.ts": 2837, - "5449-fs-glob-options.ts": 3025, - "5450-fs-glob-validation.js": 3327, - "5451-fs-promises-glob.ts": 3059, - "5452-fs-glob-callback.ts": 2936, - "5455-power-of-two-remainder.ts": 2837, - "5456-remainder-large-integer-dividends.ts": 2786, - "5457-lcg-modulus.ts": 1426, - "5458-fractional-remainder.ts": 1394, - "5460-locale-compare-contractions.ts": 2979, - "5461-locale-compare-equivalents.ts": 2661, - "5462-locale-compare-probe-sweep.ts": 3400, - "5463-locale-compare-combining-letters.ts": 2733, - "5464-locale-compare-fractions.ts": 2833, - "5470-strip-vt-osc.cjs": 2497, - "5471-strip-vt-csi.cjs": 2651, - "5472-strip-vt-random.cjs": 2375, - "5473-strip-vt-unterminated.cjs": 3521, - "5474-strip-vt-surrogates.ts": 2534, - "5540-fs-callback-forms.cjs": 3233, - "5541-fs-stats-fields.cjs": 3564, - "5542-fs-promises-parity.cjs": 3940, - "5543-fs-promises-typed.ts": 1655, - "5544-fs-opendir.cjs": 3024, - "5545-fs-options-encoding.cjs": 2994, - "5548-fs-callback-arguments.cjs": 4041, - "5549-fs-callback-option-checks.cjs": 3738, - "5550-locale-compare-ascii-words.ts": 2509, - "5551-array-search-references.ts": 3475, - "5552-string-appends.ts": 2703, - "5553-string-append-char-code.ts": 2870, - "5565-fs-dirent-untyped.mjs": 2600, - "5566-fs-dirent-dyn-boundaries/main.mjs": 2607, - "5585-record-results-inlined.ts": 2922, - "5595-net-ref-unref.ts": 2593, - "5644-net-socket-addresses.ts": 2220, - "5645-net-socket-callbacks.ts": 2442, - "5646-net-server-connections.ts": 2389, - "5647-net-is-ip.ts": 2776, - "5648-dns-lookup-forms.ts": 2992, - "5649-dns-service-servers.ts": 2712, - "5650-dns-lookup-errors-untyped.cjs": 2822, - "5800-broadcast-channel.ts": 2899, - "5801-broadcast-channel.cjs": 2727, - "5802-broadcast-channel-workers.ts": 2809, - "5803-worker-environment-data.ts": 2748, - "5804-worker-environment-data.cjs": 2469, - "5805-worker-receive-message-parent-port.ts": 2839, - "5806-worker-resource-limits.ts": 3153, - "5807-worker-error-values.ts": 3304, - "5808-worker-unhandled-rejections.ts": 3430, - "5809-process-exit-and-rejections.cjs": 3294, - "5810-mock-method-records.ts": 3976, - "5811-mock-tracker-restore.ts": 3698, - "5812-dyn-box-async-fields.ts": 3522, - "5813-mock-timers-module.ts": 3766, - "5814-mock-timers-date.ts": 3760, - "5831-util-types-brands.ts": 3347, - "5832-util-types-brands-dynamic.js": 3250, - "5833-url-search-params-iterators.ts": 2678, - "5834-url-search-params-iterators-dynamic.mjs": 2696, - "5835-util-format-integer-specifiers.ts": 2534, - "5836-url-search-params-tuple-pairs.ts": 2118, - "5837-util-format-json-circular.ts": 3117, - "5838-qs-stringify-bigint.ts": 2207, - "5841-json-typed-dyn-members.ts": 5467, - "5842-qs-parse-null-prototype.ts": 1774, - "5843-qs-parse-null-prototype-js.mjs": 1559, - "5844-date-unknown-values.ts": 3969, - "5857-regexp-unicode-sets.ts": 3350, - "5858-disposable-stack.ts": 3050, - "5859-disposable-stack-js.js": 3100, - "5870-worker-thread-names.ts": 3106, - "5871-worker-untransferable.cjs": 2786, - "5874-worker-stdio-streams.ts": 2839, - "5875-worker-stdout-pipe.cjs": 2740, - "5879-worker-stdin.ts": 3158, - "5881-inspect-weak-collections.ts": 2615, - "5882-string-decoder-runtime-encoding.ts": 2541, - "5883-os-set-priority.ts": 2380, - "5884-inspect-layout-options.ts": 3108, - "5886-inspect-dates.ts": 2559, - "5887-inspect-typed-arrays.ts": 3448, - "5888-inspect-sorted.ts": 2998, - "5900-float16-array.ts": 3000, - "5901-float16-array-js.js": 2900, - "5904-async-generator-completion.ts": 2950, - "5905-array-from-async-generator.ts": 2750, - "5906-array-from-async-generator-js.js": 2950, - "5910-set-prototype-of-primitive-object-kinds.ts": 2719, - "5911-dyn-class-to-primitive.ts": 2850, - "5912-dyn-class-conversion-methods.ts": 3251, - "5913-prototype-type-error-messages.ts": 2484, - "5914-dyn-in-class-prototype-members.ts": 3169, - "5915-dyn-error-string-conversions.ts": 3200, - "5922-matchall-groups-callbacks.ts": 3650, - "5923-matchall-groups-js.js": 3970, - "5939-js-let-object-reassigned-wider.js": 1945, + "2861-readable-iterator-close.ts": 3397, + "2862-web-readable-async-iteration.ts": 3414, + "2863-class-async-iterator.ts": 2684, + "2864-array-class-method-receivers.ts": 2886, + "2865-cjs-process-alias.cjs": 2940, + "2866-dyn-switch.ts": 2042, + "2867-array-shift-length.js": 1841, + "2868-empty-record-promise-presence.js": 1948, + "2869-nullish-mixed-lazy.ts": 2998, + "2870-and-dropped-truthy-arm.ts": 1701, + "2871-optional-promise-to-unknown.ts": 2708, + "2872-unobserved-dynamic-event.ts": 1982, + "2873-optional-receiver-loop-lifetime.js": 2129, + "2874-nullable-callback-field-cycle.js": 2941, + "2875-evolving-let-read-before-write.js": 2038, + "2876-class-unknown-roundtrip.ts": 3117, + "2877-child-unknown-roundtrip.ts": 2838, + "2878-class-private-unknown.cjs": 2816, + "2880-cjs-module-graph/main.cjs": 2623, + "2890-crypto-static-utility.ts": 3216, + "2891-json-record-key-escapes.ts": 3136, + "2892-windows-console-unicode.ts": 2711, + "2893-child-stdin-duplex.ts": 2652, + "2894-child-stdin-backpressure.ts": 3049, + "2895-child-stdin-write-after-end.ts": 2813, + "2896-child-stdin-write-after-finish.ts": 1947, + "2897-child-stdin-destroy.ts": 3101, + "2898-child-stdin-spawn-failure.ts": 3024, + "2899-nested-mutual-recursion.ts": 2834, + "2900-float64array-static.ts": 2695, + "2900-void-conditional-arrow.ts": 2641, + "2901-bigint-core.ts": 2912, + "2902-bigint-bytes.ts": 2182, + "2903-bigint-storage.ts": 2897, + "2904-bigint-errors.ts": 2971, + "2905-bigint-edges.ts": 2917, + "2906-bigint-wasi-width.ts": 2941, + "2910-overloaded-callable-alias.ts": 2710, + "2911-execfile-callback.ts": 3076, + "2912-execfile-callback-options.ts": 1918, + "2912-promisify-fs-readfile.ts": 2767, + "2913-builtin-callable-alias.ts": 2872, + "2914-fixed-tuple-call-spread.ts": 2555, + "2915-path-function-values.ts": 2085, + "2916-path-function-values-cjs.ts": 2881, + "2917-builtin-callable-widths.ts": 2912, + "2918-builtin-callable-widths-cjs.ts": 1902, + "2919-fs-function-values.ts": 2935, + "2920-fs-promises-function-values.ts": 2882, + "2921-zlib-callbacks.ts": 2124, + "2922-dyn-bitwise-not.cjs": 2707, + "2923-cjs-factory-export/main.cjs": 2830, + "2930-union-receiver-calls.ts": 2484, + "2931-union-dynamic-field-call.js": 2846, + "2932-create-require-program/main.ts": 3067, + "2933-static-dynamic-import/main.ts": 2168, + "2934-static-import-stored.ts": 3004, + "2935-module-namespace-values/main.ts": 2141, + "2936-static-loose-equality.ts": 3392, + "2937-bitwise-hot-loop.ts": 2114, + "2938-fs-sync-overloads.cjs": 1863, + "2939-child-shell-text.ts": 3119, + "2940-date-parse.ts": 2606, + "2940-fsp-realpath.ts": 2319, + "2941-fsp-lstat-identity.ts": 3102, + "2941-process-exit-code.ts": 2025, + "2942-fsp-readdir-dirent.ts": 2835, + "2942-process-exit-code-override.ts": 2255, + "2943-process-exit-code-invalid.ts": 2733, + "2944-process-exit-code-wrap.ts": 1969, + "2945-url-origin-username.ts": 1974, + "2946-child-fork-ipc/main.ts": 3772, + "2946-url-port-hash.ts": 2074, + "2947-set-readonly-seeds.ts": 3071, + "2948-child-fork-eof-callback/main.ts": 3013, + "2948-stdio-callback-record.ts": 2065, + "2949-child-fork-default/main.mjs": 2829, + "2949-url-password.ts": 2408, + "2950-fs-descriptor-publication.ts": 2677, + "2951-math-static-analytic.ts": 2349, + "2952-math-static-remaining.ts": 2335, + "2953-http-header-validation.ts": 2772, + "2954-http-outgoing-headers.ts": 3428, + "2955-http-static-state.ts": 2945, + "2956-http-static-informational.ts": 2218, + "2957-http-static-response-controls.ts": 2590, + "2961-emitter-computed-names.cjs": 2830, + "2962-emitter-computed-optional.ts": 2777, + "2963-child-fork-dispatch/main.ts": 3250, + "2964-indexed-compound.ts": 3270, + "2966-numeric-array-read-fusion.ts": 2442, + "2967-numeric-array-read-lifetime.ts": 2772, + "2968-numeric-array-borrow.ts": 3227, + "2969-scalar-record-results.ts": 3038, + "2970-constant-numeric-tables.ts": 2771, + "2971-constant-table-modules/main.ts": 1871, + "2972-http-static-distinct-snapshots.ts": 3053, + "2972-math-receiver-borrow.ts": 2947, + "2973-http-static-agent.ts": 3397, + "2973-integer-bitwise-arithmetic.ts": 2920, + "2974-array-search-from-index.ts": 124091, + "2974-http-static-module-values.ts": 2984, + "2974-numeric-remainder.ts": 3296, + "2975-bigint-default-radix.ts": 2470, + "2975-http-static-client-controls.ts": 2919, + "2976-http-static-incoming-state.ts": 2589, + "2976-json-stringify-no-arguments.js": 2303, + "2977-http-static-incoming-timeout.ts": 2944, + "2977-nested-array-binding-defaults.js": 2175, + "2978-http-static-client-timeout.ts": 2391, + "2978-regexp-optional-subject.js": 2308, + "2979-array-search-mixed-needles.js": 2358, + "2979-http-static-server-timeout.ts": 1739, + "2980-http-static-server-timeout-default.ts": 2883, + "2980-string-position-arguments.js": 2832, + "2981-http-static-events.ts": 2774, + "2981-string-optional-positions.ts": 2352, + "2982-http-static-close-all.ts": 2171, + "2982-string-position-coercion-order.js": 2155, + "2983-http-static-close-idle.ts": 3021, + "2984-http-static-close-upgrade.ts": 2125, + "2985-http-static-message-destroy.ts": 2402, + "2986-http-static-client-socket-finish.ts": 2843, + "2987-http-static-timeout-errors.ts": 3151, + "2988-http-static-client-abort.ts": 2768, + "2989-http-static-response-timeout-handled.ts": 2766, + "2990-http-static-request-timeout-handled.ts": 3159, + "2991-array-indexed-equality.ts": 2285, + "2992-stream-rows.ts": 2873, + "2993-fiber-stack-lifetimes.ts": 3425, + "2994-fromcharcode-scalar.ts": 3028, + "2995-math-hyperbolic.ts": 2938, + "2996-math-near-one.ts": 3222, + "2997-math-integer-float.ts": 2299, + "300-if-else.ts": 2867, + "3001-caught-typeof-object.js": 2563, + "3002-promise-then-two.ts": 3204, + "301-while.ts": 2926, + "302-for.ts": 2606, + "3020-string-last-index-position.ts": 2319, + "3021-buffer-union-narrow-callback.ts": 2195, + "3022-string-affix-positions.ts": 2341, + "3023-string-affix-position-coercion.js": 2752, + "3024-string-number-narrow-for-in.ts": 1609, + "3025-string-search-position-coercion.js": 2728, + "3026-array-last-index-of.js": 2413, + "3027-array-last-index-of-records.ts": 2116, + "3028-string-search-dynamic-needle.js": 1998, + "3029-string-search-union-needle.js": 2405, + "303-break-continue.ts": 2031, + "3030-array-search-no-arg.js": 3128, + "3031-array-relative-index-coercion.js": 2454, + "3031-string-last-index-search-value.js": 2375, + "3032-string-search-value.js": 2888, + "3033-string-search-needle-coercion.js": 2002, + "3034-arguments-with-parameters.js": 3443, + "3035-string-positioned-search-value.js": 2991, + "3036-string-object-search-value.js": 2678, + "3037-number-nullish-conversion.js": 2958, + "3038-number-scalar-unions.ts": 2263, + "3039-string-repeat-count.js": 2105, + "304-compound-incdec.ts": 2747, + "3040-string-prototype-call.js": 3083, + "3041-string-prototype-object-call.js": 2578, + "3042-string-padding-coercion.js": 2198, + "3043-string-split-arguments.js": 2450, + "3044-array-slice-splice-positions.js": 3038, + "3045-array-indexed-mutation.js": 3285, + "3046-array-indexed-ref-identity.ts": 1964, + "3047-object-prototype-tag.js": 2750, + "3048-object-own-prototype.ts": 3516, + "3049-array-flat-depth.ts": 2507, + "305-truthiness.ts": 3022, + "3050-array-splice-insert.ts": 3679, + "3051-array-join-default.ts": 2848, + "3052-array-construction.ts": 2982, + "3053-array-construction-js.js": 3024, + "3054-dynamic-abort-record.ts": 2146, + "3055-default-builtin-nodenext/main.ts": 2904, + "3056-default-builtin-bundler/main.ts": 2933, + "3057-default-builtin-nodenext-dynamic/main.ts": 2973, + "3058-optional-array-chain-join.ts": 2285, + "3059-arguments-property-names.ts": 2296, + "3061-concurrent-fiber-stacks.ts": 1843, + "3062-runtime-optional-record-guards.ts": 3177, + "3063-runtime-optional-class-guards.ts": 2891, + "3064-installed-entry-imports/main.ts": 2942, + "3065-installed-entry-js/main.js": 2574, + "3066-source-debugging/main.ts": 2037, + "3067-cjs-function-export-require/main.cjs": 2917, + "3068-debug-variables/main.ts": 2538, + "3068-url-checked-identity.ts": 2749, + "3069-set-checked-seed.js": 2271, + "3069-tuple-spread.ts": 2404, + "3070-checked-object-accessors.js": 3199, + "3070-collection-views.ts": 3106, + "3071-checked-specialization-narrowing.js": 2927, + "3071-map-contextual-record-seeds.ts": 3315, + "3072-optional-union-receivers.ts": 3294, + "3072-weak-process-streams.js": 2924, + "3073-optional-callable-module.js": 2944, + "3073-union-keyed-reads.ts": 2310, + "3074-discriminated-intersections.ts": 2137, + "3075-recursive-contextual-literals.ts": 3075, + "3076-nullish-record-defaults.ts": 2013, + "3077-optional-union-array-reads.ts": 3340, + "3078-forward-mutable-captures.ts": 3407, + "3079-forward-mutable-tdz.ts": 3267, + "3080-exhaustive-never-bindings.ts": 2952, + "3081-recursive-array-destinations.ts": 2842, + "3082-forward-write-unwind.ts": 2956, + "3083-set-string-seed.ts": 2684, + "3084-record-own-membership.ts": 3434, + "3085-conditional-record-construction.ts": 2128, + "3086-error-constructor-options.ts": 2138, + "3087-error-options-lifetime.ts": 2004, + "3088-array-wide-callbacks.ts": 2257, + "3089-array-find-narrowing.ts": 2620, + "3090-json-discriminated-records.ts": 2285, + "3091-json-recursive-discriminants.ts": 2190, + "3092-number-union-predicates.ts": 2281, + "3093-error-option-data.ts": 2642, + "3094-json-replacer-traversal.ts": 3187, + "3095-json-reviver-traversal.ts": 2938, + "3096-json-callback-nonfinite.ts": 2858, + "3097-json-callback-reentrancy.ts": 2787, + "3098-json-callback-space.ts": 3229, + "3099-json-callback-functions.ts": 3214, + "3100-json-callback-holder.js": 2206, + "3101-json-callback-bytes.ts": 2646, + "3102-json-unknown-fields.ts": 3086, + "3103-scalar-record-nested-control-flow.ts": 2112, + "3104-native-analysis-tables-loops.ts": 2780, + "3105-nullable-collections.ts": 3028, + "3106-mixed-insertion-spreads.ts": 2007, + "3107-nullable-collection-lifetimes.ts": 3338, + "3108-for-await-sync-arrays.js": 2682, + "3108-identity-map-keys.ts": 2227, + "3109-destructuring-unknown-defaults.js": 2915, + "3109-identity-union-collections.ts": 3423, + "3110-builtin-error-families.ts": 2570, + "3110-identity-collection-mutation.ts": 2678, + "3111-identity-collection-cycles.ts": 2997, + "3112-union-switch-control-flow.ts": 2901, + "3113-union-array-removal.ts": 3399, + "3114-union-record-field-write.ts": 2336, + "3115-zlib-static-compression-levels.ts": 2528, + "3116-sequence-expression-lifetimes.ts": 3297, + "3117-unknown-switch-control-flow.ts": 2618, + "3118-recursive-union-spread.ts": 3545, + "3119-runtime-optional-spread.ts": 2406, + "3120-dynamic-property-descriptors.js": 3089, + "3120-generator-method-loop-bindings.ts": 3317, + "3120-ir-nonfinite-numbers.ts": 1849, + "3121-compound-assignment-values.js": 2286, + "3121-dynamic-accessor-descriptors.js": 4553, + "3122-dynamic-delete-descriptors.cjs": 1796, + "3122-dynamic-delete-descriptors.js": 2168, + "3122-js-async-class-methods.js": 2287, + "3123-destructured-global-tdz.js": 2851, + "3124-array-prototype-borrow.js": 3706, + "3125-regex-global-match.ts": 3004, + "3126-js-number-radix/main.ts": 3034, + "3127-spawn-runtime-options.ts": 2691, + "3128-regex-last-index.ts": 2151, + "3129-js-token-union/main.ts": 2990, + "3130-child-generic-streams.ts": 3199, + "3131-child-rest-listeners.js": 1690, + "3132-child-generic-stream-completion.ts": 5607, + "3133-text-decoder-streaming.ts": 3055, + "3134-fs-vectors.ts": 3055, + "3135-fs-file-handle-controls.ts": 3371, + "3136-fs-file-handle-options.ts": 2897, + "3137-fs-vector-callbacks.cjs": 3277, + "3138-array-large-storage.ts": 3756, + "3139-regex-native-deep-alternatives.ts": 28435, + "3140-array-dense-access.ts": 3894, + "3141-map-string-key-hash.ts": 3167, + "3150-integer-remainder.ts": 3353, + "3151-array-push-writes.ts": 3497, + "3170-number-conversion-fast-paths.ts": 3302, + "3171-method-census-data-writes.ts": 2848, + "400-fib.ts": 2397, + "401-mutual-recursion.ts": 2469, + "402-string-functions.ts": 2915, + "403-void-and-params.ts": 1930, + "4030-runtime-object-truthiness.ts": 2360, + "4031-event-emitter-long-tuples.ts": 1978, + "4032-inline-rc-fast-paths.ts": 4391, + "404-rc-stress.ts": 2580, + "4040-adapted-function-identity.ts": 3640, + "4041-adapted-function-properties.ts": 3228, + "4042-object-is-union-subclass.ts": 1738, + "4043-nonnull-missing-element.ts": 3185, + "4044-adapted-callback-modules/main.ts": 2445, + "4045-optional-element-projections.ts": 2663, + "4046-captured-parameter-values.ts": 3523, + "4047-captured-parameter-rebinding.ts": 2990, + "405-params-optional.ts": 2128, + "406-params-defaults.ts": 2785, + "4060-cycle-shared-instance/main.ts": 2724, + "4060-stack-union-reads.ts": 3544, + "4061-cycle-evaluation-order/main.ts": 2941, + "4061-nullable-ref-fields.ts": 4633, + "4062-cycle-tdz-read/main.ts": 2132, + "4063-cycle-hoisted-calls/main.ts": 2837, + "4064-cycle-classes-after-init/main.ts": 2919, + "4065-cycle-init-throws/main.ts": 2003, + "4066-cycle-top-level-tdz/main.ts": 2633, + "4067-dynamic-import-cycle-tdz/main.ts": 2814, + "407-params-rest.ts": 3063, + "408-params-ctors-inheritance.ts": 2314, + "4080-optional-read-presence-tests.ts": 2434, + "4080-string-index-bounds.ts": 2754, + "4081-hoisted-function-tdz.ts": 3289, + "4081-string-index-unproven.ts": 2424, + "4082-hoisted-function-tdz-uncaught.ts": 2711, + "4083-dynamic-import-failure/main.ts": 3307, + "4084-record-optional-field-copies.ts": 3992, + "4085-generator-loop-close.ts": 2360, + "4086-optional-capture-defaults.ts": 2000, + "4088-class-static-tdz.ts": 2016, + "4089-class-wrapper-tdz.js": 2230, + "409-params-async-generics.ts": 2900, + "4090-computed-key-tdz.js": 2345, + "4091-class-evaluation-deferred.ts": 2676, + "410-params-rc-stress.ts": 2904, + "4140-array-callback-split-visit.ts": 4145, + "4141-array-callback-ambient-receiver/main.ts": 3151, + "4142-array-callback-factories.ts": 3774, + "4143-union-refinement-intersection-arms.ts": 1858, + "420-dead-strip-modules/main.ts": 2735, + "4290-acyclic-immutable-trees.ts": 3335, + "4291-acyclic-constructor-escapes.ts": 2796, + "4292-acyclic-readonly-bypass.ts": 2522, + "4293-acyclic-dynamic-writes.ts": 2365, + "4294-console-line-sizes.ts": 6187, + "4295-console-template-parts.ts": 2942, + "4296-console-glued-long-parts.ts": 4142, + "4410-inline-object-allocation.ts": 2124, + "4411-nullable-ref-values.ts": 4400, + "4412-nullable-record-values.ts": 4209, + "4413-cycle-old-late-death.ts": 2986, + "4413-nullable-null-and-undefined.ts": 2888, + "4414-cycle-old-weak-keys.ts": 3194, + "4414-nullable-string-values.ts": 4815, + "4415-identity-borrowed-operands.ts": 2337, + "4415-nullable-container-values.ts": 4436, + "4431-exception-pending-contexts.ts": 2184, + "4432-checked-downcast-failures.ts": 3797, + "4451-int32-field-storage.ts": 3817, + "4452-int32-field-dynamic-writes.ts": 3773, + "4453-int32-closed-locals.ts": 2395, + "4454-int32-field-reflection-reads.ts": 3951, + "4455-live-view-reads-without-stores.ts": 3598, + "4471-unassigned-field-reads.ts": 2666, + "4476-string-ascii-header-fact.ts": 2214, + "4477-buffer-write-ascii-runs.ts": 3271, + "4478-string-large-index-reads.ts": 2510, + "4481-array-optional-push.ts": 3023, + "4482-instanceof-static-class-optional.ts": 2810, + "4483-array-splice-statement.ts": 3482, + "4484-array-length-reset.ts": 2127, + "4485-array-splice-optional-item.ts": 2800, + "4486-array-field-element-borrows.ts": 3650, + "4487-array-literal-optional-values.ts": 3286, + "4520-optional-guard-narrowing.ts": 2046, + "4521-array-callback-element-presence.ts": 4687, + "4522-captured-parameter-lifetimes.ts": 2497, + "4523-array-pop-removal.ts": 3081, + "4524-array-callback-supertype-params.ts": 3932, + "4541-nullable-wrap-borrows.ts": 2389, + "4542-preserving-call-borrows.ts": 3360, + "4543-walk-borrowed-locals.ts": 3391, + "4544-checked-cast-aliases.ts": 3252, + "4545-borrowed-returns.ts": 3896, + "4546-rebound-borrowed-params.ts": 1833, + "4560-field-store-borrowed-receivers.ts": 2735, + "4561-virtual-borrowed-parameters.ts": 3750, + "4562-string-self-concat-move.ts": 2152, + "4563-array-read-projection-receivers.ts": 2323, + "4564-literal-choice-strings.ts": 2445, + "4568-for-of-hole-elements.ts": 3409, + "4569-shorthand-missing-elements.ts": 2920, + "4570-map-lookup-default-record.ts": 3442, + "4591-weak-keys-union-records.ts": 3061, + "4592-cycle-const-from-import/main.ts": 2838, + "4593-weak-keys-empty-objects.ts": 3425, + "4594-union-class-field-mixed-types.ts": 3724, + "4595-union-class-field-optional-receiver.ts": 3074, + "4596-error-subclass-int-fields.ts": 2744, + "4596-tdz-nullable-forward-capture.ts": 3971, + "4597-callback-field-method-name.ts": 2791, + "4598-live-array-ref-element-access.ts": 5033, + "4598-map-set-clear-reuse.ts": 4347, + "4598-os-available-parallelism.ts": 2628, + "4598-publish-constructor-stores.ts": 3091, + "4599-construction-only-field-borrows.ts": 3355, + "4600-construction-only-fields-dynamic-writes.ts": 2213, + "4601-cycle-holders.ts": 2024, + "4602-constructor-escape-order.ts": 2992, + "4610-array-ext-dyn-refresh.ts": 3195, + "4620-optional-chain-borrowed-receivers.ts": 3095, + "4621-edge-preserving-callees/main.ts": 3779, + "4630-contract-int32.ts": 2865, + "4630-reference-write-call-arguments.ts": 2202, + "4631-contract-nothrow.ts": 2128, + "4631-reference-write-aliases.ts": 3078, + "4632-contract-immutable.ts": 2740, + "4632-reference-write-loops-and-walks.ts": 3256, + "4633-contract-program-lifetime.ts": 2525, + "4633-reference-write-callbacks-returns.ts": 3120, + "4634-contract-program-lifetime-publish.ts": 5678, + "4634-optional-element-borrowed-returns.ts": 2332, + "4640-bitwise-identity-ranges.ts": 3148, + "4641-nothrow-try-catch.ts": 3252, + "4642-acyclic-contract.ts": 2463, + "4650-stale-property-narrowing.ts": 3973, + "4651-map-string-get-entry.ts": 2471, + "4652-stale-closure-binding-narrowing.ts": 3516, + "4660-typed-rest-spread-forwarding.ts": 3181, + "4671-scoped-array-read-borrows.ts": 2667, + "4690-cycle-bypass-long-lived.ts": 4305, + "4691-publish-constructor-self-alias.ts": 2372, + "4692-borrowed-result-mutating-callees.ts": 1719, + "4720-cold-throw-paths.ts": 3494, + "4721-integer-induction-numbers.ts": 2519, + "4730-touint32-large-magnitudes.ts": 3115, + "4731-string-mixed-ascii-slices.ts": 3544, + "4732-buffer-write-utf16le-runs.ts": 3493, + "4733-typed-array-read-compares.ts": 3222, + "4750-map-string-absent-filter.ts": 2733, + "4751-string-search-multibyte-needles.ts": 2428, + "4795-published-untyped-member-reads.ts": 3850, + "4806-uint32-loop-accumulators.ts": 3676, + "4810-union-identity-retags.ts": 4908, + "4811-union-retag-checked-cast.ts": 2426, + "4812-int32-wrapping-sums.ts": 3340, + "4813-typed-array-read-equality-bindings.ts": 4912, + "4820-sort-comparator-lent-arguments.ts": 4229, + "4821-callback-lent-arguments.ts": 4442, + "4822-stable-capture-reads.ts": 3053, + "4823-array-store-receivers.ts": 3132, + "4830-typed-teardown-chains.ts": 2523, + "4831-cycle-bypass-probe-windows.ts": 2952, + "4840-array-hole-free-prefix.ts": 3114, + "4860-int32-local-storage.ts": 4294, + "4870-array-pipeline-fusion.ts": 4815, + "4871-array-pipeline-analytics.ts": 3642, + "4872-array-pipeline-temporaries.ts": 8318, + "4875-stream-consumers-array-buffer.ts": 2209, + "4876-array-buffer-inspect.ts": 3069, + "4880-await-call-turn-order.ts": 2111, + "4881-await-call-exceptions.ts": 3319, + "4882-await-call-request-chain.ts": 2496, + "4883-await-values-and-hops.ts": 2868, + "4884-await-call-unhandled-sibling.ts": 2184, + "4885-await-call-checkpoints.ts": 3054, + "4890-regex-literal-sites.ts": 2434, + "4891-regex-plain-text-patterns.ts": 2078, + "4892-regex-exec-read-captures.ts": 2821, + "4900-bigint-division-cross-check.ts": 3490, + "4900-console-instances.ts": 3829, + "4901-bigint-radix-text.ts": 2425, + "4901-console-instance-errors.ts": 2457, + "4902-bigint-compound-assign.ts": 2147, + "4903-bigint-fixed-width.ts": 2614, + "4904-bigint-large-products.ts": 3572, + "4905-bigint-fixed-width-fusion.ts": 3509, + "4906-bigint-small-value-trees.ts": 3794, + "4907-bigint-random-trees.ts": 4702, + "4910-hole-free-prefix-upkeep.ts": 2383, + "4920-string-literal-switch.ts": 2483, + "4930-typed-record-reflection.ts": 5024, + "4931-typed-data-generic-walks.ts": 4893, + "4932-typed-json-unknown.ts": 3278, + "4940-async-return-adoption.ts": 3536, + "4941-async-return-adoption-try.ts": 2995, + "4942-async-return-adoption-values.ts": 4513, + "4943-async-return-adoption-interleaved.ts": 2479, + "4950-regex-compiled-captures.ts": 4860, + "4951-regex-compiled-last-index.ts": 3606, + "4952-regex-compiled-anchors-classes.ts": 3738, + "4953-regex-compiled-backtracking.ts": 2856, + "4954-regex-log-report.ts": 4801, + "4955-regex-compiled-differential.ts": 24475, + "4970-int32-call-abi.ts": 3417, + "4980-generator-early-exit-finally.ts": 3144, + "4981-generator-delegation-iterables.ts": 4656, + "4982-generator-sent-values.ts": 2721, + "4983-generator-reuse-iterables.ts": 3420, + "4984-generator-exceptions.ts": 3409, + "4985-generator-lazy-pipeline.ts": 3627, + "4986-generator-default-channels.ts": 3616, + "4987-generator-closures-methods.ts": 2884, + "4988-generator-async-interplay.ts": 2217, + "4989-generator-step-drives.ts": 3530, + "4990-lone-surrogates.ts": 2833, + "4991-lone-surrogate-halves.ts": 2382, + "4992-lone-surrogate-search.ts": 2602, + "4993-lone-surrogate-json.ts": 2479, + "4994-lone-surrogate-uri.ts": 3069, + "4995-lone-surrogate-regex.ts": 3486, + "4996-lone-surrogate-stdio.ts": 3232, + "4997-lone-surrogate-stdout-patch.js": 2640, + "4998-lone-surrogate-buffers.ts": 2452, + "4999-lone-surrogate-io.ts": 2454, + "500-array-basics.ts": 2089, + "501-array-push-pop.ts": 2391, + "5010-lifetime-region-generations.ts": 2818, + "5011-lifetime-region-outside-references.ts": 3377, + "5012-lifetime-region-shapes.ts": 3312, + "5013-lifetime-region-async.ts": 3967, + "5014-lifetime-region-nesting.ts": 3224, + "5015-lifetime-region-publish.ts": 3866, + "5016-lifetime-region-weak-keys.ts": 2328, + "5017-lifetime-region-cycles.ts": 2417, + "5018-lifetime-region-hierarchies.ts": 4028, + "5019-lifetime-region-values.ts": 3115, + "502-array-for-of.ts": 2081, + "5020-lifetime-region-large.ts": 4189, + "5021-lifetime-region-opaque-holders.ts": 2035, + "5022-lifetime-region-workers.ts": 3716, + "5023-lifetime-region-await.ts": 3656, + "5024-lifetime-region-await-forms.ts": 3342, + "503-array-functions.ts": 2924, + "504-array-rc-stress.ts": 3242, + "5040-promise-all-race-turns.ts": 4173, + "5041-promise-all-turns.js": 2741, + "5042-async-generator-return-promise.ts": 3826, + "5043-async-generator-return-forms.ts": 2473, + "5045-generator-delegation-typed-next.ts": 4238, + "5046-generator-delegation-return-throw.ts": 3793, + "5047-generator-delegation-iterables-typed.ts": 4942, + "5048-generator-delegation-async.ts": 3582, + "5049-generator-delegation-edges.ts": 2876, + "5050-inherited-in-membership.ts": 3767, + "5051-typed-record-prototypes.ts": 3580, + "5052-class-for-in-unknown.ts": 3587, + "5053-json-replacer-arrays.ts": 2213, + "5054-required-field-delete.ts": 2545, + "5055-object-create-record-literal.ts": 2467, + "5056-generator-in-unknown.ts": 2333, + "5060-child-fork-exit-after-messages/main.ts": 2415, + "5061-child-fork-local-disconnect/main.ts": 3036, + "5062-child-fork-message-before-eof/main.ts": 2882, + "5065-global-increment-initializers.ts": 2801, + "5066-global-compound-updates.ts": 3218, + "5067-global-updates-in-functions.ts": 3110, + "5068-global-var-let-const-updates.ts": 2271, + "5069-global-update-modules/main.ts": 2850, + "5070-optional-read-bindings.ts": 2832, + "5071-optional-chain-scalars.ts": 2863, + "5072-optional-nullish-ternary.ts": 2578, + "5073-optional-results.ts": 3429, + "5074-optional-pop-shift.ts": 2389, + "5075-optional-reads-unchecked/main.ts": 2697, + "5076-optional-escapes.ts": 2437, + "5080-boolean-array-storage.ts": 6485, + "5081-boolean-array-dynamic-views.ts": 2359, + "5085-promise-allsettled-turns.ts": 3904, + "5086-promise-any-turns.ts": 3934, + "5087-promise-allsettled-any-dyn.js": 5476, + "5088-promise-combinator-thenables.js": 3403, + "5089-promise-settled-records.ts": 3102, + "5090-generator-finished-result-value.ts": 3342, + "5091-generator-delegation-arrays-strings.ts": 4412, + "5092-generator-delegation-sets-maps.ts": 3102, + "5095-console-count-group.ts": 3177, + "5096-console-assert-dir.ts": 3849, + "5097-console-table.ts": 3057, + "5098-console-time-warnings.ts": 6642, + "5099-console-methods-dynamic.cjs": 3356, + "510-array-map-filter-foreach.ts": 3363, + "511-array-indexof-includes.ts": 3037, + "5110-var-hoisting-early-reads.ts": 2475, + "5111-var-hoisting-closures-classes.ts": 3177, + "5112-var-hoisting-function-scope.ts": 2961, + "5113-tdz-let-const-class.ts": 3714, + "5114-var-hoisting-js.js": 2962, + "5115-regex-replace-compiled.ts": 3348, + "5116-regex-capture-reads.ts": 3537, + "5117-regex-log-report-spans.ts": 2918, + "5118-regex-replace-char-chains.ts": 2863, + "512-array-join-chains.ts": 2158, + "5120-frame-records.ts": 2742, + "5121-frame-objects.ts": 2716, + "5122-frame-result-slots.ts": 2032, + "5123-frame-reassigned.ts": 2738, + "5124-frame-objects-control.ts": 2735, + "5125-frame-mutating-methods.ts": 2979, + "5126-frame-returned-params.ts": 2879, + "5127-frame-loop-bindings.ts": 3193, + "513-array-methods-rc-stress.ts": 2584, + "5130-event-target-dispatch.js": 3996, + "5131-event-target-listener-errors.js": 2992, + "5132-event-target-typed.ts": 2663, + "5133-abort-signal-event-target.ts": 3726, + "5134-events-error-monitor.ts": 3026, + "5135-event-target-warnings.js": 3015, + "5136-events-error-monitor-string-name.ts": 1974, + "5137-events-error-monitor-computed-name.js": 2881, + "5138-events-error-monitor-shared-listener.ts": 1863, + "5139-event-target-listener-throw.ts": 2263, + "514-array-find-some-every.ts": 3260, + "5140-os-host-facts.ts": 3684, + "5141-os-constants.ts": 3492, + "5142-path-parse-format.ts": 2821, + "5145-path-matches-glob-basics.ts": 2871, + "5146-path-matches-glob-braces.ts": 4793, + "5147-path-matches-glob-extglob.ts": 3097, + "5148-path-matches-glob-classes.ts": 3036, + "5149-path-matches-glob-win32.ts": 3100, + "515-array-flatmap.ts": 3003, + "5150-small-array-inline-slots.ts": 3147, + "5155-cycle-header-types.ts": 3920, + "516-array-reduce.ts": 3545, + "5160-field-callback-receiver.ts": 2905, + "5161-field-callback-receiver-escape.ts": 2883, + "5162-field-method-reference-receiver.ts": 2527, + "5163-field-callback-receiver.cjs": 3464, + "5164-worker-field-callback-receiver.ts": 3434, + "5165-interface-method-dispatch.ts": 3679, + "5166-interface-method-values.ts": 3253, + "5167-interface-method-order.ts": 3399, + "5168-interface-method-hierarchy.ts": 3873, + "5169-interface-method-chains.ts": 4165, + "517-array-hof-index-args.ts": 2228, + "5170-string-scan-eviction.ts": 2996, + "5171-lone-surrogate-replace-seams.ts": 3136, + "5175-forin-typed-as-unknown.ts": 2043, + "5176-object-keys-typed-as-unknown.ts": 3098, + "5177-forin-dynamic-objects.ts": 3236, + "5178-forin-keys-js.js": 3163, + "5179-forin-typed-shapes.ts": 3384, + "518-array-sort.ts": 2963, + "518-promise-void-union-callbacks.ts": 3093, + "5180-interface-arrays.ts": 3147, + "5181-interface-array-methods.ts": 8906, + "5182-interface-array-boundaries.ts": 2573, + "5183-interface-property-reads.ts": 3303, + "5184-interface-property-objects.ts": 2438, + "5185-util-inherits.cjs": 3986, + "5186-util-inherits.ts": 3513, + "5187-util-deprecate.cjs": 3994, + "5188-util-deprecate-flags.cjs": 2993, + "5189-util-deprecate.ts": 3292, + "519-array-from-length.ts": 2634, + "519-promise-union-await-values.ts": 2998, + "5190-util-callbackify.cjs": 2477, + "5191-util-callbackify.ts": 3362, + "5192-util-debuglog.cjs": 3445, + "5193-util-debuglog.ts": 3383, + "5194-process-emit-warning-tick.cjs": 2282, + "5195-object-entries-typed-as-unknown.ts": 3460, + "5196-object-values-typed-as-unknown.ts": 3267, + "5197-object-entries-dynamic-objects.ts": 3904, + "5198-object-entries-values-js.js": 2966, + "5199-object-entries-identity.ts": 3380, + "520-map-basics.ts": 2986, + "5200-array-sort-keyed-numbers.ts": 3421, + "5201-array-sort-keyed-records.ts": 5202, + "5202-array-sort-keyed-lookalikes.ts": 3629, + "5203-typed-array-sort-default.ts": 5027, + "5204-typed-array-sort-comparators.ts": 3154, + "521-map-number-keys.ts": 2975, + "5210-process-memory-usage.ts": 2993, + "5211-process-identity.ts": 2460, + "5212-perf-marks-measures.ts": 3807, + "5213-perf-timing-errors.ts": 2266, + "5214-perf-entry-inspect.ts": 4135, + "5215-perf-timing-validation.js": 3297, + "5216-perf-spread-arguments.js": 2533, + "5217-perf-spread-typed.ts": 2686, + "522-map-foreach.ts": 3042, + "5220-url-parse-warnings.ts": 2728, + "5221-url-parse-shapes.ts": 3405, + "5222-url-parse-hosts.ts": 3159, + "5223-url-parse-query.ts": 3506, + "5224-url-format-objects.ts": 3987, + "5225-url-format-whatwg.ts": 3370, + "5226-url-resolve.ts": 3030, + "5227-url-legacy-dependency/main.ts": 2891, + "5228-url-domain-conversion.ts": 2727, + "5229-url-legacy-errors.cjs": 3507, + "523-map-ref-values.ts": 2648, + "5230-inline-union-fields.ts": 3279, + "5231-inline-union-field-kinds.ts": 2225, + "5232-inline-union-fields-dynamic.ts": 4075, + "5233-inline-union-fields-shapes.ts": 3388, + "5234-inline-union-class-fields.ts": 3532, + "5235-inline-union-fields-publish.ts": 2491, + "5236-inline-union-unchecked-reads.ts": 3193, + "5237-inline-union-frame-objects.ts": 3058, + "524-map-cycles.ts": 2269, + "5240-event-target-subclass.ts": 4204, + "5241-event-subclass.ts": 2870, + "5242-event-target-subclass-hierarchy.ts": 2589, + "5243-event-target-subclass-dynamic.ts": 3729, + "5244-event-target-subclass.js": 3769, + "5245-event-target-subclass-signals.ts": 3471, + "5246-event-target-subclass-listener-throw.ts": 3466, + "5247-event-target-subclass-lifetimes.ts": 4294, + "5248-event-target-subclass-warnings.ts": 3036, + "5249-event-target-subclass-unconstructed.ts": 2612, + "525-map-rc-stress.ts": 2418, + "5250-cipher-aes-gcm.ts": 5771, + "5251-cipher-aes-cbc.ts": 3823, + "5252-cipher-aes-ctr.ts": 2580, + "5253-cipher-chacha20-poly1305.ts": 3418, + "5254-cipher-errors.js": 4743, + "5255-cipher-encodings.cjs": 2250, + "5256-cipher-deprecation.cjs": 3088, + "5257-cipher-vectors.ts": 3572, + "5258-cipher-handles.js": 2980, + "5259-cipher-chunked.ts": 2586, + "526-set-basics.ts": 2254, + "5260-record-unknown-identity.ts": 2475, + "5261-optional-field-identity.ts": 3183, + "5262-record-unknown-many.ts": 2600, + "527-set-foreach.ts": 2444, + "5270-url-parse-no-deprecation.cjs": 3324, + "5271-url-parse-throw-deprecation.cjs": 3222, + "5275-http-end-callback-order.ts": 3388, + "5276-style-text-hex-colors.cjs": 2986, + "5277-fs-read-file-buffer-option.cjs": 2730, + "5278-host-null-bytes.cjs": 2802, + "5279-search-params-null-init.cjs": 2749, + "528-set-seeded.ts": 3056, + "5285-blob-construct.ts": 2884, + "5286-blob-readers.ts": 2550, + "5287-blob-inspect.ts": 3329, + "5288-blob-validation.js": 3838, + "5289-blob-read-order.ts": 3430, + "529-map-seeded.ts": 2821, + "5290-blob-dynamic.js": 3167, + "5295-readline-promises-question.ts": 3310, + "5296-readline-lines.ts": 2423, + "5297-readline-callback-events.cjs": 2594, + "5298-readline-abort.cjs": 3296, + "5299-readline-stdin.ts": 3344, + "530-record-arrays.ts": 2381, + "5300-stream-add-abort-signal.ts": 3578, + "5301-stream-add-abort-signal-errors.cjs": 3164, + "5302-stream-promises-options.ts": 2875, + "5303-stream-finished-callback-opts.cjs": 3111, + "5305-json-stringify-typed-arrays.ts": 3510, + "531-record-array-hofs.ts": 3334, + "5315-map-iterate-after-front-deletes.ts": 2670, + "5316-map-lru-eviction.ts": 3171, + "5317-map-iterate-while-mutating.ts": 3109, + "5318-set-iterate-after-deletes.ts": 3338, + "5319-map-iteration-values.ts": 2901, + "532-record-arrays-rc-stress.ts": 2965, + "5320-map-iterate-across-compaction.ts": 2712, + "5321-integer-closed-arithmetic-loops.ts": 3349, + "5321-json-parse-cycles.ts": 3308, + "5322-integer-variable-step-loops.ts": 2801, + "5322-json-stringify-parsed-trees.ts": 3139, + "5323-dyn-numeric-index-reads.ts": 2266, + "5323-int32-nonnegative-slot-facts.ts": 2381, + "5324-int32-initializer-locals.ts": 3123, + "5324-structured-clone-documents.ts": 3223, + "5325-record-boundary-returns.ts": 1813, + "5326-record-boundary-params.ts": 2295, + "5327-record-boundary-identity.ts": 3016, + "5328-record-boundary-open.ts": 2956, + "5329-record-boundary-recursion.ts": 3001, + "533-array-element-cycles.ts": 2575, + "5330-record-boundary-fields.ts": 2897, + "5331-record-boundary-methods.ts": 2874, + "5332-int32-narrowed-slot-facts.ts": 2932, + "5333-integer-guarded-while-counters.ts": 2773, + "5333-record-boundary-nullable-argument.ts": 2389, + "5334-fetch-mentions-without-calls.ts": 2822, + "5335-stream-operators-values.ts": 3448, + "5335-top-level-await-pending-exit-code.ts": 2359, + "5336-stream-operators-promise-timing.ts": 3509, + "5336-top-level-await-pending-exit-listener.ts": 2649, + "5337-dc-subscriber-throw-next-tick.cjs": 2705, + "5337-stream-operators-concurrency.ts": 2478, + "5338-js-tdz-function-element.js": 2787, + "5338-stream-operators-checks.cjs": 3991, + "5339-array-buffer-constructor.js": 3134, + "5339-stream-for-await-hops.ts": 2919, + "534-record-width-subtyping.ts": 3068, + "5340-readable-from-options.ts": 1980, + "5341-compound-any-operand.ts": 3314, + "5341-generator-callback-params.ts": 3955, + "5341-integer-recursion-boundaries.ts": 2534, + "5341-readable-from-dynamic.cjs": 3057, + "5342-compound-any-operand.cjs": 3386, + "5342-generator-scalar-channels.ts": 3184, + "5342-integer-recursion-stack-and-mutual.ts": 3097, + "5342-stream-compose.ts": 2940, + "5343-any-flow-compound.ts": 2063, + "5343-integer-recursion-array-index.ts": 2905, + "5343-iterator-result-records.ts": 3426, + "5343-stream-compose-edges.ts": 3078, + "5344-json-parse-undeclared-keys.ts": 4193, + "5344-stream-pipeline-timing.ts": 3177, + "5345-object-values-for-of-unknown.ts": 3435, + "5346-object-values-length-dynamic.ts": 3274, + "5347-object-values-loop-control.ts": 3443, + "5348-object-values-for-of-js.js": 2832, + "5349-object-entries-values-optional.ts": 3325, + "535-object-statics.ts": 3232, + "5350-object-values-wide-record.ts": 2628, + "5351-object-values-methods-index.ts": 4093, + "5352-final-field-loop-reads.ts": 2894, + "5353-captured-const-values.ts": 3727, + "5354-captured-const-tdz.ts": 2243, + "5355-captured-function-declarations.ts": 2917, + "5356-captured-tdz-ordering.ts": 2726, + "5357-init-only-module-bindings.ts": 1956, + "536-map-seed-array.ts": 1912, + "5360-array-sort-comparator-calls.ts": 3363, + "5361-array-sort-inconsistent-comparators.ts": 3560, + "5362-array-sort-comparator-throws.ts": 3410, + "5363-array-sort-comparator-writes.ts": 7945, + "5364-array-sort-union-and-boolean-elements.ts": 10573, + "5365-array-sort-dynamic-values.js": 2528, + "537-map-iter-drains.ts": 3127, + "5370-zlib-stream-roundtrip.ts": 3486, + "5371-zlib-stream-chunking.cjs": 4025, + "5372-zlib-stream-errors.cjs": 2794, + "5373-zlib-stream-options.cjs": 3210, + "5374-zlib-stream-flush-params.ts": 3572, + "5375-zlib-stream-promises.ts": 3156, + "5376-zlib-constants.cjs": 2490, + "5377-zlib-stream-backpressure.cjs": 2917, + "5378-zlib-sync-options.cjs": 2777, + "5379-zlib-callback-options.ts": 2861, + "538-map-set-for-of.ts": 2968, + "5390-interface-loop-element-lifetimes.ts": 3450, + "5391-interface-dispatch-hierarchies.ts": 2856, + "5392-interface-receiver-bindings.ts": 2639, + "5393-interface-array-holes.ts": 3216, + "5394-interface-implementers.ts": 3420, + "5395-locale-compare-ascii.ts": 2643, + "5396-locale-compare-latin.ts": 3373, + "5397-locale-compare-sorting.ts": 2181, + "540-tuples-basics.ts": 3148, + "5400-json-parse-typed-keys.ts": 3311, + "5405-buffer-arraybuffer-window.ts": 2743, + "5406-util-types-keys.ts": 3041, + "5407-http-header-field-limit.ts": 3155, + "5408-http-unread-request-end.ts": 3012, + "5409-http-held-request-body.ts": 3019, + "541-ref-array-json.ts": 2632, + "5410-inspect-class-values.ts": 3232, + "5411-inspect-class-hierarchies.ts": 3027, + "5412-inspect-dynamic-collections.ts": 2914, + "5413-inspect-class-values.cjs": 3313, + "5415-zlib-dynamic-inputs.cjs": 3606, + "542-union-element-arrays.ts": 3131, + "5420-readable-from-generators.ts": 3459, + "5421-readable-from-generators.cjs": 3156, + "5422-stream-async-push-wakeup.ts": 3051, + "5423-stream-end-and-eos-timing.ts": 3065, + "5424-stream-write-buffer-encoding.ts": 2687, + "5425-pipeline-generator-stage.ts": 2503, + "5426-pipeline-generator-errors.ts": 3640, + "5427-pipeline-generator-backpressure.ts": 3096, + "5428-pipeline-generator-stages.cjs": 2057, + "5429-pipeline-generator-buffers.ts": 2918, + "5430-dyn-number-string-boxes.ts": 3282, + "5435-crypto-hash-algorithms.ts": 2301, + "5436-crypto-hash-names.js": 2084, + "5437-crypto-hmac-algorithms.ts": 3303, + "5438-crypto-kdf-digests.ts": 2137, + "5439-crypto-kdf-callbacks.js": 3353, + "5440-crypto-hash-encodings.js": 4466, + "5441-crypto-hash-output-length.ts": 3422, + "5442-crypto-random-values.ts": 3350, + "5443-crypto-random-arguments.js": 3269, + "5444-crypto-timing-safe-equal.js": 2816, + "5445-crypto-secret-key-objects.ts": 3209, + "5445-stream-buffer-chunks.ts": 3329, + "5446-crypto-secret-key-errors.js": 2813, + "5446-stream-buffer-chunks-dynamic.js": 2503, + "5447-fs-readdir-order.ts": 2263, + "5448-fs-glob-patterns.ts": 2912, + "5449-fs-glob-options.ts": 3879, + "5450-fs-glob-validation.js": 4118, + "5451-fs-promises-glob.ts": 3698, + "5452-fs-glob-callback.ts": 2619, + "5455-power-of-two-remainder.ts": 2806, + "5456-remainder-large-integer-dividends.ts": 3156, + "5457-lcg-modulus.ts": 2975, + "5458-fractional-remainder.ts": 2938, + "5460-locale-compare-contractions.ts": 2684, + "5461-locale-compare-equivalents.ts": 3166, + "5462-locale-compare-probe-sweep.ts": 4453, + "5463-locale-compare-combining-letters.ts": 2871, + "5464-locale-compare-fractions.ts": 3156, + "5465-dyn-index-read-arrays.ts": 3603, + "5466-dyn-index-read-special.ts": 2823, + "5470-strip-vt-osc.cjs": 2872, + "5471-strip-vt-csi.cjs": 2359, + "5472-strip-vt-random.cjs": 3265, + "5473-strip-vt-unterminated.cjs": 3751, + "5474-strip-vt-surrogates.ts": 2805, + "5510-assert-error-properties.js": 2744, + "5511-assert-error-class.ts": 3998, + "5512-assert-error-options.js": 3572, + "5513-assert-deep-equal-loose.js": 3537, + "5514-assert-deep-equal-typed.ts": 2922, + "5515-assert-deep-strict-typed-messages.ts": 2776, + "5516-assert-deep-equal-edges.js": 3465, + "5517-assert-partial-deep-equal.js": 4023, + "5518-assert-partial-deep-typed.ts": 3538, + "5519-assert-partial-deep-typed-edges.ts": 3741, + "5520-assert-record-literal-failures.ts": 3435, + "5520-dyn-property-read-receivers.ts": 3114, + "5521-assert-empty-message.js": 3945, + "5521-dyn-property-read-keys.ts": 2472, + "5522-assert-throws-failure-properties.ts": 3505, + "5523-assert-class-options.js": 3797, + "5524-assert-class-skip-prototype.ts": 2105, + "5530-buffer-pool.ts": 3788, + "5531-buffer-pool-first-slab.ts": 5451, + "5532-buffer-pool-first-slab-untyped.mjs": 37592, + "5540-fs-callback-forms.cjs": 3863, + "5541-fs-stats-fields.cjs": 4344, + "5542-fs-promises-parity.cjs": 2640, + "5543-fs-promises-typed.ts": 3538, + "5544-fs-opendir.cjs": 3175, + "5545-fs-options-encoding.cjs": 3831, + "5546-fs-read-stream.cjs": 3808, + "5547-fs-write-stream.cjs": 5798, + "5548-fs-callback-arguments.cjs": 4728, + "5549-fs-callback-option-checks.cjs": 4270, + "5550-locale-compare-ascii-words.ts": 2457, + "5551-array-search-references.ts": 2240, + "5552-string-appends.ts": 3367, + "5553-string-append-char-code.ts": 2628, + "5554-fs-streams-typed.ts": 4016, + "5555-fs-callback-turns.cjs": 3136, + "5555-http-server-close-keep-alive.ts": 6777, + "5556-http-client-agent-connection.ts": 3427, + "5557-http-outgoing-header-values.ts": 2893, + "5558-http-incoming-duplicate-headers.ts": 3230, + "5559-http-expect-header.ts": 3517, + "5560-http-response-framing.ts": 5004, + "5561-http-max-header-size.ts": 3610, + "5562-https-verify-errors.ts": 3646, + "5563-http-keep-alive-timeout.ts": 11895, + "5564-http-request-signal.ts": 4124, + "5565-fs-dirent-untyped.mjs": 2565, + "5566-fs-dirent-dyn-boundaries/main.mjs": 3508, + "5567-fs-cpsync.ts": 3736, + "5568-fs-cp-errors.js": 4246, + "5569-fs-cp-symlinks.ts": 2136, + "5570-fs-cp-async.ts": 3348, + "5571-fs-cp-deep.ts": 4817, + "5585-record-results-inlined.ts": 3419, + "5595-net-ref-unref.ts": 3245, + "5600-dyn-cycle-self.ts": 2318, + "5601-dyn-cycle-mutual.ts": 2455, + "5602-dyn-cycle-arrays.ts": 3115, + "5603-dyn-cycle-identity.ts": 3197, + "5604-dyn-cycle-checks.ts": 3210, + "5605-dyn-cycle-containers.ts": 2935, + "5606-dyn-cycle-capsules.ts": 2308, + "5610-readable-from-sets-and-views.ts": 3154, + "5611-readable-from-class-iterables.ts": 3984, + "5612-readable-from-iterables.cjs": 3740, + "5613-readable-from-iterable-timing.cjs": 4549, + "5614-readable-from-iterable-errors.cjs": 4240, + "5615-next-tick-callback-phases.ts": 3053, + "5616-next-tick-callback-phases.cjs": 3322, + "5617-pipeline-function-ends.ts": 2710, + "5618-pipeline-function-ends-errors.ts": 3693, + "5619-pipeline-function-ends.cjs": 3677, + "5620-timers-promises-values.ts": 2181, + "5621-util-format-float.ts": 2886, + "5622-events-once.mjs": 3520, + "5623-events-once.ts": 2509, + "5624-timers-promises-unawaited.ts": 2304, + "5625-promisify-exec.ts": 7463, + "5626-promisify-forms.ts": 2936, + "5627-exec-callback.ts": 2909, + "5628-promisify-timers.ts": 3194, + "5629-promisify-stream.ts": 2517, + "5630-promisify-zlib.ts": 2503, + "5631-promisify-crypto.ts": 4296, + "5632-promisify-dns.ts": 2784, + "5644-net-socket-addresses.ts": 2435, + "5645-net-socket-callbacks.ts": 2678, + "5646-net-server-connections.ts": 3279, + "5647-net-is-ip.ts": 2496, + "5648-dns-lookup-forms.ts": 2735, + "5649-dns-service-servers.ts": 3175, + "5650-dns-lookup-errors-untyped.cjs": 1968, + "5651-tls-socket-protocol.ts": 2771, + "5652-http-client-trailer-overflow.ts": 3884, + "5653-net-socket-read-ahead.ts": 4524, + "5654-https-empty-servername.ts": 3451, + "5655-dgram-socket-options.ts": 3481, + "5656-dgram-option-states.ts": 3002, + "5657-net-reset-and-address.ts": 3415, + "5658-net-reset-dynamic.cjs": 2564, + "5659-net-blocklist-socketaddress.ts": 4443, + "5664-tls-client-identity.ts": 3727, + "5674-union-instanceof-array-arms.ts": 2159, + "5680-array-growth-after-copy.ts": 3791, + "5681-array-pop-and-length.ts": 2915, + "5682-publish-wide-objects.ts": 3326, + "5700-cycle-release-retained.ts": 2156, + "5701-cycle-release-old-edges.ts": 2601, + "5702-publish-candidate-buffers.ts": 3469, + "5703-lifetime-region-debug.ts": 3115, + "5704-publish-refused-restores-counts.ts": 2979, + "5710-map-number-keys-hashed.ts": 3019, + "5711-map-number-write-after-read.ts": 2613, + "5712-map-number-overwrite-at-growth.ts": 2614, + "5714-cycle-large-candidate-buffers.ts": 3979, + "5715-json-parse-fitted-tables.ts": 3940, + "5717-set-prototype-of-primitives.ts": 3887, + "5719-int32-fields-dynamic-operators.ts": 3207, + "5720-intern-table-basics.ts": 3625, + "5721-intern-table-values.ts": 4014, + "5722-intern-table-workers.ts": 3210, + "5723-intern-table-shared-types.ts": 4601, + "5724-intern-table-array-keys.ts": 4072, + "5725-intern-table-callbacks.ts": 1996, + "5726-intern-table-key-limits.ts": 2644, + "5740-atomics-operation-semantics.ts": 4810, + "5741-atomics-contended-counters.ts": 2504, + "5742-atomics-lock-ordering.ts": 3000, + "5743-atomics-wait-notify.ts": 3638, + "5744-atomics-ring-buffer.ts": 3352, + "5745-atomics-semaphore-stress.ts": 3671, + "5746-shared-bulk-operations.ts": 2732, + "5747-fs-stat-throw-if-no-entry.ts": 2662, + "5748-fs-stat-throw-if-no-entry-js.cjs": 3085, + "5752-string-number-concat.ts": 3186, + "5760-crypto-hash-stream.ts": 3011, + "5761-crypto-hash-stream-states.ts": 3241, + "5762-crypto-hash-stream.cjs": 3286, + "5763-crypto-sha-blocks.ts": 3264, + "5764-class-structural-view-writes.ts": 4118, + "5764-crypto-hmac-edges.cjs": 2105, + "5765-class-structural-view-identity.ts": 3529, + "5766-class-structural-view-callbacks.ts": 3162, + "5767-class-opaque-accessor-view.ts": 2108, + "5770-guarded-union-narrows.ts": 2881, + "5771-guarded-downcasts.ts": 3555, + "5772-guarded-catch-narrows.ts": 2601, + "5773-stale-narrowing-inherited.ts": 2486, + "5776-power-of-two-remainder-integer-path.ts": 3263, + "5780-readline-history-options.ts": 2909, + "5781-readline-history-options.cjs": 3288, + "5782-readline-terminal-editing.ts": 2661, + "5783-readline-terminal-completion.cjs": 4948, + "5784-readline-terminal-promises.ts": 3042, + "5785-readline-terminal-keys.ts": 3376, + "5786-readline-terminal-history.cjs": 2745, + "5787-readline-cursor-helpers.cjs": 3241, + "5788-readline-promises-readline.ts": 3322, + "5789-readline-cursor-pos.cjs": 3206, + "5790-readline-keypress-events.ts": 3280, + "5791-dyn-constant-key-read-sites.ts": 2813, + "5791-readline-keypress-interfaces.cjs": 3397, + "5792-json-parse-repeated-layouts.ts": 3939, + "5792-readline-keypress-listeners.ts": 2151, + "5793-worker-message-channel.ts": 3274, + "5794-worker-message-port-close-order.ts": 3161, + "5795-worker-message-port-batches.ts": 1925, + "5796-worker-message-channel.cjs": 2678, + "5797-worker-message-port-transfer.ts": 3445, + "5800-broadcast-channel.ts": 3609, + "5801-broadcast-channel.cjs": 3080, + "5802-broadcast-channel-workers.ts": 2610, + "5803-worker-environment-data.ts": 3416, + "5804-worker-environment-data.cjs": 2230, + "5805-worker-receive-message-parent-port.ts": 3538, + "5806-worker-resource-limits.ts": 2813, + "5807-worker-error-values.ts": 4237, + "5808-worker-unhandled-rejections.ts": 4239, + "5809-process-exit-and-rejections.cjs": 3639, + "5810-mock-method-records.ts": 3521, + "5811-mock-tracker-restore.ts": 2838, + "5812-dyn-box-async-fields.ts": 3501, + "5813-mock-timers-module.ts": 3107, + "5814-mock-timers-date.ts": 2412, + "5830-timers-promises-signal.ts": 3875, + "5831-util-types-brands.ts": 4018, + "5832-util-types-brands-dynamic.js": 3859, + "5833-url-search-params-iterators.ts": 2320, + "5834-url-search-params-iterators-dynamic.mjs": 3863, + "5835-util-format-integer-specifiers.ts": 2838, + "5836-url-search-params-tuple-pairs.ts": 3097, + "5837-util-format-json-circular.ts": 2711, + "5838-qs-stringify-bigint.ts": 3073, + "5839-timers-promises-scheduler.ts": 3195, + "5840-promise-with-resolvers-js.js": 2842, + "5841-json-typed-dyn-members.ts": 3759, + "5842-qs-parse-null-prototype.ts": 3419, + "5843-qs-parse-null-prototype-js.mjs": 2453, + "5844-date-unknown-values.ts": 3400, + "5845-groupby-null-prototype.ts": 2868, + "5846-events-esm-default-max.ts": 2670, + "5847-events-cjs-default-max.cjs": 2274, + "5849-class-view-observations.ts": 3073, + "5850-using-disposable-values.ts": 3391, + "5851-using-suppressed-error-values.ts": 2932, + "5852-using-disposable-values-js.js": 3132, + "5853-set-methods-map-arguments.ts": 3034, + "5854-array-from-async.ts": 4140, + "5855-array-from-async-js.js": 3324, + "5856-error-is-error.ts": 2937, + "5857-regexp-unicode-sets.ts": 2520, + "5858-disposable-stack.ts": 4398, + "5859-disposable-stack-js.js": 3669, + "5860-url-ipv4-hosts.ts": 3101, + "5870-worker-thread-names.ts": 3899, + "5871-worker-untransferable.cjs": 3409, + "5872-readline-positional-create-interface.mjs": 3151, + "5873-readline-promises-namespace.cjs": 2023, + "5874-worker-stdio-streams.ts": 3395, + "5875-worker-stdout-pipe.cjs": 3257, + "5879-worker-stdin.ts": 3922, + "5880-emitter-leak-warning-delivery.ts": 3418, + "5881-inspect-weak-collections.ts": 2457, + "5882-string-decoder-runtime-encoding.ts": 2823, + "5883-os-set-priority.ts": 2720, + "5884-inspect-layout-options.ts": 3781, + "5885-events-max-listener-helpers.ts": 2992, + "5886-inspect-dates.ts": 3072, + "5887-inspect-typed-arrays.ts": 2960, + "5888-inspect-sorted.ts": 3397, + "5889-timers-scheduler-signal.ts": 2284, + "5890-while-string-length-counters.ts": 2516, + "5892-buffer-write-utf16le-runs.ts": 3107, + "5900-float16-array.ts": 3631, + "5901-float16-array-js.js": 3789, + "5904-async-generator-completion.ts": 5745, + "5905-array-from-async-generator.ts": 3532, + "5906-array-from-async-generator-js.js": 3910, + "5907-regex-groups-match-rows.ts": 4071, + "5910-set-prototype-of-primitive-object-kinds.ts": 3248, + "5911-dyn-class-to-primitive.ts": 6701, + "5912-dyn-class-conversion-methods.ts": 4374, + "5913-prototype-type-error-messages.ts": 3102, + "5914-dyn-in-class-prototype-members.ts": 2530, + "5915-dyn-error-string-conversions.ts": 3023, + "5920-promise-try-args.ts": 3028, + "5921-promise-try-args-js.js": 3179, + "5922-matchall-groups-callbacks.ts": 4418, + "5923-matchall-groups-js.js": 3161, + "5924-js-literal-identity.js": 2577, + "5925-class-view-identity-observers.ts": 2297, + "5926-class-view-switch-js.js": 2243, + "5927-switch-object-identity.ts": 2986, + "5928-groupby-iterables.ts": 3444, + "5929-groupby-js.js": 2997, + "5930-contract-int32-arrays.ts": 3715, + "5930-method-census-asserted-number-keys.ts": 3330, + "5931-asserted-reads-keep-number-parameters.ts": 2990, + "5932-generic-class-observed-methods.ts": 4272, + "5933-class-fields-unknown-object.ts": 4189, + "5934-js-class-fields-property-order.js": 4613, + "5935-js-subclass-fields-and-element-calls.js": 2484, + "5936-checked-js-class-types.js": 3412, + "5937-override-narrowed-union-returns.ts": 3036, + "5938-object-literal-computed-keys-keep-methods.ts": 3273, + "5939-js-let-object-reassigned-wider.js": 1819, + "5940-callback-helper-specialization.ts": 3109, + "5941-callback-specialization-recursion.ts": 3112, + "5942-callback-specialization-throws.ts": 3221, "5942-local-class-observed-methods.ts": 5063, - "5950-tuple-predicate-methods.ts": 5990, - "5951-result-never-widening.ts": 4710, - "5955-any-promise-methods.js": 3940, - "5956-erased-generic-members.ts": 6730, - "5958-asserted-union-receiver.ts": 2430, - "5959-date-to-json.ts": 3330, - "5960-generic-mapped-types.ts": 2540, - "5970-net-connect-host-name.ts": 2400, - "5971-buffer-chunks-to-string.ts": 2700, - "5972-net-set-encoding-listeners.ts": 2550, + "5943-fields-read-before-assignment.ts": 3902, + "5944-js-fields-read-before-assignment.js": 2868, + "5945-local-class-observed-methods.ts": 2284, + "5946-nested-record-field-optional-writes.ts": 2987, + "5950-tuple-predicate-methods.ts": 4611, + "5951-result-never-widening.ts": 2943, + "5952-record-missing-keys.ts": 2332, + "5953-nan-coded-method-returns.ts": 2799, + "5955-any-promise-methods.js": 3148, + "5956-erased-generic-members.ts": 4927, + "5958-asserted-union-receiver.ts": 2198, + "5959-date-to-json.ts": 3051, + "5960-frame-records-own-references.ts": 3563, + "5960-generic-mapped-types.ts": 2996, + "5961-frame-spread-updates.ts": 3260, + "5962-frame-tuples.ts": 3208, + "5970-net-connect-host-name.ts": 1747, + "5971-buffer-chunks-to-string.ts": 3079, + "5972-net-set-encoding-listeners.ts": 3253, "5973-buffer-concat-untyped.js": 1900, - "5974-typeof-module-exports.cjs": 2000, - "5990-worker-uncloneable.mjs": 3283, - "5991-worker-async-dispose.mjs": 5699, - "5992-worker-thread-name-lifetime.mjs": 2687, - "600-closures-basic.ts": 2349, - "601-closures-loops.ts": 1788, - "602-closures-identity-recursion.ts": 2372, - "603-closures-rc-stress.ts": 1832, - "604-closures-for-of.ts": 2639, - "605-closures-forward-capture-tdz.ts": 3277, - "700-classes-basic.ts": 2530, - "701-classes-composition.ts": 2357, - "702-classes-this-capture.ts": 2140, - "703-classes-rc-stress.ts": 2186, - "710-inheritance-basics.ts": 2728, - "711-inheritance-dispatch.ts": 2360, - "712-inheritance-instanceof.ts": 2715, - "713-inheritance-rc-stress.ts": 2448, - "720-accessors-basics.ts": 2562, - "721-accessors-eval-order.ts": 2406, - "722-accessors-inheritance.ts": 2543, - "723-accessors-rc-stress.ts": 2054, - "750-cycle-closure-box.ts": 2227, - "751-cycle-records-mutual.ts": 2118, - "752-cycle-classes.ts": 2207, - "753-cycle-owned-arrays.ts": 2148, - "754-cycle-external-ref.ts": 2118, - "755-cycle-async-promise.ts": 2303, - "756-cycle-inheritance.ts": 2162, - "757-cycle-cross-generation.ts": 2893, - "762-any-boundary.ts": 2310, - "764-any-async.ts": 2553, - "765-any-optional-chain.ts": 2593, - "766-any-union-boundary.ts": 3292, - "767-string-literal-keys.ts": 2238, - "768-any-uninitialized.ts": 2264, - "800-switch-basics.ts": 2244, - "801-switch-lazy-tests.ts": 2610, - "802-switch-loops.ts": 2329, - "803-switch-rc-stress.ts": 2314, - "804-switch-braced-blocks.ts": 2594, - "810-do-while.ts": 2658, - "820-multi-decl.ts": 2315, - "830-let-uninitialized.ts": 2519, - "900-records-basics.ts": 2736, - "901-records-eval-order.ts": 2544, - "902-records-functions-closures.ts": 2645, - "903-records-rc-stress.ts": 2871, - "904-records-optional-fields.ts": 1801, - "905-records-optional-rc-stress.ts": 2425, - "906-records-utility-types.ts": 2878, - "907-records-utility-generics.ts": 2738, - "908-records-index-signatures.ts": 2545, - "909-records-index-json.ts": 2019, - "910-records-index-rc-stress.ts": 1878, - "911-records-index-dot-access.ts": 3510, - "912-unknown-slots.ts": 2427, - "913-records-index-iteration.ts": 3462, - "914-records-from-entries.ts": 3129, - "915-unknown-tostring-eq.ts": 3072, - "916-unknown-bytes.ts": 2767, - "917-record-array-index-dot.ts": 2445, - "950-modules-basic/main.ts": 2473, - "951-modules-diamond/main.ts": 1969, - "952-modules-cross/main.ts": 2427, - "953-modules-default/main.ts": 2409, - "954-modules-reexport/main.ts": 2586, - "957-builtins-namespace.ts": 2102, - "960-generics-basics.ts": 2487, - "961-generics-recursion.ts": 2589, - "962-generics-closures.ts": 2098, - "963-generics-modules/main.ts": 2726, - "964-generics-rc-stress.ts": 2825, - "965-generics-async.ts": 2802, - "965-unions-retag.ts": 2772, - "966-unions-retag-rc-stress.ts": 2336, - "967-record-field-widening.ts": 2097, - "969-unions-arm-matrix.ts": 2419, - "970-unions-basics.ts": 2692, - "971-unions-switch.ts": 2491, - "972-unions-nested.ts": 3069, - "973-unions-rc-stress.ts": 2478, - "974-unions-modules/main.ts": 2052, - "975-unions-undefined.ts": 2811, - "976-unions-null.ts": 2344, - "977-unions-unit-rc-stress.ts": 2013, - "978-unions-nullish.ts": 2330, - "979-unions-optional-chaining.ts": 2341, - "980-exceptions-basics.ts": 1988, - "981-exceptions-values.ts": 2615, - "982-exceptions-rc-stress.ts": 2259, - "983-exceptions-control-flow.ts": 2004, - "984-exceptions-finally.ts": 2140, - "985-exceptions-call-chains.ts": 2787, - "986-exceptions-uncaught.ts": 2529, - "987-exceptions-result-unions.ts": 2390, - "990-process-basics.ts": 2623, - "991-process-exit.ts": 2155, - "992-fs-roundtrip.ts": 2680, - "993-fs-readdir.ts": 2781, - "994-fs-errors.ts": 2322, - "995-fs-uncaught.ts": 2134, - "996-fs-rc-stress.ts": 2214, - "997-fs-modules/main.ts": 2230, - "998-process-env.ts": 2196, - "abstract-property-dispatch.ts": 2956, - "arguments-arrow-capture.mjs": 2802, - "arguments-reexport/main.ts": 2655, - "array-argument-lifetimes-exits.ts": 2164, - "array-argument-lifetimes-mutation.ts": 2547, - "array-argument-lifetimes-strings.ts": 2738, - "array-argument-lifetimes-values.ts": 3081, - "array-buffer-conversions.js": 1978, - "array-buffer-storage.ts": 3147, - "array-checked-callback-results.mjs": 3053, - "array-checked-constructor.js": 3001, - "array-concat-widening.ts": 2947, - "array-element-compound-updates.ts": 3325, - "array-fill-unit-values.ts": 2281, - "array-from-checked.js": 3228, - "array-from-value.mjs": 2658, - "array-isarray-value/main.js": 2180, - "array-numeric-own-keys.ts": 2770, - "array-optional-number-write-index.ts": 2584, - "array-primitive-ordering.ts": 3806, - "array-read-helper-effects.ts": 3223, - "array-refined-storage.ts": 3132, - "array-sort-dense-effects.ts": 2813, - "array-sort-dense-values.ts": 3705, - "array-sort-holes-published.ts": 6604, - "array-sort-runs.ts": 5713, - "array-sort-runtime-comparator.ts": 5050, - "array-sort-unknown-comparator.ts": 1888, - "array-storage-transitions.ts": 3122, - "array-truncate-reuse.ts": 2475, - "async-backend-factory.mjs": 2638, - "async-local-typed-results.ts": 2579, - "atomics-integer-operations.ts": 3320, - "bigint-checked-casts.ts": 2572, - "bigint-checked-storage.js": 3519, - "bigint-union-conversion.ts": 2774, - "boxed-error-rethrows.mjs": 2034, - "buffer-codec-boundaries.ts": 3206, - "buffer-primitive-predicate.ts": 2712, - "builtin-prototype-method-values.mjs": 3158, - "byte-loop-bounds.ts": 2922, - "byte-number-pipeline.ts": 2738, - "byte-record-windows.ts": 2668, - "call-lifetimes-argument-order.ts": 1933, - "call-lifetimes-control-flow.ts": 2653, - "call-lifetimes-owned-boundaries.ts": 2979, - "call-lifetimes-projections.ts": 3011, - "call-lifetimes-union-values.ts": 2366, - "call-lifetimes-virtual-optionals.ts": 2747, - "callable-module-members.mjs": 2647, - "callable-properties.mjs": 3266, - "callable-property-cycles.js": 2241, - "callable-reexport/main.ts": 2809, - "captured-class-refinement.ts": 2580, - "catch-capture.ts": 2050, - "caught-class-identity.ts": 3173, - "caught-standalone-class.ts": 2858, - "caught-system-error-fields.ts": 2612, - "checked-addition.js": 2460, - "checked-any-addition.ts": 2551, - "checked-array-assignment.ts": 2273, - "checked-array-flat/main.ts": 2256, - "checked-bitwise/main.js": 2560, - "checked-builder-cycles.js": 3055, - "checked-byte-storage/main.ts": 3171, - "checked-callback-options.ts": 2385, - "checked-class-descriptors.mjs": 3021, - "checked-class-factory.mjs": 2266, - "checked-collection-iterators.ts": 2906, - "checked-collection-symbol-methods.ts": 2575, - "checked-dispatch-input-lifetimes.js": 3038, - "checked-increment/main.js": 2332, - "checked-inherited-fields.ts": 2382, - "checked-iterator-consumption.js": 3603, - "checked-numeric-arithmetic.js": 2783, - "checked-object-module-alias.js": 2898, - "checked-optional-calls.mjs": 2763, - "checked-promise-recovery.ts": 2689, - "checked-proxy-callback/main.ts": 2754, - "checked-record-constructor.ts": 2506, - "checked-renderer-defaults.js": 2308, - "checked-rest-function-adapters.js": 2312, - "checked-storage-transitions.js": 2557, - "checked-symbol-array.ts": 1859, - "checked-value-input-lifetimes.ts": 2728, - "class-accessor-returned-function.js": 2068, - "class-array-optional-return.ts": 2589, - "class-bracket-method-calls.js": 2833, - "class-brand-overload.js": 2700, - "class-callback-replacement.js": 3640, - "class-callback-storage.ts": 3803, - "class-checked-composites.ts": 2846, - "class-checked-upcast.ts": 2625, - "class-collection-insertion.ts": 3901, - "class-computed-presence.ts": 3018, - "class-computed-symbols.js": 3495, - "class-covariant-returns.ts": 3069, - "class-curried-factory.mjs": 3062, - "class-data-descriptors.js": 3732, - "class-factory-inherited-statics.mjs": 2710, - "class-factory-parameter-base.mjs": 2492, - "class-factory-record-base.mjs": 4680, - "class-factory-recursive-statics.mjs": 2820, - "class-factory-static-members.mjs": 2990, - "class-function-base/main.js": 2706, - "class-instance-constructor.js": 2628, - "class-interface-protocols.ts": 3911, - "class-interface-refinements.ts": 2967, - "class-json-checked.ts": 2638, - "class-local-function-base.mjs": 2970, - "class-method-spreads.ts": 3142, - "class-narrowed-views.ts": 2862, - "class-property-call-values.mjs": 3343, - "class-prototype-data.js": 3393, - "class-prototype-descriptor-copy.mjs": 3870, - "class-specialized-computed-base.mjs": 3439, - "class-static-data.js": 2153, - "class-subclass-assertions.ts": 2814, - "class-super-surplus-spread.mjs": 2024, - "class-symbol-accessors.ts": 2458, - "class-symbol-cache.mjs": 2883, - "class-value-constructor-exceptions.ts": 2222, - "closure-nullable-union-return.ts": 2472, - "closure-union-locals.ts": 2465, - "collection-array-cycles.ts": 2552, - "collection-array-maps.ts": 3481, - "collection-array-missing.ts": 2873, - "collection-array-sets.ts": 3383, - "collection-copy-identities.ts": 2585, - "collection-data-unions.ts": 2565, - "collection-ingestion-mappers.ts": 3262, - "collection-ingestion-protocol/main.ts": 3814, - "collection-ingestion-references.ts": 3375, - "collection-ingestion.ts": 3053, - "collection-storage-copies.ts": 3096, - "collection-value-keys.ts": 3176, - "compound-field-receivers.ts": 2272, - "compound-presence-guards.ts": 3094, - "conditional-tuple-destructuring.ts": 2487, - "console-imports.cjs": 2314, - "console-native-value.js": 2165, - "const-enum-module-cycle/main.ts": 2618, - "constant-callback-dispatch.ts": 2539, - "constant-module-globals/main.ts": 2455, - "constant-number-remainders.ts": 2350, - "constructed-class-dispatch.ts": 3014, - "contextual-array-union-write.ts": 5379, - "contextual-return-layouts.ts": 2919, - "counted-loop-numbers.ts": 2515, - "counted-loop-storage.ts": 2556, - "crypto-hash-optional-input.ts": 2860, - "cycle-generations-long-lived.ts": 2691, - "cycle-growth-yield.ts": 2929, - "date-checked-native.js": 2323, - "declared-index-spread.ts": 2660, - "deep-reference-teardown.ts": 3136, - "default-undefined-parameters.ts": 2589, - "deferred-field-updates.ts": 2347, - "deferred-registry-many.ts": 2661, - "derived-numeric-updates.ts": 1907, - "destructure-call-assignment.js": 2267, - "destructure-inferred-row-defaults.js": 2538, - "destructure-union-arrays.ts": 2379, - "dynamic-function-descriptors.js": 2763, - "error-cause-writes.js": 2954, - "error-inherited-message.mjs": 2631, - "error-inherited-name.mjs": 2285, - "error-property-writes.mjs": 2636, - "error-prototype-tostring.js": 3155, - "error-stack-frames.ts": 2206, - "error-stack-limit.ts": 2510, - "error-tostring-overrides.ts": 2305, - "error-values-inspect.ts": 2398, - "exact-index-round-trip.ts": 2801, - "expression-runtime-values.ts": 2027, - "fiber-switch-stress.ts": 2898, - "fiberless-async-ordering.ts": 2908, - "fiberless-async-unhandled.ts": 2133, - "field-assignment-values.ts": 2107, - "file-handle-values.ts": 2357, - "file-read-storage.ts": 2439, - "file-url-percent-decoding.ts": 2222, - "final-field-borrows.ts": 2890, - "for-multiple-bindings.ts": 2286, - "for-multiple-lexical-bindings.ts": 2283, - "for-of-checked-iterator.js": 3511, - "for-of-subclass-elements.ts": 2219, - "forward-generic-signatures.ts": 2294, - "fresh-array-union-layout.js": 2480, - "frozen-checked-dictionaries.mjs": 2629, - "fs-native-realpath.ts": 2351, - "fs-read-encodings.ts": 2505, - "fs-remove-readonly.ts": 2354, - "fs-write-string-bytes-union.ts": 2286, - "function-apply-value.mjs": 2558, - "function-constructor-global.mjs": 1848, - "function-constructor-kinds/main.js": 2532, - "function-custom-prototype.js": 2604, - "function-custom-to-string.mjs": 2607, - "function-object-assign.js": 2509, - "function-own-prototype/main.js": 2423, - "generator-checked-protocol.ts": 2631, - "generator-checked-return.js": 2326, - "generator-custom-delegation/main.ts": 3343, - "generator-default-return-pattern.ts": 2653, - "generator-delegation-values.ts": 2368, - "generator-inferred-date.js": 2465, - "generator-mixed-checked-channels.ts": 2662, - "generator-untyped-consumer/main.ts": 2958, - "generic-base-specializations.ts": 2073, - "generic-binding-ownership.ts": 2817, - "generic-class-js-functions/main.ts": 2051, - "generic-class-recovery.ts": 2415, - "generic-namespace-factories/main.ts": 2073, - "generic-nullable-collections.ts": 2677, - "generic-pattern-bindings.ts": 2698, - "generic-record-index.ts": 2753, - "generic-visitor-void.ts": 2405, - "global-error-subclasses.ts": 1773, - "global-runtime-probes.js": 2288, - "global-stored-probes.js": 2726, - "global-string-cache/main.js": 1889, - "global-symbol-cache/main.js": 3399, - "guarded-native-iterables.ts": 2496, - "heap-input-aliases.ts": 2746, - "heap-input-collections.ts": 2558, - "in-array-runtime-index.ts": 2579, - "indexed-assignment-values.js": 2971, - "indexed-assignment-values.ts": 1852, - "indexed-element-field-reads.ts": 2452, - "inferred-array-assignment-values.mjs": 2352, - "inferred-class-default.mjs": 2460, - "inferred-compound-values.mjs": 2473, - "inferred-conditional-class.mjs": 2635, - "inferred-error-fields.js": 2509, - "inferred-map-patterns.mjs": 2353, - "inferred-method-override.js": 2137, - "inferred-partial-destructuring.mjs": 2205, - "inferred-primitive-properties.mjs": 2135, - "inferred-record-return.mjs": 1793, - "inferred-recursive-record.mjs": 1833, - "inferred-undefined-field.mjs": 1777, - "input-lifetime-intervals.ts": 2714, - "input-lifetimes-modules/main.ts": 3258, - "instanceof-class-record-union.ts": 2400, - "integer-flow-arithmetic.ts": 3652, - "integer-loop-forms.ts": 2368, - "integer-view-control.ts": 1809, - "integer-view-numbers.ts": 2394, - "integer-view-storage.ts": 2439, - "intl-grapheme-conformance/main.ts": 3718, - "intl-grapheme.ts": 2449, - "iterator-typed-returns.ts": 3400, - "js-abort-portable.js": 2829, - "js-array-constructor.js": 3163, - "js-array-default-buffer.js": 2537, - "js-array-iterators.js": 2559, - "js-arrow-bind.cjs": 3343, - "js-buffer-callback-brand.cjs": 2262, - "js-callable-proxy.js": 3516, - "js-chained-native-results.mjs": 2641, - "js-checked-array-iterator.js": 2670, - "js-checked-array-reference.js": 2270, - "js-checked-number-negation.mjs": 1915, - "js-checked-sort-result.js": 2373, - "js-child-error-listener.cjs": 2357, - "js-cjs-callable-snapshots/main.cjs": 1981, - "js-class-added-parameters.js": 2472, - "js-class-checked-overrides.js": 2571, - "js-class-computed-fields.js": 1830, - "js-class-enumeration.js": 3296, - "js-class-field-accessor-override.js": 2567, - "js-class-fluent-overrides.js": 2822, - "js-class-late-method-values.js": 2309, - "js-class-member-dispatch.js": 3016, - "js-class-return-shapes.js": 2231, - "js-class-shadowed-methods.js": 2942, - "js-class-stored-method-receiver.js": 2382, - "js-class-subclass-properties.js": 3695, - "js-class-symbol-spread.cjs": 2704, - "js-computed-prototype-additions.js": 3142, - "js-constructor-returned-callable.js": 2605, - "js-constructor-spread.js": 2289, - "js-dataview-dispatch.js": 2273, - "js-declared-fields.cjs": 3263, - "js-default-listener-arguments.cjs": 2549, - "js-default-options/main.ts": 3044, - "js-deferred-field-initializers.js": 2121, - "js-deferred-registry-effects.js": 2381, - "js-derived-constructor-hoisting.js": 1805, - "js-dormant-array-write.js": 1888, - "js-dormant-overrides.js": 2569, - "js-expando-records.js": 2454, - "js-implicit-call-results.js": 3064, - "js-inferred-callback-exports/main.js": 3830, - "js-inferred-class-array-return.js": 2570, - "js-inferred-object-values.js": 2380, - "js-inherited-field-storage.js": 3121, - "js-inherited-parameter-abi.js": 2210, - "js-iterable-spread.js": 2950, - "js-loose-dynamic-primitives.js": 1960, - "js-map-object-seeds.js": 2610, - "js-member-increment.js": 2926, - "js-module-function-values.js": 2014, - "js-mutable-null-members.js": 2370, - "js-nested-native-descriptors.js": 2412, - "js-normalize-dispatch.js": 2793, - "js-nullish-inherited-fields.mjs": 2710, - "js-object-receiver-storage.js": 2613, - "js-object-returning-constructors.mjs": 2186, - "js-object-shared-arrays.js": 2465, - "js-omitted-typed-arguments.js": 2236, - "js-open-binding-writes.js": 1787, - "js-optional-class-fields.js": 2266, - "js-optional-member-call.js": 1883, - "js-primitive-union-addition.js": 2256, - "js-promise-all-values.js": 3079, - "js-promise-catch-handler.js": 1749, - "js-promise-finally-order.js": 1913, - "js-promise-pending-reactions.js": 2644, - "js-prototype-host-boundary.js": 2224, - "js-reflect-receivers.js": 3119, - "js-regex-record-captures.js": 2010, - "js-scalar-iterables.js": 2477, - "js-shared-method-dispatch.js": 2033, - "js-specialized-field-method.js": 2335, - "js-static-accessors.js": 2520, - "js-static-method-values.js": 2194, - "js-static-registries.cjs": 2703, - "js-super-accessor-initialization.js": 2100, - "js-uninitialized-native-fields.js": 2180, - "json-guarded-map-values.ts": 2374, - "json-output-bulk.ts": 2827, - "json-runtime-options.mjs": 1997, - "json-stringify-value.js": 2563, - "json-typed-parse-direct.ts": 2355, - "literal-switch-dispatch.ts": 2500, - "live-reference-shared-converters.ts": 2552, - "llvm-read-receiver-lifetime.ts": 2343, - "local-class-call-spreads.js": 3208, - "local-class-declaration.ts": 2486, - "local-class-evaluation.ts": 2605, - "local-class-inheritance.ts": 2327, - "local-class-lifetimes.ts": 2376, - "local-class-namespace/main.js": 2940, - "local-class-prototype-data.js": 2781, - "local-class-specialization.js": 2399, - "local-class-unknown.ts": 2443, - "local-class-values.ts": 1784, - "local-union-storage.ts": 2967, - "locale-compare-root-order.ts": 2518, - "logical-assignment-dynamic.js": 2592, - "logical-assignment-values.ts": 2497, - "logical-runtime-optional-record.ts": 2944, - "loop-reference-lifetimes.ts": 2542, - "map-checked-boundary/main.ts": 2629, - "map-checked-seed.js": 3013, - "map-number-keys-dense.ts": 2562, - "map-read-lifetimes-exits.ts": 2227, - "map-read-lifetimes.ts": 2725, - "map-seeds-and-spreads.ts": 2719, - "map-typed-array-values.ts": 2850, - "math-function-values.js": 2367, - "missing-read-lambda-results.ts": 2379, - "missing-read-narrowed-storage.ts": 2498, - "missing-read-number-conversions.ts": 2181, - "module-byte-loops.ts": 2636, - "module-fallback-functions/main.js": 2148, - "narrowed-nullish-values.ts": 2410, - "native-any-timer-callback.ts": 1834, - "native-array-borrowed-find-last.js": 2515, - "native-array-borrowed-slice.js": 2404, - "native-array-copying-methods.js": 2076, - "native-array-fill-copywithin.js": 2636, - "native-array-frozen-noops.js": 2476, - "native-array-index-boundaries.js": 1913, - "native-array-integrity.js": 2429, - "native-array-length.js": 2568, - "native-array-named-properties.js": 2390, - "native-array-prototype/main.js": 2161, - "native-array-read-lifetimes.ts": 2771, - "native-array-reduce-find-last.js": 2375, - "native-array-search-from-index.js": 1823, - "native-array-splice-coercion.js": 2296, - "native-array-splice-flat.js": 1933, - "native-buffer-coercion.js": 2389, - "native-callback-dispatch.ts": 3007, - "native-class-array-boundaries.ts": 2676, - "native-class-cache.js": 3391, - "native-class-properties.cjs": 3541, - "native-class-property-cycles.cjs": 2291, - "native-class-property-order.cjs": 1867, - "native-class-type-predicates.ts": 2386, - "native-factory-records.js": 2851, - "native-method-values.js": 2903, - "native-method-values.ts": 2495, - "native-npm-imports/main.ts": 2716, - "native-object-descriptors.js": 2717, - "native-object-for-in-minimal.js": 2536, - "native-object-property-attributes.js": 3149, - "native-object-prototype.js": 3180, - "native-property-names.ts": 1827, - "native-proxy-env.js": 3519, - "native-worker-arena-reuse.ts": 4699, - "native-worker-bounded-errors.ts": 2268, - "native-worker-buffer-consumers.ts": 2531, - "native-worker-cancel-hot.ts": 2446, - "native-worker-class-fields.ts": 2650, - "native-worker-clone-errors.ts": 2347, - "native-worker-commonjs.cjs": 2188, - "native-worker-delayed-messages.ts": 3114, - "native-worker-discarded-heaps.ts": 4430, - "native-worker-environment.ts": 3538, - "native-worker-error.ts": 2810, - "native-worker-exception-state.ts": 3938, - "native-worker-exit-listeners.ts": 2965, - "native-worker-io.ts": 3018, - "native-worker-loop-cancellation.ts": 2773, - "native-worker-message-graphs.ts": 2440, - "native-worker-messages.ts": 2911, - "native-worker-poll-exceptions.ts": 2706, - "native-worker-port-lifetime.ts": 3175, - "native-worker-process-exit.ts": 2168, - "native-worker-publish-classes.ts": 2797, - "native-worker-publish-refusals.ts": 2330, - "native-worker-published-graphs.ts": 3411, - "native-worker-references.ts": 2156, - "native-worker-roots/main.ts": 2962, - "native-worker-shared-views.ts": 2377, - "native-worker-stack-overflow.ts": 2443, - "native-worker-terminate-exception-state.ts": 3368, - "native-worker-terminate-loop.ts": 1929, - "native-worker-terminate-recursion.ts": 2935, - "native-worker-terminate-suspended.ts": 2843, - "native-worker-terminate-tested-loops.ts": 3082, - "native-worker-terminate-wait.ts": 2267, - "native-worker-typed-arrays.ts": 2361, - "nested-map-cycles.ts": 2306, - "nested-map-values.ts": 2038, - "never-returning-calls.ts": 2680, - "next-tick-commonjs-entry.cjs": 1924, - "node24-buffer-encoding-errors.ts": 2742, - "node24-buffer-encoding.ts": 2822, - "node24-crypto-kdf-errors.ts": 2607, - "node24-crypto-kdf-options.ts": 1925, - "node24-crypto-kdf.ts": 3401, - "node24-crypto-sha2.ts": 2336, - "node24-deep-equality-properties.js": 2551, - "node24-deep-equality.cjs": 2373, - "node24-deep-equality.ts": 3193, - "node24-env-files-errors.ts": 2555, - "node24-env-files.ts": 2556, - "node24-env-imports.cjs": 2518, - "node24-env-parse-errors.ts": 2083, - "node24-env-parse.ts": 2189, - "node24-esm-metadata/main.mjs": 2257, - "node24-file-url-buffer.ts": 1904, - "node24-file-url-errors.js": 2838, - "node24-file-url-options.ts": 2563, - "node24-fs-links-callbacks.cjs": 2964, - "node24-fs-links-errors.cjs": 2795, - "node24-fs-links-options.cjs": 3096, - "node24-fs-links-promise-errors.cjs": 2481, - "node24-fs-links-promises.ts": 2014, - "node24-fs-links.ts": 3060, - "node24-fs-statfs-callbacks.cjs": 2761, - "node24-fs-statfs-errors.cjs": 3068, - "node24-fs-statfs-options.cjs": 2927, - "node24-fs-statfs.ts": 2192, - "node24-fs-timestamps-callbacks.cjs": 2865, - "node24-fs-timestamps-errors.cjs": 2733, - "node24-fs-timestamps-promises.ts": 2476, - "node24-fs-timestamps-symlinks.ts": 2406, - "node24-fs-timestamps.ts": 2758, - "node24-no-error-assertions-coercion.js": 2950, - "node24-no-error-assertions.cjs": 2361, - "node24-no-error-assertions.ts": 2895, - "node24-parseargs.js": 3512, - "node24-string-decoder.ts": 2569, - "node24-style-text-environment.js": 2370, - "node24-style-text-validation.js": 2999, - "node24-style-text.cjs": 1883, - "node24-style-text.ts": 2102, - "node24-system-error-validation.js": 2003, - "node24-system-errors.cjs": 2247, - "node24-system-errors.ts": 2302, - "node24-text-codec-properties.ts": 2178, - "node24-text-encode-into.ts": 2427, - "node24-text-utilities-coercion.js": 2867, - "node24-text-utilities-validation.js": 1874, - "node24-text-utilities.cjs": 2132, - "node24-text-utilities.ts": 2215, - "node24-url-authority-coercion.js": 2529, - "node24-url-authority.ts": 2720, - "node24-url-factories.ts": 2586, - "node24-url-factory-errors.ts": 1931, - "node24-url-mutation-coercion.js": 2518, - "node24-url-mutation.ts": 2194, - "node24-util-types.cjs": 3057, - "node24-util-types.js": 1900, - "node24-util-types.ts": 2935, - "non-null-class-iteration.ts": 2350, - "nonnull-element-arguments.ts": 2545, - "nonnull-forwarded-class-values.ts": 2532, - "nullable-object-extension.mjs": 1922, - "nullish-asserted-class-arms.ts": 2485, - "nullish-assignment-values.ts": 1745, - "nullish-long-chain.ts": 2326, - "number-parse-statics.ts": 2443, - "number-predicates-unknown.ts": 2375, - "numeric-call-specialization.ts": 2588, - "object-enumeration-array-reads.ts": 1967, - "object-from-checked-entries.js": 2542, - "object-inherited-to-string.js": 2670, - "object-key-indexes.js": 2753, - "object-prototype-identity.mjs": 2064, - "object-reexport/main.js": 2482, - "object-spread-computed.ts": 2578, - "opentui-byte-queue.js": 2802, - "optional-callback-arity.ts": 2348, - "optional-class-flows.ts": 2247, - "optional-element-defaults.ts": 2052, - "optional-flag-comparisons.ts": 2527, - "optional-native-callables.js": 2309, - "optional-node-list-iteration.ts": 2316, - "optional-string-index/main.ts": 2950, - "path-to-file-url-options.ts": 2299, - "performance-presence.ts": 2043, - "primitive-and-function-descriptors.js": 2222, - "primitive-union-string.ts": 2856, - "process-builtin-values.js": 2616, - "process-constrained-memory.ts": 2105, - "process-env-computed-keys.js": 2252, - "process-hrtime.js": 2596, - "process-named-signals.cjs": 2604, - "process-uncaught-handler-throws.cjs": 2306, - "process-uncaught-loop.cjs": 2320, - "process-uncaught-module.mjs": 1832, - "process-uncaught-monitor.cjs": 2077, - "process-uncaught-rejection.cjs": 2431, - "process-uncaught-sync.cjs": 2425, - "process-version-pin.ts": 2418, - "process-versions-storage.ts": 2051, - "projected-collection-lifetimes.ts": 2409, - "promise-checked-rejections.ts": 1985, - "promise-resolve-void.ts": 2341, - "prototype-method-overrides.mjs": 4870, - "prototype-named-data-records.ts": 2513, - "record-field-presence.ts": 3025, - "record-key-order.ts": 2807, - "record-optional-json-presence.ts": 2623, - "record-presence-copies.ts": 2693, - "record-presence-delete-json.ts": 2748, - "record-unchecked-read-presence.ts": 2103, - "record-unknown-values.ts": 3450, - "reflect-own-keys.mjs": 2583, - "reflect-record-union-keys.mjs": 2454, - "reflect-typed-record-keys.ts": 1855, - "regex-callback-replacements.ts": 2492, - "regex-callback-values.js": 2733, - "regex-checked-casts.ts": 2350, - "regex-checked-storage.js": 2669, - "regex-exec-long-subject.ts": 4533, - "regex-input-lifetimes.ts": 2535, - "regex-native-matcher.ts": 3683, - "regex-optional-captures.ts": 2606, - "regex-optional-templates.ts": 2338, - "regex-split-captures.ts": 2583, - "regex-text-paths.ts": 2889, - "regexp-constructor-repeat.ts": 2351, - "regexp-optional-constructor.ts": 2315, - "regexp-string-coercion.js": 1897, - "renderer-export-dictionaries.js": 2582, - "renderer-null-state.js": 2185, - "renderer-specializations.js": 2653, - "repeat-and-typed-read-errors.ts": 2751, - "rest-optional-array-arguments.ts": 2671, - "rest-optional-elements.ts": 2289, - "return-void-through-finally.ts": 2486, - "runtime-optional-string-flows.ts": 2406, - "scalar-string-slices.ts": 2175, - "set-checked-methods.js": 2631, - "set-checked-storage.ts": 2064, - "set-iterator-seed-references.ts": 2863, - "set-iterator-seeds.ts": 2896, - "set-optional-seeds.ts": 2121, - "small-collection-transitions.ts": 2645, - "stack-depth-async-bodies.ts": 1910, - "stack-overflow-async.ts": 2447, - "stack-overflow-caught.ts": 2424, - "stack-overflow-finally.ts": 2613, - "stack-overflow-shapes.ts": 2534, - "stack-overflow-uncaught.ts": 2317, - "static-field-updates/main.ts": 2050, - "static-method-receivers.ts": 2243, - "stdio-values.mjs": 2578, - "stdio-write-errors.mjs": 1977, - "stdio-write-overrides.mjs": 2495, - "stored-native-builtins.js": 2831, - "stored-object-helpers.js": 2747, - "string-char-code-integer-loops.ts": 2495, - "string-code-points.ts": 1970, - "string-concat-number-parts.ts": 2044, - "string-construction-order.ts": 2502, - "string-lifetimes-boundaries.ts": 2481, - "string-lifetimes-evaluation.ts": 2486, - "string-lifetimes-iteration.ts": 2043, - "string-lifetimes-operations.ts": 2490, - "string-normalize.ts": 2391, - "string-pattern-replacement.ts": 3527, - "string-scan-fast-paths.ts": 2272, - "string-split-checked-separator.mjs": 2144, - "string-split-traversal.ts": 3038, - "structural-class-properties.ts": 2610, - "structured-text-boundaries.js": 2792, - "structured-text-storage.ts": 2942, - "switch-integer-dispatch.ts": 2290, - "symbol-checked-boundary/main.ts": 2273, - "symbol-class-fields.ts": 2065, - "symbol-class-fields/main.cjs": 2595, - "symbol-class-methods/main.ts": 1855, - "symbol-fields-deferred-factory.ts": 2253, - "symbol-fields-inferred.js": 2329, - "symbol-object-properties/main.js": 2835, - "synchronous-callback-lifetimes.ts": 2768, - "terminal-stream-properties.ts": 2586, - "text-codec-values/main.ts": 2158, - "text-codec-var.ts": 2182, - "text-encode-into-boundaries.ts": 1971, - "timers-clear-many.ts": 2704, - "tuple-and-string-search.ts": 2663, - "tuple-array-union.ts": 2706, - "tuple-callback-visitation.ts": 3464, - "tuple-forof-visitation.ts": 2357, - "typed-array-bulk-conversion.mjs": 3837, - "typed-array-byte-io.js": 2552, - "typed-array-checked-index.js": 2420, - "typed-array-constructor-alias.ts": 2507, - "typed-array-constructor-identity.js": 2453, - "typed-array-constructor-union.ts": 2428, - "typed-array-constructor-values.mjs": 2571, - "typed-array-dynamic-methods.js": 2604, - "typed-array-integer-widths.ts": 2747, - "typed-array-nan-coded-reads.ts": 2921, - "typed-array-numeric-import.ts": 2450, - "typed-array-present-reads.ts": 2582, - "typed-array-reversed-storage.ts": 2168, - "typed-array-unknown.ts": 2500, - "typed-collection-iterator-references.ts": 3186, - "typed-collection-iterators.ts": 2946, - "typed-collection-views/main.ts": 2523, - "typed-index-arguments.ts": 2494, - "typed-prototype-methods.ts": 2527, - "typed-rest-callables.ts": 2391, - "typed-rest-filtered-return.ts": 2578, - "typedarray-native-inputs.js": 3104, - "typeof-checked-comparison.js": 2116, - "uint8array-sort-domains.ts": 2494, - "unicode-case-paths.ts": 2356, - "unicode-process-argv.ts": 2569, - "union-discriminant-width.ts": 2670, - "union-literal-scalar-discriminants.ts": 2869, - "union-literal-slot-conversion.ts": 2792, - "union-nested-layout-discriminant.ts": 2120, - "union-optional-semantic-identity.ts": 2455, - "union-record-clone.ts": 2790, - "union-record-structural-copy.ts": 2905, - "union-reference-comparison.ts": 2401, - "unknown-array-holes.ts": 1853, - "unknown-collections.ts": 3166, - "unknown-object-enumeration.ts": 3783, - "unknown-record-views.ts": 3243, - "url-runtime-base.ts": 2584, - "utf8-span-decoding.ts": 2564, - "util-parseargs-wrapped/main.ts": 2257, - "virtual-runtime-optional-returns.ts": 2981, - "weak-map-array-buffer.ts": 2435, - "weak-map-fresh-array.js": 2341, - "weak-map-native.js": 3068, - "weak-native-class-identity.ts": 2403, - "weak-set-native.js": 2924, - "worker-class-instance-clone.ts": 3052 + "5973-large-closure-environment.ts": 4499, + "5974-typeof-module-exports.cjs": 2558, + "5975-zlib-class-values.ts": 2531, + "5990-worker-uncloneable.mjs": 3772, + "5991-worker-async-dispose.mjs": 4075, + "5992-worker-thread-name-lifetime.mjs": 3241, + "5993-array-length-loop-resizing.ts": 3187, + "5994-array-length-sparse-loops.ts": 3292, + "5995-array-dense-loop-boundaries.ts": 2596, + "600-closures-basic.ts": 2093, + "601-closures-loops.ts": 2964, + "602-closures-identity-recursion.ts": 2859, + "6020-class-extends-function-constructor.js": 3170, + "6021-asserted-literal-union-arm.ts": 3166, + "603-closures-rc-stress.ts": 2756, + "604-closures-for-of.ts": 2963, + "6041-error-cause-redeclared.ts": 2988, + "6042-regex-match-object-destructuring.ts": 3393, + "6043-string-index-narrowed-union.ts": 2983, + "605-closures-forward-capture-tdz.ts": 2902, + "6140-cjs-require-tdz-throws/main.js": 2731, + "6141-cjs-require-tdz-checked/main.js": 2856, + "6142-cjs-require-published-early/main.js": 3400, + "6143-cjs-require-json/main.js": 3245, + "6144-cjs-require-builtin-nested/main.js": 2181, + "6145-cjs-require-cycle/main.js": 3507, + "700-classes-basic.ts": 2476, + "701-classes-composition.ts": 2819, + "702-classes-this-capture.ts": 2834, + "703-classes-rc-stress.ts": 2484, + "710-inheritance-basics.ts": 2629, + "711-inheritance-dispatch.ts": 2199, + "712-inheritance-instanceof.ts": 2914, + "713-inheritance-rc-stress.ts": 2307, + "720-accessors-basics.ts": 2496, + "721-accessors-eval-order.ts": 3007, + "722-accessors-inheritance.ts": 2266, + "723-accessors-rc-stress.ts": 2951, + "750-cycle-closure-box.ts": 2797, + "751-cycle-records-mutual.ts": 2867, + "752-cycle-classes.ts": 2668, + "753-cycle-owned-arrays.ts": 2613, + "754-cycle-external-ref.ts": 2566, + "755-cycle-async-promise.ts": 2707, + "756-cycle-inheritance.ts": 2258, + "757-cycle-cross-generation.ts": 3080, + "762-any-boundary.ts": 3283, + "764-any-async.ts": 2876, + "765-any-optional-chain.ts": 3213, + "766-any-union-boundary.ts": 3515, + "767-string-literal-keys.ts": 2750, + "768-any-uninitialized.ts": 2902, + "800-switch-basics.ts": 2776, + "801-switch-lazy-tests.ts": 2159, + "802-switch-loops.ts": 2543, + "803-switch-rc-stress.ts": 3003, + "804-switch-braced-blocks.ts": 2593, + "810-do-while.ts": 2089, + "820-multi-decl.ts": 2503, + "830-let-uninitialized.ts": 2817, + "900-records-basics.ts": 2340, + "901-records-eval-order.ts": 2257, + "902-records-functions-closures.ts": 2757, + "903-records-rc-stress.ts": 2938, + "904-records-optional-fields.ts": 2935, + "905-records-optional-rc-stress.ts": 2626, + "906-records-utility-types.ts": 2717, + "907-records-utility-generics.ts": 2997, + "908-records-index-signatures.ts": 3162, + "909-records-index-json.ts": 3408, + "910-records-index-rc-stress.ts": 2486, + "911-records-index-dot-access.ts": 3533, + "912-unknown-slots.ts": 2761, + "913-records-index-iteration.ts": 3662, + "914-records-from-entries.ts": 3551, + "915-unknown-tostring-eq.ts": 3814, + "916-unknown-bytes.ts": 2926, + "917-record-array-index-dot.ts": 2493, + "950-modules-basic/main.ts": 2549, + "951-modules-diamond/main.ts": 2015, + "952-modules-cross/main.ts": 2203, + "953-modules-default/main.ts": 2058, + "954-modules-reexport/main.ts": 2223, + "957-builtins-namespace.ts": 2583, + "960-generics-basics.ts": 2371, + "961-generics-recursion.ts": 2698, + "962-generics-closures.ts": 2945, + "963-generics-modules/main.ts": 1815, + "964-generics-rc-stress.ts": 3183, + "965-generics-async.ts": 3138, + "965-unions-retag.ts": 2552, + "966-unions-retag-rc-stress.ts": 2838, + "967-record-field-widening.ts": 3366, + "969-unions-arm-matrix.ts": 2362, + "970-unions-basics.ts": 2298, + "971-unions-switch.ts": 2850, + "972-unions-nested.ts": 2374, + "973-unions-rc-stress.ts": 1915, + "974-unions-modules/main.ts": 2639, + "975-unions-undefined.ts": 2297, + "976-unions-null.ts": 1941, + "977-unions-unit-rc-stress.ts": 2513, + "978-unions-nullish.ts": 3070, + "979-unions-optional-chaining.ts": 3263, + "980-exceptions-basics.ts": 2631, + "981-exceptions-values.ts": 2273, + "982-exceptions-rc-stress.ts": 2398, + "983-exceptions-control-flow.ts": 2350, + "984-exceptions-finally.ts": 2928, + "985-exceptions-call-chains.ts": 2642, + "986-exceptions-uncaught.ts": 2072, + "987-exceptions-result-unions.ts": 2255, + "990-process-basics.ts": 2565, + "991-process-exit.ts": 1649, + "992-fs-roundtrip.ts": 2134, + "993-fs-readdir.ts": 2805, + "994-fs-errors.ts": 1790, + "995-fs-uncaught.ts": 2295, + "996-fs-rc-stress.ts": 3230, + "997-fs-modules/main.ts": 2893, + "998-process-env.ts": 1803, + "abstract-property-dispatch.ts": 3619, + "arguments-arrow-capture.mjs": 2943, + "arguments-reexport/main.ts": 2123, + "array-argument-lifetimes-exits.ts": 3367, + "array-argument-lifetimes-mutation.ts": 2372, + "array-argument-lifetimes-strings.ts": 2429, + "array-argument-lifetimes-values.ts": 3148, + "array-buffer-conversions.js": 2274, + "array-buffer-storage.ts": 2433, + "array-checked-callback-results.mjs": 2526, + "array-checked-constructor.js": 3336, + "array-concat-widening.ts": 1977, + "array-deque-unshift.ts": 3233, + "array-element-compound-updates.ts": 3547, + "array-fill-unit-values.ts": 2929, + "array-from-checked.js": 3397, + "array-from-value.mjs": 3059, + "array-isarray-value/main.js": 2918, + "array-numeric-own-keys.ts": 2790, + "array-optional-number-write-index.ts": 2704, + "array-primitive-ordering.ts": 4576, + "array-queue-head-room.ts": 2847, + "array-read-helper-effects.ts": 3311, + "array-refined-storage.ts": 2961, + "array-sort-dense-effects.ts": 4064, + "array-sort-dense-values.ts": 4011, + "array-sort-holes-published.ts": 7513, + "array-sort-runs.ts": 5700, + "array-sort-runtime-comparator.ts": 4830, + "array-sort-unknown-comparator.ts": 3102, + "array-storage-transitions.ts": 3965, + "array-truncate-reuse.ts": 3475, + "async-backend-factory.mjs": 2766, + "async-local-typed-results.ts": 3471, + "atomics-integer-operations.ts": 3518, + "bigint-checked-casts.ts": 3182, + "bigint-checked-storage.js": 3679, + "bigint-union-conversion.ts": 2403, + "boxed-error-rethrows.mjs": 2704, + "buffer-codec-boundaries.ts": 3023, + "buffer-primitive-predicate.ts": 2153, + "builtin-prototype-method-values.mjs": 2713, + "byte-loop-bounds.ts": 3126, + "byte-number-pipeline.ts": 3555, + "byte-record-windows.ts": 3164, + "call-lifetimes-argument-order.ts": 3386, + "call-lifetimes-control-flow.ts": 2774, + "call-lifetimes-owned-boundaries.ts": 2902, + "call-lifetimes-projections.ts": 2603, + "call-lifetimes-union-values.ts": 3045, + "call-lifetimes-virtual-optionals.ts": 2344, + "callable-module-members.mjs": 2928, + "callable-properties.mjs": 2765, + "callable-property-cycles.js": 1847, + "callable-reexport/main.ts": 2222, + "captured-class-refinement.ts": 3000, + "catch-capture.ts": 3112, + "caught-class-identity.ts": 3640, + "caught-standalone-class.ts": 3171, + "caught-system-error-fields.ts": 3152, + "checked-addition.js": 2684, + "checked-any-addition.ts": 2642, + "checked-array-assignment.ts": 2697, + "checked-array-flat/main.ts": 2840, + "checked-bitwise/main.js": 2581, + "checked-builder-cycles.js": 2948, + "checked-byte-storage/main.ts": 2611, + "checked-callback-options.ts": 1643, + "checked-class-descriptors.mjs": 3640, + "checked-class-factory.mjs": 2778, + "checked-collection-iterators.ts": 3241, + "checked-collection-symbol-methods.ts": 2293, + "checked-dispatch-input-lifetimes.js": 2742, + "checked-increment/main.js": 2072, + "checked-inherited-fields.ts": 3028, + "checked-iterator-consumption.js": 3107, + "checked-numeric-arithmetic.js": 2378, + "checked-object-module-alias.js": 2947, + "checked-optional-calls.mjs": 3142, + "checked-promise-recovery.ts": 2855, + "checked-proxy-callback/main.ts": 1847, + "checked-record-constructor.ts": 2481, + "checked-renderer-defaults.js": 2013, + "checked-rest-function-adapters.js": 3046, + "checked-storage-transitions.js": 2689, + "checked-symbol-array.ts": 2459, + "checked-value-input-lifetimes.ts": 3335, + "class-accessor-returned-function.js": 2629, + "class-array-optional-return.ts": 2795, + "class-bracket-method-calls.js": 3486, + "class-brand-overload.js": 2784, + "class-callback-replacement.js": 2660, + "class-callback-storage.ts": 3042, + "class-checked-composites.ts": 3335, + "class-checked-upcast.ts": 3076, + "class-collection-insertion.ts": 2281, + "class-computed-presence.ts": 3266, + "class-computed-symbols.js": 2803, + "class-covariant-returns.ts": 3398, + "class-curried-factory.mjs": 3626, + "class-data-descriptors.js": 3529, + "class-factory-inherited-statics.mjs": 3667, + "class-factory-parameter-base.mjs": 2767, + "class-factory-record-base.mjs": 3407, + "class-factory-recursive-statics.mjs": 3184, + "class-factory-static-members.mjs": 3515, + "class-function-base/main.js": 3746, + "class-instance-constructor.js": 3651, + "class-interface-protocols.ts": 5020, + "class-interface-refinements.ts": 2194, + "class-json-checked.ts": 1821, + "class-local-function-base.mjs": 3398, + "class-method-spreads.ts": 3880, + "class-narrowed-views.ts": 3116, + "class-property-call-values.mjs": 3490, + "class-prototype-data.js": 3512, + "class-prototype-descriptor-copy.mjs": 4571, + "class-specialized-computed-base.mjs": 3883, + "class-static-data.js": 2886, + "class-subclass-assertions.ts": 3020, + "class-super-surplus-spread.mjs": 2209, + "class-symbol-accessors.ts": 2490, + "class-symbol-cache.mjs": 2911, + "class-value-constructor-exceptions.ts": 2697, + "closed-virtual-dispatch-modules/main.ts": 2682, + "closed-virtual-dispatch.ts": 3944, + "closure-nullable-union-return.ts": 3308, + "closure-union-locals.ts": 3570, + "collection-array-cycles.ts": 2714, + "collection-array-maps.ts": 3451, + "collection-array-missing.ts": 3109, + "collection-array-sets.ts": 2862, + "collection-copy-identities.ts": 3119, + "collection-data-unions.ts": 3087, + "collection-ingestion-mappers.ts": 3823, + "collection-ingestion-protocol/main.ts": 2461, + "collection-ingestion-references.ts": 3913, + "collection-ingestion.ts": 3524, + "collection-storage-copies.ts": 3595, + "collection-value-keys.ts": 2425, + "compound-field-receivers.ts": 2962, + "compound-presence-guards.ts": 3337, + "conditional-tuple-destructuring.ts": 2631, + "console-imports.cjs": 2257, + "console-native-value.js": 2531, + "const-enum-module-cycle/main.ts": 2437, + "constant-callback-dispatch.ts": 2800, + "constant-module-globals/main.ts": 3036, + "constant-number-remainders.ts": 2724, + "constructed-class-dispatch.ts": 3005, + "contextual-array-union-write.ts": 6554, + "contextual-return-layouts.ts": 2859, + "counted-loop-numbers.ts": 2703, + "counted-loop-storage.ts": 2473, + "crypto-hash-optional-input.ts": 3036, + "cycle-generations-long-lived.ts": 2902, + "cycle-growth-yield.ts": 3252, + "date-checked-native.js": 2177, + "declared-index-spread.ts": 3543, + "deep-reference-teardown.ts": 2409, + "default-undefined-parameters.ts": 3171, + "deferred-field-updates.ts": 2290, + "deferred-registry-many.ts": 3267, + "derived-numeric-updates.ts": 2762, + "destructure-call-assignment.js": 2259, + "destructure-inferred-row-defaults.js": 2953, + "destructure-union-arrays.ts": 3202, + "dynamic-function-descriptors.js": 2214, + "error-cause-writes.js": 3441, + "error-inherited-message.mjs": 3449, + "error-inherited-name.mjs": 2955, + "error-property-writes.mjs": 2326, + "error-prototype-tostring.js": 2596, + "error-stack-frames.ts": 2880, + "error-stack-limit.ts": 2871, + "error-tostring-overrides.ts": 2481, + "error-values-inspect.ts": 2856, + "exact-index-round-trip.ts": 3660, + "expression-runtime-values.ts": 2159, + "fiber-switch-stress.ts": 2800, + "fiberless-async-ordering.ts": 3075, + "fiberless-async-unhandled.ts": 2437, + "field-assignment-values.ts": 2878, + "file-handle-values.ts": 2035, + "file-read-storage.ts": 2737, + "file-url-percent-decoding.ts": 2029, + "final-field-borrows.ts": 3105, + "for-multiple-bindings.ts": 2994, + "for-multiple-lexical-bindings.ts": 2863, + "for-of-checked-iterator.js": 3515, + "for-of-subclass-elements.ts": 2469, + "forward-generic-signatures.ts": 3287, + "fresh-array-union-layout.js": 2961, + "frozen-checked-dictionaries.mjs": 3021, + "fs-native-realpath.ts": 3263, + "fs-null-byte-paths-js.cjs": 2762, + "fs-null-byte-paths.ts": 3411, + "fs-read-encodings.ts": 3220, + "fs-read-utf8-edges.ts": 3246, + "fs-read-utf8-handle-forms.ts": 3163, + "fs-remove-readonly.ts": 2468, + "fs-write-string-bytes-union.ts": 2571, + "function-apply-value.mjs": 2118, + "function-constructor-global.mjs": 2459, + "function-constructor-kinds/main.js": 2510, + "function-custom-prototype.js": 3445, + "function-custom-to-string.mjs": 3088, + "function-object-assign.js": 2894, + "function-own-prototype/main.js": 2937, + "generator-checked-protocol.ts": 3202, + "generator-checked-return.js": 2917, + "generator-custom-delegation/main.ts": 5180, + "generator-default-return-pattern.ts": 2163, + "generator-delegation-values.ts": 2121, + "generator-inferred-date.js": 1700, + "generator-mixed-checked-channels.ts": 3203, + "generator-untyped-consumer/main.ts": 3571, + "generic-base-specializations.ts": 2610, + "generic-binding-ownership.ts": 3653, + "generic-class-js-functions/main.ts": 2631, + "generic-class-recovery.ts": 3190, + "generic-namespace-factories/main.ts": 2712, + "generic-nullable-collections.ts": 2692, + "generic-pattern-bindings.ts": 2004, + "generic-record-index.ts": 2705, + "generic-visitor-void.ts": 2865, + "global-accumulator-loops.ts": 3053, + "global-error-subclasses.ts": 2690, + "global-runtime-probes.js": 1976, + "global-stored-probes.js": 2924, + "global-string-cache/main.js": 2572, + "global-symbol-cache/main.js": 2380, + "guarded-native-iterables.ts": 2466, + "heap-input-aliases.ts": 2784, + "heap-input-collections.ts": 3100, + "in-array-runtime-index.ts": 3210, + "indexed-assignment-values.js": 3521, + "indexed-assignment-values.ts": 2224, + "indexed-element-field-reads.ts": 2611, + "inferred-array-assignment-values.mjs": 2976, + "inferred-class-default.mjs": 2564, + "inferred-compound-values.mjs": 2110, + "inferred-conditional-class.mjs": 2584, + "inferred-error-fields.js": 2163, + "inferred-map-patterns.mjs": 2233, + "inferred-method-override.js": 2516, + "inferred-partial-destructuring.mjs": 2158, + "inferred-primitive-properties.mjs": 2508, + "inferred-record-return.mjs": 2099, + "inferred-recursive-record.mjs": 2183, + "inferred-undefined-field.mjs": 3012, + "input-lifetime-intervals.ts": 3244, + "input-lifetimes-modules/main.ts": 2793, + "instanceof-class-record-union.ts": 2242, + "integer-flow-arithmetic.ts": 3060, + "integer-loop-forms.ts": 2872, + "integer-view-control.ts": 2400, + "integer-view-numbers.ts": 2753, + "integer-view-storage.ts": 2545, + "intl-grapheme-conformance/main.ts": 4747, + "intl-grapheme.ts": 2345, + "iterator-typed-returns.ts": 3833, + "js-abort-portable.js": 2996, + "js-array-constructor.js": 3177, + "js-array-default-buffer.js": 2796, + "js-array-iterators.js": 3414, + "js-arrow-bind.cjs": 4772, + "js-buffer-callback-brand.cjs": 2001, + "js-callable-proxy.js": 3739, + "js-chained-native-results.mjs": 2683, + "js-checked-array-iterator.js": 2865, + "js-checked-array-reference.js": 3607, + "js-checked-number-negation.mjs": 2731, + "js-checked-sort-result.js": 2328, + "js-child-error-listener.cjs": 2751, + "js-cjs-callable-snapshots/main.cjs": 2946, + "js-class-added-parameters.js": 2228, + "js-class-checked-overrides.js": 2130, + "js-class-computed-fields.js": 2657, + "js-class-enumeration.js": 2642, + "js-class-field-accessor-override.js": 2687, + "js-class-fluent-overrides.js": 2370, + "js-class-late-method-values.js": 3690, + "js-class-member-dispatch.js": 3108, + "js-class-return-shapes.js": 3149, + "js-class-shadowed-methods.js": 3491, + "js-class-stored-method-receiver.js": 2218, + "js-class-subclass-properties.js": 4610, + "js-class-symbol-spread.cjs": 2295, + "js-computed-prototype-additions.js": 3232, + "js-constructor-returned-callable.js": 2404, + "js-constructor-spread.js": 3538, + "js-dataview-dispatch.js": 3187, + "js-declared-fields.cjs": 4825, + "js-default-listener-arguments.cjs": 1767, + "js-default-options/main.ts": 3097, + "js-deferred-field-initializers.js": 1977, + "js-deferred-registry-effects.js": 2772, + "js-derived-constructor-hoisting.js": 2288, + "js-dormant-array-write.js": 2420, + "js-dormant-overrides.js": 2890, + "js-expando-records.js": 2867, + "js-implicit-call-results.js": 2946, + "js-inferred-callback-exports/main.js": 4147, + "js-inferred-class-array-return.js": 3209, + "js-inferred-object-values.js": 2234, + "js-inherited-field-storage.js": 2161, + "js-inherited-parameter-abi.js": 2457, + "js-iterable-spread.js": 3088, + "js-loose-dynamic-primitives.js": 2797, + "js-map-object-seeds.js": 3253, + "js-member-increment.js": 3027, + "js-module-function-values.js": 3358, + "js-mutable-null-members.js": 2588, + "js-nested-native-descriptors.js": 2862, + "js-normalize-dispatch.js": 3006, + "js-nullish-inherited-fields.mjs": 2812, + "js-object-receiver-storage.js": 2500, + "js-object-returning-constructors.mjs": 2542, + "js-object-shared-arrays.js": 2369, + "js-omitted-typed-arguments.js": 2596, + "js-open-binding-writes.js": 2909, + "js-optional-class-fields.js": 3469, + "js-optional-member-call.js": 3126, + "js-primitive-union-addition.js": 2730, + "js-promise-all-values.js": 3561, + "js-promise-catch-handler.js": 2902, + "js-promise-finally-order.js": 2511, + "js-promise-pending-reactions.js": 2712, + "js-prototype-host-boundary.js": 2617, + "js-reflect-receivers.js": 3341, + "js-regex-record-captures.js": 3157, + "js-scalar-iterables.js": 3299, + "js-shared-method-dispatch.js": 3057, + "js-specialized-field-method.js": 2254, + "js-static-accessors.js": 3374, + "js-static-method-values.js": 3629, + "js-static-registries.cjs": 3164, + "js-super-accessor-initialization.js": 2332, + "js-uninitialized-native-fields.js": 2069, + "json-guarded-map-values.ts": 3048, + "json-output-bulk.ts": 1978, + "json-runtime-options.mjs": 3034, + "json-stringify-value.js": 2308, + "json-typed-parse-direct.ts": 2696, + "json-typed-parse-index-signatures.ts": 3686, + "json-typed-parse-optional-members.ts": 3044, + "json-typed-parse-union-members.ts": 3090, + "literal-switch-dispatch.ts": 2612, + "live-reference-shared-converters.ts": 3144, + "llvm-read-receiver-lifetime.ts": 1978, + "local-class-call-spreads.js": 1989, + "local-class-declaration.ts": 1927, + "local-class-evaluation.ts": 2816, + "local-class-inheritance.ts": 2647, + "local-class-lifetimes.ts": 3188, + "local-class-namespace/main.js": 3542, + "local-class-prototype-data.js": 2714, + "local-class-specialization.js": 2650, + "local-class-unknown.ts": 2915, + "local-class-values.ts": 2331, + "local-union-storage.ts": 2638, + "locale-compare-root-order.ts": 3001, + "logical-assignment-dynamic.js": 3172, + "logical-assignment-values.ts": 2043, + "logical-runtime-optional-record.ts": 2200, + "loop-reference-lifetimes.ts": 2559, + "map-checked-boundary/main.ts": 2442, + "map-checked-seed.js": 2756, + "map-get-set-fusion.ts": 3284, + "map-number-keys-dense.ts": 2972, + "map-read-lifetimes-exits.ts": 2860, + "map-read-lifetimes.ts": 2905, + "map-seeds-and-spreads.ts": 2984, + "map-typed-array-values.ts": 2649, + "math-function-values.js": 2638, + "missing-read-lambda-results.ts": 2850, + "missing-read-narrowed-storage.ts": 3077, + "missing-read-number-conversions.ts": 3015, + "module-byte-loops.ts": 2315, + "module-fallback-functions/main.js": 2716, + "narrowed-nullish-values.ts": 2405, + "native-any-timer-callback.ts": 2380, + "native-array-borrowed-find-last.js": 2591, + "native-array-borrowed-slice.js": 2966, + "native-array-copying-methods.js": 2605, + "native-array-fill-copywithin.js": 2662, + "native-array-frozen-noops.js": 2568, + "native-array-index-boundaries.js": 2581, + "native-array-integrity.js": 3066, + "native-array-length.js": 3005, + "native-array-named-properties.js": 2812, + "native-array-prototype/main.js": 3103, + "native-array-read-lifetimes.ts": 2912, + "native-array-reduce-find-last.js": 2732, + "native-array-search-from-index.js": 2720, + "native-array-splice-coercion.js": 1769, + "native-array-splice-flat.js": 2701, + "native-buffer-coercion.js": 2837, + "native-callback-dispatch.ts": 3750, + "native-class-array-boundaries.ts": 3033, + "native-class-cache.js": 3922, + "native-class-properties.cjs": 5335, + "native-class-property-cycles.cjs": 2421, + "native-class-property-order.cjs": 2798, + "native-class-type-predicates.ts": 2523, + "native-factory-records.js": 2515, + "native-method-values.js": 4031, + "native-method-values.ts": 2347, + "native-npm-imports/main.ts": 3454, + "native-object-descriptors.js": 1755, + "native-object-for-in-minimal.js": 2603, + "native-object-property-attributes.js": 3035, + "native-object-prototype.js": 2445, + "native-property-names.ts": 2425, + "native-proxy-env.js": 3704, + "native-worker-arena-reuse.ts": 6858, + "native-worker-bounded-errors.ts": 2058, + "native-worker-buffer-consumers.ts": 3068, + "native-worker-cancel-hot.ts": 2722, + "native-worker-class-fields.ts": 2782, + "native-worker-clone-errors.ts": 2690, + "native-worker-commonjs.cjs": 3412, + "native-worker-delayed-messages.ts": 2375, + "native-worker-discarded-heaps.ts": 5717, + "native-worker-environment.ts": 2804, + "native-worker-error.ts": 2211, + "native-worker-exception-state.ts": 4167, + "native-worker-exit-listeners.ts": 2523, + "native-worker-io.ts": 3345, + "native-worker-loop-cancellation.ts": 2510, + "native-worker-message-graphs.ts": 2419, + "native-worker-messages.ts": 2757, + "native-worker-poll-exceptions.ts": 3061, + "native-worker-port-lifetime.ts": 2699, + "native-worker-process-exit.ts": 2130, + "native-worker-publish-classes.ts": 3636, + "native-worker-publish-refusals.ts": 2020, + "native-worker-published-graphs.ts": 2461, + "native-worker-references.ts": 2598, + "native-worker-roots/main.ts": 3212, + "native-worker-shared-views.ts": 2802, + "native-worker-stack-overflow.ts": 2946, + "native-worker-terminate-exception-state.ts": 3847, + "native-worker-terminate-loop.ts": 2735, + "native-worker-terminate-recursion.ts": 4636, + "native-worker-terminate-suspended.ts": 2692, + "native-worker-terminate-tested-loops.ts": 3271, + "native-worker-terminate-wait.ts": 2775, + "native-worker-typed-arrays.ts": 3254, + "nested-map-cycles.ts": 2753, + "nested-map-values.ts": 4785, + "never-returning-calls.ts": 2228, + "next-tick-commonjs-entry.cjs": 1929, + "node24-buffer-encoding-errors.ts": 2283, + "node24-buffer-encoding.ts": 2691, + "node24-crypto-kdf-errors.ts": 3129, + "node24-crypto-kdf-options.ts": 3852, + "node24-crypto-kdf.ts": 3936, + "node24-crypto-sha2.ts": 1860, + "node24-deep-equality-properties.js": 2441, + "node24-deep-equality.cjs": 2912, + "node24-deep-equality.ts": 3378, + "node24-env-files-errors.ts": 2760, + "node24-env-files.ts": 2643, + "node24-env-imports.cjs": 1749, + "node24-env-parse-errors.ts": 2987, + "node24-env-parse.ts": 1797, + "node24-esm-metadata/main.mjs": 2385, + "node24-file-url-buffer.ts": 2665, + "node24-file-url-errors.js": 3394, + "node24-file-url-options.ts": 3117, + "node24-fs-links-callbacks.cjs": 2182, + "node24-fs-links-errors.cjs": 2221, + "node24-fs-links-options.cjs": 3438, + "node24-fs-links-promise-errors.cjs": 1752, + "node24-fs-links-promises.ts": 2960, + "node24-fs-links.ts": 3132, + "node24-fs-statfs-callbacks.cjs": 2404, + "node24-fs-statfs-errors.cjs": 3053, + "node24-fs-statfs-options.cjs": 3275, + "node24-fs-statfs.ts": 2771, + "node24-fs-timestamps-callbacks.cjs": 3442, + "node24-fs-timestamps-errors.cjs": 2214, + "node24-fs-timestamps-promises.ts": 2905, + "node24-fs-timestamps-symlinks.ts": 2911, + "node24-fs-timestamps.ts": 2652, + "node24-no-error-assertions-coercion.js": 3450, + "node24-no-error-assertions.cjs": 2131, + "node24-no-error-assertions.ts": 3704, + "node24-parseargs.js": 3975, + "node24-string-decoder.ts": 1840, + "node24-style-text-environment.js": 1777, + "node24-style-text-validation.js": 2850, + "node24-style-text.cjs": 2428, + "node24-style-text.ts": 2934, + "node24-system-error-validation.js": 2301, + "node24-system-errors.cjs": 2559, + "node24-system-errors.ts": 2989, + "node24-text-codec-properties.ts": 3210, + "node24-text-encode-into.ts": 2657, + "node24-text-utilities-coercion.js": 2506, + "node24-text-utilities-validation.js": 3129, + "node24-text-utilities.cjs": 2550, + "node24-text-utilities.ts": 2610, + "node24-url-authority-coercion.js": 3075, + "node24-url-authority.ts": 2397, + "node24-url-factories.ts": 3132, + "node24-url-factory-errors.ts": 2346, + "node24-url-mutation-coercion.js": 2926, + "node24-url-mutation.ts": 2845, + "node24-util-types.cjs": 2459, + "node24-util-types.js": 2601, + "node24-util-types.ts": 3126, + "non-null-class-iteration.ts": 1674, + "nonnull-element-arguments.ts": 2593, + "nonnull-forwarded-class-values.ts": 1775, + "nullable-object-extension.mjs": 1502, + "nullish-asserted-class-arms.ts": 2652, + "nullish-assignment-values.ts": 2755, + "nullish-long-chain.ts": 3588, + "number-parse-statics.ts": 2229, + "number-predicates-unknown.ts": 2605, + "numeric-call-specialization.ts": 1690, + "object-enumeration-array-reads.ts": 3043, + "object-from-checked-entries.js": 3900, + "object-inherited-to-string.js": 1733, + "object-key-indexes.js": 3227, + "object-prototype-identity.mjs": 2689, + "object-reexport/main.js": 2801, + "object-spread-computed.ts": 2239, + "opentui-byte-queue.js": 3379, + "optional-callback-arity.ts": 1977, + "optional-class-flows.ts": 2120, + "optional-element-defaults.ts": 2378, + "optional-flag-comparisons.ts": 2194, + "optional-native-callables.js": 2420, + "optional-node-list-iteration.ts": 2425, + "optional-string-index/main.ts": 2629, + "path-to-file-url-options.ts": 1964, + "performance-presence.ts": 2369, + "primitive-and-function-descriptors.js": 2715, + "primitive-union-string.ts": 2231, + "process-builtin-values.js": 3831, + "process-constrained-memory.ts": 2154, + "process-env-computed-keys.js": 2619, + "process-hrtime.js": 2376, + "process-named-signals.cjs": 2024, + "process-uncaught-handler-throws.cjs": 2023, + "process-uncaught-loop.cjs": 1789, + "process-uncaught-module.mjs": 1660, + "process-uncaught-monitor.cjs": 2359, + "process-uncaught-rejection.cjs": 2287, + "process-uncaught-sync.cjs": 2104, + "process-version-pin.ts": 2000, + "process-versions-storage.ts": 2763, + "projected-collection-lifetimes.ts": 2478, + "promise-checked-rejections.ts": 2584, + "promise-resolve-void.ts": 1939, + "prototype-method-overrides.mjs": 5633, + "prototype-named-data-records.ts": 2571, + "rc-last-use-moves.ts": 2854, + "rc-private-array-writes.ts": 3076, + "record-field-presence.ts": 3566, + "record-key-order.ts": 2682, + "record-optional-json-presence.ts": 2822, + "record-presence-copies.ts": 3201, + "record-presence-delete-json.ts": 3074, + "record-unchecked-read-presence.ts": 2374, + "record-unknown-values.ts": 3234, + "reflect-own-keys.mjs": 1796, + "reflect-record-union-keys.mjs": 2278, + "reflect-typed-record-keys.ts": 2418, + "regex-callback-replacements.ts": 2839, + "regex-callback-values.js": 2887, + "regex-checked-casts.ts": 1808, + "regex-checked-storage.js": 2999, + "regex-exec-long-subject.ts": 2794, + "regex-exec-result-arrays.ts": 3204, + "regex-fs-text-surfaces.ts": 3720, + "regex-input-lifetimes.ts": 2060, + "regex-native-matcher.ts": 4292, + "regex-optional-captures.ts": 2856, + "regex-optional-templates.ts": 2387, + "regex-split-captures.ts": 2496, + "regex-text-paths.ts": 3201, + "regexp-constructor-repeat.ts": 2842, + "regexp-optional-constructor.ts": 2495, + "regexp-string-coercion.js": 1920, + "renderer-export-dictionaries.js": 2828, + "renderer-null-state.js": 2692, + "renderer-specializations.js": 2881, + "repeat-and-typed-read-errors.ts": 2395, + "rest-optional-array-arguments.ts": 2478, + "rest-optional-elements.ts": 2534, + "return-void-through-finally.ts": 2913, + "runtime-optional-string-flows.ts": 2188, + "scalar-string-slices.ts": 2172, + "set-checked-methods.js": 3226, + "set-checked-storage.ts": 1974, + "set-iterator-seed-references.ts": 2018, + "set-iterator-seeds.ts": 3264, + "set-optional-seeds.ts": 2769, + "small-collection-transitions.ts": 3115, + "speculative-integer-globals.ts": 2544, + "speculative-integer-loops.ts": 2308, + "speculative-integer-reads.ts": 3059, + "stack-depth-async-bodies.ts": 2625, + "stack-overflow-async.ts": 1714, + "stack-overflow-caught.ts": 2878, + "stack-overflow-finally.ts": 2668, + "stack-overflow-shapes.ts": 3524, + "stack-overflow-uncaught.ts": 2587, + "stackless-async-concurrency.ts": 3322, + "stackless-async-context.ts": 1853, + "stackless-async-exceptions.ts": 3251, + "stackless-async-ordering.ts": 3551, + "stackless-async-unhandled.ts": 1779, + "static-field-updates/main.ts": 2473, + "static-method-receivers.ts": 2567, + "stdio-values.mjs": 2963, + "stdio-write-errors.mjs": 2760, + "stdio-write-overrides.mjs": 2090, + "stored-native-builtins.js": 2900, + "stored-object-helpers.js": 2328, + "stream-dynamic-entry-kinds.cjs": 2778, + "stream-dynamic-entry-kinds.ts": 2624, + "stream-encoded-from-entries.cjs": 2924, + "stream-encoded-from-entries.ts": 3274, + "string-char-code-integer-loops.ts": 1819, + "string-code-points.ts": 1858, + "string-concat-number-parts.ts": 1705, + "string-construction-order.ts": 2928, + "string-field-append.ts": 2926, + "string-lifetimes-boundaries.ts": 1843, + "string-lifetimes-evaluation.ts": 2718, + "string-lifetimes-iteration.ts": 2668, + "string-lifetimes-operations.ts": 2866, + "string-normalize.ts": 2507, + "string-pattern-replacement.ts": 3053, + "string-scan-fast-paths.ts": 2942, + "string-split-checked-separator.mjs": 2465, + "string-split-traversal.ts": 3686, + "structural-class-properties.ts": 2071, + "structured-text-boundaries.js": 2038, + "structured-text-storage.ts": 2083, + "switch-integer-dispatch.ts": 2561, + "symbol-checked-boundary/main.ts": 3073, + "symbol-class-fields.ts": 1839, + "symbol-class-fields/main.cjs": 2716, + "symbol-class-methods/main.ts": 1920, + "symbol-fields-deferred-factory.ts": 2270, + "symbol-fields-inferred.js": 1610, + "symbol-object-properties/main.js": 2842, + "synchronous-callback-lifetimes.ts": 3460, + "terminal-stream-properties.ts": 2151, + "text-codec-values/main.ts": 2261, + "text-codec-var.ts": 2601, + "text-encode-into-boundaries.ts": 2554, + "timers-clear-many.ts": 2390, + "tuple-and-string-search.ts": 2238, + "tuple-array-union.ts": 2290, + "tuple-callback-visitation.ts": 3777, + "tuple-forof-visitation.ts": 2917, + "typed-array-bulk-conversion.mjs": 4510, + "typed-array-byte-io.js": 2794, + "typed-array-checked-index.js": 1963, + "typed-array-constructor-alias.ts": 2203, + "typed-array-constructor-identity.js": 2030, + "typed-array-constructor-union.ts": 2632, + "typed-array-constructor-values.mjs": 3162, + "typed-array-dynamic-methods.js": 2468, + "typed-array-integer-widths.ts": 2823, + "typed-array-nan-coded-reads.ts": 3658, + "typed-array-numeric-import.ts": 2758, + "typed-array-present-reads.ts": 2812, + "typed-array-reversed-storage.ts": 2702, + "typed-array-unknown.ts": 2767, + "typed-collection-iterator-references.ts": 3694, + "typed-collection-iterators.ts": 3044, + "typed-collection-views/main.ts": 3027, + "typed-index-arguments.ts": 2801, + "typed-prototype-methods.ts": 2702, + "typed-rest-callables.ts": 2066, + "typed-rest-filtered-return.ts": 1741, + "typedarray-native-inputs.js": 3278, + "typeof-checked-comparison.js": 2471, + "uint8array-sort-domains.ts": 2675, + "unicode-case-paths.ts": 2617, + "unicode-process-argv.ts": 2688, + "union-discriminant-width.ts": 3120, + "union-literal-scalar-discriminants.ts": 3160, + "union-literal-slot-conversion.ts": 2002, + "union-nested-layout-discriminant.ts": 2160, + "union-optional-semantic-identity.ts": 3319, + "union-record-clone.ts": 3245, + "union-record-structural-copy.ts": 3278, + "union-reference-comparison.ts": 2689, + "unknown-array-holes.ts": 1863, + "unknown-collections.ts": 2098, + "unknown-object-enumeration.ts": 4264, + "unknown-record-views.ts": 2126, + "url-runtime-base.ts": 2747, + "utf8-span-decoding.ts": 2143, + "util-parseargs-wrapped/main.ts": 3020, + "virtual-runtime-optional-returns.ts": 3408, + "weak-map-array-buffer.ts": 2113, + "weak-map-fresh-array.js": 2822, + "weak-map-native.js": 2623, + "weak-native-class-identity.ts": 1967, + "weak-set-native.js": 2320, + "worker-class-instance-clone.ts": 3408 }, "san": { - "001-hello.ts": 38244, - "002-log-args.ts": 31270, - "100-number-format.ts": 45195, - "1000-json-stringify-basics.ts": 46332, - "1001-json-escapes-unicode.ts": 4513, - "1002-json-parse-cast.ts": 35521, - "1003-json-parse-unions.ts": 3512, - "1004-json-parse-errors.ts": 3903, - "1005-json-nested.ts": 3425, - "1006-json-fs-config.ts": 3347, - "1007-json-rc-stress.ts": 34305, - "1008-json-null-arms.ts": 3422, - "1009-json-optional-fields.ts": 4004, - "101-arithmetic.ts": 4655, - "1010-json-stringify-space.ts": 45417, - "1011-json-unknown-typeof.ts": 4001, - "102-comparisons.ts": 3774, - "1020-async-basics.ts": 3697, - "1021-async-ordering.ts": 3078, - "1022-async-exceptions.ts": 3780, - "1023-async-rc-stress.ts": 3889, - "1024-async-pending-exit.ts": 3389, - "1025-async-promise-capture.ts": 42296, - "1026-throw-promise.ts": 4482, - "1027-async-return-promise.ts": 3534, - "1028-async-return-record-literals.ts": 3843, - "1029-async-eager-chains.ts": 4890, - "103-ternary.ts": 3470, - "104-ternary-empty-arrays.ts": 7056, - "1050-comptime-tables.ts": 4643, - "1051-comptime-strings.ts": 42895, - "1052-comptime-records.ts": 44375, - "1053-comptime-json.ts": 4497, - "1054-comptime-modules/main.ts": 4326, - "110-number-radix.ts": 3100, - "111-record-entries.ts": 7961, - "1110-math-members.ts": 3457, - "1111-math-random.ts": 4321, - "1113-string-methods.ts": 23286, - "1114-string-unicode-island.ts": 45718, - "1115-parse-globals.ts": 3277, - "1116-island-static-mix.ts": 3738, - "1117-typeof-static-union.ts": 4351, - "1118-object-spread-conditional.ts": 3602, - "1119-switch-union.ts": 4833, - "112-class-descriptor-key.js": 4674, - "1121-infinity-number-tostring.ts": 4470, - "1122-any-captures.ts": 7125, - "1124-union-narrowed-retag.ts": 3484, - "113-promise-checked-exits.ts": 4562, - "114-live-record-delete.ts": 3572, - "115-array-compound.js": 6457, - "116-text-decoder-runtime.js": 33483, - "117-weak-cache-cycle.js": 5789, - "118-fs-stored-callbacks.cjs": 52946, - "119-native-patterns.js": 6101, - "1200-regex-test-basics.ts": 47554, - "1201-regex-replace.ts": 3959, - "1202-regex-substitutions.ts": 3855, - "1203-regex-split.ts": 31517, - "1204-regex-empty-unicode.ts": 4662, - "1205-regex-rc-stress.ts": 36658, - "1300-errors-basics.ts": 4354, - "1301-errors-subclass.ts": 4687, - "1302-errors-typed-catch.ts": 4229, - "1303-errors-rc-stress.ts": 3431, - "1304-errors-uncaught.ts": 3763, - "1305-errors-async-rejections.ts": 4254, - "1306-errors-runtime-instances.ts": 4140, - "1350-path-normalize-join.ts": 4224, - "1351-path-parts.ts": 4167, - "1352-path-resolve-relative.ts": 4382, - "1353-os-basics.ts": 4007, - "1354-builtin-bare-specifiers.ts": 3999, - "1355-url-parse.ts": 3346, - "1356-url-file-bridge.ts": 3553, - "1357-fsp-roundtrip.ts": 4325, - "1358-crypto-random.ts": 3398, - "1359-json-module/main.ts": 4414, - "1360-spawn-sync.ts": 4387, - "1361-spawn-events.ts": 4260, - "1362-spawn-timers.ts": 9971, - "1363-spawn-unhandled-error.ts": 3744, - "1364-union-truthiness.ts": 3907, - "1365-union-logical.ts": 4258, - "1366-union-equality.ts": 3058, - "1367-destructuring.ts": 5461, - "1368-constructor-functions.ts": 3781, - "1369-promise-union.ts": 3864, - "1370-spread.ts": 3429, - "1371-union-template-tostring.ts": 3265, - "1372-loose-null-tests.ts": 3885, - "1373-union-array-arms.ts": 3435, - "140-bitwise-operators.ts": 4527, - "1400-typedarray-basics.ts": 5395, - "1401-typedarray-slice-set.ts": 3626, - "1402-buffer-encodings.ts": 4871, - "1403-buffer-fs.ts": 4268, - "1404-zlib-crypto-bytes.ts": 5331, - "1405-bytes-unions-arrays.ts": 3838, - "1406-dataview-mp4.ts": 3661, - "1407-dataview-bounds.ts": 5553, - "1408-string-indexing.ts": 5392, - "1409-typedarray-integer-loops.ts": 4558, - "1420-number-statics.ts": 3886, - "1421-number-parse-strings.ts": 4644, - "1422-date-iso.ts": 4015, - "1423-text-codec.ts": 38476, - "1424-fs-options.ts": 3473, - "1425-stdin-tty.ts": 4093, - "1426-stdin-read.ts": 3982, - "1427-fromcharcode-lastindexof.ts": 2946, - "1428-settled-await-order.ts": 3794, - "1429-promise-catch-finally.ts": 4465, - "1430-promise-race.ts": 4414, - "1431-caught-tostring.ts": 2971, - "1432-destructured-params.ts": 3941, - "1433-filter-narrow.ts": 3907, - "1434-async-never.ts": 3465, - "1435-math-spread.ts": 3652, - "1436-default-undef-param.ts": 3877, - "1437-pad-default.ts": 3475, - "1438-promise-all.ts": 7297, - "1440-interval-basics.ts": 4144, - "1441-interval-clear-cross.ts": 7176, - "1442-interval-throw.ts": 3609, - "1443-signal-handlers.ts": 7600, - "1444-exit-listeners.ts": 4768, - "1445-exit-process-exit.ts": 5403, - "1446-exit-uncaught.ts": 4319, - "1447-stdin-closed-events.ts": 4894, - "1448-stream-columns.ts": 4014, - "1450-incdec-expression.ts": 3639, - "1451-instanceof-uint8array-unknown.ts": 3654, - "1452-return-through-finally.ts": 4057, - "1453-pmap-settled.ts": 4531, - "1454-nonnull-narrow-and-fcc-bytes.ts": 3459, - "1455-lambda-union-return-adoption.ts": 3583, - "1460-console-error-warn.ts": 3484, - "1461-process-pid-getuid-kill.ts": 3345, - "1462-exec-sync.ts": 5307, - "1463-timeout-unref.ts": 3316, - "1464-env-writes.ts": 3946, - "1465-env-value.ts": 3554, - "1466-child-containers.ts": 4064, - "1467-string-match.ts": 3329, - "1468-exec-path.ts": 3588, - "1469-remove-listener.ts": 5747, - "1470-child-lifecycle.ts": 2972, - "1471-child-unref.ts": 8472, - "1472-errno-code.ts": 3388, - "1473-promisify-execfile.ts": 5200, - "1474-string-decoder.ts": 3868, - "1475-readline-closed-stdin.ts": 3587, - "1476-assign-expression.ts": 4319, - "1477-in-expressions.ts": 3951, - "1478-promise-reject.ts": 4331, - "1479-destructuring-assign.ts": 3821, - "1480-os-network-interfaces.ts": 4186, - "1481-string-case-static.ts": 2782, - "1482-spawnsync-error.ts": 2721, - "1483-array-slice.ts": 52661, - "1520-fs-wider-surface.ts": 3691, - "1520-string-split-static.ts": 3432, - "1521-fs-lstat-lock.ts": 3409, - "1521-string-trim-pad-static.ts": 4078, - "1522-parseint-static.ts": 3461, - "1522-spawnsync-options.ts": 5773, - "1523-isnan-floor-static.ts": 3552, - "1523-spawn-options.ts": 5294, - "1524-atomics-sleep-static.ts": 4028, - "1524-catch-narrowing.ts": 2626, - "1525-child-exit-signal.ts": 3477, - "1525-unknown-typeof-validation.ts": 2889, - "1526-set-spread.ts": 3802, - "1527-logical-mixed-operands.ts": 4773, - "1528-delete-records-env.ts": 3621, - "1529-class-with-statics.ts": 3381, - "1530-array-push-variadic.ts": 2970, - "1530-spread-override-completion.ts": 2499, - "1531-delete-optional-fields.ts": 3623, - "1531-process-arch-versions.ts": 2740, - "1532-array-splice-shift.ts": 4204, - "1532-union-shared-field-read.ts": 2834, - "1533-bool-equality.ts": 4076, - "1533-path-platform-namespaces.ts": 3406, - "1534-crypto-hash-chain.ts": 4097, - "1534-union-as-arm-cast.ts": 45787, - "1535-spawn-fd-stdio.ts": 3840, - "1535-union-param-defaults.ts": 4358, - "1536-destructuring-defaults.ts": 3812, - "1536-string-array-sweep.ts": 3859, - "1537-chalk-hybrid/main.ts": 4683, - "1537-os-release-spawnsync-stdio.ts": 2674, - "1538-await-promiselike-return.ts": 2642, - "1538-math-static-scalar.ts": 3735, - "1539-readonly-set-map.ts": 3299, - "1539-unknown-truthiness.ts": 3045, - "1540-os-userinfo.ts": 4119, - "1540-void-statement.ts": 2941, - "1541-fs-readdir-dirent.ts": 5432, - "1541-union-keyed-reads.ts": 4646, - "1542-map-promise-values.ts": 33000, - "1542-record-literal-into-union.ts": 2885, - "1543-rest-destructuring.ts": 3500, - "1543-set-server-handles.ts": 6770, - "1544-dyn-json-reads.ts": 4863, - "1544-string-matchall.ts": 4770, - "1545-spread-order-and-optional.ts": 4536, - "1546-union-element-reads.ts": 4536, - "1547-computed-key-fold.ts": 3960, - "1548-boolean-condition-forms.ts": 4649, - "1549-array-isarray-unions.ts": 4084, - "1550-param-defaults-func-set.ts": 3020, - "1551-dyn-receiver-methods.ts": 3403, - "1552-exec-options-record.ts": 2877, - "1553-truthy-hof-predicates.ts": 3895, - "1554-caught-into-unknown.ts": 3417, - "1555-index-spread-into-declared.ts": 4534, - "1556-union-retag-width-arms.ts": 3587, - "1557-url-host.ts": 2566, - "1558-any-joins-and-dyn-validation.ts": 33883, - "1559-conditional-spread-index-merge.ts": 3282, - "1560-forof-homogeneous-tuples.ts": 3680, - "1561-forof-strings.ts": 2878, - "1561-promise-then.ts": 3851, - "1562-optional-chain-tails.ts": 4618, - "1562-spawn-conditional-spread.ts": 4043, - "1563-forof-iterator-projections.ts": 3930, - "1563-string-raw-fold.ts": 3100, - "1564-fs-watch.ts": 4051, - "1564-string-raw.ts": 3075, - "1565-spawn-pipe-streams.ts": 3895, - "1565-union-element-join.ts": 4167, - "1566-child-duck-interface.ts": 3875, - "1567-server-close-override.ts": 3304, - "1568-x509-fingerprint.ts": 2839, - "1569-fsp-mkdir-unlink-chmod.ts": 3012, - "1570-child-unref-kill-reffed.ts": 9046, - "1571-optional-call-tostring-tail.ts": 26380, - "1571-stdin-set-raw-mode-non-tty.ts": 4151, - "1572-promise-reject-all-tuple.ts": 4201, - "1572-x509-validity-date-gettime.ts": 3944, - "1573-promisify-execfile-env-spread.ts": 4480, - "1573-tdz-scalar-forward-capture.ts": 2862, - "1574-dyn-optional-method-number-keys.ts": 3192, - "1574-promise-all-tuple-literal.ts": 4410, - "1575-unknown-assert-into-record.ts": 3424, - "1576-width-coercions.ts": 4144, - "1577-url-hostname.ts": 4030, - "1578-exec-input-optional.ts": 4028, - "1579-matchall-index.ts": 4108, - "1580-exec-env-conditional-spread.ts": 3516, - "1581-declare-const-read.ts": 3997, - "1582-timer-sleep-resolve.ts": 4177, - "1583-loops-and-stream-captures.ts": 4450, - "1584-union-field-unassigned.ts": 3964, - "1585-union-field-conditional-ctor.ts": 3070, - "1586-derived-field-before-super-init.ts": 4073, - "1590-js-unannotated.js": 4738, - "1591-js-closures.js": 3535, - "1592-js-classes.js": 7400, - "1593-js-jsdoc-records.js": 4695, - "1594-js-async.js": 3836, - "1595-js-island-gap/main.js": 3139, - "1596-cjs-modules/main.js": 3283, - "1597-mjs-graph/main.mjs": 3605, - "1598-cjs-builtin-require/main.js": 2977, - "1599-js-uncaught-throw.js": 3196, - "1600-assert-passing.ts": 4333, - "1601-assert-fail-exit.ts": 3725, - "1602-assert-caught-error.ts": 3373, - "1603-assert-scalar-messages.ts": 5128, - "1604-assert-deep-structures.ts": 4364, - "1605-assert-import-forms.ts": 4371, - "1606-assert-strict-module.ts": 2984, - "1607-assert-throws-match.ts": 4301, - "1608-cjs-assert/main.js": 3820, - "1609-assert-async.ts": 2695, - "1610-path-win32-full.ts": 2757, - "1611-url-file-bridge-neutral.ts": 4375, - "1612-cjs-module-globals.cjs": 3706, - "1613-cjs-esm-proxy-named/main.mjs": 3290, - "1614-js-fence-dodges.cjs": 2861, - "1615-cjs-export-shapes/main.cjs": 4723, - "1616-cjs-esm-lexer-invisible/main.mjs": 2991, - "1617-cjs-esm-lexer-visible/main.mjs": 3860, - "1618-cjs-esm-poisoned-tail/main.mjs": 3236, - "1619-cjs-esm-reexport-invisible/main.mjs": 3244, - "1620-cjs-require-effects-order/main.js": 2604, - "1621-cjs-require-cache-hit/main.js": 3531, - "1622-cjs-require-conditional/main.js": 3501, - "1623-cjs-require-diamond/main.js": 4051, - "1624-cjs-require-dir-mid-file/main.js": 2658, - "1625-cjs-require-lazy-fn/main.js": 3635, - "1626-cjs-require-mjs/main.js": 2942, - "1627-mjs-import-cjs-requires/main.mjs": 3236, - "1628-checked-dynamic-builtin-args.cjs": 2968, - "1629-require-main-filename.cjs": 2826, - "1630-inspect-scalars.ts": 3908, - "1630-rmsync-retry-options.cjs": 3797, - "1631-inspect-arrays.ts": 4065, - "1632-inspect-records.ts": 4843, - "1633-inspect-map-set.ts": 3133, - "1634-inspect-classes.ts": 3805, - "1635-inspect-buffer.ts": 3530, - "1636-util-format.ts": 2945, - "1637-inspect-dyn.ts": 3297, - "1638-inspect-cjs.cjs": 3549, - "1639-versions-openssl-probe.ts": 3544, - "1640-fd-read-decode.ts": 2932, - "1641-string-search.ts": 3476, - "1642-object-fromentries-rows.ts": 3699, - "1643-metadata-dyn-return.cjs": 4074, - "1644-ee-basics.ts": 4205, - "1645-ee-extends.ts": 3512, - "1646-ee-once-remove.ts": 3828, - "1647-ee-meta-events.ts": 3673, - "1648-ee-error-event.ts": 3271, - "1649-ee-names-counts.ts": 3057, - "1650-ee-prepend.ts": 3927, - "1651-ee-max-listeners.ts": 4648, - "1652-ee-snapshot.ts": 4220, - "1653-ee-cjs.cjs": 3230, - "1654-ee-namespace.ts": 3631, - "1655-spawnsync-neutral.ts": 5393, - "1656-execsync-neutral.ts": 3546, - "1657-spawn-async-neutral.ts": 3320, - "1660-buffer-read-write-num.ts": 4303, - "1661-buffer-encodings-full.ts": 3877, - "1662-string-decoder-encodings.ts": 3423, - "1663-buffer-compare-search-fill.ts": 4770, - "1664-dyn-fn-boundary.cjs": 3405, - "1665-dyn-fn-mustcall/main.cjs": 2819, - "1666-dyn-fn-identity.ts": 2740, - "1667-dyn-fn-not-callable.cjs": 3303, - "1668-dyn-fn-throws.cjs": 3745, - "1669-symbol-identity.ts": 3617, - "1670-symbol-registry.ts": 4207, - "1671-symbol-typeof.ts": 3400, - "1672-symbol-containers.ts": 3581, - "1673-symbol-cjs.cjs": 3624, - "1674-symbol-inspect.ts": 2758, - "1676-func-array-surface.ts": 2900, - "1677-emitter-listeners.ts": 4932, - "1678-emitter-dyn-listeners.cjs": 3287, - "1679-console-dyn.js": 3576, - "1680-assert-bytes.ts": 3903, - "1681-assert-funcs.ts": 3918, - "1685-stream-readable-basics.ts": 6503, - "1686-stream-readable-paused.ts": 6965, - "1687-stream-readable-flow-control.ts": 3669, - "1688-stream-writable-basics.ts": 3711, - "1689-stream-writable-async-drain.ts": 6004, - "1690-stream-duplex.ts": 6220, - "1691-stream-transform.ts": 6193, - "1692-stream-pipe.ts": 6662, - "1693-stream-pipe-backpressure.ts": 3700, - "1694-stream-destroy.ts": 3259, - "1695-stream-props.ts": 4843, - "1696-stream-read-inside-read.ts": 3524, - "1697-stream-tick-vs-timer.ts": 3256, - "1698-stream-uncaught-error.ts": 4826, - "1699-stream-callback-shapes.ts": 3791, - "1700-mustcall-common/main.cjs": 4329, - "1701-mustcall-exit-report/main.cjs": 3995, - "1702-dyn-proto-dispatch.cjs": 4694, - "1703-arguments-rest-props.cjs": 3485, - "1710-cjs-export-class/main.js": 2945, - "1711-cjs-export-single-values/main.js": 3527, - "1712-field-incdec.ts": 3301, - "1713-js-dyn-fields.js": 3702, - "1720-assert-throws-shape.ts": 3665, - "1721-assert-throws-regex-class.ts": 3205, - "1722-assert-rejects.ts": 3941, - "1723-assert-does-not-reject.ts": 3991, - "1724-assert-iferror.ts": 2922, - "1725-assert-symbols.ts": 4117, - "1726-promise-with-resolvers.ts": 3773, - "1727-assert-throws-shape-exit.ts": 4180, - "1730-countdown-symbol-keys/main.js": 3717, - "1731-symbol-field-shapes.cjs": 4041, - "1732-symbol-field-cross-module/main.js": 3671, - "1740-stream-extends-readable.ts": 5413, - "1741-stream-extends-writable.ts": 6469, - "1742-stream-extends-duplex-transform.ts": 6437, - "1743-stream-extends-vt-dispatch.ts": 7212, - "1744-stream-set-encoding.ts": 3635, - "1745-stream-encoding-js.cjs": 2973, - "1746-stream-for-await.ts": 4707, - "1747-stream-for-await-js.cjs": 4902, - "1750-http-agent-options.ts": 7371, - "1751-options-record-stance.ts": 4112, - "1760-surplus-args-drop.cjs": 3672, - "1761-emitter-special-event-names.cjs": 5508, - "1770-assert-dyn-strict.ts": 4072, - "1771-assert-dyn-deep.ts": 3521, - "1772-assert-dyn-js.cjs": 3744, - "1773-assert-dyn-exit.ts": 3156, - "1780-http-res-surface.ts": 7173, - "1781-http-server-shapes.cjs": 3500, - "1790-computed-key-folds.ts": 3641, - "1791-searchparams-core.ts": 3206, - "1792-searchparams-encoding.ts": 3500, - "1793-searchparams-iteration.ts": 3620, - "1794-searchparams-url-live.ts": 3114, - "1795-dyn-handle-request.cjs": 6764, - "1796-dyn-handle-body.cjs": 6226, - "1797-dyn-handle-socket.cjs": 3886, - "1798-dyn-handle-typed-box.ts": 6248, - "1800-immediate-basics.ts": 3211, - "1801-immediate-handle.ts": 3455, - "1802-immediate-unref-exit.ts": 3001, - "1803-timeout-refresh.ts": 3714, - "1804-timers-module.ts": 3580, - "1805-timer-callback-args.ts": 3293, - "1806-timer-delay-trunc.ts": 3354, - "1810-stream-options-const-record.ts": 4095, - "1811-stream-option-value-callbacks.cjs": 3113, - "1812-stream-super-options-forwarding.cjs": 3239, - "1813-stream-finished.ts": 3753, - "1814-stream-pipeline.cjs": 3633, - "1815-stream-state-reads.cjs": 3723, - "1820-static-block-basics.ts": 3496, - "1821-static-block-order.ts": 3121, - "1822-static-block-js.js": 2926, - "1823-static-instance-method-names.ts": 2971, - "1824-for-of-destructuring-defaults.ts": 3389, - "1825-exhaustive-typeof-switch.ts": 2558, - "1826-settimeout-arguments-callback.js": 3292, - "1830-enum-numeric-basics.ts": 3721, - "1831-enum-string-const-reverse.ts": 3386, - "1832-enum-modules/main.ts": 3491, - "1835-var-basics.ts": 3090, - "1836-var-loop-capture.ts": 4227, - "1837-var-undefined-hoisting.ts": 3129, - "1838-var-modules/main.ts": 3442, - "1839-var-js/main.cjs": 2943, - "1840-jsdoc-import-type-only/main.js": 3153, - "1850-overload-basics.ts": 3527, - "1851-overload-return-narrowing.ts": 3485, - "1852-overload-class-members.ts": 3165, - "1853-overload-modules/main.ts": 3369, - "1854-ambient-declare-fn.ts": 3339, - "1855-ambient-declare-fn-uncaught.ts": 2825, - "1870-unit-type-values.ts": 3760, - "1871-empty-array-never.ts": 2995, - "1872-template-tostring-composite.ts": 3281, - "1880-json-declared-field-order.ts": 3669, - "1881-default-anon/main.ts": 3229, - "1882-default-cjs-interop/main.ts": 3282, - "1883-default-exports/main.ts": 3775, - "1890-ns-imports/main.ts": 3324, - "1891-ns-reexport/main.ts": 3059, - "1940-class-values-basics.ts": 3481, - "1941-class-values-registry.ts": 3018, - "1942-class-expressions.ts": 2837, - "1943-class-statics-expanded.ts": 3294, - "1944-class-values-modules/main.ts": 3634, - "1950-generic-fn-values.ts": 3713, - "1951-generic-classes-basics.ts": 3449, - "1952-generic-class-statics-values.ts": 3863, - "1953-generic-class-hierarchy.ts": 3771, - "1954-generic-modules/main.ts": 3506, - "1960-namespace-basics.ts": 3039, - "1961-namespace-nested.ts": 2879, - "1962-namespace-classes.ts": 3820, - "1963-namespace-aliases.ts": 2943, - "1964-namespace-type-only.ts": 3637, - "1965-namespace-ambient.ts": 3549, - "1966-namespace-modules/main.ts": 3116, - "1967-namespace-alias-typeonly.ts": 2879, - "1968-namespace-import-eq-snapshot.ts": 3284, - "1970-decorators-basics.ts": 3029, - "1971-decorators-rebinding.ts": 3079, - "1972-decorators-throwing.ts": 2634, - "1973-decorators-ambient.ts": 3508, - "1980-primitive-proto-statics.ts": 3768, - "1981-date-utc.ts": 3681, - "1982-freeze-resolve-passthrough.ts": 3352, - "1983-array-tuple-surfaces.ts": 3130, - "1990-labels-basics.ts": 3662, - "1991-for-in-loops.ts": 4312, - "1992-small-syntax.ts": 3036, - "200-strings.ts": 3188, - "2000-generic-methods-basic.ts": 3233, - "2001-generic-methods-generic-class.ts": 3654, - "2002-generic-methods-inheritance.ts": 3883, - "2003-generic-methods-object-literal.ts": 4002, - "2004-generic-methods-statics.ts": 2781, - "201-templates.ts": 3259, - "2010-generators-basics.ts": 2930, - "2011-generators-forof.ts": 3439, - "2012-generators-return-throw.ts": 3077, - "2013-generators-sent-values.ts": 3157, - "2014-generators-values.ts": 3230, - "2015-generators-yieldstar.ts": 2691, - "2016-generators-rc-stress.ts": 3408, - "2017-generators-async.ts": 3645, - "2018-generators-uncaught.ts": 3372, - "2019-generators-loops.ts": 3684, - "2020-generic-value-bindings.ts": 4814, - "2021-generic-value-binding-modules/main.ts": 2613, - "2022-width-index-capture.ts": 4066, - "2023-width-readonly.ts": 3988, - "2024-width-array-elems.ts": 3088, - "2025-width-union-compose.ts": 3148, - "2026-width-spread.ts": 3721, - "2030-width-nested.ts": 3956, - "2031-width-tuples.ts": 3454, - "2032-cjs-export-assigned-class-expression/main.cjs": 3771, - "2033-property-assigned-class-extends.cjs": 2857, - "2035-ternary-array-retag.cjs": 3742, - "2036-evolving-array-decl.cjs": 3733, - "2037-fn-decl-hoisting.cjs": 3504, - "2038-evolving-array-js.cjs": 4258, - "2040-any-bindings.ts": 3664, - "2040-mixin-heritage.ts": 3192, - "2041-any-params-returns.ts": 3362, - "2041-mixin-values.ts": 3893, - "2042-any-flow-loops.ts": 3770, - "2042-mixin-modules/main.ts": 3144, - "2043-any-dom-values.ts": 3779, - "2043-mixin-rc-stress.ts": 4190, - "2044-vtable-sibling-slots.ts": 4054, - "2045-objlit-accessors-basic.ts": 3443, - "2045-parameter-properties.ts": 3049, - "2046-abstract-classes.ts": 4183, - "2046-objlit-accessors-effects.ts": 3215, - "2047-objlit-accessors-shapes.ts": 3091, - "2047-optional-class-fields.ts": 4146, - "2050-dynamic-import-own-module/main.ts": 3451, - "2051-dynamic-import-then/main.ts": 3601, - "2052-dynamic-import-self.ts": 3591, - "2053-typeof-static-fold.ts": 3479, - "2054-destructuring-island-source.ts": 3225, - "2060-empty-tuple.ts": 3198, - "2070-computed-key-folds.ts": 3432, - "2071-integer-key-enumeration-order.ts": 3897, - "2072-expando-function-members.ts": 3830, - "2073-destructuring-assignment.ts": 3540, - "2074-island-destructuring.ts": 3784, - "2075-index-signature-runtime-keys.ts": 2791, - "2080-destructuring-defaults.ts": 3781, - "2081-destructuring-rest.ts": 3728, - "2082-destructuring-assignment.ts": 3505, - "2085-destructuring-heads-rest-params.ts": 3490, - "2087-destructuring-for-heads.ts": 3190, - "2090-index-signature-number-keys.ts": 2803, - "2091-index-signature-string-intersection.ts": 2723, - "2092-package-imports/main.ts": 2857, - "2093-timers-promises.ts": 4034, - "2094-diagnostics-channel.ts": 3375, - "2095-js-catch-unknown.js": 3448, - "2096-os-type.ts": 3784, - "2097-js-evolving-globals.js": 3531, - "210-string-methods.ts": 3602, - "2100-stream-default-hwm.ts": 3572, - "2101-dyn-param-defaults.ts": 2799, - "2102-empty-pattern-decls.ts": 3305, - "2105-computed-key-static-folds.ts": 3458, - "2106-comma-expressions.ts": 3662, - "211-string-unicode.ts": 2722, - "2110-top-object-types.ts": 2930, - "2111-json-stringify-unknown.ts": 2894, - "2112-array-at-findlast.ts": 3347, - "2113-string-index-methods.js": 2954, - "212-string-aliasing-append.ts": 2936, - "2120-package-self-import/main.ts": 2637, - "2121-esm-cycle-inert-backedge/main.ts": 2688, - "2123-ambient-import-crash/main.ts": 2830, - "2124-imports-field-wildcard/main.ts": 2779, - "2140-uri-component-codecs.ts": 3028, - "2160-dc-tracing-channel.cjs": 3125, - "2161-js-object-literal-identity.cjs": 3710, - "2162-js-dyn-destructure.cjs": 2745, - "2163-js-selfref-const.cjs": 4243, - "2164-js-then-dyn-handler.cjs": 4454, - "2165-js-throw-dyn.cjs": 3859, - "2166-cjs-getter-this/main.cjs": 3481, - "2170-island-array-exits.ts": 5249, - "2171-island-json-stringify.ts": 3408, - "2172-island-regexp-boundary.ts": 4656, - "2190-new-object-and-string-wrappers.ts": 2892, - "2191-uri-encoders.ts": 2460, - "2192-object-default-tostring.ts": 3152, - "2193-discarded-stdlib-reads.ts": 3436, - "2194-ambient-undef-chains.ts": 3329, - "2195-require-missing-package/main.mjs": 3396, - "2196-island-computed-key-chain.ts": 4364, - "2210-dyn-promise-crossing.cjs": 3412, - "2211-dc-trace-promise.cjs": 4496, - "2212-unhandled-rejection-listener.cjs": 3503, - "2213-async-local-storage.cjs": 3304, - "2214-dc-bind-store.cjs": 4372, - "2215-process-emit-warning-exit.cjs": 4385, - "2240-width-class-into-record.ts": 3816, - "2241-width-record-into-class.ts": 3107, - "2242-width-statics-into-record.ts": 3187, - "2243-width-assert-satisfies-shapes.ts": 3725, - "2250-tagged-templates.ts": 2802, - "2252-class-iterators.ts": 4024, - "2253-union-coercions.ts": 2969, - "2260-http2-constants.cjs": 2611, - "2280-builtin-namespace-destructure.cjs": 2964, - "2281-domexception-atob-btoa.cjs": 3116, - "2282-queue-microtask.cjs": 3672, - "2283-structured-clone.cjs": 3661, - "2284-regexp-constructor.cjs": 2921, - "2285-iferror-dyn.cjs": 2815, - "2286-dyn-object-walks.cjs": 2814, - "2300-dyn-record-spread-and-keyed-access.cjs": 3340, - "2301-cjs-export-table-dom-attach.cjs": 2994, - "2310-process-next-tick.ts": 3462, - "2311-next-tick-js-callbacks.js": 3541, - "2312-stream-underscore-assign.ts": 4210, - "2313-stream-underscore-assign-js.cjs": 3281, - "2314-process-introspection.ts": 4007, - "2320-await-unit.ts": 2773, - "2321-emitter-static-setmax.cjs": 3576, - "2322-dyn-array-sort.cjs": 2683, - "2323-timer-callback-returns.ts": 3611, - "2330-tls-server-runtime-options.cjs": 22007, - "2331-tls-server-options-typed.ts": 22550, - "2350-nan-global.ts": 3586, - "2351-async-methods.ts": 3496, - "2352-void-coercions.ts": 2875, - "2353-decorators-member-ambient.ts": 2855, - "2354-decorators-expression-ambient.ts": 2445, - "2360-tuple-to-array.ts": 3725, - "2361-assign-index-record.ts": 3918, - "2362-execsync-inherit.ts": 3204, - "2363-nullish-retag.ts": 3277, - "2364-in-runtime-key.ts": 3653, - "2365-dyn-into-island.ts": 2967, - "2366-string-well-formed.ts": 3530, - "2367-regexp-escape.ts": 3537, - "2368-set-methods.ts": 3321, - "2369-promise-try.ts": 3477, - "2370-group-by.ts": 3834, - "2371-iterator-helpers.ts": 3503, - "2380-buffer-module-named-import.ts": 3337, - "2381-builtin-default-imports.js": 3386, - "2382-cycle-two-decl/main.ts": 2540, - "2383-cycle-assertdoc/main.js": 3635, - "2384-cycle-mixed-bindings/main.ts": 2680, - "2385-builtin-reexport-facade/main.ts": 2415, - "2386-string-to-chars.ts": 3358, - "2387-tonumber-argv.ts": 3189, - "2388-tonumber-grammar-zoo.ts": 2478, - "2389-tonumber-unary-plus.ts": 3505, - "2390-dot-requires/main.cjs": 3502, - "2391-default-builtin-imports/main.ts": 3560, - "2392-block-guard-alias-scope.ts": 2811, - "2393-loop-exit-guard-narrow.ts": 3396, - "2394-loop-narrow-containment.ts": 3368, - "2395-while-cond-guard.ts": 2736, - "2396-born-narrowed-decl.ts": 3324, - "2397-branch-kill-merge.ts": 2593, - "2398-switch-arm-kill.ts": 3348, - "2399-reassign-kill-polarity.ts": 3637, - "240-stdout-write.ts": 3882, - "2400-kill-return-edge.ts": 3195, - "2401-kill-break-staging.ts": 3449, - "2402-labeled-break-kill.ts": 2813, - "2403-kill-continue-backedge.ts": 3428, - "2404-kill-throw-catch.ts": 2824, - "2405-sibling-arm-narrow.ts": 2442, - "2406-switch-clause-sibling-narrow.ts": 2665, - "2407-infinite-loop-seal.ts": 3312, - "2408-loop-terminality-reopen.ts": 3281, - "2409-loop-body-return-not-terminal.ts": 3145, - "2410-stacked-cases-terminality.ts": 3627, - "2411-exhaustive-switch-terminality.ts": 3158, - "2412-dowhile-guard-trailing-test.ts": 3265, - "2413-dowhile-kill-renarrow.ts": 2701, - "2414-dowhile-continue-kill.ts": 3317, - "2415-dowhile-break-trailing-test.ts": 3362, - "2416-spread-ternary-positions.ts": 3467, - "2417-ternary-local-inference.ts": 3368, - "2418-wrapped-ternary-arms.ts": 3310, - "2419-selector-ternary-union.ts": 3702, - "2420-nullish-null-identity.ts": 2553, - "2421-float-lane-inference.ts": 3344, - "2422-ieee-div-rem-corners.ts": 3186, - "2423-param-null-reassign.ts": 3025, - "2424-ternary-miss-first-narrowing.ts": 3552, - "2425-elseif-head-exit-narrow.ts": 3257, - "2426-default-snapshot-mutable/main.ts": 3423, - "2427-perf-hooks-now.ts": 3049, - "2428-field-redeclare-inherited.ts": 3585, - "2429-destructure-class-instance.ts": 2694, - "2430-empty-literal-unknown-field.js": 3401, - "2431-primitive-ctor-values.ts": 2884, - "2432-generic-member-fields-async.ts": 5845, - "2433-as-const-command-tables.ts": 3180, - "2434-deferred-init-fields.ts": 3249, - "2435-loose-same-kind-equality.ts": 3363, - "2440-console-inspect-args.ts": 5418, - "2441-console-process-argv.ts": 3041, - "2442-union-literal-arm-widening.ts": 2555, - "2443-union-literal-shadow-narrowing.ts": 3302, - "2444-union-literal-reducer-spread.ts": 3618, - "2445-math-minmax-nary.ts": 2645, - "2446-performance-global.ts": 3980, - "2447-island-boundary-slots/main.ts": 6217, - "2448-entries-null-arms-regex-arrays.ts": 3545, - "2450-private-instance-methods.ts": 2931, - "2451-private-fields.ts": 3694, - "2452-private-statics.ts": 2965, - "2453-private-accessors.ts": 3478, - "2454-private-generator-methods.ts": 3697, - "2455-private-brand-checks.ts": 3515, - "2456-private-async-methods.ts": 3535, - "2457-private-members.js": 2780, - "2460-qs-parse-grammar.ts": 4534, - "2461-qs-parse-options.ts": 4115, - "2462-qs-stringify.ts": 3434, - "2463-qs-escape-unescape.ts": 3311, - "2464-qs-require-forms.cjs": 2821, - "2470-mockable-module-shape.js": 2978, - "2471-record-keyed-write-hasown.js": 2970, - "2472-string-regexp-union.ts": 3393, - "2473-option-table-widths.js": 3653, - "2474-island-literal-forms.ts": 3643, - "2475-variadic-wrapper-island.js": 3606, - "2476-map-regex-values.ts": 3711, - "2480-recursive-record-tree.ts": 3376, - "2481-mutual-recursive-records.ts": 3699, - "2482-recursive-union-tree.ts": 3347, - "2483-recursive-record-cycles.ts": 2764, - "2484-json-stringify-circular.ts": 3845, - "2485-inspect-circular-refs.ts": 3694, - "2486-recursive-record-boundaries.ts": 3988, - "2487-recursive-deep-equal.ts": 3416, - "2488-ast-walker.ts": 3100, - "2490-find-miss-null-compare.ts": 3107, - "2491-strict-unit-compare-no-arm.ts": 2820, - "2492-loose-null-compare-unions.ts": 3683, - "2493-switch-unit-cases.ts": 3703, - "2494-no-arm-compare-effects.ts": 3089, - "2500-net-autosel-timeout.ts": 3775, - "2510-dyn-evolving-array-map.ts": 3265, - "2511-dyn-evolving-array-filter-foreach.ts": 2885, - "2512-dyn-evolving-array-mixed-push.ts": 3639, - "2520-private-statics-class-name-calls.ts": 3155, - "2521-private-statics-class-expression.ts": 3520, - "2522-private-statics-decorated.ts": 3673, - "2523-private-statics-aliased-class.ts": 3795, - "2530-object-destructuring-decl.ts": 2869, - "2531-array-tuple-destructuring-decl.ts": 3168, - "2532-param-destructuring-options.ts": 2906, - "2533-forof-destructuring.ts": 2840, - "2534-object-rest-decl.ts": 3199, - "2535-destructuring-assign-basics.ts": 3816, - "2536-destructuring-assign-nested.ts": 3047, - "2537-destructuring-assign-member-targets.ts": 3590, - "2538-destructuring-assign-class-source.ts": 3544, - "2539-class-instance-rest.ts": 3764, - "2540-accessor-destructuring-defaults.ts": 3201, - "2541-destructuring-eval-order.ts": 2670, - "2550-generics-keyof-pick.ts": 3947, - "2551-generics-value-aliases.ts": 3227, - "2552-generics-iface-methods.ts": 3318, - "2553-generics-signature-bindings.ts": 3442, - "2554-generics-frontier-mix.ts": 3344, - "2555-generics-keyof-writes.ts": 3448, - "2556-crypto-introspection.ts": 3351, - "2556-width-hybrid-shapes.ts": 3462, - "2557-tls-ca-store.ts": 4438, - "2557-width-field-lifts.ts": 4453, - "2558-index-signature-func-values.ts": 3590, - "2558-rejection-events.cjs": 3641, - "2559-index-signature-container-values.ts": 4066, - "2560-arraybuffer-dataview-set.ts": 3614, - "2561-object-is.ts": 3391, - "2562-intl-numberformat-en-us.ts": 3241, - "2563-stream-promises-pipeline.ts": 3490, - "2564-stream-promises-finished.ts": 2994, - "2565-stream-promises-js.cjs": 2616, - "2566-promise-reject-dyn-reason.js": 3548, - "2567-rest-spread-forward.js": 3292, - "2568-rest-spread-forward-dynamic.js": 3367, - "2569-upcast-identity.ts": 2553, - "2570-buffer-arg-validation.cjs": 5779, - "2571-searchparams-arg-validation.cjs": 3913, - "2572-readable-emitted-readable-flag.cjs": 3556, - "2573-fs-url-arg-ladders.cjs": 3502, - "2574-emitter-max-listeners-ladders.cjs": 2895, - "2575-string-destructuring-decl.ts": 3433, - "2576-string-destructuring-assign.ts": 2984, - "2577-builtin-global-destructuring.js": 3466, - "2578-jsval-into-unknown-rows.ts": 3398, - "2579-jsval-object-param-crossing.js": 3294, - "2580-jsval-routed-keyed-ops.js": 3298, - "2581-jsval-routed-calls.js": 3542, - "2582-jsval-object-statics.js": 3509, - "2582-jsval-routed-keyed-ops.js": 3136, - "2583-dyn-nullish-coalesce.js": 2976, - "2583-jsval-routed-calls.js": 2950, - "2584-jsval-object-statics.js": 3305, - "2584-union-dyn-collapse.ts": 2970, - "2585-dyn-nullish-coalesce.js": 3632, - "2585-unknown-array.ts": 3809, - "2586-object-assign-spread.js": 4084, - "2587-array-entries-forof.ts": 2633, - "2588-array-entries-chains.ts": 4097, - "2589-object-create-null.cjs": 3112, - "2590-object-create-dynamic.js": 4279, - "2591-ambient-generic-traps.ts": 3216, - "2592-ambient-trap-uncaught.ts": 3198, - "2593-generic-inert-bindings.ts": 3007, - "2594-nullish-generic-bindings.ts": 3321, - "2595-fs-arg-ladders.cjs": 3930, - "2596-net-arg-ladders.cjs": 3528, - "2597-dgram-send-ladders.cjs": 4536, - "2598-tls-arg-ladders.cjs": 4142, - "2599-stream-arg-ladders.cjs": 5465, - "2600-dyn-keyed-write-harness.js": 2800, - "2601-dyn-keyed-write-ops.js": 3269, - "2602-dyn-array-destructure.js": 3207, - "2603-island-keyed-write-dyn.js": 3608, - "2604-cycle-classes-mutual/main.ts": 3410, - "2605-cycle-three-module/main.ts": 2592, - "2606-dynamic-import-cycle/main.ts": 2445, - "2608-regex-named-groups.ts": 3896, - "2609-regex-named-replace.ts": 3215, - "2610-regex-named-matchall.ts": 3887, - "2611-regex-named-groups-js.cjs": 3523, - "2613-for-init-uninitialized-let.ts": 3167, - "2614-trap-binding-later-writes.ts": 3345, - "2615-nullish-field-record-binding.ts": 2720, - "2616-js-collect-probe-defers.js": 3534, - "2617-enum-static-field-import/main.ts": 3068, - "2618-ee-emit-override.ts": 3915, - "2619-ee-override-chain.ts": 2567, - "2620-ee-override-once-order.ts": 3439, - "2621-ee-override-error-throw.ts": 3265, - "2622-ee-override-filter.ts": 3339, - "2623-ee-job-queue.ts": 3586, - "2624-ee-override-js.cjs": 2637, - "2625-bytes-views.ts": 2847, - "2626-stream-event-names.ts": 3436, - "2627-stream-ticks-are-nextticks.ts": 4048, - "2628-stream-push-encodings.cjs": 3219, - "2629-stream-consumers.ts": 4523, - "2630-stream-consumers-errors.ts": 3353, - "2631-create-require/main.ts": 2869, - "2632-dyn-jsval-iterate.js": 3938, - "2633-island-promise-crossing.js": 3898, - "2634-stream-pipeline-arg-ladders.cjs": 2829, - "2635-trap-binding-unmappable-written.ts": 3493, - "2636-trap-binding-ambient-rooted-rewrite.ts": 3155, - "2637-create-require-bare/main.ts": 3093, - "2638-http-dyn-res-compat.cjs": 7897, - "2639-http-dyn-req-pause.cjs": 4028, - "2640-net-dyn-socket-compat.cjs": 3877, - "2641-http-agent-surface.cjs": 6286, - "2642-http-agent-maxsockets.cjs": 4681, - "2643-or-default-retag.ts": 3292, - "2644-http-client-url-argument.ts": 3256, - "2645-https-client-url-argument.ts": 24258, - "2646-top-level-await.ts": 3392, - "2647-top-level-await-modules/main.ts": 3047, - "2648-top-level-await-rejection.ts": 3150, - "2649-top-level-await-pending.ts": 3160, - "2650-top-level-await-self-import.ts": 2440, - "2651-top-level-await-pending-unhandled.ts": 2762, - "2652-top-level-await-dynamic/main.ts": 3760, - "2653-top-level-await-rejection-stops-loop.ts": 2631, - "2654-top-level-await-unrelated-rejection.ts": 2643, - "2655-top-level-await-cycle/main.ts": 3098, - "2656-top-level-await-cycle-order/main.ts": 3167, - "2657-top-level-await-cycle-dynamic/main.ts": 2957, - "2658-top-level-await-sync-completion/main.ts": 3318, - "2659-top-level-await-dynamic-cycle/main.ts": 2585, - "2660-top-level-await-cycle-rejection/main.ts": 3210, - "2661-top-level-await-dynamic-runtime-root/main.ts": 3366, - "2662-top-level-await-cycle-external-wait/main.ts": 3005, - "2663-top-level-await-dependency-errors/main.ts": 2847, - "2664-top-level-await-same-checkpoint-rejection.ts": 3116, - "2665-top-level-await-unhandled-listener-liveness.ts": 2875, - "2666-array-to-sorted.ts": 7633, - "2668-stored-number-array-iterator.ts": 3253, - "2669-array-copying-methods.ts": 3549, - "2670-uint8array-copy-iterate.ts": 3853, - "2671-array-copying-js.cjs": 3682, - "2672-http-request-response-callback.ts": 4143, - "2673-top-level-await-implicit-module.ts": 3247, - "2674-top-level-for-await-implicit-module.ts": 3134, - "2675-create-require-nested-esm.js": 2364, - "2675-process-self-reexec.ts": 2727, - "2676-indexed-read-strict-equality.ts": 2731, - "2676-record-clone-overrides.ts": 2957, - "2676-top-level-await-ambiguous/main.ts": 2732, - "2677-runtime-optional-closure.ts": 4013, - "2677-top-level-await-module-scope/main.ts": 3413, - "2678-date-instances.ts": 2682, - "2678-large-string-sparse-index.ts": 3695, - "2678-util-parseargs.ts": 6509, - "2679-util-parseargs-cjs.cjs": 4674, - "2680-util-parseargs-default.ts": 2968, - "2681-util-parseargs-dynamic.js": 4805, - "2682-fs-rename.ts": 3870, - "2683-filter-truthy-predicate.ts": 3057, - "2683-fs-rename-js.cjs": 3194, - "2684-filter-no-predicate-dyn.cjs": 2886, - "2684-fs-rename-abort.ts": 4187, - "2685-array-unshift-reverse.ts": 3336, - "2685-fs-write-sync.ts": 3018, - "2686-fs-file-handle.ts": 4533, - "2687-fsp-write-file-options.ts": 3468, - "2687-process-write-args.ts": 4056, - "2688-fs-stats-disk-fields.ts": 3885, - "2689-http-server-timeout-properties.ts": 3928, - "2690-https-server-timeout-option.ts": 21635, - "2691-retained-lowering-record-order.ts": 4394, - "2692-inline-record-assert-index.ts": 4260, - "2692-string-accumulation.ts": 2769, - "2693-math-constants.ts": 2825, - "2700-array-sort-complexity.ts": 3067, - "2700-http-static-trailers.ts": 3428, - "2700-wasi-core.ts": 3839, - "2701-array-sort-snapshot.ts": 3995, - "2701-http-static-trailer-wire.ts": 3237, - "2701-import-meta/main.mjs": 2794, - "2702-http-static-trailer-end.ts": 3525, - "2702-type-import-link-static/main.ts": 3217, - "2702-uint8array-to-sorted.ts": 3838, - "2703-array-sparse-sort.ts": 7138, - "2703-type-import-link-static-reexport/main.ts": 2989, - "2704-array-sparse-to-sorted.ts": 7251, - "2704-type-import-link-static-success/main.ts": 3249, - "2705-array-sparse-sort-append.ts": 6240, - "2705-type-import-link-dynamic/main.ts": 2760, - "2706-array-search-missing-needle.ts": 3091, - "2706-type-import-link-dynamic-success/main.ts": 3330, - "2707-array-search-huge-sparse.ts": 45179, - "2707-type-import-link-static-default-reexport/main.ts": 2758, - "2708-type-import-link-dynamic/main.ts": 4132, - "2720-array-read-parity.ts": 3456, - "2721-array-nested-missing-receiver.ts": 3322, - "2750-array-sparse-foundation.ts": 3816, - "2751-buffer-concat-sparse-list.ts": 3043, - "2752-array-optional-stores.ts": 3548, - "2753-array-optional-method-values.ts": 3422, - "2754-array-property-undefined.ts": 3456, - "2755-array-optional-literal-forwarding.ts": 3507, - "2780-array-parity-composition.ts": 9748, - "2781-array-missing-value-escape.ts": 2984, - "2782-array-sparse-callbacks.ts": 3368, - "2783-array-defaults-rest.ts": 3426, - "2784-array-reduce-missing-result.ts": 2920, - "2785-array-missing-destructuring.ts": 4220, - "2786-array-sparse-serialization.ts": 3688, - "2787-array-missing-string-arithmetic.ts": 2565, - "2788-array-missing-builtin-conversions.ts": 2767, - "2789-array-missing-optional-tostring.ts": 3009, - "2790-dynamic-import-self-name/main.ts": 3621, - "2791-record-optional-keys.ts": 3660, - "2792-record-optional-spread-entries.ts": 3741, - "2793-union-conversion-identity-capture.ts": 3519, - "2794-array-generic-callback-values.ts": 2842, - "2795-array-optional-typedarray-source.ts": 29642, - "2796-array-sparse-class-value-instanceof.ts": 4014, - "2800-array-expression-consumers.ts": 2964, - "2801-array-switch-consumers.ts": 3634, - "2820-array-missing-method-receivers.ts": 3219, - "2821-array-optional-numeric-comparisons.ts": 3648, - "2830-cjs-require-self-name/main.cjs": 2817, - "2840-finally-completions.ts": 2484, - "2841-using-sync.ts": 3654, - "2842-await-using.ts": 3139, - "2843-using-suppressed-error.ts": 3369, - "2844-node-disposable-resources.ts": 3909, - "2845-for-using-of.ts": 3562, - "2846-generator-using.ts": 3676, - "2850-async-generators-protocol.ts": 4224, - "2851-async-generators-values.ts": 3418, - "2852-timers-promises-interval.ts": 3999, - "2854-leading-bom-literals.ts": 3644, - "2855-searchparams-sort-complexity.ts": 2896, - "2856-dynamic-any-local-operators.ts": 3122, - "2857-branded-primitives.ts": 2535, - "2858-import-meta-resolve/main.mjs": 2803, - "2859-import-meta-resolve-dynamic/main.mjs": 2791, - "2860-require-resolve/main.cjs": 3416, - "2861-readable-iterator-close.ts": 3965, - "2862-web-readable-async-iteration.ts": 48807, - "2863-class-async-iterator.ts": 3227, - "2864-array-class-method-receivers.ts": 2404, - "2865-cjs-process-alias.cjs": 3403, - "2866-dyn-switch.ts": 2311, - "2867-array-shift-length.js": 3507, - "2868-empty-record-promise-presence.js": 2925, - "2869-nullish-mixed-lazy.ts": 2958, - "2870-and-dropped-truthy-arm.ts": 2834, - "2871-optional-promise-to-unknown.ts": 3267, - "2872-unobserved-dynamic-event.ts": 3355, - "2873-optional-receiver-loop-lifetime.js": 3047, - "2874-nullable-callback-field-cycle.js": 3534, - "2875-evolving-let-read-before-write.js": 2810, - "2876-class-unknown-roundtrip.ts": 3764, - "2877-child-unknown-roundtrip.ts": 2785, - "2878-class-private-unknown.cjs": 3352, - "2880-cjs-module-graph/main.cjs": 3782, - "2890-crypto-static-utility.ts": 2826, - "2891-json-record-key-escapes.ts": 3816, - "2892-windows-console-unicode.ts": 3563, - "2893-child-stdin-duplex.ts": 3578, - "2894-child-stdin-backpressure.ts": 3536, - "2895-child-stdin-write-after-end.ts": 3038, - "2896-child-stdin-write-after-finish.ts": 3391, - "2897-child-stdin-destroy.ts": 3563, - "2898-child-stdin-spawn-failure.ts": 3036, - "2899-nested-mutual-recursion.ts": 3350, - "2900-float64array-static.ts": 3865, - "2900-void-conditional-arrow.ts": 2828, - "2901-bigint-core.ts": 3440, - "2902-bigint-bytes.ts": 2684, - "2903-bigint-storage.ts": 3029, - "2904-bigint-errors.ts": 3660, - "2905-bigint-edges.ts": 3424, - "2906-bigint-wasi-width.ts": 3017, - "2910-overloaded-callable-alias.ts": 3175, - "2911-execfile-callback.ts": 3152, - "2912-execfile-callback-options.ts": 3514, - "2912-promisify-fs-readfile.ts": 3307, - "2913-builtin-callable-alias.ts": 4333, - "2914-fixed-tuple-call-spread.ts": 3657, - "2915-path-function-values.ts": 2684, - "2916-path-function-values-cjs.ts": 3544, - "2917-builtin-callable-widths.ts": 3312, - "2918-builtin-callable-widths-cjs.ts": 4040, - "2919-fs-function-values.ts": 4214, - "2920-fs-promises-function-values.ts": 2447, - "2921-zlib-callbacks.ts": 4192, - "2922-dyn-bitwise-not.cjs": 2704, - "2923-cjs-factory-export/main.cjs": 3106, - "2930-union-receiver-calls.ts": 3700, - "2931-union-dynamic-field-call.js": 3466, - "2932-create-require-program/main.ts": 3686, - "2933-static-dynamic-import/main.ts": 3405, - "2934-static-import-stored.ts": 2381, - "2935-module-namespace-values/main.ts": 3289, - "2936-static-loose-equality.ts": 3642, - "2937-bitwise-hot-loop.ts": 3298, - "2938-fs-sync-overloads.cjs": 2598, - "2939-child-shell-text.ts": 2919, - "2940-date-parse.ts": 3171, - "2940-fsp-realpath.ts": 3497, - "2941-fsp-lstat-identity.ts": 3608, - "2941-process-exit-code.ts": 3007, - "2942-fsp-readdir-dirent.ts": 3744, - "2942-process-exit-code-override.ts": 2479, - "2943-process-exit-code-invalid.ts": 3225, - "2944-process-exit-code-wrap.ts": 3249, - "2945-url-origin-username.ts": 2371, - "2946-child-fork-ipc/main.ts": 4191, - "2946-url-port-hash.ts": 2720, - "2947-set-readonly-seeds.ts": 3363, - "2948-child-fork-eof-callback/main.ts": 3691, - "2948-stdio-callback-record.ts": 3021, - "2949-child-fork-default/main.mjs": 3752, - "2949-url-password.ts": 3268, - "2950-fs-descriptor-publication.ts": 3039, - "2951-math-static-analytic.ts": 2897, - "2952-math-static-remaining.ts": 3708, - "2953-http-header-validation.ts": 2926, - "2954-http-outgoing-headers.ts": 3568, - "2955-http-static-state.ts": 3392, - "2956-http-static-informational.ts": 3156, - "2957-http-static-response-controls.ts": 3011, - "2961-emitter-computed-names.cjs": 3061, - "2962-emitter-computed-optional.ts": 3117, - "2963-child-fork-dispatch/main.ts": 3069, - "2964-indexed-compound.ts": 4283, - "2966-numeric-array-read-fusion.ts": 2881, - "2967-numeric-array-read-lifetime.ts": 3327, - "2968-numeric-array-borrow.ts": 3624, - "2969-scalar-record-results.ts": 3705, - "2970-constant-numeric-tables.ts": 3396, - "2971-constant-table-modules/main.ts": 2786, - "2972-http-static-distinct-snapshots.ts": 3464, - "2972-math-receiver-borrow.ts": 4257, - "2973-http-static-agent.ts": 3844, - "2973-integer-bitwise-arithmetic.ts": 3703, - "2974-array-search-from-index.ts": 120829, - "2974-http-static-module-values.ts": 5733, - "2974-numeric-remainder.ts": 3749, - "2975-bigint-default-radix.ts": 2671, - "2975-http-static-client-controls.ts": 3229, - "2976-http-static-incoming-state.ts": 3560, - "2976-json-stringify-no-arguments.js": 3221, - "2977-http-static-incoming-timeout.ts": 3554, - "2977-nested-array-binding-defaults.js": 3145, - "2978-http-static-client-timeout.ts": 3607, - "2978-regexp-optional-subject.js": 3504, - "2979-array-search-mixed-needles.js": 2692, - "2979-http-static-server-timeout.ts": 3769, - "2980-http-static-server-timeout-default.ts": 3050, - "2980-string-position-arguments.js": 3269, - "2981-http-static-events.ts": 2644, - "2981-string-optional-positions.ts": 2494, - "2982-http-static-close-all.ts": 2737, - "2982-string-position-coercion-order.js": 3410, - "2983-http-static-close-idle.ts": 3031, - "2984-http-static-close-upgrade.ts": 3101, - "2985-http-static-message-destroy.ts": 3632, - "2986-http-static-client-socket-finish.ts": 2911, - "2987-http-static-timeout-errors.ts": 3301, - "2988-http-static-client-abort.ts": 2940, - "2989-http-static-response-timeout-handled.ts": 3640, - "2990-http-static-request-timeout-handled.ts": 3597, - "2991-array-indexed-equality.ts": 3696, - "2992-stream-rows.ts": 3357, - "2993-fiber-stack-lifetimes.ts": 3545, - "2994-fromcharcode-scalar.ts": 3381, - "2995-math-hyperbolic.ts": 3225, - "2996-math-near-one.ts": 2714, - "2997-math-integer-float.ts": 3635, - "300-if-else.ts": 2510, - "3001-caught-typeof-object.js": 3444, - "3002-promise-then-two.ts": 3645, - "301-while.ts": 3200, - "302-for.ts": 2865, - "3020-string-last-index-position.ts": 3627, - "3021-buffer-union-narrow-callback.ts": 3829, - "3022-string-affix-positions.ts": 3463, - "3023-string-affix-position-coercion.js": 3644, - "3024-string-number-narrow-for-in.ts": 3394, - "3025-string-search-position-coercion.js": 3367, - "3026-array-last-index-of.js": 2868, - "3027-array-last-index-of-records.ts": 3472, - "3028-string-search-dynamic-needle.js": 2934, - "3029-string-search-union-needle.js": 3701, - "303-break-continue.ts": 2701, - "3030-array-search-no-arg.js": 3806, - "3031-array-relative-index-coercion.js": 3498, - "3031-string-last-index-search-value.js": 2610, - "3032-string-search-value.js": 3067, - "3033-string-search-needle-coercion.js": 3292, - "3034-arguments-with-parameters.js": 2687, - "3035-string-positioned-search-value.js": 3575, - "3036-string-object-search-value.js": 2688, - "3037-number-nullish-conversion.js": 3018, - "3038-number-scalar-unions.ts": 2381, - "3039-string-repeat-count.js": 2336, - "304-compound-incdec.ts": 3307, - "3040-string-prototype-call.js": 3584, - "3041-string-prototype-object-call.js": 3163, - "3042-string-padding-coercion.js": 3486, - "3043-string-split-arguments.js": 3038, - "3044-array-slice-splice-positions.js": 3582, - "3045-array-indexed-mutation.js": 4003, - "3046-array-indexed-ref-identity.ts": 2594, - "3047-object-prototype-tag.js": 5012, - "3048-object-own-prototype.ts": 4080, - "3049-array-flat-depth.ts": 3414, - "305-truthiness.ts": 2664, - "3050-array-splice-insert.ts": 2966, - "3051-array-join-default.ts": 3468, - "3052-array-construction.ts": 4201, - "3053-array-construction-js.js": 2937, - "3054-dynamic-abort-record.ts": 25141, - "3055-default-builtin-nodenext/main.ts": 2702, - "3056-default-builtin-bundler/main.ts": 3487, - "3057-default-builtin-nodenext-dynamic/main.ts": 2447, - "3058-optional-array-chain-join.ts": 3991, - "3059-arguments-property-names.ts": 2550, - "3061-concurrent-fiber-stacks.ts": 3788, - "3062-runtime-optional-record-guards.ts": 3181, - "3063-runtime-optional-class-guards.ts": 3568, - "3064-installed-entry-imports/main.ts": 2606, - "3065-installed-entry-js/main.js": 3045, - "3066-source-debugging/main.ts": 3383, - "3067-cjs-function-export-require/main.cjs": 2939, - "3068-debug-variables/main.ts": 2618, - "3068-url-checked-identity.ts": 3006, - "3069-set-checked-seed.js": 3140, - "3069-tuple-spread.ts": 3376, - "3070-checked-object-accessors.js": 3573, - "3070-collection-views.ts": 3175, - "3071-checked-specialization-narrowing.js": 3321, - "3071-map-contextual-record-seeds.ts": 4329, - "3072-optional-union-receivers.ts": 3610, - "3072-weak-process-streams.js": 2918, - "3073-optional-callable-module.js": 3300, - "3073-union-keyed-reads.ts": 3869, - "3074-discriminated-intersections.ts": 3384, - "3075-recursive-contextual-literals.ts": 4502, - "3076-nullish-record-defaults.ts": 3009, - "3077-optional-union-array-reads.ts": 3992, - "3078-forward-mutable-captures.ts": 3466, - "3079-forward-mutable-tdz.ts": 3489, - "3080-exhaustive-never-bindings.ts": 3664, - "3081-recursive-array-destinations.ts": 3893, - "3082-forward-write-unwind.ts": 3471, - "3083-set-string-seed.ts": 3139, - "3084-record-own-membership.ts": 2971, - "3085-conditional-record-construction.ts": 3822, - "3086-error-constructor-options.ts": 3660, - "3087-error-options-lifetime.ts": 2905, - "3088-array-wide-callbacks.ts": 3195, - "3089-array-find-narrowing.ts": 4220, - "3090-json-discriminated-records.ts": 3463, - "3091-json-recursive-discriminants.ts": 3555, - "3092-number-union-predicates.ts": 2884, - "3093-error-option-data.ts": 2574, - "3094-json-replacer-traversal.ts": 3823, - "3095-json-reviver-traversal.ts": 3825, - "3096-json-callback-nonfinite.ts": 3620, - "3097-json-callback-reentrancy.ts": 2760, - "3098-json-callback-space.ts": 3696, - "3099-json-callback-functions.ts": 4108, - "3100-json-callback-holder.js": 3675, - "3101-json-callback-bytes.ts": 3250, - "3102-json-unknown-fields.ts": 2925, - "3103-scalar-record-nested-control-flow.ts": 3592, - "3104-native-analysis-tables-loops.ts": 3494, - "3105-nullable-collections.ts": 3244, - "3106-mixed-insertion-spreads.ts": 4146, - "3107-nullable-collection-lifetimes.ts": 3486, - "3108-for-await-sync-arrays.js": 4312, - "3108-identity-map-keys.ts": 3894, - "3109-destructuring-unknown-defaults.js": 3234, - "3109-identity-union-collections.ts": 3977, - "3110-builtin-error-families.ts": 3250, - "3110-identity-collection-mutation.ts": 3825, - "3111-identity-collection-cycles.ts": 3640, - "3112-union-switch-control-flow.ts": 2681, - "3113-union-array-removal.ts": 3778, - "3114-union-record-field-write.ts": 3884, - "3115-zlib-static-compression-levels.ts": 4085, - "3116-sequence-expression-lifetimes.ts": 3245, - "3117-unknown-switch-control-flow.ts": 3706, - "3118-recursive-union-spread.ts": 3476, - "3119-runtime-optional-spread.ts": 4508, - "3120-dynamic-property-descriptors.js": 3228, - "3120-generator-method-loop-bindings.ts": 3222, - "3120-ir-nonfinite-numbers.ts": 3251, - "3121-compound-assignment-values.js": 3525, - "3121-dynamic-accessor-descriptors.js": 4470, - "3122-dynamic-delete-descriptors.cjs": 3353, - "3122-dynamic-delete-descriptors.js": 3300, - "3122-js-async-class-methods.js": 3524, - "3123-destructured-global-tdz.js": 3896, - "3124-array-prototype-borrow.js": 4000, - "3125-regex-global-match.ts": 2889, - "3126-js-number-radix/main.ts": 3491, - "3127-spawn-runtime-options.ts": 3338, - "3128-regex-last-index.ts": 3768, - "3129-js-token-union/main.ts": 3461, - "3130-child-generic-streams.ts": 3435, - "3131-child-rest-listeners.js": 3280, - "3132-child-generic-stream-completion.ts": 6821, - "3133-text-decoder-streaming.ts": 32031, - "3134-fs-vectors.ts": 3621, - "3135-fs-file-handle-controls.ts": 3152, - "3136-fs-file-handle-options.ts": 4992, - "3137-fs-vector-callbacks.cjs": 3636, - "3138-array-large-storage.ts": 5104, - "3139-regex-native-deep-alternatives.ts": 38096, - "3140-array-dense-access.ts": 4712, - "3141-map-string-key-hash.ts": 3358, - "3150-integer-remainder.ts": 3568, - "3151-array-push-writes.ts": 3892, - "3170-number-conversion-fast-paths.ts": 2663, - "3171-method-census-data-writes.ts": 4253, - "400-fib.ts": 3919, - "401-mutual-recursion.ts": 2844, - "402-string-functions.ts": 3081, - "403-void-and-params.ts": 3112, - "4030-runtime-object-truthiness.ts": 3275, - "4031-event-emitter-long-tuples.ts": 3646, - "4032-inline-rc-fast-paths.ts": 4866, - "404-rc-stress.ts": 2781, - "4040-adapted-function-identity.ts": 3610, - "4041-adapted-function-properties.ts": 3413, - "4042-object-is-union-subclass.ts": 2746, - "4043-nonnull-missing-element.ts": 3815, - "4044-adapted-callback-modules/main.ts": 2918, - "4045-optional-element-projections.ts": 4182, - "4046-captured-parameter-values.ts": 4191, - "4047-captured-parameter-rebinding.ts": 2867, - "405-params-optional.ts": 2408, - "406-params-defaults.ts": 2614, - "4060-cycle-shared-instance/main.ts": 2758, - "4060-stack-union-reads.ts": 4386, - "4061-cycle-evaluation-order/main.ts": 3229, - "4061-nullable-ref-fields.ts": 6151, - "4062-cycle-tdz-read/main.ts": 3056, - "4063-cycle-hoisted-calls/main.ts": 3238, - "4064-cycle-classes-after-init/main.ts": 2548, - "4065-cycle-init-throws/main.ts": 2955, - "4066-cycle-top-level-tdz/main.ts": 3040, - "4067-dynamic-import-cycle-tdz/main.ts": 3004, - "407-params-rest.ts": 3335, - "408-params-ctors-inheritance.ts": 3021, - "4080-optional-read-presence-tests.ts": 3095, - "4080-string-index-bounds.ts": 3083, - "4081-hoisted-function-tdz.ts": 3193, - "4081-string-index-unproven.ts": 3273, - "4082-hoisted-function-tdz-uncaught.ts": 2845, - "4083-dynamic-import-failure/main.ts": 3748, - "4084-record-optional-field-copies.ts": 4118, - "4085-generator-loop-close.ts": 3638, - "4086-optional-capture-defaults.ts": 3227, - "4088-class-static-tdz.ts": 3234, - "4089-class-wrapper-tdz.js": 3393, - "409-params-async-generics.ts": 2742, - "4090-computed-key-tdz.js": 2796, - "4091-class-evaluation-deferred.ts": 3464, - "410-params-rc-stress.ts": 3461, - "4140-array-callback-split-visit.ts": 7289, - "4141-array-callback-ambient-receiver/main.ts": 3100, - "4142-array-callback-factories.ts": 4541, - "4143-union-refinement-intersection-arms.ts": 3822, - "420-dead-strip-modules/main.ts": 3118, - "4290-acyclic-immutable-trees.ts": 3580, - "4291-acyclic-constructor-escapes.ts": 3039, - "4292-acyclic-readonly-bypass.ts": 4316, - "4293-acyclic-dynamic-writes.ts": 3268, - "4294-console-line-sizes.ts": 6986, - "4295-console-template-parts.ts": 3358, - "4296-console-glued-long-parts.ts": 3290, - "4410-inline-object-allocation.ts": 3305, - "4411-nullable-ref-values.ts": 5301, - "4412-nullable-record-values.ts": 5088, - "4413-cycle-old-late-death.ts": 4012, - "4413-nullable-null-and-undefined.ts": 4328, - "4414-cycle-old-weak-keys.ts": 4005, - "4414-nullable-string-values.ts": 5044, - "4415-identity-borrowed-operands.ts": 3101, - "4415-nullable-container-values.ts": 4932, - "4431-exception-pending-contexts.ts": 3487, - "4432-checked-downcast-failures.ts": 3971, - "4451-int32-field-storage.ts": 3668, - "4452-int32-field-dynamic-writes.ts": 4531, - "4453-int32-closed-locals.ts": 2786, - "4471-unassigned-field-reads.ts": 3896, - "4476-string-ascii-header-fact.ts": 3522, - "4477-buffer-write-ascii-runs.ts": 3321, - "4478-string-large-index-reads.ts": 4146, - "4481-array-optional-push.ts": 3259, - "4482-instanceof-static-class-optional.ts": 3786, - "4483-array-splice-statement.ts": 4036, - "4484-array-length-reset.ts": 2849, - "4485-array-splice-optional-item.ts": 3817, - "4486-array-field-element-borrows.ts": 2903, - "4487-array-literal-optional-values.ts": 3145, - "4520-optional-guard-narrowing.ts": 2774, - "4521-array-callback-element-presence.ts": 5601, - "4522-captured-parameter-lifetimes.ts": 3907, - "4523-array-pop-removal.ts": 3769, - "4524-array-callback-supertype-params.ts": 4594, - "4541-nullable-wrap-borrows.ts": 3939, - "4542-preserving-call-borrows.ts": 3361, - "4543-walk-borrowed-locals.ts": 3633, - "4544-checked-cast-aliases.ts": 3723, - "4545-borrowed-returns.ts": 4116, - "4546-rebound-borrowed-params.ts": 2930, - "4560-field-store-borrowed-receivers.ts": 4438, - "4561-virtual-borrowed-parameters.ts": 3634, - "4562-string-self-concat-move.ts": 2703, - "4563-array-read-projection-receivers.ts": 3601, - "4564-literal-choice-strings.ts": 3137, - "4568-for-of-hole-elements.ts": 3387, - "4569-shorthand-missing-elements.ts": 3958, - "4570-map-lookup-default-record.ts": 3112, - "4591-weak-keys-union-records.ts": 3556, - "4592-cycle-const-from-import/main.ts": 3467, - "4593-weak-keys-empty-objects.ts": 2938, - "4594-union-class-field-mixed-types.ts": 4040, - "4595-union-class-field-optional-receiver.ts": 3379, - "4596-error-subclass-int-fields.ts": 3559, - "4596-tdz-nullable-forward-capture.ts": 4676, - "4597-callback-field-method-name.ts": 4014, - "4598-live-array-ref-element-access.ts": 8384, - "4598-map-set-clear-reuse.ts": 4224, - "4598-os-available-parallelism.ts": 3815, - "4598-publish-constructor-stores.ts": 3618, - "4599-construction-only-field-borrows.ts": 3619, - "4600-construction-only-fields-dynamic-writes.ts": 3184, - "4601-cycle-holders.ts": 3348, - "4602-constructor-escape-order.ts": 3674, - "4610-array-ext-dyn-refresh.ts": 4598, - "4620-optional-chain-borrowed-receivers.ts": 3411, - "4621-edge-preserving-callees/main.ts": 3288, - "4630-contract-int32.ts": 2810, - "4630-reference-write-call-arguments.ts": 3333, - "4631-contract-nothrow.ts": 3196, - "4631-reference-write-aliases.ts": 4264, - "4632-contract-immutable.ts": 3449, - "4632-reference-write-loops-and-walks.ts": 3791, - "4633-contract-program-lifetime.ts": 4644, - "4633-reference-write-callbacks-returns.ts": 4024, - "4634-contract-program-lifetime-publish.ts": 42618, - "4634-optional-element-borrowed-returns.ts": 3411, - "4640-bitwise-identity-ranges.ts": 3713, - "4641-nothrow-try-catch.ts": 3056, - "4642-acyclic-contract.ts": 4040, - "4650-stale-property-narrowing.ts": 3665, - "4651-map-string-get-entry.ts": 3369, - "4652-stale-closure-binding-narrowing.ts": 3504, - "4660-typed-rest-spread-forwarding.ts": 3148, - "4671-scoped-array-read-borrows.ts": 3020, - "4690-cycle-bypass-long-lived.ts": 5374, - "4691-publish-constructor-self-alias.ts": 38620, - "4692-borrowed-result-mutating-callees.ts": 3592, - "4720-cold-throw-paths.ts": 4339, - "4721-integer-induction-numbers.ts": 3854, - "4730-touint32-large-magnitudes.ts": 3431, - "4731-string-mixed-ascii-slices.ts": 4436, - "4732-buffer-write-utf16le-runs.ts": 4084, - "4733-typed-array-read-compares.ts": 3240, - "4750-map-string-absent-filter.ts": 3584, - "4751-string-search-multibyte-needles.ts": 3227, - "4795-published-untyped-member-reads.ts": 3450, - "4806-uint32-loop-accumulators.ts": 3543, - "4810-union-identity-retags.ts": 34911, - "4811-union-retag-checked-cast.ts": 3044, - "4812-int32-wrapping-sums.ts": 3457, - "4813-typed-array-read-equality-bindings.ts": 4853, - "4820-sort-comparator-lent-arguments.ts": 3579, - "4821-callback-lent-arguments.ts": 4210, - "4822-stable-capture-reads.ts": 3094, - "4823-array-store-receivers.ts": 3774, - "4830-typed-teardown-chains.ts": 3908, - "4831-cycle-bypass-probe-windows.ts": 6157, - "4840-array-hole-free-prefix.ts": 3932, - "4860-int32-local-storage.ts": 5136, - "4870-array-pipeline-fusion.ts": 6645, - "4871-array-pipeline-analytics.ts": 4575, - "4872-array-pipeline-temporaries.ts": 10635, - "4875-stream-consumers-array-buffer.ts": 4120, - "4876-array-buffer-inspect.ts": 2819, - "4880-await-call-turn-order.ts": 3872, - "4881-await-call-exceptions.ts": 3027, - "4882-await-call-request-chain.ts": 2730, - "4883-await-values-and-hops.ts": 3412, - "4884-await-call-unhandled-sibling.ts": 3410, - "4885-await-call-checkpoints.ts": 2703, - "4890-regex-literal-sites.ts": 3712, - "4891-regex-plain-text-patterns.ts": 3018, - "4892-regex-exec-read-captures.ts": 4130, - "4900-bigint-division-cross-check.ts": 3763, - "4900-console-instances.ts": 4286, - "4901-bigint-radix-text.ts": 3099, - "4901-console-instance-errors.ts": 3171, - "4902-bigint-compound-assign.ts": 3431, - "4903-bigint-fixed-width.ts": 3707, - "4904-bigint-large-products.ts": 3972, - "4905-bigint-fixed-width-fusion.ts": 3443, - "4906-bigint-small-value-trees.ts": 4875, - "4907-bigint-random-trees.ts": 5277, - "4910-hole-free-prefix-upkeep.ts": 4073, - "4920-string-literal-switch.ts": 3607, - "4930-typed-record-reflection.ts": 4869, - "4931-typed-data-generic-walks.ts": 5228, - "4932-typed-json-unknown.ts": 3250, - "4940-async-return-adoption.ts": 4405, - "4941-async-return-adoption-try.ts": 3533, - "4942-async-return-adoption-values.ts": 4824, - "4943-async-return-adoption-interleaved.ts": 3851, - "4950-regex-compiled-captures.ts": 5146, - "4951-regex-compiled-last-index.ts": 4316, - "4952-regex-compiled-anchors-classes.ts": 3149, - "4953-regex-compiled-backtracking.ts": 3822, - "4954-regex-log-report.ts": 4449, - "4955-regex-compiled-differential.ts": 20102, - "4970-int32-call-abi.ts": 3029, - "4980-generator-early-exit-finally.ts": 3262, - "4981-generator-delegation-iterables.ts": 5306, - "4982-generator-sent-values.ts": 3649, - "4983-generator-reuse-iterables.ts": 4023, - "4984-generator-exceptions.ts": 4220, - "4985-generator-lazy-pipeline.ts": 3164, - "4986-generator-default-channels.ts": 3434, - "4987-generator-closures-methods.ts": 3505, - "4988-generator-async-interplay.ts": 2790, - "4989-generator-step-drives.ts": 3509, - "4990-lone-surrogates.ts": 3213, - "4991-lone-surrogate-halves.ts": 4020, - "4992-lone-surrogate-search.ts": 3375, - "4993-lone-surrogate-json.ts": 3473, - "4994-lone-surrogate-uri.ts": 3848, - "4995-lone-surrogate-regex.ts": 4302, - "4996-lone-surrogate-stdio.ts": 2592, - "4997-lone-surrogate-stdout-patch.js": 3072, - "4998-lone-surrogate-buffers.ts": 3134, - "4999-lone-surrogate-io.ts": 3225, - "500-array-basics.ts": 2886, - "501-array-push-pop.ts": 2682, - "5010-lifetime-region-generations.ts": 4147, - "5011-lifetime-region-outside-references.ts": 3407, - "5012-lifetime-region-shapes.ts": 4861, - "5013-lifetime-region-async.ts": 3124, - "5014-lifetime-region-nesting.ts": 3259, - "5015-lifetime-region-publish.ts": 27669, - "5016-lifetime-region-weak-keys.ts": 2554, - "5017-lifetime-region-cycles.ts": 3105, - "5018-lifetime-region-hierarchies.ts": 4007, - "5019-lifetime-region-values.ts": 2888, - "502-array-for-of.ts": 3621, - "5020-lifetime-region-large.ts": 4812, - "5021-lifetime-region-opaque-holders.ts": 3957, - "5022-lifetime-region-workers.ts": 38841, - "503-array-functions.ts": 2982, - "504-array-rc-stress.ts": 3289, - "5040-promise-all-race-turns.ts": 4439, - "5041-promise-all-turns.js": 3708, - "5042-async-generator-return-promise.ts": 3292, - "5043-async-generator-return-forms.ts": 3922, - "5045-generator-delegation-typed-next.ts": 4740, - "5046-generator-delegation-return-throw.ts": 3192, - "5047-generator-delegation-iterables-typed.ts": 4558, - "5048-generator-delegation-async.ts": 3457, - "5049-generator-delegation-edges.ts": 3057, - "5050-inherited-in-membership.ts": 4297, - "5051-typed-record-prototypes.ts": 3534, - "5052-class-for-in-unknown.ts": 3663, - "5053-json-replacer-arrays.ts": 4093, - "5054-required-field-delete.ts": 3619, - "5055-object-create-record-literal.ts": 3237, - "5056-generator-in-unknown.ts": 3060, - "5060-child-fork-exit-after-messages/main.ts": 3584, - "5061-child-fork-local-disconnect/main.ts": 4277, - "5062-child-fork-message-before-eof/main.ts": 3570, - "5065-global-increment-initializers.ts": 2946, - "5066-global-compound-updates.ts": 3425, - "5067-global-updates-in-functions.ts": 3465, - "5068-global-var-let-const-updates.ts": 3395, - "5069-global-update-modules/main.ts": 3167, - "5070-optional-read-bindings.ts": 3316, - "5071-optional-chain-scalars.ts": 3214, - "5072-optional-nullish-ternary.ts": 2900, - "5073-optional-results.ts": 3751, - "5074-optional-pop-shift.ts": 3099, - "5075-optional-reads-unchecked/main.ts": 3754, - "5076-optional-escapes.ts": 3593, - "5080-boolean-array-storage.ts": 7564, - "5081-boolean-array-dynamic-views.ts": 3915, - "5085-promise-allsettled-turns.ts": 4557, - "5086-promise-any-turns.ts": 3994, - "5087-promise-allsettled-any-dyn.js": 5205, - "5088-promise-combinator-thenables.js": 5007, - "5089-promise-settled-records.ts": 4859, - "5090-generator-finished-result-value.ts": 3648, - "5091-generator-delegation-arrays-strings.ts": 3362, - "5092-generator-delegation-sets-maps.ts": 4496, - "5095-console-count-group.ts": 3356, - "5096-console-assert-dir.ts": 3645, - "5097-console-table.ts": 3255, - "5098-console-time-warnings.ts": 7324, - "5099-console-methods-dynamic.cjs": 3314, - "510-array-map-filter-foreach.ts": 3232, - "511-array-indexof-includes.ts": 3286, - "5110-var-hoisting-early-reads.ts": 4048, - "5111-var-hoisting-closures-classes.ts": 2474, - "5112-var-hoisting-function-scope.ts": 3383, - "5113-tdz-let-const-class.ts": 3628, - "5114-var-hoisting-js.js": 3550, - "5115-regex-replace-compiled.ts": 3344, - "5116-regex-capture-reads.ts": 3874, - "5117-regex-log-report-spans.ts": 3719, - "5118-regex-replace-char-chains.ts": 3632, - "512-array-join-chains.ts": 3268, - "5120-frame-records.ts": 3221, - "5121-frame-objects.ts": 3861, - "5122-frame-result-slots.ts": 3008, - "5123-frame-reassigned.ts": 3589, - "5124-frame-objects-control.ts": 3184, - "5125-frame-mutating-methods.ts": 3607, - "5126-frame-returned-params.ts": 3362, - "5127-frame-loop-bindings.ts": 4392, - "513-array-methods-rc-stress.ts": 3208, - "5130-event-target-dispatch.js": 4958, - "5131-event-target-listener-errors.js": 4558, - "5132-event-target-typed.ts": 5171, - "5133-abort-signal-event-target.ts": 22917, - "5134-events-error-monitor.ts": 3499, - "5135-event-target-warnings.js": 9058, - "5136-events-error-monitor-string-name.ts": 3057, - "5137-events-error-monitor-computed-name.js": 4052, - "5138-events-error-monitor-shared-listener.ts": 2466, - "5139-event-target-listener-throw.ts": 2925, - "514-array-find-some-every.ts": 3925, - "5140-os-host-facts.ts": 3438, - "5141-os-constants.ts": 2815, - "5142-path-parse-format.ts": 3808, - "5145-path-matches-glob-basics.ts": 3287, - "5146-path-matches-glob-braces.ts": 4987, - "5147-path-matches-glob-extglob.ts": 3938, - "5148-path-matches-glob-classes.ts": 3973, - "5149-path-matches-glob-win32.ts": 3835, - "515-array-flatmap.ts": 3688, - "5150-small-array-inline-slots.ts": 4419, - "5155-cycle-header-types.ts": 38708, - "516-array-reduce.ts": 4017, - "5160-field-callback-receiver.ts": 3678, - "5161-field-callback-receiver-escape.ts": 2857, - "5162-field-method-reference-receiver.ts": 2944, - "5163-field-callback-receiver.cjs": 3655, - "5164-worker-field-callback-receiver.ts": 30046, - "5165-interface-method-dispatch.ts": 2969, - "5166-interface-method-values.ts": 4129, - "5167-interface-method-order.ts": 3988, - "5168-interface-method-hierarchy.ts": 4483, - "5169-interface-method-chains.ts": 3336, - "517-array-hof-index-args.ts": 3024, - "5170-string-scan-eviction.ts": 3285, - "5171-lone-surrogate-replace-seams.ts": 3558, - "5175-forin-typed-as-unknown.ts": 4230, - "5176-object-keys-typed-as-unknown.ts": 3721, - "5177-forin-dynamic-objects.ts": 2893, - "5178-forin-keys-js.js": 2555, - "5179-forin-typed-shapes.ts": 4446, - "518-array-sort.ts": 3134, - "518-promise-void-union-callbacks.ts": 2401, - "5180-interface-arrays.ts": 3230, - "5181-interface-array-methods.ts": 11973, - "5182-interface-array-boundaries.ts": 3847, - "5183-interface-property-reads.ts": 3799, - "5184-interface-property-objects.ts": 3828, - "5185-util-inherits.cjs": 5041, - "5186-util-inherits.ts": 4542, - "5187-util-deprecate.cjs": 5818, - "5188-util-deprecate-flags.cjs": 4078, - "5189-util-deprecate.ts": 3798, - "519-array-from-length.ts": 2988, - "519-promise-union-await-values.ts": 3664, - "5190-util-callbackify.cjs": 5115, - "5191-util-callbackify.ts": 4367, - "5192-util-debuglog.cjs": 5096, - "5193-util-debuglog.ts": 4056, - "5194-process-emit-warning-tick.cjs": 2761, - "5195-object-entries-typed-as-unknown.ts": 3177, - "5196-object-values-typed-as-unknown.ts": 4077, - "5197-object-entries-dynamic-objects.ts": 4742, - "5198-object-entries-values-js.js": 3089, - "5199-object-entries-identity.ts": 3679, - "520-map-basics.ts": 3767, - "5200-array-sort-keyed-numbers.ts": 4618, - "5201-array-sort-keyed-records.ts": 5422, - "5202-array-sort-keyed-lookalikes.ts": 4034, - "5203-typed-array-sort-default.ts": 7941, - "5204-typed-array-sort-comparators.ts": 4085, - "521-map-number-keys.ts": 3317, - "5210-process-memory-usage.ts": 3262, - "5211-process-identity.ts": 3448, - "5212-perf-marks-measures.ts": 4026, - "5213-perf-timing-errors.ts": 4151, - "5214-perf-entry-inspect.ts": 4315, - "5215-perf-timing-validation.js": 2982, - "5216-perf-spread-arguments.js": 3745, - "5217-perf-spread-typed.ts": 4057, - "522-map-foreach.ts": 2964, - "5220-url-parse-warnings.ts": 4859, - "5221-url-parse-shapes.ts": 3814, - "5222-url-parse-hosts.ts": 3398, - "5223-url-parse-query.ts": 2653, - "5224-url-format-objects.ts": 5550, - "5225-url-format-whatwg.ts": 5213, - "5226-url-resolve.ts": 2600, - "5227-url-legacy-dependency/main.ts": 5803, - "5228-url-domain-conversion.ts": 4876, - "5229-url-legacy-errors.cjs": 5195, - "523-map-ref-values.ts": 4321, - "5230-inline-union-fields.ts": 3644, - "5231-inline-union-field-kinds.ts": 4871, - "5232-inline-union-fields-dynamic.ts": 3887, - "5233-inline-union-fields-shapes.ts": 4145, - "5234-inline-union-class-fields.ts": 3244, - "5235-inline-union-fields-publish.ts": 3360, - "5236-inline-union-unchecked-reads.ts": 3108, - "5237-inline-union-frame-objects.ts": 2533, - "524-map-cycles.ts": 3943, - "5240-event-target-subclass.ts": 5571, - "5241-event-subclass.ts": 4311, - "5242-event-target-subclass-hierarchy.ts": 4079, - "5243-event-target-subclass-dynamic.ts": 3528, - "5244-event-target-subclass.js": 4645, - "5245-event-target-subclass-signals.ts": 9410, - "5246-event-target-subclass-listener-throw.ts": 3995, - "5247-event-target-subclass-lifetimes.ts": 5991, - "5248-event-target-subclass-warnings.ts": 4513, - "5249-event-target-subclass-unconstructed.ts": 2679, - "525-map-rc-stress.ts": 2764, - "526-set-basics.ts": 2480, - "5260-record-unknown-identity.ts": 3505, - "5261-optional-field-identity.ts": 3914, - "5262-record-unknown-many.ts": 3839, - "527-set-foreach.ts": 3683, - "5270-url-parse-no-deprecation.cjs": 4076, - "5271-url-parse-throw-deprecation.cjs": 3487, - "5275-http-end-callback-order.ts": 3028, - "5276-style-text-hex-colors.cjs": 4502, - "5277-fs-read-file-buffer-option.cjs": 2635, - "5278-host-null-bytes.cjs": 16385, - "5279-search-params-null-init.cjs": 2915, - "528-set-seeded.ts": 3643, - "5285-blob-construct.ts": 3124, - "5286-blob-readers.ts": 3309, - "5287-blob-inspect.ts": 2504, - "5288-blob-validation.js": 3775, - "5289-blob-read-order.ts": 3614, - "529-map-seeded.ts": 3331, - "5290-blob-dynamic.js": 2674, - "5295-readline-promises-question.ts": 2401, - "5296-readline-lines.ts": 3088, - "5297-readline-callback-events.cjs": 2975, - "5298-readline-abort.cjs": 6980, - "5299-readline-stdin.ts": 3836, - "530-record-arrays.ts": 3125, - "5300-stream-add-abort-signal.ts": 7154, - "5301-stream-add-abort-signal-errors.cjs": 7763, - "5302-stream-promises-options.ts": 8927, - "5303-stream-finished-callback-opts.cjs": 7119, - "5305-json-stringify-typed-arrays.ts": 4254, - "531-record-array-hofs.ts": 3609, - "5315-map-iterate-after-front-deletes.ts": 2759, - "5316-map-lru-eviction.ts": 3688, - "5317-map-iterate-while-mutating.ts": 3035, - "5318-set-iterate-after-deletes.ts": 2543, - "5319-map-iteration-values.ts": 4062, - "532-record-arrays-rc-stress.ts": 2708, - "5320-map-iterate-across-compaction.ts": 2736, - "5325-record-boundary-returns.ts": 3046, - "5326-record-boundary-params.ts": 2989, - "5327-record-boundary-identity.ts": 3529, - "5328-record-boundary-open.ts": 3718, - "5329-record-boundary-recursion.ts": 3361, - "533-array-element-cycles.ts": 3118, - "5330-record-boundary-fields.ts": 3048, - "5331-record-boundary-methods.ts": 2560, - "5333-record-boundary-nullable-argument.ts": 3100, - "5334-fetch-mentions-without-calls.ts": 4032, - "5335-top-level-await-pending-exit-code.ts": 2383, - "5336-top-level-await-pending-exit-listener.ts": 2814, - "5337-dc-subscriber-throw-next-tick.cjs": 3273, - "5338-js-tdz-function-element.js": 3733, - "5339-array-buffer-constructor.js": 2884, - "534-record-width-subtyping.ts": 3530, - "5345-object-values-for-of-unknown.ts": 4013, - "5346-object-values-length-dynamic.ts": 3930, - "5347-object-values-loop-control.ts": 3598, - "5348-object-values-for-of-js.js": 2760, - "5349-object-entries-values-optional.ts": 3370, - "535-object-statics.ts": 2862, - "5350-object-values-wide-record.ts": 3272, - "5351-object-values-methods-index.ts": 3637, - "536-map-seed-array.ts": 3237, - "5360-array-sort-comparator-calls.ts": 4265, - "5361-array-sort-inconsistent-comparators.ts": 4181, - "5362-array-sort-comparator-throws.ts": 2826, - "5363-array-sort-comparator-writes.ts": 10362, - "5364-array-sort-union-and-boolean-elements.ts": 14300, - "5365-array-sort-dynamic-values.js": 2919, - "537-map-iter-drains.ts": 3050, - "538-map-set-for-of.ts": 3029, - "5390-interface-loop-element-lifetimes.ts": 3566, - "5391-interface-dispatch-hierarchies.ts": 3731, - "5392-interface-receiver-bindings.ts": 3061, - "5393-interface-array-holes.ts": 2702, - "5394-interface-implementers.ts": 3603, - "5395-locale-compare-ascii.ts": 3587, - "5396-locale-compare-latin.ts": 4291, - "5397-locale-compare-sorting.ts": 3576, - "540-tuples-basics.ts": 3420, - "5400-json-parse-typed-keys.ts": 3141, - "5405-buffer-arraybuffer-window.ts": 3036, - "5406-util-types-keys.ts": 2954, - "5407-http-header-field-limit.ts": 3052, - "5408-http-unread-request-end.ts": 3992, - "5409-http-held-request-body.ts": 3771, - "541-ref-array-json.ts": 3761, - "5410-inspect-class-values.ts": 3124, - "5411-inspect-class-hierarchies.ts": 2423, - "5412-inspect-dynamic-collections.ts": 3042, - "5413-inspect-class-values.cjs": 3217, - "542-union-element-arrays.ts": 2812, - "5430-dyn-number-string-boxes.ts": 3930, - "5435-crypto-hash-algorithms.ts": 3451, - "5436-crypto-hash-names.js": 3185, - "5437-crypto-hmac-algorithms.ts": 3269, - "5438-crypto-kdf-digests.ts": 3099, - "5439-crypto-kdf-callbacks.js": 4398, - "5440-crypto-hash-encodings.js": 4908, - "5441-crypto-hash-output-length.ts": 3959, - "5442-crypto-random-values.ts": 4791, - "5443-crypto-random-arguments.js": 3724, - "5444-crypto-timing-safe-equal.js": 4074, - "5445-crypto-secret-key-objects.ts": 4784, - "5445-stream-buffer-chunks.ts": 5661, - "5446-crypto-secret-key-errors.js": 3275, - "5446-stream-buffer-chunks-dynamic.js": 5780, - "5447-fs-readdir-order.ts": 3355, - "5448-fs-glob-patterns.ts": 3298, - "5449-fs-glob-options.ts": 3827, - "5450-fs-glob-validation.js": 4321, - "5451-fs-promises-glob.ts": 3074, - "5452-fs-glob-callback.ts": 3887, - "5455-power-of-two-remainder.ts": 4228, - "5456-remainder-large-integer-dividends.ts": 3551, - "5457-lcg-modulus.ts": 2909, - "5458-fractional-remainder.ts": 2650, - "5460-locale-compare-contractions.ts": 3201, - "5461-locale-compare-equivalents.ts": 3410, - "5462-locale-compare-probe-sweep.ts": 4164, - "5463-locale-compare-combining-letters.ts": 3755, - "5464-locale-compare-fractions.ts": 3312, - "5470-strip-vt-osc.cjs": 2708, - "5471-strip-vt-csi.cjs": 3038, - "5472-strip-vt-random.cjs": 2850, - "5473-strip-vt-unterminated.cjs": 4462, - "5474-strip-vt-surrogates.ts": 3554, - "5540-fs-callback-forms.cjs": 3079, - "5541-fs-stats-fields.cjs": 4039, - "5542-fs-promises-parity.cjs": 3312, - "5543-fs-promises-typed.ts": 2944, - "5544-fs-opendir.cjs": 3622, - "5545-fs-options-encoding.cjs": 3497, - "5548-fs-callback-arguments.cjs": 3905, - "5549-fs-callback-option-checks.cjs": 3471, - "5550-locale-compare-ascii-words.ts": 2824, - "5551-array-search-references.ts": 3964, - "5552-string-appends.ts": 3653, - "5553-string-append-char-code.ts": 3292, - "5565-fs-dirent-untyped.mjs": 2898, - "5566-fs-dirent-dyn-boundaries/main.mjs": 3333, - "5585-record-results-inlined.ts": 3681, - "5595-net-ref-unref.ts": 3036, - "5644-net-socket-addresses.ts": 2393, - "5645-net-socket-callbacks.ts": 2060, - "5646-net-server-connections.ts": 2755, - "5647-net-is-ip.ts": 2560, - "5648-dns-lookup-forms.ts": 3416, - "5649-dns-service-servers.ts": 4916, - "5650-dns-lookup-errors-untyped.cjs": 3889, - "5800-broadcast-channel.ts": 28374, - "5801-broadcast-channel.cjs": 27251, - "5802-broadcast-channel-workers.ts": 27251, - "5803-worker-environment-data.ts": 29298, - "5804-worker-environment-data.cjs": 26404, - "5805-worker-receive-message-parent-port.ts": 30268, - "5806-worker-resource-limits.ts": 29711, - "5807-worker-error-values.ts": 35248, - "5808-worker-unhandled-rejections.ts": 33209, - "5809-process-exit-and-rejections.cjs": 30261, - "5810-mock-method-records.ts": 6363, - "5811-mock-tracker-restore.ts": 4503, - "5812-dyn-box-async-fields.ts": 5278, - "5813-mock-timers-module.ts": 4227, - "5814-mock-timers-date.ts": 17708, - "5831-util-types-brands.ts": 4380, - "5832-util-types-brands-dynamic.js": 3658, - "5833-url-search-params-iterators.ts": 5339, - "5834-url-search-params-iterators-dynamic.mjs": 3797, - "5835-util-format-integer-specifiers.ts": 2768, - "5836-url-search-params-tuple-pairs.ts": 3193, - "5837-util-format-json-circular.ts": 3408, - "5838-qs-stringify-bigint.ts": 3519, - "5841-json-typed-dyn-members.ts": 3238, - "5842-qs-parse-null-prototype.ts": 7077, - "5843-qs-parse-null-prototype-js.mjs": 5395, - "5844-date-unknown-values.ts": 4543, - "5857-regexp-unicode-sets.ts": 3900, - "5858-disposable-stack.ts": 3750, - "5859-disposable-stack-js.js": 3600, - "5870-worker-thread-names.ts": 31697, - "5871-worker-untransferable.cjs": 27481, - "5874-worker-stdio-streams.ts": 27801, - "5875-worker-stdout-pipe.cjs": 31391, - "5879-worker-stdin.ts": 33232, - "5881-inspect-weak-collections.ts": 2915, - "5882-string-decoder-runtime-encoding.ts": 3854, - "5883-os-set-priority.ts": 3417, - "5884-inspect-layout-options.ts": 3596, - "5886-inspect-dates.ts": 2736, - "5887-inspect-typed-arrays.ts": 2997, - "5888-inspect-sorted.ts": 2861, - "5900-float16-array.ts": 3750, - "5901-float16-array-js.js": 3700, - "5904-async-generator-completion.ts": 3800, - "5905-array-from-async-generator.ts": 3500, - "5906-array-from-async-generator-js.js": 3750, - "5910-set-prototype-of-primitive-object-kinds.ts": 5033, - "5911-dyn-class-to-primitive.ts": 4732, - "5912-dyn-class-conversion-methods.ts": 4424, - "5913-prototype-type-error-messages.ts": 4068, - "5914-dyn-in-class-prototype-members.ts": 4327, - "5915-dyn-error-string-conversions.ts": 4247, - "5922-matchall-groups-callbacks.ts": 5960, - "5923-matchall-groups-js.js": 6650, - "5939-js-let-object-reassigned-wider.js": 4436, + "001-hello.ts": 48584, + "002-log-args.ts": 53231, + "100-number-format.ts": 36832, + "1000-json-stringify-basics.ts": 52367, + "1001-json-escapes-unicode.ts": 4807, + "1002-json-parse-cast.ts": 5113, + "1003-json-parse-unions.ts": 7293, + "1004-json-parse-errors.ts": 49116, + "1005-json-nested.ts": 41496, + "1006-json-fs-config.ts": 48813, + "1007-json-rc-stress.ts": 4968, + "1008-json-null-arms.ts": 49375, + "1009-json-optional-fields.ts": 52902, + "101-arithmetic.ts": 4596, + "1010-json-stringify-space.ts": 7197, + "1011-json-unknown-typeof.ts": 38530, + "102-comparisons.ts": 45324, + "1020-async-basics.ts": 5928, + "1021-async-ordering.ts": 3518, + "1022-async-exceptions.ts": 4562, + "1023-async-rc-stress.ts": 49059, + "1024-async-pending-exit.ts": 4366, + "1025-async-promise-capture.ts": 4983, + "1026-throw-promise.ts": 47886, + "1027-async-return-promise.ts": 3187, + "1028-async-return-record-literals.ts": 53064, + "1029-async-eager-chains.ts": 5109, + "103-ternary.ts": 4538, + "104-ternary-empty-arrays.ts": 50460, + "1050-comptime-tables.ts": 7149, + "1051-comptime-strings.ts": 4737, + "1052-comptime-records.ts": 37872, + "1053-comptime-json.ts": 4779, + "1054-comptime-modules/main.ts": 7660, + "110-number-radix.ts": 4935, + "111-record-entries.ts": 7207, + "1110-math-members.ts": 4805, + "1111-math-random.ts": 5313, + "1113-string-methods.ts": 39688, + "1114-string-unicode-island.ts": 61280, + "1115-parse-globals.ts": 4273, + "1116-island-static-mix.ts": 58945, + "1117-typeof-static-union.ts": 4726, + "1118-object-spread-conditional.ts": 5790, + "1119-switch-union.ts": 3967, + "112-class-descriptor-key.js": 6359, + "1121-infinity-number-tostring.ts": 6448, + "1122-any-captures.ts": 5744, + "1124-union-narrowed-retag.ts": 48623, + "113-promise-checked-exits.ts": 5906, + "114-live-record-delete.ts": 5041, + "115-array-compound.js": 45694, + "116-text-decoder-runtime.js": 42554, + "117-weak-cache-cycle.js": 4096, + "118-fs-stored-callbacks.cjs": 67405, + "119-native-patterns.js": 6976, + "1200-regex-test-basics.ts": 59964, + "1201-regex-replace.ts": 59629, + "1202-regex-substitutions.ts": 64765, + "1203-regex-split.ts": 40181, + "1204-regex-empty-unicode.ts": 4019, + "1205-regex-rc-stress.ts": 6786, + "1300-errors-basics.ts": 4602, + "1301-errors-subclass.ts": 4547, + "1302-errors-typed-catch.ts": 5456, + "1303-errors-rc-stress.ts": 3915, + "1304-errors-uncaught.ts": 4301, + "1305-errors-async-rejections.ts": 25668, + "1306-errors-runtime-instances.ts": 56774, + "1350-path-normalize-join.ts": 4099, + "1351-path-parts.ts": 3517, + "1352-path-resolve-relative.ts": 4602, + "1353-os-basics.ts": 4075, + "1354-builtin-bare-specifiers.ts": 4565, + "1355-url-parse.ts": 3650, + "1356-url-file-bridge.ts": 5481, + "1357-fsp-roundtrip.ts": 4525, + "1358-crypto-random.ts": 3809, + "1359-json-module/main.ts": 4642, + "1360-spawn-sync.ts": 14534, + "1361-spawn-events.ts": 3384, + "1362-spawn-timers.ts": 9556, + "1363-spawn-unhandled-error.ts": 3577, + "1364-union-truthiness.ts": 4455, + "1365-union-logical.ts": 5263, + "1366-union-equality.ts": 5404, + "1367-destructuring.ts": 6097, + "1368-constructor-functions.ts": 4502, + "1369-promise-union.ts": 5171, + "1370-spread.ts": 5455, + "1371-union-template-tostring.ts": 5704, + "1372-loose-null-tests.ts": 4863, + "1373-union-array-arms.ts": 6061, + "140-bitwise-operators.ts": 5704, + "1400-typedarray-basics.ts": 4417, + "1401-typedarray-slice-set.ts": 6150, + "1402-buffer-encodings.ts": 6652, + "1403-buffer-fs.ts": 4712, + "1404-zlib-crypto-bytes.ts": 8302, + "1405-bytes-unions-arrays.ts": 4908, + "1406-dataview-mp4.ts": 4190, + "1407-dataview-bounds.ts": 5248, + "1408-string-indexing.ts": 6549, + "1409-typedarray-integer-loops.ts": 5748, + "1420-number-statics.ts": 4080, + "1421-number-parse-strings.ts": 3030, + "1422-date-iso.ts": 4656, + "1423-text-codec.ts": 42827, + "1424-fs-options.ts": 4763, + "1425-stdin-tty.ts": 3515, + "1426-stdin-read.ts": 4428, + "1427-fromcharcode-lastindexof.ts": 4393, + "1428-settled-await-order.ts": 3638, + "1429-promise-catch-finally.ts": 6438, + "1430-promise-race.ts": 5872, + "1431-caught-tostring.ts": 4401, + "1432-destructured-params.ts": 3887, + "1433-filter-narrow.ts": 5268, + "1434-async-never.ts": 3971, + "1435-math-spread.ts": 5075, + "1436-default-undef-param.ts": 4896, + "1437-pad-default.ts": 5306, + "1438-promise-all.ts": 8026, + "1440-interval-basics.ts": 3886, + "1441-interval-clear-cross.ts": 8806, + "1442-interval-throw.ts": 3908, + "1443-signal-handlers.ts": 10098, + "1444-exit-listeners.ts": 6271, + "1445-exit-process-exit.ts": 6288, + "1446-exit-uncaught.ts": 4829, + "1447-stdin-closed-events.ts": 5308, + "1448-stream-columns.ts": 3842, + "1450-incdec-expression.ts": 4062, + "1451-instanceof-uint8array-unknown.ts": 4913, + "1452-return-through-finally.ts": 3940, + "1453-pmap-settled.ts": 5769, + "1454-nonnull-narrow-and-fcc-bytes.ts": 5066, + "1455-lambda-union-return-adoption.ts": 4687, + "1460-console-error-warn.ts": 4173, + "1461-process-pid-getuid-kill.ts": 3798, + "1462-exec-sync.ts": 4955, + "1463-timeout-unref.ts": 3605, + "1464-env-writes.ts": 3898, + "1465-env-value.ts": 5166, + "1466-child-containers.ts": 4567, + "1467-string-match.ts": 31065, + "1468-exec-path.ts": 3971, + "1469-remove-listener.ts": 5771, + "1470-child-lifecycle.ts": 4259, + "1471-child-unref.ts": 8596, + "1472-errno-code.ts": 3697, + "1473-promisify-execfile.ts": 5268, + "1474-string-decoder.ts": 4402, + "1475-readline-closed-stdin.ts": 7198, + "1476-assign-expression.ts": 4160, + "1477-in-expressions.ts": 3377, + "1478-promise-reject.ts": 4766, + "1479-destructuring-assign.ts": 3620, + "1480-os-network-interfaces.ts": 5191, + "1481-string-case-static.ts": 47079, + "1482-spawnsync-error.ts": 4222, + "1483-array-slice.ts": 37812, + "1520-fs-wider-surface.ts": 4505, + "1520-string-split-static.ts": 5103, + "1521-fs-lstat-lock.ts": 4862, + "1521-string-trim-pad-static.ts": 4149, + "1522-parseint-static.ts": 3342, + "1522-spawnsync-options.ts": 6589, + "1523-isnan-floor-static.ts": 4386, + "1523-spawn-options.ts": 4864, + "1524-atomics-sleep-static.ts": 5554, + "1524-catch-narrowing.ts": 4776, + "1525-child-exit-signal.ts": 3419, + "1525-unknown-typeof-validation.ts": 4171, + "1526-set-spread.ts": 6387, + "1527-logical-mixed-operands.ts": 56825, + "1528-delete-records-env.ts": 3989, + "1529-class-with-statics.ts": 4851, + "1530-array-push-variadic.ts": 4575, + "1530-spread-override-completion.ts": 4651, + "1531-delete-optional-fields.ts": 5027, + "1531-process-arch-versions.ts": 4345, + "1532-array-splice-shift.ts": 5472, + "1532-union-shared-field-read.ts": 4768, + "1533-bool-equality.ts": 4449, + "1533-path-platform-namespaces.ts": 3877, + "1534-crypto-hash-chain.ts": 4113, + "1534-union-as-arm-cast.ts": 3510, + "1535-spawn-fd-stdio.ts": 3466, + "1535-union-param-defaults.ts": 3648, + "1536-destructuring-defaults.ts": 3985, + "1536-string-array-sweep.ts": 4779, + "1537-chalk-hybrid/main.ts": 4311, + "1537-os-release-spawnsync-stdio.ts": 3803, + "1538-await-promiselike-return.ts": 4183, + "1538-math-static-scalar.ts": 4776, + "1539-readonly-set-map.ts": 4240, + "1539-unknown-truthiness.ts": 3925, + "1540-os-userinfo.ts": 3976, + "1540-void-statement.ts": 3920, + "1541-fs-readdir-dirent.ts": 6148, + "1541-union-keyed-reads.ts": 5049, + "1542-map-promise-values.ts": 4830, + "1542-record-literal-into-union.ts": 4691, + "1543-rest-destructuring.ts": 4682, + "1543-set-server-handles.ts": 11403, + "1544-dyn-json-reads.ts": 5540, + "1544-string-matchall.ts": 4621, + "1545-spread-order-and-optional.ts": 4810, + "1546-union-element-reads.ts": 4735, + "1547-computed-key-fold.ts": 4346, + "1548-boolean-condition-forms.ts": 4311, + "1549-array-isarray-unions.ts": 4783, + "1550-param-defaults-func-set.ts": 4654, + "1551-dyn-receiver-methods.ts": 47581, + "1552-exec-options-record.ts": 4931, + "1553-truthy-hof-predicates.ts": 3759, + "1554-caught-into-unknown.ts": 4792, + "1555-index-spread-into-declared.ts": 5295, + "1556-union-retag-width-arms.ts": 4339, + "1557-url-host.ts": 4062, + "1558-any-joins-and-dyn-validation.ts": 4201, + "1559-conditional-spread-index-merge.ts": 4811, + "1560-forof-homogeneous-tuples.ts": 3801, + "1561-forof-strings.ts": 4569, + "1561-promise-then.ts": 5688, + "1562-optional-chain-tails.ts": 4544, + "1562-spawn-conditional-spread.ts": 5015, + "1563-forof-iterator-projections.ts": 3931, + "1563-string-raw-fold.ts": 2934, + "1564-fs-watch.ts": 4633, + "1564-string-raw.ts": 4462, + "1565-spawn-pipe-streams.ts": 4825, + "1565-union-element-join.ts": 4303, + "1566-child-duck-interface.ts": 3784, + "1567-server-close-override.ts": 8452, + "1568-x509-fingerprint.ts": 4642, + "1569-fsp-mkdir-unlink-chmod.ts": 4604, + "1570-child-unref-kill-reffed.ts": 10519, + "1571-optional-call-tostring-tail.ts": 4149, + "1571-stdin-set-raw-mode-non-tty.ts": 3975, + "1572-promise-reject-all-tuple.ts": 4421, + "1572-x509-validity-date-gettime.ts": 3928, + "1573-promisify-execfile-env-spread.ts": 4371, + "1573-tdz-scalar-forward-capture.ts": 3510, + "1574-dyn-optional-method-number-keys.ts": 5406, + "1574-promise-all-tuple-literal.ts": 4446, + "1575-unknown-assert-into-record.ts": 4160, + "1576-width-coercions.ts": 4905, + "1577-url-hostname.ts": 3830, + "1578-exec-input-optional.ts": 4111, + "1579-matchall-index.ts": 3935, + "1580-exec-env-conditional-spread.ts": 4157, + "1581-declare-const-read.ts": 4532, + "1582-timer-sleep-resolve.ts": 5668, + "1583-loops-and-stream-captures.ts": 4447, + "1584-union-field-unassigned.ts": 4778, + "1585-union-field-conditional-ctor.ts": 4205, + "1586-derived-field-before-super-init.ts": 4167, + "1590-js-unannotated.js": 4511, + "1591-js-closures.js": 4889, + "1592-js-classes.js": 7239, + "1593-js-jsdoc-records.js": 5280, + "1594-js-async.js": 3437, + "1595-js-island-gap/main.js": 3858, + "1596-cjs-modules/main.js": 3826, + "1597-mjs-graph/main.mjs": 4207, + "1598-cjs-builtin-require/main.js": 4782, + "1599-js-uncaught-throw.js": 3029, + "1600-assert-passing.ts": 4584, + "1601-assert-fail-exit.ts": 5710, + "1602-assert-caught-error.ts": 5606, + "1603-assert-scalar-messages.ts": 6055, + "1604-assert-deep-structures.ts": 4164, + "1605-assert-import-forms.ts": 4744, + "1606-assert-strict-module.ts": 3846, + "1607-assert-throws-match.ts": 3634, + "1608-cjs-assert/main.js": 22902, + "1609-assert-async.ts": 5276, + "1610-path-win32-full.ts": 4297, + "1611-url-file-bridge-neutral.ts": 4511, + "1612-cjs-module-globals.cjs": 4120, + "1613-cjs-esm-proxy-named/main.mjs": 4168, + "1614-js-fence-dodges.cjs": 5147, + "1615-cjs-export-shapes/main.cjs": 5790, + "1616-cjs-esm-lexer-invisible/main.mjs": 3074, + "1617-cjs-esm-lexer-visible/main.mjs": 4703, + "1618-cjs-esm-poisoned-tail/main.mjs": 4175, + "1619-cjs-esm-reexport-invisible/main.mjs": 3250, + "1620-cjs-require-effects-order/main.js": 4047, + "1621-cjs-require-cache-hit/main.js": 3190, + "1622-cjs-require-conditional/main.js": 4002, + "1623-cjs-require-diamond/main.js": 3383, + "1624-cjs-require-dir-mid-file/main.js": 4621, + "1625-cjs-require-lazy-fn/main.js": 4138, + "1626-cjs-require-mjs/main.js": 3518, + "1627-mjs-import-cjs-requires/main.mjs": 4705, + "1628-checked-dynamic-builtin-args.cjs": 4815, + "1629-require-main-filename.cjs": 3830, + "1630-inspect-scalars.ts": 3402, + "1630-rmsync-retry-options.cjs": 4168, + "1631-inspect-arrays.ts": 5636, + "1632-inspect-records.ts": 5322, + "1633-inspect-map-set.ts": 4905, + "1634-inspect-classes.ts": 50857, + "1635-inspect-buffer.ts": 3662, + "1636-util-format.ts": 3856, + "1637-inspect-dyn.ts": 4713, + "1638-inspect-cjs.cjs": 4782, + "1639-versions-openssl-probe.ts": 3624, + "1640-fd-read-decode.ts": 4777, + "1641-string-search.ts": 42621, + "1642-object-fromentries-rows.ts": 3842, + "1643-metadata-dyn-return.cjs": 6131, + "1644-ee-basics.ts": 4824, + "1645-ee-extends.ts": 4269, + "1646-ee-once-remove.ts": 5597, + "1647-ee-meta-events.ts": 4609, + "1648-ee-error-event.ts": 3894, + "1649-ee-names-counts.ts": 3445, + "1650-ee-prepend.ts": 5882, + "1651-ee-max-listeners.ts": 3513, + "1652-ee-snapshot.ts": 3828, + "1653-ee-cjs.cjs": 5160, + "1654-ee-namespace.ts": 4086, + "1655-spawnsync-neutral.ts": 5575, + "1656-execsync-neutral.ts": 5201, + "1657-spawn-async-neutral.ts": 4183, + "1660-buffer-read-write-num.ts": 4602, + "1661-buffer-encodings-full.ts": 4532, + "1662-string-decoder-encodings.ts": 4447, + "1663-buffer-compare-search-fill.ts": 6507, + "1664-dyn-fn-boundary.cjs": 3753, + "1665-dyn-fn-mustcall/main.cjs": 3637, + "1666-dyn-fn-identity.ts": 4113, + "1667-dyn-fn-not-callable.cjs": 5226, + "1668-dyn-fn-throws.cjs": 3927, + "1669-symbol-identity.ts": 5374, + "1670-symbol-registry.ts": 5422, + "1671-symbol-typeof.ts": 5011, + "1672-symbol-containers.ts": 6131, + "1673-symbol-cjs.cjs": 3807, + "1674-symbol-inspect.ts": 4101, + "1676-func-array-surface.ts": 3917, + "1677-emitter-listeners.ts": 5384, + "1678-emitter-dyn-listeners.cjs": 6837, + "1679-console-dyn.js": 5132, + "1680-assert-bytes.ts": 5780, + "1681-assert-funcs.ts": 4542, + "1685-stream-readable-basics.ts": 6799, + "1686-stream-readable-paused.ts": 7460, + "1687-stream-readable-flow-control.ts": 6103, + "1688-stream-writable-basics.ts": 8189, + "1689-stream-writable-async-drain.ts": 6061, + "1690-stream-duplex.ts": 8154, + "1691-stream-transform.ts": 8459, + "1692-stream-pipe.ts": 8943, + "1693-stream-pipe-backpressure.ts": 7445, + "1694-stream-destroy.ts": 4337, + "1695-stream-props.ts": 4409, + "1696-stream-read-inside-read.ts": 4780, + "1697-stream-tick-vs-timer.ts": 4369, + "1698-stream-uncaught-error.ts": 9453, + "1699-stream-callback-shapes.ts": 6489, + "1700-mustcall-common/main.cjs": 5513, + "1701-mustcall-exit-report/main.cjs": 5169, + "1702-dyn-proto-dispatch.cjs": 3843, + "1703-arguments-rest-props.cjs": 5139, + "1710-cjs-export-class/main.js": 4603, + "1711-cjs-export-single-values/main.js": 4271, + "1712-field-incdec.ts": 4369, + "1713-js-dyn-fields.js": 3948, + "1720-assert-throws-shape.ts": 4422, + "1721-assert-throws-regex-class.ts": 3508, + "1722-assert-rejects.ts": 4110, + "1723-assert-does-not-reject.ts": 4852, + "1724-assert-iferror.ts": 5098, + "1725-assert-symbols.ts": 4520, + "1726-promise-with-resolvers.ts": 4144, + "1727-assert-throws-shape-exit.ts": 4544, + "1730-countdown-symbol-keys/main.js": 4981, + "1731-symbol-field-shapes.cjs": 4983, + "1732-symbol-field-cross-module/main.js": 6365, + "1740-stream-extends-readable.ts": 4706, + "1741-stream-extends-writable.ts": 8537, + "1742-stream-extends-duplex-transform.ts": 8628, + "1743-stream-extends-vt-dispatch.ts": 7490, + "1744-stream-set-encoding.ts": 3923, + "1745-stream-encoding-js.cjs": 3912, + "1746-stream-for-await.ts": 5266, + "1747-stream-for-await-js.cjs": 9157, + "1750-http-agent-options.ts": 4824, + "1751-options-record-stance.ts": 4860, + "1760-surplus-args-drop.cjs": 3596, + "1761-emitter-special-event-names.cjs": 5408, + "1770-assert-dyn-strict.ts": 4549, + "1771-assert-dyn-deep.ts": 4653, + "1772-assert-dyn-js.cjs": 4842, + "1773-assert-dyn-exit.ts": 3322, + "1780-http-res-surface.ts": 7900, + "1781-http-server-shapes.cjs": 5025, + "1790-computed-key-folds.ts": 4472, + "1791-searchparams-core.ts": 3100, + "1792-searchparams-encoding.ts": 4024, + "1793-searchparams-iteration.ts": 2933, + "1794-searchparams-url-live.ts": 2969, + "1795-dyn-handle-request.cjs": 8538, + "1796-dyn-handle-body.cjs": 8773, + "1797-dyn-handle-socket.cjs": 5230, + "1798-dyn-handle-typed-box.ts": 9154, + "1800-immediate-basics.ts": 3918, + "1801-immediate-handle.ts": 3182, + "1802-immediate-unref-exit.ts": 3903, + "1803-timeout-refresh.ts": 3243, + "1804-timers-module.ts": 3435, + "1805-timer-callback-args.ts": 3958, + "1806-timer-delay-trunc.ts": 4233, + "1810-stream-options-const-record.ts": 3952, + "1811-stream-option-value-callbacks.cjs": 4066, + "1812-stream-super-options-forwarding.cjs": 8532, + "1813-stream-finished.ts": 3971, + "1814-stream-pipeline.cjs": 4413, + "1815-stream-state-reads.cjs": 4085, + "1820-static-block-basics.ts": 3479, + "1821-static-block-order.ts": 4110, + "1822-static-block-js.js": 3095, + "1823-static-instance-method-names.ts": 3531, + "1824-for-of-destructuring-defaults.ts": 3925, + "1825-exhaustive-typeof-switch.ts": 3439, + "1826-settimeout-arguments-callback.js": 3758, + "1830-enum-numeric-basics.ts": 3846, + "1831-enum-string-const-reverse.ts": 3347, + "1832-enum-modules/main.ts": 3603, + "1835-var-basics.ts": 3009, + "1836-var-loop-capture.ts": 4162, + "1837-var-undefined-hoisting.ts": 3457, + "1838-var-modules/main.ts": 4306, + "1839-var-js/main.cjs": 4870, + "1840-jsdoc-import-type-only/main.js": 3497, + "1850-overload-basics.ts": 4398, + "1851-overload-return-narrowing.ts": 3452, + "1852-overload-class-members.ts": 3733, + "1853-overload-modules/main.ts": 3524, + "1854-ambient-declare-fn.ts": 4169, + "1855-ambient-declare-fn-uncaught.ts": 2640, + "1870-unit-type-values.ts": 4848, + "1871-empty-array-never.ts": 3051, + "1872-template-tostring-composite.ts": 3835, + "1880-json-declared-field-order.ts": 5173, + "1881-default-anon/main.ts": 3809, + "1882-default-cjs-interop/main.ts": 3893, + "1883-default-exports/main.ts": 4076, + "1890-ns-imports/main.ts": 3441, + "1891-ns-reexport/main.ts": 3946, + "1940-class-values-basics.ts": 3823, + "1941-class-values-registry.ts": 3834, + "1942-class-expressions.ts": 3250, + "1943-class-statics-expanded.ts": 3491, + "1944-class-values-modules/main.ts": 3807, + "1950-generic-fn-values.ts": 4479, + "1951-generic-classes-basics.ts": 4288, + "1952-generic-class-statics-values.ts": 4461, + "1953-generic-class-hierarchy.ts": 2769, + "1954-generic-modules/main.ts": 4012, + "1960-namespace-basics.ts": 4063, + "1961-namespace-nested.ts": 4436, + "1962-namespace-classes.ts": 3835, + "1963-namespace-aliases.ts": 4480, + "1964-namespace-type-only.ts": 4245, + "1965-namespace-ambient.ts": 3646, + "1966-namespace-modules/main.ts": 3158, + "1967-namespace-alias-typeonly.ts": 2894, + "1968-namespace-import-eq-snapshot.ts": 4133, + "1970-decorators-basics.ts": 5269, + "1971-decorators-rebinding.ts": 4674, + "1972-decorators-throwing.ts": 3103, + "1973-decorators-ambient.ts": 3465, + "1980-primitive-proto-statics.ts": 4370, + "1981-date-utc.ts": 3865, + "1982-freeze-resolve-passthrough.ts": 4409, + "1983-array-tuple-surfaces.ts": 4749, + "1990-labels-basics.ts": 3921, + "1991-for-in-loops.ts": 4875, + "1992-small-syntax.ts": 4940, + "200-strings.ts": 2950, + "2000-generic-methods-basic.ts": 19968, + "2001-generic-methods-generic-class.ts": 3103, + "2002-generic-methods-inheritance.ts": 4643, + "2003-generic-methods-object-literal.ts": 4397, + "2004-generic-methods-statics.ts": 2813, + "201-templates.ts": 3364, + "2010-generators-basics.ts": 4186, + "2011-generators-forof.ts": 4950, + "2012-generators-return-throw.ts": 4210, + "2013-generators-sent-values.ts": 4475, + "2014-generators-values.ts": 4791, + "2015-generators-yieldstar.ts": 3849, + "2016-generators-rc-stress.ts": 4017, + "2017-generators-async.ts": 4735, + "2018-generators-uncaught.ts": 4005, + "2019-generators-loops.ts": 2646, + "2020-generic-value-bindings.ts": 4401, + "2021-generic-value-binding-modules/main.ts": 3358, + "2022-width-index-capture.ts": 3719, + "2023-width-readonly.ts": 3719, + "2024-width-array-elems.ts": 3231, + "2025-width-union-compose.ts": 3698, + "2026-width-spread.ts": 3241, + "2030-width-nested.ts": 4055, + "2031-width-tuples.ts": 3342, + "2032-cjs-export-assigned-class-expression/main.cjs": 3882, + "2033-property-assigned-class-extends.cjs": 3781, + "2035-ternary-array-retag.cjs": 3818, + "2036-evolving-array-decl.cjs": 4090, + "2037-fn-decl-hoisting.cjs": 3678, + "2038-evolving-array-js.cjs": 5045, + "2040-any-bindings.ts": 3791, + "2040-mixin-heritage.ts": 4699, + "2041-any-params-returns.ts": 3681, + "2041-mixin-values.ts": 4051, + "2042-any-flow-loops.ts": 3594, + "2042-mixin-modules/main.ts": 4173, + "2043-any-dom-values.ts": 2471, + "2043-mixin-rc-stress.ts": 4344, + "2044-vtable-sibling-slots.ts": 3559, + "2045-objlit-accessors-basic.ts": 3871, + "2045-parameter-properties.ts": 4491, + "2046-abstract-classes.ts": 5595, + "2046-objlit-accessors-effects.ts": 2807, + "2047-objlit-accessors-shapes.ts": 3455, + "2047-optional-class-fields.ts": 5630, + "2050-dynamic-import-own-module/main.ts": 3193, + "2051-dynamic-import-then/main.ts": 4043, + "2052-dynamic-import-self.ts": 4058, + "2053-typeof-static-fold.ts": 3537, + "2054-destructuring-island-source.ts": 3138, + "2060-empty-tuple.ts": 3805, + "2070-computed-key-folds.ts": 4750, + "2071-integer-key-enumeration-order.ts": 3546, + "2072-expando-function-members.ts": 4747, + "2073-destructuring-assignment.ts": 3553, + "2074-island-destructuring.ts": 4229, + "2075-index-signature-runtime-keys.ts": 5005, + "2080-destructuring-defaults.ts": 4463, + "2081-destructuring-rest.ts": 3264, + "2082-destructuring-assignment.ts": 4012, + "2085-destructuring-heads-rest-params.ts": 3930, + "2087-destructuring-for-heads.ts": 3872, + "2090-index-signature-number-keys.ts": 4103, + "2091-index-signature-string-intersection.ts": 3918, + "2092-package-imports/main.ts": 3799, + "2093-timers-promises.ts": 3870, + "2094-diagnostics-channel.ts": 4547, + "2095-js-catch-unknown.js": 4236, + "2096-os-type.ts": 3673, + "2097-js-evolving-globals.js": 3604, + "210-string-methods.ts": 3491, + "2100-stream-default-hwm.ts": 6786, + "2101-dyn-param-defaults.ts": 2902, + "2102-empty-pattern-decls.ts": 2680, + "2105-computed-key-static-folds.ts": 4156, + "2106-comma-expressions.ts": 4616, + "211-string-unicode.ts": 3897, + "2110-top-object-types.ts": 3870, + "2111-json-stringify-unknown.ts": 4435, + "2112-array-at-findlast.ts": 4707, + "2113-string-index-methods.js": 3454, + "212-string-aliasing-append.ts": 3894, + "2120-package-self-import/main.ts": 2683, + "2121-esm-cycle-inert-backedge/main.ts": 2391, + "2123-ambient-import-crash/main.ts": 3826, + "2124-imports-field-wildcard/main.ts": 4011, + "2140-uri-component-codecs.ts": 4495, + "2160-dc-tracing-channel.cjs": 5323, + "2161-js-object-literal-identity.cjs": 4357, + "2162-js-dyn-destructure.cjs": 3222, + "2163-js-selfref-const.cjs": 3731, + "2164-js-then-dyn-handler.cjs": 3928, + "2165-js-throw-dyn.cjs": 3594, + "2166-cjs-getter-this/main.cjs": 2580, + "2170-island-array-exits.ts": 5872, + "2171-island-json-stringify.ts": 3235, + "2172-island-regexp-boundary.ts": 4811, + "2190-new-object-and-string-wrappers.ts": 3881, + "2191-uri-encoders.ts": 2785, + "2192-object-default-tostring.ts": 3767, + "2193-discarded-stdlib-reads.ts": 2753, + "2194-ambient-undef-chains.ts": 3966, + "2195-require-missing-package/main.mjs": 3949, + "2196-island-computed-key-chain.ts": 3859, + "2210-dyn-promise-crossing.cjs": 5056, + "2211-dc-trace-promise.cjs": 4674, + "2212-unhandled-rejection-listener.cjs": 3832, + "2213-async-local-storage.cjs": 4800, + "2214-dc-bind-store.cjs": 4857, + "2215-process-emit-warning-exit.cjs": 4319, + "2240-width-class-into-record.ts": 4526, + "2241-width-record-into-class.ts": 3828, + "2242-width-statics-into-record.ts": 3590, + "2243-width-assert-satisfies-shapes.ts": 4657, + "2250-tagged-templates.ts": 3291, + "2252-class-iterators.ts": 4007, + "2253-union-coercions.ts": 2990, + "2260-http2-constants.cjs": 3422, + "2280-builtin-namespace-destructure.cjs": 3322, + "2281-domexception-atob-btoa.cjs": 3717, + "2282-queue-microtask.cjs": 4471, + "2283-structured-clone.cjs": 6184, + "2284-regexp-constructor.cjs": 3737, + "2285-iferror-dyn.cjs": 3898, + "2286-dyn-object-walks.cjs": 3663, + "2300-dyn-record-spread-and-keyed-access.cjs": 3720, + "2301-cjs-export-table-dom-attach.cjs": 3384, + "2310-process-next-tick.ts": 5910, + "2311-next-tick-js-callbacks.js": 2877, + "2312-stream-underscore-assign.ts": 8104, + "2313-stream-underscore-assign-js.cjs": 4045, + "2314-process-introspection.ts": 4555, + "2320-await-unit.ts": 3726, + "2321-emitter-static-setmax.cjs": 4139, + "2322-dyn-array-sort.cjs": 3726, + "2323-timer-callback-returns.ts": 3622, + "2330-tls-server-runtime-options.cjs": 27710, + "2331-tls-server-options-typed.ts": 27128, + "2350-nan-global.ts": 4123, + "2351-async-methods.ts": 4008, + "2352-void-coercions.ts": 4044, + "2353-decorators-member-ambient.ts": 3507, + "2354-decorators-expression-ambient.ts": 3756, + "2360-tuple-to-array.ts": 3301, + "2361-assign-index-record.ts": 4658, + "2362-execsync-inherit.ts": 3671, + "2363-nullish-retag.ts": 4322, + "2364-in-runtime-key.ts": 3872, + "2365-dyn-into-island.ts": 4202, + "2366-string-well-formed.ts": 4097, + "2367-regexp-escape.ts": 2500, + "2368-set-methods.ts": 4362, + "2369-promise-try.ts": 4105, + "2370-group-by.ts": 5234, + "2371-iterator-helpers.ts": 3705, + "2380-buffer-module-named-import.ts": 3866, + "2381-builtin-default-imports.js": 3690, + "2382-cycle-two-decl/main.ts": 3053, + "2383-cycle-assertdoc/main.js": 3424, + "2384-cycle-mixed-bindings/main.ts": 3805, + "2385-builtin-reexport-facade/main.ts": 3157, + "2386-string-to-chars.ts": 3939, + "2387-tonumber-argv.ts": 2788, + "2388-tonumber-grammar-zoo.ts": 3308, + "2389-tonumber-unary-plus.ts": 4585, + "2390-dot-requires/main.cjs": 3582, + "2391-default-builtin-imports/main.ts": 3597, + "2392-block-guard-alias-scope.ts": 3540, + "2393-loop-exit-guard-narrow.ts": 4327, + "2394-loop-narrow-containment.ts": 3359, + "2395-while-cond-guard.ts": 3736, + "2396-born-narrowed-decl.ts": 3669, + "2397-branch-kill-merge.ts": 3852, + "2398-switch-arm-kill.ts": 3957, + "2399-reassign-kill-polarity.ts": 3597, + "240-stdout-write.ts": 2582, + "2400-kill-return-edge.ts": 3990, + "2401-kill-break-staging.ts": 3661, + "2402-labeled-break-kill.ts": 3719, + "2403-kill-continue-backedge.ts": 3491, + "2404-kill-throw-catch.ts": 3664, + "2405-sibling-arm-narrow.ts": 3896, + "2406-switch-clause-sibling-narrow.ts": 3538, + "2407-infinite-loop-seal.ts": 3968, + "2408-loop-terminality-reopen.ts": 3262, + "2409-loop-body-return-not-terminal.ts": 3183, + "2410-stacked-cases-terminality.ts": 4213, + "2411-exhaustive-switch-terminality.ts": 4119, + "2412-dowhile-guard-trailing-test.ts": 2693, + "2413-dowhile-kill-renarrow.ts": 3480, + "2414-dowhile-continue-kill.ts": 3748, + "2415-dowhile-break-trailing-test.ts": 3696, + "2416-spread-ternary-positions.ts": 3541, + "2417-ternary-local-inference.ts": 4012, + "2418-wrapped-ternary-arms.ts": 3646, + "2419-selector-ternary-union.ts": 4650, + "2420-nullish-null-identity.ts": 4115, + "2421-float-lane-inference.ts": 3944, + "2422-ieee-div-rem-corners.ts": 4159, + "2423-param-null-reassign.ts": 2605, + "2424-ternary-miss-first-narrowing.ts": 4261, + "2425-elseif-head-exit-narrow.ts": 2714, + "2426-default-snapshot-mutable/main.ts": 3509, + "2427-perf-hooks-now.ts": 3643, + "2428-field-redeclare-inherited.ts": 3729, + "2429-destructure-class-instance.ts": 3764, + "2430-empty-literal-unknown-field.js": 3619, + "2431-primitive-ctor-values.ts": 2975, + "2432-generic-member-fields-async.ts": 3894, + "2433-as-const-command-tables.ts": 4279, + "2434-deferred-init-fields.ts": 3904, + "2435-loose-same-kind-equality.ts": 2449, + "2440-console-inspect-args.ts": 4983, + "2441-console-process-argv.ts": 3366, + "2442-union-literal-arm-widening.ts": 3510, + "2443-union-literal-shadow-narrowing.ts": 4296, + "2444-union-literal-reducer-spread.ts": 4322, + "2445-math-minmax-nary.ts": 4287, + "2446-performance-global.ts": 2765, + "2447-island-boundary-slots/main.ts": 30163, + "2448-entries-null-arms-regex-arrays.ts": 5050, + "2450-private-instance-methods.ts": 3745, + "2451-private-fields.ts": 4055, + "2452-private-statics.ts": 4122, + "2453-private-accessors.ts": 3729, + "2454-private-generator-methods.ts": 4591, + "2455-private-brand-checks.ts": 3790, + "2456-private-async-methods.ts": 4068, + "2457-private-members.js": 3733, + "2460-qs-parse-grammar.ts": 4511, + "2461-qs-parse-options.ts": 5074, + "2462-qs-stringify.ts": 5146, + "2463-qs-escape-unescape.ts": 4740, + "2464-qs-require-forms.cjs": 3706, + "2470-mockable-module-shape.js": 4001, + "2471-record-keyed-write-hasown.js": 4306, + "2472-string-regexp-union.ts": 4333, + "2473-option-table-widths.js": 4080, + "2474-island-literal-forms.ts": 3640, + "2475-variadic-wrapper-island.js": 3607, + "2476-map-regex-values.ts": 3840, + "2480-recursive-record-tree.ts": 3961, + "2481-mutual-recursive-records.ts": 3629, + "2482-recursive-union-tree.ts": 3442, + "2483-recursive-record-cycles.ts": 3728, + "2484-json-stringify-circular.ts": 3926, + "2485-inspect-circular-refs.ts": 4207, + "2486-recursive-record-boundaries.ts": 3631, + "2487-recursive-deep-equal.ts": 4488, + "2488-ast-walker.ts": 3489, + "2490-find-miss-null-compare.ts": 3128, + "2491-strict-unit-compare-no-arm.ts": 3378, + "2492-loose-null-compare-unions.ts": 3406, + "2493-switch-unit-cases.ts": 4046, + "2494-no-arm-compare-effects.ts": 3576, + "2500-net-autosel-timeout.ts": 6361, + "2510-dyn-evolving-array-map.ts": 4082, + "2511-dyn-evolving-array-filter-foreach.ts": 4487, + "2512-dyn-evolving-array-mixed-push.ts": 3602, + "2520-private-statics-class-name-calls.ts": 3443, + "2521-private-statics-class-expression.ts": 4261, + "2522-private-statics-decorated.ts": 3872, + "2523-private-statics-aliased-class.ts": 3793, + "2530-object-destructuring-decl.ts": 3717, + "2531-array-tuple-destructuring-decl.ts": 4697, + "2532-param-destructuring-options.ts": 4524, + "2533-forof-destructuring.ts": 4725, + "2534-object-rest-decl.ts": 4160, + "2535-destructuring-assign-basics.ts": 3008, + "2536-destructuring-assign-nested.ts": 3971, + "2537-destructuring-assign-member-targets.ts": 4117, + "2538-destructuring-assign-class-source.ts": 3926, + "2539-class-instance-rest.ts": 3983, + "2540-accessor-destructuring-defaults.ts": 2587, + "2541-destructuring-eval-order.ts": 4204, + "2550-generics-keyof-pick.ts": 4104, + "2551-generics-value-aliases.ts": 3651, + "2552-generics-iface-methods.ts": 3134, + "2553-generics-signature-bindings.ts": 4036, + "2554-generics-frontier-mix.ts": 4173, + "2555-generics-keyof-writes.ts": 3734, + "2556-crypto-introspection.ts": 3814, + "2556-width-hybrid-shapes.ts": 4554, + "2557-tls-ca-store.ts": 4247, + "2557-width-field-lifts.ts": 3310, + "2558-index-signature-func-values.ts": 3316, + "2558-rejection-events.cjs": 3816, + "2559-index-signature-container-values.ts": 4363, + "2560-arraybuffer-dataview-set.ts": 4259, + "2561-object-is.ts": 3615, + "2562-intl-numberformat-en-us.ts": 4400, + "2563-stream-promises-pipeline.ts": 3735, + "2564-stream-promises-finished.ts": 8470, + "2565-stream-promises-js.cjs": 4670, + "2566-promise-reject-dyn-reason.js": 3862, + "2567-rest-spread-forward.js": 4241, + "2568-rest-spread-forward-dynamic.js": 3805, + "2569-upcast-identity.ts": 2741, + "2570-buffer-arg-validation.cjs": 5723, + "2571-searchparams-arg-validation.cjs": 4325, + "2572-readable-emitted-readable-flag.cjs": 3665, + "2573-fs-url-arg-ladders.cjs": 3433, + "2574-emitter-max-listeners-ladders.cjs": 4082, + "2575-string-destructuring-decl.ts": 4618, + "2576-string-destructuring-assign.ts": 4254, + "2577-builtin-global-destructuring.js": 3582, + "2578-jsval-into-unknown-rows.ts": 4068, + "2579-jsval-object-param-crossing.js": 4185, + "2580-jsval-routed-keyed-ops.js": 4278, + "2581-jsval-routed-calls.js": 4541, + "2582-jsval-object-statics.js": 4272, + "2582-jsval-routed-keyed-ops.js": 3765, + "2583-dyn-nullish-coalesce.js": 3622, + "2583-jsval-routed-calls.js": 4186, + "2584-jsval-object-statics.js": 3517, + "2584-union-dyn-collapse.ts": 5179, + "2585-dyn-nullish-coalesce.js": 4450, + "2585-unknown-array.ts": 4082, + "2586-object-assign-spread.js": 3285, + "2587-array-entries-forof.ts": 4350, + "2588-array-entries-chains.ts": 4523, + "2589-object-create-null.cjs": 4020, + "2590-object-create-dynamic.js": 3068, + "2591-ambient-generic-traps.ts": 4200, + "2592-ambient-trap-uncaught.ts": 3460, + "2593-generic-inert-bindings.ts": 3588, + "2594-nullish-generic-bindings.ts": 3726, + "2595-fs-arg-ladders.cjs": 6740, + "2596-net-arg-ladders.cjs": 6682, + "2597-dgram-send-ladders.cjs": 5257, + "2598-tls-arg-ladders.cjs": 28958, + "2599-stream-arg-ladders.cjs": 4068, + "2600-dyn-keyed-write-harness.js": 4122, + "2601-dyn-keyed-write-ops.js": 3049, + "2602-dyn-array-destructure.js": 3757, + "2603-island-keyed-write-dyn.js": 3437, + "2604-cycle-classes-mutual/main.ts": 3996, + "2605-cycle-three-module/main.ts": 3537, + "2606-dynamic-import-cycle/main.ts": 3810, + "2608-regex-named-groups.ts": 4091, + "2609-regex-named-replace.ts": 2758, + "2610-regex-named-matchall.ts": 4465, + "2611-regex-named-groups-js.cjs": 6008, + "2613-for-init-uninitialized-let.ts": 3817, + "2614-trap-binding-later-writes.ts": 4020, + "2615-nullish-field-record-binding.ts": 3220, + "2616-js-collect-probe-defers.js": 3633, + "2617-enum-static-field-import/main.ts": 3698, + "2618-ee-emit-override.ts": 2900, + "2619-ee-override-chain.ts": 3741, + "2620-ee-override-once-order.ts": 3799, + "2621-ee-override-error-throw.ts": 3035, + "2622-ee-override-filter.ts": 3418, + "2623-ee-job-queue.ts": 3306, + "2624-ee-override-js.cjs": 4196, + "2625-bytes-views.ts": 4141, + "2626-stream-event-names.ts": 5209, + "2627-stream-ticks-are-nextticks.ts": 3902, + "2628-stream-push-encodings.cjs": 3828, + "2629-stream-consumers.ts": 5554, + "2630-stream-consumers-errors.ts": 4330, + "2631-create-require/main.ts": 4336, + "2632-dyn-jsval-iterate.js": 4764, + "2633-island-promise-crossing.js": 5106, + "2634-stream-pipeline-arg-ladders.cjs": 3877, + "2635-trap-binding-unmappable-written.ts": 3449, + "2636-trap-binding-ambient-rooted-rewrite.ts": 4109, + "2637-create-require-bare/main.ts": 3645, + "2638-http-dyn-res-compat.cjs": 7173, + "2639-http-dyn-req-pause.cjs": 7147, + "2640-net-dyn-socket-compat.cjs": 5745, + "2641-http-agent-surface.cjs": 7414, + "2642-http-agent-maxsockets.cjs": 4580, + "2643-or-default-retag.ts": 4099, + "2644-http-client-url-argument.ts": 4758, + "2645-https-client-url-argument.ts": 45938, + "2646-top-level-await.ts": 3701, + "2647-top-level-await-modules/main.ts": 4075, + "2648-top-level-await-rejection.ts": 3331, + "2649-top-level-await-pending.ts": 3587, + "2650-top-level-await-self-import.ts": 3982, + "2651-top-level-await-pending-unhandled.ts": 3896, + "2652-top-level-await-dynamic/main.ts": 3670, + "2653-top-level-await-rejection-stops-loop.ts": 3674, + "2654-top-level-await-unrelated-rejection.ts": 4358, + "2655-top-level-await-cycle/main.ts": 3663, + "2656-top-level-await-cycle-order/main.ts": 3641, + "2657-top-level-await-cycle-dynamic/main.ts": 3605, + "2658-top-level-await-sync-completion/main.ts": 3738, + "2659-top-level-await-dynamic-cycle/main.ts": 3937, + "2660-top-level-await-cycle-rejection/main.ts": 3839, + "2661-top-level-await-dynamic-runtime-root/main.ts": 4277, + "2662-top-level-await-cycle-external-wait/main.ts": 3840, + "2663-top-level-await-dependency-errors/main.ts": 4026, + "2664-top-level-await-same-checkpoint-rejection.ts": 3900, + "2665-top-level-await-unhandled-listener-liveness.ts": 3124, + "2666-array-to-sorted.ts": 7343, + "2668-stored-number-array-iterator.ts": 4155, + "2669-array-copying-methods.ts": 4893, + "2670-uint8array-copy-iterate.ts": 4534, + "2671-array-copying-js.cjs": 4071, + "2672-http-request-response-callback.ts": 25478, + "2673-top-level-await-implicit-module.ts": 3661, + "2674-top-level-for-await-implicit-module.ts": 4714, + "2675-create-require-nested-esm.js": 3731, + "2675-process-self-reexec.ts": 3793, + "2676-indexed-read-strict-equality.ts": 3177, + "2676-record-clone-overrides.ts": 3884, + "2676-top-level-await-ambiguous/main.ts": 3335, + "2677-runtime-optional-closure.ts": 4747, + "2677-top-level-await-module-scope/main.ts": 2879, + "2678-date-instances.ts": 3537, + "2678-large-string-sparse-index.ts": 3659, + "2678-util-parseargs.ts": 8501, + "2679-util-parseargs-cjs.cjs": 5362, + "2680-util-parseargs-default.ts": 3698, + "2681-util-parseargs-dynamic.js": 5047, + "2682-fs-rename.ts": 4318, + "2683-filter-truthy-predicate.ts": 4083, + "2683-fs-rename-js.cjs": 4168, + "2684-filter-no-predicate-dyn.cjs": 3139, + "2684-fs-rename-abort.ts": 3244, + "2685-array-unshift-reverse.ts": 3752, + "2685-fs-write-sync.ts": 3562, + "2686-fs-file-handle.ts": 5916, + "2687-fsp-write-file-options.ts": 4133, + "2687-process-write-args.ts": 3739, + "2688-fs-stats-disk-fields.ts": 4196, + "2689-http-server-timeout-properties.ts": 9510, + "2690-https-server-timeout-option.ts": 26255, + "2691-retained-lowering-record-order.ts": 5714, + "2692-inline-record-assert-index.ts": 4256, + "2692-string-accumulation.ts": 4000, + "2693-math-constants.ts": 3814, + "2700-array-sort-complexity.ts": 4128, + "2700-http-static-trailers.ts": 3673, + "2700-wasi-core.ts": 3941, + "2701-array-sort-snapshot.ts": 5063, + "2701-http-static-trailer-wire.ts": 4002, + "2701-import-meta/main.mjs": 4298, + "2702-http-static-trailer-end.ts": 4488, + "2702-type-import-link-static/main.ts": 3632, + "2702-uint8array-to-sorted.ts": 4201, + "2703-array-sparse-sort.ts": 7778, + "2703-type-import-link-static-reexport/main.ts": 3591, + "2704-array-sparse-to-sorted.ts": 8196, + "2704-type-import-link-static-success/main.ts": 3547, + "2705-array-sparse-sort-append.ts": 6478, + "2705-type-import-link-dynamic/main.ts": 3402, + "2706-array-search-missing-needle.ts": 4264, + "2706-type-import-link-dynamic-success/main.ts": 3801, + "2707-array-search-huge-sparse.ts": 45112, + "2707-type-import-link-static-default-reexport/main.ts": 3162, + "2708-type-import-link-dynamic/main.ts": 2849, + "2720-array-read-parity.ts": 4418, + "2721-array-nested-missing-receiver.ts": 4224, + "2750-array-sparse-foundation.ts": 3486, + "2751-buffer-concat-sparse-list.ts": 3809, + "2752-array-optional-stores.ts": 3893, + "2753-array-optional-method-values.ts": 4447, + "2754-array-property-undefined.ts": 3437, + "2755-array-optional-literal-forwarding.ts": 3309, + "2780-array-parity-composition.ts": 11152, + "2781-array-missing-value-escape.ts": 3766, + "2782-array-sparse-callbacks.ts": 4827, + "2783-array-defaults-rest.ts": 3752, + "2784-array-reduce-missing-result.ts": 3900, + "2785-array-missing-destructuring.ts": 4350, + "2786-array-sparse-serialization.ts": 4399, + "2787-array-missing-string-arithmetic.ts": 3532, + "2788-array-missing-builtin-conversions.ts": 2629, + "2789-array-missing-optional-tostring.ts": 4012, + "2790-dynamic-import-self-name/main.ts": 3567, + "2791-record-optional-keys.ts": 4180, + "2792-record-optional-spread-entries.ts": 3957, + "2793-union-conversion-identity-capture.ts": 4016, + "2794-array-generic-callback-values.ts": 3192, + "2795-array-optional-typedarray-source.ts": 39238, + "2796-array-sparse-class-value-instanceof.ts": 3057, + "2800-array-expression-consumers.ts": 3984, + "2801-array-switch-consumers.ts": 3844, + "2820-array-missing-method-receivers.ts": 4015, + "2821-array-optional-numeric-comparisons.ts": 3715, + "2830-cjs-require-self-name/main.cjs": 3843, + "2840-finally-completions.ts": 3843, + "2841-using-sync.ts": 3805, + "2842-await-using.ts": 3336, + "2843-using-suppressed-error.ts": 3417, + "2844-node-disposable-resources.ts": 4668, + "2845-for-using-of.ts": 3487, + "2846-generator-using.ts": 4293, + "2850-async-generators-protocol.ts": 4610, + "2851-async-generators-values.ts": 4267, + "2852-timers-promises-interval.ts": 3195, + "2854-leading-bom-literals.ts": 3717, + "2855-searchparams-sort-complexity.ts": 3679, + "2856-dynamic-any-local-operators.ts": 3601, + "2857-branded-primitives.ts": 3846, + "2858-import-meta-resolve/main.mjs": 3869, + "2859-import-meta-resolve-dynamic/main.mjs": 3441, + "2860-require-resolve/main.cjs": 3856, + "2861-readable-iterator-close.ts": 8658, + "2862-web-readable-async-iteration.ts": 60491, + "2863-class-async-iterator.ts": 3807, + "2864-array-class-method-receivers.ts": 3808, + "2865-cjs-process-alias.cjs": 3058, + "2866-dyn-switch.ts": 4091, + "2867-array-shift-length.js": 3560, + "2868-empty-record-promise-presence.js": 3622, + "2869-nullish-mixed-lazy.ts": 3214, + "2870-and-dropped-truthy-arm.ts": 3668, + "2871-optional-promise-to-unknown.ts": 3151, + "2872-unobserved-dynamic-event.ts": 3479, + "2873-optional-receiver-loop-lifetime.js": 3838, + "2874-nullable-callback-field-cycle.js": 3416, + "2875-evolving-let-read-before-write.js": 3393, + "2876-class-unknown-roundtrip.ts": 3172, + "2877-child-unknown-roundtrip.ts": 3088, + "2878-class-private-unknown.cjs": 3951, + "2880-cjs-module-graph/main.cjs": 4546, + "2890-crypto-static-utility.ts": 4147, + "2891-json-record-key-escapes.ts": 4489, + "2892-windows-console-unicode.ts": 3631, + "2893-child-stdin-duplex.ts": 4001, + "2894-child-stdin-backpressure.ts": 3911, + "2895-child-stdin-write-after-end.ts": 4040, + "2896-child-stdin-write-after-finish.ts": 4046, + "2897-child-stdin-destroy.ts": 4182, + "2898-child-stdin-spawn-failure.ts": 3819, + "2899-nested-mutual-recursion.ts": 3391, + "2900-float64array-static.ts": 4170, + "2900-void-conditional-arrow.ts": 2973, + "2901-bigint-core.ts": 3785, + "2902-bigint-bytes.ts": 3878, + "2903-bigint-storage.ts": 4174, + "2904-bigint-errors.ts": 3154, + "2905-bigint-edges.ts": 3535, + "2906-bigint-wasi-width.ts": 3636, + "2910-overloaded-callable-alias.ts": 3507, + "2911-execfile-callback.ts": 3775, + "2912-execfile-callback-options.ts": 3546, + "2912-promisify-fs-readfile.ts": 3527, + "2913-builtin-callable-alias.ts": 5224, + "2914-fixed-tuple-call-spread.ts": 4266, + "2915-path-function-values.ts": 4335, + "2916-path-function-values-cjs.ts": 3661, + "2917-builtin-callable-widths.ts": 4692, + "2918-builtin-callable-widths-cjs.ts": 3911, + "2919-fs-function-values.ts": 3993, + "2920-fs-promises-function-values.ts": 3700, + "2921-zlib-callbacks.ts": 4702, + "2922-dyn-bitwise-not.cjs": 3792, + "2923-cjs-factory-export/main.cjs": 3686, + "2930-union-receiver-calls.ts": 4496, + "2931-union-dynamic-field-call.js": 3249, + "2932-create-require-program/main.ts": 4121, + "2933-static-dynamic-import/main.ts": 3790, + "2934-static-import-stored.ts": 2637, + "2935-module-namespace-values/main.ts": 4033, + "2936-static-loose-equality.ts": 4807, + "2937-bitwise-hot-loop.ts": 3730, + "2938-fs-sync-overloads.cjs": 2978, + "2939-child-shell-text.ts": 4396, + "2940-date-parse.ts": 3909, + "2940-fsp-realpath.ts": 3670, + "2941-fsp-lstat-identity.ts": 4973, + "2941-process-exit-code.ts": 2597, + "2942-fsp-readdir-dirent.ts": 3959, + "2942-process-exit-code-override.ts": 3773, + "2943-process-exit-code-invalid.ts": 3459, + "2944-process-exit-code-wrap.ts": 4312, + "2945-url-origin-username.ts": 2754, + "2946-child-fork-ipc/main.ts": 4571, + "2946-url-port-hash.ts": 3017, + "2947-set-readonly-seeds.ts": 3976, + "2948-child-fork-eof-callback/main.ts": 4009, + "2948-stdio-callback-record.ts": 2902, + "2949-child-fork-default/main.mjs": 3935, + "2949-url-password.ts": 3040, + "2950-fs-descriptor-publication.ts": 2555, + "2951-math-static-analytic.ts": 4273, + "2952-math-static-remaining.ts": 3480, + "2953-http-header-validation.ts": 6668, + "2954-http-outgoing-headers.ts": 4292, + "2955-http-static-state.ts": 3942, + "2956-http-static-informational.ts": 8174, + "2957-http-static-response-controls.ts": 4254, + "2961-emitter-computed-names.cjs": 2876, + "2962-emitter-computed-optional.ts": 3963, + "2963-child-fork-dispatch/main.ts": 3170, + "2964-indexed-compound.ts": 4621, + "2966-numeric-array-read-fusion.ts": 3495, + "2967-numeric-array-read-lifetime.ts": 3370, + "2968-numeric-array-borrow.ts": 4623, + "2969-scalar-record-results.ts": 3919, + "2970-constant-numeric-tables.ts": 2953, + "2971-constant-table-modules/main.ts": 2780, + "2972-http-static-distinct-snapshots.ts": 7024, + "2972-math-receiver-borrow.ts": 4560, + "2973-http-static-agent.ts": 8993, + "2973-integer-bitwise-arithmetic.ts": 3843, + "2974-array-search-from-index.ts": 102834, + "2974-http-static-module-values.ts": 6368, + "2974-numeric-remainder.ts": 4108, + "2975-bigint-default-radix.ts": 3498, + "2975-http-static-client-controls.ts": 3623, + "2976-http-static-incoming-state.ts": 2971, + "2976-json-stringify-no-arguments.js": 3696, + "2977-http-static-incoming-timeout.ts": 4074, + "2977-nested-array-binding-defaults.js": 4142, + "2978-http-static-client-timeout.ts": 9714, + "2978-regexp-optional-subject.js": 49628, + "2979-array-search-mixed-needles.js": 4303, + "2979-http-static-server-timeout.ts": 6006, + "2980-http-static-server-timeout-default.ts": 7270, + "2980-string-position-arguments.js": 3086, + "2981-http-static-events.ts": 3123, + "2981-string-optional-positions.ts": 3368, + "2982-http-static-close-all.ts": 3822, + "2982-string-position-coercion-order.js": 3928, + "2983-http-static-close-idle.ts": 3614, + "2984-http-static-close-upgrade.ts": 3907, + "2985-http-static-message-destroy.ts": 3388, + "2986-http-static-client-socket-finish.ts": 3798, + "2987-http-static-timeout-errors.ts": 4096, + "2988-http-static-client-abort.ts": 2850, + "2989-http-static-response-timeout-handled.ts": 3345, + "2990-http-static-request-timeout-handled.ts": 4813, + "2991-array-indexed-equality.ts": 4275, + "2992-stream-rows.ts": 3533, + "2993-fiber-stack-lifetimes.ts": 4327, + "2994-fromcharcode-scalar.ts": 4187, + "2995-math-hyperbolic.ts": 4005, + "2996-math-near-one.ts": 4413, + "2997-math-integer-float.ts": 3945, + "300-if-else.ts": 4350, + "3001-caught-typeof-object.js": 3508, + "3002-promise-then-two.ts": 4636, + "301-while.ts": 3775, + "302-for.ts": 3729, + "3020-string-last-index-position.ts": 2972, + "3021-buffer-union-narrow-callback.ts": 3784, + "3022-string-affix-positions.ts": 3752, + "3023-string-affix-position-coercion.js": 3717, + "3024-string-number-narrow-for-in.ts": 4025, + "3025-string-search-position-coercion.js": 2818, + "3026-array-last-index-of.js": 4346, + "3027-array-last-index-of-records.ts": 2717, + "3028-string-search-dynamic-needle.js": 3694, + "3029-string-search-union-needle.js": 3379, + "303-break-continue.ts": 3648, + "3030-array-search-no-arg.js": 3591, + "3031-array-relative-index-coercion.js": 3587, + "3031-string-last-index-search-value.js": 4017, + "3032-string-search-value.js": 3564, + "3033-string-search-needle-coercion.js": 3378, + "3034-arguments-with-parameters.js": 3967, + "3035-string-positioned-search-value.js": 3417, + "3036-string-object-search-value.js": 3926, + "3037-number-nullish-conversion.js": 4703, + "3038-number-scalar-unions.ts": 3646, + "3039-string-repeat-count.js": 3646, + "304-compound-incdec.ts": 3722, + "3040-string-prototype-call.js": 4670, + "3041-string-prototype-object-call.js": 3431, + "3042-string-padding-coercion.js": 3284, + "3043-string-split-arguments.js": 3505, + "3044-array-slice-splice-positions.js": 3592, + "3045-array-indexed-mutation.js": 3564, + "3046-array-indexed-ref-identity.ts": 4051, + "3047-object-prototype-tag.js": 4202, + "3048-object-own-prototype.ts": 4672, + "3049-array-flat-depth.ts": 3327, + "305-truthiness.ts": 3686, + "3050-array-splice-insert.ts": 4238, + "3051-array-join-default.ts": 3649, + "3052-array-construction.ts": 5275, + "3053-array-construction-js.js": 4051, + "3054-dynamic-abort-record.ts": 31946, + "3055-default-builtin-nodenext/main.ts": 3742, + "3056-default-builtin-bundler/main.ts": 3900, + "3057-default-builtin-nodenext-dynamic/main.ts": 3548, + "3058-optional-array-chain-join.ts": 4351, + "3059-arguments-property-names.ts": 3823, + "3061-concurrent-fiber-stacks.ts": 4696, + "3062-runtime-optional-record-guards.ts": 4448, + "3063-runtime-optional-class-guards.ts": 4574, + "3064-installed-entry-imports/main.ts": 3681, + "3065-installed-entry-js/main.js": 3134, + "3066-source-debugging/main.ts": 3285, + "3067-cjs-function-export-require/main.cjs": 3758, + "3068-debug-variables/main.ts": 3691, + "3068-url-checked-identity.ts": 3668, + "3069-set-checked-seed.js": 3693, + "3069-tuple-spread.ts": 4624, + "3070-checked-object-accessors.js": 4486, + "3070-collection-views.ts": 3364, + "3071-checked-specialization-narrowing.js": 3480, + "3071-map-contextual-record-seeds.ts": 3949, + "3072-optional-union-receivers.ts": 5187, + "3072-weak-process-streams.js": 3596, + "3073-optional-callable-module.js": 2912, + "3073-union-keyed-reads.ts": 4086, + "3074-discriminated-intersections.ts": 3579, + "3075-recursive-contextual-literals.ts": 4053, + "3076-nullish-record-defaults.ts": 4277, + "3077-optional-union-array-reads.ts": 3951, + "3078-forward-mutable-captures.ts": 4480, + "3079-forward-mutable-tdz.ts": 4237, + "3080-exhaustive-never-bindings.ts": 3901, + "3081-recursive-array-destinations.ts": 4116, + "3082-forward-write-unwind.ts": 3019, + "3083-set-string-seed.ts": 4323, + "3084-record-own-membership.ts": 4601, + "3085-conditional-record-construction.ts": 4376, + "3086-error-constructor-options.ts": 4642, + "3087-error-options-lifetime.ts": 3925, + "3088-array-wide-callbacks.ts": 4497, + "3089-array-find-narrowing.ts": 4974, + "3090-json-discriminated-records.ts": 4438, + "3091-json-recursive-discriminants.ts": 5400, + "3092-number-union-predicates.ts": 3730, + "3093-error-option-data.ts": 2865, + "3094-json-replacer-traversal.ts": 4373, + "3095-json-reviver-traversal.ts": 4032, + "3096-json-callback-nonfinite.ts": 3197, + "3097-json-callback-reentrancy.ts": 3379, + "3098-json-callback-space.ts": 3597, + "3099-json-callback-functions.ts": 4206, + "3100-json-callback-holder.js": 4588, + "3101-json-callback-bytes.ts": 4349, + "3102-json-unknown-fields.ts": 3200, + "3103-scalar-record-nested-control-flow.ts": 4214, + "3104-native-analysis-tables-loops.ts": 2623, + "3105-nullable-collections.ts": 4138, + "3106-mixed-insertion-spreads.ts": 3715, + "3107-nullable-collection-lifetimes.ts": 4148, + "3108-for-await-sync-arrays.js": 6166, + "3108-identity-map-keys.ts": 4867, + "3109-destructuring-unknown-defaults.js": 4021, + "3109-identity-union-collections.ts": 5097, + "3110-builtin-error-families.ts": 3559, + "3110-identity-collection-mutation.ts": 3934, + "3111-identity-collection-cycles.ts": 3848, + "3112-union-switch-control-flow.ts": 3478, + "3113-union-array-removal.ts": 4714, + "3114-union-record-field-write.ts": 3240, + "3115-zlib-static-compression-levels.ts": 4206, + "3116-sequence-expression-lifetimes.ts": 4356, + "3117-unknown-switch-control-flow.ts": 3816, + "3118-recursive-union-spread.ts": 4372, + "3119-runtime-optional-spread.ts": 4825, + "3120-dynamic-property-descriptors.js": 3522, + "3120-generator-method-loop-bindings.ts": 4192, + "3120-ir-nonfinite-numbers.ts": 4200, + "3121-compound-assignment-values.js": 3684, + "3121-dynamic-accessor-descriptors.js": 4951, + "3122-dynamic-delete-descriptors.cjs": 4063, + "3122-dynamic-delete-descriptors.js": 3650, + "3122-js-async-class-methods.js": 4320, + "3123-destructured-global-tdz.js": 3748, + "3124-array-prototype-borrow.js": 4580, + "3125-regex-global-match.ts": 2619, + "3126-js-number-radix/main.ts": 4047, + "3127-spawn-runtime-options.ts": 4379, + "3128-regex-last-index.ts": 4682, + "3129-js-token-union/main.ts": 4567, + "3130-child-generic-streams.ts": 4888, + "3131-child-rest-listeners.js": 2682, + "3132-child-generic-stream-completion.ts": 9689, + "3133-text-decoder-streaming.ts": 21112, + "3134-fs-vectors.ts": 3917, + "3135-fs-file-handle-controls.ts": 5050, + "3136-fs-file-handle-options.ts": 5320, + "3137-fs-vector-callbacks.cjs": 5540, + "3138-array-large-storage.ts": 6344, + "3139-regex-native-deep-alternatives.ts": 31598, + "3140-array-dense-access.ts": 6506, + "3141-map-string-key-hash.ts": 4564, + "3150-integer-remainder.ts": 4200, + "3151-array-push-writes.ts": 4614, + "3170-number-conversion-fast-paths.ts": 4996, + "3171-method-census-data-writes.ts": 4891, + "400-fib.ts": 4470, + "401-mutual-recursion.ts": 3443, + "402-string-functions.ts": 3554, + "403-void-and-params.ts": 2749, + "4030-runtime-object-truthiness.ts": 2971, + "4031-event-emitter-long-tuples.ts": 3712, + "4032-inline-rc-fast-paths.ts": 6866, + "404-rc-stress.ts": 3585, + "4040-adapted-function-identity.ts": 3063, + "4041-adapted-function-properties.ts": 4165, + "4042-object-is-union-subclass.ts": 3846, + "4043-nonnull-missing-element.ts": 3736, + "4044-adapted-callback-modules/main.ts": 4121, + "4045-optional-element-projections.ts": 4159, + "4046-captured-parameter-values.ts": 3178, + "4047-captured-parameter-rebinding.ts": 4100, + "405-params-optional.ts": 3985, + "406-params-defaults.ts": 3592, + "4060-cycle-shared-instance/main.ts": 3695, + "4060-stack-union-reads.ts": 5003, + "4061-cycle-evaluation-order/main.ts": 3985, + "4061-nullable-ref-fields.ts": 7201, + "4062-cycle-tdz-read/main.ts": 3866, + "4063-cycle-hoisted-calls/main.ts": 3773, + "4064-cycle-classes-after-init/main.ts": 3673, + "4065-cycle-init-throws/main.ts": 3693, + "4066-cycle-top-level-tdz/main.ts": 3937, + "4067-dynamic-import-cycle-tdz/main.ts": 4237, + "407-params-rest.ts": 4125, + "408-params-ctors-inheritance.ts": 3692, + "4080-optional-read-presence-tests.ts": 3038, + "4080-string-index-bounds.ts": 3844, + "4081-hoisted-function-tdz.ts": 3262, + "4081-string-index-unproven.ts": 4125, + "4082-hoisted-function-tdz-uncaught.ts": 2818, + "4083-dynamic-import-failure/main.ts": 3160, + "4084-record-optional-field-copies.ts": 5146, + "4085-generator-loop-close.ts": 4113, + "4086-optional-capture-defaults.ts": 3684, + "4088-class-static-tdz.ts": 3477, + "4089-class-wrapper-tdz.js": 2771, + "409-params-async-generics.ts": 4030, + "4090-computed-key-tdz.js": 3578, + "4091-class-evaluation-deferred.ts": 4329, + "410-params-rc-stress.ts": 4376, + "4140-array-callback-split-visit.ts": 7471, + "4141-array-callback-ambient-receiver/main.ts": 3909, + "4142-array-callback-factories.ts": 3472, + "4143-union-refinement-intersection-arms.ts": 3347, + "420-dead-strip-modules/main.ts": 3742, + "4290-acyclic-immutable-trees.ts": 4974, + "4291-acyclic-constructor-escapes.ts": 4252, + "4292-acyclic-readonly-bypass.ts": 4530, + "4293-acyclic-dynamic-writes.ts": 4090, + "4294-console-line-sizes.ts": 7343, + "4295-console-template-parts.ts": 3542, + "4296-console-glued-long-parts.ts": 5181, + "4410-inline-object-allocation.ts": 4560, + "4411-nullable-ref-values.ts": 6207, + "4412-nullable-record-values.ts": 5237, + "4413-cycle-old-late-death.ts": 5172, + "4413-nullable-null-and-undefined.ts": 4068, + "4414-cycle-old-weak-keys.ts": 4401, + "4414-nullable-string-values.ts": 7453, + "4415-identity-borrowed-operands.ts": 3629, + "4415-nullable-container-values.ts": 5776, + "4431-exception-pending-contexts.ts": 4992, + "4432-checked-downcast-failures.ts": 4520, + "4451-int32-field-storage.ts": 4554, + "4452-int32-field-dynamic-writes.ts": 4987, + "4453-int32-closed-locals.ts": 4516, + "4454-int32-field-reflection-reads.ts": 3494, + "4455-live-view-reads-without-stores.ts": 4871, + "4471-unassigned-field-reads.ts": 3438, + "4476-string-ascii-header-fact.ts": 4141, + "4477-buffer-write-ascii-runs.ts": 4249, + "4478-string-large-index-reads.ts": 4215, + "4481-array-optional-push.ts": 3757, + "4482-instanceof-static-class-optional.ts": 3228, + "4483-array-splice-statement.ts": 4359, + "4484-array-length-reset.ts": 3824, + "4485-array-splice-optional-item.ts": 4447, + "4486-array-field-element-borrows.ts": 4170, + "4487-array-literal-optional-values.ts": 3597, + "4520-optional-guard-narrowing.ts": 4519, + "4521-array-callback-element-presence.ts": 6504, + "4522-captured-parameter-lifetimes.ts": 3501, + "4523-array-pop-removal.ts": 4310, + "4524-array-callback-supertype-params.ts": 4795, + "4541-nullable-wrap-borrows.ts": 4219, + "4542-preserving-call-borrows.ts": 4390, + "4543-walk-borrowed-locals.ts": 4424, + "4544-checked-cast-aliases.ts": 4017, + "4545-borrowed-returns.ts": 5010, + "4546-rebound-borrowed-params.ts": 4038, + "4560-field-store-borrowed-receivers.ts": 4292, + "4561-virtual-borrowed-parameters.ts": 3188, + "4562-string-self-concat-move.ts": 3258, + "4563-array-read-projection-receivers.ts": 4193, + "4564-literal-choice-strings.ts": 4376, + "4568-for-of-hole-elements.ts": 4040, + "4569-shorthand-missing-elements.ts": 5489, + "4570-map-lookup-default-record.ts": 4379, + "4591-weak-keys-union-records.ts": 3376, + "4592-cycle-const-from-import/main.ts": 3235, + "4593-weak-keys-empty-objects.ts": 4125, + "4594-union-class-field-mixed-types.ts": 6642, + "4595-union-class-field-optional-receiver.ts": 3958, + "4596-error-subclass-int-fields.ts": 3809, + "4596-tdz-nullable-forward-capture.ts": 5432, + "4597-callback-field-method-name.ts": 4265, + "4598-live-array-ref-element-access.ts": 9798, + "4598-map-set-clear-reuse.ts": 5772, + "4598-os-available-parallelism.ts": 3841, + "4598-publish-constructor-stores.ts": 4373, + "4599-construction-only-field-borrows.ts": 4185, + "4600-construction-only-fields-dynamic-writes.ts": 3870, + "4601-cycle-holders.ts": 3436, + "4602-constructor-escape-order.ts": 4107, + "4610-array-ext-dyn-refresh.ts": 4200, + "4620-optional-chain-borrowed-receivers.ts": 3633, + "4621-edge-preserving-callees/main.ts": 4459, + "4630-contract-int32.ts": 3693, + "4630-reference-write-call-arguments.ts": 3005, + "4631-contract-nothrow.ts": 3977, + "4631-reference-write-aliases.ts": 4000, + "4632-contract-immutable.ts": 4206, + "4632-reference-write-loops-and-walks.ts": 3622, + "4633-contract-program-lifetime.ts": 4928, + "4633-reference-write-callbacks-returns.ts": 4472, + "4634-contract-program-lifetime-publish.ts": 58146, + "4634-optional-element-borrowed-returns.ts": 3892, + "4640-bitwise-identity-ranges.ts": 3488, + "4641-nothrow-try-catch.ts": 3941, + "4642-acyclic-contract.ts": 4488, + "4650-stale-property-narrowing.ts": 5393, + "4651-map-string-get-entry.ts": 4850, + "4652-stale-closure-binding-narrowing.ts": 4646, + "4660-typed-rest-spread-forwarding.ts": 3964, + "4671-scoped-array-read-borrows.ts": 4659, + "4690-cycle-bypass-long-lived.ts": 6611, + "4691-publish-constructor-self-alias.ts": 32098, + "4692-borrowed-result-mutating-callees.ts": 4121, + "4720-cold-throw-paths.ts": 3462, + "4721-integer-induction-numbers.ts": 4315, + "4730-touint32-large-magnitudes.ts": 4331, + "4731-string-mixed-ascii-slices.ts": 4955, + "4732-buffer-write-utf16le-runs.ts": 4567, + "4733-typed-array-read-compares.ts": 4680, + "4750-map-string-absent-filter.ts": 3842, + "4751-string-search-multibyte-needles.ts": 4006, + "4795-published-untyped-member-reads.ts": 3280, + "4806-uint32-loop-accumulators.ts": 4561, + "4810-union-identity-retags.ts": 35042, + "4811-union-retag-checked-cast.ts": 4067, + "4812-int32-wrapping-sums.ts": 5061, + "4813-typed-array-read-equality-bindings.ts": 6041, + "4820-sort-comparator-lent-arguments.ts": 5459, + "4821-callback-lent-arguments.ts": 5557, + "4822-stable-capture-reads.ts": 4662, + "4823-array-store-receivers.ts": 3343, + "4830-typed-teardown-chains.ts": 4609, + "4831-cycle-bypass-probe-windows.ts": 6532, + "4840-array-hole-free-prefix.ts": 5275, + "4860-int32-local-storage.ts": 4493, + "4870-array-pipeline-fusion.ts": 9864, + "4871-array-pipeline-analytics.ts": 5983, + "4872-array-pipeline-temporaries.ts": 10585, + "4875-stream-consumers-array-buffer.ts": 5489, + "4876-array-buffer-inspect.ts": 4454, + "4880-await-call-turn-order.ts": 4340, + "4881-await-call-exceptions.ts": 3334, + "4882-await-call-request-chain.ts": 4240, + "4883-await-values-and-hops.ts": 3792, + "4884-await-call-unhandled-sibling.ts": 4171, + "4885-await-call-checkpoints.ts": 4125, + "4890-regex-literal-sites.ts": 3489, + "4891-regex-plain-text-patterns.ts": 3217, + "4892-regex-exec-read-captures.ts": 3864, + "4900-bigint-division-cross-check.ts": 4497, + "4900-console-instances.ts": 4959, + "4901-bigint-radix-text.ts": 4256, + "4901-console-instance-errors.ts": 2879, + "4902-bigint-compound-assign.ts": 3676, + "4903-bigint-fixed-width.ts": 3178, + "4904-bigint-large-products.ts": 3926, + "4905-bigint-fixed-width-fusion.ts": 5267, + "4906-bigint-small-value-trees.ts": 5678, + "4907-bigint-random-trees.ts": 7288, + "4910-hole-free-prefix-upkeep.ts": 4535, + "4920-string-literal-switch.ts": 4267, + "4930-typed-record-reflection.ts": 5782, + "4931-typed-data-generic-walks.ts": 6195, + "4932-typed-json-unknown.ts": 4431, + "4940-async-return-adoption.ts": 4857, + "4941-async-return-adoption-try.ts": 4613, + "4942-async-return-adoption-values.ts": 6005, + "4943-async-return-adoption-interleaved.ts": 3930, + "4950-regex-compiled-captures.ts": 5282, + "4951-regex-compiled-last-index.ts": 4338, + "4952-regex-compiled-anchors-classes.ts": 4435, + "4953-regex-compiled-backtracking.ts": 5380, + "4954-regex-log-report.ts": 5479, + "4955-regex-compiled-differential.ts": 20554, + "4970-int32-call-abi.ts": 4776, + "4980-generator-early-exit-finally.ts": 4365, + "4981-generator-delegation-iterables.ts": 5570, + "4982-generator-sent-values.ts": 4472, + "4983-generator-reuse-iterables.ts": 2952, + "4984-generator-exceptions.ts": 4164, + "4985-generator-lazy-pipeline.ts": 5491, + "4986-generator-default-channels.ts": 3496, + "4987-generator-closures-methods.ts": 4260, + "4988-generator-async-interplay.ts": 4288, + "4989-generator-step-drives.ts": 4530, + "4990-lone-surrogates.ts": 4508, + "4991-lone-surrogate-halves.ts": 4237, + "4992-lone-surrogate-search.ts": 4305, + "4993-lone-surrogate-json.ts": 4527, + "4994-lone-surrogate-uri.ts": 4891, + "4995-lone-surrogate-regex.ts": 4198, + "4996-lone-surrogate-stdio.ts": 3963, + "4997-lone-surrogate-stdout-patch.js": 4611, + "4998-lone-surrogate-buffers.ts": 4299, + "4999-lone-surrogate-io.ts": 4020, + "500-array-basics.ts": 4115, + "501-array-push-pop.ts": 4134, + "5010-lifetime-region-generations.ts": 4583, + "5011-lifetime-region-outside-references.ts": 3728, + "5012-lifetime-region-shapes.ts": 4853, + "5013-lifetime-region-async.ts": 5075, + "5014-lifetime-region-nesting.ts": 4021, + "5015-lifetime-region-publish.ts": 41998, + "5016-lifetime-region-weak-keys.ts": 3440, + "5017-lifetime-region-cycles.ts": 3981, + "5018-lifetime-region-hierarchies.ts": 5065, + "5019-lifetime-region-values.ts": 4354, + "502-array-for-of.ts": 3573, + "5020-lifetime-region-large.ts": 6813, + "5021-lifetime-region-opaque-holders.ts": 3165, + "5022-lifetime-region-workers.ts": 44993, + "5023-lifetime-region-await.ts": 5986, + "5024-lifetime-region-await-forms.ts": 5123, + "503-array-functions.ts": 4084, + "504-array-rc-stress.ts": 3791, + "5040-promise-all-race-turns.ts": 5348, + "5041-promise-all-turns.js": 4084, + "5042-async-generator-return-promise.ts": 4929, + "5043-async-generator-return-forms.ts": 3900, + "5045-generator-delegation-typed-next.ts": 4863, + "5046-generator-delegation-return-throw.ts": 4938, + "5047-generator-delegation-iterables-typed.ts": 4203, + "5048-generator-delegation-async.ts": 4733, + "5049-generator-delegation-edges.ts": 5078, + "5050-inherited-in-membership.ts": 4630, + "5051-typed-record-prototypes.ts": 4286, + "5052-class-for-in-unknown.ts": 4449, + "5053-json-replacer-arrays.ts": 4672, + "5054-required-field-delete.ts": 3969, + "5055-object-create-record-literal.ts": 3612, + "5056-generator-in-unknown.ts": 3544, + "5060-child-fork-exit-after-messages/main.ts": 3945, + "5061-child-fork-local-disconnect/main.ts": 3909, + "5062-child-fork-message-before-eof/main.ts": 4129, + "5065-global-increment-initializers.ts": 3769, + "5066-global-compound-updates.ts": 4136, + "5067-global-updates-in-functions.ts": 3795, + "5068-global-var-let-const-updates.ts": 3571, + "5069-global-update-modules/main.ts": 3587, + "5070-optional-read-bindings.ts": 3671, + "5071-optional-chain-scalars.ts": 4013, + "5072-optional-nullish-ternary.ts": 4374, + "5073-optional-results.ts": 4392, + "5074-optional-pop-shift.ts": 3500, + "5075-optional-reads-unchecked/main.ts": 3399, + "5076-optional-escapes.ts": 4284, + "5080-boolean-array-storage.ts": 10145, + "5081-boolean-array-dynamic-views.ts": 5152, + "5085-promise-allsettled-turns.ts": 5572, + "5086-promise-any-turns.ts": 4418, + "5087-promise-allsettled-any-dyn.js": 6479, + "5088-promise-combinator-thenables.js": 3786, + "5089-promise-settled-records.ts": 4701, + "5090-generator-finished-result-value.ts": 4294, + "5091-generator-delegation-arrays-strings.ts": 4885, + "5092-generator-delegation-sets-maps.ts": 3752, + "5095-console-count-group.ts": 4618, + "5096-console-assert-dir.ts": 5191, + "5097-console-table.ts": 3751, + "5098-console-time-warnings.ts": 9353, + "5099-console-methods-dynamic.cjs": 4255, + "510-array-map-filter-foreach.ts": 4191, + "511-array-indexof-includes.ts": 3499, + "5110-var-hoisting-early-reads.ts": 3823, + "5111-var-hoisting-closures-classes.ts": 4157, + "5112-var-hoisting-function-scope.ts": 3874, + "5113-tdz-let-const-class.ts": 3814, + "5114-var-hoisting-js.js": 3818, + "5115-regex-replace-compiled.ts": 3620, + "5116-regex-capture-reads.ts": 2873, + "5117-regex-log-report-spans.ts": 4784, + "5118-regex-replace-char-chains.ts": 3663, + "512-array-join-chains.ts": 2771, + "5120-frame-records.ts": 3939, + "5121-frame-objects.ts": 2783, + "5122-frame-result-slots.ts": 3197, + "5123-frame-reassigned.ts": 3173, + "5124-frame-objects-control.ts": 3904, + "5125-frame-mutating-methods.ts": 3799, + "5126-frame-returned-params.ts": 3813, + "5127-frame-loop-bindings.ts": 4488, + "513-array-methods-rc-stress.ts": 3775, + "5130-event-target-dispatch.js": 6111, + "5131-event-target-listener-errors.js": 4456, + "5132-event-target-typed.ts": 7166, + "5133-abort-signal-event-target.ts": 23098, + "5134-events-error-monitor.ts": 4010, + "5135-event-target-warnings.js": 31124, + "5136-events-error-monitor-string-name.ts": 4008, + "5137-events-error-monitor-computed-name.js": 4468, + "5138-events-error-monitor-shared-listener.ts": 3052, + "5139-event-target-listener-throw.ts": 4657, + "514-array-find-some-every.ts": 3980, + "5140-os-host-facts.ts": 3296, + "5141-os-constants.ts": 4894, + "5142-path-parse-format.ts": 4155, + "5145-path-matches-glob-basics.ts": 3777, + "5146-path-matches-glob-braces.ts": 5827, + "5147-path-matches-glob-extglob.ts": 4341, + "5148-path-matches-glob-classes.ts": 2888, + "5149-path-matches-glob-win32.ts": 4096, + "515-array-flatmap.ts": 4332, + "5150-small-array-inline-slots.ts": 3637, + "5155-cycle-header-types.ts": 43009, + "516-array-reduce.ts": 4514, + "5160-field-callback-receiver.ts": 4758, + "5161-field-callback-receiver-escape.ts": 3837, + "5162-field-method-reference-receiver.ts": 4262, + "5163-field-callback-receiver.cjs": 5679, + "5164-worker-field-callback-receiver.ts": 38682, + "5165-interface-method-dispatch.ts": 4568, + "5166-interface-method-values.ts": 5588, + "5167-interface-method-order.ts": 5287, + "5168-interface-method-hierarchy.ts": 6196, + "5169-interface-method-chains.ts": 4139, + "517-array-hof-index-args.ts": 4188, + "5170-string-scan-eviction.ts": 3592, + "5171-lone-surrogate-replace-seams.ts": 3960, + "5175-forin-typed-as-unknown.ts": 4247, + "5176-object-keys-typed-as-unknown.ts": 3688, + "5177-forin-dynamic-objects.ts": 4079, + "5178-forin-keys-js.js": 3608, + "5179-forin-typed-shapes.ts": 4197, + "518-array-sort.ts": 4624, + "518-promise-void-union-callbacks.ts": 4051, + "5180-interface-arrays.ts": 3814, + "5181-interface-array-methods.ts": 11031, + "5182-interface-array-boundaries.ts": 3743, + "5183-interface-property-reads.ts": 4427, + "5184-interface-property-objects.ts": 3309, + "5185-util-inherits.cjs": 5164, + "5186-util-inherits.ts": 4876, + "5187-util-deprecate.cjs": 4074, + "5188-util-deprecate-flags.cjs": 4955, + "5189-util-deprecate.ts": 5071, + "519-array-from-length.ts": 4423, + "519-promise-union-await-values.ts": 3904, + "5190-util-callbackify.cjs": 7841, + "5191-util-callbackify.ts": 6629, + "5192-util-debuglog.cjs": 6718, + "5193-util-debuglog.ts": 4689, + "5194-process-emit-warning-tick.cjs": 3727, + "5195-object-entries-typed-as-unknown.ts": 5084, + "5196-object-values-typed-as-unknown.ts": 3864, + "5197-object-entries-dynamic-objects.ts": 4955, + "5198-object-entries-values-js.js": 4220, + "5199-object-entries-identity.ts": 3999, + "520-map-basics.ts": 3545, + "5200-array-sort-keyed-numbers.ts": 5325, + "5201-array-sort-keyed-records.ts": 7394, + "5202-array-sort-keyed-lookalikes.ts": 5009, + "5203-typed-array-sort-default.ts": 8431, + "5204-typed-array-sort-comparators.ts": 5119, + "521-map-number-keys.ts": 3823, + "5210-process-memory-usage.ts": 4235, + "5211-process-identity.ts": 3797, + "5212-perf-marks-measures.ts": 5056, + "5213-perf-timing-errors.ts": 4031, + "5214-perf-entry-inspect.ts": 4809, + "5215-perf-timing-validation.js": 6147, + "5216-perf-spread-arguments.js": 4892, + "5217-perf-spread-typed.ts": 4228, + "522-map-foreach.ts": 4028, + "5220-url-parse-warnings.ts": 3469, + "5221-url-parse-shapes.ts": 6083, + "5222-url-parse-hosts.ts": 5268, + "5223-url-parse-query.ts": 5608, + "5224-url-format-objects.ts": 5477, + "5225-url-format-whatwg.ts": 3013, + "5226-url-resolve.ts": 6250, + "5227-url-legacy-dependency/main.ts": 6988, + "5228-url-domain-conversion.ts": 4749, + "5229-url-legacy-errors.cjs": 6936, + "523-map-ref-values.ts": 4339, + "5230-inline-union-fields.ts": 4733, + "5231-inline-union-field-kinds.ts": 5128, + "5232-inline-union-fields-dynamic.ts": 4858, + "5233-inline-union-fields-shapes.ts": 3241, + "5234-inline-union-class-fields.ts": 3221, + "5235-inline-union-fields-publish.ts": 3389, + "5236-inline-union-unchecked-reads.ts": 4250, + "5237-inline-union-frame-objects.ts": 4148, + "524-map-cycles.ts": 2484, + "5240-event-target-subclass.ts": 7308, + "5241-event-subclass.ts": 5029, + "5242-event-target-subclass-hierarchy.ts": 4680, + "5243-event-target-subclass-dynamic.ts": 6118, + "5244-event-target-subclass.js": 4898, + "5245-event-target-subclass-signals.ts": 8806, + "5246-event-target-subclass-listener-throw.ts": 3474, + "5247-event-target-subclass-lifetimes.ts": 5918, + "5248-event-target-subclass-warnings.ts": 4004, + "5249-event-target-subclass-unconstructed.ts": 3498, + "525-map-rc-stress.ts": 3774, + "5250-cipher-aes-gcm.ts": 8740, + "5251-cipher-aes-cbc.ts": 42449, + "5252-cipher-aes-ctr.ts": 26060, + "5253-cipher-chacha20-poly1305.ts": 8385, + "5254-cipher-errors.js": 28463, + "5255-cipher-encodings.cjs": 25164, + "5256-cipher-deprecation.cjs": 45696, + "5257-cipher-vectors.ts": 7138, + "5258-cipher-handles.js": 24870, + "5259-cipher-chunked.ts": 7052, + "526-set-basics.ts": 3756, + "5260-record-unknown-identity.ts": 4341, + "5261-optional-field-identity.ts": 3817, + "5262-record-unknown-many.ts": 3331, + "527-set-foreach.ts": 4129, + "5270-url-parse-no-deprecation.cjs": 5103, + "5271-url-parse-throw-deprecation.cjs": 6761, + "5275-http-end-callback-order.ts": 4622, + "5276-style-text-hex-colors.cjs": 4317, + "5277-fs-read-file-buffer-option.cjs": 3338, + "5278-host-null-bytes.cjs": 64320, + "5279-search-params-null-init.cjs": 2979, + "528-set-seeded.ts": 2529, + "5285-blob-construct.ts": 4818, + "5286-blob-readers.ts": 3080, + "5287-blob-inspect.ts": 3383, + "5288-blob-validation.js": 4818, + "5289-blob-read-order.ts": 4674, + "529-map-seeded.ts": 3233, + "5290-blob-dynamic.js": 4041, + "5295-readline-promises-question.ts": 3501, + "5296-readline-lines.ts": 5784, + "5297-readline-callback-events.cjs": 3985, + "5298-readline-abort.cjs": 10936, + "5299-readline-stdin.ts": 5881, + "530-record-arrays.ts": 4186, + "5300-stream-add-abort-signal.ts": 25846, + "5301-stream-add-abort-signal-errors.cjs": 8721, + "5302-stream-promises-options.ts": 13350, + "5303-stream-finished-callback-opts.cjs": 5087, + "5305-json-stringify-typed-arrays.ts": 6867, + "531-record-array-hofs.ts": 4059, + "5315-map-iterate-after-front-deletes.ts": 3530, + "5316-map-lru-eviction.ts": 3884, + "5317-map-iterate-while-mutating.ts": 4053, + "5318-set-iterate-after-deletes.ts": 4066, + "5319-map-iteration-values.ts": 4477, + "532-record-arrays-rc-stress.ts": 3905, + "5320-map-iterate-across-compaction.ts": 4243, + "5321-integer-closed-arithmetic-loops.ts": 3710, + "5321-json-parse-cycles.ts": 3383, + "5322-integer-variable-step-loops.ts": 3384, + "5322-json-stringify-parsed-trees.ts": 3838, + "5323-dyn-numeric-index-reads.ts": 3192, + "5323-int32-nonnegative-slot-facts.ts": 3774, + "5324-int32-initializer-locals.ts": 2663, + "5324-structured-clone-documents.ts": 4264, + "5325-record-boundary-returns.ts": 4089, + "5326-record-boundary-params.ts": 3793, + "5327-record-boundary-identity.ts": 3464, + "5328-record-boundary-open.ts": 4146, + "5329-record-boundary-recursion.ts": 3748, + "533-array-element-cycles.ts": 3379, + "5330-record-boundary-fields.ts": 2753, + "5331-record-boundary-methods.ts": 3914, + "5332-int32-narrowed-slot-facts.ts": 3588, + "5333-integer-guarded-while-counters.ts": 3896, + "5333-record-boundary-nullable-argument.ts": 3869, + "5334-fetch-mentions-without-calls.ts": 3676, + "5335-stream-operators-values.ts": 6378, + "5335-top-level-await-pending-exit-code.ts": 3377, + "5336-stream-operators-promise-timing.ts": 4282, + "5336-top-level-await-pending-exit-listener.ts": 3680, + "5337-dc-subscriber-throw-next-tick.cjs": 3947, + "5337-stream-operators-concurrency.ts": 8858, + "5338-js-tdz-function-element.js": 3181, + "5338-stream-operators-checks.cjs": 5284, + "5339-array-buffer-constructor.js": 4102, + "5339-stream-for-await-hops.ts": 5704, + "534-record-width-subtyping.ts": 3856, + "5340-readable-from-options.ts": 4031, + "5341-compound-any-operand.ts": 3905, + "5341-generator-callback-params.ts": 4564, + "5341-integer-recursion-boundaries.ts": 4375, + "5341-readable-from-dynamic.cjs": 5807, + "5342-compound-any-operand.cjs": 3381, + "5342-generator-scalar-channels.ts": 3638, + "5342-integer-recursion-stack-and-mutual.ts": 3513, + "5342-stream-compose.ts": 7562, + "5343-any-flow-compound.ts": 2631, + "5343-integer-recursion-array-index.ts": 4214, + "5343-iterator-result-records.ts": 2610, + "5343-stream-compose-edges.ts": 10326, + "5344-json-parse-undeclared-keys.ts": 5823, + "5344-stream-pipeline-timing.ts": 4092, + "5345-object-values-for-of-unknown.ts": 4269, + "5346-object-values-length-dynamic.ts": 3585, + "5347-object-values-loop-control.ts": 4004, + "5348-object-values-for-of-js.js": 4048, + "5349-object-entries-values-optional.ts": 3374, + "535-object-statics.ts": 4653, + "5350-object-values-wide-record.ts": 3999, + "5351-object-values-methods-index.ts": 3750, + "5352-final-field-loop-reads.ts": 4118, + "5353-captured-const-values.ts": 3887, + "5354-captured-const-tdz.ts": 3465, + "5355-captured-function-declarations.ts": 4030, + "5356-captured-tdz-ordering.ts": 4031, + "5357-init-only-module-bindings.ts": 3717, + "536-map-seed-array.ts": 3444, + "5360-array-sort-comparator-calls.ts": 3618, + "5361-array-sort-inconsistent-comparators.ts": 4844, + "5362-array-sort-comparator-throws.ts": 4204, + "5363-array-sort-comparator-writes.ts": 13543, + "5364-array-sort-union-and-boolean-elements.ts": 21219, + "5365-array-sort-dynamic-values.js": 3456, + "537-map-iter-drains.ts": 3991, + "5370-zlib-stream-roundtrip.ts": 4546, + "5371-zlib-stream-chunking.cjs": 6071, + "5372-zlib-stream-errors.cjs": 6496, + "5373-zlib-stream-options.cjs": 6767, + "5374-zlib-stream-flush-params.ts": 5113, + "5375-zlib-stream-promises.ts": 6129, + "5376-zlib-constants.cjs": 3393, + "5377-zlib-stream-backpressure.cjs": 4992, + "5378-zlib-sync-options.cjs": 4120, + "5379-zlib-callback-options.ts": 4524, + "538-map-set-for-of.ts": 3571, + "5390-interface-loop-element-lifetimes.ts": 3555, + "5391-interface-dispatch-hierarchies.ts": 5018, + "5392-interface-receiver-bindings.ts": 2922, + "5393-interface-array-holes.ts": 4247, + "5394-interface-implementers.ts": 4974, + "5395-locale-compare-ascii.ts": 3055, + "5396-locale-compare-latin.ts": 5056, + "5397-locale-compare-sorting.ts": 5049, + "540-tuples-basics.ts": 4222, + "5400-json-parse-typed-keys.ts": 5019, + "5405-buffer-arraybuffer-window.ts": 3206, + "5406-util-types-keys.ts": 3783, + "5407-http-header-field-limit.ts": 3678, + "5408-http-unread-request-end.ts": 4813, + "5409-http-held-request-body.ts": 4829, + "541-ref-array-json.ts": 4046, + "5410-inspect-class-values.ts": 3502, + "5411-inspect-class-hierarchies.ts": 3285, + "5412-inspect-dynamic-collections.ts": 3541, + "5413-inspect-class-values.cjs": 4035, + "5415-zlib-dynamic-inputs.cjs": 4054, + "542-union-element-arrays.ts": 4182, + "5420-readable-from-generators.ts": 5843, + "5421-readable-from-generators.cjs": 3752, + "5422-stream-async-push-wakeup.ts": 3679, + "5423-stream-end-and-eos-timing.ts": 4453, + "5424-stream-write-buffer-encoding.ts": 3204, + "5425-pipeline-generator-stage.ts": 3700, + "5426-pipeline-generator-errors.ts": 4597, + "5427-pipeline-generator-backpressure.ts": 4175, + "5428-pipeline-generator-stages.cjs": 9702, + "5429-pipeline-generator-buffers.ts": 6406, + "5430-dyn-number-string-boxes.ts": 3692, + "5435-crypto-hash-algorithms.ts": 4078, + "5436-crypto-hash-names.js": 3345, + "5437-crypto-hmac-algorithms.ts": 3794, + "5438-crypto-kdf-digests.ts": 3694, + "5439-crypto-kdf-callbacks.js": 3780, + "5440-crypto-hash-encodings.js": 4405, + "5441-crypto-hash-output-length.ts": 4754, + "5442-crypto-random-values.ts": 5239, + "5443-crypto-random-arguments.js": 4330, + "5444-crypto-timing-safe-equal.js": 2751, + "5445-crypto-secret-key-objects.ts": 4863, + "5445-stream-buffer-chunks.ts": 4296, + "5446-crypto-secret-key-errors.js": 4577, + "5446-stream-buffer-chunks-dynamic.js": 8609, + "5447-fs-readdir-order.ts": 3829, + "5448-fs-glob-patterns.ts": 3945, + "5449-fs-glob-options.ts": 5046, + "5450-fs-glob-validation.js": 3911, + "5451-fs-promises-glob.ts": 5150, + "5452-fs-glob-callback.ts": 4895, + "5455-power-of-two-remainder.ts": 3875, + "5456-remainder-large-integer-dividends.ts": 3066, + "5457-lcg-modulus.ts": 3468, + "5458-fractional-remainder.ts": 3767, + "5460-locale-compare-contractions.ts": 3556, + "5461-locale-compare-equivalents.ts": 4015, + "5462-locale-compare-probe-sweep.ts": 5293, + "5463-locale-compare-combining-letters.ts": 4084, + "5464-locale-compare-fractions.ts": 3409, + "5465-dyn-index-read-arrays.ts": 3798, + "5466-dyn-index-read-special.ts": 4384, + "5470-strip-vt-osc.cjs": 3494, + "5471-strip-vt-csi.cjs": 3770, + "5472-strip-vt-random.cjs": 3968, + "5473-strip-vt-unterminated.cjs": 4621, + "5474-strip-vt-surrogates.ts": 3567, + "5510-assert-error-properties.js": 5349, + "5511-assert-error-class.ts": 4329, + "5512-assert-error-options.js": 3469, + "5513-assert-deep-equal-loose.js": 4138, + "5514-assert-deep-equal-typed.ts": 3792, + "5515-assert-deep-strict-typed-messages.ts": 3601, + "5516-assert-deep-equal-edges.js": 4686, + "5517-assert-partial-deep-equal.js": 2953, + "5518-assert-partial-deep-typed.ts": 4685, + "5519-assert-partial-deep-typed-edges.ts": 4346, + "5520-assert-record-literal-failures.ts": 4088, + "5520-dyn-property-read-receivers.ts": 4057, + "5521-assert-empty-message.js": 3734, + "5521-dyn-property-read-keys.ts": 3703, + "5522-assert-throws-failure-properties.ts": 4256, + "5523-assert-class-options.js": 4903, + "5524-assert-class-skip-prototype.ts": 4411, + "5530-buffer-pool.ts": 4413, + "5531-buffer-pool-first-slab.ts": 5557, + "5532-buffer-pool-first-slab-untyped.mjs": 220640, + "5540-fs-callback-forms.cjs": 4591, + "5541-fs-stats-fields.cjs": 6904, + "5542-fs-promises-parity.cjs": 4673, + "5543-fs-promises-typed.ts": 5025, + "5544-fs-opendir.cjs": 4673, + "5545-fs-options-encoding.cjs": 5256, + "5546-fs-read-stream.cjs": 5295, + "5547-fs-write-stream.cjs": 6075, + "5548-fs-callback-arguments.cjs": 4982, + "5549-fs-callback-option-checks.cjs": 4309, + "5550-locale-compare-ascii-words.ts": 3614, + "5551-array-search-references.ts": 4656, + "5552-string-appends.ts": 3810, + "5553-string-append-char-code.ts": 4013, + "5554-fs-streams-typed.ts": 4873, + "5555-fs-callback-turns.cjs": 3708, + "5555-http-server-close-keep-alive.ts": 15618, + "5556-http-client-agent-connection.ts": 4177, + "5557-http-outgoing-header-values.ts": 4083, + "5558-http-incoming-duplicate-headers.ts": 2888, + "5559-http-expect-header.ts": 14863, + "5560-http-response-framing.ts": 11391, + "5561-http-max-header-size.ts": 4316, + "5562-https-verify-errors.ts": 4777, + "5563-http-keep-alive-timeout.ts": 17783, + "5564-http-request-signal.ts": 10735, + "5565-fs-dirent-untyped.mjs": 4337, + "5566-fs-dirent-dyn-boundaries/main.mjs": 4371, + "5567-fs-cpsync.ts": 4945, + "5568-fs-cp-errors.js": 4099, + "5569-fs-cp-symlinks.ts": 3686, + "5570-fs-cp-async.ts": 4176, + "5571-fs-cp-deep.ts": 5306, + "5585-record-results-inlined.ts": 3957, + "5595-net-ref-unref.ts": 5321, + "5600-dyn-cycle-self.ts": 3365, + "5601-dyn-cycle-mutual.ts": 4239, + "5602-dyn-cycle-arrays.ts": 3453, + "5603-dyn-cycle-identity.ts": 3497, + "5604-dyn-cycle-checks.ts": 3761, + "5605-dyn-cycle-containers.ts": 4084, + "5606-dyn-cycle-capsules.ts": 3095, + "5610-readable-from-sets-and-views.ts": 5921, + "5611-readable-from-class-iterables.ts": 3857, + "5612-readable-from-iterables.cjs": 5191, + "5613-readable-from-iterable-timing.cjs": 5131, + "5614-readable-from-iterable-errors.cjs": 4553, + "5615-next-tick-callback-phases.ts": 4347, + "5616-next-tick-callback-phases.cjs": 3259, + "5617-pipeline-function-ends.ts": 60581, + "5618-pipeline-function-ends-errors.ts": 13092, + "5619-pipeline-function-ends.cjs": 14768, + "5620-timers-promises-values.ts": 3726, + "5621-util-format-float.ts": 3395, + "5622-events-once.mjs": 3355, + "5623-events-once.ts": 4676, + "5624-timers-promises-unawaited.ts": 4420, + "5625-promisify-exec.ts": 8488, + "5626-promisify-forms.ts": 3100, + "5627-exec-callback.ts": 3311, + "5628-promisify-timers.ts": 3913, + "5629-promisify-stream.ts": 3357, + "5630-promisify-zlib.ts": 3392, + "5631-promisify-crypto.ts": 3727, + "5632-promisify-dns.ts": 5476, + "5644-net-socket-addresses.ts": 3677, + "5645-net-socket-callbacks.ts": 4544, + "5646-net-server-connections.ts": 3663, + "5647-net-is-ip.ts": 3569, + "5648-dns-lookup-forms.ts": 5746, + "5649-dns-service-servers.ts": 4846, + "5650-dns-lookup-errors-untyped.cjs": 4466, + "5651-tls-socket-protocol.ts": 30873, + "5652-http-client-trailer-overflow.ts": 3512, + "5653-net-socket-read-ahead.ts": 6212, + "5654-https-empty-servername.ts": 6322, + "5655-dgram-socket-options.ts": 5089, + "5656-dgram-option-states.ts": 4267, + "5657-net-reset-and-address.ts": 3588, + "5658-net-reset-dynamic.cjs": 4002, + "5659-net-blocklist-socketaddress.ts": 5090, + "5664-tls-client-identity.ts": 6340, + "5674-union-instanceof-array-arms.ts": 3943, + "5680-array-growth-after-copy.ts": 4581, + "5681-array-pop-and-length.ts": 4279, + "5682-publish-wide-objects.ts": 3376, + "5700-cycle-release-retained.ts": 3414, + "5701-cycle-release-old-edges.ts": 4210, + "5702-publish-candidate-buffers.ts": 3848, + "5703-lifetime-region-debug.ts": 2875, + "5704-publish-refused-restores-counts.ts": 3759, + "5710-map-number-keys-hashed.ts": 3686, + "5711-map-number-write-after-read.ts": 3642, + "5712-map-number-overwrite-at-growth.ts": 4057, + "5714-cycle-large-candidate-buffers.ts": 4303, + "5715-json-parse-fitted-tables.ts": 4861, + "5717-set-prototype-of-primitives.ts": 3977, + "5719-int32-fields-dynamic-operators.ts": 4013, + "5720-intern-table-basics.ts": 5183, + "5721-intern-table-values.ts": 4244, + "5722-intern-table-workers.ts": 37693, + "5723-intern-table-shared-types.ts": 25115, + "5724-intern-table-array-keys.ts": 43939, + "5725-intern-table-callbacks.ts": 3924, + "5726-intern-table-key-limits.ts": 3414, + "5740-atomics-operation-semantics.ts": 4541, + "5741-atomics-contended-counters.ts": 5176, + "5742-atomics-lock-ordering.ts": 44911, + "5743-atomics-wait-notify.ts": 25944, + "5744-atomics-ring-buffer.ts": 3295, + "5745-atomics-semaphore-stress.ts": 39641, + "5746-shared-bulk-operations.ts": 3506, + "5747-fs-stat-throw-if-no-entry.ts": 3747, + "5748-fs-stat-throw-if-no-entry-js.cjs": 4239, + "5752-string-number-concat.ts": 2903, + "5760-crypto-hash-stream.ts": 3720, + "5761-crypto-hash-stream-states.ts": 3947, + "5762-crypto-hash-stream.cjs": 3953, + "5763-crypto-sha-blocks.ts": 4280, + "5764-class-structural-view-writes.ts": 4359, + "5764-crypto-hmac-edges.cjs": 3178, + "5765-class-structural-view-identity.ts": 4251, + "5766-class-structural-view-callbacks.ts": 3950, + "5767-class-opaque-accessor-view.ts": 3507, + "5770-guarded-union-narrows.ts": 3945, + "5771-guarded-downcasts.ts": 3443, + "5772-guarded-catch-narrows.ts": 3695, + "5773-stale-narrowing-inherited.ts": 3758, + "5776-power-of-two-remainder-integer-path.ts": 3584, + "5780-readline-history-options.ts": 3008, + "5781-readline-history-options.cjs": 5419, + "5782-readline-terminal-editing.ts": 5323, + "5783-readline-terminal-completion.cjs": 5914, + "5784-readline-terminal-promises.ts": 8841, + "5785-readline-terminal-keys.ts": 7210, + "5786-readline-terminal-history.cjs": 5018, + "5787-readline-cursor-helpers.cjs": 4120, + "5788-readline-promises-readline.ts": 3931, + "5789-readline-cursor-pos.cjs": 10833, + "5790-readline-keypress-events.ts": 5781, + "5791-dyn-constant-key-read-sites.ts": 3910, + "5791-readline-keypress-interfaces.cjs": 10593, + "5792-json-parse-repeated-layouts.ts": 4520, + "5792-readline-keypress-listeners.ts": 3779, + "5793-worker-message-channel.ts": 6071, + "5794-worker-message-port-close-order.ts": 4996, + "5795-worker-message-port-batches.ts": 2995, + "5796-worker-message-channel.cjs": 3812, + "5797-worker-message-port-transfer.ts": 3633, + "5800-broadcast-channel.ts": 40203, + "5801-broadcast-channel.cjs": 5126, + "5802-broadcast-channel-workers.ts": 4593, + "5803-worker-environment-data.ts": 5183, + "5804-worker-environment-data.cjs": 3173, + "5805-worker-receive-message-parent-port.ts": 2696, + "5806-worker-resource-limits.ts": 3922, + "5807-worker-error-values.ts": 6807, + "5808-worker-unhandled-rejections.ts": 7138, + "5809-process-exit-and-rejections.cjs": 33202, + "5810-mock-method-records.ts": 6251, + "5811-mock-tracker-restore.ts": 6093, + "5812-dyn-box-async-fields.ts": 3948, + "5813-mock-timers-module.ts": 5361, + "5814-mock-timers-date.ts": 7472, + "5830-timers-promises-signal.ts": 4170, + "5831-util-types-brands.ts": 3902, + "5832-util-types-brands-dynamic.js": 4287, + "5833-url-search-params-iterators.ts": 3492, + "5834-url-search-params-iterators-dynamic.mjs": 3994, + "5835-util-format-integer-specifiers.ts": 3659, + "5836-url-search-params-tuple-pairs.ts": 3814, + "5837-util-format-json-circular.ts": 4225, + "5838-qs-stringify-bigint.ts": 5010, + "5839-timers-promises-scheduler.ts": 3897, + "5840-promise-with-resolvers-js.js": 3596, + "5841-json-typed-dyn-members.ts": 5082, + "5842-qs-parse-null-prototype.ts": 3325, + "5843-qs-parse-null-prototype-js.mjs": 3033, + "5844-date-unknown-values.ts": 4145, + "5845-groupby-null-prototype.ts": 4290, + "5846-events-esm-default-max.ts": 2759, + "5847-events-cjs-default-max.cjs": 3509, + "5849-class-view-observations.ts": 4659, + "5850-using-disposable-values.ts": 3072, + "5851-using-suppressed-error-values.ts": 3950, + "5852-using-disposable-values-js.js": 3905, + "5853-set-methods-map-arguments.ts": 3831, + "5854-array-from-async.ts": 5635, + "5855-array-from-async-js.js": 3433, + "5856-error-is-error.ts": 3928, + "5857-regexp-unicode-sets.ts": 3859, + "5858-disposable-stack.ts": 5135, + "5859-disposable-stack-js.js": 3801, + "5860-url-ipv4-hosts.ts": 4018, + "5870-worker-thread-names.ts": 7328, + "5871-worker-untransferable.cjs": 43661, + "5872-readline-positional-create-interface.mjs": 6166, + "5873-readline-promises-namespace.cjs": 3811, + "5874-worker-stdio-streams.ts": 50741, + "5875-worker-stdout-pipe.cjs": 50279, + "5879-worker-stdin.ts": 57033, + "5880-emitter-leak-warning-delivery.ts": 4319, + "5881-inspect-weak-collections.ts": 3631, + "5882-string-decoder-runtime-encoding.ts": 3362, + "5883-os-set-priority.ts": 3308, + "5884-inspect-layout-options.ts": 4849, + "5885-events-max-listener-helpers.ts": 3588, + "5886-inspect-dates.ts": 2966, + "5887-inspect-typed-arrays.ts": 3325, + "5888-inspect-sorted.ts": 3559, + "5889-timers-scheduler-signal.ts": 8928, + "5890-while-string-length-counters.ts": 3822, + "5892-buffer-write-utf16le-runs.ts": 3465, + "5900-float16-array.ts": 3954, + "5901-float16-array-js.js": 3925, + "5904-async-generator-completion.ts": 6639, + "5905-array-from-async-generator.ts": 4467, + "5906-array-from-async-generator-js.js": 4458, + "5907-regex-groups-match-rows.ts": 4698, + "5910-set-prototype-of-primitive-object-kinds.ts": 4445, + "5911-dyn-class-to-primitive.ts": 5410, + "5912-dyn-class-conversion-methods.ts": 4298, + "5913-prototype-type-error-messages.ts": 3454, + "5914-dyn-in-class-prototype-members.ts": 3792, + "5915-dyn-error-string-conversions.ts": 3851, + "5920-promise-try-args.ts": 3195, + "5921-promise-try-args-js.js": 3082, + "5922-matchall-groups-callbacks.ts": 5275, + "5923-matchall-groups-js.js": 4883, + "5924-js-literal-identity.js": 3065, + "5925-class-view-identity-observers.ts": 2685, + "5926-class-view-switch-js.js": 3057, + "5927-switch-object-identity.ts": 2661, + "5928-groupby-iterables.ts": 4500, + "5929-groupby-js.js": 3482, + "5930-contract-int32-arrays.ts": 4745, + "5930-method-census-asserted-number-keys.ts": 4815, + "5931-asserted-reads-keep-number-parameters.ts": 3015, + "5932-generic-class-observed-methods.ts": 5678, + "5933-class-fields-unknown-object.ts": 4834, + "5934-js-class-fields-property-order.js": 4883, + "5935-js-subclass-fields-and-element-calls.js": 4470, + "5936-checked-js-class-types.js": 4183, + "5937-override-narrowed-union-returns.ts": 3827, + "5938-object-literal-computed-keys-keep-methods.ts": 4019, + "5939-js-let-object-reassigned-wider.js": 4037, + "5940-callback-helper-specialization.ts": 4548, + "5941-callback-specialization-recursion.ts": 4448, + "5942-callback-specialization-throws.ts": 3443, "5942-local-class-observed-methods.ts": 6212, - "5950-tuple-predicate-methods.ts": 7790, - "5951-result-never-widening.ts": 6120, - "5955-any-promise-methods.js": 5120, - "5956-erased-generic-members.ts": 8750, - "5958-asserted-union-receiver.ts": 3160, - "5959-date-to-json.ts": 4330, - "5960-generic-mapped-types.ts": 3300, - "5970-net-connect-host-name.ts": 2800, - "5971-buffer-chunks-to-string.ts": 3100, - "5972-net-set-encoding-listeners.ts": 3000, + "5943-fields-read-before-assignment.ts": 4319, + "5944-js-fields-read-before-assignment.js": 3945, + "5945-local-class-observed-methods.ts": 4458, + "5946-nested-record-field-optional-writes.ts": 3537, + "5950-tuple-predicate-methods.ts": 4018, + "5951-result-never-widening.ts": 3970, + "5952-record-missing-keys.ts": 5020, + "5953-nan-coded-method-returns.ts": 2689, + "5955-any-promise-methods.js": 3182, + "5956-erased-generic-members.ts": 5824, + "5958-asserted-union-receiver.ts": 3206, + "5959-date-to-json.ts": 3819, + "5960-frame-records-own-references.ts": 3535, + "5960-generic-mapped-types.ts": 3052, + "5961-frame-spread-updates.ts": 3732, + "5962-frame-tuples.ts": 3372, + "5970-net-connect-host-name.ts": 3461, + "5971-buffer-chunks-to-string.ts": 4886, + "5972-net-set-encoding-listeners.ts": 4672, "5973-buffer-concat-untyped.js": 2900, - "5974-typeof-module-exports.cjs": 2500, - "5990-worker-uncloneable.mjs": 22432, - "5991-worker-async-dispose.mjs": 25768, - "5992-worker-thread-name-lifetime.mjs": 28932, - "600-closures-basic.ts": 2534, - "601-closures-loops.ts": 2297, - "602-closures-identity-recursion.ts": 2427, - "603-closures-rc-stress.ts": 2589, - "604-closures-for-of.ts": 3514, - "605-closures-forward-capture-tdz.ts": 4638, - "700-classes-basic.ts": 2674, - "701-classes-composition.ts": 2606, - "702-classes-this-capture.ts": 2403, - "703-classes-rc-stress.ts": 3477, - "710-inheritance-basics.ts": 2972, - "711-inheritance-dispatch.ts": 2507, - "712-inheritance-instanceof.ts": 2847, - "713-inheritance-rc-stress.ts": 3484, - "720-accessors-basics.ts": 3144, - "721-accessors-eval-order.ts": 3241, - "722-accessors-inheritance.ts": 3694, - "723-accessors-rc-stress.ts": 2838, - "750-cycle-closure-box.ts": 3262, - "751-cycle-records-mutual.ts": 3163, - "752-cycle-classes.ts": 2239, - "753-cycle-owned-arrays.ts": 2204, - "754-cycle-external-ref.ts": 2593, - "755-cycle-async-promise.ts": 3154, - "756-cycle-inheritance.ts": 2213, - "757-cycle-cross-generation.ts": 3118, - "762-any-boundary.ts": 3063, - "764-any-async.ts": 3129, - "765-any-optional-chain.ts": 3899, - "766-any-union-boundary.ts": 3346, - "767-string-literal-keys.ts": 2340, - "768-any-uninitialized.ts": 2983, - "800-switch-basics.ts": 3128, - "801-switch-lazy-tests.ts": 2842, - "802-switch-loops.ts": 3486, - "803-switch-rc-stress.ts": 2321, - "804-switch-braced-blocks.ts": 3234, - "810-do-while.ts": 3326, - "820-multi-decl.ts": 2338, - "830-let-uninitialized.ts": 2579, - "900-records-basics.ts": 3473, - "901-records-eval-order.ts": 2890, - "902-records-functions-closures.ts": 3085, - "903-records-rc-stress.ts": 3146, - "904-records-optional-fields.ts": 2930, - "905-records-optional-rc-stress.ts": 2986, - "906-records-utility-types.ts": 2822, - "907-records-utility-generics.ts": 3048, - "908-records-index-signatures.ts": 2695, - "909-records-index-json.ts": 3037, - "910-records-index-rc-stress.ts": 2958, - "911-records-index-dot-access.ts": 4686, - "912-unknown-slots.ts": 3359, - "913-records-index-iteration.ts": 3265, - "914-records-from-entries.ts": 3414, - "915-unknown-tostring-eq.ts": 3349, - "916-unknown-bytes.ts": 3729, - "917-record-array-index-dot.ts": 3331, - "950-modules-basic/main.ts": 2717, - "951-modules-diamond/main.ts": 2315, - "952-modules-cross/main.ts": 2751, - "953-modules-default/main.ts": 3300, - "954-modules-reexport/main.ts": 2770, - "957-builtins-namespace.ts": 2759, - "960-generics-basics.ts": 3631, - "961-generics-recursion.ts": 3262, - "962-generics-closures.ts": 2245, - "963-generics-modules/main.ts": 2769, - "964-generics-rc-stress.ts": 3123, - "965-generics-async.ts": 3925, - "965-unions-retag.ts": 3882, - "966-unions-retag-rc-stress.ts": 3167, - "967-record-field-widening.ts": 2827, - "969-unions-arm-matrix.ts": 3297, - "970-unions-basics.ts": 3439, - "971-unions-switch.ts": 3593, - "972-unions-nested.ts": 2812, - "973-unions-rc-stress.ts": 2567, - "974-unions-modules/main.ts": 2485, - "975-unions-undefined.ts": 2505, - "976-unions-null.ts": 3528, - "977-unions-unit-rc-stress.ts": 2503, - "978-unions-nullish.ts": 2591, - "979-unions-optional-chaining.ts": 2499, - "980-exceptions-basics.ts": 2297, - "981-exceptions-values.ts": 3503, - "982-exceptions-rc-stress.ts": 3146, - "983-exceptions-control-flow.ts": 2518, - "984-exceptions-finally.ts": 2504, - "985-exceptions-call-chains.ts": 3540, - "986-exceptions-uncaught.ts": 3473, - "987-exceptions-result-unions.ts": 3527, - "990-process-basics.ts": 3510, - "991-process-exit.ts": 2297, - "992-fs-roundtrip.ts": 2694, - "993-fs-readdir.ts": 3781, - "994-fs-errors.ts": 2370, - "995-fs-uncaught.ts": 2239, - "996-fs-rc-stress.ts": 2822, - "997-fs-modules/main.ts": 2578, - "998-process-env.ts": 2181, - "abstract-property-dispatch.ts": 2739, - "arguments-arrow-capture.mjs": 3039, - "arguments-reexport/main.ts": 2635, - "array-argument-lifetimes-exits.ts": 3136, - "array-argument-lifetimes-mutation.ts": 3333, - "array-argument-lifetimes-strings.ts": 3015, - "array-argument-lifetimes-values.ts": 3223, - "array-buffer-conversions.js": 2594, - "array-buffer-storage.ts": 3699, - "array-checked-callback-results.mjs": 3402, - "array-checked-constructor.js": 3477, - "array-concat-widening.ts": 3858, - "array-element-compound-updates.ts": 3591, - "array-fill-unit-values.ts": 2572, - "array-from-checked.js": 3097, - "array-from-value.mjs": 3428, - "array-isarray-value/main.js": 3143, - "array-numeric-own-keys.ts": 3632, - "array-optional-number-write-index.ts": 3380, - "array-primitive-ordering.ts": 3925, - "array-read-helper-effects.ts": 3425, - "array-refined-storage.ts": 3909, - "array-sort-dense-effects.ts": 2917, - "array-sort-dense-values.ts": 5261, - "array-sort-holes-published.ts": 13040, - "array-sort-runs.ts": 10059, - "array-sort-runtime-comparator.ts": 5498, - "array-sort-unknown-comparator.ts": 2858, - "array-storage-transitions.ts": 2799, - "array-truncate-reuse.ts": 2407, - "async-backend-factory.mjs": 3609, - "async-local-typed-results.ts": 2487, - "atomics-integer-operations.ts": 3832, - "bigint-checked-casts.ts": 3603, - "bigint-checked-storage.js": 3992, - "bigint-union-conversion.ts": 3125, - "boxed-error-rethrows.mjs": 2646, - "buffer-codec-boundaries.ts": 3139, - "buffer-primitive-predicate.ts": 3495, - "builtin-prototype-method-values.mjs": 4297, - "byte-loop-bounds.ts": 3001, - "byte-number-pipeline.ts": 3966, - "byte-record-windows.ts": 3749, - "call-lifetimes-argument-order.ts": 2794, - "call-lifetimes-control-flow.ts": 3894, - "call-lifetimes-owned-boundaries.ts": 3881, - "call-lifetimes-projections.ts": 3082, - "call-lifetimes-union-values.ts": 3090, - "call-lifetimes-virtual-optionals.ts": 3774, - "callable-module-members.mjs": 2996, - "callable-properties.mjs": 3737, - "callable-property-cycles.js": 2394, - "callable-reexport/main.ts": 2995, - "captured-class-refinement.ts": 3815, - "catch-capture.ts": 2295, - "caught-class-identity.ts": 3893, - "caught-standalone-class.ts": 3591, - "caught-system-error-fields.ts": 3373, - "checked-addition.js": 3059, - "checked-any-addition.ts": 2638, - "checked-array-assignment.ts": 2861, - "checked-array-flat/main.ts": 2441, - "checked-bitwise/main.js": 3177, - "checked-builder-cycles.js": 3284, - "checked-byte-storage/main.ts": 3985, - "checked-callback-options.ts": 3034, - "checked-class-descriptors.mjs": 2786, - "checked-class-factory.mjs": 2348, - "checked-collection-iterators.ts": 3731, - "checked-collection-symbol-methods.ts": 3019, - "checked-dispatch-input-lifetimes.js": 3481, - "checked-increment/main.js": 2884, - "checked-inherited-fields.ts": 2756, - "checked-iterator-consumption.js": 4074, - "checked-numeric-arithmetic.js": 2874, - "checked-object-module-alias.js": 3204, - "checked-optional-calls.mjs": 2897, - "checked-promise-recovery.ts": 2819, - "checked-proxy-callback/main.ts": 3009, - "checked-record-constructor.ts": 3034, - "checked-renderer-defaults.js": 2935, - "checked-rest-function-adapters.js": 2965, - "checked-storage-transitions.js": 2510, - "checked-symbol-array.ts": 2504, - "checked-value-input-lifetimes.ts": 3033, - "class-accessor-returned-function.js": 2287, - "class-array-optional-return.ts": 3498, - "class-bracket-method-calls.js": 3071, - "class-brand-overload.js": 3526, - "class-callback-replacement.js": 3460, - "class-callback-storage.ts": 4585, - "class-checked-composites.ts": 3187, - "class-checked-upcast.ts": 3072, - "class-collection-insertion.ts": 4235, - "class-computed-presence.ts": 2974, - "class-computed-symbols.js": 3740, - "class-covariant-returns.ts": 3957, - "class-curried-factory.mjs": 4128, - "class-data-descriptors.js": 3270, - "class-factory-inherited-statics.mjs": 2538, - "class-factory-parameter-base.mjs": 2587, - "class-factory-record-base.mjs": 5836, - "class-factory-recursive-statics.mjs": 3651, - "class-factory-static-members.mjs": 3672, - "class-function-base/main.js": 2657, - "class-instance-constructor.js": 2942, - "class-interface-protocols.ts": 3713, - "class-interface-refinements.ts": 4567, - "class-json-checked.ts": 2571, - "class-local-function-base.mjs": 3643, - "class-method-spreads.ts": 3220, - "class-narrowed-views.ts": 3766, - "class-property-call-values.mjs": 3125, - "class-prototype-data.js": 4251, - "class-prototype-descriptor-copy.mjs": 3998, - "class-specialized-computed-base.mjs": 3397, - "class-static-data.js": 2157, - "class-subclass-assertions.ts": 3733, - "class-super-surplus-spread.mjs": 2301, - "class-symbol-accessors.ts": 2479, - "class-symbol-cache.mjs": 3721, - "class-value-constructor-exceptions.ts": 3210, - "closure-nullable-union-return.ts": 2727, - "closure-union-locals.ts": 2878, - "collection-array-cycles.ts": 3240, - "collection-array-maps.ts": 4497, - "collection-array-missing.ts": 3865, - "collection-array-sets.ts": 3087, - "collection-copy-identities.ts": 2655, - "collection-data-unions.ts": 2993, - "collection-ingestion-mappers.ts": 3305, - "collection-ingestion-protocol/main.ts": 5059, - "collection-ingestion-references.ts": 4238, - "collection-ingestion.ts": 3626, - "collection-storage-copies.ts": 4163, - "collection-value-keys.ts": 4167, - "compound-field-receivers.ts": 2877, - "compound-presence-guards.ts": 3145, - "conditional-tuple-destructuring.ts": 3213, - "console-imports.cjs": 2893, - "console-native-value.js": 2516, - "const-enum-module-cycle/main.ts": 3456, - "constant-callback-dispatch.ts": 2916, - "constant-module-globals/main.ts": 2911, - "constant-number-remainders.ts": 2372, - "constructed-class-dispatch.ts": 3382, - "contextual-array-union-write.ts": 6417, - "contextual-return-layouts.ts": 3746, - "counted-loop-numbers.ts": 2724, - "counted-loop-storage.ts": 2302, - "crypto-hash-optional-input.ts": 3311, - "cycle-generations-long-lived.ts": 3514, - "cycle-growth-yield.ts": 3352, - "date-checked-native.js": 2676, - "declared-index-spread.ts": 2941, - "deep-reference-teardown.ts": 4427, - "default-undefined-parameters.ts": 3232, - "deferred-field-updates.ts": 3345, - "deferred-registry-many.ts": 3072, - "derived-numeric-updates.ts": 2702, - "destructure-call-assignment.js": 2978, - "destructure-inferred-row-defaults.js": 3175, - "destructure-union-arrays.ts": 3248, - "dynamic-function-descriptors.js": 2750, - "error-cause-writes.js": 3104, - "error-inherited-message.mjs": 3024, - "error-inherited-name.mjs": 2500, - "error-property-writes.mjs": 3373, - "error-prototype-tostring.js": 3874, - "error-stack-frames.ts": 2418, - "error-stack-limit.ts": 2675, - "error-tostring-overrides.ts": 3246, - "error-values-inspect.ts": 3043, - "exact-index-round-trip.ts": 3701, - "expression-runtime-values.ts": 2154, - "fiber-switch-stress.ts": 5012, - "fiberless-async-ordering.ts": 3413, - "fiberless-async-unhandled.ts": 2739, - "field-assignment-values.ts": 2662, - "file-handle-values.ts": 3133, - "file-read-storage.ts": 3685, - "file-url-percent-decoding.ts": 2659, - "final-field-borrows.ts": 3350, - "for-multiple-bindings.ts": 3223, - "for-multiple-lexical-bindings.ts": 3021, - "for-of-checked-iterator.js": 3541, - "for-of-subclass-elements.ts": 3037, - "forward-generic-signatures.ts": 2999, - "fresh-array-union-layout.js": 2679, - "frozen-checked-dictionaries.mjs": 3220, - "fs-native-realpath.ts": 2477, - "fs-read-encodings.ts": 3111, - "fs-remove-readonly.ts": 2708, - "fs-write-string-bytes-union.ts": 2772, - "function-apply-value.mjs": 2976, - "function-constructor-global.mjs": 2229, - "function-constructor-kinds/main.js": 3064, - "function-custom-prototype.js": 3165, - "function-custom-to-string.mjs": 3379, - "function-object-assign.js": 3235, - "function-own-prototype/main.js": 2539, - "generator-checked-protocol.ts": 3197, - "generator-checked-return.js": 3544, - "generator-custom-delegation/main.ts": 3611, - "generator-default-return-pattern.ts": 3352, - "generator-delegation-values.ts": 3047, - "generator-inferred-date.js": 3314, - "generator-mixed-checked-channels.ts": 3576, - "generator-untyped-consumer/main.ts": 3225, - "generic-base-specializations.ts": 2247, - "generic-binding-ownership.ts": 3517, - "generic-class-js-functions/main.ts": 2300, - "generic-class-recovery.ts": 2961, - "generic-namespace-factories/main.ts": 3104, - "generic-nullable-collections.ts": 2983, - "generic-pattern-bindings.ts": 3518, - "generic-record-index.ts": 3239, - "generic-visitor-void.ts": 3402, - "global-error-subclasses.ts": 2754, - "global-runtime-probes.js": 2804, - "global-stored-probes.js": 3113, - "global-string-cache/main.js": 2090, - "global-symbol-cache/main.js": 3628, - "guarded-native-iterables.ts": 3327, - "heap-input-aliases.ts": 3457, - "heap-input-collections.ts": 3653, - "in-array-runtime-index.ts": 3275, - "indexed-assignment-values.js": 3011, - "indexed-assignment-values.ts": 2182, - "indexed-element-field-reads.ts": 3247, - "inferred-array-assignment-values.mjs": 2906, - "inferred-class-default.mjs": 2977, - "inferred-compound-values.mjs": 2759, - "inferred-conditional-class.mjs": 3292, - "inferred-error-fields.js": 3405, - "inferred-map-patterns.mjs": 2764, - "inferred-method-override.js": 2696, - "inferred-partial-destructuring.mjs": 2539, - "inferred-primitive-properties.mjs": 2255, - "inferred-record-return.mjs": 2589, - "inferred-recursive-record.mjs": 2186, - "inferred-undefined-field.mjs": 2551, - "input-lifetime-intervals.ts": 3191, - "input-lifetimes-modules/main.ts": 3221, - "instanceof-class-record-union.ts": 3279, - "integer-flow-arithmetic.ts": 3347, - "integer-loop-forms.ts": 2940, - "integer-view-control.ts": 2093, - "integer-view-numbers.ts": 2680, - "integer-view-storage.ts": 3947, - "intl-grapheme-conformance/main.ts": 5064, - "intl-grapheme.ts": 2412, - "iterator-typed-returns.ts": 3466, - "js-abort-portable.js": 4587, - "js-array-constructor.js": 3296, - "js-array-default-buffer.js": 3604, - "js-array-iterators.js": 3215, - "js-arrow-bind.cjs": 3596, - "js-buffer-callback-brand.cjs": 3526, - "js-callable-proxy.js": 3865, - "js-chained-native-results.mjs": 3076, - "js-checked-array-iterator.js": 2790, - "js-checked-array-reference.js": 2884, - "js-checked-number-negation.mjs": 2418, - "js-checked-sort-result.js": 2906, - "js-child-error-listener.cjs": 2538, - "js-cjs-callable-snapshots/main.cjs": 2461, - "js-class-added-parameters.js": 2522, - "js-class-checked-overrides.js": 2542, - "js-class-computed-fields.js": 2281, - "js-class-enumeration.js": 3084, - "js-class-field-accessor-override.js": 3865, - "js-class-fluent-overrides.js": 3368, - "js-class-late-method-values.js": 2526, - "js-class-member-dispatch.js": 3846, - "js-class-return-shapes.js": 2639, - "js-class-shadowed-methods.js": 2891, - "js-class-stored-method-receiver.js": 2685, - "js-class-subclass-properties.js": 3775, - "js-class-symbol-spread.cjs": 3550, - "js-computed-prototype-additions.js": 3462, - "js-constructor-returned-callable.js": 3346, - "js-constructor-spread.js": 3311, - "js-dataview-dispatch.js": 2423, - "js-declared-fields.cjs": 3098, - "js-default-listener-arguments.cjs": 2941, - "js-default-options/main.ts": 3442, - "js-deferred-field-initializers.js": 2409, - "js-deferred-registry-effects.js": 2980, - "js-derived-constructor-hoisting.js": 2695, - "js-dormant-array-write.js": 2014, - "js-dormant-overrides.js": 3783, - "js-expando-records.js": 2526, - "js-implicit-call-results.js": 2892, - "js-inferred-callback-exports/main.js": 3776, - "js-inferred-class-array-return.js": 3477, - "js-inferred-object-values.js": 3358, - "js-inherited-field-storage.js": 3527, - "js-inherited-parameter-abi.js": 3324, - "js-iterable-spread.js": 3504, - "js-loose-dynamic-primitives.js": 2079, - "js-map-object-seeds.js": 3322, - "js-member-increment.js": 4257, - "js-module-function-values.js": 3135, - "js-mutable-null-members.js": 2750, - "js-nested-native-descriptors.js": 2725, - "js-normalize-dispatch.js": 3264, - "js-nullish-inherited-fields.mjs": 3080, - "js-object-receiver-storage.js": 3515, - "js-object-returning-constructors.mjs": 3015, - "js-object-shared-arrays.js": 2539, - "js-omitted-typed-arguments.js": 2295, - "js-open-binding-writes.js": 1944, - "js-optional-class-fields.js": 3252, - "js-optional-member-call.js": 3095, - "js-primitive-union-addition.js": 2779, - "js-promise-all-values.js": 3443, - "js-promise-catch-handler.js": 2034, - "js-promise-finally-order.js": 2084, - "js-promise-pending-reactions.js": 3602, - "js-prototype-host-boundary.js": 4779, - "js-reflect-receivers.js": 3486, - "js-regex-record-captures.js": 2214, - "js-scalar-iterables.js": 3719, - "js-shared-method-dispatch.js": 2114, - "js-specialized-field-method.js": 3011, - "js-static-accessors.js": 3435, - "js-static-method-values.js": 3174, - "js-static-registries.cjs": 3047, - "js-super-accessor-initialization.js": 2998, - "js-uninitialized-native-fields.js": 2354, - "json-guarded-map-values.ts": 2502, - "json-output-bulk.ts": 3214, - "json-runtime-options.mjs": 2794, - "json-stringify-value.js": 2833, - "json-typed-parse-direct.ts": 3580, - "literal-switch-dispatch.ts": 2951, - "live-reference-shared-converters.ts": 19276, - "llvm-read-receiver-lifetime.ts": 3803, - "local-class-call-spreads.js": 3495, - "local-class-declaration.ts": 2661, - "local-class-evaluation.ts": 3104, - "local-class-inheritance.ts": 2552, - "local-class-lifetimes.ts": 3344, - "local-class-namespace/main.js": 3630, - "local-class-prototype-data.js": 3990, - "local-class-specialization.js": 2812, - "local-class-unknown.ts": 3002, - "local-class-values.ts": 2009, - "local-union-storage.ts": 3026, - "locale-compare-root-order.ts": 3444, - "logical-assignment-dynamic.js": 3230, - "logical-assignment-values.ts": 3566, - "logical-runtime-optional-record.ts": 3322, - "loop-reference-lifetimes.ts": 3339, - "map-checked-boundary/main.ts": 2843, - "map-checked-seed.js": 2966, - "map-number-keys-dense.ts": 2773, - "map-read-lifetimes-exits.ts": 2761, - "map-read-lifetimes.ts": 3024, - "map-seeds-and-spreads.ts": 3502, - "map-typed-array-values.ts": 3300, - "math-function-values.js": 2815, - "missing-read-lambda-results.ts": 3370, - "missing-read-narrowed-storage.ts": 2854, - "missing-read-number-conversions.ts": 2326, - "module-byte-loops.ts": 2961, - "module-fallback-functions/main.js": 2737, - "narrowed-nullish-values.ts": 3160, - "native-any-timer-callback.ts": 2388, - "native-array-borrowed-find-last.js": 2571, - "native-array-borrowed-slice.js": 3010, - "native-array-copying-methods.js": 3608, - "native-array-fill-copywithin.js": 2996, - "native-array-frozen-noops.js": 3132, - "native-array-index-boundaries.js": 2225, - "native-array-integrity.js": 3091, - "native-array-length.js": 2365, - "native-array-named-properties.js": 2772, - "native-array-prototype/main.js": 3257, - "native-array-read-lifetimes.ts": 3805, - "native-array-reduce-find-last.js": 2524, - "native-array-search-from-index.js": 2027, - "native-array-splice-coercion.js": 2902, - "native-array-splice-flat.js": 2702, - "native-buffer-coercion.js": 2533, - "native-callback-dispatch.ts": 3850, - "native-class-array-boundaries.ts": 3272, - "native-class-cache.js": 3290, - "native-class-properties.cjs": 4583, - "native-class-property-cycles.cjs": 2888, - "native-class-property-order.cjs": 2105, - "native-class-type-predicates.ts": 2960, - "native-factory-records.js": 3080, - "native-method-values.js": 3222, - "native-method-values.ts": 2808, - "native-npm-imports/main.ts": 2750, - "native-object-descriptors.js": 2979, - "native-object-for-in-minimal.js": 2411, - "native-object-property-attributes.js": 3057, - "native-object-prototype.js": 3409, - "native-property-names.ts": 2077, - "native-proxy-env.js": 4372, - "native-worker-arena-reuse.ts": 33854, - "native-worker-bounded-errors.ts": 2556, - "native-worker-buffer-consumers.ts": 38253, - "native-worker-cancel-hot.ts": 28908, - "native-worker-class-fields.ts": 3286, - "native-worker-clone-errors.ts": 3629, - "native-worker-commonjs.cjs": 3085, - "native-worker-delayed-messages.ts": 28752, - "native-worker-discarded-heaps.ts": 39440, - "native-worker-environment.ts": 27133, - "native-worker-error.ts": 2958, - "native-worker-exception-state.ts": 3945, - "native-worker-exit-listeners.ts": 4052, - "native-worker-io.ts": 3361, - "native-worker-loop-cancellation.ts": 2798, - "native-worker-message-graphs.ts": 2738, - "native-worker-messages.ts": 3079, - "native-worker-poll-exceptions.ts": 2575, - "native-worker-port-lifetime.ts": 3213, - "native-worker-process-exit.ts": 4104, - "native-worker-publish-classes.ts": 24713, - "native-worker-publish-refusals.ts": 2820, - "native-worker-published-graphs.ts": 3534, - "native-worker-references.ts": 3358, - "native-worker-roots/main.ts": 2808, - "native-worker-shared-views.ts": 3012, - "native-worker-stack-overflow.ts": 2597, - "native-worker-terminate-exception-state.ts": 3340, - "native-worker-terminate-loop.ts": 2222, - "native-worker-terminate-recursion.ts": 2829, - "native-worker-terminate-suspended.ts": 4348, - "native-worker-terminate-tested-loops.ts": 3576, - "native-worker-terminate-wait.ts": 3222, - "native-worker-typed-arrays.ts": 2653, - "nested-map-cycles.ts": 3041, - "nested-map-values.ts": 2382, - "never-returning-calls.ts": 2642, - "next-tick-commonjs-entry.cjs": 2624, - "node24-buffer-encoding-errors.ts": 2695, - "node24-buffer-encoding.ts": 3038, - "node24-crypto-kdf-errors.ts": 2745, - "node24-crypto-kdf-options.ts": 2029, - "node24-crypto-kdf.ts": 3632, - "node24-crypto-sha2.ts": 2849, - "node24-deep-equality-properties.js": 3355, - "node24-deep-equality.cjs": 2682, - "node24-deep-equality.ts": 4058, - "node24-env-files-errors.ts": 3202, - "node24-env-files.ts": 3044, - "node24-env-imports.cjs": 2529, - "node24-env-parse-errors.ts": 2062, - "node24-env-parse.ts": 3684, - "node24-esm-metadata/main.mjs": 2492, - "node24-file-url-buffer.ts": 2158, - "node24-file-url-errors.js": 3331, - "node24-file-url-options.ts": 2772, - "node24-fs-links-callbacks.cjs": 3549, - "node24-fs-links-errors.cjs": 2633, - "node24-fs-links-options.cjs": 3571, - "node24-fs-links-promise-errors.cjs": 2445, - "node24-fs-links-promises.ts": 2105, - "node24-fs-links.ts": 2967, - "node24-fs-statfs-callbacks.cjs": 3035, - "node24-fs-statfs-errors.cjs": 2969, - "node24-fs-statfs-options.cjs": 3557, - "node24-fs-statfs.ts": 3067, - "node24-fs-timestamps-callbacks.cjs": 3095, - "node24-fs-timestamps-errors.cjs": 3110, - "node24-fs-timestamps-promises.ts": 3573, - "node24-fs-timestamps-symlinks.ts": 3390, - "node24-fs-timestamps.ts": 3032, - "node24-no-error-assertions-coercion.js": 2850, - "node24-no-error-assertions.cjs": 2814, - "node24-no-error-assertions.ts": 4358, - "node24-parseargs.js": 5971, - "node24-string-decoder.ts": 3185, - "node24-style-text-environment.js": 4225, - "node24-style-text-validation.js": 3673, - "node24-style-text.cjs": 3476, - "node24-style-text.ts": 3037, - "node24-system-error-validation.js": 3005, - "node24-system-errors.cjs": 3631, - "node24-system-errors.ts": 2849, - "node24-text-codec-properties.ts": 30185, - "node24-text-encode-into.ts": 3601, - "node24-text-utilities-coercion.js": 3094, - "node24-text-utilities-validation.js": 2330, - "node24-text-utilities.cjs": 2768, - "node24-text-utilities.ts": 3312, - "node24-url-authority-coercion.js": 3307, - "node24-url-authority.ts": 2639, - "node24-url-factories.ts": 3240, - "node24-url-factory-errors.ts": 2819, - "node24-url-mutation-coercion.js": 2399, - "node24-url-mutation.ts": 3257, - "node24-util-types.cjs": 3263, - "node24-util-types.js": 2199, - "node24-util-types.ts": 3506, - "non-null-class-iteration.ts": 3295, - "nonnull-element-arguments.ts": 2857, - "nonnull-forwarded-class-values.ts": 3303, - "nullable-object-extension.mjs": 2652, - "nullish-asserted-class-arms.ts": 2669, - "nullish-assignment-values.ts": 2125, - "nullish-long-chain.ts": 2641, - "number-parse-statics.ts": 2471, - "number-predicates-unknown.ts": 2568, - "numeric-call-specialization.ts": 2498, - "object-enumeration-array-reads.ts": 2246, - "object-from-checked-entries.js": 3478, - "object-inherited-to-string.js": 2987, - "object-key-indexes.js": 3206, - "object-prototype-identity.mjs": 3083, - "object-reexport/main.js": 2466, - "object-spread-computed.ts": 2806, - "opentui-byte-queue.js": 3532, - "optional-callback-arity.ts": 2417, - "optional-class-flows.ts": 2497, - "optional-element-defaults.ts": 2632, - "optional-flag-comparisons.ts": 2536, - "optional-native-callables.js": 2751, - "optional-node-list-iteration.ts": 2564, - "optional-string-index/main.ts": 3419, - "path-to-file-url-options.ts": 2301, - "performance-presence.ts": 2913, - "primitive-and-function-descriptors.js": 3070, - "primitive-union-string.ts": 3434, - "process-builtin-values.js": 2781, - "process-constrained-memory.ts": 2857, - "process-env-computed-keys.js": 3185, - "process-hrtime.js": 2662, - "process-named-signals.cjs": 3171, - "process-uncaught-handler-throws.cjs": 2344, - "process-uncaught-loop.cjs": 3141, - "process-uncaught-module.mjs": 2646, - "process-uncaught-monitor.cjs": 2837, - "process-uncaught-rejection.cjs": 2664, - "process-uncaught-sync.cjs": 2858, - "process-version-pin.ts": 3005, - "process-versions-storage.ts": 2055, - "projected-collection-lifetimes.ts": 3232, - "promise-checked-rejections.ts": 2914, - "promise-resolve-void.ts": 2846, - "prototype-method-overrides.mjs": 4119, - "prototype-named-data-records.ts": 3179, - "record-field-presence.ts": 3698, - "record-key-order.ts": 2706, - "record-optional-json-presence.ts": 3216, - "record-presence-copies.ts": 3381, - "record-presence-delete-json.ts": 3160, - "record-unchecked-read-presence.ts": 2953, - "record-unknown-values.ts": 4095, - "reflect-own-keys.mjs": 2705, - "reflect-record-union-keys.mjs": 2815, - "reflect-typed-record-keys.ts": 2683, - "regex-callback-replacements.ts": 2631, - "regex-callback-values.js": 3741, - "regex-checked-casts.ts": 2405, - "regex-checked-storage.js": 3560, - "regex-exec-long-subject.ts": 5584, - "regex-input-lifetimes.ts": 3241, - "regex-native-matcher.ts": 3652, - "regex-optional-captures.ts": 2740, - "regex-optional-templates.ts": 2802, - "regex-split-captures.ts": 3590, - "regex-text-paths.ts": 2840, - "regexp-constructor-repeat.ts": 2602, - "regexp-optional-constructor.ts": 2950, - "regexp-string-coercion.js": 2621, - "renderer-export-dictionaries.js": 3415, - "renderer-null-state.js": 2259, - "renderer-specializations.js": 2837, - "repeat-and-typed-read-errors.ts": 3220, - "rest-optional-array-arguments.ts": 2860, - "rest-optional-elements.ts": 2445, - "return-void-through-finally.ts": 2861, - "runtime-optional-string-flows.ts": 2977, - "scalar-string-slices.ts": 2840, - "set-checked-methods.js": 3072, - "set-checked-storage.ts": 2667, - "set-iterator-seed-references.ts": 3159, - "set-iterator-seeds.ts": 3269, - "set-optional-seeds.ts": 2164, - "small-collection-transitions.ts": 3586, - "stack-depth-async-bodies.ts": 2727, - "stack-overflow-async.ts": 2942, - "stack-overflow-caught.ts": 3056, - "stack-overflow-finally.ts": 3694, - "stack-overflow-shapes.ts": 3565, - "stack-overflow-uncaught.ts": 2532, - "static-field-updates/main.ts": 2914, - "static-method-receivers.ts": 2350, - "stdio-values.mjs": 2761, - "stdio-write-errors.mjs": 2672, - "stdio-write-overrides.mjs": 3205, - "stored-native-builtins.js": 3341, - "stored-object-helpers.js": 2959, - "string-char-code-integer-loops.ts": 2575, - "string-code-points.ts": 2460, - "string-concat-number-parts.ts": 2155, - "string-construction-order.ts": 3307, - "string-lifetimes-boundaries.ts": 3304, - "string-lifetimes-evaluation.ts": 2835, - "string-lifetimes-iteration.ts": 2037, - "string-lifetimes-operations.ts": 2789, - "string-normalize.ts": 2348, - "string-pattern-replacement.ts": 3823, - "string-scan-fast-paths.ts": 3224, - "string-split-checked-separator.mjs": 2349, - "string-split-traversal.ts": 3258, - "structural-class-properties.ts": 2627, - "structured-text-boundaries.js": 3452, - "structured-text-storage.ts": 3201, - "switch-integer-dispatch.ts": 2387, - "symbol-checked-boundary/main.ts": 2796, - "symbol-class-fields.ts": 2814, - "symbol-class-fields/main.cjs": 2659, - "symbol-class-methods/main.ts": 1948, - "symbol-fields-deferred-factory.ts": 2721, - "symbol-fields-inferred.js": 2792, - "symbol-object-properties/main.js": 3376, - "synchronous-callback-lifetimes.ts": 2872, - "terminal-stream-properties.ts": 2946, - "text-codec-values/main.ts": 18964, - "text-codec-var.ts": 3480, - "text-encode-into-boundaries.ts": 2224, - "timers-clear-many.ts": 3073, - "tuple-and-string-search.ts": 2595, - "tuple-array-union.ts": 3305, - "tuple-callback-visitation.ts": 3801, - "tuple-forof-visitation.ts": 3276, - "typed-array-bulk-conversion.mjs": 5346, - "typed-array-byte-io.js": 41314, - "typed-array-checked-index.js": 2607, - "typed-array-constructor-alias.ts": 2778, - "typed-array-constructor-identity.js": 2769, - "typed-array-constructor-union.ts": 2366, - "typed-array-constructor-values.mjs": 3448, - "typed-array-dynamic-methods.js": 3373, - "typed-array-integer-widths.ts": 3014, - "typed-array-nan-coded-reads.ts": 4239, - "typed-array-numeric-import.ts": 3499, - "typed-array-present-reads.ts": 3561, - "typed-array-reversed-storage.ts": 2954, - "typed-array-unknown.ts": 3453, - "typed-collection-iterator-references.ts": 3531, - "typed-collection-iterators.ts": 3385, - "typed-collection-views/main.ts": 2577, - "typed-index-arguments.ts": 3036, - "typed-prototype-methods.ts": 2475, - "typed-rest-callables.ts": 2463, - "typed-rest-filtered-return.ts": 2800, - "typedarray-native-inputs.js": 3164, - "typeof-checked-comparison.js": 2913, - "uint8array-sort-domains.ts": 2505, - "unicode-case-paths.ts": 2924, - "unicode-process-argv.ts": 2793, - "union-discriminant-width.ts": 3726, - "union-literal-scalar-discriminants.ts": 3704, - "union-literal-slot-conversion.ts": 2972, - "union-nested-layout-discriminant.ts": 2841, - "union-optional-semantic-identity.ts": 3100, - "union-record-clone.ts": 3480, - "union-record-structural-copy.ts": 3563, - "union-reference-comparison.ts": 3338, - "unknown-array-holes.ts": 2088, - "unknown-collections.ts": 3765, - "unknown-object-enumeration.ts": 4219, - "unknown-record-views.ts": 3040, - "url-runtime-base.ts": 2778, - "utf8-span-decoding.ts": 2655, - "util-parseargs-wrapped/main.ts": 2482, - "virtual-runtime-optional-returns.ts": 3876, - "weak-map-array-buffer.ts": 2826, - "weak-map-fresh-array.js": 2495, - "weak-map-native.js": 3627, - "weak-native-class-identity.ts": 2943, - "weak-set-native.js": 3056, - "worker-class-instance-clone.ts": 35222 + "5973-large-closure-environment.ts": 5145, + "5974-typeof-module-exports.cjs": 3216, + "5975-zlib-class-values.ts": 4063, + "5990-worker-uncloneable.mjs": 4117, + "5991-worker-async-dispose.mjs": 40350, + "5992-worker-thread-name-lifetime.mjs": 47859, + "5993-array-length-loop-resizing.ts": 4127, + "5994-array-length-sparse-loops.ts": 2815, + "5995-array-dense-loop-boundaries.ts": 4171, + "600-closures-basic.ts": 3688, + "601-closures-loops.ts": 2905, + "602-closures-identity-recursion.ts": 3340, + "6020-class-extends-function-constructor.js": 3739, + "6021-asserted-literal-union-arm.ts": 3498, + "603-closures-rc-stress.ts": 2788, + "604-closures-for-of.ts": 3472, + "6041-error-cause-redeclared.ts": 4631, + "6042-regex-match-object-destructuring.ts": 3091, + "6043-string-index-narrowed-union.ts": 3627, + "605-closures-forward-capture-tdz.ts": 4043, + "6140-cjs-require-tdz-throws/main.js": 2989, + "6141-cjs-require-tdz-checked/main.js": 4062, + "6142-cjs-require-published-early/main.js": 5156, + "6143-cjs-require-json/main.js": 3050, + "6144-cjs-require-builtin-nested/main.js": 3329, + "6145-cjs-require-cycle/main.js": 4761, + "700-classes-basic.ts": 2717, + "701-classes-composition.ts": 3694, + "702-classes-this-capture.ts": 3151, + "703-classes-rc-stress.ts": 3285, + "710-inheritance-basics.ts": 2592, + "711-inheritance-dispatch.ts": 3301, + "712-inheritance-instanceof.ts": 3603, + "713-inheritance-rc-stress.ts": 2631, + "720-accessors-basics.ts": 2515, + "721-accessors-eval-order.ts": 3454, + "722-accessors-inheritance.ts": 3466, + "723-accessors-rc-stress.ts": 3570, + "750-cycle-closure-box.ts": 3720, + "751-cycle-records-mutual.ts": 2782, + "752-cycle-classes.ts": 3168, + "753-cycle-owned-arrays.ts": 3490, + "754-cycle-external-ref.ts": 3578, + "755-cycle-async-promise.ts": 3053, + "756-cycle-inheritance.ts": 2275, + "757-cycle-cross-generation.ts": 3630, + "762-any-boundary.ts": 3627, + "764-any-async.ts": 3271, + "765-any-optional-chain.ts": 2987, + "766-any-union-boundary.ts": 3659, + "767-string-literal-keys.ts": 3306, + "768-any-uninitialized.ts": 3041, + "800-switch-basics.ts": 3127, + "801-switch-lazy-tests.ts": 3237, + "802-switch-loops.ts": 3396, + "803-switch-rc-stress.ts": 3243, + "804-switch-braced-blocks.ts": 3060, + "810-do-while.ts": 3366, + "820-multi-decl.ts": 3412, + "830-let-uninitialized.ts": 2806, + "900-records-basics.ts": 2799, + "901-records-eval-order.ts": 3424, + "902-records-functions-closures.ts": 2868, + "903-records-rc-stress.ts": 3996, + "904-records-optional-fields.ts": 3692, + "905-records-optional-rc-stress.ts": 3288, + "906-records-utility-types.ts": 2706, + "907-records-utility-generics.ts": 2748, + "908-records-index-signatures.ts": 3143, + "909-records-index-json.ts": 3697, + "910-records-index-rc-stress.ts": 4050, + "911-records-index-dot-access.ts": 3683, + "912-unknown-slots.ts": 2923, + "913-records-index-iteration.ts": 4112, + "914-records-from-entries.ts": 3768, + "915-unknown-tostring-eq.ts": 3915, + "916-unknown-bytes.ts": 3466, + "917-record-array-index-dot.ts": 3287, + "950-modules-basic/main.ts": 3531, + "951-modules-diamond/main.ts": 3176, + "952-modules-cross/main.ts": 2507, + "953-modules-default/main.ts": 2473, + "954-modules-reexport/main.ts": 2897, + "957-builtins-namespace.ts": 3213, + "960-generics-basics.ts": 2743, + "961-generics-recursion.ts": 3759, + "962-generics-closures.ts": 3551, + "963-generics-modules/main.ts": 3745, + "964-generics-rc-stress.ts": 3697, + "965-generics-async.ts": 3911, + "965-unions-retag.ts": 2955, + "966-unions-retag-rc-stress.ts": 3416, + "967-record-field-widening.ts": 3252, + "969-unions-arm-matrix.ts": 3502, + "970-unions-basics.ts": 3588, + "971-unions-switch.ts": 3755, + "972-unions-nested.ts": 2659, + "973-unions-rc-stress.ts": 3021, + "974-unions-modules/main.ts": 3329, + "975-unions-undefined.ts": 3414, + "976-unions-null.ts": 3240, + "977-unions-unit-rc-stress.ts": 3807, + "978-unions-nullish.ts": 3471, + "979-unions-optional-chaining.ts": 2680, + "980-exceptions-basics.ts": 3823, + "981-exceptions-values.ts": 3366, + "982-exceptions-rc-stress.ts": 3526, + "983-exceptions-control-flow.ts": 3306, + "984-exceptions-finally.ts": 3693, + "985-exceptions-call-chains.ts": 3523, + "986-exceptions-uncaught.ts": 3377, + "987-exceptions-result-unions.ts": 3446, + "990-process-basics.ts": 3175, + "991-process-exit.ts": 3461, + "992-fs-roundtrip.ts": 3411, + "993-fs-readdir.ts": 3612, + "994-fs-errors.ts": 3748, + "995-fs-uncaught.ts": 3089, + "996-fs-rc-stress.ts": 3714, + "997-fs-modules/main.ts": 3327, + "998-process-env.ts": 2498, + "abstract-property-dispatch.ts": 4365, + "arguments-arrow-capture.mjs": 3544, + "arguments-reexport/main.ts": 3373, + "array-argument-lifetimes-exits.ts": 2870, + "array-argument-lifetimes-mutation.ts": 3721, + "array-argument-lifetimes-strings.ts": 3794, + "array-argument-lifetimes-values.ts": 4076, + "array-buffer-conversions.js": 3267, + "array-buffer-storage.ts": 3982, + "array-checked-callback-results.mjs": 3822, + "array-checked-constructor.js": 3361, + "array-concat-widening.ts": 3515, + "array-deque-unshift.ts": 4273, + "array-element-compound-updates.ts": 5390, + "array-fill-unit-values.ts": 3744, + "array-from-checked.js": 4644, + "array-from-value.mjs": 3261, + "array-isarray-value/main.js": 3569, + "array-numeric-own-keys.ts": 3719, + "array-optional-number-write-index.ts": 3067, + "array-primitive-ordering.ts": 4670, + "array-queue-head-room.ts": 5069, + "array-read-helper-effects.ts": 3808, + "array-refined-storage.ts": 3910, + "array-sort-dense-effects.ts": 4754, + "array-sort-dense-values.ts": 4991, + "array-sort-holes-published.ts": 12914, + "array-sort-runs.ts": 11006, + "array-sort-runtime-comparator.ts": 7044, + "array-sort-unknown-comparator.ts": 2921, + "array-storage-transitions.ts": 4555, + "array-truncate-reuse.ts": 3342, + "async-backend-factory.mjs": 3685, + "async-local-typed-results.ts": 4110, + "atomics-integer-operations.ts": 4324, + "bigint-checked-casts.ts": 3538, + "bigint-checked-storage.js": 3582, + "bigint-union-conversion.ts": 3627, + "boxed-error-rethrows.mjs": 3074, + "buffer-codec-boundaries.ts": 2997, + "buffer-primitive-predicate.ts": 3212, + "builtin-prototype-method-values.mjs": 4169, + "byte-loop-bounds.ts": 3289, + "byte-number-pipeline.ts": 3830, + "byte-record-windows.ts": 4084, + "call-lifetimes-argument-order.ts": 2814, + "call-lifetimes-control-flow.ts": 3242, + "call-lifetimes-owned-boundaries.ts": 3884, + "call-lifetimes-projections.ts": 3960, + "call-lifetimes-union-values.ts": 3288, + "call-lifetimes-virtual-optionals.ts": 4204, + "callable-module-members.mjs": 3631, + "callable-properties.mjs": 3358, + "callable-property-cycles.js": 2892, + "callable-reexport/main.ts": 3566, + "captured-class-refinement.ts": 3716, + "catch-capture.ts": 3398, + "caught-class-identity.ts": 4607, + "caught-standalone-class.ts": 3366, + "caught-system-error-fields.ts": 3842, + "checked-addition.js": 3129, + "checked-any-addition.ts": 3239, + "checked-array-assignment.ts": 3271, + "checked-array-flat/main.ts": 3753, + "checked-bitwise/main.js": 3435, + "checked-builder-cycles.js": 2781, + "checked-byte-storage/main.ts": 3695, + "checked-callback-options.ts": 3641, + "checked-class-descriptors.mjs": 4373, + "checked-class-factory.mjs": 3282, + "checked-collection-iterators.ts": 3261, + "checked-collection-symbol-methods.ts": 2592, + "checked-dispatch-input-lifetimes.js": 2966, + "checked-increment/main.js": 3466, + "checked-inherited-fields.ts": 3091, + "checked-iterator-consumption.js": 4432, + "checked-numeric-arithmetic.js": 3676, + "checked-object-module-alias.js": 3750, + "checked-optional-calls.mjs": 2819, + "checked-promise-recovery.ts": 3546, + "checked-proxy-callback/main.ts": 3548, + "checked-record-constructor.ts": 3491, + "checked-renderer-defaults.js": 2925, + "checked-rest-function-adapters.js": 3233, + "checked-storage-transitions.js": 2999, + "checked-symbol-array.ts": 3209, + "checked-value-input-lifetimes.ts": 2736, + "class-accessor-returned-function.js": 3222, + "class-array-optional-return.ts": 3320, + "class-bracket-method-calls.js": 3856, + "class-brand-overload.js": 3204, + "class-callback-replacement.js": 4854, + "class-callback-storage.ts": 4827, + "class-checked-composites.ts": 3368, + "class-checked-upcast.ts": 3815, + "class-collection-insertion.ts": 5133, + "class-computed-presence.ts": 4064, + "class-computed-symbols.js": 4475, + "class-covariant-returns.ts": 3062, + "class-curried-factory.mjs": 3903, + "class-data-descriptors.js": 4613, + "class-factory-inherited-statics.mjs": 3165, + "class-factory-parameter-base.mjs": 4404, + "class-factory-record-base.mjs": 5117, + "class-factory-recursive-statics.mjs": 3656, + "class-factory-static-members.mjs": 3238, + "class-function-base/main.js": 3349, + "class-instance-constructor.js": 4347, + "class-interface-protocols.ts": 5479, + "class-interface-refinements.ts": 4411, + "class-json-checked.ts": 3478, + "class-local-function-base.mjs": 2889, + "class-method-spreads.ts": 5065, + "class-narrowed-views.ts": 2591, + "class-property-call-values.mjs": 3049, + "class-prototype-data.js": 3914, + "class-prototype-descriptor-copy.mjs": 3673, + "class-specialized-computed-base.mjs": 4340, + "class-static-data.js": 3321, + "class-subclass-assertions.ts": 3532, + "class-super-surplus-spread.mjs": 3433, + "class-symbol-accessors.ts": 3674, + "class-symbol-cache.mjs": 4312, + "class-value-constructor-exceptions.ts": 2729, + "closed-virtual-dispatch-modules/main.ts": 3375, + "closed-virtual-dispatch.ts": 4025, + "closure-nullable-union-return.ts": 4235, + "closure-union-locals.ts": 3743, + "collection-array-cycles.ts": 3119, + "collection-array-maps.ts": 3580, + "collection-array-missing.ts": 3529, + "collection-array-sets.ts": 3755, + "collection-copy-identities.ts": 3108, + "collection-data-unions.ts": 3651, + "collection-ingestion-mappers.ts": 3521, + "collection-ingestion-protocol/main.ts": 4669, + "collection-ingestion-references.ts": 4710, + "collection-ingestion.ts": 3512, + "collection-storage-copies.ts": 3878, + "collection-value-keys.ts": 3854, + "compound-field-receivers.ts": 3456, + "compound-presence-guards.ts": 3018, + "conditional-tuple-destructuring.ts": 3519, + "console-imports.cjs": 3565, + "console-native-value.js": 2620, + "const-enum-module-cycle/main.ts": 2768, + "constant-callback-dispatch.ts": 3226, + "constant-module-globals/main.ts": 3076, + "constant-number-remainders.ts": 3457, + "constructed-class-dispatch.ts": 3935, + "contextual-array-union-write.ts": 5322, + "contextual-return-layouts.ts": 4143, + "counted-loop-numbers.ts": 3668, + "counted-loop-storage.ts": 2925, + "crypto-hash-optional-input.ts": 3821, + "cycle-generations-long-lived.ts": 2954, + "cycle-growth-yield.ts": 4304, + "date-checked-native.js": 3239, + "declared-index-spread.ts": 4205, + "deep-reference-teardown.ts": 3431, + "default-undefined-parameters.ts": 4155, + "deferred-field-updates.ts": 2817, + "deferred-registry-many.ts": 4413, + "derived-numeric-updates.ts": 3113, + "destructure-call-assignment.js": 2699, + "destructure-inferred-row-defaults.js": 3698, + "destructure-union-arrays.ts": 3868, + "dynamic-function-descriptors.js": 3825, + "error-cause-writes.js": 3664, + "error-inherited-message.mjs": 4115, + "error-inherited-name.mjs": 4088, + "error-property-writes.mjs": 3831, + "error-prototype-tostring.js": 3723, + "error-stack-frames.ts": 3316, + "error-stack-limit.ts": 3521, + "error-tostring-overrides.ts": 2788, + "error-values-inspect.ts": 2913, + "exact-index-round-trip.ts": 3953, + "expression-runtime-values.ts": 3293, + "fiber-switch-stress.ts": 4327, + "fiberless-async-ordering.ts": 4160, + "fiberless-async-unhandled.ts": 2861, + "field-assignment-values.ts": 3670, + "file-handle-values.ts": 3758, + "file-read-storage.ts": 5381, + "file-url-percent-decoding.ts": 3379, + "final-field-borrows.ts": 3428, + "for-multiple-bindings.ts": 2948, + "for-multiple-lexical-bindings.ts": 3036, + "for-of-checked-iterator.js": 3188, + "for-of-subclass-elements.ts": 3369, + "forward-generic-signatures.ts": 3512, + "fresh-array-union-layout.js": 3601, + "frozen-checked-dictionaries.mjs": 2948, + "fs-native-realpath.ts": 3654, + "fs-null-byte-paths-js.cjs": 3914, + "fs-null-byte-paths.ts": 5084, + "fs-read-encodings.ts": 3336, + "fs-read-utf8-edges.ts": 4161, + "fs-read-utf8-handle-forms.ts": 3562, + "fs-remove-readonly.ts": 3293, + "fs-write-string-bytes-union.ts": 2935, + "function-apply-value.mjs": 3607, + "function-constructor-global.mjs": 3295, + "function-constructor-kinds/main.js": 2837, + "function-custom-prototype.js": 3522, + "function-custom-to-string.mjs": 3399, + "function-object-assign.js": 3758, + "function-own-prototype/main.js": 2807, + "generator-checked-protocol.ts": 3503, + "generator-checked-return.js": 3369, + "generator-custom-delegation/main.ts": 5410, + "generator-default-return-pattern.ts": 2666, + "generator-delegation-values.ts": 3515, + "generator-inferred-date.js": 3280, + "generator-mixed-checked-channels.ts": 3467, + "generator-untyped-consumer/main.ts": 2805, + "generic-base-specializations.ts": 3523, + "generic-binding-ownership.ts": 4463, + "generic-class-js-functions/main.ts": 2699, + "generic-class-recovery.ts": 4031, + "generic-namespace-factories/main.ts": 3082, + "generic-nullable-collections.ts": 3376, + "generic-pattern-bindings.ts": 3388, + "generic-record-index.ts": 3789, + "generic-visitor-void.ts": 3751, + "global-accumulator-loops.ts": 3531, + "global-error-subclasses.ts": 2383, + "global-runtime-probes.js": 3149, + "global-stored-probes.js": 3656, + "global-string-cache/main.js": 2944, + "global-symbol-cache/main.js": 3152, + "guarded-native-iterables.ts": 3442, + "heap-input-aliases.ts": 3338, + "heap-input-collections.ts": 3866, + "in-array-runtime-index.ts": 4870, + "indexed-assignment-values.js": 3555, + "indexed-assignment-values.ts": 3263, + "indexed-element-field-reads.ts": 3648, + "inferred-array-assignment-values.mjs": 2599, + "inferred-class-default.mjs": 2738, + "inferred-compound-values.mjs": 3388, + "inferred-conditional-class.mjs": 2376, + "inferred-error-fields.js": 2452, + "inferred-map-patterns.mjs": 3504, + "inferred-method-override.js": 2889, + "inferred-partial-destructuring.mjs": 3489, + "inferred-primitive-properties.mjs": 2842, + "inferred-record-return.mjs": 3292, + "inferred-recursive-record.mjs": 3932, + "inferred-undefined-field.mjs": 2401, + "input-lifetime-intervals.ts": 3651, + "input-lifetimes-modules/main.ts": 3648, + "instanceof-class-record-union.ts": 3935, + "integer-flow-arithmetic.ts": 3336, + "integer-loop-forms.ts": 3549, + "integer-view-control.ts": 2594, + "integer-view-numbers.ts": 3585, + "integer-view-storage.ts": 3667, + "intl-grapheme-conformance/main.ts": 4680, + "intl-grapheme.ts": 4135, + "iterator-typed-returns.ts": 4766, + "js-abort-portable.js": 3961, + "js-array-constructor.js": 3335, + "js-array-default-buffer.js": 2734, + "js-array-iterators.js": 3671, + "js-arrow-bind.cjs": 5214, + "js-buffer-callback-brand.cjs": 4283, + "js-callable-proxy.js": 3329, + "js-chained-native-results.mjs": 3307, + "js-checked-array-iterator.js": 3738, + "js-checked-array-reference.js": 3849, + "js-checked-number-negation.mjs": 3465, + "js-checked-sort-result.js": 2437, + "js-child-error-listener.cjs": 2606, + "js-cjs-callable-snapshots/main.cjs": 3122, + "js-class-added-parameters.js": 3654, + "js-class-checked-overrides.js": 3726, + "js-class-computed-fields.js": 3260, + "js-class-enumeration.js": 4284, + "js-class-field-accessor-override.js": 3902, + "js-class-fluent-overrides.js": 3667, + "js-class-late-method-values.js": 3068, + "js-class-member-dispatch.js": 4117, + "js-class-return-shapes.js": 3935, + "js-class-shadowed-methods.js": 4040, + "js-class-stored-method-receiver.js": 2904, + "js-class-subclass-properties.js": 5036, + "js-class-symbol-spread.cjs": 3637, + "js-computed-prototype-additions.js": 2620, + "js-constructor-returned-callable.js": 2532, + "js-constructor-spread.js": 3024, + "js-dataview-dispatch.js": 3608, + "js-declared-fields.cjs": 5015, + "js-default-listener-arguments.cjs": 2326, + "js-default-options/main.ts": 3910, + "js-deferred-field-initializers.js": 2418, + "js-deferred-registry-effects.js": 3462, + "js-derived-constructor-hoisting.js": 2798, + "js-dormant-array-write.js": 3572, + "js-dormant-overrides.js": 2747, + "js-expando-records.js": 3967, + "js-implicit-call-results.js": 2920, + "js-inferred-callback-exports/main.js": 4360, + "js-inferred-class-array-return.js": 3664, + "js-inferred-object-values.js": 3561, + "js-inherited-field-storage.js": 2906, + "js-inherited-parameter-abi.js": 2442, + "js-iterable-spread.js": 2766, + "js-loose-dynamic-primitives.js": 3050, + "js-map-object-seeds.js": 3816, + "js-member-increment.js": 4372, + "js-module-function-values.js": 3724, + "js-mutable-null-members.js": 3894, + "js-nested-native-descriptors.js": 3332, + "js-normalize-dispatch.js": 3662, + "js-nullish-inherited-fields.mjs": 3226, + "js-object-receiver-storage.js": 3635, + "js-object-returning-constructors.mjs": 3324, + "js-object-shared-arrays.js": 3926, + "js-omitted-typed-arguments.js": 3274, + "js-open-binding-writes.js": 3161, + "js-optional-class-fields.js": 3414, + "js-optional-member-call.js": 3623, + "js-primitive-union-addition.js": 3186, + "js-promise-all-values.js": 3364, + "js-promise-catch-handler.js": 2469, + "js-promise-finally-order.js": 3631, + "js-promise-pending-reactions.js": 3621, + "js-prototype-host-boundary.js": 40863, + "js-reflect-receivers.js": 3717, + "js-regex-record-captures.js": 4129, + "js-scalar-iterables.js": 3831, + "js-shared-method-dispatch.js": 3185, + "js-specialized-field-method.js": 2772, + "js-static-accessors.js": 3403, + "js-static-method-values.js": 3866, + "js-static-registries.cjs": 3795, + "js-super-accessor-initialization.js": 3887, + "js-uninitialized-native-fields.js": 3361, + "json-guarded-map-values.ts": 3542, + "json-output-bulk.ts": 3334, + "json-runtime-options.mjs": 2995, + "json-stringify-value.js": 3147, + "json-typed-parse-direct.ts": 3768, + "json-typed-parse-index-signatures.ts": 3707, + "json-typed-parse-optional-members.ts": 4645, + "json-typed-parse-union-members.ts": 2884, + "literal-switch-dispatch.ts": 3317, + "live-reference-shared-converters.ts": 9504, + "llvm-read-receiver-lifetime.ts": 4439, + "local-class-call-spreads.js": 3630, + "local-class-declaration.ts": 3068, + "local-class-evaluation.ts": 3450, + "local-class-inheritance.ts": 3339, + "local-class-lifetimes.ts": 3623, + "local-class-namespace/main.js": 3916, + "local-class-prototype-data.js": 3797, + "local-class-specialization.js": 3033, + "local-class-unknown.ts": 2900, + "local-class-values.ts": 2584, + "local-union-storage.ts": 3820, + "locale-compare-root-order.ts": 3234, + "logical-assignment-dynamic.js": 3066, + "logical-assignment-values.ts": 3751, + "logical-runtime-optional-record.ts": 3473, + "loop-reference-lifetimes.ts": 2819, + "map-checked-boundary/main.ts": 3663, + "map-checked-seed.js": 3370, + "map-get-set-fusion.ts": 3638, + "map-number-keys-dense.ts": 4412, + "map-read-lifetimes-exits.ts": 2579, + "map-read-lifetimes.ts": 3849, + "map-seeds-and-spreads.ts": 3171, + "map-typed-array-values.ts": 3666, + "math-function-values.js": 2486, + "missing-read-lambda-results.ts": 3345, + "missing-read-narrowed-storage.ts": 3729, + "missing-read-number-conversions.ts": 3696, + "module-byte-loops.ts": 3029, + "module-fallback-functions/main.js": 3321, + "narrowed-nullish-values.ts": 3184, + "native-any-timer-callback.ts": 2884, + "native-array-borrowed-find-last.js": 3352, + "native-array-borrowed-slice.js": 3286, + "native-array-copying-methods.js": 2464, + "native-array-fill-copywithin.js": 2771, + "native-array-frozen-noops.js": 3256, + "native-array-index-boundaries.js": 2917, + "native-array-integrity.js": 2583, + "native-array-length.js": 2372, + "native-array-named-properties.js": 3684, + "native-array-prototype/main.js": 3743, + "native-array-read-lifetimes.ts": 3932, + "native-array-reduce-find-last.js": 2450, + "native-array-search-from-index.js": 2847, + "native-array-splice-coercion.js": 3405, + "native-array-splice-flat.js": 3104, + "native-buffer-coercion.js": 3322, + "native-callback-dispatch.ts": 3832, + "native-class-array-boundaries.ts": 3342, + "native-class-cache.js": 3840, + "native-class-properties.cjs": 5572, + "native-class-property-cycles.cjs": 3155, + "native-class-property-order.cjs": 3170, + "native-class-type-predicates.ts": 3052, + "native-factory-records.js": 3387, + "native-method-values.js": 4924, + "native-method-values.ts": 3868, + "native-npm-imports/main.ts": 3901, + "native-object-descriptors.js": 3247, + "native-object-for-in-minimal.js": 3446, + "native-object-property-attributes.js": 3224, + "native-object-prototype.js": 2952, + "native-property-names.ts": 2920, + "native-proxy-env.js": 4531, + "native-worker-arena-reuse.ts": 9740, + "native-worker-bounded-errors.ts": 3428, + "native-worker-buffer-consumers.ts": 4341, + "native-worker-cancel-hot.ts": 3046, + "native-worker-class-fields.ts": 3987, + "native-worker-clone-errors.ts": 3205, + "native-worker-commonjs.cjs": 3563, + "native-worker-delayed-messages.ts": 3820, + "native-worker-discarded-heaps.ts": 9491, + "native-worker-environment.ts": 3678, + "native-worker-error.ts": 3443, + "native-worker-exception-state.ts": 3795, + "native-worker-exit-listeners.ts": 3786, + "native-worker-io.ts": 3497, + "native-worker-loop-cancellation.ts": 3485, + "native-worker-message-graphs.ts": 3324, + "native-worker-messages.ts": 3248, + "native-worker-poll-exceptions.ts": 3463, + "native-worker-port-lifetime.ts": 3679, + "native-worker-process-exit.ts": 25459, + "native-worker-publish-classes.ts": 39360, + "native-worker-publish-refusals.ts": 3725, + "native-worker-published-graphs.ts": 3934, + "native-worker-references.ts": 4072, + "native-worker-roots/main.ts": 3317, + "native-worker-shared-views.ts": 34203, + "native-worker-stack-overflow.ts": 3534, + "native-worker-terminate-exception-state.ts": 4664, + "native-worker-terminate-loop.ts": 3303, + "native-worker-terminate-recursion.ts": 4325, + "native-worker-terminate-suspended.ts": 3580, + "native-worker-terminate-tested-loops.ts": 3001, + "native-worker-terminate-wait.ts": 3399, + "native-worker-typed-arrays.ts": 3056, + "nested-map-cycles.ts": 2892, + "nested-map-values.ts": 4020, + "never-returning-calls.ts": 3353, + "next-tick-commonjs-entry.cjs": 3537, + "node24-buffer-encoding-errors.ts": 3190, + "node24-buffer-encoding.ts": 3056, + "node24-crypto-kdf-errors.ts": 3983, + "node24-crypto-kdf-options.ts": 3444, + "node24-crypto-kdf.ts": 3815, + "node24-crypto-sha2.ts": 3261, + "node24-deep-equality-properties.js": 3213, + "node24-deep-equality.cjs": 4348, + "node24-deep-equality.ts": 4931, + "node24-env-files-errors.ts": 3190, + "node24-env-files.ts": 3237, + "node24-env-imports.cjs": 2806, + "node24-env-parse-errors.ts": 3175, + "node24-env-parse.ts": 2442, + "node24-esm-metadata/main.mjs": 3178, + "node24-file-url-buffer.ts": 2578, + "node24-file-url-errors.js": 3662, + "node24-file-url-options.ts": 3335, + "node24-fs-links-callbacks.cjs": 2914, + "node24-fs-links-errors.cjs": 2718, + "node24-fs-links-options.cjs": 5212, + "node24-fs-links-promise-errors.cjs": 3623, + "node24-fs-links-promises.ts": 3653, + "node24-fs-links.ts": 2754, + "node24-fs-statfs-callbacks.cjs": 3304, + "node24-fs-statfs-errors.cjs": 3642, + "node24-fs-statfs-options.cjs": 3933, + "node24-fs-statfs.ts": 3769, + "node24-fs-timestamps-callbacks.cjs": 3563, + "node24-fs-timestamps-errors.cjs": 3595, + "node24-fs-timestamps-promises.ts": 4047, + "node24-fs-timestamps-symlinks.ts": 2514, + "node24-fs-timestamps.ts": 3364, + "node24-no-error-assertions-coercion.js": 3593, + "node24-no-error-assertions.cjs": 2425, + "node24-no-error-assertions.ts": 4527, + "node24-parseargs.js": 5067, + "node24-string-decoder.ts": 2945, + "node24-style-text-environment.js": 3145, + "node24-style-text-validation.js": 4525, + "node24-style-text.cjs": 3538, + "node24-style-text.ts": 3894, + "node24-system-error-validation.js": 3199, + "node24-system-errors.cjs": 2805, + "node24-system-errors.ts": 3782, + "node24-text-codec-properties.ts": 3181, + "node24-text-encode-into.ts": 2752, + "node24-text-utilities-coercion.js": 2713, + "node24-text-utilities-validation.js": 3711, + "node24-text-utilities.cjs": 3142, + "node24-text-utilities.ts": 3686, + "node24-url-authority-coercion.js": 3699, + "node24-url-authority.ts": 3174, + "node24-url-factories.ts": 3606, + "node24-url-factory-errors.ts": 3314, + "node24-url-mutation-coercion.js": 3050, + "node24-url-mutation.ts": 2529, + "node24-util-types.cjs": 2859, + "node24-util-types.js": 3337, + "node24-util-types.ts": 3883, + "non-null-class-iteration.ts": 3145, + "nonnull-element-arguments.ts": 2747, + "nonnull-forwarded-class-values.ts": 3637, + "nullable-object-extension.mjs": 3028, + "nullish-asserted-class-arms.ts": 2996, + "nullish-assignment-values.ts": 3685, + "nullish-long-chain.ts": 4539, + "number-parse-statics.ts": 2378, + "number-predicates-unknown.ts": 3263, + "numeric-call-specialization.ts": 3397, + "object-enumeration-array-reads.ts": 3721, + "object-from-checked-entries.js": 4400, + "object-inherited-to-string.js": 3338, + "object-key-indexes.js": 4004, + "object-prototype-identity.mjs": 3277, + "object-reexport/main.js": 3532, + "object-spread-computed.ts": 3618, + "opentui-byte-queue.js": 2761, + "optional-callback-arity.ts": 2368, + "optional-class-flows.ts": 2553, + "optional-element-defaults.ts": 3275, + "optional-flag-comparisons.ts": 3105, + "optional-native-callables.js": 3200, + "optional-node-list-iteration.ts": 3422, + "optional-string-index/main.ts": 2937, + "path-to-file-url-options.ts": 3680, + "performance-presence.ts": 3328, + "primitive-and-function-descriptors.js": 3559, + "primitive-union-string.ts": 3162, + "process-builtin-values.js": 4636, + "process-constrained-memory.ts": 2246, + "process-env-computed-keys.js": 3276, + "process-hrtime.js": 3203, + "process-named-signals.cjs": 3440, + "process-uncaught-handler-throws.cjs": 2812, + "process-uncaught-loop.cjs": 2427, + "process-uncaught-module.mjs": 2982, + "process-uncaught-monitor.cjs": 3049, + "process-uncaught-rejection.cjs": 2978, + "process-uncaught-sync.cjs": 2919, + "process-version-pin.ts": 3275, + "process-versions-storage.ts": 3558, + "projected-collection-lifetimes.ts": 3501, + "promise-checked-rejections.ts": 2387, + "promise-resolve-void.ts": 2676, + "prototype-method-overrides.mjs": 7058, + "prototype-named-data-records.ts": 3518, + "rc-last-use-moves.ts": 4127, + "rc-private-array-writes.ts": 4075, + "record-field-presence.ts": 4445, + "record-key-order.ts": 2786, + "record-optional-json-presence.ts": 3499, + "record-presence-copies.ts": 4175, + "record-presence-delete-json.ts": 2958, + "record-unchecked-read-presence.ts": 3597, + "record-unknown-values.ts": 4381, + "reflect-own-keys.mjs": 2660, + "reflect-record-union-keys.mjs": 2359, + "reflect-typed-record-keys.ts": 2855, + "regex-callback-replacements.ts": 3541, + "regex-callback-values.js": 3624, + "regex-checked-casts.ts": 3867, + "regex-checked-storage.js": 3704, + "regex-exec-long-subject.ts": 8130, + "regex-exec-result-arrays.ts": 4036, + "regex-fs-text-surfaces.ts": 4013, + "regex-input-lifetimes.ts": 2860, + "regex-native-matcher.ts": 4866, + "regex-optional-captures.ts": 3208, + "regex-optional-templates.ts": 3554, + "regex-split-captures.ts": 3707, + "regex-text-paths.ts": 2943, + "regexp-constructor-repeat.ts": 3471, + "regexp-optional-constructor.ts": 3547, + "regexp-string-coercion.js": 2902, + "renderer-export-dictionaries.js": 2853, + "renderer-null-state.js": 3392, + "renderer-specializations.js": 3361, + "repeat-and-typed-read-errors.ts": 3813, + "rest-optional-array-arguments.ts": 4116, + "rest-optional-elements.ts": 3072, + "return-void-through-finally.ts": 2672, + "runtime-optional-string-flows.ts": 3420, + "scalar-string-slices.ts": 3228, + "set-checked-methods.js": 3654, + "set-checked-storage.ts": 3542, + "set-iterator-seed-references.ts": 3832, + "set-iterator-seeds.ts": 3864, + "set-optional-seeds.ts": 3189, + "small-collection-transitions.ts": 3703, + "speculative-integer-globals.ts": 2999, + "speculative-integer-loops.ts": 4097, + "speculative-integer-reads.ts": 3960, + "stack-depth-async-bodies.ts": 3593, + "stack-overflow-async.ts": 3327, + "stack-overflow-caught.ts": 3619, + "stack-overflow-finally.ts": 4048, + "stack-overflow-shapes.ts": 4840, + "stack-overflow-uncaught.ts": 3019, + "stackless-async-concurrency.ts": 5553, + "stackless-async-context.ts": 3712, + "stackless-async-exceptions.ts": 4059, + "stackless-async-ordering.ts": 4230, + "stackless-async-unhandled.ts": 3409, + "static-field-updates/main.ts": 3160, + "static-method-receivers.ts": 3269, + "stdio-values.mjs": 3241, + "stdio-write-errors.mjs": 3357, + "stdio-write-overrides.mjs": 3279, + "stored-native-builtins.js": 3593, + "stored-object-helpers.js": 3366, + "stream-dynamic-entry-kinds.cjs": 8165, + "stream-dynamic-entry-kinds.ts": 2619, + "stream-encoded-from-entries.cjs": 4394, + "stream-encoded-from-entries.ts": 4716, + "string-char-code-integer-loops.ts": 3604, + "string-code-points.ts": 2747, + "string-concat-number-parts.ts": 3145, + "string-construction-order.ts": 3430, + "string-field-append.ts": 3228, + "string-lifetimes-boundaries.ts": 3759, + "string-lifetimes-evaluation.ts": 3595, + "string-lifetimes-iteration.ts": 2950, + "string-lifetimes-operations.ts": 3231, + "string-normalize.ts": 3407, + "string-pattern-replacement.ts": 3745, + "string-scan-fast-paths.ts": 3455, + "string-split-checked-separator.mjs": 3539, + "string-split-traversal.ts": 4073, + "structural-class-properties.ts": 3685, + "structured-text-boundaries.js": 4089, + "structured-text-storage.ts": 3938, + "switch-integer-dispatch.ts": 3542, + "symbol-checked-boundary/main.ts": 3823, + "symbol-class-fields.ts": 3105, + "symbol-class-fields/main.cjs": 3306, + "symbol-class-methods/main.ts": 3168, + "symbol-fields-deferred-factory.ts": 2465, + "symbol-fields-inferred.js": 2781, + "symbol-object-properties/main.js": 2729, + "synchronous-callback-lifetimes.ts": 3805, + "terminal-stream-properties.ts": 2859, + "text-codec-values/main.ts": 24961, + "text-codec-var.ts": 35233, + "text-encode-into-boundaries.ts": 2835, + "timers-clear-many.ts": 3438, + "tuple-and-string-search.ts": 3771, + "tuple-array-union.ts": 3450, + "tuple-callback-visitation.ts": 3975, + "tuple-forof-visitation.ts": 3491, + "typed-array-bulk-conversion.mjs": 5922, + "typed-array-byte-io.js": 9466, + "typed-array-checked-index.js": 3008, + "typed-array-constructor-alias.ts": 3321, + "typed-array-constructor-identity.js": 3143, + "typed-array-constructor-union.ts": 2529, + "typed-array-constructor-values.mjs": 3170, + "typed-array-dynamic-methods.js": 4053, + "typed-array-integer-widths.ts": 3519, + "typed-array-nan-coded-reads.ts": 3945, + "typed-array-numeric-import.ts": 4121, + "typed-array-present-reads.ts": 3332, + "typed-array-reversed-storage.ts": 2465, + "typed-array-unknown.ts": 3664, + "typed-collection-iterator-references.ts": 4059, + "typed-collection-iterators.ts": 4014, + "typed-collection-views/main.ts": 2915, + "typed-index-arguments.ts": 3055, + "typed-prototype-methods.ts": 3929, + "typed-rest-callables.ts": 2298, + "typed-rest-filtered-return.ts": 3531, + "typedarray-native-inputs.js": 3581, + "typeof-checked-comparison.js": 2656, + "uint8array-sort-domains.ts": 3223, + "unicode-case-paths.ts": 2409, + "unicode-process-argv.ts": 3053, + "union-discriminant-width.ts": 4248, + "union-literal-scalar-discriminants.ts": 4306, + "union-literal-slot-conversion.ts": 3424, + "union-nested-layout-discriminant.ts": 3343, + "union-optional-semantic-identity.ts": 4104, + "union-record-clone.ts": 3572, + "union-record-structural-copy.ts": 2990, + "union-reference-comparison.ts": 4026, + "unknown-array-holes.ts": 3098, + "unknown-collections.ts": 4172, + "unknown-object-enumeration.ts": 5046, + "unknown-record-views.ts": 2982, + "url-runtime-base.ts": 2879, + "utf8-span-decoding.ts": 37919, + "util-parseargs-wrapped/main.ts": 4014, + "virtual-runtime-optional-returns.ts": 4644, + "weak-map-array-buffer.ts": 3584, + "weak-map-fresh-array.js": 2803, + "weak-map-native.js": 3743, + "weak-native-class-identity.ts": 3008, + "weak-set-native.js": 2800, + "worker-class-instance-clone.ts": 39705 } } diff --git a/tests/harness/coverage.test.ts b/tests/harness/coverage.test.ts index d4582d1512..06f0e75a8b 100644 --- a/tests/harness/coverage.test.ts +++ b/tests/harness/coverage.test.ts @@ -285,7 +285,9 @@ test("runtime-optional refined unions report blockers without crashing", () => { test("a rejected class reached only through a receiver probe reports its own diagnostic once", () => { const { coverage } = analyze(join(repoRoot, "tests/diagnostics/deferred-class-receiver/main.ts")); const messages = coverage.diagnostics.map((d) => d.message); - expect(messages.filter((m) => m.startsWith("'unknown'-typed class fields"))).toHaveLength(1); + expect( + messages.filter((m) => m.startsWith("'WeakMap'-typed class fields")), + ).toHaveLength(1); expect(messages.some((m) => m.startsWith("constructing through a class value"))).toBe(true); expect(coverage.unreached?.diagnostics ?? []).toEqual([]); }); diff --git a/tests/harness/dyncheck.test.ts b/tests/harness/dyncheck.test.ts index ca597c0db6..2ab323f370 100644 --- a/tests/harness/dyncheck.test.ts +++ b/tests/harness/dyncheck.test.ts @@ -764,6 +764,38 @@ console.log("recovered"); expect(r.stdout).toBe("TypeError true\narray: TypeError\nfunc: TypeError\nrecovered\n"); }); + test("a non-null assertion on a null object result throws at the assertion, even when the binding is never read", async () => { + // Node lets the null ride until a member read; the compiled program + // throws the stranded TypeError where `!` extracts the object. + const r = await compileAndRun( + "asserted-null-object-result", + `interface Info { + name: string; + base: Info | null; + callable: boolean; +} +function baseOf(info: Info): Info | null { + return info.base; +} +function describe(info: Info): string { + const base = baseOf(info)!; + if (info.callable) return info.name + ": callable base"; + return info.name + " extends " + base.name; +} +const root: Info = { name: "Root", base: null, callable: false }; +console.log(describe({ name: "Child", base: root, callable: false })); +try { + console.log("unreachable", describe({ name: "Wrapped", base: null, callable: true })); +} catch (e) { + console.log((e as Error).name, (e as Error).message.includes("not representable in the target union")); +} +console.log("recovered"); +`, + ); + expect(r.exitCode).toBe(0); + expect(r.stdout).toBe("Child extends Root\nTypeError true\nrecovered\n"); + }); + test("a checker-approved function mismatch strands per piece: mismatched params compile at the flow, void results trap after the call", async () => { // funcCoerceAdapter's stranded dispositions. Param strand: an array // literal of arrows collapses to one element signature — the diff --git a/tests/harness/lifetime-regions.test.ts b/tests/harness/lifetime-regions.test.ts index 7eb294482e..ac802cb503 100644 --- a/tests/harness/lifetime-regions.test.ts +++ b/tests/harness/lifetime-regions.test.ts @@ -145,9 +145,9 @@ describe.skipIf(process.platform === "win32")("lifetime regions", { timeout: 240 }); test("async work, nesting, holders the walk does not inspect and handles", async () => { - // Objects allocated after an await live in the default region and keep - // the regions they refer into. - expect((await run("5013-lifetime-region-async.ts")).summaries).toEqual([summary(7, 2)]); + // Work that resumes after an await allocates in its region, which is + // freed once that work has finished. + expect((await run("5013-lifetime-region-async.ts")).summaries).toEqual([summary(7, 7)]); const nesting = await run("5014-lifetime-region-nesting.ts"); expect(nesting.summaries).toEqual([summary(4, 2)]); const opaque = await run("5021-lifetime-region-opaque-holders.ts"); @@ -156,6 +156,36 @@ describe.skipIf(process.platform === "win32")("lifetime regions", { timeout: 240 expect((await run("5019-lifetime-region-values.ts")).summaries).toEqual([summary(2, 0)]); }); + test("regions carried across await by concurrent requests are freed", async () => { + const stats = await run("5023-lifetime-region-await.ts"); + expect(stats.summaries).toEqual([summary(15, 15)]); + // Each request's region (eight interleaved ones, then a later one) is + // freed when the request releases it; a region released while its work + // is pending, or while work it started is still running, is freed once + // that work ends, without an attempt that keeps it. + for (const id of [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 15]) + expect( + stats.attempts.filter((a) => a.startsWith(`${id} `)), + `region ${id}`, + ).toEqual([`${id} freed`]); + // The nested region is referenced by the outer one until that is freed; + // the escaping result keeps its region until the cache lets go. + keptThenFreed(stats.attempts, [10, 14]); + }); + + test("regions are carried by stackless and fiber async bodies alike", async () => { + // Stackless coroutine bodies and a fiber body (for await) spawn and + // await each other inside each region; whatever resumes allocates in + // the region, so each one is freed on its single release attempt. + const stats = await run("5024-lifetime-region-await-forms.ts"); + expect(stats.summaries).toEqual([summary(7, 7)]); + for (let id = 1; id <= 7; id++) + expect( + stats.attempts.filter((a) => a.startsWith(`${id} `)), + `region ${id}`, + ).toEqual([`${id} freed`]); + }); + test("weak keys of freed regions do not survive", async () => { const stats = await run("5016-lifetime-region-weak-keys.ts"); expect(stats.summaries).toEqual([summary(5, 4)]); diff --git a/tests/harness/native-frontend.ts b/tests/harness/native-frontend.ts index feee2f5911..475db54aa6 100644 --- a/tests/harness/native-frontend.ts +++ b/tests/harness/native-frontend.ts @@ -65,6 +65,7 @@ export const programs = [ "nullish-long-chain.ts", "class-array-optional-return.ts", "contextual-array-union-write.ts", + "6020-class-extends-function-constructor.js", ]; interface LowerRequest { diff --git a/tests/harness/npm-static.test.ts b/tests/harness/npm-static.test.ts index 89bd2e0b8d..c5021fd062 100644 --- a/tests/harness/npm-static.test.ts +++ b/tests/harness/npm-static.test.ts @@ -912,6 +912,24 @@ describe(`npm-static pilots${sanitize ? " (sanitized)" : ""}`, () => { expect(nativeRes.exitCode).toBe(nodeRes.exitCode); }, 180_000); + // Real CommonJS packages whose require() placement (a binding required + // below code that only reaches it after the require ran) or JSON + // require preflight refused before. + test("export-first CommonJS packages and JSON requires compile statically", () => { + const { coverage } = analyze(join(pilotRoot, "cjs-interop-cli.ts"), { + npmStatic: ["fs.realpath", "on-finished", "ee-first", "statuses"], + }); + expect(coverage.preflightFailed).toBe(false); + expect( + [...(coverage.npmStatic ?? [])].sort((a, b) => a.package.localeCompare(b.package)), + ).toEqual([ + { package: "ee-first", status: "static" }, + { package: "fs.realpath", status: "static" }, + { package: "on-finished", status: "static" }, + { package: "statuses", status: "static" }, + ]); + }, 120_000); + // A package preflight failure stays visible as a package-specific refusal. test("a preflight-refused package reports its native refusal", () => { const { coverage } = analyze(join(fixturesRoot, "npm/divergent/lazybuiltin.ts"), { diff --git a/tests/harness/project-config.test.ts b/tests/harness/project-config.test.ts index bbd6045e59..bf57a830ae 100644 --- a/tests/harness/project-config.test.ts +++ b/tests/harness/project-config.test.ts @@ -440,6 +440,42 @@ test("node-types: promisified zlib one-shots settle from the worker-pool codec", expect(comparableStderr(native.stderr)).toBe(node.stderr); }); +test("node-types: promisified crypto callback functions settle with the callback value", async () => { + const outDir = outDirFor("promisify-crypto"); + const entry = join(nodeTypesDir, "promisify-crypto.ts"); + const result = await compile(entry, { + outPath: join(outDir, "promisify-crypto"), + outDir, + sanitize, + }); + expect(result.ok, !result.ok ? JSON.stringify(result.diagnostics, null, 2) : "").toBe(true); + if (!result.ok) return; + const [native, node] = await Promise.all([ + execFileAsync(result.binaryPath), + execFileAsync(process.execPath, ["--disable-warning=MODULE_TYPELESS_PACKAGE_JSON", entry]), + ]); + expect(native.stdout).toBe(node.stdout); + expect(comparableStderr(native.stderr)).toBe(node.stderr); +}); + +test("node-types: promisified dns lookups settle with the lookup objects", async () => { + const outDir = outDirFor("promisify-dns"); + const entry = join(nodeTypesDir, "promisify-dns.ts"); + const result = await compile(entry, { + outPath: join(outDir, "promisify-dns"), + outDir, + sanitize, + }); + expect(result.ok, !result.ok ? JSON.stringify(result.diagnostics, null, 2) : "").toBe(true); + if (!result.ok) return; + const [native, node] = await Promise.all([ + execFileAsync(result.binaryPath), + execFileAsync(process.execPath, ["--disable-warning=MODULE_TYPELESS_PACKAGE_JSON", entry]), + ]); + expect(native.stdout).toBe(node.stdout); + expect(comparableStderr(native.stderr)).toBe(node.stderr); +}); + test("node-types: unsupported child stdin forms remain named fences", async () => { const dir = outDirFor("node-child-stdin-fences"); const entry = join(nodeTypesDir, "child-stdin-fences.ts"); @@ -462,6 +498,40 @@ test("node-types: unsupported child stdin forms remain named fences", async () = ]); }); +test("node-types: callback exec uses the typed ExecException overloads", async () => { + const dir = outDirFor("node-child-exec"); + const entry = join(nodeTypesDir, "child-exec.ts"); + const result = await compile(entry, { + outPath: join(dir, "child-exec"), + outDir: dir, + sanitize, + }); + expect(result.ok, !result.ok ? JSON.stringify(result.diagnostics, null, 2) : "").toBe(true); + if (!result.ok) return; + const { stdout } = await execFileAsync(result.binaryPath); + expect(stdout).toBe('typed-exec err\n"Command failed: exit 2\\n"\n'); +}); + +test("node-types: exec's numeric ExecException.code remains a named fence", async () => { + const dir = outDirFor("node-child-exec-code-fences"); + const entry = join(nodeTypesDir, "child-exec-code-fences.ts"); + const result = await compile(entry, { + outPath: join(dir, "child-exec-code-fences"), + outDir: dir, + sanitize, + }); + expect(result.ok).toBe(false); + if (result.ok) return; + expect(result.diagnostics.map((diagnostic) => diagnostic.code)).toEqual([ + "SC2020", + "SC2020", + "SC2020", + ]); + for (const diagnostic of result.diagnostics) { + expect(diagnostic.message).toContain("ExecException.code"); + } +}); + test("node-types: path and os lower statically under @types/node's shapes", async () => { const outDir = outDirFor("node-path-os"); const result = await compile(join(nodeTypesDir, "path-os.ts"), { diff --git a/tests/harness/self-hosting-range-cases.ts b/tests/harness/self-hosting-range-cases.ts index 05d6a1fe62..ed2b24ca9e 100644 --- a/tests/harness/self-hosting-range-cases.ts +++ b/tests/harness/self-hosting-range-cases.ts @@ -375,7 +375,8 @@ export function integerRangeCases(): RangeCase[] { ], }, ], - [proof(cursor, 0, 80), proof(decoded, 0, 4294967295), proof(head, 0, 124)], + // Passes enter at 0, 24, 48, 72 and 96 (at most 99), so the failing test reads at most 123. + [proof(cursor, 0, 80), proof(decoded, 0, 4294967295), proof(head, 0, 123)], (func) => { for (const id of ["cursor", "decoded"]) func.locals.push({ id, name: id, type: F64, mutable: true }); diff --git a/tests/harness/windows-differential.test.ts b/tests/harness/windows-differential.test.ts index 7e6ed205d2..dc199d38f2 100644 --- a/tests/harness/windows-differential.test.ts +++ b/tests/harness/windows-differential.test.ts @@ -149,6 +149,8 @@ const WINDOWS_SKIPS: Record = { "posix-shaped: the uncaught sh ENOENT throw crashes both sides, rendered differently (1645 covers env here)", "1464-env-writes.ts": 'posix-shaped: the observing child is ENOENT on Windows, exposing the documented spawn-failure ""-vs-null stance', + "5627-exec-callback.ts": + "posix-shaped: exec runs /bin/sh -c (execSync's static shell lowering); Windows Node runs cmd.exe", // Two programs the events unit's win32 arm surfaced (they compiled for // the first time once the events gate lifted): both drive their signal // listeners through a /bin/sh child, ENOENT on Windows Node too. diff --git a/tests/harness/zero-cost-gate.test.ts b/tests/harness/zero-cost-gate.test.ts new file mode 100644 index 0000000000..0b08da7487 --- /dev/null +++ b/tests/harness/zero-cost-gate.test.ts @@ -0,0 +1,160 @@ +import { spawnSync } from "node:child_process"; +import { expect, test } from "vitest"; +import { + cachegrindCounts, + compareArtifact, + compareCounts, + compareExecutables, + dynamicTierSymbols, + nmOutput, + programVerdict, +} from "../../scripts/zero-cost-compare.mjs"; + +const ir = (functions: object[], globals: object[]) => + Buffer.from(JSON.stringify({ irVersion: 16, functions, globals, sourceFile: "/p/main.ts" })); + +const llvm = (body: string, extra = "") => + Buffer.from( + [ + "; Generated by scriptc (LLVM backend) from /p/main.ts. Do not edit.", + '@.str = private constant [3 x i8] c"hi\\00"', + "define internal i64 @add(i64 %a, i64 %b) {", + ` ${body}`, + "}", + 'define void @"main.$1"() { ret void }', + extra, + "declare void @scr_print(ptr)", + "", + ].join("\n"), + ); + +test("the zero-cost gate names the first differing IR and LLVM unit", () => { + const f = { name: "f", params: [], body: [{ kind: "return" }] }; + const g = { name: "g", params: [], body: [] }; + const base = ir([f, g, f], [{ name: "a", type: "i64" }]); + expect(compareArtifact("ir", base, Buffer.from(base))).toEqual({ identical: true }); + + const changed = compareArtifact( + "ir", + base, + ir([f, { ...g, params: [1] }, f], [{ name: "a", type: "i64" }]), + ); + expect(changed).toMatchObject({ + identical: false, + first: "changed g", + changed: ["g"], + added: [], + module: null, + }); + + const header = compareArtifact("ir", base, ir([f, g, f], [{ name: "a", type: "f64" }])); + expect(header).toMatchObject({ first: "module globals[0:a]", changed: [] }); + // Repeated names are matched by occurrence, so an insertion shows as added. + expect(compareArtifact("ir", base, ir([f, g, f, f], [{ name: "a", type: "i64" }]))).toMatchObject( + { + first: "added f#3", + }, + ); + + const ll = llvm("%r = add i64 %a, %b\n ret i64 %r"); + expect(compareArtifact("llvm", ll, Buffer.from(ll))).toEqual({ identical: true }); + expect(compareArtifact("llvm", ll, llvm("%r = sub i64 %a, %b\n ret i64 %r"))).toMatchObject({ + first: "changed add", + changed: ["add"], + functions: { baseline: 2, candidate: 2 }, + }); + expect( + compareArtifact( + "llvm", + ll, + llvm("%r = add i64 %a, %b\n ret i64 %r", "define void @new() { ret void }"), + ), + ).toMatchObject({ first: "added new", added: ["new"] }); + const global = compareArtifact("llvm", ll, Buffer.from(ll.toString().replace("hi", "ho"))); + expect(global).toMatchObject({ first: "module line 2", changed: [] }); + expect(global.module.candidate).toContain("ho"); +}); + +test("the zero-cost gate compares executables and instruction counts per function", () => { + const nm = + "0000000000001000 0000000000000010 T main\n0000000000001010 0000000000000020 t scr_add\n"; + const resized = compareExecutables( + Buffer.from("abcd"), + Buffer.from("abXd"), + nm, + nm.replace("0000000000000020 t", "0000000000000030 t"), + ); + expect(resized).toMatchObject({ identical: false, first: "resized scr_add", offset: 2 }); + + const output = (main: number, prefault: number, loader: number) => + [ + "events: Ir", + "fl=???", + "fn=main", + `0 ${main}`, + "fn=scr_sa_prefault", + `0 ${prefault}`, + "fn=_dl_lookup_symbol_x", + `0 ${loader}`, + "fn=scr_add", + "0 7", + "0 3", + ].join("\n"); + const own = new Set(["main", "scr_add", "scr_sa_prefault"]); + const base = cachegrindCounts(output(100, 50, 9000), own); + expect([...base]).toEqual([ + ["main", 100], + ["scr_add", 10], + ]); + expect(compareCounts(base, cachegrindCounts(output(100, 75, 9300), own)).identical).toBe(true); + expect(compareCounts(base, cachegrindCounts(output(104, 50, 9000), own))).toMatchObject({ + identical: false, + total: { baseline: 110, candidate: 114 }, + deltas: [{ name: "main", delta: 4 }], + }); +}); + +test("the zero-cost gate throws when nm does not list a binary's symbols", () => { + const ok = { status: 0, signal: null, stdout: "0000000000001000 T main\n", stderr: "" }; + expect(nmOutput("prog", ok)).toBe(ok.stdout); + const missing = spawnSync("scriptc-missing-nm", ["prog"], { encoding: "utf8" }); + expect(() => nmOutput("prog", missing)).toThrow(/^nm prog: .*ENOENT/); + expect(() => + nmOutput("prog", { status: null, signal: "SIGKILL", stdout: "", stderr: "" }), + ).toThrow("nm prog: killed by SIGKILL"); + expect(() => + nmOutput("prog", { status: 1, signal: null, stdout: "", stderr: "nm: prog: no symbols\n" }), + ).toThrow("nm prog: exited with status 1: nm: prog: no symbols"); +}); + +test("the zero-cost gate recognizes dynamic-tier runtime symbols", () => { + expect( + dynamicTierSymbols([ + "main", + "scr_dyn_release", + "scr_dyn_obj_drop_keys", + "scr_dyn_this_push_dyn", + "scr_js_load_ic", + "scr_dyn_obj_read", + "scr_dyn_obj_read", + ]), + ).toEqual(["scr_dyn_obj_read", "scr_js_load_ic"]); +}); + +test("the zero-cost gate fails differing programs unless they match the signature", () => { + const ok = { ok: true }; + const same = { ir: { identical: true }, exe: { identical: true } }; + const differs = { ir: { identical: true }, exe: { identical: false } }; + const verdict = ( + artifacts: object, + allowed = false, + baseline: object = ok, + candidate: object = ok, + ) => programVerdict({ baseline, candidate, artifacts, allowed }); + expect(verdict(same)).toEqual({ verdict: "identical", fails: false }); + expect(verdict(differs)).toEqual({ verdict: "differs", fails: true }); + expect(verdict(differs, true)).toEqual({ verdict: "allowed", fails: false }); + const failed = { ok: false, error: "SC1090" }; + expect(verdict({}, false, failed, failed)).toEqual({ verdict: "both-fail", fails: false }); + expect(verdict({}, true, ok, failed)).toEqual({ verdict: "build-error", fails: true }); +});