1import dis 2import unittest 3 4from test.bytecode_helper import BytecodeTestCase 5 6class TestTranforms(BytecodeTestCase): 7 8 def test_unot(self): 9 # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE' 10 def unot(x): 11 if not x == 2: 12 del x 13 self.assertNotInBytecode(unot, 'UNARY_NOT') 14 self.assertNotInBytecode(unot, 'POP_JUMP_IF_FALSE') 15 self.assertInBytecode(unot, 'POP_JUMP_IF_TRUE') 16 17 def test_elim_inversion_of_is_or_in(self): 18 for line, cmp_op in ( 19 ('not a is b', 'is not',), 20 ('not a in b', 'not in',), 21 ('not a is not b', 'is',), 22 ('not a not in b', 'in',), 23 ): 24 code = compile(line, '', 'single') 25 self.assertInBytecode(code, 'COMPARE_OP', cmp_op) 26 27 def test_global_as_constant(self): 28 # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False 29 def f(): 30 x = None 31 x = None 32 return x 33 def g(): 34 x = True 35 return x 36 def h(): 37 x = False 38 return x 39 40 for func, elem in ((f, None), (g, True), (h, False)): 41 self.assertNotInBytecode(func, 'LOAD_GLOBAL') 42 self.assertInBytecode(func, 'LOAD_CONST', elem) 43 44 def f(): 45 'Adding a docstring made this test fail in Py2.5.0' 46 return None 47 48 self.assertNotInBytecode(f, 'LOAD_GLOBAL') 49 self.assertInBytecode(f, 'LOAD_CONST', None) 50 51 def test_while_one(self): 52 # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx 53 def f(): 54 while 1: 55 pass 56 return list 57 for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'): 58 self.assertNotInBytecode(f, elem) 59 for elem in ('JUMP_ABSOLUTE',): 60 self.assertInBytecode(f, elem) 61 62 def test_pack_unpack(self): 63 for line, elem in ( 64 ('a, = a,', 'LOAD_CONST',), 65 ('a, b = a, b', 'ROT_TWO',), 66 ('a, b, c = a, b, c', 'ROT_THREE',), 67 ): 68 code = compile(line,'','single') 69 self.assertInBytecode(code, elem) 70 self.assertNotInBytecode(code, 'BUILD_TUPLE') 71 self.assertNotInBytecode(code, 'UNPACK_TUPLE') 72 73 def test_folding_of_tuples_of_constants(self): 74 for line, elem in ( 75 ('a = 1,2,3', (1, 2, 3)), 76 ('("a","b","c")', ('a', 'b', 'c')), 77 ('a,b,c = 1,2,3', (1, 2, 3)), 78 ('(None, 1, None)', (None, 1, None)), 79 ('((1, 2), 3, 4)', ((1, 2), 3, 4)), 80 ): 81 code = compile(line,'','single') 82 self.assertInBytecode(code, 'LOAD_CONST', elem) 83 self.assertNotInBytecode(code, 'BUILD_TUPLE') 84 85 # Long tuples should be folded too. 86 code = compile(repr(tuple(range(10000))),'','single') 87 self.assertNotInBytecode(code, 'BUILD_TUPLE') 88 # One LOAD_CONST for the tuple, one for the None return value 89 load_consts = [instr for instr in dis.get_instructions(code) 90 if instr.opname == 'LOAD_CONST'] 91 self.assertEqual(len(load_consts), 2) 92 93 # Bug 1053819: Tuple of constants misidentified when presented with: 94 # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . . 95 # The following would segfault upon compilation 96 def crater(): 97 (~[ 98 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 99 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 101 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 102 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 103 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 104 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 105 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 106 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 107 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 108 ],) 109 110 def test_folding_of_lists_of_constants(self): 111 for line, elem in ( 112 # in/not in constants with BUILD_LIST should be folded to a tuple: 113 ('a in [1,2,3]', (1, 2, 3)), 114 ('a not in ["a","b","c"]', ('a', 'b', 'c')), 115 ('a in [None, 1, None]', (None, 1, None)), 116 ('a not in [(1, 2), 3, 4]', ((1, 2), 3, 4)), 117 ): 118 code = compile(line, '', 'single') 119 self.assertInBytecode(code, 'LOAD_CONST', elem) 120 self.assertNotInBytecode(code, 'BUILD_LIST') 121 122 def test_folding_of_sets_of_constants(self): 123 for line, elem in ( 124 # in/not in constants with BUILD_SET should be folded to a frozenset: 125 ('a in {1,2,3}', frozenset({1, 2, 3})), 126 ('a not in {"a","b","c"}', frozenset({'a', 'c', 'b'})), 127 ('a in {None, 1, None}', frozenset({1, None})), 128 ('a not in {(1, 2), 3, 4}', frozenset({(1, 2), 3, 4})), 129 ('a in {1, 2, 3, 3, 2, 1}', frozenset({1, 2, 3})), 130 ): 131 code = compile(line, '', 'single') 132 self.assertNotInBytecode(code, 'BUILD_SET') 133 self.assertInBytecode(code, 'LOAD_CONST', elem) 134 135 # Ensure that the resulting code actually works: 136 def f(a): 137 return a in {1, 2, 3} 138 139 def g(a): 140 return a not in {1, 2, 3} 141 142 self.assertTrue(f(3)) 143 self.assertTrue(not f(4)) 144 145 self.assertTrue(not g(3)) 146 self.assertTrue(g(4)) 147 148 149 def test_folding_of_binops_on_constants(self): 150 for line, elem in ( 151 ('a = 2+3+4', 9), # chained fold 152 ('"@"*4', '@@@@'), # check string ops 153 ('a="abc" + "def"', 'abcdef'), # check string ops 154 ('a = 3**4', 81), # binary power 155 ('a = 3*4', 12), # binary multiply 156 ('a = 13//4', 3), # binary floor divide 157 ('a = 14%4', 2), # binary modulo 158 ('a = 2+3', 5), # binary add 159 ('a = 13-4', 9), # binary subtract 160 ('a = (12,13)[1]', 13), # binary subscr 161 ('a = 13 << 2', 52), # binary lshift 162 ('a = 13 >> 2', 3), # binary rshift 163 ('a = 13 & 7', 5), # binary and 164 ('a = 13 ^ 7', 10), # binary xor 165 ('a = 13 | 7', 15), # binary or 166 ): 167 code = compile(line, '', 'single') 168 self.assertInBytecode(code, 'LOAD_CONST', elem) 169 for instr in dis.get_instructions(code): 170 self.assertFalse(instr.opname.startswith('BINARY_')) 171 172 # Verify that unfoldables are skipped 173 code = compile('a=2+"b"', '', 'single') 174 self.assertInBytecode(code, 'LOAD_CONST', 2) 175 self.assertInBytecode(code, 'LOAD_CONST', 'b') 176 177 # Verify that large sequences do not result from folding 178 code = compile('a="x"*10000', '', 'single') 179 self.assertInBytecode(code, 'LOAD_CONST', 10000) 180 self.assertNotIn("x"*10000, code.co_consts) 181 code = compile('a=1<<1000', '', 'single') 182 self.assertInBytecode(code, 'LOAD_CONST', 1000) 183 self.assertNotIn(1<<1000, code.co_consts) 184 code = compile('a=2**1000', '', 'single') 185 self.assertInBytecode(code, 'LOAD_CONST', 1000) 186 self.assertNotIn(2**1000, code.co_consts) 187 188 def test_binary_subscr_on_unicode(self): 189 # valid code get optimized 190 code = compile('"foo"[0]', '', 'single') 191 self.assertInBytecode(code, 'LOAD_CONST', 'f') 192 self.assertNotInBytecode(code, 'BINARY_SUBSCR') 193 code = compile('"\u0061\uffff"[1]', '', 'single') 194 self.assertInBytecode(code, 'LOAD_CONST', '\uffff') 195 self.assertNotInBytecode(code,'BINARY_SUBSCR') 196 197 # With PEP 393, non-BMP char get optimized 198 code = compile('"\U00012345"[0]', '', 'single') 199 self.assertInBytecode(code, 'LOAD_CONST', '\U00012345') 200 self.assertNotInBytecode(code, 'BINARY_SUBSCR') 201 202 # invalid code doesn't get optimized 203 # out of range 204 code = compile('"fuu"[10]', '', 'single') 205 self.assertInBytecode(code, 'BINARY_SUBSCR') 206 207 def test_folding_of_unaryops_on_constants(self): 208 for line, elem in ( 209 ('-0.5', -0.5), # unary negative 210 ('-0.0', -0.0), # -0.0 211 ('-(1.0-1.0)', -0.0), # -0.0 after folding 212 ('-0', 0), # -0 213 ('~-2', 1), # unary invert 214 ('+1', 1), # unary positive 215 ): 216 code = compile(line, '', 'single') 217 self.assertInBytecode(code, 'LOAD_CONST', elem) 218 for instr in dis.get_instructions(code): 219 self.assertFalse(instr.opname.startswith('UNARY_')) 220 221 # Check that -0.0 works after marshaling 222 def negzero(): 223 return -(1.0-1.0) 224 225 for instr in dis.get_instructions(code): 226 self.assertFalse(instr.opname.startswith('UNARY_')) 227 228 # Verify that unfoldables are skipped 229 for line, elem, opname in ( 230 ('-"abc"', 'abc', 'UNARY_NEGATIVE'), 231 ('~"abc"', 'abc', 'UNARY_INVERT'), 232 ): 233 code = compile(line, '', 'single') 234 self.assertInBytecode(code, 'LOAD_CONST', elem) 235 self.assertInBytecode(code, opname) 236 237 def test_elim_extra_return(self): 238 # RETURN LOAD_CONST None RETURN --> RETURN 239 def f(x): 240 return x 241 self.assertNotInBytecode(f, 'LOAD_CONST', None) 242 returns = [instr for instr in dis.get_instructions(f) 243 if instr.opname == 'RETURN_VALUE'] 244 self.assertEqual(len(returns), 1) 245 246 def test_elim_jump_to_return(self): 247 # JUMP_FORWARD to RETURN --> RETURN 248 def f(cond, true_value, false_value): 249 return true_value if cond else false_value 250 self.assertNotInBytecode(f, 'JUMP_FORWARD') 251 self.assertNotInBytecode(f, 'JUMP_ABSOLUTE') 252 returns = [instr for instr in dis.get_instructions(f) 253 if instr.opname == 'RETURN_VALUE'] 254 self.assertEqual(len(returns), 2) 255 256 def test_elim_jump_after_return1(self): 257 # Eliminate dead code: jumps immediately after returns can't be reached 258 def f(cond1, cond2): 259 if cond1: return 1 260 if cond2: return 2 261 while 1: 262 return 3 263 while 1: 264 if cond1: return 4 265 return 5 266 return 6 267 self.assertNotInBytecode(f, 'JUMP_FORWARD') 268 self.assertNotInBytecode(f, 'JUMP_ABSOLUTE') 269 returns = [instr for instr in dis.get_instructions(f) 270 if instr.opname == 'RETURN_VALUE'] 271 self.assertEqual(len(returns), 6) 272 273 def test_elim_jump_after_return2(self): 274 # Eliminate dead code: jumps immediately after returns can't be reached 275 def f(cond1, cond2): 276 while 1: 277 if cond1: return 4 278 self.assertNotInBytecode(f, 'JUMP_FORWARD') 279 # There should be one jump for the while loop. 280 returns = [instr for instr in dis.get_instructions(f) 281 if instr.opname == 'JUMP_ABSOLUTE'] 282 self.assertEqual(len(returns), 1) 283 returns = [instr for instr in dis.get_instructions(f) 284 if instr.opname == 'RETURN_VALUE'] 285 self.assertEqual(len(returns), 2) 286 287 def test_make_function_doesnt_bail(self): 288 def f(): 289 def g()->1+1: 290 pass 291 return g 292 self.assertNotInBytecode(f, 'BINARY_ADD') 293 294 def test_constant_folding(self): 295 # Issue #11244: aggressive constant folding. 296 exprs = [ 297 '3 * -5', 298 '-3 * 5', 299 '2 * (3 * 4)', 300 '(2 * 3) * 4', 301 '(-1, 2, 3)', 302 '(1, -2, 3)', 303 '(1, 2, -3)', 304 '(1, 2, -3) * 6', 305 'lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}', 306 ] 307 for e in exprs: 308 code = compile(e, '', 'single') 309 for instr in dis.get_instructions(code): 310 self.assertFalse(instr.opname.startswith('UNARY_')) 311 self.assertFalse(instr.opname.startswith('BINARY_')) 312 self.assertFalse(instr.opname.startswith('BUILD_')) 313 314 315class TestBuglets(unittest.TestCase): 316 317 def test_bug_11510(self): 318 # folded constant set optimization was commingled with the tuple 319 # unpacking optimization which would fail if the set had duplicate 320 # elements so that the set length was unexpected 321 def f(): 322 x, y = {1, 1} 323 return x, y 324 with self.assertRaises(ValueError): 325 f() 326 327 328if __name__ == "__main__": 329 unittest.main() 330