depyo 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/PycDecompiler.js
CHANGED
|
@@ -2436,6 +2436,46 @@ class PycDecompiler {
|
|
|
2436
2436
|
}
|
|
2437
2437
|
};
|
|
2438
2438
|
|
|
2439
|
+
// CPython 3.6+ `except Foo as X:` lowers to a try/finally cleanup
|
|
2440
|
+
// wrapper around the handler body. The reconstruction sometimes
|
|
2441
|
+
// emits the handler ASTCondBlock twice when the inner cleanup's
|
|
2442
|
+
// END_FINALLY is misread as a second handler match. Collapse
|
|
2443
|
+
// consecutive identical Except blocks (same rendered condition +
|
|
2444
|
+
// body) since real Python source can't legally have them.
|
|
2445
|
+
const dedupeConsecutiveExcepts = (nodes) => {
|
|
2446
|
+
const renderedKey = (n) => {
|
|
2447
|
+
try {
|
|
2448
|
+
const cf = n.codeFragment?.();
|
|
2449
|
+
return typeof cf === 'string' ? cf : (cf?.toString?.() || null);
|
|
2450
|
+
} catch (e) {
|
|
2451
|
+
return null;
|
|
2452
|
+
}
|
|
2453
|
+
};
|
|
2454
|
+
const isExcept = (n) => (n instanceof AST.ASTCondBlock)
|
|
2455
|
+
&& n.blockType === AST.ASTBlock.BlockType.Except;
|
|
2456
|
+
// Walk runs of contiguous Except siblings and drop duplicates
|
|
2457
|
+
// (handles both adjacent and non-adjacent duplicates within
|
|
2458
|
+
// the same handler chain — e.g. `except A / except: / except A`).
|
|
2459
|
+
let i = 0;
|
|
2460
|
+
while (i < nodes.length) {
|
|
2461
|
+
if (!isExcept(nodes[i])) { i++; continue; }
|
|
2462
|
+
let j = i;
|
|
2463
|
+
while (j < nodes.length && isExcept(nodes[j])) j++;
|
|
2464
|
+
const seen = new Map();
|
|
2465
|
+
for (let k = i; k < j; k++) {
|
|
2466
|
+
const key = renderedKey(nodes[k]);
|
|
2467
|
+
if (key && seen.has(key)) {
|
|
2468
|
+
nodes.splice(k, 1);
|
|
2469
|
+
j--;
|
|
2470
|
+
k--;
|
|
2471
|
+
} else if (key) {
|
|
2472
|
+
seen.set(key, true);
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
i = j;
|
|
2476
|
+
}
|
|
2477
|
+
};
|
|
2478
|
+
|
|
2439
2479
|
const visited = new WeakSet();
|
|
2440
2480
|
const visit = (n) => {
|
|
2441
2481
|
if (!n || visited.has(n)) return;
|
|
@@ -2462,6 +2502,10 @@ class PycDecompiler {
|
|
|
2462
2502
|
if (parentType === AST.ASTBlock.BlockType.Except) {
|
|
2463
2503
|
unwrapTryFinallyPassContainer(nodes);
|
|
2464
2504
|
}
|
|
2505
|
+
// Dedupe at every level (Container, Try, NodeList) since the
|
|
2506
|
+
// duplicate Except siblings can land in any of these depending
|
|
2507
|
+
// on how SETUP_FINALLY/END_FINALLY interleave.
|
|
2508
|
+
dedupeConsecutiveExcepts(nodes);
|
|
2465
2509
|
}
|
|
2466
2510
|
};
|
|
2467
2511
|
visit(root);
|
|
@@ -95,6 +95,14 @@ function processListAppend() {
|
|
|
95
95
|
let node = new AST.ASTComprehension (value);
|
|
96
96
|
node.line = this.code.Current.LineNo;
|
|
97
97
|
this.dataStack.push(node);
|
|
98
|
+
} else if (list instanceof AST.ASTList) {
|
|
99
|
+
// CPython's "big list" path: when a list literal exceeds the
|
|
100
|
+
// STACK_USE_GUIDELINE and not every element is a compile-time
|
|
101
|
+
// constant, the compiler emits BUILD_LIST 0 + N×LIST_APPEND
|
|
102
|
+
// instead of a single BUILD_LIST N. Mutate the list literal in
|
|
103
|
+
// place; the list reference must remain on top of the data stack
|
|
104
|
+
// for the next LIST_APPEND (or the final STORE_*) to find it.
|
|
105
|
+
list.values.push(value);
|
|
98
106
|
} else {
|
|
99
107
|
let node = new AST.ASTSubscr (list, value);
|
|
100
108
|
node.line = this.code.Current.LineNo;
|
|
@@ -1177,12 +1177,37 @@ function handleJumpAbsoluteA() {
|
|
|
1177
1177
|
this.blocks.push(next);
|
|
1178
1178
|
prev = null;
|
|
1179
1179
|
} else if (prev.blockType == AST.ASTBlock.BlockType.Except) {
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1180
|
+
// After closing one handler we speculatively open a fresh
|
|
1181
|
+
// Except slot in case another handler follows. But if the
|
|
1182
|
+
// chain has no more user-written handlers — only CPython's
|
|
1183
|
+
// synthetic END_FINALLY re-raise terminator before our
|
|
1184
|
+
// jump target — pushing an empty Except here produces a
|
|
1185
|
+
// phantom `except: pass` in the output.
|
|
1186
|
+
let chainTerminated = false;
|
|
1187
|
+
const target = this.code.Current.JumpTarget;
|
|
1188
|
+
const cursorStart = this.code.Next?.Offset;
|
|
1189
|
+
if (target != null && cursorStart != null && cursorStart < target) {
|
|
1190
|
+
let cursor = cursorStart;
|
|
1191
|
+
for (let k = 0; k < 32 && cursor < target; k++) {
|
|
1192
|
+
const instr = this.code.PeekInstructionAtOffset(cursor);
|
|
1193
|
+
if (!instr) break;
|
|
1194
|
+
if (instr.OpCodeID === this.OpCodes.END_FINALLY) {
|
|
1195
|
+
chainTerminated = true;
|
|
1196
|
+
break;
|
|
1197
|
+
}
|
|
1198
|
+
cursor = instr.Offset + (instr.Size || 1);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
if (chainTerminated) {
|
|
1202
|
+
prev = null;
|
|
1203
|
+
} else {
|
|
1204
|
+
let top = this.blocks.top();
|
|
1205
|
+
let next = new AST.ASTCondBlock(AST.ASTBlock.BlockType.Except, top.start, top.end, null, false);
|
|
1206
|
+
next.init();
|
|
1183
1207
|
|
|
1184
|
-
|
|
1185
|
-
|
|
1208
|
+
this.blocks.push(next);
|
|
1209
|
+
prev = null;
|
|
1210
|
+
}
|
|
1186
1211
|
} else if (prev.blockType == AST.ASTBlock.BlockType.Else) {
|
|
1187
1212
|
/* Special case */
|
|
1188
1213
|
if (this.blocks.top().blockType != AST.ASTBlock.BlockType.Main) {
|