summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/dis.py148
-rw-r--r--Lib/test/test_dis.py17
2 files changed, 101 insertions, 64 deletions
diff --git a/Lib/dis.py b/Lib/dis.py
index cad62b9..965a026 100644
--- a/Lib/dis.py
+++ b/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:
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index 69038df..43debdc 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -2001,6 +2001,23 @@ class InstructionTests(InstructionTestCase):
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