Files
rea/src/domain/javascript/javascriptSemanticCallableResults.ts
T
gensokyo-dev a95b83c2b0 perf(javascript): cache callable result syntax by AST identity (#7508)
Cache direct return/yield source expressions by immutable AST identity for all existing static and semantic consumers. Weak keys bound retention to AST lifetime; mutable analysis facts and Evidence remain unchanged. Refs #6311; full CastleIO analysis still times out.

Validation: 225 focused tests; baseline cache-regression controls; build, serial check:fast and test discovery passed. CastleIO direct-syntax facts equal with 78,272 warm lookups reduced from 2.72-2.81s to 13.0-13.4ms. Complete sourcemap-codec CLI analyses retained byte-identical Evidence; no end-to-end speedup or token reduction claimed.

CI: source 20,888 passed / 15 skipped; seven built-dependent shards, static, docs, package builds, Linux native baseline and real JS recovery CLI/MCP passed. Shard 6 Hermes path-preview failures both reproduce on exact main b9d1a61dd; Portable tests fails downstream. Greptile 5/5, no findings; Codex quota exhausted. Authorized admin bypass for unrelated baseline CI and unchecked human readiness; no human review attestation made.
2026-10-27 22:53:42 +08:00

77 lines
2.5 KiB
TypeScript

import * as t from "@babel/types";
import { childNodes } from "./javascriptSemanticTraversal.js";
/** One direct return or yield, excluding every nested callable. */
export interface SemanticCallableResultExpression {
readonly node: t.Node | null;
readonly site: t.Node;
readonly kind: "return" | "yield";
readonly delegated: boolean;
}
// Syntax is immutable across the static and semantic passes. These are source
// expressions, independent of changing binding, mutation or invocation facts.
// Weak keys keep retained results within the lifetime of their parsed AST.
const resultExpressions = new WeakMap<
t.Node,
readonly SemanticCallableResultExpression[]
>();
/** Collect direct result syntax without executing or evaluating a callable. */
export const semanticCallableResultExpressions = (
callable: t.Node,
): readonly SemanticCallableResultExpression[] => {
const cached = resultExpressions.get(callable);
if (cached !== undefined) return cached;
if (
t.isArrowFunctionExpression(callable) &&
!t.isBlockStatement(callable.body)
) {
const result: readonly SemanticCallableResultExpression[] = [
{
node: callable.body,
site: callable.body,
kind: "return",
delegated: false,
},
];
resultExpressions.set(callable, result);
return result;
}
if (!t.isFunction(callable)) return [];
const output: SemanticCallableResultExpression[] = [];
const pending = childNodes(callable.body).reverse();
while (pending.length > 0) {
const node = pending.pop();
if (node === undefined || t.isFunction(node) || t.isClass(node)) continue;
if (t.isReturnStatement(node)) {
output.push({
node: node.argument ?? null,
site: node,
kind: "return",
delegated: false,
});
}
if (t.isYieldExpression(node))
output.push({
node: node.argument ?? null,
site: node,
kind: "yield",
delegated: node.delegate,
});
for (const child of childNodes(node).reverse()) pending.push(child);
}
resultExpressions.set(callable, output);
return output;
};
/** A callable's direct invocation result differs from its body result syntax. */
export const semanticCallableResultKind = (
callable: t.Node,
): "value" | "promise" | "iterator" | "async-iterator" => {
if (!t.isFunction(callable)) return "value";
if (callable.generator) return callable.async ? "async-iterator" : "iterator";
return callable.async ? "promise" : "value";
};