]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | import dis\r |
2 | import sys\r | |
3 | from cStringIO import StringIO\r | |
4 | import unittest\r | |
5 | \r | |
6 | def disassemble(func):\r | |
7 | f = StringIO()\r | |
8 | tmp = sys.stdout\r | |
9 | sys.stdout = f\r | |
10 | dis.dis(func)\r | |
11 | sys.stdout = tmp\r | |
12 | result = f.getvalue()\r | |
13 | f.close()\r | |
14 | return result\r | |
15 | \r | |
16 | def dis_single(line):\r | |
17 | return disassemble(compile(line, '', 'single'))\r | |
18 | \r | |
19 | class TestTranforms(unittest.TestCase):\r | |
20 | \r | |
21 | def test_unot(self):\r | |
22 | # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE\r | |
23 | def unot(x):\r | |
24 | if not x == 2:\r | |
25 | del x\r | |
26 | asm = disassemble(unot)\r | |
27 | for elem in ('UNARY_NOT', 'POP_JUMP_IF_FALSE'):\r | |
28 | self.assertNotIn(elem, asm)\r | |
29 | self.assertIn('POP_JUMP_IF_TRUE', asm)\r | |
30 | \r | |
31 | def test_elim_inversion_of_is_or_in(self):\r | |
32 | for line, elem in (\r | |
33 | ('not a is b', '(is not)',),\r | |
34 | ('not a in b', '(not in)',),\r | |
35 | ('not a is not b', '(is)',),\r | |
36 | ('not a not in b', '(in)',),\r | |
37 | ):\r | |
38 | asm = dis_single(line)\r | |
39 | self.assertIn(elem, asm)\r | |
40 | \r | |
41 | def test_none_as_constant(self):\r | |
42 | # LOAD_GLOBAL None --> LOAD_CONST None\r | |
43 | def f(x):\r | |
44 | None\r | |
45 | return x\r | |
46 | asm = disassemble(f)\r | |
47 | for elem in ('LOAD_GLOBAL',):\r | |
48 | self.assertNotIn(elem, asm)\r | |
49 | for elem in ('LOAD_CONST', '(None)'):\r | |
50 | self.assertIn(elem, asm)\r | |
51 | def f():\r | |
52 | 'Adding a docstring made this test fail in Py2.5.0'\r | |
53 | return None\r | |
54 | self.assertIn('LOAD_CONST', disassemble(f))\r | |
55 | self.assertNotIn('LOAD_GLOBAL', disassemble(f))\r | |
56 | \r | |
57 | def test_while_one(self):\r | |
58 | # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx\r | |
59 | def f():\r | |
60 | while 1:\r | |
61 | pass\r | |
62 | return list\r | |
63 | asm = disassemble(f)\r | |
64 | for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):\r | |
65 | self.assertNotIn(elem, asm)\r | |
66 | for elem in ('JUMP_ABSOLUTE',):\r | |
67 | self.assertIn(elem, asm)\r | |
68 | \r | |
69 | def test_pack_unpack(self):\r | |
70 | for line, elem in (\r | |
71 | ('a, = a,', 'LOAD_CONST',),\r | |
72 | ('a, b = a, b', 'ROT_TWO',),\r | |
73 | ('a, b, c = a, b, c', 'ROT_THREE',),\r | |
74 | ):\r | |
75 | asm = dis_single(line)\r | |
76 | self.assertIn(elem, asm)\r | |
77 | self.assertNotIn('BUILD_TUPLE', asm)\r | |
78 | self.assertNotIn('UNPACK_TUPLE', asm)\r | |
79 | \r | |
80 | def test_folding_of_tuples_of_constants(self):\r | |
81 | for line, elem in (\r | |
82 | ('a = 1,2,3', '((1, 2, 3))'),\r | |
83 | ('("a","b","c")', "(('a', 'b', 'c'))"),\r | |
84 | ('a,b,c = 1,2,3', '((1, 2, 3))'),\r | |
85 | ('(None, 1, None)', '((None, 1, None))'),\r | |
86 | ('((1, 2), 3, 4)', '(((1, 2), 3, 4))'),\r | |
87 | ):\r | |
88 | asm = dis_single(line)\r | |
89 | self.assertIn(elem, asm)\r | |
90 | self.assertNotIn('BUILD_TUPLE', asm)\r | |
91 | \r | |
92 | # Bug 1053819: Tuple of constants misidentified when presented with:\r | |
93 | # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .\r | |
94 | # The following would segfault upon compilation\r | |
95 | def crater():\r | |
96 | (~[\r | |
97 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\r | |
98 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\r | |
99 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\r | |
100 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\r | |
101 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\r | |
102 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\r | |
103 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\r | |
104 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\r | |
105 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\r | |
106 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\r | |
107 | ],)\r | |
108 | \r | |
109 | def test_folding_of_binops_on_constants(self):\r | |
110 | for line, elem in (\r | |
111 | ('a = 2+3+4', '(9)'), # chained fold\r | |
112 | ('"@"*4', "('@@@@')"), # check string ops\r | |
113 | ('a="abc" + "def"', "('abcdef')"), # check string ops\r | |
114 | ('a = 3**4', '(81)'), # binary power\r | |
115 | ('a = 3*4', '(12)'), # binary multiply\r | |
116 | ('a = 13//4', '(3)'), # binary floor divide\r | |
117 | ('a = 14%4', '(2)'), # binary modulo\r | |
118 | ('a = 2+3', '(5)'), # binary add\r | |
119 | ('a = 13-4', '(9)'), # binary subtract\r | |
120 | ('a = (12,13)[1]', '(13)'), # binary subscr\r | |
121 | ('a = 13 << 2', '(52)'), # binary lshift\r | |
122 | ('a = 13 >> 2', '(3)'), # binary rshift\r | |
123 | ('a = 13 & 7', '(5)'), # binary and\r | |
124 | ('a = 13 ^ 7', '(10)'), # binary xor\r | |
125 | ('a = 13 | 7', '(15)'), # binary or\r | |
126 | ):\r | |
127 | asm = dis_single(line)\r | |
128 | self.assertIn(elem, asm, asm)\r | |
129 | self.assertNotIn('BINARY_', asm)\r | |
130 | \r | |
131 | # Verify that unfoldables are skipped\r | |
132 | asm = dis_single('a=2+"b"')\r | |
133 | self.assertIn('(2)', asm)\r | |
134 | self.assertIn("('b')", asm)\r | |
135 | \r | |
136 | # Verify that large sequences do not result from folding\r | |
137 | asm = dis_single('a="x"*1000')\r | |
138 | self.assertIn('(1000)', asm)\r | |
139 | \r | |
140 | def test_binary_subscr_on_unicode(self):\r | |
141 | # valid code get optimized\r | |
142 | asm = dis_single('u"foo"[0]')\r | |
143 | self.assertIn("(u'f')", asm)\r | |
144 | self.assertNotIn('BINARY_SUBSCR', asm)\r | |
145 | asm = dis_single('u"\u0061\uffff"[1]')\r | |
146 | self.assertIn("(u'\\uffff')", asm)\r | |
147 | self.assertNotIn('BINARY_SUBSCR', asm)\r | |
148 | \r | |
149 | # invalid code doesn't get optimized\r | |
150 | # out of range\r | |
151 | asm = dis_single('u"fuu"[10]')\r | |
152 | self.assertIn('BINARY_SUBSCR', asm)\r | |
153 | # non-BMP char (see #5057)\r | |
154 | asm = dis_single('u"\U00012345"[0]')\r | |
155 | self.assertIn('BINARY_SUBSCR', asm)\r | |
156 | \r | |
157 | \r | |
158 | def test_folding_of_unaryops_on_constants(self):\r | |
159 | for line, elem in (\r | |
160 | ('`1`', "('1')"), # unary convert\r | |
161 | ('-0.5', '(-0.5)'), # unary negative\r | |
162 | ('~-2', '(1)'), # unary invert\r | |
163 | ):\r | |
164 | asm = dis_single(line)\r | |
165 | self.assertIn(elem, asm, asm)\r | |
166 | self.assertNotIn('UNARY_', asm)\r | |
167 | \r | |
168 | # Verify that unfoldables are skipped\r | |
169 | for line, elem in (\r | |
170 | ('-"abc"', "('abc')"), # unary negative\r | |
171 | ('~"abc"', "('abc')"), # unary invert\r | |
172 | ):\r | |
173 | asm = dis_single(line)\r | |
174 | self.assertIn(elem, asm, asm)\r | |
175 | self.assertIn('UNARY_', asm)\r | |
176 | \r | |
177 | def test_elim_extra_return(self):\r | |
178 | # RETURN LOAD_CONST None RETURN --> RETURN\r | |
179 | def f(x):\r | |
180 | return x\r | |
181 | asm = disassemble(f)\r | |
182 | self.assertNotIn('LOAD_CONST', asm)\r | |
183 | self.assertNotIn('(None)', asm)\r | |
184 | self.assertEqual(asm.split().count('RETURN_VALUE'), 1)\r | |
185 | \r | |
186 | def test_elim_jump_to_return(self):\r | |
187 | # JUMP_FORWARD to RETURN --> RETURN\r | |
188 | def f(cond, true_value, false_value):\r | |
189 | return true_value if cond else false_value\r | |
190 | asm = disassemble(f)\r | |
191 | self.assertNotIn('JUMP_FORWARD', asm)\r | |
192 | self.assertNotIn('JUMP_ABSOLUTE', asm)\r | |
193 | self.assertEqual(asm.split().count('RETURN_VALUE'), 2)\r | |
194 | \r | |
195 | def test_elim_jump_after_return1(self):\r | |
196 | # Eliminate dead code: jumps immediately after returns can't be reached\r | |
197 | def f(cond1, cond2):\r | |
198 | if cond1: return 1\r | |
199 | if cond2: return 2\r | |
200 | while 1:\r | |
201 | return 3\r | |
202 | while 1:\r | |
203 | if cond1: return 4\r | |
204 | return 5\r | |
205 | return 6\r | |
206 | asm = disassemble(f)\r | |
207 | self.assertNotIn('JUMP_FORWARD', asm)\r | |
208 | self.assertNotIn('JUMP_ABSOLUTE', asm)\r | |
209 | self.assertEqual(asm.split().count('RETURN_VALUE'), 6)\r | |
210 | \r | |
211 | def test_elim_jump_after_return2(self):\r | |
212 | # Eliminate dead code: jumps immediately after returns can't be reached\r | |
213 | def f(cond1, cond2):\r | |
214 | while 1:\r | |
215 | if cond1: return 4\r | |
216 | asm = disassemble(f)\r | |
217 | self.assertNotIn('JUMP_FORWARD', asm)\r | |
218 | # There should be one jump for the while loop.\r | |
219 | self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)\r | |
220 | self.assertEqual(asm.split().count('RETURN_VALUE'), 2)\r | |
221 | \r | |
222 | \r | |
223 | def test_main(verbose=None):\r | |
224 | import sys\r | |
225 | from test import test_support\r | |
226 | test_classes = (TestTranforms,)\r | |
227 | \r | |
228 | with test_support.check_py3k_warnings(\r | |
229 | ("backquote not supported", SyntaxWarning)):\r | |
230 | test_support.run_unittest(*test_classes)\r | |
231 | \r | |
232 | # verify reference counting\r | |
233 | if verbose and hasattr(sys, "gettotalrefcount"):\r | |
234 | import gc\r | |
235 | counts = [None] * 5\r | |
236 | for i in xrange(len(counts)):\r | |
237 | test_support.run_unittest(*test_classes)\r | |
238 | gc.collect()\r | |
239 | counts[i] = sys.gettotalrefcount()\r | |
240 | print counts\r | |
241 | \r | |
242 | if __name__ == "__main__":\r | |
243 | test_main(verbose=True)\r |