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
package/lib/OpCodes.js ADDED
@@ -0,0 +1,940 @@
1
+ const OpCode = require('./OpCode');
2
+
3
+ class OpCodes
4
+ {
5
+
6
+ /* No parameter word */
7
+ static STOP_CODE = 0; // Python 1.0 - 3.2
8
+ static POP_TOP = 1; // Python 1.0 ->
9
+ static ROT_TWO = 2; // Python 1.0 - 3.10
10
+ static ROT_THREE = 3; // Python 1.0 - 3.10
11
+ static DUP_TOP = 4; // Python 1.0 - 3.10
12
+ static DUP_TOP_TWO = 5; // Python 3.2 - 3.10
13
+ static UNARY_POSITIVE = 6; // Python 1.0 - 3.11
14
+ static UNARY_NEGATIVE = 7; // Python 1.0 ->
15
+ static UNARY_NOT = 8; // Python 1.0 ->
16
+ static UNARY_CONVERT = 9; // Python 1.0 - 2.7
17
+ static UNARY_CALL = 10; // Python 1.0 - 1.2
18
+ static UNARY_INVERT = 11; // Python 1.0 ->
19
+ static BINARY_POWER = 12; // Python 1.4 - 3.10
20
+ static BINARY_MULTIPLY = 13; // Python 1.0 - 3.10
21
+ static BINARY_DIVIDE = 14; // Python 1.0 - 2.7
22
+ static BINARY_MODULO = 15; // Python 1.0 - 3.10
23
+ static BINARY_ADD = 16; // Python 1.0 - 3.10
24
+ static BINARY_SUBTRACT = 17; // Python 1.0 - 3.10
25
+ static BINARY_SUBSCR = 18; // Python 1.0 ->
26
+ static BINARY_CALL = 19; // Python 1.0 - 1.2
27
+ static SLICE_0 = 20; // Python 1.0 - 2.7
28
+ static SLICE_1 = 21; // Python 1.0 - 2.7
29
+ static SLICE_2 = 22; // Python 1.0 - 2.7
30
+ static SLICE_3 = 23; // Python 1.0 - 2.7
31
+ static STORE_SLICE_0 = 24; // Python 1.0 - 2.7
32
+ static STORE_SLICE_1 = 25; // Python 1.0 - 2.7
33
+ static STORE_SLICE_2 = 26; // Python 1.0 - 2.7
34
+ static STORE_SLICE_3 = 27; // Python 1.0 - 2.7
35
+ static DELETE_SLICE_0 = 28; // Python 1.0 - 2.7
36
+ static DELETE_SLICE_1 = 29; // Python 1.0 - 2.7
37
+ static DELETE_SLICE_2 = 30; // Python 1.0 - 2.7
38
+ static DELETE_SLICE_3 = 31; // Python 1.0 - 2.7
39
+ static STORE_SUBSCR = 32; // Python 1.0 ->
40
+ static DELETE_SUBSCR = 33; // Python 1.0 ->
41
+ static BINARY_LSHIFT = 34; // Python 1.0 - 3.10
42
+ static BINARY_RSHIFT = 35; // Python 1.0 - 3.10
43
+ static BINARY_AND = 36; // Python 1.0 - 3.10
44
+ static BINARY_XOR = 37; // Python 1.0 - 3.10
45
+ static BINARY_OR = 38; // Python 1.0 - 3.10
46
+ static PRINT_EXPR = 39; // Python 1.0 - 3.11
47
+ static PRINT_ITEM = 40; // Python 1.0 - 2.7
48
+ static PRINT_NEWLINE = 41; // Python 1.0 - 2.7
49
+ static BREAK_LOOP = 42; // Python 1.0 - 3.7
50
+ static RAISE_EXCEPTION = 43; // Python 1.0 - 1.2
51
+ static LOAD_LOCALS = 44; // Python 1.0 - 2.7, 3.12 ->
52
+ static RETURN_VALUE = 45; // Python 1.0 ->
53
+ static LOAD_GLOBALS = 46; // Python 1.0 - 1.2
54
+ static EXEC_STMT = 47; // Python 1.0 - 2.7
55
+ static BUILD_FUNCTION = 48; // Python 1.0 - 1.2
56
+ static POP_BLOCK = 49; // Python 1.0 - 3.10
57
+ static END_FINALLY = 50; // Python 1.0 - 3.8
58
+ static BUILD_CLASS = 51; // Python 1.0 - 2.7
59
+ static ROT_FOUR = 52; // Python 2.0 - 3.1, 3.8 - 3.10
60
+ static NOP = 53; // Python 2.4 ->
61
+ static LIST_APPEND = 54; // Python 2.4 - 2.6, 3.0
62
+ static BINARY_FLOOR_DIVIDE = 55; // Python 2.2 - 3.10
63
+ static BINARY_TRUE_DIVIDE = 56; // Python 2.2 - 3.10
64
+ static INPLACE_FLOOR_DIVIDE = 57; // Python 2.2 - 3.10
65
+ static INPLACE_TRUE_DIVIDE = 58; // Python 2.2 - 3.10
66
+ static GET_LEN = 59; // Python 3.10 ->
67
+ static MATCH_MAPPING = 60; // Python 3.10 ->
68
+ static MATCH_SEQUENCE = 61; // Python 3.10 ->
69
+ static MATCH_KEYS = 62; // Python 3.10 ->
70
+ // Keep NOT_TAKEN distinct from legacy DELETE_SLICE_0 to avoid handler collisions.
71
+ static NOT_TAKEN = 400; // Python 3.13+ instrumentation hint
72
+ static COPY_DICT_WITHOUT_KEYS = 63; // Python 3.10
73
+ static STORE_MAP = 64; // Python 2.6 - 3.4
74
+ static INPLACE_ADD = 65; // Python 2.0 - 3.10
75
+ static INPLACE_SUBTRACT = 66; // Python 2.0 - 3.10
76
+ static INPLACE_MULTIPLY = 67; // Python 2.0 - 3.10
77
+ static INPLACE_DIVIDE = 68; // Python 2.0 - 2.7
78
+ static INPLACE_MODULO = 69; // Python 2.0 - 3.10
79
+ static INPLACE_POWER = 70; // Python 2.0 - 3.10
80
+ static GET_ITER = 71; // Python 2.2 ->
81
+ static PRINT_ITEM_TO = 72; // Python 2.0 - 2.7
82
+ static PRINT_NEWLINE_TO = 73; // Python 2.0 - 2.7
83
+ static INPLACE_LSHIFT = 74; // Python 2.0 - 3.10
84
+ static INPLACE_RSHIFT = 75; // Python 2.0 - 3.10
85
+ static INPLACE_AND = 76; // Python 2.0 - 3.10
86
+ static INPLACE_XOR = 77; // Python 2.0 - 3.10
87
+ static INPLACE_OR = 78; // Python 2.0 - 3.10
88
+ static WITH_CLEANUP = 79; // Python 2.5 - 3.4
89
+ static WITH_CLEANUP_START = 80; // Python 3.5 - 3.8
90
+ static WITH_CLEANUP_FINISH = 81; // Python 3.5 - 3.8
91
+ static IMPORT_STAR = 82; // Python 2.0 - 3.11
92
+ static SETUP_ANNOTATIONS = 83; // Python 3.6 ->
93
+ static YIELD_VALUE = 84; // Python 2.2 - 3.11
94
+ static LOAD_BUILD_CLASS = 85; // Python 3.0 ->
95
+ static STORE_LOCALS = 86; // Python 3.0 - 3.3
96
+ static POP_EXCEPT = 87; // Python 3.0 ->
97
+ static SET_ADD = 88; // Python 3.0
98
+ static YIELD_FROM = 89; // Python 3.3 - 3.10
99
+ static BINARY_MATRIX_MULTIPLY = 90; // Python 3.5 - 3.10
100
+ static INPLACE_MATRIX_MULTIPLY = 91; // Python 3.5 - 3.10
101
+ static GET_AITER = 92; // Python 3.5 ->
102
+ static GET_ANEXT = 93; // Python 3.5 ->
103
+ static BEFORE_ASYNC_WITH = 94; // Python 3.5 ->
104
+ static GET_YIELD_FROM_ITER = 95; // Python 3.5 ->
105
+ static GET_AWAITABLE = 96; // Python 3.5 - 3.10
106
+ static BEGIN_FINALLY = 97; // Python 3.8
107
+ static END_ASYNC_FOR = 98; // Python 3.8 ->
108
+ static RERAISE = 99; // Python 3.9
109
+ static WITH_EXCEPT_START = 100; // Python 3.9 ->
110
+ static LOAD_ASSERTION_ERROR = 101; // Python 3.9 ->
111
+ static LIST_TO_TUPLE = 102; // Python 3.9 - 3.11
112
+ static CACHE = 103; // Python 3.11 ->
113
+ static PUSH_NULL = 104; // Python 3.11 ->
114
+ static PUSH_EXC_INFO = 105; // Python 3.11 ->
115
+ static CHECK_EXC_MATCH = 106; // Python 3.11 ->
116
+ static CHECK_EG_MATCH = 107; // Python 3.11 ->
117
+ static BEFORE_WITH = 108; // Python 3.11 ->
118
+ static RETURN_GENERATOR = 109; // Python 3.11 ->
119
+ static ASYNC_GEN_WRAP = 110; // Python 3.11
120
+ static PREP_RERAISE_STAR = 111; // Python 3.11
121
+ static INTERPRETER_EXIT = 112; // Python 3.12 ->
122
+ static END_FOR = 113; // Python 3.12 ->
123
+ static END_SEND = 114; // Python 3.12 ->
124
+ static RESERVED = 115; // Python 3.12 ->
125
+ static BINARY_SLICE = 116; // Python 3.12 ->
126
+ static STORE_SLICE = 117; // Python 3.12 ->
127
+ static CLEANUP_THROW = 118; // Python 3.12 ->
128
+
129
+ /* Has parameter word */
130
+ static PYC_HAVE_ARG = 119;
131
+ static STORE_NAME_A = OpCodes.PYC_HAVE_ARG; // Python 1.0 -> names[A]
132
+ static DELETE_NAME_A = 120; // Python 1.0 -> names[A]
133
+ static UNPACK_TUPLE_A = 121; // Python 1.0 - 1.6 A=count
134
+ static UNPACK_LIST_A = 122; // Python 1.0 - 1.6 A=count
135
+ static UNPACK_ARG_A = 123; // Python 1.0 - 1.4 A=count
136
+ static STORE_ATTR_A = 124; // Python 1.0 -> names[A]
137
+ static DELETE_ATTR_A = 125; // Python 1.0 -> names[A]
138
+ static STORE_GLOBAL_A = 126; // Python 1.0 -> names[A]
139
+ static DELETE_GLOBAL_A = 127; // Python 1.0 -> names[A]
140
+ static ROT_N_A = 128; // Python 3.10 A=count
141
+ static UNPACK_VARARG_A = 129; // Python 1.0 - 1.4 A=count
142
+ static LOAD_CONST_A = 130; // Python 1.0 -> consts[A]
143
+ static LOAD_NAME_A = 131; // Python 1.0 -> names[A]
144
+ static BUILD_TUPLE_A = 132; // Python 1.0 -> A=size
145
+ static BUILD_LIST_A = 133; // Python 1.0 -> A=size
146
+ static BUILD_MAP_A = 134; // Python 1.0 -> A=size
147
+ static LOAD_ATTR_A = 135; // Python 1.0 -> names[A]
148
+ static COMPARE_OP_A = 136; // Python 1.0 -> cmp_ops[A]
149
+ static IMPORT_NAME_A = 137; // Python 1.0 -> names[A]
150
+ static IMPORT_FROM_A = 138; // Python 1.0 -> names[A]
151
+ static ACCESS_MODE_A = 139; // Python 1.0 - 1.4 names[A]
152
+ static JUMP_FORWARD_A = 140; // Python 1.0 -> rel jmp +A
153
+ static JUMP_IF_FALSE_A = 141; // Python 1.0 - 2.6, 3.0 rel jmp +A
154
+ static JUMP_IF_TRUE_A = 142; // Python 1.0 - 2.6, 3.0 rel jmp +A
155
+ static JUMP_ABSOLUTE_A = 143; // Python 1.0 - 3.10 abs jmp A
156
+ static FOR_LOOP_A = 144; // Python 1.0 - 2.2 rel jmp +A
157
+ static LOAD_LOCAL_A = 145; // Python 1.0 - 1.4 names[A]
158
+ static LOAD_GLOBAL_A = 146; // Python 1.0 -> names[A]
159
+ static SET_FUNC_ARGS_A = 147; // Python 1.1 - 1.4 A=count
160
+ static SETUP_LOOP_A = 148; // Python 1.0 - 3.7 rel jmp +A
161
+ static SETUP_EXCEPT_A = 149; // Python 1.0 - 3.7 rel jmp +A
162
+ static SETUP_FINALLY_A = 150; // Python 1.0 - 3.10 rel jmp +A
163
+ static RESERVE_FAST_A = 151; // Python 1.0 - 1.2 A=count
164
+ static LOAD_FAST_A = 152; // Python 1.0 -> locals[A]
165
+ static STORE_FAST_A = 153; // Python 1.0 -> locals[A]
166
+ static DELETE_FAST_A = 154; // Python 1.0 -> locals[A]
167
+ static GEN_START_A = 155; // Python 3.10 ???
168
+ static SET_LINENO_A = 156; // Python 1.0 - 2.2 A=line
169
+ static STORE_ANNOTATION_A = 157; // Python 3.6 names[A]
170
+ static RAISE_VARARGS_A = 158; // Python 1.3 -> A=count
171
+ static CALL_FUNCTION_A = 159; // Python 1.3 - 3.5 A=(#args)+(#kwargs<<8)
172
+ // Python 3.6 - 3.10 A=#args
173
+ static MAKE_FUNCTION_A = 160; // Python 1.3 - 2.7 A=#defaults
174
+ // Python 3.0 - 3.5 A=(#defaults)+(#kwdefaults<<8)+(#annotations<<16)
175
+ // Python 3.6 -> A=flags
176
+ static MAKE_FUNCTION = 272; // Python 3.13+ -> A=flags
177
+ static BUILD_SLICE_A = 161; // Python 1.4 -> A=count
178
+ static CALL_FUNCTION_VAR_A = 162; // Python 1.6 - 3.5 A=(#args)+(#kwargs<<8)
179
+ static CALL_FUNCTION_KW_A = 163; // Python 1.6 - 3.5 A=(#args)+(#kwargs<<8)
180
+ // Python 3.6 - 3.10 A=#args
181
+ static CALL_FUNCTION_VAR_KW_A = 164; // Python 1.6 - 3.5 A=(#args)+(#kwargs<<8)
182
+ static CALL_FUNCTION_EX_A = 165; // Python 3.6 -> A=flags
183
+ static UNPACK_SEQUENCE_A = 166; // Python 2.0 -> A=count
184
+ static FOR_ITER_A = 167; // Python 2.0 -> rel jmp +A
185
+ static DUP_TOPX_A = 168; // Python 2.0 - 3.1 A=count
186
+ static BUILD_SET_A = 169; // Python 2.7 -> A=size
187
+ static JUMP_IF_FALSE_OR_POP_A = 170; // Python 2.7, 3.1 - 3.11 abs jmp A
188
+ static JUMP_IF_TRUE_OR_POP_A = 171; // Python 2.7, 3.1 - 3.11 abs jmp A
189
+ static POP_JUMP_IF_FALSE_A = 172; // Python 2.7, 3.1 - 3.10 abs jmp A
190
+ // Python 3.12 -> rel jmp +A
191
+ static POP_JUMP_IF_TRUE_A = 173; // Python 2.7, 3.1 - 3.10 abs jmp A
192
+ // Python 3.12 -> rel jmp +A
193
+ static CONTINUE_LOOP_A = 174; // Python 2.1 - 3.7 abs jmp A
194
+ static MAKE_CLOSURE_A = 175; // Python 2.1 - 2.7 A=#defaults
195
+ // Python 3.0 - 3.5 A=(#defaults)+(#kwdefaults<<8)+(#annotations<<16)
196
+ static LOAD_CLOSURE_A = 176; // Python 2.1 -> freevars[A]
197
+ static LOAD_DEREF_A = 177; // Python 2.1 -> freevars[A]
198
+ static STORE_DEREF_A = 178; // Python 2.1 -> freevars[A]
199
+ static DELETE_DEREF_A = 179; // Python 3.2 -> freevars[A]
200
+ static EXTENDED_ARG_A = 180; // Python 2.0 -> A=extended_arg
201
+ static SETUP_WITH_A = 181; // Python 2.7, 3.2 - 3.10 rel jmp +A
202
+ static SET_ADD_A = 182; // Python 2.7, 3.1 -> stack[A]
203
+ static MAP_ADD_A = 183; // Python 2.7, 3.1 -> stack[A]
204
+ static UNPACK_EX_A = 184; // Python 3.0 -> A=(before)+(after<<8)
205
+ static LIST_APPEND_A = 185; // Python 2.7, 3.1 -> stack[A]
206
+ static LOAD_CLASSDEREF_A = 186; // Python 3.4 - 3.10 (cellvars+freevars)[A]
207
+ // Python 3.11 localsplusnames[A]
208
+ static MATCH_CLASS_A = 187; // Python 3.10 -> A=#args
209
+ static BUILD_LIST_UNPACK_A = 188; // Python 3.5 - 3.8 A=count
210
+ static BUILD_MAP_UNPACK_A = 189; // Python 3.5 - 3.8 A=count
211
+ static BUILD_MAP_UNPACK_WITH_CALL_A = 190; // Python 3.5 A=(count)+(fnloc<<8)
212
+ // Python 3.6 - 3.8 A=count
213
+ static BUILD_TUPLE_UNPACK_A = 191; // Python 3.5 - 3.8 A=count
214
+ static BUILD_SET_UNPACK_A = 192; // Python 3.5 - 3.8 A=count
215
+ static SETUP_ASYNC_WITH_A = 193; // Python 3.5 - 3.10 rel jmp +A
216
+ static FORMAT_VALUE_A = 194; // Python 3.6 -> A=conversion_type
217
+ static BUILD_CONST_KEY_MAP_A = 195; // Python 3.6 -> A=count
218
+ static BUILD_STRING_A = 196; // Python 3.6 -> A=count
219
+ static BUILD_TUPLE_UNPACK_WITH_CALL_A = 197; // Python 3.6 - 3.8 A=count
220
+ static LOAD_METHOD_A = 198; // Python 3.7 - 3.11 names[A]
221
+ static CALL_METHOD_A = 199; // Python 3.7 - 3.10 A=#args
222
+ static CALL_FINALLY_A = 200; // Python 3.8 rel jmp +A
223
+ static POP_FINALLY_A = 201; // Python 3.8 A=flags
224
+ static IS_OP_A = 202; // Python 3.9 -> A=inverted
225
+ static CONTAINS_OP_A = 203; // Python 3.9 -> A=inverted
226
+ static RERAISE_A = 204; // Python 3.10 -> A=flag
227
+ static JUMP_IF_NOT_EXC_MATCH_A = 205; // Python 3.9 - 3.10 abs jmp A
228
+ static LIST_EXTEND_A = 206; // Python 3.9 -> stack[A]
229
+ static SET_UPDATE_A = 207; // Python 3.9 -> stack[A]
230
+ static DICT_MERGE_A = 208; // Python 3.9 -> stack[A]
231
+ static DICT_UPDATE_A = 209; // Python 3.9 -> stack[A]
232
+ static SWAP_A = 210; // Python 3.11 -> stack[A]
233
+ static POP_JUMP_FORWARD_IF_FALSE_A = 211; // Python 3.11 rel jmp +A
234
+ static POP_JUMP_FORWARD_IF_TRUE_A = 212; // Python 3.11 rel jmp +A
235
+ static COPY_A = 213; // Python 3.11 -> stack[A]
236
+ static BINARY_OP_A = 214; // Python 3.11 -> bin_ops[A]
237
+ static SEND_A = 215; // Python 3.11 -> rel jmp +A
238
+ static POP_JUMP_FORWARD_IF_NOT_NONE_A = 216; // Python 3.11 rel jmp +A
239
+ static POP_JUMP_FORWARD_IF_NONE_A = 217; // Python 3.11 rel jmp +A
240
+ static GET_AWAITABLE_A = 218; // Python 3.11 -> A=awaitable_type
241
+ static JUMP_BACKWARD_NO_INTERRUPT_A = 219; // Python 3.11 -> rel jmp -A
242
+ static MAKE_CELL_A = 220; // Python 3.11 -> locals[A]
243
+ static JUMP_BACKWARD_A = 221; // Python 3.11 -> rel jmp -A
244
+ static COPY_FREE_VARS_A = 222; // Python 3.11 -> A=count
245
+ static RESUME_A = 223; // Python 3.11 -> ???
246
+ static PRECALL_A = 224; // Python 3.11 A=#args
247
+ static CALL_A = 225; // Python 3.11 -> A=#args
248
+ static KW_NAMES_A = 226; // Python 3.11 -> consts[A]
249
+ static POP_JUMP_BACKWARD_IF_NOT_NONE_A = 227; // Python 3.11 jmp rel -A
250
+ static POP_JUMP_BACKWARD_IF_NONE_A = 228; // Python 3.11 jmp rel -A
251
+ static POP_JUMP_BACKWARD_IF_FALSE_A = 229; // Python 3.11 jmp rel -A
252
+ static POP_JUMP_BACKWARD_IF_TRUE_A = 230; // Python 3.11 jmp rel -A
253
+ static RETURN_CONST_A = 231; // Python 3.12 -> consts[A]
254
+ static LOAD_FAST_CHECK_A = 232; // Python 3.12 -> locals[A]
255
+ static POP_JUMP_IF_NOT_NONE_A = 233; // Python 3.12 -> rel jmp +A
256
+ static POP_JUMP_IF_NONE_A = 234; // Python 3.12 -> rel jmp +A
257
+ static LOAD_SUPER_ATTR_A = 235; // Python 3.12 -> A=(flags&0x3)+names[A<<2]
258
+ static LOAD_FAST_AND_CLEAR_A = 236; // Python 3.12 -> locals[A]
259
+ static YIELD_VALUE_A = 237; // Python 3.12 -> ???
260
+ static CALL_INTRINSIC_1_A = 238; // Python 3.12 -> intrinsics_1[A]
261
+ static CALL_INTRINSIC_2_A = 239; // Python 3.12 -> intrinsics_2[A]
262
+ static LOAD_FROM_DICT_OR_GLOBALS_A = 240; // Python 3.12 -> names[A]
263
+ static LOAD_FROM_DICT_OR_DEREF_A = 241; // Python 3.12 -> localsplusnames[A]
264
+
265
+ /* Instrumented opcodes */
266
+ static INSTRUMENTED_NOT_TAKEN_A = 401; // Python 3.14 -> no-op marker
267
+ static INSTRUMENTED_POP_ITER_A = 402; // Python 3.14 -> (see POP_ITER)
268
+ static INSTRUMENTED_END_ASYNC_FOR_A = 403; // Python 3.14 -> (see END_ASYNC_FOR)
269
+ static INSTRUMENTED_CALL_KW_A = 404; // Python 3.14 -> (see CALL_KW)
270
+ static INSTRUMENTED_LOAD_SUPER_ATTR_A = 242; // Python 3.12 -> (see LOAD_SUPER_ATTR)
271
+ static INSTRUMENTED_POP_JUMP_IF_NONE_A = 243; // Python 3.12 -> (see POP_JUMP_IF_NONE)
272
+ static INSTRUMENTED_POP_JUMP_IF_NOT_NONE_A = 244; // Python 3.12 -> (see POP_JUMP_IF_NOT_NONE)
273
+ static INSTRUMENTED_RESUME_A = 245; // Python 3.12 -> (see RESUME)
274
+ static INSTRUMENTED_CALL_A = 246; // Python 3.12 -> (see CALL)
275
+ static INSTRUMENTED_RETURN_VALUE_A = 247; // Python 3.12 -> (see RETURN_VALUE)
276
+ static INSTRUMENTED_YIELD_VALUE_A = 248; // Python 3.12 -> (see YIELD_VALUE)
277
+ static INSTRUMENTED_CALL_FUNCTION_EX_A = 249; // Python 3.12 -> (see CALL_FUNCTION_EX)
278
+ static INSTRUMENTED_JUMP_FORWARD_A = 250; // Python 3.12 -> (see JUMP_FORWARD)
279
+ static INSTRUMENTED_JUMP_BACKWARD_A = 251; // Python 3.12 -> (see JUMP_BACKWARD)
280
+ static INSTRUMENTED_RETURN_CONST_A = 252; // Python 3.12 -> (see RETURN_CONST)
281
+ static INSTRUMENTED_FOR_ITER_A = 253; // Python 3.12 -> (see FOR_ITER)
282
+ static INSTRUMENTED_POP_JUMP_IF_FALSE_A = 254; // Python 3.12 -> (see POP_JUMP_IF_FALSE)
283
+ static INSTRUMENTED_POP_JUMP_IF_TRUE_A = 255; // Python 3.12 -> (see POP_JUMP_IF_TRUE)
284
+ static INSTRUMENTED_END_FOR_A = 256; // Python 3.12 -> (see END_FOR)
285
+ static INSTRUMENTED_END_SEND_A = 257; // Python 3.12 -> (see END_SEND)
286
+ static INSTRUMENTED_INSTRUCTION_A = 258; // Python 3.12 -> ???
287
+ static INSTRUMENTED_LINE_A = 259; // Python 3.12 -> ???
288
+
289
+ // Python 3.12 intrinsic function calls
290
+ static CALL_INTRINSIC_1 = 265; // Python 3.12 -> intrinsics_1[A]
291
+ static CALL_INTRINSIC_2 = 266; // Python 3.12 -> intrinsics_2[A]
292
+
293
+ // Aliases for handler name mapping
294
+ static CALL_INTRINSIC_1A = 238; // Alias for CALL_INTRINSIC_1_A
295
+ static CALL_INTRINSIC_2A = 239; // Alias for CALL_INTRINSIC_2_A
296
+
297
+ // Python 3.13/3.14 new opcodes
298
+ static EXIT_INIT_CHECK = 267; // Python 3.13 -> exit __init__ check
299
+ static FORMAT_SIMPLE = 268; // Python 3.13 -> format without spec
300
+ static FORMAT_WITH_SPEC = 269; // Python 3.13 -> format with spec
301
+ static TO_BOOL = 270; // Python 3.13 -> convert to bool
302
+ static BUILD_TEMPLATE = 271; // Python 3.14 -> build template object
303
+ static LOAD_FAST_BORROW_A = 274; // Python 3.14 -> optimized LOAD_FAST (distinct ID from MAKE_FUNCTION=272)
304
+ static LOAD_SMALL_INT_A = 275; // Python 3.14 -> load small integer
305
+ static LOAD_FAST_LOAD_FAST_A = 276; // Python 3.13+ -> packed two locals
306
+ static STORE_FAST_LOAD_FAST_A = 277; // Python 3.13+ -> store then load packed locals
307
+ static SET_FUNCTION_ATTRIBUTE_A = 278; // Python 3.13+ -> set function attribute
308
+
309
+ // Aliases / placeholders for 3.13+ opcodes with distinct IDs to avoid collisions
310
+ static POP_ITER = 300; // Python 3.14 instrumentation helper
311
+ static WITH_EXCEPT_START_A = 301; // Python 3.13+ arg variant
312
+ static END_ASYNC_FOR_A = 302; // Python 3.13+ encoded end async for
313
+ static POP_BLOCK_A = 303; // Python 3.13+ encoded pop block
314
+ static CALL_FUNCTION_EX = 304; // Python 3.14 CALL (EX) wrapper
315
+ static CALL_KW_A = 305; // Python 3.14 CALL_KW wrapper
316
+ static CONVERT_VALUE_A = 306; // Python 3.14 conversion helper
317
+ static JUMP_A = 307; // Python 3.14 generic jump
318
+ static JUMP_NO_INTERRUPT_A = 308; // Python 3.14 jump without signal checks
319
+ static LOAD_COMMON_CONSTANT_A = 309; // Python 3.14 common constants table
320
+ static LOAD_FAST_BORROW_LOAD_FAST_BORROW_A = 310; // Python 3.14 packed borrow load
321
+ static LOAD_SPECIAL_A = 311; // Python 3.14 LOAD_SPECIAL
322
+ static SETUP_CLEANUP_A = 312; // Python 3.13+ cleanup setup
323
+ static STORE_FAST_MAYBE_NULL_A = 313; // Python 3.13+ tolerant store
324
+ static STORE_FAST_STORE_FAST_A = 314; // Python 3.13+ packed store/store
325
+ static ANNOTATIONS_PLACEHOLDER_A = 315; // Python 3.14 placeholder
326
+ static BUILD_INTERPOLATION_A = 316; // Python 3.14 string template builder
327
+ static ENTER_EXECUTOR_A = 317; // Python 3.14 executor entry (ignore)
328
+ static LOAD_SUPER_METHOD_A = 318; // Python 3.13+ super method load
329
+ static LOAD_ZERO_SUPER_ATTR_A = 319; // Python 3.13+ zero-cost super attr
330
+ static LOAD_ZERO_SUPER_METHOD_A = 320; // Python 3.13+ zero-cost super method
331
+
332
+
333
+ // enum cmp_op
334
+ // {
335
+ // PyCmp_LT, PyCmp_LE, PyCmp_EQ, PyCmp_NE, PyCmp_GT, PyCmp_GE,
336
+ // PyCmp_IN, PyCmp_NOT_IN, PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD
337
+ // };
338
+
339
+ OpCodeList = [];
340
+
341
+ static CompareOpNames = ["<", "<=", "==", "!=", ">", ">=", "in", "not in", "is", "is not", "exception match", "BAD"];
342
+
343
+ Instructions = [];
344
+ CurrentInstructionIndex = -1;
345
+
346
+ get HasInstructionsToProcess() {
347
+ return this.CurrentInstructionIndex < this.Instructions.length - 1;
348
+ }
349
+
350
+ CodeObject = [];
351
+
352
+ constructor() {
353
+
354
+ }
355
+
356
+ get Current() {
357
+ return (this.CurrentInstructionIndex < 0 || this.CurrentInstructionIndex >= this.Instructions.length) ? null : this.Instructions[this.CurrentInstructionIndex];
358
+ }
359
+
360
+ GetOpCodeID(code, offset) {
361
+ // Bounds checking for bytecode array access
362
+ if (offset < 0 || offset >= code.length) {
363
+ if (global.g_cliArgs?.debug) {
364
+ console.error(`GetOpCodeID: offset ${offset} out of bounds [0, ${code.length})`);
365
+ }
366
+ return null;
367
+ }
368
+
369
+ let opcode = code[offset];
370
+ let opcodeEntry = this.OpCodeList[opcode];
371
+
372
+ if (!opcodeEntry) {
373
+ if (global.g_cliArgs?.debug) {
374
+ console.error(`GetOpCodeID: unknown opcode ${opcode} (0x${opcode?.toString(16)}) at offset ${offset}`);
375
+ }
376
+ return null;
377
+ }
378
+
379
+ return opcodeEntry.OpCodeID;
380
+ }
381
+
382
+ ReadExtendedArg(code, opOffset) {
383
+ let reader = this.CodeObject.Reader;
384
+ let argument = 0;
385
+ let opCodeID = this.GetOpCodeID(code, opOffset);
386
+
387
+ // If opCodeID is null, bytecode is truncated/corrupted
388
+ if (opCodeID === null) {
389
+ return [0, opOffset];
390
+ }
391
+
392
+ if (reader.versionCompare(3, 6) >= 0) {
393
+ while (opCodeID == OpCodes.EXTENDED_ARG_A) {
394
+ argument = argument | code[++opOffset] << 8;
395
+ opCodeID = this.GetOpCodeID(code, ++opOffset);
396
+
397
+ // Break if we hit end of bytecode
398
+ if (opCodeID === null) {
399
+ break;
400
+ }
401
+ }
402
+ argument <<= 8;
403
+ } else {
404
+ if (opCodeID == OpCodes.EXTENDED_ARG_A) {
405
+ argument = code[++opOffset] | code[++opOffset] << 8;
406
+ argument <<= 16;
407
+ }
408
+ }
409
+
410
+ return [argument, opOffset];
411
+ }
412
+
413
+ SetupByteCode(co)
414
+ {
415
+ if (!co || !co.Code || !co.Code.Value) {
416
+ return;
417
+ }
418
+
419
+ this.CodeObject = co;
420
+ let opOffset = 0;
421
+ let opCodeID = 0;
422
+ let extendedArg = 0;
423
+ let instructionIndex = 0;
424
+ let code = this.CodeObject.Code.Value;
425
+
426
+ while (opOffset < code.length) {
427
+ [extendedArg, opOffset] = this.ReadExtendedArg(code, opOffset);
428
+
429
+ let bytecode = code[opOffset];
430
+ let opcodeEntry = this.OpCodeList[bytecode];
431
+
432
+ // Skip unknown/invalid opcodes
433
+ if (!opcodeEntry) {
434
+ if (global.g_cliArgs?.debug) {
435
+ console.error(`SetupByteCode: unknown opcode ${bytecode} (0x${bytecode?.toString(16)}) at offset ${opOffset}`);
436
+ }
437
+ opOffset++; // Skip this byte and continue
438
+ continue;
439
+ }
440
+
441
+ let opCode = opcodeEntry.Clone();
442
+ opCode.Offset = opOffset++;
443
+ opCode.CodeBlock = this;
444
+ opCode.InstructionIndex = instructionIndex++;
445
+
446
+ // Set instruction size based on Python version
447
+ if (this.CodeObject.Reader.versionCompare(3, 6) >= 0) {
448
+ opCode.Size = 2; // Python 3.6+ uses 2-byte word-aligned instructions
449
+ opCode.Argument = extendedArg | code[opOffset++];
450
+ } else if (opCode.HasArgument) {
451
+ opCode.Size = 3; // Python < 3.6 with argument: opcode + 2 arg bytes
452
+ opCode.Argument = extendedArg | code[opOffset++] | code[opOffset++] << 8;
453
+ } else {
454
+ opCode.Size = 1; // Python < 3.6 without argument: just opcode
455
+ }
456
+
457
+ if (opCode.HasArgument) {
458
+ if (opCode.OpCodeID == OpCodes.SET_LINENO_A) {
459
+ this.CodeObject.CachedLineNo = opCode.Argument;
460
+ } else {
461
+ opCode.LineNo = this.CodeObject.getLineNumber(opCode.Offset);
462
+ }
463
+
464
+ if (opCode.HasConstant) {
465
+ let val = this.CodeObject.Consts.Value[opCode.Argument];
466
+ if (global.g_cliArgs?.debug && opCode.Offset < 20) {
467
+ console.log(`[SetupByteCode] offset=${opCode.Offset}, arg=${opCode.Argument}, Consts.length=${this.CodeObject.Consts?.Value?.length}, val=${val?.ClassName || 'null'}`);
468
+ }
469
+ if (val) {
470
+ opCode.ConstantObject = val;
471
+ switch(val.ClassName) {
472
+ case "Py_CodeObject":
473
+ opCode.Constant = opCode.Argument;
474
+ break;
475
+ case "Py_Unicode":
476
+ case "Py_String":
477
+ let strVal = val.toString();
478
+ if (!["\"", "\'"].includes(strVal[0])) {
479
+ strVal = strVal.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\n", "\\n");
480
+ if (strVal.indexOf("'")) {
481
+ opCode.Constant = `"${strVal}"`;
482
+ } else {
483
+ opCode.Constant = `'${strVal}'`;
484
+ }
485
+ } else {
486
+ opCode.Constant = strVal;
487
+ }
488
+ break;
489
+ default:
490
+ opCode.Constant = val.toString();
491
+ }
492
+ } else {
493
+ // Malformed bytecode: oparg beyond consts. Keep placeholder instead of crashing.
494
+ opCode.Constant = `##CONST_${opCode.Argument}##`;
495
+ }
496
+ } else if (opCode.HasName) {
497
+ let nameIndex = opCode.Argument;
498
+
499
+ // Python 3.11+ LOAD_GLOBAL uses oparg>>1 as name index
500
+ if (opCode.InstructionName === 'LOAD_GLOBAL' &&
501
+ this.CodeObject.Reader.versionCompare(3, 11) >= 0) {
502
+ nameIndex = opCode.Argument >> 1;
503
+ } else if (['LOAD_ATTR', 'LOAD_METHOD'].includes(opCode.InstructionName) &&
504
+ this.CodeObject.Reader.versionCompare(3, 12) >= 0) {
505
+ nameIndex = opCode.Argument >> 1;
506
+ }
507
+
508
+ if (nameIndex < this.CodeObject.Names.Value.length) {
509
+ opCode.Name = this.CodeObject.Names.Value[nameIndex].toString();
510
+ } else {
511
+ if (global.g_cliArgs?.debug) {
512
+ console.error(`HasName: opCode.Argument ${opCode.Argument} out of bounds (Names.length=${this.CodeObject.Names.Value.length})`);
513
+ }
514
+ opCode.Name = `##NAME_${opCode.Argument}##`;
515
+ }
516
+ } else if (opCode.HasCompare) {
517
+ opCode.CompareOperator = OpCodes.CompareOpNames[opCode.Argument];
518
+ } else if (opCode.HasLocal) {
519
+ if (opCode.Argument < this.CodeObject.VarNames.Value.length) {
520
+ opCode.Name = this.CodeObject.VarNames.Value[opCode.Argument].toString();
521
+ } else if (opCode.Argument < this.CodeObject.Names.Value.length) {
522
+ opCode.Name = this.CodeObject.Names.Value[opCode.Argument].toString();
523
+ }
524
+ } else if (opCode.HasFree) {
525
+ if (opCode.Argument < this.CodeObject.CellVars.Value.length) {
526
+ opCode.FreeName = this.CodeObject.CellVars.Value[opCode.Argument].toString();
527
+ } else if ((opCode.Argument - this.CodeObject.CellVars.Value.length) < this.CodeObject.FreeVars.Value.length) {
528
+ opCode.FreeName = this.CodeObject.FreeVars.Value[opCode.Argument - this.CodeObject.CellVars.Value.length].toString();
529
+ } else {
530
+ opCode.FreeName = `##FREEVAR_${opCode.Argument}##`;
531
+ }
532
+ }
533
+ }
534
+ this.Instructions.push(opCode);
535
+ }
536
+ }
537
+
538
+ CanGoNext(offset = 1) {
539
+ if (this.CurrentInstructionIndex + offset >= 0 && this.CurrentInstructionIndex + offset < this.Instructions.length) {
540
+ return true;
541
+ }
542
+ return false;
543
+ }
544
+
545
+ GoNext(offset = 1) {
546
+ if (this.CanGoNext(offset)) {
547
+ this.CurrentInstructionIndex += offset;
548
+ return true;
549
+ }
550
+ return false;
551
+ }
552
+
553
+ GoToOffset(offset) {
554
+ let instructionIndex = this.GetIndexByOffset(offset);
555
+ if (instructionIndex < 0) {
556
+ return false;
557
+ }
558
+ this.CurrentInstructionIndex = instructionIndex;
559
+ return true;
560
+ }
561
+
562
+ MoveBack() {
563
+ this.GoNext(-1);
564
+ return "";
565
+ }
566
+
567
+ GetNextInstruction(offset = 1) {
568
+ if (!this.GoNext(offset)) {
569
+ return null;
570
+ }
571
+
572
+ return this.Instructions[this.CurrentInstructionIndex];
573
+ }
574
+
575
+ //
576
+ // Look behind and look ahead functions
577
+ //
578
+ get Prev() {
579
+ if (this.CanGoNext(-1)) {
580
+ return this.Instructions[this.CurrentInstructionIndex - 1];
581
+ }
582
+ return null;
583
+ }
584
+
585
+ get Next()
586
+ {
587
+ return this.PeekNextInstruction();
588
+ }
589
+
590
+ PeekNextInstruction(position = 1)
591
+ {
592
+ if (this.CurrentInstructionIndex + position >= this.Instructions.length) {
593
+ return null;
594
+ }
595
+
596
+ return this.Instructions[this.CurrentInstructionIndex + position];
597
+ }
598
+
599
+ PeekInstructionAt(position) {
600
+ if (position >= this.Instructions.length || position < 0) {
601
+ return null;
602
+ }
603
+
604
+ return this.Instructions[position];
605
+ }
606
+
607
+ PeekInstructionAtOffset(offset) {
608
+ let startPos = 0;
609
+ if (offset > this.Instructions[this.CurrentInstructionIndex].Offset) {
610
+ startPos = this.CurrentInstructionIndex + 1;
611
+ }
612
+
613
+ for (let position = startPos; position < this.Instructions.length; position++) {
614
+ if (this.Instructions[position].Offset == offset) {
615
+ return this.Instructions[position];
616
+ }
617
+ }
618
+
619
+ return null;
620
+ }
621
+
622
+ PeekInstructionBeforeOffset(offset, backOffset = 1) {
623
+ offset = this.GetIndexByOffset(offset);
624
+
625
+ return this.PeekInstructionAt(offset - backOffset);
626
+ }
627
+
628
+ get LastOffset() {
629
+ return this.Instructions[this.Instructions.length - 1].Offset;
630
+ }
631
+
632
+ GetIndexByOffset(offset) {
633
+ // TODO: Refactor this code to use binary search
634
+ if (offset < 0) {
635
+ return -1;
636
+ }
637
+ for (let position = 0; position < this.Instructions.length; position++) {
638
+ if (this.Instructions[position].Offset == offset) {
639
+ return position;
640
+ }
641
+ }
642
+
643
+ return -1;
644
+ }
645
+
646
+ GetIndexByOpCode(opCodeID) {
647
+ for (let position = this.CurrentInstructionIndex + 1; position < this.Instructions.length; position++) {
648
+ if (this.Instructions[position].OpCodeID == opCodeID)
649
+ return position;
650
+ }
651
+
652
+ return -1;
653
+ }
654
+
655
+ GetOpCodeByID(opCodeID, fromOffset = -1, toOffset = -1) {
656
+ let startPosition = fromOffset < 0 ? this.CurrentInstructionIndex + 1 : this.GetIndexByOffset(fromOffset);
657
+ let endPosition = toOffset <= 0 ? this.Instructions.length - 1 : this.GetIndexByOffset(toOffset);
658
+ if (startPosition > endPosition) {
659
+ let tempPosition = startPosition;
660
+ startPosition = endPosition;
661
+ endPosition = tempPosition;
662
+ }
663
+ for (let position = startPosition; position <= endPosition; position++) {
664
+ if (this.Instructions[position].OpCodeID == opCodeID)
665
+ return this.Instructions[position];
666
+ }
667
+
668
+ return null;
669
+ }
670
+
671
+ GetOpCodeByName(opCodeName, fromOffset = -1, toOffset = -1) {
672
+ let partialMatch = false;
673
+ let startPosition = fromOffset < 0 ? this.CurrentInstructionIndex + 1 : this.GetIndexByOffset(fromOffset);
674
+ let endPosition = toOffset <= 0 ? this.Instructions.length - 1 : this.GetIndexByOffset(toOffset);
675
+ if (startPosition > endPosition) {
676
+ let tempPosition = startPosition;
677
+ startPosition = endPosition;
678
+ endPosition = tempPosition;
679
+ }
680
+
681
+ if (opCodeName.endsWith("*")) {
682
+ partialMatch = true;
683
+ opCodeName = opCodeName.substring(0, opCodeName.length - 1);
684
+ }
685
+
686
+ for (let position = startPosition; position <= endPosition; position++) {
687
+ if (partialMatch) {
688
+ if (this.Instructions[position].InstructionName.startsWith(opCodeName)) {
689
+ return this.Instructions[position];
690
+ }
691
+ } else if (this.Instructions[position].InstructionName == opCodeName) {
692
+ return this.Instructions[position];
693
+ }
694
+ }
695
+
696
+ return null;
697
+ }
698
+
699
+
700
+ GetOffsetByOpCode(opCodeID, fromOffset = -1, toOffset = -1) {
701
+ let startPosition = fromOffset < 0 ? this.CurrentInstructionIndex + 1 : this.GetIndexByOffset(fromOffset);
702
+ let endPosition = toOffset <= 0 ? this.Instructions.length - 1 : this.GetIndexByOffset(toOffset);
703
+ if (startPosition > endPosition) {
704
+ let tempPosition = startPosition;
705
+ startPosition = endPosition;
706
+ endPosition = tempPosition;
707
+ }
708
+ for (let position = startPosition; position <= endPosition; position++) {
709
+ if (this.Instructions[position].OpCodeID == opCodeID)
710
+ return this.Instructions[position].Offset;
711
+ }
712
+
713
+ return -1;
714
+ }
715
+
716
+ GetReversedOffsetByOpCode(opCodeID, startOffset = -1, endOffset = -1) {
717
+ let startPosition = this.CurrentInstructionIndex - 1;
718
+ let endPosition = 0;
719
+
720
+ if (startOffset > 0) {
721
+ startPosition = this.GetIndexByOffset(startOffset);
722
+ }
723
+
724
+ if (startPosition < 0 && startPosition >= this.Instructions.length) {
725
+ return -1;
726
+ }
727
+
728
+ if (endOffset > 0) {
729
+ endPosition = this.GetIndexByOffset(endOffset);
730
+ }
731
+
732
+ if (endPosition < 0 && endPosition >= this.Instructions.length) {
733
+ return -1;
734
+ }
735
+
736
+ for (let position = startPosition; position >= endPosition; position--) {
737
+ if (this.Instructions[position].OpCodeID == opCodeID)
738
+ return this.Instructions[position].Offset;
739
+ }
740
+
741
+ return -1;
742
+ }
743
+
744
+ GetOffsetByOpCodeName(opName) {
745
+ for (let position = this.CurrentInstructionIndex + 1; position < this.Instructions.length; position++) {
746
+ if (this.Instructions[position].InstructionName.startsWith(opName)) {
747
+ return this.Instructions[position].Offset;
748
+ }
749
+ }
750
+
751
+ return -1;
752
+ }
753
+
754
+ GetBackOffsetByOpCodeName(opName) {
755
+ for (let position = this.CurrentInstructionIndex - 1; position >= 0; position--) {
756
+ if (this.Instructions[position].InstructionName.startsWith(opName)) {
757
+ return this.Instructions[position].Offset;
758
+ }
759
+ }
760
+
761
+ return -1;
762
+ }
763
+
764
+ GetLineOffsetRangeForOffset(offset) {
765
+ let startOffset = this.GetIndexByOffset(offset);
766
+ let endOffset = startOffset;
767
+ let line = this.CodeObject.LineNoTab[offset];
768
+
769
+ while (startOffset > 0) {
770
+ startOffset--;
771
+ if (this.CodeObject.LineNoTab[this.Instructions[startOffset].Offset] != line) {
772
+ startOffset++;
773
+ break;
774
+ }
775
+ }
776
+
777
+ while (endOffset < this.Instructions.length - 1) {
778
+ endOffset++;
779
+ if (this.CodeObject.LineNoTab[this.Instructions[endOffset].Offset] != line) {
780
+ endOffset--;
781
+ break;
782
+ }
783
+ }
784
+
785
+ startOffset = this.Instructions[startOffset].Offset;
786
+ endOffset = this.Instructions[endOffset].Offset;
787
+
788
+ return [line, startOffset, endOffset];
789
+ }
790
+
791
+ CountSpecificOpCodes(opCodes, offset = -1, endOffset = -1) {
792
+ let startPos = offset == -1 ? 0 : this.GetIndexByOffset(offset);
793
+ let endPos = endOffset == -1 ? this.Instructions.length : this.GetIndexByOffset(endOffset);
794
+ let count = 0;
795
+
796
+
797
+ if (!Array.isArray(opCodes)) {
798
+ opCodes = [opCodes];
799
+ }
800
+
801
+ for (let position = startPos; position < endPos; position++) {
802
+ if (opCodes.includes(this.Instructions[position].OpCodeID)) {
803
+ count++;
804
+ }
805
+ }
806
+
807
+ return count;
808
+ }
809
+
810
+ CheckIfOpCodesExistsInLine(opCodes, startOffset, endOffset) {
811
+ let startPosition = this.GetIndexByOffset(startOffset);
812
+ let endPosition = this.GetIndexByOffset(endOffset);
813
+ let mapExists = {};
814
+
815
+ for (let opCode of opCodes) {
816
+ mapExists[opCode] = false;
817
+ }
818
+
819
+ for (let position = startPosition; position <= endPosition; position++) {
820
+ if (opCodes.includes(this.Instructions[position].OpCodeID)) {
821
+ mapExists[this.Instructions[position].OpCodeID] = true;
822
+ }
823
+ }
824
+
825
+ for (let item of Object.values(mapExists)) {
826
+ if (item == false) {
827
+ return false;
828
+ }
829
+ }
830
+ return true;
831
+ }
832
+
833
+ DOMINATORS = [OpCodes.SETUP_LOOP_A, OpCodes.SETUP_EXCEPT_A, OpCodes.SETUP_FINALLY_A, OpCodes.SETUP_WITH_A, OpCodes.FOR_ITER_A];
834
+
835
+ FindEndOfBlock(originalOffset, currentOffset = -1){
836
+ currentOffset = currentOffset == -1 ? this.Instructions[this.CurrentInstructionIndex].Offset : currentOffset;
837
+
838
+ if (global.g_cliArgs?.debug) {
839
+ console.log(`FindEndOfBlock(originalOffset=${originalOffset}, currentOffset=${currentOffset})`);
840
+ }
841
+
842
+ // NEW LOGIC: For backward jumps (loop/branch returns), scan forward to find
843
+ // the actual end of the block by looking for JUMP instructions
844
+ if (originalOffset < currentOffset) {
845
+ // Try to find the end by scanning forward for JUMP instructions
846
+ // The block ends where we find a JUMP to a DIFFERENT target
847
+ let lastMatchingJump = -1;
848
+ let searchLimit = 50; // reasonable limit to avoid infinite loops
849
+
850
+ // Start scanning from current instruction index forward
851
+ let startIdx = this.CurrentInstructionIndex;
852
+ for (let i = 0; i < searchLimit; i++) {
853
+ let instr = this.PeekInstructionAt(startIdx + i);
854
+ if (!instr) break;
855
+
856
+ // Found unconditional JUMP
857
+ if (instr.InstructionName === 'JUMP_ABSOLUTE' || instr.InstructionName === 'JUMP_FORWARD') {
858
+ let jumpTarget = instr.JumpTarget;
859
+
860
+ if (global.g_cliArgs?.debug) {
861
+ console.log(` Checking JUMP at offset ${instr.Offset}: target=${jumpTarget} vs originalOffset=${originalOffset}`);
862
+ }
863
+
864
+ // If JUMP target matches originalOffset, this is a loop-back jump
865
+ // Mark it as potential end, but keep scanning
866
+ if (jumpTarget === originalOffset) {
867
+ lastMatchingJump = instr.Offset;
868
+ if (global.g_cliArgs?.debug) {
869
+ console.log(` → Matching jump found at ${instr.Offset}`);
870
+ }
871
+ } else if (lastMatchingJump > 0) {
872
+ // Found a JUMP with DIFFERENT target after a matching jump
873
+ // This means the matching jump was the end of the block
874
+ if (global.g_cliArgs?.debug) {
875
+ console.log(` → Different target! Block ends at ${lastMatchingJump}`);
876
+ }
877
+ // Return the offset AFTER the matching jump as block end
878
+ return [lastMatchingJump + 3, null];
879
+ } else {
880
+ // First JUMP has different target - this might be the block end
881
+ if (global.g_cliArgs?.debug) {
882
+ console.log(` → First JUMP has different target, block might end at ${instr.Offset}`);
883
+ }
884
+ return [instr.Offset, null];
885
+ }
886
+ }
887
+ }
888
+
889
+ // If we found matching jumps but no different target, use last matching jump
890
+ if (lastMatchingJump > 0) {
891
+ if (global.g_cliArgs?.debug) {
892
+ console.log(` Using last matching jump at ${lastMatchingJump + 3} as block end`);
893
+ }
894
+ return [lastMatchingJump + 3, null];
895
+ }
896
+
897
+ // FALLBACK: Old dominator-based logic
898
+ let dominatorOp = this.PeekInstructionAtOffset(originalOffset);
899
+ if (this.DOMINATORS.includes(dominatorOp?.OpCodeID)) {
900
+ if (global.g_cliArgs?.debug) {
901
+ console.log(` Fallback: Found dominator at ${originalOffset}: ${dominatorOp.InstructionName} → JumpTarget=${dominatorOp.JumpTarget}`);
902
+ }
903
+ return [dominatorOp.JumpTarget, dominatorOp];
904
+ }
905
+ dominatorOp = this.PeekInstructionAtOffset(originalOffset - 3);
906
+ if (this.DOMINATORS.includes(dominatorOp?.OpCodeID)) {
907
+ if (global.g_cliArgs?.debug) {
908
+ console.log(` Fallback: Found dominator at ${originalOffset - 3}: ${dominatorOp.InstructionName} → JumpTarget=${dominatorOp.JumpTarget}`);
909
+ }
910
+ return [dominatorOp.JumpTarget, dominatorOp];
911
+ }
912
+ }
913
+
914
+ if (global.g_cliArgs?.debug) {
915
+ console.log(` No dominator found, returning originalOffset=${originalOffset}`);
916
+ }
917
+ return [originalOffset, null];
918
+ }
919
+
920
+ extractImportNames(fromlist, callback) {
921
+ let count = fromlist.Value.length;
922
+ let partIdx = 0;
923
+ for (let idx = this.CurrentInstructionIndex + 1; idx < this.Instructions.length; idx++) {
924
+ let opCode = this.Instructions[idx];
925
+
926
+ if (opCode.OpCodeID == OpCodes.POP_TOP) {
927
+ this.CurrentInstructionIndex = idx;
928
+ if (partIdx != count) {
929
+ console.log(`WARNING: `);
930
+ }
931
+ return;
932
+ } else if (opCode.InstructionName.startsWith("STORE_")) {
933
+ callback(fromlist.Value[partIdx].toString(), opCode.Label);
934
+ partIdx++;
935
+ }
936
+ }
937
+ }
938
+ }
939
+
940
+ module.exports = OpCodes;