fix(javascript): preserve container ownership for getter mutations (#2765)

This commit is contained in:
To3akaRin authored and GitHub committed 2026-10-13 20:10:39 +08:00
1 parent f974353c32
commit eeeb4ca379
5 files changed
+233 -28

No files matched your search

@@ -18,6 +18,7 @@ import {
semanticClassCandidates,
semanticClassPropertyReferences,
semanticObjectPropertyReferences,
type JavaScriptSemanticObjectReference,
} from "./javascriptSemanticObjectReferences.js";
import { semanticIterationSources } from "./javascriptSemanticIterationSources.js";
import { semanticCallableResultExpressions } from "./javascriptSemanticCallableResults.js";
@@ -98,8 +99,20 @@ export function* collectSemanticMemberMutationsSteps(
effect: ValueEffect = "write",
mutation?: t.Node,
originAt: t.Node | undefined = mutation,
ownerInitializer?: t.Node,
): void => {
const pending = [{ node, path, bindings, originAt }];
const pending: (ReferencedValue & {
readonly bindings: ReadonlySet<string>;
readonly originAt: t.Node | undefined;
})[] = [
{
node,
path,
bindings,
originAt,
...(ownerInitializer === undefined ? {} : { ownerInitializer }),
},
];
// Visit shallower alias traversals first so their exact prefixes suppress
// longer loop paths before those paths enumerate every assignment order.
for (let cursor = 0; cursor < pending.length; cursor++) {
@@ -132,6 +145,8 @@ export function* collectSemanticMemberMutationsSteps(
binding.bindingId,
current.originAt?.start,
current.originAt?.end,
current.ownerInitializer?.start,
current.ownerInitializer?.end,
];
const lifetime = JSON.stringify([...capture, current.path]);
if (current.bindings.has(lifetime)) continue;
@@ -165,6 +180,7 @@ export function* collectSemanticMemberMutationsSteps(
"escape",
mutation,
current.originAt,
current.ownerInitializer,
);
continue;
}
@@ -195,6 +211,8 @@ export function* collectSemanticMemberMutationsSteps(
mutation?.end,
current.originAt?.start,
current.originAt?.end,
current.ownerInitializer?.start,
current.ownerInitializer?.end,
]);
const previous = selections.get(selectionIdentity);
const excludedKeys =
@@ -220,6 +238,8 @@ export function* collectSemanticMemberMutationsSteps(
mutation?.end,
current.originAt?.start,
current.originAt?.end,
current.ownerInitializer?.start,
current.ownerInitializer?.end,
]);
let covering = coveringPaths.get(pathState);
if (covering === undefined) {
@@ -235,6 +255,8 @@ export function* collectSemanticMemberMutationsSteps(
mutation?.end,
current.originAt?.start,
current.originAt?.end,
current.ownerInitializer?.start,
current.ownerInitializer?.end,
]);
if (recordedEffects.has(identity)) continue;
// A recorded write invalidates the binding to a non-primitive value, so
@@ -248,10 +270,23 @@ export function* collectSemanticMemberMutationsSteps(
value?.status === "literal" || value?.status === "union";
recordedEffects.add(identity);
const nested = new Set([...current.bindings, lifetime, captureKey]);
// Returning a literal's receiver reaches that allocation, not the
// binding's older values read while evaluating its initializer.
const origins =
current.originAt === undefined
? binding
: semanticMutationInitializers(binding, current.originAt, parents);
current.ownerInitializer !== undefined
? {
initializers: binding.initializers.filter(
({ node }) => node === current.ownerInitializer,
),
referenceInitializers: [],
}
: current.originAt === undefined
? binding
: semanticMutationInitializers(
binding,
current.originAt,
parents,
);
if (!primitiveWrite) {
if (effect === "escape")
binding.escapedPaths.push({ path, node: mutation });
@@ -367,6 +402,8 @@ export function* collectSemanticMemberMutationsSteps(
binding === undefined ? null : binding.bindingId,
binding === undefined ? node.name : null,
pathKey,
value.ownerInitializer?.start,
value.ownerInitializer?.end,
]);
if (leafIdentities.has(identity)) continue;
leafIdentities.add(identity);
@@ -551,9 +588,7 @@ export function* collectSemanticMemberMutationsSteps(
clearSemanticValueCaches(state);
}
interface ReferencedValue {
readonly node: t.Node;
readonly path: PropertyPath;
interface ReferencedValue extends JavaScriptSemanticObjectReference {
readonly iterableFallbackPath?: PropertyPath;
}