mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
fix(javascript): bound static getter reference expansion
Replace recursive getter histories with a query-owned worklist and memo. Bound precise expansion at 512 states and widen growing receiver cycles; retain escaped roots and constructed children rather than silently dropping suffixes. Carry the resource limit through semantic coverage, worker transport and graph Evidence while preserving direct writes, imports and unrelated literal facts. The small source probe reduced 65/326 paths to one widened reference for five and six getters. The original large Claude artifact was not rerun. Validate receiver/alias/mutation controls and compiled CLI/MCP consumers. Refs #3839, #3848 Co-authored-by: Yi Han <yhan606@aucklanduni.ac.nz>
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
} from "../../domain/javascript/javascriptSemanticAnalysis.js";
|
||||
import { parseJavaScriptSource } from "../../domain/javascript/javascriptSourceParser.js";
|
||||
import { semanticCoverageResourceLimits } from "../../domain/javascript/javascriptSemanticCoverage.js";
|
||||
import { getterExpansionSource } from "../../../tests/fixtures/javascriptGetterExpansion.js";
|
||||
|
||||
const SHA256 = "a".repeat(64);
|
||||
|
||||
@@ -177,13 +178,15 @@ it("preserves primitive candidate budget unknowns in semantic graph coverage", (
|
||||
status: "partial",
|
||||
resourceLimits: ["primitive-candidates"],
|
||||
});
|
||||
expect(graph.unknowns).toContainEqual(
|
||||
expect(
|
||||
graph.unknowns.filter(({ reason }) => reason === "resource-limit"),
|
||||
).toEqual([
|
||||
expect.objectContaining({
|
||||
family: "data-flow",
|
||||
reason: "resource-limit",
|
||||
detail: expect.stringMatching(/primitive candidate budget exceeded/i),
|
||||
}),
|
||||
);
|
||||
]);
|
||||
});
|
||||
|
||||
it("preserves derived string-byte limit reasons in semantic graph unknowns", () => {
|
||||
@@ -242,12 +245,14 @@ it("preserves deep expression resource reasons through graph projection", () =>
|
||||
expect(semanticCoverageResourceLimits(ir.coverage)).toEqual([
|
||||
"expression-depth",
|
||||
]);
|
||||
expect(graph.unknowns).toContainEqual(
|
||||
expect(
|
||||
graph.unknowns.filter(({ reason }) => reason === "resource-limit"),
|
||||
).toEqual([
|
||||
expect.objectContaining({
|
||||
reason: "resource-limit",
|
||||
detail: expect.stringMatching(/expression depth budget exceeded/i),
|
||||
}),
|
||||
);
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps ambiguous interprocedural flow out of the default traversal", () => {
|
||||
@@ -473,6 +478,26 @@ const semanticNodeFor = (roleKey: string) => {
|
||||
);
|
||||
};
|
||||
|
||||
it("preserves getter expansion limits in semantic graph coverage", () => {
|
||||
const source = getterExpansionSource();
|
||||
const graph = graphFor(source, analyzeJavaScriptSemantics(source));
|
||||
const module = graph.nodes.find(({ kind }) => kind === "module");
|
||||
if (module === undefined) throw new Error("Expected module node");
|
||||
expect(
|
||||
graph.unknowns.filter(({ reason }) => reason === "resource-limit"),
|
||||
).toEqual([
|
||||
expect.objectContaining({
|
||||
node_id: module.node_id,
|
||||
family: "data-flow",
|
||||
reason: "resource-limit",
|
||||
detail: expect.stringMatching(
|
||||
/getter expansion was conservatively bounded/i,
|
||||
),
|
||||
}),
|
||||
]);
|
||||
expect(graph.coverage.status).toBe("unknown");
|
||||
});
|
||||
|
||||
it.each([
|
||||
"const value = consume(Promise.resolve(1));",
|
||||
"async function run() { await consume(Promise.resolve(1)); }",
|
||||
|
||||
@@ -34,8 +34,16 @@ import {
|
||||
import { projectSemanticResources } from "./JavaScriptSemanticGraphResourceProjection.js";
|
||||
import { projectSemanticObjects } from "./JavaScriptSemanticGraphObjectProjection.js";
|
||||
import { projectSemanticValues } from "./JavaScriptSemanticGraphValueProjection.js";
|
||||
import { inferredSemanticEvidenceAt } from "./JavaScriptSemanticGraphEvidence.js";
|
||||
import { observedSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
||||
import {
|
||||
inferredSemanticEvidenceAt,
|
||||
observedSemanticEvidence,
|
||||
unknownSemanticEvidence,
|
||||
} from "./JavaScriptSemanticGraphEvidence.js";
|
||||
import {
|
||||
semanticCoverageResourceLimits,
|
||||
semanticResourceLimitCoverage,
|
||||
} from "../../domain/javascript/javascriptSemanticCoverage.js";
|
||||
import { semanticResourceLimitReason } from "../../domain/javascript/javascriptSemanticResourceLimits.js";
|
||||
import {
|
||||
partialApplicationCoverage,
|
||||
type ApplicationCoverage,
|
||||
@@ -465,6 +473,7 @@ function* projectFileSteps(
|
||||
});
|
||||
if (moduleNode === null) return [];
|
||||
state.roots.add(moduleNode.node_id);
|
||||
projectFileResourceLimits({ file, ir, state, moduleNode });
|
||||
yield* projectElectronSemanticCallPaths(file, paths, state);
|
||||
// Node creation keeps source order, which the per-file node budget relies
|
||||
// on; yields between nodes do not change it.
|
||||
@@ -553,6 +562,36 @@ function* projectFileSteps(
|
||||
);
|
||||
}
|
||||
|
||||
const projectFileResourceLimits = (
|
||||
context: Pick<FileContext, "file" | "ir" | "state" | "moduleNode">,
|
||||
): void => {
|
||||
// Getter limits describe query effects without tagged values; other limits
|
||||
// already project from binding or return-site values and must not be duplicated.
|
||||
for (const limit of semanticCoverageResourceLimits(context.ir.coverage)) {
|
||||
if (limit !== "getter-expansion") continue;
|
||||
const unknown = createJavaScriptSemanticGraphUnknown(
|
||||
{
|
||||
node_id: context.moduleNode.node_id,
|
||||
family: "data-flow",
|
||||
relation_kinds: ["defines"],
|
||||
reason: "resource-limit",
|
||||
detail: semanticResourceLimitReason(limit),
|
||||
candidate_node_ids: [],
|
||||
evidence: {
|
||||
...unknownSemanticEvidence(context.file, null),
|
||||
coverage: partialApplicationCoverage(
|
||||
semanticResourceLimitCoverage([limit]),
|
||||
null,
|
||||
),
|
||||
limitations: [semanticResourceLimitReason(limit)],
|
||||
},
|
||||
},
|
||||
context.state.evidenceContexts,
|
||||
);
|
||||
context.state.unknowns.set(unknown.unknown_id, unknown);
|
||||
}
|
||||
};
|
||||
|
||||
function* createReturnSiteNodes(
|
||||
file: JavaScriptArtifactFile,
|
||||
ir: JavaScriptSemanticIr,
|
||||
|
||||
Reference in New Issue
Block a user