Skip to content

Commit abca94f

Browse files
authored
Merge pull request #172 from rocky/graal-disassembly
Graal disassembly
2 parents 8d4d89a + 03c93dd commit abca94f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2910
-543
lines changed

xdis/bytecode.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
from xdis.op_imports import get_opcode_module
4141
from xdis.opcodes.opcode_36 import format_CALL_FUNCTION, format_CALL_FUNCTION_EX
4242
from xdis.util import code2num, num2code
43-
from xdis.version_info import PYTHON_IMPLEMENTATION
43+
from xdis.version_info import PYTHON_IMPLEMENTATION, PythonImplementation
4444

4545

4646
def get_docstring(filename: str, line_number: int, doc_str: str) -> str:
@@ -523,7 +523,7 @@ def __init__(
523523
) -> None:
524524
self.codeobj = co = get_code_object(x)
525525
self._line_offset = 0
526-
self._cell_names = ()
526+
self._cell_names = tuple()
527527
if opc.version_tuple >= (1, 5):
528528
if first_line is None:
529529
self.first_line = co.co_firstlineno
@@ -697,7 +697,13 @@ def show_source_text(line_number: Optional[int]) -> None:
697697
extended_arg_starts_line: Optional[int] = None
698698
extended_arg_jump_target_offset: Optional[int] = None
699699

700-
for instr in get_instructions_bytes(
700+
if self.opc.python_implementation == PythonImplementation.Graal:
701+
from xdis.bytecode_graal import get_instructions_bytes_graal
702+
get_instructions_fn = get_instructions_bytes_graal
703+
else:
704+
get_instructions_fn = get_instructions_bytes
705+
706+
for instr in get_instructions_fn(
701707
bytecode,
702708
self.opc,
703709
varnames,

xdis/bytecode_graal.py

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
# from xdis.bytecode import get_optype
2+
from xdis.bytecode import Bytecode
3+
from xdis.cross_dis import get_code_object
4+
from xdis.instruction import Instruction
5+
from xdis.opcodes.base_graal import (
6+
BINARY_OPS,
7+
COLLECTION_KIND,
8+
UNARY_OPS,
9+
get_optype_graal,
10+
)
11+
12+
13+
def get_instructions_bytes_graal(
14+
bytecode,
15+
opc,
16+
varnames,
17+
names,
18+
constants,
19+
cells,
20+
freevars,
21+
line_offset,
22+
exception_entries,
23+
):
24+
"""
25+
Iterate over the instructions in a bytecode string.
26+
27+
Generates a sequence of Instruction namedtuples giving the details of each
28+
opcode. Additional information about the code's runtime environment
29+
e.g., variable names, constants, can be specified using optional
30+
arguments.
31+
"""
32+
# source_map = ???
33+
34+
i = 0
35+
n = len(bytecode)
36+
37+
extended_arg_count = 0
38+
labels = opc.findlabels(bytecode, opc)
39+
40+
while i < n:
41+
opcode = bytecode[i]
42+
opname = opc.opname[opcode]
43+
optype = get_optype_graal(opcode, opc)
44+
offset = i
45+
46+
arg_count = opc.arg_counts[opcode]
47+
is_jump_target = i in labels
48+
49+
# print(
50+
# f"offset: {offset} {hex(opcode)} {opname} arg_count: {arg_count}, optype: {optype}"
51+
# )
52+
53+
i += 1
54+
55+
arg = -1
56+
argval = None
57+
argrepr = ""
58+
59+
following_args = []
60+
has_arg = arg_count == 0
61+
62+
if has_arg:
63+
argrepr = ""
64+
else:
65+
arg = bytecode[i]
66+
i += 1
67+
if arg_count > 1:
68+
following_args = []
69+
for j in range(arg_count - 1):
70+
following_args.append(bytecode[i + j])
71+
i += 1
72+
argrepr = str(arg)
73+
74+
while True:
75+
if opcode == opc.opmap["EXTENDED_ARG"]:
76+
argrepr = ""
77+
break
78+
elif opcode in (opc.opmap["LOAD_BYTE_O"], opc.opmap["LOAD_BYTE_I"]):
79+
argrepr = str(arg)
80+
argval = arg
81+
break
82+
elif optype == "const":
83+
arg = arg
84+
argval = constants[arg]
85+
argrepr = str(constants[arg])
86+
elif opcode == opc.opmap["MAKE_FUNCTION"]:
87+
if following_args:
88+
argrepr = str(following_args[0])
89+
argval = constants[arg]
90+
argrepr = argval.co_name
91+
break
92+
93+
elif opcode in (opc.opmap["LOAD_INT"], opc.opmap["LOAD_LONG"]):
94+
argrepr = Objects.toString(primitiveConstants[arg])
95+
break
96+
elif opcode == opc.opmap["LOAD_DOUBLE"]:
97+
argrepr = Objects.toString(
98+
Double.longBitsToDouble(primitiveConstants[arg])
99+
)
100+
break
101+
elif opcode == opc.opmap["LOAD_COMPLEX"]:
102+
argval = constants[arg]
103+
if num[0] == 0.0:
104+
argrepr = "%g" % argval[1]
105+
else:
106+
argrepr = "%g%+gj" % (argval[0], argval[1])
107+
break
108+
elif opcode in (
109+
opc.opmap["LOAD_CLOSURE"],
110+
opc.opmap["LOAD_DEREF"],
111+
opc.opmap["STORE_DEREF"],
112+
opc.opmap["DELETE_DEREF"],
113+
):
114+
if arg >= len(cells):
115+
argrepr = freevars[arg - len(cells)]
116+
else:
117+
argrepr = cells[arg]
118+
break
119+
elif opcode in (
120+
opc.opmap["LOAD_FAST"],
121+
opc.opmap["STORE_FAST"],
122+
opc.opmap["DELETE_FAST"],
123+
):
124+
argval = argrepr = varnames[arg]
125+
break
126+
127+
elif optype == "name":
128+
argval = arg
129+
argrepr = names[arg]
130+
break
131+
elif opcode == opc.opmap["FORMAT_VALUE"]:
132+
argval = arg
133+
kind = arg & 0x3
134+
if kind ==0x1:
135+
argrepr = "STR"
136+
break
137+
elif kind == 0x2:
138+
argrepr = "REPR"
139+
break
140+
elif kind == 0x3:
141+
argrepr = "ASCII"
142+
break
143+
elif kind == 0:
144+
argrepr = "NONE"
145+
break
146+
147+
if (arg & 0x4) == 0x4:
148+
argrepr += " + SPEC"
149+
break
150+
151+
elif opcode == opc.opmap["CALL_METHOD"]:
152+
argrepr = str(arg)
153+
break
154+
155+
elif opcode == "unary":
156+
argval = arg
157+
argrepr = UNARY_OPS.get(arg, "??")
158+
break
159+
160+
elif optype == "binary":
161+
argval = arg
162+
argrepr = BINARY_OPS.get(arg, "??")
163+
break
164+
165+
elif optype == "collection":
166+
argval = arg
167+
argrepr = COLLECTION_KIND.get(arg, "??")
168+
break
169+
elif opcode == opc.opmap["UNPACK_EX"]:
170+
argrepr = "%d, %d" % (arg, Byte.toUnsignedInt(following_args[0]))
171+
break
172+
elif optype == "jrel":
173+
# fields.computeIfAbsent(offset + arg, k -> new String[DISASSEMBLY_NUM_COLUMNS])[1] = ">>"
174+
arg = arg
175+
if opcode == opc.opmap["JUMP_BACKWARD"]:
176+
argval = offset - arg
177+
else:
178+
argval = offset + arg
179+
argrepr = "to %d" % argval
180+
break
181+
else:
182+
pass
183+
# if opcode.quickens:
184+
# opcode = opcode.quickens
185+
# continue
186+
187+
if opcode == opc.opmap["EXTENDED_ARG"]:
188+
arg <<= 8
189+
else:
190+
arg = 0
191+
break
192+
193+
inst_size = (i - offset + 1) + (extended_arg_count * 2)
194+
start_offset = offset if opc.oppop[opcode] == 0 else None
195+
196+
# for (int i = 0 i < exceptionHandlerRanges.length; i += 4) {
197+
# int start = exceptionHandlerRanges[i];
198+
# int stop = exceptionHandlerRanges[i + 1];
199+
# int handler = exceptionHandlerRanges[i + 2];
200+
# int stackAtHandler = exceptionHandlerRanges[i + 3];
201+
# String[] field = fields.get(handler);
202+
# assert field != null;
203+
# String handlerStr = String.format("exc handler %d - %d; stack: %d", start, stop, stackAtHandler);
204+
# if (field[6] == null) {
205+
# field[6] = handlerStr;
206+
# } else {
207+
# field[6] += " | " + handlerStr;
208+
# }
209+
# }
210+
211+
# for (i = 0; i < bytecode.length; i++) {
212+
# String[] field = fields.get(i);
213+
# if (field != null) {
214+
# field[5] = field[5] == null ? "" : String.format("(%s)", field[5]);
215+
# field[6] = field[6] == null ? "" : String.format("(%s)", field[6]);
216+
# field[7] = "";
217+
# if (outputCanQuicken != null && (outputCanQuicken[i] != 0 || generalizeInputsMap[i] != null)) {
218+
# StringBuilder quickenSb = new StringBuilder();
219+
# if (outputCanQuicken[i] != 0) {
220+
# quickenSb.append("can quicken");
221+
# }
222+
# if (generalizeInputsMap[i] != null) {
223+
# if (quickenSb.length() > 0) {
224+
# quickenSb.append(", ");
225+
# }
226+
# quickenSb.append("generalizes: ");
227+
# for (int j = 0; i < generalizeInputsMap[i].length; i++) {
228+
# if (j > 0) {
229+
# quickenSb.append(", ");
230+
# }
231+
# quickenSb.append(generalizeInputsMap[i][j]);
232+
# }
233+
# }
234+
# field[7] = quickenSb.toString();
235+
# }
236+
# formatted = "%-8s %2s %4s %-32s %-3s %-32s %s %s" % field;
237+
# sb.append(formatted.strip());
238+
# sb.append('\n');
239+
# }
240+
# }
241+
242+
yield Instruction(
243+
is_jump_target=is_jump_target,
244+
starts_line=False, # starts_line,
245+
offset=offset,
246+
opname=opname,
247+
opcode=opcode,
248+
has_arg=has_arg,
249+
arg=arg,
250+
argval=argval,
251+
argrepr=argrepr,
252+
tos_str=None,
253+
positions=None,
254+
optype=optype,
255+
inst_size=inst_size,
256+
has_extended_arg=extended_arg_count != 0,
257+
fallthrough=None,
258+
start_offset=start_offset,
259+
)
260+
261+
262+
class Bytecode_Graal(Bytecode):
263+
"""Bytecode operations involving a Python code object.
264+
265+
Instantiate this with a function, method, string of code, or a code object
266+
(as returned by compile()).
267+
268+
Iterating over these yields the bytecode operations as Instruction instances.
269+
"""
270+
271+
def __init__(self, x, opc, first_line=None, current_offset=None) -> None:
272+
self.codeobj = co = get_code_object(x)
273+
self._line_offset = 0
274+
self._cell_names = tuple()
275+
if first_line is None:
276+
self.first_line = co.co_firstlineno
277+
else:
278+
self.first_line = first_line
279+
self._line_offset = first_line - co.co_firstlineno
280+
pass
281+
282+
# self._linestarts = dict(opc.findlinestarts(co, dup_lines=dup_lines))
283+
self._linestarts = None
284+
self._original_object = x
285+
self.opc = opc
286+
self.opnames = opc.opname
287+
self.current_offset = current_offset
288+
289+
if opc.version_tuple >= (3, 11) and hasattr(co, "co_exceptiontable"):
290+
self.exception_entries = None
291+
# self.exception_entries = parse_exception_table(co.co_exceptiontable)
292+
else:
293+
self.exception_entries = None
294+
295+
def __iter__(self):
296+
co = self.codeobj
297+
return get_instructions_bytes_graal(
298+
co.co_code,
299+
self.opc,
300+
co.co_varnames,
301+
co.co_names,
302+
co.co_consts,
303+
co.co_cellvars,
304+
co.co_freevars,
305+
)
306+
307+
def __repr__(self) -> str:
308+
return f"{self.__class__.__name__}({self._original_object!r})"
309+
310+
def get_instructions(self, x):
311+
"""Iterator for the opcodes in methods, functions or code
312+
313+
Generates a series of Instruction named tuples giving the details of
314+
each operation in the supplied code.
315+
316+
If *first_line* is not None, it indicates the line number that should
317+
be reported for the first source line in the disassembled code.
318+
Otherwise, the source line information (if any) is taken directly from
319+
the disassembled code object.
320+
"""
321+
co = get_code_object(x)
322+
return get_instructions_bytes_graal(
323+
co.co_code,
324+
self.opc,
325+
co.co_varnames,
326+
co.co_names,
327+
co.co_consts,
328+
co.co_cellvars,
329+
co.co_freevars,
330+
)

0 commit comments

Comments
 (0)