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
2026-10-14 08:44:52 +08:00
parent 0f4c484be5
commit 04672f2b91
34 changed files with 3778 additions and 321 deletions
@@ -27,6 +27,10 @@ import { range } from "./javascriptStaticAnalysisHelpers.js";
import {
propertyName,
semanticStaticPropertyKey,
semanticWrappedWriteSite,
semanticWriteSiteReadsFirst,
semanticWriteSiteTarget,
unwrapJavaScriptExpression,
} from "./javascriptAstValues.js";
import {
isTypeOnlyExportDeclaration,
@@ -175,12 +179,17 @@ export function* collectSemanticReferencesSteps(
!patterns.some((pattern) => bindsIdentifier(pattern, parent, node)))
)
return;
const role = semanticIdentifierRole(node, parent, readAncestors());
const ancestors = readAncestors();
const role = semanticIdentifierRole(node, parent, ancestors);
if (role === null) return;
// A compound assignment or update reads before it writes; emit both so
// reference consumers see the read they actually execute.
const roles =
role === "write" && semanticReadsBeforeWrite(node, parent)
role === "write" &&
semanticReadsBeforeWrite(
node,
semanticWrappedWriteSite(node, parent, ancestors) ?? parent,
)
? (["read", "write"] as const)
: ([role] as const);
for (const resolvedRole of roles) {
@@ -594,12 +603,15 @@ const declarationCallableId = (
*/
export const semanticReadsBeforeWrite = (
node: t.Node,
parent: t.Node,
): boolean =>
(t.isAssignmentExpression(parent) &&
parent.left === node &&
parent.operator !== "=") ||
(t.isUpdateExpression(parent) && parent.argument === node);
site: t.Node,
): boolean => {
const target = semanticWriteSiteTarget(site);
return (
target !== null &&
unwrapJavaScriptExpression(target).node === node &&
semanticWriteSiteReadsFirst(site)
);
};
/**
* Role of one identifier occurrence, given the node's ancestry.
@@ -620,6 +632,8 @@ export const semanticIdentifierRole = (
if (isPatternOwnerDeclaration(patternAncestor)) return null;
return "write";
}
const site = semanticWrappedWriteSite(node, parent, ancestors);
if (site !== null && site !== parent && !isDeleteSite(site)) return "write";
return semanticReferenceRole(node, parent);
};
@@ -684,6 +698,9 @@ const isPatternNode = (node: t.Node): boolean =>
t.isAssignmentPattern(node) ||
t.isRestElement(node);
const isDeleteSite = (site: t.Node): boolean =>
t.isUnaryExpression(site, { operator: "delete" });
const isPatternOwnerDeclaration = (node: t.Node): boolean =>
(t.isVariableDeclarator(node) && t.isPattern(node.id)) ||
(t.isFunction(node) &&
@@ -815,16 +832,15 @@ const commonJsExportName = (
if (!t.isMemberExpression(node) && !t.isOptionalMemberExpression(node))
return undefined;
const key = semanticStaticPropertyKey(node.property, node.computed);
// `exports[key]`/`module.exports[key]` assign an export whose name is not
// knowable. Report the wildcard rather than the variable name, and never
// collapse it into `default`, which would claim a real default export.
// A computed nonliteral key is unknown, but the empty string is a real exact
// CommonJS export name and must not collapse into the wildcard/default.
if (isUnshadowedGlobal(node.object, state, "exports"))
return key === null ? "*" : key || "*";
return key === null ? "*" : key;
if (
t.isMemberExpression(node.object) &&
isCommonJsModuleExports(node.object, state)
)
return key === null ? "*" : key || "default";
return key === null ? "*" : key;
if (isCommonJsModuleExports(node, state)) return "default";
return undefined;
};