fix(javascript): preserve builtin identity and share alias traversal

Track module writes and escapes at their owner, preserve capture and reassignment ordering, and share CommonJS/default namespace identity without erasing parent-slot changes. Carry write sites into ambient alias resolution and use parsed write targets across consumers. Deduplicate returned-reference work with cycle context; retain unchanged temporal selection identity so provenance memoization stays effective.

Reviewed fixes include #2041, #2465, #2830, #2881, and #2948. Remove superseded mutation representations and the ineffective per-markValue dedup layer.

Validation: 380 focused JavaScript tests passed under a 384 MiB heap cap, including the previously aborting 30-level/128-call alias regression. Six public CLI/MCP builtin tests passed. Exact pdf.js completed-workflow performance remains unmeasured.
This commit is contained in:
morluto committed 2026-10-14 08:44:52 +08:00
1 parent 0f4c484be5
commit 04672f2b91
34 files changed
+3778 -321

No files matched your search

@@ -2,6 +2,7 @@ import * as t from "@babel/types";
import {
semanticStaticPropertyKey,
semanticWriteSiteTarget,
semanticWriteTargets,
unwrapJavaScriptExpression,
} from "./javascriptAstValues.js";
@@ -62,6 +63,7 @@ export function* collectJavaScriptIdentityWritesSteps(
origin: (
root: t.Node,
kind: "binding" | "member" | "opaque-call",
writeSite: t.Node,
) => readonly (readonly (string | null)[])[],
intrinsicInvocation: (
node: t.CallExpression | t.OptionalCallExpression,
@@ -88,7 +90,8 @@ export function* collectJavaScriptIdentityWritesSteps(
if (t.isCallExpression(node) || t.isOptionalCallExpression(node)) {
const invocation = intrinsicInvocation(node);
if (invocation?.kind === "opaque") {
for (const scope of origin(node, "opaque-call")) retain(node, scope);
for (const scope of origin(node, "opaque-call", node))
retain(node, scope);
return;
}
const callee = invocation?.path;
@@ -117,11 +120,11 @@ export function* collectJavaScriptIdentityWritesSteps(
// A known mutator with an opaque argument list can overwrite any
// captured intrinsic. Do not turn an unexamined target into proof.
if (target === undefined || t.isSpreadElement(target)) {
for (const scope of origin(node, "opaque-call"))
for (const scope of origin(node, "opaque-call", node))
retain(node, scope);
}
if (t.isNode(target) && !t.isSpreadElement(target)) {
const identities = origin(target, "member");
const identities = origin(target, "member", node);
if (identities.length > 0) {
const key = keyedMutators.has(method ?? "")
? [staticString(arguments_?.[1]) ?? null]
@@ -129,26 +132,18 @@ export function* collectJavaScriptIdentityWritesSteps(
for (const identity of identities)
retain(node, [...identity, ...key]);
} else if (!freshObject(target)) {
for (const scope of origin(node, "opaque-call"))
for (const scope of origin(node, "opaque-call", node))
retain(node, scope);
}
}
}
}
}
const target = t.isAssignmentExpression(node)
? node.left
: t.isUpdateExpression(node)
? node.argument
: t.isUnaryExpression(node, { operator: "delete" })
? node.argument
: t.isForOfStatement(node) || t.isForInStatement(node)
? node.left
: undefined;
if (target === undefined) return;
const target = semanticWriteSiteTarget(node);
if (target === null) return;
for (const leaf of semanticWriteTargets(target)) {
if (t.isIdentifier(leaf)) {
for (const identity of origin(leaf, "binding"))
for (const identity of origin(leaf, "binding", node))
retain(node, identity);
continue;
}
@@ -159,7 +154,7 @@ export function* collectJavaScriptIdentityWritesSteps(
)
continue;
const key = semanticStaticPropertyKey(member.property, member.computed);
for (const identity of origin(member.object, "member"))
for (const identity of origin(member.object, "member", node))
retain(node, [...identity, key]);
}
},