blob: 9e8bb69f67a6f14c52085230028e9dcd170e7cb8 [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):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000022 # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000023 def unot(x):
24 if not x == 2:
25 del x
26 asm = disassemble(unot)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000027 for elem in ('UNARY_NOT', 'POP_JUMP_IF_FALSE'):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000028 self.assertTrue(elem not in asm)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000029 for elem in ('POP_JUMP_IF_TRUE',):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000030 self.assertTrue(elem in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000031
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)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000040 self.assertTrue(elem in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000041
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',):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000057 self.assertTrue(elem not in asm)
Guido van Rossumcd16bf62007-06-13 18:07:49 +000058 for elem in ('LOAD_CONST', '('+name+')'):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000059 self.assertTrue(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
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000063 self.assertTrue('LOAD_CONST' in disassemble(f))
64 self.assertTrue('LOAD_GLOBAL' not in disassemble(f))
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000065
66 def test_while_one(self):
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000067 # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000068 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)
Jeffrey Yasskin9de7ec72009-02-25 02:25:04 +000073 for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000074 self.assertTrue(elem not in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000075 for elem in ('JUMP_ABSOLUTE',):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000076 self.assertTrue(elem in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000077
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)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000085 self.assertTrue(elem in asm)
86 self.assertTrue('BUILD_TUPLE' not in asm)
87 self.assertTrue('UNPACK_TUPLE' not in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +000088
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)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000098 self.assertTrue(elem in asm)
99 self.assertTrue('BUILD_TUPLE' not in asm)
Raymond Hettinger2c31a052004-09-22 18:44:21 +0000100
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)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000137 self.assertTrue(elem in asm, asm)
138 self.assertTrue('BINARY_' not in asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000139
140 # Verify that unfoldables are skipped
141 asm = dis_single('a=2+"b"')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000142 self.assertTrue('(2)' in asm)
143 self.assertTrue("('b')" in asm)
Raymond Hettingerc34f8672005-01-02 06:17:33 +0000144
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000145 # Verify that large sequences do not result from folding
146 asm = dis_single('a="x"*1000')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000147 self.assertTrue('(1000)' in asm)
Raymond Hettinger9feb2672005-01-26 12:50:05 +0000148
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
Raymond Hettingeraf7adad2009-10-22 11:22:50 +0000153 ('+1', '(1)'), # unary positive
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000154 ):
155 asm = dis_single(line)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000156 self.assertTrue(elem in asm, asm)
157 self.assertTrue('UNARY_' not in asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000158
159 # Verify that unfoldables are skipped
160 for line, elem in (
161 ('-"abc"', "('abc')"), # unary negative
162 ('~"abc"', "('abc')"), # unary invert
163 ):
164 asm = dis_single(line)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000165 self.assertTrue(elem in asm, asm)
166 self.assertTrue('UNARY_' in asm)
Raymond Hettingerafd842f2005-02-20 12:46:54 +0000167
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000168 def test_elim_extra_return(self):
169 # RETURN LOAD_CONST None RETURN --> RETURN
170 def f(x):
171 return x
172 asm = disassemble(f)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000173 self.assertTrue('LOAD_CONST' not in asm)
174 self.assertTrue('(None)' not in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000175 self.assertEqual(asm.split().count('RETURN_VALUE'), 1)
176
Thomas Wouters89f507f2006-12-13 04:49:30 +0000177 def test_elim_jump_to_return(self):
178 # JUMP_FORWARD to RETURN --> RETURN
179 def f(cond, true_value, false_value):
180 return true_value if cond else false_value
181 asm = disassemble(f)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000182 self.assertTrue('JUMP_FORWARD' not in asm)
183 self.assertTrue('JUMP_ABSOLUTE' not in asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000184 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
185
186 def test_elim_jump_after_return1(self):
187 # Eliminate dead code: jumps immediately after returns can't be reached
188 def f(cond1, cond2):
189 if cond1: return 1
190 if cond2: return 2
191 while 1:
192 return 3
193 while 1:
194 if cond1: return 4
195 return 5
196 return 6
197 asm = disassemble(f)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000198 self.assertTrue('JUMP_FORWARD' not in asm)
199 self.assertTrue('JUMP_ABSOLUTE' not in asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000200 self.assertEqual(asm.split().count('RETURN_VALUE'), 6)
201
202 def test_elim_jump_after_return2(self):
203 # Eliminate dead code: jumps immediately after returns can't be reached
204 def f(cond1, cond2):
205 while 1:
206 if cond1: return 4
207 asm = disassemble(f)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000208 self.assertTrue('JUMP_FORWARD' not in asm)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000209 # There should be one jump for the while loop.
210 self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1)
211 self.assertEqual(asm.split().count('RETURN_VALUE'), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000212
Guido van Rossum0240b922007-02-26 21:23:50 +0000213 def test_make_function_doesnt_bail(self):
214 def f():
Guido van Rossumd8faa362007-04-27 19:54:29 +0000215 def g()->1+1:
Guido van Rossum0240b922007-02-26 21:23:50 +0000216 pass
217 return g
218 asm = disassemble(f)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000219 self.assertTrue('BINARY_ADD' not in asm)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000220
221
222def test_main(verbose=None):
223 import sys
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000224 from test import support
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000225 test_classes = (TestTranforms,)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000226 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000227
228 # verify reference counting
229 if verbose and hasattr(sys, "gettotalrefcount"):
230 import gc
231 counts = [None] * 5
Guido van Rossum805365e2007-05-07 22:24:25 +0000232 for i in range(len(counts)):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000233 support.run_unittest(*test_classes)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000234 gc.collect()
235 counts[i] = sys.gettotalrefcount()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000236 print(counts)
Raymond Hettingerfd2d1f72004-08-23 23:37:48 +0000237
238if __name__ == "__main__":
239 test_main(verbose=True)