Skip to content

Commit b3bc2d7

Browse files
authored
Merge pull request #177 from jdw170000/python-3.14
END_ASYNC_FOR Python 3.14 backwards jump formatting
2 parents 004d144 + 149ae38 commit b3bc2d7

File tree

1 file changed

+29
-61
lines changed

1 file changed

+29
-61
lines changed

xdis/bytecode.py

Lines changed: 29 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,12 @@ def get_const_info(const_index, const_list):
7070
if const_list is not None:
7171
arg_val = const_list[const_index]
7272

73-
arg_repr = (
74-
prefer_double_quote(repr(arg_val))
75-
if isinstance(arg_val, str)
76-
else repr(arg_val)
77-
)
73+
arg_repr = prefer_double_quote(repr(arg_val)) if isinstance(arg_val, str) else repr(arg_val)
7874

7975
# Float values "nan" and "inf" are not directly representable in Python at least
8076
# before 3.5 and even there it is via a library constant.
8177
# So we will canonicalize their representation as float('nan') and float('inf')
82-
if isinstance(arg_val, float) and str(arg_val) in frozenset(
83-
["nan", "-nan", "inf", "-inf"]
84-
):
78+
if isinstance(arg_val, float) and str(arg_val) in frozenset(["nan", "-nan", "inf", "-inf"]):
8579
return arg_val, f"float('{arg_val}')"
8680
return arg_val, arg_repr
8781

@@ -140,6 +134,7 @@ def get_optype(opcode: int, opc) -> str:
140134

141135
return "??"
142136

137+
143138
def offset2line(offset: int, linestarts):
144139
"""linestarts is expected to be a *list of (offset, line number)
145140
where both offset and line number are in increasing order.
@@ -177,9 +172,7 @@ def _parse_varint(iterator: Iterator[int]) -> int:
177172
return val
178173

179174

180-
_ExceptionTableEntry = collections.namedtuple(
181-
"_ExceptionTableEntry", "start end target depth lasti"
182-
)
175+
_ExceptionTableEntry = collections.namedtuple("_ExceptionTableEntry", "start end target depth lasti")
183176

184177

185178
def parse_exception_table(exception_table: bytes) -> list:
@@ -215,6 +208,7 @@ def prefer_double_quote(string: str) -> str:
215208
return f'"{string[1:-1]}"'
216209
return string
217210

211+
218212
def is_fixed_wordsize_bytecode(opc) -> bool:
219213
"""
220214
Returns True if intructions in opc are fixed length (2 bytes)
@@ -225,6 +219,7 @@ def is_fixed_wordsize_bytecode(opc) -> bool:
225219
# See below FIXME.
226220
return True if opc.python_version >= (3, 6) else False
227221

222+
228223
def get_logical_instruction_at_offset(
229224
bytecode,
230225
offset: int,
@@ -277,9 +272,7 @@ def get_logical_instruction_at_offset(
277272
i = offset
278273

279274
# create a localsplusnames table that resolves duplicates.
280-
localsplusnames = (varnames or tuple()) + tuple(
281-
name for name in (cells or tuple()) if name not in varnames
282-
)
275+
localsplusnames = (varnames or tuple()) + tuple(name for name in (cells or tuple()) if name not in varnames)
283276

284277
while i < n and last_op_was_extended_arg:
285278
op = code2num(bytecode, i)
@@ -306,11 +299,7 @@ def get_logical_instruction_at_offset(
306299
# FIXME: Python 3.6.0a1 is 2, for 3.6.a3 we have 1
307300
i += 1
308301
else:
309-
arg = (
310-
code2num(bytecode, i)
311-
+ code2num(bytecode, i + 1) * 0x100
312-
+ extended_arg
313-
)
302+
arg = code2num(bytecode, i) + code2num(bytecode, i + 1) * 0x100 + extended_arg
314303
i += 2
315304
extended_arg = arg * 0x10000 if opname == "EXTENDED_ARG" else 0
316305

@@ -339,7 +328,12 @@ def get_logical_instruction_at_offset(
339328
else:
340329
argval, argrepr = get_name_info(arg, names)
341330
elif op in opc.JREL_OPS:
342-
signed_arg = -arg if "JUMP_BACKWARD" in opname else arg
331+
signed_arg = arg
332+
if "JUMP_BACKWARD" in opname:
333+
signed_arg = -arg
334+
elif opc.version_tuple >= (3, 14) and "END_ASYNC_FOR" in opname:
335+
signed_arg = -arg
336+
343337
argval = i + get_jump_val(signed_arg, opc.python_version)
344338

345339
# check cache instructions for python 3.13
@@ -357,6 +351,9 @@ def get_logical_instruction_at_offset(
357351
if opc.version_tuple >= (3, 12) and opname == "FOR_ITER":
358352
argval += 2
359353
argrepr = "to " + repr(argval)
354+
if opc.version_tuple >= (3, 14) and "END_ASYNC_FOR" in opname:
355+
argrepr = "from " + repr(argval)
356+
360357
elif op in opc.JABS_OPS:
361358
argval = get_jump_val(arg, opc.python_version)
362359
argrepr = "to " + repr(argval)
@@ -403,10 +400,7 @@ def get_logical_instruction_at_offset(
403400
assert opname == "CALL_FUNCTION_EX"
404401
argrepr = format_CALL_FUNCTION_EX(code2num(bytecode, i - 1))
405402
else:
406-
if not (
407-
fixed_length_instructions
408-
or opname in ("RAISE_VARARGS", "DUP_TOPX", "MAKE_FUNCTION")
409-
):
403+
if not (fixed_length_instructions or opname in ("RAISE_VARARGS", "DUP_TOPX", "MAKE_FUNCTION")):
410404
argrepr = "%d positional, %d named" % (
411405
code2num(bytecode, i - 2),
412406
code2num(bytecode, i - 1),
@@ -527,9 +521,7 @@ class Bytecode:
527521
Iterating over these yields the bytecode operations as Instruction instances.
528522
"""
529523

530-
def __init__(
531-
self, x, opc, first_line=None, current_offset=None, dup_lines: bool = True
532-
) -> None:
524+
def __init__(self, x, opc, first_line=None, current_offset=None, dup_lines: bool = True) -> None:
533525
self.codeobj = co = get_code_object(x)
534526
self._line_offset = 0
535527
self._cell_names = tuple()
@@ -550,11 +542,7 @@ def __init__(
550542
self.opnames = opc.opname
551543
self.current_offset = current_offset
552544

553-
if (
554-
opc.version_tuple >= (3, 11)
555-
and not opc.is_pypy
556-
and hasattr(co, "co_exceptiontable")
557-
):
545+
if opc.version_tuple >= (3, 11) and not opc.is_pypy and hasattr(co, "co_exceptiontable"):
558546
self.exception_entries = parse_exception_table(co.co_exceptiontable)
559547
else:
560548
self.exception_entries = None
@@ -573,9 +561,7 @@ def from_traceback(cls, tb, opc=None):
573561
opc = get_opcode_module(sys.version_info, PYTHON_IMPLEMENTATION)
574562
while tb.tb_next:
575563
tb = tb.tb_next
576-
return cls(
577-
tb.tb_frame.f_code, opc=opc, first_line=None, current_offset=tb.tb_lasti
578-
)
564+
return cls(tb.tb_frame.f_code, opc=opc, first_line=None, current_offset=tb.tb_lasti)
579565

580566
def info(self) -> str:
581567
"""Return formatted information about the code object."""
@@ -661,9 +647,7 @@ def show_source_text(line_number: Optional[int]) -> None:
661647
if show_source and filename and line_number:
662648
source_text = getline(filename, line_number).lstrip()
663649
if source_text.startswith('"""'):
664-
source_text = get_docstring(
665-
filename, line_number + 1, source_text.rstrip()
666-
)
650+
source_text = get_docstring(filename, line_number + 1, source_text.rstrip())
667651
if source_text:
668652
file.write(" " * 13 + "# " + source_text)
669653

@@ -681,6 +665,7 @@ def show_source_text(line_number: Optional[int]) -> None:
681665

682666
if self.opc.python_implementation == PythonImplementation.Graal:
683667
from xdis.bytecode_graal import get_instructions_bytes_graal
668+
684669
get_instructions_fn = get_instructions_bytes_graal
685670
else:
686671
get_instructions_fn = get_instructions_bytes
@@ -758,18 +743,10 @@ def show_source_text(line_number: Optional[int]) -> None:
758743
extended_arg_jump_target_offset = None
759744

760745
instructions.append(instr)
761-
new_source_line = show_lineno and (
762-
extended_arg_starts_line
763-
or instr.starts_line is not None
764-
and instr.offset >= 0
765-
)
746+
new_source_line = show_lineno and (extended_arg_starts_line or instr.starts_line is not None and instr.offset >= 0)
766747
if new_source_line:
767748
file.write("\n")
768-
show_source_text(
769-
extended_arg_starts_line
770-
if extended_arg_starts_line
771-
else instr.starts_line
772-
)
749+
show_source_text(extended_arg_starts_line if extended_arg_starts_line else instr.starts_line)
773750

774751
is_current_instr = instr.offset == lasti
775752

@@ -798,10 +775,7 @@ def show_source_text(line_number: Optional[int]) -> None:
798775
# currently we can't track names in this area, but instead use
799776
# locals and hope the two are the same.
800777
if instr.opname == "RESERVE_FAST":
801-
file.write(
802-
"# Warning: subsequent LOAD_FAST and STORE_FAST after RESERVE_FAST "
803-
"are inaccurate here in Python before 1.5\n"
804-
)
778+
file.write("# Warning: subsequent LOAD_FAST and STORE_FAST after RESERVE_FAST are inaccurate here in Python before 1.5\n")
805779
pass
806780
return instructions
807781

@@ -820,9 +794,7 @@ def get_instructions(self, x):
820794
return get_instructions_bytes(co, self.opc)
821795

822796

823-
def list2bytecode(
824-
inst_list: Iterable, opc, varnames: str, consts: Tuple[None, int]
825-
) -> bytes:
797+
def list2bytecode(inst_list: Iterable, opc, varnames: str, consts: Tuple[None, int]) -> bytes:
826798
"""Convert list/tuple of list/tuples to bytecode
827799
_names_ contains a list of name objects
828800
"""
@@ -831,19 +803,15 @@ def list2bytecode(
831803
opname = opcodes[0]
832804
operands = opcodes[1:]
833805
if opname not in opc.opname:
834-
raise TypeError(
835-
"error at item %d [%s, %s], opcode not valid" % (i, opname, operands)
836-
)
806+
raise TypeError("error at item %d [%s, %s], opcode not valid" % (i, opname, operands))
837807
opcode = opc.opmap[opname]
838808
bc.append(opcode)
839809
print(opname, operands)
840810
gen = (j for j in operands if operands)
841811
for j in gen:
842812
k = (consts if opcode in opc.CONST_OPS else varnames).index(j)
843813
if k == -1:
844-
raise TypeError(
845-
f"operand {i} [{opname}, {operands}], not found in names"
846-
)
814+
raise TypeError(f"operand {i} [{opname}, {operands}], not found in names")
847815
else:
848816
bc += num2code(k)
849817
pass

0 commit comments

Comments
 (0)