]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-111969: refactor to make it easier to construct a dis.Instruction object (#111970)
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>
Sun, 12 Nov 2023 14:06:02 +0000 (14:06 +0000)
committerGitHub <noreply@github.com>
Sun, 12 Nov 2023 14:06:02 +0000 (14:06 +0000)
Lib/dis.py
Lib/test/test_dis.py

index cad62b95990c3073c0b446af6755d6f636266efc..965a0268635ebc8dc258e7ede3fb0837b919100a 100644 (file)
@@ -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:
index 69038dfdf9eec88987e0b58c0c9ca1410103fba0..43debdcf6e16078f6d7723dd3332a084f79bcc3b 100644 (file)
@@ -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