blob: 53719d3a823aa1afb89648dc89fe218cdc7b32ac [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
Ezio Melotti2df6a932011-04-15 16:38:34 +0300198 def test_binary_subscr_on_unicode(self):
199 # valid code get optimized
200 asm = dis_single('"foo"[0]')
201 self.assertIn("('f')", asm)
202 self.assertNotIn('BINARY_SUBSCR', asm)
203 asm = dis_single('"\u0061\uffff"[1]')
204 self.assertIn("('\\uffff')", asm)
205 self.assertNotIn('BINARY_SUBSCR', asm)
206
207 # invalid code doesn't get optimized
208 # out of range
209 asm = dis_single('"fuu"[10]')
210 self.assertIn('BINARY_SUBSCR', asm)
211 # non-BMP char (see #5057)
212 asm = dis_single('"\U00012345"[0]')
213 self.assertIn('BINARY_SUBSCR', asm)
214
215
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000216 def test_folding_of_unaryops_on_constants(self):
217 for line, elem in (
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000218 ('-0.5', '(-0.5)'), # unary negative
219 ('~-2', '(1)'), # unary invert
Raymond Hettingeraf7adad2009-10-22 11:22:50 +0000220 ('+1', '(1)'), # unary positive
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000221 ):
222 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000223 self.assertIn(elem, asm, asm)
224 self.assertNotIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000225
226 # Verify that unfoldables are skipped
227 for line, elem in (
228 ('-"abc"', "('abc')"), # unary negative
229 ('~"abc"', "('abc')"), # unary invert
230 ):
231 asm = dis_single(line)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000232 self.assertIn(elem, asm, asm)
233 self.assertIn('UNARY_', asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000234
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000235 def test_elim_extra_return(self):
236 # RETURN LOAD_CONST None RETURN --> RETURN
237 def f(x):
238 return x
239 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000240 self.assertNotIn('LOAD_CONST', asm)
241 self.assertNotIn('(None)', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000242 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
243
Thomas Wouters89f507f2006-12-13 04:49:30 +0000244 def test_elim_jump_to_return(self):
245 # JUMP_FORWARD to RETURN --> RETURN
246 def f(cond, true_value, false_value):
247 return true_value if cond else false_value
248 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000249 self.assertNotIn('JUMP_FORWARD', asm)
250 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000251 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
252
253 def test_elim_jump_after_return1(self):
254 # Eliminate dead code: jumps immediately after returns can't be reached
255 def f(cond1, cond2):
256 if cond1: return 1
257 if cond2: return 2
258 while 1:
259 return 3
260 while 1:
261 if cond1: return 4
262 return 5
263 return 6
264 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000265 self.assertNotIn('JUMP_FORWARD', asm)
266 self.assertNotIn('JUMP_ABSOLUTE', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000267 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
268
269 def test_elim_jump_after_return2(self):
270 # Eliminate dead code: jumps immediately after returns can't be reached
271 def f(cond1, cond2):
272 while 1:
273 if cond1: return 4
274 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000275 self.assertNotIn('JUMP_FORWARD', asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000276 # There should be one jump for the while loop.
277 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
278 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000279
Guido van Rossum0240b922007-02-26 21:23:50 +0000280 def test_make_function_doesnt_bail(self):
281 def f():
Guido van Rossumd8faa362007-04-27 19:54:29 +0000282 def g()->1+1:
Guido van Rossum0240b922007-02-26 21:23:50 +0000283 pass
284 return g
285 asm = disassemble(f)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000286 self.assertNotIn('BINARY_ADD', asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000287
Raymond Hettinger29dcaad2011-03-15 14:50:16 -0700288class TestBuglets(unittest.TestCase):
289
290 def test_bug_11510(self):
291 # folded constant set optimization was commingled with the tuple
292 # unpacking optimization which would fail if the set had duplicate
293 # elements so that the set length was unexpected
294 def f():
295 x, y = {1, 1}
296 return x, y
297 with self.assertRaises(ValueError):
298 f()
299
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000300
301def test_main(verbose=None):
302 import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000303 from test import support
Raymond Hettinger29dcaad2011-03-15 14:50:16 -0700304 test_classes = (TestTranforms, TestBuglets)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000305 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000306
307 # verify reference counting
308 if verbose and hasattr(sys, "gettotalrefcount"):
309 import gc
310 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000311 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000312 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000313 gc.collect()
314 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000315 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000316
317if __name__ == "__main__":
318 test_main(verbose=True)