diff --git a/docs/src/generated/node-v24-compatibility-meta.json b/docs/src/generated/node-v24-compatibility-meta.json index 14b42ba7fa..e782885583 100644 --- a/docs/src/generated/node-v24-compatibility-meta.json +++ b/docs/src/generated/node-v24-compatibility-meta.json @@ -2,6 +2,6 @@ "schemaVersion": 3, "nodeVersion": "24.22.0", "nodeCommit": "f14cd3aa7bf9ca8df2cc5b8bb14beb0f335b1d93", - "artifactVersion": "c51180a4672492a73f1d", + "artifactVersion": "fba8a4c99de7c5424f50", "rowCount": 3880 } diff --git a/docs/src/generated/node-v24-compatibility.json b/docs/src/generated/node-v24-compatibility.json index ec30a1c1bc..8e2807d585 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": 17, "refused": 0, - "not-implemented": 21, + "not-implemented": 22, "by-design": 0, "unreviewed": 0, "not-applicable": 0 @@ -9312,9 +9312,9 @@ "inherited": true }, "static": { - "status": "partial", - "detail": "Implemented for the documented scriptc module-loader subset.", - "verification": "test-backed" + "status": "not-implemented", + "detail": "No static compiler lowering is registered for this API yet.", + "verification": "registry-gap" } }, { diff --git a/internal/compatibility/generated/node-v24-backlog.json b/internal/compatibility/generated/node-v24-backlog.json index 1db4326a5a..13ead17e6c 100644 --- a/internal/compatibility/generated/node-v24-backlog.json +++ b/internal/compatibility/generated/node-v24-backlog.json @@ -7,8 +7,8 @@ "tierItemCount": 3472, "tiers": { "static": { - "audit-partial": 1059, - "verify-gap": 2286, + "audit-partial": 1058, + "verify-gap": 2287, "replace-refusal": 40, "classify": 2, "implement": 85 @@ -3941,16 +3941,12 @@ "priority": "high", "tiers": { "static": { - "status": "partial", - "action": "audit-partial", - "verification": "test-backed", - "confidence": "high", - "source": "compiler-feature:child_process.child_process.exec", - "tests": [ - "tests/corpus/5627-exec-callback.ts", - "tests/fixtures/node-types/child-exec.ts", - "tests/harness/project-config.test.ts" - ] + "status": "not-implemented", + "action": "verify-gap", + "verification": "registry-gap", + "confidence": "low", + "source": "compiler-unmatched:child_process", + "tests": [] } } }, diff --git a/internal/compatibility/generated/node-v24-internal.json b/internal/compatibility/generated/node-v24-internal.json index 180e0be8dc..bc9f9e32e6 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": 17, "refused": 0, - "not-implemented": 21, + "not-implemented": 22, "by-design": 0, "unreviewed": 0, "not-applicable": 0 @@ -11663,10 +11663,7 @@ "tests/corpus/3127-spawn-runtime-options.ts", "tests/corpus/3130-child-generic-streams.ts", "tests/corpus/3131-child-rest-listeners.js", - "tests/corpus/3132-child-generic-stream-completion.ts", - "tests/corpus/5627-exec-callback.ts", - "tests/fixtures/node-types/child-exec.ts", - "tests/harness/project-config.test.ts" + "tests/corpus/3132-child-generic-stream-completion.ts" ] }, "anchorSource": "chapter" @@ -11735,13 +11732,8 @@ "inherited": true }, "static": { - "status": "partial", - "evidence": "compiler-feature:child_process.child_process.exec", - "tests": [ - "tests/corpus/5627-exec-callback.ts", - "tests/fixtures/node-types/child-exec.ts", - "tests/harness/project-config.test.ts" - ] + "status": "not-implemented", + "evidence": "compiler-unmatched:child_process" }, "anchorSource": "exact" }, diff --git a/internal/compatibility/static-support.json b/internal/compatibility/static-support.json index 4a46aec53c..c5c99779aa 100644 --- a/internal/compatibility/static-support.json +++ b/internal/compatibility/static-support.json @@ -306,7 +306,6 @@ { "chapter": "packages", "symbols": ["--input-type"], "status": "not-applicable" }, { "chapter": "child_process", "signatures": ["`subprocess[Symbol.dispose]()`"], "status": "partial", "evidence": ["tests/corpus/2844-node-disposable-resources.ts"] }, { "chapter": "child_process", "signatures": ["`child_process.execFile(file[, args][, options][, callback])`"], "status": "partial", "evidence": ["tests/corpus/2911-execfile-callback.ts"] }, - { "chapter": "child_process", "signatures": ["`child_process.exec(command[, options][, callback])`"], "status": "partial", "evidence": ["tests/corpus/5627-exec-callback.ts", "tests/fixtures/node-types/child-exec.ts", "tests/harness/project-config.test.ts"] }, { "chapter": "child_process", "signatures": ["`child_process.fork(modulePath[, args][, options])`", "`subprocess.send(message[, sendHandle[, options]][, callback])`", "`subprocess.disconnect()`", "`connected` Type: {boolean} Set to `false` after `subprocess.disconnect()` is called.", "Event: `'message'`", "Event: `'disconnect'`"], "status": "partial", "evidence": ["tests/corpus/2946-child-fork-ipc/main.ts"] }, { "chapter": "child_process", "signatures": ["Event: `'close'`"], "status": "partial", "evidence": ["tests/corpus/2939-child-shell-text.ts", "tests/corpus/3131-child-rest-listeners.js"] }, { "chapter": "child_process", "signatures": ["`stdout` Type: {stream.Readable|null|undefined}", "`stderr` Type: {stream.Readable|null|undefined}"], "status": "partial", "evidence": ["tests/corpus/1565-spawn-pipe-streams.ts", "tests/corpus/2939-child-shell-text.ts"] }, diff --git a/packages/compiler/ambient/scriptc-node-fallback.d.ts b/packages/compiler/ambient/scriptc-node-fallback.d.ts index 49dbf948fb..ff1fd81de3 100644 --- a/packages/compiler/ambient/scriptc-node-fallback.d.ts +++ b/packages/compiler/ambient/scriptc-node-fallback.d.ts @@ -2971,10 +2971,10 @@ declare module "child_process" { args?: string[] | null, options?: ExecSyncOptions, ): unknown; - /* The async shell form. The callback forms lower like execFile's, with - * the command line run by /bin/sh -c, and util.promisify recognizes it - * statically, as it does execFile; reached calls without a callback - * fence at their site. */ + /* The async shell form — declared surface without full lowering (the + * type-level tolerance story): harness code on paths tests don't reach + * must typecheck; a reached call fences at its site. util.promisify + * recognizes it statically, as it does execFile. */ export function exec( command: string, options?: ExecSyncOptions | ((error: Error | null, stdout: string, stderr: string) => void), diff --git a/packages/compiler/src/backend/llvm/lib-process.ts b/packages/compiler/src/backend/llvm/lib-process.ts index 8c3571c3d5..05d3c27848 100644 --- a/packages/compiler/src/backend/llvm/lib-process.ts +++ b/packages/compiler/src/backend/llvm/lib-process.ts @@ -18,13 +18,12 @@ export function emitChildProcessLibCall(host: LlvmEmitterContext, e: LibCallExpr const argv = host.emitExpr(e.args[1]!); const cb = host.emitExpr(callbackArg); host.moveTemp(cb); - const shell = host.emitExpr(e.args[3]!); const adapter = host.execFileThunkFor(cbT); host.usesTimers = true; - host.declare(`declare ptr @scr_exec_file(ptr, ptr, ptr, ptr, i1)`); + host.declare(`declare ptr @scr_exec_file(ptr, ptr, ptr, ptr)`); 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})`, ); return host.own({ name: out, type: e.type }); } diff --git a/packages/compiler/src/frontend/lowering/builtin-calls.ts b/packages/compiler/src/frontend/lowering/builtin-calls.ts index 9c2eeed45c..8aec7420fe 100644 --- a/packages/compiler/src/frontend/lowering/builtin-calls.ts +++ b/packages/compiler/src/frontend/lowering/builtin-calls.ts @@ -55,8 +55,8 @@ export function lowerBuiltinCall( loc: SrcLoc, namespaceMember?: ts.MemberName, ): IrExpr { - if (bi.module === "child_process" && (bi.member === "execFile" || bi.member === "exec")) { - return lowerer.lowerExecFileCall(expr, loc, bi.member === "exec"); + if (bi.module === "child_process" && bi.member === "execFile") { + return lowerer.lowerExecFileCall(expr, loc); } if (bi.module === "events" && bi.member === "addAbortListener") { return lowerAddAbortListener(lowerer, expr, loc); diff --git a/packages/compiler/src/frontend/lowering/builtins/child-process.ts b/packages/compiler/src/frontend/lowering/builtins/child-process.ts index ecec14fc5b..8c06a28df6 100644 --- a/packages/compiler/src/frontend/lowering/builtins/child-process.ts +++ b/packages/compiler/src/frontend/lowering/builtins/child-process.ts @@ -725,57 +725,35 @@ export function lowerForkCall(lowerer: Lowerer, expr: ts.CallExpression, loc: Sr } /** `execFile(file[, args][, options], callback)` — the asynchronous callback - * slice. With `shell`, `exec(command[, options], callback)`: the command - * line runs with /bin/sh -c, as execSync runs it, and failure messages show - * the command line as Node's do. The child starts immediately with - * stdin/stdout/stderr piped; the runtime captures both outputs and invokes - * the error-first callback after settlement. The callback may ignore a suffix of `(error, stdout, + * slice. The child starts immediately with stdin/stdout/stderr piped; the + * runtime captures both outputs and invokes the error-first callback after + * settlement. The callback may ignore a suffix of `(error, stdout, * stderr)`, but every declared parameter must have the Node shape. Inline * `encoding: "utf8"` and a pure numeric `maxBuffer` are accepted; the latter * is not enforced by the growing native capture, as * with the synchronous and promisified capture forms. Other options remain * fenced until their lifecycle can share this asynchronous core. */ -export function lowerExecFileCall( - lowerer: Lowerer, - expr: ts.CallExpression, - loc: SrcLoc, - shell = false, -): IrExpr { - const what = shell ? "exec" : "execFile"; +export function lowerExecFileCall(lowerer: Lowerer, expr: ts.CallExpression, loc: SrcLoc): IrExpr { if ( expr.arguments.some(ts.isSpreadElement) || expr.arguments.length < 2 || - expr.arguments.length > (shell ? 3 : 4) + expr.arguments.length > 4 ) { lowerer.noLowering( - `${what} with ${expr.arguments.length} arguments`, + `execFile with ${expr.arguments.length} arguments`, expr, - shell - ? "the supported callback forms are exec(command, callback) and exec(command, { encoding: 'utf8', maxBuffer: N }, callback)" - : "the supported callback forms are execFile(file, callback), execFile(file, args, callback), and execFile(file, args, { encoding: 'utf8', maxBuffer: N }, callback)", + "the supported callback forms are execFile(file, callback), execFile(file, args, callback), and execFile(file, args, { encoding: 'utf8', maxBuffer: N }, callback)", ); } - const first = lowerer.lowerExprExpecting(expr.arguments[0]!, STRING); + const cmd = lowerer.lowerExprExpecting(expr.arguments[0]!, STRING); + const argsNode = expr.arguments.length >= 3 ? expr.arguments[1] : undefined; const callbackNode = expr.arguments[expr.arguments.length - 1]!; - // exec: /bin/sh -c , execSync's lowering (the runtime reads the - // command line back from argv[1] for failure messages); execFile: the - // file and its args list. - const cmd: IrExpr = shell ? { kind: "strLit", value: "/bin/sh", type: STRING, loc } : first; - const argv: IrExpr = shell - ? { - kind: "arrayLit", - elems: [{ kind: "strLit", value: "-c", type: STRING, loc }, first], - type: arrayOf(STRING), - loc, - } - : lowerChildArgsArg(lowerer, expr.arguments.length >= 3 ? expr.arguments[1] : undefined, loc); - const optionsNode = - expr.arguments.length === (shell ? 3 : 4) ? expr.arguments[shell ? 1 : 2] : undefined; - if (optionsNode) { - const options = optionsNode; + const argv = lowerChildArgsArg(lowerer, argsNode, loc); + if (expr.arguments.length === 4) { + const options = expr.arguments[2]!; if (!ts.isObjectLiteralExpression(options)) { lowerer.noLowering( - `${what} with a non-literal options argument`, + "execFile with a non-literal options argument", options, "pass encoding and maxBuffer in an inline object literal", ); @@ -796,7 +774,7 @@ export function lowerExecFileCall( const member = optionMember(property); if (!member) lowerer.noLowering( - `${what} with this options shape`, + "execFile with this options shape", property, "use plain encoding and maxBuffer properties without spreads or computed keys", ); @@ -806,7 +784,7 @@ export function lowerExecFileCall( (member.value.text !== "utf8" && member.value.text !== "utf-8") ) { lowerer.noLowering( - `${what} with a non-literal utf8 encoding`, + "execFile with a non-literal utf8 encoding", member.value, "pass the literal 'utf8' or 'utf-8' (other expressions might have side effects)", ); @@ -814,14 +792,14 @@ export function lowerExecFileCall( } else if (member.name === "maxBuffer") { if (!pureNumber(member.value)) { lowerer.noLowering( - `${what} with a non-literal maxBuffer`, + "execFile with a non-literal maxBuffer", member.value, "pass a numeric literal or literal arithmetic (the native capture grows without enforcing this limit)", ); } } else { lowerer.noLowering( - `${what} option '${member.name}'`, + `execFile option '${member.name}'`, property, "only encoding: 'utf8' and a pure numeric maxBuffer are supported in the callback form", ); @@ -837,14 +815,14 @@ export function lowerExecFileCall( lowerer.unsupported( "SC1090", callbackNode, - `${what} callbacks take (error), (error, stdout), (error, stdout, stderr), or no parameters`, + "execFile callbacks take (error), (error, stdout), (error, stdout, stderr), or no parameters", ); } if (callback.type.ret.kind !== "void") { lowerer.unsupported( "SC1090", callbackNode, - `${what} callbacks returning a value (make the callback body a block, or return nothing)`, + "execFile callbacks returning a value (make the callback body a block, or return nothing)", ); } const errorParam = callback.type.params[0]; @@ -859,7 +837,7 @@ export function lowerExecFileCall( lowerer.unsupported( "SC1090", callbackNode, - `${what} callbacks whose first parameter is not 'Error | null' (got '${lowerer.fmt(errorParam)}')`, + `execFile callbacks whose first parameter is not 'Error | null' (got '${lowerer.fmt(errorParam)}')`, ); } } @@ -868,15 +846,9 @@ export function lowerExecFileCall( lowerer.unsupported( "SC1090", callbackNode, - `${what} callbacks whose ${i === 1 ? "stdout" : "stderr"} parameter is not 'string'`, + `execFile callbacks whose ${i === 1 ? "stdout" : "stderr"} parameter is not 'string'`, ); } } - return { - kind: "libCall", - fn: "cp.execFile", - args: [cmd, argv, callback, boolLit(shell, loc)], - type: CHILD_T, - loc, - }; + return { kind: "libCall", fn: "cp.execFile", args: [cmd, argv, callback], type: CHILD_T, loc }; } diff --git a/packages/compiler/src/frontend/lowering/builtins/errors.ts b/packages/compiler/src/frontend/lowering/builtins/errors.ts index 2521ded2e7..7ea7be56ae 100644 --- a/packages/compiler/src/frontend/lowering/builtins/errors.ts +++ b/packages/compiler/src/frontend/lowering/builtins/errors.ts @@ -21,28 +21,6 @@ const ASSERTION_ERROR_PROPERTIES: ReadonlySet = new Set([ * declaration is not stdlib). Reads only: writes keep their fence (no * compiled program constructs an errno error). Null for non-error * receivers and non-stdlib members, so the chain keeps trying. */ -/** True for child_process.ExecException, exec's callback error. Its - * `code` is the child's numeric exit status (null after a signal), which - * the runtime error's string code slot does not hold, so reads, `in` tests - * and typeof tests of `code` refuse instead of answering from the slot. */ -export function isExecExceptionType(lowerer: Lowerer, t: ts.Type): boolean { - const sym = t.getSymbol(); - return ( - sym?.name === "ExecException" && - lowerer.checker - .declarationsOf(sym) - .some((d) => ts.isInterfaceDeclaration(d) && lowerer.isStdlibFile(d.getSourceFile())) - ); -} - -export function refuseExecExceptionCode(lowerer: Lowerer, node: ts.Node): never { - return lowerer.noLowering( - "ExecException.code", - node, - "spawn reports the exit status through its 'close' event", - ); -} - export function lowerErrorCodeProperty( lowerer: Lowerer, expr: ts.PropertyAccessExpression, @@ -142,9 +120,6 @@ export function lowerErrorCodeProperty( !(recvT.className === "%Error" && lowerer.typeOf(expr.expression).isIntersectionType()) ) return null; - if (expr.name.text === "code" && isExecExceptionType(lowerer, lowerer.typeOf(expr.expression))) { - refuseExecExceptionCode(lowerer, expr); - } const rawReceiver = lowerer.lowerExpr(expr.expression); const receiver = rawReceiver.type.kind === "dyn" diff --git a/packages/compiler/src/frontend/lowering/lower-exprs.ts b/packages/compiler/src/frontend/lowering/lower-exprs.ts index dcb69d67a0..7e1524022a 100644 --- a/packages/compiler/src/frontend/lowering/lower-exprs.ts +++ b/packages/compiler/src/frontend/lowering/lower-exprs.ts @@ -8,7 +8,6 @@ import { lowerWorkerMetadata } from "./builtins/workers.js"; import { lowerReadlineProperty } from "./builtins/readline.js"; import { lowerBlobInstanceof } from "./builtins/blob.js"; import { lowerKeyObjectInstanceof } from "./builtins/key-object.js"; -import { isExecExceptionType, refuseExecExceptionCode } from "./builtins/errors.js"; import { STATS_TYPE_TESTS } from "./builtins/filesystem.js"; import { threadsImportOf } from "../threads-import.js"; import { experimentalImportOf, lifetimeRegionMemberOf } from "../experimental-import.js"; @@ -11119,7 +11118,6 @@ function lowerErrorCodeTypeofTest( recv = recv.expression; const errT = errorRootedObjectOf(lowerer, lowerer.typeOf(recv)); if (!errT) continue; - if (isExecExceptionType(lowerer, lowerer.typeOf(recv))) refuseExecExceptionCode(lowerer, prop); const caught = lowerer.caughtLocalOf(recv); let receiver: IrExpr; if (caught) { @@ -12716,9 +12714,6 @@ function lowerInExpression(lowerer: Lowerer, expr: ts.BinaryExpression, loc: Src loc, }; } - if (key === "code" && isExecExceptionType(lowerer, lowerer.typeOf(expr.right))) { - refuseExecExceptionCode(lowerer, expr); - } let recv = lowerer.lowerExpr(expr.right); const classInfo = recv.type.kind === "classval" ? lowerer.classes.get(recv.type.className) : undefined; diff --git a/packages/compiler/src/frontend/lowering/lowerer.ts b/packages/compiler/src/frontend/lowering/lowerer.ts index 0fff3c86a7..25c70e2f4c 100644 --- a/packages/compiler/src/frontend/lowering/lowerer.ts +++ b/packages/compiler/src/frontend/lowering/lowerer.ts @@ -11485,8 +11485,8 @@ export class Lowerer { return lowerSpawnCall(this, expr, loc); } - lowerExecFileCall(expr: ts.CallExpression, loc: SrcLoc, shell = false): IrExpr { - return lowerExecFileCall(this, expr, loc, shell); + lowerExecFileCall(expr: ts.CallExpression, loc: SrcLoc): IrExpr { + return lowerExecFileCall(this, expr, loc); } lowerExecSyncCall(expr: ts.CallExpression, shell: boolean, loc: SrcLoc): IrExpr { diff --git a/packages/compiler/src/frontend/type-mapper.ts b/packages/compiler/src/frontend/type-mapper.ts index 4b577fd435..320d4b2379 100644 --- a/packages/compiler/src/frontend/type-mapper.ts +++ b/packages/compiler/src/frontend/type-mapper.ts @@ -1341,23 +1341,6 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null { ) { return { kind: "object", className: "%Error" }; } - // child_process.ExecException, exec's callback error, is an interface - // extending Error with optional metadata members; the value is one - // ordinary Error as well. - const execException = type.getSymbol(); - if ( - execException?.name === "ExecException" && - checker - .declarationsOf(execException) - .some( - (decl) => - ts.isInterfaceDeclaration(decl) && - ctx.isStdlibFile(decl.getSourceFile()) && - isDeclaredInAmbientModule(decl, "child_process"), - ) - ) { - return { kind: "object", className: "%Error" }; - } if (isOsConstantsType(type, ctx) || isLegacyUrlType(type, ctx)) return DYN; // Immutable (@scriptc/types) is a deep-readonly view: the value is // still the T it maps. diff --git a/packages/compiler/src/ir/builtin-signatures.ts b/packages/compiler/src/ir/builtin-signatures.ts index 43940e2d89..969af228b4 100644 --- a/packages/compiler/src/ir/builtin-signatures.ts +++ b/packages/compiler/src/ir/builtin-signatures.ts @@ -2222,9 +2222,8 @@ 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. */ + "cp.execFile": { argTypes: [STRING, arrayOf(STRING), null], 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 56d571cfec..463b9bc626 100644 --- a/packages/compiler/src/ir/validate.test.ts +++ b/packages/compiler/src/ir/validate.test.ts @@ -951,7 +951,6 @@ test("library callbacks retain child, specialized, and generic result diagnostic { kind: "strLit", value: "tool", type: STRING, loc }, { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc }, { kind: "boolLit", value: true, type: F64, loc }, - { kind: "boolLit", value: false, type: BOOL, loc }, ], }; expect(validateModule(expressionModule(expr, [])).map((error) => error.message)).toEqual([ diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5627-exec-callback.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5627-exec-callback.ts.json deleted file mode 100644 index cbefe8acca..0000000000 --- a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/5627-exec-callback.ts.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "order": [ - "/tests/corpus/5627-exec-callback.ts" - ], - "diags": [] -} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec-code-fences.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec-code-fences.ts.json deleted file mode 100644 index 8f8a2a4b89..0000000000 --- a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec-code-fences.ts.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "order": [ - "/tests/fixtures/node-types/child-exec-code-fences.ts" - ], - "diags": [] -} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec.ts.json deleted file mode 100644 index 73db22acae..0000000000 --- a/packages/compiler/test/ts7/baselines/order-parity/tests/fixtures/node-types/child-exec.ts.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "order": [ - "/tests/fixtures/node-types/child-exec.ts" - ], - "diags": [] -} diff --git a/packages/runtime/src/scr_child.c b/packages/runtime/src/scr_child.c index 3bce01fc71..715b69ab0a 100644 --- a/packages/runtime/src/scr_child.c +++ b/packages/runtime/src/scr_child.c @@ -2265,14 +2265,14 @@ static void scr_exec_file_fire(ScrChild *c) { } ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb, - ScrExecFileFn fn, bool shell) { + ScrExecFileFn fn) { ScrStr *empty = scr_str_new("", 0); ScrChild *c = scr_spawn_opts(cmd, args, 3, 3, 3, 0, 0, false, false, false, NULL, empty); scr_str_release(empty); c->exec_cb = cb; c->exec_fn = fn; - char *display = scr_exec_display(cmd, args, shell); + char *display = scr_exec_display(cmd, args, false); c->exec_display = scr_str_new(display, strlen(display)); free(display); scr_child_stream_capture_begin(c->out_stream); @@ -4905,14 +4905,14 @@ static void scr_exec_file_fire(ScrChild *c) { } ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb, - ScrExecFileFn fn, bool shell) { + ScrExecFileFn fn) { ScrStr *empty = scr_str_new("", 0); ScrChild *c = scr_spawn_opts(cmd, args, 3, 3, 3, 0, 0, false, false, false, NULL, empty); scr_str_release(empty); c->exec_cb = cb; c->exec_fn = fn; - char *display = scr_exec_display(cmd, args, shell); + char *display = scr_exec_display(cmd, args, false); c->exec_display = scr_str_new(display, strlen(display)); free(display); scr_child_stream_capture_begin(c->out_stream); diff --git a/packages/runtime/src/scr_child.h b/packages/runtime/src/scr_child.h index 1819b9c538..350995e315 100644 --- a/packages/runtime/src/scr_child.h +++ b/packages/runtime/src/scr_child.h @@ -155,11 +155,9 @@ ScrChild *scr_spawn_dynamic(ScrStr *cmd, ScrArr *args, const ScrDyn *options); void scr_child_on_spawn(ScrChild *child, ScrClosure *cb); /* moves zero-argument callback */ /* The callback slice: file + args, default options. The returned child has * all three stdio slots piped; stdout/stderr are captured internally and - * the callback moves into the child registry until settlement. With shell - * (exec), args is ["-c", command] for /bin/sh and failure messages show - * the command, as for scr_exec_sync. */ + * the callback moves into the child registry until settlement. */ ScrChild *scr_exec_file(ScrStr *cmd, ScrArr *args, ScrClosure *cb /*moves*/, - ScrExecFileFn fn, bool shell); + ScrExecFileFn fn); ScrChild *scr_child_retain(ScrChild *c); void scr_child_release(ScrChild *c); void *scr_child_retain_v(void *p); diff --git a/tests/corpus/5627-exec-callback.ts b/tests/corpus/5627-exec-callback.ts deleted file mode 100644 index eed79e738c..0000000000 --- a/tests/corpus/5627-exec-callback.ts +++ /dev/null @@ -1,36 +0,0 @@ -// child_process.exec's callback forms: the command line runs with -// /bin/sh -c, the call returns a live ChildProcess immediately, utf8 -// stdout/stderr are captured, and the error-first callback fires after the -// current turn. A non-zero exit (a missing command is the shell's 127) -// reports "Command failed: \n". -import { exec } from "node:child_process"; -import * as childProcess from "node:child_process"; - -const child = exec("printf out; printf err >&2", (error, stdout, stderr) => { - console.log("ok", error === null, JSON.stringify(stdout), JSON.stringify(stderr)); - exec("printf 'a\\nb\\nc\\n' | wc -l", (pipeError, lines) => { - console.log("pipe", pipeError === null, lines.trim()); - exec(`printf '%s|' "a b" 'c d' e`, { encoding: "utf8", maxBuffer: 1024 * 1024 }, (_, quoted) => { - console.log("quoting", quoted); - exec("printf partial; printf failed >&2; exit 3", (failed, failedOut, failedErr) => { - console.log("fail", failed !== null, JSON.stringify(failedOut), JSON.stringify(failedErr)); - console.log("fail-message", JSON.stringify(failed === null ? "" : failed.message)); - exec("exit 1", (silent) => { - console.log("silent", JSON.stringify(silent === null ? "" : silent.message)); - exec("scriptc-definitely-not-a-command", (missing, missingOut) => { - const message = missing === null ? "" : missing.message; - console.log("missing", missing !== null, JSON.stringify(missingOut)); - console.log("missing-message", message.split("\n")[0]); - exec("true", () => { - console.log("zero-parameter callback"); - childProcess.exec("printf namespace", (_, out) => console.log(out)); - }); - }); - }); - }); - }); - }); -}); - -console.log("returned", child.pid !== undefined, child.stdout !== null, child.stderr !== null); -console.log("scheduled"); diff --git a/tests/fixtures/node-types/child-exec-code-fences.ts b/tests/fixtures/node-types/child-exec-code-fences.ts deleted file mode 100644 index 71f627df46..0000000000 --- a/tests/fixtures/node-types/child-exec-code-fences.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { exec } from "node:child_process"; - -exec("exit 3", (error) => { - if (error === null) return; - console.log(error.code); - console.log("code" in error); - console.log(typeof error.code === "number"); -}); diff --git a/tests/fixtures/node-types/child-exec.ts b/tests/fixtures/node-types/child-exec.ts deleted file mode 100644 index 97e9c546df..0000000000 --- a/tests/fixtures/node-types/child-exec.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { exec } from "node:child_process"; - -exec("printf typed-exec; printf err >&2", (error, stdout, stderr) => { - if (error !== null) throw error; - console.log(stdout, stderr); - exec("exit 2", { encoding: "utf8" }, (failed) => { - console.log(JSON.stringify(failed === null ? "" : failed.message)); - }); -}); diff --git a/tests/harness/project-config.test.ts b/tests/harness/project-config.test.ts index d828750892..5a20739d2b 100644 --- a/tests/harness/project-config.test.ts +++ b/tests/harness/project-config.test.ts @@ -426,40 +426,6 @@ test("node-types: unsupported child stdin forms remain named fences", async () = ]); }); -test("node-types: callback exec uses the typed ExecException overloads", async () => { - const dir = outDirFor("node-child-exec"); - const entry = join(nodeTypesDir, "child-exec.ts"); - const result = await compile(entry, { - outPath: join(dir, "child-exec"), - outDir: dir, - sanitize, - }); - expect(result.ok, !result.ok ? JSON.stringify(result.diagnostics, null, 2) : "").toBe(true); - if (!result.ok) return; - const { stdout } = await execFileAsync(result.binaryPath); - expect(stdout).toBe('typed-exec err\n"Command failed: exit 2\\n"\n'); -}); - -test("node-types: exec's numeric ExecException.code remains a named fence", async () => { - const dir = outDirFor("node-child-exec-code-fences"); - const entry = join(nodeTypesDir, "child-exec-code-fences.ts"); - const result = await compile(entry, { - outPath: join(dir, "child-exec-code-fences"), - outDir: dir, - sanitize, - }); - expect(result.ok).toBe(false); - if (result.ok) return; - expect(result.diagnostics.map((diagnostic) => diagnostic.code)).toEqual([ - "SC2020", - "SC2020", - "SC2020", - ]); - for (const diagnostic of result.diagnostics) { - expect(diagnostic.message).toContain("ExecException.code"); - } -}); - test("node-types: path and os lower statically under @types/node's shapes", async () => { const outDir = outDirFor("node-path-os"); const result = await compile(join(nodeTypesDir, "path-os.ts"), { diff --git a/tests/harness/windows-differential.test.ts b/tests/harness/windows-differential.test.ts index dc199d38f2..7e6ed205d2 100644 --- a/tests/harness/windows-differential.test.ts +++ b/tests/harness/windows-differential.test.ts @@ -149,8 +149,6 @@ const WINDOWS_SKIPS: Record = { "posix-shaped: the uncaught sh ENOENT throw crashes both sides, rendered differently (1645 covers env here)", "1464-env-writes.ts": 'posix-shaped: the observing child is ENOENT on Windows, exposing the documented spawn-failure ""-vs-null stance', - "5627-exec-callback.ts": - "posix-shaped: exec runs /bin/sh -c (execSync's static shell lowering); Windows Node runs cmd.exe", // Two programs the events unit's win32 arm surfaced (they compiled for // the first time once the events gate lifted): both drive their signal // listeners through a /bin/sh child, ENOENT on Windows Node too.