blob: c3fa7b7b97e47d2b01e5303b59d76b25c31f08a4 [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().
58
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.
113
114 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.
119
120 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
199
200 del blocks[:]
201 for c in chains:
202 for b in c:
203 blocks.append(b)
204
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000205 def getBlocks(self):
206 return self.blocks.elements()
207
208 def getRoot(self):
209 """Return nodes appropriate for use with dominator"""
210 return self.entry
211
212 def getContainedGraphs(self):
213 l = []
214 for b in self.getBlocks():
215 l.extend(b.getContainedGraphs())
216 return l
217
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000218def dfs_postorder(b, seen):
219 """Depth-first search of tree rooted at b, return in postorder"""
220 order = []
221 seen[b] = b
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000222 for c in b.get_children():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000223 if seen.has_key(c):
224 continue
225 order = order + dfs_postorder(c, seen)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000226 order.append(b)
227 return order
228
229class Block:
230 _count = 0
231
232 def __init__(self, label=''):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000233 self.insts = []
234 self.inEdges = misc.Set()
235 self.outEdges = misc.Set()
236 self.label = label
237 self.bid = Block._count
238 self.next = []
239 Block._count = Block._count + 1
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000240
241 def __repr__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000242 if self.label:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000243 return "<block %s id=%d>" % (self.label, self.bid)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000244 else:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000245 return "<block id=%d>" % (self.bid)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000246
247 def __str__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000248 insts = map(str, self.insts)
249 return "<block %s %d:\n%s>" % (self.label, self.bid,
250 string.join(insts, '\n'))
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000251
252 def emit(self, inst):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000253 op = inst[0]
254 if op[:4] == 'JUMP':
255 self.outEdges.add(inst[1])
256 self.insts.append(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000257
258 def getInstructions(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000259 return self.insts
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000260
261 def addInEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000262 self.inEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000263
264 def addOutEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000265 self.outEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000266
267 def addNext(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000268 self.next.append(block)
269 assert len(self.next) == 1, map(str, self.next)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000270
Jeremy Hyltoneefaeb72000-11-06 03:47:39 +0000271 _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS',
Jeremy Hylton92638482001-08-29 22:27:14 +0000272 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP')
Jeremy Hyltoneefaeb72000-11-06 03:47:39 +0000273
274 def pruneNext(self):
275 """Remove bogus edge for unconditional transfers
276
277 Each block has a next edge that accounts for implicit control
278 transfers, e.g. from a JUMP_IF_FALSE to the block that will be
279 executed if the test is true.
280
281 These edges must remain for the current assembler code to
282 work. If they are removed, the dfs_postorder gets things in
283 weird orders. However, they shouldn't be there for other
284 purposes, e.g. conversion to SSA form. This method will
285 remove the next edge when it follows an unconditional control
286 transfer.
287 """
288 try:
289 op, arg = self.insts[-1]
290 except (IndexError, ValueError):
291 return
292 if op in self._uncond_transfer:
293 self.next = []
294
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000295 def get_children(self):
296 if self.next and self.next[0] in self.outEdges:
297 self.outEdges.remove(self.next[0])
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000298 return self.outEdges.elements() + self.next
Jeremy Hyltona5058122000-02-14 14:14:29 +0000299
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000300 def getContainedGraphs(self):
301 """Return all graphs contained within this block.
302
303 For example, a MAKE_FUNCTION block will contain a reference to
304 the graph for the function body.
305 """
306 contained = []
307 for inst in self.insts:
308 if len(inst) == 1:
309 continue
310 op = inst[1]
311 if hasattr(op, 'graph'):
312 contained.append(op.graph)
313 return contained
314
Jeremy Hyltona5058122000-02-14 14:14:29 +0000315# flags for code objects
Jeremy Hyltona5058122000-02-14 14:14:29 +0000316
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000317# the FlowGraph is transformed in place; it exists in one of these states
318RAW = "RAW"
319FLAT = "FLAT"
320CONV = "CONV"
321DONE = "DONE"
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000322
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000323class PyFlowGraph(FlowGraph):
324 super_init = FlowGraph.__init__
Jeremy Hyltona5058122000-02-14 14:14:29 +0000325
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000326 def __init__(self, name, filename, args=(), optimized=0, klass=None):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000327 self.super_init()
328 self.name = name
329 self.filename = filename
330 self.docstring = None
331 self.args = args # XXX
332 self.argcount = getArgCount(args)
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000333 self.klass = klass
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000334 if optimized:
335 self.flags = CO_OPTIMIZED | CO_NEWLOCALS
336 else:
337 self.flags = 0
338 self.consts = []
339 self.names = []
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000340 # Free variables found by the symbol table scan, including
341 # variables used only in nested scopes, are included here.
342 self.freevars = []
343 self.cellvars = []
344 # The closure list is used to track the order of cell
345 # variables and free variables in the resulting code object.
346 # The offsets used by LOAD_CLOSURE/LOAD_DEREF refer to both
347 # kinds of variables.
348 self.closure = []
Jeremy Hyltona5058122000-02-14 14:14:29 +0000349 self.varnames = list(args) or []
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000350 for i in range(len(self.varnames)):
351 var = self.varnames[i]
352 if isinstance(var, TupleArg):
353 self.varnames[i] = var.getName()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000354 self.stage = RAW
Jeremy Hyltona5058122000-02-14 14:14:29 +0000355
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000356 def setDocstring(self, doc):
357 self.docstring = doc
Jeremy Hylton2ce27b22000-02-16 00:50:29 +0000358
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000359 def setFlag(self, flag):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000360 self.flags = self.flags | flag
361 if flag == CO_VARARGS:
362 self.argcount = self.argcount - 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000363
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000364 def setFreeVars(self, names):
365 self.freevars = list(names)
366
367 def setCellVars(self, names):
368 self.cellvars = names
369
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000370 def getCode(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000371 """Get a Python code object"""
372 if self.stage == RAW:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000373 self.flattenGraph()
374 if self.stage == FLAT:
375 self.convertArgs()
376 if self.stage == CONV:
377 self.makeByteCode()
378 if self.stage == DONE:
379 return self.newCodeObject()
380 raise RuntimeError, "inconsistent PyFlowGraph state"
Jeremy Hyltona5058122000-02-14 14:14:29 +0000381
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000382 def dump(self, io=None):
383 if io:
384 save = sys.stdout
385 sys.stdout = io
386 pc = 0
Jeremy Hyltona5058122000-02-14 14:14:29 +0000387 for t in self.insts:
388 opname = t[0]
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000389 if opname == "SET_LINENO":
390 print
Jeremy Hyltona5058122000-02-14 14:14:29 +0000391 if len(t) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000392 print "\t", "%3d" % pc, opname
393 pc = pc + 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000394 else:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000395 print "\t", "%3d" % pc, opname, t[1]
396 pc = pc + 3
397 if io:
398 sys.stdout = save
Jeremy Hyltona5058122000-02-14 14:14:29 +0000399
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000400 def flattenGraph(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000401 """Arrange the blocks in order and resolve jumps"""
402 assert self.stage == RAW
403 self.insts = insts = []
404 pc = 0
405 begin = {}
406 end = {}
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000407 for b in self.getBlocksInOrder():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000408 begin[b] = pc
409 for inst in b.getInstructions():
410 insts.append(inst)
411 if len(inst) == 1:
412 pc = pc + 1
413 else:
414 # arg takes 2 bytes
415 pc = pc + 3
416 end[b] = pc
417 pc = 0
418 for i in range(len(insts)):
419 inst = insts[i]
420 if len(inst) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000421 pc = pc + 1
422 else:
423 pc = pc + 3
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000424 opname = inst[0]
425 if self.hasjrel.has_elt(opname):
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000426 oparg = inst[1]
427 offset = begin[oparg] - pc
428 insts[i] = opname, offset
429 elif self.hasjabs.has_elt(opname):
430 insts[i] = opname, begin[inst[1]]
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000431 self.stacksize = findDepth(self.insts)
432 self.stage = FLAT
Jeremy Hyltona5058122000-02-14 14:14:29 +0000433
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000434 hasjrel = misc.Set()
435 for i in dis.hasjrel:
436 hasjrel.add(dis.opname[i])
437 hasjabs = misc.Set()
438 for i in dis.hasjabs:
439 hasjabs.add(dis.opname[i])
Jeremy Hyltona5058122000-02-14 14:14:29 +0000440
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000441 def convertArgs(self):
442 """Convert arguments from symbolic to concrete form"""
443 assert self.stage == FLAT
Jeremy Hylton5af105e2001-04-11 16:21:51 +0000444 self.consts.insert(0, self.docstring)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000445 self.sort_cellvars()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000446 for i in range(len(self.insts)):
447 t = self.insts[i]
448 if len(t) == 2:
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000449 opname, oparg = t
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000450 conv = self._converters.get(opname, None)
451 if conv:
452 self.insts[i] = opname, conv(self, oparg)
453 self.stage = CONV
Jeremy Hyltona5058122000-02-14 14:14:29 +0000454
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000455 def sort_cellvars(self):
456 """Sort cellvars in the order of varnames and prune from freevars.
457 """
458 cells = {}
459 for name in self.cellvars:
460 cells[name] = 1
461 self.cellvars = [name for name in self.varnames
462 if cells.has_key(name)]
463 for name in self.cellvars:
464 del cells[name]
465 self.cellvars = self.cellvars + cells.keys()
466 self.closure = self.cellvars + self.freevars
467
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000468 def _lookupName(self, name, list):
469 """Return index of name in list, appending if necessary
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000470
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000471 This routine uses a list instead of a dictionary, because a
472 dictionary can't store two different keys if the keys have the
473 same value but different types, e.g. 2 and 2L. The compiler
474 must treat these two separately, so it does an explicit type
475 comparison before comparing the values.
476 """
Jeremy Hylton9c048f92000-10-13 21:58:13 +0000477 t = type(name)
478 for i in range(len(list)):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000479 if t == type(list[i]) and list[i] == name:
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000480 return i
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000481 end = len(list)
482 list.append(name)
483 return end
Jeremy Hyltona5058122000-02-14 14:14:29 +0000484
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000485 _converters = {}
486 def _convert_LOAD_CONST(self, arg):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000487 if hasattr(arg, 'getCode'):
488 arg = arg.getCode()
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000489 return self._lookupName(arg, self.consts)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000490
491 def _convert_LOAD_FAST(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000492 self._lookupName(arg, self.names)
493 return self._lookupName(arg, self.varnames)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000494 _convert_STORE_FAST = _convert_LOAD_FAST
495 _convert_DELETE_FAST = _convert_LOAD_FAST
496
Jeremy Hylton63db7b92001-08-28 16:36:12 +0000497 def _convert_LOAD_NAME(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000498 if self.klass is None:
499 self._lookupName(arg, self.varnames)
500 return self._lookupName(arg, self.names)
Jeremy Hylton63db7b92001-08-28 16:36:12 +0000501
502 def _convert_NAME(self, arg):
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000503 if self.klass is None:
504 self._lookupName(arg, self.varnames)
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000505 return self._lookupName(arg, self.names)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000506 _convert_STORE_NAME = _convert_NAME
507 _convert_DELETE_NAME = _convert_NAME
508 _convert_IMPORT_NAME = _convert_NAME
509 _convert_IMPORT_FROM = _convert_NAME
510 _convert_STORE_ATTR = _convert_NAME
511 _convert_LOAD_ATTR = _convert_NAME
512 _convert_DELETE_ATTR = _convert_NAME
513 _convert_LOAD_GLOBAL = _convert_NAME
514 _convert_STORE_GLOBAL = _convert_NAME
515 _convert_DELETE_GLOBAL = _convert_NAME
516
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000517 def _convert_DEREF(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000518 self._lookupName(arg, self.names)
519 self._lookupName(arg, self.varnames)
520 return self._lookupName(arg, self.closure)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000521 _convert_LOAD_DEREF = _convert_DEREF
522 _convert_STORE_DEREF = _convert_DEREF
523
524 def _convert_LOAD_CLOSURE(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000525 self._lookupName(arg, self.varnames)
526 return self._lookupName(arg, self.closure)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000527
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000528 _cmp = list(dis.cmp_op)
529 def _convert_COMPARE_OP(self, arg):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000530 return self._cmp.index(arg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000531
532 # similarly for other opcodes...
533
534 for name, obj in locals().items():
535 if name[:9] == "_convert_":
536 opname = name[9:]
537 _converters[opname] = obj
538 del name, obj, opname
539
540 def makeByteCode(self):
541 assert self.stage == CONV
542 self.lnotab = lnotab = LineAddrTable()
543 for t in self.insts:
544 opname = t[0]
545 if len(t) == 1:
546 lnotab.addCode(self.opnum[opname])
547 else:
548 oparg = t[1]
549 if opname == "SET_LINENO":
550 lnotab.nextLine(oparg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000551 hi, lo = twobyte(oparg)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000552 try:
553 lnotab.addCode(self.opnum[opname], lo, hi)
554 except ValueError:
555 print opname, oparg
556 print self.opnum[opname], lo, hi
557 raise
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000558 self.stage = DONE
559
Jeremy Hyltona5058122000-02-14 14:14:29 +0000560 opnum = {}
561 for num in range(len(dis.opname)):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000562 opnum[dis.opname[num]] = num
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000563 del num
Jeremy Hyltona5058122000-02-14 14:14:29 +0000564
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000565 def newCodeObject(self):
566 assert self.stage == DONE
567 if self.flags == 0:
568 nlocals = 0
569 else:
570 nlocals = len(self.varnames)
571 argcount = self.argcount
572 if self.flags & CO_VARKEYWORDS:
573 argcount = argcount - 1
574 return new.code(argcount, nlocals, self.stacksize, self.flags,
575 self.lnotab.getCode(), self.getConsts(),
576 tuple(self.names), tuple(self.varnames),
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000577 self.filename, self.name, self.lnotab.firstline,
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000578 self.lnotab.getTable(), tuple(self.freevars),
579 tuple(self.cellvars))
Jeremy Hyltona5058122000-02-14 14:14:29 +0000580
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000581 def getConsts(self):
582 """Return a tuple for the const slot of the code object
Jeremy Hyltona5058122000-02-14 14:14:29 +0000583
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000584 Must convert references to code (MAKE_FUNCTION) to code
585 objects recursively.
586 """
587 l = []
588 for elt in self.consts:
589 if isinstance(elt, PyFlowGraph):
590 elt = elt.getCode()
591 l.append(elt)
592 return tuple(l)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000593
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000594def isJump(opname):
595 if opname[:4] == 'JUMP':
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000596 return 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000597
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000598class TupleArg:
599 """Helper for marking func defs with nested tuples in arglist"""
600 def __init__(self, count, names):
601 self.count = count
602 self.names = names
603 def __repr__(self):
604 return "TupleArg(%s, %s)" % (self.count, self.names)
605 def getName(self):
Jeremy Hyltonbfb0cf82001-04-12 17:33:34 +0000606 return ".%d" % self.count
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000607
608def getArgCount(args):
609 argcount = len(args)
610 if args:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000611 for arg in args:
612 if isinstance(arg, TupleArg):
613 numNames = len(misc.flatten(arg.names))
614 argcount = argcount - numNames
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000615 return argcount
616
617def twobyte(val):
618 """Convert an int argument into high and low bytes"""
619 assert type(val) == types.IntType
620 return divmod(val, 256)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000621
622class LineAddrTable:
623 """lnotab
624
Tim Peters2a7f3842001-06-09 09:26:21 +0000625 This class builds the lnotab, which is documented in compile.c.
626 Here's a brief recap:
Jeremy Hyltona5058122000-02-14 14:14:29 +0000627
628 For each SET_LINENO instruction after the first one, two bytes are
629 added to lnotab. (In some cases, multiple two-byte entries are
630 added.) The first byte is the distance in bytes between the
631 instruction for the last SET_LINENO and the current SET_LINENO.
632 The second byte is offset in line numbers. If either offset is
Tim Peters2a7f3842001-06-09 09:26:21 +0000633 greater than 255, multiple two-byte entries are added -- see
634 compile.c for the delicate details.
Jeremy Hyltona5058122000-02-14 14:14:29 +0000635 """
636
637 def __init__(self):
638 self.code = []
639 self.codeOffset = 0
640 self.firstline = 0
641 self.lastline = 0
642 self.lastoff = 0
643 self.lnotab = []
644
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000645 def addCode(self, *args):
646 for arg in args:
647 self.code.append(chr(arg))
648 self.codeOffset = self.codeOffset + len(args)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000649
650 def nextLine(self, lineno):
651 if self.firstline == 0:
652 self.firstline = lineno
653 self.lastline = lineno
654 else:
655 # compute deltas
656 addr = self.codeOffset - self.lastoff
657 line = lineno - self.lastline
Jeremy Hylton92f39722000-09-01 20:47:37 +0000658 # Python assumes that lineno always increases with
659 # increasing bytecode address (lnotab is unsigned char).
660 # Depending on when SET_LINENO instructions are emitted
661 # this is not always true. Consider the code:
662 # a = (1,
663 # b)
664 # In the bytecode stream, the assignment to "a" occurs
665 # after the loading of "b". This works with the C Python
666 # compiler because it only generates a SET_LINENO instruction
667 # for the assignment.
668 if line > 0:
Tim Peters2a7f3842001-06-09 09:26:21 +0000669 push = self.lnotab.append
670 while addr > 255:
671 push(255); push(0)
672 addr -= 255
673 while line > 255:
674 push(addr); push(255)
675 line -= 255
676 addr = 0
677 if addr > 0 or line > 0:
678 push(addr); push(line)
Jeremy Hylton92f39722000-09-01 20:47:37 +0000679 self.lastline = lineno
680 self.lastoff = self.codeOffset
Jeremy Hyltona5058122000-02-14 14:14:29 +0000681
682 def getCode(self):
683 return string.join(self.code, '')
684
685 def getTable(self):
686 return string.join(map(chr, self.lnotab), '')
687
Jeremy Hyltona5058122000-02-14 14:14:29 +0000688class StackDepthTracker:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000689 # XXX 1. need to keep track of stack depth on jumps
690 # XXX 2. at least partly as a result, this code is broken
Jeremy Hyltona5058122000-02-14 14:14:29 +0000691
692 def findDepth(self, insts):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000693 depth = 0
694 maxDepth = 0
695 for i in insts:
696 opname = i[0]
697 delta = self.effect.get(opname, 0)
698 if delta > 1:
699 depth = depth + delta
700 elif delta < 0:
701 if depth > maxDepth:
702 maxDepth = depth
703 depth = depth + delta
704 else:
705 if depth > maxDepth:
706 maxDepth = depth
707 # now check patterns
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000708 for pat, pat_delta in self.patterns:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000709 if opname[:len(pat)] == pat:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000710 delta = pat_delta
Jeremy Hylton772dd412000-02-21 22:46:00 +0000711 depth = depth + delta
712 break
713 # if we still haven't found a match
714 if delta == 0:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000715 meth = getattr(self, opname, None)
716 if meth is not None:
717 depth = depth + meth(i[1])
Jeremy Hylton772dd412000-02-21 22:46:00 +0000718 if depth < 0:
719 depth = 0
720 return maxDepth
Jeremy Hyltona5058122000-02-14 14:14:29 +0000721
722 effect = {
Jeremy Hylton772dd412000-02-21 22:46:00 +0000723 'POP_TOP': -1,
724 'DUP_TOP': 1,
725 'SLICE+1': -1,
726 'SLICE+2': -1,
727 'SLICE+3': -2,
728 'STORE_SLICE+0': -1,
729 'STORE_SLICE+1': -2,
730 'STORE_SLICE+2': -2,
731 'STORE_SLICE+3': -3,
732 'DELETE_SLICE+0': -1,
733 'DELETE_SLICE+1': -2,
734 'DELETE_SLICE+2': -2,
735 'DELETE_SLICE+3': -3,
736 'STORE_SUBSCR': -3,
737 'DELETE_SUBSCR': -2,
738 # PRINT_EXPR?
739 'PRINT_ITEM': -1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000740 'RETURN_VALUE': -1,
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000741 'EXEC_STMT': -3,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000742 'BUILD_CLASS': -2,
743 'STORE_NAME': -1,
744 'STORE_ATTR': -2,
745 'DELETE_ATTR': -1,
746 'STORE_GLOBAL': -1,
747 'BUILD_MAP': 1,
748 'COMPARE_OP': -1,
749 'STORE_FAST': -1,
Jeremy Hylton4e1be722000-10-12 20:23:23 +0000750 'IMPORT_STAR': -1,
751 'IMPORT_NAME': 0,
752 'IMPORT_FROM': 1,
Jeremy Hylton92638482001-08-29 22:27:14 +0000753 # close enough...
754 'SETUP_EXCEPT': 3,
755 'SETUP_FINALLY': 3,
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000756 'FOR_ITER': 1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000757 }
Jeremy Hyltona5058122000-02-14 14:14:29 +0000758 # use pattern match
759 patterns = [
Jeremy Hylton772dd412000-02-21 22:46:00 +0000760 ('BINARY_', -1),
761 ('LOAD_', 1),
Jeremy Hylton772dd412000-02-21 22:46:00 +0000762 ]
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000763
764 # special cases:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000765 # UNPACK_SEQUENCE, BUILD_TUPLE,
Jeremy Hyltona5058122000-02-14 14:14:29 +0000766 # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000767 def UNPACK_SEQUENCE(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000768 return count-1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000769 def BUILD_TUPLE(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000770 return -count+1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000771 def BUILD_LIST(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000772 return -count+1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000773 def CALL_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000774 hi, lo = divmod(argc, 256)
775 return lo + hi * 2
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000776 def CALL_FUNCTION_VAR(self, argc):
777 return self.CALL_FUNCTION(argc)+1
778 def CALL_FUNCTION_KW(self, argc):
779 return self.CALL_FUNCTION(argc)+1
780 def CALL_FUNCTION_VAR_KW(self, argc):
781 return self.CALL_FUNCTION(argc)+2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000782 def MAKE_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000783 return -argc
Jeremy Hyltona5058122000-02-14 14:14:29 +0000784 def BUILD_SLICE(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000785 if argc == 2:
786 return -1
787 elif argc == 3:
788 return -2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000789
790findDepth = StackDepthTracker().findDepth