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