blob: bab4c1e5d9dfffa15b785a22d9472b2d68e5c1ba [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
6
7def disassemble(func):
8 f = StringIO()
9 tmp = sys.stdout
10 sys.stdout = f
Antoine Pitrou17b880a2011-03-11 17:27:02 +010011 try:
12 dis.dis(func)
13 finally:
14 sys.stdout = tmp
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000015 result = f.getvalue()
16 f.close()
17 return result
18
19def dis_single(line):
20 return disassemble(compile(line, '', 'single'))
21
Raymond Hettingerf932f742011-03-15 15:06:09 -070022
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000023class TestTranforms(unittest.TestCase):
24
25 def test_unot(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000026 # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000027 def unot(x):
28 if not x == 2:
29 del x
30 asm = disassemble(unot)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000031 for elem in ('UNARY_NOT', 'POP_JUMP_IF_FALSE'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000032 self.assertNotIn(elem, asm)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000033 for elem in ('POP_JUMP_IF_TRUE',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000034 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000035
36 def test_elim_inversion_of_is_or_in(self):
37 for line, elem in (
38 ('not a is b', '(is not)',),
39 ('not a in b', '(not in)',),
40 ('not a is not b', '(is)',),
41 ('not a not in b', '(in)',),
42 ):
43 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +000044 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000045
Guido van Rossumcd16bf62007-06-13 18:07:49 +000046 def test_global_as_constant(self):
47 # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000048 def f(x):
Tim Peters66cb0182004-08-26 05:23:19 +000049 None
Guido van Rossumcd16bf62007-06-13 18:07:49 +000050 None
Tim Peters66cb0182004-08-26 05:23:19 +000051 return x
Guido van Rossumcd16bf62007-06-13 18:07:49 +000052 def g(x):
53 True
54 return x
55 def h(x):
56 False
57 return x
58 for func, name in ((f, 'None'), (g, 'True'), (h, 'False')):
59 asm = disassemble(func)
60 for elem in ('LOAD_GLOBAL',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000061 self.assertNotIn(elem, asm)
Guido van Rossumcd16bf62007-06-13 18:07:49 +000062 for elem in ('LOAD_CONST', '('+name+')'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000063 self.assertIn(elem, asm)
Guido van Rossumd8faa362007-04-27 19:54:29 +000064 def f():
65 'Adding a docstring made this test fail in Py2.5.0'
66 return None
Benjamin Peterson577473f2010-01-19 00:09:57 +000067 self.assertIn('LOAD_CONST', disassemble(f))
68 self.assertNotIn('LOAD_GLOBAL', disassemble(f))
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000069
70 def test_while_one(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000071 # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000072 def f():
Tim Peters66cb0182004-08-26 05:23:19 +000073 while 1:
74 pass
75 return list
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000076 asm = disassemble(f)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000077 for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000078 self.assertNotIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000079 for elem in ('JUMP_ABSOLUTE',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000080 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000081
82 def test_pack_unpack(self):
83 for line, elem in (
Raymond Hettinger2c31a052004-09-22 18:44:21 +000084 ('a, = a,', 'LOAD_CONST',),
85 ('a, b = a, b', 'ROT_TWO',),
86 ('a, b, c = a, b, c', 'ROT_THREE',),
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000087 ):
88 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +000089 self.assertIn(elem, asm)
90 self.assertNotIn('BUILD_TUPLE', asm)
91 self.assertNotIn('UNPACK_TUPLE', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000092
Raymond Hettinger2c31a052004-09-22 18:44:21 +000093 def test_folding_of_tuples_of_constants(self):
94 for line, elem in (
Raymond Hettinger5dec0962004-11-02 04:20:10 +000095 ('a = 1,2,3', '((1, 2, 3))'),
96 ('("a","b","c")', "(('a', 'b', 'c'))"),
97 ('a,b,c = 1,2,3', '((1, 2, 3))'),
98 ('(None, 1, None)', '((None, 1, None))'),
99 ('((1, 2), 3, 4)', '(((1, 2), 3, 4))'),
Raymond Hettinger2c31a052004-09-22 18:44:21 +0000100 ):
101 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000102 self.assertIn(elem, asm)
103 self.assertNotIn('BUILD_TUPLE', asm)
Raymond Hettinger2c31a052004-09-22 18:44:21 +0000104
Antoine Pitrou17b880a2011-03-11 17:27:02 +0100105 # Long tuples should be folded too.
106 asm = dis_single(repr(tuple(range(10000))))
107 # One LOAD_CONST for the tuple, one for the None return value
108 self.assertEqual(asm.count('LOAD_CONST'), 2)
109 self.assertNotIn('BUILD_TUPLE', asm)
110
Raymond Hettinger23109ef2004-10-26 08:59:14 +0000111 # Bug 1053819: Tuple of constants misidentified when presented with:
112 # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .
113 # The following would segfault upon compilation
114 def crater():
115 (~[
116 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
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 ],)
127
Antoine Pitroub7fbcd32010-01-16 18:37:38 +0000128 def test_folding_of_lists_of_constants(self):
129 for line, elem in (
130 # in/not in constants with BUILD_LIST should be folded to a tuple:
131 ('a in [1,2,3]', '(1, 2, 3)'),
132 ('a not in ["a","b","c"]', "(('a', 'b', 'c'))"),
133 ('a in [None, 1, None]', '((None, 1, None))'),
134 ('a not in [(1, 2), 3, 4]', '(((1, 2), 3, 4))'),
135 ):
136 asm = dis_single(line)
137 self.assertIn(elem, asm)
138 self.assertNotIn('BUILD_LIST', asm)
139
140 def test_folding_of_sets_of_constants(self):
141 for line, elem in (
142 # in/not in constants with BUILD_SET should be folded to a frozenset:
143 ('a in {1,2,3}', frozenset({1, 2, 3})),
144 ('a not in {"a","b","c"}', frozenset({'a', 'c', 'b'})),
145 ('a in {None, 1, None}', frozenset({1, None})),
146 ('a not in {(1, 2), 3, 4}', frozenset({(1, 2), 3, 4})),
147 ('a in {1, 2, 3, 3, 2, 1}', frozenset({1, 2, 3})),
148 ):
149 asm = dis_single(line)
150 self.assertNotIn('BUILD_SET', asm)
151
152 # Verify that the frozenset 'elem' is in the disassembly
153 # The ordering of the elements in repr( frozenset ) isn't
154 # guaranteed, so we jump through some hoops to ensure that we have
155 # the frozenset we expect:
156 self.assertIn('frozenset', asm)
157 # Extract the frozenset literal from the disassembly:
158 m = re.match(r'.*(frozenset\({.*}\)).*', asm, re.DOTALL)
159 self.assertTrue(m)
160 self.assertEqual(eval(m.group(1)), elem)
161
162 # Ensure that the resulting code actually works:
163 def f(a):
164 return a in {1, 2, 3}
165
166 def g(a):
167 return a not in {1, 2, 3}
168
169 self.assertTrue(f(3))
170 self.assertTrue(not f(4))
171
172 self.assertTrue(not g(3))
173 self.assertTrue(g(4))
174
175
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000176 def test_folding_of_binops_on_constants(self):
177 for line, elem in (
178 ('a = 2+3+4', '(9)'), # chained fold
179 ('"@"*4', "('@@@@')"), # check string ops
180 ('a="abc" + "def"', "('abcdef')"), # check string ops
181 ('a = 3**4', '(81)'), # binary power
182 ('a = 3*4', '(12)'), # binary multiply
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000183 ('a = 13//4', '(3)'), # binary floor divide
184 ('a = 14%4', '(2)'), # binary modulo
185 ('a = 2+3', '(5)'), # binary add
186 ('a = 13-4', '(9)'), # binary subtract
187 ('a = (12,13)[1]', '(13)'), # binary subscr
188 ('a = 13 << 2', '(52)'), # binary lshift
189 ('a = 13 >> 2', '(3)'), # binary rshift
190 ('a = 13 & 7', '(5)'), # binary and
191 ('a = 13 ^ 7', '(10)'), # binary xor
192 ('a = 13 | 7', '(15)'), # binary or
193 ):
194 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000195 self.assertIn(elem, asm, asm)
196 self.assertNotIn('BINARY_', asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000197
198 # Verify that unfoldables are skipped
199 asm = dis_single('a=2+"b"')
Benjamin Peterson577473f2010-01-19 00:09:57 +0000200 self.assertIn('(2)', asm)
201 self.assertIn("('b')", asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000202
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000203 # Verify that large sequences do not result from folding
204 asm = dis_single('a="x"*1000')
Benjamin Peterson577473f2010-01-19 00:09:57 +0000205 self.assertIn('(1000)', asm)
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000206
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000207 def test_folding_of_unaryops_on_constants(self):
208 for line, elem in (
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000209 ('-0.5', '(-0.5)'), # unary negative
210 ('~-2', '(1)'), # unary invert
Raymond Hettingeraf7adad2009-10-22 11:22:50 +0000211 ('+1', '(1)'), # unary positive
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000212 ):
213 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000214 self.assertIn(elem, asm, asm)
215 self.assertNotIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000216
217 # Verify that unfoldables are skipped
218 for line, elem in (
219 ('-"abc"', "('abc')"), # unary negative
220 ('~"abc"', "('abc')"), # unary invert
221 ):
222 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000223 self.assertIn(elem, asm, asm)
224 self.assertIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000225
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000226 def test_elim_extra_return(self):
227 # RETURN LOAD_CONST None RETURN --> RETURN
228 def f(x):
229 return x
230 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000231 self.assertNotIn('LOAD_CONST', asm)
232 self.assertNotIn('(None)', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000233 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
234
Thomas Wouters89f507f2006-12-13 04:49:30 +0000235 def test_elim_jump_to_return(self):
236 # JUMP_FORWARD to RETURN --> RETURN
237 def f(cond, true_value, false_value):
238 return true_value if cond else false_value
239 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000240 self.assertNotIn('JUMP_FORWARD', asm)
241 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000242 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
243
244 def test_elim_jump_after_return1(self):
245 # Eliminate dead code: jumps immediately after returns can't be reached
246 def f(cond1, cond2):
247 if cond1: return 1
248 if cond2: return 2
249 while 1:
250 return 3
251 while 1:
252 if cond1: return 4
253 return 5
254 return 6
255 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000256 self.assertNotIn('JUMP_FORWARD', asm)
257 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000258 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
259
260 def test_elim_jump_after_return2(self):
261 # Eliminate dead code: jumps immediately after returns can't be reached
262 def f(cond1, cond2):
263 while 1:
264 if cond1: return 4
265 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000266 self.assertNotIn('JUMP_FORWARD', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000267 # There should be one jump for the while loop.
268 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
269 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000270
Guido van Rossum0240b922007-02-26 21:23:50 +0000271 def test_make_function_doesnt_bail(self):
272 def f():
Guido van Rossumd8faa362007-04-27 19:54:29 +0000273 def g()->1+1:
Guido van Rossum0240b922007-02-26 21:23:50 +0000274 pass
275 return g
276 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000277 self.assertNotIn('BINARY_ADD', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000278
Antoine Pitrou17b880a2011-03-11 17:27:02 +0100279 def test_constant_folding(self):
280 # Issue #11244: aggressive constant folding.
281 exprs = [
282 "3 * -5",
283 "-3 * 5",
284 "2 * (3 * 4)",
285 "(2 * 3) * 4",
286 "(-1, 2, 3)",
287 "(1, -2, 3)",
288 "(1, 2, -3)",
289 "(1, 2, -3) * 6",
290 "lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}",
291 ]
292 for e in exprs:
293 asm = dis_single(e)
294 self.assertNotIn('UNARY_', asm, e)
295 self.assertNotIn('BINARY_', asm, e)
296 self.assertNotIn('BUILD_', asm, e)
297
Raymond Hettinger0661e912011-03-15 15:03:36 -0700298class TestBuglets(unittest.TestCase):
299
300 def test_bug_11510(self):
301 # folded constant set optimization was commingled with the tuple
302 # unpacking optimization which would fail if the set had duplicate
303 # elements so that the set length was unexpected
304 def f():
305 x, y = {1, 1}
306 return x, y
307 with self.assertRaises(ValueError):
308 f()
309
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000310
311def test_main(verbose=None):
312 import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000313 from test import support
Raymond Hettinger0661e912011-03-15 15:03:36 -0700314 test_classes = (TestTranforms, TestBuglets)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000315 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000316
317 # verify reference counting
318 if verbose and hasattr(sys, "gettotalrefcount"):
319 import gc
320 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000321 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000322 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000323 gc.collect()
324 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000325 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000326
327if __name__ == "__main__":
328 test_main(verbose=True)