fix: seed _generateCallWithError's local visited set from main traversal

- _emitBranch now sets _genVisited on blocks before calling generateOwnCode
  so that proc calls inside loop bodies can seed their error-handling
  visited set from the main traversal, preventing loop-decision re-walk
- Add _noWalkNext flag on proc calls when error handling is active, so
  _emitBranch skips redundant nextFlow walking (the success path was
  already covered by the error-handling branch emission)
- Known issue: shared blocks that are direct roots of emitBranch (from
  the shared set) may still be re-walked when they were already emitted
  by a downstream proc call's error-handling success path
This commit is contained in:
2026-06-23 03:08:20 +02:00
parent cefee37cd8
commit 400870d888
4 changed files with 38 additions and 16 deletions
+13 -6
View File
@@ -108,18 +108,23 @@
return;
}
if (isDecision(el)) {
// Decisions manage their own visited tracking inside generateCode.
parts.push(el.generateCode(visited, loopTargets));
} else {
visited.add(el);
if (loopTargets.has(el)) {
parts.push("\n" + (el.id || "block") + ":");
}
el._genVisited = visited;
parts.push(el.generateOwnCode());
var flow = el.wcNextFlow || el.nextFlow || [];
for (var j = 0; j < flow.length; j++) {
walk(flow[j].toElement);
// If the block's generateOwnCode already walked the success path
// (e.g. error-handling proc call), don't re-walk its nextFlow.
if (!el._noWalkNext) {
var flow = el.wcNextFlow || el.nextFlow || [];
for (var j = 0; j < flow.length; j++) {
walk(flow[j].toElement);
}
}
el._noWalkNext = false;
}
}
@@ -137,7 +142,7 @@
* then success-exclusive and shared code after it (no else).
*/
CodeGen.generateCallWithError = function (
ifHeader, errTargets, okTargets, isDecision, loopTargets
ifHeader, errTargets, okTargets, isDecision, loopTargets, mainVisited
) {
loopTargets = loopTargets || new Set();
var errAll = CodeGen.branchDescendants(errTargets, isDecision);
@@ -147,7 +152,9 @@
if (okAll.has(b)) shared.add(b);
});
var visited = new Set();
// Create a local visited seeded from the main set so blocks walked by
// the error/success branches don't pollute the main traversal's set.
var visited = new Set(mainVisited || []);
var errCode = CodeGen.emitBranch(errTargets, shared, visited, isDecision, loopTargets);
var okCode = CodeGen.emitBranch(okTargets, shared, visited, isDecision, loopTargets);
var sharedCode = CodeGen.emitBranch(