blob: 1cacdea5692cecebe2afd7cce0d722202f27e3c3 [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)
Ezio Melotti570942e2012-11-05 00:13:57 +0200216 asm = dis_single('"\U00012345abcdef"[3]')
217 self.assertIn("('c')", asm)
218 self.assertNotIn('BINARY_SUBSCR', asm)
Ezio Melotti2df6a932011-04-15 16:38:34 +0300219
220 # invalid code doesn't get optimized
221 # out of range
222 asm = dis_single('"fuu"[10]')
223 self.assertIn('BINARY_SUBSCR', asm)
Ezio Melotti2df6a932011-04-15 16:38:34 +0300224
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000225 def test_folding_of_unaryops_on_constants(self):
226 for line, elem in (
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000227 ('-0.5', '(-0.5)'), # unary negative
Mark Dickinson7c9e8032011-03-23 17:59:37 +0000228 ('-0.0', '(-0.0)'), # -0.0
229 ('-(1.0-1.0)','(-0.0)'), # -0.0 after folding
230 ('-0', '(0)'), # -0
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000231 ('~-2', '(1)'), # unary invert
Raymond Hettingeraf7adad2009-10-22 11:22:50 +0000232 ('+1', '(1)'), # unary positive
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000233 ):
234 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000235 self.assertIn(elem, asm, asm)
236 self.assertNotIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000237
Mark Dickinson7c9e8032011-03-23 17:59:37 +0000238 # Check that -0.0 works after marshaling
239 def negzero():
240 return -(1.0-1.0)
241
242 self.assertNotIn('UNARY_', disassemble(negzero))
243 self.assertTrue(copysign(1.0, negzero()) < 0)
244
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000245 # Verify that unfoldables are skipped
246 for line, elem in (
247 ('-"abc"', "('abc')"), # unary negative
248 ('~"abc"', "('abc')"), # unary invert
249 ):
250 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000251 self.assertIn(elem, asm, asm)
252 self.assertIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000253
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000254 def test_elim_extra_return(self):
255 # RETURN LOAD_CONST None RETURN --> RETURN
256 def f(x):
257 return x
258 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000259 self.assertNotIn('LOAD_CONST', asm)
260 self.assertNotIn('(None)', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000261 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
262
Thomas Wouters89f507f2006-12-13 04:49:30 +0000263 def test_elim_jump_to_return(self):
264 # JUMP_FORWARD to RETURN --> RETURN
265 def f(cond, true_value, false_value):
266 return true_value if cond else false_value
267 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000268 self.assertNotIn('JUMP_FORWARD', asm)
269 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000270 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
271
272 def test_elim_jump_after_return1(self):
273 # Eliminate dead code: jumps immediately after returns can't be reached
274 def f(cond1, cond2):
275 if cond1: return 1
276 if cond2: return 2
277 while 1:
278 return 3
279 while 1:
280 if cond1: return 4
281 return 5
282 return 6
283 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000284 self.assertNotIn('JUMP_FORWARD', asm)
285 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000286 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
287
288 def test_elim_jump_after_return2(self):
289 # Eliminate dead code: jumps immediately after returns can't be reached
290 def f(cond1, cond2):
291 while 1:
292 if cond1: return 4
293 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000294 self.assertNotIn('JUMP_FORWARD', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000295 # There should be one jump for the while loop.
296 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
297 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000298
Guido van Rossum0240b922007-02-26 21:23:50 +0000299 def test_make_function_doesnt_bail(self):
300 def f():
Guido van Rossumd8faa362007-04-27 19:54:29 +0000301 def g()->1+1:
Guido van Rossum0240b922007-02-26 21:23:50 +0000302 pass
303 return g
304 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000305 self.assertNotIn('BINARY_ADD', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000306
Antoine Pitrou17b880a2011-03-11 17:27:02 +0100307 def test_constant_folding(self):
308 # Issue #11244: aggressive constant folding.
309 exprs = [
310 "3 * -5",
311 "-3 * 5",
312 "2 * (3 * 4)",
313 "(2 * 3) * 4",
314 "(-1, 2, 3)",
315 "(1, -2, 3)",
316 "(1, 2, -3)",
317 "(1, 2, -3) * 6",
318 "lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}",
319 ]
320 for e in exprs:
321 asm = dis_single(e)
322 self.assertNotIn('UNARY_', asm, e)
323 self.assertNotIn('BINARY_', asm, e)
324 self.assertNotIn('BUILD_', asm, e)
325
Raymond Hettinger0661e912011-03-15 15:03:36 -0700326class TestBuglets(unittest.TestCase):
327
Raymond Hettinger5bd75b82011-03-15 15:07:38 -0700328 def test_bug_11510(self):
329 # folded constant set optimization was commingled with the tuple
330 # unpacking optimization which would fail if the set had duplicate
331 # elements so that the set length was unexpected
332 def f():
333 x, y = {1, 1}
334 return x, y
335 with self.assertRaises(ValueError):
336 f()
Raymond Hettinger0661e912011-03-15 15:03:36 -0700337
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000338
339def test_main(verbose=None):
340 import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000341 from test import support
Raymond Hettinger0661e912011-03-15 15:03:36 -0700342 test_classes = (TestTranforms, TestBuglets)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000343 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000344
345 # verify reference counting
346 if verbose and hasattr(sys, "gettotalrefcount"):
347 import gc
348 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000349 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000350 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000351 gc.collect()
352 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000353 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000354
355if __name__ == "__main__":
356 test_main(verbose=True)