summarylogtreecommitdiffstats
path: root/0007-js2py-xxxxx-python3.13.patch
blob: bc59f7f21ccfebb00800ce81b339c78872edc28f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
diff --git a/js2py/utils/injector.py b/js2py/utils/injector.py
index 835229f0..2953c639 100644
--- a/js2py/utils/injector.py
+++ b/js2py/utils/injector.py
@@ -16,6 +16,10 @@
 LOAD_GLOBAL = opcode.opmap['LOAD_GLOBAL']
 LOAD_ATTR = opcode.opmap['LOAD_ATTR']
 STORE_FAST = opcode.opmap['STORE_FAST']
+LOAD_FAST_LOAD_FAST = opcode.opmap.get('LOAD_FAST_LOAD_FAST')
+STORE_FAST_LOAD_FAST = opcode.opmap.get('STORE_FAST_LOAD_FAST')
+STORE_FAST_STORE_FAST = opcode.opmap.get('STORE_FAST_STORE_FAST')
+EXTENDED_ARG = opcode.opmap.get('EXTENDED_ARG')
 
 
 def fix_js_args(func):
@@ -34,6 +38,40 @@ def fix_js_args(func):
         closure=six.get_function_closure(func))
     return result
 
+def get_list_of_var_indices_in_compound_opcodes(code_obj, non_arg_varnames, arg_count): #E.g. STORE_FAST_LOAD_FAST
+    indices = set()
+    for inst in instructions(code_obj):
+        extended = False
+        if inst.opcode == EXTENDED_ARG:
+            extended = True
+            continue
+        if not extended and inst.opcode in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST):
+            first_index = (inst.arg >> 4 & 0xF) - arg_count
+            second_index = (inst.arg & 0xF) - arg_count
+            if first_index >= 0:
+                indices.add(first_index)
+            if second_index >= 0:
+                indices.add(second_index)
+        extended = False
+    return indices
+
+def rearrange_by_indices(strings, indices):
+    """
+    Rearranges the strings in the list so that the elements corresponding
+    to the indices are at the beginning of the list.
+
+    Args:
+        strings (list): The list of strings.
+        indices (list): A list of indices into the strings list.
+
+    Returns:
+        list: A new list with rearranged elements.
+    """
+    # Extract elements based on indices
+    prioritized = [strings[i] for i in indices if 0 <= i < len(strings)]
+    # Include elements not in indices
+    remaining = [strings[i] for i in range(len(strings)) if i not in indices]
+    return prioritized + remaining
 
 def append_arguments(code_obj, new_locals):
     co_varnames = code_obj.co_varnames  # Old locals
@@ -47,9 +85,9 @@ def append_arguments(code_obj, new_locals):
     # left in code_obj.co_names.
     not_removed = set(opcode.hasname) - set([LOAD_GLOBAL])
     saved_names = set()
-    for inst in instructions(code_obj):
-        if inst[0] in not_removed:
-            saved_names.add(co_names[inst[1]])
+    # for inst in instructions(code_obj):
+    #     if inst.opcode in not_removed:
+    #         saved_names.add(co_names[inst.oparg])
 
     # Build co_names for the new code object. This should consist of
     # globals that were only accessed via LOAD_GLOBAL
@@ -61,18 +99,21 @@ def append_arguments(code_obj, new_locals):
     name_translations = dict(
         (co_names.index(name), i) for i, name in enumerate(names))
 
+    non_arg_varnames = co_varnames[co_argcount:] # All varibles after the arguments
+    early_indices = get_list_of_var_indices_in_compound_opcodes(code_obj, non_arg_varnames, co_argcount)
+    rearranged_non_arg_varnames = rearrange_by_indices(non_arg_varnames, early_indices)
     # Build co_varnames for the new code object. This should consist of
     # the entirety of co_varnames with new_locals spliced in after the
     # arguments
     new_locals_len = len(new_locals)
     varnames = (
-        co_varnames[:co_argcount] + new_locals + co_varnames[co_argcount:])
+        co_varnames[:co_argcount] + new_locals + tuple(rearranged_non_arg_varnames))
 
     # Build the dictionary that maps indices of entries in the old co_varnames
     # to their indices in the new co_varnames
     range1, range2 = xrange(co_argcount), xrange(co_argcount, len(co_varnames))
     varname_translations = dict((i, i) for i in range1)
-    varname_translations.update((i, i + new_locals_len) for i in range2)
+    varname_translations.update((i, varnames.index(co_varnames[i])) for i in range2)
 
     # Build the dictionary that maps indices of deleted entries of co_names
     # to their indices in the new co_varnames
@@ -85,6 +126,7 @@ def append_arguments(code_obj, new_locals):
     modified = []
     drop_future_cache = False
     for inst in instructions(code_obj):
+        # print(inst.opname + ' - ' + str(inst.line_number))
         if is_new_bytecode and inst.opname == "CACHE":
             assert inst.arg == 0
             if not drop_future_cache:
@@ -115,9 +157,18 @@ def append_arguments(code_obj, new_locals):
                 arg = tgt
             else:
                 raise(ValueError("a name was lost in translation last instruction %s" % str(inst)))
+        elif inst.opcode in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST):
+            old_first_arg = inst.arg >> 4 & 0xF
+            old_second_arg = inst.arg & 0xF
+            new_first_arg = varname_translations[old_first_arg]
+            new_second_arg = varname_translations[old_second_arg]
+            arg = new_first_arg << 4 | new_second_arg
         # If it accesses co_varnames or co_names then update its argument.
         elif inst.opcode in opcode.haslocal:
-            arg = varname_translations[inst.arg]
+            if arg < len(varname_translations): # Otherwise cellvar whose arg gets incremented by new_locals_len
+                arg = varname_translations[inst.arg]
+            else:
+                arg += new_locals_len
         elif inst.opcode in opcode.hasname:
             # for example STORE_GLOBAL
             arg = name_translations[inst.arg]
@@ -126,6 +177,12 @@ def append_arguments(code_obj, new_locals):
             if inst.argval not in code_obj.co_varnames[:code_obj.co_argcount]:  # we do not need to remap existing arguments, they are not shifted by new ones.
                 arg = inst.arg + len(new_locals)
         modified.extend(write_instruction(op, arg))
+        if hasattr(inst, 'end_offset'): # Assume otherwise instructions will have explicit CACHE entries
+            for inline_cache in range(int((inst.end_offset - inst.cache_offset) / 2)):
+                if not drop_future_cache:
+                    modified.extend(write_instruction(0, 0))
+                else:
+                    modified.extend(write_instruction(dis.opmap["NOP"], 0))
     if six.PY2:
         code = ''.join(modified)
         args = (co_argcount + new_locals_len,
@@ -231,9 +288,16 @@ def check(code_obj):
     pos_to_inst = {}
     bytelist = []
 
+    offset = 0
+    instruction_bytes = None
     for inst in insts:
         pos_to_inst[len(bytelist)] = inst
-        bytelist.extend(write_instruction(inst.opcode, inst.arg))
+        if instruction_bytes:
+            for gap_byte in range(inst.offset - offset - len(instruction_bytes)):
+                bytelist.append(0)
+        offset = inst.offset
+        instruction_bytes = write_instruction(inst.opcode, inst.arg)
+        bytelist.extend(instruction_bytes)
     if six.PY2:
         new_bytecode = ''.join(bytelist)
     else: