|
1 import dis |
|
2 import sys |
|
3 from cStringIO import StringIO |
|
4 import unittest |
|
5 |
|
6 def 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 |
|
16 def dis_single(line): |
|
17 return disassemble(compile(line, '', 'single')) |
|
18 |
|
19 class 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): |
|
45 None |
|
46 return x |
|
47 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 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)) |
|
57 |
|
58 def test_while_one(self): |
|
59 # Skip over: LOAD_CONST trueconst JUMP_IF_FALSE xx POP_TOP |
|
60 def f(): |
|
61 while 1: |
|
62 pass |
|
63 return list |
|
64 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 ( |
|
72 ('a, = a,', 'LOAD_CONST',), |
|
73 ('a, b = a, b', 'ROT_TWO',), |
|
74 ('a, b, c = a, b, c', 'ROT_THREE',), |
|
75 ): |
|
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 |
|
81 def test_folding_of_tuples_of_constants(self): |
|
82 for line, elem in ( |
|
83 ('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))'), |
|
88 ): |
|
89 asm = dis_single(line) |
|
90 self.assert_(elem in asm) |
|
91 self.assert_('BUILD_TUPLE' not in asm) |
|
92 |
|
93 # 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 |
|
110 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 |
|
117 ('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 |
|
137 # Verify that large sequences do not result from folding |
|
138 asm = dis_single('a="x"*1000') |
|
139 self.assert_('(1000)' in asm) |
|
140 |
|
141 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 |
|
160 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 |
|
169 |
|
170 |
|
171 def test_main(verbose=None): |
|
172 import sys |
|
173 from test import test_support |
|
174 test_classes = (TestTranforms,) |
|
175 test_support.run_unittest(*test_classes) |
|
176 |
|
177 # verify reference counting |
|
178 if verbose and hasattr(sys, "gettotalrefcount"): |
|
179 import gc |
|
180 counts = [None] * 5 |
|
181 for i in xrange(len(counts)): |
|
182 test_support.run_unittest(*test_classes) |
|
183 gc.collect() |
|
184 counts[i] = sys.gettotalrefcount() |
|
185 print counts |
|
186 |
|
187 if __name__ == "__main__": |
|
188 test_main(verbose=True) |