Track module writes and escapes at their owner, preserve capture and reassignment ordering, and share CommonJS/default namespace identity without erasing parent-slot changes. Carry write sites into ambient alias resolution and use parsed write targets across consumers. Deduplicate returned-reference work with cycle context; retain unchanged temporal selection identity so provenance memoization stays effective. Reviewed fixes include #2041, #2465, #2830, #2881, and #2948. Remove superseded mutation representations and the ineffective per-markValue dedup layer. Validation: 380 focused JavaScript tests passed under a 384 MiB heap cap, including the previously aborting 30-level/128-call alias regression. Six public CLI/MCP builtin tests passed. Exact pdf.js completed-workflow performance remains unmeasured.
228 lines
8.5 KiB
TypeScript
228 lines
8.5 KiB
TypeScript
import { expect, it, vi } from "vitest";
|
|
|
|
import {
|
|
analyzeJavaScriptSemantics,
|
|
indexJavaScriptBindingsSteps,
|
|
} from "./javascriptSemanticAnalysis.js";
|
|
import { SemanticDependencyProofs } from "./javascriptSemanticDependencies.js";
|
|
import type { JavaScriptSemanticValue } from "./javascriptSemanticValueTypes.js";
|
|
import {
|
|
clearSemanticValueCaches,
|
|
evaluateSemanticBinding,
|
|
evaluateSemanticProvenance,
|
|
} from "./javascriptSemanticValues.js";
|
|
import { parseJavaScriptSource } from "./javascriptSourceParser.js";
|
|
|
|
const property = (
|
|
value: JavaScriptSemanticValue,
|
|
name: string,
|
|
): JavaScriptSemanticValue => {
|
|
if (value.status !== "object")
|
|
throw new Error(`Expected object at ${name}, got ${value.status}`);
|
|
const result = value.properties.find((entry) => entry.name === name)?.value;
|
|
if (result === undefined) throw new Error(`Missing ${name}`);
|
|
return result;
|
|
};
|
|
|
|
it.each([false, true])(
|
|
"recomputes a shared cyclic value under a different active ancestor (prewarmed: %s)",
|
|
(prewarmed) => {
|
|
const parsed = parseJavaScriptSource(
|
|
`
|
|
const a = { shared: common, own: "a" };
|
|
const b = { shared: common, own: "b" };
|
|
const common = { a, b };
|
|
const result = { first: a, second: b };
|
|
`,
|
|
"cycles.js",
|
|
);
|
|
if (parsed === null) throw new Error("Expected parsed source");
|
|
const steps = indexJavaScriptBindingsSteps(parsed.program);
|
|
let step = steps.next();
|
|
while (step.done !== true) step = steps.next();
|
|
const state = step.value;
|
|
const binding = [...state.bindingsById.values()].find(
|
|
(entry) => entry.name === "result",
|
|
);
|
|
if (binding === undefined) throw new Error("Missing result binding");
|
|
if (prewarmed)
|
|
for (const entry of state.bindingsById.values())
|
|
if (entry.name === "a" || entry.name === "b")
|
|
evaluateSemanticBinding(entry, state);
|
|
const value = evaluateSemanticBinding(binding, state);
|
|
const firstCommon = property(property(value, "first"), "shared");
|
|
const secondCommon = property(property(value, "second"), "shared");
|
|
expect(property(firstCommon, "a").status).toBe("cycle");
|
|
expect(property(property(firstCommon, "b"), "own")).toEqual({
|
|
status: "literal",
|
|
value: "b",
|
|
});
|
|
expect(property(secondCommon, "b").status).toBe("cycle");
|
|
expect(property(property(secondCommon, "a"), "own")).toEqual({
|
|
status: "literal",
|
|
value: "a",
|
|
});
|
|
},
|
|
);
|
|
|
|
it("invalidates retained alias values at the producing mutation boundary", () => {
|
|
const parsed = parseJavaScriptSource(
|
|
"const target = { value: 1 }; const alias = target; const root = { alias };",
|
|
"mutation.js",
|
|
);
|
|
if (parsed === null) throw new Error("Expected parsed source");
|
|
const steps = indexJavaScriptBindingsSteps(parsed.program);
|
|
let step = steps.next();
|
|
while (step.done !== true) step = steps.next();
|
|
const state = step.value;
|
|
const target = [...state.bindingsById.values()].find(
|
|
({ name }) => name === "target",
|
|
);
|
|
const root = [...state.bindingsById.values()].find(
|
|
({ name }) => name === "root",
|
|
);
|
|
if (target === undefined || root === undefined)
|
|
throw new Error("Missing binding");
|
|
expect(
|
|
property(property(evaluateSemanticBinding(root, state), "alias"), "value"),
|
|
).toEqual({ status: "literal", value: 1 });
|
|
target.memberWrites.push({ path: ["value"], node: undefined });
|
|
clearSemanticValueCaches(state);
|
|
expect(
|
|
property(property(evaluateSemanticBinding(root, state), "alias"), "value")
|
|
.status,
|
|
).toBe("unknown");
|
|
});
|
|
|
|
const balancedOr = (names: string[]): string => {
|
|
if (names.length === 1) return names[0]!;
|
|
const middle = Math.floor(names.length / 2);
|
|
return `(${balancedOr(names.slice(0, middle))} || ${balancedOr(names.slice(middle))})`;
|
|
};
|
|
|
|
it("shares dependency proofs across a shallow wide reconvergent graph", () => {
|
|
const measure = (width: number) => {
|
|
const imports = Array.from({ length: width }, (_, index) => `l${index}`);
|
|
const aliases = Array.from({ length: width }, (_, index) => `a${index}`);
|
|
const source = `${imports.map((name) => `import * as ${name} from "fs";`).join("\n")}
|
|
const shared = ${balancedOr(imports)};
|
|
function consumer() {
|
|
${aliases.map((name) => `const ${name} = shared;`).join("\n")}
|
|
const root = ${balancedOr(aliases)};
|
|
}`;
|
|
const parsed = parseJavaScriptSource(source, "wide.js");
|
|
if (parsed === null) throw new Error("Expected parsed source");
|
|
const steps = indexJavaScriptBindingsSteps(parsed.program);
|
|
let step = steps.next();
|
|
while (step.done !== true) step = steps.next();
|
|
const state = step.value;
|
|
const rootBinding = [...state.bindingsById.values()].find(
|
|
({ name }) => name === "root",
|
|
);
|
|
if (rootBinding === undefined) throw new Error("Missing root binding");
|
|
// One root query simultaneously retains every alias memo in this family.
|
|
// Separate per-binding provenance queries have baseline repeated traversal;
|
|
// they must not be confused with this cache's retained closure expansion.
|
|
// Count representation construction and union work, not elapsed time/heap.
|
|
// Binding-ID additions also discriminate the superseded flattened Sets.
|
|
let bindingMemberships = 0;
|
|
let proofBranches = 0;
|
|
let unionCalls = 0;
|
|
const add = Set.prototype.add;
|
|
const freeze = Object.freeze;
|
|
const union = SemanticDependencyProofs.prototype.union;
|
|
const addSpy = vi.spyOn(Set.prototype, "add").mockImplementation(function (
|
|
this: Set<unknown>,
|
|
value: unknown,
|
|
) {
|
|
if (typeof value === "string" && value.includes("binding:"))
|
|
bindingMemberships++;
|
|
return add.call(this, value);
|
|
});
|
|
const freezeSpy = vi.spyOn(Object, "freeze").mockImplementation((value) => {
|
|
if (
|
|
typeof value === "object" &&
|
|
value !== null &&
|
|
Object.hasOwn(value, "zero") &&
|
|
Object.hasOwn(value, "one") &&
|
|
Object.keys(value).length === 2
|
|
)
|
|
proofBranches++;
|
|
return freeze(value);
|
|
});
|
|
const unionSpy = vi
|
|
.spyOn(SemanticDependencyProofs.prototype, "union")
|
|
.mockImplementation(function (
|
|
this: SemanticDependencyProofs,
|
|
first,
|
|
second,
|
|
) {
|
|
unionCalls++;
|
|
return union.call(this, first, second);
|
|
});
|
|
try {
|
|
expect(evaluateSemanticBinding(rootBinding, state).status).toBe(
|
|
"ambiguous",
|
|
);
|
|
expect(evaluateSemanticProvenance(rootBinding, state).origins).toEqual([
|
|
{ specifier: "fs", importedPath: [] },
|
|
]);
|
|
} finally {
|
|
unionSpy.mockRestore();
|
|
freezeSpy.mockRestore();
|
|
addSpy.mockRestore();
|
|
}
|
|
const result = analyzeJavaScriptSemantics(source);
|
|
expect(result.coverage).toEqual({ status: "complete", omittedCount: 0 });
|
|
for (const name of [...imports, ...aliases, "shared", "root"])
|
|
expect(
|
|
result.bindings.filter((entry) => entry.name === name),
|
|
).toHaveLength(1);
|
|
const root = result.bindings.find((entry) => entry.name === "root");
|
|
expect(root?.value.status).toBe("ambiguous");
|
|
expect(root?.provenance.origins).toEqual([
|
|
{ specifier: "fs", importedPath: [] },
|
|
]);
|
|
return {
|
|
storage: bindingMemberships + proofBranches,
|
|
work: bindingMemberships + unionCalls,
|
|
};
|
|
};
|
|
// Balanced depths remain small; doubling both independent widths must not
|
|
// quadruple proof construction/work while all meaningful aliases survive.
|
|
const small = measure(32);
|
|
const large = measure(64);
|
|
expect(small.storage).toBeGreaterThan(0);
|
|
expect(small.work).toBeGreaterThan(0);
|
|
expect(large.storage).toBeLessThan(small.storage * 3);
|
|
expect(large.work).toBeLessThan(small.work * 3);
|
|
});
|
|
|
|
it("uses immutable exact identity proofs and shares unchanged union branches", () => {
|
|
const proofs = new SemanticDependencyProofs(["a", "b", "c", "d", "e"]);
|
|
const a = proofs.binding("a");
|
|
const b = proofs.binding("b");
|
|
const c = proofs.binding("c");
|
|
const ab = proofs.union(a, b);
|
|
const abc = proofs.union(ab, c);
|
|
expect(proofs.union(abc, ab)).toBe(abc);
|
|
expect(proofs.binding("a")).toBe(a);
|
|
expect(Object.isFrozen(abc)).toBe(true);
|
|
expect(["a", "b", "c", "d", "e"].map((id) => proofs.has(abc, id))).toEqual([
|
|
true,
|
|
true,
|
|
true,
|
|
false,
|
|
false,
|
|
]);
|
|
expect(proofs.has(undefined, "a")).toBe(false);
|
|
expect(proofs.intersects(abc, new Map([["d", 0]]))).toBe(false);
|
|
expect(proofs.intersects(abc, new Map([["c", 0]]))).toBe(true);
|
|
const single = new SemanticDependencyProofs(["only"]);
|
|
expect(single.has(single.binding("only"), "only")).toBe(true);
|
|
expect(single.has(undefined, "only")).toBe(false);
|
|
expect(() => proofs.binding("unindexed")).toThrow(
|
|
"Unindexed semantic dependency",
|
|
);
|
|
});
|