fix(javascript): share iteration and equality invocation facts

This commit is contained in:
morluto
2026-10-24 02:54:25 +08:00
parent 0729aba6d7
commit e597e090b0
5 changed files with 37 additions and 10 deletions
@@ -225,9 +225,30 @@ it("follows superclass getter effects through the selected receiver", () => {
returned(`
const shared = { value: 1 };
class Base { get trigger2() { this.state.value = 2; return 0; } }
class Box extends Base { state = shared; get trigger() { return super.trigger2; } }
class Box extends Base { state = shared; get trigger2() { return 0; } get trigger() { return super.trigger2; } }
new Box().trigger;
return shared.value;
`)?.status,
).toBe("unknown");
});
it("summarizes per-iteration disposal inside invoked getters", () => {
expect(
returned(`
const shared = {value: 1, [Symbol.dispose]() {this.value = 2;}};
const box = {state: shared, get trigger() {for (using owned of [this.state]) {} return 0;}};
box.trigger;
return shared.value;
`)?.status,
).toBe("unknown");
});
it("keeps same-receiver equality in an inert getter precise", () => {
expect(
returned(`
const state = {value: 1, get trigger() {return this == this;}};
state.trigger;
return state.value;
`),
).toEqual({ status: "literal", value: 1 });
});
@@ -196,10 +196,9 @@ const emitterIdentity = (
...read.binding.initializers,
...read.binding.referenceInitializers,
];
const replaced = [
...binding.initializers,
...binding.referenceInitializers,
].some(({ assignment }) => assignment);
const replaced = binding.definitions.some(
({ kind }) => kind === "assignment",
);
const origin = origins.length === 1 ? origins[0] : undefined;
const complete =
!replaced ||
@@ -365,6 +365,14 @@ class GetterResultWalker {
#visit(current: GetterWork): void {
const expression = unwrapJavaScriptExpression(current.node).node;
if (t.isIdentifier(expression) && this.#alias(current, expression)) return;
if (t.isForOfStatement(expression)) {
this.#enqueue(() => ({
...current,
node: expression.right,
path: [null, ...current.path],
}));
return;
}
if (t.isThisExpression(expression) || t.isSuper(expression))
this.#thisValue(current, expression);
else if (
@@ -10,8 +10,8 @@ export const semanticCoercionOperands = (
if (node.operator === "instanceof") return [node.left, node.right];
if (
["==", "!="].includes(node.operator) &&
isObject(node.left) &&
isObject(node.right)
((t.isThisExpression(node.left) && t.isThisExpression(node.right)) ||
(isObject(node.left) && isObject(node.right)))
)
return [];
return node.operator === "in" ? [node.left] : [node.left, node.right];
@@ -323,11 +323,10 @@ const accessorEffectOperands = (
});
if (
t.isVariableDeclarator(node) &&
node.init != null &&
t.isVariableDeclaration(parent) &&
(parent.kind === "using" || parent.kind === "await using")
)
operands.push({ kind: "escape", node: node.init });
operands.push({ kind: "escape", node: node.init ?? node.id });
for (const child of childNodes(node).reverse())
pending.push({ node: child, parent: node });
}
@@ -445,7 +444,7 @@ export function* collectSemanticMemberMutationsSteps(
const target = receiverReference
? [...receiver, ...reference.path]
: reference.path;
if (operand.kind === "escape")
if (operand.kind === "escape" || t.isSuper(operand.node))
effects.push({ node: targetRoot, path: target });
else
pending.push({