fix(javascript): read utilityProcess.fork options from the argv slot (#6280)

Electron's utilityProcess.fork(modulePath[, args][, options]) treats a
non-array object in the second argument as the options and ignores the
third. The static extractor only read the third argument, so
`fork("./worker.js", { serviceName })` lost the declared name while
reporting exact, complete coverage, and an object in the second slot let
an ignored third argument supply the name.

Move the optional-argv options selection that child_process already used
into the shared expression-shape owner and reuse it for utility
processes. Unproven argv shapes, spreads and identifier options remain
unknown with partial coverage; nullish options or a nullish serviceName
declare no name, matching Electron's runtime checks.

Fixes #6208

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822andClaude Opus 5.5 authored and GitHub committed 2026-10-22 01:17:40 -07:00
1 parent 2452efa921
commit 73b756430f
7 files changed
+126 -36

No files matched your search

@@ -24,6 +24,11 @@ import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
import { javaScriptRuntimeContext } from "./javascriptSourceParser.js";
type Shape = "array" | "callback" | "unknown";
/** Bounded argument-shape queries shared by call-signature owners. */
export type JavaScriptExpressionShapeResolver = ReturnType<
typeof createSemanticExpressionShapeResolver
>;
const commonJsWrapperParameterNames = new Set([
"exports",
"require",
@@ -137,3 +142,26 @@ export const createSemanticExpressionShapeResolver = (
},
};
};
/**
* Select the options argument of a `(target[, argv][, options])` signature,
* such as Node child_process spawn/execFile/fork or Electron
* utilityProcess.fork. Only a proven argv array or nullish placeholder admits
* third-slot options; any other second argument may itself be the options
* object, which makes the runtime ignore the third. Spread arguments are
* returned unchanged so callers keep their unknown positions unknown. The
* returned node is unwrapped; `undefined` means no options were supplied.
*/
export const optionsAfterOptionalArgv = (
args: readonly t.CallExpression["arguments"][number][],
shapes: JavaScriptExpressionShapeResolver,
): t.Node | undefined => {
const [first, second, third] = args;
if (t.isSpreadElement(first)) return first;
if (second === undefined) return undefined;
const options =
shapes.isArray(second) || shapes.isNullish(second) ? third : second;
return options === undefined
? undefined
: unwrapJavaScriptExpression(options).node;
};