blob: f3e240e8f8b154ed45150ce67bce36788ef695d9 [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):
22 # UNARY_NOT JUMP_IF_FALSE POP_TOP --> JUMP_IF_TRUE POP_TOP'
23 def unot(x):
24 if not x == 2:
25 del x
26 asm = disassemble(unot)
27 for elem in ('UNARY_NOT', 'JUMP_IF_FALSE'):
28 self.assert_(elem not in asm)
29 for elem in ('JUMP_IF_TRUE', 'POP_TOP'):
30 self.assert_(elem in asm)
31
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)
40 self.assert_(elem in asm)
41
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',):
57 self.assert_(elem not in asm)
58 for elem in ('LOAD_CONST', '('+name+')'):
59 self.assert_(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
63 self.assert_('LOAD_CONST' in disassemble(f))
64 self.assert_('LOAD_GLOBAL' not in disassemble(f))
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000065
66 def test_while_one(self):
67 # Skip over: LOAD_CONST trueconst JUMP_IF_FALSE xx POP_TOP
68 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)
73 for elem in ('LOAD_CONST', 'JUMP_IF_FALSE'):
74 self.assert_(elem not in asm)
75 for elem in ('JUMP_ABSOLUTE',):
76 self.assert_(elem in asm)
77
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)
85 self.assert_(elem in asm)
86 self.assert_('BUILD_TUPLE' not in asm)
87 self.assert_('UNPACK_TUPLE' not in asm)
88
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)
98 self.assert_(elem in asm)
99 self.assert_('BUILD_TUPLE' not in asm)
100
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)
137 self.assert_(elem in asm, asm)
138 self.assert_('BINARY_' not in asm)
139
140 # Verify that unfoldables are skipped
141 asm = dis_single('a=2+"b"')
142 self.assert_('(2)' in asm)
143 self.assert_("('b')" in asm)
144
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000145 # Verify that large sequences do not result from folding
146 asm = dis_single('a="x"*1000')
147 self.assert_('(1000)' in asm)
148
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000149 def test_folding_of_unaryops_on_constants(self):
150 for line, elem in (
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000151 ('-0.5', '(-0.5)'), # unary negative
152 ('~-2', '(1)'), # unary invert
153 ):
154 asm = dis_single(line)
155 self.assert_(elem in asm, asm)
156 self.assert_('UNARY_' not in asm)
157
158 # Verify that unfoldables are skipped
159 for line, elem in (
160 ('-"abc"', "('abc')"), # unary negative
161 ('~"abc"', "('abc')"), # unary invert
162 ):
163 asm = dis_single(line)
164 self.assert_(elem in asm, asm)
165 self.assert_('UNARY_' in asm)
166
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000167 def test_elim_extra_return(self):
168 # RETURN LOAD_CONST None RETURN --> RETURN
169 def f(x):
170 return x
171 asm = disassemble(f)
172 self.assert_('LOAD_CONST' not in asm)
173 self.assert_('(None)' not in asm)
174 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
175
Thomas Wouters89f507f2006-12-13 04:49:30 +0000176 def test_elim_jump_to_return(self):
177 # JUMP_FORWARD to RETURN --> RETURN
178 def f(cond, true_value, false_value):
179 return true_value if cond else false_value
180 asm = disassemble(f)
181 self.assert_('JUMP_FORWARD' not in asm)
182 self.assert_('JUMP_ABSOLUTE' not in asm)
183 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
184
185 def test_elim_jump_after_return1(self):
186 # Eliminate dead code: jumps immediately after returns can't be reached
187 def f(cond1, cond2):
188 if cond1: return 1
189 if cond2: return 2
190 while 1:
191 return 3
192 while 1:
193 if cond1: return 4
194 return 5
195 return 6
196 asm = disassemble(f)
197 self.assert_('JUMP_FORWARD' not in asm)
198 self.assert_('JUMP_ABSOLUTE' not in asm)
199 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
200
201 def test_elim_jump_after_return2(self):
202 # Eliminate dead code: jumps immediately after returns can't be reached
203 def f(cond1, cond2):
204 while 1:
205 if cond1: return 4
206 asm = disassemble(f)
207 self.assert_('JUMP_FORWARD' not in asm)
208 # There should be one jump for the while loop.
209 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
210 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000211
Guido van Rossum0240b922007-02-26 21:23:50 +0000212 def test_make_function_doesnt_bail(self):
213 def f():
Guido van Rossumd8faa362007-04-27 19:54:29 +0000214 def g()->1+1:
Guido van Rossum0240b922007-02-26 21:23:50 +0000215 pass
216 return g
217 asm = disassemble(f)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000218 self.assert_('BINARY_ADD' not in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000219
220
221def test_main(verbose=None):
222 import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000223 from test import support
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000224 test_classes = (TestTranforms,)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000225 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000226
227 # verify reference counting
228 if verbose and hasattr(sys, "gettotalrefcount"):
229 import gc
230 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000231 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000232 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000233 gc.collect()
234 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000235 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000236
237if __name__ == "__main__":
238 test_main(verbose=True)