blob: 3063b5b253b5b636933150125d4890f5937ab1d3 [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
Jeremy Hylton63db7b92001-08-28 16:36:12 +0000494 def _convert_LOAD_NAME(self, arg):
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000495 return self._lookupName(arg, self.names)
Jeremy Hylton63db7b92001-08-28 16:36:12 +0000496
497 def _convert_NAME(self, arg):
498 self._lookupName(arg, self.varnames)
499 return self._lookupName(arg, self.names)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000500 _convert_STORE_NAME = _convert_NAME
501 _convert_DELETE_NAME = _convert_NAME
502 _convert_IMPORT_NAME = _convert_NAME
503 _convert_IMPORT_FROM = _convert_NAME
504 _convert_STORE_ATTR = _convert_NAME
505 _convert_LOAD_ATTR = _convert_NAME
506 _convert_DELETE_ATTR = _convert_NAME
507 _convert_LOAD_GLOBAL = _convert_NAME
508 _convert_STORE_GLOBAL = _convert_NAME
509 _convert_DELETE_GLOBAL = _convert_NAME
510
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000511 def _convert_DEREF(self, arg):
512 self._lookupName(arg, self.names)
513 self._lookupName(arg, self.varnames)
514 return self._lookupName(arg, self.closure)
515 _convert_LOAD_DEREF = _convert_DEREF
516 _convert_STORE_DEREF = _convert_DEREF
517
518 def _convert_LOAD_CLOSURE(self, arg):
519 self._lookupName(arg, self.varnames)
520 return self._lookupName(arg, self.closure)
521
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000522 _cmp = list(dis.cmp_op)
523 def _convert_COMPARE_OP(self, arg):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000524 return self._cmp.index(arg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000525
526 # similarly for other opcodes...
527
528 for name, obj in locals().items():
529 if name[:9] == "_convert_":
530 opname = name[9:]
531 _converters[opname] = obj
532 del name, obj, opname
533
534 def makeByteCode(self):
535 assert self.stage == CONV
536 self.lnotab = lnotab = LineAddrTable()
537 for t in self.insts:
538 opname = t[0]
539 if len(t) == 1:
540 lnotab.addCode(self.opnum[opname])
541 else:
542 oparg = t[1]
543 if opname == "SET_LINENO":
544 lnotab.nextLine(oparg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000545 hi, lo = twobyte(oparg)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000546 try:
547 lnotab.addCode(self.opnum[opname], lo, hi)
548 except ValueError:
549 print opname, oparg
550 print self.opnum[opname], lo, hi
551 raise
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000552 self.stage = DONE
553
Jeremy Hyltona5058122000-02-14 14:14:29 +0000554 opnum = {}
555 for num in range(len(dis.opname)):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000556 opnum[dis.opname[num]] = num
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000557 del num
Jeremy Hyltona5058122000-02-14 14:14:29 +0000558
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000559 def newCodeObject(self):
560 assert self.stage == DONE
561 if self.flags == 0:
562 nlocals = 0
563 else:
564 nlocals = len(self.varnames)
565 argcount = self.argcount
566 if self.flags & CO_VARKEYWORDS:
567 argcount = argcount - 1
568 return new.code(argcount, nlocals, self.stacksize, self.flags,
569 self.lnotab.getCode(), self.getConsts(),
570 tuple(self.names), tuple(self.varnames),
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000571 self.filename, self.name, self.lnotab.firstline,
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000572 self.lnotab.getTable(), tuple(self.freevars),
573 tuple(self.cellvars))
Jeremy Hyltona5058122000-02-14 14:14:29 +0000574
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000575 def getConsts(self):
576 """Return a tuple for the const slot of the code object
Jeremy Hyltona5058122000-02-14 14:14:29 +0000577
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000578 Must convert references to code (MAKE_FUNCTION) to code
579 objects recursively.
580 """
581 l = []
582 for elt in self.consts:
583 if isinstance(elt, PyFlowGraph):
584 elt = elt.getCode()
585 l.append(elt)
586 return tuple(l)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000587
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000588def isJump(opname):
589 if opname[:4] == 'JUMP':
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000590 return 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000591
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000592class TupleArg:
593 """Helper for marking func defs with nested tuples in arglist"""
594 def __init__(self, count, names):
595 self.count = count
596 self.names = names
597 def __repr__(self):
598 return "TupleArg(%s, %s)" % (self.count, self.names)
599 def getName(self):
Jeremy Hyltonbfb0cf82001-04-12 17:33:34 +0000600 return ".%d" % self.count
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000601
602def getArgCount(args):
603 argcount = len(args)
604 if args:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000605 for arg in args:
606 if isinstance(arg, TupleArg):
607 numNames = len(misc.flatten(arg.names))
608 argcount = argcount - numNames
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000609 return argcount
610
611def twobyte(val):
612 """Convert an int argument into high and low bytes"""
613 assert type(val) == types.IntType
614 return divmod(val, 256)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000615
616class LineAddrTable:
617 """lnotab
618
Tim Peters2a7f3842001-06-09 09:26:21 +0000619 This class builds the lnotab, which is documented in compile.c.
620 Here's a brief recap:
Jeremy Hyltona5058122000-02-14 14:14:29 +0000621
622 For each SET_LINENO instruction after the first one, two bytes are
623 added to lnotab. (In some cases, multiple two-byte entries are
624 added.) The first byte is the distance in bytes between the
625 instruction for the last SET_LINENO and the current SET_LINENO.
626 The second byte is offset in line numbers. If either offset is
Tim Peters2a7f3842001-06-09 09:26:21 +0000627 greater than 255, multiple two-byte entries are added -- see
628 compile.c for the delicate details.
Jeremy Hyltona5058122000-02-14 14:14:29 +0000629 """
630
631 def __init__(self):
632 self.code = []
633 self.codeOffset = 0
634 self.firstline = 0
635 self.lastline = 0
636 self.lastoff = 0
637 self.lnotab = []
638
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000639 def addCode(self, *args):
640 for arg in args:
641 self.code.append(chr(arg))
642 self.codeOffset = self.codeOffset + len(args)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000643
644 def nextLine(self, lineno):
645 if self.firstline == 0:
646 self.firstline = lineno
647 self.lastline = lineno
648 else:
649 # compute deltas
650 addr = self.codeOffset - self.lastoff
651 line = lineno - self.lastline
Jeremy Hylton92f39722000-09-01 20:47:37 +0000652 # Python assumes that lineno always increases with
653 # increasing bytecode address (lnotab is unsigned char).
654 # Depending on when SET_LINENO instructions are emitted
655 # this is not always true. Consider the code:
656 # a = (1,
657 # b)
658 # In the bytecode stream, the assignment to "a" occurs
659 # after the loading of "b". This works with the C Python
660 # compiler because it only generates a SET_LINENO instruction
661 # for the assignment.
662 if line > 0:
Tim Peters2a7f3842001-06-09 09:26:21 +0000663 push = self.lnotab.append
664 while addr > 255:
665 push(255); push(0)
666 addr -= 255
667 while line > 255:
668 push(addr); push(255)
669 line -= 255
670 addr = 0
671 if addr > 0 or line > 0:
672 push(addr); push(line)
Jeremy Hylton92f39722000-09-01 20:47:37 +0000673 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