depyo 1.0.0

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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -0
  3. package/depyo.js +213 -0
  4. package/lib/BinaryReader.js +153 -0
  5. package/lib/OpCode.js +90 -0
  6. package/lib/OpCodes.js +940 -0
  7. package/lib/PycDecompiler.js +2031 -0
  8. package/lib/PycDisassembler.js +55 -0
  9. package/lib/PycReader.js +905 -0
  10. package/lib/PycResult.js +82 -0
  11. package/lib/PythonObject.js +242 -0
  12. package/lib/Unpickle.js +173 -0
  13. package/lib/ast/ast_node.js +3442 -0
  14. package/lib/bytecode/python_1_0.js +116 -0
  15. package/lib/bytecode/python_1_1.js +116 -0
  16. package/lib/bytecode/python_1_3.js +119 -0
  17. package/lib/bytecode/python_1_4.js +121 -0
  18. package/lib/bytecode/python_1_5.js +120 -0
  19. package/lib/bytecode/python_1_6.js +124 -0
  20. package/lib/bytecode/python_2_0.js +137 -0
  21. package/lib/bytecode/python_2_1.js +142 -0
  22. package/lib/bytecode/python_2_2.js +147 -0
  23. package/lib/bytecode/python_2_3.js +145 -0
  24. package/lib/bytecode/python_2_4.js +147 -0
  25. package/lib/bytecode/python_2_5.js +147 -0
  26. package/lib/bytecode/python_2_6.js +147 -0
  27. package/lib/bytecode/python_2_7.js +151 -0
  28. package/lib/bytecode/python_3_0.js +132 -0
  29. package/lib/bytecode/python_3_1.js +135 -0
  30. package/lib/bytecode/python_3_10.js +312 -0
  31. package/lib/bytecode/python_3_11.js +284 -0
  32. package/lib/bytecode/python_3_12.js +327 -0
  33. package/lib/bytecode/python_3_13.js +173 -0
  34. package/lib/bytecode/python_3_14.js +177 -0
  35. package/lib/bytecode/python_3_2.js +136 -0
  36. package/lib/bytecode/python_3_3.js +136 -0
  37. package/lib/bytecode/python_3_4.js +137 -0
  38. package/lib/bytecode/python_3_5.js +149 -0
  39. package/lib/bytecode/python_3_6.js +153 -0
  40. package/lib/bytecode/python_3_7.js +292 -0
  41. package/lib/bytecode/python_3_8.js +294 -0
  42. package/lib/bytecode/python_3_9.js +296 -0
  43. package/lib/code_reader.js +146 -0
  44. package/lib/handlers/binary_ops.js +174 -0
  45. package/lib/handlers/collections_update.js +239 -0
  46. package/lib/handlers/comparisons.js +95 -0
  47. package/lib/handlers/context_managers.js +250 -0
  48. package/lib/handlers/control_flow_jumps.js +954 -0
  49. package/lib/handlers/exceptions_blocks.js +952 -0
  50. package/lib/handlers/formatting.js +31 -0
  51. package/lib/handlers/function_calls.js +496 -0
  52. package/lib/handlers/function_class_build.js +330 -0
  53. package/lib/handlers/generators_async.js +172 -0
  54. package/lib/handlers/imports.js +53 -0
  55. package/lib/handlers/load_store_names.js +711 -0
  56. package/lib/handlers/loop_iterator.js +318 -0
  57. package/lib/handlers/misc_other.js +1201 -0
  58. package/lib/handlers/pattern_matching.js +226 -0
  59. package/lib/handlers/stack_ops.js +280 -0
  60. package/lib/handlers/subscript_slice.js +394 -0
  61. package/lib/handlers/unary_ops.js +91 -0
  62. package/lib/handlers/unpack.js +141 -0
  63. package/lib/stack_history.js +63 -0
  64. package/lib/zip_reader.js +217 -0
  65. package/package.json +35 -0
@@ -0,0 +1,31 @@
1
+ const AST = require('../ast/ast_node');
2
+
3
+ function handleBuildTemplate() {
4
+ // Python 3.14 BUILD_TEMPLATE: template objects for f-strings; keep stack as-is (operands already handled by formatting ops).
5
+ const count = this.code.Current.Argument || 0;
6
+ const items = [];
7
+ for (let i = 0; i < count; i++) {
8
+ items.unshift(this.dataStack.pop());
9
+ }
10
+ // Re-push items as a tuple-like collection to preserve stack shape.
11
+ const tpl = new AST.ASTTuple(items);
12
+ tpl.requireParens = false;
13
+ this.dataStack.push(tpl);
14
+ }
15
+
16
+ function handleBuildInterpolationA() {
17
+ // Python 3.14 BUILD_INTERPOLATION: build f-string pieces (similar to BUILD_STRING).
18
+ const count = this.code.Current.Argument || 0;
19
+ const values = [];
20
+ for (let i = 0; i < count; i++) {
21
+ values.push(this.dataStack.pop());
22
+ }
23
+ const joined = new AST.ASTJoinedStr(values);
24
+ joined.line = this.code.Current.LineNo;
25
+ this.dataStack.push(joined);
26
+ }
27
+
28
+ module.exports = {
29
+ handleBuildTemplate,
30
+ handleBuildInterpolationA
31
+ };
@@ -0,0 +1,496 @@
1
+ const AST = require('../ast/ast_node');
2
+
3
+ function handleKwNamesA() {
4
+ let astNode = new AST.ASTKwNamesMap();
5
+ let keys = this.code.Current.ConstantObject;
6
+ for (let idx = keys.length - 1; idx >= 0; idx--) {
7
+ astNode.add(keys[idx], this.dataStack.pop());
8
+ }
9
+
10
+ astNode.line = this.code.Current.LineNo;
11
+ this.dataStack.push(astNode);
12
+ }
13
+
14
+ function handleCallA() {
15
+ handleInstrumentedCallA.call(this);
16
+ }
17
+
18
+ function handleCallFunctionA() {
19
+ handleInstrumentedCallA.call(this);
20
+ }
21
+
22
+ function handleInstrumentedCallKwA() {
23
+ // Instrumented CALL_KW behaves like CALL with kw count encoded in argument.
24
+ handleInstrumentedCallA.call(this);
25
+ }
26
+
27
+ function handleEnterExecutorA() {
28
+ // 3.14 ENTER_EXECUTOR: instrumentation hook, ignore for decompilation.
29
+ if (global.g_cliArgs?.debug) {
30
+ console.log(`[ENTER_EXECUTOR] arg=${this.code.Current.Argument} at offset ${this.code.Current.Offset}`);
31
+ }
32
+ }
33
+
34
+ function handleInstrumentedCallA() {
35
+ let kwparams = (this.code.Current.Argument & 0xFF00) >> 8;
36
+ let pparams = (this.code.Current.Argument & 0xFF);
37
+ let kwparamList = [];
38
+ let pparamList = [];
39
+ let loadBuildClassFound = false;
40
+
41
+ for (let idx = this.dataStack.length - 1; idx >= 0; idx--) {
42
+ if (this.dataStack[idx] instanceof AST.ASTLoadBuildClass) {
43
+ loadBuildClassFound = true;
44
+ break;
45
+ }
46
+ }
47
+
48
+ if (loadBuildClassFound) {
49
+ let bases = [];
50
+ let TOS = this.dataStack.top();
51
+
52
+ while (TOS instanceof AST.ASTName || TOS instanceof AST.ASTBinary) {
53
+ bases.push(TOS);
54
+ this.dataStack.pop();
55
+ TOS = this.dataStack.top();
56
+ }
57
+
58
+ // qualified name is PycString at TOS
59
+ let name = this.dataStack.pop();
60
+ let functionNode = this.dataStack.pop();
61
+ let loadbuild = this.dataStack.pop();
62
+ if (loadbuild instanceof AST.ASTLoadBuildClass) {
63
+ let callNode = new AST.ASTCall(functionNode, pparamList, kwparamList);
64
+ callNode.line = this.code.Current.LineNo;
65
+ let classNode = new AST.ASTClass(callNode, new AST.ASTTuple(bases), name);
66
+ classNode.line = this.code.Current.LineNo;
67
+ this.dataStack.push(classNode);
68
+ return;
69
+ }
70
+ }
71
+
72
+ if (this.object.Reader.versionCompare(3, 11) >= 0) {
73
+ let kwparams_map = this.dataStack.top();
74
+ if (kwparams_map instanceof AST.ASTKwNamesMap) {
75
+ this.dataStack.pop();
76
+ for (let kwParam of kwparams_map.values) {
77
+ kwparamList.unshift(kwParam);
78
+ kwparams--;
79
+ }
80
+ }
81
+ }
82
+ else {
83
+ for (let idx = 0; idx < kwparams; idx++) {
84
+ let value = this.dataStack.pop();
85
+ let key = this.dataStack.pop();
86
+ kwparamList.unshift({key, value});
87
+ }
88
+ }
89
+ let skipCallNode = false;
90
+ for (let idx = 0; idx < pparams; idx++) {
91
+ let param = this.dataStack.pop();
92
+ if (param instanceof AST.ASTFunction) {
93
+ let fun_code = param.code;
94
+ let code_src = fun_code.object;
95
+ let function_name = code_src.Name;
96
+ if (function_name == "<lambda>") {
97
+ pparamList.unshift(param);
98
+ } else if ( pparams == 1) {
99
+ // Decorator used
100
+ let decorator = this.dataStack.pop();
101
+ param.add_decorator(decorator);
102
+ // Decorating function and returning it back to data stack
103
+ this.dataStack.push(param);
104
+ skipCallNode = true;
105
+ break;
106
+ }
107
+ } else {
108
+ pparamList.unshift(param);
109
+ }
110
+ }
111
+
112
+ if (skipCallNode) {
113
+ return;
114
+ }
115
+
116
+ let func = this.dataStack.pop();
117
+ // 3.11+ PUSH_NULL precedes callable; if func is null, grab the real callable underneath.
118
+ if ([this.OpCodes.CALL_A, this.OpCodes.INSTRUMENTED_CALL_A].includes(this.code.Current.OpCodeID)) {
119
+ if (func === null && this.dataStack.length > 0) {
120
+ func = this.dataStack.pop();
121
+ } else if (this.dataStack.length > 0 && this.dataStack.top() == null) {
122
+ this.dataStack.pop();
123
+ }
124
+ // Python 3.14 LOAD_SPECIAL pushes [self, method] for special method calls.
125
+ // If func is an attr access for __enter__/__exit__/__aenter__/__aexit__,
126
+ // pop the self since it's consumed by the unbound method call.
127
+ else if (func instanceof AST.ASTBinary && func.op === AST.ASTBinary.BinOp.Attr) {
128
+ const attrName = func.right?.name;
129
+ if (['__enter__', '__exit__', '__aenter__', '__aexit__'].includes(attrName)) {
130
+ if (this.dataStack.length > 0) {
131
+ this.dataStack.pop(); // Pop self consumed by special method
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ if (func instanceof AST.ASTFunction) {
138
+ const compNames = new Set(["<listcomp>", "<setcomp>", "<dictcomp>", "<genexpr>"]);
139
+ const codeObj = func.code?.object;
140
+ if (codeObj && !codeObj.SourceCode) {
141
+ try {
142
+ const PycDecompiler = require('../PycDecompiler');
143
+ codeObj.SourceCode = new PycDecompiler(codeObj).decompile();
144
+ } catch (e) {
145
+ if (global.g_cliArgs?.debug) {
146
+ console.error(`[CALL] Failed to decompile nested function: ${e?.message}`);
147
+ }
148
+ }
149
+ }
150
+
151
+ const sourceTop = codeObj?.SourceCode?.list?.top?.() || null;
152
+ const compNode = codeObj?.SourceCode?.list?.find?.(n => n instanceof AST.ASTComprehension) || null;
153
+ if (global.g_cliArgs?.debug) {
154
+ const topType = sourceTop?.constructor?.name || 'null';
155
+ console.log(`[CALL] func=${codeObj?.Name || codeObj?.QualName?.Value || '<?>'} sourceTop=${topType} compNode=${compNode?.constructor?.name || 'null'} hasSource=${!!codeObj?.SourceCode} pparams=${pparamList.length}`);
156
+ }
157
+ const looksLikeComp = compNames.has(func.code?.object?.Name) ||
158
+ sourceTop instanceof AST.ASTComprehension ||
159
+ compNode instanceof AST.ASTComprehension ||
160
+ (sourceTop instanceof AST.ASTReturn && sourceTop.value instanceof AST.ASTComprehension);
161
+ if (looksLikeComp) {
162
+ let resultNode = compNode || sourceTop;
163
+ if (resultNode instanceof AST.ASTReturn) {
164
+ resultNode = resultNode.value;
165
+ }
166
+
167
+ // Map placeholder iter (.0) to actual argument for comprehensions.
168
+ if (resultNode?.generators) {
169
+ if (global.g_cliArgs?.debug) {
170
+ console.log(`[CALL] iter placeholders:`, resultNode.generators.map(gen => gen.iter?.codeFragment?.()));
171
+ }
172
+ for (let gen of resultNode.generators) {
173
+ if (gen.iter instanceof AST.ASTName && gen.iter.name?.match(/^\.\d+$/)) {
174
+ const paramIdx = ~~gen.iter.name.substring(1);
175
+ const param = pparamList[paramIdx];
176
+ gen.iter = param instanceof AST.ASTIteratorValue ? param.value : param;
177
+ if (global.g_cliArgs?.debug) {
178
+ console.log(`[CALL] remapped iter .${paramIdx} -> ${gen.iter?.constructor?.name}`);
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ if (resultNode) {
185
+ this.dataStack.push(resultNode);
186
+ return;
187
+ }
188
+ }
189
+ }
190
+
191
+ if ([this.OpCodes.GET_ITER, this.OpCodes.GET_AITER].includes(this.code.Prev.OpCodeID)) {
192
+ let ast = func.code.object.SourceCode.list.top();
193
+ if (!(ast instanceof AST.ASTKeyword)) {
194
+ if (ast instanceof AST.ASTReturn) {
195
+ ast = ast.value;
196
+ }
197
+ if (ast?.generators) {
198
+ for (let gen of ast.generators) {
199
+ if (gen.iter instanceof AST.ASTName && gen.iter.name.match(/^\.\d+$/)) {
200
+ let paramIdx = ~~gen.iter.name.substring(1);
201
+ let param = pparamList[paramIdx];
202
+ if (param instanceof AST.ASTIteratorValue) {
203
+ param = param.value;
204
+ }
205
+ gen.iter = param;
206
+ }
207
+ }
208
+ }
209
+ }
210
+ this.dataStack.push(ast);
211
+
212
+ } else {
213
+ // Python 3.14 with-statement pattern: when calling __enter__,
214
+ // push the context manager (not the call) so STORE_* sets it as withBlock.expr
215
+ if (this._py314WithCtxMgrForStore &&
216
+ func instanceof AST.ASTBinary &&
217
+ func.op === AST.ASTBinary.BinOp.Attr &&
218
+ func.right?.name === '__enter__') {
219
+ this.dataStack.push(this._py314WithCtxMgrForStore);
220
+ this._py314WithCtxMgrForStore = null;
221
+ return;
222
+ }
223
+
224
+ let callNode = new AST.ASTCall( func, pparamList, kwparamList);
225
+ callNode.line = this.code.Current.LineNo;
226
+ this.dataStack.push(callNode);
227
+ }
228
+ }
229
+
230
+ function handleCallFunctionVarA() {
231
+ let variable = this.dataStack.pop();
232
+ let kwparams = (this.code.Current.Argument & 0xFF00) >> 8;
233
+ let pparams = (this.code.Current.Argument & 0xFF);
234
+ let kwparamList = [];
235
+ let pparamList = [];
236
+ for (let idx = 0; idx < kwparams; idx++) {
237
+ let value = this.dataStack.pop();
238
+ let key = this.dataStack.pop();
239
+ kwparamList.unshift({key, value});
240
+ }
241
+ for (let idx = 0; idx < pparams; idx++) {
242
+ pparamList.unshift(this.dataStack.pop());
243
+ }
244
+ let func = this.dataStack.pop();
245
+
246
+ let callNode = new AST.ASTCall( func, pparamList, kwparamList);
247
+ callNode.var = variable;
248
+ callNode.line = this.code.Current.LineNo;
249
+ this.dataStack.push(callNode);
250
+ }
251
+
252
+ function handleCallFunctionKwA() {
253
+ let kw = this.dataStack.pop();
254
+ let kwparams = (this.code.Current.Argument & 0xFF00) >> 8;
255
+ let pparams = (this.code.Current.Argument & 0xFF);
256
+ let kwparamList = [];
257
+ let pparamList = [];
258
+ for (let idx = 0; idx < kwparams; idx++) {
259
+ let value = this.dataStack.pop();
260
+ let key = this.dataStack.pop();
261
+ kwparamList.unshift({key, value});
262
+ }
263
+ for (let idx = 0; idx < pparams; idx++) {
264
+ pparamList.unshift(this.dataStack.pop());
265
+ }
266
+ let func = this.dataStack.pop();
267
+
268
+ let callNode = new AST.ASTCall( func, pparamList, kwparamList);
269
+ callNode.kw = kw;
270
+ callNode.line = this.code.Current.LineNo;
271
+ this.dataStack.push(callNode);
272
+ }
273
+
274
+ function handleCallFunctionVarKwA() {
275
+ let kw = this.dataStack.pop();
276
+ let variable = this.dataStack.pop();
277
+ let kwparams = (this.code.Current.Argument & 0xFF00) >> 8;
278
+ let pparams = (this.code.Current.Argument & 0xFF);
279
+ let kwparamList = [];
280
+ let pparamList = [];
281
+ for (let idx = 0; idx < kwparams; idx++) {
282
+ let value = this.dataStack.pop();
283
+ let key = this.dataStack.pop();
284
+ kwparamList.unshift({key, value});
285
+ }
286
+ for (let idx = 0; idx < pparams; idx++) {
287
+ pparamList.unshift(this.dataStack.pop());
288
+ }
289
+ let func = this.dataStack.pop();
290
+
291
+ let callNode = new AST.ASTCall( func, pparamList, kwparamList);
292
+ callNode.kw = kw;
293
+ callNode.var = variable;
294
+ callNode.line = this.code.Current.LineNo;
295
+ this.dataStack.push(callNode);
296
+ }
297
+
298
+ function handleCallMethodA() {
299
+ let pparamList = [];
300
+ for (let idx = 0; idx < this.code.Current.Argument; idx++) {
301
+ let param = this.dataStack.pop();
302
+ if (param instanceof AST.ASTFunction) {
303
+ let fun_code = param.code;
304
+ let code_src = fun_code.object;
305
+ let function_name = code_src.name;
306
+ if (function_name == "<lambda>") {
307
+ pparamList.unshift(param);
308
+ } else {
309
+ // Decorator used
310
+ let decorNameNode = new AST.ASTName(function_name);
311
+ let storeNode = new AST.ASTStore(param, decorNameNode);
312
+ storeNode.line = this.code.Current.LineNo;
313
+ this.curBlock.nodes.push(storeNode);
314
+
315
+ pparamList.unshift(decorNameNode);
316
+ }
317
+ } else {
318
+ pparamList.unshift(param);
319
+ }
320
+ }
321
+ let func = this.dataStack.pop();
322
+ let callNode = new AST.ASTCall (func, pparamList, []);
323
+ callNode.line = this.code.Current.LineNo;
324
+ this.dataStack.push(callNode);
325
+ }
326
+
327
+ function handlePrecallA() {}
328
+
329
+ function handleBinaryCall() {
330
+ let paramsTuple = this.dataStack.pop();
331
+ let func = this.dataStack.pop();
332
+ let params = [];
333
+
334
+ if (paramsTuple instanceof AST.ASTTuple) {
335
+ params = paramsTuple.values;
336
+ }
337
+
338
+ let callNode = new AST.ASTCall(func, params);
339
+ this.dataStack.push(callNode);
340
+ }
341
+
342
+ function handleCallFunctionExA() {
343
+ let flags = this.code.Current.Argument;
344
+ let kwparams = [];
345
+ let pparams = [];
346
+ if (flags & 0x01) { // **kwargs
347
+ let kw = this.dataStack.pop();
348
+ if (kw instanceof AST.ASTMap) {
349
+ kwparams = kw.values;
350
+ } else if (kw?.object?.ClassName === "Py_Dict" && kw.object.Value) {
351
+ kwparams = kw.object.Value.map(entry => ({key: new AST.ASTObject(entry.key), value: new AST.ASTObject(entry.value)}));
352
+ } else {
353
+ if (global.g_cliArgs?.debug) {
354
+ console.error("Expected a map for CALL_FUNCTION_EX kwargs");
355
+ }
356
+ }
357
+ }
358
+ if (flags & 0x02) { // *args
359
+ let args = this.dataStack.pop();
360
+ if (args instanceof AST.ASTTuple || args instanceof AST.ASTList) {
361
+ pparams = args.values;
362
+ } else {
363
+ if (global.g_cliArgs?.debug) {
364
+ console.error("Expected a tuple or list for CALL_FUNCTION_EX args");
365
+ }
366
+ }
367
+ }
368
+ let func = this.dataStack.pop();
369
+ let callNode = new AST.ASTCall(func, pparams, kwparams);
370
+ callNode.line = this.code.Current.LineNo;
371
+ this.dataStack.push(callNode);
372
+ }
373
+
374
+ function handleCallIntrinsic1() {
375
+ // Python 3.12+ CALL_INTRINSIC_1 opcode
376
+ // Calls built-in intrinsic functions (argument specifies which)
377
+ // Intrinsic 3: ASYNC_GEN_WRAP (wraps async generator function)
378
+ // Intrinsic 4: STOPITERATION_ERROR, etc.
379
+
380
+ // Intrinsic takes 1 arg from stack, returns result
381
+ let arg = this.dataStack.pop();
382
+
383
+ if (global.g_cliArgs?.debug) {
384
+ console.log(`[CALL_INTRINSIC_1] intrinsic=${this.code.Current.Argument}, arg=${arg?.constructor?.name}`);
385
+ }
386
+
387
+ // Intrinsic 11 (PEP 695 type statement) wraps a tuple(name, qualname, type-fn)
388
+ const TYPE_ALIAS_INTRINSIC = 11;
389
+ if (this.code.Current.Argument === TYPE_ALIAS_INTRINSIC && arg instanceof AST.ASTTuple) {
390
+ const values = arg.values || [];
391
+ const aliasName = values[0]?.object?.Value || values[0]?.name || values[0]?.codeFragment?.();
392
+ const typeFunc = values[2];
393
+ const extractTypeValue = (fn) => {
394
+ if (!(fn instanceof AST.ASTFunction)) {
395
+ return fn;
396
+ }
397
+ const body = fn.code?.object?.SourceCode;
398
+ if (body?.list?.length) {
399
+ const first = body.list[0];
400
+ if (first instanceof AST.ASTReturn) {
401
+ return first.value;
402
+ }
403
+ return first;
404
+ }
405
+ return fn;
406
+ };
407
+ const typeValue = extractTypeValue(typeFunc);
408
+ if (typeValue instanceof AST.ASTSubscr && typeValue.key instanceof AST.ASTTuple) {
409
+ typeValue.key.m_requireParens = false;
410
+ }
411
+ const aliasNode = new AST.ASTTypeAlias(aliasName, typeValue);
412
+ aliasNode.line = this.code.Current.LineNo;
413
+ this.dataStack.push(aliasNode);
414
+ return;
415
+ }
416
+
417
+ // Default: preserve the argument as result
418
+ if (arg) {
419
+ this.dataStack.push(arg);
420
+ }
421
+ }
422
+
423
+ function handleCallIntrinsic2() {
424
+ // Python 3.12+ CALL_INTRINSIC_2 opcode
425
+ // Calls intrinsics with 2 arguments
426
+
427
+ if (global.g_cliArgs?.debug) {
428
+ console.log(`[CALL_INTRINSIC_2] intrinsic=${this.code.Current.Argument}`);
429
+ }
430
+
431
+ const argB = this.dataStack.pop();
432
+ const argA = this.dataStack.pop();
433
+ const args = [argA, argB];
434
+
435
+ // Intrinsic 1: PREP_RERAISE_STAR equivalent in 3.12+.
436
+ // Do not emit; mark to skip the following conditional jump.
437
+ if (this.code.Current.Argument === 1) {
438
+ this.ignoreNextConditional = true;
439
+ this.cleanupStackDepth = this.dataStack.length + 1; // after we push a placeholder
440
+ this.dataStack.push(new AST.ASTNone());
441
+ this.inExceptionGroup = true;
442
+ return;
443
+ }
444
+
445
+ // Intrinsic 4: build generic function from type parameters + function
446
+ if (this.code.Current.Argument === 4) {
447
+ const funcNode = args.find(a => a instanceof AST.ASTFunction);
448
+ const typeArg = args.find(a => a instanceof AST.ASTTuple || a instanceof AST.ASTList);
449
+ if (funcNode && typeArg) {
450
+ const names = (typeArg.values || []).map(v => {
451
+ if (v instanceof AST.ASTName) return v.name;
452
+ if (v?.object?.Value) return v.object.Value;
453
+ const frag = v?.codeFragment?.();
454
+ return frag?.toString?.() || 'T';
455
+ });
456
+ funcNode.typeParams = names;
457
+ this.dataStack.push(funcNode);
458
+ return;
459
+ }
460
+ }
461
+
462
+ // Default: preserve the left argument to avoid stack underflow
463
+ if (argA !== undefined) {
464
+ this.dataStack.push(argA);
465
+ }
466
+ }
467
+
468
+ function handleCallIntrinsic1A() {
469
+ // Python 3.12+ CALL_INTRINSIC_1_A opcode (wrapper)
470
+ handleCallIntrinsic1.call(this);
471
+ }
472
+
473
+ function handleCallIntrinsic2A() {
474
+ // Python 3.12+ CALL_INTRINSIC_2_A opcode (wrapper)
475
+ handleCallIntrinsic2.call(this);
476
+ }
477
+
478
+ module.exports = {
479
+ handleKwNamesA,
480
+ handleCallA,
481
+ handleInstrumentedCallKwA,
482
+ handleCallFunctionA,
483
+ handleInstrumentedCallA,
484
+ handleCallFunctionVarA,
485
+ handleCallFunctionKwA,
486
+ handleCallFunctionVarKwA,
487
+ handleCallMethodA,
488
+ handlePrecallA,
489
+ handleBinaryCall,
490
+ handleCallFunctionExA,
491
+ handleCallIntrinsic1,
492
+ handleCallIntrinsic2,
493
+ handleCallIntrinsic1A,
494
+ handleCallIntrinsic2A,
495
+ handleEnterExecutorA
496
+ };