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)
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, lineno=1), ConcreteInstr("LOAD_CONST", 0, lineno=2), ConcreteInstr("STORE_NAME", 1, lineno=2), ConcreteInstr("JUMP_FORWARD", 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(concrete), expected) self.assertListEqual(concrete.consts, [5, 7, None]) self.assertListEqual(concrete.names, ["test", "x"]) self.assertListEqual(concrete.varnames, [])
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", "first_lineno", "name", "filename", "docstring", "cellvars", "freevars", "argnames", ): Undefined = object() self.assertEqual(getattr(code, name, Undefined), getattr(sliced_code, name, Undefined))
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, [])
def test_negative_size_unpack(self): with self.subTest(): code = Bytecode() code.first_lineno = 1 code.extend([Instr("UNPACK_SEQUENCE", 1)]) with self.assertRaises(RuntimeError): code.compute_stacksize()
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')])
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)])
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_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"]) self.assertListEqual( list(concrete), [ 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), ], )
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), ], )
def test_copy(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"), ] ) copy_code = code.copy() self.assertEqual(code, copy_code) for name in ( "argcount", "posonlyargcount", "kwonlyargcount", "first_lineno", "name", "filename", "docstring", "cellvars", "freevars", "argnames", ): self.assertEqual(getattr(code, name, None), getattr(copy_code, name, None))
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) ])
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_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), ], )
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))
def test_negative_size_build_const_map(self): code = Bytecode() code.first_lineno = 1 code.extend( [Instr("LOAD_CONST", ("a", )), Instr("BUILD_CONST_KEY_MAP", 1)]) with self.assertRaises(RuntimeError): code.compute_stacksize()
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))
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)
def test_not_enough_rot(self): opnames = ["ROT_TWO", "ROT_THREE"] for opname in opnames: with self.subTest(): code = Bytecode() code.first_lineno = 1 code.extend([Instr("LOAD_CONST", 1), Instr(opname)]) with self.assertRaises(RuntimeError): code.compute_stacksize()
def test_negative_size_build_const_map_with_disable_check_of_pre_and_post( self): code = Bytecode() code.first_lineno = 1 code.extend( [Instr("LOAD_CONST", ("a", )), Instr("BUILD_CONST_KEY_MAP", 1)]) co = code.to_code(check_pre_and_post=False) self.assertEqual(co.co_stacksize, 1)
def test_not_enough_rot_with_disable_check_of_pre_and_post(self): opnames = ["ROT_TWO", "ROT_THREE"] for opname in opnames: with self.subTest(): code = Bytecode() code.first_lineno = 1 code.extend([Instr("LOAD_CONST", 1), Instr(opname)]) co = code.to_code(check_pre_and_post=False) self.assertEqual(co.co_stacksize, 1)
def test_handling_of_extended_arg(self): code = Bytecode() code.first_lineno = 3 code.extend([Instr("LOAD_CONST", 7), Instr("STORE_NAME", 'x'), Instr("EXTENDED_ARG", 1), Instr("LOAD_CONST", 8), Instr("STORE_NAME", 'y')]) self.assertEqual(code.compute_stacksize(), 1)
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)])
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)
def test_handling_of_set_lineno(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')]) self.assertEqual(code.compute_stacksize(), 1)
def test_negative_size_build(self): opnames = ( "BUILD_TUPLE", "BUILD_LIST", "BUILD_SET", ) for opname in opnames: with self.subTest(): code = Bytecode() code.first_lineno = 1 code.extend([Instr(opname, 1)]) with self.assertRaises(RuntimeError): code.compute_stacksize()
def test_negative_size_unary_with_disable_check_of_pre_and_post(self): opnames = ( "UNARY_POSITIVE", "UNARY_NEGATIVE", "UNARY_NOT", "UNARY_INVERT", ) for opname in opnames: with self.subTest(): code = Bytecode() code.first_lineno = 1 code.extend([Instr(opname)]) co = code.to_code(check_pre_and_post=False) self.assertEqual(co.co_stacksize, 0)
def test_negative_size_unary(self): opnames = ( "UNARY_POSITIVE", "UNARY_NEGATIVE", "UNARY_NOT", "UNARY_INVERT", ) for opname in opnames: with self.subTest(): code = Bytecode() code.first_lineno = 1 code.extend([Instr(opname)]) with self.assertRaises(RuntimeError): code.compute_stacksize()
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'])
def r_compile(): jit_func = Aware.f(self, id(start_func)) bc = Bytecode() bc.append(PyInstr(InstrNames.LOAD_CONST, jit_func)) bc.extend([load_arg(each, cellvars, lineno) for each in argnames]) bc.extend([ PyInstr(InstrNames.CALL_FUNCTION, len(argnames)), PyInstr(InstrNames.RETURN_VALUE) ]) bc._copy_attr_from(code) start_func.__code__ = bc.to_code() start_func.__jit__ = jit_func return jit_func
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), ])
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])
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)
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)
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)
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)])