mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 01:41:21 +00:00
fix(javascript): evaluate each short-circuit left operand once (#7126)
Logical branch selection evaluated the left operand to decide which branches stay reachable, and value evaluation then evaluated the selected left branch again. In a left-nested `a || b || c ...` chain every level repeated that, so semantic analysis doubled per operand and a 24-operand chain exhausted the per-source deadline. The selection now carries the left operand's value with the branch, and value evaluation reuses it. Provenance still walks the branch nodes. Results are unchanged. Fixes #7115 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7336a566ac
commit
81afb043f7
3 files changed
+178
-12
No files matched your search
@@ -721,8 +721,10 @@ const evaluateExpressionValue = (
|
||||
if (t.isTemplateLiteral(node)) return evaluateTemplate(node, context);
|
||||
if (t.isConditionalExpression(node) || t.isLogicalExpression(node))
|
||||
return mergeValues(
|
||||
semanticSelectedBranches(node, context).map((branch) =>
|
||||
evaluateExpression(branch, nestedContext(context)),
|
||||
semanticSelectedBranches(node, context).map(
|
||||
(branch) =>
|
||||
branch.value ??
|
||||
evaluateExpression(branch.node, nestedContext(context)),
|
||||
),
|
||||
);
|
||||
if (t.isObjectExpression(node)) return evaluateObject(node, context);
|
||||
@@ -774,27 +776,39 @@ const evaluateTemplate = (
|
||||
return primitiveSet(candidates);
|
||||
};
|
||||
|
||||
/**
|
||||
* A branch that may produce the expression's value. A logical left operand
|
||||
* carries the value its selection already evaluated, so value consumers reuse
|
||||
* it instead of re-evaluating that operand; re-evaluating it at every level of
|
||||
* a left-nested `a || b || c` chain doubles the work per operand.
|
||||
*/
|
||||
interface SemanticSelectedBranch {
|
||||
readonly node: t.Node;
|
||||
readonly value?: JavaScriptSemanticValue;
|
||||
}
|
||||
|
||||
/** Value and provenance selection consume the same established branch facts. */
|
||||
const semanticSelectedBranches = (
|
||||
node: t.ConditionalExpression | t.LogicalExpression,
|
||||
context: EvaluationContext,
|
||||
): readonly t.Node[] => {
|
||||
): readonly SemanticSelectedBranch[] => {
|
||||
if (t.isConditionalExpression(node)) {
|
||||
const test = evaluateExpression(node.test, nestedContext(context));
|
||||
const choice = semanticCondition(test, Boolean);
|
||||
return choice === undefined
|
||||
? [node.consequent, node.alternate]
|
||||
: [choice ? node.consequent : node.alternate];
|
||||
? [{ node: node.consequent }, { node: node.alternate }]
|
||||
: [{ node: choice ? node.consequent : node.alternate }];
|
||||
}
|
||||
const left = evaluateExpression(node.left, nestedContext(context));
|
||||
const value = evaluateExpression(node.left, nestedContext(context));
|
||||
const left = { node: node.left, value };
|
||||
const choice = logicalRightOperand(
|
||||
node.operator,
|
||||
semanticCondition(left, Boolean),
|
||||
semanticCondition(left, (value) => value === null, false),
|
||||
semanticCondition(value, Boolean),
|
||||
semanticCondition(value, (candidate) => candidate === null, false),
|
||||
);
|
||||
return choice === undefined
|
||||
? [node.left, node.right]
|
||||
: [choice ? node.right : node.left];
|
||||
? [left, { node: node.right }]
|
||||
: [choice ? { node: node.right } : left];
|
||||
};
|
||||
|
||||
/** All retained alternatives must agree before discarding a branch. */
|
||||
@@ -1320,7 +1334,7 @@ const provenanceForExpression = (
|
||||
}
|
||||
if (t.isConditionalExpression(node) || t.isLogicalExpression(node)) {
|
||||
const candidates = semanticSelectedBranches(node, context).map((branch) =>
|
||||
provenanceForExpression(branch, nestedContext(context)),
|
||||
provenanceForExpression(branch.node, nestedContext(context)),
|
||||
);
|
||||
const origins = uniqueSemanticOrigins(
|
||||
candidates.flatMap((candidate) => candidate.origins),
|
||||
|
||||
Reference in new issue
Block a user