fix(javascript): bound primitive string growth and repeated evaluation

This commit is contained in:
morluto
2026-10-09 00:40:22 +08:00
parent 770d2f60aa
commit 4a1837d9d5
10 changed files with 298 additions and 25 deletions
@@ -25,6 +25,7 @@ import { semanticCoverageResourceLimits } from "../../domain/javascript/javascri
import {
SEMANTIC_EXPRESSION_DEPTH_LIMIT,
SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT,
SEMANTIC_PRIMITIVE_JSON_BYTES_LIMIT,
semanticResourceLimitReason,
} from "../../domain/javascript/javascriptSemanticResourceLimits.js";
import {
@@ -136,11 +137,15 @@ const applicationGraphResourceLimit = (
value:
resourceLimit === "primitive-candidates"
? SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT
: SEMANTIC_EXPRESSION_DEPTH_LIMIT,
: resourceLimit === "primitive-bytes"
? SEMANTIC_PRIMITIVE_JSON_BYTES_LIMIT
: SEMANTIC_EXPRESSION_DEPTH_LIMIT,
unit:
resourceLimit === "expression-depth"
? ("depth" as const)
: ("items" as const),
: resourceLimit === "primitive-bytes"
? ("bytes" as const)
: ("items" as const),
});
const graphLimitations = (
@@ -10,6 +10,7 @@ import { semanticCoverageResourceLimits } from "../../domain/javascript/javascri
import {
SEMANTIC_EXPRESSION_DEPTH_LIMIT,
SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT,
SEMANTIC_PRIMITIVE_JSON_BYTES_LIMIT,
semanticResourceLimitReason,
} from "../../domain/javascript/javascriptSemanticResourceLimits.js";
import type {
@@ -389,11 +390,15 @@ const semanticCoverage = (
value:
resourceLimit === "primitive-candidates"
? SEMANTIC_PRIMITIVE_CANDIDATE_LIMIT
: SEMANTIC_EXPRESSION_DEPTH_LIMIT,
: resourceLimit === "primitive-bytes"
? SEMANTIC_PRIMITIVE_JSON_BYTES_LIMIT
: SEMANTIC_EXPRESSION_DEPTH_LIMIT,
unit:
resourceLimit === "expression-depth"
? ("depth" as const)
: ("items" as const),
: resourceLimit === "primitive-bytes"
? ("bytes" as const)
: ("items" as const),
})),
semantic.ir.coverage.omittedCount,
);
@@ -111,6 +111,30 @@ it("preserves primitive candidate budget unknowns in semantic graph coverage", (
);
});
it("preserves derived string-byte limit reasons in semantic graph unknowns", () => {
const declarations = ['const value0 = "x";'];
for (let index = 1; index <= 30; index += 1) {
const previous = `value${String(index - 1)}`;
declarations.push(
`const value${String(index)} = ${previous} + ${previous};`,
);
}
declarations.push("const answer = value30;");
const source = declarations.join("\n");
const ir = analyzeJavaScriptSemantics(source);
const graph = graphFor(source, ir);
expect(semanticCoverageResourceLimits(ir.coverage)).toContain(
"primitive-bytes",
);
expect(graph.unknowns).toContainEqual(
expect.objectContaining({
reason: "resource-limit",
detail: expect.stringMatching(/primitive string-byte budget exceeded/i),
}),
);
});
it("retains resource-limit reasons at nested object property slots", () => {
const term = '(true ? "a" : "b")';
const source = `const answer = { nested: { value: ${Array.from({ length: 20 }, () => term).join(" + ")} } };`;