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:
+7
-2
@@ -259,15 +259,20 @@ class WCDecision extends WCBlock {
|
||||
return;
|
||||
}
|
||||
if (el instanceof WCDecision) {
|
||||
// Decisions manage their own visited tracking inside generateCode.
|
||||
c += el.generateCode(visited, loopTargets);
|
||||
} else {
|
||||
visited.add(el);
|
||||
if (loopTargets && loopTargets.has(el)) {
|
||||
c += "\n" + el.id + ":";
|
||||
}
|
||||
// Let proc calls know about the visited set so their error-path
|
||||
// generation uses it instead of a fresh local set.
|
||||
el._genVisited = visited;
|
||||
c += el.generateOwnCode();
|
||||
for (const p of el.wcNextFlow) walk(p.toElement);
|
||||
if (!el._noWalkNext) {
|
||||
for (const p of el.wcNextFlow) walk(p.toElement);
|
||||
}
|
||||
el._noWalkNext = false;
|
||||
}
|
||||
};
|
||||
for (const el of roots) {
|
||||
|
||||
+12
-6
@@ -509,10 +509,12 @@ class WCProcCall extends WCBlock {
|
||||
return "";
|
||||
}
|
||||
visited.add(this);
|
||||
// Store the main-traversal visited set so _generateCallWithError can
|
||||
// use it instead of a fresh local set — prevents re-walking blocks
|
||||
// (like a loop-decision) that are already covered by the main traversal.
|
||||
this._genVisited = visited;
|
||||
let own = this.generateOwnCode();
|
||||
// The PROC CALL itself might be targeted by a back-edge for the success path.
|
||||
// That's handled by generateNextCode on the predecessor block.
|
||||
// If this block is a loop target, emit the label before ownCode.
|
||||
this._genVisited = null;
|
||||
if (loopTargets && loopTargets.has(this)) {
|
||||
own = "\n" + this.id + ":\n" + own;
|
||||
}
|
||||
@@ -572,7 +574,9 @@ class WCProcCall extends WCBlock {
|
||||
if (returnStyle === "error") {
|
||||
const call = `${procName}(${inputs.join(", ")})`;
|
||||
if (hasErrPath) {
|
||||
return this._generateCallWithError(`\nif (${call} != 0)`, errPaths);
|
||||
const result = this._generateCallWithError(`\nif (${call} != 0)`, errPaths);
|
||||
this._noWalkNext = true;
|
||||
return result;
|
||||
}
|
||||
return `\n${call};\n`;
|
||||
}
|
||||
@@ -580,7 +584,9 @@ class WCProcCall extends WCBlock {
|
||||
if (returnStyle === "value" && outputs.length === 1) {
|
||||
const outVar = outputs[0].replace(/^&/, "");
|
||||
if (hasErrPath) {
|
||||
return this._generateCallWithError(`\nif ((${outVar} = ${procName}(${inputs.join(", ")})) != 0)`, errPaths);
|
||||
const result = this._generateCallWithError(`\nif ((${outVar} = ${procName}(${inputs.join(", ")})) != 0)`, errPaths);
|
||||
this._noWalkNext = true;
|
||||
return result;
|
||||
}
|
||||
return `\n${procName}(${inputs.join(", ")});\n`;
|
||||
}
|
||||
@@ -596,7 +602,7 @@ class WCProcCall extends WCBlock {
|
||||
|
||||
return CodeGen.generateCallWithError(ifHeader, errTargets, okTargets, function (b) {
|
||||
return b instanceof WCDecision;
|
||||
}, this._loopTargets);
|
||||
}, this._loopTargets, this._genVisited);
|
||||
}
|
||||
|
||||
saveData() {
|
||||
|
||||
+13
-6
@@ -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(
|
||||
|
||||
@@ -99,18 +99,22 @@ function procCallBlock(id, procName, inputs, returnStyle, returnValueName, okTar
|
||||
var okTargets = self.nextFlow.map(function (p) {
|
||||
return p.toElement;
|
||||
});
|
||||
return CodeGen.generateCallWithError(
|
||||
var result = CodeGen.generateCallWithError(
|
||||
"\n" + ifHeader,
|
||||
self.errTargets,
|
||||
okTargets,
|
||||
function (b) {
|
||||
return b.isDecision;
|
||||
},
|
||||
self._loopTargets
|
||||
self._loopTargets,
|
||||
self._genVisited
|
||||
);
|
||||
self._noWalkNext = true;
|
||||
return result;
|
||||
},
|
||||
generateCode: function (visited, loopTargets) {
|
||||
self._loopTargets = loopTargets;
|
||||
self._genVisited = visited;
|
||||
if (visited.has(this)) {
|
||||
if (loopTargets && loopTargets.has(this)) return "\n goto " + this.id + ";\n";
|
||||
return "";
|
||||
|
||||
Reference in New Issue
Block a user