perf(javascript): reduce analysis and source-map work (#955)
* perf(javascript): reduce semantic traversal overhead * perf(source-maps): reuse validated regular map rows
This commit is contained in:
1 parent
dda8e6a728
commit
c7530f9a81
6 files changed
+59
-10
No files matched your search
@@ -149,6 +149,31 @@ describe("JavaScript semantic analysis: dataflow 1", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("JavaScript semantic call result flows", () => {
|
||||
it("links only directly assigned scalar call results", () => {
|
||||
const ir = analyzeJavaScriptSemantics(`
|
||||
function run() {
|
||||
const direct = produce();
|
||||
const { field } = unpack();
|
||||
const nested = outer(inner());
|
||||
return direct;
|
||||
}
|
||||
`);
|
||||
const bindingNames = new Map(
|
||||
ir.bindings.map(({ bindingId, name }) => [bindingId, name]),
|
||||
);
|
||||
|
||||
expect(
|
||||
ir.callResultFlows.map(({ bindingId }) => bindingNames.get(bindingId)),
|
||||
).toEqual(["direct", "nested"]);
|
||||
expect(ir.callSites).toHaveLength(4);
|
||||
expect(ir.callResultFlows.map(({ callSiteId }) => callSiteId)).toEqual([
|
||||
ir.callSites[0]?.callSiteId,
|
||||
ir.callSites[2]?.callSiteId,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("JavaScript semantic analysis: read-modify-write", () => {
|
||||
it.each(["+= 1", "++", "--", "||= 1", "&&= 1", "??= 1"])(
|
||||
"retains the property read in a read-modify-write operation: %s",
|
||||
|
||||
@@ -183,9 +183,7 @@ const collectCallResultFlow = (
|
||||
context.ancestors,
|
||||
context.state,
|
||||
).filter(({ projectionPath }) => projectionPath.length === 0)) {
|
||||
const binding = [...context.state.bindingsById.values()].find(
|
||||
({ bindingId }) => bindingId === assigned.bindingId,
|
||||
);
|
||||
const binding = context.state.bindingsById.get(assigned.bindingId);
|
||||
if (binding === undefined) continue;
|
||||
const identifierRange = range(assigned.identifier);
|
||||
const definition = binding.definitions.find(
|
||||
|
||||
@@ -51,3 +51,16 @@ it("preserves source-tree enter/exit order and detached ancestor snapshots", ()
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it("skips sparse child slots without disturbing sibling visit order", () => {
|
||||
const parsed = parseJavaScriptSource("const values = [first, , second];");
|
||||
if (parsed === null)
|
||||
throw new TypeError("Expected a parsed JavaScript fixture");
|
||||
const identifiers: string[] = [];
|
||||
traverseJavaScriptAst(parsed.program, {
|
||||
enter: (node) => {
|
||||
if (t.isIdentifier(node)) identifiers.push(node.name);
|
||||
},
|
||||
});
|
||||
expect(identifiers).toEqual(["values", "first", "second"]);
|
||||
});
|
||||
@@ -51,11 +51,19 @@ interface TraversalFrame {
|
||||
}
|
||||
|
||||
const childNodes = (node: t.Node): t.Node[] => {
|
||||
const keys: readonly string[] = t.VISITOR_KEYS[node.type] ?? [];
|
||||
return keys.flatMap((key) => {
|
||||
const keys = t.VISITOR_KEYS[node.type];
|
||||
if (keys === undefined) return [];
|
||||
const children: t.Node[] = [];
|
||||
for (const key of keys) {
|
||||
const value: unknown = Reflect.get(node, key);
|
||||
if (t.isNode(value)) return [value];
|
||||
if (!Array.isArray(value)) return [];
|
||||
return value.filter((item): item is t.Node => t.isNode(item));
|
||||
});
|
||||
if (t.isNode(value)) {
|
||||
children.push(value);
|
||||
continue;
|
||||
}
|
||||
if (!Array.isArray(value)) continue;
|
||||
for (const item of value) {
|
||||
if (t.isNode(item)) children.push(item);
|
||||
}
|
||||
}
|
||||
return children;
|
||||
};
|
||||
@@ -151,6 +151,9 @@ describe("source-map layout admission", () => {
|
||||
expect(() => trace({ ...leaf, mappings })).toThrow();
|
||||
},
|
||||
);
|
||||
it("validates regular-map source indexes after reusing its decoded rows", () => {
|
||||
expect(() => trace({ ...leaf, mappings: "ACAA" })).toThrow(/index/u);
|
||||
});
|
||||
it("rejects huge indexed offsets before upstream row allocation", () => {
|
||||
expect(() =>
|
||||
trace({
|
||||
|
||||
@@ -81,10 +81,12 @@ const decodeValidatedMap = (
|
||||
inspected: ReturnType<typeof inspectSourceMap>,
|
||||
url: string,
|
||||
) => {
|
||||
let regularRows: ReturnType<typeof decodedMappings> | undefined;
|
||||
for (const leaf of inspected.leaves) {
|
||||
const decoded = decodedMappings(
|
||||
new TraceMap(JSON.stringify(leaf.map), url),
|
||||
);
|
||||
if (inspected.format === "regular") regularRows = decoded;
|
||||
for (const [line, row] of decoded.entries())
|
||||
for (const segment of row) {
|
||||
if (
|
||||
@@ -112,7 +114,7 @@ const decodeValidatedMap = (
|
||||
}
|
||||
}
|
||||
const map = new AnyMap(inspected.jsonText, url);
|
||||
const rows = decodedMappings(map);
|
||||
const rows = regularRows ?? decodedMappings(map);
|
||||
for (const row of rows)
|
||||
for (const segment of row) {
|
||||
if (
|
||||
|
||||
Reference in new issue
Block a user