blob: e8810aee00c12a0947ed6e1736dc901dda2ab2fe [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
10
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000011def xxx_sort(l):
12 l = l[:]
13 def sorter(a, b):
14 return cmp(a.bid, b.bid)
15 l.sort(sorter)
16 return l
17
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000018class FlowGraph:
19 def __init__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000020 self.current = self.entry = Block()
21 self.exit = Block("exit")
22 self.blocks = misc.Set()
23 self.blocks.add(self.entry)
24 self.blocks.add(self.exit)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000025
26 def startBlock(self, block):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000027 if self._debug:
28 if self.current:
29 print "end", repr(self.current)
Jeremy Hylton542b11a2001-04-12 20:21:39 +000030 print " next", self.current.next
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000031 print " ", self.current.get_children()
32 print repr(block)
Thomas Wouters46cc7c02000-08-12 20:32:46 +000033 self.current = block
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000034
Jeremy Hylton542b11a2001-04-12 20:21:39 +000035 def nextBlock(self, block=None):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000036 # XXX think we need to specify when there is implicit transfer
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000037 # from one block to the next. might be better to represent this
38 # with explicit JUMP_ABSOLUTE instructions that are optimized
39 # out when they are unnecessary.
Thomas Wouters46cc7c02000-08-12 20:32:46 +000040 #
41 # I think this strategy works: each block has a child
42 # designated as "next" which is returned as the last of the
43 # children. because the nodes in a graph are emitted in
44 # reverse post order, the "next" block will always be emitted
45 # immediately after its parent.
46 # Worry: maintaining this invariant could be tricky
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000047 if block is None:
48 block = self.newBlock()
49
50 # Note: If the current block ends with an unconditional
51 # control transfer, then it is incorrect to add an implicit
52 # transfer to the block graph. The current code requires
53 # these edges to get the blocks emitted in the right order,
54 # however. :-( If a client needs to remove these edges, call
55 # pruneEdges().
56
Thomas Wouters46cc7c02000-08-12 20:32:46 +000057 self.current.addNext(block)
58 self.startBlock(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000059
60 def newBlock(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000061 b = Block()
62 self.blocks.add(b)
63 return b
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000064
65 def startExitBlock(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000066 self.startBlock(self.exit)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000067
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000068 _debug = 0
69
70 def _enable_debug(self):
71 self._debug = 1
72
73 def _disable_debug(self):
74 self._debug = 0
75
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000076 def emit(self, *inst):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000077 if self._debug:
78 print "\t", inst
Thomas Wouters46cc7c02000-08-12 20:32:46 +000079 if inst[0] == 'RETURN_VALUE':
80 self.current.addOutEdge(self.exit)
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000081 if len(inst) == 2 and isinstance(inst[1], Block):
82 self.current.addOutEdge(inst[1])
Thomas Wouters46cc7c02000-08-12 20:32:46 +000083 self.current.emit(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000084
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000085 def getBlocksInOrder(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000086 """Return the blocks in reverse postorder
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000087
Thomas Wouters46cc7c02000-08-12 20:32:46 +000088 i.e. each node appears before all of its successors
89 """
90 # XXX make sure every node that doesn't have an explicit next
91 # is set so that next points to exit
92 for b in self.blocks.elements():
93 if b is self.exit:
94 continue
95 if not b.next:
96 b.addNext(self.exit)
97 order = dfs_postorder(self.entry, {})
98 order.reverse()
Jeremy Hylton542b11a2001-04-12 20:21:39 +000099 self.fixupOrder(order, self.exit)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000100 # hack alert
101 if not self.exit in order:
102 order.append(self.exit)
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000103
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000104 return order
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000105
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000106 def fixupOrder(self, blocks, default_next):
107 """Fixup bad order introduced by DFS."""
108
109 # XXX This is a total mess. There must be a better way to get
110 # the code blocks in the right order.
111
112 self.fixupOrderHonorNext(blocks, default_next)
113 self.fixupOrderForward(blocks, default_next)
114
115 def fixupOrderHonorNext(self, blocks, default_next):
116 """Fix one problem with DFS.
117
118 The DFS uses child block, but doesn't know about the special
119 "next" block. As a result, the DFS can order blocks so that a
120 block isn't next to the right block for implicit control
121 transfers.
122 """
123 index = {}
124 for i in range(len(blocks)):
125 index[blocks[i]] = i
126
127 for i in range(0, len(blocks) - 1):
128 b = blocks[i]
129 n = blocks[i + 1]
130 if not b.next or b.next[0] == default_next or b.next[0] == n:
131 continue
132 # The blocks are in the wrong order. Find the chain of
133 # blocks to insert where they belong.
134 cur = b
135 chain = []
136 elt = cur
137 while elt.next and elt.next[0] != default_next:
138 chain.append(elt.next[0])
139 elt = elt.next[0]
140 # Now remove the blocks in the chain from the current
141 # block list, so that they can be re-inserted.
142 l = []
143 for b in chain:
144 assert index[b] > i
145 l.append((index[b], b))
146 l.sort()
147 l.reverse()
148 for j, b in l:
149 del blocks[index[b]]
150 # Insert the chain in the proper location
151 blocks[i:i + 1] = [cur] + chain
152 # Finally, re-compute the block indexes
153 for i in range(len(blocks)):
154 index[blocks[i]] = i
155
156 def fixupOrderForward(self, blocks, default_next):
157 """Make sure all JUMP_FORWARDs jump forward"""
158 index = {}
159 chains = []
160 cur = []
161 for b in blocks:
162 index[b] = len(chains)
163 cur.append(b)
164 if b.next and b.next[0] == default_next:
165 chains.append(cur)
166 cur = []
167 chains.append(cur)
168
169 while 1:
170 constraints = []
171
172 for i in range(len(chains)):
173 l = chains[i]
174 for b in l:
175 for c in b.get_children():
176 if index[c] < i:
177 forward_p = 0
178 for inst in b.insts:
179 if inst[0] == 'JUMP_FORWARD':
180 if inst[1] == c:
181 forward_p = 1
182 if not forward_p:
183 continue
184 constraints.append((index[c], i))
185
186 if not constraints:
187 break
188
189 # XXX just do one for now
190 # do swaps to get things in the right order
191 goes_before, a_chain = constraints[0]
192 assert a_chain > goes_before
193 c = chains[a_chain]
194 chains.remove(c)
195 chains.insert(goes_before, c)
196
197
198 del blocks[:]
199 for c in chains:
200 for b in c:
201 blocks.append(b)
202
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000203 def getBlocks(self):
204 return self.blocks.elements()
205
206 def getRoot(self):
207 """Return nodes appropriate for use with dominator"""
208 return self.entry
209
210 def getContainedGraphs(self):
211 l = []
212 for b in self.getBlocks():
213 l.extend(b.getContainedGraphs())
214 return l
215
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000216def dfs_postorder(b, seen):
217 """Depth-first search of tree rooted at b, return in postorder"""
218 order = []
219 seen[b] = b
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000220 for c in b.get_children():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000221 if seen.has_key(c):
222 continue
223 order = order + dfs_postorder(c, seen)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000224 order.append(b)
225 return order
226
227class Block:
228 _count = 0
229
230 def __init__(self, label=''):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000231 self.insts = []
232 self.inEdges = misc.Set()
233 self.outEdges = misc.Set()
234 self.label = label
235 self.bid = Block._count
236 self.next = []
237 Block._count = Block._count + 1
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000238
239 def __repr__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000240 if self.label:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000241 return "<block %s id=%d>" % (self.label, self.bid)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000242 else:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000243 return "<block id=%d>" % (self.bid)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000244
245 def __str__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000246 insts = map(str, self.insts)
247 return "<block %s %d:\n%s>" % (self.label, self.bid,
248 string.join(insts, '\n'))
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000249
250 def emit(self, inst):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000251 op = inst[0]
252 if op[:4] == 'JUMP':
253 self.outEdges.add(inst[1])
254 self.insts.append(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000255
256 def getInstructions(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000257 return self.insts
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000258
259 def addInEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000260 self.inEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000261
262 def addOutEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000263 self.outEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000264
265 def addNext(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000266 self.next.append(block)
267 assert len(self.next) == 1, map(str, self.next)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000268
Jeremy Hyltoneefaeb72000-11-06 03:47:39 +0000269 _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS',
270 'JUMP_ABSOLUTE', 'JUMP_FORWARD')
271
272 def pruneNext(self):
273 """Remove bogus edge for unconditional transfers
274
275 Each block has a next edge that accounts for implicit control
276 transfers, e.g. from a JUMP_IF_FALSE to the block that will be
277 executed if the test is true.
278
279 These edges must remain for the current assembler code to
280 work. If they are removed, the dfs_postorder gets things in
281 weird orders. However, they shouldn't be there for other
282 purposes, e.g. conversion to SSA form. This method will
283 remove the next edge when it follows an unconditional control
284 transfer.
285 """
286 try:
287 op, arg = self.insts[-1]
288 except (IndexError, ValueError):
289 return
290 if op in self._uncond_transfer:
291 self.next = []
292
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000293 def get_children(self):
294 if self.next and self.next[0] in self.outEdges:
295 self.outEdges.remove(self.next[0])
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000296 return self.outEdges.elements() + self.next
Jeremy Hyltona5058122000-02-14 14:14:29 +0000297
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000298 def getContainedGraphs(self):
299 """Return all graphs contained within this block.
300
301 For example, a MAKE_FUNCTION block will contain a reference to
302 the graph for the function body.
303 """
304 contained = []
305 for inst in self.insts:
306 if len(inst) == 1:
307 continue
308 op = inst[1]
309 if hasattr(op, 'graph'):
310 contained.append(op.graph)
311 return contained
312
Jeremy Hyltona5058122000-02-14 14:14:29 +0000313# flags for code objects
314CO_OPTIMIZED = 0x0001
315CO_NEWLOCALS = 0x0002
316CO_VARARGS = 0x0004
317CO_VARKEYWORDS = 0x0008
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000318CO_NESTED = 0x0010
Jeremy Hyltona5058122000-02-14 14:14:29 +0000319
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000320# the FlowGraph is transformed in place; it exists in one of these states
321RAW = "RAW"
322FLAT = "FLAT"
323CONV = "CONV"
324DONE = "DONE"
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000325
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000326class PyFlowGraph(FlowGraph):
327 super_init = FlowGraph.__init__
Jeremy Hyltona5058122000-02-14 14:14:29 +0000328
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000329 def __init__(self, name, filename, args=(), optimized=0):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000330 self.super_init()
331 self.name = name
332 self.filename = filename
333 self.docstring = None
334 self.args = args # XXX
335 self.argcount = getArgCount(args)
336 if optimized:
337 self.flags = CO_OPTIMIZED | CO_NEWLOCALS
338 else:
339 self.flags = 0
340 self.consts = []
341 self.names = []
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000342 # Free variables found by the symbol table scan, including
343 # variables used only in nested scopes, are included here.
344 self.freevars = []
345 self.cellvars = []
346 # The closure list is used to track the order of cell
347 # variables and free variables in the resulting code object.
348 # The offsets used by LOAD_CLOSURE/LOAD_DEREF refer to both
349 # kinds of variables.
350 self.closure = []
Jeremy Hyltona5058122000-02-14 14:14:29 +0000351 self.varnames = list(args) or []
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000352 for i in range(len(self.varnames)):
353 var = self.varnames[i]
354 if isinstance(var, TupleArg):
355 self.varnames[i] = var.getName()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000356 self.stage = RAW
Jeremy Hyltona5058122000-02-14 14:14:29 +0000357
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000358 def setDocstring(self, doc):
359 self.docstring = doc
Jeremy Hylton2ce27b22000-02-16 00:50:29 +0000360
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000361 def setFlag(self, flag):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000362 self.flags = self.flags | flag
363 if flag == CO_VARARGS:
364 self.argcount = self.argcount - 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000365
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000366 def setFreeVars(self, names):
367 self.freevars = list(names)
368
369 def setCellVars(self, names):
370 self.cellvars = names
371
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000372 def getCode(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000373 """Get a Python code object"""
374 if self.stage == RAW:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000375 self.flattenGraph()
376 if self.stage == FLAT:
377 self.convertArgs()
378 if self.stage == CONV:
379 self.makeByteCode()
380 if self.stage == DONE:
381 return self.newCodeObject()
382 raise RuntimeError, "inconsistent PyFlowGraph state"
Jeremy Hyltona5058122000-02-14 14:14:29 +0000383
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000384 def dump(self, io=None):
385 if io:
386 save = sys.stdout
387 sys.stdout = io
388 pc = 0
Jeremy Hyltona5058122000-02-14 14:14:29 +0000389 for t in self.insts:
390 opname = t[0]
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000391 if opname == "SET_LINENO":
392 print
Jeremy Hyltona5058122000-02-14 14:14:29 +0000393 if len(t) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000394 print "\t", "%3d" % pc, opname
395 pc = pc + 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000396 else:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000397 print "\t", "%3d" % pc, opname, t[1]
398 pc = pc + 3
399 if io:
400 sys.stdout = save
Jeremy Hyltona5058122000-02-14 14:14:29 +0000401
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000402 def flattenGraph(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000403 """Arrange the blocks in order and resolve jumps"""
404 assert self.stage == RAW
405 self.insts = insts = []
406 pc = 0
407 begin = {}
408 end = {}
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000409 for b in self.getBlocksInOrder():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000410 begin[b] = pc
411 for inst in b.getInstructions():
412 insts.append(inst)
413 if len(inst) == 1:
414 pc = pc + 1
415 else:
416 # arg takes 2 bytes
417 pc = pc + 3
418 end[b] = pc
419 pc = 0
420 for i in range(len(insts)):
421 inst = insts[i]
422 if len(inst) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000423 pc = pc + 1
424 else:
425 pc = pc + 3
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000426 opname = inst[0]
427 if self.hasjrel.has_elt(opname):
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000428 oparg = inst[1]
429 offset = begin[oparg] - pc
430 insts[i] = opname, offset
431 elif self.hasjabs.has_elt(opname):
432 insts[i] = opname, begin[inst[1]]
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000433 self.stacksize = findDepth(self.insts)
434 self.stage = FLAT
Jeremy Hyltona5058122000-02-14 14:14:29 +0000435
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000436 hasjrel = misc.Set()
437 for i in dis.hasjrel:
438 hasjrel.add(dis.opname[i])
439 hasjabs = misc.Set()
440 for i in dis.hasjabs:
441 hasjabs.add(dis.opname[i])
Jeremy Hyltona5058122000-02-14 14:14:29 +0000442
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000443 def convertArgs(self):
444 """Convert arguments from symbolic to concrete form"""
445 assert self.stage == FLAT
Jeremy Hylton5af105e2001-04-11 16:21:51 +0000446 self.consts.insert(0, self.docstring)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000447 self.sort_cellvars()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000448 for i in range(len(self.insts)):
449 t = self.insts[i]
450 if len(t) == 2:
451 opname = t[0]
452 oparg = t[1]
453 conv = self._converters.get(opname, None)
454 if conv:
455 self.insts[i] = opname, conv(self, oparg)
456 self.stage = CONV
Jeremy Hyltona5058122000-02-14 14:14:29 +0000457
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000458 def sort_cellvars(self):
459 """Sort cellvars in the order of varnames and prune from freevars.
460 """
461 cells = {}
462 for name in self.cellvars:
463 cells[name] = 1
464 self.cellvars = [name for name in self.varnames
465 if cells.has_key(name)]
466 for name in self.cellvars:
467 del cells[name]
468 self.cellvars = self.cellvars + cells.keys()
469 self.closure = self.cellvars + self.freevars
470
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000471 def _lookupName(self, name, list):
472 """Return index of name in list, appending if necessary"""
Jeremy Hylton9c048f92000-10-13 21:58:13 +0000473 t = type(name)
474 for i in range(len(list)):
475 # must do a comparison on type first to prevent UnicodeErrors
476 if t == type(list[i]) and list[i] == name:
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000477 return i
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000478 end = len(list)
479 list.append(name)
480 return end
Jeremy Hyltona5058122000-02-14 14:14:29 +0000481
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000482 _converters = {}
483 def _convert_LOAD_CONST(self, arg):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000484 if hasattr(arg, 'getCode'):
485 arg = arg.getCode()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000486 return self._lookupName(arg, self.consts)
487
488 def _convert_LOAD_FAST(self, arg):
489 self._lookupName(arg, self.names)
490 return self._lookupName(arg, self.varnames)
491 _convert_STORE_FAST = _convert_LOAD_FAST
492 _convert_DELETE_FAST = _convert_LOAD_FAST
493
494 def _convert_NAME(self, arg):
495 return self._lookupName(arg, self.names)
496 _convert_LOAD_NAME = _convert_NAME
497 _convert_STORE_NAME = _convert_NAME
498 _convert_DELETE_NAME = _convert_NAME
499 _convert_IMPORT_NAME = _convert_NAME
500 _convert_IMPORT_FROM = _convert_NAME
501 _convert_STORE_ATTR = _convert_NAME
502 _convert_LOAD_ATTR = _convert_NAME
503 _convert_DELETE_ATTR = _convert_NAME
504 _convert_LOAD_GLOBAL = _convert_NAME
505 _convert_STORE_GLOBAL = _convert_NAME
506 _convert_DELETE_GLOBAL = _convert_NAME
507
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000508 def _convert_DEREF(self, arg):
509 self._lookupName(arg, self.names)
510 self._lookupName(arg, self.varnames)
511 return self._lookupName(arg, self.closure)
512 _convert_LOAD_DEREF = _convert_DEREF
513 _convert_STORE_DEREF = _convert_DEREF
514
515 def _convert_LOAD_CLOSURE(self, arg):
516 self._lookupName(arg, self.varnames)
517 return self._lookupName(arg, self.closure)
518
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000519 _cmp = list(dis.cmp_op)
520 def _convert_COMPARE_OP(self, arg):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000521 return self._cmp.index(arg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000522
523 # similarly for other opcodes...
524
525 for name, obj in locals().items():
526 if name[:9] == "_convert_":
527 opname = name[9:]
528 _converters[opname] = obj
529 del name, obj, opname
530
531 def makeByteCode(self):
532 assert self.stage == CONV
533 self.lnotab = lnotab = LineAddrTable()
534 for t in self.insts:
535 opname = t[0]
536 if len(t) == 1:
537 lnotab.addCode(self.opnum[opname])
538 else:
539 oparg = t[1]
540 if opname == "SET_LINENO":
541 lnotab.nextLine(oparg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000542 hi, lo = twobyte(oparg)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000543 try:
544 lnotab.addCode(self.opnum[opname], lo, hi)
545 except ValueError:
546 print opname, oparg
547 print self.opnum[opname], lo, hi
548 raise
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000549 self.stage = DONE
550
Jeremy Hyltona5058122000-02-14 14:14:29 +0000551 opnum = {}
552 for num in range(len(dis.opname)):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000553 opnum[dis.opname[num]] = num
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000554 del num
Jeremy Hyltona5058122000-02-14 14:14:29 +0000555
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000556 def newCodeObject(self):
557 assert self.stage == DONE
558 if self.flags == 0:
559 nlocals = 0
560 else:
561 nlocals = len(self.varnames)
562 argcount = self.argcount
563 if self.flags & CO_VARKEYWORDS:
564 argcount = argcount - 1
565 return new.code(argcount, nlocals, self.stacksize, self.flags,
566 self.lnotab.getCode(), self.getConsts(),
567 tuple(self.names), tuple(self.varnames),
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000568 self.filename, self.name, self.lnotab.firstline,
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000569 self.lnotab.getTable(), tuple(self.freevars),
570 tuple(self.cellvars))
Jeremy Hyltona5058122000-02-14 14:14:29 +0000571
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000572 def getConsts(self):
573 """Return a tuple for the const slot of the code object
Jeremy Hyltona5058122000-02-14 14:14:29 +0000574
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000575 Must convert references to code (MAKE_FUNCTION) to code
576 objects recursively.
577 """
578 l = []
579 for elt in self.consts:
580 if isinstance(elt, PyFlowGraph):
581 elt = elt.getCode()
582 l.append(elt)
583 return tuple(l)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000584
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000585def isJump(opname):
586 if opname[:4] == 'JUMP':
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000587 return 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000588
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000589class TupleArg:
590 """Helper for marking func defs with nested tuples in arglist"""
591 def __init__(self, count, names):
592 self.count = count
593 self.names = names
594 def __repr__(self):
595 return "TupleArg(%s, %s)" % (self.count, self.names)
596 def getName(self):
Jeremy Hyltonbfb0cf82001-04-12 17:33:34 +0000597 return ".%d" % self.count
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000598
599def getArgCount(args):
600 argcount = len(args)
601 if args:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000602 for arg in args:
603 if isinstance(arg, TupleArg):
604 numNames = len(misc.flatten(arg.names))
605 argcount = argcount - numNames
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000606 return argcount
607
608def twobyte(val):
609 """Convert an int argument into high and low bytes"""
610 assert type(val) == types.IntType
611 return divmod(val, 256)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000612
613class LineAddrTable:
614 """lnotab
615
616 This class builds the lnotab, which is undocumented but described
617 by com_set_lineno in compile.c. Here's an attempt at explanation:
618
619 For each SET_LINENO instruction after the first one, two bytes are
620 added to lnotab. (In some cases, multiple two-byte entries are
621 added.) The first byte is the distance in bytes between the
622 instruction for the last SET_LINENO and the current SET_LINENO.
623 The second byte is offset in line numbers. If either offset is
624 greater than 255, multiple two-byte entries are added -- one entry
625 for each factor of 255.
626 """
627
628 def __init__(self):
629 self.code = []
630 self.codeOffset = 0
631 self.firstline = 0
632 self.lastline = 0
633 self.lastoff = 0
634 self.lnotab = []
635
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000636 def addCode(self, *args):
637 for arg in args:
638 self.code.append(chr(arg))
639 self.codeOffset = self.codeOffset + len(args)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000640
641 def nextLine(self, lineno):
642 if self.firstline == 0:
643 self.firstline = lineno
644 self.lastline = lineno
645 else:
646 # compute deltas
647 addr = self.codeOffset - self.lastoff
648 line = lineno - self.lastline
Jeremy Hylton92f39722000-09-01 20:47:37 +0000649 # Python assumes that lineno always increases with
650 # increasing bytecode address (lnotab is unsigned char).
651 # Depending on when SET_LINENO instructions are emitted
652 # this is not always true. Consider the code:
653 # a = (1,
654 # b)
655 # In the bytecode stream, the assignment to "a" occurs
656 # after the loading of "b". This works with the C Python
657 # compiler because it only generates a SET_LINENO instruction
658 # for the assignment.
659 if line > 0:
660 while addr > 0 or line > 0:
661 # write the values in 1-byte chunks that sum
662 # to desired value
663 trunc_addr = addr
664 trunc_line = line
665 if trunc_addr > 255:
666 trunc_addr = 255
667 if trunc_line > 255:
668 trunc_line = 255
669 self.lnotab.append(trunc_addr)
670 self.lnotab.append(trunc_line)
671 addr = addr - trunc_addr
672 line = line - trunc_line
673 self.lastline = lineno
674 self.lastoff = self.codeOffset
Jeremy Hyltona5058122000-02-14 14:14:29 +0000675
676 def getCode(self):
677 return string.join(self.code, '')
678
679 def getTable(self):
680 return string.join(map(chr, self.lnotab), '')
681
Jeremy Hyltona5058122000-02-14 14:14:29 +0000682class StackDepthTracker:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000683 # XXX 1. need to keep track of stack depth on jumps
684 # XXX 2. at least partly as a result, this code is broken
Jeremy Hyltona5058122000-02-14 14:14:29 +0000685
686 def findDepth(self, insts):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000687 depth = 0
688 maxDepth = 0
689 for i in insts:
690 opname = i[0]
691 delta = self.effect.get(opname, 0)
692 if delta > 1:
693 depth = depth + delta
694 elif delta < 0:
695 if depth > maxDepth:
696 maxDepth = depth
697 depth = depth + delta
698 else:
699 if depth > maxDepth:
700 maxDepth = depth
701 # now check patterns
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000702 for pat, pat_delta in self.patterns:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000703 if opname[:len(pat)] == pat:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000704 delta = pat_delta
Jeremy Hylton772dd412000-02-21 22:46:00 +0000705 depth = depth + delta
706 break
707 # if we still haven't found a match
708 if delta == 0:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000709 meth = getattr(self, opname, None)
710 if meth is not None:
711 depth = depth + meth(i[1])
Jeremy Hylton772dd412000-02-21 22:46:00 +0000712 if depth < 0:
713 depth = 0
714 return maxDepth
Jeremy Hyltona5058122000-02-14 14:14:29 +0000715
716 effect = {
Jeremy Hylton772dd412000-02-21 22:46:00 +0000717 'POP_TOP': -1,
718 'DUP_TOP': 1,
719 'SLICE+1': -1,
720 'SLICE+2': -1,
721 'SLICE+3': -2,
722 'STORE_SLICE+0': -1,
723 'STORE_SLICE+1': -2,
724 'STORE_SLICE+2': -2,
725 'STORE_SLICE+3': -3,
726 'DELETE_SLICE+0': -1,
727 'DELETE_SLICE+1': -2,
728 'DELETE_SLICE+2': -2,
729 'DELETE_SLICE+3': -3,
730 'STORE_SUBSCR': -3,
731 'DELETE_SUBSCR': -2,
732 # PRINT_EXPR?
733 'PRINT_ITEM': -1,
734 'LOAD_LOCALS': 1,
735 'RETURN_VALUE': -1,
736 'EXEC_STMT': -2,
737 'BUILD_CLASS': -2,
738 'STORE_NAME': -1,
739 'STORE_ATTR': -2,
740 'DELETE_ATTR': -1,
741 'STORE_GLOBAL': -1,
742 'BUILD_MAP': 1,
743 'COMPARE_OP': -1,
744 'STORE_FAST': -1,
Jeremy Hylton4e1be722000-10-12 20:23:23 +0000745 'IMPORT_STAR': -1,
746 'IMPORT_NAME': 0,
747 'IMPORT_FROM': 1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000748 }
Jeremy Hyltona5058122000-02-14 14:14:29 +0000749 # use pattern match
750 patterns = [
Jeremy Hylton772dd412000-02-21 22:46:00 +0000751 ('BINARY_', -1),
752 ('LOAD_', 1),
Jeremy Hylton772dd412000-02-21 22:46:00 +0000753 ]
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000754
755 # special cases:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000756 # UNPACK_SEQUENCE, BUILD_TUPLE,
Jeremy Hyltona5058122000-02-14 14:14:29 +0000757 # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000758 def UNPACK_SEQUENCE(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000759 return count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000760 def BUILD_TUPLE(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000761 return -count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000762 def BUILD_LIST(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000763 return -count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000764 def CALL_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000765 hi, lo = divmod(argc, 256)
766 return lo + hi * 2
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000767 def CALL_FUNCTION_VAR(self, argc):
768 return self.CALL_FUNCTION(argc)+1
769 def CALL_FUNCTION_KW(self, argc):
770 return self.CALL_FUNCTION(argc)+1
771 def CALL_FUNCTION_VAR_KW(self, argc):
772 return self.CALL_FUNCTION(argc)+2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000773 def MAKE_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000774 return -argc
Jeremy Hyltona5058122000-02-14 14:14:29 +0000775 def BUILD_SLICE(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000776 if argc == 2:
777 return -1
778 elif argc == 3:
779 return -2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000780
781findDepth = StackDepthTracker().findDepth