diff --git a/pytest/test_instructions.py b/pytest/test_instructions.py index fde2f3b5..169df0e8 100644 --- a/pytest/test_instructions.py +++ b/pytest/test_instructions.py @@ -1,11 +1,43 @@ """xdis.bytecode testing""" +import dis import sys import pytest from xdis import IS_GRAAL, IS_PYPY from xdis.bytecode import Bytecode from xdis.op_imports import get_opcode_module -from xdis.version_info import PYTHON_VERSION_TRIPLE +from xdis.version_info import PYTHON_IMPLEMENTATION, PYTHON_VERSION_TRIPLE + + +@pytest.mark.skipif( + IS_PYPY or PYTHON_VERSION_TRIPLE[:2] not in ((3, 14), (3, 15)), + reason="LOAD_COMMON_CONSTANT is specific to CPython 3.14 and 3.15", +) +def test_load_common_constant_argval_matches_dis() -> None: + codes = [ + compile(source, "", "exec") + for source in ( + "assert False", + 'result = {"success": True, "failure": False}', + ) + ] + + native = [ + (inst.arg, inst.argval, inst.argrepr) + for code in codes + for inst in dis.get_instructions(code) + if inst.opname == "LOAD_COMMON_CONSTANT" + ] + cross = [ + (inst.arg, inst.argval, inst.argrepr) + for code in codes + for inst in Bytecode( + code, get_opcode_module(PYTHON_VERSION_TRIPLE, PYTHON_IMPLEMENTATION) + ) + if inst.opname == "LOAD_COMMON_CONSTANT" + ] + + assert cross == native def extended_arg_fn36() -> int: diff --git a/xdis/bytecode.py b/xdis/bytecode.py index 948b8c5b..ef4ca7a1 100644 --- a/xdis/bytecode.py +++ b/xdis/bytecode.py @@ -433,6 +433,8 @@ def get_logical_instruction_at_offset( ) if hasattr(opc, "opcode_arg_fmt") and opname in opc.opcode_arg_fmt: argrepr = opc.opcode_arg_fmt[opname](arg) + if hasattr(opc, "opcode_arg_val") and opname in opc.opcode_arg_val: + argval = opc.opcode_arg_val[opname](arg) else: if fixed_length_instructions: i += 1 diff --git a/xdis/opcodes/opcode_3x/opcode_315.py b/xdis/opcodes/opcode_3x/opcode_315.py index 0fd81e27..e9b64371 100644 --- a/xdis/opcodes/opcode_3x/opcode_315.py +++ b/xdis/opcodes/opcode_3x/opcode_315.py @@ -373,24 +373,29 @@ def extended_BINARY_OP_315(opc, instructions): _common_constants = ( - "AssertionError", - "NotImplementedError", - "tuple", - "all", - "any", - "list", - "set", - "None", # <--- There is your oparg 7! - '""', - "True", - "False", - "-1", - "frozenset", - "()", + AssertionError, + NotImplementedError, + tuple, + all, + any, + list, + set, + None, + "", + True, + False, + -1, + frozenset, + (), ) def format_LOAD_COMMON_CONSTANT_315(arg: int): + obj = _common_constants[arg] + return obj.__name__ if isinstance(obj, type) else repr(obj) + + +def resolve_LOAD_COMMON_CONSTANT_315(arg: int): return _common_constants[arg] @@ -400,6 +405,10 @@ def format_LOAD_COMMON_CONSTANT_315(arg: int): **{"LOAD_COMMON_CONSTANT": format_LOAD_COMMON_CONSTANT_315}, } +opcode_arg_val = opcode_arg_val315 = { + "LOAD_COMMON_CONSTANT": resolve_LOAD_COMMON_CONSTANT_315, +} + opcode_extended_fmt = opcode_extended_fmt315 = { **opcode_314.opcode_extended_fmt314, **{"BINARY_OP": extended_BINARY_OP_315},