示例#1
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', 15, lineno=1),
                    ConcreteInstr('LOAD_CONST', 0, lineno=2),
                    ConcreteInstr('STORE_NAME', 1, lineno=2),
                    ConcreteInstr('JUMP_FORWARD', 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(code), expected)
        self.assertListEqual(code.consts, [12, 37, None])
        self.assertListEqual(code.names, ['test', 'x'])
        self.assertListEqual(code.varnames, [])
示例#2
0
 def test_invalid_types(self):
     code = Bytecode()
     code.append(123)
     with self.assertRaises(ValueError):
         list(code)
     with self.assertRaises(ValueError):
         Bytecode([123])
示例#3
0
    def test_optimize_code_obj(self):
        # Test optimize() method with a code object
        #
        # x = 3 + 5 => x = 8
        noopt = Bytecode([
            Instr("LOAD_CONST", 3),
            Instr("LOAD_CONST", 5),
            Instr("BINARY_ADD"),
            Instr("STORE_NAME", "x"),
            Instr("LOAD_CONST", None),
            Instr("RETURN_VALUE"),
        ])
        noopt = noopt.to_code()

        optimizer = peephole_opt.PeepholeOptimizer()
        optim = optimizer.optimize(noopt)

        code = Bytecode.from_code(optim)
        self.assertEqual(
            code,
            [
                Instr("LOAD_CONST", 8, lineno=1),
                Instr("STORE_NAME", "x", lineno=1),
                Instr("LOAD_CONST", None, lineno=1),
                Instr("RETURN_VALUE", lineno=1),
            ],
        )
示例#4
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)
示例#5
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"),
            SetLineno(5),
            Instr("LOAD_CONST", 9, lineno=6),
            Instr("STORE_NAME", "z"),
        ])

        blocks = ControlFlowGraph.from_bytecode(code)
        blocks.legalize()
        self.assertBlocksEqual(
            blocks,
            [
                Instr("LOAD_CONST", 7, lineno=3),
                Instr("STORE_NAME", "x", lineno=3),
                Instr("LOAD_CONST", 8, lineno=4),
                Instr("STORE_NAME", "y", lineno=4),
                Instr("LOAD_CONST", 9, lineno=5),
                Instr("STORE_NAME", "z", lineno=5),
            ],
        )
示例#6
0
    def test_setlineno(self):
        # x = 7
        # y = 8
        # z = 9
        code = Bytecode()
        code.first_lineno = 3
        code.extend([
            Instr("LOAD_CONST", 7),
            Instr("STORE_NAME", "x"),
            SetLineno(4),
            Instr("LOAD_CONST", 8),
            Instr("STORE_NAME", "y"),
            SetLineno(5),
            Instr("LOAD_CONST", 9),
            Instr("STORE_NAME", "z"),
        ])

        blocks = ControlFlowGraph.from_bytecode(code)
        self.assertBlocksEqual(
            blocks,
            [
                Instr("LOAD_CONST", 7),
                Instr("STORE_NAME", "x"),
                SetLineno(4),
                Instr("LOAD_CONST", 8),
                Instr("STORE_NAME", "y"),
                SetLineno(5),
                Instr("LOAD_CONST", 9),
                Instr("STORE_NAME", "z"),
            ],
        )
示例#7
0
    def test_max_size(self):
        max_size = 3
        with mock.patch.object(peephole_opt, 'MAX_SIZE', max_size):
            # optimized binary operation: size <= maximum size
            #
            # (9,) * size
            size = max_size
            result = (9, ) * size
            code = Bytecode([
                Instr('LOAD_CONST', 9),
                Instr('BUILD_TUPLE', 1),
                Instr('LOAD_CONST', size),
                Instr('BINARY_MULTIPLY'),
                Instr('STORE_NAME', 'x')
            ])
            self.check(code, Instr('LOAD_CONST', result),
                       Instr('STORE_NAME', 'x'))

            # don't optimize  binary operation: size > maximum size
            #
            # x = (9,) * size
            size = (max_size + 1)
            code = Bytecode([
                Instr('LOAD_CONST', 9),
                Instr('BUILD_TUPLE', 1),
                Instr('LOAD_CONST', size),
                Instr('BINARY_MULTIPLY'),
                Instr('STORE_NAME', 'x')
            ])
            self.check(code, Instr('LOAD_CONST', (9, )),
                       Instr('LOAD_CONST', size), Instr('BINARY_MULTIPLY'),
                       Instr('STORE_NAME', 'x'))
示例#8
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, [])
示例#9
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 WORDCODE:
            expected = b'\x90\x01\x90\x00q\x06' + NOP * nb_nop + b'd\x00S\x00'
        else:
            expected = b'\x90\x01\x00q\x06\x00' + NOP * nb_nop + b'd\x00\x00S'
        self.assertEqual(code_obj.co_code, expected)
示例#10
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)
        ])
示例#11
0
    def test_code_object_containing_mutable_data(self):
        from types import CodeType

        from bytecode import Bytecode, Instr

        def f():
            def g():
                return "value"

            return g

        f_code = Bytecode.from_code(f.__code__)
        instr_load_code = None
        mutable_datum = [4, 2]

        for each in f_code:
            if (isinstance(each, Instr) and each.name == "LOAD_CONST"
                    and isinstance(each.arg, CodeType)):
                instr_load_code = each
                break

        self.assertIsNotNone(instr_load_code)

        g_code = Bytecode.from_code(instr_load_code.arg)
        g_code[0].arg = mutable_datum
        instr_load_code.arg = g_code.to_code()
        f.__code__ = f_code.to_code()

        self.assertIs(f()(), mutable_datum)
示例#12
0
def case(code, ctx, debug=False, cpython_compat=True):
    stmt = parse(code).result
    code_obj = py_compile(stmt, is_entrypoint=False)

    if debug:
        with open('out_yapypy_bc.log',
                  'w') as yapypy_bc, open('out_yapypy_info.log',
                                          'w') as yapypy_info:

            dis_code(code_obj, yapypy_bc)
            show_code(code_obj, yapypy_info)
            if cpython_compat:
                code_obj2 = compile(code, "", "exec")
                with open('out_cpy_bc.log',
                          'w') as cpy_bc, open('out_cpy_info.log',
                                               'w') as cpy_info:
                    dis_code(code_obj2, cpy_bc)
                    show_code(code_obj2, cpy_info)
                    print('python:')
                    exec(Bytecode.from_code(code_obj2).to_code(), ctx or {})
        print('yapypy')
        exec(Bytecode.from_code(code_obj).to_code(), ctx or {})

    else:
        exec(code_obj, ctx)
示例#13
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, [])
    def test_dont_optimize(self):
        # x = 3 < 5
        code = Bytecode(
            [
                Instr("LOAD_CONST", 3),
                Instr("LOAD_CONST", 5),
                Instr("COMPARE_OP", Compare.LT),
                Instr("STORE_NAME", "x"),
                Instr("LOAD_CONST", None),
                Instr("RETURN_VALUE"),
            ]
        )
        self.check_dont_optimize(code)

        # x = (10, 20, 30)[1:]
        code = Bytecode(
            [
                Instr("LOAD_CONST", (10, 20, 30)),
                Instr("LOAD_CONST", 1),
                Instr("LOAD_CONST", None),
                Instr("BUILD_SLICE", 2),
                Instr("BINARY_SUBSCR"),
                Instr("STORE_NAME", "x"),
            ]
        )
        self.check_dont_optimize(code)
示例#15
0
    def test_general_constants(self):
        """Test if general object could be linked as constants.

        """
        class CustomObject:
            pass

        class UnHashableCustomObject:
            __hash__ = None

        obj1 = [1, 2, 3]
        obj2 = {1, 2, 3}
        obj3 = CustomObject()
        obj4 = UnHashableCustomObject()
        code = Bytecode([Instr('LOAD_CONST', obj1, lineno=1),
                         Instr('LOAD_CONST', obj2, lineno=1),
                         Instr('LOAD_CONST', obj3, lineno=1),
                         Instr('LOAD_CONST', obj4, lineno=1),
                         Instr('BUILD_TUPLE', 4, lineno=1),
                         Instr('RETURN_VALUE', lineno=1)])
        self.assertEqual(code.to_code().co_consts,
                         (obj1, obj2, obj3, obj4))

        def f():
            return  # pragma: no cover

        f.__code__ = code.to_code()
        self.assertEqual(f(), (obj1, obj2, obj3, obj4))
示例#16
0
    def test_setlineno(self):
        # x = 7
        # y = 8
        # z = 9
        code = Bytecode()
        code.first_lineno = 3
        code.extend([Instr("LOAD_CONST", 7),
                     Instr("STORE_NAME", 'x'),
                     SetLineno(4),
                     Instr("LOAD_CONST", 8),
                     Instr("STORE_NAME", 'y'),
                     SetLineno(5),
                     Instr("LOAD_CONST", 9),
                     Instr("STORE_NAME", 'z')])

        blocks = ControlFlowGraph.from_bytecode(code)
        self.assertBlocksEqual(blocks,
                               [Instr("LOAD_CONST", 7),
                                Instr("STORE_NAME", 'x'),
                                SetLineno(4),
                                Instr("LOAD_CONST", 8),
                                Instr("STORE_NAME", 'y'),
                                SetLineno(5),
                                Instr("LOAD_CONST", 9),
                                Instr("STORE_NAME", 'z')])
示例#17
0
 def test_slice(self):
     code = Bytecode()
     code.first_lineno = 3
     code.extend([
         Instr("LOAD_CONST", 7),
         Instr("STORE_NAME", "x"),
         SetLineno(4),
         Instr("LOAD_CONST", 8),
         Instr("STORE_NAME", "y"),
         SetLineno(5),
         Instr("LOAD_CONST", 9),
         Instr("STORE_NAME", "z"),
     ])
     sliced_code = code[:]
     self.assertEqual(code, sliced_code)
     for name in (
             "argcount",
             "posonlyargcount",
             "kwonlyargcount",
             "first_lineno",
             "name",
             "filename",
             "docstring",
             "cellvars",
             "freevars",
             "argnames",
     ):
         self.assertEqual(getattr(code, name, None),
                          getattr(sliced_code, name, None))
示例#18
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)])
示例#19
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 WORDCODE:
            expected = b'\x90\x01\x90\x00q\x06' + NOP * nb_nop + b'd\x00S\x00'
        else:
            expected = b'\x90\x01\x00q\x06\x00' + NOP * nb_nop + b'd\x00\x00S'
        self.assertEqual(code_obj.co_code, expected)
示例#20
0
    def test_from_bytecode_loop(self):
        # for x in (1, 2, 3):
        #     if x == 2:
        #         break
        #     continue

        label_loop_start = Label()
        label_loop_exit = Label()
        label_loop_end = Label()

        code = Bytecode()
        code.extend((Instr('SETUP_LOOP', label_loop_end, lineno=1),
                     Instr('LOAD_CONST', (1, 2, 3), lineno=1),
                     Instr('GET_ITER', lineno=1),

                     label_loop_start,
                     Instr('FOR_ITER', label_loop_exit, lineno=1),
                     Instr('STORE_NAME', 'x', lineno=1),
                     Instr('LOAD_NAME', 'x', lineno=2),
                     Instr('LOAD_CONST', 2, lineno=2),
                     Instr('COMPARE_OP', Compare.EQ, lineno=2),
                     Instr('POP_JUMP_IF_FALSE', label_loop_start, lineno=2),
                     Instr('BREAK_LOOP', lineno=3),
                     Instr('JUMP_ABSOLUTE', label_loop_start, lineno=4),

                     Instr('JUMP_ABSOLUTE', label_loop_start, lineno=4),

                     label_loop_exit,
                     Instr('POP_BLOCK', lineno=4),

                     label_loop_end,
                     Instr('LOAD_CONST', None, lineno=4),
                     Instr('RETURN_VALUE', lineno=4),
        ))
        blocks = ControlFlowGraph.from_bytecode(code)

        expected = [[Instr('SETUP_LOOP', blocks[8], lineno=1)],

                    [Instr('LOAD_CONST', (1, 2, 3), lineno=1),
                     Instr('GET_ITER', lineno=1)],

                    [Instr('FOR_ITER', blocks[7], lineno=1)],

                    [Instr('STORE_NAME', 'x', lineno=1),
                     Instr('LOAD_NAME', 'x', lineno=2),
                     Instr('LOAD_CONST', 2, lineno=2),
                     Instr('COMPARE_OP', Compare.EQ, lineno=2),
                     Instr('POP_JUMP_IF_FALSE', blocks[2], lineno=2)],

                    [Instr('BREAK_LOOP', lineno=3)],

                    [Instr('JUMP_ABSOLUTE', blocks[2], lineno=4)],

                    [Instr('JUMP_ABSOLUTE', blocks[2], lineno=4)],

                    [Instr('POP_BLOCK', lineno=4)],

                    [Instr('LOAD_CONST', None, lineno=4),
                     Instr('RETURN_VALUE', lineno=4)]]
        self.assertBlocksEqual(blocks, *expected)
示例#21
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)
示例#22
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, [])
示例#23
0
    def test_return_value(self):
        # return+return: remove second return
        #
        #     def func():
        #         return 4
        #         return 5
        code = Bytecode([
            Instr('LOAD_CONST', 4, lineno=2),
            Instr('RETURN_VALUE', lineno=2),
            Instr('LOAD_CONST', 5, lineno=3),
            Instr('RETURN_VALUE', lineno=3)
        ])
        code = ControlFlowGraph.from_bytecode(code)
        self.check(code, Instr('LOAD_CONST', 4, lineno=2),
                   Instr('RETURN_VALUE', lineno=2))

        # return+return + return+return: remove second and fourth return
        #
        #     def func():
        #         return 4
        #         return 5
        #         return 6
        #         return 7
        code = Bytecode([
            Instr('LOAD_CONST', 4, lineno=2),
            Instr('RETURN_VALUE', lineno=2),
            Instr('LOAD_CONST', 5, lineno=3),
            Instr('RETURN_VALUE', lineno=3),
            Instr('LOAD_CONST', 6, lineno=4),
            Instr('RETURN_VALUE', lineno=4),
            Instr('LOAD_CONST', 7, lineno=5),
            Instr('RETURN_VALUE', lineno=5)
        ])
        code = ControlFlowGraph.from_bytecode(code)
        self.check(code, Instr('LOAD_CONST', 4, lineno=2),
                   Instr('RETURN_VALUE', lineno=2))

        # return + JUMP_ABSOLUTE: remove JUMP_ABSOLUTE
        # while 1:
        #     return 7
        setup_loop = Label()
        return_label = Label()
        code = Bytecode([
            setup_loop,
            Instr('SETUP_LOOP', return_label, lineno=2),
            Instr('LOAD_CONST', 7, lineno=3),
            Instr('RETURN_VALUE', lineno=3),
            Instr('JUMP_ABSOLUTE', setup_loop, lineno=3),
            Instr('POP_BLOCK', lineno=3), return_label,
            Instr('LOAD_CONST', None, lineno=3),
            Instr('RETURN_VALUE', lineno=3)
        ])
        code = ControlFlowGraph.from_bytecode(code)

        end_loop = Label()
        self.check(code, Instr('SETUP_LOOP', end_loop, lineno=2),
                   Instr('LOAD_CONST', 7, lineno=3),
                   Instr('RETURN_VALUE', lineno=3), end_loop,
                   Instr('LOAD_CONST', None, lineno=3),
                   Instr('RETURN_VALUE', lineno=3))
示例#24
0
    def test_general_constants(self):
        """Test if general object could be linked as constants.

        """
        class CustomObject:
            pass

        class UnHashableCustomObject:
            __hash__ = None

        obj1 = [1, 2, 3]
        obj2 = {1, 2, 3}
        obj3 = CustomObject()
        obj4 = UnHashableCustomObject()
        code = Bytecode([
            Instr('LOAD_CONST', obj1, lineno=1),
            Instr('LOAD_CONST', obj2, lineno=1),
            Instr('LOAD_CONST', obj3, lineno=1),
            Instr('LOAD_CONST', obj4, lineno=1),
            Instr('BUILD_TUPLE', 4, lineno=1),
            Instr('RETURN_VALUE', lineno=1)
        ])
        self.assertEqual(code.to_code().co_consts, (obj1, obj2, obj3, obj4))

        def f():
            return  # pragma: no cover

        f.__code__ = code.to_code()
        self.assertEqual(f(), (obj1, obj2, obj3, obj4))
示例#25
0
    def test_combined_unary_bin_ops(self):
        # x = 1 + 3 + 7
        code = Bytecode([
            Instr("LOAD_CONST", 1),
            Instr("LOAD_CONST", 3),
            Instr("BINARY_ADD"),
            Instr("LOAD_CONST", 7),
            Instr("BINARY_ADD"),
            Instr("STORE_NAME", "x"),
        ])
        self.check(code, Instr("LOAD_CONST", 11), Instr("STORE_NAME", "x"))

        # x = ~(~(5))
        code = Bytecode([
            Instr("LOAD_CONST", 5),
            Instr("UNARY_INVERT"),
            Instr("UNARY_INVERT"),
            Instr("STORE_NAME", "x"),
        ])
        self.check(code, Instr("LOAD_CONST", 5), Instr("STORE_NAME", "x"))

        # "events = [(0, 'call'), (1, 'line'), (-(3), 'call')]"
        code = Bytecode([
            Instr("LOAD_CONST", 0),
            Instr("LOAD_CONST", "call"),
            Instr("BUILD_TUPLE", 2),
            Instr("LOAD_CONST", 1),
            Instr("LOAD_CONST", "line"),
            Instr("BUILD_TUPLE", 2),
            Instr("LOAD_CONST", 3),
            Instr("UNARY_NEGATIVE"),
            Instr("LOAD_CONST", "call"),
            Instr("BUILD_TUPLE", 2),
            Instr("BUILD_LIST", 3),
            Instr("STORE_NAME", "events"),
        ])
        self.check(
            code,
            Instr("LOAD_CONST", (0, "call")),
            Instr("LOAD_CONST", (1, "line")),
            Instr("LOAD_CONST", (-3, "call")),
            Instr("BUILD_LIST", 3),
            Instr("STORE_NAME", "events"),
        )

        # 'x = (1,) + (0,) * 8'
        code = Bytecode([
            Instr("LOAD_CONST", 1),
            Instr("BUILD_TUPLE", 1),
            Instr("LOAD_CONST", 0),
            Instr("BUILD_TUPLE", 1),
            Instr("LOAD_CONST", 8),
            Instr("BINARY_MULTIPLY"),
            Instr("BINARY_ADD"),
            Instr("STORE_NAME", "x"),
        ])
        zeros = (0, ) * 8
        result = (1, ) + zeros
        self.check(code, Instr("LOAD_CONST", result), Instr("STORE_NAME", "x"))
示例#26
0
 def __init__(self):
     self.co_code = Bytecode()
     self.co_consts = [None]
     self.co_names = []
     self.co_varnames = []
     self.co_lnotab = array('B')
     self.blocks = []
     self.stack_history = []
示例#27
0
    def test_all(self):
        for each in filter(lambda p: p[-1].endswith('.py'), yapypy.collect()):
            filename = each.__str__()

            if each.parent().exists():
                pass
            else:
                each.parent().mkdir()

            with each.open('r') as fr:
                collector = DocStringsCollector()
                mod = ast.parse(fr.read())
                collector.visit(mod)

            mod_name, _ = splitext(each.relative())

            for idx, [fn_name, lineno, title, prepare_code,
                      test_code] in enumerate(collector.docs):

                print(f'tests of {mod_name}.{title or fn_name} started...')

                context = {'self': self}
                prepare_code = dedent_all(prepare_code)
                test_code = dedent_all(test_code)
                fixer = FixLineno(lineno)
                try:
                    node = ast.parse(prepare_code, filename, mode='exec')

                    fixer.visit(node)
                    code = compile(node, filename, "exec")
                except SyntaxError as exc:
                    exc.lineno = lineno
                    exc.filename = filename
                    raise exc
                bc = Bytecode.from_code(code)
                bc.filename = filename
                bc.first_lineno = lineno
                exec(bc.to_code(), context)

                # not correct but as a workaround

                fixer = FixLineno(lineno + test_code.count('\n'))
                try:
                    node = parse(test_code, filename).result
                    # pprint(node)
                    fixer.visit(node)
                    code = py_compile(node, filename, is_entrypoint=True)
                except SyntaxError as exc:
                    exc.lineno = lineno
                    exc.filename = filename
                    raise exc
                bc = Bytecode.from_code(code)
                bc.filename = filename
                bc.first_lineno = lineno
                code_obj = bc.to_code()

                exec(code_obj, context)
                print(f'tests of {mod_name}.{title or fn_name} passed.')
    def test_build_list_unpack_seq(self):
        for build_list in ("BUILD_TUPLE", "BUILD_LIST"):
            # x, = [a]
            code = Bytecode(
                [
                    Instr("LOAD_NAME", "a"),
                    Instr(build_list, 1),
                    Instr("UNPACK_SEQUENCE", 1),
                    Instr("STORE_NAME", "x"),
                ]
            )
            self.check(code, Instr("LOAD_NAME", "a"), Instr("STORE_NAME", "x"))

            # x, y = [a, b]
            code = Bytecode(
                [
                    Instr("LOAD_NAME", "a"),
                    Instr("LOAD_NAME", "b"),
                    Instr(build_list, 2),
                    Instr("UNPACK_SEQUENCE", 2),
                    Instr("STORE_NAME", "x"),
                    Instr("STORE_NAME", "y"),
                ]
            )
            self.check(
                code,
                Instr("LOAD_NAME", "a"),
                Instr("LOAD_NAME", "b"),
                Instr("ROT_TWO"),
                Instr("STORE_NAME", "x"),
                Instr("STORE_NAME", "y"),
            )

            # x, y, z = [a, b, c]
            code = Bytecode(
                [
                    Instr("LOAD_NAME", "a"),
                    Instr("LOAD_NAME", "b"),
                    Instr("LOAD_NAME", "c"),
                    Instr(build_list, 3),
                    Instr("UNPACK_SEQUENCE", 3),
                    Instr("STORE_NAME", "x"),
                    Instr("STORE_NAME", "y"),
                    Instr("STORE_NAME", "z"),
                ]
            )
            self.check(
                code,
                Instr("LOAD_NAME", "a"),
                Instr("LOAD_NAME", "b"),
                Instr("LOAD_NAME", "c"),
                Instr("ROT_THREE"),
                Instr("ROT_TWO"),
                Instr("STORE_NAME", "x"),
                Instr("STORE_NAME", "y"),
                Instr("STORE_NAME", "z"),
            )
示例#29
0
    def _get_predefine_code(self, predefine):
        bytecode_ = Bytecode.from_code(predefine.__code__)
        for bc in bytecode_:
            # stubstitute *_FAST to *_NAME such as STORE_FAST to STORE_NAME
            # so that variable will be stored in given namespace
            bc.name = bc.name.replace('_FAST', '_NAME')

        # there is a mysterious bug with 'return bytecode_.to_code()'
        return Bytecode(bytecode_).to_code()
示例#30
0
    def test_from_bytecode_loop(self):
        # for x in (1, 2, 3):
        #     if x == 2:
        #         break
        #     continue

        label_loop_start = Label()
        label_loop_exit = Label()
        label_loop_end = Label()

        code = Bytecode()
        code.extend((
            Instr("SETUP_LOOP", label_loop_end, lineno=1),
            Instr("LOAD_CONST", (1, 2, 3), lineno=1),
            Instr("GET_ITER", lineno=1),
            label_loop_start,
            Instr("FOR_ITER", label_loop_exit, lineno=1),
            Instr("STORE_NAME", "x", lineno=1),
            Instr("LOAD_NAME", "x", lineno=2),
            Instr("LOAD_CONST", 2, lineno=2),
            Instr("COMPARE_OP", Compare.EQ, lineno=2),
            Instr("POP_JUMP_IF_FALSE", label_loop_start, lineno=2),
            Instr("BREAK_LOOP", lineno=3),
            Instr("JUMP_ABSOLUTE", label_loop_start, lineno=4),
            Instr("JUMP_ABSOLUTE", label_loop_start, lineno=4),
            label_loop_exit,
            Instr("POP_BLOCK", lineno=4),
            label_loop_end,
            Instr("LOAD_CONST", None, lineno=4),
            Instr("RETURN_VALUE", lineno=4),
        ))
        blocks = ControlFlowGraph.from_bytecode(code)

        expected = [
            [Instr("SETUP_LOOP", blocks[8], lineno=1)],
            [
                Instr("LOAD_CONST", (1, 2, 3), lineno=1),
                Instr("GET_ITER", lineno=1)
            ],
            [Instr("FOR_ITER", blocks[7], lineno=1)],
            [
                Instr("STORE_NAME", "x", lineno=1),
                Instr("LOAD_NAME", "x", lineno=2),
                Instr("LOAD_CONST", 2, lineno=2),
                Instr("COMPARE_OP", Compare.EQ, lineno=2),
                Instr("POP_JUMP_IF_FALSE", blocks[2], lineno=2),
            ],
            [Instr("BREAK_LOOP", lineno=3)],
            [Instr("JUMP_ABSOLUTE", blocks[2], lineno=4)],
            [Instr("JUMP_ABSOLUTE", blocks[2], lineno=4)],
            [Instr("POP_BLOCK", lineno=4)],
            [
                Instr("LOAD_CONST", None, lineno=4),
                Instr("RETURN_VALUE", lineno=4)
            ],
        ]
        self.assertBlocksEqual(blocks, *expected)
示例#31
0
    def test_combined_unary_bin_ops(self):
        # x = 1 + 3 + 7
        code = Bytecode([
            Instr('LOAD_CONST', 1),
            Instr('LOAD_CONST', 3),
            Instr('BINARY_ADD'),
            Instr('LOAD_CONST', 7),
            Instr('BINARY_ADD'),
            Instr('STORE_NAME', 'x')
        ])
        self.check(code, Instr('LOAD_CONST', 11), Instr('STORE_NAME', 'x'))

        # x = ~(~(5))
        code = Bytecode([
            Instr('LOAD_CONST', 5),
            Instr('UNARY_INVERT'),
            Instr('UNARY_INVERT'),
            Instr('STORE_NAME', 'x')
        ])
        self.check(code, Instr('LOAD_CONST', 5), Instr('STORE_NAME', 'x'))

        # "events = [(0, 'call'), (1, 'line'), (-(3), 'call')]"
        code = Bytecode([
            Instr('LOAD_CONST', 0),
            Instr('LOAD_CONST', 'call'),
            Instr('BUILD_TUPLE', 2),
            Instr('LOAD_CONST', 1),
            Instr('LOAD_CONST', 'line'),
            Instr('BUILD_TUPLE', 2),
            Instr('LOAD_CONST', 3),
            Instr('UNARY_NEGATIVE'),
            Instr('LOAD_CONST', 'call'),
            Instr('BUILD_TUPLE', 2),
            Instr('BUILD_LIST', 3),
            Instr('STORE_NAME', 'events')
        ])
        self.check(code, Instr('LOAD_CONST', (0, 'call')),
                   Instr('LOAD_CONST', (1, 'line')),
                   Instr('LOAD_CONST', (-3, 'call')), Instr('BUILD_LIST', 3),
                   Instr('STORE_NAME', 'events'))

        # 'x = (1,) + (0,) * 8'
        code = Bytecode([
            Instr('LOAD_CONST', 1),
            Instr('BUILD_TUPLE', 1),
            Instr('LOAD_CONST', 0),
            Instr('BUILD_TUPLE', 1),
            Instr('LOAD_CONST', 8),
            Instr('BINARY_MULTIPLY'),
            Instr('BINARY_ADD'),
            Instr('STORE_NAME', 'x')
        ])
        zeros = (0, ) * 8
        result = (1, ) + zeros
        self.check(code, Instr('LOAD_CONST', result), Instr('STORE_NAME', 'x'))
示例#32
0
    def test_from_bytecode_loop(self):
        # for x in (1, 2, 3):
        #     if x == 2:
        #         break
        #     continue

        label_loop_start = Label()
        label_loop_exit = Label()
        label_loop_end = Label()

        code = Bytecode()
        code.extend((
            Instr('SETUP_LOOP', label_loop_end, lineno=1),
            Instr('LOAD_CONST', (1, 2, 3), lineno=1),
            Instr('GET_ITER', lineno=1),
            label_loop_start,
            Instr('FOR_ITER', label_loop_exit, lineno=1),
            Instr('STORE_NAME', 'x', lineno=1),
            Instr('LOAD_NAME', 'x', lineno=2),
            Instr('LOAD_CONST', 2, lineno=2),
            Instr('COMPARE_OP', Compare.EQ, lineno=2),
            Instr('POP_JUMP_IF_FALSE', label_loop_start, lineno=2),
            Instr('BREAK_LOOP', lineno=3),
            Instr('JUMP_ABSOLUTE', label_loop_start, lineno=4),
            Instr('JUMP_ABSOLUTE', label_loop_start, lineno=4),
            label_loop_exit,
            Instr('POP_BLOCK', lineno=4),
            label_loop_end,
            Instr('LOAD_CONST', None, lineno=4),
            Instr('RETURN_VALUE', lineno=4),
        ))
        blocks = ControlFlowGraph.from_bytecode(code)

        expected = [[Instr('SETUP_LOOP', blocks[8], lineno=1)],
                    [
                        Instr('LOAD_CONST', (1, 2, 3), lineno=1),
                        Instr('GET_ITER', lineno=1)
                    ], [Instr('FOR_ITER', blocks[7], lineno=1)],
                    [
                        Instr('STORE_NAME', 'x', lineno=1),
                        Instr('LOAD_NAME', 'x', lineno=2),
                        Instr('LOAD_CONST', 2, lineno=2),
                        Instr('COMPARE_OP', Compare.EQ, lineno=2),
                        Instr('POP_JUMP_IF_FALSE', blocks[2], lineno=2)
                    ], [Instr('BREAK_LOOP', lineno=3)],
                    [Instr('JUMP_ABSOLUTE', blocks[2], lineno=4)],
                    [Instr('JUMP_ABSOLUTE', blocks[2], lineno=4)],
                    [Instr('POP_BLOCK', lineno=4)],
                    [
                        Instr('LOAD_CONST', None, lineno=4),
                        Instr('RETURN_VALUE', lineno=4)
                    ]]
        self.assertBlocksEqual(blocks, *expected)
示例#33
0
    def check(self, source, function=False):
        ref_code = get_code(source, function=function)

        code = ConcreteBytecode.from_code(ref_code).to_code()
        self.assertEqual(code, ref_code)

        code = Bytecode.from_code(ref_code).to_code()
        self.assertEqual(code, ref_code)

        bytecode = Bytecode.from_code(ref_code)
        blocks = ControlFlowGraph.from_bytecode(bytecode)
        code = blocks.to_bytecode().to_code()
        self.assertEqual(code, ref_code)
示例#34
0
    def check(self, source, function=False):
        ref_code = get_code(source, function=function)

        code = ConcreteBytecode.from_code(ref_code).to_code()
        self.assertEqual(code, ref_code)

        code = Bytecode.from_code(ref_code).to_code()
        self.assertEqual(code, ref_code)

        bytecode = Bytecode.from_code(ref_code)
        blocks = ControlFlowGraph.from_bytecode(bytecode)
        code = blocks.to_bytecode().to_code()
        self.assertEqual(code, ref_code)
示例#35
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'])
示例#36
0
    def _make_bytecode(self, source, template_locator):
        instructions = []
        symbol_table = {"write_func": io.StringIO.write}

        parser_obj = parser.Parser(self._template_locator)
        sequence = parser_obj.parse(self._get_chunks(source))

        for item in sequence.elements:
            instructions += item.make_bytecode(symbol_table)

        bytecode = Bytecode(instructions +
                            [Instr("LOAD_CONST", None),
                             Instr("RETURN_VALUE")])
        return bytecode.to_code()
示例#37
0
    def test_setlineno(self):
        # x = 7
        # y = 8
        # z = 9
        code = Bytecode()
        code.first_lineno = 3
        code.extend([
            Instr("LOAD_CONST", 7),
            Instr("STORE_NAME", 'x'),
            SetLineno(4),
            Instr("LOAD_CONST", 8),
            Instr("STORE_NAME", 'y'),
            SetLineno(5),
            Instr("LOAD_CONST", 9),
            Instr("STORE_NAME", 'z')
        ])

        concrete = code.to_concrete_bytecode()
        self.assertEqual(concrete.consts, [7, 8, 9])
        self.assertEqual(concrete.names, ['x', 'y', 'z'])
        code.extend([
            ConcreteInstr("LOAD_CONST", 0, lineno=3),
            ConcreteInstr("STORE_NAME", 0, lineno=3),
            ConcreteInstr("LOAD_CONST", 1, lineno=4),
            ConcreteInstr("STORE_NAME", 1, lineno=4),
            ConcreteInstr("LOAD_CONST", 2, lineno=5),
            ConcreteInstr("STORE_NAME", 2, lineno=5)
        ])
示例#38
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'])
示例#39
0
 def test_cellvars(self):
     code = Bytecode()
     code.cellvars = ['x']
     code.freevars = ['y']
     code.extend([Instr('LOAD_DEREF', CellVar('x'), lineno=1),
                  Instr('LOAD_DEREF', FreeVar('y'), lineno=1)])
     concrete = code.to_concrete_bytecode()
     self.assertEqual(concrete.cellvars, ['x'])
     self.assertEqual(concrete.freevars, ['y'])
     code.extend([ConcreteInstr("LOAD_DEREF", 0, lineno=1),
                  ConcreteInstr("LOAD_DEREF", 1, lineno=1)])
示例#40
0
    def test_dont_merge_constants(self):
        # test two constants which are equal but have a different type
        code = Bytecode()
        code.extend([Instr('LOAD_CONST', 5, lineno=1),
                     Instr('LOAD_CONST', 5.0, lineno=1),
                     Instr('LOAD_CONST', -0.0, lineno=1),
                     Instr('LOAD_CONST', +0.0, lineno=1)])

        code = code.to_concrete_bytecode()
        expected = [ConcreteInstr('LOAD_CONST', 0, lineno=1),
                    ConcreteInstr('LOAD_CONST', 1, lineno=1),
                    ConcreteInstr('LOAD_CONST', 2, lineno=1),
                    ConcreteInstr('LOAD_CONST', 3, lineno=1)]
        self.assertListEqual(list(code), expected)
        self.assertListEqual(code.consts, [5, 5.0, -0.0, +0.0])
示例#41
0
    def test_dont_merge_constants(self):
        # test two constants which are equal but have a different type
        code = Bytecode()
        code.extend([Instr('LOAD_CONST', 5, lineno=1),
                     Instr('LOAD_CONST', 5.0, lineno=1),
                     Instr('LOAD_CONST', -0.0, lineno=1),
                     Instr('LOAD_CONST', +0.0, lineno=1)])

        code = code.to_concrete_bytecode()
        expected = [ConcreteInstr('LOAD_CONST', 0, lineno=1),
                    ConcreteInstr('LOAD_CONST', 1, lineno=1),
                    ConcreteInstr('LOAD_CONST', 2, lineno=1),
                    ConcreteInstr('LOAD_CONST', 3, lineno=1)]
        self.assertListEqual(list(code), expected)
        self.assertListEqual(code.consts, [5, 5.0, -0.0, +0.0])
示例#42
0
    def optimize(self, code_obj):
        bytecode = Bytecode.from_code(code_obj)
        cfg = ControlFlowGraph.from_bytecode(bytecode)

        self.optimize_cfg(cfg)

        bytecode = cfg.to_bytecode()
        code = bytecode.to_code()
        return code
示例#43
0
 def test_cellvars(self):
     code = Bytecode()
     code.cellvars = ['x']
     code.freevars = ['y']
     code.extend([Instr('LOAD_DEREF', CellVar('x'), lineno=1),
                  Instr('LOAD_DEREF', FreeVar('y'), lineno=1)])
     concrete = code.to_concrete_bytecode()
     self.assertEqual(concrete.cellvars, ['x'])
     self.assertEqual(concrete.freevars, ['y'])
     code.extend([ConcreteInstr("LOAD_DEREF", 0, lineno=1),
                  ConcreteInstr("LOAD_DEREF", 1, lineno=1)])
示例#44
0
    def test_optimize_code_obj(self):
        # Test optimize() method with a code object
        #
        # x = 3 + 5 => x = 8
        noopt = Bytecode([Instr('LOAD_CONST', 3),
                          Instr('LOAD_CONST', 5),
                          Instr('BINARY_ADD'),
                          Instr('STORE_NAME', 'x'),
                          Instr('LOAD_CONST', None),
                          Instr('RETURN_VALUE')])
        noopt = noopt.to_code()

        optimizer = peephole_opt.PeepholeOptimizer()
        optim = optimizer.optimize(noopt)

        code = Bytecode.from_code(optim)
        self.assertEqual(code,
                         [Instr('LOAD_CONST', 8, lineno=1),
                          Instr('STORE_NAME', 'x', lineno=1),
                          Instr('LOAD_CONST', None, lineno=1),
                          Instr('RETURN_VALUE', lineno=1)])
示例#45
0
 def test_from_code_load_fast(self):
     code = get_code("""
         def func():
             x = 33
             y = x
     """, function=True)
     code = Bytecode.from_code(code)
     self.assertEqual(code,
                      [Instr('LOAD_CONST', 33, lineno=2),
                       Instr('STORE_FAST', 'x', lineno=2),
                       Instr('LOAD_FAST', 'x', lineno=3),
                       Instr('STORE_FAST', 'y', lineno=3),
                       Instr('LOAD_CONST', None, lineno=3),
                       Instr('RETURN_VALUE', lineno=3)])
示例#46
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])

        code_obj = code.to_code()
        expected = (b'\x90\x01\x00q\x06\x00' + NOP * nb_nop)
        self.assertEqual(code_obj.co_code, expected)
示例#47
0
    def test_from_code_freevars(self):
        ns = {}
        exec(textwrap.dedent('''
            def create_func():
                x = 1
                def func():
                    return x
                return func

            func = create_func()
        '''), ns, ns)
        code = ns['func'].__code__

        bytecode = Bytecode.from_code(code)
        self.assertEqual(bytecode,
                         [Instr('LOAD_DEREF', FreeVar('x'), lineno=5),
                          Instr('RETURN_VALUE', lineno=5)])
示例#48
0
    def test_to_code(self):
        code = Bytecode()
        code.first_lineno = 50
        code.extend([Instr("LOAD_NAME", "print"),
                     Instr("LOAD_CONST", "%s"),
                     Instr("LOAD_GLOBAL", "a"),
                     Instr("BINARY_MODULO"),
                     Instr("CALL_FUNCTION", 1),
                     Instr("RETURN_VALUE")])
        co = code.to_code()
        # hopefully this is obvious from inspection? :-)
        self.assertEqual(co.co_stacksize, 3)

        co = code.to_code(stacksize=42)
        self.assertEqual(co.co_stacksize, 42)
示例#49
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)])
示例#50
0
    def test_setlineno(self):
        # x = 7
        # y = 8
        # z = 9
        code = Bytecode()
        code.first_lineno = 3
        code.extend([Instr("LOAD_CONST", 7),
                     Instr("STORE_NAME", 'x'),
                     SetLineno(4),
                     Instr("LOAD_CONST", 8),
                     Instr("STORE_NAME", 'y'),
                     SetLineno(5),
                     Instr("LOAD_CONST", 9),
                     Instr("STORE_NAME", 'z')])

        concrete = code.to_concrete_bytecode()
        self.assertEqual(concrete.consts, [7, 8, 9])
        self.assertEqual(concrete.names, ['x', 'y', 'z'])
        code.extend([ConcreteInstr("LOAD_CONST", 0, lineno=3),
                     ConcreteInstr("STORE_NAME", 0, lineno=3),
                     ConcreteInstr("LOAD_CONST", 1, lineno=4),
                     ConcreteInstr("STORE_NAME", 1, lineno=4),
                     ConcreteInstr("LOAD_CONST", 2, lineno=5),
                     ConcreteInstr("STORE_NAME", 2, lineno=5)])
示例#51
0
    def test_label3(self):
        """
        CPython generates useless EXTENDED_ARG 0 in some cases. We need to
        properly track them as otherwise we can end up with broken offset for
        jumps.
        """
        source = """
            def func(x):
                if x == 1:
                    return x + 0
                elif x == 2:
                    return x + 1
                elif x == 3:
                    return x + 2
                elif x == 4:
                    return x + 3
                elif x == 5:
                    return x + 4
                elif x == 6:
                    return x + 5
                elif x == 7:
                    return x + 6
                elif x == 8:
                    return x + 7
                elif x == 9:
                    return x + 8
                elif x == 10:
                    return x + 9
                elif x == 11:
                    return x + 10
                elif x == 12:
                    return x + 11
                elif x == 13:
                    return x + 12
                elif x == 14:
                    return x + 13
                elif x == 15:
                    return x + 14
                elif x == 16:
                    return x + 15
                elif x == 17:
                    return x + 16
                return -1
        """
        code = get_code(source, function=True)
        bcode = Bytecode.from_code(code)
        concrete = bcode.to_concrete_bytecode()
        self.assertIsInstance(concrete, ConcreteBytecode)

        # Ensure that we do not generate broken code
        loc = {}
        exec(textwrap.dedent(source), loc)
        func = loc['func']
        func.__code__ = bcode.to_code()
        for i, x in enumerate(range(1, 18)):
            self.assertEqual(func(x), x + i)
        self.assertEqual(func(18), -1)

        # Ensure that we properly round trip in such cases
        self.assertEqual(ConcreteBytecode.from_code(code).to_code().co_code,
                         code.co_code)
示例#52
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 l in range(0, len(labels)):
            code.insert(offset, labels[l])
            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))
示例#53
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 = 'UNARY_POSITIVE'   # don't use NOP, dis.stack_effect will raise
        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)