def test_extended_arg_make_function(self): code_obj = get_code( """ def foo(x: int, y: int): pass """ ) # without EXTENDED_ARG concrete = ConcreteBytecode.from_code(code_obj) func_code = concrete.consts[1] self.assertEqual(concrete.names, ["int", "foo"]) self.assertEqual(concrete.consts, [("x", "y"), func_code, "foo", None]) if WORDCODE: expected = [ ConcreteInstr("LOAD_NAME", 0, lineno=1), ConcreteInstr("LOAD_NAME", 0, lineno=1), ConcreteInstr("LOAD_CONST", 0, lineno=1), ConcreteInstr("BUILD_CONST_KEY_MAP", 2, lineno=1), ConcreteInstr("LOAD_CONST", 1, lineno=1), ConcreteInstr("LOAD_CONST", 2, lineno=1), ConcreteInstr("MAKE_FUNCTION", 4, lineno=1), ConcreteInstr("STORE_NAME", 1, lineno=1), ConcreteInstr("LOAD_CONST", 3, lineno=1), ConcreteInstr("RETURN_VALUE", lineno=1), ] else: expected = [ ConcreteInstr("LOAD_NAME", 0, lineno=1), ConcreteInstr("LOAD_NAME", 0, lineno=1), ConcreteInstr("LOAD_CONST", 0, lineno=1), ConcreteInstr("LOAD_CONST", 1, lineno=1), ConcreteInstr("LOAD_CONST", 2, lineno=1), ConcreteInstr("MAKE_FUNCTION", 3 << 16, lineno=1), ConcreteInstr("STORE_NAME", 1, lineno=1), ConcreteInstr("LOAD_CONST", 3, lineno=1), ConcreteInstr("RETURN_VALUE", lineno=1), ] self.assertListEqual(list(concrete), expected) # with EXTENDED_ARG concrete = ConcreteBytecode.from_code(code_obj, extended_arg=True) func_code = concrete.consts[1] self.assertEqual(concrete.names, ["int", "foo"]) self.assertEqual(concrete.consts, [("x", "y"), func_code, "foo", None]) if not WORDCODE: expected = [ ConcreteInstr("LOAD_NAME", 0, lineno=1), ConcreteInstr("LOAD_NAME", 0, lineno=1), ConcreteInstr("LOAD_CONST", 0, lineno=1), ConcreteInstr("LOAD_CONST", 1, lineno=1), ConcreteInstr("LOAD_CONST", 2, lineno=1), ConcreteInstr("EXTENDED_ARG", 3, lineno=1), ConcreteInstr("MAKE_FUNCTION", 0, lineno=1), ConcreteInstr("STORE_NAME", 1, lineno=1), ConcreteInstr("LOAD_CONST", 3, lineno=1), ConcreteInstr("RETURN_VALUE", lineno=1), ] self.assertListEqual(list(concrete), expected)
def test_cellvar_freevar(self): concrete = ConcreteBytecode() concrete.cellvars = ["cell"] concrete.freevars = ["free"] concrete.append(ConcreteInstr("LOAD_DEREF", 0)) concrete.append(ConcreteInstr("LOAD_DEREF", 1)) code = concrete.to_code() concrete = ConcreteBytecode.from_code(code) self.assertEqual(concrete.cellvars, ["cell"]) self.assertEqual(concrete.freevars, ["free"]) self.assertEqual( list(concrete), [ ConcreteInstr("LOAD_DEREF", 0, lineno=1), ConcreteInstr("LOAD_DEREF", 1, lineno=1), ], ) bytecode = concrete.to_bytecode() self.assertEqual(bytecode.cellvars, ["cell"]) self.assertEqual( list(bytecode), [ Instr("LOAD_DEREF", CellVar("cell"), lineno=1), Instr("LOAD_DEREF", FreeVar("free"), lineno=1), ], )
def test_legalize(self): concrete = ConcreteBytecode() concrete.first_lineno = 3 concrete.consts = [7, 8, 9] concrete.names = ["x", "y", "z"] concrete.extend([ ConcreteInstr("LOAD_CONST", 0), ConcreteInstr("STORE_NAME", 0), ConcreteInstr("LOAD_CONST", 1, lineno=4), ConcreteInstr("STORE_NAME", 1), SetLineno(5), ConcreteInstr("LOAD_CONST", 2, lineno=6), ConcreteInstr("STORE_NAME", 2), ]) concrete.legalize() 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_to_code_lnotab(self): # We use an actual function for the simple case to # ensure we get lnotab right def f(): # # x = 7 # noqa y = 8 # noqa z = 9 # noqa fl = f.__code__.co_firstlineno concrete = ConcreteBytecode() concrete.consts = [None, 7, 8, 9] concrete.varnames = ["x", "y", "z"] concrete.first_lineno = fl concrete.extend([ SetLineno(fl + 3), ConcreteInstr("LOAD_CONST", 1), ConcreteInstr("STORE_FAST", 0), SetLineno(fl + 4), ConcreteInstr("LOAD_CONST", 2), ConcreteInstr("STORE_FAST", 1), SetLineno(fl + 5), ConcreteInstr("LOAD_CONST", 3), ConcreteInstr("STORE_FAST", 2), ConcreteInstr("LOAD_CONST", 0), ConcreteInstr("RETURN_VALUE"), ]) code = concrete.to_code() self.assertEqual(code.co_code, f.__code__.co_code) self.assertEqual(code.co_lnotab, f.__code__.co_lnotab) if sys.version_info >= (3, 10): self.assertEqual(code.co_linetable, f.__code__.co_linetable)
def test_setlineno(self): # x = 7 # y = 8 # z = 9 concrete = ConcreteBytecode() concrete.consts = [7, 8, 9] concrete.names = ["x", "y", "z"] concrete.first_lineno = 3 concrete.extend([ ConcreteInstr("LOAD_CONST", 0), ConcreteInstr("STORE_NAME", 0), SetLineno(4), ConcreteInstr("LOAD_CONST", 1), ConcreteInstr("STORE_NAME", 1), SetLineno(5), ConcreteInstr("LOAD_CONST", 2), ConcreteInstr("STORE_NAME", 2), ]) code = concrete.to_bytecode() self.assertEqual( 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), Instr("LOAD_CONST", 9, lineno=5), Instr("STORE_NAME", "z", lineno=5), ], )
def test_to_bytecode_consts(self): # x = -0.0 # x = +0.0 # # code optimized by the CPython 3.6 peephole optimizer which emits # duplicated constants (0.0 is twice in consts). code = ConcreteBytecode() code.consts = [0.0, None, -0.0, 0.0] code.names = ["x", "y"] code.extend([ ConcreteInstr("LOAD_CONST", 2, lineno=1), ConcreteInstr("STORE_NAME", 0, lineno=1), ConcreteInstr("LOAD_CONST", 3, lineno=2), ConcreteInstr("STORE_NAME", 1, lineno=2), ConcreteInstr("LOAD_CONST", 1, lineno=2), ConcreteInstr("RETURN_VALUE", lineno=2), ]) code = code.to_bytecode().to_concrete_bytecode() # the conversion changes the constant order: the order comes from # the order of LOAD_CONST instructions self.assertEqual(code.consts, [-0.0, 0.0, None]) code.names = ["x", "y"] self.assertListEqual( list(code), [ ConcreteInstr("LOAD_CONST", 0, lineno=1), ConcreteInstr("STORE_NAME", 0, lineno=1), ConcreteInstr("LOAD_CONST", 1, lineno=2), ConcreteInstr("STORE_NAME", 1, lineno=2), ConcreteInstr("LOAD_CONST", 2, lineno=2), ConcreteInstr("RETURN_VALUE", lineno=2), ], )
def test_to_code_lnotab(self): # x = 7 # y = 8 # z = 9 concrete = ConcreteBytecode() concrete.consts = [7, 8, 9] concrete.names = ["x", "y", "z"] concrete.first_lineno = 3 concrete.extend([ ConcreteInstr("LOAD_CONST", 0), ConcreteInstr("STORE_NAME", 0), SetLineno(4), ConcreteInstr("LOAD_CONST", 1), ConcreteInstr("STORE_NAME", 1), SetLineno(5), ConcreteInstr("LOAD_CONST", 2), ConcreteInstr("STORE_NAME", 2), ]) code = concrete.to_code() if WORDCODE: expected = b"d\x00Z\x00d\x01Z\x01d\x02Z\x02" else: expected = (b"d\x00\x00" b"Z\x00\x00" b"d\x01\x00" b"Z\x01\x00" b"d\x02\x00" b"Z\x02\x00") self.assertEqual(code.co_code, expected) self.assertEqual(code.co_firstlineno, 3) self.assertEqual( code.co_lnotab, b"\x04\x01\x04\x01" if WORDCODE else b"\x06\x01\x06\x01")
def test_extended_arg(self): # Create a code object from arbitrary bytecode co_code = b"\x90\x12\x904\x90\xabd\xcd" if WORDCODE else b"\x904\x12d\xcd\xab" code = get_code("x=1") args = ( (code.co_argcount,) if sys.version_info < (3, 8) else (code.co_argcount, code.co_posonlyargcount) ) args += ( code.co_kwonlyargcount, code.co_nlocals, code.co_stacksize, code.co_flags, co_code, code.co_consts, code.co_names, code.co_varnames, code.co_filename, code.co_name, code.co_firstlineno, code.co_lnotab, code.co_freevars, code.co_cellvars, ) code = types.CodeType(*args) # without EXTENDED_ARG opcode bytecode = ConcreteBytecode.from_code(code) self.assertListEqual( list(bytecode), [ConcreteInstr("LOAD_CONST", 0x1234ABCD, lineno=1)] ) # with EXTENDED_ARG opcode bytecode = ConcreteBytecode.from_code(code, extended_arg=True) if WORDCODE: expected = [ ConcreteInstr("EXTENDED_ARG", 0x12, lineno=1), ConcreteInstr("EXTENDED_ARG", 0x34, lineno=1), ConcreteInstr("EXTENDED_ARG", 0xAB, lineno=1), ConcreteInstr("LOAD_CONST", 0xCD, lineno=1), ] else: expected = [ ConcreteInstr("EXTENDED_ARG", 0x1234, lineno=1), ConcreteInstr("LOAD_CONST", 0xABCD, lineno=1), ] self.assertListEqual(list(bytecode), expected)
def test_async_gen_no_flag_is_async_True(self): # Test inference when we request an async function # Force coroutine code = ConcreteBytecode() code.update_flags(is_async=True) self.assertTrue(bool(code.flags & CompilerFlags.COROUTINE)) # Infer coroutine or async generator for i, expected in ( ("YIELD_VALUE", CompilerFlags.ASYNC_GENERATOR), ("YIELD_FROM", CompilerFlags.COROUTINE), ): code = ConcreteBytecode() code.append(ConcreteInstr(i)) code.update_flags(is_async=True) self.assertTrue(bool(code.flags & expected))
def test_extended_lnotab(self): # x = 7 # y = 8 concrete = ConcreteBytecode([ ConcreteInstr("LOAD_CONST", 0), SetLineno(1 + 128), ConcreteInstr("STORE_NAME", 0), # line number goes backward! SetLineno(1 + 129), ConcreteInstr("LOAD_CONST", 1), SetLineno(1), ConcreteInstr("STORE_NAME", 1), ]) concrete.consts = [7, 8] concrete.names = ["x", "y"] concrete.first_lineno = 1 if sys.version_info >= (3, 6): code = concrete.to_code() expected = b"d\x00Z\x00d\x01Z\x01" self.assertEqual(code.co_code, expected) self.assertEqual(code.co_firstlineno, 1) self.assertEqual(code.co_lnotab, b"\x00\x7f\x02\x01\x02\x01\x00\x80\x02\xff") else: with self.assertRaises(ValueError) as cm: code = concrete.to_code() self.assertEqual( str(cm.exception), "negative line number delta is not supported " "on Python < 3.6", )
def test_extended_arg_unpack_ex(self): def test(): p = [1, 2, 3, 4, 5, 6] q, r, *s, t = p return q, r, s, t test.__code__ = ConcreteBytecode.from_code( test.__code__, extended_arg=True).to_code() self.assertEqual(test.__code__.co_stacksize, 6) self.assertEqual(test(), (1, 2, [3, 4, 5], 6))
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()])
def test_freevar(self): concrete = ConcreteBytecode() concrete.freevars = ["x"] concrete.append(ConcreteInstr("LOAD_DEREF", 0)) code = concrete.to_code() concrete = ConcreteBytecode.from_code(code) self.assertEqual(concrete.cellvars, []) self.assertEqual(concrete.freevars, ["x"]) self.assertEqual(list(concrete), [ConcreteInstr("LOAD_DEREF", 0, lineno=1)]) bytecode = concrete.to_bytecode() self.assertEqual(bytecode.cellvars, []) self.assertEqual(list(bytecode), [Instr("LOAD_DEREF", FreeVar("x"), lineno=1)])
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)
def test_copy(self): concrete = ConcreteBytecode() concrete.first_lineno = 3 concrete.consts = [7, 8, 9] concrete.names = ["x", "y", "z"] concrete.extend([ ConcreteInstr("LOAD_CONST", 0), ConcreteInstr("STORE_NAME", 0), SetLineno(4), ConcreteInstr("LOAD_CONST", 1), ConcreteInstr("STORE_NAME", 1), SetLineno(5), ConcreteInstr("LOAD_CONST", 2), ConcreteInstr("STORE_NAME", 2), ]) self.assertEqual(concrete, concrete.copy())
def test_attr(self): code_obj = get_code("x = 5") code = ConcreteBytecode.from_code(code_obj) self.assertEqual(code.consts, [5, None]) self.assertEqual(code.names, ["x"]) self.assertEqual(code.varnames, []) self.assertEqual(code.freevars, []) self.assertListEqual( list(code), [ ConcreteInstr("LOAD_CONST", 0, lineno=1), ConcreteInstr("STORE_NAME", 0, lineno=1), ConcreteInstr("LOAD_CONST", 1, lineno=1), ConcreteInstr("RETURN_VALUE", lineno=1), ], )
def test_explicit_stacksize(self): # Passing stacksize=... to ConcreteBytecode.to_code should result in a # code object with the specified stacksize. We pass some silly values # and assert that they are honored. code_obj = get_code("print('%s' % (a,b,c))") original_stacksize = code_obj.co_stacksize concrete = ConcreteBytecode.from_code(code_obj) # First with something bigger than necessary. explicit_stacksize = original_stacksize + 42 new_code_obj = concrete.to_code(stacksize=explicit_stacksize) self.assertEqual(new_code_obj.co_stacksize, explicit_stacksize) # Then with something bogus. We probably don't want to advertise this # in the documentation. If this fails then decide if it's for good # reason, and remove if so. explicit_stacksize = 0 new_code_obj = concrete.to_code(stacksize=explicit_stacksize) self.assertEqual(new_code_obj.co_stacksize, explicit_stacksize)
def test_load_classderef(self): concrete = ConcreteBytecode() concrete.cellvars = ["__class__"] concrete.freevars = ["__class__"] concrete.extend([ ConcreteInstr("LOAD_CLASSDEREF", 1), ConcreteInstr("STORE_DEREF", 1) ]) bytecode = concrete.to_bytecode() self.assertEqual(bytecode.freevars, ["__class__"]) self.assertEqual(bytecode.cellvars, ["__class__"]) self.assertEqual( list(bytecode), [ Instr("LOAD_CLASSDEREF", FreeVar("__class__"), lineno=1), Instr("STORE_DEREF", FreeVar("__class__"), lineno=1), ], ) concrete = bytecode.to_concrete_bytecode() self.assertEqual(concrete.freevars, ["__class__"]) self.assertEqual(concrete.cellvars, ["__class__"]) self.assertEqual( list(concrete), [ ConcreteInstr("LOAD_CLASSDEREF", 1, lineno=1), ConcreteInstr("STORE_DEREF", 1, lineno=1), ], ) code = concrete.to_code() self.assertEqual(code.co_freevars, ("__class__", )) self.assertEqual(code.co_cellvars, ("__class__", )) self.assertEqual( code.co_code, b"\x94\x01\x89\x01", )
def test_negative_lnotab(self): # x = 7 # y = 8 concrete = ConcreteBytecode([ ConcreteInstr("LOAD_CONST", 0), ConcreteInstr("STORE_NAME", 0), # line number goes backward! SetLineno(2), ConcreteInstr("LOAD_CONST", 1), ConcreteInstr("STORE_NAME", 1), ]) concrete.consts = [7, 8] concrete.names = ["x", "y"] concrete.first_lineno = 5 code = concrete.to_code() expected = b"d\x00Z\x00d\x01Z\x01" self.assertEqual(code.co_code, expected) self.assertEqual(code.co_firstlineno, 5) self.assertEqual(code.co_lnotab, b"\x04\xfd")
def test_extended_lnotab2(self): # x = 7 # 200 blank lines # y = 8 base_code = compile("x = 7" + "\n" * 200 + "y = 8", "", "exec") concrete = ConcreteBytecode([ ConcreteInstr("LOAD_CONST", 0), ConcreteInstr("STORE_NAME", 0), SetLineno(201), ConcreteInstr("LOAD_CONST", 1), ConcreteInstr("STORE_NAME", 1), ConcreteInstr("LOAD_CONST", 2), ConcreteInstr("RETURN_VALUE"), ]) concrete.consts = [None, 7, 8] concrete.names = ["x", "y"] concrete.first_lineno = 1 code = concrete.to_code() self.assertEqual(code.co_code, base_code.co_code) self.assertEqual(code.co_firstlineno, base_code.co_firstlineno) self.assertEqual(code.co_lnotab, base_code.co_lnotab) if sys.version_info >= (3, 10): self.assertEqual(code.co_linetable, base_code.co_linetable)
def test_extended_lnotab(self): # x = 7 # 200 blank lines # y = 8 concrete = ConcreteBytecode([ ConcreteInstr("LOAD_CONST", 0), SetLineno(1 + 128), ConcreteInstr("STORE_NAME", 0), # line number goes backward! SetLineno(1 + 129), ConcreteInstr("LOAD_CONST", 1), SetLineno(1), ConcreteInstr("STORE_NAME", 1), ]) concrete.consts = [7, 8] concrete.names = ["x", "y"] concrete.first_lineno = 1 code = concrete.to_code() expected = b"d\x00Z\x00d\x01Z\x01" self.assertEqual(code.co_code, expected) self.assertEqual(code.co_firstlineno, 1) self.assertEqual(code.co_lnotab, b"\x02\x7f\x00\x01\x02\x01\x02\x80\x00\xff")
def test_repr(self): r = repr(ConcreteBytecode()) self.assertIn("ConcreteBytecode", r) self.assertIn("0", r)
def f(a, b): # res = a + b return def g(a, b): res = a + b if a < b else b + a r = 0 for a in range(res): r += 1 return r or 2 for x in (f, g): #get byte code for f dis(x) print(f.__code__.co_code) c = Bytecode.from_code(x.__code__) cc = ConcreteBytecode.from_code(x.__code__) dump_bytecode(c) dump_bytecode(cc) #generate byte code cnew = c.to_code() x.__code__ = cnew dis(x) print(x(3, 5))
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)
def test_expected_arg_with_many_consts(self): def test(): var = 0 var = 1 var = 2 var = 3 var = 4 var = 5 var = 6 var = 7 var = 8 var = 9 var = 10 var = 11 var = 12 var = 13 var = 14 var = 15 var = 16 var = 17 var = 18 var = 19 var = 20 var = 21 var = 22 var = 23 var = 24 var = 25 var = 26 var = 27 var = 28 var = 29 var = 30 var = 31 var = 32 var = 33 var = 34 var = 35 var = 36 var = 37 var = 38 var = 39 var = 40 var = 41 var = 42 var = 43 var = 44 var = 45 var = 46 var = 47 var = 48 var = 49 var = 50 var = 51 var = 52 var = 53 var = 54 var = 55 var = 56 var = 57 var = 58 var = 59 var = 60 var = 61 var = 62 var = 63 var = 64 var = 65 var = 66 var = 67 var = 68 var = 69 var = 70 var = 71 var = 72 var = 73 var = 74 var = 75 var = 76 var = 77 var = 78 var = 79 var = 80 var = 81 var = 82 var = 83 var = 84 var = 85 var = 86 var = 87 var = 88 var = 89 var = 90 var = 91 var = 92 var = 93 var = 94 var = 95 var = 96 var = 97 var = 98 var = 99 var = 100 var = 101 var = 102 var = 103 var = 104 var = 105 var = 106 var = 107 var = 108 var = 109 var = 110 var = 111 var = 112 var = 113 var = 114 var = 115 var = 116 var = 117 var = 118 var = 119 var = 120 var = 121 var = 122 var = 123 var = 124 var = 125 var = 126 var = 127 var = 128 var = 129 var = 130 var = 131 var = 132 var = 133 var = 134 var = 135 var = 136 var = 137 var = 138 var = 139 var = 140 var = 141 var = 142 var = 143 var = 144 var = 145 var = 146 var = 147 var = 148 var = 149 var = 150 var = 151 var = 152 var = 153 var = 154 var = 155 var = 156 var = 157 var = 158 var = 159 var = 160 var = 161 var = 162 var = 163 var = 164 var = 165 var = 166 var = 167 var = 168 var = 169 var = 170 var = 171 var = 172 var = 173 var = 174 var = 175 var = 176 var = 177 var = 178 var = 179 var = 180 var = 181 var = 182 var = 183 var = 184 var = 185 var = 186 var = 187 var = 188 var = 189 var = 190 var = 191 var = 192 var = 193 var = 194 var = 195 var = 196 var = 197 var = 198 var = 199 var = 200 var = 201 var = 202 var = 203 var = 204 var = 205 var = 206 var = 207 var = 208 var = 209 var = 210 var = 211 var = 212 var = 213 var = 214 var = 215 var = 216 var = 217 var = 218 var = 219 var = 220 var = 221 var = 222 var = 223 var = 224 var = 225 var = 226 var = 227 var = 228 var = 229 var = 230 var = 231 var = 232 var = 233 var = 234 var = 235 var = 236 var = 237 var = 238 var = 239 var = 240 var = 241 var = 242 var = 243 var = 244 var = 245 var = 246 var = 247 var = 248 var = 249 var = 250 var = 251 var = 252 var = 253 var = 254 var = 255 var = 256 var = 257 var = 258 var = 259 return var test.__code__ = ConcreteBytecode.from_code( test.__code__, extended_arg=True).to_code() self.assertEqual(test.__code__.co_stacksize, 1) self.assertEqual(test(), 259)
def test_async_gen_no_flag_is_async_None(self): # Test inference in the absence of any flag set on the bytecode # Infer generator code = ConcreteBytecode() code.append(ConcreteInstr("YIELD_VALUE")) code.update_flags() self.assertTrue(bool(code.flags & CompilerFlags.GENERATOR)) # Infer coroutine code = ConcreteBytecode() code.append(ConcreteInstr("GET_AWAITABLE")) code.update_flags() self.assertTrue(bool(code.flags & CompilerFlags.COROUTINE)) # Infer coroutine or async generator for i, expected in ( ("YIELD_VALUE", CompilerFlags.ASYNC_GENERATOR), ("YIELD_FROM", CompilerFlags.COROUTINE), ): code = ConcreteBytecode() code.append(ConcreteInstr("GET_AWAITABLE")) code.append(ConcreteInstr(i)) code.update_flags() self.assertTrue(bool(code.flags & expected))
def test_extended_arg_make_function(self): if (3, 9) <= sys.version_info < (3, 10): from _pydevd_frame_eval.vendored.bytecode.tests.util_annotation import get_code as get_code_future code_obj = get_code_future(""" def foo(x: int, y: int): pass """) else: code_obj = get_code(""" def foo(x: int, y: int): pass """) # without EXTENDED_ARG concrete = ConcreteBytecode.from_code(code_obj) if sys.version_info >= (3, 10): func_code = concrete.consts[2] names = ["int", "foo"] consts = ["x", "y", func_code, "foo", None] const_offset = 1 name_offset = 1 first_instrs = [ ConcreteInstr("LOAD_CONST", 0, lineno=1), ConcreteInstr("LOAD_NAME", 0, lineno=1), ConcreteInstr("LOAD_CONST", 1, lineno=1), ConcreteInstr("LOAD_NAME", 0, lineno=1), ConcreteInstr("BUILD_TUPLE", 4, lineno=1), ] elif (sys.version_info >= (3, 7) and concrete.flags & CompilerFlags.FUTURE_ANNOTATIONS): func_code = concrete.consts[2] names = ["foo"] consts = ["int", ("x", "y"), func_code, "foo", None] const_offset = 1 name_offset = 0 first_instrs = [ ConcreteInstr("LOAD_CONST", 0, lineno=1), ConcreteInstr("LOAD_CONST", 0, lineno=1), ConcreteInstr("LOAD_CONST", 0 + const_offset, lineno=1), ConcreteInstr("BUILD_CONST_KEY_MAP", 2, lineno=1), ] else: func_code = concrete.consts[1] names = ["int", "foo"] consts = [("x", "y"), func_code, "foo", None] const_offset = 0 name_offset = 1 first_instrs = [ ConcreteInstr("LOAD_NAME", 0, lineno=1), ConcreteInstr("LOAD_NAME", 0, lineno=1), ConcreteInstr("LOAD_CONST", 0 + const_offset, lineno=1), ConcreteInstr("BUILD_CONST_KEY_MAP", 2, lineno=1), ] self.assertEqual(concrete.names, names) self.assertEqual(concrete.consts, consts) expected = first_instrs + [ ConcreteInstr("LOAD_CONST", 1 + const_offset, lineno=1), ConcreteInstr("LOAD_CONST", 2 + const_offset, lineno=1), ConcreteInstr("MAKE_FUNCTION", 4, lineno=1), ConcreteInstr("STORE_NAME", name_offset, lineno=1), ConcreteInstr("LOAD_CONST", 3 + const_offset, lineno=1), ConcreteInstr("RETURN_VALUE", lineno=1), ] self.assertListEqual(list(concrete), expected) # with EXTENDED_ARG concrete = ConcreteBytecode.from_code(code_obj, extended_arg=True) # With future annotation the int annotation is stringified and # stored as constant this the default behavior under Python 3.10 if sys.version_info >= (3, 10): func_code = concrete.consts[2] names = ["int", "foo"] consts = ["x", "y", func_code, "foo", None] elif concrete.flags & CompilerFlags.FUTURE_ANNOTATIONS: func_code = concrete.consts[2] names = ["foo"] consts = ["int", ("x", "y"), func_code, "foo", None] else: func_code = concrete.consts[1] names = ["int", "foo"] consts = [("x", "y"), func_code, "foo", None] self.assertEqual(concrete.names, names) self.assertEqual(concrete.consts, consts) self.assertListEqual(list(concrete), expected)
def test_eq(self): code = ConcreteBytecode() self.assertFalse(code == 1) for name, val in ( ("names", ["a"]), ("varnames", ["a"]), ("consts", [1]), ("argcount", 1), ("kwonlyargcount", 2), ("flags", CompilerFlags(CompilerFlags.GENERATOR)), ("first_lineno", 10), ("filename", "xxxx.py"), ("name", "__x"), ("docstring", "x-x-x"), ("cellvars", [CellVar("x")]), ("freevars", [FreeVar("x")]), ): c = ConcreteBytecode() setattr(c, name, val) # For obscure reasons using assertNotEqual here fail self.assertFalse(code == c) if sys.version_info > (3, 8): c = ConcreteBytecode() c.posonlyargcount = 10 self.assertFalse(code == c) c = ConcreteBytecode() c.consts = [1] code.consts = [1] c.append(ConcreteInstr("LOAD_CONST", 0)) self.assertFalse(code == c)
def test_fail_extended_arg_jump(self): def test(): var = None for _ in range(0, 1): var = 0 var = 1 var = 2 var = 3 var = 4 var = 5 var = 6 var = 7 var = 8 var = 9 var = 10 var = 11 var = 12 var = 13 var = 14 var = 15 var = 16 var = 17 var = 18 var = 19 var = 20 var = 21 var = 22 var = 23 var = 24 var = 25 var = 26 var = 27 var = 28 var = 29 var = 30 var = 31 var = 32 var = 33 var = 34 var = 35 var = 36 var = 37 var = 38 var = 39 var = 40 var = 41 var = 42 var = 43 var = 44 var = 45 var = 46 var = 47 var = 48 var = 49 var = 50 var = 51 var = 52 var = 53 var = 54 var = 55 var = 56 var = 57 var = 58 var = 59 var = 60 var = 61 var = 62 var = 63 var = 64 var = 65 var = 66 var = 67 var = 68 var = 69 var = 70 return var # Generate the bytecode with extended arguments bytecode = ConcreteBytecode.from_code(test.__code__, extended_arg=True) bytecode.to_code()
def test_async_gen_flags(self): # Test inference in the presence of pre-existing flags for is_async in (None, True): # Infer generator code = ConcreteBytecode() code.append(ConcreteInstr("YIELD_VALUE")) for f, expected in ( (CompilerFlags.COROUTINE, CompilerFlags.ASYNC_GENERATOR), (CompilerFlags.ASYNC_GENERATOR, CompilerFlags.ASYNC_GENERATOR), (CompilerFlags.ITERABLE_COROUTINE, CompilerFlags.ITERABLE_COROUTINE), ): code.flags = CompilerFlags(f) code.update_flags(is_async=is_async) self.assertTrue(bool(code.flags & expected)) # Infer coroutine code = ConcreteBytecode() code.append(ConcreteInstr("YIELD_FROM")) for f, expected in ( (CompilerFlags.COROUTINE, CompilerFlags.COROUTINE), (CompilerFlags.ASYNC_GENERATOR, CompilerFlags.COROUTINE), (CompilerFlags.ITERABLE_COROUTINE, CompilerFlags.ITERABLE_COROUTINE), ): code.flags = CompilerFlags(f) code.update_flags(is_async=is_async) self.assertTrue(bool(code.flags & expected)) # Crash on ITERABLE_COROUTINE with async bytecode code = ConcreteBytecode() code.append(ConcreteInstr("GET_AWAITABLE")) code.flags = CompilerFlags(CompilerFlags.ITERABLE_COROUTINE) with self.assertRaises(ValueError): code.update_flags(is_async=is_async)