fix(javascript): preserve initializer execution and lexical ownership

This commit is contained in:
morluto
2026-10-24 02:28:12 +08:00
parent 5cbcd58550
commit fba4faf88a
8 changed files with 488 additions and 41 deletions
@@ -13,11 +13,12 @@ import {
} from "./javascriptAstValues.js";
import {
resolveSemanticBindingState,
semanticResolutionBlocked,
type JavaScriptSemanticAnalysisState,
} from "./javascriptSemanticState.js";
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
import { semanticBindingAtRead } from "./javascriptSemanticMutationInitializers.js";
import { isUnmodifiedGlobalReference } from "./javascriptSemanticAmbientWrites.js";
import { range } from "./javascriptStaticAnalysisHelpers.js";
import {
assignedSemanticResultBindings,
@@ -140,7 +141,7 @@ const promiseCandidateDetails = (
if (
t.isNewExpression(node) &&
t.isIdentifier(node.callee, { name: "Promise" }) &&
isUnshadowedGlobalPromise(node.callee, state)
isUnmodifiedGlobalReference(node.callee, state, "Promise")
)
return { kind: "constructor", method: "new" };
if (t.isCallExpression(node) || t.isOptionalCallExpression(node)) {
@@ -149,7 +150,7 @@ const promiseCandidateDetails = (
const method = semanticStaticPropertyName(member.property, member.computed);
if (
t.isIdentifier(member.object, { name: "Promise" }) &&
isUnshadowedGlobalPromise(member.object, state)
isUnmodifiedGlobalReference(node.callee, state, ["Promise", method ?? ""])
) {
if (method === "all" || method === "allSettled")
return { kind: "aggregate", method };
@@ -164,13 +165,6 @@ const promiseCandidateDetails = (
return null;
};
const isUnshadowedGlobalPromise = (
node: t.Identifier,
state: JavaScriptSemanticAnalysisState,
): boolean =>
resolveSemanticBindingState(state, node, "Promise") === undefined &&
!semanticResolutionBlocked(state, node, "Promise");
const isPromiseProducer = (
node: t.Node,
state: JavaScriptSemanticAnalysisState,
@@ -447,14 +441,16 @@ const resolvePromiseExpression = (
return { promiseIds: [direct.promiseId], status: "complete" };
if (!t.isIdentifier(node)) return unresolvedPromise();
const binding = resolveSemanticBindingState(state, node, node.name);
if (binding === undefined || seenBindings.has(binding.bindingId))
return unresolvedPromise();
const read = semanticBindingAtRead(binding, node, state.parentsByNode);
if (
binding === undefined ||
binding.mutable ||
binding.initializers.length !== 1 ||
seenBindings.has(binding.bindingId)
read.status !== "ordered" ||
!read.hasDefiniteInitializer ||
read.binding.initializers.length !== 1
)
return unresolvedPromise();
const initializer = binding.initializers[0];
const initializer = read.binding.initializers[0];
if (initializer === undefined || initializer.projection.length > 0)
return unresolvedPromise();
const nestedSeen = new Set(seenBindings);