fix(javascript): preserve uncertainty after references escape to calls (#1292)

* fix(javascript): preserve uncertainty after references escape to calls

* fix(javascript): track iterator root escapes and preserve static captures

* fix(javascript): scope comparison unknowns to their evidence

* fix(javascript): track parameter origins at function body entry

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
You Wishandmorluto authored and GitHub committed 2026-10-10 06:53:42 +08:00
1 parent d0e606930a
commit 355a292198
17 files changed
+2574 -220

No files matched your search

@@ -52,6 +52,19 @@ interface BindPatternInput {
readonly mutable: boolean;
readonly kind?: JavaScriptSemanticDefinition["kind"];
readonly projection?: readonly (string | number | null)[];
readonly referenceOnly?: boolean;
readonly copyKind?: "object-rest" | "array-rest";
readonly copyProjectionOffset?: number;
readonly copyExcludedKeys?: readonly string[];
readonly copyStartIndex?: number;
readonly fallbackSources?: readonly {
readonly node: t.Node;
readonly projection: readonly (string | number | null)[];
}[];
readonly requiredSources?: readonly {
readonly node: t.Node;
readonly projection: readonly (string | number | null)[];
}[];
}
interface AddBindingInput {
@@ -279,6 +292,7 @@ const createState = (program: t.Program): JavaScriptSemanticAnalysisState => {
bindings: new Map(),
};
return {
parentsByNode: new WeakMap(),
scopes: [root],
scopesById: new Map([[root.scopeId, root]]),
scopeByNode: new WeakMap([[program, root]]),
@@ -752,12 +766,36 @@ const bindPattern = (input: BindPatternInput): void => {
mutable,
kind = "variable",
projection = [],
referenceOnly = false,
copyKind,
copyProjectionOffset,
copyExcludedKeys,
copyStartIndex,
fallbackSources,
requiredSources,
} = input;
if (t.isTSParameterProperty(pattern)) {
bindPattern({ ...input, pattern: pattern.parameter });
return;
}
if (t.isIdentifier(pattern)) {
if (referenceOnly) {
const binding = scope.bindings.get(pattern.name);
if (binding !== undefined && initializer !== null)
binding.referenceInitializers.push({
node: initializer,
projection,
...(copyKind === undefined ? {} : { copyKind }),
...(copyProjectionOffset === undefined
? {}
: { copyProjectionOffset }),
...(copyExcludedKeys === undefined ? {} : { copyExcludedKeys }),
...(copyStartIndex === undefined ? {} : { copyStartIndex }),
...(fallbackSources === undefined ? {} : { fallbackSources }),
...(requiredSources === undefined ? {} : { requiredSources }),
});
return;
}
addBinding({
state,
scope,
@@ -778,6 +816,25 @@ const bindPattern = (input: BindPatternInput): void => {
kind === "parameter" || kind === "catch"
? initializer
: (initializer ?? pattern.right),
requiredSources: [
...(requiredSources ?? []),
...(initializer === null ? [] : [{ node: initializer, projection }]),
],
});
bindPattern({
state,
scope,
mutable,
kind,
pattern: pattern.left,
initializer: pattern.right,
projection: [],
referenceOnly: true,
fallbackSources: [
...(fallbackSources ?? []),
...(initializer === null ? [] : [{ node: initializer, projection }]),
],
...(requiredSources === undefined ? {} : { requiredSources }),
});
return;
}
@@ -792,7 +849,7 @@ const bindPattern = (input: BindPatternInput): void => {
}
if (t.isObjectPattern(pattern))
for (const property of pattern.properties) {
if (t.isRestElement(property))
if (t.isRestElement(property)) {
bindPattern({
...input,
pattern: property.argument,
@@ -800,7 +857,23 @@ const bindPattern = (input: BindPatternInput): void => {
mutable: true,
projection: [],
});
else {
bindPattern({
...input,
pattern: property.argument,
projection: [...projection, null],
referenceOnly: true,
copyKind: "object-rest",
copyProjectionOffset: projection.length,
copyExcludedKeys: pattern.properties.flatMap((property) => {
if (t.isRestElement(property)) return [];
const key = semanticStaticPropertyKey(
property.key,
property.computed,
);
return key === null ? [] : [key];
}),
});
} else {
const name = semanticStaticPropertyKey(property.key, property.computed);
bindPattern({
...input,
@@ -811,12 +884,23 @@ const bindPattern = (input: BindPatternInput): void => {
}
else if (t.isArrayPattern(pattern))
pattern.elements.forEach((element, index) => {
if (element !== null)
if (element !== null) {
bindPattern({
...input,
pattern: element,
projection: [...projection, index],
});
if (t.isRestElement(element))
bindPattern({
...input,
pattern: element.argument,
projection: [...projection, null],
referenceOnly: true,
copyKind: "array-rest",
copyProjectionOffset: projection.length,
copyStartIndex: index,
});
}
});
};
@@ -858,8 +942,10 @@ const createBinding = (
kind,
mutable,
mutatedPaths: [],
escapedPaths: [],
definitions: [],
initializers: [],
referenceInitializers: [],
directOrigins: [],
});