diff options
-rw-r--r-- | Lib/dis.py | 148 | ||||
-rw-r--r-- | Lib/test/test_dis.py | 17 |
2 files changed, 101 insertions, 64 deletions
@@ -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 |