blob: 1e782cfd6da6fd0ddc52b82b84090c2b0c62bef0 [file] [log] [blame]
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +00001import dis
Antoine Pitroub7fbcd32010-01-16 18:37:38 +00002import re
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +00003import sys
Guido van Rossum34d19282007-08-09 01:03:29 +00004from io import StringIO
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +00005import unittest
Mark Dickinson7c9e8032011-03-23 17:59:37 +00006from math import copysign
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +00007
8def disassemble(func):
9 f = StringIO()
10 tmp = sys.stdout
11 sys.stdout = f
Antoine Pitrou17b880a2011-03-11 17:27:02 +010012 try:
13 dis.dis(func)
14 finally:
15 sys.stdout = tmp
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000016 result = f.getvalue()
17 f.close()
18 return result
19
20def dis_single(line):
21 return disassemble(compile(line, '', 'single'))
22
Raymond Hettingerf932f742011-03-15 15:06:09 -070023
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000024class TestTranforms(unittest.TestCase):
25
26 def test_unot(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000027 # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000028 def unot(x):
29 if not x == 2:
30 del x
31 asm = disassemble(unot)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000032 for elem in ('UNARY_NOT', 'POP_JUMP_IF_FALSE'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000033 self.assertNotIn(elem, asm)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000034 for elem in ('POP_JUMP_IF_TRUE',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000035 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000036
37 def test_elim_inversion_of_is_or_in(self):
38 for line, elem in (
39 ('not a is b', '(is not)',),
40 ('not a in b', '(not in)',),
41 ('not a is not b', '(is)',),
42 ('not a not in b', '(in)',),
43 ):
44 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +000045 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000046
Guido van Rossumcd16bf62007-06-13 18:07:49 +000047 def test_global_as_constant(self):
48 # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000049 def f(x):
Tim Peters66cb0182004-08-26 05:23:19 +000050 None
Guido van Rossumcd16bf62007-06-13 18:07:49 +000051 None
Tim Peters66cb0182004-08-26 05:23:19 +000052 return x
Guido van Rossumcd16bf62007-06-13 18:07:49 +000053 def g(x):
54 True
55 return x
56 def h(x):
57 False
58 return x
59 for func, name in ((f, 'None'), (g, 'True'), (h, 'False')):
60 asm = disassemble(func)
61 for elem in ('LOAD_GLOBAL',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000062 self.assertNotIn(elem, asm)
Guido van Rossumcd16bf62007-06-13 18:07:49 +000063 for elem in ('LOAD_CONST', '('+name+')'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000064 self.assertIn(elem, asm)
Guido van Rossumd8faa362007-04-27 19:54:29 +000065 def f():
66 'Adding a docstring made this test fail in Py2.5.0'
67 return None
Benjamin Peterson577473f2010-01-19 00:09:57 +000068 self.assertIn('LOAD_CONST', disassemble(f))
69 self.assertNotIn('LOAD_GLOBAL', disassemble(f))
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000070
71 def test_while_one(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000072 # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000073 def f():
Tim Peters66cb0182004-08-26 05:23:19 +000074 while 1:
75 pass
76 return list
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000077 asm = disassemble(f)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000078 for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000079 self.assertNotIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000080 for elem in ('JUMP_ABSOLUTE',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000081 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000082
83 def test_pack_unpack(self):
84 for line, elem in (
Raymond Hettinger2c31a052004-09-22 18:44:21 +000085 ('a, = a,', 'LOAD_CONST',),
86 ('a, b = a, b', 'ROT_TWO',),
87 ('a, b, c = a, b, c', 'ROT_THREE',),
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000088 ):
89 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +000090 self.assertIn(elem, asm)
91 self.assertNotIn('BUILD_TUPLE', asm)
92 self.assertNotIn('UNPACK_TUPLE', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000093
Raymond Hettinger2c31a052004-09-22 18:44:21 +000094 def test_folding_of_tuples_of_constants(self):
95 for line, elem in (
Raymond Hettinger5dec0962004-11-02 04:20:10 +000096 ('a = 1,2,3', '((1, 2, 3))'),
97 ('("a","b","c")', "(('a', 'b', 'c'))"),
98 ('a,b,c = 1,2,3', '((1, 2, 3))'),
99 ('(None, 1, None)', '((None, 1, None))'),
100 ('((1, 2), 3, 4)', '(((1, 2), 3, 4))'),
Raymond Hettinger2c31a052004-09-22 18:44:21 +0000101 ):
102 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000103 self.assertIn(elem, asm)
104 self.assertNotIn('BUILD_TUPLE', asm)
Raymond Hettinger2c31a052004-09-22 18:44:21 +0000105
Antoine Pitrou17b880a2011-03-11 17:27:02 +0100106 # Long tuples should be folded too.
107 asm = dis_single(repr(tuple(range(10000))))
108 # One LOAD_CONST for the tuple, one for the None return value
109 self.assertEqual(asm.count('LOAD_CONST'), 2)
110 self.assertNotIn('BUILD_TUPLE', asm)
111
Raymond Hettinger23109ef2004-10-26 08:59:14 +0000112 # Bug 1053819: Tuple of constants misidentified when presented with:
113 # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .
114 # The following would segfault upon compilation
115 def crater():
116 (~[
117 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
118 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
119 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
120 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
121 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
122 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
123 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
124 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
125 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
126 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
127 ],)
128
Antoine Pitroub7fbcd32010-01-16 18:37:38 +0000129 def test_folding_of_lists_of_constants(self):
130 for line, elem in (
131 # in/not in constants with BUILD_LIST should be folded to a tuple:
132 ('a in [1,2,3]', '(1, 2, 3)'),
133 ('a not in ["a","b","c"]', "(('a', 'b', 'c'))"),
134 ('a in [None, 1, None]', '((None, 1, None))'),
135 ('a not in [(1, 2), 3, 4]', '(((1, 2), 3, 4))'),
136 ):
137 asm = dis_single(line)
138 self.assertIn(elem, asm)
139 self.assertNotIn('BUILD_LIST', asm)
140
141 def test_folding_of_sets_of_constants(self):
142 for line, elem in (
143 # in/not in constants with BUILD_SET should be folded to a frozenset:
144 ('a in {1,2,3}', frozenset({1, 2, 3})),
145 ('a not in {"a","b","c"}', frozenset({'a', 'c', 'b'})),
146 ('a in {None, 1, None}', frozenset({1, None})),
147 ('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})),
149 ):
150 asm = dis_single(line)
151 self.assertNotIn('BUILD_SET', asm)
152
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
163 # Ensure that the resulting code actually works:
164 def f(a):
165 return a in {1, 2, 3}
166
167 def g(a):
168 return a not in {1, 2, 3}
169
170 self.assertTrue(f(3))
171 self.assertTrue(not f(4))
172
173 self.assertTrue(not g(3))
174 self.assertTrue(g(4))
175
176
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000177 def test_folding_of_binops_on_constants(self):
178 for line, elem in (
179 ('a = 2+3+4', '(9)'), # chained fold
180 ('"@"*4', "('@@@@')"), # check string ops
181 ('a="abc" + "def"', "('abcdef')"), # check string ops
182 ('a = 3**4', '(81)'), # binary power
183 ('a = 3*4', '(12)'), # binary multiply
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000184 ('a = 13//4', '(3)'), # binary floor divide
185 ('a = 14%4', '(2)'), # binary modulo
186 ('a = 2+3', '(5)'), # binary add
187 ('a = 13-4', '(9)'), # binary subtract
188 ('a = (12,13)[1]', '(13)'), # binary subscr
189 ('a = 13 << 2', '(52)'), # binary lshift
190 ('a = 13 >> 2', '(3)'), # binary rshift
191 ('a = 13 & 7', '(5)'), # binary and
192 ('a = 13 ^ 7', '(10)'), # binary xor
193 ('a = 13 | 7', '(15)'), # binary or
194 ):
195 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000196 self.assertIn(elem, asm, asm)
197 self.assertNotIn('BINARY_', asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000198
199 # Verify that unfoldables are skipped
200 asm = dis_single('a=2+"b"')
Benjamin Peterson577473f2010-01-19 00:09:57 +0000201 self.assertIn('(2)', asm)
202 self.assertIn("('b')", asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000203
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000204 # Verify that large sequences do not result from folding
205 asm = dis_single('a="x"*1000')
Benjamin Peterson577473f2010-01-19 00:09:57 +0000206 self.assertIn('(1000)', asm)
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000207
Ezio Melotti2df6a932011-04-15 16:38:34 +0300208 def test_binary_subscr_on_unicode(self):
209 # valid code get optimized
210 asm = dis_single('"foo"[0]')
211 self.assertIn("('f')", asm)
212 self.assertNotIn('BINARY_SUBSCR', asm)
213 asm = dis_single('"\u0061\uffff"[1]')
214 self.assertIn("('\\uffff')", asm)
215 self.assertNotIn('BINARY_SUBSCR', asm)
216
217 # invalid code doesn't get optimized
218 # out of range
219 asm = dis_single('"fuu"[10]')
220 self.assertIn('BINARY_SUBSCR', asm)
Ezio Melotti2df6a932011-04-15 16:38:34 +0300221
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000222 def test_folding_of_unaryops_on_constants(self):
223 for line, elem in (
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000224 ('-0.5', '(-0.5)'), # unary negative
Mark Dickinson7c9e8032011-03-23 17:59:37 +0000225 ('-0.0', '(-0.0)'), # -0.0
226 ('-(1.0-1.0)','(-0.0)'), # -0.0 after folding
227 ('-0', '(0)'), # -0
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000228 ('~-2', '(1)'), # unary invert
Raymond Hettingeraf7adad2009-10-22 11:22:50 +0000229 ('+1', '(1)'), # unary positive
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000230 ):
231 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000232 self.assertIn(elem, asm, asm)
233 self.assertNotIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000234
Mark Dickinson7c9e8032011-03-23 17:59:37 +0000235 # Check that -0.0 works after marshaling
236 def negzero():
237 return -(1.0-1.0)
238
239 self.assertNotIn('UNARY_', disassemble(negzero))
240 self.assertTrue(copysign(1.0, negzero()) < 0)
241
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000242 # Verify that unfoldables are skipped
243 for line, elem in (
244 ('-"abc"', "('abc')"), # unary negative
245 ('~"abc"', "('abc')"), # unary invert
246 ):
247 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000248 self.assertIn(elem, asm, asm)
249 self.assertIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000250
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000251 def test_elim_extra_return(self):
252 # RETURN LOAD_CONST None RETURN --> RETURN
253 def f(x):
254 return x
255 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000256 self.assertNotIn('LOAD_CONST', asm)
257 self.assertNotIn('(None)', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000258 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
259
Thomas Wouters89f507f2006-12-13 04:49:30 +0000260 def test_elim_jump_to_return(self):
261 # JUMP_FORWARD to RETURN --> RETURN
262 def f(cond, true_value, false_value):
263 return true_value if cond else false_value
264 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000265 self.assertNotIn('JUMP_FORWARD', asm)
266 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000267 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
268
269 def test_elim_jump_after_return1(self):
270 # Eliminate dead code: jumps immediately after returns can't be reached
271 def f(cond1, cond2):
272 if cond1: return 1
273 if cond2: return 2
274 while 1:
275 return 3
276 while 1:
277 if cond1: return 4
278 return 5
279 return 6
280 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000281 self.assertNotIn('JUMP_FORWARD', asm)
282 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000283 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
284
285 def test_elim_jump_after_return2(self):
286 # Eliminate dead code: jumps immediately after returns can't be reached
287 def f(cond1, cond2):
288 while 1:
289 if cond1: return 4
290 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000291 self.assertNotIn('JUMP_FORWARD', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000292 # There should be one jump for the while loop.
293 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
294 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000295
Guido van Rossum0240b922007-02-26 21:23:50 +0000296 def test_make_function_doesnt_bail(self):
297 def f():
Guido van Rossumd8faa362007-04-27 19:54:29 +0000298 def g()->1+1:
Guido van Rossum0240b922007-02-26 21:23:50 +0000299 pass
300 return g
301 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000302 self.assertNotIn('BINARY_ADD', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000303
Antoine Pitrou17b880a2011-03-11 17:27:02 +0100304 def test_constant_folding(self):
305 # Issue #11244: aggressive constant folding.
306 exprs = [
307 "3 * -5",
308 "-3 * 5",
309 "2 * (3 * 4)",
310 "(2 * 3) * 4",
311 "(-1, 2, 3)",
312 "(1, -2, 3)",
313 "(1, 2, -3)",
314 "(1, 2, -3) * 6",
315 "lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}",
316 ]
317 for e in exprs:
318 asm = dis_single(e)
319 self.assertNotIn('UNARY_', asm, e)
320 self.assertNotIn('BINARY_', asm, e)
321 self.assertNotIn('BUILD_', asm, e)
322
Raymond Hettinger0661e912011-03-15 15:03:36 -0700323class TestBuglets(unittest.TestCase):
324
Raymond Hettinger5bd75b82011-03-15 15:07:38 -0700325 def test_bug_11510(self):
326 # folded constant set optimization was commingled with the tuple
327 # unpacking optimization which would fail if the set had duplicate
328 # elements so that the set length was unexpected
329 def f():
330 x, y = {1, 1}
331 return x, y
332 with self.assertRaises(ValueError):
333 f()
Raymond Hettinger0661e912011-03-15 15:03:36 -0700334
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000335
336def test_main(verbose=None):
337 import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000338 from test import support
Raymond Hettinger0661e912011-03-15 15:03:36 -0700339 test_classes = (TestTranforms, TestBuglets)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000340 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000341
342 # verify reference counting
343 if verbose and hasattr(sys, "gettotalrefcount"):
344 import gc
345 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000346 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000347 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000348 gc.collect()
349 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000350 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000351
352if __name__ == "__main__":
353 test_main(verbose=True)