depyo 1.0.1 → 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/README.md +4 -0
- package/depyo.js +103 -2
- package/lib/OpCodes.js +22 -5
- package/lib/PycDecompiler.js +402 -39
- package/lib/PycDisassembler.js +1 -1
- package/lib/PycReader.js +51 -4
- 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/PycReader.js
CHANGED
|
@@ -93,14 +93,29 @@ const MagicToVersion = {
|
|
|
93
93
|
0x0A0D0D33: {major: 3, minor: 6, IsUnicode: true, opcode: require('./bytecode/python_3_6')},
|
|
94
94
|
0x0A0D0D41: {major: 3, minor: 7, IsUnicode: true, opcode: require('./bytecode/python_3_7')},
|
|
95
95
|
0x0A0D0D42: {major: 3, minor: 7, IsUnicode: true, opcode: require('./bytecode/python_3_7')},
|
|
96
|
-
|
|
96
|
+
// Python 3.8a1 (3400) and 3.8a2 (3401) predate PEP 570 — no posonly field in marshal.
|
|
97
|
+
0x0A0D0D48: {major: 3, minor: 8, preReleaseMarshal: true, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
|
|
98
|
+
0x0A0D0D49: {major: 3, minor: 8, preReleaseMarshal: true, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
|
|
99
|
+
// 3.8a3 (3410) and later include posonly.
|
|
100
|
+
0x0A0D0D52: {major: 3, minor: 8, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
|
|
101
|
+
0x0A0D0D53: {major: 3, minor: 8, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
|
|
102
|
+
0x0A0D0D54: {major: 3, minor: 8, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
|
|
97
103
|
0x0A0D0D55: {major: 3, minor: 8, IsUnicode: true, opcode: require('./bytecode/python_3_8')},
|
|
98
104
|
0x0A0D0D61: {major: 3, minor: 9, IsUnicode: true, opcode: require('./bytecode/python_3_9')},
|
|
99
105
|
0x0A0D0D6F: {major: 3, minor: 10, IsUnicode: true, opcode: require('./bytecode/python_3_10')},
|
|
100
106
|
0x0A0D0DA7: {major: 3, minor: 11, IsUnicode: true, opcode: require('./bytecode/python_3_11')},
|
|
101
107
|
0x0A0D0DCB: {major: 3, minor: 12, IsUnicode: true, opcode: require('./bytecode/python_3_12')},
|
|
102
108
|
0x0A0D0DF3: {major: 3, minor: 13, IsUnicode: true, opcode: require('./bytecode/python_3_13')},
|
|
103
|
-
0x0A0D0E2B: {major: 3, minor: 14, IsUnicode: true, opcode: require('./bytecode/python_3_14')}
|
|
109
|
+
0x0A0D0E2B: {major: 3, minor: 14, IsUnicode: true, opcode: require('./bytecode/python_3_14')},
|
|
110
|
+
0x0A0D0E4F: {major: 3, minor: 15, IsUnicode: true, opcode: require('./bytecode/python_3_15')},
|
|
111
|
+
|
|
112
|
+
// PyPy magics (shares opcode tables with matching CPython release).
|
|
113
|
+
// PyPy adds a handful of custom opcodes — unknowns degrade decode rather than fail.
|
|
114
|
+
0x0A0DF30A: {major: 2, minor: 7, IsUnicode: true, opcode: require('./bytecode/python_2_7'), pypy: true},
|
|
115
|
+
0x0A0D0030: {major: 3, minor: 2, IsUnicode: true, opcode: require('./bytecode/python_3_2'), pypy: true},
|
|
116
|
+
0x0A0D0070: {major: 3, minor: 5, IsUnicode: true, opcode: require('./bytecode/python_3_5'), pypy: true},
|
|
117
|
+
0x0A0D00A0: {major: 3, minor: 6, IsUnicode: true, opcode: require('./bytecode/python_3_6'), pypy: true},
|
|
118
|
+
0x0A0D00F0: {major: 3, minor: 7, IsUnicode: true, opcode: require('./bytecode/python_3_7'), pypy: true}
|
|
104
119
|
};
|
|
105
120
|
|
|
106
121
|
const VersionAliases = {
|
|
@@ -109,6 +124,9 @@ const VersionAliases = {
|
|
|
109
124
|
|
|
110
125
|
const VersionToInfo = {};
|
|
111
126
|
for (const [magic, info] of Object.entries(MagicToVersion)) {
|
|
127
|
+
if (info.pypy) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
112
130
|
const key = `${info.major}.${info.minor}`;
|
|
113
131
|
const existing = VersionToInfo[key];
|
|
114
132
|
const candidate = {...info, magic: Number(magic)};
|
|
@@ -280,6 +298,34 @@ class PycReader
|
|
|
280
298
|
return best ? best.candidate : null;
|
|
281
299
|
}
|
|
282
300
|
|
|
301
|
+
static ScanMarshalCandidates(buffer) {
|
|
302
|
+
const candidates = PycReader.ListSupportedVersions(false);
|
|
303
|
+
const results = [];
|
|
304
|
+
for (const candidate of candidates) {
|
|
305
|
+
const trial = PycReader.TryParseMarshal(buffer, candidate);
|
|
306
|
+
if (!trial) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
results.push({...trial, versionInfo: candidate});
|
|
310
|
+
}
|
|
311
|
+
results.sort((a, b) => {
|
|
312
|
+
if (a.unknownRatio !== b.unknownRatio) {
|
|
313
|
+
return a.unknownRatio - b.unknownRatio;
|
|
314
|
+
}
|
|
315
|
+
if (a.remaining !== b.remaining) {
|
|
316
|
+
return a.remaining - b.remaining;
|
|
317
|
+
}
|
|
318
|
+
if (a.unknown !== b.unknown) {
|
|
319
|
+
return a.unknown - b.unknown;
|
|
320
|
+
}
|
|
321
|
+
if (a.versionInfo.major !== b.versionInfo.major) {
|
|
322
|
+
return a.versionInfo.major - b.versionInfo.major;
|
|
323
|
+
}
|
|
324
|
+
return a.versionInfo.minor - b.versionInfo.minor;
|
|
325
|
+
});
|
|
326
|
+
return results;
|
|
327
|
+
}
|
|
328
|
+
|
|
283
329
|
static CountUnknownOpcodes(codeObject, reader, opCodeList) {
|
|
284
330
|
const code = codeObject?.Code?.Value;
|
|
285
331
|
if (!code || !opCodeList) {
|
|
@@ -356,7 +402,7 @@ class PycReader
|
|
|
356
402
|
if (obj.Names?.ClassName === "Py_Tuple") {
|
|
357
403
|
score += 1;
|
|
358
404
|
}
|
|
359
|
-
return {score, remaining, unknown, unknownRatio};
|
|
405
|
+
return {score, remaining, unknown, total, unknownRatio};
|
|
360
406
|
} catch (ex) {
|
|
361
407
|
return null;
|
|
362
408
|
}
|
|
@@ -598,7 +644,8 @@ class PycReader
|
|
|
598
644
|
}
|
|
599
645
|
codeObject.ArgCount = argCount;
|
|
600
646
|
|
|
601
|
-
|
|
647
|
+
const hasPosOnly = this.versionCompare(3, 8) >= 0 && !this.m_version?.preReleaseMarshal;
|
|
648
|
+
codeObject.PosOnlyArgCount = hasPosOnly ? this.m_rdr.readUInt32() : 0;
|
|
602
649
|
codeObject.KWOnlyArgCount = this.versionCompare(3, 0) >= 0 ? this.m_rdr.readUInt32() : 0;
|
|
603
650
|
|
|
604
651
|
codeObject.NumLocals = 0;
|
package/lib/PythonObject.js
CHANGED
|
@@ -48,6 +48,29 @@ class PythonObject {
|
|
|
48
48
|
|
|
49
49
|
// TODO: Refactor to use Symbol.toPrimitive() and Object.valueOf()
|
|
50
50
|
|
|
51
|
+
toReprString() {
|
|
52
|
+
if (this.ClassName === "Py_String" || this.ClassName === "Py_Unicode") {
|
|
53
|
+
let raw = this.Value;
|
|
54
|
+
if (raw == null || raw.length === 0) return '""';
|
|
55
|
+
raw = raw.toString();
|
|
56
|
+
let escaped = raw
|
|
57
|
+
.replace(/\\/g, '\\\\')
|
|
58
|
+
.replace(/\n/g, '\\n')
|
|
59
|
+
.replace(/\r/g, '\\r')
|
|
60
|
+
.replace(/\t/g, '\\t');
|
|
61
|
+
let quote = '"';
|
|
62
|
+
if (escaped.includes('"')) {
|
|
63
|
+
if (!escaped.includes("'")) {
|
|
64
|
+
quote = "'";
|
|
65
|
+
} else {
|
|
66
|
+
escaped = escaped.replace(/"/g, '\\"');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return quote + escaped + quote;
|
|
70
|
+
}
|
|
71
|
+
return this.toString();
|
|
72
|
+
}
|
|
73
|
+
|
|
51
74
|
toString()
|
|
52
75
|
{
|
|
53
76
|
switch(this.ClassName) {
|
|
@@ -74,8 +97,16 @@ class PythonObject {
|
|
|
74
97
|
case "Py_Interned":
|
|
75
98
|
return this.Value !== null ? this.Value.toString() : "0";
|
|
76
99
|
|
|
77
|
-
case "Py_Float":
|
|
78
|
-
|
|
100
|
+
case "Py_Float": {
|
|
101
|
+
if (this.Value === null) return "0.0";
|
|
102
|
+
let s = `${this.Value}`;
|
|
103
|
+
// Python rejects `1e+300.0`; only append `.0` when the
|
|
104
|
+
// printed form is pure digits (optional sign).
|
|
105
|
+
if (/^-?\d+$/.test(s)) {
|
|
106
|
+
s += ".0";
|
|
107
|
+
}
|
|
108
|
+
return s;
|
|
109
|
+
}
|
|
79
110
|
|
|
80
111
|
case "Py_VeryLong":
|
|
81
112
|
if (this.Value) {
|
|
@@ -123,10 +154,11 @@ class PythonObject {
|
|
|
123
154
|
let res = "(";
|
|
124
155
|
if (this.Value) {
|
|
125
156
|
for (let obj of this.Value) {
|
|
157
|
+
const part = obj instanceof PythonObject ? obj.toReprString() : String(obj);
|
|
126
158
|
if (res != "(") {
|
|
127
|
-
res += ", " +
|
|
159
|
+
res += ", " + part;
|
|
128
160
|
} else {
|
|
129
|
-
res +=
|
|
161
|
+
res += part;
|
|
130
162
|
}
|
|
131
163
|
}
|
|
132
164
|
res += ")";
|
|
@@ -144,8 +176,10 @@ class PythonObject {
|
|
|
144
176
|
for (let pair of this.Value) {
|
|
145
177
|
if (res != "(") {
|
|
146
178
|
res += ", ";
|
|
147
|
-
}
|
|
148
|
-
|
|
179
|
+
}
|
|
180
|
+
const kPart = pair.key instanceof PythonObject ? pair.key.toReprString() : String(pair.key);
|
|
181
|
+
const vPart = pair.value instanceof PythonObject ? pair.value.toReprString() : String(pair.value);
|
|
182
|
+
res += kPart + ": " + vPart;
|
|
149
183
|
}
|
|
150
184
|
res += ")";
|
|
151
185
|
return res;
|