blob: 447a8e78e472794c9aa19a2e9cf2f6b9ab80942c [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)
30 print " ", self.current.get_children()
31 print repr(block)
Thomas Wouters46cc7c02000-08-12 20:32:46 +000032 self.current = block
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000033
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000034 def nextBlock(self, block=None, force=0):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000035 # XXX think we need to specify when there is implicit transfer
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000036 # from one block to the next. might be better to represent this
37 # with explicit JUMP_ABSOLUTE instructions that are optimized
38 # out when they are unnecessary.
Thomas Wouters46cc7c02000-08-12 20:32:46 +000039 #
40 # I think this strategy works: each block has a child
41 # designated as "next" which is returned as the last of the
42 # children. because the nodes in a graph are emitted in
43 # reverse post order, the "next" block will always be emitted
44 # immediately after its parent.
45 # Worry: maintaining this invariant could be tricky
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000046 if block is None:
47 block = self.newBlock()
48
49 # Note: If the current block ends with an unconditional
50 # control transfer, then it is incorrect to add an implicit
51 # transfer to the block graph. The current code requires
52 # these edges to get the blocks emitted in the right order,
53 # however. :-( If a client needs to remove these edges, call
54 # pruneEdges().
55
Thomas Wouters46cc7c02000-08-12 20:32:46 +000056 self.current.addNext(block)
57 self.startBlock(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000058
59 def newBlock(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000060 b = Block()
61 self.blocks.add(b)
62 return b
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000063
64 def startExitBlock(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000065 self.startBlock(self.exit)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000066
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000067 _debug = 0
68
69 def _enable_debug(self):
70 self._debug = 1
71
72 def _disable_debug(self):
73 self._debug = 0
74
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000075 def emit(self, *inst):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000076 if self._debug:
77 print "\t", inst
Thomas Wouters46cc7c02000-08-12 20:32:46 +000078 if inst[0] == 'RETURN_VALUE':
79 self.current.addOutEdge(self.exit)
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000080 if len(inst) == 2 and isinstance(inst[1], Block):
81 self.current.addOutEdge(inst[1])
Thomas Wouters46cc7c02000-08-12 20:32:46 +000082 self.current.emit(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000083
Jeremy Hylton314e3fb2000-11-06 03:43:11 +000084 def getBlocksInOrder(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +000085 """Return the blocks in reverse postorder
Jeremy Hylton36cc6a22000-03-16 20:06:59 +000086
Thomas Wouters46cc7c02000-08-12 20:32:46 +000087 i.e. each node appears before all of its successors
88 """
89 # XXX make sure every node that doesn't have an explicit next
90 # is set so that next points to exit
91 for b in self.blocks.elements():
92 if b is self.exit:
93 continue
94 if not b.next:
95 b.addNext(self.exit)
96 order = dfs_postorder(self.entry, {})
97 order.reverse()
98 # hack alert
99 if not self.exit in order:
100 order.append(self.exit)
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000101
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000102 return order
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000103
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000104 def getBlocks(self):
105 return self.blocks.elements()
106
107 def getRoot(self):
108 """Return nodes appropriate for use with dominator"""
109 return self.entry
110
111 def getContainedGraphs(self):
112 l = []
113 for b in self.getBlocks():
114 l.extend(b.getContainedGraphs())
115 return l
116
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000117def dfs_postorder(b, seen):
118 """Depth-first search of tree rooted at b, return in postorder"""
119 order = []
120 seen[b] = b
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000121 for c in b.get_children():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000122 if seen.has_key(c):
123 continue
124 order = order + dfs_postorder(c, seen)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000125 order.append(b)
126 return order
127
128class Block:
129 _count = 0
130
131 def __init__(self, label=''):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000132 self.insts = []
133 self.inEdges = misc.Set()
134 self.outEdges = misc.Set()
135 self.label = label
136 self.bid = Block._count
137 self.next = []
138 Block._count = Block._count + 1
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000139
140 def __repr__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000141 if self.label:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000142 return "<block %s id=%d>" % (self.label, self.bid)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000143 else:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000144 return "<block id=%d>" % (self.bid)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000145
146 def __str__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000147 insts = map(str, self.insts)
148 return "<block %s %d:\n%s>" % (self.label, self.bid,
149 string.join(insts, '\n'))
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000150
151 def emit(self, inst):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000152 op = inst[0]
153 if op[:4] == 'JUMP':
154 self.outEdges.add(inst[1])
155 self.insts.append(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000156
157 def getInstructions(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000158 return self.insts
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000159
160 def addInEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000161 self.inEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000162
163 def addOutEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000164 self.outEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000165
166 def addNext(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000167 self.next.append(block)
168 assert len(self.next) == 1, map(str, self.next)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000169
Jeremy Hyltoneefaeb72000-11-06 03:47:39 +0000170 _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS',
171 'JUMP_ABSOLUTE', 'JUMP_FORWARD')
172
173 def pruneNext(self):
174 """Remove bogus edge for unconditional transfers
175
176 Each block has a next edge that accounts for implicit control
177 transfers, e.g. from a JUMP_IF_FALSE to the block that will be
178 executed if the test is true.
179
180 These edges must remain for the current assembler code to
181 work. If they are removed, the dfs_postorder gets things in
182 weird orders. However, they shouldn't be there for other
183 purposes, e.g. conversion to SSA form. This method will
184 remove the next edge when it follows an unconditional control
185 transfer.
186 """
187 try:
188 op, arg = self.insts[-1]
189 except (IndexError, ValueError):
190 return
191 if op in self._uncond_transfer:
192 self.next = []
193
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000194 def get_children(self):
195 if self.next and self.next[0] in self.outEdges:
196 self.outEdges.remove(self.next[0])
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000197 return self.outEdges.elements() + self.next
Jeremy Hyltona5058122000-02-14 14:14:29 +0000198
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000199 def getContainedGraphs(self):
200 """Return all graphs contained within this block.
201
202 For example, a MAKE_FUNCTION block will contain a reference to
203 the graph for the function body.
204 """
205 contained = []
206 for inst in self.insts:
207 if len(inst) == 1:
208 continue
209 op = inst[1]
210 if hasattr(op, 'graph'):
211 contained.append(op.graph)
212 return contained
213
Jeremy Hyltona5058122000-02-14 14:14:29 +0000214# flags for code objects
215CO_OPTIMIZED = 0x0001
216CO_NEWLOCALS = 0x0002
217CO_VARARGS = 0x0004
218CO_VARKEYWORDS = 0x0008
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000219CO_NESTED = 0x0010
Jeremy Hyltona5058122000-02-14 14:14:29 +0000220
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000221# the FlowGraph is transformed in place; it exists in one of these states
222RAW = "RAW"
223FLAT = "FLAT"
224CONV = "CONV"
225DONE = "DONE"
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000226
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000227class PyFlowGraph(FlowGraph):
228 super_init = FlowGraph.__init__
Jeremy Hyltona5058122000-02-14 14:14:29 +0000229
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000230 def __init__(self, name, filename, args=(), optimized=0):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000231 self.super_init()
232 self.name = name
233 self.filename = filename
234 self.docstring = None
235 self.args = args # XXX
236 self.argcount = getArgCount(args)
237 if optimized:
238 self.flags = CO_OPTIMIZED | CO_NEWLOCALS
239 else:
240 self.flags = 0
241 self.consts = []
242 self.names = []
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000243 # Free variables found by the symbol table scan, including
244 # variables used only in nested scopes, are included here.
245 self.freevars = []
246 self.cellvars = []
247 # The closure list is used to track the order of cell
248 # variables and free variables in the resulting code object.
249 # The offsets used by LOAD_CLOSURE/LOAD_DEREF refer to both
250 # kinds of variables.
251 self.closure = []
Jeremy Hyltona5058122000-02-14 14:14:29 +0000252 self.varnames = list(args) or []
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000253 for i in range(len(self.varnames)):
254 var = self.varnames[i]
255 if isinstance(var, TupleArg):
256 self.varnames[i] = var.getName()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000257 self.stage = RAW
Jeremy Hyltona5058122000-02-14 14:14:29 +0000258
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000259 def setDocstring(self, doc):
260 self.docstring = doc
Jeremy Hylton2ce27b22000-02-16 00:50:29 +0000261
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000262 def setFlag(self, flag):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000263 self.flags = self.flags | flag
264 if flag == CO_VARARGS:
265 self.argcount = self.argcount - 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000266
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000267 def setFreeVars(self, names):
268 self.freevars = list(names)
269
270 def setCellVars(self, names):
271 self.cellvars = names
272
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000273 def getCode(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000274 """Get a Python code object"""
275 if self.stage == RAW:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000276 self.flattenGraph()
277 if self.stage == FLAT:
278 self.convertArgs()
279 if self.stage == CONV:
280 self.makeByteCode()
281 if self.stage == DONE:
282 return self.newCodeObject()
283 raise RuntimeError, "inconsistent PyFlowGraph state"
Jeremy Hyltona5058122000-02-14 14:14:29 +0000284
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000285 def dump(self, io=None):
286 if io:
287 save = sys.stdout
288 sys.stdout = io
289 pc = 0
Jeremy Hyltona5058122000-02-14 14:14:29 +0000290 for t in self.insts:
291 opname = t[0]
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000292 if opname == "SET_LINENO":
293 print
Jeremy Hyltona5058122000-02-14 14:14:29 +0000294 if len(t) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000295 print "\t", "%3d" % pc, opname
296 pc = pc + 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000297 else:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000298 print "\t", "%3d" % pc, opname, t[1]
299 pc = pc + 3
300 if io:
301 sys.stdout = save
Jeremy Hyltona5058122000-02-14 14:14:29 +0000302
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000303 def flattenGraph(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000304 """Arrange the blocks in order and resolve jumps"""
305 assert self.stage == RAW
306 self.insts = insts = []
307 pc = 0
308 begin = {}
309 end = {}
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000310 for b in self.getBlocksInOrder():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000311 begin[b] = pc
312 for inst in b.getInstructions():
313 insts.append(inst)
314 if len(inst) == 1:
315 pc = pc + 1
316 else:
317 # arg takes 2 bytes
318 pc = pc + 3
319 end[b] = pc
320 pc = 0
321 for i in range(len(insts)):
322 inst = insts[i]
323 if len(inst) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000324 pc = pc + 1
325 else:
326 pc = pc + 3
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000327 opname = inst[0]
328 if self.hasjrel.has_elt(opname):
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000329 oparg = inst[1]
330 offset = begin[oparg] - pc
331 insts[i] = opname, offset
332 elif self.hasjabs.has_elt(opname):
333 insts[i] = opname, begin[inst[1]]
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000334 self.stacksize = findDepth(self.insts)
335 self.stage = FLAT
Jeremy Hyltona5058122000-02-14 14:14:29 +0000336
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000337 hasjrel = misc.Set()
338 for i in dis.hasjrel:
339 hasjrel.add(dis.opname[i])
340 hasjabs = misc.Set()
341 for i in dis.hasjabs:
342 hasjabs.add(dis.opname[i])
Jeremy Hyltona5058122000-02-14 14:14:29 +0000343
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000344 def convertArgs(self):
345 """Convert arguments from symbolic to concrete form"""
346 assert self.stage == FLAT
Jeremy Hylton5af105e2001-04-11 16:21:51 +0000347 self.consts.insert(0, self.docstring)
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000348 self.sort_cellvars()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000349 for i in range(len(self.insts)):
350 t = self.insts[i]
351 if len(t) == 2:
352 opname = t[0]
353 oparg = t[1]
354 conv = self._converters.get(opname, None)
355 if conv:
356 self.insts[i] = opname, conv(self, oparg)
357 self.stage = CONV
Jeremy Hyltona5058122000-02-14 14:14:29 +0000358
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000359 def sort_cellvars(self):
360 """Sort cellvars in the order of varnames and prune from freevars.
361 """
362 cells = {}
363 for name in self.cellvars:
364 cells[name] = 1
365 self.cellvars = [name for name in self.varnames
366 if cells.has_key(name)]
367 for name in self.cellvars:
368 del cells[name]
369 self.cellvars = self.cellvars + cells.keys()
370 self.closure = self.cellvars + self.freevars
371
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000372 def _lookupName(self, name, list):
373 """Return index of name in list, appending if necessary"""
Jeremy Hylton9c048f92000-10-13 21:58:13 +0000374 t = type(name)
375 for i in range(len(list)):
376 # must do a comparison on type first to prevent UnicodeErrors
377 if t == type(list[i]) and list[i] == name:
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000378 return i
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000379 end = len(list)
380 list.append(name)
381 return end
Jeremy Hyltona5058122000-02-14 14:14:29 +0000382
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000383 _converters = {}
384 def _convert_LOAD_CONST(self, arg):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000385 if hasattr(arg, 'getCode'):
386 arg = arg.getCode()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000387 return self._lookupName(arg, self.consts)
388
389 def _convert_LOAD_FAST(self, arg):
390 self._lookupName(arg, self.names)
391 return self._lookupName(arg, self.varnames)
392 _convert_STORE_FAST = _convert_LOAD_FAST
393 _convert_DELETE_FAST = _convert_LOAD_FAST
394
395 def _convert_NAME(self, arg):
396 return self._lookupName(arg, self.names)
397 _convert_LOAD_NAME = _convert_NAME
398 _convert_STORE_NAME = _convert_NAME
399 _convert_DELETE_NAME = _convert_NAME
400 _convert_IMPORT_NAME = _convert_NAME
401 _convert_IMPORT_FROM = _convert_NAME
402 _convert_STORE_ATTR = _convert_NAME
403 _convert_LOAD_ATTR = _convert_NAME
404 _convert_DELETE_ATTR = _convert_NAME
405 _convert_LOAD_GLOBAL = _convert_NAME
406 _convert_STORE_GLOBAL = _convert_NAME
407 _convert_DELETE_GLOBAL = _convert_NAME
408
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000409 def _convert_DEREF(self, arg):
410 self._lookupName(arg, self.names)
411 self._lookupName(arg, self.varnames)
412 return self._lookupName(arg, self.closure)
413 _convert_LOAD_DEREF = _convert_DEREF
414 _convert_STORE_DEREF = _convert_DEREF
415
416 def _convert_LOAD_CLOSURE(self, arg):
417 self._lookupName(arg, self.varnames)
418 return self._lookupName(arg, self.closure)
419
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000420 _cmp = list(dis.cmp_op)
421 def _convert_COMPARE_OP(self, arg):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000422 return self._cmp.index(arg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000423
424 # similarly for other opcodes...
425
426 for name, obj in locals().items():
427 if name[:9] == "_convert_":
428 opname = name[9:]
429 _converters[opname] = obj
430 del name, obj, opname
431
432 def makeByteCode(self):
433 assert self.stage == CONV
434 self.lnotab = lnotab = LineAddrTable()
435 for t in self.insts:
436 opname = t[0]
437 if len(t) == 1:
438 lnotab.addCode(self.opnum[opname])
439 else:
440 oparg = t[1]
441 if opname == "SET_LINENO":
442 lnotab.nextLine(oparg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000443 hi, lo = twobyte(oparg)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000444 try:
445 lnotab.addCode(self.opnum[opname], lo, hi)
446 except ValueError:
447 print opname, oparg
448 print self.opnum[opname], lo, hi
449 raise
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000450 self.stage = DONE
451
Jeremy Hyltona5058122000-02-14 14:14:29 +0000452 opnum = {}
453 for num in range(len(dis.opname)):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000454 opnum[dis.opname[num]] = num
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000455 del num
Jeremy Hyltona5058122000-02-14 14:14:29 +0000456
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000457 def newCodeObject(self):
458 assert self.stage == DONE
459 if self.flags == 0:
460 nlocals = 0
461 else:
462 nlocals = len(self.varnames)
463 argcount = self.argcount
464 if self.flags & CO_VARKEYWORDS:
465 argcount = argcount - 1
466 return new.code(argcount, nlocals, self.stacksize, self.flags,
467 self.lnotab.getCode(), self.getConsts(),
468 tuple(self.names), tuple(self.varnames),
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000469 self.filename, self.name, self.lnotab.firstline,
Jeremy Hylton364f9b92001-04-12 06:40:42 +0000470 self.lnotab.getTable(), tuple(self.freevars),
471 tuple(self.cellvars))
Jeremy Hyltona5058122000-02-14 14:14:29 +0000472
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000473 def getConsts(self):
474 """Return a tuple for the const slot of the code object
Jeremy Hyltona5058122000-02-14 14:14:29 +0000475
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000476 Must convert references to code (MAKE_FUNCTION) to code
477 objects recursively.
478 """
479 l = []
480 for elt in self.consts:
481 if isinstance(elt, PyFlowGraph):
482 elt = elt.getCode()
483 l.append(elt)
484 return tuple(l)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000485
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000486def isJump(opname):
487 if opname[:4] == 'JUMP':
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000488 return 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000489
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000490class TupleArg:
491 """Helper for marking func defs with nested tuples in arglist"""
492 def __init__(self, count, names):
493 self.count = count
494 self.names = names
495 def __repr__(self):
496 return "TupleArg(%s, %s)" % (self.count, self.names)
497 def getName(self):
498 return ".nested%d" % self.count
499
500def getArgCount(args):
501 argcount = len(args)
502 if args:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000503 for arg in args:
504 if isinstance(arg, TupleArg):
505 numNames = len(misc.flatten(arg.names))
506 argcount = argcount - numNames
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000507 return argcount
508
509def twobyte(val):
510 """Convert an int argument into high and low bytes"""
511 assert type(val) == types.IntType
512 return divmod(val, 256)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000513
514class LineAddrTable:
515 """lnotab
516
517 This class builds the lnotab, which is undocumented but described
518 by com_set_lineno in compile.c. Here's an attempt at explanation:
519
520 For each SET_LINENO instruction after the first one, two bytes are
521 added to lnotab. (In some cases, multiple two-byte entries are
522 added.) The first byte is the distance in bytes between the
523 instruction for the last SET_LINENO and the current SET_LINENO.
524 The second byte is offset in line numbers. If either offset is
525 greater than 255, multiple two-byte entries are added -- one entry
526 for each factor of 255.
527 """
528
529 def __init__(self):
530 self.code = []
531 self.codeOffset = 0
532 self.firstline = 0
533 self.lastline = 0
534 self.lastoff = 0
535 self.lnotab = []
536
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000537 def addCode(self, *args):
538 for arg in args:
539 self.code.append(chr(arg))
540 self.codeOffset = self.codeOffset + len(args)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000541
542 def nextLine(self, lineno):
543 if self.firstline == 0:
544 self.firstline = lineno
545 self.lastline = lineno
546 else:
547 # compute deltas
548 addr = self.codeOffset - self.lastoff
549 line = lineno - self.lastline
Jeremy Hylton92f39722000-09-01 20:47:37 +0000550 # Python assumes that lineno always increases with
551 # increasing bytecode address (lnotab is unsigned char).
552 # Depending on when SET_LINENO instructions are emitted
553 # this is not always true. Consider the code:
554 # a = (1,
555 # b)
556 # In the bytecode stream, the assignment to "a" occurs
557 # after the loading of "b". This works with the C Python
558 # compiler because it only generates a SET_LINENO instruction
559 # for the assignment.
560 if line > 0:
561 while addr > 0 or line > 0:
562 # write the values in 1-byte chunks that sum
563 # to desired value
564 trunc_addr = addr
565 trunc_line = line
566 if trunc_addr > 255:
567 trunc_addr = 255
568 if trunc_line > 255:
569 trunc_line = 255
570 self.lnotab.append(trunc_addr)
571 self.lnotab.append(trunc_line)
572 addr = addr - trunc_addr
573 line = line - trunc_line
574 self.lastline = lineno
575 self.lastoff = self.codeOffset
Jeremy Hyltona5058122000-02-14 14:14:29 +0000576
577 def getCode(self):
578 return string.join(self.code, '')
579
580 def getTable(self):
581 return string.join(map(chr, self.lnotab), '')
582
Jeremy Hyltona5058122000-02-14 14:14:29 +0000583class StackDepthTracker:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000584 # XXX 1. need to keep track of stack depth on jumps
585 # XXX 2. at least partly as a result, this code is broken
Jeremy Hyltona5058122000-02-14 14:14:29 +0000586
587 def findDepth(self, insts):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000588 depth = 0
589 maxDepth = 0
590 for i in insts:
591 opname = i[0]
592 delta = self.effect.get(opname, 0)
593 if delta > 1:
594 depth = depth + delta
595 elif delta < 0:
596 if depth > maxDepth:
597 maxDepth = depth
598 depth = depth + delta
599 else:
600 if depth > maxDepth:
601 maxDepth = depth
602 # now check patterns
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000603 for pat, pat_delta in self.patterns:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000604 if opname[:len(pat)] == pat:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000605 delta = pat_delta
Jeremy Hylton772dd412000-02-21 22:46:00 +0000606 depth = depth + delta
607 break
608 # if we still haven't found a match
609 if delta == 0:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000610 meth = getattr(self, opname, None)
611 if meth is not None:
612 depth = depth + meth(i[1])
Jeremy Hylton772dd412000-02-21 22:46:00 +0000613 if depth < 0:
614 depth = 0
615 return maxDepth
Jeremy Hyltona5058122000-02-14 14:14:29 +0000616
617 effect = {
Jeremy Hylton772dd412000-02-21 22:46:00 +0000618 'POP_TOP': -1,
619 'DUP_TOP': 1,
620 'SLICE+1': -1,
621 'SLICE+2': -1,
622 'SLICE+3': -2,
623 'STORE_SLICE+0': -1,
624 'STORE_SLICE+1': -2,
625 'STORE_SLICE+2': -2,
626 'STORE_SLICE+3': -3,
627 'DELETE_SLICE+0': -1,
628 'DELETE_SLICE+1': -2,
629 'DELETE_SLICE+2': -2,
630 'DELETE_SLICE+3': -3,
631 'STORE_SUBSCR': -3,
632 'DELETE_SUBSCR': -2,
633 # PRINT_EXPR?
634 'PRINT_ITEM': -1,
635 'LOAD_LOCALS': 1,
636 'RETURN_VALUE': -1,
637 'EXEC_STMT': -2,
638 'BUILD_CLASS': -2,
639 'STORE_NAME': -1,
640 'STORE_ATTR': -2,
641 'DELETE_ATTR': -1,
642 'STORE_GLOBAL': -1,
643 'BUILD_MAP': 1,
644 'COMPARE_OP': -1,
645 'STORE_FAST': -1,
Jeremy Hylton4e1be722000-10-12 20:23:23 +0000646 'IMPORT_STAR': -1,
647 'IMPORT_NAME': 0,
648 'IMPORT_FROM': 1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000649 }
Jeremy Hyltona5058122000-02-14 14:14:29 +0000650 # use pattern match
651 patterns = [
Jeremy Hylton772dd412000-02-21 22:46:00 +0000652 ('BINARY_', -1),
653 ('LOAD_', 1),
Jeremy Hylton772dd412000-02-21 22:46:00 +0000654 ]
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000655
656 # special cases:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000657 # UNPACK_SEQUENCE, BUILD_TUPLE,
Jeremy Hyltona5058122000-02-14 14:14:29 +0000658 # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000659 def UNPACK_SEQUENCE(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000660 return count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000661 def BUILD_TUPLE(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000662 return -count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000663 def BUILD_LIST(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000664 return -count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000665 def CALL_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000666 hi, lo = divmod(argc, 256)
667 return lo + hi * 2
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000668 def CALL_FUNCTION_VAR(self, argc):
669 return self.CALL_FUNCTION(argc)+1
670 def CALL_FUNCTION_KW(self, argc):
671 return self.CALL_FUNCTION(argc)+1
672 def CALL_FUNCTION_VAR_KW(self, argc):
673 return self.CALL_FUNCTION(argc)+2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000674 def MAKE_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000675 return -argc
Jeremy Hyltona5058122000-02-14 14:14:29 +0000676 def BUILD_SLICE(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000677 if argc == 2:
678 return -1
679 elif argc == 3:
680 return -2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000681
682findDepth = StackDepthTracker().findDepth