perf(javascript): reuse cycle sets during semantic evaluation

Track active bindings with per-evaluation push/pop instead of copying ancestor sets on every alias. Restore membership in finally so cycles and sibling branches keep their existing semantics.

Validation: 173 domain tests, compiled CLI/stdio MCP returned-reference acceptance, and check:fast pass. A randomized six-block Flameox experiment retained identical complete semantic output and reduced the bounded 112-alias analyzer median from 36.33 ms to 17.00 ms.
This commit is contained in:
morluto committed 2026-10-11 08:14:45 +08:00
1 parent 204e2c42a1
commit bb074d84cb
1 file changed
+32 -26
@@ -53,7 +53,7 @@ import {
interface EvaluationContext {
readonly state: JavaScriptSemanticAnalysisState;
readonly bindings: ReadonlySet<string>;
readonly bindings: Set<string>;
readonly expressionDepth: number;
readonly capturePoint?: t.Node;
readonly primitiveBindingValues: Map<string, JavaScriptSemanticValue>;
@@ -196,20 +196,26 @@ const evaluateBinding = (
const initializer = binding.initializers[0];
if (initializer === undefined)
return { status: "unknown", reason: "Missing binding initializer." };
const nested = nestedContext(context, binding.bindingId);
let value = projectValue(
evaluateExpression(initializer.node, nested),
initializer.projection,
);
if (!isPrimitive(value)) {
const captureContext = primitiveCaptureContext(initializer.node, nested);
if (captureContext !== undefined) {
const captured = projectValue(
evaluateExpression(initializer.node, captureContext),
initializer.projection,
);
if (isPrimitive(captured)) value = captured;
context.bindings.add(binding.bindingId);
let value: JavaScriptSemanticValue;
try {
const nested = nestedContext(context);
value = projectValue(
evaluateExpression(initializer.node, nested),
initializer.projection,
);
if (!isPrimitive(value)) {
const captureContext = primitiveCaptureContext(initializer.node, nested);
if (captureContext !== undefined) {
const captured = projectValue(
evaluateExpression(initializer.node, captureContext),
initializer.projection,
);
if (isPrimitive(captured)) value = captured;
}
}
} finally {
context.bindings.delete(binding.bindingId);
}
let projected = invalidateSemanticMutationPaths(value, binding.mutatedPaths);
for (const escape of binding.escapedPaths) {
@@ -586,10 +592,16 @@ const provenanceForBinding = (
"unknown",
"Dynamic provenance projection.",
);
const resolved = provenanceForExpression(
initializer.node,
nestedContext(context, binding.bindingId),
);
context.bindings.add(binding.bindingId);
let resolved: JavaScriptBindingProvenance;
try {
resolved = provenanceForExpression(
initializer.node,
nestedContext(context),
);
} finally {
context.bindings.delete(binding.bindingId);
}
if (resolved.status !== "module" || initializer.projection.length === 0)
return resolved;
return semanticOriginsProvenance(
@@ -718,20 +730,14 @@ const mergeValues = (
: { status: "ambiguous", reason: "Branches have incompatible values." };
};
const nestedContext = (
context: EvaluationContext,
bindingId?: string,
): EvaluationContext => ({
const nestedContext = (context: EvaluationContext): EvaluationContext => ({
state: context.state,
primitiveBindingValues: context.primitiveBindingValues,
...(context.capturePoint === undefined
? {}
: { capturePoint: context.capturePoint }),
expressionDepth: context.expressionDepth + 1,
bindings:
bindingId === undefined
? context.bindings
: new Set([...context.bindings, bindingId]),
bindings: context.bindings,
});
const isPrimitive = (value: JavaScriptSemanticValue): boolean =>