perf(javascript): defer ancestry for member identifier roles

This commit is contained in:
morluto committed 2026-10-24 08:22:14 +08:00
1 parent aeeb4b72c1
commit 3e6ac23b50
3 files changed
+87 -5

No files matched your search

@@ -0,0 +1,75 @@
import * as t from "@babel/types";
import { expect, it } from "vitest";
import { semanticIdentifierRole } from "./javascriptSemanticProjection.js";
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
import { parseJavaScriptSource } from "./javascriptSourceParser.js";
const rolesFor = (source: string) => {
const parsed = parseJavaScriptSource(source);
if (parsed === null) throw new Error("Expected parsed source");
const references: { name: string; role: string }[] = [];
let snapshots = 0;
traverseJavaScriptAst(parsed.program, {
enter(node, parent, readAncestors) {
if (!t.isIdentifier(node) || parent === null) return;
const role = semanticIdentifierRole(node, parent, () => {
snapshots++;
return readAncestors();
});
if (role !== null) references.push({ name: node.name, role });
},
});
return { references, snapshots };
};
it.each([256, 512])(
"classifies a %s-member chain without copying or scanning its ancestry",
(depth) => {
expect(rolesFor(`object${".property".repeat(depth)};`)).toEqual({
references: [{ name: "object", role: "read" }],
snapshots: 0,
});
},
);
it("retains optional and computed member reads without ancestor snapshots", () => {
expect(
rolesFor("object.property; object[key]; object?.property; object?.[key];"),
).toEqual({
references: [
{ name: "object", role: "read" },
{ name: "object", role: "read" },
{ name: "key", role: "read" },
{ name: "object", role: "read" },
{ name: "object", role: "read" },
{ name: "key", role: "read" },
],
snapshots: 0,
});
});
it("still reads ancestry to distinguish bound patterns from default-value reads", () => {
const result = rolesFor(
"const {key: value = compute()} = object; ({key: target} = source);",
);
expect(result.references).toEqual([
{ name: "compute", role: "read" },
{ name: "object", role: "read" },
{ name: "target", role: "write" },
{ name: "source", role: "read" },
]);
expect(result.snapshots).toBeGreaterThan(0);
});
it("keeps member receivers and computed keys as reads in assignment patterns and updates", () => {
expect(
rolesFor("({field: object[key]} = source); object[key]++;").references,
).toEqual([
{ name: "object", role: "read" },
{ name: "key", role: "read" },
{ name: "source", role: "read" },
{ name: "object", role: "read" },
{ name: "key", role: "read" },
]);
});