mirror of
https://github.com/python/cpython
synced 2024-09-05 00:05:39 +00:00
gh-111969: refactor to make it easier to construct a dis.Instruction object (#111970)
This commit is contained in:
parent
40752c1c1e
commit
b2af50cb02
148
Lib/dis.py
148
Lib/dis.py
|
@ -330,6 +330,84 @@ class Instruction(_Instruction):
|
|||
covered by this instruction
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg):
|
||||
get_name = None if names is None else names.__getitem__
|
||||
argval = None
|
||||
argrepr = ''
|
||||
deop = _deoptop(op)
|
||||
if arg is not None:
|
||||
# Set argval to the dereferenced value of the argument when
|
||||
# available, and argrepr to the string representation of argval.
|
||||
# _disassemble_bytes needs the string repr of the
|
||||
# raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
|
||||
argval = arg
|
||||
if deop in hasconst:
|
||||
argval, argrepr = _get_const_info(deop, arg, co_consts)
|
||||
elif deop in hasname:
|
||||
if deop == LOAD_GLOBAL:
|
||||
argval, argrepr = _get_name_info(arg//2, get_name)
|
||||
if (arg & 1) and argrepr:
|
||||
argrepr = f"{argrepr} + NULL"
|
||||
elif deop == LOAD_ATTR:
|
||||
argval, argrepr = _get_name_info(arg//2, get_name)
|
||||
if (arg & 1) and argrepr:
|
||||
argrepr = f"{argrepr} + NULL|self"
|
||||
elif deop == LOAD_SUPER_ATTR:
|
||||
argval, argrepr = _get_name_info(arg//4, get_name)
|
||||
if (arg & 1) and argrepr:
|
||||
argrepr = f"{argrepr} + NULL|self"
|
||||
else:
|
||||
argval, argrepr = _get_name_info(arg, get_name)
|
||||
elif deop in hasjabs:
|
||||
argval = arg*2
|
||||
argrepr = "to " + repr(argval)
|
||||
elif deop in hasjrel:
|
||||
signed_arg = -arg if _is_backward_jump(deop) else arg
|
||||
argval = offset + 2 + signed_arg*2
|
||||
caches = _get_cache_size(_all_opname[deop])
|
||||
argval += 2 * caches
|
||||
argrepr = "to " + repr(argval)
|
||||
elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST):
|
||||
arg1 = arg >> 4
|
||||
arg2 = arg & 15
|
||||
val1, argrepr1 = _get_name_info(arg1, varname_from_oparg)
|
||||
val2, argrepr2 = _get_name_info(arg2, varname_from_oparg)
|
||||
argrepr = argrepr1 + ", " + argrepr2
|
||||
argval = val1, val2
|
||||
elif deop in haslocal or deop in hasfree:
|
||||
argval, argrepr = _get_name_info(arg, varname_from_oparg)
|
||||
elif deop in hascompare:
|
||||
argval = cmp_op[arg >> 5]
|
||||
argrepr = argval
|
||||
if arg & 16:
|
||||
argrepr = f"bool({argrepr})"
|
||||
elif deop == CONVERT_VALUE:
|
||||
argval = (None, str, repr, ascii)[arg]
|
||||
argrepr = ('', 'str', 'repr', 'ascii')[arg]
|
||||
elif deop == SET_FUNCTION_ATTRIBUTE:
|
||||
argrepr = ', '.join(s for i, s in enumerate(FUNCTION_ATTR_FLAGS)
|
||||
if arg & (1<<i))
|
||||
elif deop == BINARY_OP:
|
||||
_, argrepr = _nb_ops[arg]
|
||||
elif deop == CALL_INTRINSIC_1:
|
||||
argrepr = _intrinsic_1_descs[arg]
|
||||
elif deop == CALL_INTRINSIC_2:
|
||||
argrepr = _intrinsic_2_descs[arg]
|
||||
return argval, argrepr
|
||||
|
||||
|
||||
@classmethod
|
||||
def _create(cls, op, arg, offset, start_offset, starts_line, line_number,
|
||||
is_jump_target, positions,
|
||||
co_consts=None, varname_from_oparg=None, names=None):
|
||||
argval, argrepr = cls._get_argval_argrepr(
|
||||
op, arg, offset,
|
||||
co_consts, names, varname_from_oparg)
|
||||
return Instruction(_all_opname[op], op, arg, argval, argrepr,
|
||||
offset, start_offset, starts_line, line_number,
|
||||
is_jump_target, positions)
|
||||
|
||||
@property
|
||||
def oparg(self):
|
||||
"""Alias for Instruction.arg."""
|
||||
|
@ -544,73 +622,15 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
|
|||
else:
|
||||
line_number = None
|
||||
is_jump_target = offset in labels
|
||||
argval = None
|
||||
argrepr = ''
|
||||
positions = Positions(*next(co_positions, ()))
|
||||
deop = _deoptop(op)
|
||||
caches = _get_cache_size(_all_opname[deop])
|
||||
op = code[offset]
|
||||
if arg is not None:
|
||||
# Set argval to the dereferenced value of the argument when
|
||||
# available, and argrepr to the string representation of argval.
|
||||
# _disassemble_bytes needs the string repr of the
|
||||
# raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
|
||||
argval = arg
|
||||
if deop in hasconst:
|
||||
argval, argrepr = _get_const_info(deop, arg, co_consts)
|
||||
elif deop in hasname:
|
||||
if deop == LOAD_GLOBAL:
|
||||
argval, argrepr = _get_name_info(arg//2, get_name)
|
||||
if (arg & 1) and argrepr:
|
||||
argrepr = f"{argrepr} + NULL"
|
||||
elif deop == LOAD_ATTR:
|
||||
argval, argrepr = _get_name_info(arg//2, get_name)
|
||||
if (arg & 1) and argrepr:
|
||||
argrepr = f"{argrepr} + NULL|self"
|
||||
elif deop == LOAD_SUPER_ATTR:
|
||||
argval, argrepr = _get_name_info(arg//4, get_name)
|
||||
if (arg & 1) and argrepr:
|
||||
argrepr = f"{argrepr} + NULL|self"
|
||||
else:
|
||||
argval, argrepr = _get_name_info(arg, get_name)
|
||||
elif deop in hasjabs:
|
||||
argval = arg*2
|
||||
argrepr = "to " + repr(argval)
|
||||
elif deop in hasjrel:
|
||||
signed_arg = -arg if _is_backward_jump(deop) else arg
|
||||
argval = offset + 2 + signed_arg*2
|
||||
argval += 2 * caches
|
||||
argrepr = "to " + repr(argval)
|
||||
elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST):
|
||||
arg1 = arg >> 4
|
||||
arg2 = arg & 15
|
||||
val1, argrepr1 = _get_name_info(arg1, varname_from_oparg)
|
||||
val2, argrepr2 = _get_name_info(arg2, varname_from_oparg)
|
||||
argrepr = argrepr1 + ", " + argrepr2
|
||||
argval = val1, val2
|
||||
elif deop in haslocal or deop in hasfree:
|
||||
argval, argrepr = _get_name_info(arg, varname_from_oparg)
|
||||
elif deop in hascompare:
|
||||
argval = cmp_op[arg >> 5]
|
||||
argrepr = argval
|
||||
if arg & 16:
|
||||
argrepr = f"bool({argrepr})"
|
||||
elif deop == CONVERT_VALUE:
|
||||
argval = (None, str, repr, ascii)[arg]
|
||||
argrepr = ('', 'str', 'repr', 'ascii')[arg]
|
||||
elif deop == SET_FUNCTION_ATTRIBUTE:
|
||||
argrepr = ', '.join(s for i, s in enumerate(FUNCTION_ATTR_FLAGS)
|
||||
if arg & (1<<i))
|
||||
elif deop == BINARY_OP:
|
||||
_, argrepr = _nb_ops[arg]
|
||||
elif deop == CALL_INTRINSIC_1:
|
||||
argrepr = _intrinsic_1_descs[arg]
|
||||
elif deop == CALL_INTRINSIC_2:
|
||||
argrepr = _intrinsic_2_descs[arg]
|
||||
yield Instruction(_all_opname[op], op,
|
||||
arg, argval, argrepr,
|
||||
offset, start_offset, starts_line, line_number,
|
||||
is_jump_target, positions)
|
||||
|
||||
yield Instruction._create(op, arg, offset, start_offset, starts_line, line_number,
|
||||
is_jump_target, positions, co_consts=co_consts,
|
||||
varname_from_oparg=varname_from_oparg, names=names)
|
||||
|
||||
caches = _get_cache_size(_all_opname[deop])
|
||||
if not caches:
|
||||
continue
|
||||
if not show_caches:
|
||||
|
|
|
@ -2001,6 +2001,23 @@ def test_jump_target(self):
|
|||
positions=None)
|
||||
self.assertEqual(10 + 2 + 1*2 + 100*2, instruction.jump_target)
|
||||
|
||||
def test_argval_argrepr(self):
|
||||
f = dis.Instruction._get_argval_argrepr
|
||||
|
||||
offset = 42
|
||||
co_consts = (0, 1, 2, 3)
|
||||
names = {1: 'a', 2: 'b'}
|
||||
varname_from_oparg = lambda i : names[i]
|
||||
args = (offset, co_consts, names, varname_from_oparg)
|
||||
self.assertEqual(f(opcode.opmap["POP_TOP"], None, *args), (None, ''))
|
||||
self.assertEqual(f(opcode.opmap["LOAD_CONST"], 1, *args), (1, '1'))
|
||||
self.assertEqual(f(opcode.opmap["LOAD_GLOBAL"], 2, *args), ('a', 'a'))
|
||||
self.assertEqual(f(opcode.opmap["JUMP_BACKWARD"], 11, *args), (24, 'to 24'))
|
||||
self.assertEqual(f(opcode.opmap["COMPARE_OP"], 3, *args), ('<', '<'))
|
||||
self.assertEqual(f(opcode.opmap["SET_FUNCTION_ATTRIBUTE"], 2, *args), (2, 'kwdefaults'))
|
||||
self.assertEqual(f(opcode.opmap["BINARY_OP"], 3, *args), (3, '<<'))
|
||||
self.assertEqual(f(opcode.opmap["CALL_INTRINSIC_1"], 2, *args), (2, 'INTRINSIC_IMPORT_STAR'))
|
||||
|
||||
def test_start_offset(self):
|
||||
# When no extended args are present,
|
||||
# start_offset should be equal to offset
|
||||
|
|
Loading…
Reference in a new issue