blob: 9f9e90487f13f9c3e842d1148ecda085e80c8303 [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
257 self.consts.insert(0, doc)
Jeremy Hylton2ce27b22000-02-16 00:50:29 +0000258
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000259 def setFlag(self, flag):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000260 self.flags = self.flags | flag
261 if flag == CO_VARARGS:
262 self.argcount = self.argcount - 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000263
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000264 def getCode(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000265 """Get a Python code object"""
266 if self.stage == RAW:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000267 self.flattenGraph()
268 if self.stage == FLAT:
269 self.convertArgs()
270 if self.stage == CONV:
271 self.makeByteCode()
272 if self.stage == DONE:
273 return self.newCodeObject()
274 raise RuntimeError, "inconsistent PyFlowGraph state"
Jeremy Hyltona5058122000-02-14 14:14:29 +0000275
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000276 def dump(self, io=None):
277 if io:
278 save = sys.stdout
279 sys.stdout = io
280 pc = 0
Jeremy Hyltona5058122000-02-14 14:14:29 +0000281 for t in self.insts:
282 opname = t[0]
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000283 if opname == "SET_LINENO":
284 print
Jeremy Hyltona5058122000-02-14 14:14:29 +0000285 if len(t) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000286 print "\t", "%3d" % pc, opname
287 pc = pc + 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000288 else:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000289 print "\t", "%3d" % pc, opname, t[1]
290 pc = pc + 3
291 if io:
292 sys.stdout = save
Jeremy Hyltona5058122000-02-14 14:14:29 +0000293
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000294 def flattenGraph(self):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000295 """Arrange the blocks in order and resolve jumps"""
296 assert self.stage == RAW
297 self.insts = insts = []
298 pc = 0
299 begin = {}
300 end = {}
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000301 for b in self.getBlocksInOrder():
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000302 begin[b] = pc
303 for inst in b.getInstructions():
304 insts.append(inst)
305 if len(inst) == 1:
306 pc = pc + 1
307 else:
308 # arg takes 2 bytes
309 pc = pc + 3
310 end[b] = pc
311 pc = 0
312 for i in range(len(insts)):
313 inst = insts[i]
314 if len(inst) == 1:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000315 pc = pc + 1
316 else:
317 pc = pc + 3
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000318 opname = inst[0]
319 if self.hasjrel.has_elt(opname):
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000320 oparg = inst[1]
321 offset = begin[oparg] - pc
322 insts[i] = opname, offset
323 elif self.hasjabs.has_elt(opname):
324 insts[i] = opname, begin[inst[1]]
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000325 self.stacksize = findDepth(self.insts)
326 self.stage = FLAT
Jeremy Hyltona5058122000-02-14 14:14:29 +0000327
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000328 hasjrel = misc.Set()
329 for i in dis.hasjrel:
330 hasjrel.add(dis.opname[i])
331 hasjabs = misc.Set()
332 for i in dis.hasjabs:
333 hasjabs.add(dis.opname[i])
Jeremy Hyltona5058122000-02-14 14:14:29 +0000334
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000335 def convertArgs(self):
336 """Convert arguments from symbolic to concrete form"""
337 assert self.stage == FLAT
338 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 found = None
351 t = type(name)
352 for i in range(len(list)):
353 # must do a comparison on type first to prevent UnicodeErrors
354 if t == type(list[i]) and list[i] == name:
355 found = 1
356 break
357 if found:
Jeremy Hyltonefd06942000-02-17 22:58:54 +0000358 # this is cheap, but incorrect in some cases, e.g 2 vs. 2L
359 if type(name) == type(list[i]):
360 return i
361 for i in range(len(list)):
362 elt = list[i]
363 if type(elt) == type(name) and elt == name:
364 return i
365 end = len(list)
366 list.append(name)
367 return end
Jeremy Hyltona5058122000-02-14 14:14:29 +0000368
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000369 _converters = {}
370 def _convert_LOAD_CONST(self, arg):
Jeremy Hylton314e3fb2000-11-06 03:43:11 +0000371 if hasattr(arg, 'getCode'):
372 arg = arg.getCode()
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000373 return self._lookupName(arg, self.consts)
374
375 def _convert_LOAD_FAST(self, arg):
376 self._lookupName(arg, self.names)
377 return self._lookupName(arg, self.varnames)
378 _convert_STORE_FAST = _convert_LOAD_FAST
379 _convert_DELETE_FAST = _convert_LOAD_FAST
380
381 def _convert_NAME(self, arg):
382 return self._lookupName(arg, self.names)
383 _convert_LOAD_NAME = _convert_NAME
384 _convert_STORE_NAME = _convert_NAME
385 _convert_DELETE_NAME = _convert_NAME
386 _convert_IMPORT_NAME = _convert_NAME
387 _convert_IMPORT_FROM = _convert_NAME
388 _convert_STORE_ATTR = _convert_NAME
389 _convert_LOAD_ATTR = _convert_NAME
390 _convert_DELETE_ATTR = _convert_NAME
391 _convert_LOAD_GLOBAL = _convert_NAME
392 _convert_STORE_GLOBAL = _convert_NAME
393 _convert_DELETE_GLOBAL = _convert_NAME
394
395 _cmp = list(dis.cmp_op)
396 def _convert_COMPARE_OP(self, arg):
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000397 return self._cmp.index(arg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000398
399 # similarly for other opcodes...
400
401 for name, obj in locals().items():
402 if name[:9] == "_convert_":
403 opname = name[9:]
404 _converters[opname] = obj
405 del name, obj, opname
406
407 def makeByteCode(self):
408 assert self.stage == CONV
409 self.lnotab = lnotab = LineAddrTable()
410 for t in self.insts:
411 opname = t[0]
412 if len(t) == 1:
413 lnotab.addCode(self.opnum[opname])
414 else:
415 oparg = t[1]
416 if opname == "SET_LINENO":
417 lnotab.nextLine(oparg)
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000418 hi, lo = twobyte(oparg)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000419 try:
420 lnotab.addCode(self.opnum[opname], lo, hi)
421 except ValueError:
422 print opname, oparg
423 print self.opnum[opname], lo, hi
424 raise
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000425 self.stage = DONE
426
Jeremy Hyltona5058122000-02-14 14:14:29 +0000427 opnum = {}
428 for num in range(len(dis.opname)):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000429 opnum[dis.opname[num]] = num
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000430 del num
Jeremy Hyltona5058122000-02-14 14:14:29 +0000431
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000432 def newCodeObject(self):
433 assert self.stage == DONE
434 if self.flags == 0:
435 nlocals = 0
436 else:
437 nlocals = len(self.varnames)
438 argcount = self.argcount
439 if self.flags & CO_VARKEYWORDS:
440 argcount = argcount - 1
441 return new.code(argcount, nlocals, self.stacksize, self.flags,
442 self.lnotab.getCode(), self.getConsts(),
443 tuple(self.names), tuple(self.varnames),
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000444 self.filename, self.name, self.lnotab.firstline,
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000445 self.lnotab.getTable())
Jeremy Hyltona5058122000-02-14 14:14:29 +0000446
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000447 def getConsts(self):
448 """Return a tuple for the const slot of the code object
Jeremy Hyltona5058122000-02-14 14:14:29 +0000449
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000450 Must convert references to code (MAKE_FUNCTION) to code
451 objects recursively.
452 """
453 l = []
454 for elt in self.consts:
455 if isinstance(elt, PyFlowGraph):
456 elt = elt.getCode()
457 l.append(elt)
458 return tuple(l)
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000459
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000460def isJump(opname):
461 if opname[:4] == 'JUMP':
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000462 return 1
Jeremy Hyltona5058122000-02-14 14:14:29 +0000463
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000464class TupleArg:
465 """Helper for marking func defs with nested tuples in arglist"""
466 def __init__(self, count, names):
467 self.count = count
468 self.names = names
469 def __repr__(self):
470 return "TupleArg(%s, %s)" % (self.count, self.names)
471 def getName(self):
472 return ".nested%d" % self.count
473
474def getArgCount(args):
475 argcount = len(args)
476 if args:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000477 for arg in args:
478 if isinstance(arg, TupleArg):
479 numNames = len(misc.flatten(arg.names))
480 argcount = argcount - numNames
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000481 return argcount
482
483def twobyte(val):
484 """Convert an int argument into high and low bytes"""
485 assert type(val) == types.IntType
486 return divmod(val, 256)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000487
488class LineAddrTable:
489 """lnotab
490
491 This class builds the lnotab, which is undocumented but described
492 by com_set_lineno in compile.c. Here's an attempt at explanation:
493
494 For each SET_LINENO instruction after the first one, two bytes are
495 added to lnotab. (In some cases, multiple two-byte entries are
496 added.) The first byte is the distance in bytes between the
497 instruction for the last SET_LINENO and the current SET_LINENO.
498 The second byte is offset in line numbers. If either offset is
499 greater than 255, multiple two-byte entries are added -- one entry
500 for each factor of 255.
501 """
502
503 def __init__(self):
504 self.code = []
505 self.codeOffset = 0
506 self.firstline = 0
507 self.lastline = 0
508 self.lastoff = 0
509 self.lnotab = []
510
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000511 def addCode(self, *args):
512 for arg in args:
513 self.code.append(chr(arg))
514 self.codeOffset = self.codeOffset + len(args)
Jeremy Hyltona5058122000-02-14 14:14:29 +0000515
516 def nextLine(self, lineno):
517 if self.firstline == 0:
518 self.firstline = lineno
519 self.lastline = lineno
520 else:
521 # compute deltas
522 addr = self.codeOffset - self.lastoff
523 line = lineno - self.lastline
Jeremy Hylton92f39722000-09-01 20:47:37 +0000524 # Python assumes that lineno always increases with
525 # increasing bytecode address (lnotab is unsigned char).
526 # Depending on when SET_LINENO instructions are emitted
527 # this is not always true. Consider the code:
528 # a = (1,
529 # b)
530 # In the bytecode stream, the assignment to "a" occurs
531 # after the loading of "b". This works with the C Python
532 # compiler because it only generates a SET_LINENO instruction
533 # for the assignment.
534 if line > 0:
535 while addr > 0 or line > 0:
536 # write the values in 1-byte chunks that sum
537 # to desired value
538 trunc_addr = addr
539 trunc_line = line
540 if trunc_addr > 255:
541 trunc_addr = 255
542 if trunc_line > 255:
543 trunc_line = 255
544 self.lnotab.append(trunc_addr)
545 self.lnotab.append(trunc_line)
546 addr = addr - trunc_addr
547 line = line - trunc_line
548 self.lastline = lineno
549 self.lastoff = self.codeOffset
Jeremy Hyltona5058122000-02-14 14:14:29 +0000550
551 def getCode(self):
552 return string.join(self.code, '')
553
554 def getTable(self):
555 return string.join(map(chr, self.lnotab), '')
556
Jeremy Hyltona5058122000-02-14 14:14:29 +0000557class StackDepthTracker:
Jeremy Hylton36cc6a22000-03-16 20:06:59 +0000558 # XXX 1. need to keep track of stack depth on jumps
559 # XXX 2. at least partly as a result, this code is broken
Jeremy Hyltona5058122000-02-14 14:14:29 +0000560
561 def findDepth(self, insts):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000562 depth = 0
563 maxDepth = 0
564 for i in insts:
565 opname = i[0]
566 delta = self.effect.get(opname, 0)
567 if delta > 1:
568 depth = depth + delta
569 elif delta < 0:
570 if depth > maxDepth:
571 maxDepth = depth
572 depth = depth + delta
573 else:
574 if depth > maxDepth:
575 maxDepth = depth
576 # now check patterns
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000577 for pat, pat_delta in self.patterns:
Jeremy Hylton772dd412000-02-21 22:46:00 +0000578 if opname[:len(pat)] == pat:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000579 delta = pat_delta
Jeremy Hylton772dd412000-02-21 22:46:00 +0000580 depth = depth + delta
581 break
582 # if we still haven't found a match
583 if delta == 0:
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000584 meth = getattr(self, opname, None)
585 if meth is not None:
586 depth = depth + meth(i[1])
Jeremy Hylton772dd412000-02-21 22:46:00 +0000587 if depth < 0:
588 depth = 0
589 return maxDepth
Jeremy Hyltona5058122000-02-14 14:14:29 +0000590
591 effect = {
Jeremy Hylton772dd412000-02-21 22:46:00 +0000592 'POP_TOP': -1,
593 'DUP_TOP': 1,
594 'SLICE+1': -1,
595 'SLICE+2': -1,
596 'SLICE+3': -2,
597 'STORE_SLICE+0': -1,
598 'STORE_SLICE+1': -2,
599 'STORE_SLICE+2': -2,
600 'STORE_SLICE+3': -3,
601 'DELETE_SLICE+0': -1,
602 'DELETE_SLICE+1': -2,
603 'DELETE_SLICE+2': -2,
604 'DELETE_SLICE+3': -3,
605 'STORE_SUBSCR': -3,
606 'DELETE_SUBSCR': -2,
607 # PRINT_EXPR?
608 'PRINT_ITEM': -1,
609 'LOAD_LOCALS': 1,
610 'RETURN_VALUE': -1,
611 'EXEC_STMT': -2,
612 'BUILD_CLASS': -2,
613 'STORE_NAME': -1,
614 'STORE_ATTR': -2,
615 'DELETE_ATTR': -1,
616 'STORE_GLOBAL': -1,
617 'BUILD_MAP': 1,
618 'COMPARE_OP': -1,
619 'STORE_FAST': -1,
Jeremy Hylton4e1be722000-10-12 20:23:23 +0000620 'IMPORT_STAR': -1,
621 'IMPORT_NAME': 0,
622 'IMPORT_FROM': 1,
Jeremy Hylton772dd412000-02-21 22:46:00 +0000623 }
Jeremy Hyltona5058122000-02-14 14:14:29 +0000624 # use pattern match
625 patterns = [
Jeremy Hylton772dd412000-02-21 22:46:00 +0000626 ('BINARY_', -1),
627 ('LOAD_', 1),
Jeremy Hylton772dd412000-02-21 22:46:00 +0000628 ]
Jeremy Hyltonabd7ebf2000-03-06 18:53:14 +0000629
630 # special cases:
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000631 # UNPACK_SEQUENCE, BUILD_TUPLE,
Jeremy Hyltona5058122000-02-14 14:14:29 +0000632 # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
Thomas Wouters46cc7c02000-08-12 20:32:46 +0000633 def UNPACK_SEQUENCE(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000634 return count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000635 def BUILD_TUPLE(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000636 return -count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000637 def BUILD_LIST(self, count):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000638 return -count
Jeremy Hyltona5058122000-02-14 14:14:29 +0000639 def CALL_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000640 hi, lo = divmod(argc, 256)
641 return lo + hi * 2
Jeremy Hyltonbe317e62000-05-02 22:32:59 +0000642 def CALL_FUNCTION_VAR(self, argc):
643 return self.CALL_FUNCTION(argc)+1
644 def CALL_FUNCTION_KW(self, argc):
645 return self.CALL_FUNCTION(argc)+1
646 def CALL_FUNCTION_VAR_KW(self, argc):
647 return self.CALL_FUNCTION(argc)+2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000648 def MAKE_FUNCTION(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000649 return -argc
Jeremy Hyltona5058122000-02-14 14:14:29 +0000650 def BUILD_SLICE(self, argc):
Jeremy Hylton772dd412000-02-21 22:46:00 +0000651 if argc == 2:
652 return -1
653 elif argc == 3:
654 return -2
Jeremy Hyltona5058122000-02-14 14:14:29 +0000655
656findDepth = StackDepthTracker().findDepth