fix(javascript): preserve effective child-process stdio options (#4336)

* fix(javascript): preserve effective child-process stdio options

* fix(javascript): retain proven callbacks and primitive stdio keys

* fix(javascript): preserve argv shapes and nullish spreads

* fix(javascript): bound callable proofs and retain exposed binding uncertainty

* test(javascript): keep callback acceptance within transport budget

* test(javascript): inspect application coverage for callback proof limits

* fix(javascript): keep spread command argument slots uncertain

* fix(javascript): bound mapped argument content proofs

* fix(javascript): distinguish erased declarations from runtime shadows

* fix(javascript): prove runtime module context before binding isolation

* fix(javascript): share erased declaration and opaque scope proofs

* fix(javascript): keep lowered enum scopes opaque

* fix(javascript): narrow stdio proofs and consolidate regressions

* perf(javascript): reuse lexical references for child shapes

* fix(javascript): retain CommonJS child argument context

* fix(javascript): keep Annex B affected child arguments unknown

* fix(javascript): recognize value import-equals runtime modules

* fix(javascript): retain exact stdio through module and primitive syntax

* fix(javascript): bound inline spread property resolution

* fix(javascript): distinguish copied methods from getters
This commit is contained in:
星闌
2026-10-17 15:51:55 +08:00
committed by GitHub
parent 0cd4eafd61
commit de9371b85d
18 changed files with 1114 additions and 93 deletions
+84 -15
View File
@@ -1,5 +1,6 @@
import * as t from "@babel/types";
import type { JavaScriptSemanticPrimitive } from "./javascriptSemanticValueTypes.js";
import { SEMANTIC_EXPRESSION_DEPTH_LIMIT } from "./javascriptSemanticResourceLimits.js";
/** TypeScript receiver annotations occupy no positional runtime parameter slot. */
export const runtimeFunctionParameters = (
@@ -88,14 +89,24 @@ export const semanticStaticPropertyKey = (
computed: boolean,
): string | null => {
property = unwrapJavaScriptExpression(property).node;
if (t.isStringLiteral(property) || t.isNumericLiteral(property))
return String(property.value);
if (t.isBigIntLiteral(property)) return String(property.value);
if (
t.isUnaryExpression(property, { operator: "-" }) &&
t.isBigIntLiteral(property.argument)
t.isStringLiteral(property) ||
t.isNumericLiteral(property) ||
t.isBooleanLiteral(property)
)
return String(-BigInt(property.argument.value));
return String(property.value);
if (t.isNullLiteral(property)) return "null";
if (t.isBigIntLiteral(property)) return String(property.value);
if (t.isUnaryExpression(property, { operator: "void" })) return "undefined";
if (t.isUnaryExpression(property, { operator: "+" })) {
const argument = unwrapJavaScriptExpression(property.argument).node;
if (t.isNumericLiteral(argument)) return String(argument.value);
}
if (t.isUnaryExpression(property, { operator: "-" })) {
const argument = unwrapJavaScriptExpression(property.argument).node;
if (t.isNumericLiteral(argument)) return String(-argument.value);
if (t.isBigIntLiteral(argument)) return String(-BigInt(argument.value));
}
if (
t.isTemplateLiteral(property) &&
property.expressions.length === 0 &&
@@ -107,20 +118,78 @@ export const semanticStaticPropertyKey = (
return !computed && t.isIdentifier(property) ? property.name : null;
};
/** Syntax whose value cannot contribute own properties to an object spread. */
export const isNullishJavaScriptSyntax = (node: t.Node): boolean => {
node = unwrapJavaScriptExpression(node).node;
return (
t.isNullLiteral(node) || t.isUnaryExpression(node, { operator: "void" })
);
};
type StaticObjectProperty =
| { readonly status: "missing" | "dynamic" }
| { readonly status: "explicit"; readonly property: t.ObjectProperty };
/** Find the effective explicit property, preserving unresolved overrides. */
export const staticObjectProperty = (
object: t.Node | undefined,
name: string,
isNullish?: (node: t.Node) => boolean,
): StaticObjectProperty => {
if (object === undefined) return { status: "missing" };
if (!t.isObjectExpression(object)) return { status: "dynamic" };
const lookup = (
current: t.ObjectExpression,
depth: number,
): StaticObjectProperty => {
for (const property of current.properties.toReversed()) {
if (t.isSpreadElement(property)) {
const { node: spread, depth: wrappers } = unwrapJavaScriptExpression(
property.argument,
);
if (t.isObjectExpression(spread)) {
const nextDepth = depth + wrappers + 1;
// Copying a getter can run code that changes another source key.
if (
nextDepth > SEMANTIC_EXPRESSION_DEPTH_LIMIT ||
spread.properties.some((entry) =>
t.isObjectMethod(entry, { kind: "get" }),
)
)
return { status: "dynamic" };
const found = lookup(spread, nextDepth);
if (found.status === "missing") continue;
return found;
}
// These primitive boxes have no own keys; strings can own index keys.
if (
t.isBooleanLiteral(spread) ||
t.isNumericLiteral(spread) ||
t.isBigIntLiteral(spread) ||
isNullish?.(property.argument) === true
)
continue;
return { status: "dynamic" };
}
const key = semanticStaticPropertyKey(property.key, property.computed);
if (key === null) return { status: "dynamic" };
if (key !== name) continue;
return t.isObjectProperty(property)
? { status: "explicit", property }
: { status: "dynamic" };
}
return { status: "missing" };
};
return lookup(object, 0);
};
/** Later properties own a key; an intervening dynamic write leaves it unknown. */
export const semanticFinalObjectProperty = (
node: t.ObjectExpression,
key: string,
): t.Node | undefined => {
for (let index = node.properties.length - 1; index >= 0; index -= 1) {
const property = node.properties[index];
if (property === undefined || t.isSpreadElement(property)) return undefined;
const name = semanticStaticPropertyKey(property.key, property.computed);
if (name === null) return undefined;
if (name === key)
return t.isObjectProperty(property) ? property.value : undefined;
}
return undefined;
const property = staticObjectProperty(node, key);
return property.status === "explicit" ? property.property.value : undefined;
};
export const semanticFinalObjectStringProperty = (