blob: 9ed814a6530ed99322b4c21695aec53753986e53 [file] [log] [blame]
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +00001import dis
2import sys
3from cStringIO import StringIO
4import 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
42 def test_none_as_constant(self):
43 # LOAD_GLOBAL None --> LOAD_CONST None
44 def f(x):
Tim Peters66cb0182004-08-26 05:23:19 +000045 None
46 return x
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000047 asm = disassemble(f)
48 for elem in ('LOAD_GLOBAL',):
49 self.assert_(elem not in asm)
50 for elem in ('LOAD_CONST', '(None)'):
51 self.assert_(elem in asm)
52
53 def test_while_one(self):
54 # Skip over: LOAD_CONST trueconst JUMP_IF_FALSE xx POP_TOP
55 def f():
Tim Peters66cb0182004-08-26 05:23:19 +000056 while 1:
57 pass
58 return list
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000059 asm = disassemble(f)
60 for elem in ('LOAD_CONST', 'JUMP_IF_FALSE'):
61 self.assert_(elem not in asm)
62 for elem in ('JUMP_ABSOLUTE',):
63 self.assert_(elem in asm)
64
65 def test_pack_unpack(self):
66 for line, elem in (
Raymond Hettinger2c31a052004-09-22 18:44:21 +000067 ('a, = a,', 'LOAD_CONST',),
68 ('a, b = a, b', 'ROT_TWO',),
69 ('a, b, c = a, b, c', 'ROT_THREE',),
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000070 ):
71 asm = dis_single(line)
72 self.assert_(elem in asm)
73 self.assert_('BUILD_TUPLE' not in asm)
74 self.assert_('UNPACK_TUPLE' not in asm)
75
Raymond Hettinger2c31a052004-09-22 18:44:21 +000076 def test_folding_of_tuples_of_constants(self):
77 for line, elem in (
Raymond Hettinger5dec0962004-11-02 04:20:10 +000078 ('a = 1,2,3', '((1, 2, 3))'),
79 ('("a","b","c")', "(('a', 'b', 'c'))"),
80 ('a,b,c = 1,2,3', '((1, 2, 3))'),
81 ('(None, 1, None)', '((None, 1, None))'),
82 ('((1, 2), 3, 4)', '(((1, 2), 3, 4))'),
Raymond Hettinger2c31a052004-09-22 18:44:21 +000083 ):
84 asm = dis_single(line)
85 self.assert_(elem in asm)
86 self.assert_('BUILD_TUPLE' not in asm)
87
Raymond Hettinger23109ef2004-10-26 08:59:14 +000088 # Bug 1053819: Tuple of constants misidentified when presented with:
89 # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .
90 # The following would segfault upon compilation
91 def crater():
92 (~[
93 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
94 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
95 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
96 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
97 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
98 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
99 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
100 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
101 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
102 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
103 ],)
104
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000105 def test_folding_of_binops_on_constants(self):
106 for line, elem in (
107 ('a = 2+3+4', '(9)'), # chained fold
108 ('"@"*4', "('@@@@')"), # check string ops
109 ('a="abc" + "def"', "('abcdef')"), # check string ops
110 ('a = 3**4', '(81)'), # binary power
111 ('a = 3*4', '(12)'), # binary multiply
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000112 ('a = 13//4', '(3)'), # binary floor divide
113 ('a = 14%4', '(2)'), # binary modulo
114 ('a = 2+3', '(5)'), # binary add
115 ('a = 13-4', '(9)'), # binary subtract
116 ('a = (12,13)[1]', '(13)'), # binary subscr
117 ('a = 13 << 2', '(52)'), # binary lshift
118 ('a = 13 >> 2', '(3)'), # binary rshift
119 ('a = 13 & 7', '(5)'), # binary and
120 ('a = 13 ^ 7', '(10)'), # binary xor
121 ('a = 13 | 7', '(15)'), # binary or
122 ):
123 asm = dis_single(line)
124 self.assert_(elem in asm, asm)
125 self.assert_('BINARY_' not in asm)
126
127 # Verify that unfoldables are skipped
128 asm = dis_single('a=2+"b"')
129 self.assert_('(2)' in asm)
130 self.assert_("('b')" in asm)
131
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000132 # Verify that large sequences do not result from folding
133 asm = dis_single('a="x"*1000')
134 self.assert_('(1000)' in asm)
135
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000136 def test_folding_of_unaryops_on_constants(self):
137 for line, elem in (
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000138 ('-0.5', '(-0.5)'), # unary negative
139 ('~-2', '(1)'), # unary invert
140 ):
141 asm = dis_single(line)
142 self.assert_(elem in asm, asm)
143 self.assert_('UNARY_' not in asm)
144
145 # Verify that unfoldables are skipped
146 for line, elem in (
147 ('-"abc"', "('abc')"), # unary negative
148 ('~"abc"', "('abc')"), # unary invert
149 ):
150 asm = dis_single(line)
151 self.assert_(elem in asm, asm)
152 self.assert_('UNARY_' in asm)
153
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000154 def test_elim_extra_return(self):
155 # RETURN LOAD_CONST None RETURN --> RETURN
156 def f(x):
157 return x
158 asm = disassemble(f)
159 self.assert_('LOAD_CONST' not in asm)
160 self.assert_('(None)' not in asm)
161 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
162
Thomas Wouters89f507f2006-12-13 04:49:30 +0000163 def test_elim_jump_to_return(self):
164 # JUMP_FORWARD to RETURN --> RETURN
165 def f(cond, true_value, false_value):
166 return true_value if cond else false_value
167 asm = disassemble(f)
168 self.assert_('JUMP_FORWARD' not in asm)
169 self.assert_('JUMP_ABSOLUTE' not in asm)
170 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
171
172 def test_elim_jump_after_return1(self):
173 # Eliminate dead code: jumps immediately after returns can't be reached
174 def f(cond1, cond2):
175 if cond1: return 1
176 if cond2: return 2
177 while 1:
178 return 3
179 while 1:
180 if cond1: return 4
181 return 5
182 return 6
183 asm = disassemble(f)
184 self.assert_('JUMP_FORWARD' not in asm)
185 self.assert_('JUMP_ABSOLUTE' not in asm)
186 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
187
188 def test_elim_jump_after_return2(self):
189 # Eliminate dead code: jumps immediately after returns can't be reached
190 def f(cond1, cond2):
191 while 1:
192 if cond1: return 4
193 asm = disassemble(f)
194 self.assert_('JUMP_FORWARD' not in asm)
195 # There should be one jump for the while loop.
196 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
197 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossum0240b922007-02-26 21:23:50 +0000198
199 def test_make_function_doesnt_bail(self):
200 def f():
201 def g()->1+1:
202 pass
203 return g
204 asm = disassemble(f)
205 self.assert_('BINARY_ADD' not in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000206
207
208def test_main(verbose=None):
209 import sys
210 from test import test_support
211 test_classes = (TestTranforms,)
212 test_support.run_unittest(*test_classes)
213
214 # verify reference counting
215 if verbose and hasattr(sys, "gettotalrefcount"):
216 import gc
217 counts = [None] * 5
218 for i in xrange(len(counts)):
219 test_support.run_unittest(*test_classes)
220 gc.collect()
221 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000222 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000223
224if __name__ == "__main__":
225 test_main(verbose=True)