blob: 10a8dbde7c44dc84fa1d1da7c382d2fd37e8a155 [file] [log] [blame]
Jeremy Hylton36cc6a22000-03-16 20:06:59 +00001"""A flow graph representation for Python bytecode"""
Jeremy Hyltona5058122000-02-14 14:14:29 +00002
Jeremy Hyltona5058122000-02-14 14:14:29 +00003import dis
4import new
Jeremy Hylton314e3fb2000-11-06 03:43:11 +00005import sys
Jeremy Hylton36cc6a22000-03-16 20:06:59 +00006import types
Jeremy Hyltona5058122000-02-14 14:14:29 +00007
Jeremy Hylton36cc6a22000-03-16 20:06:59 +00008from compiler import misc
Jeremy Hylton71ebc332001-08-30 20:25:55 +00009from compiler.consts import CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, \
10 CO_VARKEYWORDS
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000011
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000012def xxx_sort(l):
13 l = l[:]
14 def sorter(a, b):
15 return cmp(a.bid, b.bid)
16 l.sort(sorter)
17 return l
18
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000019class FlowGraph:
20 def __init__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000021 self.current = self.entry = Block()
22 self.exit = Block("exit")
23 self.blocks = misc.Set()
24 self.blocks.add(self.entry)
25 self.blocks.add(self.exit)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000026
27 def startBlock(self, block):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000028 if self._debug:
29 if self.current:
30 print "end", repr(self.current)
Jeremy Hylton542b11a2001-04-12 20:21:39 +000031 print " next", self.current.next
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000032 print " ", self.current.get_children()
33 print repr(block)
Thomas Wouters46cc7c02000-08-12 20:32:46 +000034 self.current = block
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000035
Jeremy Hylton542b11a2001-04-12 20:21:39 +000036 def nextBlock(self, block=None):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000037 # XXX think we need to specify when there is implicit transfer
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000038 # from one block to the next. might be better to represent this
39 # with explicit JUMP_ABSOLUTE instructions that are optimized
40 # out when they are unnecessary.
Thomas Wouters46cc7c02000-08-12 20:32:46 +000041 #
42 # I think this strategy works: each block has a child
43 # designated as "next" which is returned as the last of the
44 # children. because the nodes in a graph are emitted in
45 # reverse post order, the "next" block will always be emitted
46 # immediately after its parent.
47 # Worry: maintaining this invariant could be tricky
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000048 if block is None:
49 block = self.newBlock()
50
51 # Note: If the current block ends with an unconditional
52 # control transfer, then it is incorrect to add an implicit
53 # transfer to the block graph. The current code requires
54 # these edges to get the blocks emitted in the right order,
55 # however. :-( If a client needs to remove these edges, call
56 # pruneEdges().
Tim Peterse0c446b2001-10-18 21:57:37 +000057
Thomas Wouters46cc7c02000-08-12 20:32:46 +000058 self.current.addNext(block)
59 self.startBlock(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000060
61 def newBlock(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000062 b = Block()
63 self.blocks.add(b)
64 return b
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000065
66 def startExitBlock(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000067 self.startBlock(self.exit)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000068
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000069 _debug = 0
70
71 def _enable_debug(self):
72 self._debug = 1
73
74 def _disable_debug(self):
75 self._debug = 0
76
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000077 def emit(self, *inst):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000078 if self._debug:
79 print "\t", inst
Thomas Wouters46cc7c02000-08-12 20:32:46 +000080 if inst[0] == 'RETURN_VALUE':
81 self.current.addOutEdge(self.exit)
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000082 if len(inst) == 2 and isinstance(inst[1], Block):
83 self.current.addOutEdge(inst[1])
Thomas Wouters46cc7c02000-08-12 20:32:46 +000084 self.current.emit(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000085
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000086 def getBlocksInOrder(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000087 """Return the blocks in reverse postorder
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000088
Thomas Wouters46cc7c02000-08-12 20:32:46 +000089 i.e. each node appears before all of its successors
90 """
91 # XXX make sure every node that doesn't have an explicit next
92 # is set so that next points to exit
93 for b in self.blocks.elements():
94 if b is self.exit:
95 continue
96 if not b.next:
97 b.addNext(self.exit)
98 order = dfs_postorder(self.entry, {})
99 order.reverse()
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000100 self.fixupOrder(order, self.exit)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000101 # hack alert
102 if not self.exit in order:
103 order.append(self.exit)
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000104
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000105 return order
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000106
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000107 def fixupOrder(self, blocks, default_next):
108 """Fixup bad order introduced by DFS."""
109
110 # XXX This is a total mess. There must be a better way to get
111 # the code blocks in the right order.
Tim Peterse0c446b2001-10-18 21:57:37 +0000112
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000113 self.fixupOrderHonorNext(blocks, default_next)
114 self.fixupOrderForward(blocks, default_next)
115
116 def fixupOrderHonorNext(self, blocks, default_next):
117 """Fix one problem with DFS.
Tim Peterse0c446b2001-10-18 21:57:37 +0000118
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000119 The DFS uses child block, but doesn't know about the special
120 "next" block. As a result, the DFS can order blocks so that a
121 block isn't next to the right block for implicit control
122 transfers.
123 """
124 index = {}
125 for i in range(len(blocks)):
126 index[blocks[i]] = i
127
128 for i in range(0, len(blocks) - 1):
129 b = blocks[i]
130 n = blocks[i + 1]
131 if not b.next or b.next[0] == default_next or b.next[0] == n:
132 continue
133 # The blocks are in the wrong order. Find the chain of
134 # blocks to insert where they belong.
135 cur = b
136 chain = []
137 elt = cur
138 while elt.next and elt.next[0] != default_next:
139 chain.append(elt.next[0])
140 elt = elt.next[0]
141 # Now remove the blocks in the chain from the current
142 # block list, so that they can be re-inserted.
143 l = []
144 for b in chain:
145 assert index[b] > i
146 l.append((index[b], b))
147 l.sort()
148 l.reverse()
149 for j, b in l:
150 del blocks[index[b]]
151 # Insert the chain in the proper location
152 blocks[i:i + 1] = [cur] + chain
153 # Finally, re-compute the block indexes
154 for i in range(len(blocks)):
155 index[blocks[i]] = i
156
157 def fixupOrderForward(self, blocks, default_next):
158 """Make sure all JUMP_FORWARDs jump forward"""
159 index = {}
160 chains = []
161 cur = []
162 for b in blocks:
163 index[b] = len(chains)
164 cur.append(b)
165 if b.next and b.next[0] == default_next:
166 chains.append(cur)
167 cur = []
168 chains.append(cur)
169
170 while 1:
171 constraints = []
172
173 for i in range(len(chains)):
174 l = chains[i]
175 for b in l:
176 for c in b.get_children():
177 if index[c] < i:
178 forward_p = 0
179 for inst in b.insts:
180 if inst[0] == 'JUMP_FORWARD':
181 if inst[1] == c:
182 forward_p = 1
183 if not forward_p:
184 continue
185 constraints.append((index[c], i))
186
187 if not constraints:
188 break
189
190 # XXX just do one for now
191 # do swaps to get things in the right order
192 goes_before, a_chain = constraints[0]
193 assert a_chain > goes_before
194 c = chains[a_chain]
195 chains.remove(c)
196 chains.insert(goes_before, c)
197
Jeremy Hylton542b11a2001-04-12 20:21:39 +0000198 del blocks[:]
199 for c in chains:
200 for b in c:
201 blocks.append(b)
Tim Peterse0c446b2001-10-18 21:57:37 +0000202
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
Tim Peterse0c446b2001-10-18 21:57:37 +0000209
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000210 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,
Neal Norwitza312c3a2002-06-06 18:30:10 +0000248 '\n'.join(insts))
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',
Jeremy Hylton92638482001-08-29 22:27:14 +0000270 'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP')
Jeremy Hyltoneefaeb72000-11-06 03:47:39 +0000271
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
Jeremy Hyltona5058122000-02-14 14:14:29 +0000314
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000315# the FlowGraph is transformed in place; it exists in one of these states
316RAW = "RAW"
317FLAT = "FLAT"
318CONV = "CONV"
319DONE = "DONE"
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000320
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000321class PyFlowGraph(FlowGraph):
322 super_init = FlowGraph.__init__
Jeremy Hyltona5058122000-02-14 14:14:29 +0000323
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000324 def __init__(self, name, filename, args=(), optimized=0, klass=None):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000325 self.super_init()
326 self.name = name
327 self.filename = filename
328 self.docstring = None
329 self.args = args # XXX
330 self.argcount = getArgCount(args)
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000331 self.klass = klass
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000332 if optimized:
Tim Peterse0c446b2001-10-18 21:57:37 +0000333 self.flags = CO_OPTIMIZED | CO_NEWLOCALS
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000334 else:
335 self.flags = 0
336 self.consts = []
337 self.names = []
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000338 # Free variables found by the symbol table scan, including
339 # variables used only in nested scopes, are included here.
340 self.freevars = []
341 self.cellvars = []
342 # The closure list is used to track the order of cell
343 # variables and free variables in the resulting code object.
344 # The offsets used by LOAD_CLOSURE/LOAD_DEREF refer to both
345 # kinds of variables.
346 self.closure = []
Jeremy Hyltona5058122000-02-14 14:14:29 +0000347 self.varnames = list(args) or []
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000348 for i in range(len(self.varnames)):
349 var = self.varnames[i]
350 if isinstance(var, TupleArg):
351 self.varnames[i] = var.getName()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000352 self.stage = RAW
Jeremy Hyltona5058122000-02-14 14:14:29 +0000353
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000354 def setDocstring(self, doc):
355 self.docstring = doc
Jeremy Hylton2ce27b22000-02-16 00:50:29 +0000356
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000357 def setFlag(self, flag):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000358 self.flags = self.flags | flag
359 if flag == CO_VARARGS:
360 self.argcount = self.argcount - 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000361
Jeremy Hylton1e99a772001-09-14 22:49:08 +0000362 def checkFlag(self, flag):
363 if self.flags & flag:
364 return 1
365
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 Hylton138d90e2001-10-17 13:37:29 +0000375 self.computeStackDepth()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000376 self.flattenGraph()
377 if self.stage == FLAT:
378 self.convertArgs()
379 if self.stage == CONV:
380 self.makeByteCode()
381 if self.stage == DONE:
382 return self.newCodeObject()
383 raise RuntimeError, "inconsistent PyFlowGraph state"
Jeremy Hyltona5058122000-02-14 14:14:29 +0000384
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000385 def dump(self, io=None):
386 if io:
387 save = sys.stdout
388 sys.stdout = io
389 pc = 0
Jeremy Hyltona5058122000-02-14 14:14:29 +0000390 for t in self.insts:
391 opname = t[0]
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000392 if opname == "SET_LINENO":
393 print
Jeremy Hyltona5058122000-02-14 14:14:29 +0000394 if len(t) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000395 print "\t", "%3d" % pc, opname
396 pc = pc + 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000397 else:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000398 print "\t", "%3d" % pc, opname, t[1]
399 pc = pc + 3
400 if io:
401 sys.stdout = save
Jeremy Hyltona5058122000-02-14 14:14:29 +0000402
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000403 def computeStackDepth(self):
404 """Compute the max stack depth.
405
406 Approach is to compute the stack effect of each basic block.
407 Then find the path through the code with the largest total
408 effect.
409 """
410 depth = {}
411 exit = None
412 for b in self.getBlocks():
413 depth[b] = findDepth(b.getInstructions())
414
415 seen = {}
416
417 def max_depth(b, d):
418 if seen.has_key(b):
419 return d
420 seen[b] = 1
421 d = d + depth[b]
422 children = b.get_children()
423 if children:
424 return max([max_depth(c, d) for c in children])
425 else:
426 if not b.label == "exit":
427 return max_depth(self.exit, d)
428 else:
429 return d
430
431 self.stacksize = max_depth(self.entry, 0)
432
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000433 def flattenGraph(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000434 """Arrange the blocks in order and resolve jumps"""
435 assert self.stage == RAW
436 self.insts = insts = []
437 pc = 0
438 begin = {}
439 end = {}
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000440 for b in self.getBlocksInOrder():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000441 begin[b] = pc
442 for inst in b.getInstructions():
443 insts.append(inst)
444 if len(inst) == 1:
445 pc = pc + 1
446 else:
447 # arg takes 2 bytes
448 pc = pc + 3
449 end[b] = pc
450 pc = 0
451 for i in range(len(insts)):
452 inst = insts[i]
453 if len(inst) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000454 pc = pc + 1
455 else:
456 pc = pc + 3
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000457 opname = inst[0]
458 if self.hasjrel.has_elt(opname):
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000459 oparg = inst[1]
460 offset = begin[oparg] - pc
461 insts[i] = opname, offset
462 elif self.hasjabs.has_elt(opname):
463 insts[i] = opname, begin[inst[1]]
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000464 self.stage = FLAT
Jeremy Hyltona5058122000-02-14 14:14:29 +0000465
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000466 hasjrel = misc.Set()
467 for i in dis.hasjrel:
468 hasjrel.add(dis.opname[i])
469 hasjabs = misc.Set()
470 for i in dis.hasjabs:
471 hasjabs.add(dis.opname[i])
Jeremy Hyltona5058122000-02-14 14:14:29 +0000472
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000473 def convertArgs(self):
474 """Convert arguments from symbolic to concrete form"""
475 assert self.stage == FLAT
Jeremy Hylton5af105e2001-04-11 16:21:51 +0000476 self.consts.insert(0, self.docstring)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000477 self.sort_cellvars()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000478 for i in range(len(self.insts)):
479 t = self.insts[i]
480 if len(t) == 2:
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000481 opname, oparg = t
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000482 conv = self._converters.get(opname, None)
483 if conv:
484 self.insts[i] = opname, conv(self, oparg)
485 self.stage = CONV
Jeremy Hyltona5058122000-02-14 14:14:29 +0000486
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000487 def sort_cellvars(self):
488 """Sort cellvars in the order of varnames and prune from freevars.
489 """
490 cells = {}
491 for name in self.cellvars:
492 cells[name] = 1
493 self.cellvars = [name for name in self.varnames
494 if cells.has_key(name)]
495 for name in self.cellvars:
496 del cells[name]
497 self.cellvars = self.cellvars + cells.keys()
498 self.closure = self.cellvars + self.freevars
499
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000500 def _lookupName(self, name, list):
501 """Return index of name in list, appending if necessary
Jeremy Hyltone4e9cd42001-08-29 18:09:50 +0000502
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000503 This routine uses a list instead of a dictionary, because a
504 dictionary can't store two different keys if the keys have the
505 same value but different types, e.g. 2 and 2L. The compiler
506 must treat these two separately, so it does an explicit type
507 comparison before comparing the values.
508 """
Jeremy Hylton9c048f92000-10-13 21:58:13 +0000509 t = type(name)
510 for i in range(len(list)):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000511 if t == type(list[i]) and list[i] == name:
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000512 return i
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000513 end = len(list)
514 list.append(name)
515 return end
Jeremy Hyltona5058122000-02-14 14:14:29 +0000516
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000517 _converters = {}
518 def _convert_LOAD_CONST(self, arg):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000519 if hasattr(arg, 'getCode'):
520 arg = arg.getCode()
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000521 return self._lookupName(arg, self.consts)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000522
523 def _convert_LOAD_FAST(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000524 self._lookupName(arg, self.names)
525 return self._lookupName(arg, self.varnames)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000526 _convert_STORE_FAST = _convert_LOAD_FAST
527 _convert_DELETE_FAST = _convert_LOAD_FAST
528
Jeremy Hylton63db7b92001-08-28 16:36:12 +0000529 def _convert_LOAD_NAME(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000530 if self.klass is None:
531 self._lookupName(arg, self.varnames)
532 return self._lookupName(arg, self.names)
Jeremy Hylton63db7b92001-08-28 16:36:12 +0000533
534 def _convert_NAME(self, arg):
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000535 if self.klass is None:
536 self._lookupName(arg, self.varnames)
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000537 return self._lookupName(arg, self.names)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000538 _convert_STORE_NAME = _convert_NAME
539 _convert_DELETE_NAME = _convert_NAME
540 _convert_IMPORT_NAME = _convert_NAME
541 _convert_IMPORT_FROM = _convert_NAME
542 _convert_STORE_ATTR = _convert_NAME
543 _convert_LOAD_ATTR = _convert_NAME
544 _convert_DELETE_ATTR = _convert_NAME
545 _convert_LOAD_GLOBAL = _convert_NAME
546 _convert_STORE_GLOBAL = _convert_NAME
547 _convert_DELETE_GLOBAL = _convert_NAME
548
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000549 def _convert_DEREF(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000550 self._lookupName(arg, self.names)
551 self._lookupName(arg, self.varnames)
552 return self._lookupName(arg, self.closure)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000553 _convert_LOAD_DEREF = _convert_DEREF
554 _convert_STORE_DEREF = _convert_DEREF
555
556 def _convert_LOAD_CLOSURE(self, arg):
Jeremy Hyltonbf77c462001-08-29 19:45:33 +0000557 self._lookupName(arg, self.varnames)
558 return self._lookupName(arg, self.closure)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000559
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000560 _cmp = list(dis.cmp_op)
561 def _convert_COMPARE_OP(self, arg):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000562 return self._cmp.index(arg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000563
564 # similarly for other opcodes...
565
566 for name, obj in locals().items():
567 if name[:9] == "_convert_":
568 opname = name[9:]
Tim Peterse0c446b2001-10-18 21:57:37 +0000569 _converters[opname] = obj
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000570 del name, obj, opname
571
572 def makeByteCode(self):
573 assert self.stage == CONV
574 self.lnotab = lnotab = LineAddrTable()
575 for t in self.insts:
576 opname = t[0]
577 if len(t) == 1:
578 lnotab.addCode(self.opnum[opname])
579 else:
580 oparg = t[1]
581 if opname == "SET_LINENO":
582 lnotab.nextLine(oparg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000583 hi, lo = twobyte(oparg)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000584 try:
585 lnotab.addCode(self.opnum[opname], lo, hi)
586 except ValueError:
587 print opname, oparg
588 print self.opnum[opname], lo, hi
589 raise
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000590 self.stage = DONE
591
Jeremy Hyltona5058122000-02-14 14:14:29 +0000592 opnum = {}
593 for num in range(len(dis.opname)):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000594 opnum[dis.opname[num]] = num
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000595 del num
Jeremy Hyltona5058122000-02-14 14:14:29 +0000596
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000597 def newCodeObject(self):
598 assert self.stage == DONE
Jeremy Hylton1e99a772001-09-14 22:49:08 +0000599 if (self.flags & CO_NEWLOCALS) == 0:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000600 nlocals = 0
601 else:
602 nlocals = len(self.varnames)
603 argcount = self.argcount
604 if self.flags & CO_VARKEYWORDS:
605 argcount = argcount - 1
606 return new.code(argcount, nlocals, self.stacksize, self.flags,
607 self.lnotab.getCode(), self.getConsts(),
608 tuple(self.names), tuple(self.varnames),
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000609 self.filename, self.name, self.lnotab.firstline,
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000610 self.lnotab.getTable(), tuple(self.freevars),
611 tuple(self.cellvars))
Jeremy Hyltona5058122000-02-14 14:14:29 +0000612
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000613 def getConsts(self):
614 """Return a tuple for the const slot of the code object
Jeremy Hyltona5058122000-02-14 14:14:29 +0000615
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000616 Must convert references to code (MAKE_FUNCTION) to code
617 objects recursively.
618 """
619 l = []
620 for elt in self.consts:
621 if isinstance(elt, PyFlowGraph):
622 elt = elt.getCode()
623 l.append(elt)
624 return tuple(l)
Tim Peterse0c446b2001-10-18 21:57:37 +0000625
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000626def isJump(opname):
627 if opname[:4] == 'JUMP':
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000628 return 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000629
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000630class TupleArg:
631 """Helper for marking func defs with nested tuples in arglist"""
632 def __init__(self, count, names):
633 self.count = count
634 self.names = names
635 def __repr__(self):
636 return "TupleArg(%s, %s)" % (self.count, self.names)
637 def getName(self):
Jeremy Hyltonbfb0cf82001-04-12 17:33:34 +0000638 return ".%d" % self.count
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000639
640def getArgCount(args):
641 argcount = len(args)
642 if args:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000643 for arg in args:
644 if isinstance(arg, TupleArg):
645 numNames = len(misc.flatten(arg.names))
646 argcount = argcount - numNames
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000647 return argcount
648
649def twobyte(val):
650 """Convert an int argument into high and low bytes"""
651 assert type(val) == types.IntType
652 return divmod(val, 256)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000653
654class LineAddrTable:
655 """lnotab
Tim Peterse0c446b2001-10-18 21:57:37 +0000656
Tim Peters2a7f3842001-06-09 09:26:21 +0000657 This class builds the lnotab, which is documented in compile.c.
658 Here's a brief recap:
Jeremy Hyltona5058122000-02-14 14:14:29 +0000659
660 For each SET_LINENO instruction after the first one, two bytes are
661 added to lnotab. (In some cases, multiple two-byte entries are
662 added.) The first byte is the distance in bytes between the
663 instruction for the last SET_LINENO and the current SET_LINENO.
664 The second byte is offset in line numbers. If either offset is
Tim Peters2a7f3842001-06-09 09:26:21 +0000665 greater than 255, multiple two-byte entries are added -- see
666 compile.c for the delicate details.
Jeremy Hyltona5058122000-02-14 14:14:29 +0000667 """
668
669 def __init__(self):
670 self.code = []
671 self.codeOffset = 0
672 self.firstline = 0
673 self.lastline = 0
674 self.lastoff = 0
675 self.lnotab = []
676
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000677 def addCode(self, *args):
678 for arg in args:
679 self.code.append(chr(arg))
680 self.codeOffset = self.codeOffset + len(args)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000681
682 def nextLine(self, lineno):
683 if self.firstline == 0:
684 self.firstline = lineno
685 self.lastline = lineno
686 else:
687 # compute deltas
688 addr = self.codeOffset - self.lastoff
689 line = lineno - self.lastline
Jeremy Hylton92f39722000-09-01 20:47:37 +0000690 # Python assumes that lineno always increases with
691 # increasing bytecode address (lnotab is unsigned char).
692 # Depending on when SET_LINENO instructions are emitted
693 # this is not always true. Consider the code:
694 # a = (1,
695 # b)
696 # In the bytecode stream, the assignment to "a" occurs
697 # after the loading of "b". This works with the C Python
698 # compiler because it only generates a SET_LINENO instruction
699 # for the assignment.
700 if line > 0:
Tim Peters2a7f3842001-06-09 09:26:21 +0000701 push = self.lnotab.append
702 while addr > 255:
703 push(255); push(0)
704 addr -= 255
705 while line > 255:
706 push(addr); push(255)
707 line -= 255
708 addr = 0
709 if addr > 0 or line > 0:
710 push(addr); push(line)
Jeremy Hylton92f39722000-09-01 20:47:37 +0000711 self.lastline = lineno
712 self.lastoff = self.codeOffset
Jeremy Hyltona5058122000-02-14 14:14:29 +0000713
714 def getCode(self):
Neal Norwitza312c3a2002-06-06 18:30:10 +0000715 return ''.join(self.code)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000716
717 def getTable(self):
Neal Norwitza312c3a2002-06-06 18:30:10 +0000718 return ''.join(map(chr, self.lnotab))
Tim Peterse0c446b2001-10-18 21:57:37 +0000719
Jeremy Hyltona5058122000-02-14 14:14:29 +0000720class StackDepthTracker:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000721 # XXX 1. need to keep track of stack depth on jumps
722 # XXX 2. at least partly as a result, this code is broken
Jeremy Hyltona5058122000-02-14 14:14:29 +0000723
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000724 def findDepth(self, insts, debug=0):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000725 depth = 0
726 maxDepth = 0
727 for i in insts:
728 opname = i[0]
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000729 if debug:
730 print i,
731 delta = self.effect.get(opname, None)
732 if delta is not None:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000733 depth = depth + delta
734 else:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000735 # now check patterns
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000736 for pat, pat_delta in self.patterns:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000737 if opname[:len(pat)] == pat:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000738 delta = pat_delta
Jeremy Hylton772dd412000-02-21 22:46:00 +0000739 depth = depth + delta
740 break
741 # if we still haven't found a match
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000742 if delta is None:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000743 meth = getattr(self, opname, None)
744 if meth is not None:
745 depth = depth + meth(i[1])
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000746 if depth > maxDepth:
747 maxDepth = depth
748 if debug:
749 print depth, maxDepth
Jeremy Hylton772dd412000-02-21 22:46:00 +0000750 return maxDepth
Jeremy Hyltona5058122000-02-14 14:14:29 +0000751
752 effect = {
Jeremy Hylton772dd412000-02-21 22:46:00 +0000753 'POP_TOP': -1,
754 'DUP_TOP': 1,
755 'SLICE+1': -1,
756 'SLICE+2': -1,
757 'SLICE+3': -2,
758 'STORE_SLICE+0': -1,
759 'STORE_SLICE+1': -2,
760 'STORE_SLICE+2': -2,
761 'STORE_SLICE+3': -3,
762 'DELETE_SLICE+0': -1,
763 'DELETE_SLICE+1': -2,
764 'DELETE_SLICE+2': -2,
765 'DELETE_SLICE+3': -3,
766 'STORE_SUBSCR': -3,
767 'DELETE_SUBSCR': -2,
768 # PRINT_EXPR?
769 'PRINT_ITEM': -1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000770 'RETURN_VALUE': -1,
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000771 'EXEC_STMT': -3,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000772 'BUILD_CLASS': -2,
773 'STORE_NAME': -1,
774 'STORE_ATTR': -2,
775 'DELETE_ATTR': -1,
776 'STORE_GLOBAL': -1,
777 'BUILD_MAP': 1,
778 'COMPARE_OP': -1,
779 'STORE_FAST': -1,
Jeremy Hylton4e1be722000-10-12 20:23:23 +0000780 'IMPORT_STAR': -1,
781 'IMPORT_NAME': 0,
782 'IMPORT_FROM': 1,
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000783 'LOAD_ATTR': 0, # unlike other loads
Jeremy Hylton92638482001-08-29 22:27:14 +0000784 # close enough...
785 'SETUP_EXCEPT': 3,
786 'SETUP_FINALLY': 3,
Jeremy Hylton71ebc332001-08-30 20:25:55 +0000787 'FOR_ITER': 1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000788 }
Jeremy Hyltona5058122000-02-14 14:14:29 +0000789 # use pattern match
790 patterns = [
Jeremy Hylton772dd412000-02-21 22:46:00 +0000791 ('BINARY_', -1),
792 ('LOAD_', 1),
Jeremy Hylton772dd412000-02-21 22:46:00 +0000793 ]
Tim Peterse0c446b2001-10-18 21:57:37 +0000794
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000795 def UNPACK_SEQUENCE(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000796 return count-1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000797 def BUILD_TUPLE(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000798 return -count+1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000799 def BUILD_LIST(self, count):
Jeremy Hylton4ba90012001-08-29 20:55:17 +0000800 return -count+1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000801 def CALL_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000802 hi, lo = divmod(argc, 256)
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000803 return -(lo + hi * 2)
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000804 def CALL_FUNCTION_VAR(self, argc):
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000805 return self.CALL_FUNCTION(argc)-1
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000806 def CALL_FUNCTION_KW(self, argc):
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000807 return self.CALL_FUNCTION(argc)-1
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000808 def CALL_FUNCTION_VAR_KW(self, argc):
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000809 return self.CALL_FUNCTION(argc)-2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000810 def MAKE_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000811 return -argc
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000812 def MAKE_CLOSURE(self, argc):
813 # XXX need to account for free variables too!
814 return -argc
Jeremy Hyltona5058122000-02-14 14:14:29 +0000815 def BUILD_SLICE(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000816 if argc == 2:
817 return -1
818 elif argc == 3:
819 return -2
Jeremy Hylton138d90e2001-10-17 13:37:29 +0000820 def DUP_TOPX(self, argc):
821 return argc
Tim Peterse0c446b2001-10-18 21:57:37 +0000822
Jeremy Hyltona5058122000-02-14 14:14:29 +0000823findDepth = StackDepthTracker().findDepth