depyo 1.0.2 → 1.0.3
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/depyo.js +43 -2
- package/lib/OpCodes.js +22 -5
- package/lib/PycDecompiler.js +402 -39
- package/lib/PycDisassembler.js +1 -1
- package/lib/PycReader.js +22 -3
- package/lib/PythonObject.js +40 -6
- package/lib/ast/ast_node.js +292 -71
- package/lib/bytecode/python_3_0.js +1 -1
- package/lib/bytecode/python_3_12.js +1 -1
- package/lib/bytecode/python_3_13.js +13 -13
- package/lib/bytecode/python_3_14.js +13 -13
- package/lib/bytecode/python_3_15.js +183 -0
- package/lib/code_reader.js +107 -146
- package/lib/handlers/collections_update.js +50 -1
- package/lib/handlers/context_managers.js +202 -13
- package/lib/handlers/control_flow_jumps.js +516 -23
- package/lib/handlers/exceptions_blocks.js +85 -22
- package/lib/handlers/formatting.js +60 -17
- package/lib/handlers/function_calls.js +454 -57
- package/lib/handlers/function_class_build.js +159 -64
- package/lib/handlers/generators_async.js +67 -0
- package/lib/handlers/load_store_names.js +190 -57
- package/lib/handlers/loop_iterator.js +162 -6
- package/lib/handlers/misc_other.js +216 -43
- package/lib/handlers/stack_ops.js +81 -19
- package/lib/handlers/subscript_slice.js +103 -1
- package/lib/handlers/unpack.js +18 -16
- package/package.json +1 -1
package/lib/ast/ast_node.js
CHANGED
|
@@ -7,6 +7,19 @@ function indent(level = 1) {
|
|
|
7
7
|
return Buffer.alloc(level * SPACES_PER_LEVEL, ' ').toString('ascii');
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
function renderTypeParam(tp) {
|
|
11
|
+
// PEP 696: a typeparam may be {name, default: ASTNode} instead of a string.
|
|
12
|
+
if (tp && typeof tp === 'object' && tp.name && tp.default != null && !tp.codeFragment) {
|
|
13
|
+
const defFrag = tp.default?.codeFragment
|
|
14
|
+
? tp.default.codeFragment()
|
|
15
|
+
: tp.default?.toString?.() ?? String(tp.default);
|
|
16
|
+
return `${tp.name} = ${defFrag}`;
|
|
17
|
+
}
|
|
18
|
+
if (tp?.codeFragment) return tp.codeFragment();
|
|
19
|
+
if (tp?.toString) return tp.toString();
|
|
20
|
+
return tp;
|
|
21
|
+
}
|
|
22
|
+
|
|
10
23
|
class ASTNode {
|
|
11
24
|
|
|
12
25
|
m_lineNo = -1;
|
|
@@ -51,11 +64,7 @@ class ASTNode {
|
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
codeFragment() {
|
|
54
|
-
|
|
55
|
-
if (result === null || result === undefined) {
|
|
56
|
-
return `${result}`;
|
|
57
|
-
}
|
|
58
|
-
return result;
|
|
67
|
+
throw new Error(`${this.constructor.name}.codeFragment() not implemented — subclass must override`);
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
static calculateSpacing(prevNode, node) {
|
|
@@ -446,19 +455,29 @@ class ASTObject extends ASTNode {
|
|
|
446
455
|
}
|
|
447
456
|
}
|
|
448
457
|
|
|
449
|
-
let result = this.object.toString();
|
|
450
458
|
if (["Py_String", "Py_Unicode"].includes(this.object.ClassName)) {
|
|
459
|
+
let raw = this.object.Value;
|
|
460
|
+
if (raw == null || raw.length === 0) {
|
|
461
|
+
return '""';
|
|
462
|
+
}
|
|
463
|
+
raw = raw.toString();
|
|
464
|
+
let escaped = raw
|
|
465
|
+
.replace(/\\/g, '\\\\')
|
|
466
|
+
.replace(/\n/g, '\\n')
|
|
467
|
+
.replace(/\r/g, '\\r')
|
|
468
|
+
.replace(/\t/g, '\\t');
|
|
451
469
|
let quote = '"';
|
|
452
|
-
if (
|
|
453
|
-
if (!
|
|
470
|
+
if (escaped.includes('"')) {
|
|
471
|
+
if (!escaped.includes("'")) {
|
|
454
472
|
quote = "'";
|
|
455
473
|
} else {
|
|
456
|
-
|
|
457
|
-
result = result.replace(/"/g, '\\"');
|
|
474
|
+
escaped = escaped.replace(/"/g, '\\"');
|
|
458
475
|
}
|
|
459
476
|
}
|
|
460
|
-
return quote +
|
|
477
|
+
return quote + escaped + quote;
|
|
461
478
|
}
|
|
479
|
+
|
|
480
|
+
let result = this.object.toString();
|
|
462
481
|
if (result === null || result === undefined) {
|
|
463
482
|
return `${result}`;
|
|
464
483
|
}
|
|
@@ -498,9 +517,8 @@ class ASTUnary extends ASTNode {
|
|
|
498
517
|
}
|
|
499
518
|
|
|
500
519
|
codeFragment() {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
return result;
|
|
520
|
+
const operand = this.operand?.codeFragment ? this.operand.codeFragment() : '##ERROR##';
|
|
521
|
+
return `${ASTUnary.UnaryOpString[this.op]}${operand}`;
|
|
504
522
|
}
|
|
505
523
|
|
|
506
524
|
toString() {
|
|
@@ -568,11 +586,11 @@ class ASTBinary extends ASTNode {
|
|
|
568
586
|
}
|
|
569
587
|
|
|
570
588
|
get line() {
|
|
571
|
-
return this.m_left.line;
|
|
589
|
+
return this.m_left ? this.m_left.line : this.m_lineNo;
|
|
572
590
|
}
|
|
573
591
|
|
|
574
592
|
get lastLine() {
|
|
575
|
-
return this.m_right.line;
|
|
593
|
+
return this.m_right ? this.m_right.line : this.m_lineNo;
|
|
576
594
|
}
|
|
577
595
|
|
|
578
596
|
get isInplace() {
|
|
@@ -730,6 +748,13 @@ class ASTBinary extends ASTNode {
|
|
|
730
748
|
case ASTBinary.BinOp.LeftShift:
|
|
731
749
|
case ASTBinary.BinOp.RightShift:
|
|
732
750
|
return 1;
|
|
751
|
+
// Python `and` binds tighter than `or`; ranking them so that
|
|
752
|
+
// AND wrapping OR forces parens keeps `(b or c) and d` from
|
|
753
|
+
// rendering as `b or c and d` (which parses as `b or (c and d)`).
|
|
754
|
+
case ASTBinary.BinOp.LogicalAnd:
|
|
755
|
+
return -1;
|
|
756
|
+
case ASTBinary.BinOp.LogicalOr:
|
|
757
|
+
return -2;
|
|
733
758
|
default:
|
|
734
759
|
return 0;
|
|
735
760
|
}
|
|
@@ -744,6 +769,13 @@ class ASTBinary extends ASTNode {
|
|
|
744
769
|
fragment = `(${fragment})`;
|
|
745
770
|
}
|
|
746
771
|
}
|
|
772
|
+
// `0.bit_length()` parses as a bad float literal; wrap integer
|
|
773
|
+
// literal targets of attribute access in parens.
|
|
774
|
+
if (this.op === ASTBinary.BinOp.Attr && position === 'left' &&
|
|
775
|
+
child instanceof ASTObject &&
|
|
776
|
+
child.object?.ClassName === 'Py_Int') {
|
|
777
|
+
fragment = `(${fragment})`;
|
|
778
|
+
}
|
|
747
779
|
return fragment;
|
|
748
780
|
};
|
|
749
781
|
|
|
@@ -944,15 +976,7 @@ class ASTStore extends ASTNode {
|
|
|
944
976
|
let isAsync = (codeObject.Flags & ASTFunction.CodeFlags.CO_COROUTINE) ||
|
|
945
977
|
(codeObject.Flags & ASTFunction.CodeFlags.CO_ASYNC_GENERATOR);
|
|
946
978
|
result.add(isAsync ? "async ": "");
|
|
947
|
-
const typeParams = (this.src.typeParams || []).map(tp =>
|
|
948
|
-
if (tp?.codeFragment) {
|
|
949
|
-
return tp.codeFragment();
|
|
950
|
-
}
|
|
951
|
-
if (tp?.toString) {
|
|
952
|
-
return tp.toString();
|
|
953
|
-
}
|
|
954
|
-
return tp;
|
|
955
|
-
});
|
|
979
|
+
const typeParams = (this.src.typeParams || []).map(tp => renderTypeParam(tp));
|
|
956
980
|
const typeParamsStr = typeParams.length ? `[${typeParams.join(", ")}]` : "";
|
|
957
981
|
result.lastLineAppend(`def ${destName}${typeParamsStr}(`);
|
|
958
982
|
}
|
|
@@ -1004,29 +1028,58 @@ class ASTStore extends ASTNode {
|
|
|
1004
1028
|
const rawKwName = toVarName(codeObject.VarNames.Value?.[argIndex++]);
|
|
1005
1029
|
let argName = rawKwName;
|
|
1006
1030
|
|
|
1007
|
-
//
|
|
1031
|
+
// Python syntax is `name: annotation = default`, so append the
|
|
1032
|
+
// annotation first and the default afterwards.
|
|
1033
|
+
const kwAnn = this.src.annotations?.[rawKwName];
|
|
1034
|
+
if (kwAnn) {
|
|
1035
|
+
argName = `${argName}: ${kwAnn.codeFragment?.() || kwAnn.toString?.() || '##ERROR##'}`;
|
|
1036
|
+
}
|
|
1008
1037
|
if (default_params && default_params.length > 0) {
|
|
1009
1038
|
let defaultIdx = kwIdx - (codeObject.KWOnlyArgCount - default_params.length);
|
|
1010
1039
|
if (defaultIdx >= 0 && default_params[defaultIdx]) {
|
|
1011
1040
|
argName += "=" + default_params[defaultIdx].value.codeFragment();
|
|
1012
1041
|
}
|
|
1013
1042
|
}
|
|
1014
|
-
const kwAnn = this.src.annotations?.[rawKwName];
|
|
1015
|
-
if (kwAnn) {
|
|
1016
|
-
argName = `${argName}: ${kwAnn.codeFragment?.() || kwAnn.toString?.() || '##ERROR##'}`;
|
|
1017
|
-
}
|
|
1018
1043
|
argNames.push(argName);
|
|
1019
1044
|
}
|
|
1020
1045
|
}
|
|
1021
1046
|
}
|
|
1022
1047
|
if (codeObject.Flags & ASTFunction.CodeFlags.CO_VARARGS) {
|
|
1023
|
-
|
|
1048
|
+
const vaName = toVarName(codeObject.VarNames.Value?.[argIndex++]);
|
|
1049
|
+
let entry = '*' + vaName;
|
|
1050
|
+
const vaAnn = this.src.annotations?.[vaName];
|
|
1051
|
+
if (vaAnn) {
|
|
1052
|
+
entry += `: ${vaAnn.codeFragment?.() || vaAnn.toString?.() || '##ERROR##'}`;
|
|
1053
|
+
}
|
|
1054
|
+
if (codeObject.KWOnlyArgCount) {
|
|
1055
|
+
// Replace the placeholder "*" we already pushed with the named varargs.
|
|
1056
|
+
const starIdx = argNames.indexOf('*');
|
|
1057
|
+
if (starIdx >= 0) {
|
|
1058
|
+
argNames[starIdx] = entry;
|
|
1059
|
+
} else {
|
|
1060
|
+
argNames.push(entry);
|
|
1061
|
+
}
|
|
1062
|
+
} else {
|
|
1063
|
+
argNames.push(entry);
|
|
1064
|
+
}
|
|
1024
1065
|
}
|
|
1025
1066
|
if (codeObject.Flags & ASTFunction.CodeFlags.CO_VARKEYWORDS) {
|
|
1026
|
-
|
|
1067
|
+
const vkName = toVarName(codeObject.VarNames.Value?.[argIndex++]);
|
|
1068
|
+
let entry = '**' + vkName;
|
|
1069
|
+
const vkAnn = this.src.annotations?.[vkName];
|
|
1070
|
+
if (vkAnn) {
|
|
1071
|
+
entry += `: ${vkAnn.codeFragment?.() || vkAnn.toString?.() || '##ERROR##'}`;
|
|
1072
|
+
}
|
|
1073
|
+
argNames.push(entry);
|
|
1027
1074
|
}
|
|
1028
1075
|
|
|
1029
|
-
|
|
1076
|
+
// Lambda needs a space after the `lambda` keyword before params (shouldTrim would
|
|
1077
|
+
// eat it); `def X(` already has the opening paren and wants no leading space.
|
|
1078
|
+
if (inLambda) {
|
|
1079
|
+
result.lastLineAppend((argNames.length > 0 ? " " : "") + argNames.join(", "), false);
|
|
1080
|
+
} else {
|
|
1081
|
+
result.lastLineAppend(argNames.join(", "));
|
|
1082
|
+
}
|
|
1030
1083
|
|
|
1031
1084
|
if (inLambda) {
|
|
1032
1085
|
result.lastLineAppend(": ", false);
|
|
@@ -1055,27 +1108,31 @@ class ASTStore extends ASTNode {
|
|
|
1055
1108
|
result.decreaseIndent();
|
|
1056
1109
|
|
|
1057
1110
|
} else if (this.src instanceof ASTClass) {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1111
|
+
const allDecorators = [
|
|
1112
|
+
...(this.src.decorators || []),
|
|
1113
|
+
...(this.decorators || []),
|
|
1114
|
+
];
|
|
1115
|
+
for (let decorator of allDecorators) {
|
|
1116
|
+
const decoText = decorator?.codeFragment ? decorator.codeFragment() : decorator;
|
|
1117
|
+
result.add('@' + decoText);
|
|
1063
1118
|
}
|
|
1064
1119
|
let classNode = this.src;
|
|
1065
|
-
const typeParams = (classNode.typeParams || []).map(tp =>
|
|
1066
|
-
if (tp?.codeFragment) {
|
|
1067
|
-
return tp.codeFragment();
|
|
1068
|
-
}
|
|
1069
|
-
if (tp?.toString) {
|
|
1070
|
-
return tp.toString();
|
|
1071
|
-
}
|
|
1072
|
-
return tp;
|
|
1073
|
-
});
|
|
1120
|
+
const typeParams = (classNode.typeParams || []).map(tp => renderTypeParam(tp));
|
|
1074
1121
|
const typeParamsStr = typeParams.length ? `[${typeParams.join(", ")}]` : "";
|
|
1075
1122
|
result.add(`class ${this.dest.codeFragment()}${typeParamsStr}`);
|
|
1076
1123
|
const bases = classNode?.bases?.values || [];
|
|
1077
|
-
|
|
1078
|
-
|
|
1124
|
+
const kwargs = classNode?.kwargs || [];
|
|
1125
|
+
const baseStrs = bases.map(node => node?.codeFragment() || "##ERROR##");
|
|
1126
|
+
const kwargStrs = kwargs.map(({key, value}) => {
|
|
1127
|
+
const keyStr = key?.object?.Value?.toString?.() ??
|
|
1128
|
+
key?.name ??
|
|
1129
|
+
(typeof key === 'string' ? key : key?.codeFragment?.()?.toString?.());
|
|
1130
|
+
const valStr = value?.codeFragment?.()?.toString?.() ?? "##ERROR##";
|
|
1131
|
+
return `${keyStr}=${valStr}`;
|
|
1132
|
+
});
|
|
1133
|
+
const headerArgs = [...baseStrs, ...kwargStrs];
|
|
1134
|
+
if (headerArgs.length > 0) {
|
|
1135
|
+
result.lastLineAppend(`(${headerArgs.join(", ")})`, false);
|
|
1079
1136
|
}
|
|
1080
1137
|
result.lastLineAppend(":");
|
|
1081
1138
|
let codeObject = classNode.code?.func?.code?.object || classNode.code?.code?.object || {};
|
|
@@ -1097,16 +1154,9 @@ class ASTStore extends ASTNode {
|
|
|
1097
1154
|
if (global.g_cliArgs?.debug) {
|
|
1098
1155
|
console.log(`[ASTStore class render] name=${this.dest?.name}, len=${classBodyNodeList.length}, first=${classBodyNodeList[0]?.constructor?.name}, retVal=${classBodyNodeList[0]?.value?.constructor?.name}, isSyntheticEmptyReturn=${isSyntheticEmptyReturn}, hasContent=${hasContent}`);
|
|
1099
1156
|
}
|
|
1100
|
-
const hasMetaTypeBase = Array.isArray(classNode?.bases?.values) &&
|
|
1101
|
-
classNode.bases.values.some(b => {
|
|
1102
|
-
const frag = b?.codeFragment?.();
|
|
1103
|
-
const str = frag?.toString?.();
|
|
1104
|
-
return typeof str === 'string' && str.startsWith('type(');
|
|
1105
|
-
});
|
|
1106
|
-
|
|
1107
1157
|
if (hasContent && !isSyntheticEmptyReturn) {
|
|
1108
1158
|
result.add(classBody);
|
|
1109
|
-
} else if (!isSyntheticEmptyReturn
|
|
1159
|
+
} else if (!isSyntheticEmptyReturn) {
|
|
1110
1160
|
result.add("pass");
|
|
1111
1161
|
}
|
|
1112
1162
|
result.decreaseIndent();
|
|
@@ -1271,6 +1321,23 @@ class ASTReturn extends ASTNode {
|
|
|
1271
1321
|
return result;
|
|
1272
1322
|
}
|
|
1273
1323
|
|
|
1324
|
+
switch (this.rettype) {
|
|
1325
|
+
case ASTReturn.RetType.Yield: {
|
|
1326
|
+
if (!this.value || this.value instanceof ASTNone) {
|
|
1327
|
+
return new PycResult("(yield)", true);
|
|
1328
|
+
}
|
|
1329
|
+
let yres = new PycResult("(yield ", true);
|
|
1330
|
+
yres.lastLineAppend(this.value.codeFragment());
|
|
1331
|
+
yres.lastLineAppend(")");
|
|
1332
|
+
return yres;
|
|
1333
|
+
}
|
|
1334
|
+
case ASTReturn.RetType.YieldFrom: {
|
|
1335
|
+
let yfres = new PycResult("(yield from ", true);
|
|
1336
|
+
yfres.lastLineAppend(this.value.codeFragment());
|
|
1337
|
+
yfres.lastLineAppend(")");
|
|
1338
|
+
return yfres;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1274
1341
|
return this.value?.codeFragment() || "";
|
|
1275
1342
|
}
|
|
1276
1343
|
|
|
@@ -1398,12 +1465,11 @@ class ASTFunction extends ASTNode {
|
|
|
1398
1465
|
m_annotations = {};
|
|
1399
1466
|
m_typeParams = [];
|
|
1400
1467
|
|
|
1401
|
-
constructor(code, defargs = [], kwdefargs = []
|
|
1468
|
+
constructor(code, defargs = [], kwdefargs = []) {
|
|
1402
1469
|
super();
|
|
1403
1470
|
this.m_code = code;
|
|
1404
1471
|
this.m_defargs = defargs;
|
|
1405
1472
|
this.m_kwdefargs = kwdefargs;
|
|
1406
|
-
this.m_decorators = annotations;
|
|
1407
1473
|
}
|
|
1408
1474
|
|
|
1409
1475
|
get code() {
|
|
@@ -1509,6 +1575,8 @@ class ASTClass extends ASTNode {
|
|
|
1509
1575
|
m_bases = null;
|
|
1510
1576
|
m_name = null;
|
|
1511
1577
|
m_typeParams = [];
|
|
1578
|
+
m_kwargs = [];
|
|
1579
|
+
m_decorators = [];
|
|
1512
1580
|
|
|
1513
1581
|
constructor(code, bases, name) {
|
|
1514
1582
|
super();
|
|
@@ -1517,6 +1585,14 @@ class ASTClass extends ASTNode {
|
|
|
1517
1585
|
this.m_name = name;
|
|
1518
1586
|
}
|
|
1519
1587
|
|
|
1588
|
+
add_decorator(decorator) {
|
|
1589
|
+
this.m_decorators.unshift(decorator);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
get decorators() {
|
|
1593
|
+
return this.m_decorators;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1520
1596
|
get code() {
|
|
1521
1597
|
return this.m_code;
|
|
1522
1598
|
}
|
|
@@ -1529,6 +1605,14 @@ class ASTClass extends ASTNode {
|
|
|
1529
1605
|
return this.m_name;
|
|
1530
1606
|
}
|
|
1531
1607
|
|
|
1608
|
+
get kwargs() {
|
|
1609
|
+
return this.m_kwargs || [];
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
set kwargs(value) {
|
|
1613
|
+
this.m_kwargs = value || [];
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1532
1616
|
get typeParams() {
|
|
1533
1617
|
return this.m_typeParams || [];
|
|
1534
1618
|
}
|
|
@@ -1631,13 +1715,31 @@ class ASTCall extends ASTNode {
|
|
|
1631
1715
|
params.push(this.pparams.map(formatParam).join(', ').trim());
|
|
1632
1716
|
}
|
|
1633
1717
|
if (this.kwparams?.length > 0) {
|
|
1634
|
-
params.push(this.kwparams.map(node =>
|
|
1718
|
+
params.push(this.kwparams.map(node => {
|
|
1719
|
+
// Keyword key is stored as a Py_String/Py_Unicode; extract raw identifier
|
|
1720
|
+
// rather than stripping quotes from the rendered literal (which misses
|
|
1721
|
+
// double quotes when the name contains a single quote, etc).
|
|
1722
|
+
let keyStr;
|
|
1723
|
+
const keyObj = node?.key;
|
|
1724
|
+
if (keyObj && keyObj.object && ['Py_String', 'Py_Unicode'].includes(keyObj.object.ClassName)) {
|
|
1725
|
+
keyStr = keyObj.object.toString();
|
|
1726
|
+
} else {
|
|
1727
|
+
keyStr = (keyObj?.codeFragment()?.toString() || '').replace(/^['"]|['"]$/g, '');
|
|
1728
|
+
}
|
|
1729
|
+
return `${keyStr}=${formatParam(node?.value)}`;
|
|
1730
|
+
}).join(', ').trim());
|
|
1635
1731
|
}
|
|
1636
1732
|
if (this.hasVar) {
|
|
1637
1733
|
params.push('*' + this.var.codeFragment());
|
|
1638
1734
|
}
|
|
1639
1735
|
if (this.hasKw) {
|
|
1640
|
-
|
|
1736
|
+
if (this.kw instanceof ASTMapUnpack) {
|
|
1737
|
+
for (const item of this.kw.items) {
|
|
1738
|
+
params.push('**' + (item?.codeFragment?.() ?? '##ERROR##'));
|
|
1739
|
+
}
|
|
1740
|
+
} else {
|
|
1741
|
+
params.push('**' + this.kw.codeFragment());
|
|
1742
|
+
}
|
|
1641
1743
|
}
|
|
1642
1744
|
result.lastLineAppend(params.join(', ') + ')');
|
|
1643
1745
|
return result;
|
|
@@ -1858,6 +1960,31 @@ class ASTMap extends ASTNode {
|
|
|
1858
1960
|
}
|
|
1859
1961
|
}
|
|
1860
1962
|
|
|
1963
|
+
// Py 3.5: BUILD_MAP_UNPACK / BUILD_MAP_UNPACK_WITH_CALL push this. Each item is
|
|
1964
|
+
// a mapping expression prefixed with ** at the source level. Items may be
|
|
1965
|
+
// literal ASTMap or arbitrary expressions (names, subscripts, calls).
|
|
1966
|
+
class ASTMapUnpack extends ASTNode {
|
|
1967
|
+
m_items = [];
|
|
1968
|
+
|
|
1969
|
+
constructor(items) {
|
|
1970
|
+
super();
|
|
1971
|
+
this.m_items = items || [];
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
get items() {
|
|
1975
|
+
return this.m_items;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
codeFragment() {
|
|
1979
|
+
const parts = this.m_items.map(item => '**' + (item?.codeFragment?.() ?? '##ERROR##'));
|
|
1980
|
+
return '{' + parts.join(', ') + '}';
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
toString() {
|
|
1984
|
+
return `ASTMapUnpack: line=${this.line}, ${this.codeFragment()}`;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1861
1988
|
class ASTKwNamesMap extends ASTNode {
|
|
1862
1989
|
m_values = [];
|
|
1863
1990
|
|
|
@@ -2105,10 +2232,12 @@ class ASTKeyword extends ASTNode {
|
|
|
2105
2232
|
|
|
2106
2233
|
class ASTRaise extends ASTNode {
|
|
2107
2234
|
m_params = [];
|
|
2235
|
+
m_fromClause = false;
|
|
2108
2236
|
|
|
2109
|
-
constructor(params) {
|
|
2237
|
+
constructor(params, fromClause = false) {
|
|
2110
2238
|
super();
|
|
2111
2239
|
this.m_params = params;
|
|
2240
|
+
this.m_fromClause = fromClause;
|
|
2112
2241
|
}
|
|
2113
2242
|
|
|
2114
2243
|
get params() {
|
|
@@ -2123,6 +2252,9 @@ class ASTRaise extends ASTNode {
|
|
|
2123
2252
|
if (!this.params || this.params.length === 0) {
|
|
2124
2253
|
return 'raise';
|
|
2125
2254
|
}
|
|
2255
|
+
if (this.m_fromClause && this.params.length === 2) {
|
|
2256
|
+
return 'raise ' + this.params[0].codeFragment() + ' from ' + this.params[1].codeFragment();
|
|
2257
|
+
}
|
|
2126
2258
|
return 'raise ' + this.params.map(node => node.codeFragment()).join(', ');
|
|
2127
2259
|
}
|
|
2128
2260
|
|
|
@@ -2200,7 +2332,8 @@ class ASTBlock extends ASTNode {
|
|
|
2200
2332
|
While: 8,
|
|
2201
2333
|
For: 9,
|
|
2202
2334
|
With: 10,
|
|
2203
|
-
AsyncFor: 11
|
|
2335
|
+
AsyncFor: 11,
|
|
2336
|
+
AsyncWith: 12
|
|
2204
2337
|
}
|
|
2205
2338
|
|
|
2206
2339
|
m_blockType = ASTBlock.BlockType.Main;
|
|
@@ -2277,7 +2410,7 @@ class ASTBlock extends ASTNode {
|
|
|
2277
2410
|
get type_str() {
|
|
2278
2411
|
return [
|
|
2279
2412
|
"", "if", "else", "elif", "try", "CONTAINER", "except",
|
|
2280
|
-
"finally", "while", "for", "with", "async for"
|
|
2413
|
+
"finally", "while", "for", "with", "async for", "async with"
|
|
2281
2414
|
][this.blockType];
|
|
2282
2415
|
}
|
|
2283
2416
|
|
|
@@ -2556,6 +2689,15 @@ class ASTCondBlock extends ASTBlock {
|
|
|
2556
2689
|
node.name == '__exception__') {
|
|
2557
2690
|
return false;
|
|
2558
2691
|
}
|
|
2692
|
+
// Inside an except body, an empty else block is a control-flow
|
|
2693
|
+
// artifact (JUMP_ABSOLUTE leaving the handler with no body);
|
|
2694
|
+
// keeping it leaves the handler visually empty without `pass`.
|
|
2695
|
+
if (this.blockType == ASTBlock.BlockType.Except &&
|
|
2696
|
+
node instanceof ASTBlock &&
|
|
2697
|
+
node.blockType === ASTBlock.BlockType.Else &&
|
|
2698
|
+
node.empty()) {
|
|
2699
|
+
return false;
|
|
2700
|
+
}
|
|
2559
2701
|
return node && !node.skip && node.codeFragment;
|
|
2560
2702
|
});
|
|
2561
2703
|
|
|
@@ -2654,7 +2796,12 @@ class ASTIterBlock extends ASTBlock {
|
|
|
2654
2796
|
result.lastLineAppend(this.iter?.codeFragment() || "##ERROR##");
|
|
2655
2797
|
result.lastLineAppend(":");
|
|
2656
2798
|
result.increaseIndent();
|
|
2657
|
-
|
|
2799
|
+
const body = this.nodes.filter(Boolean);
|
|
2800
|
+
if (body.length === 0) {
|
|
2801
|
+
result.add("pass");
|
|
2802
|
+
} else {
|
|
2803
|
+
body.forEach(node => result.add(node.codeFragment()));
|
|
2804
|
+
}
|
|
2658
2805
|
result.decreaseIndent();
|
|
2659
2806
|
|
|
2660
2807
|
return result;
|
|
@@ -2753,9 +2900,70 @@ class ASTWithBlock extends ASTBlock {
|
|
|
2753
2900
|
|
|
2754
2901
|
result.lastLineAppend(":");
|
|
2755
2902
|
result.increaseIndent();
|
|
2756
|
-
this.nodes.filter(Boolean)
|
|
2903
|
+
const body = this.nodes.filter(Boolean);
|
|
2904
|
+
if (body.length === 0) {
|
|
2905
|
+
result.add("pass");
|
|
2906
|
+
} else {
|
|
2907
|
+
body.forEach(node => result.add(node.codeFragment()));
|
|
2908
|
+
}
|
|
2757
2909
|
result.decreaseIndent();
|
|
2758
|
-
|
|
2910
|
+
|
|
2911
|
+
return result;
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
toString() {
|
|
2915
|
+
return `${this.type_str} block: {${this.start} - ${this.end}}`;
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2919
|
+
class ASTAsyncWithBlock extends ASTBlock {
|
|
2920
|
+
|
|
2921
|
+
m_expr = null;
|
|
2922
|
+
m_var = null;
|
|
2923
|
+
|
|
2924
|
+
constructor(start = 0, end = 0) {
|
|
2925
|
+
super(ASTBlock.BlockType.AsyncWith, start, end);
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
get expr() {
|
|
2929
|
+
return this.m_expr;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
set expr(value) {
|
|
2933
|
+
this.m_expr = value;
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2936
|
+
get var() {
|
|
2937
|
+
return this.m_var;
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
set var(value) {
|
|
2941
|
+
this.m_var = value;
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2944
|
+
codeFragment() {
|
|
2945
|
+
let result = new PycResult();
|
|
2946
|
+
result.doNotIndent = true;
|
|
2947
|
+
|
|
2948
|
+
result.lastLineAppend("async with ", false);
|
|
2949
|
+
const exprCode = this.expr?.codeFragment ? this.expr.codeFragment() : "None";
|
|
2950
|
+
result.lastLineAppend(exprCode);
|
|
2951
|
+
|
|
2952
|
+
if (this.var) {
|
|
2953
|
+
result.lastLineAppend(" as ", false);
|
|
2954
|
+
result.lastLineAppend(this.var.codeFragment());
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2957
|
+
result.lastLineAppend(":");
|
|
2958
|
+
result.increaseIndent();
|
|
2959
|
+
const body = this.nodes.filter(Boolean);
|
|
2960
|
+
if (body.length === 0) {
|
|
2961
|
+
result.add("pass");
|
|
2962
|
+
} else {
|
|
2963
|
+
body.forEach(node => result.add(node.codeFragment()));
|
|
2964
|
+
}
|
|
2965
|
+
result.decreaseIndent();
|
|
2966
|
+
|
|
2759
2967
|
return result;
|
|
2760
2968
|
}
|
|
2761
2969
|
|
|
@@ -2769,6 +2977,7 @@ class ASTComprehension extends ASTNode {
|
|
|
2769
2977
|
static LIST = 0;
|
|
2770
2978
|
static SET = 1;
|
|
2771
2979
|
static DICT = 2;
|
|
2980
|
+
static GENERATOR = 3;
|
|
2772
2981
|
|
|
2773
2982
|
m_kind = ASTComprehension.LIST;
|
|
2774
2983
|
m_key = null;
|
|
@@ -2820,6 +3029,9 @@ class ASTComprehension extends ASTNode {
|
|
|
2820
3029
|
if ([ASTComprehension.SET, ASTComprehension.DICT].includes(this.kind)) {
|
|
2821
3030
|
openingBracket = '{';
|
|
2822
3031
|
closingBracket = '}';
|
|
3032
|
+
} else if (this.kind == ASTComprehension.GENERATOR) {
|
|
3033
|
+
openingBracket = '(';
|
|
3034
|
+
closingBracket = ')';
|
|
2823
3035
|
}
|
|
2824
3036
|
|
|
2825
3037
|
if (!this.result) {
|
|
@@ -2829,7 +3041,8 @@ class ASTComprehension extends ASTNode {
|
|
|
2829
3041
|
let result = `${openingBracket}${this.kind == ASTComprehension.DICT ? (this.key?.codeFragment() || "##ERROR##") + ": " : ""}${this.result.codeFragment?.() || "##ERROR##"}`;
|
|
2830
3042
|
|
|
2831
3043
|
result += this.generators.map(gen => {
|
|
2832
|
-
let
|
|
3044
|
+
let asyncPrefix = gen.blockType == ASTBlock.BlockType.AsyncFor ? 'async ' : '';
|
|
3045
|
+
let genString = ` ${asyncPrefix}for ${gen.index?.codeFragment?.() || "##ERROR##"} in ${gen.iter?.codeFragment?.() || "##ERROR##"}`;
|
|
2833
3046
|
|
|
2834
3047
|
if (gen.condition) {
|
|
2835
3048
|
genString += ` if ${gen.condition.codeFragment?.() || "##ERROR##"}`;
|
|
@@ -2984,8 +3197,8 @@ class ASTJoinedStr extends ASTNode {
|
|
|
2984
3197
|
return this.values[this.values.length - 1]?.lastLine;
|
|
2985
3198
|
}
|
|
2986
3199
|
codeFragment() {
|
|
2987
|
-
//
|
|
2988
|
-
let result = 'f"';
|
|
3200
|
+
// PEP 750 t-strings use t"..." prefix; otherwise f-string.
|
|
3201
|
+
let result = (this.isTemplateString ? 't"' : 'f"');
|
|
2989
3202
|
|
|
2990
3203
|
// Values are in reverse order (BUILD_STRING pops from stack)
|
|
2991
3204
|
// So we need to reverse them
|
|
@@ -2994,6 +3207,12 @@ class ASTJoinedStr extends ASTNode {
|
|
|
2994
3207
|
for (let i = 0; i < values.length; i++) {
|
|
2995
3208
|
let value = values[i];
|
|
2996
3209
|
|
|
3210
|
+
// Stack underflow during BUILD_STRING leaves undefined slots; skip them
|
|
3211
|
+
// rather than crashing post-decompile passes that re-render this node.
|
|
3212
|
+
if (value == null) {
|
|
3213
|
+
continue;
|
|
3214
|
+
}
|
|
3215
|
+
|
|
2997
3216
|
if (value instanceof ASTFormattedValue) {
|
|
2998
3217
|
// Check for f-string = debugging pattern (Python 3.8+)
|
|
2999
3218
|
// Pattern: literal ending with "varname=" followed by {varname!r}
|
|
@@ -3379,7 +3598,7 @@ class ASTPattern extends ASTNode {
|
|
|
3379
3598
|
return '##ERROR##';
|
|
3380
3599
|
|
|
3381
3600
|
default:
|
|
3382
|
-
|
|
3601
|
+
throw new Error(`ASTPattern.codeFragment(): unsupported pattern type '${this.m_type}'`);
|
|
3383
3602
|
}
|
|
3384
3603
|
}
|
|
3385
3604
|
|
|
@@ -3412,6 +3631,7 @@ module.exports = {
|
|
|
3412
3631
|
ASTList,
|
|
3413
3632
|
ASTSet,
|
|
3414
3633
|
ASTMap,
|
|
3634
|
+
ASTMapUnpack,
|
|
3415
3635
|
ASTKwNamesMap,
|
|
3416
3636
|
ASTConstMap,
|
|
3417
3637
|
ASTSubscr,
|
|
@@ -3425,6 +3645,7 @@ module.exports = {
|
|
|
3425
3645
|
ASTIterBlock,
|
|
3426
3646
|
ASTContainerBlock,
|
|
3427
3647
|
ASTWithBlock,
|
|
3648
|
+
ASTAsyncWithBlock,
|
|
3428
3649
|
ASTComprehension,
|
|
3429
3650
|
ASTLoadBuildClass,
|
|
3430
3651
|
ASTAwaitable,
|
|
@@ -47,7 +47,7 @@ const opcodes = [
|
|
|
47
47
|
[68, new OpCode(OpCodes.GET_ITER, "GET_ITER")],
|
|
48
48
|
[69, new OpCode(OpCodes.STORE_LOCALS, "STORE_LOCALS")],
|
|
49
49
|
[70, new OpCode(OpCodes.PRINT_EXPR, "PRINT_EXPR")],
|
|
50
|
-
[71, new OpCode(OpCodes.
|
|
50
|
+
[71, new OpCode(OpCodes.LOAD_BUILD_CLASS, "LOAD_BUILD_CLASS")],
|
|
51
51
|
|
|
52
52
|
[75, new OpCode(OpCodes.INPLACE_LSHIFT, "INPLACE_LSHIFT")],
|
|
53
53
|
[76, new OpCode(OpCodes.INPLACE_RSHIFT, "INPLACE_RSHIFT")],
|
|
@@ -134,7 +134,7 @@ const opcodes = [
|
|
|
134
134
|
[173, new OpCode(OpCodes.CALL_INTRINSIC_1, "CALL_INTRINSIC_1", {HasArgument: true, HasIntrisic1: true})],
|
|
135
135
|
[174, new OpCode(OpCodes.CALL_INTRINSIC_2, "CALL_INTRINSIC_2", {HasArgument: true, HasIntrisic2: true})],
|
|
136
136
|
[175, new OpCode(OpCodes.LOAD_FROM_DICT_OR_GLOBALS_A, "LOAD_FROM_DICT_OR_GLOBALS", {HasArgument: true, HasName: true})],
|
|
137
|
-
[176, new OpCode(OpCodes.LOAD_FROM_DICT_OR_DEREF_A, "LOAD_FROM_DICT_OR_DEREF", {HasArgument: true,
|
|
137
|
+
[176, new OpCode(OpCodes.LOAD_FROM_DICT_OR_DEREF_A, "LOAD_FROM_DICT_OR_DEREF", {HasArgument: true, HasFree: true})],
|
|
138
138
|
|
|
139
139
|
[237, new OpCode(OpCodes.INSTRUMENTED_LOAD_SUPER_ATTR_A, "INSTRUMENTED_LOAD_SUPER_ATTR", {HasArgument: true, HasName: true, HasFlags: true})],
|
|
140
140
|
[238, new OpCode(OpCodes.INSTRUMENTED_POP_JUMP_IF_NONE_A, "INSTRUMENTED_POP_JUMP_IF_NONE", {HasArgument: true, HasJumpRelative: true})],
|