diff --git a/xdis/bytecode.py b/xdis/bytecode.py index 3dd7c303..71274bc7 100644 --- a/xdis/bytecode.py +++ b/xdis/bytecode.py @@ -40,7 +40,7 @@ from xdis.op_imports import get_opcode_module from xdis.opcodes.opcode_36 import format_CALL_FUNCTION, format_CALL_FUNCTION_EX from xdis.util import code2num, num2code -from xdis.version_info import PYTHON_IMPLEMENTATION +from xdis.version_info import PYTHON_IMPLEMENTATION, PythonImplementation def get_docstring(filename: str, line_number: int, doc_str: str) -> str: @@ -523,7 +523,7 @@ def __init__( ) -> None: self.codeobj = co = get_code_object(x) self._line_offset = 0 - self._cell_names = () + self._cell_names = tuple() if opc.version_tuple >= (1, 5): if first_line is None: self.first_line = co.co_firstlineno @@ -697,7 +697,13 @@ def show_source_text(line_number: Optional[int]) -> None: extended_arg_starts_line: Optional[int] = None extended_arg_jump_target_offset: Optional[int] = None - for instr in get_instructions_bytes( + if self.opc.python_implementation == PythonImplementation.Graal: + from xdis.bytecode_graal import get_instructions_bytes_graal + get_instructions_fn = get_instructions_bytes_graal + else: + get_instructions_fn = get_instructions_bytes + + for instr in get_instructions_fn( bytecode, self.opc, varnames, diff --git a/xdis/bytecode_graal.py b/xdis/bytecode_graal.py new file mode 100644 index 00000000..ceeb1bf2 --- /dev/null +++ b/xdis/bytecode_graal.py @@ -0,0 +1,330 @@ +# from xdis.bytecode import get_optype +from xdis.bytecode import Bytecode +from xdis.cross_dis import get_code_object +from xdis.instruction import Instruction +from xdis.opcodes.base_graal import ( + BINARY_OPS, + COLLECTION_KIND, + UNARY_OPS, + get_optype_graal, +) + + +def get_instructions_bytes_graal( + bytecode, + opc, + varnames, + names, + constants, + cells, + freevars, + line_offset, + exception_entries, +): + """ + Iterate over the instructions in a bytecode string. + + Generates a sequence of Instruction namedtuples giving the details of each + opcode. Additional information about the code's runtime environment + e.g., variable names, constants, can be specified using optional + arguments. + """ + # source_map = ??? + + i = 0 + n = len(bytecode) + + extended_arg_count = 0 + labels = opc.findlabels(bytecode, opc) + + while i < n: + opcode = bytecode[i] + opname = opc.opname[opcode] + optype = get_optype_graal(opcode, opc) + offset = i + + arg_count = opc.arg_counts[opcode] + is_jump_target = i in labels + + # print( + # f"offset: {offset} {hex(opcode)} {opname} arg_count: {arg_count}, optype: {optype}" + # ) + + i += 1 + + arg = -1 + argval = None + argrepr = "" + + following_args = [] + has_arg = arg_count == 0 + + if has_arg: + argrepr = "" + else: + arg = bytecode[i] + i += 1 + if arg_count > 1: + following_args = [] + for j in range(arg_count - 1): + following_args.append(bytecode[i + j]) + i += 1 + argrepr = str(arg) + + while True: + if opcode == opc.opmap["EXTENDED_ARG"]: + argrepr = "" + break + elif opcode in (opc.opmap["LOAD_BYTE_O"], opc.opmap["LOAD_BYTE_I"]): + argrepr = str(arg) + argval = arg + break + elif optype == "const": + arg = arg + argval = constants[arg] + argrepr = str(constants[arg]) + elif opcode == opc.opmap["MAKE_FUNCTION"]: + if following_args: + argrepr = str(following_args[0]) + argval = constants[arg] + argrepr = argval.co_name + break + + elif opcode in (opc.opmap["LOAD_INT"], opc.opmap["LOAD_LONG"]): + argrepr = Objects.toString(primitiveConstants[arg]) + break + elif opcode == opc.opmap["LOAD_DOUBLE"]: + argrepr = Objects.toString( + Double.longBitsToDouble(primitiveConstants[arg]) + ) + break + elif opcode == opc.opmap["LOAD_COMPLEX"]: + argval = constants[arg] + if num[0] == 0.0: + argrepr = "%g" % argval[1] + else: + argrepr = "%g%+gj" % (argval[0], argval[1]) + break + elif opcode in ( + opc.opmap["LOAD_CLOSURE"], + opc.opmap["LOAD_DEREF"], + opc.opmap["STORE_DEREF"], + opc.opmap["DELETE_DEREF"], + ): + if arg >= len(cells): + argrepr = freevars[arg - len(cells)] + else: + argrepr = cells[arg] + break + elif opcode in ( + opc.opmap["LOAD_FAST"], + opc.opmap["STORE_FAST"], + opc.opmap["DELETE_FAST"], + ): + argval = argrepr = varnames[arg] + break + + elif optype == "name": + argval = arg + argrepr = names[arg] + break + elif opcode == opc.opmap["FORMAT_VALUE"]: + argval = arg + kind = arg & 0x3 + if kind ==0x1: + argrepr = "STR" + break + elif kind == 0x2: + argrepr = "REPR" + break + elif kind == 0x3: + argrepr = "ASCII" + break + elif kind == 0: + argrepr = "NONE" + break + + if (arg & 0x4) == 0x4: + argrepr += " + SPEC" + break + + elif opcode == opc.opmap["CALL_METHOD"]: + argrepr = str(arg) + break + + elif opcode == "unary": + argval = arg + argrepr = UNARY_OPS.get(arg, "??") + break + + elif optype == "binary": + argval = arg + argrepr = BINARY_OPS.get(arg, "??") + break + + elif optype == "collection": + argval = arg + argrepr = COLLECTION_KIND.get(arg, "??") + break + elif opcode == opc.opmap["UNPACK_EX"]: + argrepr = "%d, %d" % (arg, Byte.toUnsignedInt(following_args[0])) + break + elif optype == "jrel": + # fields.computeIfAbsent(offset + arg, k -> new String[DISASSEMBLY_NUM_COLUMNS])[1] = ">>" + arg = arg + if opcode == opc.opmap["JUMP_BACKWARD"]: + argval = offset - arg + else: + argval = offset + arg + argrepr = "to %d" % argval + break + else: + pass + # if opcode.quickens: + # opcode = opcode.quickens + # continue + + if opcode == opc.opmap["EXTENDED_ARG"]: + arg <<= 8 + else: + arg = 0 + break + + inst_size = (i - offset + 1) + (extended_arg_count * 2) + start_offset = offset if opc.oppop[opcode] == 0 else None + + # for (int i = 0 i < exceptionHandlerRanges.length; i += 4) { + # int start = exceptionHandlerRanges[i]; + # int stop = exceptionHandlerRanges[i + 1]; + # int handler = exceptionHandlerRanges[i + 2]; + # int stackAtHandler = exceptionHandlerRanges[i + 3]; + # String[] field = fields.get(handler); + # assert field != null; + # String handlerStr = String.format("exc handler %d - %d; stack: %d", start, stop, stackAtHandler); + # if (field[6] == null) { + # field[6] = handlerStr; + # } else { + # field[6] += " | " + handlerStr; + # } + # } + + # for (i = 0; i < bytecode.length; i++) { + # String[] field = fields.get(i); + # if (field != null) { + # field[5] = field[5] == null ? "" : String.format("(%s)", field[5]); + # field[6] = field[6] == null ? "" : String.format("(%s)", field[6]); + # field[7] = ""; + # if (outputCanQuicken != null && (outputCanQuicken[i] != 0 || generalizeInputsMap[i] != null)) { + # StringBuilder quickenSb = new StringBuilder(); + # if (outputCanQuicken[i] != 0) { + # quickenSb.append("can quicken"); + # } + # if (generalizeInputsMap[i] != null) { + # if (quickenSb.length() > 0) { + # quickenSb.append(", "); + # } + # quickenSb.append("generalizes: "); + # for (int j = 0; i < generalizeInputsMap[i].length; i++) { + # if (j > 0) { + # quickenSb.append(", "); + # } + # quickenSb.append(generalizeInputsMap[i][j]); + # } + # } + # field[7] = quickenSb.toString(); + # } + # formatted = "%-8s %2s %4s %-32s %-3s %-32s %s %s" % field; + # sb.append(formatted.strip()); + # sb.append('\n'); + # } + # } + + yield Instruction( + is_jump_target=is_jump_target, + starts_line=False, # starts_line, + offset=offset, + opname=opname, + opcode=opcode, + has_arg=has_arg, + arg=arg, + argval=argval, + argrepr=argrepr, + tos_str=None, + positions=None, + optype=optype, + inst_size=inst_size, + has_extended_arg=extended_arg_count != 0, + fallthrough=None, + start_offset=start_offset, + ) + + +class Bytecode_Graal(Bytecode): + """Bytecode operations involving a Python code object. + + Instantiate this with a function, method, string of code, or a code object + (as returned by compile()). + + Iterating over these yields the bytecode operations as Instruction instances. + """ + + def __init__(self, x, opc, first_line=None, current_offset=None) -> None: + self.codeobj = co = get_code_object(x) + self._line_offset = 0 + self._cell_names = tuple() + if first_line is None: + self.first_line = co.co_firstlineno + else: + self.first_line = first_line + self._line_offset = first_line - co.co_firstlineno + pass + + # self._linestarts = dict(opc.findlinestarts(co, dup_lines=dup_lines)) + self._linestarts = None + self._original_object = x + self.opc = opc + self.opnames = opc.opname + self.current_offset = current_offset + + if opc.version_tuple >= (3, 11) and hasattr(co, "co_exceptiontable"): + self.exception_entries = None + # self.exception_entries = parse_exception_table(co.co_exceptiontable) + else: + self.exception_entries = None + + def __iter__(self): + co = self.codeobj + return get_instructions_bytes_graal( + co.co_code, + self.opc, + co.co_varnames, + co.co_names, + co.co_consts, + co.co_cellvars, + co.co_freevars, + ) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self._original_object!r})" + + def get_instructions(self, x): + """Iterator for the opcodes in methods, functions or code + + Generates a series of Instruction named tuples giving the details of + each operation in the supplied code. + + If *first_line* is not None, it indicates the line number that should + be reported for the first source line in the disassembled code. + Otherwise, the source line information (if any) is taken directly from + the disassembled code object. + """ + co = get_code_object(x) + return get_instructions_bytes_graal( + co.co_code, + self.opc, + co.co_varnames, + co.co_names, + co.co_consts, + co.co_cellvars, + co.co_freevars, + ) diff --git a/xdis/disasm.py b/xdis/disasm.py index f42ade94..534dc05f 100644 --- a/xdis/disasm.py +++ b/xdis/disasm.py @@ -33,11 +33,12 @@ import xdis from xdis.bytecode import Bytecode +from xdis.bytecode_graal import Bytecode_Graal from xdis.codetype import codeType2Portable from xdis.codetype.base import iscode from xdis.cross_dis import format_code_info, format_exception_table from xdis.load import check_object_path, load_module -from xdis.magics import PYTHON_MAGIC_INT +from xdis.magics import GRAAL3_MAGICS, PYTHON_MAGIC_INT from xdis.op_imports import op_imports, remap_opcodes from xdis.version import __version__ from xdis.version_info import ( @@ -47,21 +48,33 @@ ) -def get_opcode(version_tuple, python_implementation, alternate_opmap=None): +# FIXME we may also need to distinguish by magic_int2magic +# (for 3.8.5 Graal for example.) +def get_opcode(version_tuple: tuple, python_implementation, alternate_opmap=None, magic_int: int=-1): # Set up disassembler with the right opcodes lookup = ".".join((str(i) for i in version_tuple)) if python_implementation == PythonImplementation.PyPy: lookup += "PyPy" + elif python_implementation == PythonImplementation.Graal: + if magic_int == 21290: + if version_tuple == (3, 11, 7): + lookup = "3.11.7Graal" + else: + lookup = "3.12.7Graal" + else: + lookup += "Graal" if lookup in op_imports.keys(): if alternate_opmap is not None: # TODO: change bytecode version number comment line to indicate altered return remap_opcodes(op_imports[lookup], alternate_opmap) return op_imports[lookup] - if python_implementation != PythonImplementation.CPyton: - pypy_str = f" for {python_implementation}" + if python_implementation != PythonImplementation.CPython: + implementation_str = f" for {python_implementation}" else: - pypy_str = "" - raise TypeError(f"{lookup} is not a Python version{pypy_str} I know about") + implementation_str = "" + raise TypeError( + f"{lookup} is not a Python version{implementation_str} I know about" + ) def show_module_header( @@ -150,8 +163,8 @@ def disco( sip_hash, header=True, show_filename=False, - python_implementation=python_implementation) - + python_implementation=python_implementation, + ) # Store final output stream when there is an error. real_out = out or sys.stdout @@ -169,7 +182,7 @@ def disco( ) pass - opc = get_opcode(version_tuple, python_implementation, alternate_opmap) + opc = get_opcode(version_tuple, python_implementation, alternate_opmap, magic_int) if asm_format == "xasm": disco_loop_asm_format(opc, version_tuple, co, real_out, {}, set([])) @@ -185,7 +198,7 @@ def disco( show_source=show_source, methods=methods, file_offsets=file_offsets, - is_unusual_bytecode=python_implementation == PythonImplementation.Graal, + is_unusual_bytecode=magic_int in GRAAL3_MAGICS ) @@ -235,33 +248,24 @@ def disco_loop( if asm_format in ("extended_bytes", "bytes"): real_out.write("instruction bytecode:\n%s\n" % co.co_code) - else: - if co.co_name == "??": - real_out.write( - "\n# Instruction disassembly not supported here.\n" - ) - else: - real_out.write( - f"\n# Instruction disassembly for {co.co_name} not supported here.\n" - ) - real_out.write(f"instruction bytecode:\n{co.co_code.hex(':')}\n") - + if opc.python_implementation == PythonImplementation.Graal: + bytecode = Bytecode_Graal(co, opc) else: bytecode = Bytecode(co, opc, dup_lines=dup_lines) - real_out.write( - bytecode.dis( - asm_format=asm_format, - show_source=show_source, - ) - + "\n" + real_out.write( + bytecode.dis( + asm_format=asm_format, + show_source=show_source, ) + + "\n" + ) - if version_tuple >= (3, 11): - if bytecode.exception_entries not in (None, []): - exception_table = format_exception_table( - bytecode, version_tuple - ) - real_out.write(exception_table + "\n") + if version_tuple >= (3, 11): + if bytecode.exception_entries not in (None, []): + exception_table = format_exception_table( + bytecode, version_tuple + ) + real_out.write(exception_table + "\n") for c in co.co_consts: if iscode(c): diff --git a/xdis/load.py b/xdis/load.py index 70dfdcfd..30d03eaa 100644 --- a/xdis/load.py +++ b/xdis/load.py @@ -358,6 +358,12 @@ def load_module_from_file_object( if is_pypy(magic_int, filename): python_implementation = PythonImplementation.PyPy + # Below we need to return co.version_triple instead of version_triple, + # because Graal uses the *same* magic number but different bytecode + # for Python 3.11 and Python 3.12. What a zoo we have here. + if hasattr(co, "version_triple") and co.version_triple != (0, 0, 0): + version_triple = co.version_triple + return ( version_triple, timestamp, diff --git a/xdis/magics.py b/xdis/magics.py index f42c507f..8ff0f782 100755 --- a/xdis/magics.py +++ b/xdis/magics.py @@ -698,11 +698,13 @@ def __by_version(magic_versions: Dict[bytes, str]) -> dict: add_magic_from_int(13413, "3.13.0a.rust") # RustPython 3.13.0 add_magic_from_int(24881, "3.13.0b.rust") # RustPython 3.13.0 -# Graal Python. Graal uses JVM bytecode, not CPython or PyPy bytecode -# Its magic number is +# Graal Python. Graal uses its own JVM-ish CPython bytecode, not +# true CPython or PyPy bytecode. +# +# Graal's magic number: # MAGIC_NUMBER = 21000 + Compiler.BYTECODE_VERSION * 10; -# Note: Different major/minor releases can have the same magic! -# Graal for 3.11 and 3.12 are like that. +# Note: Different major/minor releases +# can have the same magic! Graal for 3.11 and 3.12 are like that. # 21250 = 21000 + 15 * 10 add_magic_from_int(21150, "3.8.5Graal (15)") @@ -732,7 +734,9 @@ def __by_version(magic_versions: Dict[bytes, str]) -> dict: # From a Python version given in sys.info, e.g. 3.6.1, # what is the "canonic" version number, e.g. '3.6.0rc1' -canonic_python_version = {} +canonic_python_version = { + "3.12.8Graal": "3.12.8Graal" +} def add_canonic_versions(release_versions: str, canonic: str) -> None: diff --git a/xdis/op_imports.py b/xdis/op_imports.py index 79e7a4b7..67a7d6ea 100644 --- a/xdis/op_imports.py +++ b/xdis/op_imports.py @@ -51,14 +51,18 @@ opcode_37, opcode_37pypy, opcode_38, + opcode_38graal, opcode_38pypy, opcode_39, opcode_39pypy, opcode_310, + opcode_310graal, opcode_310pypy, opcode_311, + opcode_311graal, opcode_311pypy, opcode_312, + opcode_312graal, opcode_313, opcode_314, ) @@ -146,6 +150,7 @@ "3.8.12PyPy": opcode_38pypy, "3.8.13PyPy": opcode_38pypy, "3.8.14PyPy": opcode_38pypy, + "3.8.5Graal": opcode_38graal, "3.8.15PyPy": opcode_38pypy, "3.8.16PyPy": opcode_38pypy, "3.8.17PyPy": opcode_38pypy, @@ -162,6 +167,7 @@ "3.10.0rc2": opcode_310, "3.10.b1": opcode_310, "3.10": opcode_310, + "3.10.8Graal": opcode_310graal, "3.10PyPy": opcode_310pypy, "3.10.12PyPy": opcode_310pypy, "3.11": opcode_311, @@ -172,8 +178,11 @@ "3.11.4": opcode_311, "3.11.5": opcode_311, "3.11a7e": opcode_311, - "3.11.13PyPy": opcode_311pypy, + "3.11.7Graal": opcode_311graal, # is this right? + "3.11.13PyPy": opcode_311pypy, # is this right? 3.11: opcode_311, + "3.12.7Graal": opcode_312graal, # this right? + "3.12.8Graal": opcode_312graal, # this right? "3.12.0rc2": opcode_312, "3.12.0": opcode_312, "3.13.0rc3": opcode_313, diff --git a/xdis/opcodes/base.py b/xdis/opcodes/base.py index ed7f8dde..9c4bde17 100644 --- a/xdis/opcodes/base.py +++ b/xdis/opcodes/base.py @@ -24,7 +24,9 @@ from xdis import wordcode from xdis.cross_dis import findlabels, findlinestarts, get_jump_target_maps -from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE +from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE, PythonImplementation + +cpython_implementation = PythonImplementation("CPython") cmp_op = ( "<", # 0 diff --git a/xdis/opcodes/base_graal.py b/xdis/opcodes/base_graal.py new file mode 100644 index 00000000..8b4ce4d9 --- /dev/null +++ b/xdis/opcodes/base_graal.py @@ -0,0 +1,319 @@ +# (C) Copyright 2025 by Rocky Bernstein +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +""" +Common routines for entering and classifying opcodes. Inspired by, +limited by, and somewhat compatible with the corresponding +Python opcode.py structures +""" + + +def get_optype_graal(opcode: int, opc) -> str: + """Helper to determine what class of instructions ``opcode`` is in. + Return is a string in: + compare, const, free, jabs, jrel, local, name, nargs, or ?? + """ + if opcode in opc.BINARY_OPS: + return "binary" + elif opcode in opc.COLLECTION_OPS: + return "collection" + elif opcode in opc.CONST_OPS: + return "const" + elif opcode in opc.COMPARE_OPS: + return "compare" + elif opcode in opc.ENCODED_ARG_OPS: + return "encoded_arg" + elif opcode in opc.FREE_OPS: + return "free" + elif opcode in opc.JABS_OPS: + return "jabs" + elif opcode in opc.JREL_OPS: + return "jrel" + # elif opcode in opc.LOCAL_OPS: + # return "local" + elif opcode in opc.NAME_OPS: + return "name" + elif opcode in opc.NARGS_OPS: + return "nargs" + # This has to come after NARGS_OPS. Some are in both? + elif opcode in opc.VARGS_OPS: + return "vargs" + elif opcode in opc.UNARY_OPS: + return "unary" + + return "??" + + +def findlabels(bytecode, opc): + """Returns a list of instruction offsets in the supplied bytecode + which are the targets of some sort of jump instruction. + """ + offset = 0 + n = len(bytecode) + offsets = [] + while offset < n: + opcode = bytecode[offset] + optype = get_optype_graal(opcode, opc) + + # opname = opc.opname[opcode] + # print( + # f"offset: {offset} {hex(opcode)} {opname} optype: {optype}" + # ) + + jump_offset = -1 + if optype == "jrel": + arg = bytecode[offset + 1] + if opcode == opc.opmap["JUMP_BACKWARD"]: + jump_offset = offset - arg + else: + jump_offset = offset + arg + # elif op in opc.JABS_OPS: + # jump_offset = arg + if jump_offset >= 0: + if jump_offset not in offsets: + offsets.append(jump_offset) + inst_size = opc.arg_counts[opcode] + 1 + offset += inst_size + + return offsets + + +# FIXME: Use an enumeration? Or tuple? +BINARY_OPS = { + 0: "ADD", + 1: "INPLACE_ADD", + 2: "SUB", + 3: "INPLACE_SUB", + 4: "MUL", + 5: "INPLACE_MUL", + 6: "FLOORDIV", + 7: "INPLACE_FLOORDIV", + 8: "TRUEDIV", + 9: "INPLACE_TRUEDIV", + 10: "MOD", + 11: "INPLACE_MOD", + 12: "EQ", + 13: "NE", + 14: "LT", + 15: "LE", + 16: "GT", + 17: "GE", + 18: "LSHIFT", + 19: "INPLACE_LSHIFT", + 20: "RSHIFT", + 21: "INPLACE_RSHIFT", + 22: "AND", + 23: "INPLACE_AND", + 24: "OR", + 25: "INPLACE_OR", + 26: "XOR", + 27: "INPLACE_XOR", + 28: "POW", + 29: "INPLACE_POW", + 30: "IN", + 31: "IS", + 32: "MATMUL", + 33: "INPLACE_MATMUL", +} + +COLLECTION_KIND = { + 32: "list", + 67: "tuple", # Probably need to use a mask + 99: "tuple", # probably need to use a mask + 3: "set", + 4: "dict", + 5: "kw", + 6: "object", +} + +UNARY_OPS = { + 0: "NOT", + 31: "IS", + 30: "IN", +} + + +def binary_op_graal( + loc: dict, + name: str, + opcode: int, + pop: int = 2, + push: int = 1, + arg_count: int = 1, +) -> None: + """ + Put opcode in the class of instructions that are binary operations. + """ + loc["binaryop"].add(opcode) + def_op_graal(loc, name, opcode, pop, push, arg_count) + + +def def_op_graal( + loc: dict, + op_name: str, + opcode: int, + pop: int = -2, + push: int = -2, + arg_count: int = 0, + fallthrough: bool = True, +) -> None: + loc["opname"][opcode] = op_name + loc["opmap"][op_name] = opcode + loc["oppush"][opcode] = push + loc["oppop"][opcode] = pop + if not fallthrough: + loc["nofollow"].append(opcode) + loc["arg_counts"][opcode] = arg_count + loc["operator_set"] = frozenset(loc["nullaryop"] | loc["unaryop"] | loc["binaryop"]) + + +def call_op_graal( + loc: dict, + name: str, + opcode: int, + pop: int = -2, + push: int = 1, + fallthrough: bool = True, +) -> None: + """ + Put opcode in the class of instructions that perform calls. + """ + loc["callop"].add(opcode) + nargs_op_graal(loc, name, opcode, pop, push, fallthrough) + + +def collection_op_graal( + loc: dict, name: str, opcode: int, pop: int = 0, push: int = 1, arg_count: int = 1 +) -> None: + def_op_graal(loc, name, opcode, pop, push, arg_count) + loc["collectionop"].add(opcode) + + +def const_op_graal( + loc: dict, name: str, opcode: int, pop: int = 0, push: int = 1, arg_count: int = 1 +) -> None: + def_op_graal(loc, name, opcode, pop, push, arg_count) + loc["hasconst"].append(opcode) + loc["nullaryop"].add(opcode) + + +def free_op_graal( + loc: dict, name: str, opcode: int, pop: int = 0, push: int = 1, arg_count: int = 1 +) -> None: + def_op_graal(loc, name, opcode, pop, push, arg_count) + loc["hasfree"].append(opcode) + + +def jrel_op_graal( + loc, + name: str, + opcode: int, + pop: int = 0, + push: int = 0, + conditional=False, + fallthrough=True, + arg_count: int = 1, +) -> None: + """ + Put opcode in the class of instructions that can perform a relative jump. + """ + def_op_graal(loc, name, opcode, pop, push, arg_count) + loc["hasjrel"].append(opcode) + if conditional: + loc["hascondition"].append(opcode) + + +def name_op_graal( + loc: dict, op_name, opcode: int, pop=-2, push=-2, arg_count: int = 1 +) -> None: + """ + Put opcode in the class of instructions that index into the "name" table. + """ + def_op_graal(loc, op_name, opcode, pop, push, arg_count) + loc["hasname"].append(opcode) + loc["nullaryop"].add(opcode) + + +def nargs_op_graal( + loc, + name: str, + opcode: int, + pop: int = -2, + push: int = -1, + arg_count: int = 0, + fallthrough=True, +) -> None: + """ + Put opcode in the class of instructions that have a variable number of (or *n*) arguments + """ + def_op_graal( + loc, name, opcode, pop, push, arg_count=arg_count, fallthrough=fallthrough + ) + loc["hasnargs"].append(opcode) + + +def store_op_graal( + loc: dict, name: str, op, pop=0, push=1, is_type="def", arg_count: int = 1 +) -> None: + if is_type == "name": + name_op_graal(loc, name, op, pop, push, arg_count) + loc["nullaryop"].remove(op) + # elif is_type == "local": + # local_op(loc, name, op, pop, push) + # loc["nullaryop"].remove(op) + # elif is_type == "free": + # free_op(loc, name, op, pop, push) + else: + assert is_type == "def" + def_op_graal(loc, name, op, pop, push) + loc["hasstore"].append(op) + + +def unary_op_graal( + loc: dict, + name: str, + opcode: int, + pop: int = 1, + push: int = 1, + arg_count: int = 1, +) -> None: + """ + Put opcode in the class of instructions that are binary operations. + """ + loc["unaryop"].add(opcode) + def_op_graal(loc, name, opcode, pop, push, arg_count) + + +def update_sets(loc) -> None: + """ + Updates various category sets all opcode have been defined. + """ + + loc["BINARY_OPS"] = frozenset(loc["binaryop"]) + loc["COLLECTION_OPS"] = frozenset(loc["collectionop"]) + loc["COMPARE_OPS"] = frozenset(loc["hascompare"]) + loc["CONDITION_OPS"] = frozenset(loc["hascondition"]) + loc["CONST_OPS"] = frozenset(loc["hasconst"]) + loc["EXTENDED_ARG"] = loc["opmap"]["EXTENDED_ARG"] + loc["ENCODED_ARG_OPS"] = frozenset(loc["encoded_arg"]) + loc["FREE_OPS"] = frozenset(loc["hasfree"]) + loc["JREL_OPS"] = frozenset(loc["hasjrel"]) + loc["JABS_OPS"] = frozenset(loc["hasjabs"]) + loc["NAME_OPS"] = frozenset(loc["hasname"]) + loc["NARGS_OPS"] = frozenset(loc["hasnargs"]) + loc["VARGS_OPS"] = frozenset(loc["hasvargs"]) + loc["UNARY_OPS"] = frozenset(loc["unaryop"]) + loc["findlabels"] = findlabels + loc["get_jump_targets"] = findlabels diff --git a/xdis/opcodes/opcode_10.py b/xdis/opcodes/opcode_10.py index 42c89a93..d3f513b2 100644 --- a/xdis/opcodes/opcode_10.py +++ b/xdis/opcodes/opcode_10.py @@ -24,7 +24,8 @@ # This is used from outside this module from xdis.cross_dis import findlabels # noqa -from xdis.opcodes.base import ( # Although these aren't used here, they are exported +from xdis.opcodes.base import ( # Although these aren't used here, they are exported; noqa + cpython_implementation as python_implementation, finalize_opcodes, init_opdata, name_op, @@ -34,7 +35,6 @@ from xdis.opcodes.opcode_11 import opcode_arg_fmt11, opcode_extended_fmt11 version_tuple = (1, 0) -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_11, version_tuple) diff --git a/xdis/opcodes/opcode_11.py b/xdis/opcodes/opcode_11.py index 91ddb90f..d2a12bc4 100644 --- a/xdis/opcodes/opcode_11.py +++ b/xdis/opcodes/opcode_11.py @@ -24,7 +24,8 @@ # This is used from outside this module from xdis.cross_dis import findlabels -from xdis.opcodes.base import ( # Although these aren't used here, they are exported +from xdis.opcodes.base import ( # Although these aren't used here, they are exported; noqa + cpython_implementation as python_implementation, finalize_opcodes, init_opdata, update_pj2, @@ -32,7 +33,6 @@ from xdis.opcodes.opcode_12 import opcode_arg_fmt12, opcode_extended_fmt12 version_tuple = (1, 1) # 1.2 is the same -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_12, version_tuple) diff --git a/xdis/opcodes/opcode_12.py b/xdis/opcodes/opcode_12.py index b2ef94e9..021587bc 100644 --- a/xdis/opcodes/opcode_12.py +++ b/xdis/opcodes/opcode_12.py @@ -24,7 +24,8 @@ # This is used from outside this module from xdis.cross_dis import findlabels # noqa -from xdis.opcodes.base import ( # Although these aren't used here, they are exported +from xdis.opcodes.base import ( # Although these aren't used here, they are exported; noqa + cpython_implementation as python_implementation, finalize_opcodes, init_opdata, name_op, @@ -35,7 +36,6 @@ from xdis.opcodes.opcode_13 import opcode_arg_fmt13, opcode_extended_fmt13 version_tuple = (1, 2) -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_13, version_tuple) diff --git a/xdis/opcodes/opcode_13.py b/xdis/opcodes/opcode_13.py index e3a3016d..416467ef 100644 --- a/xdis/opcodes/opcode_13.py +++ b/xdis/opcodes/opcode_13.py @@ -24,7 +24,8 @@ # This is used from outside this module from xdis.cross_dis import findlabels # noqa -from xdis.opcodes.base import ( # Although these aren't used here, they are exported +from xdis.opcodes.base import ( # Although these aren't used here, they are exported; noqa + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, @@ -34,7 +35,6 @@ from xdis.opcodes.opcode_1x import opcode_extended_fmt_base1x, update_arg_fmt_base1x version_tuple = (1, 3) -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_14, version_tuple) diff --git a/xdis/opcodes/opcode_15.py b/xdis/opcodes/opcode_15.py index 4a6e9a0b..c0e666be 100644 --- a/xdis/opcodes/opcode_15.py +++ b/xdis/opcodes/opcode_15.py @@ -22,7 +22,8 @@ """ import xdis.opcodes.opcode_1x as opcode_1x -from xdis.opcodes.base import ( # Although these aren't used here, they are exported +from xdis.opcodes.base import ( # Although these aren't used here, they are exported; noqa + cpython_implementation as python_implementation, finalize_opcodes, init_opdata, update_pj2, @@ -30,7 +31,6 @@ from xdis.opcodes.opcode_1x import opcode_extended_fmt_base1x, update_arg_fmt_base1x version_tuple = (1, 5) -python_implementation = "CPython" loc = locals() diff --git a/xdis/opcodes/opcode_16.py b/xdis/opcodes/opcode_16.py index 37e64f47..3cc46b70 100644 --- a/xdis/opcodes/opcode_16.py +++ b/xdis/opcodes/opcode_16.py @@ -1,4 +1,4 @@ -# (C) Copyright 2019-2021, 2023-2024 by Rocky Bernstein +# (C) Copyright 2019-2021, 2023-2025 by Rocky Bernstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -24,11 +24,16 @@ # This is used from outside this module from xdis.cross_dis import findlabels, findlinestarts # noqa -from xdis.opcodes.base import call_op, finalize_opcodes, init_opdata, update_pj2 +from xdis.opcodes.base import ( # noqa + call_op, + cpython_implementation as python_implementation, + finalize_opcodes, + init_opdata, + update_pj2, +) from xdis.opcodes.opcode_2x import opcode_extended_fmt_base2x, update_arg_fmt_base2x version_tuple = (1, 6) -python_implementation = "CPython" loc = locals() diff --git a/xdis/opcodes/opcode_1x.py b/xdis/opcodes/opcode_1x.py index 20ed27bc..14a14c13 100644 --- a/xdis/opcodes/opcode_1x.py +++ b/xdis/opcodes/opcode_1x.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017, 2019-2021, 2023 by Rocky Bernstein +# (C) Copyright 2017, 2019-2021, 2023, 2025 by Rocky Bernstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -32,6 +32,7 @@ jrel_op, local_op, name_op, + nargs_op, store_op, ternary_op, unary_op, @@ -47,8 +48,11 @@ extended_format_SLICE_2, extended_format_SLICE_3, ) +from xdis.version_info import PythonImplementation loc = locals() +python_implementation = PythonImplementation("CPython") + init_opdata(loc, None, None) # Opcodes greater than 90 take an instruction operand or "argument" @@ -187,7 +191,7 @@ # Number of raise arguments (1, 2, or 3) call_op(loc, "CALL_FUNCTION", 131, -1, 1) # #args + (#kwargs << 8) -def_op(loc, "MAKE_FUNCTION", 132, -1, 1) # Number of args with default values +nargs_op(loc, "MAKE_FUNCTION", 132, -1, 1) # Number of args with default values varargs_op(loc, "BUILD_SLICE", 133, -1, 1) # Number of items def_op(loc, "EXTENDED_ARG", 143) diff --git a/xdis/opcodes/opcode_20.py b/xdis/opcodes/opcode_20.py index 6522ed39..0941f960 100644 --- a/xdis/opcodes/opcode_20.py +++ b/xdis/opcodes/opcode_20.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017, 2019-2021, 2023 by Rocky Bernstein +# (C) Copyright 2017, 2019-2021, 2025 by Rocky Bernstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,17 +20,16 @@ """ import xdis.opcodes.opcode_21 as opcode_21 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, finalize_opcodes, init_opdata, rm_op, update_pj2, ) - -from xdis.opcodes.opcode_2x import update_arg_fmt_base2x, opcode_extended_fmt_base2x +from xdis.opcodes.opcode_2x import opcode_extended_fmt_base2x, update_arg_fmt_base2x version_tuple = (2, 0) -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_21, version_tuple) diff --git a/xdis/opcodes/opcode_21.py b/xdis/opcodes/opcode_21.py index 4d236aa0..e3340587 100644 --- a/xdis/opcodes/opcode_21.py +++ b/xdis/opcodes/opcode_21.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017, 2019, 2023 by Rocky Bernstein +# (C) Copyright 2017, 2019, 2023, 2025 by Rocky Bernstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,17 +20,16 @@ """ import xdis.opcodes.opcode_22 as opcode_22 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, finalize_opcodes, init_opdata, rm_op, update_pj2, ) - -from xdis.opcodes.opcode_2x import update_arg_fmt_base2x, opcode_extended_fmt_base2x +from xdis.opcodes.opcode_2x import opcode_extended_fmt_base2x, update_arg_fmt_base2x version_tuple = (2, 1) -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_22, version_tuple) diff --git a/xdis/opcodes/opcode_22.py b/xdis/opcodes/opcode_22.py index 5e7a58da..b8effafa 100644 --- a/xdis/opcodes/opcode_22.py +++ b/xdis/opcodes/opcode_22.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017, 2019, 2021, 2023 by Rocky Bernstein +# (C) Copyright 2017, 2019, 2021, 2023, 2025 by Rocky Bernstein """ CPython 2.2 bytecode opcodes @@ -6,17 +6,16 @@ """ import xdis.opcodes.opcode_2x as opcode_2x -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, update_pj2, ) - -from xdis.opcodes.opcode_2x import update_arg_fmt_base2x, opcode_extended_fmt_base2x +from xdis.opcodes.opcode_2x import opcode_extended_fmt_base2x, update_arg_fmt_base2x version_tuple = (2, 2) -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_2x, version_tuple) diff --git a/xdis/opcodes/opcode_23.py b/xdis/opcodes/opcode_23.py index 6de04993..2035dd43 100644 --- a/xdis/opcodes/opcode_23.py +++ b/xdis/opcodes/opcode_23.py @@ -7,16 +7,15 @@ """ import xdis.opcodes.opcode_2x as opcode_2x -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, finalize_opcodes, init_opdata, update_pj2, ) - -from xdis.opcodes.opcode_2x import update_arg_fmt_base2x, opcode_extended_fmt_base2x +from xdis.opcodes.opcode_2x import opcode_extended_fmt_base2x, update_arg_fmt_base2x version_tuple = (2, 3) -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_2x, version_tuple) diff --git a/xdis/opcodes/opcode_24.py b/xdis/opcodes/opcode_24.py index f6fa54fd..5f8eb6ac 100644 --- a/xdis/opcodes/opcode_24.py +++ b/xdis/opcodes/opcode_24.py @@ -7,17 +7,16 @@ """ import xdis.opcodes.opcode_2x as opcode_2x -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, update_pj2, ) - -from xdis.opcodes.opcode_2x import update_arg_fmt_base2x, opcode_extended_fmt_base2x +from xdis.opcodes.opcode_2x import opcode_extended_fmt_base2x, update_arg_fmt_base2x version_tuple = (2, 4) -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_2x, version_tuple) diff --git a/xdis/opcodes/opcode_25.py b/xdis/opcodes/opcode_25.py index 105dedef..3ef75652 100644 --- a/xdis/opcodes/opcode_25.py +++ b/xdis/opcodes/opcode_25.py @@ -7,17 +7,16 @@ """ import xdis.opcodes.opcode_24 as opcode_24 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, update_pj2, ) - -from xdis.opcodes.opcode_2x import update_arg_fmt_base2x, opcode_extended_fmt_base2x +from xdis.opcodes.opcode_2x import opcode_extended_fmt_base2x, update_arg_fmt_base2x version_tuple = (2, 5) -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_24, version_tuple) diff --git a/xdis/opcodes/opcode_26.py b/xdis/opcodes/opcode_26.py index d79b6a85..d0839261 100644 --- a/xdis/opcodes/opcode_26.py +++ b/xdis/opcodes/opcode_26.py @@ -21,7 +21,8 @@ """ import xdis.opcodes.opcode_25 as opcode_25 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, finalize_opcodes, init_opdata, name_op, @@ -31,8 +32,6 @@ ) from xdis.opcodes.opcode_2x import opcode_extended_fmt_base2x, update_arg_fmt_base2x -python_implementation = "CPython" - version_tuple = (2, 6) loc = locals() diff --git a/xdis/opcodes/opcode_26pypy.py b/xdis/opcodes/opcode_26pypy.py index 793f8e2b..14d94898 100644 --- a/xdis/opcodes/opcode_26pypy.py +++ b/xdis/opcodes/opcode_26pypy.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017, 2021, 2023 by Rocky Bernstein +# (C) Copyright 2017, 2021, 2023, 2025 by Rocky Bernstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -34,9 +34,10 @@ extended_format_ATTR, extended_format_RETURN_VALUE, ) +from xdis.version_info import PythonImplementation version_tuple = (2, 6) -python_implementation = "PyPy" +python_implementation = PythonImplementation("PyPy") loc = locals() init_opdata(loc, opcode_26, version_tuple, is_pypy=True) diff --git a/xdis/opcodes/opcode_27.py b/xdis/opcodes/opcode_27.py index dafb3a98..2a074a69 100644 --- a/xdis/opcodes/opcode_27.py +++ b/xdis/opcodes/opcode_27.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017, 2019-2021, 2023-2024 by Rocky Bernstein +# (C) Copyright 2017, 2019-2021, 2023-2025 by Rocky Bernstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -21,8 +21,9 @@ """ import xdis.opcodes.opcode_26 as opcode_26 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa compare_op, + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, @@ -36,59 +37,58 @@ from xdis.opcodes.opcode_2x import opcode_extended_fmt_base2x, update_arg_fmt_base2x version_tuple = (2, 7) -python_implementation = "CPython" -loc = l = locals() -init_opdata(l, opcode_26, version_tuple) +loc = locals() +init_opdata(loc, opcode_26, version_tuple) # fmt: off # Below are opcode changes since Python 2.6 -rm_op(l, "LIST_APPEND", 18) -rm_op(l, "BUILD_MAP", 104) -rm_op(l, "LOAD_ATTR", 105) -rm_op(l, "COMPARE_OP", 106) -rm_op(l, "IMPORT_NAME", 107) -rm_op(l, "IMPORT_FROM", 108) -rm_op(l, "JUMP_IF_FALSE", 111) -rm_op(l, "EXTENDED_ARG", 143) -rm_op(l, "JUMP_IF_TRUE", 112) -rm_op(l, "SETUP_EXCEPT", 121) -rm_op(l, "SETUP_FINALLY", 122) +rm_op(loc, "LIST_APPEND", 18) +rm_op(loc, "BUILD_MAP", 104) +rm_op(loc, "LOAD_ATTR", 105) +rm_op(loc, "COMPARE_OP", 106) +rm_op(loc, "IMPORT_NAME", 107) +rm_op(loc, "IMPORT_FROM", 108) +rm_op(loc, "JUMP_IF_FALSE", 111) +rm_op(loc, "EXTENDED_ARG", 143) +rm_op(loc, "JUMP_IF_TRUE", 112) +rm_op(loc, "SETUP_EXCEPT", 121) +rm_op(loc, "SETUP_FINALLY", 122) # These have changed since 2.6 in stack effects. # OP NAME OPCODE POP PUSH #----------------------------------------------- -def_op(l, "END_FINALLY", 88, 3, 0) -jrel_op(l, "SETUP_EXCEPT", 121, 0, 0, conditional=True) # "" -jrel_op(l, "SETUP_FINALLY" , 122, 0, 0, conditional=True) # "" +def_op(loc, "END_FINALLY", 88, 3, 0) +jrel_op(loc, "SETUP_EXCEPT", 121, 0, 0, conditional=True) # "" +jrel_op(loc, "SETUP_FINALLY" , 122, 0, 0, conditional=True) # "" -def_op(l, "LIST_APPEND", 94, 2, 1) # Calls list.append(TOS[-i], TOS). +def_op(loc, "LIST_APPEND", 94, 2, 1) # Calls list.append(TOS[-i], TOS). # Used to implement list comprehensions. -varargs_op(l, 'BUILD_SET', 104, -1, 1) # TOS is count of set items -varargs_op(l, "BUILD_MAP", 105, 0, 1) # count is in argument -name_op(l, "LOAD_ATTR", 106, 1, 1) # Operand is in name list -compare_op(l, "COMPARE_OP", 107) +varargs_op(loc, 'BUILD_SET', 104, -1, 1) # TOS is count of set items +varargs_op(loc, "BUILD_MAP", 105, 0, 1) # count is in argument +name_op(loc, "LOAD_ATTR", 106, 1, 1) # Operand is in name list +compare_op(loc, "COMPARE_OP", 107) -name_op(l, "IMPORT_NAME", 108, 2, 1) # Index in name list -name_op(l, "IMPORT_FROM", 109, 0, 1) +name_op(loc, "IMPORT_NAME", 108, 2, 1) # Index in name list +name_op(loc, "IMPORT_FROM", 109, 0, 1) -jabs_op(l, "JUMP_IF_FALSE_OR_POP", 111) # Target byte offset from beginning of code -jabs_op(l, "JUMP_IF_TRUE_OR_POP", 112) # "" -jabs_op(l, "POP_JUMP_IF_FALSE", 114, 2, 1, conditional=True) # "" -jabs_op(l, "POP_JUMP_IF_TRUE", 115, 2, 1, conditional=True) # "" -jrel_op(l, "SETUP_WITH", 143, 0, 4) +jabs_op(loc, "JUMP_IF_FALSE_OR_POP", 111) # Target byte offset from beginning of code +jabs_op(loc, "JUMP_IF_TRUE_OR_POP", 112) # "" +jabs_op(loc, "POP_JUMP_IF_FALSE", 114, 2, 1, conditional=True) # "" +jabs_op(loc, "POP_JUMP_IF_TRUE", 115, 2, 1, conditional=True) # "" +jrel_op(loc, "SETUP_WITH", 143, 0, 4) -def_op(l, "EXTENDED_ARG", 145) -def_op(l, "SET_ADD", 146, 1, 0) # Calls set.add(TOS1[-i], TOS). +def_op(loc, "EXTENDED_ARG", 145) +def_op(loc, "SET_ADD", 146, 1, 0) # Calls set.add(TOS1[-i], TOS). # Used to implement set comprehensions. -def_op(l, "MAP_ADD", 147, 3, 1) # Calls dict.setitem(TOS1[-i], TOS, TOS1) +def_op(loc, "MAP_ADD", 147, 3, 1) # Calls dict.setitem(TOS1[-i], TOS, TOS1) # Used to implement dict comprehensions. # fmt: on opcode_arg_fmt = opcode_arg_fmt27 = update_arg_fmt_base2x.copy() opcode_extended_fmt = opcode_extended_fmt27 = opcode_extended_fmt_base2x.copy() -update_pj3(globals(), l) -finalize_opcodes(l) +update_pj3(globals(), loc) +finalize_opcodes(loc) diff --git a/xdis/opcodes/opcode_27pypy.py b/xdis/opcodes/opcode_27pypy.py index 0b3f6256..8fea9cb9 100644 --- a/xdis/opcodes/opcode_27pypy.py +++ b/xdis/opcodes/opcode_27pypy.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017, 2020, 2023 by Rocky Bernstein +# (C) Copyright 2017, 2020, 2023, 2025 by Rocky Bernstein """ PYPY 2.7 opcodes @@ -6,6 +6,7 @@ of stack usage. """ import sys + import xdis.opcodes.opcode_27 as opcode_27 from xdis.opcodes.base import ( def_op, @@ -16,11 +17,11 @@ nargs_op, update_pj3, ) - -from xdis.opcodes.opcode_2x import update_arg_fmt_base2x, opcode_extended_fmt_base2x +from xdis.opcodes.opcode_2x import opcode_extended_fmt_base2x, update_arg_fmt_base2x +from xdis.version_info import PythonImplementation version_tuple = (2, 7) -python_implementation = "PyPy" +python_implementation = PythonImplementation("PyPy") loc = locals() diff --git a/xdis/opcodes/opcode_30.py b/xdis/opcodes/opcode_30.py index 8d986f22..7597c2d6 100644 --- a/xdis/opcodes/opcode_30.py +++ b/xdis/opcodes/opcode_30.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017, 2019-2021, 2023-2024 by Rocky Bernstein +# (C) Copyright 2017, 2019-2021, 2023-2025 by Rocky Bernstein """ CPython 3.0 bytecode opcodes @@ -7,7 +7,8 @@ """ import xdis.opcodes.opcode_31 as opcode_31 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, @@ -18,7 +19,6 @@ from xdis.opcodes.opcode_33 import opcode_arg_fmt33, opcode_extended_fmt33 version_tuple = (3, 0) -python_implementation = "CPython" loc = locals() diff --git a/xdis/opcodes/opcode_31.py b/xdis/opcodes/opcode_31.py index a235f424..f85074e5 100644 --- a/xdis/opcodes/opcode_31.py +++ b/xdis/opcodes/opcode_31.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017, 2020-2021, 2023-2024 by Rocky Bernstein +# (C) Copyright 2017, 2020-2021, 2023-2025 by Rocky Bernstein """ CPython 3.1 bytecode opcodes @@ -7,7 +7,8 @@ """ import xdis.opcodes.opcode_32 as opcode_32 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, @@ -20,7 +21,6 @@ loc = locals() version_tuple = (3, 1) -python_implementation = "CPython" init_opdata(loc, opcode_32, version_tuple) diff --git a/xdis/opcodes/opcode_310.py b/xdis/opcodes/opcode_310.py index c4f0dc4f..62fcbd79 100644 --- a/xdis/opcodes/opcode_310.py +++ b/xdis/opcodes/opcode_310.py @@ -1,4 +1,4 @@ -# (C) Copyright 2021, 2023-2024 by Rocky Bernstein +# (C) Copyright 2021, 2023-2025 by Rocky Bernstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -22,11 +22,17 @@ import xdis.opcodes.opcode_39 as opcode_39 from xdis.cross_dis import findlinestarts # noqa -from xdis.opcodes.base import def_op, finalize_opcodes, init_opdata, rm_op, update_pj3 +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, + def_op, + finalize_opcodes, + init_opdata, + rm_op, + update_pj3, +) from xdis.opcodes.opcode_39 import opcode_arg_fmt39, opcode_extended_fmt39 version_tuple = (3, 10) -python_implementation = "CPython" loc = locals() diff --git a/xdis/opcodes/opcode_310graal.py b/xdis/opcodes/opcode_310graal.py index e752b0c8..fab5c9d9 100644 --- a/xdis/opcodes/opcode_310graal.py +++ b/xdis/opcodes/opcode_310graal.py @@ -5,28 +5,47 @@ See com.oracle.graal.python/src/com/oracle/graal/python/compiler/OpCodes.java """ +from typing import Dict, Set + from xdis.opcodes.base import init_opdata +from xdis.opcodes.base_graal import findlabels # noqa +from xdis.opcodes.base_graal import ( # find_linestarts, # noqa + binary_op_graal, + call_op_graal, + collection_op_graal, + const_op_graal, + def_op_graal, + free_op_graal, + jrel_op_graal, + name_op_graal, + nargs_op_graal, + store_op_graal, + unary_op_graal, + update_sets, +) +from xdis.version_info import PythonImplementation + +python_implementation = PythonImplementation("Graal") + +version_tuple = (3, 10, 8) + +arg_counts: Dict[int, int] = {} + +# opcodes that perform a binary operation on the top two stack entries +binaryop: Set[int] = set([]) + +# opcodes that perform some sort of call +callop: Set[int] = set([]) -opc = locals() -init_opdata(opc, None, None) +# opcodes that perform some sort of call +collectionop: Set[int] = set([]) +# opcodes that perform a unary operation on the toip stack entry +unaryop: Set[int] = set([]) -def def_graal_op( - loc: dict, - op_name: str, - opcode: int, - pop: int = -2, - push: int = -2, - unknown: int = 0, - fallthrough: bool = True, -) -> None: - loc["opname"][opcode] = op_name - loc["opmap"][op_name] = opcode - loc["oppush"][opcode] = push - loc["oppop"][opcode] = pop - if not fallthrough: - loc["nofollow"].append(opcode) +loc = locals() +init_opdata(loc, None, None) # Instruction opcodes for compiled code # Blank lines correspond to available opcodes @@ -38,170 +57,165 @@ def def_graal_op( # then pop the operand amount plus the negative of the POP amount. # Pop a single item from the stack. -def_graal_op(opc, "POP_TOP", 0, 0, 1, 0) +def_op_graal(loc, "POP_TOP", 0x0, 0, 1, 0) # Exchange two top stack items. -def_graal_op(opc, "ROT_TWO", 1, 0, 2, 2) +def_op_graal(loc, "ROT_TWO", 0x1, 0, 2, 0) # Exchange three top stack items. [a, b, c] (a is top) becomes [b, c, a] -def_graal_op(opc, "ROT_THREE", 2, 0, 3, 3) +def_op_graal(loc, "ROT_THREE", 0x2, 0, 3, 0) # Exchange N top stack items. [a, b, c, ..., N] (a is top) becomes [b, c, ..., N, a]. -def_graal_op( - opc, "ROT_N", 3, -1, -1 +def_op_graal( + loc, "ROT_N", 0x3, -1, -1 ) # (oparg, followingArgs, withJump) -> oparg, (oparg, followingArgs, withJump) -> oparg) # Duplicates the top stack item. -def_graal_op(opc, "DUP_TOP", 4, 0, 1, 2) +def_op_graal(loc, "DUP_TOP", 0x4, 0, 1, 0) # Does nothing. Might still be useful to maintain a line number. -def_graal_op(opc, "NOP", 5, 0, 0, 0) +def_op_graal(loc, "NOP", 0x5, 0, 0, 0) # Performs a unary operation specified by the immediate operand. It # has to be the ordinal of one of {@link UnaryOps} constants. # Pops: operand # Pushes: result -def_graal_op(opc, "UNARY_OP", 6, 1, 1, 1) +unary_op_graal(loc, "UNARY_OP", 0x6, 1, 1, 1) # Performs a binary operation specified by the immediate operand. It has to be the ordinal of # one of {@link BinaryOps} constants. # Pops: right operand, then left operand # Pushes: result -def_graal_op(opc, "BINARY_OP", 7, 1, 2, 1) +binary_op_graal(loc, "BINARY_OP", 0x7, 1, 2, 1) # Performs subscript get operation - {@code a[b]}. # Pops: {@code b}, then {@code a} # Pushes: result -def_graal_op(opc, "BINARY_SUBSCR", 8, 0, 2, 1) +def_op_graal(loc, "BINARY_SUBSCR", 0x8, 1, 2, 0) # Performs subscript set operation - {@code a[b] = c}. # Pops: {@code b}, then {@code a}, then {@code c} -def_graal_op(opc, "STORE_SUBSCR", 9, 0, 3, 0) +def_op_graal(loc, "STORE_SUBSCR", 0x9, 0, 3, 0) # Performs subscript delete operation - {@code del a[b]}. # Pops: {@code b}, then {@code a} # -def_graal_op(opc, "DELETE_SUBSCR", 10, 0, 2, 0) +def_op_graal(loc, "DELETE_SUBSCR", 0xA, 0, 2, 0) # Gets an iterator of an object. # Pops: object # Pushes: iterator -def_graal_op(opc, "GET_ITER", 11, 0, 1, 1) +def_op_graal(loc, "GET_ITER", 0xB, 1, 1, 0) # Gets an iterator of an object, does nothing for a generator iterator or a coroutine. # Pops: object # Pushes: iterator -def_graal_op(opc, "GET_YIELD_FROM_ITER", 12, 0, 1, 1) +def_op_graal(loc, "GET_YIELD_FROM_ITER", 0xC, 0, 1, 1) # Gets an awaitable of an object. # Pops: object # Pushes: awaitable -def_graal_op(opc, "GET_AWAITABLE", 13, 0, 1, 1) - -# Gets the async iterator of an object - error if a coroutine is returned. -# Pops: object -# Pushes: async iterator -def_graal_op(opc, "GET_AITER", 14, 0, 1, 1) - -# Get the awaitable that will return the next element of an async iterator. -# Pops: object -# Pushes: awaitable -def_graal_op(opc, "GET_ANEXT", 15, 0, 1, 1) +def_op_graal(loc, "GET_AWAITABLE", 0xD, 0, 1, 1) # Pushes: {@code __build_class__} builtin -def_graal_op(opc, "LOAD_BUILD_CLASS", 16, 0, 0, 1) +def_op_graal(loc, "LOAD_BUILD_CLASS", 0xE, 0, 1, 0) # Pushes: {@code AssertionError} builtin exception type -def_graal_op(opc, "LOAD_ASSERTION_ERROR", 17, 0, 0, 1) +def_op_graal(loc, "LOAD_ASSERTION_ERROR", 0xF, 0, 0, 1) -# Returns the value to the caller. In generators, performs generator return. -# Pops: return value -def_graal_op(opc, "RETURN_VALUE", 0x12, 0, 1, 0) +def_op_graal(loc, "RETURN_VALUE", 0x10, 0, 1, 0) # This is observed # # Reads a name from locals dict, globals or builtins determined by the # immediate operand which indexes the names array ({@code co_names}). # Pushes: read object -def_graal_op(opc, "LOAD_NAME", 19, 1, 0, 1) -opc["nullaryloadop"].add(19) - +name_op_graal(loc, "LOAD_NAME", 0x11, 1, 0, 1) +loc["nullaryloadop"].add(0x13) # Writes the stack top into a name in locals dict or globals # determined by the immediate operand which indexes the names array # ({@code co_names}). # Pops: object to be written -def_graal_op(opc, "STORE_NAME", 20, 1, 1, 0) +store_op_graal(loc, "STORE_NAME", 0x14, 1, 1, "name", 1) +# observed # Deletes the name in locals dict or globals determined by the # immediate operand which indexes the names array ({@code co_names}). -def_graal_op(opc, "DELETE_NAME", 21, 1, 0, 0) +name_op_graal(loc, "DELETE_NAME", 0x15, 1, 0, 1) # Reads an attribute - {@code a.b}. {@code b} is determined by the immediate operand which # indexes the names array ({@code co_names}). # Pops: {@code a} # Pushes: read attribute -def_graal_op(opc, "LOAD_ATTR", 22, 1, 1, 1) +name_op_graal(loc, "LOAD_ATTR", 0x16, 1, 1, 1) # # Reads method on an object. The method name is determined by the # first immediate operand which indexes the names array ({@code # co_names}). # Pushes: read method -def_graal_op(opc, "LOAD_METHOD", 23, 1, 1, 2) +name_op_graal(loc, "LOAD_METHOD", 0x17, 1, 1, 1) # Writes an attribute - {@code a.b = c}. {@code b} is determined by # the immediate operand which indexes the names array ({@code # co_names}). # Pops: {@code c}, then {@code a} -def_graal_op(opc, "STORE_ATTR", 24, 1, 2, 0) +name_op_graal(loc, "STORE_ATTR", 0x18, 1, 2, 1) # Deletes an attribute - {@code del a.b}. {@code b} is determined by # the immediate operand which indexes the names array ({@code # co_names}). # Pops: {@code a} -def_graal_op(opc, "DELETE_ATTR", 25, 1, 1, 0) +name_op_graal(loc, "DELETE_ATTR", 0x19, 1, 1, 1) # Reads a global variable. The name is determined by the immediate # operand which indexes the names array ({@code co_names}). # Pushes: read object -def_graal_op(opc, "LOAD_GLOBAL", 26, 1, 0, 1) -opc["nullaryloadop"].add(26) +name_op_graal(loc, "LOAD_GLOBAL", 0x1A, 1, 0, 1) +loc["nullaryloadop"].add(0x1A) # Writes a global variable. The name is determined by the immediate # operand which indexes the names array ({@code co_names}). # Pops: value to be written -def_graal_op(opc, "STORE_GLOBAL", 27, 1, 1, 0) +name_op_graal(loc, "STORE_GLOBAL", 0x1B, 1, 1, 0) # Deletes a global variable. The name is determined by the immediate operand which indexes the # names array ({@code co_names}). -def_graal_op(opc, "DELETE_GLOBAL", 28, 1, 0, 0) +name_op_graal(loc, "DELETE_GLOBAL", 0x1C, 1, 0, 0) # Reads a constant object from constants array ({@code co_consts}). Performs no conversion. # Pushes: read constant -def_graal_op(opc, "LOAD_CONST", 29, 1, 0, 1) -opc["nullaryloadop"].add(29) +const_op_graal(loc, "LOAD_CONST", 0x1D, 1, 0, 1) +loc["nullaryloadop"].add(0x1D) # Reads a local variable determined by the immediate operand which indexes a stack slot and a # variable name in varnames array ({@code co_varnames}). # Pushes: read value -def_graal_op(opc, "LOAD_FAST", 30, 1, 0, 1) -opc["nullaryloadop"].add(30) +def_op_graal(loc, "LOAD_FAST", 0x1E, 1, 0, 1) +loc["nullaryloadop"].add(0x1E) # Writes a local variable determined by the immediate operand which indexes a stack slot and a # variable name in varnames array ({@code co_varnames}). # Pops: value to be written -def_graal_op(opc, "STORE_FAST", 31, 1, 1, 0) +def_op_graal(loc, "STORE_FAST", 0x1F, 1, 1, 1) # Deletes a local variable determined by the immediate operand which indexes a stack slot and a # variable name in varnames array ({@code co_varnames}). -def_graal_op(opc, "DELETE_FAST", 32, 1, 0, 0) +def_op_graal(loc, "DELETE_FAST", 0x20, 1, 0, 1) # Reads a local cell variable determined by the immediate operand # which indexes a stack slot after celloffset and a variable name in # cellvars or freevars array ({@code co_cellvars}, {@code # co_freevars}). # Pushes: cell contents -def_graal_op(opc, "LOAD_DEREF", 33, 1, 0, 1) +free_op_graal(loc, "LOAD_DEREF", 0x21, 1, 0, 1) + +# Deletes a local cell variable determined by the immediate operand +# which indexes a stack slot after celloffset and a variable name in +# cellvars or freevars array ({@code co_cellvars}, {@code +# co_freevars}). Note that it doesn't delete the cell, just its +# contents. +free_op_graal(loc, "DELETE_DEREF", 0x22, 1, 0, 0) # Writes a local cell variable determined by the immediate operand # which indexes a stack slot after celloffset and a variable name in @@ -209,17 +223,11 @@ def def_graal_op( # co_freevars}). # Pops: value to be written into the cell contents # FIXME: this should be tagged as both a "free" and as "store" op. -def_graal_op(opc, "STORE_DEREF", 34, 1, 1, 0) -# Deletes a local cell variable determined by the immediate operand -# which indexes a stack slot after celloffset and a variable name in -# cellvars or freevars array ({@code co_cellvars}, {@code -# co_freevars}). Note that it doesn't delete the cell, just its -# contents. -def_graal_op(opc, "DELETE_DEREF", 35, 1, 0, 0) +free_op_graal(loc, "STORE_DEREF", 0x23, 1, 1, 0) # TODO not implemented -def_graal_op(opc, "LOAD_CLASSDEREF", 36, 1, 0, 1) +def_op_graal(loc, "LOAD_CLASSDEREF", 0x24, 1, 0, 1) # Raises an exception. If the immediate operand is 0, it pops nothing # and is equivalent to {@code raise} without arguments. If the @@ -227,8 +235,8 @@ def def_graal_op( # pops {@code e}. If the immediate operand is 2, it is equivalent to # {@code raise e from c} and it pops {@code c}, then {@code e}. Other # immediate operand values are illegal. -def_graal_op( - opc, "RAISE_VARARGS", 37, 1 +def_op_graal( + loc, "RAISE_VARARGS", 0x25, 1, 0, 1 ) # , (oparg, followingArgs, withJump) -> oparg, 0) # Creates a slice object. If the immediate argument is 2, it is equivalent to a slice @@ -236,7 +244,9 @@ def def_graal_op( # equivalent to a slice {@code a:b:c}. It pops {@code c}, then {@code b}, then {@code a}. Other # immediate operand values are illegal. # Pushes: the created slice object -def_graal_op(opc, "BUILD_SLICE", 38, 1) # (oparg, followingArgs, withJump) -> oparg, 1) +def_op_graal( + loc, "BUILD_SLICE", 0x26, 1 +) # (oparg, followingArgs, withJump) -> oparg, 1) # Formats a value. If the immediate argument contains flag {@link FormatOptions#FVS_HAVE_SPEC}, # it is equivalent to {@code format(conv(v), spec)}. It pops {@code spec}, then {@code v}. @@ -244,148 +254,150 @@ def def_graal_op( # is determined by the immediate operand which contains one of the {@code FVC} options in # {@link FormatOptions}. # Pushes: the formatted value -def_graal_op( - opc, "FORMAT_VALUE", 39, 1 +def_op_graal( + loc, "FORMAT_VALUE", 0x27, 2, 1, 1 ) # , (oparg, followingArgs, withJump) -> (oparg & FormatOptions.FVS_MASK) == FormatOptions.FVS_HAVE_SPEC ? 2 : 1, 1) # Extends the immediate operand of the following instruction by its own operand shifted left by # a byte. -def_graal_op(opc, "EXTENDED_ARG", 40, 1, 0, 0) +def_op_graal(loc, "EXTENDED_ARG", 0x28, 1, 0, 0) + # Imports a module by name determined by the immediate operand which # indexes the names array ({@code co_names}). # Pops: fromlist (must be a constant {@code TruffleString[]}), then level (must be {@code int}) # Pushes: imported module -def_graal_op(opc, "IMPORT_NAME", 41, 1, 2, 1) -opc["nullaryloadop"].add(41) +name_op_graal(loc, "IMPORT_NAME", 0x29, 1, 2, 1) +loc["nullaryloadop"].add(0x2A) # Imports a name from a module. The name determined by the immediate operand which indexes the # names array ({@code co_names}). # Pops: module object # Pushes: module object, imported object -def_graal_op(opc, "IMPORT_FROM", 42, 1, 1, 2) +name_op_graal(loc, "IMPORT_FROM", 0x2B, 1, 1, 1) # Imports all names from a module of name determined by the immediate operand which indexes the # names array ({@code co_names}). The imported names are written to locals dict (can only be # invoked on module level). # Pops: level (must be {@code int}) -def_graal_op(opc, "IMPORT_STAR", 43, 1, 1, 0) +def_op_graal(loc, "IMPORT_STAR", 0x2C, 1, 1, 0) # Prints the top of the stack. Used by "single" parsing mode to echo # expressions. # Pops: the value to print -def_graal_op(opc, "PRINT_EXPR", 44, 0, 1, 0) +def_op_graal(loc, "PRINT_EXPR", 0x2D, 0, 1, 0) # Creates annotations dict in locals -def_graal_op(opc, "SETUP_ANNOTATIONS", 45, 0, 0, 0) +def_op_graal(loc, "SETUP_ANNOTATIONS", 0x2E, 0, 0, 0) # Determines if a python object is a sequence. -def_graal_op(opc, "MATCH_SEQUENCE", 46, 0, 0, 1) +def_op_graal(loc, "MATCH_SEQUENCE", 0x2F, 0, 0, 1) # Determines if a Python object is a mapping. -def_graal_op(opc, "MATCH_MAPPING", 47, 0, 0, 1) +def_op_graal(loc, "MATCH_MAPPING", 0x30, 0, 0, 1) # Determines if a Python object is of a particular type. -def_graal_op(opc, "MATCH_CLASS", 48, 1, 3, 2) +def_op_graal(loc, "MATCH_CLASS", 0x31, 1, 3, 2) # Matches the keys (stack top) in a dict (stack second). On successful # match pushes the values and True, otherwise None and False. -def_graal_op(opc, "MATCH_KEYS", 49, 0, 2, 4) +def_op_graal(loc, "MATCH_KEYS", 0x32, 0, 2, 4) # Creates a copy of a dict (stack second) without elements matching a # tuple of keys (stack top). -def_graal_op(opc, "COPY_DICT_WITHOUT_KEYS", 50, 0, 1, 1) +def_op_graal(loc, "COPY_DICT_WITHOUT_KEYS", 0x33, 0, 1, 1) # Retrieves the length of a Python object and stores it on top. -def_graal_op(opc, "GET_LEN", 51, 0, 0, 1) +def_op_graal(loc, "GET_LEN", 0x34, 0, 0, 1) # ------------------------------------- # load bytecodes for special constants # ------------------------------------- -def_graal_op(opc, "LOAD_NONE", 52, 0, 0, 1) -opc["nullaryloadop"].add(52) +def_op_graal(loc, "LOAD_NONE", 0x35, 0, 1, 0) +loc["nullaryloadop"].add(0x34) -def_graal_op(opc, "LOAD_ELLIPSIS", 53, 0, 0, 1) -def_graal_op(opc, "LOAD_TRUE", 54, 0, 0, 1) -opc["nullaryloadop"].add(54) +def_op_graal(loc, "LOAD_ELLIPSIS", 0x36, 0, 1, 0) +def_op_graal(loc, "LOAD_TRUE", 0x37, 0, 1, 0) +loc["nullaryloadop"].add(0x38) -def_graal_op(opc, "LOAD_FALSE", 55, 0, 0, 1) -opc["nullaryloadop"].add(55) +def_op_graal(loc, "LOAD_FALSE", 0x39, 0, 1, 0) +loc["nullaryloadop"].add(0x40) +def_op_graal(loc, "LOAD_BYTE", 0x41, 1, 0, 1) +# +# Loads signed byte from immediate operand. +# +# LOAD_BYTE(1, 0, 1), #### Continue adding opcode numbers here... -# Loads signed byte from immediate operand. -# -def_graal_op(opc, "LOAD_BYTE", 0x70, 1, 0, 1) -# # Loads {@code int} from primitiveConstants array indexed by the immediate operand. # -def_graal_op(opc, "LOAD_INT", 57, 1, 0, 1) +def_op_graal(loc, "LOAD_INT", 0x42, 1, 0, 1) # # Loads {@code long} from primitiveConstants array indexed by the immediate operand. # -def_graal_op(opc, "LOAD_LONG", 58, 1, 0, 1) +def_op_graal(loc, "LOAD_LONG", 0x43, 1, 0, 1) # # Loads {@code double} from primitiveConstants array indexed by the immediate operand # (converted from long). # -def_graal_op(opc, "LOAD_DOUBLE", 59, 1, 0, 1) +def_op_graal(loc, "LOAD_DOUBLE", 0x44, 1, 0, 1) # + # Creates a {@link PInt} from a {@link BigInteger} in constants array indexed by the immediate # operand. # -def_graal_op(opc, "LOAD_BIGINT", 60, 1, 0, 1) +def_op_graal(loc, "LOAD_BIGINT", 0x45, 1, 0, 1) # # Currently the same as {@link #LOAD_CONST}. # -def_graal_op(opc, "LOAD_STRING", 61, 0, 1) +const_op_graal(loc, "LOAD_STRING", 0x46, 0, 1) # # Creates python {@code bytes} from a {@code byte[]} array in constants array indexed by the # immediate operand. # -def_graal_op(opc, "LOAD_BYTES", 62, 0, 1) +def_op_graal(loc, "LOAD_BYTES", 0x47, 0, 1) # # Creates python {@code complex} from a {@code double[]} array of size 2 in constants array # indexed by the immediate operand. # -def_graal_op(opc, "LOAD_COMPLEX", 63, 1, 0, 1) +def_op_graal(loc, "LOAD_COMPLEX", 0x48, 1, 0, 1) # Creates a collection out of a Java array in constants array indexed by the immediate operand. # The second immediate operand determines the array type and kind, using values from {@link # CollectionBits}. The only allowed kinds are list and tuple. # -def_graal_op(opc, "LOAD_CONST_COLLECTION", 64, 2, 0, 1) +const_op_graal(loc, "LOAD_CONST_COLLECTION", 0x49, 2, 0, 2) # ------- # calling # ------- -# -# Calls method on an object using an array as args. The receiver is taken from the first -# element of the array. The method name is determined by the immediate operand which indexes +# calls method on an object using an array as args. the receiver is taken from the first +# element of the array. the method name is determined by the immediate operand which indexes # the names array ({@code co_names}). # -# Pops: args ({@code Object[]} of size >= 1) +# pops: args ({@code object[]} of size >= 1) # -# Pushes: call result +# pushes: call result # -def_graal_op(opc, "CALL_METHOD_VARARGS", 65, 1, 1, 1) +call_op_graal(loc, "call_method_varargs", 0x4A, 1, 1, 1) # -# Calls method on an object using a number of stack args determined by the first immediate +# calls method on an object using a number of stack args determined by the first immediate # operand. # -# Pops: multiple arguments depending on the first immediate operand, then the method and the +# pops: multiple arguments depending on the first immediate operand, then the method and the # receiver # -# Pushes: call result +# pushes: call result # -def_graal_op( - opc, "CALL_METHOD", 66, 1 +call_op_graal( + loc, "CALL_METHOD", 0x4B, 1, 1, 1 ) # , (oparg, followingArgs, withJump) -> oparg + 2, 1) # # Calls a callable using a number of stack args determined by the immediate operand. @@ -394,8 +406,8 @@ def def_graal_op( # # Pushes: call result # -def_graal_op( - opc, "CALL_FUNCTION", 67, 1 +call_op_graal( + loc, "CALL_FUNCTION", 0x4C, 2, 1, 1 ) # , (oparg, followingArgs, withJump) -> oparg + 1, 1) # # Calls a comprehension function with a single iterator argument. Comprehension functions have @@ -407,7 +419,7 @@ def def_graal_op( # # Pushes: call result # -def_graal_op(opc, "CALL_COMPREHENSION", 68, 0, 2, 1) +call_op_graal(loc, "CALL_COMPREHENSION", 0x4D, 0, 2, 1) # # Calls a callable using an arguments array and keywords array. # @@ -415,7 +427,7 @@ def def_graal_op( # # Pushes: call result # -def_graal_op(opc, "CALL_FUNCTION_KW", 69, 0, 3, 1) +call_op_graal(loc, "CALL_FUNCTION_KW", 0x4E, 0, 3, 1) # # Calls a callable using an arguments array. No keywords are passed. # @@ -423,7 +435,7 @@ def def_graal_op( # # Pushes: call result # -def_graal_op(opc, "CALL_FUNCTION_VARARGS", 70, 0, 2, 1) +def_op_graal(loc, "CALL_FUNCTION_VARARGS", 0x4F, 0, 2, 0) # ---------------------- # destructuring bytecodes @@ -435,8 +447,8 @@ def def_graal_op( # # Pushed: unpacked items, the count is determined by the immediate operand # -def_graal_op( - opc, "UNPACK_SEQUENCE", 71, 1, 1 +def_op_graal( + loc, "UNPACK_SEQUENCE", 0x47, 1, 1, 1 ) # , (oparg, followingArgs, withJump) -> oparg) # Unpacks an iterable into multiple stack items with a star item that gets the rest. The first @@ -448,8 +460,8 @@ def def_graal_op( # Pushed: unpacked items (count = first operand), star item, unpacked items (count = second # operand) # -def_graal_op( - opc, "UNPACK_EX", 73, 2, 1 +def_op_graal( + loc, "UNPACK_EX", 0x48, 2, 1, 1 ) # (oparg, followingArgs, withJump) -> oparg + 1 + Byte.toUnsignedInt(followingArgs[0])) # jumps @@ -462,24 +474,24 @@ def def_graal_op( # # Pushes (only if not jumping): the iterator, then the next value # -def_graal_op( - opc, "FOR_ITER", 74, 1, 1 +jrel_op_graal( + loc, "FOR_ITER", 0x49, 1, 1, True, True, 1 ) # (, (oparg, followingArgs, withJump) -> withJump ? 0 : 2) # # Jump forward by the offset in the immediate operand. # -def_graal_op(opc, "JUMP_FORWARD", 75, 1, 0, 0) +jrel_op_graal(loc, "JUMP_FORWARD", 0x4A, 1, 0, False, False, 1) # Jump backward by the offset in the immediate operand. May trigger OSR compilation. # -def_graal_op(opc, "JUMP_BACKWARD", 76, 1, 0, 0) +jrel_op_graal(loc, "JUMP_BACKWARD", 0x4B, 1, 0, False, False, 1) # Jump forward by the offset in the immediate operand if the top of the stack is false (in # Python sense). # # Pops (if not jumping): top of the stack -def_graal_op( - opc, "JUMP_IF_FALSE_OR_POP", 77, 3 +jrel_op_graal( + loc, "JUMP_IF_FALSE_OR_POP", 0x4C, 1, 1, True, True, 3, ) # , (oparg, followingArgs, withJump) -> withJump ? 0 : 1, 0) # Jump forward by the offset in the immediate operand if the top of the stack is true (in @@ -487,8 +499,8 @@ def def_graal_op( # # Pops (if not jumping): top of the stack # -def_graal_op( - opc, "JUMP_IF_TRUE_OR_POP", 78, 3 +jrel_op_graal( + loc, "JUMP_IF_TRUE_OR_POP", 0x4D, 1, 1, True, True, 3, ) # , (oparg, followingArgs, withJump) -> withJump ? 0 : 1, 0) # # Jump forward by the offset in the immediate operand if the top of the stack is false (in @@ -496,14 +508,14 @@ def def_graal_op( # # Pops: top of the stack # -def_graal_op(opc, "POP_AND_JUMP_IF_FALSE", 79, 3, 1, 0) +jrel_op_graal(loc, "POP_AND_JUMP_IF_FALSE", 0x4E, 1, 1, True, True, 3) # # Jump forward by the offset in the immediate operand if the top of the stack is true (in # Python sense). # # Pops: top of the stack # -def_graal_op(opc, "POP_AND_JUMP_IF_TRUE", 80, 3, 1, 0) +jrel_op_graal(loc, "POP_AND_JUMP_IF_TRUE", 0x4F, 1, 1, True, True, 3) # ---------------- @@ -515,7 +527,7 @@ def def_graal_op( # # Pushes: the cell object # -def_graal_op(opc, "LOAD_CLOSURE", 81, 1, 0, 1) +free_op_graal(loc, "LOAD_CLOSURE", 0x50, 1, 0, 1) # # Reduces multiple stack items into an array of cell objects. # @@ -523,8 +535,8 @@ def def_graal_op( # # Pushes: cell object array ({@code PCell[]}) # -def_graal_op( - opc, "CLOSURE_FROM_STACK", 82, 1 +def_op_graal( + loc, "CLOSURE_FROM_STACK", 0x51, 1 ) # , (oparg, followingArgs, withJump) -> oparg, 1) # # Creates a function object. The first immediate argument is an index to the constants array @@ -536,9 +548,10 @@ def def_graal_op( # # Pushes: created function # -def_graal_op( - opc, "MAKE_FUNCTION", 83, 2 +nargs_op_graal( + loc, "MAKE_FUNCTION", 0x52, 1, 2, 2 ) # , (oparg, followingArgs, withJump) -> Integer.bitCount(followingArgs[0]), 1) +# observed. # ------------------- # collection literals @@ -552,8 +565,8 @@ def def_graal_op( # # Pushes: new collection # -def_graal_op( - opc, "COLLECTION_FROM_STACK", 84, 1 +collection_op_graal( + loc, "COLLECTION_FROM_STACK", 0x53, 1 ) # , (oparg, followingArgs, withJump) -> CollectionBits.elementCount(oparg), 1) # # Add multiple elements from the stack to the collection below them. Collection type is @@ -561,8 +574,8 @@ def def_graal_op( # # Pops: items to be added (count = immediate argument) # -def_graal_op( - opc, "COLLECTION_ADD_STACK", 85, 1 +collection_op_graal( + loc, "COLLECTION_ADD_STACK", 0x54, 1 ) # , (oparg, followingArgs, withJump) -> CollectionBits.elementCount(oparg) + 1, 1) # # Concatenates two collection of the same type. Collection type is determined by @@ -572,7 +585,7 @@ def def_graal_op( # # Pushes: concatenated collection # -def_graal_op(opc, "COLLECTION_ADD_COLLECTION", 86, 1, 2, 1) +collection_op_graal(loc, "COLLECTION_ADD_COLLECTION", 0x55, 1, 2, 1) # # Converts collection to another type determined by {@link CollectionBits} in immediate # operand. The converted collection is expected to be an independent copy (they don't share @@ -582,7 +595,7 @@ def def_graal_op( # # Pushes: converted collection # -def_graal_op(opc, "COLLECTION_FROM_COLLECTION", 87, 1, 1, 1) +collection_op_graal(loc, "COLLECTION_FROM_COLLECTION", 0x56, 1, 1, 1) # # Converts list to tuple by reusing the underlying storage. # @@ -590,7 +603,7 @@ def def_graal_op( # # Pushes: tuple # -def_graal_op(opc, "TUPLE_FROM_LIST", 88, 0, 1, 1) +def_op_graal(loc, "TUPLE_FROM_LIST", 0x57, 0, 1, 1) # # Converts list to frozenset. # @@ -598,22 +611,22 @@ def def_graal_op( # # Pushes: frozenset # -def_graal_op(opc, "FROZENSET_FROM_LIST", 89, 0, 1, 1) +def_op_graal(loc, "FROZENSET_FROM_LIST", 0x58, 0, 1, 1) # # Adds an item to a collection that is multiple items deep under the top of the stack, # determined by the immediate argument. # # Pops: item to be added # -def_graal_op( - opc, "ADD_TO_COLLECTION", 90, 1 +collection_op_graal( + loc, "ADD_TO_COLLECTION", 0x59, 1 ) # (oparg, followingArgs, withJump) -> CollectionBits.collectionKind(oparg) == CollectionBits.KIND_DICT ? 2 : 1, 0) # # Like {@link #COLLECTION_ADD_COLLECTION} for dicts, but with checks for duplicate keys # necessary for keyword arguments merge. Note it works with dicts. Keyword arrays need to be # converted to dicts first. # -def_graal_op(opc, "KWARGS_DICT_MERGE", 91, 0, 2, 1) +def_op_graal(loc, "KWARGS_DICT_MERGE", 0x5A, 1, 0, 2, 1) # # Create a single {@link PKeyword} object. The name is determined by the immediate operand # which indexes the names array ({@code co_names}) @@ -622,7 +635,7 @@ def def_graal_op( # # Pushes: keyword object # -def_graal_op(opc, "MAKE_KEYWORD", 92, 1, 1, 1) +def_op_graal(loc, "MAKE_KEYWORD", 0x5B, 1, 1, 1) # ----------- # exceptions @@ -636,7 +649,7 @@ def def_graal_op( # # Pushes (if jumping): the exception # -def_graal_op(opc, "MATCH_EXC_OR_JUMP", 93, 3, 2, 1) +jrel_op_graal(loc, "MATCH_EXC_OR_JUMP", 0x5C, 1, 0, 1) # # Save the current exception state on the stack and set it to the exception on the stack. The # exception object is {@link PException}, not a python exception. The exception is pushed back @@ -646,24 +659,24 @@ def def_graal_op( # # Pushes: the saved exception state, the exception # -def_graal_op(opc, "PUSH_EXC_INFO", 94, 0, 0, 1) +def_op_graal(loc, "PUSH_EXC_INFO", 0x5D, 0, 0, 1) # Sets the current exception state to the saved state (by {@link #PUSH_EXC_INFO}) on the stack # and pop it. # # Pops: save exception state -def_graal_op(opc, "POP_EXCEPT", 95, 0, 1, 0) +def_op_graal(loc, "POP_EXCEPT", 0x5E, 0, 1, 0) # Restore exception state and reraise exception. # # Pops: exception to reraise, then saved exception state -def_graal_op(opc, "END_EXC_HANDLER", 96, 0, 2, 0) +def_op_graal(loc, "END_EXC_HANDLER", 0x5F, 0, 2, 0) # Gets the python-level exception object from a {@link PException}. # # Pops: a {@link PException} Pushes: python exception # -def_graal_op(opc, "UNWRAP_EXC", 97, 0, 1, 1) +def_op_graal(loc, "UNWRAP_EXC", 0x60, 0, 1, 1) # ---------- # generators @@ -674,20 +687,14 @@ def def_graal_op( # # Pops: yielded value # -def_graal_op(opc, "YIELD_VALUE", 98, 0, 1, 0) -# -# Wrap value from the stack in a {@link PAsyncGenWrappedValue}. CPython 3.11 opcode, used here -# to avoid a runtime check -# -# Pops: an object Pushes: async_generator_wrapped_value -# -def_graal_op(opc, "ASYNCGEN_WRAP", 99, 0, 1, 1) +def_op_graal(loc, "YIELD_VALUE", 0x61, 0, 1, 0) +# ### Additional opcode in 3.11 here # # Resume after yield. Will raise exception passed by {@code throw} if any. # # Pushes: value received from {@code send} or {@code None}. # -def_graal_op(opc, "RESUME_YIELD", 100, 0, 0, 1) +def_op_graal(loc, "RESUME_YIELD", 0x63, 0, 0, 1) # # Send value into a generator. Jumps forward by the offset in the immediate argument if the # generator is exhausted. Used to implement {@code yield from}. @@ -698,8 +705,8 @@ def def_graal_op( # # Pushes (if jumping): the generator return value # -def_graal_op( - opc, "SEND", 101, 1, 2 +jrel_op_graal( + loc, "SEND", 0x64, 1, 2, 1 ) # , (oparg, followingArgs, withJump) -> withJump ? 1 : 2) # Exception handler for forwarding {@code throw} calls into {@code yield from}. @@ -709,16 +716,10 @@ def def_graal_op( # Pushes (if not jumping): the generator, then the yielded value # # Pushes (if jumping): the generator return value -def_graal_op( - opc, "THROW", 102, 1, 2 +def_op_graal( + loc, "THROW", 0x65, 1, 2, 1 ) # , (oparg, followingArgs, withJump) -> withJump ? 1 : 2) -# Exception handler for async for loops. If the current exception is StopAsyncIteration, handle -# it, otherwise, reraise. -# -# Pops: exception, then the anext coroutine, then the async iterator -def_graal_op(opc, "END_ASYNC_FOR", 103, 0, 3, 0) - # with statements # # Enter a context manager and save data for its exit. @@ -727,13 +728,13 @@ def def_graal_op( # # Pushes: the context manager, then maybe-bound {@code __exit__}, then the result of # {@code __enter__} -def_graal_op(opc, "SETUP_WITH", 104, 0, 1, 3) +def_op_graal(loc, "SETUP_WITH", 0x67, 1, 0, 0) # Run the exit handler of a context manager and reraise if necessary. # # Pops: exception or {@code None}, then maybe-bound {@code __exit__}, then the context manager -def_graal_op(opc, "EXIT_WITH", 105, 0, 3, 0) +def_op_graal(loc, "EXIT_WITH", 0x68, 0, 3, 0) # Enter a context manager and save data for its exit # @@ -742,16 +743,30 @@ def def_graal_op( # Pushes: the context manager, then the maybe-bound async function {@code __aexit__}, then the # awaitable returned by {@code __aenter__} # -def_graal_op(opc, "SETUP_AWITH", 106, 0, 1, 3) +def_op_graal(loc, "SETUP_AWITH", 0x69, 0, 1, 3) # Run the exit handler of a context manager # # Pops: exception or {@code None}, then maybe-bound {@code __aexit__}, then the context manager # # Pushes: the exception or {@code None}, then the awaitable returned by {@code __aexit__} -def_graal_op(opc, "GET_AEXIT_CORO", 107, 0, 3, 2) +def_op_graal(loc, "GET_AEXIT_CORO", 0x6A, 0, 3, 2) # Reraise the exception passed to {@code __aexit__} if appropriate # #!Pops: The result of awaiting {@code __aexit__}, then the exception -def_graal_op(opc, "EXIT_AWITH", 108, 0, 2, 0) +def_op_graal(loc, "EXIT_AWITH", 0x6B, 0, 2, 0) + +# Loads signed byte from immediate operand. +# +def_op_graal(loc, "LOAD_TRUE_O", 0x6C, 1, 0, 0) +def_op_graal(loc, "LOAD_TRUE_B", 0x6D, 1, 0, 0) +def_op_graal(loc, "LOAD_FALSE_O", 0x6E, 1, 0, 0) +def_op_graal(loc, "LOAD_FALSE_B", 0x6F, 1, 0, 0) +def_op_graal(loc, "LOAD_BYTE_O", 0x70, 1, 0, 1) +def_op_graal(loc, "LOAD_BYTE_I", 0x71, 1, 0, 1) +def_op_graal(loc, "LOAD_INT_O", 0x72, 1, 0, 1) +def_op_graal(loc, "LOAD_BYTE_I", 0x73, 1, 0, 1) + + +update_sets(loc) diff --git a/xdis/opcodes/opcode_310pypy.py b/xdis/opcodes/opcode_310pypy.py index 8b677466..d4615141 100644 --- a/xdis/opcodes/opcode_310pypy.py +++ b/xdis/opcodes/opcode_310pypy.py @@ -1,4 +1,4 @@ -# (C) Copyright 2022-2024 by Rocky Bernstein +# (C) Copyright 2022-2025 by Rocky Bernstein """ PYPY 3.10 opcodes @@ -18,9 +18,10 @@ varargs_op, ) from xdis.opcodes.opcode_310 import opcode_arg_fmt310, opcode_extended_fmt310 +from xdis.version_info import PythonImplementation version_tuple = (3, 10) -python_implementation = "PyPy" +python_implementation = PythonImplementation("PyPy") loc = locals() init_opdata(loc, opcode_310, version_tuple, is_pypy=True) diff --git a/xdis/opcodes/opcode_311graal.py b/xdis/opcodes/opcode_311graal.py new file mode 100644 index 00000000..497c9eb4 --- /dev/null +++ b/xdis/opcodes/opcode_311graal.py @@ -0,0 +1,797 @@ +# (C) 2025 by Rocky Bernstein +""" +Python Graal 3.11 bytecode opcodes + +See com.oracle.graal.python/src/com/oracle/graal/python/compiler/OpCodes.java +""" + +from typing import Dict, Set + +from xdis.opcodes.base import init_opdata +from xdis.opcodes.base_graal import findlabels # noqa +from xdis.opcodes.base_graal import ( # find_linestarts, # noqa + binary_op_graal, + call_op_graal, + collection_op_graal, + const_op_graal, + def_op_graal, + free_op_graal, + jrel_op_graal, + name_op_graal, + nargs_op_graal, + store_op_graal, + unary_op_graal, + update_sets, +) +from xdis.version_info import PythonImplementation + +python_implementation = PythonImplementation("Graal") +version_tuple = (3, 11, 7) + +arg_counts: Dict[int, int] = {} + +# opcodes that perform a binary operation on the top two stack entries +binaryop: Set[int] = set([]) + +# opcodes that perform some sort of call +callop: Set[int] = set([]) + +# opcodes that perform some sort of call +collectionop: Set[int] = set([]) + +# opcodes that perform a unary operation on the toip stack entry +unaryop: Set[int] = set([]) + +loc = locals() + +init_opdata(loc, None, None) + +# Instruction opcodes for compiled code +# Blank lines correspond to available opcodes + +# If the POP field is -1 and the opcode is a var args operation +# then the operand holds the size. +# +# If the POP field is negative and the opcode is a nargs operation +# then pop the operand amount plus the negative of the POP amount. + +# Pop a single item from the stack. +def_op_graal(loc, "POP_TOP", 0x0, 0, 1, 0) + +# Exchange two top stack items. +def_op_graal(loc, "ROT_TWO", 0x1, 0, 2, 0) + +# Exchange three top stack items. [a, b, c] (a is top) becomes [b, c, a] +def_op_graal(loc, "ROT_THREE", 0x2, 0, 3, 0) + +# Exchange N top stack items. [a, b, c, ..., N] (a is top) becomes [b, c, ..., N, a]. +def_op_graal( + loc, "ROT_N", 0x3, -1, -1 +) # (oparg, followingArgs, withJump) -> oparg, (oparg, followingArgs, withJump) -> oparg) + +# Duplicates the top stack item. +def_op_graal(loc, "DUP_TOP", 0x4, 0, 1, 0) + +# Does nothing. Might still be useful to maintain a line number. +def_op_graal(loc, "NOP", 0x5, 0, 0, 0) + +# Performs a unary operation specified by the immediate operand. It +# has to be the ordinal of one of {@link UnaryOps} constants. +# Pops: operand +# Pushes: result +unary_op_graal(loc, "UNARY_OP", 0x6, 1, 1, 1) + +# Performs a binary operation specified by the immediate operand. It has to be the ordinal of +# one of {@link BinaryOps} constants. +# Pops: right operand, then left operand +# Pushes: result +binary_op_graal(loc, "BINARY_OP", 0x7, 1, 2, 1) + +# Performs subscript get operation - {@code a[b]}. +# Pops: {@code b}, then {@code a} +# Pushes: result +def_op_graal(loc, "BINARY_SUBSCR", 0x8, 0, 2, 1) + +# Performs subscript set operation - {@code a[b] = c}. +# Pops: {@code b}, then {@code a}, then {@code c} +def_op_graal(loc, "STORE_SUBSCR", 0x9, 0, 3, 0) + +# Performs subscript delete operation - {@code del a[b]}. +# Pops: {@code b}, then {@code a} +# +def_op_graal(loc, "DELETE_SUBSCR", 0xA, 0, 2, 0) + +# Gets an iterator of an object. +# Pops: object +# Pushes: iterator +def_op_graal(loc, "GET_ITER", 0xB, 1, 1, 0) + +# Gets an iterator of an object, does nothing for a generator iterator or a coroutine. +# Pops: object +# Pushes: iterator +def_op_graal(loc, "GET_YIELD_FROM_ITER", 0xC, 0, 1, 1) + +# Gets an awaitable of an object. +# Pops: object +# Pushes: awaitable +def_op_graal(loc, "GET_AWAITABLE", 0xD, 0, 1, 1) + +# Gets the async iterator of an object - error if a coroutine is returned. +# Pops: object +# Pushes: async iterator +# Not in 3.8 +def_op_graal(loc, "GET_AITER", 0xe, 0, 1, 1) + +# Get the awaitable that will return the next element of an async iterator. +# Pops: object +# Pushes: awaitable +# Not in 3.8 +def_op_graal(loc, "GET_ANEXT", 0xf, 0, 1, 1) + +# Pushes: {@code __build_class__} builtin +def_op_graal(loc, "LOAD_BUILD_CLASS", 0x10, 0, 1, 0) + +# Pushes: {@code AssertionError} builtin exception type +def_op_graal(loc, "LOAD_ASSERTION_ERROR", 0x11, 0, 0, 1) + +# Returns the value to the caller. In generators, performs generator return. +# Pops: return value +# def_op_graal(loc, "RETURN_VALUE", 0x10, 0, 1, 0) +def_op_graal(loc, "RETURN_VALUE", 0x12, 0, 1, 0) # This is observed +# +# Reads a name from locals dict, globals or builtins determined by the +# immediate operand which indexes the names array ({@code co_names}). +# Pushes: read object +name_op_graal(loc, "LOAD_NAME", 0x13, 1, 0, 1) +loc["nullaryloadop"].add(0x13) + +# Writes the stack top into a name in locals dict or globals +# determined by the immediate operand which indexes the names array +# ({@code co_names}). +# Pops: object to be written +store_op_graal(loc, "STORE_NAME", 0x14, 1, 1, "name", 1) +# observed + +# Deletes the name in locals dict or globals determined by the +# immediate operand which indexes the names array ({@code co_names}). +name_op_graal(loc, "DELETE_NAME", 0x15, 1, 0, 1) + +# Reads an attribute - {@code a.b}. {@code b} is determined by the immediate operand which +# indexes the names array ({@code co_names}). +# Pops: {@code a} +# Pushes: read attribute +name_op_graal(loc, "LOAD_ATTR", 0x16, 1, 1, 1) +# +# Reads method on an object. The method name is determined by the +# first immediate operand which indexes the names array ({@code +# co_names}). +# Pushes: read method +name_op_graal(loc, "LOAD_METHOD", 0x17, 1, 1, 1) + +# Writes an attribute - {@code a.b = c}. {@code b} is determined by +# the immediate operand which indexes the names array ({@code +# co_names}). +# Pops: {@code c}, then {@code a} +name_op_graal(loc, "STORE_ATTR", 0x18, 1, 2, 1) + +# Deletes an attribute - {@code del a.b}. {@code b} is determined by +# the immediate operand which indexes the names array ({@code +# co_names}). +# Pops: {@code a} +name_op_graal(loc, "DELETE_ATTR", 0x19, 1, 1, 1) + +# Reads a global variable. The name is determined by the immediate +# operand which indexes the names array ({@code co_names}). +# Pushes: read object +name_op_graal(loc, "LOAD_GLOBAL", 0x1A, 1, 0, 1) +loc["nullaryloadop"].add(0x1A) + + +# Writes a global variable. The name is determined by the immediate +# operand which indexes the names array ({@code co_names}). +# Pops: value to be written +name_op_graal(loc, "STORE_GLOBAL", 0x1B, 1, 1, 0) + +# Deletes a global variable. The name is determined by the immediate operand which indexes the +# names array ({@code co_names}). +name_op_graal(loc, "DELETE_GLOBAL", 0x1C, 1, 0, 0) + +# Reads a constant object from constants array ({@code co_consts}). Performs no conversion. +# Pushes: read constant +const_op_graal(loc, "LOAD_CONST", 0x1D, 1, 0, 1) +loc["nullaryloadop"].add(0x1D) + +# Reads a local variable determined by the immediate operand which indexes a stack slot and a +# variable name in varnames array ({@code co_varnames}). +# Pushes: read value +def_op_graal(loc, "LOAD_FAST", 0x1E, 1, 0, 1) +loc["nullaryloadop"].add(0x1EC) + + +# Writes a local variable determined by the immediate operand which indexes a stack slot and a +# variable name in varnames array ({@code co_varnames}). +# Pops: value to be written +def_op_graal(loc, "STORE_FAST", 0x1F, 1, 1, 1) + +# Deletes a local variable determined by the immediate operand which indexes a stack slot and a +# variable name in varnames array ({@code co_varnames}). +def_op_graal(loc, "DELETE_FAST", 0x20, 1, 0, 1) + +# Reads a local cell variable determined by the immediate operand +# which indexes a stack slot after celloffset and a variable name in +# cellvars or freevars array ({@code co_cellvars}, {@code +# co_freevars}). +# Pushes: cell contents +free_op_graal(loc, "LOAD_DEREF", 0x21, 1, 0, 1) + +# Deletes a local cell variable determined by the immediate operand +# which indexes a stack slot after celloffset and a variable name in +# cellvars or freevars array ({@code co_cellvars}, {@code +# co_freevars}). Note that it doesn't delete the cell, just its +# contents. +free_op_graal(loc, "DELETE_DEREF", 0x22, 1, 0, 0) + +# Writes a local cell variable determined by the immediate operand +# which indexes a stack slot after celloffset and a variable name in +# cellvars or freevars array ({@code co_cellvars}, {@code +# co_freevars}). +# Pops: value to be written into the cell contents +# FIXME: this should be tagged as both a "free" and as "store" op. +free_op_graal(loc, "STORE_DEREF", 0x23, 1, 1, 0) + +# TODO not implemented +def_op_graal(loc, "LOAD_CLASSDEREF", 0x24, 1, 0, 1) + +# Raises an exception. If the immediate operand is 0, it pops nothing +# and is equivalent to {@code raise} without arguments. If the +# immediate operand is 1, it is equivalent to {@code raise e} and it +# pops {@code e}. If the immediate operand is 2, it is equivalent to +# {@code raise e from c} and it pops {@code c}, then {@code e}. Other +# immediate operand values are illegal. +def_op_graal( + loc, "RAISE_VARARGS", 0x25, 1, 0, 1 +) # , (oparg, followingArgs, withJump) -> oparg, 0) + +# Creates a slice object. If the immediate argument is 2, it is equivalent to a slice +# {@code a:b}. It pops {@code b}, then {@code a}. If the immediate argument is 3, it is +# equivalent to a slice {@code a:b:c}. It pops {@code c}, then {@code b}, then {@code a}. Other +# immediate operand values are illegal. +# Pushes: the created slice object +def_op_graal( + loc, "BUILD_SLICE", 0x26, 1, 0, 1 +) # (oparg, followingArgs, withJump) -> oparg, 1) + +# Formats a value. If the immediate argument contains flag {@link FormatOptions#FVS_HAVE_SPEC}, +# it is equivalent to {@code format(conv(v), spec)}. It pops {@code spec}, then {@code v}. +# Otherwise, it is equivalent to {@code format(conv(v), None)}. It pops {@code v}. {@code conv} +# is determined by the immediate operand which contains one of the {@code FVC} options in +# {@link FormatOptions}. +# Pushes: the formatted value +def_op_graal( + loc, "FORMAT_VALUE", 0x27, 2, 1, 1 +) # , (oparg, followingArgs, withJump) -> (oparg & FormatOptions.FVS_MASK) == FormatOptions.FVS_HAVE_SPEC ? 2 : 1, 1) + +# Extends the immediate operand of the following instruction by its own operand shifted left by +# a byte. +def_op_graal(loc, "EXTENDED_ARG", 0x28, 1, 0, 0) + + +# Imports a module by name determined by the immediate operand which +# indexes the names array ({@code co_names}). +# Pops: fromlist (must be a constant {@code TruffleString[]}), then level (must be {@code int}) +# Pushes: imported module +name_op_graal(loc, "IMPORT_NAME", 0x29, 1, 2, 1) +loc["nullaryloadop"].add(0x29) + + +# Imports a name from a module. The name determined by the immediate operand which indexes the +# names array ({@code co_names}). +# Pops: module object +# Pushes: module object, imported object +name_op_graal(loc, "IMPORT_FROM", 0x2A, 1, 1, 1) + +# Imports all names from a module of name determined by the immediate operand which indexes the +# names array ({@code co_names}). The imported names are written to locals dict (can only be +# invoked on module level). +# Pops: level (must be {@code int}) +def_op_graal(loc, "IMPORT_STAR", 0x2B, 1, 1, 0) + +# Prints the top of the stack. Used by "single" parsing mode to echo +# expressions. +# Pops: the value to print +def_op_graal(loc, "PRINT_EXPR", 0x2C, 0, 1, 0) + +# Creates annotations dict in locals +def_op_graal(loc, "SETUP_ANNOTATIONS", 0x2D, 0, 0, 0) + +# Determines if a python object is a sequence. +def_op_graal(loc, "MATCH_SEQUENCE", 0x2E, 0, 0, 1) + +# Determines if a Python object is a mapping. +def_op_graal(loc, "MATCH_MAPPING", 0x2F, 0, 0, 1) + +# Determines if a Python object is of a particular type. +def_op_graal(loc, "MATCH_CLASS", 0x30, 1, 3, 2) + +# Matches the keys (stack top) in a dict (stack second). On successful +# match pushes the values and True, otherwise None and False. +def_op_graal(loc, "MATCH_KEYS", 0x31, 0, 2, 4) + +# Creates a copy of a dict (stack second) without elements matching a +# tuple of keys (stack top). +def_op_graal(loc, "COPY_DICT_WITHOUT_KEYS", 0x32, 0, 1, 1) + +# Retrieves the length of a Python object and stores it on top. +def_op_graal(loc, "GET_LEN", 0x33, 0, 0, 1) + +# ------------------------------------- +# load bytecodes for special constants +# ------------------------------------- + +def_op_graal(loc, "LOAD_NONE", 0x34, 0, 1, 0) +loc["nullaryloadop"].add(0x34) + +def_op_graal(loc, "LOAD_ELLIPSIS", 0x35, 0, 1, 0) +def_op_graal(loc, "LOAD_TRUE", 0x35, 0, 1, 0) +loc["nullaryloadop"].add(0x35) + +def_op_graal(loc, "LOAD_FALSE", 0x36, 0, 1, 0) +loc["nullaryloadop"].add(0x36) + +def_op_graal(loc, "LOAD_BYTE", 0x37, 1, 0, 1) +# +# Loads signed byte from immediate operand. +# +# LOAD_BYTE(1, 0, 1), + +#### Continue adding opcode numbers here... + + +# Loads {@code int} from primitiveConstants array indexed by the immediate operand. +# +def_op_graal(loc, "LOAD_INT", 0x38, 1, 0, 1) +# +# Loads {@code long} from primitiveConstants array indexed by the immediate operand. +# +def_op_graal(loc, "LOAD_LONG", 0x3A, 1, 0, 1) +# +# Loads {@code double} from primitiveConstants array indexed by the immediate operand +# (converted from long). +# +def_op_graal(loc, "LOAD_DOUBLE", 0x3B, 1, 0, 1) +# + +# Creates a {@link PInt} from a {@link BigInteger} in constants array indexed by the immediate +# operand. +# +def_op_graal(loc, "LOAD_BIGINT", 0x3C, 1, 0, 1) +# +# Currently the same as {@link #LOAD_CONST}. +# +const_op_graal(loc, "LOAD_STRING", 0x3D, 0, 1) + +# +# Creates python {@code bytes} from a {@code byte[]} array in constants array indexed by the +# immediate operand. +# +def_op_graal(loc, "LOAD_BYTES", 0x3E, 0, 1) +# +# Creates python {@code complex} from a {@code double[]} array of size 2 in constants array +# indexed by the immediate operand. +# +def_op_graal(loc, "LOAD_COMPLEX", 0x3F, 1, 0, 1) + +# Creates a collection out of a Java array in constants array indexed by the immediate operand. +# The second immediate operand determines the array type and kind, using values from {@link +# CollectionBits}. The only allowed kinds are list and tuple. +# +const_op_graal(loc, "LOAD_CONST_COLLECTION", 0x40, 2, 0, 2) + +# ------- +# calling +# ------- + +# calls method on an object using an array as args. the receiver is taken from the first +# element of the array. the method name is determined by the immediate operand which indexes +# the names array ({@code co_names}). +# +# pops: args ({@code object[]} of size >= 1) +# +# pushes: call result +# +call_op_graal(loc, "call_method_varargs", 0x41, 1, 1, 1) +# +# calls method on an object using a number of stack args determined by the first immediate +# operand. +# +# pops: multiple arguments depending on the first immediate operand, then the method and the +# receiver +# +# pushes: call result +# +call_op_graal( + loc, "CALL_METHOD", 0x42, 1, 1, 1 +) # , (oparg, followingArgs, withJump) -> oparg + 2, 1) +# +# Calls a callable using a number of stack args determined by the immediate operand. +# +# Pops: multiple arguments depending on the immediate operand (0 - 4), then the callable +# +# Pushes: call result +# +call_op_graal( + loc, "CALL_FUNCTION", 0x43, 2, 1, 1 +) # , (oparg, followingArgs, withJump) -> oparg + 1, 1) +# +# Calls a comprehension function with a single iterator argument. Comprehension functions have +# to always have the same call target at given calls site. The instruction makes use of this +# fact and bypasses function object inline caching which would otherwise slow down warmup since +# comprehensions functions are always created anew and thus the cache would always miss. +# +# Pops: iterator, then the function +# +# Pushes: call result +# +call_op_graal(loc, "CALL_COMPREHENSION", 0x44, 0, 2, 1) +# +# Calls a callable using an arguments array and keywords array. +# +# Pops: keyword args ({@code PKeyword[]}), then args ({@code Object[]}), then callable +# +# Pushes: call result +# +call_op_graal(loc, "CALL_FUNCTION_KW", 0x45, 0, 3, 1) +# +# Calls a callable using an arguments array. No keywords are passed. +# +# Pops: args ({@code Object[]}), then callable +# +# Pushes: call result +# +def_op_graal(loc, "CALL_FUNCTION_VARARGS", 0x46, 0, 2, 0) + +# ---------------------- +# destructuring bytecodes +# ---------------------- + +# Unpacks an iterable into multiple stack items. +# +# Pops: iterable +# +# Pushed: unpacked items, the count is determined by the immediate operand +# +def_op_graal( + loc, "UNPACK_SEQUENCE", 0x47, 1, 1, 1 +) # , (oparg, followingArgs, withJump) -> oparg) + +# Unpacks an iterable into multiple stack items with a star item that gets the rest. The first +# immediate operand determines the count before the star item, the second determines the count +# after. +# +# Pops: iterable +# +# Pushed: unpacked items (count = first operand), star item, unpacked items (count = second +# operand) +# +def_op_graal( + loc, "UNPACK_EX", 0x48, 2, 1, 1 +) # (oparg, followingArgs, withJump) -> oparg + 1 + Byte.toUnsignedInt(followingArgs[0])) + +# jumps + +# +# Get next value from an iterator. If the iterable is exhausted, jump forward by the offset in +# the immediate argument. +# +# Pops: iterator +# +# Pushes (only if not jumping): the iterator, then the next value +# +jrel_op_graal( + loc, "FOR_ITER", 0x49, 1, 1, True, True, 1 +) # (, (oparg, followingArgs, withJump) -> withJump ? 0 : 2) +# +# Jump forward by the offset in the immediate operand. +# +jrel_op_graal(loc, "JUMP_FORWARD", 0x4A, 1, 0, False, False, 1) + +# Jump backward by the offset in the immediate operand. May trigger OSR compilation. +# +jrel_op_graal(loc, "JUMP_BACKWARD", 0x4B, 1, 0, False, False, 1) + +# Jump forward by the offset in the immediate operand if the top of the stack is false (in +# Python sense). +# +# Pops (if not jumping): top of the stack +jrel_op_graal( + loc, "JUMP_IF_FALSE_OR_POP", 0x4C, 1, 1, True, True, 3, +) # , (oparg, followingArgs, withJump) -> withJump ? 0 : 1, 0) + +# Jump forward by the offset in the immediate operand if the top of the stack is true (in +# Python sense). +# +# Pops (if not jumping): top of the stack +# +jrel_op_graal( + loc, "JUMP_IF_TRUE_OR_POP", 0x4D, 1, 1, True, True, 3, +) # , (oparg, followingArgs, withJump) -> withJump ? 0 : 1, 0) +# +# Jump forward by the offset in the immediate operand if the top of the stack is false (in +# Python sense). +# +# Pops: top of the stack +# +jrel_op_graal(loc, "POP_AND_JUMP_IF_FALSE", 0x4E, 1, 1, True, True, 3) +# +# Jump forward by the offset in the immediate operand if the top of the stack is true (in +# Python sense). +# +# Pops: top of the stack +# +jrel_op_graal(loc, "POP_AND_JUMP_IF_TRUE", 0x4F, 1, 1, True, True, 3) + + +# ---------------- +# making callables +# ---------------- + +# +# Like {@link #LOAD_DEREF}, but loads the cell itself, not the contents. +# +# Pushes: the cell object +# +free_op_graal(loc, "LOAD_CLOSURE", 0x50, 1, 0, 1) +# +# Reduces multiple stack items into an array of cell objects. +# +# Pops: multiple cells (count = immediate argument) +# +# Pushes: cell object array ({@code PCell[]}) +# +def_op_graal( + loc, "CLOSURE_FROM_STACK", 0x51, 1 +) # , (oparg, followingArgs, withJump) -> oparg, 1) +# +# Creates a function object. The first immediate argument is an index to the constants array +# that determines the {@link CodeUnit} object that will provide the function's code. +# +# Pops: The second immediate arguments contains flags (defined in {@link CodeUnit}) that +# determine whether it will need to pop (in this order): closure, annotations, keyword only +# defaults, defaults. +# +# Pushes: created function +# +nargs_op_graal( + loc, "MAKE_FUNCTION", 0x52, 1, 2, 2 +) # , (oparg, followingArgs, withJump) -> Integer.bitCount(followingArgs[0]), 1) +# observed. + +# ------------------- +# collection literals +# ------------------- + +# +# Creates a collection from multiple elements from the stack. Collection type is determined by +# {@link CollectionBits} in immediate operand. +# +# Pops: items for the collection (count = immediate argument) +# +# Pushes: new collection +# +collection_op_graal( + loc, "COLLECTION_FROM_STACK", 0x53, 1 +) # , (oparg, followingArgs, withJump) -> CollectionBits.elementCount(oparg), 1) +# +# Add multiple elements from the stack to the collection below them. Collection type is +# determined by {@link CollectionBits} in immediate operand. Tuple is not supported. +# +# Pops: items to be added (count = immediate argument) +# +collection_op_graal( + loc, "COLLECTION_ADD_STACK", 0x54, 1 +) # , (oparg, followingArgs, withJump) -> CollectionBits.elementCount(oparg) + 1, 1) +# +# Concatenates two collection of the same type. Collection type is determined by +# {@link CollectionBits} in immediate operand. Tuple is not supported. +# +# Pops: second collection, first collection +# +# Pushes: concatenated collection +# +collection_op_graal(loc, "COLLECTION_ADD_COLLECTION", 0x55, 1, 2, 1) +# +# Converts collection to another type determined by {@link CollectionBits} in immediate +# operand. The converted collection is expected to be an independent copy (they don't share +# storage). +# +# Pops: original collection +# +# Pushes: converted collection +# +collection_op_graal(loc, "COLLECTION_FROM_COLLECTION", 0x56, 1, 1, 1) +# +# Converts list to tuple by reusing the underlying storage. +# +# Pops: list +# +# Pushes: tuple +# +def_op_graal(loc, "TUPLE_FROM_LIST", 0x57, 0, 1, 1) +# +# Converts list to frozenset. +# +# Pops: list +# +# Pushes: frozenset +# +def_op_graal(loc, "FROZENSET_FROM_LIST", 0x58, 0, 1, 1) +# +# Adds an item to a collection that is multiple items deep under the top of the stack, +# determined by the immediate argument. +# +# Pops: item to be added +# +collection_op_graal( + loc, "ADD_TO_COLLECTION", 0x59, 1 +) # (oparg, followingArgs, withJump) -> CollectionBits.collectionKind(oparg) == CollectionBits.KIND_DICT ? 2 : 1, 0) +# +# Like {@link #COLLECTION_ADD_COLLECTION} for dicts, but with checks for duplicate keys +# necessary for keyword arguments merge. Note it works with dicts. Keyword arrays need to be +# converted to dicts first. +# +def_op_graal(loc, "KWARGS_DICT_MERGE", 0x5A, 1, 0, 2, 1) +# +# Create a single {@link PKeyword} object. The name is determined by the immediate operand +# which indexes the names array ({@code co_names}) +# +# Pops: keyword value +# +# Pushes: keyword object +# +def_op_graal(loc, "MAKE_KEYWORD", 0x5B, 1, 1, 1) + +# ----------- +# exceptions +# ----------- + +# +# Jump forward by the offset in the immediate argument if the exception doesn't match the +# expected type. The exception object is {@link PException}, not a python exception. +# +# Pops: expected type, then exception +# +# Pushes (if jumping): the exception +# +jrel_op_graal(loc, "MATCH_EXC_OR_JUMP", 0x5C, 1, 0, 1) +# +# Save the current exception state on the stack and set it to the exception on the stack. The +# exception object is {@link PException}, not a python exception. The exception is pushed back +# to the top. +# +# Pops: the exception +# +# Pushes: the saved exception state, the exception +# +def_op_graal(loc, "PUSH_EXC_INFO", 0x5D, 0, 0, 1) + +# Sets the current exception state to the saved state (by {@link #PUSH_EXC_INFO}) on the stack +# and pop it. +# +# Pops: save exception state +def_op_graal(loc, "POP_EXCEPT", 0x5E, 0, 1, 0) + +# Restore exception state and reraise exception. +# +# Pops: exception to reraise, then saved exception state +def_op_graal(loc, "END_EXC_HANDLER", 0x5F, 0, 2, 0) + +# Gets the python-level exception object from a {@link PException}. +# +# Pops: a {@link PException} Pushes: python exception +# +def_op_graal(loc, "UNWRAP_EXC", 0x60, 0, 1, 1) + +# ---------- +# generators +# ---------- +# +# Yield value from the stack to the caller. Saves execution state. The generator will resume at +# the next instruction. +# +# Pops: yielded value +# +def_op_graal(loc, "YIELD_VALUE", 0x61, 0, 1, 0) +# +# Wrap value from the stack in a {@link PAsyncGenWrappedValue}. CPython 3.11 opcode, used here +# to avoid a runtime check +# +# Pops: an object Pushes: async_generator_wrapped_value +# +def_op_graal(loc, "ASYNCGEN_WRAP", 0x62, 0, 1, 1) +# +# Resume after yield. Will raise exception passed by {@code throw} if any. +# +# Pushes: value received from {@code send} or {@code None}. +# +def_op_graal(loc, "RESUME_YIELD", 0x63, 0, 0, 1) +# +# Send value into a generator. Jumps forward by the offset in the immediate argument if the +# generator is exhausted. Used to implement {@code yield from}. +# +# Pops: value to be sent, then generator +# +# Pushes (if not jumping): the generator, then the yielded value +# +# Pushes (if jumping): the generator return value +# +jrel_op_graal( + loc, "SEND", 0x64, 1, 2, 1 +) # , (oparg, followingArgs, withJump) -> withJump ? 1 : 2) + +# Exception handler for forwarding {@code throw} calls into {@code yield from}. +# +# Pops: exception, then the generator +# +# Pushes (if not jumping): the generator, then the yielded value +# +# Pushes (if jumping): the generator return value +def_op_graal( + loc, "THROW", 0x65, 1, 2, 1 +) # , (oparg, followingArgs, withJump) -> withJump ? 1 : 2) + +# Exception handler for async for loops. If the current exception is StopAsyncIteration, handle +# it, otherwise, reraise. +# +# Pops: exception, then the anext coroutine, then the async iterator +def_op_graal(loc, "END_ASYNC_FOR", 0x66, 0, 3, 0) + +# with statements +# +# Enter a context manager and save data for its exit. +# +# Pops: the context manager +# +# Pushes: the context manager, then maybe-bound {@code __exit__}, then the result of +# {@code __enter__} +def_op_graal(loc, "SETUP_WITH", 0x67, 1, 0, 0) + +# Run the exit handler of a context manager and reraise if necessary. +# +# Pops: exception or {@code None}, then maybe-bound {@code __exit__}, then the context manager + +def_op_graal(loc, "EXIT_WITH", 0x68, 0, 3, 0) + +# Enter a context manager and save data for its exit +# +# Pops: the context manager +# +# Pushes: the context manager, then the maybe-bound async function {@code __aexit__}, then the +# awaitable returned by {@code __aenter__} +# +def_op_graal(loc, "SETUP_AWITH", 0x69, 0, 1, 3) + +# Run the exit handler of a context manager +# +# Pops: exception or {@code None}, then maybe-bound {@code __aexit__}, then the context manager +# +# Pushes: the exception or {@code None}, then the awaitable returned by {@code __aexit__} +def_op_graal(loc, "GET_AEXIT_CORO", 0x6A, 0, 3, 2) + +# Reraise the exception passed to {@code __aexit__} if appropriate +# +#!Pops: The result of awaiting {@code __aexit__}, then the exception +def_op_graal(loc, "EXIT_AWITH", 0x6B, 0, 2, 0) + +# Loads signed byte from immediate operand. +# +def_op_graal(loc, "LOAD_TRUE_O", 0x6C, 1, 0, 0) +def_op_graal(loc, "LOAD_TRUE_B", 0x6D, 1, 0, 0) +def_op_graal(loc, "LOAD_FALSE_O", 0x6E, 1, 0, 0) +def_op_graal(loc, "LOAD_FALSE_B", 0x6F, 1, 0, 0) +def_op_graal(loc, "LOAD_BYTE_O", 0x70, 1, 0, 1) +def_op_graal(loc, "LOAD_BYTE_I", 0x71, 1, 0, 1) +def_op_graal(loc, "LOAD_INT_O", 0x72, 1, 0, 1) +def_op_graal(loc, "LOAD_INT_I", 0x78, 1, 0, 1) + + +update_sets(loc) diff --git a/xdis/opcodes/opcode_311pypy.py b/xdis/opcodes/opcode_311pypy.py index 559ce3f8..5734aa3c 100644 --- a/xdis/opcodes/opcode_311pypy.py +++ b/xdis/opcodes/opcode_311pypy.py @@ -18,9 +18,10 @@ varargs_op, ) from xdis.opcodes.opcode_311 import opcode_arg_fmt311, opcode_extended_fmt311 +from xdis.version_info import PythonImplementation version_tuple = (3, 11) -python_implementation = "PyPy" +python_implementation = PythonImplementation("PyPy") loc = locals() init_opdata(loc, opcode_310, version_tuple, is_pypy=True) diff --git a/xdis/opcodes/opcode_312.py b/xdis/opcodes/opcode_312.py index 976627f0..0a0e0070 100644 --- a/xdis/opcodes/opcode_312.py +++ b/xdis/opcodes/opcode_312.py @@ -191,7 +191,7 @@ def format_CALL_INTRINSIC_2(arg) -> str: }, } -from xdis.opcodes.opcode_311 import findlinestarts +from xdis.opcodes.opcode_311 import findlinestarts # noqa update_pj3(globals(), loc) finalize_opcodes(loc) diff --git a/xdis/opcodes/opcode_312graal.py b/xdis/opcodes/opcode_312graal.py new file mode 100644 index 00000000..3906bc43 --- /dev/null +++ b/xdis/opcodes/opcode_312graal.py @@ -0,0 +1,801 @@ +# (C) 2025 by Rocky Bernstein +""" +Python Graal 3.12 bytecode opcodes + +See com.oracle.graal.python/src/com/oracle/graal/python/compiler/OpCodes.java +""" + +from typing import Dict, Set + +from xdis.opcodes.base import init_opdata +from xdis.opcodes.base_graal import findlabels # noqa +from xdis.opcodes.base_graal import ( # find_linestarts, # noqa + binary_op_graal, + call_op_graal, + collection_op_graal, + const_op_graal, + def_op_graal, + free_op_graal, + jrel_op_graal, + name_op_graal, + nargs_op_graal, + store_op_graal, + unary_op_graal, + update_sets, +) +from xdis.version_info import PythonImplementation + +python_implementation = PythonImplementation("Graal") +version_tuple = (3, 12, 7) + +arg_counts: Dict[int, int] = {} + +# opcodes that perform a binary operation on the top two stack entries +binaryop: Set[int] = set([]) + +# opcodes that perform some sort of call +callop: Set[int] = set([]) + +# opcodes that perform some sort of call +collectionop: Set[int] = set([]) + +# opcodes that perform a unary operation on the toip stack entry +unaryop: Set[int] = set([]) + +loc = locals() + +init_opdata(loc, None, None) + +# Instruction opcodes for compiled code +# Blank lines correspond to available opcodes + +# If the POP field is -1 and the opcode is a var args operation +# then the operand holds the size. +# +# If the POP field is negative and the opcode is a nargs operation +# then pop the operand amount plus the negative of the POP amount. + +# Pop a single item from the stack. +def_op_graal(loc, "POP_TOP", 0x0, 0, 1, 0) + +# Exchange two top stack items. +def_op_graal(loc, "ROT_TWO", 0x1, 0, 2, 0) + +# Exchange three top stack items. [a, b, c] (a is top) becomes [b, c, a] +def_op_graal(loc, "ROT_THREE", 0x2, 0, 3, 0) + +# Exchange N top stack items. [a, b, c, ..., N] (a is top) becomes [b, c, ..., N, a]. +def_op_graal( + loc, "ROT_N", 0x3, -1, -1 +) # (oparg, followingArgs, withJump) -> oparg, (oparg, followingArgs, withJump) -> oparg) + +# Duplicates the top stack item. +def_op_graal(loc, "DUP_TOP", 0x4, 0, 1, 0) + +# Does nothing. Might still be useful to maintain a line number. +def_op_graal(loc, "NOP", 0x5, 0, 0, 0) + +# Performs a unary operation specified by the immediate operand. It +# has to be the ordinal of one of {@link UnaryOps} constants. +# Pops: operand +# Pushes: result +unary_op_graal(loc, "UNARY_OP", 0x6, 1, 1, 1) + +# Performs a binary operation specified by the immediate operand. It has to be the ordinal of +# one of {@link BinaryOps} constants. +# Pops: right operand, then left operand +# Pushes: result +binary_op_graal(loc, "BINARY_OP", 0x7, 1, 2, 1) + +# Performs subscript get operation - {@code a[b]}. +# Pops: {@code b}, then {@code a} +# Pushes: result +def_op_graal(loc, "BINARY_SUBSCR", 0x8, 1, 2, 0) + +# Performs subscript set operation - {@code a[b] = c}. +# Pops: {@code b}, then {@code a}, then {@code c} +def_op_graal(loc, "STORE_SUBSCR", 0x9, 0, 3, 0) + +# Performs subscript delete operation - {@code del a[b]}. +# Pops: {@code b}, then {@code a} +# +def_op_graal(loc, "DELETE_SUBSCR", 0xA, 0, 2, 0) + +# Gets an iterator of an object. +# Pops: object +# Pushes: iterator +def_op_graal(loc, "GET_ITER", 0xB, 1, 1, 0) + +# Gets an iterator of an object, does nothing for a generator iterator or a coroutine. +# Pops: object +# Pushes: iterator +def_op_graal(loc, "GET_YIELD_FROM_ITER", 0xC, 0, 1, 1) + +# Gets an awaitable of an object. +# Pops: object +# Pushes: awaitable +def_op_graal(loc, "GET_AWAITABLE", 0xD, 0, 1, 1) + +# Gets the async iterator of an object - error if a coroutine is returned. +# Pops: object +# Pushes: async iterator +# Not in 3.8 +def_op_graal(loc, "GET_AITER", 0xe, 0, 1, 1) + +# Get the awaitable that will return the next element of an async iterator. +# Pops: object +# Pushes: awaitable +# Not in 3.8 +def_op_graal(loc, "GET_ANEXT", 0xf, 0, 1, 1) + +# Pushes: {@code __build_class__} builtin +def_op_graal(loc, "LOAD_BUILD_CLASS", 0x10, 0, 1, 0) + +# Pushes: {@code AssertionError} builtin exception type +def_op_graal(loc, "LOAD_ASSERTION_ERROR", 0x11, 0, 0, 1) + +# Returns the value to the caller. In generators, performs generator return. +# Pops: return value +def_op_graal(loc, "RETURN_VALUE", 0x12, 0, 1, 0) +# +# Reads a name from locals dict, globals or builtins determined by the +# immediate operand which indexes the names array ({@code co_names}). +# Pushes: read object +name_op_graal(loc, "LOAD_NAME", 0x13, 1, 0, 1) +loc["nullaryloadop"].add(19) + +# Writes the stack top into a name in locals dict or globals +# determined by the immediate operand which indexes the names array +# ({@code co_names}). +# Pops: object to be written +store_op_graal(loc, "STORE_NAME", 0x14, 1, 1, "name", 1) +# observed + +# Deletes the name in locals dict or globals determined by the +# immediate operand which indexes the names array ({@code co_names}). +name_op_graal(loc, "DELETE_NAME", 0x15, 1, 0, 1) + +# Reads an attribute - {@code a.b}. {@code b} is determined by the immediate operand which +# indexes the names array ({@code co_names}). +# Pops: {@code a} +# Pushes: read attribute +name_op_graal(loc, "LOAD_ATTR", 0x16, 1, 1, 1) +# +# Reads method on an object. The method name is determined by the +# first immediate operand which indexes the names array ({@code +# co_names}). +# Pushes: read method +name_op_graal(loc, "LOAD_METHOD", 0x17, 1, 1, 1) + +# Writes an attribute - {@code a.b = c}. {@code b} is determined by +# the immediate operand which indexes the names array ({@code +# co_names}). +# Pops: {@code c}, then {@code a} +name_op_graal(loc, "STORE_ATTR", 0x18, 1, 2, 1) + +# Deletes an attribute - {@code del a.b}. {@code b} is determined by +# the immediate operand which indexes the names array ({@code +# co_names}). +# Pops: {@code a} +name_op_graal(loc, "DELETE_ATTR", 0x19, 1, 1, 1) + +# Reads a global variable. The name is determined by the immediate +# operand which indexes the names array ({@code co_names}). +# Pushes: read object +name_op_graal(loc, "LOAD_GLOBAL", 0x1A, 1, 0, 1) +loc["nullaryloadop"].add(0x1A) + + +# Writes a global variable. The name is determined by the immediate +# operand which indexes the names array ({@code co_names}). +# Pops: value to be written +name_op_graal(loc, "STORE_GLOBAL", 0x1B, 1, 1, 0) + +# Deletes a global variable. The name is determined by the immediate operand which indexes the +# names array ({@code co_names}). +name_op_graal(loc, "DELETE_GLOBAL", 0x1C, 1, 0, 0) + +# Reads a constant object from constants array ({@code co_consts}). Performs no conversion. +# Pushes: read constant +const_op_graal(loc, "LOAD_CONST", 0x1D, 1, 0, 1) +loc["nullaryloadop"].add(0x1D) + +# Reads a local variable determined by the immediate operand which indexes a stack slot and a +# variable name in varnames array ({@code co_varnames}). +# Pushes: read value +def_op_graal(loc, "LOAD_FAST", 0x1E, 1, 0, 1) +loc["nullaryloadop"].add(0x1EC) + + +# Writes a local variable determined by the immediate operand which indexes a stack slot and a +# variable name in varnames array ({@code co_varnames}). +# Pops: value to be written +def_op_graal(loc, "STORE_FAST", 0x1F, 1, 1, 1) + +# Deletes a local variable determined by the immediate operand which indexes a stack slot and a +# variable name in varnames array ({@code co_varnames}). +def_op_graal(loc, "DELETE_FAST", 0x20, 1, 0, 1) + +# Reads a local cell variable determined by the immediate operand +# which indexes a stack slot after celloffset and a variable name in +# cellvars or freevars array ({@code co_cellvars}, {@code +# co_freevars}). +# Pushes: cell contents +free_op_graal(loc, "LOAD_DEREF", 0x21, 1, 0, 1) + +# Deletes a local cell variable determined by the immediate operand +# which indexes a stack slot after celloffset and a variable name in +# cellvars or freevars array ({@code co_cellvars}, {@code +# co_freevars}). Note that it doesn't delete the cell, just its +# contents. +free_op_graal(loc, "DELETE_DEREF", 0x22, 1, 0, 0) + +# Writes a local cell variable determined by the immediate operand +# which indexes a stack slot after celloffset and a variable name in +# cellvars or freevars array ({@code co_cellvars}, {@code +# co_freevars}). +# Pops: value to be written into the cell contents +# FIXME: this should be tagged as both a "free" and as "store" op. +free_op_graal(loc, "STORE_DEREF", 0x23, 1, 1, 0) + +# TODO not implemented +def_op_graal(loc, "LOAD_CLASSDEREF", 0x24, 1, 0, 1) + +# Raises an exception. If the immediate operand is 0, it pops nothing +# and is equivalent to {@code raise} without arguments. If the +# immediate operand is 1, it is equivalent to {@code raise e} and it +# pops {@code e}. If the immediate operand is 2, it is equivalent to +# {@code raise e from c} and it pops {@code c}, then {@code e}. Other +# immediate operand values are illegal. +def_op_graal( + loc, "RAISE_VARARGS", 0x27, 1, 0, 1 +) # , (oparg, followingArgs, withJump) -> oparg, 0) + +# Creates a slice object. If the immediate argument is 2, it is equivalent to a slice +# {@code a:b}. It pops {@code b}, then {@code a}. If the immediate argument is 3, it is +# equivalent to a slice {@code a:b:c}. It pops {@code c}, then {@code b}, then {@code a}. Other +# immediate operand values are illegal. +# Pushes: the created slice object +def_op_graal( + loc, "BUILD_SLICE", 0x28, 1, 0, 1 +) # (oparg, followingArgs, withJump) -> oparg, 1) + +# Formats a value. If the immediate argument contains flag {@link FormatOptions#FVS_HAVE_SPEC}, +# it is equivalent to {@code format(conv(v), spec)}. It pops {@code spec}, then {@code v}. +# Otherwise, it is equivalent to {@code format(conv(v), None)}. It pops {@code v}. {@code conv} +# is determined by the immediate operand which contains one of the {@code FVC} options in +# {@link FormatOptions}. +# Pushes: the formatted value +def_op_graal( + loc, "FORMAT_VALUE", 0x29, 2, 1, 1 +) # , (oparg, followingArgs, withJump) -> (oparg & FormatOptions.FVS_MASK) == FormatOptions.FVS_HAVE_SPEC ? 2 : 1, 1) + +# Extends the immediate operand of the following instruction by its own operand shifted left by +# a byte. +def_op_graal(loc, "EXTENDED_ARG", 0x2A, 1, 0, 0) + + +# Imports a module by name determined by the immediate operand which +# indexes the names array ({@code co_names}). +# Pops: fromlist (must be a constant {@code TruffleString[]}), then level (must be {@code int}) +# Pushes: imported module +name_op_graal(loc, "IMPORT_NAME", 0x2B, 1, 2, 1) +loc["nullaryloadop"].add(0x2B) + + +# Imports a name from a module. The name determined by the immediate operand which indexes the +# names array ({@code co_names}). +# Pops: module object +# Pushes: module object, imported object +name_op_graal(loc, "IMPORT_FROM", 0x2C, 1, 1, 1) + +# Imports all names from a module of name determined by the immediate operand which indexes the +# names array ({@code co_names}). The imported names are written to locals dict (can only be +# invoked on module level). +# Pops: level (must be {@code int}) +def_op_graal(loc, "IMPORT_STAR", 0x2D, 1, 1, 0) + +# Prints the top of the stack. Used by "single" parsing mode to echo +# expressions. +# Pops: the value to print +def_op_graal(loc, "PRINT_EXPR", 0x2E, 0, 1, 0) + +# Creates annotations dict in locals +def_op_graal(loc, "SETUP_ANNOTATIONS", 100, 0, 0, 0) + +# Determines if a python object is a sequence. +def_op_graal(loc, "MATCH_SEQUENCE", 101, 0, 0, 1) + +# Determines if a Python object is a mapping. +def_op_graal(loc, "MATCH_MAPPING", 102, 0, 0, 1) + +# Determines if a Python object is of a particular type. +def_op_graal(loc, "MATCH_CLASS", 102, 1, 3, 2) + +# Matches the keys (stack top) in a dict (stack second). On successful +# match pushes the values and True, otherwise None and False. +def_op_graal(loc, "MATCH_KEYS", 0x2F, 0, 2, 4) + +# Creates a copy of a dict (stack second) without elements matching a +# tuple of keys (stack top). +def_op_graal(loc, "COPY_DICT_WITHOUT_KEYS", 0x30, 0, 1, 1) + +# Retrieves the length of a Python object and stores it on top. +def_op_graal(loc, "GET_LEN", 0x31, 0, 0, 1) + +# ------------------------------------- +# load bytecodes for special constants +# ------------------------------------- + +def_op_graal(loc, "LOAD_NONE", 0x36, 0, 1, 0) +loc["nullaryloadop"].add(0x36) + +def_op_graal(loc, "LOAD_ELLIPSIS", 0x37, 0, 1, 0) +def_op_graal(loc, "LOAD_TRUE", 0x38, 0, 1, 0) +loc["nullaryloadop"].add(0x32) + +def_op_graal(loc, "LOAD_FALSE", 0x39, 0, 1, 0) +loc["nullaryloadop"].add(0x38) + + +#### Continue adding opcode numbers here... + + +# Loads {@code int} from primitiveConstants array indexed by the immediate operand. +# +def_op_graal(loc, "LOAD_INT", 0x39, 1, 0, 1) +# +# Loads {@code long} from primitiveConstants array indexed by the immediate operand. +# +def_op_graal(loc, "LOAD_LONG", 0x3A, 1, 0, 1) +# +# Loads {@code double} from primitiveConstants array indexed by the immediate operand +# (converted from long). +# +def_op_graal(loc, "LOAD_DOUBLE", 0x3B, 1, 0, 1) +# + +# Creates a {@link PInt} from a {@link BigInteger} in constants array indexed by the immediate +# operand. +# +def_op_graal(loc, "LOAD_BIGINT", 0x3C, 1, 0, 1) +# +# Currently the same as {@link #LOAD_CONST}. +# +const_op_graal(loc, "LOAD_STRING", 0x3F, 0, 1) + +# +# Creates python {@code bytes} from a {@code byte[]} array in constants array indexed by the +# immediate operand. +# +def_op_graal(loc, "LOAD_BYTES", 0x40, 0, 1) +# +# Creates python {@code complex} from a {@code double[]} array of size 2 in constants array +# indexed by the immediate operand. +# +def_op_graal(loc, "LOAD_COMPLEX", 0x41, 1, 0, 1) + +# Creates a collection out of a Java array in constants array indexed by the immediate operand. +# The second immediate operand determines the array type and kind, using values from {@link +# CollectionBits}. The only allowed kinds are list and tuple. +# +const_op_graal(loc, "LOAD_CONST_COLLECTION", 0x42, 2, 0, 2) + +# ------- +# calling +# ------- + +# calls method on an object using an array as args. the receiver is taken from the first +# element of the array. the method name is determined by the immediate operand which indexes +# the names array ({@code co_names}). +# +# pops: args ({@code object[]} of size >= 1) +# +# pushes: call result +# +call_op_graal(loc, "call_method_varargs", 65, 1, 1, 1) +# +# calls method on an object using a number of stack args determined by the first immediate +# operand. +# +# pops: multiple arguments depending on the first immediate operand, then the method and the +# receiver +# +# pushes: call result +# +call_op_graal( + loc, "CALL_METHOD", 0x44, 1, 1, 1 +) # , (oparg, followingArgs, withJump) -> oparg + 2, 1) +# +# Calls a callable using a number of stack args determined by the immediate operand. +# +# Pops: multiple arguments depending on the immediate operand (0 - 4), then the callable +# +# Pushes: call result +# +call_op_graal( + loc, "CALL_FUNCTION", 0x45, 2, 1, 1 +) # , (oparg, followingArgs, withJump) -> oparg + 1, 1) +# +# Calls a comprehension function with a single iterator argument. Comprehension functions have +# to always have the same call target at given calls site. The instruction makes use of this +# fact and bypasses function object inline caching which would otherwise slow down warmup since +# comprehensions functions are always created anew and thus the cache would always miss. +# +# Pops: iterator, then the function +# +# Pushes: call result +# +call_op_graal(loc, "CALL_COMPREHENSION", 0x46, 0, 2, 1) +# +# Calls a callable using an arguments array and keywords array. +# +# Pops: keyword args ({@code PKeyword[]}), then args ({@code Object[]}), then callable +# +# Pushes: call result +# +call_op_graal(loc, "CALL_FUNCTION_KW", 0x47, 0, 3, 1) +# +# Calls a callable using an arguments array. No keywords are passed. +# +# Pops: args ({@code Object[]}), then callable +# +# Pushes: call result +# +def_op_graal(loc, "CALL_FUNCTION_VARARGS", 0x48, 0, 2, 0) + +# ---------------------- +# destructuring bytecodes +# ---------------------- + +# Unpacks an iterable into multiple stack items. +# +# Pops: iterable +# +# Pushed: unpacked items, the count is determined by the immediate operand +# +def_op_graal( + loc, "UNPACK_SEQUENCE", 0x49, 1, 1, 1 +) # , (oparg, followingArgs, withJump) -> oparg) + +# Unpacks an iterable into multiple stack items with a star item that gets the rest. The first +# immediate operand determines the count before the star item, the second determines the count +# after. +# +# Pops: iterable +# +# Pushed: unpacked items (count = first operand), star item, unpacked items (count = second +# operand) +# +def_op_graal( + loc, "UNPACK_EX", 0x4a, 2, 1, 1 +) # (oparg, followingArgs, withJump) -> oparg + 1 + Byte.toUnsignedInt(followingArgs[0])) + +# jumps + +# +# Get next value from an iterator. If the iterable is exhausted, jump forward by the offset in +# the immediate argument. +# +# Pops: iterator +# +# Pushes (only if not jumping): the iterator, then the next value +# +jrel_op_graal( + loc, "FOR_ITER", 0x4b, 1, 1, True, True, 1 +) # (, (oparg, followingArgs, withJump) -> withJump ? 0 : 2) +# +# Jump forward by the offset in the immediate operand. +# +jrel_op_graal(loc, "JUMP_FORWARD", 0x4c, 1, 0, False, False, 1) + +# Jump backward by the offset in the immediate operand. May trigger OSR compilation. +# +jrel_op_graal(loc, "JUMP_BACKWARD", 0x4d, 1, 0, False, False, 1) + +# Jump forward by the offset in the immediate operand if the top of the stack is false (in +# Python sense). +# +# Pops (if not jumping): top of the stack +jrel_op_graal( + loc, "JUMP_IF_FALSE_OR_POP", 0x4e, 1, 1, True, True, 3, +) # , (oparg, followingArgs, withJump) -> withJump ? 0 : 1, 0) + +# Jump forward by the offset in the immediate operand if the top of the stack is true (in +# Python sense). +# +# Pops (if not jumping): top of the stack +# +jrel_op_graal( + loc, "JUMP_IF_TRUE_OR_POP", 0x4f, 1, 1, True, True, 3, +) # , (oparg, followingArgs, withJump) -> withJump ? 0 : 1, 0) +# +# Jump forward by the offset in the immediate operand if the top of the stack is false (in +# Python sense). +# +# Pops: top of the stack +# +jrel_op_graal(loc, "POP_AND_JUMP_IF_FALSE", 0x50, 1, 1, True, True, 3) +# +# Jump forward by the offset in the immediate operand if the top of the stack is true (in +# Python sense). +# +# Pops: top of the stack +# +jrel_op_graal(loc, "POP_AND_JUMP_IF_TRUE", 0x51, 1, 1, True, True, 3) + + +# ---------------- +# making callables +# ---------------- + +# +# Like {@link #LOAD_DEREF}, but loads the cell itself, not the contents. +# +# Pushes: the cell object +# +free_op_graal(loc, "LOAD_CLOSURE", 81, 1, 0, 1) +# +# Reduces multiple stack items into an array of cell objects. +# +# Pops: multiple cells (count = immediate argument) +# +# Pushes: cell object array ({@code PCell[]}) +# +def_op_graal( + loc, "CLOSURE_FROM_STACK", 82, 1 +) # , (oparg, followingArgs, withJump) -> oparg, 1) +# +# Creates a function object. The first immediate argument is an index to the constants array +# that determines the {@link CodeUnit} object that will provide the function's code. +# +# Pops: The second immediate arguments contains flags (defined in {@link CodeUnit}) that +# determine whether it will need to pop (in this order): closure, annotations, keyword only +# defaults, defaults. +# +# Pushes: created function +# +nargs_op_graal( + loc, "MAKE_FUNCTION", 0x54, 1, 2, 2 +) # , (oparg, followingArgs, withJump) -> Integer.bitCount(followingArgs[0]), 1) +# observed. + +# ------------------- +# collection literals +# ------------------- + +# +# Creates a collection from multiple elements from the stack. Collection type is determined by +# {@link CollectionBits} in immediate operand. +# +# Pops: items for the collection (count = immediate argument) +# +# Pushes: new collection +# +collection_op_graal( + loc, "COLLECTION_FROM_STACK", 0x58, 1 +) # , (oparg, followingArgs, withJump) -> CollectionBits.elementCount(oparg), 1) +# +# Add multiple elements from the stack to the collection below them. Collection type is +# determined by {@link CollectionBits} in immediate operand. Tuple is not supported. +# +# Pops: items to be added (count = immediate argument) +# +collection_op_graal( + loc, "COLLECTION_ADD_STACK", 0x59, 1 +) # , (oparg, followingArgs, withJump) -> CollectionBits.elementCount(oparg) + 1, 1) +# +# Concatenates two collection of the same type. Collection type is determined by +# {@link CollectionBits} in immediate operand. Tuple is not supported. +# +# Pops: second collection, first collection +# +# Pushes: concatenated collection +# +collection_op_graal(loc, "COLLECTION_ADD_COLLECTION", 0x5a, 1, 2, 1) +# +# Converts collection to another type determined by {@link CollectionBits} in immediate +# operand. The converted collection is expected to be an independent copy (they don't share +# storage). +# +# Pops: original collection +# +# Pushes: converted collection +# +collection_op_graal(loc, "COLLECTION_FROM_COLLECTION", 0x5b, 1, 1, 1) +# +# Converts list to tuple by reusing the underlying storage. +# +# Pops: list +# +# Pushes: tuple +# +def_op_graal(loc, "TUPLE_FROM_LIST", 0x5c, 0, 1, 1) +# +# Converts list to frozenset. +# +# Pops: list +# +# Pushes: frozenset +# +def_op_graal(loc, "FROZENSET_FROM_LIST", 0x5d, 0, 1, 1) +# +# Adds an item to a collection that is multiple items deep under the top of the stack, +# determined by the immediate argument. +# +# Pops: item to be added +# +collection_op_graal( + loc, "ADD_TO_COLLECTION", 0x5e, 1 +) # (oparg, followingArgs, withJump) -> CollectionBits.collectionKind(oparg) == CollectionBits.KIND_DICT ? 2 : 1, 0) +# +# Like {@link #COLLECTION_ADD_COLLECTION} for dicts, but with checks for duplicate keys +# necessary for keyword arguments merge. Note it works with dicts. Keyword arrays need to be +# converted to dicts first. +# +def_op_graal(loc, "KWARGS_DICT_MERGE", 0x5F, 0, 2, 1) +# +# Create a single {@link PKeyword} object. The name is determined by the immediate operand +# which indexes the names array ({@code co_names}) +# +# Pops: keyword value +# +# Pushes: keyword object +# +def_op_graal(loc, "MAKE_KEYWORD", 0x60, 1, 1, 1) + +# ----------- +# exceptions +# ----------- + +# +# Jump forward by the offset in the immediate argument if the exception doesn't match the +# expected type. The exception object is {@link PException}, not a python exception. +# +# Pops: expected type, then exception +# +# Pushes (if jumping): the exception +# +jrel_op_graal(loc, "MATCH_EXC_OR_JUMP", 0x61, 1, 0, 1) +# +# Save the current exception state on the stack and set it to the exception on the stack. The +# exception object is {@link PException}, not a python exception. The exception is pushed back +# to the top. +# +# Pops: the exception +# +# Pushes: the saved exception state, the exception +# +def_op_graal(loc, "PUSH_EXC_INFO", 0x62, 0, 0, 1) + +# Sets the current exception state to the saved state (by {@link #PUSH_EXC_INFO}) on the stack +# and pop it. +# +# Pops: save exception state +def_op_graal(loc, "POP_EXCEPT", 0x63, 0, 1, 0) + +# Restore exception state and reraise exception. +# +# Pops: exception to reraise, then saved exception state +def_op_graal(loc, "END_EXC_HANDLER", 0x64, 0, 2, 0) + +# Gets the python-level exception object from a {@link PException}. +# +# Pops: a {@link PException} Pushes: python exception +# +def_op_graal(loc, "UNWRAP_EXC", 0x65, 0, 1, 1) + +# ---------- +# generators +# ---------- +# +# Yield value from the stack to the caller. Saves execution state. The generator will resume at +# the next instruction. +# +# Pops: yielded value +# +def_op_graal(loc, "YIELD_VALUE", 0x66, 0, 1, 0) +# +# Wrap value from the stack in a {@link PAsyncGenWrappedValue}. CPython 3.11 opcode, used here +# to avoid a runtime check +# +# Pops: an object Pushes: async_generator_wrapped_value +# +def_op_graal(loc, "ASYNCGEN_WRAP", 0x67, 0, 1, 1) +# +# Resume after yield. Will raise exception passed by {@code throw} if any. +# +# Pushes: value received from {@code send} or {@code None}. +# +def_op_graal(loc, "RESUME_YIELD", 0x68, 0, 0, 1) +# +# Send value into a generator. Jumps forward by the offset in the immediate argument if the +# generator is exhausted. Used to implement {@code yield from}. +# +# Pops: value to be sent, then generator +# +# Pushes (if not jumping): the generator, then the yielded value +# +# Pushes (if jumping): the generator return value +# +jrel_op_graal( + loc, "SEND", 0x69, 1, 2, 1 +) # , (oparg, followingArgs, withJump) -> withJump ? 1 : 2) + +# Exception handler for forwarding {@code throw} calls into {@code yield from}. +# +# Pops: exception, then the generator +# +# Pushes (if not jumping): the generator, then the yielded value +# +# Pushes (if jumping): the generator return value +def_op_graal( + loc, "THROW", 0x6A, 1, 2, 1 +) # , (oparg, followingArgs, withJump) -> withJump ? 1 : 2) + +# Exception handler for async for loops. If the current exception is StopAsyncIteration, handle +# it, otherwise, reraise. +# +# Pops: exception, then the anext coroutine, then the async iterator +def_op_graal(loc, "END_ASYNC_FOR", 0x6B, 0, 3, 0) + +# with statements +# +# Enter a context manager and save data for its exit. +# +# Pops: the context manager +# +# Pushes: the context manager, then maybe-bound {@code __exit__}, then the result of +# {@code __enter__} +def_op_graal(loc, "SETUP_WITH", 0x6c, 1, 0, 0) + +# Run the exit handler of a context manager and reraise if necessary. +# +# Pops: exception or {@code None}, then maybe-bound {@code __exit__}, then the context manager + +def_op_graal(loc, "EXIT_WITH", 0x6d, 0, 3, 0) + +# Enter a context manager and save data for its exit +# +# Pops: the context manager +# +# Pushes: the context manager, then the maybe-bound async function {@code __aexit__}, then the +# awaitable returned by {@code __aenter__} +# +def_op_graal(loc, "SETUP_AWITH", 0x6e, 0, 1, 3) + +# Run the exit handler of a context manager +# +# Pops: exception or {@code None}, then maybe-bound {@code __aexit__}, then the context manager +# +# Pushes: the exception or {@code None}, then the awaitable returned by {@code __aexit__} +def_op_graal(loc, "GET_AEXIT_CORO", 0x6f, 0, 3, 2) + +# Reraise the exception passed to {@code __aexit__} if appropriate +# +#!Pops: The result of awaiting {@code __aexit__}, then the exception +def_op_graal(loc, "EXIT_AWITH", 0x70, 0, 2, 0) + +# Quickened bytecodes +# +def_op_graal(loc, "LOAD_TRUE_O", 0x71, 1, 0, 0) +def_op_graal(loc, "LOAD_TRUE_B", 0x72, 1, 0, 0) +def_op_graal(loc, "LOAD_FALSE_O", 0x73, 1, 0, 0) +def_op_graal(loc, "LOAD_FALSE_B", 0x74, 1, 0, 0) +def_op_graal(loc, "LOAD_BYTE_O", 0x75, 1, 0, 1) +def_op_graal(loc, "LOAD_BYTE_I", 0x76, 1, 0, 1) +def_op_graal(loc, "LOAD_INT_O", 0x77, 1, 0, 1) +def_op_graal(loc, "LOAD_INT_I", 0x78, 1, 0, 1) +def_op_graal(loc, "LOAD_LONG_O", 0x7A, 1, 0, 1) +def_op_graal(loc, "LOAD_LONG_L", 0x7A, 1, 0, 1) +def_op_graal(loc, "LOAD_DOUBLE_O", 0x7B, 1, 0, 1) +def_op_graal(loc, "LOAD_DOUBLE_D", 0x7C, 1, 0, 1) +# There are more... +# LOAD_FAST* +# STORE_FAST* +# UNARY_OP* +# BINARY_OP* +# FOR_ITER* +# BINARY_SUBSCR* +# and MORE! +update_sets(loc) diff --git a/xdis/opcodes/opcode_312rust.py b/xdis/opcodes/opcode_312rust.py index ff291a33..951d09f3 100644 --- a/xdis/opcodes/opcode_312rust.py +++ b/xdis/opcodes/opcode_312rust.py @@ -19,15 +19,17 @@ jrel_op, local_op, name_op, + nargs_op, store_op, unary_op, update_pj3, ) from xdis.opcodes.format.extended import extended_format_binary_op from xdis.opcodes.opcode_312 import opcode_arg_fmt312, opcode_extended_fmt312 +from xdis.version_info import PythonImplementation version_tuple = (3, 12) -python_implementation = "RustPython" +python_implementation = PythonImplementation("RustPython") # oppush[op] => number of stack entries pushed oppush: List[int] = [0] * 256 @@ -214,7 +216,7 @@ def pseudo_op(name: str, op: int, real_ops: list): jrel_op(loc, "POP_JUMP_IF_NONE", 129) def_op(loc, "RAISE_VARARGS", 130) # Number of raise arguments (1, 2, or 3) def_op(loc, "GET_AWAITABLE", 131) -def_op(loc, "MAKE_FUNCTION", 132) # Flags +nargs_op(loc, "MAKE_FUNCTION", 132) # Flags def_op(loc, "BUILD_SLICE", 133) # Number of items jrel_op(loc, "JUMP_BACKWARD_NO_INTERRUPT", 134) # Number of words to skip (backwards) free_op(loc, "MAKE_CELL", 135, 0, 0) diff --git a/xdis/opcodes/opcode_313.py b/xdis/opcodes/opcode_313.py index e931eb75..6474269b 100644 --- a/xdis/opcodes/opcode_313.py +++ b/xdis/opcodes/opcode_313.py @@ -5,7 +5,8 @@ from typing import Optional, Tuple import xdis.opcodes.opcode_312 as opcode_312 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, def_op, finalize_opcodes, free_op, @@ -18,7 +19,6 @@ from xdis.opcodes.opcode_36pypy import format_CALL_METHOD version_tuple = (3, 13) -python_implementation = "CPython" loc = locals() diff --git a/xdis/opcodes/opcode_314.py b/xdis/opcodes/opcode_314.py index f68e2f89..4a5fcc4f 100644 --- a/xdis/opcodes/opcode_314.py +++ b/xdis/opcodes/opcode_314.py @@ -3,10 +3,14 @@ """ import xdis.opcodes.opcode_313 as opcode_313 -from xdis.opcodes.base import finalize_opcodes, init_opdata, update_pj3 +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, + finalize_opcodes, + init_opdata, + update_pj3, +) version_tuple = (3, 14) -python_implementation = "CPython" loc = locals() @@ -15,7 +19,7 @@ ### update formatting opcode_arg_fmt = opcode_arg_fmt314 = opcode_313.opcode_arg_fmt313.copy() -opcode_extended_fmt = opcode_extended_fmt313 = opcode_313.opcode_extended_fmt313, +opcode_extended_fmt = opcode_extended_fmt313 = (opcode_313.opcode_extended_fmt313,) findlinestarts = opcode_313.findlinestarts_313 update_pj3(globals(), loc) diff --git a/xdis/opcodes/opcode_32.py b/xdis/opcodes/opcode_32.py index 33f71d5b..32715e03 100644 --- a/xdis/opcodes/opcode_32.py +++ b/xdis/opcodes/opcode_32.py @@ -1,4 +1,4 @@ -# (C) Copyright 2016-2017, 2020, 2023 by Rocky Bernstein +# (C) Copyright 2016-2017, 2020, 2023, 2025 by Rocky Bernstein """ CPython 3.2 bytecode opcodes @@ -7,13 +7,17 @@ """ import xdis.opcodes.opcode_3x as opcode_3x -from xdis.opcodes.base import finalize_opcodes, init_opdata, update_pj3 +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, + finalize_opcodes, + init_opdata, + update_pj3, +) from xdis.opcodes.opcode_33 import opcode_arg_fmt33, opcode_extended_fmt33 # FIXME: can we DRY this even more? version_tuple = (3, 2) -python_implementation = "CPython" loc = locals() diff --git a/xdis/opcodes/opcode_32pypy.py b/xdis/opcodes/opcode_32pypy.py index 0f108ca3..0b619670 100644 --- a/xdis/opcodes/opcode_32pypy.py +++ b/xdis/opcodes/opcode_32pypy.py @@ -21,9 +21,10 @@ extended_format_CALL_METHOD, extended_format_RETURN_VALUE, ) +from xdis.version_info import PythonImplementation version_tuple = (3, 2) -python_implementation = "PyPy" +python_implementation = PythonImplementation("PyPy") loc = locals() init_opdata(loc, opcode_32, version_tuple, is_pypy=True) diff --git a/xdis/opcodes/opcode_33.py b/xdis/opcodes/opcode_33.py index 32a3d190..175d6c3c 100644 --- a/xdis/opcodes/opcode_33.py +++ b/xdis/opcodes/opcode_33.py @@ -7,13 +7,19 @@ """ import xdis.opcodes.opcode_3x as opcode_3x -from xdis.opcodes.base import def_op, finalize_opcodes, init_opdata, rm_op, update_pj3 +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, + def_op, + finalize_opcodes, + init_opdata, + rm_op, + update_pj3, +) from xdis.opcodes.format.basic import format_RAISE_VARARGS_older, opcode_arg_fmt_base from xdis.opcodes.format.extended import opcode_extended_fmt_base from xdis.opcodes.opcode_3x import format_MAKE_FUNCTION_30_35 version_tuple = (3, 3) -python_implementation = "CPython" loc = locals() init_opdata(loc, opcode_3x, version_tuple) diff --git a/xdis/opcodes/opcode_33pypy.py b/xdis/opcodes/opcode_33pypy.py index 30614486..f46606ab 100644 --- a/xdis/opcodes/opcode_33pypy.py +++ b/xdis/opcodes/opcode_33pypy.py @@ -1,4 +1,4 @@ -# (C) Copyright 2019-2021, 2023-2024 by Rocky Bernstein +# (C) Copyright 2019-2021, 2023-2025 by Rocky Bernstein """ PYPY 3.3 opcodes @@ -21,9 +21,10 @@ extended_format_CALL_METHOD, extended_format_RETURN_VALUE, ) +from xdis.version_info import PythonImplementation version_tuple = (3, 3) -python_implementation = "PyPy" +python_implementation = PythonImplementation("PyPy") loc = locals() init_opdata(loc, opcode_33, version_tuple, is_pypy=True) diff --git a/xdis/opcodes/opcode_34.py b/xdis/opcodes/opcode_34.py index 128bdb68..2d91d0d0 100644 --- a/xdis/opcodes/opcode_34.py +++ b/xdis/opcodes/opcode_34.py @@ -21,11 +21,17 @@ """ import xdis.opcodes.opcode_33 as opcode_33 -from xdis.opcodes.base import finalize_opcodes, free_op, init_opdata, rm_op, update_pj3 +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, + finalize_opcodes, + free_op, + init_opdata, + rm_op, + update_pj3, +) from xdis.opcodes.opcode_33 import opcode_arg_fmt33, opcode_extended_fmt33 version_tuple = (3, 4) -python_implementation = "CPython" loc = locals() diff --git a/xdis/opcodes/opcode_35.py b/xdis/opcodes/opcode_35.py index 9a544e86..5136c39e 100644 --- a/xdis/opcodes/opcode_35.py +++ b/xdis/opcodes/opcode_35.py @@ -24,7 +24,8 @@ from typing import Optional, Tuple import xdis.opcodes.opcode_34 as opcode_34 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, @@ -37,7 +38,6 @@ from xdis.opcodes.opcode_34 import opcode_arg_fmt34, opcode_extended_fmt34 version_tuple = (3, 5) -python_implementation = "CPython" loc = locals() diff --git a/xdis/opcodes/opcode_35pypy.py b/xdis/opcodes/opcode_35pypy.py index d3a55a4b..55367945 100644 --- a/xdis/opcodes/opcode_35pypy.py +++ b/xdis/opcodes/opcode_35pypy.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017, 2020, 2023-2024 by Rocky Bernstein +# (C) Copyright 2017, 2020, 2023-2025 by Rocky Bernstein """ PYPY 3.5 opcodes @@ -23,9 +23,10 @@ extended_format_RETURN_VALUE, ) from xdis.opcodes.opcode_36 import extended_format_BUILD_STRING +from xdis.version_info import PythonImplementation version_tuple = (3, 5) -python_implementation = "PyPy" +python_implementation = PythonImplementation("PyPy") loc = locals() init_opdata(loc, opcode_35, version_tuple, is_pypy=True) diff --git a/xdis/opcodes/opcode_36.py b/xdis/opcodes/opcode_36.py index d3700dfc..76edf4ac 100644 --- a/xdis/opcodes/opcode_36.py +++ b/xdis/opcodes/opcode_36.py @@ -25,8 +25,9 @@ import xdis.opcodes.opcode_35 as opcode_35 from xdis.instruction import Instruction -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa call_op, + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, diff --git a/xdis/opcodes/opcode_36pypy.py b/xdis/opcodes/opcode_36pypy.py index 701feeaf..f15343a5 100644 --- a/xdis/opcodes/opcode_36pypy.py +++ b/xdis/opcodes/opcode_36pypy.py @@ -22,9 +22,10 @@ varargs_op, ) from xdis.opcodes.opcode_36 import opcode_arg_fmt36, opcode_extended_fmt36 +from xdis.version_info import PythonImplementation version_tuple = (3, 6) -python_implementation = "PyPy" +python_implementation = PythonImplementation("PyPy") # oppush[op] => number of stack entries pushed oppush: List[int] = [0] * 256 diff --git a/xdis/opcodes/opcode_37.py b/xdis/opcodes/opcode_37.py index 229be900..0683c1cc 100644 --- a/xdis/opcodes/opcode_37.py +++ b/xdis/opcodes/opcode_37.py @@ -24,8 +24,9 @@ from typing import Optional, Tuple import xdis.opcodes.opcode_36 as opcode_36 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa call_op, + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, diff --git a/xdis/opcodes/opcode_37pypy.py b/xdis/opcodes/opcode_37pypy.py index db1c8441..f7c462e5 100644 --- a/xdis/opcodes/opcode_37pypy.py +++ b/xdis/opcodes/opcode_37pypy.py @@ -21,11 +21,11 @@ update_pj3, varargs_op, ) +from xdis.opcodes.opcode_36pypy import python_implementation # noqa from xdis.opcodes.opcode_36pypy import opcode_arg_fmt36pypy, opcode_extended_fmt36pypy from xdis.opcodes.opcode_37 import opcode_arg_fmt37, opcode_extended_fmt37 version_tuple = (3, 7) -python_implementation = "PyPy" # oppush[op] => number of stack entries pushed oppush: List[int] = [0] * 256 diff --git a/xdis/opcodes/opcode_38.py b/xdis/opcodes/opcode_38.py index 3aea5846..d1e1594b 100644 --- a/xdis/opcodes/opcode_38.py +++ b/xdis/opcodes/opcode_38.py @@ -1,4 +1,4 @@ -# (C) Copyright 2019-2021, 2023 by Rocky Bernstein +# (C) Copyright 2019-2021, 2023, 2025 by Rocky Bernstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -21,7 +21,8 @@ """ import xdis.opcodes.opcode_37 as opcode_37 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, @@ -33,29 +34,28 @@ from xdis.opcodes.opcode_37 import opcode_arg_fmt37, opcode_extended_fmt37 version_tuple = (3, 8) -python_implementation = "CPython" -loc = l = locals() +loc = locals() -init_opdata(l, opcode_37, version_tuple) +init_opdata(loc, opcode_37, version_tuple) # fmt: off # These are removed since 3.7... -rm_op(l, "BREAK_LOOP", 80) -rm_op(l, "CONTINUE_LOOP", 119) -rm_op(l, "SETUP_LOOP", 120) -rm_op(l, "SETUP_EXCEPT", 121) +rm_op(loc, "BREAK_LOOP", 80) +rm_op(loc, "CONTINUE_LOOP", 119) +rm_op(loc, "SETUP_LOOP", 120) +rm_op(loc, "SETUP_EXCEPT", 121) # These are new/changed since Python 3.7 # OP NAME OPCODE POP PUSH # -------------------------------------------- -def_op(l, "ROT_FOUR", 6, 4, 4) # Opcode number changed from 5 to 6. Why? -def_op(l, "BEGIN_FINALLY", 53, 0, 6) -def_op(l, "END_ASYNC_FOR", 54, 7, 0) # POP is 0, when not 7 -def_op(l, "END_FINALLY", 88, 6, 0) # POP is 6, when not 1 -jrel_op(l, "CALL_FINALLY", 162, 0, 1) -nargs_op(l, "POP_FINALLY", 163, 6, 0) # PUSH/POP vary +def_op(loc, "ROT_FOUR", 6, 4, 4) # Opcode number changed from 5 to 6. Why? +def_op(loc, "BEGIN_FINALLY", 53, 0, 6) +def_op(loc, "END_ASYNC_FOR", 54, 7, 0) # POP is 0, when not 7 +def_op(loc, "END_FINALLY", 88, 6, 0) # POP is 6, when not 1 +jrel_op(loc, "CALL_FINALLY", 162, 0, 1) +nargs_op(loc, "POP_FINALLY", 163, 6, 0) # PUSH/POP vary # fmt: on @@ -63,4 +63,4 @@ opcode_extended_fmt = opcode_extended_fmt38 = opcode_extended_fmt37.copy() update_pj3(globals(), loc) -finalize_opcodes(l) +finalize_opcodes(loc) diff --git a/xdis/opcodes/opcode_38graal.py b/xdis/opcodes/opcode_38graal.py index 214aa2e4..c6627d79 100644 --- a/xdis/opcodes/opcode_38graal.py +++ b/xdis/opcodes/opcode_38graal.py @@ -1,31 +1,64 @@ -# (C) 2024-2025 by Rocky Bernstein +# (C) 2025 by Rocky Bernstein +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """ -Python Graal 3.10 bytecode opcodes +Python Graal 3.8 (graal-23.0.0) bytecode opcodes See com.oracle.graal.python/src/com/oracle/graal/python/compiler/OpCodes.java """ +from typing import Dict, Set + from xdis.opcodes.base import init_opdata +from xdis.opcodes.base_graal import findlabels # noqa +from xdis.opcodes.base_graal import ( + binary_op_graal, + call_op_graal, + collection_op_graal, + const_op_graal, + def_op_graal, + free_op_graal, + jrel_op_graal, + name_op_graal, + nargs_op_graal, + store_op_graal, + unary_op_graal, + update_sets, +) +from xdis.version_info import PythonImplementation + +python_implementation = PythonImplementation("Graal") +version_tuple = (3, 8, 5) + +arg_counts: Dict[int, int] = {} + +# opcodes that perform a binary operation on the top two stack entries +binaryop: Set[int] = set([]) + +# opcodes that perform some sort of call +callop: Set[int] = set([]) + +# opcodes that perform some sort of call +collectionop: Set[int] = set([]) -opc = locals() -init_opdata(opc, None, None) +# opcodes that perform a unary operation on the toip stack entry +unaryop: Set[int] = set([]) +loc = locals() -def def_graal_op( - loc: dict, - op_name: str, - opcode: int, - pop: int = -2, - push: int = -2, - unknown: int = 0, - fallthrough: bool = True, -) -> None: - loc["opname"][opcode] = op_name - loc["opmap"][op_name] = opcode - loc["oppush"][opcode] = push - loc["oppop"][opcode] = pop - if not fallthrough: - loc["nofollow"].append(opcode) +init_opdata(loc, None, None) # Instruction opcodes for compiled code @@ -38,170 +71,163 @@ def def_graal_op( # then pop the operand amount plus the negative of the POP amount. # Pop a single item from the stack. -def_graal_op(opc, "POP_TOP", 0, 0, 1, 0) +def_op_graal(loc, "POP_TOP", 0x0, 0, 1, 0) # Exchange two top stack items. -def_graal_op(opc, "ROT_TWO", 1, 0, 2, 2) +def_op_graal(loc, "ROT_TWO", 0x1, 0, 2, 2) # Exchange three top stack items. [a, b, c] (a is top) becomes [b, c, a] -def_graal_op(opc, "ROT_THREE", 2, 0, 3, 3) +def_op_graal(loc, "ROT_THREE", 0x2, 0, 3, 3) # Exchange N top stack items. [a, b, c, ..., N] (a is top) becomes [b, c, ..., N, a]. -def_graal_op( - opc, "ROT_N", 3, -1, -1 +def_op_graal( + loc, "ROT_N", 0x3, -1, -1 ) # (oparg, followingArgs, withJump) -> oparg, (oparg, followingArgs, withJump) -> oparg) # Duplicates the top stack item. -def_graal_op(opc, "DUP_TOP", 4, 0, 1, 2) +def_op_graal(loc, "DUP_TOP", 0x4, 0, 1, 2) # Does nothing. Might still be useful to maintain a line number. -def_graal_op(opc, "NOP", 5, 0, 0, 0) +def_op_graal(loc, "NOP", 0x5, 0, 0, 0) # Performs a unary operation specified by the immediate operand. It # has to be the ordinal of one of {@link UnaryOps} constants. # Pops: operand # Pushes: result -def_graal_op(opc, "UNARY_OP", 6, 1, 1, 1) +unary_op_graal(loc, "UNARY_OP", 0x6, 1, 1, 1) # Performs a binary operation specified by the immediate operand. It has to be the ordinal of # one of {@link BinaryOps} constants. # Pops: right operand, then left operand # Pushes: result -def_graal_op(opc, "BINARY_OP", 7, 1, 2, 1) +binary_op_graal(loc, "BINARY_OP", 0x7, 1, 2, 1) # Performs subscript get operation - {@code a[b]}. # Pops: {@code b}, then {@code a} # Pushes: result -def_graal_op(opc, "BINARY_SUBSCR", 8, 0, 2, 1) +def_op_graal(loc, "BINARY_SUBSCR", 0x8, 0, 2, 1) # Performs subscript set operation - {@code a[b] = c}. # Pops: {@code b}, then {@code a}, then {@code c} -def_graal_op(opc, "STORE_SUBSCR", 9, 0, 3, 0) +def_op_graal(loc, "STORE_SUBSCR", 0x9, 0, 3, 0) # Performs subscript delete operation - {@code del a[b]}. # Pops: {@code b}, then {@code a} # -def_graal_op(opc, "DELETE_SUBSCR", 10, 0, 2, 0) +def_op_graal(loc, "DELETE_SUBSCR", 0xA, 0, 2, 0) # Gets an iterator of an object. # Pops: object # Pushes: iterator -def_graal_op(opc, "GET_ITER", 11, 0, 1, 1) +def_op_graal(loc, "GET_ITER", 0xB, 0, 1, 1) # Gets an iterator of an object, does nothing for a generator iterator or a coroutine. # Pops: object # Pushes: iterator -def_graal_op(opc, "GET_YIELD_FROM_ITER", 12, 0, 1, 1) +def_op_graal(loc, "GET_YIELD_FROM_ITER", 0xC, 0, 1, 1) # Gets an awaitable of an object. # Pops: object # Pushes: awaitable -def_graal_op(opc, "GET_AWAITABLE", 13, 0, 1, 1) - -# Gets the async iterator of an object - error if a coroutine is returned. -# Pops: object -# Pushes: async iterator -def_graal_op(opc, "GET_AITER", 14, 0, 1, 1) - -# Get the awaitable that will return the next element of an async iterator. -# Pops: object -# Pushes: awaitable -def_graal_op(opc, "GET_ANEXT", 15, 0, 1, 1) +def_op_graal(loc, "GET_AWAITABLE", 0xD, 0, 1, 1) # Pushes: {@code __build_class__} builtin -def_graal_op(opc, "LOAD_BUILD_CLASS", 16, 0, 0, 1) +# def_op_graal(loc, "LOAD_BUILD_CLASS", 0xe, 0, 0, 1) +def_op_graal(loc, "LOAD_BUILD_CLASS", 0x12, 0, 0, 1) # This is wrong # Pushes: {@code AssertionError} builtin exception type -def_graal_op(opc, "LOAD_ASSERTION_ERROR", 17, 0, 0, 1) +def_op_graal(loc, "LOAD_ASSERTION_ERROR", 0x11, 0, 0, 1) # Returns the value to the caller. In generators, performs generator return. # Pops: return value -def_graal_op(opc, "RETURN_VALUE", 0x0e, 0, 1, 0) +# def_op_graal(loc, "RETURN_VALUE", 0x10, 0, 1, 0) +def_op_graal(loc, "RETURN_VALUE", 0xE, 0, 1, 0) # This is observed # # Reads a name from locals dict, globals or builtins determined by the # immediate operand which indexes the names array ({@code co_names}). # Pushes: read object -def_graal_op(opc, "LOAD_NAME", 19, 1, 0, 1) -opc["nullaryloadop"].add(19) +name_op_graal(loc, "LOAD_NAME", 0x0F, 1, 0, 0) +loc["nullaryloadop"].add(0x0F) # Writes the stack top into a name in locals dict or globals # determined by the immediate operand which indexes the names array # ({@code co_names}). # Pops: object to be written -def_graal_op(opc, "STORE_NAME", 20, 1, 1, 0) +store_op_graal(loc, "STORE_NAME", 0x10, 1, 1, "name", 1) +# observed # Deletes the name in locals dict or globals determined by the # immediate operand which indexes the names array ({@code co_names}). -def_graal_op(opc, "DELETE_NAME", 21, 1, 0, 0) +name_op_graal(loc, "DELETE_NAME", 0x13, 1, 0, 1) # Reads an attribute - {@code a.b}. {@code b} is determined by the immediate operand which # indexes the names array ({@code co_names}). # Pops: {@code a} # Pushes: read attribute -def_graal_op(opc, "LOAD_ATTR", 22, 1, 1, 1) +name_op_graal(loc, "LOAD_ATTR", 0x14, 1, 1, 1) # # Reads method on an object. The method name is determined by the # first immediate operand which indexes the names array ({@code # co_names}). # Pushes: read method -def_graal_op(opc, "LOAD_METHOD", 23, 1, 1, 2) +name_op_graal(loc, "LOAD_METHOD", 0x15, 1, 1, 2) # Writes an attribute - {@code a.b = c}. {@code b} is determined by # the immediate operand which indexes the names array ({@code # co_names}). # Pops: {@code c}, then {@code a} -def_graal_op(opc, "STORE_ATTR", 24, 1, 2, 0) +name_op_graal(loc, "STORE_ATTR", 0x16, 1, 2, 0) # Deletes an attribute - {@code del a.b}. {@code b} is determined by # the immediate operand which indexes the names array ({@code # co_names}). # Pops: {@code a} -def_graal_op(opc, "DELETE_ATTR", 25, 1, 1, 0) +name_op_graal(loc, "DELETE_ATTR", 0x17, 1, 1, 0) # Reads a global variable. The name is determined by the immediate # operand which indexes the names array ({@code co_names}). # Pushes: read object -def_graal_op(opc, "LOAD_GLOBAL", 26, 1, 0, 1) -opc["nullaryloadop"].add(26) +name_op_graal(loc, "LOAD_GLOBAL", 0x18, 1, 0, 1) +loc["nullaryloadop"].add(26) # Writes a global variable. The name is determined by the immediate # operand which indexes the names array ({@code co_names}). # Pops: value to be written -def_graal_op(opc, "STORE_GLOBAL", 27, 1, 1, 0) +name_op_graal(loc, "STORE_GLOBAL", 0x19, 1, 1, 0) # Deletes a global variable. The name is determined by the immediate operand which indexes the # names array ({@code co_names}). -def_graal_op(opc, "DELETE_GLOBAL", 28, 1, 0, 0) +name_op_graal(loc, "DELETE_GLOBAL", 0x1A, 1, 0, 0) # Reads a constant object from constants array ({@code co_consts}). Performs no conversion. # Pushes: read constant -def_graal_op(opc, "LOAD_CONST", 29, 1, 0, 1) -opc["nullaryloadop"].add(29) +const_op_graal(loc, "LOAD_CONST", 0x1B, 1, 0, 1) +loc["nullaryloadop"].add(0x1B) # Reads a local variable determined by the immediate operand which indexes a stack slot and a # variable name in varnames array ({@code co_varnames}). # Pushes: read value -def_graal_op(opc, "LOAD_FAST", 30, 1, 0, 1) -opc["nullaryloadop"].add(30) +def_op_graal(loc, "LOAD_FAST", 0x1C, 1, 0, 1) +loc["nullaryloadop"].add(0x1C) # Writes a local variable determined by the immediate operand which indexes a stack slot and a # variable name in varnames array ({@code co_varnames}). # Pops: value to be written -def_graal_op(opc, "STORE_FAST", 31, 1, 1, 0) +def_op_graal(loc, "STORE_FAST", 0x1D, 1, 1, 0) # Deletes a local variable determined by the immediate operand which indexes a stack slot and a # variable name in varnames array ({@code co_varnames}). -def_graal_op(opc, "DELETE_FAST", 32, 1, 0, 0) +def_op_graal(loc, "DELETE_FAST", 0x1E, 1, 0, 0) # Reads a local cell variable determined by the immediate operand # which indexes a stack slot after celloffset and a variable name in # cellvars or freevars array ({@code co_cellvars}, {@code # co_freevars}). # Pushes: cell contents -def_graal_op(opc, "LOAD_DEREF", 33, 1, 0, 1) +free_op_graal(loc, "LOAD_DEREF", 0x1F, 1, 0, 1) # Writes a local cell variable determined by the immediate operand # which indexes a stack slot after celloffset and a variable name in @@ -209,17 +235,17 @@ def def_graal_op( # co_freevars}). # Pops: value to be written into the cell contents # FIXME: this should be tagged as both a "free" and as "store" op. -def_graal_op(opc, "STORE_DEREF", 34, 1, 1, 0) +free_op_graal(loc, "STORE_DEREF", 0x20, 1, 1, 0) # Deletes a local cell variable determined by the immediate operand # which indexes a stack slot after celloffset and a variable name in # cellvars or freevars array ({@code co_cellvars}, {@code # co_freevars}). Note that it doesn't delete the cell, just its # contents. -def_graal_op(opc, "DELETE_DEREF", 35, 1, 0, 0) +free_op_graal(loc, "DELETE_DEREF", 0x21, 1, 0, 0) # TODO not implemented -def_graal_op(opc, "LOAD_CLASSDEREF", 36, 1, 0, 1) +def_op_graal(loc, "LOAD_CLASSDEREF", 0x22, 1, 0, 1) # Raises an exception. If the immediate operand is 0, it pops nothing # and is equivalent to {@code raise} without arguments. If the @@ -227,8 +253,8 @@ def def_graal_op( # pops {@code e}. If the immediate operand is 2, it is equivalent to # {@code raise e from c} and it pops {@code c}, then {@code e}. Other # immediate operand values are illegal. -def_graal_op( - opc, "RAISE_VARARGS", 37, 1 +def_op_graal( + loc, "RAISE_VARARGS", 0x23, 1 ) # , (oparg, followingArgs, withJump) -> oparg, 0) # Creates a slice object. If the immediate argument is 2, it is equivalent to a slice @@ -236,7 +262,9 @@ def def_graal_op( # equivalent to a slice {@code a:b:c}. It pops {@code c}, then {@code b}, then {@code a}. Other # immediate operand values are illegal. # Pushes: the created slice object -def_graal_op(opc, "BUILD_SLICE", 38, 1) # (oparg, followingArgs, withJump) -> oparg, 1) +def_op_graal( + loc, "BUILD_SLICE", 0x24, 1 +) # (oparg, followingArgs, withJump) -> oparg, 1) # Formats a value. If the immediate argument contains flag {@link FormatOptions#FVS_HAVE_SPEC}, # it is equivalent to {@code format(conv(v), spec)}. It pops {@code spec}, then {@code v}. @@ -244,122 +272,119 @@ def def_graal_op( # is determined by the immediate operand which contains one of the {@code FVC} options in # {@link FormatOptions}. # Pushes: the formatted value -def_graal_op( - opc, "FORMAT_VALUE", 39, 1 +def_op_graal( + loc, "FORMAT_VALUE", 0x25, 1 ) # , (oparg, followingArgs, withJump) -> (oparg & FormatOptions.FVS_MASK) == FormatOptions.FVS_HAVE_SPEC ? 2 : 1, 1) # Extends the immediate operand of the following instruction by its own operand shifted left by # a byte. -def_graal_op(opc, "EXTENDED_ARG", 40, 1, 0, 0) +def_op_graal(loc, "EXTENDED_ARG", 0x26, 1, 0, 0) # Imports a module by name determined by the immediate operand which # indexes the names array ({@code co_names}). # Pops: fromlist (must be a constant {@code TruffleString[]}), then level (must be {@code int}) # Pushes: imported module -def_graal_op(opc, "IMPORT_NAME", 41, 1, 2, 1) -opc["nullaryloadop"].add(41) +name_op_graal(loc, "IMPORT_NAME", 0x27, 1, 2, 1) +loc["nullaryloadop"].add(0x27) # Imports a name from a module. The name determined by the immediate operand which indexes the # names array ({@code co_names}). # Pops: module object # Pushes: module object, imported object -def_graal_op(opc, "IMPORT_FROM", 42, 1, 1, 2) +name_op_graal(loc, "IMPORT_FROM", 0x28, 1, 1, 1) # Imports all names from a module of name determined by the immediate operand which indexes the # names array ({@code co_names}). The imported names are written to locals dict (can only be # invoked on module level). # Pops: level (must be {@code int}) -def_graal_op(opc, "IMPORT_STAR", 43, 1, 1, 0) +def_op_graal(loc, "IMPORT_STAR", 0x29, 1, 1, 0) # Prints the top of the stack. Used by "single" parsing mode to echo # expressions. # Pops: the value to print -def_graal_op(opc, "PRINT_EXPR", 44, 0, 1, 0) +def_op_graal(loc, "PRINT_EXPR", 0x2A, 0, 1, 0) # Creates annotations dict in locals -def_graal_op(opc, "SETUP_ANNOTATIONS", 45, 0, 0, 0) +def_op_graal(loc, "SETUP_ANNOTATIONS", 0x2B, 0, 0, 0) # Determines if a python object is a sequence. -def_graal_op(opc, "MATCH_SEQUENCE", 46, 0, 0, 1) +def_op_graal(loc, "MATCH_SEQUENCE", 0x2C, 0, 0, 1) # Determines if a Python object is a mapping. -def_graal_op(opc, "MATCH_MAPPING", 47, 0, 0, 1) +def_op_graal(loc, "MATCH_MAPPING", 0x2D, 0, 0, 1) # Determines if a Python object is of a particular type. -def_graal_op(opc, "MATCH_CLASS", 48, 1, 3, 2) +def_op_graal(loc, "MATCH_CLASS", 0x2E, 1, 3, 2) # Matches the keys (stack top) in a dict (stack second). On successful # match pushes the values and True, otherwise None and False. -def_graal_op(opc, "MATCH_KEYS", 49, 0, 2, 4) +def_op_graal(loc, "MATCH_KEYS", 0x2F, 0, 2, 4) # Creates a copy of a dict (stack second) without elements matching a # tuple of keys (stack top). -def_graal_op(opc, "COPY_DICT_WITHOUT_KEYS", 50, 0, 1, 1) +def_op_graal(loc, "COPY_DICT_WITHOUT_KEYS", 0x30, 0, 1, 1) # Retrieves the length of a Python object and stores it on top. -def_graal_op(opc, "GET_LEN", 51, 0, 0, 1) +def_op_graal(loc, "GET_LEN", 0x31, 0, 0, 1) # ------------------------------------- # load bytecodes for special constants # ------------------------------------- -def_graal_op(opc, "LOAD_NONE", 52, 0, 0, 1) -opc["nullaryloadop"].add(52) +def_op_graal(loc, "LOAD_NONE", 0x32, 0, 1, 0) +loc["nullaryloadop"].add(0x32) -def_graal_op(opc, "LOAD_ELLIPSIS", 53, 0, 0, 1) -def_graal_op(opc, "LOAD_TRUE", 54, 0, 0, 1) -opc["nullaryloadop"].add(54) - -def_graal_op(opc, "LOAD_FALSE", 55, 0, 0, 1) -opc["nullaryloadop"].add(55) - - -#### Continue adding opcode numbers here... +def_op_graal(loc, "LOAD_ELLIPSIS", 0x33, 0, 1, 0) +def_op_graal(loc, "LOAD_TRUE", 0x34, 0, 1, 0) +loc["nullaryloadop"].add(0x34) +def_op_graal(loc, "LOAD_FALSE", 0x35, 0, 1, 0) +loc["nullaryloadop"].add(0x35) # Loads signed byte from immediate operand. # -def_graal_op(opc, "LOAD_BYTE", 95, 1, 0, 1) +# def_op_graal(loc, "LOAD_BYTE", 0x36, 1, 0, 1) +def_op_graal(loc, "LOAD_BYTE", 0x5F, 1, 0, 1) # this is observed # # Loads {@code int} from primitiveConstants array indexed by the immediate operand. # -def_graal_op(opc, "LOAD_INT", 57, 1, 0, 1) +def_op_graal(loc, "LOAD_INT", 0x37, 1, 0, 1) # # Loads {@code long} from primitiveConstants array indexed by the immediate operand. # -def_graal_op(opc, "LOAD_LONG", 58, 1, 0, 1) +def_op_graal(loc, "LOAD_LONG", 0x38, 1, 0, 1) # # Loads {@code double} from primitiveConstants array indexed by the immediate operand # (converted from long). # -def_graal_op(opc, "LOAD_DOUBLE", 59, 1, 0, 1) +def_op_graal(loc, "LOAD_DOUBLE", 59, 1, 0, 1) # # Creates a {@link PInt} from a {@link BigInteger} in constants array indexed by the immediate # operand. # -def_graal_op(opc, "LOAD_BIGINT", 60, 1, 0, 1) +const_op_graal(loc, "LOAD_BIGINT", 60, 1, 0, 1) # # Currently the same as {@link #LOAD_CONST}. # -def_graal_op(opc, "LOAD_STRING", 61, 0, 1) +const_op_graal(loc, "LOAD_STRING", 61, 0, 1) # # Creates python {@code bytes} from a {@code byte[]} array in constants array indexed by the # immediate operand. # -def_graal_op(opc, "LOAD_BYTES", 62, 0, 1) +const_op_graal(loc, "LOAD_BYTES", 62, 0, 1) # # Creates python {@code complex} from a {@code double[]} array of size 2 in constants array # indexed by the immediate operand. # -def_graal_op(opc, "LOAD_COMPLEX", 63, 1, 0, 1) +def_op_graal(loc, "LOAD_COMPLEX", 63, 1, 0, 1) # Creates a collection out of a Java array in constants array indexed by the immediate operand. # The second immediate operand determines the array type and kind, using values from {@link # CollectionBits}. The only allowed kinds are list and tuple. # -def_graal_op(opc, "LOAD_CONST_COLLECTION", 64, 2, 0, 1) +const_op_graal(loc, "LOAD_CONST_COLLECTION", 64, 2, 0, 1) # ------- # calling @@ -374,7 +399,7 @@ def def_graal_op( # # Pushes: call result # -def_graal_op(opc, "CALL_METHOD_VARARGS", 65, 1, 1, 1) +call_op_graal(loc, "CALL_METHOD_VARARGS", 65, 1, 1, 1) # # Calls method on an object using a number of stack args determined by the first immediate # operand. @@ -384,8 +409,8 @@ def def_graal_op( # # Pushes: call result # -def_graal_op( - opc, "CALL_METHOD", 66, 1 +call_op_graal( + loc, "CALL_METHOD", 66, 1 ) # , (oparg, followingArgs, withJump) -> oparg + 2, 1) # # Calls a callable using a number of stack args determined by the immediate operand. @@ -394,8 +419,8 @@ def def_graal_op( # # Pushes: call result # -def_graal_op( - opc, "CALL_FUNCTION", 67, 1 +call_op_graal( + loc, "CALL_FUNCTION", 0x39, 2, 1, 1 ) # , (oparg, followingArgs, withJump) -> oparg + 1, 1) # # Calls a comprehension function with a single iterator argument. Comprehension functions have @@ -407,7 +432,7 @@ def def_graal_op( # # Pushes: call result # -def_graal_op(opc, "CALL_COMPREHENSION", 68, 0, 2, 1) +call_op_graal(loc, "CALL_COMPREHENSION", 68, 0, 2, 1) # # Calls a callable using an arguments array and keywords array. # @@ -415,7 +440,7 @@ def def_graal_op( # # Pushes: call result # -def_graal_op(opc, "CALL_FUNCTION_KW", 69, 0, 3, 1) +call_op_graal(loc, "CALL_FUNCTION_KW", 69, 0, 3, 1) # # Calls a callable using an arguments array. No keywords are passed. # @@ -423,7 +448,7 @@ def def_graal_op( # # Pushes: call result # -def_graal_op(opc, "CALL_FUNCTION_VARARGS", 70, 0, 2, 1) +def_op_graal(loc, "CALL_FUNCTION_VARARGS", 70, 0, 2, 1) # ---------------------- # destructuring bytecodes @@ -435,8 +460,8 @@ def def_graal_op( # # Pushed: unpacked items, the count is determined by the immediate operand # -def_graal_op( - opc, "UNPACK_SEQUENCE", 71, 1, 1 +def_op_graal( + loc, "UNPACK_SEQUENCE", 71, 1, 1 ) # , (oparg, followingArgs, withJump) -> oparg) # Unpacks an iterable into multiple stack items with a star item that gets the rest. The first @@ -448,8 +473,8 @@ def def_graal_op( # Pushed: unpacked items (count = first operand), star item, unpacked items (count = second # operand) # -def_graal_op( - opc, "UNPACK_EX", 73, 2, 1 +def_op_graal( + loc, "UNPACK_EX", 73, 2, 1 ) # (oparg, followingArgs, withJump) -> oparg + 1 + Byte.toUnsignedInt(followingArgs[0])) # jumps @@ -462,24 +487,24 @@ def def_graal_op( # # Pushes (only if not jumping): the iterator, then the next value # -def_graal_op( - opc, "FOR_ITER", 74, 1, 1 +jrel_op_graal( + loc, "FOR_ITER", 74, 1, 0, 1 ) # (, (oparg, followingArgs, withJump) -> withJump ? 0 : 2) # # Jump forward by the offset in the immediate operand. # -def_graal_op(opc, "JUMP_FORWARD", 75, 1, 0, 0) +jrel_op_graal(loc, "JUMP_FORWARD", 75, 1, 0, 1) # Jump backward by the offset in the immediate operand. May trigger OSR compilation. # -def_graal_op(opc, "JUMP_BACKWARD", 76, 1, 0, 0) +jrel_op_graal(loc, "JUMP_BACKWARD", 76, 1, 0, 1) # Jump forward by the offset in the immediate operand if the top of the stack is false (in # Python sense). # # Pops (if not jumping): top of the stack -def_graal_op( - opc, "JUMP_IF_FALSE_OR_POP", 77, 3 +jrel_op_graal( + loc, "JUMP_IF_FALSE_OR_POP", 77, 1, 1, 1, ) # , (oparg, followingArgs, withJump) -> withJump ? 0 : 1, 0) # Jump forward by the offset in the immediate operand if the top of the stack is true (in @@ -487,8 +512,8 @@ def def_graal_op( # # Pops (if not jumping): top of the stack # -def_graal_op( - opc, "JUMP_IF_TRUE_OR_POP", 78, 3 +jrel_op_graal( + loc, "JUMP_IF_TRUE_OR_POP", 78, 1, 1, 1, ) # , (oparg, followingArgs, withJump) -> withJump ? 0 : 1, 0) # # Jump forward by the offset in the immediate operand if the top of the stack is false (in @@ -496,14 +521,14 @@ def def_graal_op( # # Pops: top of the stack # -def_graal_op(opc, "POP_AND_JUMP_IF_FALSE", 79, 3, 1, 0) +jrel_op_graal(loc, "POP_AND_JUMP_IF_FALSE", 79, 1, 1, 1) # # Jump forward by the offset in the immediate operand if the top of the stack is true (in # Python sense). # # Pops: top of the stack # -def_graal_op(opc, "POP_AND_JUMP_IF_TRUE", 80, 3, 1, 0) +jrel_op_graal(loc, "POP_AND_JUMP_IF_TRUE", 80, 1, 1, 1) # ---------------- @@ -515,7 +540,7 @@ def def_graal_op( # # Pushes: the cell object # -def_graal_op(opc, "LOAD_CLOSURE", 81, 1, 0, 1) +free_op_graal(loc, "LOAD_CLOSURE", 81, 1, 0, 1) # # Reduces multiple stack items into an array of cell objects. # @@ -523,8 +548,8 @@ def def_graal_op( # # Pushes: cell object array ({@code PCell[]}) # -def_graal_op( - opc, "CLOSURE_FROM_STACK", 82, 1 +def_op_graal( + loc, "CLOSURE_FROM_STACK", 82, 1 ) # , (oparg, followingArgs, withJump) -> oparg, 1) # # Creates a function object. The first immediate argument is an index to the constants array @@ -536,9 +561,10 @@ def def_graal_op( # # Pushes: created function # -def_graal_op( - opc, "MAKE_FUNCTION", 83, 2 +nargs_op_graal( + loc, "MAKE_FUNCTION", 0x47, 1, 2, 2 ) # , (oparg, followingArgs, withJump) -> Integer.bitCount(followingArgs[0]), 1) +# observed. # ------------------- # collection literals @@ -552,8 +578,8 @@ def def_graal_op( # # Pushes: new collection # -def_graal_op( - opc, "COLLECTION_FROM_STACK", 84, 1 +collection_op_graal( + loc, "COLLECTION_FROM_STACK", 84, 1 ) # , (oparg, followingArgs, withJump) -> CollectionBits.elementCount(oparg), 1) # # Add multiple elements from the stack to the collection below them. Collection type is @@ -561,8 +587,8 @@ def def_graal_op( # # Pops: items to be added (count = immediate argument) # -def_graal_op( - opc, "COLLECTION_ADD_STACK", 85, 1 +collection_op_graal( + loc, "COLLECTION_ADD_STACK", 85, 1 ) # , (oparg, followingArgs, withJump) -> CollectionBits.elementCount(oparg) + 1, 1) # # Concatenates two collection of the same type. Collection type is determined by @@ -572,7 +598,7 @@ def def_graal_op( # # Pushes: concatenated collection # -def_graal_op(opc, "COLLECTION_ADD_COLLECTION", 86, 1, 2, 1) +collection_op_graal(loc, "COLLECTION_ADD_COLLECTION", 86, 1, 2, 1) # # Converts collection to another type determined by {@link CollectionBits} in immediate # operand. The converted collection is expected to be an independent copy (they don't share @@ -582,7 +608,7 @@ def def_graal_op( # # Pushes: converted collection # -def_graal_op(opc, "COLLECTION_FROM_COLLECTION", 87, 1, 1, 1) +collection_op_graal(loc, "COLLECTION_FROM_COLLECTION", 87, 1, 1, 1) # # Converts list to tuple by reusing the underlying storage. # @@ -590,7 +616,7 @@ def def_graal_op( # # Pushes: tuple # -def_graal_op(opc, "TUPLE_FROM_LIST", 88, 0, 1, 1) +def_op_graal(loc, "TUPLE_FROM_LIST", 88, 0, 1, 1) # # Converts list to frozenset. # @@ -598,22 +624,22 @@ def def_graal_op( # # Pushes: frozenset # -def_graal_op(opc, "FROZENSET_FROM_LIST", 89, 0, 1, 1) +def_op_graal(loc, "FROZENSET_FROM_LIST", 89, 0, 1, 1) # # Adds an item to a collection that is multiple items deep under the top of the stack, # determined by the immediate argument. # # Pops: item to be added # -def_graal_op( - opc, "ADD_TO_COLLECTION", 90, 1 +collection_op_graal( + loc, "ADD_TO_COLLECTION", 90, 1 ) # (oparg, followingArgs, withJump) -> CollectionBits.collectionKind(oparg) == CollectionBits.KIND_DICT ? 2 : 1, 0) # # Like {@link #COLLECTION_ADD_COLLECTION} for dicts, but with checks for duplicate keys # necessary for keyword arguments merge. Note it works with dicts. Keyword arrays need to be # converted to dicts first. # -def_graal_op(opc, "KWARGS_DICT_MERGE", 91, 0, 2, 1) +def_op_graal(loc, "KWARGS_DICT_MERGE", 91, 0, 2, 1) # # Create a single {@link PKeyword} object. The name is determined by the immediate operand # which indexes the names array ({@code co_names}) @@ -622,7 +648,7 @@ def def_graal_op( # # Pushes: keyword object # -def_graal_op(opc, "MAKE_KEYWORD", 92, 1, 1, 1) +const_op_graal(loc, "MAKE_KEYWORD", 92, 1, 1, 1) # ----------- # exceptions @@ -636,7 +662,7 @@ def def_graal_op( # # Pushes (if jumping): the exception # -def_graal_op(opc, "MATCH_EXC_OR_JUMP", 93, 3, 2, 1) +def_op_graal(loc, "MATCH_EXC_OR_JUMP", 93, 3, 2, 1) # # Save the current exception state on the stack and set it to the exception on the stack. The # exception object is {@link PException}, not a python exception. The exception is pushed back @@ -646,24 +672,24 @@ def def_graal_op( # # Pushes: the saved exception state, the exception # -def_graal_op(opc, "PUSH_EXC_INFO", 94, 0, 0, 1) +def_op_graal(loc, "PUSH_EXC_INFO", 94, 0, 0, 1) # Sets the current exception state to the saved state (by {@link #PUSH_EXC_INFO}) on the stack # and pop it. # # Pops: save exception state -def_graal_op(opc, "POP_EXCEPT", 95, 0, 1, 0) +## def_op_graal(loc, "POP_EXCEPT", 95, 0, 1, 0) # not observed # Restore exception state and reraise exception. # # Pops: exception to reraise, then saved exception state -def_graal_op(opc, "END_EXC_HANDLER", 96, 0, 2, 0) +def_op_graal(loc, "END_EXC_HANDLER", 96, 0, 2, 0) # Gets the python-level exception object from a {@link PException}. # # Pops: a {@link PException} Pushes: python exception # -def_graal_op(opc, "UNWRAP_EXC", 97, 0, 1, 1) +def_op_graal(loc, "UNWRAP_EXC", 97, 0, 1, 1) # ---------- # generators @@ -674,20 +700,20 @@ def def_graal_op( # # Pops: yielded value # -def_graal_op(opc, "YIELD_VALUE", 98, 0, 1, 0) +def_op_graal(loc, "YIELD_VALUE", 98, 0, 1, 0) # # Wrap value from the stack in a {@link PAsyncGenWrappedValue}. CPython 3.11 opcode, used here # to avoid a runtime check # # Pops: an object Pushes: async_generator_wrapped_value # -def_graal_op(opc, "ASYNCGEN_WRAP", 99, 0, 1, 1) +def_op_graal(loc, "ASYNCGEN_WRAP", 99, 0, 1, 1) # # Resume after yield. Will raise exception passed by {@code throw} if any. # # Pushes: value received from {@code send} or {@code None}. # -def_graal_op(opc, "RESUME_YIELD", 100, 0, 0, 1) +def_op_graal(loc, "RESUME_YIELD", 100, 0, 0, 1) # # Send value into a generator. Jumps forward by the offset in the immediate argument if the # generator is exhausted. Used to implement {@code yield from}. @@ -698,8 +724,8 @@ def def_graal_op( # # Pushes (if jumping): the generator return value # -def_graal_op( - opc, "SEND", 101, 1, 2 +def_op_graal( + loc, "SEND", 101, 1, 2 ) # , (oparg, followingArgs, withJump) -> withJump ? 1 : 2) # Exception handler for forwarding {@code throw} calls into {@code yield from}. @@ -709,15 +735,15 @@ def def_graal_op( # Pushes (if not jumping): the generator, then the yielded value # # Pushes (if jumping): the generator return value -def_graal_op( - opc, "THROW", 102, 1, 2 +def_op_graal( + loc, "THROW", 102, 1, 2 ) # , (oparg, followingArgs, withJump) -> withJump ? 1 : 2) # Exception handler for async for loops. If the current exception is StopAsyncIteration, handle # it, otherwise, reraise. # # Pops: exception, then the anext coroutine, then the async iterator -def_graal_op(opc, "END_ASYNC_FOR", 103, 0, 3, 0) +def_op_graal(loc, "END_ASYNC_FOR", 103, 0, 3, 0) # with statements # @@ -727,13 +753,13 @@ def def_graal_op( # # Pushes: the context manager, then maybe-bound {@code __exit__}, then the result of # {@code __enter__} -def_graal_op(opc, "SETUP_WITH", 104, 0, 1, 3) +def_op_graal(loc, "SETUP_WITH", 104, 0, 1, 3) # Run the exit handler of a context manager and reraise if necessary. # # Pops: exception or {@code None}, then maybe-bound {@code __exit__}, then the context manager -def_graal_op(opc, "EXIT_WITH", 105, 0, 3, 0) +def_op_graal(loc, "EXIT_WITH", 105, 0, 3, 0) # Enter a context manager and save data for its exit # @@ -742,16 +768,18 @@ def def_graal_op( # Pushes: the context manager, then the maybe-bound async function {@code __aexit__}, then the # awaitable returned by {@code __aenter__} # -def_graal_op(opc, "SETUP_AWITH", 106, 0, 1, 3) +def_op_graal(loc, "SETUP_AWITH", 106, 0, 1, 3) # Run the exit handler of a context manager # # Pops: exception or {@code None}, then maybe-bound {@code __aexit__}, then the context manager # # Pushes: the exception or {@code None}, then the awaitable returned by {@code __aexit__} -def_graal_op(opc, "GET_AEXIT_CORO", 107, 0, 3, 2) +def_op_graal(loc, "GET_AEXIT_CORO", 107, 0, 3, 2) # Reraise the exception passed to {@code __aexit__} if appropriate # #!Pops: The result of awaiting {@code __aexit__}, then the exception -def_graal_op(opc, "EXIT_AWITH", 108, 0, 2, 0) +def_op_graal(loc, "EXIT_AWITH", 108, 0, 2, 0) + +update_sets(loc) diff --git a/xdis/opcodes/opcode_38pypy.py b/xdis/opcodes/opcode_38pypy.py index b7042a78..0a562f80 100644 --- a/xdis/opcodes/opcode_38pypy.py +++ b/xdis/opcodes/opcode_38pypy.py @@ -1,4 +1,4 @@ -# (C) Copyright 2021, 2023 by Rocky Bernstein +# (C) Copyright 2021, 2023, 2025 by Rocky Bernstein """ PYPY 3.8 opcodes @@ -19,9 +19,10 @@ varargs_op, ) from xdis.opcodes.opcode_37pypy import opcode_arg_fmt37pypy, opcode_extended_fmt37pypy +from xdis.version_info import PythonImplementation version_tuple = (3, 8) -python_implementation = "PyPy" +python_implementation = PythonImplementation("PyPy") loc = locals() init_opdata(loc, opcode_38, version_tuple, is_pypy=True) diff --git a/xdis/opcodes/opcode_39.py b/xdis/opcodes/opcode_39.py index 298ea65c..983c3696 100644 --- a/xdis/opcodes/opcode_39.py +++ b/xdis/opcodes/opcode_39.py @@ -23,8 +23,9 @@ from typing import Optional, Tuple import xdis.opcodes.opcode_38 as opcode_38 -from xdis.opcodes.base import ( +from xdis.opcodes.base import ( # noqa binary_op, + cpython_implementation as python_implementation, def_op, finalize_opcodes, init_opdata, diff --git a/xdis/opcodes/opcode_39pypy.py b/xdis/opcodes/opcode_39pypy.py index 1800deee..f9f49287 100644 --- a/xdis/opcodes/opcode_39pypy.py +++ b/xdis/opcodes/opcode_39pypy.py @@ -19,9 +19,10 @@ varargs_op, ) from xdis.opcodes.opcode_38pypy import opcode_arg_fmt38pypy, opcode_extended_fmt38pypy +from xdis.version_info import PythonImplementation version_tuple = (3, 9) -python_implementation = "PyPy" +python_implementation = PythonImplementation("PyPy") loc = locals() init_opdata(loc, opcode_39, version_tuple, is_pypy=True) diff --git a/xdis/opcodes/opcode_3x.py b/xdis/opcodes/opcode_3x.py index 9f513463..3904fb45 100644 --- a/xdis/opcodes/opcode_3x.py +++ b/xdis/opcodes/opcode_3x.py @@ -1,4 +1,4 @@ -# (C) Copyright 2017-2018, 2020-2021, 2023-2024 +# (C) Copyright 2017-2018, 2020-2021, 2023-2025 # by Rocky Bernstein # # This program is free software; you can redistribute it and/or diff --git a/xdis/std.py b/xdis/std.py index f6ae1613..ceac4d81 100644 --- a/xdis/std.py +++ b/xdis/std.py @@ -49,6 +49,7 @@ # std import sys +# xdis from xdis.bytecode import Bytecode as _Bytecode, get_optype from xdis.cross_dis import ( code_info as _code_info, @@ -60,8 +61,6 @@ from xdis.disasm import disco as _disco from xdis.instruction import Instruction as Instruction_xdis from xdis.op_imports import get_opcode_module - -# xdis from xdis.version_info import PYTHON_IMPLEMENTATION diff --git a/xdis/unmarshal.py b/xdis/unmarshal.py index 6c69f398..fd69f827 100644 --- a/xdis/unmarshal.py +++ b/xdis/unmarshal.py @@ -1127,6 +1127,10 @@ def t_graal_CodeUnit(self, save_ref, bytes_for_s: bool = False): [self.graal_code_info["co_codeunit_position"], co_code_offset_in_file] ) + # if graal_bytecode_version == 26: + # from xdis.bytecode_graal import get_instructions_bytes_graal + # from xdis.opcodes import opcode_38graal + # get_instructions_bytes_graal(code, opcode_38graal) return code # Since Python 3.4