Example #1
0
    def test_jump_to_return(self):
        # def func(condition):
        #     return 'yes' if condition else 'no'
        label_instr4 = Label()
        label_instr6 = Label()
        code = Bytecode([
            Instr("LOAD_FAST", "condition"),
            Instr("POP_JUMP_IF_FALSE", label_instr4),
            Instr("LOAD_CONST", "yes"),
            Instr("JUMP_FORWARD", label_instr6),
            label_instr4,
            Instr("LOAD_CONST", "no"),
            label_instr6,
            Instr("RETURN_VALUE"),
        ])

        label = Label()
        self.check(
            code,
            Instr("LOAD_FAST", "condition"),
            Instr("POP_JUMP_IF_FALSE", label),
            Instr("LOAD_CONST", "yes"),
            Instr("RETURN_VALUE"),
            label,
            Instr("LOAD_CONST", "no"),
            Instr("RETURN_VALUE"),
        )
Example #2
0
 def test_from_code(self):
     code = get_code("""
         if test:
             x = 1
         else:
             x = 2
     """)
     bytecode = Bytecode.from_code(code)
     label_else = Label()
     label_exit = Label()
     self.assertEqual(
         bytecode,
         [
             Instr("LOAD_NAME", "test", lineno=1),
             Instr("POP_JUMP_IF_FALSE", label_else, lineno=1),
             Instr("LOAD_CONST", 1, lineno=2),
             Instr("STORE_NAME", "x", lineno=2),
             Instr("JUMP_FORWARD", label_exit, lineno=2),
             label_else,
             Instr("LOAD_CONST", 2, lineno=4),
             Instr("STORE_NAME", "x", lineno=4),
             label_exit,
             Instr("LOAD_CONST", None, lineno=4),
             Instr("RETURN_VALUE", lineno=4),
         ],
     )
Example #3
0
    def test_unconditional_jumps(self):
        # def func():
        #     if x:
        #         if y:
        #             func()
        label_instr7 = Label()
        code = Bytecode([
            Instr("LOAD_GLOBAL", "x", lineno=2),
            Instr("POP_JUMP_IF_FALSE", label_instr7, lineno=2),
            Instr("LOAD_GLOBAL", "y", lineno=3),
            Instr("POP_JUMP_IF_FALSE", label_instr7, lineno=3),
            Instr("LOAD_GLOBAL", "func", lineno=4),
            Instr("CALL_FUNCTION", 0, lineno=4),
            Instr("POP_TOP", lineno=4),
            label_instr7,
            Instr("LOAD_CONST", None, lineno=4),
            Instr("RETURN_VALUE", lineno=4),
        ])

        label_return = Label()
        self.check(
            code,
            Instr("LOAD_GLOBAL", "x", lineno=2),
            Instr("POP_JUMP_IF_FALSE", label_return, lineno=2),
            Instr("LOAD_GLOBAL", "y", lineno=3),
            Instr("POP_JUMP_IF_FALSE", label_return, lineno=3),
            Instr("LOAD_GLOBAL", "func", lineno=4),
            Instr("CALL_FUNCTION", 0, lineno=4),
            Instr("POP_TOP", lineno=4),
            label_return,
            Instr("LOAD_CONST", None, lineno=4),
            Instr("RETURN_VALUE", lineno=4),
        )
Example #4
0
    def test_not_jump_if_false(self):
        # Replace UNARY_NOT+POP_JUMP_IF_FALSE with POP_JUMP_IF_TRUE
        #
        # if not x:
        #     y = 9
        label = Label()
        code = Bytecode([
            Instr("LOAD_NAME", "x"),
            Instr("UNARY_NOT"),
            Instr("POP_JUMP_IF_FALSE", label),
            Instr("LOAD_CONST", 9),
            Instr("STORE_NAME", "y"),
            label,
        ])

        code = self.optimize_blocks(code)
        label = Label()
        self.check(
            code,
            Instr("LOAD_NAME", "x"),
            Instr("POP_JUMP_IF_TRUE", label),
            Instr("LOAD_CONST", 9),
            Instr("STORE_NAME", "y"),
            label,
        )
Example #5
0
    def test_iter_invalid_types(self):
        # Labels are not allowed in basic blocks
        block = BasicBlock()
        block.append(Label())
        with self.assertRaises(ValueError):
            list(block)
        with self.assertRaises(ValueError):
            block.legalize(1)

        # Only one jump allowed and only at the end
        block = BasicBlock()
        block2 = BasicBlock()
        block.extend([Instr("JUMP_ABSOLUTE", block2), Instr("NOP")])
        with self.assertRaises(ValueError):
            list(block)
        with self.assertRaises(ValueError):
            block.legalize(1)

        # jump target must be a BasicBlock
        block = BasicBlock()
        label = Label()
        block.extend([Instr("JUMP_ABSOLUTE", label)])
        with self.assertRaises(ValueError):
            list(block)
        with self.assertRaises(ValueError):
            block.legalize(1)
Example #6
0
    def test_legalize(self):
        code = Bytecode()
        code.first_lineno = 3
        code.extend([
            Instr("LOAD_CONST", 7),
            Instr("STORE_NAME", "x"),
            Instr("LOAD_CONST", 8, lineno=4),
            Instr("STORE_NAME", "y"),
            Label(),
            SetLineno(5),
            Instr("LOAD_CONST", 9, lineno=6),
            Instr("STORE_NAME", "z"),
        ])

        code.legalize()
        self.assertListEqual(
            code,
            [
                Instr("LOAD_CONST", 7, lineno=3),
                Instr("STORE_NAME", "x", lineno=3),
                Instr("LOAD_CONST", 8, lineno=4),
                Instr("STORE_NAME", "y", lineno=4),
                Label(),
                Instr("LOAD_CONST", 9, lineno=5),
                Instr("STORE_NAME", "z", lineno=5),
            ],
        )
Example #7
0
 def test_invalid_types(self):
     code = ConcreteBytecode()
     code.append(Label())
     with self.assertRaises(ValueError):
         list(code)
     with self.assertRaises(ValueError):
         code.legalize()
     with self.assertRaises(ValueError):
         ConcreteBytecode([Label()])
Example #8
0
    def test_compute_jumps_convergence(self):
        # Consider the following sequence of instructions:
        #
        #     JUMP_ABSOLUTE Label1
        #     JUMP_ABSOLUTE Label2
        #     ...126 instructions...
        #   Label1:                 Offset 254 on first pass, 256 second pass
        #     NOP
        #     ... many more instructions ...
        #   Label2:                 Offset > 256 on first pass
        #
        # On first pass of compute_jumps(), Label2 will be at address 254, so
        # that value encodes into the single byte arg of JUMP_ABSOLUTE.
        #
        # On second pass compute_jumps() the instr at Label1 will have offset
        # of 256 so will also be given an EXTENDED_ARG.
        #
        # Thus we need to make an additional pass.  This test only verifies
        # case where 2 passes is insufficient but three is enough.
        #
        # On Python > 3.10 we need to double the number since the offset is now
        # in term of instructions and not bytes.

        # Create code from comment above.
        code = Bytecode()
        label1 = Label()
        label2 = Label()
        nop = "NOP"
        code.append(Instr("JUMP_ABSOLUTE", label1))
        code.append(Instr("JUMP_ABSOLUTE", label2))
        # Need 254 * 2 + 2 since the arg will change by 1 instruction rather than 2
        # bytes.
        for x in range(4, 510 if OFFSET_AS_INSTRUCTION else 254, 2):
            code.append(Instr(nop))
        code.append(label1)
        code.append(Instr(nop))
        for x in range(
                514 if OFFSET_AS_INSTRUCTION else 256,
                600 if OFFSET_AS_INSTRUCTION else 300,
                2,
        ):
            code.append(Instr(nop))
        code.append(label2)
        code.append(Instr(nop))

        # This should pass by default.
        code.to_code()

        # Try with max of two passes:  it should raise
        with self.assertRaises(RuntimeError):
            code.to_code(compute_jumps_passes=2)
Example #9
0
    def test_has_jump(self):
        label = Label()
        jump = Instr("JUMP_ABSOLUTE", label)
        self.assertTrue(jump.has_jump())

        instr = Instr("LOAD_FAST", "x")
        self.assertFalse(instr.has_jump())
Example #10
0
    def test_is_cond_jump(self):
        label = Label()
        jump = Instr("POP_JUMP_IF_TRUE", label)
        self.assertTrue(jump.is_cond_jump())

        instr = Instr("LOAD_FAST", "x")
        self.assertFalse(instr.is_cond_jump())
Example #11
0
    def test_label2(self):
        bytecode = Bytecode()
        label = Label()
        bytecode.extend([
            Instr("LOAD_NAME", "test", lineno=1),
            Instr("POP_JUMP_IF_FALSE", label),
            Instr("LOAD_CONST", 5, lineno=2),
            Instr("STORE_NAME", "x"),
            Instr("JUMP_FORWARD", label),
            Instr("LOAD_CONST", 7, lineno=4),
            Instr("STORE_NAME", "x"),
            label,
            Instr("LOAD_CONST", None),
            Instr("RETURN_VALUE"),
        ])

        concrete = bytecode.to_concrete_bytecode()
        expected = [
            ConcreteInstr("LOAD_NAME", 0, lineno=1),
            ConcreteInstr("POP_JUMP_IF_FALSE",
                          14 if WORDCODE else 21,
                          lineno=1),
            ConcreteInstr("LOAD_CONST", 0, lineno=2),
            ConcreteInstr("STORE_NAME", 1, lineno=2),
            ConcreteInstr("JUMP_FORWARD", 4 if WORDCODE else 6, lineno=2),
            ConcreteInstr("LOAD_CONST", 1, lineno=4),
            ConcreteInstr("STORE_NAME", 1, lineno=4),
            ConcreteInstr("LOAD_CONST", 2, lineno=4),
            ConcreteInstr("RETURN_VALUE", lineno=4),
        ]
        self.assertListEqual(list(concrete), expected)
        self.assertListEqual(concrete.consts, [5, 7, None])
        self.assertListEqual(concrete.names, ["test", "x"])
        self.assertListEqual(concrete.varnames, [])
Example #12
0
    def test_extreme_compute_jumps_convergence(self):
        """Test of compute_jumps() requiring absurd number of passes.

        NOTE:  This test also serves to demonstrate that there is no worst
        case: the number of passes can be unlimited (or, actually, limited by
        the size of the provided code).

        This is an extension of test_compute_jumps_convergence.  Instead of
        two jumps, where the earlier gets extended after the latter, we
        instead generate a series of many jumps.  Each pass of compute_jumps()
        extends one more instruction, which in turn causes the one behind it
        to be extended on the next pass.
        """
        if not WORDCODE:
            return

        # N: the number of unextended instructions that can be squeezed into a
        # set of bytes adressable by the arg of an unextended instruction.
        # The answer is "128", but here's how we arrive at it (and it also
        # hints at how to make this work for pre-WORDCODE).
        max_unextended_offset = 1 << 8
        unextended_branch_instr_size = 2
        N = max_unextended_offset // unextended_branch_instr_size

        nop = "UNARY_POSITIVE"  # don't use NOP, dis.stack_effect will raise

        # The number of jumps will be equal to the number of labels.  The
        # number of passes of compute_jumps() required will be one greater
        # than this.
        labels = [Label() for x in range(0, 3 * N)]

        code = Bytecode()
        code.extend(
            Instr("JUMP_FORWARD", labels[len(labels) - x - 1])
            for x in range(0, len(labels))
        )
        end_of_jumps = len(code)
        code.extend(Instr(nop) for x in range(0, N))

        # Now insert the labels.  The first is N instructions (i.e. 256
        # bytes) after the last jump.  Then they proceed to earlier positions
        # 4 bytes at a time.  While the targets are in the range of the nop
        # instructions, 4 bytes is two instructions.  When the targets are in
        # the range of JUMP_FORWARD instructions we have to allow for the fact
        # that the instructions will have been extended to four bytes each, so
        # working backwards 4 bytes per label means just one instruction per
        # label.
        offset = end_of_jumps + N
        for index in range(0, len(labels)):
            code.insert(offset, labels[index])
            if offset <= end_of_jumps:
                offset -= 1
            else:
                offset -= 2

        code.insert(0, Instr("LOAD_CONST", 0))
        del end_of_jumps
        code.append(Instr("RETURN_VALUE"))

        code.to_code(compute_jumps_passes=(len(labels) + 1))
Example #13
0
    def test_is_uncond_jump(self):
        label = Label()
        jump = Instr("JUMP_ABSOLUTE", label)
        self.assertTrue(jump.is_uncond_jump())

        instr = Instr("POP_JUMP_IF_TRUE", label)
        self.assertFalse(instr.is_uncond_jump())
Example #14
0
    def test_extended_jump(self):
        NOP = bytes((opcode.opmap["NOP"], ))

        class BigInstr(ConcreteInstr):
            def __init__(self, size):
                super().__init__("NOP")
                self._size = size

            def copy(self):
                return self

            def assemble(self):
                return NOP * self._size

        # (invalid) code using jumps > 0xffff to test extended arg
        label = Label()
        nb_nop = 2**16
        code = Bytecode([
            Instr("JUMP_ABSOLUTE", label),
            BigInstr(nb_nop),
            label,
            Instr("LOAD_CONST", None),
            Instr("RETURN_VALUE"),
        ])

        code_obj = code.to_code()
        if OFFSET_AS_INSTRUCTION:
            expected = b"\x90\x80q\x02" + NOP * nb_nop + b"d\x00S\x00"
        else:
            expected = b"\x90\x01\x90\x00q\x06" + NOP * nb_nop + b"d\x00S\x00"
        self.assertEqual(code_obj.co_code, expected)
Example #15
0
    def test_compute_jumps_convergence(self):
        # Consider the following sequence of instructions:
        #
        #     JUMP_ABSOLUTE Label1
        #     JUMP_ABSOLUTE Label2
        #     ...126 instructions...
        #   Label1:                 Offset 254 on first pass, 256 second pass
        #     NOP
        #     ... many more instructions ...
        #   Label2:                 Offset > 256 on first pass
        #
        # On first pass of compute_jumps(), Label2 will be at address 254, so
        # that value encodes into the single byte arg of JUMP_ABSOLUTE.
        #
        # On second pass compute_jumps() the instr at Label1 will have offset
        # of 256 so will also be given an EXTENDED_ARG.
        #
        # Thus we need to make an additional pass.  This test only verifies
        # case where 2 passes is insufficient but three is enough.

        if not WORDCODE:
            # Could be done pre-WORDCODE, but that requires 2**16 bytes of
            # code.
            return

        # Create code from comment above.
        code = Bytecode()
        label1 = Label()
        label2 = Label()
        nop = "NOP"
        code.append(Instr("JUMP_ABSOLUTE", label1))
        code.append(Instr("JUMP_ABSOLUTE", label2))
        for x in range(4, 254, 2):
            code.append(Instr(nop))
        code.append(label1)
        code.append(Instr(nop))
        for x in range(256, 300, 2):
            code.append(Instr(nop))
        code.append(label2)
        code.append(Instr(nop))

        # This should pass by default.
        code.to_code()

        # Try with max of two passes:  it should raise
        with self.assertRaises(RuntimeError):
            code.to_code(compute_jumps_passes=2)
Example #16
0
 def test_from_code(self):
     code = get_code(
         """
         if test:
             x = 1
         else:
             x = 2
     """
     )
     bytecode = Bytecode.from_code(code)
     label_else = Label()
     label_exit = Label()
     if sys.version_info < (3, 10):
         self.assertEqual(
             bytecode,
             [
                 Instr("LOAD_NAME", "test", lineno=1),
                 Instr("POP_JUMP_IF_FALSE", label_else, lineno=1),
                 Instr("LOAD_CONST", 1, lineno=2),
                 Instr("STORE_NAME", "x", lineno=2),
                 Instr("JUMP_FORWARD", label_exit, lineno=2),
                 label_else,
                 Instr("LOAD_CONST", 2, lineno=4),
                 Instr("STORE_NAME", "x", lineno=4),
                 label_exit,
                 Instr("LOAD_CONST", None, lineno=4),
                 Instr("RETURN_VALUE", lineno=4),
             ],
         )
     # Control flow handling appears to have changed under Python 3.10
     else:
         self.assertEqual(
             bytecode,
             [
                 Instr("LOAD_NAME", "test", lineno=1),
                 Instr("POP_JUMP_IF_FALSE", label_else, lineno=1),
                 Instr("LOAD_CONST", 1, lineno=2),
                 Instr("STORE_NAME", "x", lineno=2),
                 Instr("LOAD_CONST", None, lineno=2),
                 Instr("RETURN_VALUE", lineno=2),
                 label_else,
                 Instr("LOAD_CONST", 2, lineno=4),
                 Instr("STORE_NAME", "x", lineno=4),
                 Instr("LOAD_CONST", None, lineno=4),
                 Instr("RETURN_VALUE", lineno=4),
             ],
         )
Example #17
0
 def test_for_iter_stack_effect_computation(self):
     with self.subTest():
         code = Bytecode()
         code.first_lineno = 1
         lab1 = Label()
         lab2 = Label()
         code.extend([
             lab1,
             Instr("FOR_ITER", lab2),
             Instr("STORE_FAST", "i"),
             Instr("JUMP_ABSOLUTE", lab1),
             lab2,
         ])
         with self.assertRaises(RuntimeError):
             # Use compute_stacksize since the code is so broken that conversion
             # to from concrete is actually broken
             code.compute_stacksize(check_pre_and_post=False)
Example #18
0
    def test_jumps(self):
        # if test:
        #     x = 12
        # else:
        #     x = 37
        code = Bytecode()
        label_else = Label()
        label_return = Label()
        code.extend([
            Instr("LOAD_NAME", "test", lineno=1),
            Instr("POP_JUMP_IF_FALSE", label_else),
            Instr("LOAD_CONST", 12, lineno=2),
            Instr("STORE_NAME", "x"),
            Instr("JUMP_FORWARD", label_return),
            label_else,
            Instr("LOAD_CONST", 37, lineno=4),
            Instr("STORE_NAME", "x"),
            label_return,
            Instr("LOAD_CONST", None, lineno=4),
            Instr("RETURN_VALUE"),
        ])

        code = code.to_concrete_bytecode()
        expected = [
            ConcreteInstr("LOAD_NAME", 0, lineno=1),
            ConcreteInstr("POP_JUMP_IF_FALSE",
                          5 if OFFSET_AS_INSTRUCTION else 10,
                          lineno=1),
            ConcreteInstr("LOAD_CONST", 0, lineno=2),
            ConcreteInstr("STORE_NAME", 1, lineno=2),
            ConcreteInstr("JUMP_FORWARD",
                          2 if OFFSET_AS_INSTRUCTION else 4,
                          lineno=2),
            ConcreteInstr("LOAD_CONST", 1, lineno=4),
            ConcreteInstr("STORE_NAME", 1, lineno=4),
            ConcreteInstr("LOAD_CONST", 2, lineno=4),
            ConcreteInstr("RETURN_VALUE", lineno=4),
        ]
        self.assertListEqual(list(code), expected)
        self.assertListEqual(code.consts, [12, 37, None])
        self.assertListEqual(code.names, ["test", "x"])
        self.assertListEqual(code.varnames, [])
Example #19
0
    def test_from_bytecode(self):
        bytecode = Bytecode()
        label = Label()
        bytecode.extend([
            Instr("LOAD_NAME", "test", lineno=1),
            Instr("POP_JUMP_IF_FALSE", label, lineno=1),
            Instr("LOAD_CONST", 5, lineno=2),
            Instr("STORE_NAME", "x", lineno=2),
            Instr("JUMP_FORWARD", label, lineno=2),
            # dead code!
            Instr("LOAD_CONST", 7, lineno=4),
            Instr("STORE_NAME", "x", lineno=4),
            Label(),  # unused label
            label,
            Label(),  # unused label
            Instr("LOAD_CONST", None, lineno=4),
            Instr("RETURN_VALUE", lineno=4),
        ])

        blocks = ControlFlowGraph.from_bytecode(bytecode)
        label2 = blocks[3]
        self.assertBlocksEqual(
            blocks,
            [
                Instr("LOAD_NAME", "test", lineno=1),
                Instr("POP_JUMP_IF_FALSE", label2, lineno=1),
            ],
            [
                Instr("LOAD_CONST", 5, lineno=2),
                Instr("STORE_NAME", "x", lineno=2),
                Instr("JUMP_FORWARD", label2, lineno=2),
            ],
            [
                Instr("LOAD_CONST", 7, lineno=4),
                Instr("STORE_NAME", "x", lineno=4)
            ],
            [
                Instr("LOAD_CONST", None, lineno=4),
                Instr("RETURN_VALUE", lineno=4)
            ],
        )
Example #20
0
    def test_dead_code_jump(self):
        label = Label()
        code = Bytecode([
            Instr("LOAD_NAME", "x"),
            Instr("JUMP_ABSOLUTE", label),
            # dead code
            Instr("LOAD_NAME", "y"),
            Instr("STORE_NAME", "test"),
            label,
            Instr("STORE_NAME", "test"),
        ])

        self.check(code, Instr("LOAD_NAME", "x"), Instr("STORE_NAME", "test"))
Example #21
0
    def test_invalid_arg(self):
        label = Label()
        block = BasicBlock()

        # EXTENDED_ARG
        self.assertRaises(ValueError, Instr, "EXTENDED_ARG", 0)

        # has_jump()
        self.assertRaises(TypeError, Instr, "JUMP_ABSOLUTE", 1)
        self.assertRaises(TypeError, Instr, "JUMP_ABSOLUTE", 1.0)
        Instr("JUMP_ABSOLUTE", label)
        Instr("JUMP_ABSOLUTE", block)

        # hasfree
        self.assertRaises(TypeError, Instr, "LOAD_DEREF", "x")
        Instr("LOAD_DEREF", CellVar("x"))
        Instr("LOAD_DEREF", FreeVar("x"))

        # haslocal
        self.assertRaises(TypeError, Instr, "LOAD_FAST", 1)
        Instr("LOAD_FAST", "x")

        # hasname
        self.assertRaises(TypeError, Instr, "LOAD_NAME", 1)
        Instr("LOAD_NAME", "x")

        # hasconst
        self.assertRaises(ValueError, Instr, "LOAD_CONST")  # UNSET
        self.assertRaises(ValueError, Instr, "LOAD_CONST", label)
        self.assertRaises(ValueError, Instr, "LOAD_CONST", block)
        Instr("LOAD_CONST", 1.0)
        Instr("LOAD_CONST", object())

        # hascompare
        self.assertRaises(TypeError, Instr, "COMPARE_OP", 1)
        Instr("COMPARE_OP", Compare.EQ)

        # HAVE_ARGUMENT
        self.assertRaises(ValueError, Instr, "CALL_FUNCTION", -1)
        self.assertRaises(TypeError, Instr, "CALL_FUNCTION", 3.0)
        Instr("CALL_FUNCTION", 3)

        # test maximum argument
        self.assertRaises(ValueError, Instr, "CALL_FUNCTION", 2147483647 + 1)
        instr = Instr("CALL_FUNCTION", 2147483647)
        self.assertEqual(instr.arg, 2147483647)

        # not HAVE_ARGUMENT
        self.assertRaises(ValueError, Instr, "NOP", 0)
        Instr("NOP")
Example #22
0
    def test_jump_if_true_to_jump_if_false(self):
        # Replace JUMP_IF_TRUE_OR_POP jumping to POP_JUMP_IF_FALSE <target>
        # with POP_JUMP_IF_TRUE <offset after the second POP_JUMP_IF_FALSE>
        #
        #     if x or y:
        #         z = 1

        label_instr3 = Label()
        label_instr7 = Label()
        code = Bytecode([
            Instr("LOAD_NAME", "x"),
            Instr("JUMP_IF_TRUE_OR_POP", label_instr3),
            Instr("LOAD_NAME", "y"),
            label_instr3,
            Instr("POP_JUMP_IF_FALSE", label_instr7),
            Instr("LOAD_CONST", 1),
            Instr("STORE_NAME", "z"),
            label_instr7,
            Instr("LOAD_CONST", None),
            Instr("RETURN_VALUE"),
        ])

        label_instr4 = Label()
        label_instr7 = Label()
        self.check(
            code,
            Instr("LOAD_NAME", "x"),
            Instr("POP_JUMP_IF_TRUE", label_instr4),
            Instr("LOAD_NAME", "y"),
            Instr("POP_JUMP_IF_FALSE", label_instr7),
            label_instr4,
            Instr("LOAD_CONST", 1),
            Instr("STORE_NAME", "z"),
            label_instr7,
            Instr("LOAD_CONST", None),
            Instr("RETURN_VALUE"),
        )
Example #23
0
 def mk_if_then_else(depth):
     instructions = []
     for i in range(depth):
         label_else = Label()
         instructions.extend([
             Instr("LOAD_FAST", "x"),
             Instr("POP_JUMP_IF_FALSE", label_else),
             Instr("LOAD_GLOBAL", "f{}".format(i)),
             Instr("RETURN_VALUE"),
             label_else,
         ])
     instructions.extend(
         [Instr("LOAD_CONST", None),
          Instr("RETURN_VALUE")])
     return instructions
Example #24
0
    def test_unconditional_jump_to_return(self):
        # def func():
        #     if test:
        #         if test2:
        #             x = 10
        #         else:
        #             x = 20
        #     else:
        #         x = 30

        label_instr11 = Label()
        label_instr14 = Label()
        label_instr7 = Label()
        code = Bytecode([
            Instr("LOAD_GLOBAL", "test", lineno=2),
            Instr("POP_JUMP_IF_FALSE", label_instr11, lineno=2),
            Instr("LOAD_GLOBAL", "test2", lineno=3),
            Instr("POP_JUMP_IF_FALSE", label_instr7, lineno=3),
            Instr("LOAD_CONST", 10, lineno=4),
            Instr("STORE_FAST", "x", lineno=4),
            Instr("JUMP_ABSOLUTE", label_instr14, lineno=4),
            label_instr7,
            Instr("LOAD_CONST", 20, lineno=6),
            Instr("STORE_FAST", "x", lineno=6),
            Instr("JUMP_FORWARD", label_instr14, lineno=6),
            label_instr11,
            Instr("LOAD_CONST", 30, lineno=8),
            Instr("STORE_FAST", "x", lineno=8),
            label_instr14,
            Instr("LOAD_CONST", None, lineno=8),
            Instr("RETURN_VALUE", lineno=8),
        ])

        label1 = Label()
        label3 = Label()
        label4 = Label()
        self.check(
            code,
            Instr("LOAD_GLOBAL", "test", lineno=2),
            Instr("POP_JUMP_IF_FALSE", label3, lineno=2),
            Instr("LOAD_GLOBAL", "test2", lineno=3),
            Instr("POP_JUMP_IF_FALSE", label1, lineno=3),
            Instr("LOAD_CONST", 10, lineno=4),
            Instr("STORE_FAST", "x", lineno=4),
            Instr("JUMP_ABSOLUTE", label4, lineno=4),
            label1,
            Instr("LOAD_CONST", 20, lineno=6),
            Instr("STORE_FAST", "x", lineno=6),
            Instr("JUMP_FORWARD", label4, lineno=6),
            label3,
            Instr("LOAD_CONST", 30, lineno=8),
            Instr("STORE_FAST", "x", lineno=8),
            label4,
            Instr("LOAD_CONST", None, lineno=8),
            Instr("RETURN_VALUE", lineno=8),
        )
Example #25
0
    def test_to_bytecode(self):
        # if test:
        #     x = 2
        # x = 5
        blocks = ControlFlowGraph()
        blocks.add_block()
        blocks.add_block()
        blocks[0].extend(
            [
                Instr("LOAD_NAME", "test", lineno=1),
                Instr("POP_JUMP_IF_FALSE", blocks[2], lineno=1),
            ]
        )

        blocks[1].extend(
            [
                Instr("LOAD_CONST", 5, lineno=2),
                Instr("STORE_NAME", "x", lineno=2),
                Instr("JUMP_FORWARD", blocks[2], lineno=2),
            ]
        )

        blocks[2].extend(
            [
                Instr("LOAD_CONST", 7, lineno=3),
                Instr("STORE_NAME", "x", lineno=3),
                Instr("LOAD_CONST", None, lineno=3),
                Instr("RETURN_VALUE", lineno=3),
            ]
        )

        bytecode = blocks.to_bytecode()
        label = Label()
        self.assertEqual(
            bytecode,
            [
                Instr("LOAD_NAME", "test", lineno=1),
                Instr("POP_JUMP_IF_FALSE", label, lineno=1),
                Instr("LOAD_CONST", 5, lineno=2),
                Instr("STORE_NAME", "x", lineno=2),
                Instr("JUMP_FORWARD", label, lineno=2),
                label,
                Instr("LOAD_CONST", 7, lineno=3),
                Instr("STORE_NAME", "x", lineno=3),
                Instr("LOAD_CONST", None, lineno=3),
                Instr("RETURN_VALUE", lineno=3),
            ],
        )
Example #26
0
    def test_uncond_jump_to_uncond_jump(self):
        # Replace JUMP_FORWARD t1 jumping to JUMP_FORWARD t2
        # with JUMP_ABSOLUTE t2

        label = Label()
        label2 = Label()
        label3 = Label()
        label4 = Label()
        code = Bytecode([
            Instr("LOAD_NAME", "test"),
            Instr("POP_JUMP_IF_TRUE", label),
            # redundant jump
            Instr("JUMP_FORWARD", label2),
            label,
            Instr("LOAD_CONST", 1),
            Instr("STORE_NAME", "x"),
            Instr("LOAD_NAME", "test"),
            Instr("POP_JUMP_IF_TRUE", label3),
            label2,
            Instr("JUMP_FORWARD", label4),
            label3,
            Instr("LOAD_CONST", 1),
            Instr("STORE_NAME", "x"),
            label4,
            Instr("LOAD_CONST", None),
            Instr("RETURN_VALUE"),
        ])

        label = Label()
        label3 = Label()
        label4 = Label()
        self.check(
            code,
            Instr("LOAD_NAME", "test"),
            Instr("POP_JUMP_IF_TRUE", label),
            # JUMP_FORWARD label2 was replaced with JUMP_ABSOLUTE label4
            Instr("JUMP_ABSOLUTE", label4),
            label,
            Instr("LOAD_CONST", 1),
            Instr("STORE_NAME", "x"),
            Instr("LOAD_NAME", "test"),
            Instr("POP_JUMP_IF_TRUE", label3),
            Instr("JUMP_FORWARD", label4),
            label3,
            Instr("LOAD_CONST", 1),
            Instr("STORE_NAME", "x"),
            label4,
            Instr("LOAD_CONST", None),
            Instr("RETURN_VALUE"),
        )
Example #27
0
    def test_label(self):
        code = Bytecode()
        label = Label()
        code.extend([
            Instr("LOAD_CONST", "hello", lineno=1),
            Instr("JUMP_FORWARD", label, lineno=1),
            label,
            Instr("POP_TOP", lineno=1),
        ])

        code = code.to_concrete_bytecode()
        expected = [
            ConcreteInstr("LOAD_CONST", 0, lineno=1),
            ConcreteInstr("JUMP_FORWARD", 0, lineno=1),
            ConcreteInstr("POP_TOP", lineno=1),
        ]
        self.assertListEqual(list(code), expected)
        self.assertListEqual(code.consts, ["hello"])
Example #28
0
    def test_compare_op_unary_not(self):
        for op, not_op in (
            (Compare.IN, Compare.NOT_IN),  # in => not in
            (Compare.NOT_IN, Compare.IN),  # not in => in
            (Compare.IS, Compare.IS_NOT),  # is => is not
            (Compare.IS_NOT, Compare.IS),  # is not => is
        ):
            code = Bytecode([
                Instr("LOAD_NAME", "a"),
                Instr("LOAD_NAME", "b"),
                Instr("COMPARE_OP", op),
                Instr("UNARY_NOT"),
                Instr("STORE_NAME", "x"),
            ])
            self.check(
                code,
                Instr("LOAD_NAME", "a"),
                Instr("LOAD_NAME", "b"),
                Instr("COMPARE_OP", not_op),
                Instr("STORE_NAME", "x"),
            )

        # don't optimize:
        # x = not (a and b is True)
        label_instr5 = Label()
        code = Bytecode([
            Instr("LOAD_NAME", "a"),
            Instr("JUMP_IF_FALSE_OR_POP", label_instr5),
            Instr("LOAD_NAME", "b"),
            Instr("LOAD_CONST", True),
            Instr("COMPARE_OP", Compare.IS),
            label_instr5,
            Instr("UNARY_NOT"),
            Instr("STORE_NAME", "x"),
            Instr("LOAD_CONST", None),
            Instr("RETURN_VALUE"),
        ])
        self.check_dont_optimize(code)
Example #29
0
    def test_label_at_the_end(self):
        label = Label()
        code = Bytecode([
            Instr("LOAD_NAME", "x"),
            Instr("UNARY_NOT"),
            Instr("POP_JUMP_IF_FALSE", label),
            Instr("LOAD_CONST", 9),
            Instr("STORE_NAME", "y"),
            label,
        ])

        cfg = ControlFlowGraph.from_bytecode(code)
        self.assertBlocksEqual(
            cfg,
            [
                Instr("LOAD_NAME", "x"),
                Instr("UNARY_NOT"),
                Instr("POP_JUMP_IF_FALSE", cfg[2]),
            ],
            [Instr("LOAD_CONST", 9),
             Instr("STORE_NAME", "y")],
            [],
        )
Example #30
0
    def test_jump_if_false_to_jump_if_false(self):
        # Replace JUMP_IF_FALSE_OR_POP jumping to POP_JUMP_IF_FALSE <label>
        # with POP_JUMP_IF_FALSE <label>
        #
        #     while n > 0 and start > 3:
        #         func()
        if sys.version_info < (3, 8):
            label_instr1 = Label()
            label_instr15 = Label()
            label_instr17 = Label()
            label_instr9 = Label()
            code = Bytecode([
                Instr("SETUP_LOOP", label_instr17),
                label_instr1,
                Instr("LOAD_NAME", "n"),
                Instr("LOAD_CONST", 0),
                Instr("COMPARE_OP", Compare.GT),
                # JUMP_IF_FALSE_OR_POP jumps to POP_JUMP_IF_FALSE
                # which jumps to label_instr15
                Instr("JUMP_IF_FALSE_OR_POP", label_instr9),
                Instr("LOAD_NAME", "start"),
                Instr("LOAD_CONST", 3),
                Instr("COMPARE_OP", Compare.GT),
                label_instr9,
                Instr("POP_JUMP_IF_FALSE", label_instr15),
                Instr("LOAD_NAME", "func"),
                Instr("CALL_FUNCTION", 0),
                Instr("POP_TOP"),
                Instr("JUMP_ABSOLUTE", label_instr1),
                label_instr15,
                Instr("POP_BLOCK"),
                label_instr17,
                Instr("LOAD_CONST", None),
                Instr("RETURN_VALUE"),
            ])

            label_instr1 = Label()
            label_instr14 = Label()
            label_instr16 = Label()
            self.check(
                code,
                Instr("SETUP_LOOP", label_instr16),
                label_instr1,
                Instr("LOAD_NAME", "n"),
                Instr("LOAD_CONST", 0),
                Instr("COMPARE_OP", Compare.GT),
                Instr("POP_JUMP_IF_FALSE", label_instr14),
                Instr("LOAD_NAME", "start"),
                Instr("LOAD_CONST", 3),
                Instr("COMPARE_OP", Compare.GT),
                Instr("POP_JUMP_IF_FALSE", label_instr14),
                Instr("LOAD_NAME", "func"),
                Instr("CALL_FUNCTION", 0),
                Instr("POP_TOP"),
                Instr("JUMP_ABSOLUTE", label_instr1),
                label_instr14,
                Instr("POP_BLOCK"),
                label_instr16,
                Instr("LOAD_CONST", None),
                Instr("RETURN_VALUE"),
            )
        else:
            label_instr1 = Label()
            label_instr15 = Label()
            label_instr9 = Label()
            code = Bytecode([
                label_instr1,
                Instr("LOAD_NAME", "n"),
                Instr("LOAD_CONST", 0),
                Instr("COMPARE_OP", Compare.GT),
                # JUMP_IF_FALSE_OR_POP jumps to POP_JUMP_IF_FALSE
                # which jumps to label_instr15
                Instr("JUMP_IF_FALSE_OR_POP", label_instr9),
                Instr("LOAD_NAME", "start"),
                Instr("LOAD_CONST", 3),
                Instr("COMPARE_OP", Compare.GT),
                label_instr9,
                Instr("POP_JUMP_IF_FALSE", label_instr15),
                Instr("LOAD_NAME", "func"),
                Instr("CALL_FUNCTION", 0),
                Instr("POP_TOP"),
                Instr("JUMP_ABSOLUTE", label_instr1),
                label_instr15,
                Instr("LOAD_CONST", None),
                Instr("RETURN_VALUE"),
            ])

            label_instr1 = Label()
            label_instr14 = Label()
            self.check(
                code,
                label_instr1,
                Instr("LOAD_NAME", "n"),
                Instr("LOAD_CONST", 0),
                Instr("COMPARE_OP", Compare.GT),
                Instr("POP_JUMP_IF_FALSE", label_instr14),
                Instr("LOAD_NAME", "start"),
                Instr("LOAD_CONST", 3),
                Instr("COMPARE_OP", Compare.GT),
                Instr("POP_JUMP_IF_FALSE", label_instr14),
                Instr("LOAD_NAME", "func"),
                Instr("CALL_FUNCTION", 0),
                Instr("POP_TOP"),
                Instr("JUMP_ABSOLUTE", label_instr1),
                label_instr14,
                Instr("LOAD_CONST", None),
                Instr("RETURN_VALUE"),
            )