blob: d1d5874bb7700cffadbe8844ffce4d96d12146c8 [file] [log] [blame]
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +00001import dis
2import sys
Guido van Rossum34d19282007-08-09 01:03:29 +00003from io import StringIO
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +00004import unittest
5
6def disassemble(func):
7 f = StringIO()
8 tmp = sys.stdout
9 sys.stdout = f
10 dis.dis(func)
11 sys.stdout = tmp
12 result = f.getvalue()
13 f.close()
14 return result
15
16def dis_single(line):
17 return disassemble(compile(line, '', 'single'))
18
19class TestTranforms(unittest.TestCase):
20
21 def test_unot(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000022 # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000023 def unot(x):
24 if not x == 2:
25 del x
26 asm = disassemble(unot)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000027 for elem in ('UNARY_NOT', 'POP_JUMP_IF_FALSE'):
Georg Brandlab91fde2009-08-13 08:51:18 +000028 self.assertTrue(elem not in asm)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000029 for elem in ('POP_JUMP_IF_TRUE',):
Georg Brandlab91fde2009-08-13 08:51:18 +000030 self.assertTrue(elem in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000031
32 def test_elim_inversion_of_is_or_in(self):
33 for line, elem in (
34 ('not a is b', '(is not)',),
35 ('not a in b', '(not in)',),
36 ('not a is not b', '(is)',),
37 ('not a not in b', '(in)',),
38 ):
39 asm = dis_single(line)
Georg Brandlab91fde2009-08-13 08:51:18 +000040 self.assertTrue(elem in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000041
Guido van Rossumcd16bf62007-06-13 18:07:49 +000042 def test_global_as_constant(self):
43 # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000044 def f(x):
Tim Peters66cb0182004-08-26 05:23:19 +000045 None
Guido van Rossumcd16bf62007-06-13 18:07:49 +000046 None
Tim Peters66cb0182004-08-26 05:23:19 +000047 return x
Guido van Rossumcd16bf62007-06-13 18:07:49 +000048 def g(x):
49 True
50 return x
51 def h(x):
52 False
53 return x
54 for func, name in ((f, 'None'), (g, 'True'), (h, 'False')):
55 asm = disassemble(func)
56 for elem in ('LOAD_GLOBAL',):
Georg Brandlab91fde2009-08-13 08:51:18 +000057 self.assertTrue(elem not in asm)
Guido van Rossumcd16bf62007-06-13 18:07:49 +000058 for elem in ('LOAD_CONST', '('+name+')'):
Georg Brandlab91fde2009-08-13 08:51:18 +000059 self.assertTrue(elem in asm)
Guido van Rossumd8faa362007-04-27 19:54:29 +000060 def f():
61 'Adding a docstring made this test fail in Py2.5.0'
62 return None
Georg Brandlab91fde2009-08-13 08:51:18 +000063 self.assertTrue('LOAD_CONST' in disassemble(f))
64 self.assertTrue('LOAD_GLOBAL' not in disassemble(f))
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000065
66 def test_while_one(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000067 # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000068 def f():
Tim Peters66cb0182004-08-26 05:23:19 +000069 while 1:
70 pass
71 return list
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000072 asm = disassemble(f)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000073 for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):
Georg Brandlab91fde2009-08-13 08:51:18 +000074 self.assertTrue(elem not in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000075 for elem in ('JUMP_ABSOLUTE',):
Georg Brandlab91fde2009-08-13 08:51:18 +000076 self.assertTrue(elem in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000077
78 def test_pack_unpack(self):
79 for line, elem in (
Raymond Hettinger2c31a052004-09-22 18:44:21 +000080 ('a, = a,', 'LOAD_CONST',),
81 ('a, b = a, b', 'ROT_TWO',),
82 ('a, b, c = a, b, c', 'ROT_THREE',),
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000083 ):
84 asm = dis_single(line)
Georg Brandlab91fde2009-08-13 08:51:18 +000085 self.assertTrue(elem in asm)
86 self.assertTrue('BUILD_TUPLE' not in asm)
87 self.assertTrue('UNPACK_TUPLE' not in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000088
Raymond Hettinger2c31a052004-09-22 18:44:21 +000089 def test_folding_of_tuples_of_constants(self):
90 for line, elem in (
Raymond Hettinger5dec0962004-11-02 04:20:10 +000091 ('a = 1,2,3', '((1, 2, 3))'),
92 ('("a","b","c")', "(('a', 'b', 'c'))"),
93 ('a,b,c = 1,2,3', '((1, 2, 3))'),
94 ('(None, 1, None)', '((None, 1, None))'),
95 ('((1, 2), 3, 4)', '(((1, 2), 3, 4))'),
Raymond Hettinger2c31a052004-09-22 18:44:21 +000096 ):
97 asm = dis_single(line)
Georg Brandlab91fde2009-08-13 08:51:18 +000098 self.assertTrue(elem in asm)
99 self.assertTrue('BUILD_TUPLE' not in asm)
Raymond Hettinger2c31a052004-09-22 18:44:21 +0000100
Raymond Hettinger23109ef2004-10-26 08:59:14 +0000101 # Bug 1053819: Tuple of constants misidentified when presented with:
102 # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .
103 # The following would segfault upon compilation
104 def crater():
105 (~[
106 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
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 ],)
117
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000118 def test_folding_of_binops_on_constants(self):
119 for line, elem in (
120 ('a = 2+3+4', '(9)'), # chained fold
121 ('"@"*4', "('@@@@')"), # check string ops
122 ('a="abc" + "def"', "('abcdef')"), # check string ops
123 ('a = 3**4', '(81)'), # binary power
124 ('a = 3*4', '(12)'), # binary multiply
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000125 ('a = 13//4', '(3)'), # binary floor divide
126 ('a = 14%4', '(2)'), # binary modulo
127 ('a = 2+3', '(5)'), # binary add
128 ('a = 13-4', '(9)'), # binary subtract
129 ('a = (12,13)[1]', '(13)'), # binary subscr
130 ('a = 13 << 2', '(52)'), # binary lshift
131 ('a = 13 >> 2', '(3)'), # binary rshift
132 ('a = 13 & 7', '(5)'), # binary and
133 ('a = 13 ^ 7', '(10)'), # binary xor
134 ('a = 13 | 7', '(15)'), # binary or
135 ):
136 asm = dis_single(line)
Georg Brandlab91fde2009-08-13 08:51:18 +0000137 self.assertTrue(elem in asm, asm)
138 self.assertTrue('BINARY_' not in asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000139
140 # Verify that unfoldables are skipped
141 asm = dis_single('a=2+"b"')
Georg Brandlab91fde2009-08-13 08:51:18 +0000142 self.assertTrue('(2)' in asm)
143 self.assertTrue("('b')" in asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000144
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000145 # Verify that large sequences do not result from folding
146 asm = dis_single('a="x"*1000')
Georg Brandlab91fde2009-08-13 08:51:18 +0000147 self.assertTrue('(1000)' in asm)
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000148
Ezio Melotti2df6a932011-04-15 16:38:34 +0300149 def test_binary_subscr_on_unicode(self):
150 # valid code get optimized
151 asm = dis_single('"foo"[0]')
152 self.assertIn("('f')", asm)
153 self.assertNotIn('BINARY_SUBSCR', asm)
154 asm = dis_single('"\u0061\uffff"[1]')
155 self.assertIn("('\\uffff')", asm)
156 self.assertNotIn('BINARY_SUBSCR', asm)
157
158 # invalid code doesn't get optimized
159 # out of range
160 asm = dis_single('"fuu"[10]')
161 self.assertIn('BINARY_SUBSCR', asm)
162 # non-BMP char (see #5057)
163 asm = dis_single('"\U00012345"[0]')
164 self.assertIn('BINARY_SUBSCR', asm)
165
166
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000167 def test_folding_of_unaryops_on_constants(self):
168 for line, elem in (
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000169 ('-0.5', '(-0.5)'), # unary negative
170 ('~-2', '(1)'), # unary invert
171 ):
172 asm = dis_single(line)
Georg Brandlab91fde2009-08-13 08:51:18 +0000173 self.assertTrue(elem in asm, asm)
174 self.assertTrue('UNARY_' not in asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000175
176 # Verify that unfoldables are skipped
177 for line, elem in (
178 ('-"abc"', "('abc')"), # unary negative
179 ('~"abc"', "('abc')"), # unary invert
180 ):
181 asm = dis_single(line)
Georg Brandlab91fde2009-08-13 08:51:18 +0000182 self.assertTrue(elem in asm, asm)
183 self.assertTrue('UNARY_' in asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000184
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000185 def test_elim_extra_return(self):
186 # RETURN LOAD_CONST None RETURN --> RETURN
187 def f(x):
188 return x
189 asm = disassemble(f)
Georg Brandlab91fde2009-08-13 08:51:18 +0000190 self.assertTrue('LOAD_CONST' not in asm)
191 self.assertTrue('(None)' not in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000192 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
193
Thomas Wouters89f507f2006-12-13 04:49:30 +0000194 def test_elim_jump_to_return(self):
195 # JUMP_FORWARD to RETURN --> RETURN
196 def f(cond, true_value, false_value):
197 return true_value if cond else false_value
198 asm = disassemble(f)
Georg Brandlab91fde2009-08-13 08:51:18 +0000199 self.assertTrue('JUMP_FORWARD' not in asm)
200 self.assertTrue('JUMP_ABSOLUTE' not in asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000201 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
202
203 def test_elim_jump_after_return1(self):
204 # Eliminate dead code: jumps immediately after returns can't be reached
205 def f(cond1, cond2):
206 if cond1: return 1
207 if cond2: return 2
208 while 1:
209 return 3
210 while 1:
211 if cond1: return 4
212 return 5
213 return 6
214 asm = disassemble(f)
Georg Brandlab91fde2009-08-13 08:51:18 +0000215 self.assertTrue('JUMP_FORWARD' not in asm)
216 self.assertTrue('JUMP_ABSOLUTE' not in asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000217 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
218
219 def test_elim_jump_after_return2(self):
220 # Eliminate dead code: jumps immediately after returns can't be reached
221 def f(cond1, cond2):
222 while 1:
223 if cond1: return 4
224 asm = disassemble(f)
Georg Brandlab91fde2009-08-13 08:51:18 +0000225 self.assertTrue('JUMP_FORWARD' not in asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000226 # There should be one jump for the while loop.
227 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
228 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000229
Guido van Rossum0240b922007-02-26 21:23:50 +0000230 def test_make_function_doesnt_bail(self):
231 def f():
Guido van Rossumd8faa362007-04-27 19:54:29 +0000232 def g()->1+1:
Guido van Rossum0240b922007-02-26 21:23:50 +0000233 pass
234 return g
235 asm = disassemble(f)
Georg Brandlab91fde2009-08-13 08:51:18 +0000236 self.assertTrue('BINARY_ADD' not in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000237
238
239def test_main(verbose=None):
240 import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000241 from test import support
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000242 test_classes = (TestTranforms,)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000243 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000244
245 # verify reference counting
246 if verbose and hasattr(sys, "gettotalrefcount"):
247 import gc
248 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000249 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000250 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000251 gc.collect()
252 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000253 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000254
255if __name__ == "__main__":
256 test_main(verbose=True)