blob: 26b900135c490f807fcb732be3a9b41c6074848b [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
5import string
Jeremy Hylton314e3fb2000-11-06 03:43:11 +00006import sys
Jeremy Hylton36cc6a22000-03-16 20:06:59 +00007import types
Jeremy Hyltona5058122000-02-14 14:14:29 +00008
Jeremy Hylton36cc6a22000-03-16 20:06:59 +00009from compiler import misc
Jeremy Hylton71ebc332001-08-30 20:25:55 +000010from compiler.consts import CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, \
11 CO_VARKEYWORDS
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000012
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000013def xxx_sort(l):
14 l = l[:]
15 def sorter(a, b):
16 return cmp(a.bid, b.bid)
17 l.sort(sorter)
18 return l
19
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000020class FlowGraph:
21 def __init__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000022 self.current = self.entry = Block()
23 self.exit = Block("exit")
24 self.blocks = misc.Set()
25 self.blocks.add(self.entry)
26 self.blocks.add(self.exit)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000027
28 def startBlock(self, block):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000029 if self._debug:
30 if self.current:
31 print "end", repr(self.current)
Jeremy Hylton542b11a2001-04-12 20:21:39 +000032 print " next", self.current.next
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000033 print " ", self.current.get_children()
34 print repr(block)
Thomas Wouters46cc7c02000-08-12 20:32:46 +000035 self.current = block
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000036
Jeremy Hylton542b11a2001-04-12 20:21:39 +000037 def nextBlock(self, block=None):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000038 # XXX think we need to specify when there is implicit transfer
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000039 # from one block to the next. might be better to represent this
40 # with explicit JUMP_ABSOLUTE instructions that are optimized
41 # out when they are unnecessary.
Thomas Wouters46cc7c02000-08-12 20:32:46 +000042 #
43 # I think this strategy works: each block has a child
44 # designated as "next" which is returned as the last of the
45 # children. because the nodes in a graph are emitted in
46 # reverse post order, the "next" block will always be emitted
47 # immediately after its parent.
48 # Worry: maintaining this invariant could be tricky
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000049 if block is None:
50 block = self.newBlock()
51
52 # Note: If the current block ends with an unconditional
53 # control transfer, then it is incorrect to add an implicit
54 # transfer to the block graph. The current code requires
55 # these edges to get the blocks emitted in the right order,
56 # however. :-( If a client needs to remove these edges, call
57 # pruneEdges().
Tim Peterse0c446b2001-10-18 21:57:37 +000058
Thomas Wouters46cc7c02000-08-12 20:32:46 +000059 self.current.addNext(block)
60 self.startBlock(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000061
62 def newBlock(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000063 b = Block()
64 self.blocks.add(b)
65 return b
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000066
67 def startExitBlock(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000068 self.startBlock(self.exit)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000069
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000070 _debug = 0
71
72 def _enable_debug(self):
73 self._debug = 1
74
75 def _disable_debug(self):
76 self._debug = 0
77
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000078 def emit(self, *inst):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000079 if self._debug:
80 print "\t", inst
Thomas Wouters46cc7c02000-08-12 20:32:46 +000081 if inst[0] == 'RETURN_VALUE':
82 self.current.addOutEdge(self.exit)
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000083 if len(inst) == 2 and isinstance(inst[1], Block):
84 self.current.addOutEdge(inst[1])
Thomas Wouters46cc7c02000-08-12 20:32:46 +000085 self.current.emit(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000086
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000087 def getBlocksInOrder(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000088 """Return the blocks in reverse postorder
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000089
Thomas Wouters46cc7c02000-08-12 20:32:46 +000090 i.e. each node appears before all of its successors
91 """
92 # XXX make sure every node that doesn't have an explicit next
93 # is set so that next points to exit
94 for b in self.blocks.elements():
95 if b is self.exit:
96 continue
97 if not b.next:
98 b.addNext(self.exit)
99 order = dfs_postorder(self.entry, {})
100 order.reverse()
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000101 self.fixupOrder(order, self.exit)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000102 # hack alert
103 if not self.exit in order:
104 order.append(self.exit)
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000105
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000106 return order
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000107
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000108 def fixupOrder(self, blocks, default_next):
109 """Fixup bad order introduced by DFS."""
110
111 # XXX This is a total mess. There must be a better way to get
112 # the code blocks in the right order.
Tim Peterse0c446b2001-10-18 21:57:37 +0000113
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000114 self.fixupOrderHonorNext(blocks, default_next)
115 self.fixupOrderForward(blocks, default_next)
116
117 def fixupOrderHonorNext(self, blocks, default_next):
118 """Fix one problem with DFS.
Tim Peterse0c446b2001-10-18 21:57:37 +0000119
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000120 The DFS uses child block, but doesn't know about the special
121 "next" block. As a result, the DFS can order blocks so that a
122 block isn't next to the right block for implicit control
123 transfers.
124 """
125 index = {}
126 for i in range(len(blocks)):
127 index[blocks[i]] = i
128
129 for i in range(0, len(blocks) - 1):
130 b = blocks[i]
131 n = blocks[i + 1]
132 if not b.next or b.next[0] == default_next or b.next[0] == n:
133 continue
134 # The blocks are in the wrong order. Find the chain of
135 # blocks to insert where they belong.
136 cur = b
137 chain = []
138 elt = cur
139 while elt.next and elt.next[0] != default_next:
140 chain.append(elt.next[0])
141 elt = elt.next[0]
142 # Now remove the blocks in the chain from the current
143 # block list, so that they can be re-inserted.
144 l = []
145 for b in chain:
146 assert index[b] > i
147 l.append((index[b], b))
148 l.sort()
149 l.reverse()
150 for j, b in l:
151 del blocks[index[b]]
152 # Insert the chain in the proper location
153 blocks[i:i + 1] = [cur] + chain
154 # Finally, re-compute the block indexes
155 for i in range(len(blocks)):
156 index[blocks[i]] = i
157
158 def fixupOrderForward(self, blocks, default_next):
159 """Make sure all JUMP_FORWARDs jump forward"""
160 index = {}
161 chains = []
162 cur = []
163 for b in blocks:
164 index[b] = len(chains)
165 cur.append(b)
166 if b.next and b.next[0] == default_next:
167 chains.append(cur)
168 cur = []
169 chains.append(cur)
170
171 while 1:
172 constraints = []
173
174 for i in range(len(chains)):
175 l = chains[i]
176 for b in l:
177 for c in b.get_children():
178 if index[c] < i:
179 forward_p = 0
180 for inst in b.insts:
181 if inst[0] == 'JUMP_FORWARD':
182 if inst[1] == c:
183 forward_p = 1
184 if not forward_p:
185 continue
186 constraints.append((index[c], i))
187
188 if not constraints:
189 break
190
191 # XXX just do one for now
192 # do swaps to get things in the right order
193 goes_before, a_chain = constraints[0]
194 assert a_chain > goes_before
195 c = chains[a_chain]
196 chains.remove(c)
197 chains.insert(goes_before, c)
198
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000199 del blocks[:]
200 for c in chains:
201 for b in c:
202 blocks.append(b)
Tim Peterse0c446b2001-10-18 21:57:37 +0000203
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000204 def getBlocks(self):
205 return self.blocks.elements()
206
207 def getRoot(self):
208 """Return nodes appropriate for use with dominator"""
209 return self.entry
Tim Peterse0c446b2001-10-18 21:57:37 +0000210
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000211 def getContainedGraphs(self):
212 l = []
213 for b in self.getBlocks():
214 l.extend(b.getContainedGraphs())
215 return l
216
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000217def dfs_postorder(b, seen):
218 """Depth-first search of tree rooted at b, return in postorder"""
219 order = []
220 seen[b] = b
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000221 for c in b.get_children():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000222 if seen.has_key(c):
223 continue
224 order = order + dfs_postorder(c, seen)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000225 order.append(b)
226 return order
227
228class Block:
229 _count = 0
230
231 def __init__(self, label=''):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000232 self.insts = []
233 self.inEdges = misc.Set()
234 self.outEdges = misc.Set()
235 self.label = label
236 self.bid = Block._count
237 self.next = []
238 Block._count = Block._count + 1
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000239
240 def __repr__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000241 if self.label:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000242 return "<block %s id=%d>" % (self.label, self.bid)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000243 else:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000244 return "<block id=%d>" % (self.bid)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000245
246 def __str__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000247 insts = map(str, self.insts)
248 return "<block %s %d:\n%s>" % (self.label, self.bid,
Tim Peterse0c446b2001-10-18 21:57:37 +0000249 string.join(insts, '\n'))
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000250
251 def emit(self, inst):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000252 op = inst[0]
253 if op[:4] == 'JUMP':
254 self.outEdges.add(inst[1])
255 self.insts.append(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000256
257 def getInstructions(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000258 return self.insts
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000259
260 def addInEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000261 self.inEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000262
263 def addOutEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000264 self.outEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000265
266 def addNext(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000267 self.next.append(block)
268 assert len(self.next) == 1, map(str, self.next)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000269
Jeremy Hyltoneefaeb72000-11-06 03:47:39 +0000270 _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS',
Jeremy Hylton92638482001-08-29 22:27:14 +0000271 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP')
Jeremy Hyltoneefaeb72000-11-06 03:47:39 +0000272
273 def pruneNext(self):
274 """Remove bogus edge for unconditional transfers
275
276 Each block has a next edge that accounts for implicit control
277 transfers, e.g. from a JUMP_IF_FALSE to the block that will be
278 executed if the test is true.
279
280 These edges must remain for the current assembler code to
281 work. If they are removed, the dfs_postorder gets things in
282 weird orders. However, they shouldn't be there for other
283 purposes, e.g. conversion to SSA form. This method will
284 remove the next edge when it follows an unconditional control
285 transfer.
286 """
287 try:
288 op, arg = self.insts[-1]
289 except (IndexError, ValueError):
290 return
291 if op in self._uncond_transfer:
292 self.next = []
293
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000294 def get_children(self):
295 if self.next and self.next[0] in self.outEdges:
296 self.outEdges.remove(self.next[0])
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000297 return self.outEdges.elements() + self.next
Jeremy Hyltona5058122000-02-14 14:14:29 +0000298
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000299 def getContainedGraphs(self):
300 """Return all graphs contained within this block.
301
302 For example, a MAKE_FUNCTION block will contain a reference to
303 the graph for the function body.
304 """
305 contained = []
306 for inst in self.insts:
307 if len(inst) == 1:
308 continue
309 op = inst[1]
310 if hasattr(op, 'graph'):
311 contained.append(op.graph)
312 return contained
313
Jeremy Hyltona5058122000-02-14 14:14:29 +0000314# flags for code objects
Jeremy Hyltona5058122000-02-14 14:14:29 +0000315
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000316# the FlowGraph is transformed in place; it exists in one of these states
317RAW = "RAW"
318FLAT = "FLAT"
319CONV = "CONV"
320DONE = "DONE"
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000321
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000322class PyFlowGraph(FlowGraph):
323 super_init = FlowGraph.__init__
Jeremy Hyltona5058122000-02-14 14:14:29 +0000324
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000325 def __init__(self, name, filename, args=(), optimized=0, klass=None):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000326 self.super_init()
327 self.name = name
328 self.filename = filename
329 self.docstring = None
330 self.args = args # XXX
331 self.argcount = getArgCount(args)
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000332 self.klass = klass
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000333 if optimized:
Tim Peterse0c446b2001-10-18 21:57:37 +0000334 self.flags = CO_OPTIMIZED | CO_NEWLOCALS
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000335 else:
336 self.flags = 0
337 self.consts = []
338 self.names = []
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000339 # Free variables found by the symbol table scan, including
340 # variables used only in nested scopes, are included here.
341 self.freevars = []
342 self.cellvars = []
343 # The closure list is used to track the order of cell
344 # variables and free variables in the resulting code object.
345 # The offsets used by LOAD_CLOSURE/LOAD_DEREF refer to both
346 # kinds of variables.
347 self.closure = []
Jeremy Hyltona5058122000-02-14 14:14:29 +0000348 self.varnames = list(args) or []
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000349 for i in range(len(self.varnames)):
350 var = self.varnames[i]
351 if isinstance(var, TupleArg):
352 self.varnames[i] = var.getName()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000353 self.stage = RAW
Jeremy Hyltona5058122000-02-14 14:14:29 +0000354
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000355 def setDocstring(self, doc):
356 self.docstring = doc
Jeremy Hylton2ce27b22000-02-16 00:50:29 +0000357
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000358 def setFlag(self, flag):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000359 self.flags = self.flags | flag
360 if flag == CO_VARARGS:
361 self.argcount = self.argcount - 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000362
Jeremy Hylton1e99a772001-09-14 22:49:08 +0000363 def checkFlag(self, flag):
364 if self.flags & flag:
365 return 1
366
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000367 def setFreeVars(self, names):
368 self.freevars = list(names)
369
370 def setCellVars(self, names):
371 self.cellvars = names
372
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000373 def getCode(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000374 """Get a Python code object"""
375 if self.stage == RAW:
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000376 self.computeStackDepth()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000377 self.flattenGraph()
378 if self.stage == FLAT:
379 self.convertArgs()
380 if self.stage == CONV:
381 self.makeByteCode()
382 if self.stage == DONE:
383 return self.newCodeObject()
384 raise RuntimeError, "inconsistent PyFlowGraph state"
Jeremy Hyltona5058122000-02-14 14:14:29 +0000385
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000386 def dump(self, io=None):
387 if io:
388 save = sys.stdout
389 sys.stdout = io
390 pc = 0
Jeremy Hyltona5058122000-02-14 14:14:29 +0000391 for t in self.insts:
392 opname = t[0]
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000393 if opname == "SET_LINENO":
394 print
Jeremy Hyltona5058122000-02-14 14:14:29 +0000395 if len(t) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000396 print "\t", "%3d" % pc, opname
397 pc = pc + 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000398 else:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000399 print "\t", "%3d" % pc, opname, t[1]
400 pc = pc + 3
401 if io:
402 sys.stdout = save
Jeremy Hyltona5058122000-02-14 14:14:29 +0000403
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000404 def computeStackDepth(self):
405 """Compute the max stack depth.
406
407 Approach is to compute the stack effect of each basic block.
408 Then find the path through the code with the largest total
409 effect.
410 """
411 depth = {}
412 exit = None
413 for b in self.getBlocks():
414 depth[b] = findDepth(b.getInstructions())
415
416 seen = {}
417
418 def max_depth(b, d):
419 if seen.has_key(b):
420 return d
421 seen[b] = 1
422 d = d + depth[b]
423 children = b.get_children()
424 if children:
425 return max([max_depth(c, d) for c in children])
426 else:
427 if not b.label == "exit":
428 return max_depth(self.exit, d)
429 else:
430 return d
431
432 self.stacksize = max_depth(self.entry, 0)
433
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000434 def flattenGraph(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000435 """Arrange the blocks in order and resolve jumps"""
436 assert self.stage == RAW
437 self.insts = insts = []
438 pc = 0
439 begin = {}
440 end = {}
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000441 for b in self.getBlocksInOrder():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000442 begin[b] = pc
443 for inst in b.getInstructions():
444 insts.append(inst)
445 if len(inst) == 1:
446 pc = pc + 1
447 else:
448 # arg takes 2 bytes
449 pc = pc + 3
450 end[b] = pc
451 pc = 0
452 for i in range(len(insts)):
453 inst = insts[i]
454 if len(inst) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000455 pc = pc + 1
456 else:
457 pc = pc + 3
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000458 opname = inst[0]
459 if self.hasjrel.has_elt(opname):
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000460 oparg = inst[1]
461 offset = begin[oparg] - pc
462 insts[i] = opname, offset
463 elif self.hasjabs.has_elt(opname):
464 insts[i] = opname, begin[inst[1]]
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000465 self.stage = FLAT
Jeremy Hyltona5058122000-02-14 14:14:29 +0000466
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000467 hasjrel = misc.Set()
468 for i in dis.hasjrel:
469 hasjrel.add(dis.opname[i])
470 hasjabs = misc.Set()
471 for i in dis.hasjabs:
472 hasjabs.add(dis.opname[i])
Jeremy Hyltona5058122000-02-14 14:14:29 +0000473
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000474 def convertArgs(self):
475 """Convert arguments from symbolic to concrete form"""
476 assert self.stage == FLAT
Jeremy Hylton5af105e2001-04-11 16:21:51 +0000477 self.consts.insert(0, self.docstring)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000478 self.sort_cellvars()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000479 for i in range(len(self.insts)):
480 t = self.insts[i]
481 if len(t) == 2:
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000482 opname, oparg = t
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000483 conv = self._converters.get(opname, None)
484 if conv:
485 self.insts[i] = opname, conv(self, oparg)
486 self.stage = CONV
Jeremy Hyltona5058122000-02-14 14:14:29 +0000487
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000488 def sort_cellvars(self):
489 """Sort cellvars in the order of varnames and prune from freevars.
490 """
491 cells = {}
492 for name in self.cellvars:
493 cells[name] = 1
494 self.cellvars = [name for name in self.varnames
495 if cells.has_key(name)]
496 for name in self.cellvars:
497 del cells[name]
498 self.cellvars = self.cellvars + cells.keys()
499 self.closure = self.cellvars + self.freevars
500
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000501 def _lookupName(self, name, list):
502 """Return index of name in list, appending if necessary
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000503
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000504 This routine uses a list instead of a dictionary, because a
505 dictionary can't store two different keys if the keys have the
506 same value but different types, e.g. 2 and 2L. The compiler
507 must treat these two separately, so it does an explicit type
508 comparison before comparing the values.
509 """
Jeremy Hylton9c048f92000-10-13 21:58:13 +0000510 t = type(name)
511 for i in range(len(list)):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000512 if t == type(list[i]) and list[i] == name:
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000513 return i
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000514 end = len(list)
515 list.append(name)
516 return end
Jeremy Hyltona5058122000-02-14 14:14:29 +0000517
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000518 _converters = {}
519 def _convert_LOAD_CONST(self, arg):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000520 if hasattr(arg, 'getCode'):
521 arg = arg.getCode()
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000522 return self._lookupName(arg, self.consts)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000523
524 def _convert_LOAD_FAST(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000525 self._lookupName(arg, self.names)
526 return self._lookupName(arg, self.varnames)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000527 _convert_STORE_FAST = _convert_LOAD_FAST
528 _convert_DELETE_FAST = _convert_LOAD_FAST
529
Jeremy Hylton63db7b92001-08-28 16:36:12 +0000530 def _convert_LOAD_NAME(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000531 if self.klass is None:
532 self._lookupName(arg, self.varnames)
533 return self._lookupName(arg, self.names)
Jeremy Hylton63db7b92001-08-28 16:36:12 +0000534
535 def _convert_NAME(self, arg):
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000536 if self.klass is None:
537 self._lookupName(arg, self.varnames)
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000538 return self._lookupName(arg, self.names)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000539 _convert_STORE_NAME = _convert_NAME
540 _convert_DELETE_NAME = _convert_NAME
541 _convert_IMPORT_NAME = _convert_NAME
542 _convert_IMPORT_FROM = _convert_NAME
543 _convert_STORE_ATTR = _convert_NAME
544 _convert_LOAD_ATTR = _convert_NAME
545 _convert_DELETE_ATTR = _convert_NAME
546 _convert_LOAD_GLOBAL = _convert_NAME
547 _convert_STORE_GLOBAL = _convert_NAME
548 _convert_DELETE_GLOBAL = _convert_NAME
549
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000550 def _convert_DEREF(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000551 self._lookupName(arg, self.names)
552 self._lookupName(arg, self.varnames)
553 return self._lookupName(arg, self.closure)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000554 _convert_LOAD_DEREF = _convert_DEREF
555 _convert_STORE_DEREF = _convert_DEREF
556
557 def _convert_LOAD_CLOSURE(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000558 self._lookupName(arg, self.varnames)
559 return self._lookupName(arg, self.closure)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000560
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000561 _cmp = list(dis.cmp_op)
562 def _convert_COMPARE_OP(self, arg):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000563 return self._cmp.index(arg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000564
565 # similarly for other opcodes...
566
567 for name, obj in locals().items():
568 if name[:9] == "_convert_":
569 opname = name[9:]
Tim Peterse0c446b2001-10-18 21:57:37 +0000570 _converters[opname] = obj
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000571 del name, obj, opname
572
573 def makeByteCode(self):
574 assert self.stage == CONV
575 self.lnotab = lnotab = LineAddrTable()
576 for t in self.insts:
577 opname = t[0]
578 if len(t) == 1:
579 lnotab.addCode(self.opnum[opname])
580 else:
581 oparg = t[1]
582 if opname == "SET_LINENO":
583 lnotab.nextLine(oparg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000584 hi, lo = twobyte(oparg)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000585 try:
586 lnotab.addCode(self.opnum[opname], lo, hi)
587 except ValueError:
588 print opname, oparg
589 print self.opnum[opname], lo, hi
590 raise
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000591 self.stage = DONE
592
Jeremy Hyltona5058122000-02-14 14:14:29 +0000593 opnum = {}
594 for num in range(len(dis.opname)):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000595 opnum[dis.opname[num]] = num
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000596 del num
Jeremy Hyltona5058122000-02-14 14:14:29 +0000597
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000598 def newCodeObject(self):
599 assert self.stage == DONE
Jeremy Hylton1e99a772001-09-14 22:49:08 +0000600 if (self.flags & CO_NEWLOCALS) == 0:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000601 nlocals = 0
602 else:
603 nlocals = len(self.varnames)
604 argcount = self.argcount
605 if self.flags & CO_VARKEYWORDS:
606 argcount = argcount - 1
607 return new.code(argcount, nlocals, self.stacksize, self.flags,
608 self.lnotab.getCode(), self.getConsts(),
609 tuple(self.names), tuple(self.varnames),
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000610 self.filename, self.name, self.lnotab.firstline,
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000611 self.lnotab.getTable(), tuple(self.freevars),
612 tuple(self.cellvars))
Jeremy Hyltona5058122000-02-14 14:14:29 +0000613
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000614 def getConsts(self):
615 """Return a tuple for the const slot of the code object
Jeremy Hyltona5058122000-02-14 14:14:29 +0000616
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000617 Must convert references to code (MAKE_FUNCTION) to code
618 objects recursively.
619 """
620 l = []
621 for elt in self.consts:
622 if isinstance(elt, PyFlowGraph):
623 elt = elt.getCode()
624 l.append(elt)
625 return tuple(l)
Tim Peterse0c446b2001-10-18 21:57:37 +0000626
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000627def isJump(opname):
628 if opname[:4] == 'JUMP':
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000629 return 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000630
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000631class TupleArg:
632 """Helper for marking func defs with nested tuples in arglist"""
633 def __init__(self, count, names):
634 self.count = count
635 self.names = names
636 def __repr__(self):
637 return "TupleArg(%s, %s)" % (self.count, self.names)
638 def getName(self):
Jeremy Hyltonbfb0cf82001-04-12 17:33:34 +0000639 return ".%d" % self.count
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000640
641def getArgCount(args):
642 argcount = len(args)
643 if args:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000644 for arg in args:
645 if isinstance(arg, TupleArg):
646 numNames = len(misc.flatten(arg.names))
647 argcount = argcount - numNames
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000648 return argcount
649
650def twobyte(val):
651 """Convert an int argument into high and low bytes"""
652 assert type(val) == types.IntType
653 return divmod(val, 256)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000654
655class LineAddrTable:
656 """lnotab
Tim Peterse0c446b2001-10-18 21:57:37 +0000657
Tim Peters2a7f3842001-06-09 09:26:21 +0000658 This class builds the lnotab, which is documented in compile.c.
659 Here's a brief recap:
Jeremy Hyltona5058122000-02-14 14:14:29 +0000660
661 For each SET_LINENO instruction after the first one, two bytes are
662 added to lnotab. (In some cases, multiple two-byte entries are
663 added.) The first byte is the distance in bytes between the
664 instruction for the last SET_LINENO and the current SET_LINENO.
665 The second byte is offset in line numbers. If either offset is
Tim Peters2a7f3842001-06-09 09:26:21 +0000666 greater than 255, multiple two-byte entries are added -- see
667 compile.c for the delicate details.
Jeremy Hyltona5058122000-02-14 14:14:29 +0000668 """
669
670 def __init__(self):
671 self.code = []
672 self.codeOffset = 0
673 self.firstline = 0
674 self.lastline = 0
675 self.lastoff = 0
676 self.lnotab = []
677
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000678 def addCode(self, *args):
679 for arg in args:
680 self.code.append(chr(arg))
681 self.codeOffset = self.codeOffset + len(args)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000682
683 def nextLine(self, lineno):
684 if self.firstline == 0:
685 self.firstline = lineno
686 self.lastline = lineno
687 else:
688 # compute deltas
689 addr = self.codeOffset - self.lastoff
690 line = lineno - self.lastline
Jeremy Hylton92f39722000-09-01 20:47:37 +0000691 # Python assumes that lineno always increases with
692 # increasing bytecode address (lnotab is unsigned char).
693 # Depending on when SET_LINENO instructions are emitted
694 # this is not always true. Consider the code:
695 # a = (1,
696 # b)
697 # In the bytecode stream, the assignment to "a" occurs
698 # after the loading of "b". This works with the C Python
699 # compiler because it only generates a SET_LINENO instruction
700 # for the assignment.
701 if line > 0:
Tim Peters2a7f3842001-06-09 09:26:21 +0000702 push = self.lnotab.append
703 while addr > 255:
704 push(255); push(0)
705 addr -= 255
706 while line > 255:
707 push(addr); push(255)
708 line -= 255
709 addr = 0
710 if addr > 0 or line > 0:
711 push(addr); push(line)
Jeremy Hylton92f39722000-09-01 20:47:37 +0000712 self.lastline = lineno
713 self.lastoff = self.codeOffset
Jeremy Hyltona5058122000-02-14 14:14:29 +0000714
715 def getCode(self):
716 return string.join(self.code, '')
717
718 def getTable(self):
719 return string.join(map(chr, self.lnotab), '')
Tim Peterse0c446b2001-10-18 21:57:37 +0000720
Jeremy Hyltona5058122000-02-14 14:14:29 +0000721class StackDepthTracker:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000722 # XXX 1. need to keep track of stack depth on jumps
723 # XXX 2. at least partly as a result, this code is broken
Jeremy Hyltona5058122000-02-14 14:14:29 +0000724
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000725 def findDepth(self, insts, debug=0):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000726 depth = 0
727 maxDepth = 0
728 for i in insts:
729 opname = i[0]
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000730 if debug:
731 print i,
732 delta = self.effect.get(opname, None)
733 if delta is not None:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000734 depth = depth + delta
735 else:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000736 # now check patterns
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000737 for pat, pat_delta in self.patterns:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000738 if opname[:len(pat)] == pat:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000739 delta = pat_delta
Jeremy Hylton772dd412000-02-21 22:46:00 +0000740 depth = depth + delta
741 break
742 # if we still haven't found a match
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000743 if delta is None:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000744 meth = getattr(self, opname, None)
745 if meth is not None:
746 depth = depth + meth(i[1])
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000747 if depth > maxDepth:
748 maxDepth = depth
749 if debug:
750 print depth, maxDepth
Jeremy Hylton772dd412000-02-21 22:46:00 +0000751 return maxDepth
Jeremy Hyltona5058122000-02-14 14:14:29 +0000752
753 effect = {
Jeremy Hylton772dd412000-02-21 22:46:00 +0000754 'POP_TOP': -1,
755 'DUP_TOP': 1,
756 'SLICE+1': -1,
757 'SLICE+2': -1,
758 'SLICE+3': -2,
759 'STORE_SLICE+0': -1,
760 'STORE_SLICE+1': -2,
761 'STORE_SLICE+2': -2,
762 'STORE_SLICE+3': -3,
763 'DELETE_SLICE+0': -1,
764 'DELETE_SLICE+1': -2,
765 'DELETE_SLICE+2': -2,
766 'DELETE_SLICE+3': -3,
767 'STORE_SUBSCR': -3,
768 'DELETE_SUBSCR': -2,
769 # PRINT_EXPR?
770 'PRINT_ITEM': -1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000771 'RETURN_VALUE': -1,
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000772 'EXEC_STMT': -3,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000773 'BUILD_CLASS': -2,
774 'STORE_NAME': -1,
775 'STORE_ATTR': -2,
776 'DELETE_ATTR': -1,
777 'STORE_GLOBAL': -1,
778 'BUILD_MAP': 1,
779 'COMPARE_OP': -1,
780 'STORE_FAST': -1,
Jeremy Hylton4e1be722000-10-12 20:23:23 +0000781 'IMPORT_STAR': -1,
782 'IMPORT_NAME': 0,
783 'IMPORT_FROM': 1,
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000784 'LOAD_ATTR': 0, # unlike other loads
Jeremy Hylton92638482001-08-29 22:27:14 +0000785 # close enough...
786 'SETUP_EXCEPT': 3,
787 'SETUP_FINALLY': 3,
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000788 'FOR_ITER': 1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000789 }
Jeremy Hyltona5058122000-02-14 14:14:29 +0000790 # use pattern match
791 patterns = [
Jeremy Hylton772dd412000-02-21 22:46:00 +0000792 ('BINARY_', -1),
793 ('LOAD_', 1),
Jeremy Hylton772dd412000-02-21 22:46:00 +0000794 ]
Tim Peterse0c446b2001-10-18 21:57:37 +0000795
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000796 def UNPACK_SEQUENCE(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000797 return count-1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000798 def BUILD_TUPLE(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000799 return -count+1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000800 def BUILD_LIST(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000801 return -count+1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000802 def CALL_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000803 hi, lo = divmod(argc, 256)
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000804 return -(lo + hi * 2)
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000805 def CALL_FUNCTION_VAR(self, argc):
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000806 return self.CALL_FUNCTION(argc)-1
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000807 def CALL_FUNCTION_KW(self, argc):
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000808 return self.CALL_FUNCTION(argc)-1
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000809 def CALL_FUNCTION_VAR_KW(self, argc):
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000810 return self.CALL_FUNCTION(argc)-2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000811 def MAKE_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000812 return -argc
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000813 def MAKE_CLOSURE(self, argc):
814 # XXX need to account for free variables too!
815 return -argc
Jeremy Hyltona5058122000-02-14 14:14:29 +0000816 def BUILD_SLICE(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000817 if argc == 2:
818 return -1
819 elif argc == 3:
820 return -2
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000821 def DUP_TOPX(self, argc):
822 return argc
Tim Peterse0c446b2001-10-18 21:57:37 +0000823
Jeremy Hyltona5058122000-02-14 14:14:29 +0000824findDepth = StackDepthTracker().findDepth