blob: 0547eeb068a11af4a978bce9db3bbdba2bcb38c0 [file] [log] [blame]
Jeremy Hylton36cc6a22000-03-16 20:06:59 +00001"""A flow graph representation for Python bytecode"""
Jeremy Hyltona5058122000-02-14 14:14:29 +00002
Jeremy Hyltona5058122000-02-14 14:14:29 +00003import dis
4import new
Jeremy Hylton314e3fb2000-11-06 03:43:11 +00005import sys
Jeremy Hylton36cc6a22000-03-16 20:06:59 +00006import types
Jeremy Hyltona5058122000-02-14 14:14:29 +00007
Jeremy Hylton36cc6a22000-03-16 20:06:59 +00008from compiler import misc
Jeremy Hyltonaccb62b2002-12-31 18:17:44 +00009from compiler.consts \
10 import CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000011
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000012class FlowGraph:
13 def __init__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000014 self.current = self.entry = Block()
15 self.exit = Block("exit")
16 self.blocks = misc.Set()
17 self.blocks.add(self.entry)
18 self.blocks.add(self.exit)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000019
20 def startBlock(self, block):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000021 if self._debug:
22 if self.current:
23 print "end", repr(self.current)
Jeremy Hylton542b11a2001-04-12 20:21:39 +000024 print " next", self.current.next
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000025 print " ", self.current.get_children()
26 print repr(block)
Thomas Wouters46cc7c02000-08-12 20:32:46 +000027 self.current = block
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000028
Jeremy Hylton542b11a2001-04-12 20:21:39 +000029 def nextBlock(self, block=None):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000030 # XXX think we need to specify when there is implicit transfer
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000031 # from one block to the next. might be better to represent this
32 # with explicit JUMP_ABSOLUTE instructions that are optimized
33 # out when they are unnecessary.
Thomas Wouters46cc7c02000-08-12 20:32:46 +000034 #
35 # I think this strategy works: each block has a child
36 # designated as "next" which is returned as the last of the
37 # children. because the nodes in a graph are emitted in
38 # reverse post order, the "next" block will always be emitted
39 # immediately after its parent.
40 # Worry: maintaining this invariant could be tricky
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000041 if block is None:
42 block = self.newBlock()
43
44 # Note: If the current block ends with an unconditional
45 # control transfer, then it is incorrect to add an implicit
46 # transfer to the block graph. The current code requires
47 # these edges to get the blocks emitted in the right order,
48 # however. :-( If a client needs to remove these edges, call
49 # pruneEdges().
Tim Peterse0c446b2001-10-18 21:57:37 +000050
Thomas Wouters46cc7c02000-08-12 20:32:46 +000051 self.current.addNext(block)
52 self.startBlock(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000053
54 def newBlock(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000055 b = Block()
56 self.blocks.add(b)
57 return b
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000058
59 def startExitBlock(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000060 self.startBlock(self.exit)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000061
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000062 _debug = 0
63
64 def _enable_debug(self):
65 self._debug = 1
66
67 def _disable_debug(self):
68 self._debug = 0
69
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000070 def emit(self, *inst):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000071 if self._debug:
72 print "\t", inst
Jeremy Hyltonaccb62b2002-12-31 18:17:44 +000073 if inst[0] in ['RETURN_VALUE', 'YIELD_VALUE']:
Thomas Wouters46cc7c02000-08-12 20:32:46 +000074 self.current.addOutEdge(self.exit)
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000075 if len(inst) == 2 and isinstance(inst[1], Block):
76 self.current.addOutEdge(inst[1])
Thomas Wouters46cc7c02000-08-12 20:32:46 +000077 self.current.emit(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000078
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000079 def getBlocksInOrder(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000080 """Return the blocks in reverse postorder
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000081
Thomas Wouters46cc7c02000-08-12 20:32:46 +000082 i.e. each node appears before all of its successors
83 """
84 # XXX make sure every node that doesn't have an explicit next
85 # is set so that next points to exit
86 for b in self.blocks.elements():
87 if b is self.exit:
88 continue
89 if not b.next:
90 b.addNext(self.exit)
91 order = dfs_postorder(self.entry, {})
92 order.reverse()
Jeremy Hylton542b11a2001-04-12 20:21:39 +000093 self.fixupOrder(order, self.exit)
Thomas Wouters46cc7c02000-08-12 20:32:46 +000094 # hack alert
95 if not self.exit in order:
96 order.append(self.exit)
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000097
Thomas Wouters46cc7c02000-08-12 20:32:46 +000098 return order
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000099
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000100 def fixupOrder(self, blocks, default_next):
101 """Fixup bad order introduced by DFS."""
102
103 # XXX This is a total mess. There must be a better way to get
104 # the code blocks in the right order.
Tim Peterse0c446b2001-10-18 21:57:37 +0000105
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000106 self.fixupOrderHonorNext(blocks, default_next)
107 self.fixupOrderForward(blocks, default_next)
108
109 def fixupOrderHonorNext(self, blocks, default_next):
110 """Fix one problem with DFS.
Tim Peterse0c446b2001-10-18 21:57:37 +0000111
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000112 The DFS uses child block, but doesn't know about the special
113 "next" block. As a result, the DFS can order blocks so that a
114 block isn't next to the right block for implicit control
115 transfers.
116 """
117 index = {}
118 for i in range(len(blocks)):
119 index[blocks[i]] = i
120
121 for i in range(0, len(blocks) - 1):
122 b = blocks[i]
123 n = blocks[i + 1]
124 if not b.next or b.next[0] == default_next or b.next[0] == n:
125 continue
126 # The blocks are in the wrong order. Find the chain of
127 # blocks to insert where they belong.
128 cur = b
129 chain = []
130 elt = cur
131 while elt.next and elt.next[0] != default_next:
132 chain.append(elt.next[0])
133 elt = elt.next[0]
134 # Now remove the blocks in the chain from the current
135 # block list, so that they can be re-inserted.
136 l = []
137 for b in chain:
138 assert index[b] > i
139 l.append((index[b], b))
140 l.sort()
141 l.reverse()
142 for j, b in l:
143 del blocks[index[b]]
144 # Insert the chain in the proper location
145 blocks[i:i + 1] = [cur] + chain
146 # Finally, re-compute the block indexes
147 for i in range(len(blocks)):
148 index[blocks[i]] = i
149
150 def fixupOrderForward(self, blocks, default_next):
151 """Make sure all JUMP_FORWARDs jump forward"""
152 index = {}
153 chains = []
154 cur = []
155 for b in blocks:
156 index[b] = len(chains)
157 cur.append(b)
158 if b.next and b.next[0] == default_next:
159 chains.append(cur)
160 cur = []
161 chains.append(cur)
162
163 while 1:
164 constraints = []
165
166 for i in range(len(chains)):
167 l = chains[i]
168 for b in l:
169 for c in b.get_children():
170 if index[c] < i:
171 forward_p = 0
172 for inst in b.insts:
173 if inst[0] == 'JUMP_FORWARD':
174 if inst[1] == c:
175 forward_p = 1
176 if not forward_p:
177 continue
178 constraints.append((index[c], i))
179
180 if not constraints:
181 break
182
183 # XXX just do one for now
184 # do swaps to get things in the right order
185 goes_before, a_chain = constraints[0]
186 assert a_chain > goes_before
187 c = chains[a_chain]
188 chains.remove(c)
189 chains.insert(goes_before, c)
190
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000191 del blocks[:]
192 for c in chains:
193 for b in c:
194 blocks.append(b)
Tim Peterse0c446b2001-10-18 21:57:37 +0000195
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000196 def getBlocks(self):
197 return self.blocks.elements()
198
199 def getRoot(self):
200 """Return nodes appropriate for use with dominator"""
201 return self.entry
Tim Peterse0c446b2001-10-18 21:57:37 +0000202
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000203 def getContainedGraphs(self):
204 l = []
205 for b in self.getBlocks():
206 l.extend(b.getContainedGraphs())
207 return l
208
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000209def dfs_postorder(b, seen):
210 """Depth-first search of tree rooted at b, return in postorder"""
211 order = []
212 seen[b] = b
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000213 for c in b.get_children():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000214 if seen.has_key(c):
215 continue
216 order = order + dfs_postorder(c, seen)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000217 order.append(b)
218 return order
219
220class Block:
221 _count = 0
222
223 def __init__(self, label=''):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000224 self.insts = []
225 self.inEdges = misc.Set()
226 self.outEdges = misc.Set()
227 self.label = label
228 self.bid = Block._count
229 self.next = []
230 Block._count = Block._count + 1
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000231
232 def __repr__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000233 if self.label:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000234 return "<block %s id=%d>" % (self.label, self.bid)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000235 else:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000236 return "<block id=%d>" % (self.bid)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000237
238 def __str__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000239 insts = map(str, self.insts)
240 return "<block %s %d:\n%s>" % (self.label, self.bid,
Neal Norwitza312c3a2002-06-06 18:30:10 +0000241 '\n'.join(insts))
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000242
243 def emit(self, inst):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000244 op = inst[0]
245 if op[:4] == 'JUMP':
246 self.outEdges.add(inst[1])
247 self.insts.append(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000248
249 def getInstructions(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000250 return self.insts
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000251
252 def addInEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000253 self.inEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000254
255 def addOutEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000256 self.outEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000257
258 def addNext(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000259 self.next.append(block)
260 assert len(self.next) == 1, map(str, self.next)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000261
Jeremy Hyltonaccb62b2002-12-31 18:17:44 +0000262 _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS', 'YIELD_VALUE',
Jeremy Hylton92638482001-08-29 22:27:14 +0000263 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP')
Jeremy Hyltoneefaeb72000-11-06 03:47:39 +0000264
265 def pruneNext(self):
266 """Remove bogus edge for unconditional transfers
267
268 Each block has a next edge that accounts for implicit control
269 transfers, e.g. from a JUMP_IF_FALSE to the block that will be
270 executed if the test is true.
271
272 These edges must remain for the current assembler code to
273 work. If they are removed, the dfs_postorder gets things in
274 weird orders. However, they shouldn't be there for other
275 purposes, e.g. conversion to SSA form. This method will
276 remove the next edge when it follows an unconditional control
277 transfer.
278 """
279 try:
280 op, arg = self.insts[-1]
281 except (IndexError, ValueError):
282 return
283 if op in self._uncond_transfer:
284 self.next = []
285
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000286 def get_children(self):
287 if self.next and self.next[0] in self.outEdges:
288 self.outEdges.remove(self.next[0])
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000289 return self.outEdges.elements() + self.next
Jeremy Hyltona5058122000-02-14 14:14:29 +0000290
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000291 def getContainedGraphs(self):
292 """Return all graphs contained within this block.
293
294 For example, a MAKE_FUNCTION block will contain a reference to
295 the graph for the function body.
296 """
297 contained = []
298 for inst in self.insts:
299 if len(inst) == 1:
300 continue
301 op = inst[1]
302 if hasattr(op, 'graph'):
303 contained.append(op.graph)
304 return contained
305
Jeremy Hyltona5058122000-02-14 14:14:29 +0000306# flags for code objects
Jeremy Hyltona5058122000-02-14 14:14:29 +0000307
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000308# the FlowGraph is transformed in place; it exists in one of these states
309RAW = "RAW"
310FLAT = "FLAT"
311CONV = "CONV"
312DONE = "DONE"
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000313
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000314class PyFlowGraph(FlowGraph):
315 super_init = FlowGraph.__init__
Jeremy Hyltona5058122000-02-14 14:14:29 +0000316
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000317 def __init__(self, name, filename, args=(), optimized=0, klass=None):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000318 self.super_init()
319 self.name = name
320 self.filename = filename
321 self.docstring = None
322 self.args = args # XXX
323 self.argcount = getArgCount(args)
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000324 self.klass = klass
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000325 if optimized:
Tim Peterse0c446b2001-10-18 21:57:37 +0000326 self.flags = CO_OPTIMIZED | CO_NEWLOCALS
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000327 else:
328 self.flags = 0
329 self.consts = []
330 self.names = []
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000331 # Free variables found by the symbol table scan, including
332 # variables used only in nested scopes, are included here.
333 self.freevars = []
334 self.cellvars = []
335 # The closure list is used to track the order of cell
336 # variables and free variables in the resulting code object.
337 # The offsets used by LOAD_CLOSURE/LOAD_DEREF refer to both
338 # kinds of variables.
339 self.closure = []
Jeremy Hyltona5058122000-02-14 14:14:29 +0000340 self.varnames = list(args) or []
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000341 for i in range(len(self.varnames)):
342 var = self.varnames[i]
343 if isinstance(var, TupleArg):
344 self.varnames[i] = var.getName()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000345 self.stage = RAW
Jeremy Hyltona5058122000-02-14 14:14:29 +0000346
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000347 def setDocstring(self, doc):
348 self.docstring = doc
Jeremy Hylton2ce27b22000-02-16 00:50:29 +0000349
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000350 def setFlag(self, flag):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000351 self.flags = self.flags | flag
352 if flag == CO_VARARGS:
353 self.argcount = self.argcount - 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000354
Jeremy Hylton1e99a772001-09-14 22:49:08 +0000355 def checkFlag(self, flag):
356 if self.flags & flag:
357 return 1
358
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000359 def setFreeVars(self, names):
360 self.freevars = list(names)
361
362 def setCellVars(self, names):
363 self.cellvars = names
364
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000365 def getCode(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000366 """Get a Python code object"""
367 if self.stage == RAW:
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000368 self.computeStackDepth()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000369 self.flattenGraph()
370 if self.stage == FLAT:
371 self.convertArgs()
372 if self.stage == CONV:
373 self.makeByteCode()
374 if self.stage == DONE:
375 return self.newCodeObject()
376 raise RuntimeError, "inconsistent PyFlowGraph state"
Jeremy Hyltona5058122000-02-14 14:14:29 +0000377
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000378 def dump(self, io=None):
379 if io:
380 save = sys.stdout
381 sys.stdout = io
382 pc = 0
Jeremy Hyltona5058122000-02-14 14:14:29 +0000383 for t in self.insts:
384 opname = t[0]
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000385 if opname == "SET_LINENO":
386 print
Jeremy Hyltona5058122000-02-14 14:14:29 +0000387 if len(t) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000388 print "\t", "%3d" % pc, opname
389 pc = pc + 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000390 else:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000391 print "\t", "%3d" % pc, opname, t[1]
392 pc = pc + 3
393 if io:
394 sys.stdout = save
Jeremy Hyltona5058122000-02-14 14:14:29 +0000395
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000396 def computeStackDepth(self):
397 """Compute the max stack depth.
398
399 Approach is to compute the stack effect of each basic block.
400 Then find the path through the code with the largest total
401 effect.
402 """
403 depth = {}
404 exit = None
405 for b in self.getBlocks():
406 depth[b] = findDepth(b.getInstructions())
407
408 seen = {}
409
410 def max_depth(b, d):
411 if seen.has_key(b):
412 return d
413 seen[b] = 1
414 d = d + depth[b]
415 children = b.get_children()
416 if children:
417 return max([max_depth(c, d) for c in children])
418 else:
419 if not b.label == "exit":
420 return max_depth(self.exit, d)
421 else:
422 return d
423
424 self.stacksize = max_depth(self.entry, 0)
425
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000426 def flattenGraph(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000427 """Arrange the blocks in order and resolve jumps"""
428 assert self.stage == RAW
429 self.insts = insts = []
430 pc = 0
431 begin = {}
432 end = {}
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000433 for b in self.getBlocksInOrder():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000434 begin[b] = pc
435 for inst in b.getInstructions():
436 insts.append(inst)
437 if len(inst) == 1:
438 pc = pc + 1
Jeremy Hyltonaccb62b2002-12-31 18:17:44 +0000439 elif inst[0] != "SET_LINENO":
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000440 # arg takes 2 bytes
441 pc = pc + 3
442 end[b] = pc
443 pc = 0
444 for i in range(len(insts)):
445 inst = insts[i]
446 if len(inst) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000447 pc = pc + 1
Jeremy Hyltonaccb62b2002-12-31 18:17:44 +0000448 elif inst[0] != "SET_LINENO":
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000449 pc = pc + 3
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000450 opname = inst[0]
451 if self.hasjrel.has_elt(opname):
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000452 oparg = inst[1]
453 offset = begin[oparg] - pc
454 insts[i] = opname, offset
455 elif self.hasjabs.has_elt(opname):
456 insts[i] = opname, begin[inst[1]]
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000457 self.stage = FLAT
Jeremy Hyltona5058122000-02-14 14:14:29 +0000458
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000459 hasjrel = misc.Set()
460 for i in dis.hasjrel:
461 hasjrel.add(dis.opname[i])
462 hasjabs = misc.Set()
463 for i in dis.hasjabs:
464 hasjabs.add(dis.opname[i])
Jeremy Hyltona5058122000-02-14 14:14:29 +0000465
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000466 def convertArgs(self):
467 """Convert arguments from symbolic to concrete form"""
468 assert self.stage == FLAT
Jeremy Hylton5af105e2001-04-11 16:21:51 +0000469 self.consts.insert(0, self.docstring)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000470 self.sort_cellvars()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000471 for i in range(len(self.insts)):
472 t = self.insts[i]
473 if len(t) == 2:
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000474 opname, oparg = t
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000475 conv = self._converters.get(opname, None)
476 if conv:
477 self.insts[i] = opname, conv(self, oparg)
478 self.stage = CONV
Jeremy Hyltona5058122000-02-14 14:14:29 +0000479
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000480 def sort_cellvars(self):
481 """Sort cellvars in the order of varnames and prune from freevars.
482 """
483 cells = {}
484 for name in self.cellvars:
485 cells[name] = 1
486 self.cellvars = [name for name in self.varnames
487 if cells.has_key(name)]
488 for name in self.cellvars:
489 del cells[name]
490 self.cellvars = self.cellvars + cells.keys()
491 self.closure = self.cellvars + self.freevars
492
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000493 def _lookupName(self, name, list):
494 """Return index of name in list, appending if necessary
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000495
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000496 This routine uses a list instead of a dictionary, because a
497 dictionary can't store two different keys if the keys have the
498 same value but different types, e.g. 2 and 2L. The compiler
499 must treat these two separately, so it does an explicit type
500 comparison before comparing the values.
501 """
Jeremy Hylton9c048f92000-10-13 21:58:13 +0000502 t = type(name)
503 for i in range(len(list)):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000504 if t == type(list[i]) and list[i] == name:
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000505 return i
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000506 end = len(list)
507 list.append(name)
508 return end
Jeremy Hyltona5058122000-02-14 14:14:29 +0000509
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000510 _converters = {}
511 def _convert_LOAD_CONST(self, arg):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000512 if hasattr(arg, 'getCode'):
513 arg = arg.getCode()
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000514 return self._lookupName(arg, self.consts)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000515
516 def _convert_LOAD_FAST(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000517 self._lookupName(arg, self.names)
518 return self._lookupName(arg, self.varnames)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000519 _convert_STORE_FAST = _convert_LOAD_FAST
520 _convert_DELETE_FAST = _convert_LOAD_FAST
521
Jeremy Hylton63db7b92001-08-28 16:36:12 +0000522 def _convert_LOAD_NAME(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000523 if self.klass is None:
524 self._lookupName(arg, self.varnames)
525 return self._lookupName(arg, self.names)
Jeremy Hylton63db7b92001-08-28 16:36:12 +0000526
527 def _convert_NAME(self, arg):
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000528 if self.klass is None:
529 self._lookupName(arg, self.varnames)
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000530 return self._lookupName(arg, self.names)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000531 _convert_STORE_NAME = _convert_NAME
532 _convert_DELETE_NAME = _convert_NAME
533 _convert_IMPORT_NAME = _convert_NAME
534 _convert_IMPORT_FROM = _convert_NAME
535 _convert_STORE_ATTR = _convert_NAME
536 _convert_LOAD_ATTR = _convert_NAME
537 _convert_DELETE_ATTR = _convert_NAME
538 _convert_LOAD_GLOBAL = _convert_NAME
539 _convert_STORE_GLOBAL = _convert_NAME
540 _convert_DELETE_GLOBAL = _convert_NAME
541
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000542 def _convert_DEREF(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000543 self._lookupName(arg, self.names)
544 self._lookupName(arg, self.varnames)
545 return self._lookupName(arg, self.closure)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000546 _convert_LOAD_DEREF = _convert_DEREF
547 _convert_STORE_DEREF = _convert_DEREF
548
549 def _convert_LOAD_CLOSURE(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000550 self._lookupName(arg, self.varnames)
551 return self._lookupName(arg, self.closure)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000552
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000553 _cmp = list(dis.cmp_op)
554 def _convert_COMPARE_OP(self, arg):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000555 return self._cmp.index(arg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000556
557 # similarly for other opcodes...
558
559 for name, obj in locals().items():
560 if name[:9] == "_convert_":
561 opname = name[9:]
Tim Peterse0c446b2001-10-18 21:57:37 +0000562 _converters[opname] = obj
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000563 del name, obj, opname
564
565 def makeByteCode(self):
566 assert self.stage == CONV
567 self.lnotab = lnotab = LineAddrTable()
568 for t in self.insts:
569 opname = t[0]
570 if len(t) == 1:
571 lnotab.addCode(self.opnum[opname])
572 else:
573 oparg = t[1]
574 if opname == "SET_LINENO":
575 lnotab.nextLine(oparg)
Jeremy Hyltonaccb62b2002-12-31 18:17:44 +0000576 continue
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000577 hi, lo = twobyte(oparg)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000578 try:
579 lnotab.addCode(self.opnum[opname], lo, hi)
580 except ValueError:
581 print opname, oparg
582 print self.opnum[opname], lo, hi
583 raise
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000584 self.stage = DONE
585
Jeremy Hyltona5058122000-02-14 14:14:29 +0000586 opnum = {}
587 for num in range(len(dis.opname)):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000588 opnum[dis.opname[num]] = num
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000589 del num
Jeremy Hyltona5058122000-02-14 14:14:29 +0000590
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000591 def newCodeObject(self):
592 assert self.stage == DONE
Jeremy Hylton1e99a772001-09-14 22:49:08 +0000593 if (self.flags & CO_NEWLOCALS) == 0:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000594 nlocals = 0
595 else:
596 nlocals = len(self.varnames)
597 argcount = self.argcount
598 if self.flags & CO_VARKEYWORDS:
599 argcount = argcount - 1
600 return new.code(argcount, nlocals, self.stacksize, self.flags,
601 self.lnotab.getCode(), self.getConsts(),
602 tuple(self.names), tuple(self.varnames),
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000603 self.filename, self.name, self.lnotab.firstline,
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000604 self.lnotab.getTable(), tuple(self.freevars),
605 tuple(self.cellvars))
Jeremy Hyltona5058122000-02-14 14:14:29 +0000606
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000607 def getConsts(self):
608 """Return a tuple for the const slot of the code object
Jeremy Hyltona5058122000-02-14 14:14:29 +0000609
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000610 Must convert references to code (MAKE_FUNCTION) to code
611 objects recursively.
612 """
613 l = []
614 for elt in self.consts:
615 if isinstance(elt, PyFlowGraph):
616 elt = elt.getCode()
617 l.append(elt)
618 return tuple(l)
Tim Peterse0c446b2001-10-18 21:57:37 +0000619
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000620def isJump(opname):
621 if opname[:4] == 'JUMP':
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000622 return 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000623
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000624class TupleArg:
625 """Helper for marking func defs with nested tuples in arglist"""
626 def __init__(self, count, names):
627 self.count = count
628 self.names = names
629 def __repr__(self):
630 return "TupleArg(%s, %s)" % (self.count, self.names)
631 def getName(self):
Jeremy Hyltonbfb0cf82001-04-12 17:33:34 +0000632 return ".%d" % self.count
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000633
634def getArgCount(args):
635 argcount = len(args)
636 if args:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000637 for arg in args:
638 if isinstance(arg, TupleArg):
639 numNames = len(misc.flatten(arg.names))
640 argcount = argcount - numNames
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000641 return argcount
642
643def twobyte(val):
644 """Convert an int argument into high and low bytes"""
645 assert type(val) == types.IntType
646 return divmod(val, 256)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000647
648class LineAddrTable:
649 """lnotab
Tim Peterse0c446b2001-10-18 21:57:37 +0000650
Tim Peters2a7f3842001-06-09 09:26:21 +0000651 This class builds the lnotab, which is documented in compile.c.
652 Here's a brief recap:
Jeremy Hyltona5058122000-02-14 14:14:29 +0000653
654 For each SET_LINENO instruction after the first one, two bytes are
655 added to lnotab. (In some cases, multiple two-byte entries are
656 added.) The first byte is the distance in bytes between the
657 instruction for the last SET_LINENO and the current SET_LINENO.
658 The second byte is offset in line numbers. If either offset is
Tim Peters2a7f3842001-06-09 09:26:21 +0000659 greater than 255, multiple two-byte entries are added -- see
660 compile.c for the delicate details.
Jeremy Hyltona5058122000-02-14 14:14:29 +0000661 """
662
663 def __init__(self):
664 self.code = []
665 self.codeOffset = 0
666 self.firstline = 0
667 self.lastline = 0
668 self.lastoff = 0
669 self.lnotab = []
670
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000671 def addCode(self, *args):
672 for arg in args:
673 self.code.append(chr(arg))
674 self.codeOffset = self.codeOffset + len(args)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000675
676 def nextLine(self, lineno):
677 if self.firstline == 0:
678 self.firstline = lineno
679 self.lastline = lineno
680 else:
681 # compute deltas
682 addr = self.codeOffset - self.lastoff
683 line = lineno - self.lastline
Jeremy Hylton92f39722000-09-01 20:47:37 +0000684 # Python assumes that lineno always increases with
685 # increasing bytecode address (lnotab is unsigned char).
686 # Depending on when SET_LINENO instructions are emitted
687 # this is not always true. Consider the code:
688 # a = (1,
689 # b)
690 # In the bytecode stream, the assignment to "a" occurs
691 # after the loading of "b". This works with the C Python
692 # compiler because it only generates a SET_LINENO instruction
693 # for the assignment.
Jeremy Hyltonaccb62b2002-12-31 18:17:44 +0000694 if line >= 0:
Tim Peters2a7f3842001-06-09 09:26:21 +0000695 push = self.lnotab.append
696 while addr > 255:
697 push(255); push(0)
698 addr -= 255
699 while line > 255:
700 push(addr); push(255)
701 line -= 255
702 addr = 0
703 if addr > 0 or line > 0:
704 push(addr); push(line)
Jeremy Hylton92f39722000-09-01 20:47:37 +0000705 self.lastline = lineno
706 self.lastoff = self.codeOffset
Jeremy Hyltona5058122000-02-14 14:14:29 +0000707
708 def getCode(self):
Neal Norwitza312c3a2002-06-06 18:30:10 +0000709 return ''.join(self.code)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000710
711 def getTable(self):
Neal Norwitza312c3a2002-06-06 18:30:10 +0000712 return ''.join(map(chr, self.lnotab))
Tim Peterse0c446b2001-10-18 21:57:37 +0000713
Jeremy Hyltona5058122000-02-14 14:14:29 +0000714class StackDepthTracker:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000715 # XXX 1. need to keep track of stack depth on jumps
716 # XXX 2. at least partly as a result, this code is broken
Jeremy Hyltona5058122000-02-14 14:14:29 +0000717
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000718 def findDepth(self, insts, debug=0):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000719 depth = 0
720 maxDepth = 0
721 for i in insts:
722 opname = i[0]
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000723 if debug:
724 print i,
725 delta = self.effect.get(opname, None)
726 if delta is not None:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000727 depth = depth + delta
728 else:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000729 # now check patterns
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000730 for pat, pat_delta in self.patterns:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000731 if opname[:len(pat)] == pat:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000732 delta = pat_delta
Jeremy Hylton772dd412000-02-21 22:46:00 +0000733 depth = depth + delta
734 break
735 # if we still haven't found a match
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000736 if delta is None:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000737 meth = getattr(self, opname, None)
738 if meth is not None:
739 depth = depth + meth(i[1])
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000740 if depth > maxDepth:
741 maxDepth = depth
742 if debug:
743 print depth, maxDepth
Jeremy Hylton772dd412000-02-21 22:46:00 +0000744 return maxDepth
Jeremy Hyltona5058122000-02-14 14:14:29 +0000745
746 effect = {
Jeremy Hylton772dd412000-02-21 22:46:00 +0000747 'POP_TOP': -1,
748 'DUP_TOP': 1,
749 'SLICE+1': -1,
750 'SLICE+2': -1,
751 'SLICE+3': -2,
752 'STORE_SLICE+0': -1,
753 'STORE_SLICE+1': -2,
754 'STORE_SLICE+2': -2,
755 'STORE_SLICE+3': -3,
756 'DELETE_SLICE+0': -1,
757 'DELETE_SLICE+1': -2,
758 'DELETE_SLICE+2': -2,
759 'DELETE_SLICE+3': -3,
760 'STORE_SUBSCR': -3,
761 'DELETE_SUBSCR': -2,
762 # PRINT_EXPR?
763 'PRINT_ITEM': -1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000764 'RETURN_VALUE': -1,
Jeremy Hyltonaccb62b2002-12-31 18:17:44 +0000765 'YIELD_VALUE': -1,
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000766 'EXEC_STMT': -3,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000767 'BUILD_CLASS': -2,
768 'STORE_NAME': -1,
769 'STORE_ATTR': -2,
770 'DELETE_ATTR': -1,
771 'STORE_GLOBAL': -1,
772 'BUILD_MAP': 1,
773 'COMPARE_OP': -1,
774 'STORE_FAST': -1,
Jeremy Hylton4e1be722000-10-12 20:23:23 +0000775 'IMPORT_STAR': -1,
776 'IMPORT_NAME': 0,
777 'IMPORT_FROM': 1,
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000778 'LOAD_ATTR': 0, # unlike other loads
Jeremy Hylton92638482001-08-29 22:27:14 +0000779 # close enough...
780 'SETUP_EXCEPT': 3,
781 'SETUP_FINALLY': 3,
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000782 'FOR_ITER': 1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000783 }
Jeremy Hyltona5058122000-02-14 14:14:29 +0000784 # use pattern match
785 patterns = [
Jeremy Hylton772dd412000-02-21 22:46:00 +0000786 ('BINARY_', -1),
787 ('LOAD_', 1),
Jeremy Hylton772dd412000-02-21 22:46:00 +0000788 ]
Tim Peterse0c446b2001-10-18 21:57:37 +0000789
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000790 def UNPACK_SEQUENCE(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000791 return count-1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000792 def BUILD_TUPLE(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000793 return -count+1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000794 def BUILD_LIST(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000795 return -count+1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000796 def CALL_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000797 hi, lo = divmod(argc, 256)
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000798 return -(lo + hi * 2)
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000799 def CALL_FUNCTION_VAR(self, argc):
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000800 return self.CALL_FUNCTION(argc)-1
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000801 def CALL_FUNCTION_KW(self, argc):
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000802 return self.CALL_FUNCTION(argc)-1
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000803 def CALL_FUNCTION_VAR_KW(self, argc):
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000804 return self.CALL_FUNCTION(argc)-2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000805 def MAKE_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000806 return -argc
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000807 def MAKE_CLOSURE(self, argc):
808 # XXX need to account for free variables too!
809 return -argc
Jeremy Hyltona5058122000-02-14 14:14:29 +0000810 def BUILD_SLICE(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000811 if argc == 2:
812 return -1
813 elif argc == 3:
814 return -2
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000815 def DUP_TOPX(self, argc):
816 return argc
Tim Peterse0c446b2001-10-18 21:57:37 +0000817
Jeremy Hyltona5058122000-02-14 14:14:29 +0000818findDepth = StackDepthTracker().findDepth