blob: 78de90923eccd17338345d3372cf12ff50bcfb51 [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
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000208 def test_folding_of_unaryops_on_constants(self):
209 for line, elem in (
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000210 ('-0.5', '(-0.5)'), # unary negative
Mark Dickinson7c9e8032011-03-23 17:59:37 +0000211 ('-0.0', '(-0.0)'), # -0.0
212 ('-(1.0-1.0)','(-0.0)'), # -0.0 after folding
213 ('-0', '(0)'), # -0
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000214 ('~-2', '(1)'), # unary invert
Raymond Hettingeraf7adad2009-10-22 11:22:50 +0000215 ('+1', '(1)'), # unary positive
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000216 ):
217 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000218 self.assertIn(elem, asm, asm)
219 self.assertNotIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000220
Mark Dickinson7c9e8032011-03-23 17:59:37 +0000221 # Check that -0.0 works after marshaling
222 def negzero():
223 return -(1.0-1.0)
224
225 self.assertNotIn('UNARY_', disassemble(negzero))
226 self.assertTrue(copysign(1.0, negzero()) < 0)
227
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000228 # Verify that unfoldables are skipped
229 for line, elem in (
230 ('-"abc"', "('abc')"), # unary negative
231 ('~"abc"', "('abc')"), # unary invert
232 ):
233 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000234 self.assertIn(elem, asm, asm)
235 self.assertIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000236
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000237 def test_elim_extra_return(self):
238 # RETURN LOAD_CONST None RETURN --> RETURN
239 def f(x):
240 return x
241 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000242 self.assertNotIn('LOAD_CONST', asm)
243 self.assertNotIn('(None)', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000244 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
245
Thomas Wouters89f507f2006-12-13 04:49:30 +0000246 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 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000251 self.assertNotIn('JUMP_FORWARD', asm)
252 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000253 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
254
255 def test_elim_jump_after_return1(self):
256 # Eliminate dead code: jumps immediately after returns can't be reached
257 def f(cond1, cond2):
258 if cond1: return 1
259 if cond2: return 2
260 while 1:
261 return 3
262 while 1:
263 if cond1: return 4
264 return 5
265 return 6
266 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000267 self.assertNotIn('JUMP_FORWARD', asm)
268 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000269 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
270
271 def test_elim_jump_after_return2(self):
272 # Eliminate dead code: jumps immediately after returns can't be reached
273 def f(cond1, cond2):
274 while 1:
275 if cond1: return 4
276 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000277 self.assertNotIn('JUMP_FORWARD', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000278 # There should be one jump for the while loop.
279 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
280 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000281
Guido van Rossum0240b922007-02-26 21:23:50 +0000282 def test_make_function_doesnt_bail(self):
283 def f():
Guido van Rossumd8faa362007-04-27 19:54:29 +0000284 def g()->1+1:
Guido van Rossum0240b922007-02-26 21:23:50 +0000285 pass
286 return g
287 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000288 self.assertNotIn('BINARY_ADD', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000289
Antoine Pitrou17b880a2011-03-11 17:27:02 +0100290 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 asm = dis_single(e)
305 self.assertNotIn('UNARY_', asm, e)
306 self.assertNotIn('BINARY_', asm, e)
307 self.assertNotIn('BUILD_', asm, e)
308
Raymond Hettinger0661e912011-03-15 15:03:36 -0700309class TestBuglets(unittest.TestCase):
310
Raymond Hettinger5bd75b82011-03-15 15:07:38 -0700311 def test_bug_11510(self):
312 # folded constant set optimization was commingled with the tuple
313 # unpacking optimization which would fail if the set had duplicate
314 # elements so that the set length was unexpected
315 def f():
316 x, y = {1, 1}
317 return x, y
318 with self.assertRaises(ValueError):
319 f()
Raymond Hettinger0661e912011-03-15 15:03:36 -0700320
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000321
322def test_main(verbose=None):
323 import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000324 from test import support
Raymond Hettinger0661e912011-03-15 15:03:36 -0700325 test_classes = (TestTranforms, TestBuglets)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000326 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000327
328 # verify reference counting
329 if verbose and hasattr(sys, "gettotalrefcount"):
330 import gc
331 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000332 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000333 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000334 gc.collect()
335 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000336 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000337
338if __name__ == "__main__":
339 test_main(verbose=True)