blob: c547984daaaf0b36020cd21cde5a25ffccb6fd9b [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)
Raymond Hettinger20e11992007-03-02 19:20:46 +000052 def f():
53 'Adding a docstring made this test fail in Py2.5.0'
54 return None
55 self.assert_('LOAD_CONST' in disassemble(f))
56 self.assert_('LOAD_GLOBAL' not in disassemble(f))
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000057
58 def test_while_one(self):
59 # Skip over: LOAD_CONST trueconst JUMP_IF_FALSE xx POP_TOP
60 def f():
Tim Peters66cb0182004-08-26 05:23:19 +000061 while 1:
62 pass
63 return list
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000064 asm = disassemble(f)
65 for elem in ('LOAD_CONST', 'JUMP_IF_FALSE'):
66 self.assert_(elem not in asm)
67 for elem in ('JUMP_ABSOLUTE',):
68 self.assert_(elem in asm)
69
70 def test_pack_unpack(self):
71 for line, elem in (
Raymond Hettinger2c31a052004-09-22 18:44:21 +000072 ('a, = a,', 'LOAD_CONST',),
73 ('a, b = a, b', 'ROT_TWO',),
74 ('a, b, c = a, b, c', 'ROT_THREE',),
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000075 ):
76 asm = dis_single(line)
77 self.assert_(elem in asm)
78 self.assert_('BUILD_TUPLE' not in asm)
79 self.assert_('UNPACK_TUPLE' not in asm)
80
Raymond Hettinger2c31a052004-09-22 18:44:21 +000081 def test_folding_of_tuples_of_constants(self):
82 for line, elem in (
Raymond Hettinger5dec0962004-11-02 04:20:10 +000083 ('a = 1,2,3', '((1, 2, 3))'),
84 ('("a","b","c")', "(('a', 'b', 'c'))"),
85 ('a,b,c = 1,2,3', '((1, 2, 3))'),
86 ('(None, 1, None)', '((None, 1, None))'),
87 ('((1, 2), 3, 4)', '(((1, 2), 3, 4))'),
Raymond Hettinger2c31a052004-09-22 18:44:21 +000088 ):
89 asm = dis_single(line)
90 self.assert_(elem in asm)
91 self.assert_('BUILD_TUPLE' not in asm)
92
Raymond Hettinger23109ef2004-10-26 08:59:14 +000093 # Bug 1053819: Tuple of constants misidentified when presented with:
94 # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .
95 # The following would segfault upon compilation
96 def crater():
97 (~[
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 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
104 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
105 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
106 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
107 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
108 ],)
109
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000110 def test_folding_of_binops_on_constants(self):
111 for line, elem in (
112 ('a = 2+3+4', '(9)'), # chained fold
113 ('"@"*4', "('@@@@')"), # check string ops
114 ('a="abc" + "def"', "('abcdef')"), # check string ops
115 ('a = 3**4', '(81)'), # binary power
116 ('a = 3*4', '(12)'), # binary multiply
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000117 ('a = 13//4', '(3)'), # binary floor divide
118 ('a = 14%4', '(2)'), # binary modulo
119 ('a = 2+3', '(5)'), # binary add
120 ('a = 13-4', '(9)'), # binary subtract
121 ('a = (12,13)[1]', '(13)'), # binary subscr
122 ('a = 13 << 2', '(52)'), # binary lshift
123 ('a = 13 >> 2', '(3)'), # binary rshift
124 ('a = 13 & 7', '(5)'), # binary and
125 ('a = 13 ^ 7', '(10)'), # binary xor
126 ('a = 13 | 7', '(15)'), # binary or
127 ):
128 asm = dis_single(line)
129 self.assert_(elem in asm, asm)
130 self.assert_('BINARY_' not in asm)
131
132 # Verify that unfoldables are skipped
133 asm = dis_single('a=2+"b"')
134 self.assert_('(2)' in asm)
135 self.assert_("('b')" in asm)
136
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000137 # Verify that large sequences do not result from folding
138 asm = dis_single('a="x"*1000')
139 self.assert_('(1000)' in asm)
140
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000141 def test_folding_of_unaryops_on_constants(self):
142 for line, elem in (
143 ('`1`', "('1')"), # unary convert
144 ('-0.5', '(-0.5)'), # unary negative
145 ('~-2', '(1)'), # unary invert
146 ):
147 asm = dis_single(line)
148 self.assert_(elem in asm, asm)
149 self.assert_('UNARY_' not in asm)
150
151 # Verify that unfoldables are skipped
152 for line, elem in (
153 ('-"abc"', "('abc')"), # unary negative
154 ('~"abc"', "('abc')"), # unary invert
155 ):
156 asm = dis_single(line)
157 self.assert_(elem in asm, asm)
158 self.assert_('UNARY_' in asm)
159
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000160 def test_elim_extra_return(self):
161 # RETURN LOAD_CONST None RETURN --> RETURN
162 def f(x):
163 return x
164 asm = disassemble(f)
165 self.assert_('LOAD_CONST' not in asm)
166 self.assert_('(None)' not in asm)
167 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
168
Neal Norwitzcbeb6872006-10-14 21:33:38 +0000169 def test_elim_jump_to_return(self):
170 # JUMP_FORWARD to RETURN --> RETURN
171 def f(cond, true_value, false_value):
172 return true_value if cond else false_value
173 asm = disassemble(f)
174 self.assert_('JUMP_FORWARD' not in asm)
175 self.assert_('JUMP_ABSOLUTE' not in asm)
176 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
177
178 def test_elim_jump_after_return1(self):
179 # Eliminate dead code: jumps immediately after returns can't be reached
180 def f(cond1, cond2):
181 if cond1: return 1
182 if cond2: return 2
183 while 1:
184 return 3
185 while 1:
186 if cond1: return 4
187 return 5
188 return 6
189 asm = disassemble(f)
190 self.assert_('JUMP_FORWARD' not in asm)
191 self.assert_('JUMP_ABSOLUTE' not in asm)
192 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
193
194 def test_elim_jump_after_return2(self):
195 # Eliminate dead code: jumps immediately after returns can't be reached
196 def f(cond1, cond2):
197 while 1:
198 if cond1: return 4
199 asm = disassemble(f)
200 self.assert_('JUMP_FORWARD' not in asm)
201 # There should be one jump for the while loop.
202 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
203 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000204
205
206def test_main(verbose=None):
207 import sys
208 from test import test_support
209 test_classes = (TestTranforms,)
210 test_support.run_unittest(*test_classes)
211
212 # verify reference counting
213 if verbose and hasattr(sys, "gettotalrefcount"):
214 import gc
215 counts = [None] * 5
216 for i in xrange(len(counts)):
217 test_support.run_unittest(*test_classes)
218 gc.collect()
219 counts[i] = sys.gettotalrefcount()
220 print counts
221
222if __name__ == "__main__":
223 test_main(verbose=True)