fix(javascript): reuse shared AST child traversal (#7967)

This commit is contained in:
Wenyu Chiou authored and GitHub committed 2026-11-04 19:36:00 +08:00
1 parent d5ba7cec60
commit 6aeaa04e54
2 files changed
+122 -1

No files matched your search

@@ -2,7 +2,10 @@ import * as t from "@babel/types";
import { expect, it } from "vitest";
import { ElectronCallPathBlockPrefixes } from "./electronCallPathBlockPrefixes.js";
import { parseJavaScriptSource } from "./javascriptSourceParser.js";
import { completeSemanticSteps } from "./javascriptSemanticTraversal.js";
import {
completeSemanticSteps,
traverseJavaScriptAst,
} from "./javascriptSemanticTraversal.js";
it("indexes a real flat block once, retaining contextual guards in source order", () => {
const parsed = parseJavaScriptSource(`{
@@ -94,3 +97,119 @@ it("charges nested exit checks to the same budget and does not cache a partial i
entries: [{ fact: { kind: "branch", consequentExits: true } }],
});
});
it("retains labelled-jump and budget semantics when scanning loop children", () => {
const parsed = parseJavaScriptSource(`guard: {
if (outer) {
while (again) { if (leave) break guard; }
throw 0;
}
if (inner) {
while (again) { local: { if (leave) break local; } }
throw 0;
}
send("tail");
}`);
if (!parsed) throw new Error("Expected parsed program");
const label = parsed.program.body[0];
if (!t.isLabeledStatement(label) || !t.isBlockStatement(label.body))
throw new Error("Expected labelled block");
const block = label.body;
const tail = block.body.at(-1);
if (!tail) throw new Error("Expected terminal statement");
const parents = new WeakMap<t.Node, t.Node>();
traverseJavaScriptAst(parsed.program, {
enter(node, parent) {
if (parent) parents.set(node, parent);
},
});
let visits = 0;
const complete = new ElectronCallPathBlockPrefixes(() => {
visits += 1;
return true;
}, parents);
const result = completeSemanticSteps(complete.before(block, tail));
expect(result).toMatchObject({
before: 2,
entries: [
{ position: 0, fact: { kind: "branch", consequentExits: true } },
{ position: 1, fact: { kind: "branch", consequentExits: true } },
],
});
const noParents = new ElectronCallPathBlockPrefixes(() => true);
const unknownLabels = completeSemanticSteps(noParents.before(block, tail));
expect(unknownLabels).toMatchObject({
entries: [
{ fact: { kind: "branch", consequentExits: false } },
{ fact: { kind: "branch", consequentExits: false } },
],
});
// This count is characterized on the unchanged base: changing child
// ordering or budget charging must not silently shift the threshold.
expect(visits).toBe(35);
const requiredVisits = 35;
let remaining = requiredVisits - 1;
const limited = new ElectronCallPathBlockPrefixes(
() => --remaining >= 0,
parents,
);
expect(completeSemanticSteps(limited.before(block, tail))).toBeUndefined();
remaining = requiredVisits;
expect(completeSemanticSteps(limited.before(block, tail))).toEqual(result);
expect(remaining).toBeGreaterThanOrEqual(0);
});
it.each([
[126, true],
[127, false],
])(
"retains the conservative loop-child depth boundary at %i wrappers",
(wrappers, exits) => {
let expression: t.Expression = t.identifier("condition");
for (let index = 0; index < wrappers; index += 1)
expression = t.unaryExpression("!", expression);
const branch = t.ifStatement(
t.identifier("selected"),
t.blockStatement([
t.whileStatement(expression, t.blockStatement([])),
t.throwStatement(t.numericLiteral(0)),
]),
);
const block = t.blockStatement([branch]);
const prefixes = new ElectronCallPathBlockPrefixes(() => true);
expect(prefixes.statement(branch, block)).toMatchObject({
kind: "branch",
consequentExits: exits,
});
},
);
it("does not charge or enter nested function and class bodies during loop scans", () => {
const inspect = (size: number) => {
const parsed = parseJavaScriptSource(`{
if (selected) {
while (again) {
function nested() { ${"0;".repeat(size)} }
class Nested { method() { ${"0;".repeat(size)} } }
}
throw 0;
}
}`);
const block = parsed?.program.body[0];
if (!t.isBlockStatement(block) || !t.isIfStatement(block.body[0]))
throw new Error("Expected parsed branch");
let visits = 0;
const prefixes = new ElectronCallPathBlockPrefixes(() => {
visits += 1;
return true;
});
const fact = prefixes.statement(block.body[0], block);
return { visits, fact };
};
const small = inspect(1);
const large = inspect(300);
expect(small.fact).toMatchObject({ kind: "branch", consequentExits: true });
expect(large.fact).toMatchObject({ kind: "branch", consequentExits: true });
expect(large.visits).toBe(small.visits);
});
@@ -1,6 +1,8 @@
import * as t from "@babel/types";
import { childNodes } from "./javascriptSemanticTraversal.js";
import { childNodes } from "./javascriptSemanticTraversal.js";
/** Syntax alone can establish an exit, but never a branch's contextual truth. */
export type ElectronStatementFallthrough =
| { readonly kind: "exit" }