blob: 43bf6f4e9b1cfbf7d9802250a0e4d89fe80c4d9b [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
102## for b in order:
103## print repr(b)
104## print "\t", b.get_children()
105## print b
106## print
107
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000108 return order
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000109
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000110 def getBlocks(self):
111 return self.blocks.elements()
112
113 def getRoot(self):
114 """Return nodes appropriate for use with dominator"""
115 return self.entry
116
117 def getContainedGraphs(self):
118 l = []
119 for b in self.getBlocks():
120 l.extend(b.getContainedGraphs())
121 return l
122
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000123def dfs_postorder(b, seen):
124 """Depth-first search of tree rooted at b, return in postorder"""
125 order = []
126 seen[b] = b
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000127 for c in b.get_children():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000128 if seen.has_key(c):
129 continue
130 order = order + dfs_postorder(c, seen)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000131 order.append(b)
132 return order
133
134class Block:
135 _count = 0
136
137 def __init__(self, label=''):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000138 self.insts = []
139 self.inEdges = misc.Set()
140 self.outEdges = misc.Set()
141 self.label = label
142 self.bid = Block._count
143 self.next = []
144 Block._count = Block._count + 1
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000145
146 def __repr__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000147 if self.label:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000148 return "<block %s id=%d>" % (self.label, self.bid)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000149 else:
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000150 return "<block id=%d>" % (self.bid)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000151
152 def __str__(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000153 insts = map(str, self.insts)
154 return "<block %s %d:\n%s>" % (self.label, self.bid,
155 string.join(insts, '\n'))
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000156
157 def emit(self, inst):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000158 op = inst[0]
159 if op[:4] == 'JUMP':
160 self.outEdges.add(inst[1])
161 self.insts.append(inst)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000162
163 def getInstructions(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000164 return self.insts
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000165
166 def addInEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000167 self.inEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000168
169 def addOutEdge(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000170 self.outEdges.add(block)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000171
172 def addNext(self, block):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000173 self.next.append(block)
174 assert len(self.next) == 1, map(str, self.next)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000175
Jeremy Hyltoneefaeb72000-11-06 03:47:39 +0000176 _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS',
177 'JUMP_ABSOLUTE', 'JUMP_FORWARD')
178
179 def pruneNext(self):
180 """Remove bogus edge for unconditional transfers
181
182 Each block has a next edge that accounts for implicit control
183 transfers, e.g. from a JUMP_IF_FALSE to the block that will be
184 executed if the test is true.
185
186 These edges must remain for the current assembler code to
187 work. If they are removed, the dfs_postorder gets things in
188 weird orders. However, they shouldn't be there for other
189 purposes, e.g. conversion to SSA form. This method will
190 remove the next edge when it follows an unconditional control
191 transfer.
192 """
193 try:
194 op, arg = self.insts[-1]
195 except (IndexError, ValueError):
196 return
197 if op in self._uncond_transfer:
198 self.next = []
199
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000200 def get_children(self):
201 if self.next and self.next[0] in self.outEdges:
202 self.outEdges.remove(self.next[0])
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000203 return self.outEdges.elements() + self.next
Jeremy Hyltona5058122000-02-14 14:14:29 +0000204
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000205 def getContainedGraphs(self):
206 """Return all graphs contained within this block.
207
208 For example, a MAKE_FUNCTION block will contain a reference to
209 the graph for the function body.
210 """
211 contained = []
212 for inst in self.insts:
213 if len(inst) == 1:
214 continue
215 op = inst[1]
216 if hasattr(op, 'graph'):
217 contained.append(op.graph)
218 return contained
219
Jeremy Hyltona5058122000-02-14 14:14:29 +0000220# flags for code objects
221CO_OPTIMIZED = 0x0001
222CO_NEWLOCALS = 0x0002
223CO_VARARGS = 0x0004
224CO_VARKEYWORDS = 0x0008
225
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000226# the FlowGraph is transformed in place; it exists in one of these states
227RAW = "RAW"
228FLAT = "FLAT"
229CONV = "CONV"
230DONE = "DONE"
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000231
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000232class PyFlowGraph(FlowGraph):
233 super_init = FlowGraph.__init__
Jeremy Hyltona5058122000-02-14 14:14:29 +0000234
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000235 def __init__(self, name, filename, args=(), optimized=0):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000236 self.super_init()
237 self.name = name
238 self.filename = filename
239 self.docstring = None
240 self.args = args # XXX
241 self.argcount = getArgCount(args)
242 if optimized:
243 self.flags = CO_OPTIMIZED | CO_NEWLOCALS
244 else:
245 self.flags = 0
246 self.consts = []
247 self.names = []
Jeremy Hyltona5058122000-02-14 14:14:29 +0000248 self.varnames = list(args) or []
Jeremy Hylton3ec7e2c2000-02-17 22:09:35 +0000249 for i in range(len(self.varnames)):
250 var = self.varnames[i]
251 if isinstance(var, TupleArg):
252 self.varnames[i] = var.getName()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000253 self.stage = RAW
Jeremy Hyltona5058122000-02-14 14:14:29 +0000254
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000255 def setDocstring(self, doc):
256 self.docstring = doc
Jeremy Hylton2ce27b22000-02-16 00:50:29 +0000257
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000258 def setFlag(self, flag):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000259 self.flags = self.flags | flag
260 if flag == CO_VARARGS:
261 self.argcount = self.argcount - 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000262
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000263 def getCode(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000264 """Get a Python code object"""
265 if self.stage == RAW:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000266 self.flattenGraph()
267 if self.stage == FLAT:
268 self.convertArgs()
269 if self.stage == CONV:
270 self.makeByteCode()
271 if self.stage == DONE:
272 return self.newCodeObject()
273 raise RuntimeError, "inconsistent PyFlowGraph state"
Jeremy Hyltona5058122000-02-14 14:14:29 +0000274
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000275 def dump(self, io=None):
276 if io:
277 save = sys.stdout
278 sys.stdout = io
279 pc = 0
Jeremy Hyltona5058122000-02-14 14:14:29 +0000280 for t in self.insts:
281 opname = t[0]
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000282 if opname == "SET_LINENO":
283 print
Jeremy Hyltona5058122000-02-14 14:14:29 +0000284 if len(t) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000285 print "\t", "%3d" % pc, opname
286 pc = pc + 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000287 else:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000288 print "\t", "%3d" % pc, opname, t[1]
289 pc = pc + 3
290 if io:
291 sys.stdout = save
Jeremy Hyltona5058122000-02-14 14:14:29 +0000292
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000293 def flattenGraph(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000294 """Arrange the blocks in order and resolve jumps"""
295 assert self.stage == RAW
296 self.insts = insts = []
297 pc = 0
298 begin = {}
299 end = {}
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000300 for b in self.getBlocksInOrder():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000301 begin[b] = pc
302 for inst in b.getInstructions():
303 insts.append(inst)
304 if len(inst) == 1:
305 pc = pc + 1
306 else:
307 # arg takes 2 bytes
308 pc = pc + 3
309 end[b] = pc
310 pc = 0
311 for i in range(len(insts)):
312 inst = insts[i]
313 if len(inst) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000314 pc = pc + 1
315 else:
316 pc = pc + 3
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000317 opname = inst[0]
318 if self.hasjrel.has_elt(opname):
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000319 oparg = inst[1]
320 offset = begin[oparg] - pc
321 insts[i] = opname, offset
322 elif self.hasjabs.has_elt(opname):
323 insts[i] = opname, begin[inst[1]]
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000324 self.stacksize = findDepth(self.insts)
325 self.stage = FLAT
Jeremy Hyltona5058122000-02-14 14:14:29 +0000326
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000327 hasjrel = misc.Set()
328 for i in dis.hasjrel:
329 hasjrel.add(dis.opname[i])
330 hasjabs = misc.Set()
331 for i in dis.hasjabs:
332 hasjabs.add(dis.opname[i])
Jeremy Hyltona5058122000-02-14 14:14:29 +0000333
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000334 def convertArgs(self):
335 """Convert arguments from symbolic to concrete form"""
336 assert self.stage == FLAT
Jeremy Hylton5af105e2001-04-11 16:21:51 +0000337 self.consts.insert(0, self.docstring)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000338 for i in range(len(self.insts)):
339 t = self.insts[i]
340 if len(t) == 2:
341 opname = t[0]
342 oparg = t[1]
343 conv = self._converters.get(opname, None)
344 if conv:
345 self.insts[i] = opname, conv(self, oparg)
346 self.stage = CONV
Jeremy Hyltona5058122000-02-14 14:14:29 +0000347
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000348 def _lookupName(self, name, list):
349 """Return index of name in list, appending if necessary"""
Jeremy Hylton9c048f92000-10-13 21:58:13 +0000350 t = type(name)
351 for i in range(len(list)):
352 # must do a comparison on type first to prevent UnicodeErrors
353 if t == type(list[i]) and list[i] == name:
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000354 return i
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000355 end = len(list)
356 list.append(name)
357 return end
Jeremy Hyltona5058122000-02-14 14:14:29 +0000358
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000359 _converters = {}
360 def _convert_LOAD_CONST(self, arg):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000361 if hasattr(arg, 'getCode'):
362 arg = arg.getCode()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000363 return self._lookupName(arg, self.consts)
364
365 def _convert_LOAD_FAST(self, arg):
366 self._lookupName(arg, self.names)
367 return self._lookupName(arg, self.varnames)
368 _convert_STORE_FAST = _convert_LOAD_FAST
369 _convert_DELETE_FAST = _convert_LOAD_FAST
370
371 def _convert_NAME(self, arg):
372 return self._lookupName(arg, self.names)
373 _convert_LOAD_NAME = _convert_NAME
374 _convert_STORE_NAME = _convert_NAME
375 _convert_DELETE_NAME = _convert_NAME
376 _convert_IMPORT_NAME = _convert_NAME
377 _convert_IMPORT_FROM = _convert_NAME
378 _convert_STORE_ATTR = _convert_NAME
379 _convert_LOAD_ATTR = _convert_NAME
380 _convert_DELETE_ATTR = _convert_NAME
381 _convert_LOAD_GLOBAL = _convert_NAME
382 _convert_STORE_GLOBAL = _convert_NAME
383 _convert_DELETE_GLOBAL = _convert_NAME
384
385 _cmp = list(dis.cmp_op)
386 def _convert_COMPARE_OP(self, arg):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000387 return self._cmp.index(arg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000388
389 # similarly for other opcodes...
390
391 for name, obj in locals().items():
392 if name[:9] == "_convert_":
393 opname = name[9:]
394 _converters[opname] = obj
395 del name, obj, opname
396
397 def makeByteCode(self):
398 assert self.stage == CONV
399 self.lnotab = lnotab = LineAddrTable()
400 for t in self.insts:
401 opname = t[0]
402 if len(t) == 1:
403 lnotab.addCode(self.opnum[opname])
404 else:
405 oparg = t[1]
406 if opname == "SET_LINENO":
407 lnotab.nextLine(oparg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000408 hi, lo = twobyte(oparg)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000409 try:
410 lnotab.addCode(self.opnum[opname], lo, hi)
411 except ValueError:
412 print opname, oparg
413 print self.opnum[opname], lo, hi
414 raise
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000415 self.stage = DONE
416
Jeremy Hyltona5058122000-02-14 14:14:29 +0000417 opnum = {}
418 for num in range(len(dis.opname)):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000419 opnum[dis.opname[num]] = num
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000420 del num
Jeremy Hyltona5058122000-02-14 14:14:29 +0000421
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000422 def newCodeObject(self):
423 assert self.stage == DONE
424 if self.flags == 0:
425 nlocals = 0
426 else:
427 nlocals = len(self.varnames)
428 argcount = self.argcount
429 if self.flags & CO_VARKEYWORDS:
430 argcount = argcount - 1
431 return new.code(argcount, nlocals, self.stacksize, self.flags,
432 self.lnotab.getCode(), self.getConsts(),
433 tuple(self.names), tuple(self.varnames),
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000434 self.filename, self.name, self.lnotab.firstline,
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000435 self.lnotab.getTable())
Jeremy Hyltona5058122000-02-14 14:14:29 +0000436
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000437 def getConsts(self):
438 """Return a tuple for the const slot of the code object
Jeremy Hyltona5058122000-02-14 14:14:29 +0000439
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000440 Must convert references to code (MAKE_FUNCTION) to code
441 objects recursively.
442 """
443 l = []
444 for elt in self.consts:
445 if isinstance(elt, PyFlowGraph):
446 elt = elt.getCode()
447 l.append(elt)
448 return tuple(l)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000449
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000450def isJump(opname):
451 if opname[:4] == 'JUMP':
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000452 return 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000453
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000454class TupleArg:
455 """Helper for marking func defs with nested tuples in arglist"""
456 def __init__(self, count, names):
457 self.count = count
458 self.names = names
459 def __repr__(self):
460 return "TupleArg(%s, %s)" % (self.count, self.names)
461 def getName(self):
462 return ".nested%d" % self.count
463
464def getArgCount(args):
465 argcount = len(args)
466 if args:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000467 for arg in args:
468 if isinstance(arg, TupleArg):
469 numNames = len(misc.flatten(arg.names))
470 argcount = argcount - numNames
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000471 return argcount
472
473def twobyte(val):
474 """Convert an int argument into high and low bytes"""
475 assert type(val) == types.IntType
476 return divmod(val, 256)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000477
478class LineAddrTable:
479 """lnotab
480
481 This class builds the lnotab, which is undocumented but described
482 by com_set_lineno in compile.c. Here's an attempt at explanation:
483
484 For each SET_LINENO instruction after the first one, two bytes are
485 added to lnotab. (In some cases, multiple two-byte entries are
486 added.) The first byte is the distance in bytes between the
487 instruction for the last SET_LINENO and the current SET_LINENO.
488 The second byte is offset in line numbers. If either offset is
489 greater than 255, multiple two-byte entries are added -- one entry
490 for each factor of 255.
491 """
492
493 def __init__(self):
494 self.code = []
495 self.codeOffset = 0
496 self.firstline = 0
497 self.lastline = 0
498 self.lastoff = 0
499 self.lnotab = []
500
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000501 def addCode(self, *args):
502 for arg in args:
503 self.code.append(chr(arg))
504 self.codeOffset = self.codeOffset + len(args)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000505
506 def nextLine(self, lineno):
507 if self.firstline == 0:
508 self.firstline = lineno
509 self.lastline = lineno
510 else:
511 # compute deltas
512 addr = self.codeOffset - self.lastoff
513 line = lineno - self.lastline
Jeremy Hylton92f39722000-09-01 20:47:37 +0000514 # Python assumes that lineno always increases with
515 # increasing bytecode address (lnotab is unsigned char).
516 # Depending on when SET_LINENO instructions are emitted
517 # this is not always true. Consider the code:
518 # a = (1,
519 # b)
520 # In the bytecode stream, the assignment to "a" occurs
521 # after the loading of "b". This works with the C Python
522 # compiler because it only generates a SET_LINENO instruction
523 # for the assignment.
524 if line > 0:
525 while addr > 0 or line > 0:
526 # write the values in 1-byte chunks that sum
527 # to desired value
528 trunc_addr = addr
529 trunc_line = line
530 if trunc_addr > 255:
531 trunc_addr = 255
532 if trunc_line > 255:
533 trunc_line = 255
534 self.lnotab.append(trunc_addr)
535 self.lnotab.append(trunc_line)
536 addr = addr - trunc_addr
537 line = line - trunc_line
538 self.lastline = lineno
539 self.lastoff = self.codeOffset
Jeremy Hyltona5058122000-02-14 14:14:29 +0000540
541 def getCode(self):
542 return string.join(self.code, '')
543
544 def getTable(self):
545 return string.join(map(chr, self.lnotab), '')
546
Jeremy Hyltona5058122000-02-14 14:14:29 +0000547class StackDepthTracker:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000548 # XXX 1. need to keep track of stack depth on jumps
549 # XXX 2. at least partly as a result, this code is broken
Jeremy Hyltona5058122000-02-14 14:14:29 +0000550
551 def findDepth(self, insts):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000552 depth = 0
553 maxDepth = 0
554 for i in insts:
555 opname = i[0]
556 delta = self.effect.get(opname, 0)
557 if delta > 1:
558 depth = depth + delta
559 elif delta < 0:
560 if depth > maxDepth:
561 maxDepth = depth
562 depth = depth + delta
563 else:
564 if depth > maxDepth:
565 maxDepth = depth
566 # now check patterns
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000567 for pat, pat_delta in self.patterns:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000568 if opname[:len(pat)] == pat:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000569 delta = pat_delta
Jeremy Hylton772dd412000-02-21 22:46:00 +0000570 depth = depth + delta
571 break
572 # if we still haven't found a match
573 if delta == 0:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000574 meth = getattr(self, opname, None)
575 if meth is not None:
576 depth = depth + meth(i[1])
Jeremy Hylton772dd412000-02-21 22:46:00 +0000577 if depth < 0:
578 depth = 0
579 return maxDepth
Jeremy Hyltona5058122000-02-14 14:14:29 +0000580
581 effect = {
Jeremy Hylton772dd412000-02-21 22:46:00 +0000582 'POP_TOP': -1,
583 'DUP_TOP': 1,
584 'SLICE+1': -1,
585 'SLICE+2': -1,
586 'SLICE+3': -2,
587 'STORE_SLICE+0': -1,
588 'STORE_SLICE+1': -2,
589 'STORE_SLICE+2': -2,
590 'STORE_SLICE+3': -3,
591 'DELETE_SLICE+0': -1,
592 'DELETE_SLICE+1': -2,
593 'DELETE_SLICE+2': -2,
594 'DELETE_SLICE+3': -3,
595 'STORE_SUBSCR': -3,
596 'DELETE_SUBSCR': -2,
597 # PRINT_EXPR?
598 'PRINT_ITEM': -1,
599 'LOAD_LOCALS': 1,
600 'RETURN_VALUE': -1,
601 'EXEC_STMT': -2,
602 'BUILD_CLASS': -2,
603 'STORE_NAME': -1,
604 'STORE_ATTR': -2,
605 'DELETE_ATTR': -1,
606 'STORE_GLOBAL': -1,
607 'BUILD_MAP': 1,
608 'COMPARE_OP': -1,
609 'STORE_FAST': -1,
Jeremy Hylton4e1be722000-10-12 20:23:23 +0000610 'IMPORT_STAR': -1,
611 'IMPORT_NAME': 0,
612 'IMPORT_FROM': 1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000613 }
Jeremy Hyltona5058122000-02-14 14:14:29 +0000614 # use pattern match
615 patterns = [
Jeremy Hylton772dd412000-02-21 22:46:00 +0000616 ('BINARY_', -1),
617 ('LOAD_', 1),
Jeremy Hylton772dd412000-02-21 22:46:00 +0000618 ]
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000619
620 # special cases:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000621 # UNPACK_SEQUENCE, BUILD_TUPLE,
Jeremy Hyltona5058122000-02-14 14:14:29 +0000622 # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000623 def UNPACK_SEQUENCE(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000624 return count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000625 def BUILD_TUPLE(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000626 return -count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000627 def BUILD_LIST(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000628 return -count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000629 def CALL_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000630 hi, lo = divmod(argc, 256)
631 return lo + hi * 2
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000632 def CALL_FUNCTION_VAR(self, argc):
633 return self.CALL_FUNCTION(argc)+1
634 def CALL_FUNCTION_KW(self, argc):
635 return self.CALL_FUNCTION(argc)+1
636 def CALL_FUNCTION_VAR_KW(self, argc):
637 return self.CALL_FUNCTION(argc)+2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000638 def MAKE_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000639 return -argc
Jeremy Hyltona5058122000-02-14 14:14:29 +0000640 def BUILD_SLICE(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000641 if argc == 2:
642 return -1
643 elif argc == 3:
644 return -2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000645
646findDepth = StackDepthTracker().findDepth