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