blob: a9eb23fde316df8758a9e4af6df883a04c739ba5 [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
22class TestTranforms(unittest.TestCase):
23
24 def test_unot(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000025 # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000026 def unot(x):
27 if not x == 2:
28 del x
29 asm = disassemble(unot)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000030 for elem in ('UNARY_NOT', 'POP_JUMP_IF_FALSE'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000031 self.assertNotIn(elem, asm)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000032 for elem in ('POP_JUMP_IF_TRUE',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000033 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000034
35 def test_elim_inversion_of_is_or_in(self):
36 for line, elem in (
37 ('not a is b', '(is not)',),
38 ('not a in b', '(not in)',),
39 ('not a is not b', '(is)',),
40 ('not a not in b', '(in)',),
41 ):
42 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +000043 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000044
Guido van Rossumcd16bf62007-06-13 18:07:49 +000045 def test_global_as_constant(self):
46 # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000047 def f(x):
Tim Peters66cb0182004-08-26 05:23:19 +000048 None
Guido van Rossumcd16bf62007-06-13 18:07:49 +000049 None
Tim Peters66cb0182004-08-26 05:23:19 +000050 return x
Guido van Rossumcd16bf62007-06-13 18:07:49 +000051 def g(x):
52 True
53 return x
54 def h(x):
55 False
56 return x
57 for func, name in ((f, 'None'), (g, 'True'), (h, 'False')):
58 asm = disassemble(func)
59 for elem in ('LOAD_GLOBAL',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000060 self.assertNotIn(elem, asm)
Guido van Rossumcd16bf62007-06-13 18:07:49 +000061 for elem in ('LOAD_CONST', '('+name+')'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000062 self.assertIn(elem, asm)
Guido van Rossumd8faa362007-04-27 19:54:29 +000063 def f():
64 'Adding a docstring made this test fail in Py2.5.0'
65 return None
Benjamin Peterson577473f2010-01-19 00:09:57 +000066 self.assertIn('LOAD_CONST', disassemble(f))
67 self.assertNotIn('LOAD_GLOBAL', disassemble(f))
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000068
69 def test_while_one(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000070 # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000071 def f():
Tim Peters66cb0182004-08-26 05:23:19 +000072 while 1:
73 pass
74 return list
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000075 asm = disassemble(f)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000076 for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000077 self.assertNotIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000078 for elem in ('JUMP_ABSOLUTE',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000079 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000080
81 def test_pack_unpack(self):
82 for line, elem in (
Raymond Hettinger2c31a052004-09-22 18:44:21 +000083 ('a, = a,', 'LOAD_CONST',),
84 ('a, b = a, b', 'ROT_TWO',),
85 ('a, b, c = a, b, c', 'ROT_THREE',),
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000086 ):
87 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +000088 self.assertIn(elem, asm)
89 self.assertNotIn('BUILD_TUPLE', asm)
90 self.assertNotIn('UNPACK_TUPLE', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000091
Raymond Hettinger2c31a052004-09-22 18:44:21 +000092 def test_folding_of_tuples_of_constants(self):
93 for line, elem in (
Raymond Hettinger5dec0962004-11-02 04:20:10 +000094 ('a = 1,2,3', '((1, 2, 3))'),
95 ('("a","b","c")', "(('a', 'b', 'c'))"),
96 ('a,b,c = 1,2,3', '((1, 2, 3))'),
97 ('(None, 1, None)', '((None, 1, None))'),
98 ('((1, 2), 3, 4)', '(((1, 2), 3, 4))'),
Raymond Hettinger2c31a052004-09-22 18:44:21 +000099 ):
100 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000101 self.assertIn(elem, asm)
102 self.assertNotIn('BUILD_TUPLE', asm)
Raymond Hettinger2c31a052004-09-22 18:44:21 +0000103
Antoine Pitrou17b880a2011-03-11 17:27:02 +0100104 # Long tuples should be folded too.
105 asm = dis_single(repr(tuple(range(10000))))
106 # One LOAD_CONST for the tuple, one for the None return value
107 self.assertEqual(asm.count('LOAD_CONST'), 2)
108 self.assertNotIn('BUILD_TUPLE', asm)
109
Raymond Hettinger23109ef2004-10-26 08:59:14 +0000110 # Bug 1053819: Tuple of constants misidentified when presented with:
111 # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .
112 # The following would segfault upon compilation
113 def crater():
114 (~[
115 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
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 ],)
126
Antoine Pitroub7fbcd32010-01-16 18:37:38 +0000127 def test_folding_of_lists_of_constants(self):
128 for line, elem in (
129 # in/not in constants with BUILD_LIST should be folded to a tuple:
130 ('a in [1,2,3]', '(1, 2, 3)'),
131 ('a not in ["a","b","c"]', "(('a', 'b', 'c'))"),
132 ('a in [None, 1, None]', '((None, 1, None))'),
133 ('a not in [(1, 2), 3, 4]', '(((1, 2), 3, 4))'),
134 ):
135 asm = dis_single(line)
136 self.assertIn(elem, asm)
137 self.assertNotIn('BUILD_LIST', asm)
138
139 def test_folding_of_sets_of_constants(self):
140 for line, elem in (
141 # in/not in constants with BUILD_SET should be folded to a frozenset:
142 ('a in {1,2,3}', frozenset({1, 2, 3})),
143 ('a not in {"a","b","c"}', frozenset({'a', 'c', 'b'})),
144 ('a in {None, 1, None}', frozenset({1, None})),
145 ('a not in {(1, 2), 3, 4}', frozenset({(1, 2), 3, 4})),
146 ('a in {1, 2, 3, 3, 2, 1}', frozenset({1, 2, 3})),
147 ):
148 asm = dis_single(line)
149 self.assertNotIn('BUILD_SET', asm)
150
151 # Verify that the frozenset 'elem' is in the disassembly
152 # The ordering of the elements in repr( frozenset ) isn't
153 # guaranteed, so we jump through some hoops to ensure that we have
154 # the frozenset we expect:
155 self.assertIn('frozenset', asm)
156 # Extract the frozenset literal from the disassembly:
157 m = re.match(r'.*(frozenset\({.*}\)).*', asm, re.DOTALL)
158 self.assertTrue(m)
159 self.assertEqual(eval(m.group(1)), elem)
160
161 # Ensure that the resulting code actually works:
162 def f(a):
163 return a in {1, 2, 3}
164
165 def g(a):
166 return a not in {1, 2, 3}
167
168 self.assertTrue(f(3))
169 self.assertTrue(not f(4))
170
171 self.assertTrue(not g(3))
172 self.assertTrue(g(4))
173
174
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000175 def test_folding_of_binops_on_constants(self):
176 for line, elem in (
177 ('a = 2+3+4', '(9)'), # chained fold
178 ('"@"*4', "('@@@@')"), # check string ops
179 ('a="abc" + "def"', "('abcdef')"), # check string ops
180 ('a = 3**4', '(81)'), # binary power
181 ('a = 3*4', '(12)'), # binary multiply
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000182 ('a = 13//4', '(3)'), # binary floor divide
183 ('a = 14%4', '(2)'), # binary modulo
184 ('a = 2+3', '(5)'), # binary add
185 ('a = 13-4', '(9)'), # binary subtract
186 ('a = (12,13)[1]', '(13)'), # binary subscr
187 ('a = 13 << 2', '(52)'), # binary lshift
188 ('a = 13 >> 2', '(3)'), # binary rshift
189 ('a = 13 & 7', '(5)'), # binary and
190 ('a = 13 ^ 7', '(10)'), # binary xor
191 ('a = 13 | 7', '(15)'), # binary or
192 ):
193 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000194 self.assertIn(elem, asm, asm)
195 self.assertNotIn('BINARY_', asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000196
197 # Verify that unfoldables are skipped
198 asm = dis_single('a=2+"b"')
Benjamin Peterson577473f2010-01-19 00:09:57 +0000199 self.assertIn('(2)', asm)
200 self.assertIn("('b')", asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000201
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000202 # Verify that large sequences do not result from folding
203 asm = dis_single('a="x"*1000')
Benjamin Peterson577473f2010-01-19 00:09:57 +0000204 self.assertIn('(1000)', asm)
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000205
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000206 def test_folding_of_unaryops_on_constants(self):
207 for line, elem in (
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000208 ('-0.5', '(-0.5)'), # unary negative
209 ('~-2', '(1)'), # unary invert
Raymond Hettingeraf7adad2009-10-22 11:22:50 +0000210 ('+1', '(1)'), # unary positive
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000211 ):
212 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000213 self.assertIn(elem, asm, asm)
214 self.assertNotIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000215
216 # Verify that unfoldables are skipped
217 for line, elem in (
218 ('-"abc"', "('abc')"), # unary negative
219 ('~"abc"', "('abc')"), # unary invert
220 ):
221 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000222 self.assertIn(elem, asm, asm)
223 self.assertIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000224
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000225 def test_elim_extra_return(self):
226 # RETURN LOAD_CONST None RETURN --> RETURN
227 def f(x):
228 return x
229 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000230 self.assertNotIn('LOAD_CONST', asm)
231 self.assertNotIn('(None)', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000232 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
233
Thomas Wouters89f507f2006-12-13 04:49:30 +0000234 def test_elim_jump_to_return(self):
235 # JUMP_FORWARD to RETURN --> RETURN
236 def f(cond, true_value, false_value):
237 return true_value if cond else false_value
238 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000239 self.assertNotIn('JUMP_FORWARD', asm)
240 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000241 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
242
243 def test_elim_jump_after_return1(self):
244 # Eliminate dead code: jumps immediately after returns can't be reached
245 def f(cond1, cond2):
246 if cond1: return 1
247 if cond2: return 2
248 while 1:
249 return 3
250 while 1:
251 if cond1: return 4
252 return 5
253 return 6
254 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000255 self.assertNotIn('JUMP_FORWARD', asm)
256 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000257 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
258
259 def test_elim_jump_after_return2(self):
260 # Eliminate dead code: jumps immediately after returns can't be reached
261 def f(cond1, cond2):
262 while 1:
263 if cond1: return 4
264 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000265 self.assertNotIn('JUMP_FORWARD', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000266 # There should be one jump for the while loop.
267 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
268 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000269
Guido van Rossum0240b922007-02-26 21:23:50 +0000270 def test_make_function_doesnt_bail(self):
271 def f():
Guido van Rossumd8faa362007-04-27 19:54:29 +0000272 def g()->1+1:
Guido van Rossum0240b922007-02-26 21:23:50 +0000273 pass
274 return g
275 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000276 self.assertNotIn('BINARY_ADD', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000277
Antoine Pitrou17b880a2011-03-11 17:27:02 +0100278 def test_constant_folding(self):
279 # Issue #11244: aggressive constant folding.
280 exprs = [
281 "3 * -5",
282 "-3 * 5",
283 "2 * (3 * 4)",
284 "(2 * 3) * 4",
285 "(-1, 2, 3)",
286 "(1, -2, 3)",
287 "(1, 2, -3)",
288 "(1, 2, -3) * 6",
289 "lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}",
290 ]
291 for e in exprs:
292 asm = dis_single(e)
293 self.assertNotIn('UNARY_', asm, e)
294 self.assertNotIn('BINARY_', asm, e)
295 self.assertNotIn('BUILD_', asm, e)
296
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000297
298def test_main(verbose=None):
299 import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000300 from test import support
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000301 test_classes = (TestTranforms,)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000302 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000303
304 # verify reference counting
305 if verbose and hasattr(sys, "gettotalrefcount"):
306 import gc
307 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000308 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000309 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000310 gc.collect()
311 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000312 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000313
314if __name__ == "__main__":
315 test_main(verbose=True)