blob: b7d446fdd07e537028bb1bc499fd3c52f1cd84fa [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
11 dis.dis(func)
12 sys.stdout = tmp
13 result = f.getvalue()
14 f.close()
15 return result
16
17def dis_single(line):
18 return disassemble(compile(line, '', 'single'))
19
20class TestTranforms(unittest.TestCase):
21
22 def test_unot(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000023 # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000024 def unot(x):
25 if not x == 2:
26 del x
27 asm = disassemble(unot)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000028 for elem in ('UNARY_NOT', 'POP_JUMP_IF_FALSE'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000029 self.assertNotIn(elem, asm)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000030 for elem in ('POP_JUMP_IF_TRUE',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000031 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000032
33 def test_elim_inversion_of_is_or_in(self):
34 for line, elem in (
35 ('not a is b', '(is not)',),
36 ('not a in b', '(not in)',),
37 ('not a is not b', '(is)',),
38 ('not a not in b', '(in)',),
39 ):
40 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +000041 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000042
Guido van Rossumcd16bf62007-06-13 18:07:49 +000043 def test_global_as_constant(self):
44 # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000045 def f(x):
Tim Peters66cb0182004-08-26 05:23:19 +000046 None
Guido van Rossumcd16bf62007-06-13 18:07:49 +000047 None
Tim Peters66cb0182004-08-26 05:23:19 +000048 return x
Guido van Rossumcd16bf62007-06-13 18:07:49 +000049 def g(x):
50 True
51 return x
52 def h(x):
53 False
54 return x
55 for func, name in ((f, 'None'), (g, 'True'), (h, 'False')):
56 asm = disassemble(func)
57 for elem in ('LOAD_GLOBAL',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000058 self.assertNotIn(elem, asm)
Guido van Rossumcd16bf62007-06-13 18:07:49 +000059 for elem in ('LOAD_CONST', '('+name+')'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000060 self.assertIn(elem, asm)
Guido van Rossumd8faa362007-04-27 19:54:29 +000061 def f():
62 'Adding a docstring made this test fail in Py2.5.0'
63 return None
Benjamin Peterson577473f2010-01-19 00:09:57 +000064 self.assertIn('LOAD_CONST', disassemble(f))
65 self.assertNotIn('LOAD_GLOBAL', disassemble(f))
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000066
67 def test_while_one(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000068 # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000069 def f():
Tim Peters66cb0182004-08-26 05:23:19 +000070 while 1:
71 pass
72 return list
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000073 asm = disassemble(f)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000074 for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):
Benjamin Peterson577473f2010-01-19 00:09:57 +000075 self.assertNotIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000076 for elem in ('JUMP_ABSOLUTE',):
Benjamin Peterson577473f2010-01-19 00:09:57 +000077 self.assertIn(elem, asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000078
79 def test_pack_unpack(self):
80 for line, elem in (
Raymond Hettinger2c31a052004-09-22 18:44:21 +000081 ('a, = a,', 'LOAD_CONST',),
82 ('a, b = a, b', 'ROT_TWO',),
83 ('a, b, c = a, b, c', 'ROT_THREE',),
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000084 ):
85 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +000086 self.assertIn(elem, asm)
87 self.assertNotIn('BUILD_TUPLE', asm)
88 self.assertNotIn('UNPACK_TUPLE', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000089
Raymond Hettinger2c31a052004-09-22 18:44:21 +000090 def test_folding_of_tuples_of_constants(self):
91 for line, elem in (
Raymond Hettinger5dec0962004-11-02 04:20:10 +000092 ('a = 1,2,3', '((1, 2, 3))'),
93 ('("a","b","c")', "(('a', 'b', 'c'))"),
94 ('a,b,c = 1,2,3', '((1, 2, 3))'),
95 ('(None, 1, None)', '((None, 1, None))'),
96 ('((1, 2), 3, 4)', '(((1, 2), 3, 4))'),
Raymond Hettinger2c31a052004-09-22 18:44:21 +000097 ):
98 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +000099 self.assertIn(elem, asm)
100 self.assertNotIn('BUILD_TUPLE', asm)
Raymond Hettinger2c31a052004-09-22 18:44:21 +0000101
Raymond Hettinger23109ef2004-10-26 08:59:14 +0000102 # Bug 1053819: Tuple of constants misidentified when presented with:
103 # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .
104 # The following would segfault upon compilation
105 def crater():
106 (~[
107 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
108 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
109 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
110 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
111 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
112 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
113 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
114 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
115 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
116 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
117 ],)
118
Antoine Pitroub7fbcd32010-01-16 18:37:38 +0000119 def test_folding_of_lists_of_constants(self):
120 for line, elem in (
121 # in/not in constants with BUILD_LIST should be folded to a tuple:
122 ('a in [1,2,3]', '(1, 2, 3)'),
123 ('a not in ["a","b","c"]', "(('a', 'b', 'c'))"),
124 ('a in [None, 1, None]', '((None, 1, None))'),
125 ('a not in [(1, 2), 3, 4]', '(((1, 2), 3, 4))'),
126 ):
127 asm = dis_single(line)
128 self.assertIn(elem, asm)
129 self.assertNotIn('BUILD_LIST', asm)
130
131 def test_folding_of_sets_of_constants(self):
132 for line, elem in (
133 # in/not in constants with BUILD_SET should be folded to a frozenset:
134 ('a in {1,2,3}', frozenset({1, 2, 3})),
135 ('a not in {"a","b","c"}', frozenset({'a', 'c', 'b'})),
136 ('a in {None, 1, None}', frozenset({1, None})),
137 ('a not in {(1, 2), 3, 4}', frozenset({(1, 2), 3, 4})),
138 ('a in {1, 2, 3, 3, 2, 1}', frozenset({1, 2, 3})),
139 ):
140 asm = dis_single(line)
141 self.assertNotIn('BUILD_SET', asm)
142
143 # Verify that the frozenset 'elem' is in the disassembly
144 # The ordering of the elements in repr( frozenset ) isn't
145 # guaranteed, so we jump through some hoops to ensure that we have
146 # the frozenset we expect:
147 self.assertIn('frozenset', asm)
148 # Extract the frozenset literal from the disassembly:
149 m = re.match(r'.*(frozenset\({.*}\)).*', asm, re.DOTALL)
150 self.assertTrue(m)
151 self.assertEqual(eval(m.group(1)), elem)
152
153 # Ensure that the resulting code actually works:
154 def f(a):
155 return a in {1, 2, 3}
156
157 def g(a):
158 return a not in {1, 2, 3}
159
160 self.assertTrue(f(3))
161 self.assertTrue(not f(4))
162
163 self.assertTrue(not g(3))
164 self.assertTrue(g(4))
165
166
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000167 def test_folding_of_binops_on_constants(self):
168 for line, elem in (
169 ('a = 2+3+4', '(9)'), # chained fold
170 ('"@"*4', "('@@@@')"), # check string ops
171 ('a="abc" + "def"', "('abcdef')"), # check string ops
172 ('a = 3**4', '(81)'), # binary power
173 ('a = 3*4', '(12)'), # binary multiply
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000174 ('a = 13//4', '(3)'), # binary floor divide
175 ('a = 14%4', '(2)'), # binary modulo
176 ('a = 2+3', '(5)'), # binary add
177 ('a = 13-4', '(9)'), # binary subtract
178 ('a = (12,13)[1]', '(13)'), # binary subscr
179 ('a = 13 << 2', '(52)'), # binary lshift
180 ('a = 13 >> 2', '(3)'), # binary rshift
181 ('a = 13 & 7', '(5)'), # binary and
182 ('a = 13 ^ 7', '(10)'), # binary xor
183 ('a = 13 | 7', '(15)'), # binary or
184 ):
185 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000186 self.assertIn(elem, asm, asm)
187 self.assertNotIn('BINARY_', asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000188
189 # Verify that unfoldables are skipped
190 asm = dis_single('a=2+"b"')
Benjamin Peterson577473f2010-01-19 00:09:57 +0000191 self.assertIn('(2)', asm)
192 self.assertIn("('b')", asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000193
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000194 # Verify that large sequences do not result from folding
195 asm = dis_single('a="x"*1000')
Benjamin Peterson577473f2010-01-19 00:09:57 +0000196 self.assertIn('(1000)', asm)
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000197
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000198 def test_folding_of_unaryops_on_constants(self):
199 for line, elem in (
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000200 ('-0.5', '(-0.5)'), # unary negative
201 ('~-2', '(1)'), # unary invert
Raymond Hettingeraf7adad2009-10-22 11:22:50 +0000202 ('+1', '(1)'), # unary positive
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000203 ):
204 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000205 self.assertIn(elem, asm, asm)
206 self.assertNotIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000207
208 # Verify that unfoldables are skipped
209 for line, elem in (
210 ('-"abc"', "('abc')"), # unary negative
211 ('~"abc"', "('abc')"), # unary invert
212 ):
213 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000214 self.assertIn(elem, asm, asm)
215 self.assertIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000216
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000217 def test_elim_extra_return(self):
218 # RETURN LOAD_CONST None RETURN --> RETURN
219 def f(x):
220 return x
221 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000222 self.assertNotIn('LOAD_CONST', asm)
223 self.assertNotIn('(None)', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000224 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
225
Thomas Wouters89f507f2006-12-13 04:49:30 +0000226 def test_elim_jump_to_return(self):
227 # JUMP_FORWARD to RETURN --> RETURN
228 def f(cond, true_value, false_value):
229 return true_value if cond else false_value
230 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000231 self.assertNotIn('JUMP_FORWARD', asm)
232 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000233 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
234
235 def test_elim_jump_after_return1(self):
236 # Eliminate dead code: jumps immediately after returns can't be reached
237 def f(cond1, cond2):
238 if cond1: return 1
239 if cond2: return 2
240 while 1:
241 return 3
242 while 1:
243 if cond1: return 4
244 return 5
245 return 6
246 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000247 self.assertNotIn('JUMP_FORWARD', asm)
248 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000249 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
250
251 def test_elim_jump_after_return2(self):
252 # Eliminate dead code: jumps immediately after returns can't be reached
253 def f(cond1, cond2):
254 while 1:
255 if cond1: return 4
256 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000257 self.assertNotIn('JUMP_FORWARD', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000258 # There should be one jump for the while loop.
259 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
260 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000261
Guido van Rossum0240b922007-02-26 21:23:50 +0000262 def test_make_function_doesnt_bail(self):
263 def f():
Guido van Rossumd8faa362007-04-27 19:54:29 +0000264 def g()->1+1:
Guido van Rossum0240b922007-02-26 21:23:50 +0000265 pass
266 return g
267 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000268 self.assertNotIn('BINARY_ADD', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000269
Raymond Hettinger29dcaad2011-03-15 14:50:16 -0700270class TestBuglets(unittest.TestCase):
271
272 def test_bug_11510(self):
273 # folded constant set optimization was commingled with the tuple
274 # unpacking optimization which would fail if the set had duplicate
275 # elements so that the set length was unexpected
276 def f():
277 x, y = {1, 1}
278 return x, y
279 with self.assertRaises(ValueError):
280 f()
281
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000282
283def test_main(verbose=None):
284 import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000285 from test import support
Raymond Hettinger29dcaad2011-03-15 14:50:16 -0700286 test_classes = (TestTranforms, TestBuglets)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000287 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000288
289 # verify reference counting
290 if verbose and hasattr(sys, "gettotalrefcount"):
291 import gc
292 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000293 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000294 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000295 gc.collect()
296 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000297 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000298
299if __name__ == "__main__":
300 test_main(verbose=True)