From c59f90054d68699f84e39b2152c11caa86bb938d Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Mon, 19 Oct 2026 01:59:04 -0500 Subject: [PATCH 1/6] Enforce maxBuffer and validate timeout in child_process The sync forms (execSync, execFileSync) and spawnSync apply maxBuffer to both outputs together, with Node's 1 MiB default and 0 meaning no limit. Past it the child gets the kill signal, every stdio pipe is closed so a grandchild holding one cannot keep the call waiting, and the call throws Node's ENOBUFS spawnSync error (spawnSync reports it on result.error). Pipes are also closed on a timeout kill. exec and execFile, in the callback and promisified forms, apply it per stream as lib/child_process.js does: the chunk that passes the limit is cut to the remaining UTF-16 length, both streams are destroyed and the child is killed, and the error is a RangeError with code ERR_CHILD_PROCESS_STDIO_MAXBUFFER and cmd (plus stdout and stderr when promisified). A negative or NaN maxBuffer, or a timeout that is not a non-negative integer, throws ERR_OUT_OF_RANGE before anything spawns. The promisified forms throw it synchronously, as Node's custom promisify calls execFile directly. --- .../src/generated/node-v24-compatibility.json | 10 +- .../generated/node-v24-backlog.json | 19 +- .../generated/node-v24-internal.json | 14 +- internal/compatibility/static-support.json | 1 + .../compiler/src/backend/llvm/lib-process.ts | 9 +- .../compiler/src/backend/llvm/lib-shared.ts | 4 +- .../lowering/builtins/child-process-async.ts | 54 +- .../lowering/builtins/child-process-sync.ts | 14 +- .../lowering/builtins/child-process.ts | 33 +- .../src/frontend/lowering/surfaces.ts | 2 +- packages/compiler/src/ir/builtin-effects.ts | 7 + .../compiler/src/ir/builtin-signatures.ts | 28 +- packages/compiler/src/ir/validate.test.ts | 1 + packages/compiler/surface-manifest.json | 2 +- .../corpus/5637-child-max-buffer-sync.ts.json | 6 + .../5638-child-max-buffer-async.ts.json | 6 + packages/runtime/src/scr_child.c | 484 ++++++++++++++++-- packages/runtime/src/scr_child.h | 23 +- tests/corpus/5637-child-max-buffer-sync.ts | 77 +++ tests/corpus/5638-child-max-buffer-async.ts | 100 ++++ tests/diagnostics/child-process.ts | 2 - .../__snapshots__/child-process.ts.txt | 45 +- tests/harness/corpus-costs.json | 4 + 23 files changed, 793 insertions(+), 152 deletions(-) create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5637-child-max-buffer-sync.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5638-child-max-buffer-async.ts.json create mode 100644 tests/corpus/5637-child-max-buffer-sync.ts create mode 100644 tests/corpus/5638-child-max-buffer-async.ts diff --git a/docs/src/generated/node-v24-compatibility.json b/docs/src/generated/node-v24-compatibility.json index 0e6387ea9a..fda9596668 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": 18, + "partial": 19, "refused": 0, - "not-implemented": 21, + "not-implemented": 20, "by-design": 0, "unreviewed": 0, "not-applicable": 0 @@ -9519,9 +9519,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/internal/compatibility/generated/node-v24-backlog.json b/internal/compatibility/generated/node-v24-backlog.json index a0d7bec4b6..542c04b285 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": 1162, - "verify-gap": 2166, + "audit-partial": 1163, + "verify-gap": 2165, "replace-refusal": 41, "classify": 2, "implement": 85 @@ -4221,12 +4221,15 @@ "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.maxBuffer", + "tests": [ + "tests/corpus/5637-child-max-buffer-sync.ts", + "tests/corpus/5638-child-max-buffer-async.ts" + ] } } }, diff --git a/internal/compatibility/generated/node-v24-internal.json b/internal/compatibility/generated/node-v24-internal.json index cfbde89403..60d5afc447 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": 18, + "partial": 19, "refused": 0, - "not-implemented": 21, + "not-implemented": 20, "by-design": 0, "unreviewed": 0, "not-applicable": 0 @@ -11674,6 +11674,8 @@ "tests/corpus/3131-child-rest-listeners.js", "tests/corpus/3132-child-generic-stream-completion.ts", "tests/corpus/5627-exec-callback.ts", + "tests/corpus/5637-child-max-buffer-sync.ts", + "tests/corpus/5638-child-max-buffer-async.ts", "tests/fixtures/node-types/child-exec.ts", "tests/harness/project-config.test.ts" ] @@ -12029,8 +12031,12 @@ "inherited": true }, "static": { - "status": "not-implemented", - "evidence": "compiler-unmatched:child_process" + "status": "partial", + "evidence": "compiler-feature:child_process.maxBuffer", + "tests": [ + "tests/corpus/5637-child-max-buffer-sync.ts", + "tests/corpus/5638-child-max-buffer-async.ts" + ] }, "anchorSource": "exact" }, diff --git a/internal/compatibility/static-support.json b/internal/compatibility/static-support.json index 2e5eb1c4c8..872280560d 100644 --- a/internal/compatibility/static-support.json +++ b/internal/compatibility/static-support.json @@ -324,6 +324,7 @@ { "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": ["`maxBuffer` and Unicode"], "status": "partial", "evidence": ["tests/corpus/5637-child-max-buffer-sync.ts", "tests/corpus/5638-child-max-buffer-async.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"] }, diff --git a/packages/compiler/src/backend/llvm/lib-process.ts b/packages/compiler/src/backend/llvm/lib-process.ts index 3c35ae6dc2..7b9407a866 100644 --- a/packages/compiler/src/backend/llvm/lib-process.ts +++ b/packages/compiler/src/backend/llvm/lib-process.ts @@ -20,14 +20,17 @@ export function emitChildProcessLibCall(host: LlvmEmitterContext, e: LibCallExpr const cb = host.emitExpr(callbackArg); host.moveTemp(cb); const shell = host.emitExpr(e.args[3]!); + const maxBuffer = host.emitExpr(e.args[4]!); const adapter = host.execFileThunkFor(cbT); host.usesTimers = true; - host.declare(`declare ptr @scr_exec_file(ptr, ptr, ptr, ptr, i1)`); + host.declare(`declare ptr @scr_exec_file(ptr, ptr, ptr, ptr, i1, double)`); const out = B.tmp(); B.line( - `${out} = call ptr @scr_exec_file(ptr ${cmd.name}, ptr ${argv.name}, ptr ${cb.name}, ptr @${adapter}, i1 ${shell.name})`, + `${out} = call ptr @scr_exec_file(ptr ${cmd.name}, ptr ${argv.name}, ptr ${cb.name}, ptr @${adapter}, i1 ${shell.name}, double ${maxBuffer.name})`, ); - return host.own({ name: out, type: e.type }); + const owned = host.own({ name: out, type: e.type }); + host.emitPendingCheck(); + return owned; } if ( e.fn === "cp.spawn" || diff --git a/packages/compiler/src/backend/llvm/lib-shared.ts b/packages/compiler/src/backend/llvm/lib-shared.ts index 8d758a0dba..0afb9baad0 100644 --- a/packages/compiler/src/backend/llvm/lib-shared.ts +++ b/packages/compiler/src/backend/llvm/lib-shared.ts @@ -691,7 +691,8 @@ export const LIB_FN_SYMS: Record = { "process.offSignal": "scr_signal_off", "process.offExit": "scr_process_off_exit", // child_process (scr_child.c): spawnSync/exec forms are synchronous - // (execSync/execCapture throw — the may-throw check); handle getters + // (spawnSync, execSync, execCapture and execValidate throw — the + // may-throw check); handle getters // are pure reads. Registration forms are special-cased (moves + per- // shape adapters); spawn itself is special-cased for usesTimers. "cp.spawnSync": "scr_spawn_sync", @@ -699,6 +700,7 @@ export const LIB_FN_SYMS: Record = { "cp.spawnSyncStdioStr": "scr_spawn_sync_stdio_str", "cp.execSync": "scr_exec_sync", "cp.execCapture": "scr_exec_capture", + "cp.execValidate": "scr_exec_validate", "spawnRes.stdout": "scr_spawn_res_stdout", "spawnRes.stderr": "scr_spawn_res_stderr", "child.killed": "scr_child_killed", diff --git a/packages/compiler/src/frontend/lowering/builtins/child-process-async.ts b/packages/compiler/src/frontend/lowering/builtins/child-process-async.ts index 04b3caba83..1f717dde5f 100644 --- a/packages/compiler/src/frontend/lowering/builtins/child-process-async.ts +++ b/packages/compiler/src/frontend/lowering/builtins/child-process-async.ts @@ -13,6 +13,7 @@ import { SPAWNRES_T, STRING, type SrcLoc, + VOID, arrayOf, } from "../../../ir/ir.js"; import { optionMember } from "./arguments.js"; @@ -28,7 +29,8 @@ import { execResultShape } from "../../type-mapper.js"; * promisified execFile/exec behind an already-settled promise (the * fs/promises stance, divergence 23). Options are the exec-sync slice minus stdio (the async form always * captures both streams, no echo): encoding must spell utf8, cwd/env/ - * timeout lower, maxBuffer/windowsHide are accepted no-ops, killSignal + * timeout lower, maxBuffer applies per stream (Node's RangeError + * ERR_CHILD_PROCESS_STDIO_MAXBUFFER), windowsHide is a no-op, killSignal * only as its SIGTERM default. Rejections carry Node's async messages * ("Command failed: \n" — the trailing newline is Node's — * and "spawn ENOENT" with .code); Node's numeric `.code` on a @@ -85,6 +87,8 @@ export function lowerExecFileAsyncCall( let cwd: IrExpr = emptyStr; let env: IrExpr = envRec(false, emptyPairs()); let timeout: IrExpr = numLit(0, loc); + // Node's default maxBuffer for exec and execFile. + let maxBuffer: IrExpr = numLit(1024 * 1024, loc); if (optsNode) { if (!ts.isObjectLiteralExpression(optsNode)) { lowerer.noLowering( @@ -161,7 +165,7 @@ export function lowerExecFileAsyncCall( timeout = lowerer.lowerExprExpecting(m.value, F64); break; case "maxBuffer": - lowerer.lowerExpr(m.value); // accepted, not enforced (divergence 50) + maxBuffer = lowerer.lowerExprExpecting(m.value, F64); break; case "windowsHide": lowerer.lowerExpr(m.value); // Node no-op on POSIX @@ -186,18 +190,51 @@ export function lowerExecFileAsyncCall( } } } + const resultT: IrType = { kind: "promise", inner: { kind: "record", shapeId: helper.shapeId } }; + const args = [cmd, argv, cwd, env, timeout, maxBuffer]; + if (literalOptionsValid(timeout, maxBuffer)) { + return { kind: "call", callee: helper.name, args, type: resultT, loc }; + } + // Node's promisified forms call execFile synchronously, so an invalid + // timeout or maxBuffer throws before the promise exists: the arguments + // are evaluated in order, checked, then passed to the helper. + const types = [STRING, arrayOf(STRING), STRING, envRecT, F64, F64]; + const stmts: IrStmt[] = []; + const saved = args.map((init, i) => { + const local = lowerer.declareHiddenLocal("%execArg", types[i]!); + stmts.push({ kind: "varDecl", localId: local.id, init, loc }); + return varRef(local.id, types[i]!, loc); + }); + stmts.push({ + kind: "exprStmt", + expr: { kind: "libCall", fn: "cp.execValidate", args: [saved[4]!, saved[5]!], type: VOID, loc }, + loc, + }); return { - kind: "call", - callee: helper.name, - args: [cmd, argv, cwd, env, timeout], - type: { kind: "promise", inner: { kind: "record", shapeId: helper.shapeId } }, + kind: "seqExpr", + stmts, + result: { kind: "call", callee: helper.name, args: saved, type: resultT, loc }, + type: resultT, loc, }; } +/** Literal options Node's validateTimeout and validateMaxBuffer accept, + * which need no check before the call. */ +function literalOptionsValid(timeout: IrExpr, maxBuffer: IrExpr): boolean { + return ( + timeout.kind === "numLit" && + Number.isSafeInteger(timeout.value) && + timeout.value >= 0 && + maxBuffer.kind === "numLit" && + maxBuffer.value >= 0 + ); +} + /** The interned `%execFileAsync` helper behind promisified-execFile * calls (`%execAsync`, with `shell`, behind promisified-exec ones): an - * ASYNC IR function (params: cmd, argv, cwd, env {has, pairs}, timeoutMs) + * ASYNC IR function (params: cmd, argv, cwd, env {has, pairs}, timeoutMs, + * maxBuffer) * whose body runs the may-throw cp.execCapture libCall — the async * machinery turns a throw into the rejection, Node's promisified * behavior — and returns the `{ stdout, stderr }` record built from the @@ -253,6 +290,7 @@ function execFileAsyncHelper( loc, }, varRef("timeout.0", F64, loc), + varRef("maxBuffer.0", F64, loc), ], type: SPAWNRES_T, loc, @@ -299,6 +337,7 @@ function execFileAsyncHelper( { localId: "cwd.0", name: "cwd", type: STRING }, { localId: "env.0", name: "env", type: envRecT }, { localId: "timeout.0", name: "timeout", type: F64 }, + { localId: "maxBuffer.0", name: "maxBuffer", type: F64 }, ], returnType: recT, async: true, @@ -308,6 +347,7 @@ function execFileAsyncHelper( { id: "cwd.0", name: "cwd", type: STRING, mutable: false }, { id: "env.0", name: "env", type: envRecT, mutable: false }, { id: "timeout.0", name: "timeout", type: F64, mutable: false }, + { id: "maxBuffer.0", name: "maxBuffer", type: F64, mutable: false }, { id: "r.0", name: "r", type: SPAWNRES_T, mutable: false }, ], body, diff --git a/packages/compiler/src/frontend/lowering/builtins/child-process-sync.ts b/packages/compiler/src/frontend/lowering/builtins/child-process-sync.ts index bfa2bc99a8..951809ee43 100644 --- a/packages/compiler/src/frontend/lowering/builtins/child-process-sync.ts +++ b/packages/compiler/src/frontend/lowering/builtins/child-process-sync.ts @@ -26,8 +26,8 @@ import { optionMember } from "./arguments.js"; * are drawn from the honestly-implemented set — `encoding` (must be the * "utf8"/"utf-8" literal, like spawnSync — outputs are captured utf8), * `cwd`, `env`, `input`, `timeout`, `stdio` (the "pipe"/"ignore" - * string form or a 3-tuple of those), `maxBuffer` (accepted, not - * enforced — the capture grows), `killSignal` (accepted only as the + * string form or a 3-tuple of those), `maxBuffer` (Node's 1 MiB default + * otherwise; 0 means no limit), `killSignal` (accepted only as the * SIGTERM default), `windowsHide`/`shell:false` on execFileSync (Node * no-ops here). Every other member (a non-default killSignal, shell:true * on execFileSync, ...) fences by name. */ @@ -99,6 +99,8 @@ export function lowerExecSyncCall( let hasEnv: IrExpr = boolLit(false, loc); let envPairs: IrExpr = emptyPairs; let timeout: IrExpr = numLit(0, loc); + // Node's default maxBuffer for the sync forms. + let maxBuffer: IrExpr = numLit(1024 * 1024, loc); let stdoutMode = 1; let stderrMode = 0; let stdinInherit = false; @@ -138,6 +140,7 @@ export function lowerExecSyncCall( runtime.stderrMode, // The runtime options carry a utf8 encoding (others throw). boolLit(false, loc), + runtime.maxBuffer, ], type: STRING, loc, @@ -338,9 +341,7 @@ export function lowerExecSyncCall( applyStdio(m.value); break; case "maxBuffer": - // Accepted, not enforced (the capture grows unbounded — no - // real corpus hits the cap); evaluate for side effects. - lowerer.lowerExpr(m.value); + maxBuffer = lowerer.lowerExprExpecting(m.value, F64); break; case "killSignal": { const t = lowerer.typeOf(m.value); @@ -401,6 +402,7 @@ export function lowerExecSyncCall( numLit(stdoutMode + (stdinInherit ? 4 : 0), loc), numLit(stderrMode, loc), boolLit(!encodingGiven, loc), + maxBuffer, ], type: STRING, loc, @@ -445,6 +447,7 @@ function lowerExecSyncRuntimeOptions( hasInput: IrExpr; cwd: IrExpr; timeout: IrExpr; + maxBuffer: IrExpr; stdoutMode: IrExpr; stderrMode: IrExpr; } | null { @@ -518,6 +521,7 @@ function lowerExecSyncRuntimeOptions( hasInput, cwd: readOpt("cwd", emptyStr, STRING), timeout: readOpt("timeout", { kind: "numLit", value: 0, type: F64, loc }, F64), + maxBuffer: readOpt("maxBuffer", { kind: "numLit", value: 1024 * 1024, type: F64, loc }, F64), stdoutMode: modeCall(1), stderrMode: modeCall(2), }; diff --git a/packages/compiler/src/frontend/lowering/builtins/child-process.ts b/packages/compiler/src/frontend/lowering/builtins/child-process.ts index ecec14fc5b..80c4234709 100644 --- a/packages/compiler/src/frontend/lowering/builtins/child-process.ts +++ b/packages/compiler/src/frontend/lowering/builtins/child-process.ts @@ -731,10 +731,9 @@ export function lowerForkCall(lowerer: Lowerer, expr: ts.CallExpression, loc: Sr * 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. */ + * `encoding: "utf8"` and a numeric `maxBuffer` (Node's 1 MiB default + * otherwise, validated by the runtime) are accepted. Other options remain fenced until their + * lifecycle can share this asynchronous core. */ export function lowerExecFileCall( lowerer: Lowerer, expr: ts.CallExpression, @@ -769,6 +768,8 @@ export function lowerExecFileCall( loc, } : lowerChildArgsArg(lowerer, expr.arguments.length >= 3 ? expr.arguments[1] : undefined, loc); + // Node's default maxBuffer for exec and execFile. + let maxBuffer: IrExpr = numLit(1024 * 1024, loc); const optionsNode = expr.arguments.length === (shell ? 3 : 4) ? expr.arguments[shell ? 1 : 2] : undefined; if (optionsNode) { @@ -780,18 +781,6 @@ export function lowerExecFileCall( "pass encoding and maxBuffer in an inline object literal", ); } - const pureNumber = (node: ts.Expression): boolean => - ts.isNumericLiteral(node) || - (ts.isParenthesizedExpression(node) && pureNumber(node.expression)) || - (ts.isBinaryExpression(node) && - [ - ts.SyntaxKind.PlusToken, - ts.SyntaxKind.MinusToken, - ts.SyntaxKind.AsteriskToken, - ts.SyntaxKind.SlashToken, - ].includes(node.operatorToken.kind) && - pureNumber(node.left) && - pureNumber(node.right)); for (const property of options.properties) { const member = optionMember(property); if (!member) @@ -812,18 +801,12 @@ export function lowerExecFileCall( ); } } else if (member.name === "maxBuffer") { - if (!pureNumber(member.value)) { - lowerer.noLowering( - `${what} with a non-literal maxBuffer`, - member.value, - "pass a numeric literal or literal arithmetic (the native capture grows without enforcing this limit)", - ); - } + maxBuffer = lowerer.lowerExprExpecting(member.value, F64); } else { lowerer.noLowering( `${what} option '${member.name}'`, property, - "only encoding: 'utf8' and a pure numeric maxBuffer are supported in the callback form", + "only encoding: 'utf8' and maxBuffer are supported in the callback form", ); } } @@ -875,7 +858,7 @@ export function lowerExecFileCall( return { kind: "libCall", fn: "cp.execFile", - args: [cmd, argv, callback, boolLit(shell, loc)], + args: [cmd, argv, callback, boolLit(shell, loc), maxBuffer], type: CHILD_T, loc, }; diff --git a/packages/compiler/src/frontend/lowering/surfaces.ts b/packages/compiler/src/frontend/lowering/surfaces.ts index e3b508d869..c00fe43159 100644 --- a/packages/compiler/src/frontend/lowering/surfaces.ts +++ b/packages/compiler/src/frontend/lowering/surfaces.ts @@ -2928,7 +2928,7 @@ export const BUILTIN_MODULE_FENCE_HINTS: Record< child_process: { execFile: "the callback forms execFile(file, callback), execFile(file, args, callback), and execFile(file, args, { encoding: 'utf8', maxBuffer: N }, callback) lower; " + - "maxBuffer is not enforced by the growing native capture; other options and reached no-callback calls remain fenced, while util.promisify(execFile) retains its wider options slice", + "maxBuffer applies per stream as in Node; other options and reached no-callback calls remain fenced, while util.promisify(execFile) retains its wider options slice", }, crypto: { ...Object.fromEntries( diff --git a/packages/compiler/src/ir/builtin-effects.ts b/packages/compiler/src/ir/builtin-effects.ts index fc4c383ff5..ef7b1f5c25 100644 --- a/packages/compiler/src/ir/builtin-effects.ts +++ b/packages/compiler/src/ir/builtin-effects.ts @@ -411,6 +411,13 @@ export const MAY_THROW_LIB_FNS: ReadonlySet = new Set([ "net.connectLookup", "cp.execSync", "cp.execCapture", + // execFile/exec's callback form, the promisified forms' pre-call check + // and spawnSync throw Node's ERR_OUT_OF_RANGE for an invalid timeout or + // maxBuffer before anything spawns. + "cp.execFile", + "cp.execValidate", + "cp.spawnSyncOpts", + "cp.spawnSyncStdioStr", // The client request constructors validate the method token // synchronously (Node's ClientRequest ctor throw: // ERR_INVALID_HTTP_TOKEN). diff --git a/packages/compiler/src/ir/builtin-signatures.ts b/packages/compiler/src/ir/builtin-signatures.ts index 51765286b3..7149d1953f 100644 --- a/packages/compiler/src/ir/builtin-signatures.ts +++ b/packages/compiler/src/ir/builtin-signatures.ts @@ -2291,9 +2291,11 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ * see scr_child.h). Throws Node's exact errors: "Command failed: " * (+ captured stderr) on non-zero exit or signal death, "spawnSync * ENOENT" on spawn failure, "spawnSync ETIMEDOUT" after - * the SIGTERM timeout, with Node's status, signal, output, pid, stdout - * and stderr properties (Buffer outputs when buffers is set: no - * encoding option). Result is the captured utf8 stdout (+1). */ + * the SIGTERM timeout, "spawnSync ENOBUFS" when the outputs + * together pass maxBuffer (the last argument; 0 means no limit), with + * Node's status, signal, output, pid, stdout and stderr properties + * (Buffer outputs when buffers is set: no encoding option). Result is + * the captured utf8 stdout (+1). */ "cp.execSync": { argTypes: [ STRING, @@ -2308,6 +2310,7 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ F64, F64, BOOL, + F64, ], result: STRING, }, @@ -2321,11 +2324,18 @@ export const LIB_FN_SIGS = defineLibFnSignatures({ * ScrSpawnRes container (+1; stdout/stderr strings, status unused). * Called only from the frontend's interned %execFileAsync and * %execAsync ASYNC helpers, whose fiber turns the throw into the - * rejection. */ + * rejection. The last argument is maxBuffer: a stream past it is cut + * there, the child is killed and the capture fails with Node's + * ERR_CHILD_PROCESS_STDIO_MAXBUFFER. */ "cp.execCapture": { - argTypes: [STRING, arrayOf(STRING), BOOL, STRING, BOOL, arrayOf(STRING), F64], + argTypes: [STRING, arrayOf(STRING), BOOL, STRING, BOOL, arrayOf(STRING), F64, F64], result: SPAWNRES_T, }, + /** Node's validateTimeout then validateMaxBuffer (timeout, maxBuffer), + * run before the %execFileAsync/%execAsync helper call: the promisified + * forms call execFile synchronously, so these throw instead of + * rejecting. */ + "cp.execValidate": { argTypes: [F64, F64], result: VOID }, /** child_process.spawn (scr_child.c + the scr_async.c loop): posix_spawnp * with per-slot ignore/inherit/pipe modes (and number output fds), the * all-piped Node default, and the child registered with the event loop, @@ -2377,9 +2387,11 @@ 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. 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 }, + * by the validator/backends. Then exec's shell flag (the argv is + * ["-c", command] and failure messages show the command) and maxBuffer + * (a stream past it is cut there, the child is killed and the callback + * gets Node's ERR_CHILD_PROCESS_STDIO_MAXBUFFER). */ + "cp.execFile": { argTypes: [STRING, arrayOf(STRING), null, BOOL, F64], 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). diff --git a/packages/compiler/src/ir/validate.test.ts b/packages/compiler/src/ir/validate.test.ts index e4f4234118..b550ed5c79 100644 --- a/packages/compiler/src/ir/validate.test.ts +++ b/packages/compiler/src/ir/validate.test.ts @@ -964,6 +964,7 @@ test("library callbacks retain child, specialized, and generic result diagnostic { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc }, { kind: "boolLit", value: true, type: F64, loc }, { kind: "boolLit", value: false, type: BOOL, loc }, + { kind: "numLit", value: 1024 * 1024, type: F64, loc }, ], }; expect(validateModule(expressionModule(expr, [])).map((error) => error.message)).toEqual([ diff --git a/packages/compiler/surface-manifest.json b/packages/compiler/surface-manifest.json index 350a81a2d4..d2c0658a24 100644 --- a/packages/compiler/surface-manifest.json +++ b/packages/compiler/surface-manifest.json @@ -399,7 +399,7 @@ "name": "child_process.execFile", "status": "unsupported", "code": "SC2020", - "note": "the callback forms execFile(file, callback), execFile(file, args, callback), and execFile(file, args, { encoding: 'utf8', maxBuffer: N }, callback) lower; maxBuffer is not enforced by the growing native capture; other options and reached no-callback calls remain fenced, while util.promisify(execFile) retains its wider options slice" + "note": "the callback forms execFile(file, callback), execFile(file, args, callback), and execFile(file, args, { encoding: 'utf8', maxBuffer: N }, callback) lower; maxBuffer applies per stream as in Node; other options and reached no-callback calls remain fenced, while util.promisify(execFile) retains its wider options slice" }, { "id": "node-builtin.child_process.execFileSync", diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5637-child-max-buffer-sync.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5637-child-max-buffer-sync.ts.json new file mode 100644 index 0000000000..6b40828372 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5637-child-max-buffer-sync.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5637-child-max-buffer-sync.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5638-child-max-buffer-async.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5638-child-max-buffer-async.ts.json new file mode 100644 index 0000000000..8ab722dbfc --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5638-child-max-buffer-async.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5638-child-max-buffer-async.ts" + ], + "diags": [] +} diff --git a/packages/runtime/src/scr_child.c b/packages/runtime/src/scr_child.c index 87ead57559..6a5abb6ffd 100644 --- a/packages/runtime/src/scr_child.c +++ b/packages/runtime/src/scr_child.c @@ -174,6 +174,100 @@ static void scr_child_error_sync_fields(ScrError *e, bool has_status, double sta scr_error_set_prop(e, "stderr", 6, err); } +/* ── maxBuffer (lib/child_process.js and spawn_sync.cc) ──────────────── + * Node's default for exec, execFile and the sync forms is 1024 * 1024. */ +#define SCR_EXEC_MAX_BUFFER_DEFAULT 1048576.0 + +/* The bytes at the end of `data` that start a UTF-8 sequence the data + * does not complete (0-3): the part a StringDecoder holds back until the + * next chunk. */ +static size_t scr_utf8_incomplete_tail(const uint8_t *data, size_t len) { + for (size_t back = 1; back <= 3 && back <= len; back++) { + uint8_t b = data[len - back]; + if ((b & 0xC0) == 0x80) continue; + size_t need = (b & 0xE0) == 0xC0 ? 2 : (b & 0xF0) == 0xE0 ? 3 : (b & 0xF8) == 0xF0 ? 4 : 1; + return need > back ? back : 0; + } + return 0; +} + +/* validateTimeout and validateMaxBuffer, in Node's order: a timeout that + * is not a non-negative integer, or a negative or NaN maxBuffer, throws + * Node's ERR_OUT_OF_RANGE before anything spawns. An absent timeout + * arrives as 0. */ +static bool scr_exec_options_valid(double timeout_ms, double max_buffer) { + const char *name, *range; + double value; + if (!(timeout_ms >= 0 && timeout_ms <= 9007199254740991.0 && timeout_ms == floor(timeout_ms))) { + name = "timeout"; + range = "an unsigned integer"; + value = timeout_ms; + } else if (!(max_buffer >= 0)) { + name = "options.maxBuffer"; + range = "a positive number"; + value = max_buffer; + } else { + return true; + } + char received[48]; + scr_num_received(value, received); + char msg[160]; + int len = snprintf(msg, sizeof msg, + "The value of \"%s\" is out of range. It must be %s. Received %s", name, + range, received); + scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE"); + return false; +} + +void scr_exec_validate(double timeout_ms, double max_buffer) { + (void)scr_exec_options_valid(timeout_ms, max_buffer); +} + +/* One chunk of an exec/execFile capture under Node's per-stream maxBuffer + * check. The chunk was appended to data[chunk_start..*len). Node decodes + * each chunk as utf8, adds its byte length to the stream's total + * (*text_len), and once the total exceeds maxBuffer keeps only + * `maxBuffer - previous total` UTF-16 code units of that chunk's text. On + * that overflow the capture is cut accordingly (*len shrinks; a split + * surrogate pair keeps its high half as WTF-8) and the result is true. */ +static bool scr_exec_limit_chunk(uint8_t *data, size_t *len, size_t chunk_start, + double *text_len, double max_buffer) { + size_t carried = scr_utf8_incomplete_tail(data, chunk_start); + size_t text_start = chunk_start - carried; + size_t text_end = *len - scr_utf8_incomplete_tail(data, *len); + if (text_end < text_start) text_end = text_start; + double chunk_len = (double)(text_end - text_start); + double before = *text_len; + *text_len = before + chunk_len; + if (!(*text_len > max_buffer)) return false; + double keep = max_buffer - before; + double units = 0; + size_t at = text_start; + while (at < text_end && units < keep) { + uint8_t b = data[at]; + size_t n = (b & 0xE0) == 0xC0 ? 2 : (b & 0xF0) == 0xE0 ? 3 : (b & 0xF8) == 0xF0 ? 4 : 1; + if (at + n > text_end) n = 1; + double u = n == 4 ? 2 : 1; + if (units + u <= keep) { + at += n; + units += u; + continue; + } + /* One unit left inside an astral character: Node's slice keeps the + * lone high surrogate. */ + uint32_t cp = ((uint32_t)(b & 0x07) << 18) | ((uint32_t)(data[at + 1] & 0x3F) << 12) | + ((uint32_t)(data[at + 2] & 0x3F) << 6) | (uint32_t)(data[at + 3] & 0x3F); + uint32_t hi = 0xD800 + ((cp - 0x10000) >> 10); + data[at] = 0xED; + data[at + 1] = (uint8_t)(0xA0 | ((hi >> 6) & 0x0F)); + data[at + 2] = (uint8_t)(0x80 | (hi & 0x3F)); + at += 3; + break; + } + *len = at; + return true; +} + #ifdef _WIN32 /* ── Windows arm: real children via CreateProcessW ──────────────────── * The POSIX arm below owns the corresponding implementation; this arm @@ -879,6 +973,8 @@ typedef struct { bool timed_out; /* the deadline TerminateProcess fired */ DWORD exit_code; DWORD pid; /* the child's process id (0 when it never ran) */ + bool enobufs; /* the outputs together passed maxBuffer */ + const char *over; /* per-stream maxBuffer: the stream that passed it */ ScrCapBuf out, err; /* always initialized; caller frees */ } ScrWinSyncRes; @@ -889,7 +985,10 @@ typedef struct { static void scr_win_run_sync(ScrStr *cmd, ScrArr *args, const ScrStr *input, int in_mode, int out_mode, int err_mode, const ScrStr *cwd, ScrArr *env_pairs, - double timeout_ms, ScrWinSyncRes *res) { + double timeout_ms, double max_buffer, bool per_stream, + ScrWinSyncRes *res) { + res->enobufs = false; + res->over = NULL; res->spawn_errname = NULL; res->timed_out = false; res->exit_code = 0; @@ -969,11 +1068,28 @@ static void scr_win_run_sync(ScrStr *cmd, ScrArr *args, const ScrStr *input, char *input_owned = NULL; const char *input_bytes = in_open ? scr_str_utf8(input, &input_owned) : NULL; bool exited = false; + double out_text = 0, err_text = 0; double deadline = timeout_ms > 0 ? scr_now_ms() + timeout_ms : 0; for (;;) { bool progress = false; + bool kill_now = false; + size_t out_before = res->out.len, err_before = res->err.len; if (out_open) progress |= scr_win_cap_pump(out_parent, &res->out, &out_open); if (err_open) progress |= scr_win_cap_pump(err_parent, &res->err, &err_open); + /* maxBuffer: per stream with exec's cut (the async shape), or both + * streams together, 0 meaning no limit (spawn_sync.cc). */ + if (per_stream) { + if (scr_exec_limit_chunk((uint8_t *)res->out.data, &res->out.len, out_before, &out_text, + max_buffer)) + res->over = "stdout"; + else if (scr_exec_limit_chunk((uint8_t *)res->err.data, &res->err.len, err_before, + &err_text, max_buffer)) + res->over = "stderr"; + kill_now = res->over != NULL; + } else if (max_buffer > 0 && (double)(res->out.len + res->err.len) > max_buffer) { + res->enobufs = true; + kill_now = true; + } if (in_open) { /* Small chunks: PIPE_NOWAIT's partial-write behavior is only * dependable for writes no larger than the pipe buffer. */ @@ -997,9 +1113,18 @@ static void scr_win_run_sync(ScrStr *cmd, ScrArr *args, const ScrStr *input, in_open = false; } } - if (deadline > 0 && !res->timed_out && scr_now_ms() >= deadline) { + if (deadline > 0 && !res->timed_out && !kill_now && scr_now_ms() >= deadline) { res->timed_out = true; + kill_now = true; + } + if (kill_now) { + /* Node kills the child and closes every stdio pipe, so a grandchild + * holding one cannot keep the call waiting. */ (void)TerminateProcess(sp.proc, 1); /* killed children exit 1 (uv) */ + if (out_open) CloseHandle(out_parent); + if (err_open) CloseHandle(err_parent); + if (in_open) CloseHandle(in_parent); + out_open = err_open = in_open = false; } if (!exited) { exited = WaitForSingleObject(sp.proc, progress ? 0 : 1) == WAIT_OBJECT_0; @@ -1107,7 +1232,7 @@ static ScrSpawnRes *scr_spawn_sync_core(ScrStr *cmd, ScrArr *args, double timeou int err_mode) { ScrWinSyncRes w; scr_win_run_sync(cmd, args, NULL, in_mode, out_mode, err_mode, NULL, NULL, - timeout_ms, &w); + timeout_ms, SCR_EXEC_MAX_BUFFER_DEFAULT, false, &w); if (w.spawn_errname != NULL) { free(w.out.data); free(w.err.data); @@ -1120,16 +1245,16 @@ static ScrSpawnRes *scr_spawn_sync_core(ScrStr *cmd, ScrArr *args, double timeou ScrStr *err_s = scr_str_new(w.err.data, w.err.len); free(w.out.data); free(w.err.data); - if (!w.timed_out) { + if (!w.timed_out && !w.enobufs) { return scr_spawn_res_new(true, (double)w.exit_code, out_s, err_s); } - /* Node's timeout shape: status null, signal = killSignal's name, error - * ETIMEDOUT (the kill may have raced a normal exit; Node reports the - * timeout either way). */ + /* Node's timeout and maxBuffer shape: status null, signal = killSignal's + * name, error ETIMEDOUT or ENOBUFS (the kill may have raced a normal + * exit; Node reports the error either way). */ ScrSpawnRes *r = scr_spawn_res_new(false, 0, out_s, err_s); r->signal_name = scr_signal_name(killsig); if (r->signal_name == NULL) r->signal_name = "SIGTERM"; - r->spawn_errname = "ETIMEDOUT"; + r->spawn_errname = w.timed_out ? "ETIMEDOUT" : "ENOBUFS"; r->cmd = scr_str_retain(cmd); return r; } @@ -1141,6 +1266,8 @@ ScrSpawnRes *scr_spawn_sync(ScrStr *cmd, ScrArr *args) { ScrSpawnRes *scr_spawn_sync_opts(ScrStr *cmd, ScrArr *args, double timeout_ms, ScrStr *killsignal, double in_mode, double out_mode, double err_mode) { + /* spawnSync's validateTimeout (its maxBuffer is the 1 MiB default). */ + if (!scr_exec_options_valid(timeout_ms, 0)) return NULL; int killsig = SIGTERM; if (killsignal->len > 0) { int resolved = scr_signal_from_name(killsignal); @@ -1245,7 +1372,8 @@ static ScrStr *scr_exec_run(ScrStr *cmd, ScrArr *args, bool shell, const ScrStr *input, const ScrStr *cwd, ScrArr *env_pairs, double timeout_ms, int stdout_mode, int stderr_mode, bool async_shape, - bool buffers, ScrStr **stderr_out) { + bool buffers, double max_buffer, ScrStr **stderr_out) { + if (!scr_exec_options_valid(timeout_ms, max_buffer)) return NULL; /* stdio "inherit"'s stdin rides bit 4 of stdout_mode (scr_runtime.h). */ bool stdin_inherit = (stdout_mode & 4) != 0; stdout_mode &= 3; @@ -1255,7 +1383,7 @@ static ScrStr *scr_exec_run(ScrStr *cmd, ScrArr *args, bool shell, scr_win_run_sync(cmd, args, input, stdin_inherit ? 2 : 0, cap_out ? 0 : (stdout_mode == 2 ? 2 : 1), cap_err ? 0 : (stderr_mode == 3 ? 2 : 1), cwd, - env_pairs, timeout_ms, &w); + env_pairs, timeout_ms, max_buffer, async_shape, &w); if (w.spawn_errname != NULL) { free(w.out.data); free(w.err.data); @@ -1281,11 +1409,11 @@ static ScrStr *scr_exec_run(ScrStr *cmd, ScrArr *args, bool shell, fflush(stdout); fwrite(w.err.data, 1, w.err.len, stderr); } - if (w.timed_out && !async_shape) { - /* The SYNC forms throw Node's spawnSync ETIMEDOUT; the async shape - * reports the kill's death as an ordinary command failure below, + if ((w.timed_out || w.enobufs) && !async_shape) { + /* The SYNC forms throw Node's spawnSync ETIMEDOUT or ENOBUFS; the async + * shape reports the kill's death as an ordinary command failure below, * exactly Node's promisified execFile. */ - ScrError *e = scr_exec_spawn_error(cmd, args, "ETIMEDOUT", false); + ScrError *e = scr_exec_spawn_error(cmd, args, w.timed_out ? "ETIMEDOUT" : "ENOBUFS", false); scr_child_error_self(e); scr_child_error_sync_fields(e, false, 0, "SIGTERM", (double)w.pid, true, scr_child_sync_output(w.out.data, w.out.len, cap_out, buffers), @@ -1295,6 +1423,28 @@ static ScrStr *scr_exec_run(ScrStr *cmd, ScrArr *args, bool shell, scr_exec_throw(e); return NULL; } + if (w.over != NULL) { + /* exec's maxBuffer error: a RangeError with Node's code, then cmd and + * the outputs as cut. */ + char msg[48]; + int len = snprintf(msg, sizeof msg, "%s maxBuffer length exceeded", w.over); + ScrStr *message = scr_str_new(msg, (size_t)len); + ScrError *e = scr_error_new(SCR_ERR_RANGE, message); + scr_str_release(message); + scr_error_set_code(e, "ERR_CHILD_PROCESS_STDIO_MAXBUFFER"); + char *display = scr_exec_display(cmd, args, shell); + scr_child_error_cmd(e, display); + free(display); + ScrStr *out_s = scr_str_new(w.out.data, w.out.len); + ScrStr *err_s = scr_str_new(w.err.data, w.err.len); + scr_child_error_outputs(e, out_s, err_s); + scr_str_release(out_s); + scr_str_release(err_s); + free(w.out.data); + free(w.err.data); + scr_exec_throw(e); + return NULL; + } bool failed = w.exit_code != 0 || w.timed_out; if (failed) { char *display = scr_exec_display(cmd, args, shell); @@ -1334,18 +1484,20 @@ static ScrStr *scr_exec_run(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *scr_exec_sync(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *input, bool has_input, ScrStr *cwd, bool has_env, ScrArr *env_pairs, double timeout_ms, double stdout_mode, double stderr_mode, - bool buffers) { + bool buffers, double max_buffer) { return scr_exec_run(cmd, args, shell, has_input ? input : NULL, cwd->len > 0 ? cwd : NULL, has_env ? env_pairs : NULL, - timeout_ms, (int)stdout_mode, (int)stderr_mode, false, buffers, NULL); + timeout_ms, (int)stdout_mode, (int)stderr_mode, false, buffers, + max_buffer, NULL); } ScrSpawnRes *scr_exec_capture(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *cwd, - bool has_env, ScrArr *env_pairs, double timeout_ms) { + bool has_env, ScrArr *env_pairs, double timeout_ms, + double max_buffer) { ScrStr *err_s = NULL; ScrStr *out_s = scr_exec_run(cmd, args, shell, NULL, cwd->len > 0 ? cwd : NULL, has_env ? env_pairs : NULL, timeout_ms, 1, 1, - true, false, &err_s); + true, false, max_buffer, &err_s); if (out_s == NULL) return NULL; /* exception pending; dummy result */ return scr_spawn_res_new(true, 0, out_s, err_s); } @@ -1399,6 +1551,16 @@ struct ScrChildStream { double dec_pending; uint8_t *capture_data; size_t capture_len, capture_cap; + /* exec's maxBuffer: the limit, the decoded byte count so far, the + * child to kill when the capture passes the limit, and the check of + * each appended chunk (both NULL once the exec callback fired or outside + * exec); capture_stopped ends the capture. */ + double capture_max; + double capture_text_len; + struct ScrChild *capture_child; + void (*capture_check)(struct ScrChildStream *s, size_t chunk_start); + bool capture_stopped; + bool destroyed; /* exec's maxBuffer kill: ends without 'end', reads nothing more */ ScrChildStreamDataL *data_ls; size_t n_data, cap_data; ScrChildStreamEndL *end_ls; @@ -1472,6 +1634,7 @@ struct ScrChild { ScrStr *exec_file; /* the spawned file and its arguments (+1 each), for */ ScrArr *exec_args; /* a spawn failure's path and spawnargs */ void (*exec_props)(struct ScrChild *c, ScrError *error); /* Node's error properties */ + ScrError *exec_over; /* exec's maxBuffer RangeError once a capture passed the limit (+1) */ ScrChildStream *out_stream; ScrChildStream *err_stream; ScrChildWriter *in_stream; @@ -1526,6 +1689,7 @@ void scr_child_release(ScrChild *c) { scr_child_drop_listeners(c); /* only reachable pre-settle via leaks */ scr_str_release(c->err_msg); scr_str_release(c->exec_display); + if (c->exec_over) scr_error_release(c->exec_over); scr_str_release(c->exec_file); if (c->exec_args) scr_arr_release(c->exec_args); scr_dyn_release(c->out_dyn); @@ -1612,7 +1776,8 @@ static void scr_child_stream_close_wait(ScrChildStream *s) { static void scr_child_stream_capture_append(ScrChildStream *s, const char *data, size_t len) { - if (!s->capture || len == 0) return; + if (!s->capture || s->capture_stopped || len == 0) return; + size_t start = s->capture_len; size_t need = s->capture_len + len; if (need > s->capture_cap) { size_t cap = s->capture_cap ? s->capture_cap : 256; @@ -1624,6 +1789,7 @@ static void scr_child_stream_capture_append(ScrChildStream *s, const char *data, } memcpy(s->capture_data + s->capture_len, data, len); s->capture_len = need; + if (s->capture_check != NULL) s->capture_check(s, start); } static ScrStr *scr_child_stream_capture_value(ScrChildStream *s) { @@ -1676,6 +1842,7 @@ static void scr_child_stream_emit(ScrChildStream *s, ScrBytes *bytes, } static void scr_child_stream_finish(ScrChildStream *s, bool fire_end) { + if (s->destroyed) fire_end = false; if (fire_end && s->encoding != NULL && s->n_data > 0) { ScrStr *tail = scr_strdec_end(s->encoding, s->dec_pending); s->dec_pending = 0; @@ -1713,6 +1880,7 @@ static void scr_child_stream_finish(ScrChildStream *s, bool fire_end) { * (broken pipe: every writer closed). Returns true at EOF: the caller * finishes and unlinks. */ static bool scr_child_stream_pump(ScrChildStream *s) { + if (s->destroyed) return true; while (scr_child_stream_watching(s)) { DWORD avail = 0; if (!PeekNamedPipe(s->h, NULL, 0, NULL, &avail, NULL)) return true; @@ -1722,6 +1890,7 @@ static bool scr_child_stream_pump(ScrChildStream *s) { DWORD got = 0; if (!ReadFile(s->h, buf, avail, &got, NULL) || got == 0) return true; scr_child_stream_capture_append(s, buf, (size_t)got); + if (s->destroyed) return true; if (s->n_data == 0) continue; ScrBytes *chunk = scr_bytes_new(SCR_BYTES_U8, (double)got); chunk->is_buffer = true; /* Node delivers a Buffer */ @@ -2428,6 +2597,11 @@ static ScrError *scr_exec_file_error(ScrChild *c, ScrStr *stderr_value) { if (c->exec_props) c->exec_props(c, error); return error; } + if (c->exec_over != NULL) { + ScrError *error = c->exec_over; /* moves */ + c->exec_over = NULL; + return error; + } if (c->has_code && c->code == 0) return NULL; size_t cap = 16 + c->exec_display->len + 1 + stderr_value->len + 1; char *msg = malloc(cap); @@ -2444,8 +2618,48 @@ static ScrError *scr_exec_file_error(ScrChild *c, ScrStr *stderr_value) { return error; } +static bool scr_child_kill_signo(ScrChild *c, int signo); + +/* exec's maxBuffer kill: the callback's RangeError (code, then cmd) is + * built now, both streams are destroyed and the child gets the kill signal + * (lib/child_process.js), so a grandchild holding a pipe cannot keep the + * loop waiting. The next pump of each stream closes it. */ +static void scr_child_stream_capture_over(ScrChildStream *s) { + ScrChild *c = s->capture_child; + char msg[48]; + int len = snprintf(msg, sizeof msg, "%s maxBuffer length exceeded", + s == c->out_stream ? "stdout" : "stderr"); + ScrStr *message = scr_str_new(msg, (size_t)len); + c->exec_over = scr_error_new(SCR_ERR_RANGE, message); + scr_str_release(message); + scr_error_set_code(c->exec_over, "ERR_CHILD_PROCESS_STDIO_MAXBUFFER"); + scr_child_error_cmd(c->exec_over, c->exec_display->data); + ScrChildStream *streams[2] = {c->out_stream, c->err_stream}; + for (int i = 0; i < 2; i++) { + if (streams[i] == NULL) continue; + streams[i]->capture_stopped = true; + streams[i]->destroyed = true; + } + (void)scr_child_kill_signo(c, SIGTERM); +} + +/* exec's maxBuffer check of one appended chunk. scr_exec_file installs it + * per stream, so programs without the exec callback forms link none of it + * (every program with an event loop links the capture path). */ +static void scr_exec_capture_check(ScrChildStream *s, size_t chunk_start) { + if (scr_exec_limit_chunk(s->capture_data, &s->capture_len, chunk_start, &s->capture_text_len, + s->capture_max)) + scr_child_stream_capture_over(s); +} + static void scr_exec_file_fire(ScrChild *c) { if (c->exec_cb == NULL || c->exec_fn == NULL) return; + ScrChildStream *streams[2] = {c->out_stream, c->err_stream}; + for (int i = 0; i < 2; i++) { + if (streams[i] == NULL) continue; + streams[i]->capture_child = NULL; + streams[i]->capture_check = NULL; + } ScrStr *out = scr_child_stream_capture_value(c->out_stream); ScrStr *err = scr_child_stream_capture_value(c->err_stream); ScrError *error = scr_exec_file_error(c, err); @@ -2453,7 +2667,11 @@ static void scr_exec_file_fire(ScrChild *c) { } ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb, - ScrExecFileFn fn, bool shell) { + ScrExecFileFn fn, bool shell, double max_buffer) { + if (!scr_exec_options_valid(0, max_buffer)) { + scr_closure_release(cb); + return NULL; + } ScrStr *empty = scr_str_new("", 0); ScrChild *c = scr_spawn_opts(cmd, args, 3, 3, 3, 0, 0, false, false, false, NULL, empty); @@ -2468,6 +2686,13 @@ ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb, free(display); scr_child_stream_capture_begin(c->out_stream); scr_child_stream_capture_begin(c->err_stream); + ScrChildStream *streams[2] = {c->out_stream, c->err_stream}; + for (int i = 0; i < 2; i++) { + if (streams[i] == NULL) continue; + streams[i]->capture_max = max_buffer; + streams[i]->capture_child = c; + streams[i]->capture_check = &scr_exec_capture_check; + } return c; } @@ -3074,6 +3299,12 @@ static bool scr_cap_read(ScrCapBuf *b, int fd) { return true; } +/* Closes a capture pipe's read end once (-1 afterwards). */ +static void scr_exec_close_fd(int *fd) { + if (*fd >= 0) close(*fd); + *fd = -1; +} + /* ── spawnSync ───────────────────────────────────────────────────────── */ static ScrSpawnRes *scr_spawn_res_new(bool has_status, double status, @@ -3162,6 +3393,7 @@ static ScrSpawnRes *scr_spawn_sync_core(ScrStr *cmd, ScrArr *args, double timeou scr_cap_init(&err); bool out_open = cap_out, err_open = cap_err; bool timed_out = false; + bool enobufs = false; /* the outputs together passed Node's default maxBuffer */ double deadline = timeout_ms > 0 ? scr_now_ms() + timeout_ms : 0; while (out_open || err_open) { struct pollfd fds[2]; @@ -3178,19 +3410,30 @@ static ScrSpawnRes *scr_spawn_sync_core(ScrStr *cmd, ScrArr *args, double timeou if (errno == EINTR) continue; break; } + bool kill_now = false; if (deadline > 0 && !timed_out && scr_now_ms() >= deadline) { timed_out = true; - kill(pid, killsig); + kill_now = true; } - if (rc == 0) continue; - for (nfds_t i = 0; i < n; i++) { + for (nfds_t i = 0; rc > 0 && !kill_now && i < n; i++) { if (!(fds[i].revents & (POLLIN | POLLHUP | POLLERR))) continue; if (out_open && fds[i].fd == outfd[0]) out_open = scr_cap_read(&out, outfd[0]); else if (err_open && fds[i].fd == errfd[0]) err_open = scr_cap_read(&err, errfd[0]); + if ((double)(out.len + err.len) > SCR_EXEC_MAX_BUFFER_DEFAULT) { + enobufs = true; + kill_now = true; + } + } + if (kill_now) { + /* Node kills the child and closes every stdio pipe (spawn_sync.cc). */ + kill(pid, killsig); + scr_exec_close_fd(&outfd[0]); + scr_exec_close_fd(&errfd[0]); + out_open = err_open = false; } } - if (cap_out) close(outfd[0]); - if (cap_err) close(errfd[0]); + scr_exec_close_fd(&outfd[0]); + scr_exec_close_fd(&errfd[0]); /* Reap. With nothing captured the deadline lives here: WNOHANG-poll * until the child exits or the timeout kill fires (the exec core's @@ -3221,18 +3464,18 @@ static ScrSpawnRes *scr_spawn_sync_core(ScrStr *cmd, ScrArr *args, double timeou free(out.data); free(err.data); ScrSpawnRes *r; - if (WIFEXITED(st) && !timed_out) { + if (WIFEXITED(st) && !timed_out && !enobufs) { r = scr_spawn_res_new(true, (double)WEXITSTATUS(st), out_s, err_s); } else { /* Killed by a signal (the timeout's included): Node's status null, * signal = the terminating signal's name. */ r = scr_spawn_res_new(false, 0, out_s, err_s); if (WIFSIGNALED(st)) r->signal_name = scr_signal_name(WTERMSIG(st)); - if (timed_out) { - /* Node: error ETIMEDOUT + signal = killSignal (the kill may have - * raced a normal exit; Node reports the timeout either way). */ + if (timed_out || enobufs) { + /* Node: error ETIMEDOUT or ENOBUFS + signal = killSignal (the kill + * may have raced a normal exit; Node reports the error either way). */ if (r->signal_name == NULL) r->signal_name = scr_signal_name(killsig); - r->spawn_errname = "ETIMEDOUT"; + r->spawn_errname = timed_out ? "ETIMEDOUT" : "ENOBUFS"; r->cmd = scr_str_retain(cmd); } } @@ -3250,6 +3493,8 @@ ScrSpawnRes *scr_spawn_sync(ScrStr *cmd, ScrArr *args) { ScrSpawnRes *scr_spawn_sync_opts(ScrStr *cmd, ScrArr *args, double timeout_ms, ScrStr *killsignal, double in_mode, double out_mode, double err_mode) { + /* spawnSync's validateTimeout (its maxBuffer is the 1 MiB default). */ + if (!scr_exec_options_valid(timeout_ms, 0)) return NULL; int killsig = SIGTERM; if (killsignal->len > 0) { int resolved = scr_signal_from_name(killsignal); @@ -3314,6 +3559,9 @@ typedef struct { bool async_shape; /* The sync forms' error outputs are Buffers (no encoding option). */ bool buffers; + /* maxBuffer: the async shape limits each stream, the sync forms both + * together (0 meaning no limit). */ + double max_buffer; /* async_shape success: the captured stderr string (+1) lands here. */ ScrStr **stderr_out; } ScrExecOpts; @@ -3427,7 +3675,19 @@ static void scr_exec_throw(ScrError *e /* moves */) { scr_throw_obj(e, &scr_error_retain_v, &scr_error_release_v, scr_error_trace_arg()); } +/* After a read into buf (which held `before` bytes): whether the capture + * just passed maxBuffer. The async shape limits each stream and cuts it + * where Node's exec does; the sync forms limit both streams together, 0 + * meaning no limit (spawn_sync.cc). */ +static bool scr_exec_capture_over(const ScrExecOpts *o, ScrCapBuf *buf, size_t before, + double *text_len, const ScrCapBuf *other) { + if (o->async_shape) + return scr_exec_limit_chunk((uint8_t *)buf->data, &buf->len, before, text_len, o->max_buffer); + return o->max_buffer > 0 && (double)(buf->len + other->len) > o->max_buffer; +} + ScrStr *scr_exec_sync_core(ScrStr *cmd, ScrArr *args, const ScrExecOpts *o) { + if (!scr_exec_options_valid(o->timeout_ms, o->max_buffer)) return NULL; int outfd[2] = {-1, -1}, errfd[2] = {-1, -1}, infd[2] = {-1, -1}; bool cap_out = o->stdout_mode == 1; bool cap_err = o->stderr_mode == 0 || o->stderr_mode == 1; @@ -3518,6 +3778,9 @@ ScrStr *scr_exec_sync_core(ScrStr *cmd, ScrArr *args, const ScrExecOpts *o) { char *input_owned = NULL; const char *input = has_input ? scr_str_utf8(o->input, &input_owned) : NULL; bool timed_out = false; + bool enobufs = false; /* the sync forms passed maxBuffer */ + const char *over = NULL; /* the async shape's stream that passed it */ + double out_text = 0, err_text = 0; double deadline = o->timeout_ms > 0 ? scr_now_ms() + o->timeout_ms : 0; while (out_open || err_open || in_open) { struct pollfd fds[3]; @@ -3535,19 +3798,34 @@ ScrStr *scr_exec_sync_core(ScrStr *cmd, ScrArr *args, const ScrExecOpts *o) { if (errno == EINTR) continue; break; } + bool kill_now = false; if (deadline > 0 && !timed_out && scr_now_ms() >= deadline) { - /* Node's timeout: killSignal (SIGTERM default) to the child; the - * capture loop keeps draining until the pipes close. */ + /* Node's timeout: killSignal (SIGTERM default) to the child. */ timed_out = true; - kill(pid, SIGTERM); + kill_now = true; } - if (rc == 0) continue; - for (nfds_t i = 0; i < n; i++) { + for (nfds_t i = 0; rc > 0 && !kill_now && i < n; i++) { if (fds[i].revents == 0) continue; if (out_open && fds[i].fd == outfd[0]) { - if (fds[i].revents & (POLLIN | POLLHUP | POLLERR)) out_open = scr_cap_read(&out, outfd[0]); + if (fds[i].revents & (POLLIN | POLLHUP | POLLERR)) { + size_t before = out.len; + out_open = scr_cap_read(&out, outfd[0]); + if (scr_exec_capture_over(o, &out, before, &out_text, &err)) { + if (o->async_shape) over = "stdout"; + else enobufs = true; + kill_now = true; + } + } } else if (err_open && fds[i].fd == errfd[0]) { - if (fds[i].revents & (POLLIN | POLLHUP | POLLERR)) err_open = scr_cap_read(&err, errfd[0]); + if (fds[i].revents & (POLLIN | POLLHUP | POLLERR)) { + size_t before = err.len; + err_open = scr_cap_read(&err, errfd[0]); + if (scr_exec_capture_over(o, &err, before, &err_text, &out)) { + if (o->async_shape) over = "stderr"; + else enobufs = true; + kill_now = true; + } + } } else if (in_open && fds[i].fd == infd[1]) { if (fds[i].revents & (POLLERR | POLLHUP)) { close(infd[1]); @@ -3566,9 +3844,19 @@ ScrStr *scr_exec_sync_core(ScrStr *cmd, ScrArr *args, const ScrExecOpts *o) { } } } + if (kill_now) { + /* Node kills the child and closes every stdio pipe, so a grandchild + * holding one cannot keep the call waiting (spawn_sync.cc's Kill and + * exec's kill, which destroys both streams). */ + kill(pid, SIGTERM); + scr_exec_close_fd(&outfd[0]); + scr_exec_close_fd(&errfd[0]); + if (in_open) close(infd[1]); + out_open = err_open = in_open = false; + } } - if (cap_out) close(outfd[0]); - if (cap_err) close(errfd[0]); + scr_exec_close_fd(&outfd[0]); + scr_exec_close_fd(&errfd[0]); if (in_open) close(infd[1]); scr_str_utf8_end(input_owned); @@ -3605,12 +3893,12 @@ ScrStr *scr_exec_sync_core(ScrStr *cmd, ScrArr *args, const ScrExecOpts *o) { } bool exited = WIFEXITED(st) && !(timed_out && !o->async_shape); const char *signal = WIFSIGNALED(st) ? scr_signal_name(WTERMSIG(st)) : NULL; - if (timed_out && !o->async_shape) { - /* The SYNC forms throw Node's spawnSync ETIMEDOUT (signal = the kill - * signal even when the child raced it); the async shape reports the - * SIGTERM death as an ordinary command failure below, exactly Node's - * promisified execFile. */ - ScrError *e = scr_exec_spawn_error(cmd, args, ETIMEDOUT, false); + if ((timed_out || enobufs) && !o->async_shape) { + /* The SYNC forms throw Node's spawnSync ETIMEDOUT or ENOBUFS (signal = + * the kill signal even when the child raced it); the async shape + * reports the SIGTERM death as an ordinary command failure below, + * exactly Node's promisified execFile. */ + ScrError *e = scr_exec_spawn_error(cmd, args, timed_out ? ETIMEDOUT : ENOBUFS, false); scr_child_error_self(e); scr_child_error_sync_fields(e, false, 0, signal != NULL ? signal : "SIGTERM", (double)pid, true, scr_child_sync_output(out.data, out.len, cap_out, o->buffers), @@ -3620,6 +3908,28 @@ ScrStr *scr_exec_sync_core(ScrStr *cmd, ScrArr *args, const ScrExecOpts *o) { scr_exec_throw(e); return NULL; } + if (over != NULL) { + /* exec's maxBuffer error: a RangeError with Node's code, then cmd and + * the outputs as cut. */ + char msg[48]; + int len = snprintf(msg, sizeof msg, "%s maxBuffer length exceeded", over); + ScrStr *message = scr_str_new(msg, (size_t)len); + ScrError *e = scr_error_new(SCR_ERR_RANGE, message); + scr_str_release(message); + scr_error_set_code(e, "ERR_CHILD_PROCESS_STDIO_MAXBUFFER"); + char *display = scr_exec_display(cmd, args, o->shell); + scr_child_error_cmd(e, display); + free(display); + ScrStr *out_s = scr_str_new(out.data, out.len); + ScrStr *err_s = scr_str_new(err.data, err.len); + scr_child_error_outputs(e, out_s, err_s); + scr_str_release(out_s); + scr_str_release(err_s); + free(out.data); + free(err.data); + scr_exec_throw(e); + return NULL; + } bool failed = !exited || WEXITSTATUS(st) != 0; if (failed) { char *display = scr_exec_display(cmd, args, o->shell); @@ -3663,7 +3973,7 @@ ScrStr *scr_exec_sync_core(ScrStr *cmd, ScrArr *args, const ScrExecOpts *o) { ScrStr *scr_exec_sync(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *input, bool has_input, ScrStr *cwd, bool has_env, ScrArr *env_pairs, double timeout_ms, double stdout_mode, double stderr_mode, - bool buffers) { + bool buffers, double max_buffer) { ScrExecOpts o = { .shell = shell, .input = has_input ? input : NULL, @@ -3676,6 +3986,7 @@ ScrStr *scr_exec_sync(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *input, .stderr_mode = (int)stderr_mode, .stdin_inherit = ((int)stdout_mode & 4) != 0, .buffers = buffers, + .max_buffer = max_buffer, }; return scr_exec_sync_core(cmd, args, &o); } @@ -3687,7 +3998,8 @@ ScrStr *scr_exec_sync(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *input, * the throw into the rejection). Success answers a +1 ScrSpawnRes reusing * the spawnSync result container (status unused — a failure threw). */ ScrSpawnRes *scr_exec_capture(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *cwd, - bool has_env, ScrArr *env_pairs, double timeout_ms) { + bool has_env, ScrArr *env_pairs, double timeout_ms, + double max_buffer) { ScrStr *err_s = NULL; ScrExecOpts o = { .shell = shell, @@ -3698,6 +4010,7 @@ ScrSpawnRes *scr_exec_capture(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *cwd .stdout_mode = 1, .stderr_mode = 1, .async_shape = true, + .max_buffer = max_buffer, .stderr_out = &err_s, }; ScrStr *out_s = scr_exec_sync_core(cmd, args, &o); @@ -3805,6 +4118,16 @@ struct ScrChildStream { double dec_pending; uint8_t *capture_data; size_t capture_len, capture_cap; + /* exec's maxBuffer: the limit, the decoded byte count so far, the + * child to kill when the capture passes the limit, and the check of + * each appended chunk (both NULL once the exec callback fired or outside + * exec); capture_stopped ends the capture. */ + double capture_max; + double capture_text_len; + struct ScrChild *capture_child; + void (*capture_check)(struct ScrChildStream *s, size_t chunk_start); + bool capture_stopped; + bool destroyed; /* exec's maxBuffer kill: ends without 'end', reads nothing more */ bool armed; /* EVFILT_READ registered on the child kqueue */ bool uncounted; /* counted into scr_children_unwatched (arm failure) */ bool short_read_yields; /* a short read ends this pass's reads */ @@ -3880,6 +4203,7 @@ struct ScrChild { ScrStr *exec_file; /* the spawned file and its arguments (+1 each), for */ ScrArr *exec_args; /* a spawn failure's path and spawnargs */ void (*exec_props)(struct ScrChild *c, ScrError *error); /* Node's error properties */ + ScrError *exec_over; /* exec's maxBuffer RangeError once a capture passed the limit (+1) */ /* Piped stdio (stdio mode 3): the stream handles child.stdout / * child.stderr answer — owned (+1 each), NULL when not piped. */ ScrChildStream *out_stream; @@ -4074,6 +4398,7 @@ void scr_child_release(ScrChild *c) { scr_child_drop_listeners(c); /* only reachable pre-settle via leaks */ scr_str_release(c->err_msg); scr_str_release(c->exec_display); + if (c->exec_over) scr_error_release(c->exec_over); scr_str_release(c->exec_file); if (c->exec_args) scr_arr_release(c->exec_args); scr_dyn_release(c->out_dyn); @@ -4107,6 +4432,7 @@ static const char *scr_errname(int err) { case EIO: return "EIO"; case EINVAL: return "EINVAL"; case ETIMEDOUT: return "ETIMEDOUT"; + case ENOBUFS: return "ENOBUFS"; default: return "EUNKNOWN"; } } @@ -4189,9 +4515,11 @@ static void scr_child_stream_close_wait(ScrChildStream *s) { } } +static bool scr_child_kill_signo(struct ScrChild *c, int signo); static void scr_child_stream_capture_append(ScrChildStream *s, const char *data, size_t len) { - if (!s->capture || len == 0) return; + if (!s->capture || s->capture_stopped || len == 0) return; + size_t start = s->capture_len; size_t need = s->capture_len + len; if (need > s->capture_cap) { size_t cap = s->capture_cap ? s->capture_cap : 256; @@ -4203,6 +4531,7 @@ static void scr_child_stream_capture_append(ScrChildStream *s, const char *data, } memcpy(s->capture_data + s->capture_len, data, len); s->capture_len = need; + if (s->capture_check != NULL) s->capture_check(s, start); } static ScrStr *scr_child_stream_capture_value(ScrChildStream *s) { @@ -4263,6 +4592,7 @@ static void scr_child_stream_emit_posix(ScrChildStream *s, ScrBytes *bytes, * closes (deleting its kqueue filter), and the liveness/fallback counters * settle. The caller unlinks from the registry and releases its ref. */ static void scr_child_stream_finish(ScrChildStream *s, bool fire_end) { + if (s->destroyed) fire_end = false; if (fire_end && s->encoding != NULL && s->n_data > 0) { ScrStr *tail = scr_strdec_end(s->encoding, s->dec_pending); s->dec_pending = 0; @@ -4306,6 +4636,7 @@ static void scr_child_stream_finish(ScrChildStream *s, bool fire_end) { * meaningfully reachable, treated as EOF like the stdin slice's * no-listener path): the caller finishes and unlinks it. */ static bool scr_child_stream_pump(ScrChildStream *s) { + if (s->destroyed) return true; if (s->short_read_yields && s->short_read_pass == scr_children_pass) return false; while (scr_child_stream_watching(s)) { char buf[65536]; @@ -4316,6 +4647,7 @@ static bool scr_child_stream_pump(ScrChildStream *s) { } if (n == 0) return true; scr_child_stream_capture_append(s, buf, (size_t)n); + if (s->destroyed) return true; if (s->n_data == 0) continue; ScrBytes *chunk = scr_bytes_new(SCR_BYTES_U8, (double)n); chunk->is_buffer = true; /* Node delivers a Buffer */ @@ -5130,6 +5462,38 @@ ScrChild *scr_fork(double target, ScrArr *args, double in_mode, return child; } +/* exec's maxBuffer kill: the callback's RangeError (code, then cmd) is + * built now, both streams are destroyed and the child gets the kill signal + * (lib/child_process.js), so a grandchild holding a pipe cannot keep the + * loop waiting. The next pump of each stream closes it. */ +static void scr_child_stream_capture_over(ScrChildStream *s) { + ScrChild *c = s->capture_child; + char msg[48]; + int len = snprintf(msg, sizeof msg, "%s maxBuffer length exceeded", + s == c->out_stream ? "stdout" : "stderr"); + ScrStr *message = scr_str_new(msg, (size_t)len); + c->exec_over = scr_error_new(SCR_ERR_RANGE, message); + scr_str_release(message); + scr_error_set_code(c->exec_over, "ERR_CHILD_PROCESS_STDIO_MAXBUFFER"); + scr_child_error_cmd(c->exec_over, c->exec_display->data); + ScrChildStream *streams[2] = {c->out_stream, c->err_stream}; + for (int i = 0; i < 2; i++) { + if (streams[i] == NULL) continue; + streams[i]->capture_stopped = true; + streams[i]->destroyed = true; + } + (void)scr_child_kill_signo(c, SIGTERM); +} + +/* exec's maxBuffer check of one appended chunk. scr_exec_file installs it + * per stream, so programs without the exec callback forms link none of it + * (every program with an event loop links the capture path). */ +static void scr_exec_capture_check(ScrChildStream *s, size_t chunk_start) { + if (scr_exec_limit_chunk(s->capture_data, &s->capture_len, chunk_start, &s->capture_text_len, + s->capture_max)) + scr_child_stream_capture_over(s); +} + /* Node's properties on the callback form's error. Reached only through * the hook scr_exec_file installs, so programs without the callback form * link none of the property code (the settle path is shared by every @@ -5150,6 +5514,11 @@ static ScrError *scr_exec_file_error(ScrChild *c, ScrStr *stderr_value) { if (c->exec_props) c->exec_props(c, error); return error; } + if (c->exec_over != NULL) { + ScrError *error = c->exec_over; /* moves */ + c->exec_over = NULL; + return error; + } if (c->has_code && c->code == 0) return NULL; size_t cap = 16 + c->exec_display->len + 1 + stderr_value->len + 1; char *msg = malloc(cap); @@ -5168,6 +5537,12 @@ static ScrError *scr_exec_file_error(ScrChild *c, ScrStr *stderr_value) { static void scr_exec_file_fire(ScrChild *c) { if (c->exec_cb == NULL || c->exec_fn == NULL) return; + ScrChildStream *streams[2] = {c->out_stream, c->err_stream}; + for (int i = 0; i < 2; i++) { + if (streams[i] == NULL) continue; + streams[i]->capture_child = NULL; + streams[i]->capture_check = NULL; + } ScrStr *out = scr_child_stream_capture_value(c->out_stream); ScrStr *err = scr_child_stream_capture_value(c->err_stream); ScrError *error = scr_exec_file_error(c, err); @@ -5175,7 +5550,11 @@ static void scr_exec_file_fire(ScrChild *c) { } ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb, - ScrExecFileFn fn, bool shell) { + ScrExecFileFn fn, bool shell, double max_buffer) { + if (!scr_exec_options_valid(0, max_buffer)) { + scr_closure_release(cb); + return NULL; + } ScrStr *empty = scr_str_new("", 0); ScrChild *c = scr_spawn_opts(cmd, args, 3, 3, 3, 0, 0, false, false, false, NULL, empty); @@ -5190,6 +5569,13 @@ ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb, free(display); scr_child_stream_capture_begin(c->out_stream); scr_child_stream_capture_begin(c->err_stream); + ScrChildStream *streams[2] = {c->out_stream, c->err_stream}; + for (int i = 0; i < 2; i++) { + if (streams[i] == NULL) continue; + streams[i]->capture_max = max_buffer; + streams[i]->capture_child = c; + streams[i]->capture_check = &scr_exec_capture_check; + } return c; } diff --git a/packages/runtime/src/scr_child.h b/packages/runtime/src/scr_child.h index 1187985c8d..7455d7d87f 100644 --- a/packages/runtime/src/scr_child.h +++ b/packages/runtime/src/scr_child.h @@ -46,12 +46,14 @@ ScrSpawnRes *scr_spawn_sync_stdio_str(ScrStr *cmd, ScrArr *args, double timeout_ * Result is the captured utf8 stdout (+1) — "" when nothing captures * (Node answers null there; discarded results cannot tell). A failure's * error carries Node's status, signal, output, pid, stdout and stderr; - * `buffers` (no encoding option) makes those outputs Buffers. All args - * borrowed. */ + * `buffers` (no encoding option) makes those outputs Buffers. Outputs + * together past max_buffer (0 = no limit) kill the child and throw Node's + * "spawnSync ENOBUFS"; a negative or NaN max_buffer throws + * ERR_OUT_OF_RANGE. All args borrowed. */ ScrStr *scr_exec_sync(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *input, bool has_input, ScrStr *cwd, bool has_env, ScrArr *env_pairs, double timeout_ms, double stdout_mode, double stderr_mode, - bool buffers); + bool buffers, double max_buffer); /* The promisified-execFile/exec capture (cp.execCapture; shell as for * scr_exec_sync — the /bin/sh -c argv): the same core in the * async shape — both streams captured (no echo), Node's ASYNC error @@ -61,7 +63,13 @@ ScrStr *scr_exec_sync(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *input, * signal, cmd, stdout and stderr properties. +1 ScrSpawnRes on success * (stdout + stderr; status unused). All args borrowed. */ ScrSpawnRes *scr_exec_capture(ScrStr *cmd, ScrArr *args, bool shell, ScrStr *cwd, - bool has_env, ScrArr *env_pairs, double timeout_ms); + bool has_env, ScrArr *env_pairs, double timeout_ms, + double max_buffer); +/* Node's validateTimeout then validateMaxBuffer for the promisified exec + * forms, which throw them synchronously before the promise exists: + * ERR_OUT_OF_RANGE for a timeout that is not a non-negative integer or a + * negative or NaN maxBuffer. */ +void scr_exec_validate(double timeout_ms, double max_buffer); ScrSpawnRes *scr_spawn_res_retain(ScrSpawnRes *r); void scr_spawn_res_release(ScrSpawnRes *r); void *scr_spawn_res_retain_v(void *p); @@ -161,9 +169,12 @@ void scr_child_on_spawn(ScrChild *child, ScrClosure *cb); /* moves zero-argument * all three stdio slots piped; stdout/stderr are captured internally and * 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. */ + * the command, as for scr_exec_sync. A stream past max_buffer is cut there, + * both captures end, the child gets SIGTERM and the callback receives + * Node's ERR_CHILD_PROCESS_STDIO_MAXBUFFER RangeError; a negative or NaN + * max_buffer throws ERR_OUT_OF_RANGE (NULL, the callback released). */ ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb /*moves*/, - ScrExecFileFn fn, bool shell); + ScrExecFileFn fn, bool shell, double max_buffer); ScrChild *scr_child_retain(ScrChild *c); void scr_child_release(ScrChild *c); void *scr_child_retain_v(void *p); diff --git a/tests/corpus/5637-child-max-buffer-sync.ts b/tests/corpus/5637-child-max-buffer-sync.ts new file mode 100644 index 0000000000..6260a50801 --- /dev/null +++ b/tests/corpus/5637-child-max-buffer-sync.ts @@ -0,0 +1,77 @@ +// The sync child_process forms enforce maxBuffer over both outputs +// together: past it the child is killed and Node's ENOBUFS spawnSync +// error is thrown, with the outputs cut at the chunk that passed it. A +// limit or timeout kill closes the pipes, so a grandchild holding one +// does not keep the call waiting. +import { execFileSync, execSync, spawnSync } from "node:child_process"; + +function show(label: string, e: unknown): void { + const err = e as { + message: string; + code?: string; + errno?: number; + syscall?: string; + status?: number | null; + signal?: string | null; + stdout?: string; + }; + console.log(label, err.message, err.code, err.errno, err.syscall, err.status, err.signal); + console.log(label, "stdout", typeof err.stdout, (err.stdout ?? "").length > 0); +} + +try { + execFileSync("/bin/sh", ["-c", "head -c 100000 /dev/zero | tr '\\0' a; sleep 5"], { + encoding: "utf8", + stdio: "pipe", + maxBuffer: 1000, + }); + console.log("no throw"); +} catch (e) { + show("execFileSync", e); +} +try { + execSync("head -c 4000 /dev/zero | tr '\\0' b; sleep 5", { encoding: "utf8", stdio: "pipe", maxBuffer: 10 }); +} catch (e) { + show("execSync", e); +} +// Under the limit, and 0 (no limit). +const small = execSync("echo small", { encoding: "utf8", stdio: "pipe", maxBuffer: 6 }); +console.log("small", JSON.stringify(small)); +const big = execSync("head -c 3000000 /dev/zero | tr '\\0' c", { + encoding: "utf8", + stdio: "pipe", + maxBuffer: 0, +}); +console.log("unlimited", big.length); +// The default is 1 MiB. +try { + execSync("head -c 2000000 /dev/zero | tr '\\0' d; sleep 5", { encoding: "utf8", stdio: "pipe" }); +} catch (e) { + show("default", e); +} +// A grandchild holding the pipe open does not keep the call waiting. +const started = Date.now(); +try { + execSync("head -c 5000 /dev/zero; sleep 5", { encoding: "utf8", stdio: "pipe", maxBuffer: 100 }); +} catch (e) { + show("grandchild", e); +} +console.log("prompt", Date.now() - started < 3000); +// The same after a timeout kill. +const timed = Date.now(); +try { + execSync("sleep 5; true", { encoding: "utf8", stdio: "pipe", timeout: 200 }); +} catch (e) { + show("timeout", e); +} +console.log("timeout prompt", Date.now() - timed < 3000); +// spawnSync applies the 1 MiB default. +const r = spawnSync("/bin/sh", ["-c", "head -c 2000000 /dev/zero; sleep 5"]); +console.log("spawnSync", r.status, r.signal, r.error?.message, (r.error as { code?: string } | undefined)?.code); +// A negative maxBuffer throws before anything runs. +try { + execSync("echo never", { encoding: "utf8", maxBuffer: -1 }); +} catch (e) { + const err = e as { name: string; message: string; code?: string }; + console.log(err.name, err.code, err.message); +} diff --git a/tests/corpus/5638-child-max-buffer-async.ts b/tests/corpus/5638-child-max-buffer-async.ts new file mode 100644 index 0000000000..88e1dbaeb0 --- /dev/null +++ b/tests/corpus/5638-child-max-buffer-async.ts @@ -0,0 +1,100 @@ +// exec and execFile enforce maxBuffer per stream: the stream that passes +// it keeps its first maxBuffer characters, the child is killed, and the +// callback gets Node's RangeError ERR_CHILD_PROCESS_STDIO_MAXBUFFER with +// cmd (and stdout/stderr when promisified). An invalid timeout or +// maxBuffer throws synchronously, the promisified forms included. +import { exec, execFile } from "node:child_process"; +import { inspect, promisify } from "node:util"; + +const execAsync = promisify(exec); +const execFileAsync = promisify(execFile); + +function show(label: string, e: unknown): void { + const err = e as { name: string; message: string; code?: string; cmd?: string; killed?: boolean; stdout?: string }; + console.log(label, err.name, err.message, err.code, err.cmd, err.killed, Object.keys(e as object).join(",")); +} + +async function main(): Promise { + try { + await execFileAsync("/bin/sh", ["-c", "printf 0123456789abcdef; sleep 5"], { encoding: "utf8", maxBuffer: 10 }); + } catch (e) { + const err = e as { stdout?: string; stderr?: string }; + show("execFileAsync", e); + console.log("cut", JSON.stringify(err.stdout), JSON.stringify(err.stderr)); + } + try { + await execAsync("printf 'abc' 1>&2; sleep 1; printf 'ok'", { encoding: "utf8", maxBuffer: 2 }); + } catch (e) { + const err = e as { stdout?: string; stderr?: string }; + show("execAsync", e); + console.log("cut", JSON.stringify(err.stdout), JSON.stringify(err.stderr)); + } + const fits = await execAsync("printf 'four'", { encoding: "utf8", maxBuffer: 4 }); + console.log("fits", JSON.stringify(fits.stdout)); + try { + await execAsync("printf x", { encoding: "utf8", maxBuffer: 0 }); + } catch (e) { + const err = e as { stdout?: string }; + show("zero", e); + console.log("zero", JSON.stringify(err.stdout)); + } + const empty = await execAsync("true", { encoding: "utf8", maxBuffer: 0 }); + console.log("empty", JSON.stringify(empty.stdout)); + // The cut counts UTF-16 code units of the chunk that passed the limit. + try { + await execAsync("printf 'éééééééééé'", { encoding: "utf8", maxBuffer: 5 }); + } catch (e) { + console.log("utf8", JSON.stringify((e as { stdout?: string }).stdout)); + } + try { + await execAsync("echo never", { encoding: "utf8", maxBuffer: -1 }); + } catch (e) { + show("negative", e); + } +} + +const started = Date.now(); +const limit = Number(process.argv.length > 99 ? "1" : "4"); +execFile("/bin/sh", ["-c", "printf 0123456789; sleep 5"], { encoding: "utf8", maxBuffer: limit }, (error, stdout, stderr) => { + show("execFile", error); + if (error) { + error.stack = ""; + console.log(inspect(error)); + } + console.log("callback", JSON.stringify(stdout), JSON.stringify(stderr), Date.now() - started < 3000); + exec("printf hello", { encoding: "utf8", maxBuffer: 3 }, (error2, stdout2) => { + show("exec", error2); + console.log("callback", JSON.stringify(stdout2)); + try { + execFile("/bin/echo", [], { encoding: "utf8", maxBuffer: 0 / 0 }, () => { + console.log("never"); + }); + } catch (e) { + show("nan", e); + } + void main().then(() => { + console.log("prompt", Date.now() - started < 6000); + checks(); + }); + }); +}); + +function checks(): void { + for (const timeout of [-1, 1.5, Infinity]) { + try { + void execAsync("echo never", { encoding: "utf8", timeout }); + } catch (e) { + show("timeout", e); + } + } + let n = 0; + const order = (v: number): number => { + console.log("arg", ++n, v); + return v; + }; + try { + void execFileAsync("/bin/echo", ["never"], { encoding: "utf8", timeout: order(-2), maxBuffer: order(-3) }); + } catch (e) { + show("order", e); + } +} diff --git a/tests/diagnostics/child-process.ts b/tests/diagnostics/child-process.ts index f96bce4189..82e66a4a46 100644 --- a/tests/diagnostics/child-process.ts +++ b/tests/diagnostics/child-process.ts @@ -31,8 +31,6 @@ const f = c.on; // Node's options or the optional-callback overload family. execFile("true"); execFile("true", [], { cwd: "/tmp" }, () => {}); -const bufferLimit = 1024; -execFile("true", [], { maxBuffer: bufferLimit }, () => {}); const getEncoding = (): "utf8" => "utf8"; execFile("true", [], { encoding: getEncoding() }, () => {}); // Fork targets are part of the compiled graph and therefore must resolve at diff --git a/tests/harness/__snapshots__/child-process.ts.txt b/tests/harness/__snapshots__/child-process.ts.txt index 2d83132f58..80fd3bf771 100644 --- a/tests/harness/__snapshots__/child-process.ts.txt +++ b/tests/harness/__snapshots__/child-process.ts.txt @@ -44,51 +44,42 @@ child-process.ts:33:24 - error SC2020: 'execFile option 'cwd'' is part of the st 32 | execFile("true"); 33 | execFile("true", [], { cwd: "/tmp" }, () => {}); | ^~~~~~~~~~~ - 34 | const bufferLimit = 1024; + 34 | const getEncoding = (): "utf8" => "utf8"; - hint: only encoding: 'utf8' and a pure numeric maxBuffer are supported in the callback form + hint: only encoding: 'utf8' and maxBuffer are supported in the callback form -child-process.ts:35:35 - error SC2020: 'execFile with a non-literal maxBuffer' is part of the standard library types but has no scriptc lowering yet +child-process.ts:35:34 - error SC2020: 'execFile with a non-literal utf8 encoding' is part of the standard library types but has no scriptc lowering yet - 34 | const bufferLimit = 1024; - 35 | execFile("true", [], { maxBuffer: bufferLimit }, () => {}); - | ^~~~~~~~~~~ - 36 | const getEncoding = (): "utf8" => "utf8"; - - hint: pass a numeric literal or literal arithmetic (the native capture grows without enforcing this limit) - -child-process.ts:37:34 - error SC2020: 'execFile with a non-literal utf8 encoding' is part of the standard library types but has no scriptc lowering yet - - 36 | const getEncoding = (): "utf8" => "utf8"; - 37 | execFile("true", [], { encoding: getEncoding() }, () => {}); + 34 | const getEncoding = (): "utf8" => "utf8"; + 35 | execFile("true", [], { encoding: getEncoding() }, () => {}); | ^~~~~~~~~~~~~ - 38 | // Fork targets are part of the compiled graph and therefore must resolve at + 36 | // Fork targets are part of the compiled graph and therefore must resolve at hint: pass the literal 'utf8' or 'utf-8' (other expressions might have side effects) -child-process.ts:40:6 - error SC2020: 'fork with a runtime-valued module path' is part of the standard library types but has no scriptc lowering yet +child-process.ts:38:6 - error SC2020: 'fork with a runtime-valued module path' is part of the standard library types but has no scriptc lowering yet - 39 | // build time. The remaining channel is JSON-only and occupies stdio slot 3. - 40 | fork(process.argv[1]!); + 37 | // build time. The remaining channel is JSON-only and occupies stdio slot 3. + 38 | fork(process.argv[1]!); | ^~~~~~~~~~~~~~~~ - 41 | const staticWorker = new URL("./child-process.ts", import.meta.url); + 39 | const staticWorker = new URL("./child-process.ts", import.meta.url); hint: resolve a relative worker with new URL("./worker.ts", import.meta.url) or fileURLToPath(...) and bind it to a const if needed -child-process.ts:42:41 - error SC2020: 'fork with non-JSON serialization' is part of the standard library types but has no scriptc lowering yet +child-process.ts:40:41 - error SC2020: 'fork with non-JSON serialization' is part of the standard library types but has no scriptc lowering yet - 41 | const staticWorker = new URL("./child-process.ts", import.meta.url); - 42 | fork(staticWorker, [], { serialization: "advanced" }); + 39 | const staticWorker = new URL("./child-process.ts", import.meta.url); + 40 | fork(staticWorker, [], { serialization: "advanced" }); | ^~~~~~~~~~ - 43 | fork(staticWorker, [], { stdio: ["ignore", "ignore", "ignore", "ignore"] }); + 41 | fork(staticWorker, [], { stdio: ["ignore", "ignore", "ignore", "ignore"] }); hint: the static IPC channel supports Node's default serialization or serialization: "json" -child-process.ts:43:64 - error SC2020: 'fork without a fourth-slot IPC channel' is part of the standard library types but has no scriptc lowering yet +child-process.ts:41:64 - error SC2020: 'fork without a fourth-slot IPC channel' is part of the standard library types but has no scriptc lowering yet - 42 | fork(staticWorker, [], { serialization: "advanced" }); - 43 | fork(staticWorker, [], { stdio: ["ignore", "ignore", "ignore", "ignore"] }); + 40 | fork(staticWorker, [], { serialization: "advanced" }); + 41 | fork(staticWorker, [], { stdio: ["ignore", "ignore", "ignore", "ignore"] }); | ^~~~~~~~ - 44 | // Keep each fence on its own statement so diagnostics remain site-specific. + 42 | // Keep each fence on its own statement so diagnostics remain site-specific. hint: the fourth stdio entry must be "ipc" \ No newline at end of file diff --git a/tests/harness/corpus-costs.json b/tests/harness/corpus-costs.json index c37b780eac..5be26e1ca4 100644 --- a/tests/harness/corpus-costs.json +++ b/tests/harness/corpus-costs.json @@ -1985,6 +1985,8 @@ "5634-error-extra-props-inspect.ts": 2300, "5635-error-extra-props-retry-loop.ts": 3300, "5636-child-error-props.ts": 7600, + "5637-child-max-buffer-sync.ts": 2700, + "5638-child-max-buffer-async.ts": 2500, "5644-net-socket-addresses.ts": 2220, "5645-net-socket-callbacks.ts": 2442, "5646-net-server-connections.ts": 2389, @@ -5063,6 +5065,8 @@ "5634-error-extra-props-inspect.ts": 2000, "5635-error-extra-props-retry-loop.ts": 6100, "5636-child-error-props.ts": 9800, + "5637-child-max-buffer-sync.ts": 3200, + "5638-child-max-buffer-async.ts": 4800, "5644-net-socket-addresses.ts": 2393, "5645-net-socket-callbacks.ts": 2060, "5646-net-server-connections.ts": 2755, From 3a2c98e297f9eb2625bce0eeefcb436a9de498e5 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Mon, 19 Oct 2026 19:58:23 -0500 Subject: [PATCH 2/6] Keep whole code units when a fractional maxBuffer cuts exec output exec compares its output's length with maxBuffer as a number and keeps `maxBuffer - previous total` UTF-16 code units of the chunk that passed it, as Node does. With a fractional limit, that allowance had a fraction left: an ASCII character with less than one unit left took the branch that keeps the high surrogate of an astral character, which decoded and rewrote four bytes from that position, past the end of the capture buffer when the cut fell at its end. The allowance is now truncated to whole units, as Node's slice truncates its end, and the high surrogate is kept only for a complete four-byte character with exactly one unit left. Keeping it now records that a lone surrogate exists, so JSON.stringify and other string operations escape it as Node does. --- .../5641-child-max-buffer-fractional.ts.json | 6 +++ packages/runtime/src/scr_child.c | 12 ++++-- .../5641-child-max-buffer-fractional.ts | 40 +++++++++++++++++++ tests/harness/corpus-costs.json | 2 + 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5641-child-max-buffer-fractional.ts.json create mode 100644 tests/corpus/5641-child-max-buffer-fractional.ts diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5641-child-max-buffer-fractional.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5641-child-max-buffer-fractional.ts.json new file mode 100644 index 0000000000..bfa319327c --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5641-child-max-buffer-fractional.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/5641-child-max-buffer-fractional.ts" + ], + "diags": [] +} diff --git a/packages/runtime/src/scr_child.c b/packages/runtime/src/scr_child.c index 6a5abb6ffd..1652cb7820 100644 --- a/packages/runtime/src/scr_child.c +++ b/packages/runtime/src/scr_child.c @@ -240,7 +240,11 @@ static bool scr_exec_limit_chunk(uint8_t *data, size_t *len, size_t chunk_start, double before = *text_len; *text_len = before + chunk_len; if (!(*text_len > max_buffer)) return false; - double keep = max_buffer - before; + /* Node slices the chunk at `maxBuffer - previous total`, and a slice end + * is truncated to an integer: a fractional limit keeps only whole units. + * The previous total was within the limit, so `keep` is not negative and + * below the chunk's length. */ + double keep = floor(max_buffer - before); double units = 0; size_t at = text_start; while (at < text_end && units < keep) { @@ -253,14 +257,16 @@ static bool scr_exec_limit_chunk(uint8_t *data, size_t *len, size_t chunk_start, units += u; continue; } - /* One unit left inside an astral character: Node's slice keeps the - * lone high surrogate. */ + /* Exactly one unit left inside a complete astral character: Node's + * slice keeps the lone high surrogate. */ + if (n != 4 || keep - units != 1) break; uint32_t cp = ((uint32_t)(b & 0x07) << 18) | ((uint32_t)(data[at + 1] & 0x3F) << 12) | ((uint32_t)(data[at + 2] & 0x3F) << 6) | (uint32_t)(data[at + 3] & 0x3F); uint32_t hi = 0xD800 + ((cp - 0x10000) >> 10); data[at] = 0xED; data[at + 1] = (uint8_t)(0xA0 | ((hi >> 6) & 0x0F)); data[at + 2] = (uint8_t)(0x80 | (hi & 0x3F)); + scr_wtf8_note(); at += 3; break; } diff --git a/tests/corpus/5641-child-max-buffer-fractional.ts b/tests/corpus/5641-child-max-buffer-fractional.ts new file mode 100644 index 0000000000..8def98b964 --- /dev/null +++ b/tests/corpus/5641-child-max-buffer-fractional.ts @@ -0,0 +1,40 @@ +// A fractional maxBuffer: exec compares the output's length with the +// limit as a number, then keeps `maxBuffer - previous total` UTF-16 code +// units truncated to an integer, as Node's slice does. A cut with less than +// one unit left keeps nothing more, and a cut with exactly one unit left +// inside an astral character keeps its high surrogate. +import { exec } from "node:child_process"; + +function run(command: string, maxBuffer: number): Promise { + return new Promise((resolve) => { + exec(command, { encoding: "utf8", maxBuffer }, (error, stdout) => { + const code = error ? (error as { code?: string }).code : "ok"; + resolve(`${maxBuffer} ${code} ${stdout.length} ${JSON.stringify(stdout.length > 16 ? stdout.slice(-4) : stdout)}`); + }); + }); +} + +async function main(): Promise { + for (const [text, max] of [ + ["abc", 2.5], + ["abc", 0.5], + ["abc", 2.999], + ["abc", 3.5], + ["a😀b", 1.5], + ["a😀b", 2.5], + ["a😀b", 3.5], + ["😀", 0.5], + ["😀", 1.5], + ["é😀", 2.5], + ] as [string, number][]) { + console.log(JSON.stringify(text), await run(`printf '%s' '${text}'`, max)); + } + // Cuts on the last character of outputs whose lengths are powers of two, + // so some cut lands on the end of the capture buffer. + for (let length = 1; length <= 1 << 17; length *= 2) { + console.log("x", length, await run(`head -c ${length} /dev/zero | tr '\\0' x`, length - 0.5)); + if (length > 1) console.log("y", length, await run(`head -c ${length} /dev/zero | tr '\\0' y`, length - 1.5)); + } +} + +void main(); diff --git a/tests/harness/corpus-costs.json b/tests/harness/corpus-costs.json index 5be26e1ca4..3c76ffe411 100644 --- a/tests/harness/corpus-costs.json +++ b/tests/harness/corpus-costs.json @@ -1987,6 +1987,7 @@ "5636-child-error-props.ts": 7600, "5637-child-max-buffer-sync.ts": 2700, "5638-child-max-buffer-async.ts": 2500, + "5641-child-max-buffer-fractional.ts": 1500, "5644-net-socket-addresses.ts": 2220, "5645-net-socket-callbacks.ts": 2442, "5646-net-server-connections.ts": 2389, @@ -5067,6 +5068,7 @@ "5636-child-error-props.ts": 9800, "5637-child-max-buffer-sync.ts": 3200, "5638-child-max-buffer-async.ts": 4800, + "5641-child-max-buffer-fractional.ts": 3200, "5644-net-socket-addresses.ts": 2393, "5645-net-socket-callbacks.ts": 2060, "5646-net-server-connections.ts": 2755, From 3e4ceb6c71360edf2be7718c9e002419eeeb027d Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 20 Oct 2026 00:20:47 -0500 Subject: [PATCH 3/6] Merge main --- .gitignore | 1 + AGENTS.md | 3 + docs/content/docs/coverage.mdx | 2 +- .../array-bounds-and-runtime-errors.mdx | 17 +- .../content/limitations/browser-embedding.mdx | 7 +- .../limitations/buffer-construction.mdx | 4 +- docs/content/limitations/builtin-apis.mdx | 4 +- .../arraybuffers-and-typed-arrays.mdx | 10 +- .../limitations/builtin-apis/assertions.mdx | 13 +- .../builtin-apis/asynclocalstorage.mdx | 3 +- .../builtin-apis/blob-and-file.mdx | 5 +- .../builtin-apis/builtin-module-lookup.mdx | 6 +- .../builtin-apis/commonjs-metadata.mdx | 4 +- .../limitations/builtin-apis/compression.mdx | 8 +- .../content/limitations/builtin-apis/copy.mdx | 6 +- .../limitations/builtin-apis/crypto.mdx | 17 +- .../content/limitations/builtin-apis/date.mdx | 6 +- .../builtin-apis/deprecated-modules.mdx | 5 +- .../limitations/builtin-apis/esm-metadata.mdx | 5 +- .../limitations/builtin-apis/eventemitter.mdx | 5 +- .../limitations/builtin-apis/exit-status.mdx | 4 +- .../limitations/builtin-apis/file-streams.mdx | 5 +- .../filehandle-and-descriptor-i-o.mdx | 8 +- .../builtin-apis/filesystem-flags.mdx | 5 +- .../content/limitations/builtin-apis/glob.mdx | 5 +- .../limitations/builtin-apis/globals.mdx | 3 +- .../builtin-apis/intl-segmenter.mdx | 3 +- .../builtin-apis/iterator-helpers.mdx | 10 +- .../builtin-apis/legacy-url-api.mdx | 4 +- .../limitations/builtin-apis/map-and-set.mdx | 11 +- .../builtin-apis/object-freezing.mdx | 4 +- .../builtin-apis/performance-entries.mdx | 3 +- .../builtin-apis/process-memory.mdx | 3 +- .../limitations/builtin-apis/prototypes.mdx | 3 +- .../builtin-apis/regular-expressions.mdx | 10 +- .../builtin-apis/streams-and-readline.mdx | 25 +- .../builtin-apis/synchronous-iteration.mdx | 5 +- .../limitations/builtin-apis/test-mocks.mdx | 16 +- .../limitations/builtin-apis/test-runner.mdx | 37 +- .../builtin-apis/text-encoding.mdx | 8 +- docs/content/limitations/builtin-apis/url.mdx | 10 +- .../limitations/builtin-function-values.mdx | 28 +- .../limitations/child-process-workers.mdx | 15 +- .../console-and-process-streams.mdx | 13 +- docs/content/limitations/contract-types.mdx | 8 +- .../limitations/dates-and-timezones.mdx | 14 +- .../content/limitations/dns-lookup-errors.mdx | 4 +- .../limitations/foreign-thread-callbacks.mdx | 7 +- docs/content/limitations/http-headers.mdx | 4 +- docs/content/limitations/introduction.mdx | 9 +- docs/content/limitations/json-callbacks.mdx | 13 +- .../language-and-api-restrictions.mdx | 3 +- .../language-features/argument-spreads.mdx | 6 +- .../language-features/async-iteration.mdx | 7 +- .../language-features/class-reflection.mdx | 5 +- .../language-features/commonjs-require.mdx | 10 +- .../language-features/dynamic-imports.mdx | 4 +- .../language-features/error-causes.mdx | 7 +- .../language-features/function-values.mdx | 5 +- .../language-features/generics.mdx | 6 +- .../language-features/loose-equality.mdx | 4 +- .../language-features/method-values.mdx | 6 +- .../language-features/resource-disposal.mdx | 8 +- docs/content/limitations/native-addons.mdx | 7 +- docs/content/limitations/native-debugging.mdx | 10 +- .../limitations/native-worker-threads.mdx | 48 +- .../process-arguments-and-errors.mdx | 12 +- docs/content/limitations/process-events.mdx | 10 +- docs/content/limitations/published-graphs.mdx | 10 +- .../record-identity-and-property-order.mdx | 25 +- .../limitations/runtime-code-evaluation.mdx | 5 +- .../runtime-differences-from-node-js.mdx | 3 +- .../limitations/runtime-type-checks.mdx | 14 +- .../limitations/socket-data-encoding.mdx | 4 +- .../limitations/strings-and-sorting.mdx | 18 +- docs/content/limitations/three-js.mdx | 12 +- docs/content/limitations/tls-ciphers.mdx | 5 +- .../limitations/tooling-and-native-ffi.mdx | 9 +- docs/content/limitations/type-checking.mdx | 7 +- .../types-and-class-definitions.mdx | 4 +- .../types-and-class-definitions/bigint.mdx | 5 +- .../constructor-function-inheritance.mdx | 4 +- .../interfaces.mdx | 8 +- .../local-class-expressions.mdx | 6 +- .../null-and-optional-values.mdx | 4 +- .../record-shapes.mdx | 4 +- .../static-field-updates.mdx | 3 +- .../symbol-fields.mdx | 7 +- .../types-and-class-definitions/unions.mdx | 7 +- .../limitations/udp-socket-options.mdx | 3 +- docs/content/limitations/untyped-values.mdx | 18 +- docs/content/limitations/wasi-target.mdx | 10 +- .../weak-collections-and-memory.mdx | 10 +- .../limitations/webassembly-modules.mdx | 8 +- docs/scripts/limitations-partials.mjs | 29 +- .../node-v24-compatibility-meta.json | 7 + .../src/generated/node-v24-compatibility.json | 144 +- .../generated/node-v24-backlog.json | 377 +- .../generated/node-v24-internal.json | 243 +- internal/compatibility/static-support.json | 12 + .../ambient/scriptc-node-fallback.d.ts | 238 +- .../src/backend/cycle-analysis.test.ts | 2 +- .../compiler/src/backend/cycle-analysis.ts | 3 +- .../src/backend/executable-features.ts | 6 + .../compiler/src/backend/immutability.test.ts | 2 +- packages/compiler/src/backend/library-pack.ts | 2 + .../llvm/appended-array-literals.test.ts | 2 +- .../src/backend/llvm/array-search.test.ts | 2 +- .../src/backend/llvm/checked-narrows.test.ts | 2 +- .../compiler/src/backend/llvm/classes.test.ts | 65 +- packages/compiler/src/backend/llvm/classes.ts | 32 +- .../backend/llvm/constant-callbacks.test.ts | 2 +- .../backend/llvm/dense-array-access.test.ts | 2 +- .../src/backend/llvm/dense-array-access.ts | 8 +- .../src/backend/llvm/dyn-handle-tags.test.ts | 16 +- packages/compiler/src/backend/llvm/dyn.ts | 94 +- .../src/backend/llvm/emit-function.ts | 1347 ++ .../compiler/src/backend/llvm/emit-module.ts | 2245 ++ .../src/backend/llvm/emit-statements.ts | 1985 ++ .../compiler/src/backend/llvm/emitter.test.ts | 6 +- packages/compiler/src/backend/llvm/emitter.ts | 7573 +------ .../src/backend/llvm/expr-async.test.ts | 2 +- .../compiler/src/backend/llvm/expr-async.ts | 24 +- .../src/backend/llvm/expr-calls.test.ts | 2 +- .../compiler/src/backend/llvm/expr-calls.ts | 66 +- .../src/backend/llvm/expr-containers.ts | 10 +- .../compiler/src/backend/llvm/expr-control.ts | 28 +- .../compiler/src/backend/llvm/expr-dynamic.ts | 14 +- .../src/backend/llvm/expr-primitives.test.ts | 4 +- .../src/backend/llvm/expr-primitives.ts | 73 +- .../compiler/src/backend/llvm/expr-records.ts | 24 +- .../backend/llvm/expr-stream-bridges.test.ts | 4 +- .../src/backend/llvm/expr-stream-bridges.ts | 9 +- .../src/backend/llvm/int32-abi.test.ts | 4 +- .../src/backend/llvm/integer-clones.test.ts | 6 +- .../src/backend/llvm/intern-tables.test.ts | 2 +- .../src/backend/llvm/jsval-encoding.ts | 21 + packages/compiler/src/backend/llvm/jsval.ts | 575 + packages/compiler/src/backend/llvm/lib-abi.ts | 2 +- .../compiler/src/backend/llvm/lib-connect.ts | 2 +- .../compiler/src/backend/llvm/lib-datagram.ts | 12 +- .../compiler/src/backend/llvm/lib-dispatch.ts | 20 +- .../src/backend/llvm/lib-filesystem.ts | 20 +- .../compiler/src/backend/llvm/lib-http2.ts | 6 +- .../compiler/src/backend/llvm/lib-lookup.ts | 2 +- .../compiler/src/backend/llvm/lib-network.ts | 42 +- .../compiler/src/backend/llvm/lib-process.ts | 47 +- .../compiler/src/backend/llvm/lib-shared.ts | 25 + .../compiler/src/backend/llvm/lib-stream.ts | 26 +- .../compiler/src/backend/llvm/lib-test.ts | 18 +- packages/compiler/src/backend/llvm/lib-tls.ts | 4 +- .../backend/llvm/local-array-reads.test.ts | 2 +- .../src/backend/llvm/local-array-reads.ts | 4 +- .../backend/llvm/local-union-storage.test.ts | 4 +- .../src/backend/llvm/local-union-storage.ts | 14 +- .../src/backend/llvm/map-entry-fusion.test.ts | 2 +- .../backend/llvm/map-number-lookup.test.ts | 4 +- .../src/backend/llvm/map-number-lookup.ts | 4 +- .../backend/llvm/map-read-lifetimes.test.ts | 4 +- .../src/backend/llvm/nullable-unions.test.ts | 2 +- .../compiler/src/backend/llvm/publish.test.ts | 2 +- .../compiler/src/backend/llvm/receivers.ts | 409 + .../src/backend/llvm/reference-effects.ts | 9 + .../src/backend/llvm/reference-writes.ts | 33 + .../compiler/src/backend/llvm/regions.test.ts | 2 +- packages/compiler/src/backend/llvm/regions.ts | 1 + .../compiler/src/backend/llvm/shapes.test.ts | 6 +- packages/compiler/src/backend/llvm/shapes.ts | 162 +- .../compiler/src/backend/llvm/split-loops.ts | 4 +- .../src/backend/llvm/stack-objects.test.ts | 2 +- .../src/backend/llvm/stack-objects.ts | 2 +- .../src/backend/llvm/stack-unions.test.ts | 2 +- .../backend/llvm/string-construction.test.ts | 2 +- .../src/backend/llvm/string-lifetimes.test.ts | 46 +- .../src/backend/llvm/string-slices.ts | 2 +- .../src/backend/llvm/switch-dispatch.test.ts | 4 +- .../src/backend/llvm/thread-slots.test.ts | 2 +- .../compiler/src/backend/llvm/thread-slots.ts | 8 +- .../src/backend/llvm/union-retype.test.ts | 2 +- .../compiler/src/backend/may-throw.test.ts | 2 +- packages/compiler/src/backend/may-throw.ts | 4 +- .../src/backend/native-link-info-core.ts | 15 + .../compiler/src/backend/native/contracts.ts | 14 + .../compiler/src/backend/native/executable.ts | 370 +- .../compiler/src/backend/native/library.ts | 1 + .../compiler/src/backend/publication.test.ts | 2 +- .../compiler/src/backend/runtime-pack-core.ts | 8 + .../src/backend/runtime-pack-targets.test.ts | 1 + .../compiler/src/backend/runtime-pack.test.ts | 1 + .../src/backend/stack-observations.test.ts | 2 +- .../src/backend/target-diagnostics.test.ts | 2 +- packages/compiler/src/backend/warm-cache.ts | 1 + .../compiler/src/backend/young-nursery.ts | 9 + .../src/coverage/verdict-cache.test.ts | 3 +- .../src/executable/executable-cache.test.ts | 1 + .../src/executable/executable-cache.ts | 13 +- packages/compiler/src/frontend/cjs-syntax.ts | 62 +- .../compiler/src/frontend/contract-types.ts | 10 +- .../src/frontend/experimental-import.ts | 8 +- packages/compiler/src/frontend/fork-target.ts | 37 +- .../src/frontend/function-completion.ts | 12 +- .../src/frontend/inferred-js-diagnostics.ts | 33 +- .../compiler/src/frontend/lib-contract.ts | 48 +- packages/compiler/src/frontend/lib-exports.ts | 4 +- .../src/frontend/lowering/assert-instances.ts | 6 +- .../frontend/lowering/assertion-identity.ts | 78 + .../src/frontend/lowering/binding-analysis.ts | 2 +- .../src/frontend/lowering/builtin-calls.ts | 2 +- .../lowering/builtin-function-fences.ts | 52 + .../frontend/lowering/builtins/arguments.ts | 2 +- .../lowering/builtins/async-context.ts | 105 +- .../src/frontend/lowering/builtins/atomics.ts | 12 +- .../src/frontend/lowering/builtins/blob.ts | 6 +- .../lowering/builtins/callable-aliases.ts | 2 +- .../lowering/builtins/child-process-async.ts | 14 +- .../builtins/child-process-methods.ts | 96 +- .../lowering/builtins/child-process-sync.ts | 20 +- .../lowering/builtins/child-process.ts | 96 +- .../lowering/builtins/class-values.ts | 59 +- .../frontend/lowering/builtins/compression.ts | 93 +- .../frontend/lowering/builtins/constants.ts | 40 +- .../src/frontend/lowering/builtins/crypto.ts | 117 +- .../src/frontend/lowering/builtins/dates.ts | 40 +- .../lowering/builtins/disposable-stack.ts | 6 +- .../frontend/lowering/builtins/environment.ts | 6 +- .../src/frontend/lowering/builtins/errors.ts | 10 +- .../lowering/builtins/filesystem-calls.ts | 233 +- .../frontend/lowering/builtins/filesystem.ts | 154 +- .../src/frontend/lowering/builtins/fs-glob.ts | 6 +- .../frontend/lowering/builtins/fs-streams.ts | 26 +- .../src/frontend/lowering/builtins/ipc.ts | 14 +- .../src/frontend/lowering/builtins/json.ts | 36 +- .../frontend/lowering/builtins/key-object.ts | 4 +- .../lowering/builtins/module-bindings.ts | 22 +- .../lowering/builtins/module-calls.ts | 92 +- .../lowering/builtins/module-resolution.ts | 32 +- .../frontend/lowering/builtins/navigator.ts | 60 + .../frontend/lowering/builtins/net-address.ts | 8 +- .../src/frontend/lowering/builtins/numbers.ts | 18 +- .../lowering/builtins/operating-system.ts | 24 +- .../frontend/lowering/builtins/performance.ts | 15 +- .../src/frontend/lowering/builtins/process.ts | 315 +- .../frontend/lowering/builtins/promises.ts | 50 +- .../frontend/lowering/builtins/querystring.ts | 28 +- .../frontend/lowering/builtins/readline.ts | 43 +- .../lowering/builtins/receiver-types.ts | 12 +- .../frontend/lowering/builtins/reflection.ts | 30 +- .../src/frontend/lowering/builtins/strings.ts | 134 +- .../frontend/lowering/builtins/text-codecs.ts | 75 +- .../src/frontend/lowering/builtins/timers.ts | 32 +- .../src/frontend/lowering/builtins/url.ts | 28 +- .../frontend/lowering/builtins/util-types.ts | 12 +- .../frontend/lowering/builtins/webcrypto.ts | 130 + .../src/frontend/lowering/builtins/workers.ts | 22 +- .../src/frontend/lowering/call-arguments.ts | 8 +- .../src/frontend/lowering/call-signatures.ts | 86 +- .../frontend/lowering/cjs-exports-object.ts | 12 +- .../frontend/lowering/cjs-inline-require.ts | 8 +- .../src/frontend/lowering/cjs-tsc-interop.ts | 14 +- .../frontend/lowering/class-construction.ts | 5 +- .../frontend/lowering/class-descriptors.ts | 14 +- .../lowering/class-dynamic-dispatch.ts | 64 +- .../lowering/class-instance-constructor.ts | 2 +- .../lowering/class-method-mutations.ts | 43 +- .../frontend/lowering/class-method-values.ts | 20 +- .../lowering/class-protocol-dispatch.ts | 21 +- .../src/frontend/lowering/class-protocols.ts | 83 +- .../src/frontend/lowering/class-prototypes.ts | 70 +- .../lowering/class-runtime-statics.ts | 2 +- .../frontend/lowering/class-static-data.ts | 2 +- .../src/frontend/lowering/class-unions.ts | 109 +- .../lowering/coercions/structural-plans.ts | 22 +- .../frontend/lowering/coercions/structural.ts | 27 +- .../src/frontend/lowering/coercions/unions.ts | 33 + .../frontend/lowering/collection-ingestion.ts | 2 +- .../frontend/lowering/collection-narrowing.ts | 7 +- .../lowering/construction-escapes.test.ts | 6 +- .../frontend/lowering/construction-escapes.ts | 9 +- .../frontend/lowering/constructor-returns.ts | 522 + .../containers/array-callback-call.ts | 2 +- .../lowering/containers/array-callbacks.ts | 36 +- .../lowering/containers/array-concat.ts | 2 +- .../lowering/containers/array-construction.ts | 20 +- .../lowering/containers/array-from-async.ts | 2 +- .../lowering/containers/array-methods.ts | 180 +- .../src/frontend/lowering/containers/bytes.ts | 239 +- .../lowering/containers/callback-arguments.ts | 8 +- .../containers/collection-grouping.ts | 17 +- .../lowering/containers/collection-methods.ts | 42 +- .../lowering/containers/exec-result-uses.ts | 2 +- .../frontend/lowering/containers/for-of.ts | 42 +- .../lowering/containers/indexed-objects.ts | 31 +- .../lowering/containers/iterator-helpers.ts | 111 +- .../lowering/containers/match-row-uses.ts | 302 + .../lowering/containers/string-and-regexp.ts | 477 +- .../lowering/containers/string-replacement.ts | 2 +- .../frontend/lowering/cycle-initialization.ts | 45 + .../lowering/deferred-module-initializers.ts | 41 +- .../src/frontend/lowering/devirtualize.ts | 4 +- .../src/frontend/lowering/divergences.ts | 111 +- .../lowering/expressions/array-literals.ts | 767 + .../frontend/lowering/expressions/binary.ts | 2791 +++ .../lowering/expressions/element-access.ts | 2189 ++ .../lowering/expressions/identifiers.ts | 922 + .../lowering/expressions/narrowing.ts | 1413 ++ .../lowering/expressions/native-proxy.ts | 6 +- .../expressions/nullish-assignment.ts | 39 + .../lowering/expressions/object-literals.ts | 133 +- .../lowering/expressions/optional-chains.ts | 4 +- .../lowering/expressions/property-access.ts | 3568 ++++ .../lowering/expressions/relational.ts | 1726 ++ .../frontend/lowering/expressions/typeof.ts | 528 + .../frontend/lowering/expressions/updates.ts | 969 + .../src/frontend/lowering/function-returns.ts | 17 +- .../lowering/generator-dynamic-dispatch.ts | 259 +- .../frontend/lowering/generic-functions.ts | 123 +- .../src/frontend/lowering/import-meta.ts | 2 +- .../src/frontend/lowering/intern-tables.ts | 19 +- .../frontend/lowering/iterator-adapters.ts | 22 +- .../src/frontend/lowering/js-class-fields.ts | 14 +- .../frontend/lowering/js-dynamic-objects.ts | 40 +- .../lowering/key-order-witness.test.ts | 329 + .../frontend/lowering/key-order-witness.ts | 675 + .../src/frontend/lowering/lifetime-regions.ts | 17 +- .../src/frontend/lowering/lower-assert.ts | 116 +- .../lowering/lower-async-iteration.ts | 49 +- .../frontend/lowering/lower-builtin-values.ts | 12 +- .../src/frontend/lowering/lower-calls.ts | 1092 +- .../src/frontend/lowering/lower-classes.ts | 1003 +- .../src/frontend/lowering/lower-comptime.ts | 8 +- .../src/frontend/lowering/lower-console.ts | 24 +- .../src/frontend/lowering/lower-dgram.ts | 10 +- .../src/frontend/lowering/lower-enums.ts | 16 +- .../frontend/lowering/lower-event-emitter.ts | 62 +- .../frontend/lowering/lower-event-target.ts | 4 +- .../src/frontend/lowering/lower-expando.ts | 8 +- .../src/frontend/lowering/lower-exprs.ts | 17456 ++-------------- .../src/frontend/lowering/lower-functions.ts | 22 +- .../src/frontend/lowering/lower-generators.ts | 50 +- .../src/frontend/lowering/lower-inspect.ts | 149 +- .../src/frontend/lowering/lower-mixins.ts | 36 +- .../src/frontend/lowering/lower-mock.ts | 22 +- .../src/frontend/lowering/lower-modules.ts | 157 +- .../src/frontend/lowering/lower-namespaces.ts | 24 +- .../frontend/lowering/lower-node-module.ts | 12 +- .../src/frontend/lowering/lower-server.ts | 132 +- .../src/frontend/lowering/lower-stdlib.ts | 189 +- .../src/frontend/lowering/lower-stmts.ts | 725 +- .../src/frontend/lowering/lower-stream.ts | 58 +- .../src/frontend/lowering/lower-test.ts | 294 +- .../src/frontend/lowering/lower-timers.ts | 68 +- .../src/frontend/lowering/lower-url.ts | 2 +- .../src/frontend/lowering/lowerer.test.ts | 23 +- .../compiler/src/frontend/lowering/lowerer.ts | 626 +- .../frontend/lowering/module-member-this.ts | 64 + .../lowering/module-namespace-elements.ts | 2 +- .../src/frontend/lowering/nan-coded-reads.ts | 36 +- .../frontend/lowering/narrowing-stability.ts | 40 +- .../src/frontend/lowering/native-ffi.ts | 16 +- .../frontend/lowering/object-factory-new.ts | 24 +- .../src/frontend/lowering/private-brands.ts | 328 + .../lowering/require-initialization.ts | 6 +- .../lowering/runtime-optional-bounds.ts | 8 +- .../lowering/runtime-optional-elements.ts | 22 +- .../lowering/runtime-optional-guards.test.ts | 2 +- .../lowering/runtime-optional-guards.ts | 18 +- .../lowering/runtime-optional-ownership.ts | 14 +- .../lowering/runtime-optional-records.ts | 42 +- .../lowering/sanitize-class-types.test.ts | 4 +- .../src/frontend/lowering/split-consumers.ts | 6 +- .../frontend/lowering/string-index-bounds.ts | 4 +- .../src/frontend/lowering/surfaces.ts | 115 +- .../src/frontend/lowering/symbol-fields.ts | 264 +- .../src/frontend/lowering/symbol-methods.ts | 2 +- .../src/frontend/lowering/thread-slots.ts | 24 +- .../src/frontend/lowering/union-retag.test.ts | 31 +- .../src/frontend/lowering/union-retag.ts | 30 + .../src/frontend/lowering/var-hoisting.ts | 8 +- .../src/frontend/lowering/width-lift.ts | 1 + .../src/frontend/module-source-candidates.ts | 2 +- .../compiler/src/frontend/module-syntax.ts | 18 +- .../src/frontend/npm-static-bundled-cjs.ts | 32 +- .../frontend/npm-static-declaration-syntax.ts | 154 +- .../compiler/src/frontend/npm-static-prune.ts | 18 +- .../src/frontend/npm-static-rewrite-syntax.ts | 82 +- .../src/frontend/npm-static-subtyping.ts | 20 +- .../compiler/src/frontend/pipeline.test.ts | 2 +- packages/compiler/src/frontend/pipeline.ts | 4 +- .../compiler/src/frontend/program.test.ts | 4 +- packages/compiler/src/frontend/program.ts | 291 +- .../compiler/src/frontend/services.test.ts | 2 +- .../compiler/src/frontend/threads-import.ts | 2 +- .../compiler/src/frontend/tier-analysis.ts | 6 +- .../src/frontend/ts7/ast-node.test.ts | 30 +- .../compiler/src/frontend/ts7/ast-node.ts | 81 +- .../src/frontend/ts7/ast-types.generated.ts | 2 +- .../compiler/src/frontend/ts7/ast-types.ts | 6 +- packages/compiler/src/frontend/ts7/ast.ts | 4 +- .../compiler/src/frontend/ts7/census-check.ts | 2 +- .../src/frontend/ts7/checker-cache.ts | 6 - .../compiler/src/frontend/ts7/checker.test.ts | 9 +- packages/compiler/src/frontend/ts7/checker.ts | 57 +- .../src/frontend/ts7/semantic-checker.test.ts | 8 +- .../src/frontend/ts7/semantic-model.ts | 54 +- .../compiler/src/frontend/ts7/session.test.ts | 2 +- packages/compiler/src/frontend/ts7/session.ts | 3 +- .../src/frontend/ts7/source-parser.test.ts | 32 +- packages/compiler/src/frontend/type-mapper.ts | 1075 +- .../src/frontend/type-only-imports.ts | 8 +- .../compiler/src/frontend/worker-target.ts | 14 +- packages/compiler/src/index.ts | 9 + packages/compiler/src/ir/analysis.ts | 4 +- packages/compiler/src/ir/api.ts | 1 + packages/compiler/src/ir/builtin-effects.ts | 16 + .../compiler/src/ir/builtin-signatures.ts | 72 +- .../src/ir/callback-specialization.test.ts | 2 +- .../compiler/src/ir/constant-globals.test.ts | 2 +- .../compiler/src/ir/constant-tables.test.ts | 2 +- packages/compiler/src/ir/field-values.test.ts | 2 +- packages/compiler/src/ir/final-fields.test.ts | 2 +- .../src/ir/function-references.test.ts | 3 +- .../compiler/src/ir/init-only-globals.test.ts | 2 +- .../src/ir/initializer-bindings.test.ts | 2 +- packages/compiler/src/ir/int32-slots.test.ts | 2 +- packages/compiler/src/ir/int32-slots.ts | 15 +- .../compiler/src/ir/intrinsic-signatures.ts | 6 + packages/compiler/src/ir/ir.ts | 72 +- packages/compiler/src/ir/jsval.test.ts | 279 + packages/compiler/src/ir/jsval.ts | 384 + packages/compiler/src/ir/runtime-features.ts | 708 +- .../compiler/src/ir/scalar-records.test.ts | 2 +- packages/compiler/src/ir/serialize.ts | 22 +- packages/compiler/src/ir/tiers.test.ts | 68 +- packages/compiler/src/ir/tiers.ts | 32 +- packages/compiler/src/ir/traverse.test.ts | 38 +- packages/compiler/src/ir/traverse.ts | 197 +- packages/compiler/src/ir/unshare.test.ts | 2 +- packages/compiler/src/ir/validate.test.ts | 12 +- packages/compiler/src/ir/validate.ts | 47 +- .../compiler/src/library/fence-eval.test.ts | 2 +- .../compiler/src/library/int-infer.test.ts | 8 +- packages/compiler/src/library/ir-policy.ts | 1 + .../src/library/library-cache.test.ts | 10 +- .../compiler/src/library/library-cache.ts | 1 + packages/compiler/src/native/cli.ts | 1 + packages/compiler/src/native/compiler.test.ts | 4 +- packages/compiler/src/native/prepare.ts | 3 + .../compiler/src/native/sanitizer.test.ts | 5 + packages/compiler/src/wasm/discover.ts | 10 +- packages/compiler/surface-manifest.json | 50 +- .../test/bytes-element-emission.test.ts | 8 +- packages/compiler/test/emit-llvm.test.ts | 6 +- packages/compiler/test/fixtures/fib-ir.ts | 2 +- .../test/fromcharcode-emission.test.ts | 2 +- packages/compiler/test/ir.test.ts | 4 +- .../compiler/test/llvm-lib-dispatch.test.ts | 2 +- .../compiler/test/llvm-runtime-abi.test.ts | 11 +- .../local-class-private-boundaries.test.ts | 63 + .../compiler/test/match-row-emission.test.ts | 69 + .../test/string-accumulation-emission.test.ts | 2 +- .../tests/corpus/1112-number-methods.ts.json | 6 + .../5639-union-arm-by-discriminant.ts.json | 6 + ...generic-structural-class-narrowing.ts.json | 6 + .../5730-thread-slots-widened-values.ts.json | 6 + .../tests/corpus/5963-date-to-string.ts.json | 6 + .../5964-date-dynamic-conversions.js.json | 6 + .../corpus/5981-runtime-class-values.ts.json | 6 + .../corpus/5981-young-nursery-cycles.ts.json | 6 + .../main.ts.json | 7 + .../corpus/5982-young-nursery-workers.ts.json | 6 + ...002-inspect-class-added-properties.ts.json | 6 + .../6003-inspect-js-class-properties.js.json | 6 + .../tests/corpus/6010-match-metadata.ts.json | 6 + .../corpus/6011-match-metadata-js.js.json | 6 + .../corpus/6023-string-compare-utf16.ts.json | 6 + .../6061-type-only-class-fields/main.ts.json | 7 + .../6063-union-class-field-checks.ts.json | 6 + ...4-protocol-subinterface-assertions.ts.json | 6 + .../tests/corpus/6075-print-iterators.ts.json | 6 + .../corpus/6076-print-iterators-js.js.json | 6 + .../6077-generator-helpers-untyped.js.json | 6 + ...6078-destructure-untyped-iterables.js.json | 6 + .../corpus/6079-generator-helpers-any.ts.json | 6 + .../corpus/6080-take-then-flatmap.ts.json | 6 + .../tests/corpus/6146-jsval-operators.js.json | 6 + .../corpus/6147-jsval-any-operators.ts.json | 6 + .../6200-http-agent-free-socket-data.ts.json | 6 + .../6200-string-annexb-locale-methods.ts.json | 6 + .../6201-number-precision-exponential.ts.json | 6 + .../6202-dyn-string-number-methods.js.json | 6 + .../6203-any-string-number-methods.ts.json | 6 + .../6204-crypto-get-random-values.ts.json | 6 + .../6205-crypto-get-random-values.js.json | 6 + .../6206-buffer-is-buffer-views.ts.json | 6 + .../corpus/6207-fs-constants-object.ts.json | 6 + .../corpus/6208-fs-constants-require.cjs.json | 6 + .../corpus/6209-buffer-module-alias.cjs.json | 6 + .../6210-typeof-global-static-members.js.json | 6 + .../6231-process-emit-warning-assign.ts.json | 6 + .../main.ts.json | 6 + ...33-for-await-class-async-generator.ts.json | 6 + .../6234-weak-collection-class-fields.ts.json | 6 + .../main.ts.json | 7 + .../main.ts.json | 6 + .../main.js.json | 6 + .../corpus/6260-js-var-dyn-identity.js.json | 6 + ...261-js-destructure-for-of-identity.js.json | 6 + .../6262-js-file-scope-var-identity.js.json | 6 + .../6263-js-factory-return-identity.cjs.json | 6 + .../6291-webcrypto-hmac-sign-verify.ts.json | 6 + .../corpus/6292-webcrypto-hmac-errors.ts.json | 6 + .../corpus/6293-webcrypto-digest.ts.json | 6 + .../corpus/6294-webcrypto-js-values.mjs.json | 6 + .../corpus/6295-jose-hs256/main.mjs.json | 6 + .../6296-array-position-patterns.ts.json | 6 + .../6297-library-runtime-idioms.mjs.json | 6 + .../corpus/6331-jose-root-entry/main.ts.json | 6 + .../6332-fluent-this-declaration/main.ts.json | 6 + .../main.mjs.json | 6 + ...6334-discarded-logical-statements.mjs.json | 6 + .../6335-discarded-logical-statements.ts.json | 6 + ...ype-reflection-optional-view-param.ts.json | 6 + .../corpus/6341-throw-array-identity.ts.json | 6 + .../6342-throw-builtin-identity.ts.json | 6 + .../corpus/6345-inspect-dyn-holes.ts.json | 6 + .../6350-globalthis-assertion-state.ts.json | 6 + .../main.ts.json | 8 + .../corpus/6416-tls-session-reused.ts.json | 6 + .../6417-object-assign-class-target.ts.json | 6 + .../6418-nullish-assign-index-record.ts.json | 6 + .../bound-constructor-inspect-names.cjs.json | 6 + .../main.ts.json | 7 + .../corpus/builtin-function-values.mjs.json | 6 + .../cjs-class-hierarchy-npm/main.ts.json | 6 + .../corpus/cjs-open-options-npm/main.ts.json | 6 + .../corpus/cjs-strict-arguments.cjs.json | 6 + .../cjs-symbol-key-collisions/main.js.json | 10 + .../corpus/cjs-symbol-key-table/main.js.json | 9 + .../corpus/cjs-unmapped-arguments.cjs.json | 6 + .../constructor-returns-chained-this.js.json | 6 + .../constructor-returns-chained-this.ts.json | 6 + .../constructor-returns-instance.js.json | 6 + .../constructor-returns-instance.ts.json | 6 + .../constructor-returns-this-super.js.json | 6 + .../es5-constructor-inspect-names.cjs.json | 6 + .../tests/corpus/exponent-unit-base.ts.json | 6 + .../function-local-detached-methods.js.json | 6 + .../function-local-detached-methods.ts.json | 6 + ...function-local-private-evaluations.js.json | 6 + .../function-local-private-fields.js.json | 6 + .../corpus/key-order-late-optional.ts.json | 6 + .../corpus/key-order-literal-flows.ts.json | 6 + .../corpus/key-order-spread-copies.ts.json | 6 + ...let-uninitialized-function-binding.ts.json | 6 + .../corpus/logical-and-truthy-index.ts.json | 6 + .../private-in-checked-primitives.js.json | 6 + ...constructor-returns-instance-super.ts.json | 6 + .../constructor-returns-object.ts.json | 6 + .../object-assign-class-copy.ts.json | 6 + ...retired-engine-1112-number-methods.ts.json | 6 - .../runtime-class-value-fences.ts.json | 6 + .../diagnostics/webcrypto-refusals.ts.json | 6 + .../fixtures/node-types/navigator.ts.json | 6 + .../node-types/tls-session-timings.ts.json | 6 + .../node-types/webcrypto-hmac.ts.json | 6 + packages/compiler/test/ts7/facade.test.ts | 46 +- packages/compiler/test/ts7/parity.test.ts | 2 +- .../compiler/test/ts7/program-adapter.test.ts | 2 +- packages/compiler/test/ts7/rpc-api.test.ts | 69 +- .../runtime-pack-matrix.mjs | 117 +- .../runtime-darwin-arm64/scripts/build.mjs | 7 +- .../runtime-pack-common/library-matrix.mjs | 7 +- .../runtime-pack-matrix.mjs | 42 +- .../runtime-pack-common/scripts/build.mjs | 7 +- .../test/library-matrix.test.ts | 20 + packages/runtime/src/scr_array.c | 6 + packages/runtime/src/scr_array.h | 12 +- packages/runtime/src/scr_async.c | 68 +- packages/runtime/src/scr_async.h | 8 + packages/runtime/src/scr_async_dyn.c | 107 +- packages/runtime/src/scr_bytes.c | 3 + packages/runtime/src/scr_bytes.h | 2 + packages/runtime/src/scr_crypto.h | 23 +- packages/runtime/src/scr_cycle.c | 431 +- packages/runtime/src/scr_dyn.h | 205 +- packages/runtime/src/scr_dyn_invoke.c | 12 +- packages/runtime/src/scr_events.c | 269 + packages/runtime/src/scr_events.h | 7 + packages/runtime/src/scr_exception.c | 7 +- packages/runtime/src/scr_exception.h | 7 +- packages/runtime/src/scr_generator.h | 14 + packages/runtime/src/scr_http.c | 7 + packages/runtime/src/scr_inspect.c | 177 +- packages/runtime/src/scr_inspect.h | 4 +- packages/runtime/src/scr_js.h | 183 + packages/runtime/src/scr_js_value.c | 318 + packages/runtime/src/scr_json.c | 262 +- packages/runtime/src/scr_lib.c | 561 +- packages/runtime/src/scr_node_builtin.c | 20 +- packages/runtime/src/scr_number.c | 160 + packages/runtime/src/scr_number.h | 20 + packages/runtime/src/scr_os.c | 175 + packages/runtime/src/scr_os.h | 2 + packages/runtime/src/scr_rc.h | 32 + packages/runtime/src/scr_regex.c | 400 +- packages/runtime/src/scr_regex.h | 14 +- packages/runtime/src/scr_region.c | 25 +- packages/runtime/src/scr_string.c | 106 +- packages/runtime/src/scr_string.h | 13 +- packages/runtime/src/scr_string_methods.h | 11 + packages/runtime/src/scr_symbol.c | 73 +- packages/runtime/src/scr_test.c | 104 +- packages/runtime/src/scr_test.h | 4 + packages/runtime/src/scr_timezone_data.h | 1555 ++ packages/runtime/src/scr_webcrypto.c | 829 + packages/runtime/test/headers.test.ts | 13 +- packages/runtime/test/test_yin.c | 442 + packages/runtime/test/yin.test.ts | 80 + scripts/ci-corpus-costs.mjs | 19 +- scripts/gen-timezone-names.mjs | 292 + scripts/generate-ts7-ast-schema.mjs | 6 +- scripts/generate-ts7-ast-views.mjs | 2 + scripts/zero-cost-compare.mjs | 32 + scripts/zero-cost-gate.mjs | 32 +- .../1112-number-methods.ts} | 6 +- tests/corpus/1536-string-array-sweep.ts | 3 +- .../corpus/5639-union-arm-by-discriminant.ts | 48 + ...5640-generic-structural-class-narrowing.ts | 78 + .../5730-thread-slots-widened-values.ts | 45 + tests/corpus/5963-date-to-string.ts | 93 + tests/corpus/5964-date-dynamic-conversions.js | 29 + tests/corpus/5981-runtime-class-values.ts | 117 + tests/corpus/5981-young-nursery-cycles.ts | 178 + .../5982-runtime-class-value-modules/kinds.ts | 14 + .../5982-runtime-class-value-modules/main.ts | 16 + tests/corpus/5982-young-nursery-workers.ts | 81 + .../6002-inspect-class-added-properties.ts | 117 + .../6003-inspect-js-class-properties.js | 40 + tests/corpus/6010-match-metadata.ts | 40 + tests/corpus/6011-match-metadata-js.js | 31 + tests/corpus/6023-string-compare-utf16.ts | 72 + .../6061-type-only-class-fields/emitted.ts | 23 + .../6061-type-only-class-fields/holder.ts | 13 + .../6061-type-only-class-fields/main.ts | 17 + tests/corpus/6063-union-class-field-checks.ts | 65 + .../6064-protocol-subinterface-assertions.ts | 80 + tests/corpus/6075-print-iterators.ts | 19 + tests/corpus/6076-print-iterators-js.js | 17 + .../corpus/6077-generator-helpers-untyped.js | 81 + .../6078-destructure-untyped-iterables.js | 60 + tests/corpus/6079-generator-helpers-any.ts | 24 + tests/corpus/6080-take-then-flatmap.ts | 19 + tests/corpus/6146-jsval-operators.js | 151 + tests/corpus/6147-jsval-any-operators.ts | 48 + .../6200-http-agent-free-socket-data.ts | 57 + .../6200-string-annexb-locale-methods.ts | 33 + .../6201-number-precision-exponential.ts | 34 + .../corpus/6202-dyn-string-number-methods.js | 92 + .../corpus/6203-any-string-number-methods.ts | 15 + tests/corpus/6204-crypto-get-random-values.ts | 36 + tests/corpus/6205-crypto-get-random-values.js | 38 + tests/corpus/6206-buffer-is-buffer-views.ts | 16 + tests/corpus/6207-fs-constants-object.ts | 19 + tests/corpus/6208-fs-constants-require.cjs | 8 + tests/corpus/6209-buffer-module-alias.cjs | 25 + .../6210-typeof-global-static-members.js | 11 + .../6231-process-emit-warning-assign.ts | 35 + .../6232-process-builtin-module-typed/main.ts | 28 + .../package.json | 1 + .../tsconfig.json | 7 + .../6233-for-await-class-async-generator.ts | 168 + .../6234-weak-collection-class-fields.ts | 39 + .../helper.cjs | 6 + .../main.ts | 17 + .../node_modules/events/events.js | 1 + .../node_modules/events/package.json | 1 + .../node_modules/string_decoder/index.js | 3 + .../node_modules/string_decoder/package.json | 1 + .../package.json | 1 + .../6236-stdin-encoding-tty-probes/main.ts | 62 + .../package.json | 1 + .../tsconfig.json | 7 + .../6237-error-subclass-delete-stack/main.js | 12 + .../node_modules/guest-error/index.d.ts | 6 + .../node_modules/guest-error/index.js | 18 + .../node_modules/guest-error/package.json | 1 + .../package.json | 1 + tests/corpus/6260-js-var-dyn-identity.js | 103 + .../6261-js-destructure-for-of-identity.js | 46 + .../corpus/6262-js-file-scope-var-identity.js | 40 + .../6263-js-factory-return-identity.cjs | 56 + .../corpus/6291-webcrypto-hmac-sign-verify.ts | 94 + tests/corpus/6292-webcrypto-hmac-errors.ts | 83 + tests/corpus/6293-webcrypto-digest.ts | 35 + tests/corpus/6294-webcrypto-js-values.mjs | 52 + tests/corpus/6295-jose-hs256/main.mjs | 103 + .../node_modules/jose/LICENSE.md | 21 + .../jose/dist/types/jwt/sign.d.ts | 21 + .../jose/dist/types/jwt/verify.d.ts | 35 + .../node_modules/jose/dist/types/types.d.ts | 736 + .../jose/dist/types/util/errors.d.ts | 182 + .../node_modules/jose/dist/webapi/jwt/sign.js | 19 + .../jose/dist/webapi/jwt/verify.js | 13 + .../jose/dist/webapi/lib/buffer_utils.js | 75 + .../jose/dist/webapi/lib/jws_algorithms.js | 74 + .../jose/dist/webapi/lib/jws_sign.js | 46 + .../jose/dist/webapi/lib/jws_verify.js | 86 + .../jose/dist/webapi/lib/jwt_claims_set.js | 146 + .../node_modules/jose/dist/webapi/lib/key.js | 155 + .../jose/dist/webapi/lib/key_descriptor.js | 9 + .../jose/dist/webapi/lib/validate.js | 122 + .../jose/dist/webapi/util/base64url.js | 16 + .../jose/dist/webapi/util/errors.js | 109 + .../node_modules/jose/package.json | 17 + tests/corpus/6296-array-position-patterns.ts | 23 + tests/corpus/6297-library-runtime-idioms.mjs | 34 + tests/corpus/6331-jose-root-entry/main.ts | 110 + .../node_modules/jose/LICENSE.md | 21 + .../node_modules/jose/dist/types/index.d.ts | 55 + .../jose/dist/types/jwe/compact/decrypt.d.ts | 24 + .../jose/dist/types/jwe/compact/encrypt.d.ts | 40 + .../dist/types/jwe/flattened/decrypt.d.ts | 24 + .../dist/types/jwe/flattened/encrypt.d.ts | 46 + .../jose/dist/types/jwe/general/decrypt.d.ts | 28 + .../jose/dist/types/jwe/general/encrypt.d.ts | 51 + .../jose/dist/types/jwk/embedded.d.ts | 10 + .../jose/dist/types/jwk/thumbprint.d.ts | 15 + .../jose/dist/types/jwks/local.d.ts | 17 + .../jose/dist/types/jwks/remote.d.ts | 94 + .../jose/dist/types/jws/compact/sign.d.ts | 20 + .../jose/dist/types/jws/compact/verify.d.ts | 25 + .../jose/dist/types/jws/flattened/sign.d.ts | 22 + .../jose/dist/types/jws/flattened/verify.d.ts | 25 + .../jose/dist/types/jws/general/sign.d.ts | 38 + .../jose/dist/types/jws/general/verify.d.ts | 29 + .../jose/dist/types/jwt/decrypt.d.ts | 34 + .../jose/dist/types/jwt/encrypt.d.ts | 57 + .../jose/dist/types/jwt/sign.d.ts | 21 + .../jose/dist/types/jwt/unsecured.d.ts | 19 + .../jose/dist/types/jwt/verify.d.ts | 35 + .../jose/dist/types/key/export.d.ts | 19 + .../dist/types/key/generate_key_pair.d.ts | 33 + .../jose/dist/types/key/generate_secret.d.ts | 30 + .../jose/dist/types/key/import.d.ts | 67 + .../node_modules/jose/dist/types/types.d.ts | 736 + .../jose/dist/types/util/base64url.d.ts | 16 + .../jose/dist/types/util/decode_jwt.d.ts | 8 + .../types/util/decode_protected_header.d.ts | 10 + .../jose/dist/types/util/errors.d.ts | 182 + .../node_modules/jose/dist/webapi/index.js | 70 + .../jose/dist/webapi/jwe/compact/decrypt.js | 7 + .../jose/dist/webapi/jwe/compact/encrypt.js | 26 + .../jose/dist/webapi/jwe/flattened/decrypt.js | 15 + .../jose/dist/webapi/jwe/flattened/encrypt.js | 35 + .../jose/dist/webapi/jwe/general/decrypt.js | 40 + .../jose/dist/webapi/jwe/general/encrypt.js | 105 + .../jose/dist/webapi/jwk/embedded.js | 30 + .../jose/dist/webapi/jwk/thumbprint.js | 55 + .../jose/dist/webapi/jwks/local.js | 59 + .../jose/dist/webapi/jwks/remote.js | 101 + .../jose/dist/webapi/jws/compact/sign.js | 20 + .../jose/dist/webapi/jws/compact/verify.js | 8 + .../jose/dist/webapi/jws/flattened/sign.js | 23 + .../jose/dist/webapi/jws/flattened/verify.js | 12 + .../jose/dist/webapi/jws/general/sign.js | 58 + .../jose/dist/webapi/jws/general/verify.js | 48 + .../jose/dist/webapi/jwt/decrypt.js | 13 + .../jose/dist/webapi/jwt/encrypt.js | 45 + .../node_modules/jose/dist/webapi/jwt/sign.js | 19 + .../jose/dist/webapi/jwt/unsecured.js | 34 + .../jose/dist/webapi/jwt/verify.js | 13 + .../jose/dist/webapi/key/export.js | 35 + .../jose/dist/webapi/key/generate_key_pair.js | 35 + .../jose/dist/webapi/key/generate_secret.js | 36 + .../jose/dist/webapi/key/import.js | 53 + .../node_modules/jose/dist/webapi/lib/asn1.js | 138 + .../jose/dist/webapi/lib/buffer_utils.js | 75 + .../dist/webapi/lib/content_encryption.js | 90 + .../jose/dist/webapi/lib/deflate.js | 40 + .../jose/dist/webapi/lib/jwe_algorithms.js | 116 + .../jose/dist/webapi/lib/jwe_decrypt.js | 187 + .../jose/dist/webapi/lib/jwe_encrypt.js | 75 + .../jose/dist/webapi/lib/jws_algorithms.js | 74 + .../jose/dist/webapi/lib/jws_sign.js | 46 + .../jose/dist/webapi/lib/jws_verify.js | 86 + .../jose/dist/webapi/lib/jwt_claims_set.js | 146 + .../node_modules/jose/dist/webapi/lib/key.js | 155 + .../jose/dist/webapi/lib/key_algorithm.js | 15 + .../jose/dist/webapi/lib/key_descriptor.js | 9 + .../jose/dist/webapi/lib/key_management.js | 196 + .../jose/dist/webapi/lib/validate.js | 122 + .../jose/dist/webapi/util/base64url.js | 16 + .../jose/dist/webapi/util/decode_jwt.js | 33 + .../webapi/util/decode_protected_header.js | 19 + .../jose/dist/webapi/util/errors.js | 109 + .../node_modules/jose/package.json | 134 + .../6332-fluent-this-declaration/main.ts | 17 + .../node_modules/fluent/index.d.ts | 14 + .../node_modules/fluent/index.js | 31 + .../node_modules/fluent/package.json | 11 + .../6333-fluent-this-declaration-js/main.mjs | 17 + .../node_modules/fluent/index.d.ts | 14 + .../node_modules/fluent/index.js | 31 + .../node_modules/fluent/package.json | 11 + .../6334-discarded-logical-statements.mjs | 42 + .../6335-discarded-logical-statements.ts | 27 + ...rototype-reflection-optional-view-param.ts | 42 + tests/corpus/6341-throw-array-identity.ts | 56 + tests/corpus/6342-throw-builtin-identity.ts | 56 + tests/corpus/6345-inspect-dyn-holes.ts | 29 + .../corpus/6350-globalthis-assertion-state.ts | 50 + .../6352-globalthis-state-modules/boot.ts | 14 + .../6352-globalthis-state-modules/main.ts | 18 + .../6352-globalthis-state-modules/state.ts | 18 + tests/corpus/6416-tls-session-reused.ts | 33 + .../corpus/6417-object-assign-class-target.ts | 36 + .../6418-nullish-assign-index-record.ts | 29 + .../bound-constructor-inspect-names.cjs | 36 + .../builtin-function-record-values/main.ts | 10 + .../builtin-function-record-values/native.js | 3 + tests/corpus/builtin-function-values.mjs | 17 + tests/corpus/cjs-class-hierarchy-npm/main.ts | 58 + .../node_modules/dispatchkit/index.d.ts | 27 + .../node_modules/dispatchkit/index.js | 12 + .../node_modules/dispatchkit/lib/agent.js | 18 + .../node_modules/dispatchkit/lib/describe.js | 9 + .../dispatchkit/lib/dispatcher-base.js | 48 + .../dispatchkit/lib/dispatcher.js | 15 + .../node_modules/dispatchkit/lib/pool.js | 20 + .../node_modules/dispatchkit/lib/symbols.js | 11 + .../node_modules/dispatchkit/package.json | 6 + tests/corpus/cjs-open-options-npm/main.ts | 12 + .../node_modules/optkit/index.d.ts | 11 + .../node_modules/optkit/index.js | 21 + .../node_modules/optkit/package.json | 6 + tests/corpus/cjs-strict-arguments.cjs | 43 + .../cjs-symbol-key-collisions/client.js | 16 + .../cjs-symbol-key-collisions/core-symbols.js | 6 + .../corpus/cjs-symbol-key-collisions/main.js | 11 + .../cjs-symbol-key-collisions/mock-client.js | 24 + .../cjs-symbol-key-collisions/mock-symbols.js | 7 + .../cjs-symbol-key-collisions/package.json | 3 + tests/corpus/cjs-symbol-key-table/main.js | 19 + .../corpus/cjs-symbol-key-table/package.json | 3 + tests/corpus/cjs-symbol-key-table/queue.js | 37 + tests/corpus/cjs-symbol-key-table/symbols.js | 11 + .../cjs-symbol-key-table/upper-queue.js | 12 + tests/corpus/cjs-unmapped-arguments.cjs | 28 + .../constructor-returns-chained-this.js | 40 + .../constructor-returns-chained-this.ts | 38 + tests/corpus/constructor-returns-instance.js | 45 + tests/corpus/constructor-returns-instance.ts | 112 + .../corpus/constructor-returns-this-super.js | 33 + .../corpus/es5-constructor-inspect-names.cjs | 41 + tests/corpus/exponent-unit-base.ts | 16 + .../corpus/function-local-detached-methods.js | 22 + .../corpus/function-local-detached-methods.ts | 19 + .../function-local-private-evaluations.js | 172 + tests/corpus/function-local-private-fields.js | 48 + tests/corpus/key-order-late-optional.ts | 84 + tests/corpus/key-order-literal-flows.ts | 150 + tests/corpus/key-order-spread-copies.ts | 107 + .../let-uninitialized-function-binding.ts | 46 + tests/corpus/logical-and-truthy-index.ts | 44 + tests/corpus/private-in-checked-primitives.js | 28 + tests/coverage-fixtures/divergences.ts | 6 +- tests/coverage-fixtures/dynamic-mix.ts | 4 +- tests/coverage-fixtures/mixed.ts | 2 +- tests/coverage-fixtures/no-divergences.ts | 10 +- tests/coverage-fixtures/tier-demotions.ts | 4 +- .../diagnostics/builtin-class-alias-escape.ts | 14 +- tests/diagnostics/class-values.ts | 6 +- .../constructor-returns-instance-super.ts | 15 + .../diagnostics/constructor-returns-object.ts | 10 + tests/diagnostics/date-formatters.ts | 2 +- .../deferred-class-receiver/shelf.ts | 2 +- tests/diagnostics/destructuring-fences.ts | 4 +- tests/diagnostics/dynamic-surface.ts | 9 +- tests/diagnostics/local-class-boundaries.ts | 2 +- .../local-class-type-query-brand.ts | 6 +- tests/diagnostics/object-assign-class-copy.ts | 11 + .../diagnostics/runtime-class-value-fences.ts | 23 + tests/diagnostics/thread-slots-fences.ts | 5 + tests/diagnostics/webcrypto-refusals.ts | 18 + .../cases/get-test-context-cancelled.ts | 40 + .../node-test/cases/get-test-context.ts | 122 + .../node-test/cases/test-name-forms.ts | 55 + .../fixtures/node-test/cases/thrown-values.ts | 18 +- tests/fixtures/node-types/fenced.ts | 11 + tests/fixtures/node-types/navigator.ts | 6 + .../node-types/tls-session-timings.ts | 31 + tests/fixtures/node-types/webcrypto-hmac.ts | 25 + tests/fixtures/npm/cases/chalk5/main.ts | 8 + tests/fixtures/self-hosting/class-types.ts | 2 +- .../self-hosting/frontend-services-cases.ts | 4 +- tests/fixtures/self-hosting/ts7-ast-views.ts | 22 +- .../self-hosting/ts7-checker-cases.ts | 26 +- .../self-hosting/ts7-checker-snapshots.ts | 6 +- .../fixtures/self-hosting/ts7-client-cases.ts | 26 +- .../self-hosting/ts7-program-host-cases.ts | 4 +- .../self-hosting/ts7-semantic-cases.ts | 28 +- .../self-hosting/ts7-source-parser-cases.ts | 4 +- .../self-hosting/union-conversions.ts | 3 + .../builtin-class-alias-escape.ts.txt | 26 +- .../harness/__snapshots__/class-values.ts.txt | 8 +- .../constructor-returns-instance-super.ts.txt | 6 + .../constructor-returns-object.ts.txt | 6 + .../__snapshots__/coverage-dynamic-mix.txt | 4 +- .../coverage-explain-dynamic.txt | 2 +- .../harness/__snapshots__/coverage-mixed.txt | 2 +- .../__snapshots__/coverage-tier-demotions.txt | 4 +- .../__snapshots__/date-formatters.ts.txt | 12 +- .../deferred-class-receiver/main.ts.txt | 6 +- .../__snapshots__/destructuring-fences.ts.txt | 6 +- .../__snapshots__/dynamic-surface.ts.txt | 12 +- .../local-class-boundaries.ts.txt | 6 +- .../local-class-type-query-brand.ts.txt | 8 +- .../__snapshots__/node-types-fenced.txt | 22 +- .../object-assign-class-copy.ts.txt | 6 + .../retired-engine-1112-number-methods.ts.txt | 8 - .../runtime-class-value-fences.ts.txt | 63 + .../harness/__snapshots__/stdlib-fence.ts.txt | 2 +- .../__snapshots__/thread-slots-fences.ts.txt | 11 +- .../__snapshots__/webcrypto-refusals.ts.txt | 26 + tests/harness/bench-dynamic-tier.test.ts | 65 + tests/harness/corpus-costs.json | 32 + .../corpus-costs/1112-number-methods.ts.json | 1 + .../5639-union-arm-by-discriminant.ts.json | 1 + ...generic-structural-class-narrowing.ts.json | 1 + .../5730-thread-slots-widened-values.ts.json | 1 + .../6023-string-compare-utf16.ts.json | 1 + .../6061-type-only-class-fields/main.ts.json | 1 + .../6063-union-class-field-checks.ts.json | 1 + ...4-protocol-subinterface-assertions.ts.json | 1 + .../corpus-costs/6146-jsval-operators.js.json | 1 + .../6147-jsval-any-operators.ts.json | 1 + .../6200-string-annexb-locale-methods.ts.json | 1 + .../6201-number-precision-exponential.ts.json | 1 + .../6202-dyn-string-number-methods.js.json | 1 + .../6203-any-string-number-methods.ts.json | 1 + .../6204-crypto-get-random-values.ts.json | 1 + .../6205-crypto-get-random-values.js.json | 1 + .../6206-buffer-is-buffer-views.ts.json | 1 + .../6207-fs-constants-object.ts.json | 1 + .../6208-fs-constants-require.cjs.json | 1 + .../6209-buffer-module-alias.cjs.json | 1 + .../6210-typeof-global-static-members.js.json | 1 + .../6331-jose-root-entry/main.ts.json | 1 + .../6332-fluent-this-declaration/main.ts.json | 1 + .../main.mjs.json | 1 + ...6334-discarded-logical-statements.mjs.json | 1 + .../6335-discarded-logical-statements.ts.json | 1 + .../6341-throw-array-identity.ts.json | 1 + .../6342-throw-builtin-identity.ts.json | 1 + .../6345-inspect-dyn-holes.ts.json | 1 + tests/harness/corpus-costs/README.md | 9 + .../bound-constructor-inspect-names.cjs.json | 1 + .../cjs-class-hierarchy-npm/main.ts.json | 1 + .../cjs-open-options-npm/main.ts.json | 1 + .../cjs-strict-arguments.cjs.json | 1 + .../cjs-symbol-key-collisions/main.js.json | 1 + .../cjs-symbol-key-table/main.js.json | 1 + .../cjs-unmapped-arguments.cjs.json | 1 + .../function-local-detached-methods.js.json | 1 + .../function-local-detached-methods.ts.json | 1 + ...function-local-private-evaluations.js.json | 1 + .../function-local-private-fields.js.json | 1 + .../private-in-checked-primitives.js.json | 1 + tests/harness/coverage.test.ts | 4 +- tests/harness/differential.test.ts | 2 +- tests/harness/errors.test.ts | 163 + tests/harness/library-mode.test.ts | 21 +- tests/harness/library-profile.test.ts | 5 +- tests/harness/link-features.ts | 7 + tests/harness/native-frontend.ts | 24 +- tests/harness/npm.test.ts | 2 +- tests/harness/project-config.test.ts | 57 + .../self-hosting-backend-analysis.test.ts | 12 +- tests/harness/self-hosting-emission.test.ts | 12 +- .../harness/self-hosting-llvm-emitter.test.ts | 6 +- .../self-hosting-native-toolchain.test.ts | 1 + .../harness/self-hosting-runtime-pack.test.ts | 1 + .../self-hosting-serialization.test.ts | 8 +- .../self-hosting-union-conversions.test.ts | 9 +- tests/harness/self-hosting-validator-cases.ts | 2 +- tests/harness/self-hosting.test.ts | 2 + tests/harness/shard.test.ts | 33 +- tests/harness/shard.ts | 56 +- tests/harness/stdlib-surface.test.ts | 6 +- tests/harness/zero-cost-gate.test.ts | 29 + 990 files changed, 59944 insertions(+), 29234 deletions(-) create mode 100644 docs/src/generated/node-v24-compatibility-meta.json create mode 100644 packages/compiler/src/backend/llvm/emit-function.ts create mode 100644 packages/compiler/src/backend/llvm/emit-module.ts create mode 100644 packages/compiler/src/backend/llvm/emit-statements.ts create mode 100644 packages/compiler/src/backend/llvm/jsval-encoding.ts create mode 100644 packages/compiler/src/backend/llvm/jsval.ts create mode 100644 packages/compiler/src/backend/llvm/receivers.ts create mode 100644 packages/compiler/src/backend/young-nursery.ts create mode 100644 packages/compiler/src/frontend/lowering/assertion-identity.ts create mode 100644 packages/compiler/src/frontend/lowering/builtin-function-fences.ts create mode 100644 packages/compiler/src/frontend/lowering/builtins/navigator.ts create mode 100644 packages/compiler/src/frontend/lowering/builtins/webcrypto.ts create mode 100644 packages/compiler/src/frontend/lowering/constructor-returns.ts create mode 100644 packages/compiler/src/frontend/lowering/containers/match-row-uses.ts create mode 100644 packages/compiler/src/frontend/lowering/expressions/array-literals.ts create mode 100644 packages/compiler/src/frontend/lowering/expressions/binary.ts create mode 100644 packages/compiler/src/frontend/lowering/expressions/element-access.ts create mode 100644 packages/compiler/src/frontend/lowering/expressions/identifiers.ts create mode 100644 packages/compiler/src/frontend/lowering/expressions/narrowing.ts create mode 100644 packages/compiler/src/frontend/lowering/expressions/property-access.ts create mode 100644 packages/compiler/src/frontend/lowering/expressions/relational.ts create mode 100644 packages/compiler/src/frontend/lowering/expressions/typeof.ts create mode 100644 packages/compiler/src/frontend/lowering/expressions/updates.ts create mode 100644 packages/compiler/src/frontend/lowering/key-order-witness.test.ts create mode 100644 packages/compiler/src/frontend/lowering/key-order-witness.ts create mode 100644 packages/compiler/src/frontend/lowering/module-member-this.ts create mode 100644 packages/compiler/src/frontend/lowering/private-brands.ts create mode 100644 packages/compiler/src/ir/jsval.test.ts create mode 100644 packages/compiler/src/ir/jsval.ts create mode 100644 packages/compiler/test/local-class-private-boundaries.test.ts create mode 100644 packages/compiler/test/match-row-emission.test.ts create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/1112-number-methods.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5639-union-arm-by-discriminant.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5640-generic-structural-class-narrowing.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5730-thread-slots-widened-values.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5963-date-to-string.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5964-date-dynamic-conversions.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5981-runtime-class-values.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5981-young-nursery-cycles.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5982-runtime-class-value-modules/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5982-young-nursery-workers.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6002-inspect-class-added-properties.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6003-inspect-js-class-properties.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6010-match-metadata.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6011-match-metadata-js.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6023-string-compare-utf16.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6061-type-only-class-fields/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6063-union-class-field-checks.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6064-protocol-subinterface-assertions.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6075-print-iterators.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6076-print-iterators-js.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6077-generator-helpers-untyped.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6078-destructure-untyped-iterables.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6079-generator-helpers-any.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6080-take-then-flatmap.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6146-jsval-operators.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6147-jsval-any-operators.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6200-http-agent-free-socket-data.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6200-string-annexb-locale-methods.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6201-number-precision-exponential.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6202-dyn-string-number-methods.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6203-any-string-number-methods.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6204-crypto-get-random-values.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6205-crypto-get-random-values.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6206-buffer-is-buffer-views.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6207-fs-constants-object.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6208-fs-constants-require.cjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6209-buffer-module-alias.cjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6210-typeof-global-static-members.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6231-process-emit-warning-assign.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6232-process-builtin-module-typed/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6233-for-await-class-async-generator.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6234-weak-collection-class-fields.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6235-bare-builtin-shadowed-package/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6236-stdin-encoding-tty-probes/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6237-error-subclass-delete-stack/main.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6260-js-var-dyn-identity.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6261-js-destructure-for-of-identity.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6262-js-file-scope-var-identity.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6263-js-factory-return-identity.cjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6291-webcrypto-hmac-sign-verify.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6292-webcrypto-hmac-errors.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6293-webcrypto-digest.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6294-webcrypto-js-values.mjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6295-jose-hs256/main.mjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6296-array-position-patterns.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6297-library-runtime-idioms.mjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6331-jose-root-entry/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6332-fluent-this-declaration/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6333-fluent-this-declaration-js/main.mjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6334-discarded-logical-statements.mjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6335-discarded-logical-statements.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6340-prototype-reflection-optional-view-param.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6341-throw-array-identity.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6342-throw-builtin-identity.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6345-inspect-dyn-holes.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6350-globalthis-assertion-state.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6352-globalthis-state-modules/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6416-tls-session-reused.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6417-object-assign-class-target.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6418-nullish-assign-index-record.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/bound-constructor-inspect-names.cjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/builtin-function-record-values/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/builtin-function-values.mjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/cjs-class-hierarchy-npm/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/cjs-open-options-npm/main.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/cjs-strict-arguments.cjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/cjs-symbol-key-collisions/main.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/cjs-symbol-key-table/main.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/cjs-unmapped-arguments.cjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/constructor-returns-chained-this.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/constructor-returns-chained-this.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/constructor-returns-instance.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/constructor-returns-instance.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/constructor-returns-this-super.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/es5-constructor-inspect-names.cjs.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/exponent-unit-base.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/function-local-detached-methods.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/function-local-detached-methods.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/function-local-private-evaluations.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/function-local-private-fields.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/key-order-late-optional.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/key-order-literal-flows.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/key-order-spread-copies.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/let-uninitialized-function-binding.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/logical-and-truthy-index.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/private-in-checked-primitives.js.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/constructor-returns-instance-super.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/constructor-returns-object.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/object-assign-class-copy.ts.json delete mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/retired-engine-1112-number-methods.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/runtime-class-value-fences.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/diagnostics/webcrypto-refusals.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/navigator.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/tls-session-timings.ts.json create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/webcrypto-hmac.ts.json create mode 100644 packages/runtime/src/scr_js.h create mode 100644 packages/runtime/src/scr_js_value.c create mode 100644 packages/runtime/src/scr_timezone_data.h create mode 100644 packages/runtime/src/scr_webcrypto.c create mode 100644 packages/runtime/test/test_yin.c create mode 100644 packages/runtime/test/yin.test.ts create mode 100644 scripts/gen-timezone-names.mjs rename tests/{diagnostics/retired-engine-1112-number-methods.ts => corpus/1112-number-methods.ts} (73%) create mode 100644 tests/corpus/5639-union-arm-by-discriminant.ts create mode 100644 tests/corpus/5640-generic-structural-class-narrowing.ts create mode 100644 tests/corpus/5730-thread-slots-widened-values.ts create mode 100644 tests/corpus/5963-date-to-string.ts create mode 100644 tests/corpus/5964-date-dynamic-conversions.js create mode 100644 tests/corpus/5981-runtime-class-values.ts create mode 100644 tests/corpus/5981-young-nursery-cycles.ts create mode 100644 tests/corpus/5982-runtime-class-value-modules/kinds.ts create mode 100644 tests/corpus/5982-runtime-class-value-modules/main.ts create mode 100644 tests/corpus/5982-young-nursery-workers.ts create mode 100644 tests/corpus/6002-inspect-class-added-properties.ts create mode 100644 tests/corpus/6003-inspect-js-class-properties.js create mode 100644 tests/corpus/6010-match-metadata.ts create mode 100644 tests/corpus/6011-match-metadata-js.js create mode 100644 tests/corpus/6023-string-compare-utf16.ts create mode 100644 tests/corpus/6061-type-only-class-fields/emitted.ts create mode 100644 tests/corpus/6061-type-only-class-fields/holder.ts create mode 100644 tests/corpus/6061-type-only-class-fields/main.ts create mode 100644 tests/corpus/6063-union-class-field-checks.ts create mode 100644 tests/corpus/6064-protocol-subinterface-assertions.ts create mode 100644 tests/corpus/6075-print-iterators.ts create mode 100644 tests/corpus/6076-print-iterators-js.js create mode 100644 tests/corpus/6077-generator-helpers-untyped.js create mode 100644 tests/corpus/6078-destructure-untyped-iterables.js create mode 100644 tests/corpus/6079-generator-helpers-any.ts create mode 100644 tests/corpus/6080-take-then-flatmap.ts create mode 100644 tests/corpus/6146-jsval-operators.js create mode 100644 tests/corpus/6147-jsval-any-operators.ts create mode 100644 tests/corpus/6200-http-agent-free-socket-data.ts create mode 100644 tests/corpus/6200-string-annexb-locale-methods.ts create mode 100644 tests/corpus/6201-number-precision-exponential.ts create mode 100644 tests/corpus/6202-dyn-string-number-methods.js create mode 100644 tests/corpus/6203-any-string-number-methods.ts create mode 100644 tests/corpus/6204-crypto-get-random-values.ts create mode 100644 tests/corpus/6205-crypto-get-random-values.js create mode 100644 tests/corpus/6206-buffer-is-buffer-views.ts create mode 100644 tests/corpus/6207-fs-constants-object.ts create mode 100644 tests/corpus/6208-fs-constants-require.cjs create mode 100644 tests/corpus/6209-buffer-module-alias.cjs create mode 100644 tests/corpus/6210-typeof-global-static-members.js create mode 100644 tests/corpus/6231-process-emit-warning-assign.ts create mode 100644 tests/corpus/6232-process-builtin-module-typed/main.ts create mode 100644 tests/corpus/6232-process-builtin-module-typed/package.json create mode 100644 tests/corpus/6232-process-builtin-module-typed/tsconfig.json create mode 100644 tests/corpus/6233-for-await-class-async-generator.ts create mode 100644 tests/corpus/6234-weak-collection-class-fields.ts create mode 100644 tests/corpus/6235-bare-builtin-shadowed-package/helper.cjs create mode 100644 tests/corpus/6235-bare-builtin-shadowed-package/main.ts create mode 100644 tests/corpus/6235-bare-builtin-shadowed-package/node_modules/events/events.js create mode 100644 tests/corpus/6235-bare-builtin-shadowed-package/node_modules/events/package.json create mode 100644 tests/corpus/6235-bare-builtin-shadowed-package/node_modules/string_decoder/index.js create mode 100644 tests/corpus/6235-bare-builtin-shadowed-package/node_modules/string_decoder/package.json create mode 100644 tests/corpus/6235-bare-builtin-shadowed-package/package.json create mode 100644 tests/corpus/6236-stdin-encoding-tty-probes/main.ts create mode 100644 tests/corpus/6236-stdin-encoding-tty-probes/package.json create mode 100644 tests/corpus/6236-stdin-encoding-tty-probes/tsconfig.json create mode 100644 tests/corpus/6237-error-subclass-delete-stack/main.js create mode 100644 tests/corpus/6237-error-subclass-delete-stack/node_modules/guest-error/index.d.ts create mode 100644 tests/corpus/6237-error-subclass-delete-stack/node_modules/guest-error/index.js create mode 100644 tests/corpus/6237-error-subclass-delete-stack/node_modules/guest-error/package.json create mode 100644 tests/corpus/6237-error-subclass-delete-stack/package.json create mode 100644 tests/corpus/6260-js-var-dyn-identity.js create mode 100644 tests/corpus/6261-js-destructure-for-of-identity.js create mode 100644 tests/corpus/6262-js-file-scope-var-identity.js create mode 100644 tests/corpus/6263-js-factory-return-identity.cjs create mode 100644 tests/corpus/6291-webcrypto-hmac-sign-verify.ts create mode 100644 tests/corpus/6292-webcrypto-hmac-errors.ts create mode 100644 tests/corpus/6293-webcrypto-digest.ts create mode 100644 tests/corpus/6294-webcrypto-js-values.mjs create mode 100644 tests/corpus/6295-jose-hs256/main.mjs create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/LICENSE.md create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/types/jwt/sign.d.ts create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/types/jwt/verify.d.ts create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/types/types.d.ts create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/types/util/errors.d.ts create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/jwt/sign.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/jwt/verify.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/lib/buffer_utils.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/lib/jws_algorithms.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/lib/jws_sign.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/lib/jws_verify.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/lib/jwt_claims_set.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/lib/key.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/lib/key_descriptor.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/lib/validate.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/util/base64url.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/dist/webapi/util/errors.js create mode 100644 tests/corpus/6295-jose-hs256/node_modules/jose/package.json create mode 100644 tests/corpus/6296-array-position-patterns.ts create mode 100644 tests/corpus/6297-library-runtime-idioms.mjs create mode 100644 tests/corpus/6331-jose-root-entry/main.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/LICENSE.md create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/index.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwe/general/decrypt.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwe/general/encrypt.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwk/embedded.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwk/thumbprint.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwks/local.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwks/remote.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jws/compact/sign.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jws/compact/verify.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jws/flattened/sign.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jws/flattened/verify.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jws/general/sign.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jws/general/verify.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwt/decrypt.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwt/encrypt.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwt/sign.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwt/unsecured.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/jwt/verify.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/key/export.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/key/generate_key_pair.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/key/generate_secret.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/key/import.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/types.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/util/base64url.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/util/decode_jwt.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/util/decode_protected_header.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/types/util/errors.d.ts create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/index.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwe/compact/decrypt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwe/compact/encrypt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwe/general/decrypt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwe/general/encrypt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwk/embedded.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwk/thumbprint.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwks/local.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwks/remote.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jws/compact/sign.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jws/compact/verify.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jws/flattened/sign.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jws/flattened/verify.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jws/general/sign.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jws/general/verify.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwt/decrypt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwt/encrypt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwt/sign.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwt/unsecured.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/jwt/verify.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/key/export.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/key/generate_key_pair.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/key/generate_secret.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/key/import.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/asn1.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/buffer_utils.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/content_encryption.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/deflate.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/jwe_algorithms.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/jwe_decrypt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/jwe_encrypt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/jws_algorithms.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/jws_sign.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/jws_verify.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/jwt_claims_set.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/key.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/key_algorithm.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/key_descriptor.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/key_management.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/lib/validate.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/util/base64url.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/util/decode_jwt.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/util/decode_protected_header.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/dist/webapi/util/errors.js create mode 100644 tests/corpus/6331-jose-root-entry/node_modules/jose/package.json create mode 100644 tests/corpus/6332-fluent-this-declaration/main.ts create mode 100644 tests/corpus/6332-fluent-this-declaration/node_modules/fluent/index.d.ts create mode 100644 tests/corpus/6332-fluent-this-declaration/node_modules/fluent/index.js create mode 100644 tests/corpus/6332-fluent-this-declaration/node_modules/fluent/package.json create mode 100644 tests/corpus/6333-fluent-this-declaration-js/main.mjs create mode 100644 tests/corpus/6333-fluent-this-declaration-js/node_modules/fluent/index.d.ts create mode 100644 tests/corpus/6333-fluent-this-declaration-js/node_modules/fluent/index.js create mode 100644 tests/corpus/6333-fluent-this-declaration-js/node_modules/fluent/package.json create mode 100644 tests/corpus/6334-discarded-logical-statements.mjs create mode 100644 tests/corpus/6335-discarded-logical-statements.ts create mode 100644 tests/corpus/6340-prototype-reflection-optional-view-param.ts create mode 100644 tests/corpus/6341-throw-array-identity.ts create mode 100644 tests/corpus/6342-throw-builtin-identity.ts create mode 100644 tests/corpus/6345-inspect-dyn-holes.ts create mode 100644 tests/corpus/6350-globalthis-assertion-state.ts create mode 100644 tests/corpus/6352-globalthis-state-modules/boot.ts create mode 100644 tests/corpus/6352-globalthis-state-modules/main.ts create mode 100644 tests/corpus/6352-globalthis-state-modules/state.ts create mode 100644 tests/corpus/6416-tls-session-reused.ts create mode 100644 tests/corpus/6417-object-assign-class-target.ts create mode 100644 tests/corpus/6418-nullish-assign-index-record.ts create mode 100644 tests/corpus/bound-constructor-inspect-names.cjs create mode 100644 tests/corpus/builtin-function-record-values/main.ts create mode 100644 tests/corpus/builtin-function-record-values/native.js create mode 100644 tests/corpus/builtin-function-values.mjs create mode 100644 tests/corpus/cjs-class-hierarchy-npm/main.ts create mode 100644 tests/corpus/cjs-class-hierarchy-npm/node_modules/dispatchkit/index.d.ts create mode 100644 tests/corpus/cjs-class-hierarchy-npm/node_modules/dispatchkit/index.js create mode 100644 tests/corpus/cjs-class-hierarchy-npm/node_modules/dispatchkit/lib/agent.js create mode 100644 tests/corpus/cjs-class-hierarchy-npm/node_modules/dispatchkit/lib/describe.js create mode 100644 tests/corpus/cjs-class-hierarchy-npm/node_modules/dispatchkit/lib/dispatcher-base.js create mode 100644 tests/corpus/cjs-class-hierarchy-npm/node_modules/dispatchkit/lib/dispatcher.js create mode 100644 tests/corpus/cjs-class-hierarchy-npm/node_modules/dispatchkit/lib/pool.js create mode 100644 tests/corpus/cjs-class-hierarchy-npm/node_modules/dispatchkit/lib/symbols.js create mode 100644 tests/corpus/cjs-class-hierarchy-npm/node_modules/dispatchkit/package.json create mode 100644 tests/corpus/cjs-open-options-npm/main.ts create mode 100644 tests/corpus/cjs-open-options-npm/node_modules/optkit/index.d.ts create mode 100644 tests/corpus/cjs-open-options-npm/node_modules/optkit/index.js create mode 100644 tests/corpus/cjs-open-options-npm/node_modules/optkit/package.json create mode 100644 tests/corpus/cjs-strict-arguments.cjs create mode 100644 tests/corpus/cjs-symbol-key-collisions/client.js create mode 100644 tests/corpus/cjs-symbol-key-collisions/core-symbols.js create mode 100644 tests/corpus/cjs-symbol-key-collisions/main.js create mode 100644 tests/corpus/cjs-symbol-key-collisions/mock-client.js create mode 100644 tests/corpus/cjs-symbol-key-collisions/mock-symbols.js create mode 100644 tests/corpus/cjs-symbol-key-collisions/package.json create mode 100644 tests/corpus/cjs-symbol-key-table/main.js create mode 100644 tests/corpus/cjs-symbol-key-table/package.json create mode 100644 tests/corpus/cjs-symbol-key-table/queue.js create mode 100644 tests/corpus/cjs-symbol-key-table/symbols.js create mode 100644 tests/corpus/cjs-symbol-key-table/upper-queue.js create mode 100644 tests/corpus/cjs-unmapped-arguments.cjs create mode 100644 tests/corpus/constructor-returns-chained-this.js create mode 100644 tests/corpus/constructor-returns-chained-this.ts create mode 100644 tests/corpus/constructor-returns-instance.js create mode 100644 tests/corpus/constructor-returns-instance.ts create mode 100644 tests/corpus/constructor-returns-this-super.js create mode 100644 tests/corpus/es5-constructor-inspect-names.cjs create mode 100644 tests/corpus/exponent-unit-base.ts create mode 100644 tests/corpus/function-local-detached-methods.js create mode 100644 tests/corpus/function-local-detached-methods.ts create mode 100644 tests/corpus/function-local-private-evaluations.js create mode 100644 tests/corpus/function-local-private-fields.js create mode 100644 tests/corpus/key-order-late-optional.ts create mode 100644 tests/corpus/key-order-literal-flows.ts create mode 100644 tests/corpus/key-order-spread-copies.ts create mode 100644 tests/corpus/let-uninitialized-function-binding.ts create mode 100644 tests/corpus/logical-and-truthy-index.ts create mode 100644 tests/corpus/private-in-checked-primitives.js create mode 100644 tests/diagnostics/constructor-returns-instance-super.ts create mode 100644 tests/diagnostics/constructor-returns-object.ts create mode 100644 tests/diagnostics/object-assign-class-copy.ts create mode 100644 tests/diagnostics/runtime-class-value-fences.ts create mode 100644 tests/diagnostics/webcrypto-refusals.ts create mode 100644 tests/fixtures/node-test/cases/get-test-context-cancelled.ts create mode 100644 tests/fixtures/node-test/cases/get-test-context.ts create mode 100644 tests/fixtures/node-test/cases/test-name-forms.ts create mode 100644 tests/fixtures/node-types/navigator.ts create mode 100644 tests/fixtures/node-types/tls-session-timings.ts create mode 100644 tests/fixtures/node-types/webcrypto-hmac.ts create mode 100644 tests/harness/__snapshots__/constructor-returns-instance-super.ts.txt create mode 100644 tests/harness/__snapshots__/constructor-returns-object.ts.txt create mode 100644 tests/harness/__snapshots__/object-assign-class-copy.ts.txt delete mode 100644 tests/harness/__snapshots__/retired-engine-1112-number-methods.ts.txt create mode 100644 tests/harness/__snapshots__/runtime-class-value-fences.ts.txt create mode 100644 tests/harness/__snapshots__/webcrypto-refusals.ts.txt create mode 100644 tests/harness/bench-dynamic-tier.test.ts create mode 100644 tests/harness/corpus-costs/1112-number-methods.ts.json create mode 100644 tests/harness/corpus-costs/5639-union-arm-by-discriminant.ts.json create mode 100644 tests/harness/corpus-costs/5640-generic-structural-class-narrowing.ts.json create mode 100644 tests/harness/corpus-costs/5730-thread-slots-widened-values.ts.json create mode 100644 tests/harness/corpus-costs/6023-string-compare-utf16.ts.json create mode 100644 tests/harness/corpus-costs/6061-type-only-class-fields/main.ts.json create mode 100644 tests/harness/corpus-costs/6063-union-class-field-checks.ts.json create mode 100644 tests/harness/corpus-costs/6064-protocol-subinterface-assertions.ts.json create mode 100644 tests/harness/corpus-costs/6146-jsval-operators.js.json create mode 100644 tests/harness/corpus-costs/6147-jsval-any-operators.ts.json create mode 100644 tests/harness/corpus-costs/6200-string-annexb-locale-methods.ts.json create mode 100644 tests/harness/corpus-costs/6201-number-precision-exponential.ts.json create mode 100644 tests/harness/corpus-costs/6202-dyn-string-number-methods.js.json create mode 100644 tests/harness/corpus-costs/6203-any-string-number-methods.ts.json create mode 100644 tests/harness/corpus-costs/6204-crypto-get-random-values.ts.json create mode 100644 tests/harness/corpus-costs/6205-crypto-get-random-values.js.json create mode 100644 tests/harness/corpus-costs/6206-buffer-is-buffer-views.ts.json create mode 100644 tests/harness/corpus-costs/6207-fs-constants-object.ts.json create mode 100644 tests/harness/corpus-costs/6208-fs-constants-require.cjs.json create mode 100644 tests/harness/corpus-costs/6209-buffer-module-alias.cjs.json create mode 100644 tests/harness/corpus-costs/6210-typeof-global-static-members.js.json create mode 100644 tests/harness/corpus-costs/6331-jose-root-entry/main.ts.json create mode 100644 tests/harness/corpus-costs/6332-fluent-this-declaration/main.ts.json create mode 100644 tests/harness/corpus-costs/6333-fluent-this-declaration-js/main.mjs.json create mode 100644 tests/harness/corpus-costs/6334-discarded-logical-statements.mjs.json create mode 100644 tests/harness/corpus-costs/6335-discarded-logical-statements.ts.json create mode 100644 tests/harness/corpus-costs/6341-throw-array-identity.ts.json create mode 100644 tests/harness/corpus-costs/6342-throw-builtin-identity.ts.json create mode 100644 tests/harness/corpus-costs/6345-inspect-dyn-holes.ts.json create mode 100644 tests/harness/corpus-costs/README.md create mode 100644 tests/harness/corpus-costs/bound-constructor-inspect-names.cjs.json create mode 100644 tests/harness/corpus-costs/cjs-class-hierarchy-npm/main.ts.json create mode 100644 tests/harness/corpus-costs/cjs-open-options-npm/main.ts.json create mode 100644 tests/harness/corpus-costs/cjs-strict-arguments.cjs.json create mode 100644 tests/harness/corpus-costs/cjs-symbol-key-collisions/main.js.json create mode 100644 tests/harness/corpus-costs/cjs-symbol-key-table/main.js.json create mode 100644 tests/harness/corpus-costs/cjs-unmapped-arguments.cjs.json create mode 100644 tests/harness/corpus-costs/function-local-detached-methods.js.json create mode 100644 tests/harness/corpus-costs/function-local-detached-methods.ts.json create mode 100644 tests/harness/corpus-costs/function-local-private-evaluations.js.json create mode 100644 tests/harness/corpus-costs/function-local-private-fields.js.json create mode 100644 tests/harness/corpus-costs/private-in-checked-primitives.js.json create mode 100644 tests/harness/link-features.ts diff --git a/.gitignore b/.gitignore index 4922ef6df2..7b7bc57f2b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ output/ !tests/fixtures/npm/cases/workspace-copied/node_modules/*/dist/ !tests/fixtures/provenance/node_modules/*/dist/ !tests/fixtures/npm-static/node_modules/*/dist/ +!tests/corpus/*/node_modules/*/dist/ .scriptc/ *.log .DS_Store diff --git a/AGENTS.md b/AGENTS.md index c2bdbea2c0..ba32fe6881 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -46,6 +46,8 @@ SCRIPTC_TEST_WORKERS=4 SCRIPTC_SAN=1 pnpm test # sanitized lane Corpus programs are differential tests against Node: every program runs under Node and as a compiled native binary, and stdout, stderr, and exit codes must match byte-for-byte. A new feature lands with corpus programs that pin its behavior both ways. +Record a new or remeasured program's CI cost (used to balance the test shards) in `tests/harness/corpus-costs/.json`, not in `tests/harness/corpus-costs.json`; see the README there. + 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: @@ -194,6 +196,7 @@ The docs site is a standalone pnpm workspace under `docs/`. It uses Next.js App - A labeled bullet (`- **Crypto:** ...`) is `docs/content/limitations/
/