mirror of
https://github.com/morluto/rea
synced 2026-10-28 15:07:26 -04:00
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:
@@ -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 = (
|
||||
|
||||
Reference in New Issue
Block a user