PyXR

c:\python24\lib \ compiler \ pyassem.py



0001 """A flow graph representation for Python bytecode"""
0002 
0003 import dis
0004 import new
0005 import sys
0006 import types
0007 
0008 from compiler import misc
0009 from compiler.consts \
0010      import CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS
0011 
0012 class FlowGraph:
0013     def __init__(self):
0014         self.current = self.entry = Block()
0015         self.exit = Block("exit")
0016         self.blocks = misc.Set()
0017         self.blocks.add(self.entry)
0018         self.blocks.add(self.exit)
0019 
0020     def startBlock(self, block):
0021         if self._debug:
0022             if self.current:
0023                 print "end", repr(self.current)
0024                 print "    next", self.current.next
0025                 print "   ", self.current.get_children()
0026             print repr(block)
0027         self.current = block
0028 
0029     def nextBlock(self, block=None):
0030         # XXX think we need to specify when there is implicit transfer
0031         # from one block to the next.  might be better to represent this
0032         # with explicit JUMP_ABSOLUTE instructions that are optimized
0033         # out when they are unnecessary.
0034         #
0035         # I think this strategy works: each block has a child
0036         # designated as "next" which is returned as the last of the
0037         # children.  because the nodes in a graph are emitted in
0038         # reverse post order, the "next" block will always be emitted
0039         # immediately after its parent.
0040         # Worry: maintaining this invariant could be tricky
0041         if block is None:
0042             block = self.newBlock()
0043 
0044         # Note: If the current block ends with an unconditional
0045         # control transfer, then it is incorrect to add an implicit
0046         # transfer to the block graph.  The current code requires
0047         # these edges to get the blocks emitted in the right order,
0048         # however. :-(  If a client needs to remove these edges, call
0049         # pruneEdges().
0050 
0051         self.current.addNext(block)
0052         self.startBlock(block)
0053 
0054     def newBlock(self):
0055         b = Block()
0056         self.blocks.add(b)
0057         return b
0058 
0059     def startExitBlock(self):
0060         self.startBlock(self.exit)
0061 
0062     _debug = 0
0063 
0064     def _enable_debug(self):
0065         self._debug = 1
0066 
0067     def _disable_debug(self):
0068         self._debug = 0
0069 
0070     def emit(self, *inst):
0071         if self._debug:
0072             print "\t", inst
0073         if inst[0] in ['RETURN_VALUE', 'YIELD_VALUE']:
0074             self.current.addOutEdge(self.exit)
0075         if len(inst) == 2 and isinstance(inst[1], Block):
0076             self.current.addOutEdge(inst[1])
0077         self.current.emit(inst)
0078 
0079     def getBlocksInOrder(self):
0080         """Return the blocks in reverse postorder
0081 
0082         i.e. each node appears before all of its successors
0083         """
0084         # XXX make sure every node that doesn't have an explicit next
0085         # is set so that next points to exit
0086         for b in self.blocks.elements():
0087             if b is self.exit:
0088                 continue
0089             if not b.next:
0090                 b.addNext(self.exit)
0091         order = dfs_postorder(self.entry, {})
0092         order.reverse()
0093         self.fixupOrder(order, self.exit)
0094         # hack alert
0095         if not self.exit in order:
0096             order.append(self.exit)
0097 
0098         return order
0099 
0100     def fixupOrder(self, blocks, default_next):
0101         """Fixup bad order introduced by DFS."""
0102 
0103         # XXX This is a total mess.  There must be a better way to get
0104         # the code blocks in the right order.
0105 
0106         self.fixupOrderHonorNext(blocks, default_next)
0107         self.fixupOrderForward(blocks, default_next)
0108 
0109     def fixupOrderHonorNext(self, blocks, default_next):
0110         """Fix one problem with DFS.
0111 
0112         The DFS uses child block, but doesn't know about the special
0113         "next" block.  As a result, the DFS can order blocks so that a
0114         block isn't next to the right block for implicit control
0115         transfers.
0116         """
0117         index = {}
0118         for i in range(len(blocks)):
0119             index[blocks[i]] = i
0120 
0121         for i in range(0, len(blocks) - 1):
0122             b = blocks[i]
0123             n = blocks[i + 1]
0124             if not b.next or b.next[0] == default_next or b.next[0] == n:
0125                 continue
0126             # The blocks are in the wrong order.  Find the chain of
0127             # blocks to insert where they belong.
0128             cur = b
0129             chain = []
0130             elt = cur
0131             while elt.next and elt.next[0] != default_next:
0132                 chain.append(elt.next[0])
0133                 elt = elt.next[0]
0134             # Now remove the blocks in the chain from the current
0135             # block list, so that they can be re-inserted.
0136             l = []
0137             for b in chain:
0138                 assert index[b] > i
0139                 l.append((index[b], b))
0140             l.sort()
0141             l.reverse()
0142             for j, b in l:
0143                 del blocks[index[b]]
0144             # Insert the chain in the proper location
0145             blocks[i:i + 1] = [cur] + chain
0146             # Finally, re-compute the block indexes
0147             for i in range(len(blocks)):
0148                 index[blocks[i]] = i
0149 
0150     def fixupOrderForward(self, blocks, default_next):
0151         """Make sure all JUMP_FORWARDs jump forward"""
0152         index = {}
0153         chains = []
0154         cur = []
0155         for b in blocks:
0156             index[b] = len(chains)
0157             cur.append(b)
0158             if b.next and b.next[0] == default_next:
0159                 chains.append(cur)
0160                 cur = []
0161         chains.append(cur)
0162 
0163         while 1:
0164             constraints = []
0165 
0166             for i in range(len(chains)):
0167                 l = chains[i]
0168                 for b in l:
0169                     for c in b.get_children():
0170                         if index[c] < i:
0171                             forward_p = 0
0172                             for inst in b.insts:
0173                                 if inst[0] == 'JUMP_FORWARD':
0174                                     if inst[1] == c:
0175                                         forward_p = 1
0176                             if not forward_p:
0177                                 continue
0178                             constraints.append((index[c], i))
0179 
0180             if not constraints:
0181                 break
0182 
0183             # XXX just do one for now
0184             # do swaps to get things in the right order
0185             goes_before, a_chain = constraints[0]
0186             assert a_chain > goes_before
0187             c = chains[a_chain]
0188             chains.remove(c)
0189             chains.insert(goes_before, c)
0190 
0191         del blocks[:]
0192         for c in chains:
0193             for b in c:
0194                 blocks.append(b)
0195 
0196     def getBlocks(self):
0197         return self.blocks.elements()
0198 
0199     def getRoot(self):
0200         """Return nodes appropriate for use with dominator"""
0201         return self.entry
0202 
0203     def getContainedGraphs(self):
0204         l = []
0205         for b in self.getBlocks():
0206             l.extend(b.getContainedGraphs())
0207         return l
0208 
0209 def dfs_postorder(b, seen):
0210     """Depth-first search of tree rooted at b, return in postorder"""
0211     order = []
0212     seen[b] = b
0213     for c in b.get_children():
0214         if seen.has_key(c):
0215             continue
0216         order = order + dfs_postorder(c, seen)
0217     order.append(b)
0218     return order
0219 
0220 class Block:
0221     _count = 0
0222 
0223     def __init__(self, label=''):
0224         self.insts = []
0225         self.inEdges = misc.Set()
0226         self.outEdges = misc.Set()
0227         self.label = label
0228         self.bid = Block._count
0229         self.next = []
0230         Block._count = Block._count + 1
0231 
0232     def __repr__(self):
0233         if self.label:
0234             return "<block %s id=%d>" % (self.label, self.bid)
0235         else:
0236             return "<block id=%d>" % (self.bid)
0237 
0238     def __str__(self):
0239         insts = map(str, self.insts)
0240         return "<block %s %d:\n%s>" % (self.label, self.bid,
0241                                        '\n'.join(insts))
0242 
0243     def emit(self, inst):
0244         op = inst[0]
0245         if op[:4] == 'JUMP':
0246             self.outEdges.add(inst[1])
0247         self.insts.append(inst)
0248 
0249     def getInstructions(self):
0250         return self.insts
0251 
0252     def addInEdge(self, block):
0253         self.inEdges.add(block)
0254 
0255     def addOutEdge(self, block):
0256         self.outEdges.add(block)
0257 
0258     def addNext(self, block):
0259         self.next.append(block)
0260         assert len(self.next) == 1, map(str, self.next)
0261 
0262     _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS', 'YIELD_VALUE',
0263                         'JUMP_ABSOLUTE', 'JUMP_FORWARD', 'CONTINUE_LOOP')
0264 
0265     def pruneNext(self):
0266         """Remove bogus edge for unconditional transfers
0267 
0268         Each block has a next edge that accounts for implicit control
0269         transfers, e.g. from a JUMP_IF_FALSE to the block that will be
0270         executed if the test is true.
0271 
0272         These edges must remain for the current assembler code to
0273         work. If they are removed, the dfs_postorder gets things in
0274         weird orders.  However, they shouldn't be there for other
0275         purposes, e.g. conversion to SSA form.  This method will
0276         remove the next edge when it follows an unconditional control
0277         transfer.
0278         """
0279         try:
0280             op, arg = self.insts[-1]
0281         except (IndexError, ValueError):
0282             return
0283         if op in self._uncond_transfer:
0284             self.next = []
0285 
0286     def get_children(self):
0287         if self.next and self.next[0] in self.outEdges:
0288             self.outEdges.remove(self.next[0])
0289         return self.outEdges.elements() + self.next
0290 
0291     def getContainedGraphs(self):
0292         """Return all graphs contained within this block.
0293 
0294         For example, a MAKE_FUNCTION block will contain a reference to
0295         the graph for the function body.
0296         """
0297         contained = []
0298         for inst in self.insts:
0299             if len(inst) == 1:
0300                 continue
0301             op = inst[1]
0302             if hasattr(op, 'graph'):
0303                 contained.append(op.graph)
0304         return contained
0305 
0306 # flags for code objects
0307 
0308 # the FlowGraph is transformed in place; it exists in one of these states
0309 RAW = "RAW"
0310 FLAT = "FLAT"
0311 CONV = "CONV"
0312 DONE = "DONE"
0313 
0314 class PyFlowGraph(FlowGraph):
0315     super_init = FlowGraph.__init__
0316 
0317     def __init__(self, name, filename, args=(), optimized=0, klass=None):
0318         self.super_init()
0319         self.name = name
0320         self.filename = filename
0321         self.docstring = None
0322         self.args = args # XXX
0323         self.argcount = getArgCount(args)
0324         self.klass = klass
0325         if optimized:
0326             self.flags = CO_OPTIMIZED | CO_NEWLOCALS
0327         else:
0328             self.flags = 0
0329         self.consts = []
0330         self.names = []
0331         # Free variables found by the symbol table scan, including
0332         # variables used only in nested scopes, are included here.
0333         self.freevars = []
0334         self.cellvars = []
0335         # The closure list is used to track the order of cell
0336         # variables and free variables in the resulting code object.
0337         # The offsets used by LOAD_CLOSURE/LOAD_DEREF refer to both
0338         # kinds of variables.
0339         self.closure = []
0340         self.varnames = list(args) or []
0341         for i in range(len(self.varnames)):
0342             var = self.varnames[i]
0343             if isinstance(var, TupleArg):
0344                 self.varnames[i] = var.getName()
0345         self.stage = RAW
0346 
0347     def setDocstring(self, doc):
0348         self.docstring = doc
0349 
0350     def setFlag(self, flag):
0351         self.flags = self.flags | flag
0352         if flag == CO_VARARGS:
0353             self.argcount = self.argcount - 1
0354 
0355     def checkFlag(self, flag):
0356         if self.flags & flag:
0357             return 1
0358 
0359     def setFreeVars(self, names):
0360         self.freevars = list(names)
0361 
0362     def setCellVars(self, names):
0363         self.cellvars = names
0364 
0365     def getCode(self):
0366         """Get a Python code object"""
0367         if self.stage == RAW:
0368             self.computeStackDepth()
0369             self.flattenGraph()
0370         if self.stage == FLAT:
0371             self.convertArgs()
0372         if self.stage == CONV:
0373             self.makeByteCode()
0374         if self.stage == DONE:
0375             return self.newCodeObject()
0376         raise RuntimeError, "inconsistent PyFlowGraph state"
0377 
0378     def dump(self, io=None):
0379         if io:
0380             save = sys.stdout
0381             sys.stdout = io
0382         pc = 0
0383         for t in self.insts:
0384             opname = t[0]
0385             if opname == "SET_LINENO":
0386                 print
0387             if len(t) == 1:
0388                 print "\t", "%3d" % pc, opname
0389                 pc = pc + 1
0390             else:
0391                 print "\t", "%3d" % pc, opname, t[1]
0392                 pc = pc + 3
0393         if io:
0394             sys.stdout = save
0395 
0396     def computeStackDepth(self):
0397         """Compute the max stack depth.
0398 
0399         Approach is to compute the stack effect of each basic block.
0400         Then find the path through the code with the largest total
0401         effect.
0402         """
0403         depth = {}
0404         exit = None
0405         for b in self.getBlocks():
0406             depth[b] = findDepth(b.getInstructions())
0407 
0408         seen = {}
0409 
0410         def max_depth(b, d):
0411             if seen.has_key(b):
0412                 return d
0413             seen[b] = 1
0414             d = d + depth[b]
0415             children = b.get_children()
0416             if children:
0417                 return max([max_depth(c, d) for c in children])
0418             else:
0419                 if not b.label == "exit":
0420                     return max_depth(self.exit, d)
0421                 else:
0422                     return d
0423 
0424         self.stacksize = max_depth(self.entry, 0)
0425 
0426     def flattenGraph(self):
0427         """Arrange the blocks in order and resolve jumps"""
0428         assert self.stage == RAW
0429         self.insts = insts = []
0430         pc = 0
0431         begin = {}
0432         end = {}
0433         for b in self.getBlocksInOrder():
0434             begin[b] = pc
0435             for inst in b.getInstructions():
0436                 insts.append(inst)
0437                 if len(inst) == 1:
0438                     pc = pc + 1
0439                 elif inst[0] != "SET_LINENO":
0440                     # arg takes 2 bytes
0441                     pc = pc + 3
0442             end[b] = pc
0443         pc = 0
0444         for i in range(len(insts)):
0445             inst = insts[i]
0446             if len(inst) == 1:
0447                 pc = pc + 1
0448             elif inst[0] != "SET_LINENO":
0449                 pc = pc + 3
0450             opname = inst[0]
0451             if self.hasjrel.has_elt(opname):
0452                 oparg = inst[1]
0453                 offset = begin[oparg] - pc
0454                 insts[i] = opname, offset
0455             elif self.hasjabs.has_elt(opname):
0456                 insts[i] = opname, begin[inst[1]]
0457         self.stage = FLAT
0458 
0459     hasjrel = misc.Set()
0460     for i in dis.hasjrel:
0461         hasjrel.add(dis.opname[i])
0462     hasjabs = misc.Set()
0463     for i in dis.hasjabs:
0464         hasjabs.add(dis.opname[i])
0465 
0466     def convertArgs(self):
0467         """Convert arguments from symbolic to concrete form"""
0468         assert self.stage == FLAT
0469         self.consts.insert(0, self.docstring)
0470         self.sort_cellvars()
0471         for i in range(len(self.insts)):
0472             t = self.insts[i]
0473             if len(t) == 2:
0474                 opname, oparg = t
0475                 conv = self._converters.get(opname, None)
0476                 if conv:
0477                     self.insts[i] = opname, conv(self, oparg)
0478         self.stage = CONV
0479 
0480     def sort_cellvars(self):
0481         """Sort cellvars in the order of varnames and prune from freevars.
0482         """
0483         cells = {}
0484         for name in self.cellvars:
0485             cells[name] = 1
0486         self.cellvars = [name for name in self.varnames
0487                          if cells.has_key(name)]
0488         for name in self.cellvars:
0489             del cells[name]
0490         self.cellvars = self.cellvars + cells.keys()
0491         self.closure = self.cellvars + self.freevars
0492 
0493     def _lookupName(self, name, list):
0494         """Return index of name in list, appending if necessary
0495 
0496         This routine uses a list instead of a dictionary, because a
0497         dictionary can't store two different keys if the keys have the
0498         same value but different types, e.g. 2 and 2L.  The compiler
0499         must treat these two separately, so it does an explicit type
0500         comparison before comparing the values.
0501         """
0502         t = type(name)
0503         for i in range(len(list)):
0504             if t == type(list[i]) and list[i] == name:
0505                 return i
0506         end = len(list)
0507         list.append(name)
0508         return end
0509 
0510     _converters = {}
0511     def _convert_LOAD_CONST(self, arg):
0512         if hasattr(arg, 'getCode'):
0513             arg = arg.getCode()
0514         return self._lookupName(arg, self.consts)
0515 
0516     def _convert_LOAD_FAST(self, arg):
0517         self._lookupName(arg, self.names)
0518         return self._lookupName(arg, self.varnames)
0519     _convert_STORE_FAST = _convert_LOAD_FAST
0520     _convert_DELETE_FAST = _convert_LOAD_FAST
0521 
0522     def _convert_LOAD_NAME(self, arg):
0523         if self.klass is None:
0524             self._lookupName(arg, self.varnames)
0525         return self._lookupName(arg, self.names)
0526 
0527     def _convert_NAME(self, arg):
0528         if self.klass is None:
0529             self._lookupName(arg, self.varnames)
0530         return self._lookupName(arg, self.names)
0531     _convert_STORE_NAME = _convert_NAME
0532     _convert_DELETE_NAME = _convert_NAME
0533     _convert_IMPORT_NAME = _convert_NAME
0534     _convert_IMPORT_FROM = _convert_NAME
0535     _convert_STORE_ATTR = _convert_NAME
0536     _convert_LOAD_ATTR = _convert_NAME
0537     _convert_DELETE_ATTR = _convert_NAME
0538     _convert_LOAD_GLOBAL = _convert_NAME
0539     _convert_STORE_GLOBAL = _convert_NAME
0540     _convert_DELETE_GLOBAL = _convert_NAME
0541 
0542     def _convert_DEREF(self, arg):
0543         self._lookupName(arg, self.names)
0544         self._lookupName(arg, self.varnames)
0545         return self._lookupName(arg, self.closure)
0546     _convert_LOAD_DEREF = _convert_DEREF
0547     _convert_STORE_DEREF = _convert_DEREF
0548 
0549     def _convert_LOAD_CLOSURE(self, arg):
0550         self._lookupName(arg, self.varnames)
0551         return self._lookupName(arg, self.closure)
0552 
0553     _cmp = list(dis.cmp_op)
0554     def _convert_COMPARE_OP(self, arg):
0555         return self._cmp.index(arg)
0556 
0557     # similarly for other opcodes...
0558 
0559     for name, obj in locals().items():
0560         if name[:9] == "_convert_":
0561             opname = name[9:]
0562             _converters[opname] = obj
0563     del name, obj, opname
0564 
0565     def makeByteCode(self):
0566         assert self.stage == CONV
0567         self.lnotab = lnotab = LineAddrTable()
0568         for t in self.insts:
0569             opname = t[0]
0570             if len(t) == 1:
0571                 lnotab.addCode(self.opnum[opname])
0572             else:
0573                 oparg = t[1]
0574                 if opname == "SET_LINENO":
0575                     lnotab.nextLine(oparg)
0576                     continue
0577                 hi, lo = twobyte(oparg)
0578                 try:
0579                     lnotab.addCode(self.opnum[opname], lo, hi)
0580                 except ValueError:
0581                     print opname, oparg
0582                     print self.opnum[opname], lo, hi
0583                     raise
0584         self.stage = DONE
0585 
0586     opnum = {}
0587     for num in range(len(dis.opname)):
0588         opnum[dis.opname[num]] = num
0589     del num
0590 
0591     def newCodeObject(self):
0592         assert self.stage == DONE
0593         if (self.flags & CO_NEWLOCALS) == 0:
0594             nlocals = 0
0595         else:
0596             nlocals = len(self.varnames)
0597         argcount = self.argcount
0598         if self.flags & CO_VARKEYWORDS:
0599             argcount = argcount - 1
0600         return new.code(argcount, nlocals, self.stacksize, self.flags,
0601                         self.lnotab.getCode(), self.getConsts(),
0602                         tuple(self.names), tuple(self.varnames),
0603                         self.filename, self.name, self.lnotab.firstline,
0604                         self.lnotab.getTable(), tuple(self.freevars),
0605                         tuple(self.cellvars))
0606 
0607     def getConsts(self):
0608         """Return a tuple for the const slot of the code object
0609 
0610         Must convert references to code (MAKE_FUNCTION) to code
0611         objects recursively.
0612         """
0613         l = []
0614         for elt in self.consts:
0615             if isinstance(elt, PyFlowGraph):
0616                 elt = elt.getCode()
0617             l.append(elt)
0618         return tuple(l)
0619 
0620 def isJump(opname):
0621     if opname[:4] == 'JUMP':
0622         return 1
0623 
0624 class TupleArg:
0625     """Helper for marking func defs with nested tuples in arglist"""
0626     def __init__(self, count, names):
0627         self.count = count
0628         self.names = names
0629     def __repr__(self):
0630         return "TupleArg(%s, %s)" % (self.count, self.names)
0631     def getName(self):
0632         return ".%d" % self.count
0633 
0634 def getArgCount(args):
0635     argcount = len(args)
0636     if args:
0637         for arg in args:
0638             if isinstance(arg, TupleArg):
0639                 numNames = len(misc.flatten(arg.names))
0640                 argcount = argcount - numNames
0641     return argcount
0642 
0643 def twobyte(val):
0644     """Convert an int argument into high and low bytes"""
0645     assert type(val) == types.IntType
0646     return divmod(val, 256)
0647 
0648 class LineAddrTable:
0649     """lnotab
0650 
0651     This class builds the lnotab, which is documented in compile.c.
0652     Here's a brief recap:
0653 
0654     For each SET_LINENO instruction after the first one, two bytes are
0655     added to lnotab.  (In some cases, multiple two-byte entries are
0656     added.)  The first byte is the distance in bytes between the
0657     instruction for the last SET_LINENO and the current SET_LINENO.
0658     The second byte is offset in line numbers.  If either offset is
0659     greater than 255, multiple two-byte entries are added -- see
0660     compile.c for the delicate details.
0661     """
0662 
0663     def __init__(self):
0664         self.code = []
0665         self.codeOffset = 0
0666         self.firstline = 0
0667         self.lastline = 0
0668         self.lastoff = 0
0669         self.lnotab = []
0670 
0671     def addCode(self, *args):
0672         for arg in args:
0673             self.code.append(chr(arg))
0674         self.codeOffset = self.codeOffset + len(args)
0675 
0676     def nextLine(self, lineno):
0677         if self.firstline == 0:
0678             self.firstline = lineno
0679             self.lastline = lineno
0680         else:
0681             # compute deltas
0682             addr = self.codeOffset - self.lastoff
0683             line = lineno - self.lastline
0684             # Python assumes that lineno always increases with
0685             # increasing bytecode address (lnotab is unsigned char).
0686             # Depending on when SET_LINENO instructions are emitted
0687             # this is not always true.  Consider the code:
0688             #     a = (1,
0689             #          b)
0690             # In the bytecode stream, the assignment to "a" occurs
0691             # after the loading of "b".  This works with the C Python
0692             # compiler because it only generates a SET_LINENO instruction
0693             # for the assignment.
0694             if line >= 0:
0695                 push = self.lnotab.append
0696                 while addr > 255:
0697                     push(255); push(0)
0698                     addr -= 255
0699                 while line > 255:
0700                     push(addr); push(255)
0701                     line -= 255
0702                     addr = 0
0703                 if addr > 0 or line > 0:
0704                     push(addr); push(line)
0705                 self.lastline = lineno
0706                 self.lastoff = self.codeOffset
0707 
0708     def getCode(self):
0709         return ''.join(self.code)
0710 
0711     def getTable(self):
0712         return ''.join(map(chr, self.lnotab))
0713 
0714 class StackDepthTracker:
0715     # XXX 1. need to keep track of stack depth on jumps
0716     # XXX 2. at least partly as a result, this code is broken
0717 
0718     def findDepth(self, insts, debug=0):
0719         depth = 0
0720         maxDepth = 0
0721         for i in insts:
0722             opname = i[0]
0723             if debug:
0724                 print i,
0725             delta = self.effect.get(opname, None)
0726             if delta is not None:
0727                 depth = depth + delta
0728             else:
0729                 # now check patterns
0730                 for pat, pat_delta in self.patterns:
0731                     if opname[:len(pat)] == pat:
0732                         delta = pat_delta
0733                         depth = depth + delta
0734                         break
0735                 # if we still haven't found a match
0736                 if delta is None:
0737                     meth = getattr(self, opname, None)
0738                     if meth is not None:
0739                         depth = depth + meth(i[1])
0740             if depth > maxDepth:
0741                 maxDepth = depth
0742             if debug:
0743                 print depth, maxDepth
0744         return maxDepth
0745 
0746     effect = {
0747         'POP_TOP': -1,
0748         'DUP_TOP': 1,
0749         'SLICE+1': -1,
0750         'SLICE+2': -1,
0751         'SLICE+3': -2,
0752         'STORE_SLICE+0': -1,
0753         'STORE_SLICE+1': -2,
0754         'STORE_SLICE+2': -2,
0755         'STORE_SLICE+3': -3,
0756         'DELETE_SLICE+0': -1,
0757         'DELETE_SLICE+1': -2,
0758         'DELETE_SLICE+2': -2,
0759         'DELETE_SLICE+3': -3,
0760         'STORE_SUBSCR': -3,
0761         'DELETE_SUBSCR': -2,
0762         # PRINT_EXPR?
0763         'PRINT_ITEM': -1,
0764         'RETURN_VALUE': -1,
0765         'YIELD_VALUE': -1,
0766         'EXEC_STMT': -3,
0767         'BUILD_CLASS': -2,
0768         'STORE_NAME': -1,
0769         'STORE_ATTR': -2,
0770         'DELETE_ATTR': -1,
0771         'STORE_GLOBAL': -1,
0772         'BUILD_MAP': 1,
0773         'COMPARE_OP': -1,
0774         'STORE_FAST': -1,
0775         'IMPORT_STAR': -1,
0776         'IMPORT_NAME': 0,
0777         'IMPORT_FROM': 1,
0778         'LOAD_ATTR': 0, # unlike other loads
0779         # close enough...
0780         'SETUP_EXCEPT': 3,
0781         'SETUP_FINALLY': 3,
0782         'FOR_ITER': 1,
0783         }
0784     # use pattern match
0785     patterns = [
0786         ('BINARY_', -1),
0787         ('LOAD_', 1),
0788         ]
0789 
0790     def UNPACK_SEQUENCE(self, count):
0791         return count-1
0792     def BUILD_TUPLE(self, count):
0793         return -count+1
0794     def BUILD_LIST(self, count):
0795         return -count+1
0796     def CALL_FUNCTION(self, argc):
0797         hi, lo = divmod(argc, 256)
0798         return -(lo + hi * 2)
0799     def CALL_FUNCTION_VAR(self, argc):
0800         return self.CALL_FUNCTION(argc)-1
0801     def CALL_FUNCTION_KW(self, argc):
0802         return self.CALL_FUNCTION(argc)-1
0803     def CALL_FUNCTION_VAR_KW(self, argc):
0804         return self.CALL_FUNCTION(argc)-2
0805     def MAKE_FUNCTION(self, argc):
0806         return -argc
0807     def MAKE_CLOSURE(self, argc):
0808         # XXX need to account for free variables too!
0809         return -argc
0810     def BUILD_SLICE(self, argc):
0811         if argc == 2:
0812             return -1
0813         elif argc == 3:
0814             return -2
0815     def DUP_TOPX(self, argc):
0816         return argc
0817 
0818 findDepth = StackDepthTracker().findDepth
0819 

Generated by PyXR 0.9.4
SourceForge.net Logo