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