| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | """A flow graph representation for Python bytecode""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | import dis | 
					
						
							|  |  |  | import new | 
					
						
							|  |  |  | import string | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | import types | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from compiler import misc | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  | def xxx_sort(l): | 
					
						
							|  |  |  |     l = l[:] | 
					
						
							|  |  |  |     def sorter(a, b): | 
					
						
							|  |  |  |         return cmp(a.bid, b.bid) | 
					
						
							|  |  |  |     l.sort(sorter) | 
					
						
							|  |  |  |     return l | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | class FlowGraph: | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.current = self.entry = Block() | 
					
						
							|  |  |  |         self.exit = Block("exit") | 
					
						
							|  |  |  |         self.blocks = misc.Set() | 
					
						
							|  |  |  |         self.blocks.add(self.entry) | 
					
						
							|  |  |  |         self.blocks.add(self.exit) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def startBlock(self, block): | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |         if self._debug: | 
					
						
							|  |  |  |             if self.current: | 
					
						
							|  |  |  |                 print "end", repr(self.current) | 
					
						
							| 
									
										
										
										
											2001-04-12 20:21:39 +00:00
										 |  |  |                 print "    next", self.current.next | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |                 print "   ", self.current.get_children() | 
					
						
							|  |  |  |             print repr(block) | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.current = block | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-04-12 20:21:39 +00:00
										 |  |  |     def nextBlock(self, block=None): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         # XXX think we need to specify when there is implicit transfer | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |         # from one block to the next.  might be better to represent this | 
					
						
							|  |  |  |         # with explicit JUMP_ABSOLUTE instructions that are optimized | 
					
						
							|  |  |  |         # out when they are unnecessary. | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         # | 
					
						
							|  |  |  |         # I think this strategy works: each block has a child | 
					
						
							|  |  |  |         # designated as "next" which is returned as the last of the | 
					
						
							|  |  |  |         # children.  because the nodes in a graph are emitted in | 
					
						
							|  |  |  |         # reverse post order, the "next" block will always be emitted | 
					
						
							|  |  |  |         # immediately after its parent. | 
					
						
							|  |  |  |         # Worry: maintaining this invariant could be tricky | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |         if block is None: | 
					
						
							|  |  |  |             block = self.newBlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Note: If the current block ends with an unconditional | 
					
						
							|  |  |  |         # control transfer, then it is incorrect to add an implicit | 
					
						
							|  |  |  |         # transfer to the block graph.  The current code requires | 
					
						
							|  |  |  |         # these edges to get the blocks emitted in the right order, | 
					
						
							|  |  |  |         # however. :-(  If a client needs to remove these edges, call | 
					
						
							|  |  |  |         # pruneEdges(). | 
					
						
							|  |  |  |          | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.current.addNext(block) | 
					
						
							|  |  |  |         self.startBlock(block) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def newBlock(self): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         b = Block() | 
					
						
							|  |  |  |         self.blocks.add(b) | 
					
						
							|  |  |  |         return b | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def startExitBlock(self): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.startBlock(self.exit) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |     _debug = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _enable_debug(self): | 
					
						
							|  |  |  |         self._debug = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _disable_debug(self): | 
					
						
							|  |  |  |         self._debug = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     def emit(self, *inst): | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |         if self._debug: | 
					
						
							|  |  |  |             print "\t", inst | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         if inst[0] == 'RETURN_VALUE': | 
					
						
							|  |  |  |             self.current.addOutEdge(self.exit) | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |         if len(inst) == 2 and isinstance(inst[1], Block): | 
					
						
							|  |  |  |             self.current.addOutEdge(inst[1]) | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.current.emit(inst) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |     def getBlocksInOrder(self): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         """Return the blocks in reverse postorder
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         i.e. each node appears before all of its successors | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # XXX make sure every node that doesn't have an explicit next | 
					
						
							|  |  |  |         # is set so that next points to exit | 
					
						
							|  |  |  |         for b in self.blocks.elements(): | 
					
						
							|  |  |  |             if b is self.exit: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             if not b.next: | 
					
						
							|  |  |  |                 b.addNext(self.exit) | 
					
						
							|  |  |  |         order = dfs_postorder(self.entry, {}) | 
					
						
							|  |  |  |         order.reverse() | 
					
						
							| 
									
										
										
										
											2001-04-12 20:21:39 +00:00
										 |  |  |         self.fixupOrder(order, self.exit) | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         # hack alert | 
					
						
							|  |  |  |         if not self.exit in order: | 
					
						
							|  |  |  |             order.append(self.exit) | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         return order | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-04-12 20:21:39 +00:00
										 |  |  |     def fixupOrder(self, blocks, default_next): | 
					
						
							|  |  |  |         """Fixup bad order introduced by DFS.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # XXX This is a total mess.  There must be a better way to get | 
					
						
							|  |  |  |         # the code blocks in the right order. | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         self.fixupOrderHonorNext(blocks, default_next) | 
					
						
							|  |  |  |         self.fixupOrderForward(blocks, default_next) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def fixupOrderHonorNext(self, blocks, default_next): | 
					
						
							|  |  |  |         """Fix one problem with DFS.
 | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         The DFS uses child block, but doesn't know about the special | 
					
						
							|  |  |  |         "next" block.  As a result, the DFS can order blocks so that a | 
					
						
							|  |  |  |         block isn't next to the right block for implicit control | 
					
						
							|  |  |  |         transfers. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         index = {} | 
					
						
							|  |  |  |         for i in range(len(blocks)): | 
					
						
							|  |  |  |             index[blocks[i]] = i | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for i in range(0, len(blocks) - 1): | 
					
						
							|  |  |  |             b = blocks[i] | 
					
						
							|  |  |  |             n = blocks[i + 1] | 
					
						
							|  |  |  |             if not b.next or b.next[0] == default_next or b.next[0] == n: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             # The blocks are in the wrong order.  Find the chain of | 
					
						
							|  |  |  |             # blocks to insert where they belong. | 
					
						
							|  |  |  |             cur = b | 
					
						
							|  |  |  |             chain = [] | 
					
						
							|  |  |  |             elt = cur | 
					
						
							|  |  |  |             while elt.next and elt.next[0] != default_next: | 
					
						
							|  |  |  |                 chain.append(elt.next[0]) | 
					
						
							|  |  |  |                 elt = elt.next[0] | 
					
						
							|  |  |  |             # Now remove the blocks in the chain from the current | 
					
						
							|  |  |  |             # block list, so that they can be re-inserted. | 
					
						
							|  |  |  |             l = [] | 
					
						
							|  |  |  |             for b in chain: | 
					
						
							|  |  |  |                 assert index[b] > i | 
					
						
							|  |  |  |                 l.append((index[b], b)) | 
					
						
							|  |  |  |             l.sort() | 
					
						
							|  |  |  |             l.reverse() | 
					
						
							|  |  |  |             for j, b in l: | 
					
						
							|  |  |  |                 del blocks[index[b]] | 
					
						
							|  |  |  |             # Insert the chain in the proper location | 
					
						
							|  |  |  |             blocks[i:i + 1] = [cur] + chain | 
					
						
							|  |  |  |             # Finally, re-compute the block indexes | 
					
						
							|  |  |  |             for i in range(len(blocks)): | 
					
						
							|  |  |  |                 index[blocks[i]] = i | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def fixupOrderForward(self, blocks, default_next): | 
					
						
							|  |  |  |         """Make sure all JUMP_FORWARDs jump forward""" | 
					
						
							|  |  |  |         index = {} | 
					
						
							|  |  |  |         chains = [] | 
					
						
							|  |  |  |         cur = [] | 
					
						
							|  |  |  |         for b in blocks: | 
					
						
							|  |  |  |             index[b] = len(chains) | 
					
						
							|  |  |  |             cur.append(b) | 
					
						
							|  |  |  |             if b.next and b.next[0] == default_next: | 
					
						
							|  |  |  |                 chains.append(cur) | 
					
						
							|  |  |  |                 cur = [] | 
					
						
							|  |  |  |         chains.append(cur) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             constraints = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for i in range(len(chains)): | 
					
						
							|  |  |  |                 l = chains[i] | 
					
						
							|  |  |  |                 for b in l: | 
					
						
							|  |  |  |                     for c in b.get_children(): | 
					
						
							|  |  |  |                         if index[c] < i: | 
					
						
							|  |  |  |                             forward_p = 0 | 
					
						
							|  |  |  |                             for inst in b.insts: | 
					
						
							|  |  |  |                                 if inst[0] == 'JUMP_FORWARD': | 
					
						
							|  |  |  |                                     if inst[1] == c: | 
					
						
							|  |  |  |                                         forward_p = 1 | 
					
						
							|  |  |  |                             if not forward_p: | 
					
						
							|  |  |  |                                 continue | 
					
						
							|  |  |  |                             constraints.append((index[c], i)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if not constraints: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # XXX just do one for now | 
					
						
							|  |  |  |             # do swaps to get things in the right order | 
					
						
							|  |  |  |             goes_before, a_chain = constraints[0] | 
					
						
							|  |  |  |             assert a_chain > goes_before | 
					
						
							|  |  |  |             c = chains[a_chain] | 
					
						
							|  |  |  |             chains.remove(c) | 
					
						
							|  |  |  |             chains.insert(goes_before, c) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         del blocks[:] | 
					
						
							|  |  |  |         for c in chains: | 
					
						
							|  |  |  |             for b in c: | 
					
						
							|  |  |  |                 blocks.append(b) | 
					
						
							|  |  |  |              | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |     def getBlocks(self): | 
					
						
							|  |  |  |         return self.blocks.elements() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getRoot(self): | 
					
						
							|  |  |  |         """Return nodes appropriate for use with dominator""" | 
					
						
							|  |  |  |         return self.entry | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     def getContainedGraphs(self): | 
					
						
							|  |  |  |         l = [] | 
					
						
							|  |  |  |         for b in self.getBlocks(): | 
					
						
							|  |  |  |             l.extend(b.getContainedGraphs()) | 
					
						
							|  |  |  |         return l | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | def dfs_postorder(b, seen): | 
					
						
							|  |  |  |     """Depth-first search of tree rooted at b, return in postorder""" | 
					
						
							|  |  |  |     order = [] | 
					
						
							|  |  |  |     seen[b] = b | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |     for c in b.get_children(): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         if seen.has_key(c): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         order = order + dfs_postorder(c, seen) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     order.append(b) | 
					
						
							|  |  |  |     return order | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Block: | 
					
						
							|  |  |  |     _count = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, label=''): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.insts = [] | 
					
						
							|  |  |  |         self.inEdges = misc.Set() | 
					
						
							|  |  |  |         self.outEdges = misc.Set() | 
					
						
							|  |  |  |         self.label = label | 
					
						
							|  |  |  |         self.bid = Block._count | 
					
						
							|  |  |  |         self.next = [] | 
					
						
							|  |  |  |         Block._count = Block._count + 1 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         if self.label: | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |             return "<block %s id=%d>" % (self.label, self.bid) | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |             return "<block id=%d>" % (self.bid) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __str__(self): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         insts = map(str, self.insts) | 
					
						
							|  |  |  |         return "<block %s %d:\n%s>" % (self.label, self.bid, | 
					
						
							|  |  |  |                                        string.join(insts, '\n'))  | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def emit(self, inst): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         op = inst[0] | 
					
						
							|  |  |  |         if op[:4] == 'JUMP': | 
					
						
							|  |  |  |             self.outEdges.add(inst[1]) | 
					
						
							|  |  |  |         self.insts.append(inst) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getInstructions(self): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         return self.insts | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def addInEdge(self, block): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.inEdges.add(block) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def addOutEdge(self, block): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.outEdges.add(block) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def addNext(self, block): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.next.append(block) | 
					
						
							|  |  |  |         assert len(self.next) == 1, map(str, self.next) | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-11-06 03:47:39 +00:00
										 |  |  |     _uncond_transfer = ('RETURN_VALUE', 'RAISE_VARARGS', | 
					
						
							|  |  |  |                         'JUMP_ABSOLUTE', 'JUMP_FORWARD') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def pruneNext(self): | 
					
						
							|  |  |  |         """Remove bogus edge for unconditional transfers
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Each block has a next edge that accounts for implicit control | 
					
						
							|  |  |  |         transfers, e.g. from a JUMP_IF_FALSE to the block that will be | 
					
						
							|  |  |  |         executed if the test is true. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         These edges must remain for the current assembler code to | 
					
						
							|  |  |  |         work. If they are removed, the dfs_postorder gets things in | 
					
						
							|  |  |  |         weird orders.  However, they shouldn't be there for other | 
					
						
							|  |  |  |         purposes, e.g. conversion to SSA form.  This method will | 
					
						
							|  |  |  |         remove the next edge when it follows an unconditional control | 
					
						
							|  |  |  |         transfer. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             op, arg = self.insts[-1] | 
					
						
							|  |  |  |         except (IndexError, ValueError): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if op in self._uncond_transfer: | 
					
						
							|  |  |  |             self.next = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |     def get_children(self): | 
					
						
							|  |  |  |         if self.next and self.next[0] in self.outEdges: | 
					
						
							|  |  |  |             self.outEdges.remove(self.next[0]) | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         return self.outEdges.elements() + self.next | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |     def getContainedGraphs(self): | 
					
						
							|  |  |  |         """Return all graphs contained within this block.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         For example, a MAKE_FUNCTION block will contain a reference to | 
					
						
							|  |  |  |         the graph for the function body. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         contained = [] | 
					
						
							|  |  |  |         for inst in self.insts: | 
					
						
							|  |  |  |             if len(inst) == 1: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             op = inst[1] | 
					
						
							|  |  |  |             if hasattr(op, 'graph'): | 
					
						
							|  |  |  |                 contained.append(op.graph) | 
					
						
							|  |  |  |         return contained | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | # flags for code objects | 
					
						
							|  |  |  | CO_OPTIMIZED = 0x0001 | 
					
						
							|  |  |  | CO_NEWLOCALS = 0x0002 | 
					
						
							|  |  |  | CO_VARARGS = 0x0004 | 
					
						
							|  |  |  | CO_VARKEYWORDS = 0x0008 | 
					
						
							| 
									
										
										
										
											2001-04-12 06:40:42 +00:00
										 |  |  | CO_NESTED = 0x0010 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | # the FlowGraph is transformed in place; it exists in one of these states | 
					
						
							|  |  |  | RAW = "RAW" | 
					
						
							|  |  |  | FLAT = "FLAT" | 
					
						
							|  |  |  | CONV = "CONV" | 
					
						
							|  |  |  | DONE = "DONE" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PyFlowGraph(FlowGraph): | 
					
						
							|  |  |  |     super_init = FlowGraph.__init__ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-08-29 18:09:50 +00:00
										 |  |  |     def __init__(self, name, filename, args=(), optimized=0, klass=None): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.super_init() | 
					
						
							|  |  |  |         self.name = name | 
					
						
							|  |  |  |         self.filename = filename | 
					
						
							|  |  |  |         self.docstring = None | 
					
						
							|  |  |  |         self.args = args # XXX | 
					
						
							|  |  |  |         self.argcount = getArgCount(args) | 
					
						
							| 
									
										
										
										
											2001-08-29 18:09:50 +00:00
										 |  |  |         self.klass = klass | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         if optimized: | 
					
						
							|  |  |  |             self.flags = CO_OPTIMIZED | CO_NEWLOCALS  | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.flags = 0 | 
					
						
							|  |  |  |         self.consts = [] | 
					
						
							|  |  |  |         self.names = [] | 
					
						
							| 
									
										
										
										
											2001-04-12 06:40:42 +00:00
										 |  |  |         # Free variables found by the symbol table scan, including | 
					
						
							|  |  |  |         # variables used only in nested scopes, are included here. | 
					
						
							|  |  |  |         self.freevars = [] | 
					
						
							|  |  |  |         self.cellvars = [] | 
					
						
							|  |  |  |         # The closure list is used to track the order of cell | 
					
						
							|  |  |  |         # variables and free variables in the resulting code object. | 
					
						
							|  |  |  |         # The offsets used by LOAD_CLOSURE/LOAD_DEREF refer to both | 
					
						
							|  |  |  |         # kinds of variables. | 
					
						
							|  |  |  |         self.closure = [] | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |         self.varnames = list(args) or [] | 
					
						
							| 
									
										
										
										
											2000-02-17 22:09:35 +00:00
										 |  |  |         for i in range(len(self.varnames)): | 
					
						
							|  |  |  |             var = self.varnames[i] | 
					
						
							|  |  |  |             if isinstance(var, TupleArg): | 
					
						
							|  |  |  |                 self.varnames[i] = var.getName() | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |         self.stage = RAW | 
					
						
							| 
									
										
										
										
											2000-02-16 00:50:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     def setDocstring(self, doc): | 
					
						
							|  |  |  |         self.docstring = doc | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     def setFlag(self, flag): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.flags = self.flags | flag | 
					
						
							|  |  |  |         if flag == CO_VARARGS: | 
					
						
							|  |  |  |             self.argcount = self.argcount - 1 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-04-12 06:40:42 +00:00
										 |  |  |     def setFreeVars(self, names): | 
					
						
							|  |  |  |         self.freevars = list(names) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setCellVars(self, names): | 
					
						
							|  |  |  |         self.cellvars = names | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     def getCode(self): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         """Get a Python code object""" | 
					
						
							|  |  |  |         if self.stage == RAW: | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |             self.flattenGraph() | 
					
						
							|  |  |  |         if self.stage == FLAT: | 
					
						
							|  |  |  |             self.convertArgs() | 
					
						
							|  |  |  |         if self.stage == CONV: | 
					
						
							|  |  |  |             self.makeByteCode() | 
					
						
							|  |  |  |         if self.stage == DONE: | 
					
						
							|  |  |  |             return self.newCodeObject() | 
					
						
							|  |  |  |         raise RuntimeError, "inconsistent PyFlowGraph state" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def dump(self, io=None): | 
					
						
							|  |  |  |         if io: | 
					
						
							|  |  |  |             save = sys.stdout | 
					
						
							|  |  |  |             sys.stdout = io | 
					
						
							|  |  |  |         pc = 0 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |         for t in self.insts: | 
					
						
							|  |  |  |             opname = t[0] | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |             if opname == "SET_LINENO": | 
					
						
							|  |  |  |                 print | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |             if len(t) == 1: | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |                 print "\t", "%3d" % pc, opname | 
					
						
							|  |  |  |                 pc = pc + 1 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 print "\t", "%3d" % pc, opname, t[1] | 
					
						
							|  |  |  |                 pc = pc + 3 | 
					
						
							|  |  |  |         if io: | 
					
						
							|  |  |  |             sys.stdout = save | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def flattenGraph(self): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         """Arrange the blocks in order and resolve jumps""" | 
					
						
							|  |  |  |         assert self.stage == RAW | 
					
						
							|  |  |  |         self.insts = insts = [] | 
					
						
							|  |  |  |         pc = 0 | 
					
						
							|  |  |  |         begin = {} | 
					
						
							|  |  |  |         end = {} | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |         for b in self.getBlocksInOrder(): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |             begin[b] = pc | 
					
						
							|  |  |  |             for inst in b.getInstructions(): | 
					
						
							|  |  |  |                 insts.append(inst) | 
					
						
							|  |  |  |                 if len(inst) == 1: | 
					
						
							|  |  |  |                     pc = pc + 1 | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     # arg takes 2 bytes | 
					
						
							|  |  |  |                     pc = pc + 3 | 
					
						
							|  |  |  |             end[b] = pc | 
					
						
							|  |  |  |         pc = 0 | 
					
						
							|  |  |  |         for i in range(len(insts)): | 
					
						
							|  |  |  |             inst = insts[i] | 
					
						
							|  |  |  |             if len(inst) == 1: | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |                 pc = pc + 1 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |                 pc = pc + 3 | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |             opname = inst[0] | 
					
						
							|  |  |  |             if self.hasjrel.has_elt(opname): | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |                 oparg = inst[1] | 
					
						
							|  |  |  |                 offset = begin[oparg] - pc | 
					
						
							|  |  |  |                 insts[i] = opname, offset | 
					
						
							|  |  |  |             elif self.hasjabs.has_elt(opname): | 
					
						
							|  |  |  |                 insts[i] = opname, begin[inst[1]] | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self.stacksize = findDepth(self.insts) | 
					
						
							|  |  |  |         self.stage = FLAT | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     hasjrel = misc.Set() | 
					
						
							|  |  |  |     for i in dis.hasjrel: | 
					
						
							|  |  |  |         hasjrel.add(dis.opname[i]) | 
					
						
							|  |  |  |     hasjabs = misc.Set() | 
					
						
							|  |  |  |     for i in dis.hasjabs: | 
					
						
							|  |  |  |         hasjabs.add(dis.opname[i]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def convertArgs(self): | 
					
						
							|  |  |  |         """Convert arguments from symbolic to concrete form""" | 
					
						
							|  |  |  |         assert self.stage == FLAT | 
					
						
							| 
									
										
										
										
											2001-04-11 16:21:51 +00:00
										 |  |  |         self.consts.insert(0, self.docstring) | 
					
						
							| 
									
										
										
										
											2001-04-12 06:40:42 +00:00
										 |  |  |         self.sort_cellvars() | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |         for i in range(len(self.insts)): | 
					
						
							|  |  |  |             t = self.insts[i] | 
					
						
							|  |  |  |             if len(t) == 2: | 
					
						
							| 
									
										
										
										
											2001-08-29 18:09:50 +00:00
										 |  |  |                 opname, oparg = t | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |                 conv = self._converters.get(opname, None) | 
					
						
							|  |  |  |                 if conv: | 
					
						
							|  |  |  |                     self.insts[i] = opname, conv(self, oparg) | 
					
						
							|  |  |  |         self.stage = CONV | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-04-12 06:40:42 +00:00
										 |  |  |     def sort_cellvars(self): | 
					
						
							|  |  |  |         """Sort cellvars in the order of varnames and prune from freevars.
 | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         cells = {} | 
					
						
							|  |  |  |         for name in self.cellvars: | 
					
						
							|  |  |  |             cells[name] = 1 | 
					
						
							|  |  |  |         self.cellvars = [name for name in self.varnames | 
					
						
							|  |  |  |                          if cells.has_key(name)] | 
					
						
							|  |  |  |         for name in self.cellvars: | 
					
						
							|  |  |  |             del cells[name] | 
					
						
							|  |  |  |         self.cellvars = self.cellvars + cells.keys() | 
					
						
							|  |  |  |         self.closure = self.cellvars + self.freevars | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-08-29 19:45:33 +00:00
										 |  |  |     def _lookupName(self, name, list): | 
					
						
							|  |  |  |         """Return index of name in list, appending if necessary
 | 
					
						
							| 
									
										
										
										
											2001-08-29 18:09:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-08-29 19:45:33 +00:00
										 |  |  |         This routine uses a list instead of a dictionary, because a | 
					
						
							|  |  |  |         dictionary can't store two different keys if the keys have the | 
					
						
							|  |  |  |         same value but different types, e.g. 2 and 2L.  The compiler | 
					
						
							|  |  |  |         must treat these two separately, so it does an explicit type | 
					
						
							|  |  |  |         comparison before comparing the values. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2000-10-13 21:58:13 +00:00
										 |  |  |         t = type(name) | 
					
						
							|  |  |  |         for i in range(len(list)): | 
					
						
							| 
									
										
										
										
											2001-08-29 19:45:33 +00:00
										 |  |  |             if t == type(list[i]) and list[i] == name: | 
					
						
							| 
									
										
										
										
											2000-02-17 22:58:54 +00:00
										 |  |  |                 return i | 
					
						
							|  |  |  |         end = len(list) | 
					
						
							|  |  |  |         list.append(name) | 
					
						
							|  |  |  |         return end | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     _converters = {} | 
					
						
							|  |  |  |     def _convert_LOAD_CONST(self, arg): | 
					
						
							| 
									
										
										
										
											2000-11-06 03:43:11 +00:00
										 |  |  |         if hasattr(arg, 'getCode'): | 
					
						
							|  |  |  |             arg = arg.getCode() | 
					
						
							| 
									
										
										
										
											2001-08-29 19:45:33 +00:00
										 |  |  |         return self._lookupName(arg, self.consts) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _convert_LOAD_FAST(self, arg): | 
					
						
							| 
									
										
										
										
											2001-08-29 19:45:33 +00:00
										 |  |  |         self._lookupName(arg, self.names) | 
					
						
							|  |  |  |         return self._lookupName(arg, self.varnames) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     _convert_STORE_FAST = _convert_LOAD_FAST | 
					
						
							|  |  |  |     _convert_DELETE_FAST = _convert_LOAD_FAST | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-08-28 16:36:12 +00:00
										 |  |  |     def _convert_LOAD_NAME(self, arg): | 
					
						
							| 
									
										
										
										
											2001-08-29 19:45:33 +00:00
										 |  |  |         if self.klass is None: | 
					
						
							|  |  |  |             self._lookupName(arg, self.varnames) | 
					
						
							|  |  |  |         return self._lookupName(arg, self.names) | 
					
						
							| 
									
										
										
										
											2001-08-28 16:36:12 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     def _convert_NAME(self, arg): | 
					
						
							| 
									
										
										
										
											2001-08-29 19:45:33 +00:00
										 |  |  |         self._lookupName(arg, self.varnames) | 
					
						
							|  |  |  |         return self._lookupName(arg, self.names) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     _convert_STORE_NAME = _convert_NAME | 
					
						
							|  |  |  |     _convert_DELETE_NAME = _convert_NAME | 
					
						
							|  |  |  |     _convert_IMPORT_NAME = _convert_NAME | 
					
						
							|  |  |  |     _convert_IMPORT_FROM = _convert_NAME | 
					
						
							|  |  |  |     _convert_STORE_ATTR = _convert_NAME | 
					
						
							|  |  |  |     _convert_LOAD_ATTR = _convert_NAME | 
					
						
							|  |  |  |     _convert_DELETE_ATTR = _convert_NAME | 
					
						
							|  |  |  |     _convert_LOAD_GLOBAL = _convert_NAME | 
					
						
							|  |  |  |     _convert_STORE_GLOBAL = _convert_NAME | 
					
						
							|  |  |  |     _convert_DELETE_GLOBAL = _convert_NAME | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-04-12 06:40:42 +00:00
										 |  |  |     def _convert_DEREF(self, arg): | 
					
						
							| 
									
										
										
										
											2001-08-29 19:45:33 +00:00
										 |  |  |         self._lookupName(arg, self.names) | 
					
						
							|  |  |  |         self._lookupName(arg, self.varnames) | 
					
						
							|  |  |  |         return self._lookupName(arg, self.closure) | 
					
						
							| 
									
										
										
										
											2001-04-12 06:40:42 +00:00
										 |  |  |     _convert_LOAD_DEREF = _convert_DEREF | 
					
						
							|  |  |  |     _convert_STORE_DEREF = _convert_DEREF | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _convert_LOAD_CLOSURE(self, arg): | 
					
						
							| 
									
										
										
										
											2001-08-29 19:45:33 +00:00
										 |  |  |         self._lookupName(arg, self.varnames) | 
					
						
							|  |  |  |         return self._lookupName(arg, self.closure) | 
					
						
							| 
									
										
										
										
											2001-04-12 06:40:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     _cmp = list(dis.cmp_op) | 
					
						
							|  |  |  |     def _convert_COMPARE_OP(self, arg): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         return self._cmp.index(arg) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # similarly for other opcodes... | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for name, obj in locals().items(): | 
					
						
							|  |  |  |         if name[:9] == "_convert_": | 
					
						
							|  |  |  |             opname = name[9:] | 
					
						
							|  |  |  |             _converters[opname] = obj             | 
					
						
							|  |  |  |     del name, obj, opname | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def makeByteCode(self): | 
					
						
							|  |  |  |         assert self.stage == CONV | 
					
						
							|  |  |  |         self.lnotab = lnotab = LineAddrTable() | 
					
						
							|  |  |  |         for t in self.insts: | 
					
						
							|  |  |  |             opname = t[0] | 
					
						
							|  |  |  |             if len(t) == 1: | 
					
						
							|  |  |  |                 lnotab.addCode(self.opnum[opname]) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 oparg = t[1] | 
					
						
							|  |  |  |                 if opname == "SET_LINENO": | 
					
						
							|  |  |  |                     lnotab.nextLine(oparg) | 
					
						
							|  |  |  |                 hi, lo = twobyte(oparg) | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |                 try: | 
					
						
							|  |  |  |                     lnotab.addCode(self.opnum[opname], lo, hi) | 
					
						
							|  |  |  |                 except ValueError: | 
					
						
							|  |  |  |                     print opname, oparg | 
					
						
							|  |  |  |                     print self.opnum[opname], lo, hi | 
					
						
							|  |  |  |                     raise | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |         self.stage = DONE | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |     opnum = {} | 
					
						
							|  |  |  |     for num in range(len(dis.opname)): | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |         opnum[dis.opname[num]] = num | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     del num | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     def newCodeObject(self): | 
					
						
							|  |  |  |         assert self.stage == DONE | 
					
						
							|  |  |  |         if self.flags == 0: | 
					
						
							|  |  |  |             nlocals = 0 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             nlocals = len(self.varnames) | 
					
						
							|  |  |  |         argcount = self.argcount | 
					
						
							|  |  |  |         if self.flags & CO_VARKEYWORDS: | 
					
						
							|  |  |  |             argcount = argcount - 1 | 
					
						
							|  |  |  |         return new.code(argcount, nlocals, self.stacksize, self.flags, | 
					
						
							|  |  |  |                         self.lnotab.getCode(), self.getConsts(), | 
					
						
							|  |  |  |                         tuple(self.names), tuple(self.varnames), | 
					
						
							| 
									
										
										
										
											2000-05-02 22:32:59 +00:00
										 |  |  |                         self.filename, self.name, self.lnotab.firstline, | 
					
						
							| 
									
										
										
										
											2001-04-12 06:40:42 +00:00
										 |  |  |                         self.lnotab.getTable(), tuple(self.freevars), | 
					
						
							|  |  |  |                         tuple(self.cellvars)) | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getConsts(self): | 
					
						
							|  |  |  |         """Return a tuple for the const slot of the code object
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Must convert references to code (MAKE_FUNCTION) to code | 
					
						
							|  |  |  |         objects recursively. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         l = [] | 
					
						
							|  |  |  |         for elt in self.consts: | 
					
						
							|  |  |  |             if isinstance(elt, PyFlowGraph): | 
					
						
							|  |  |  |                 elt = elt.getCode() | 
					
						
							|  |  |  |             l.append(elt) | 
					
						
							|  |  |  |         return tuple(l) | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |              | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | def isJump(opname): | 
					
						
							|  |  |  |     if opname[:4] == 'JUMP': | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         return 1 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | class TupleArg: | 
					
						
							|  |  |  |     """Helper for marking func defs with nested tuples in arglist""" | 
					
						
							|  |  |  |     def __init__(self, count, names): | 
					
						
							|  |  |  |         self.count = count | 
					
						
							|  |  |  |         self.names = names | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         return "TupleArg(%s, %s)" % (self.count, self.names) | 
					
						
							|  |  |  |     def getName(self): | 
					
						
							| 
									
										
										
										
											2001-04-12 17:33:34 +00:00
										 |  |  |         return ".%d" % self.count | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | def getArgCount(args): | 
					
						
							|  |  |  |     argcount = len(args) | 
					
						
							|  |  |  |     if args: | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         for arg in args: | 
					
						
							|  |  |  |             if isinstance(arg, TupleArg): | 
					
						
							|  |  |  |                 numNames = len(misc.flatten(arg.names)) | 
					
						
							|  |  |  |                 argcount = argcount - numNames | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     return argcount | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  | def twobyte(val): | 
					
						
							|  |  |  |     """Convert an int argument into high and low bytes""" | 
					
						
							|  |  |  |     assert type(val) == types.IntType | 
					
						
							|  |  |  |     return divmod(val, 256) | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class LineAddrTable: | 
					
						
							|  |  |  |     """lnotab
 | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2001-06-09 09:26:21 +00:00
										 |  |  |     This class builds the lnotab, which is documented in compile.c. | 
					
						
							|  |  |  |     Here's a brief recap: | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     For each SET_LINENO instruction after the first one, two bytes are | 
					
						
							|  |  |  |     added to lnotab.  (In some cases, multiple two-byte entries are | 
					
						
							|  |  |  |     added.)  The first byte is the distance in bytes between the | 
					
						
							|  |  |  |     instruction for the last SET_LINENO and the current SET_LINENO. | 
					
						
							|  |  |  |     The second byte is offset in line numbers.  If either offset is | 
					
						
							| 
									
										
										
										
											2001-06-09 09:26:21 +00:00
										 |  |  |     greater than 255, multiple two-byte entries are added -- see | 
					
						
							|  |  |  |     compile.c for the delicate details. | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self.code = [] | 
					
						
							|  |  |  |         self.codeOffset = 0 | 
					
						
							|  |  |  |         self.firstline = 0 | 
					
						
							|  |  |  |         self.lastline = 0 | 
					
						
							|  |  |  |         self.lastoff = 0 | 
					
						
							|  |  |  |         self.lnotab = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-06 18:53:14 +00:00
										 |  |  |     def addCode(self, *args): | 
					
						
							|  |  |  |         for arg in args: | 
					
						
							|  |  |  |             self.code.append(chr(arg)) | 
					
						
							|  |  |  |         self.codeOffset = self.codeOffset + len(args) | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def nextLine(self, lineno): | 
					
						
							|  |  |  |         if self.firstline == 0: | 
					
						
							|  |  |  |             self.firstline = lineno | 
					
						
							|  |  |  |             self.lastline = lineno | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # compute deltas | 
					
						
							|  |  |  |             addr = self.codeOffset - self.lastoff | 
					
						
							|  |  |  |             line = lineno - self.lastline | 
					
						
							| 
									
										
										
										
											2000-09-01 20:47:37 +00:00
										 |  |  |             # Python assumes that lineno always increases with | 
					
						
							|  |  |  |             # increasing bytecode address (lnotab is unsigned char). | 
					
						
							|  |  |  |             # Depending on when SET_LINENO instructions are emitted | 
					
						
							|  |  |  |             # this is not always true.  Consider the code: | 
					
						
							|  |  |  |             #     a = (1, | 
					
						
							|  |  |  |             #          b) | 
					
						
							|  |  |  |             # In the bytecode stream, the assignment to "a" occurs | 
					
						
							|  |  |  |             # after the loading of "b".  This works with the C Python | 
					
						
							|  |  |  |             # compiler because it only generates a SET_LINENO instruction | 
					
						
							|  |  |  |             # for the assignment. | 
					
						
							|  |  |  |             if line > 0: | 
					
						
							| 
									
										
										
										
											2001-06-09 09:26:21 +00:00
										 |  |  |                 push = self.lnotab.append | 
					
						
							|  |  |  |                 while addr > 255: | 
					
						
							|  |  |  |                     push(255); push(0) | 
					
						
							|  |  |  |                     addr -= 255 | 
					
						
							|  |  |  |                 while line > 255: | 
					
						
							|  |  |  |                     push(addr); push(255) | 
					
						
							|  |  |  |                     line -= 255 | 
					
						
							|  |  |  |                     addr = 0 | 
					
						
							|  |  |  |                 if addr > 0 or line > 0: | 
					
						
							|  |  |  |                     push(addr); push(line) | 
					
						
							| 
									
										
										
										
											2000-09-01 20:47:37 +00:00
										 |  |  |                 self.lastline = lineno | 
					
						
							|  |  |  |                 self.lastoff = self.codeOffset | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getCode(self): | 
					
						
							|  |  |  |         return string.join(self.code, '') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getTable(self): | 
					
						
							|  |  |  |         return string.join(map(chr, self.lnotab), '') | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | class StackDepthTracker: | 
					
						
							| 
									
										
										
										
											2000-03-16 20:06:59 +00:00
										 |  |  |     # XXX 1. need to keep track of stack depth on jumps | 
					
						
							|  |  |  |     # XXX 2. at least partly as a result, this code is broken | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def findDepth(self, insts): | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |         depth = 0 | 
					
						
							|  |  |  |         maxDepth = 0 | 
					
						
							|  |  |  |         for i in insts: | 
					
						
							|  |  |  |             opname = i[0] | 
					
						
							|  |  |  |             delta = self.effect.get(opname, 0) | 
					
						
							|  |  |  |             if delta > 1: | 
					
						
							|  |  |  |                 depth = depth + delta | 
					
						
							|  |  |  |             elif delta < 0: | 
					
						
							|  |  |  |                 if depth > maxDepth: | 
					
						
							|  |  |  |                     maxDepth = depth | 
					
						
							|  |  |  |                 depth = depth + delta | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 if depth > maxDepth: | 
					
						
							|  |  |  |                     maxDepth = depth | 
					
						
							|  |  |  |                 # now check patterns | 
					
						
							| 
									
										
										
										
											2000-05-02 22:32:59 +00:00
										 |  |  |                 for pat, pat_delta in self.patterns: | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |                     if opname[:len(pat)] == pat: | 
					
						
							| 
									
										
										
										
											2000-05-02 22:32:59 +00:00
										 |  |  |                         delta = pat_delta | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |                         depth = depth + delta | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                 # if we still haven't found a match | 
					
						
							|  |  |  |                 if delta == 0: | 
					
						
							| 
									
										
										
										
											2000-05-02 22:32:59 +00:00
										 |  |  |                     meth = getattr(self, opname, None) | 
					
						
							|  |  |  |                     if meth is not None: | 
					
						
							|  |  |  |                         depth = depth + meth(i[1]) | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |             if depth < 0: | 
					
						
							|  |  |  |                 depth = 0 | 
					
						
							|  |  |  |         return maxDepth | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     effect = { | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |         'POP_TOP': -1, | 
					
						
							|  |  |  |         'DUP_TOP': 1, | 
					
						
							|  |  |  |         'SLICE+1': -1, | 
					
						
							|  |  |  |         'SLICE+2': -1, | 
					
						
							|  |  |  |         'SLICE+3': -2, | 
					
						
							|  |  |  |         'STORE_SLICE+0': -1, | 
					
						
							|  |  |  |         'STORE_SLICE+1': -2, | 
					
						
							|  |  |  |         'STORE_SLICE+2': -2, | 
					
						
							|  |  |  |         'STORE_SLICE+3': -3, | 
					
						
							|  |  |  |         'DELETE_SLICE+0': -1, | 
					
						
							|  |  |  |         'DELETE_SLICE+1': -2, | 
					
						
							|  |  |  |         'DELETE_SLICE+2': -2, | 
					
						
							|  |  |  |         'DELETE_SLICE+3': -3, | 
					
						
							|  |  |  |         'STORE_SUBSCR': -3, | 
					
						
							|  |  |  |         'DELETE_SUBSCR': -2, | 
					
						
							|  |  |  |         # PRINT_EXPR? | 
					
						
							|  |  |  |         'PRINT_ITEM': -1, | 
					
						
							|  |  |  |         'LOAD_LOCALS': 1, | 
					
						
							|  |  |  |         'RETURN_VALUE': -1, | 
					
						
							|  |  |  |         'EXEC_STMT': -2, | 
					
						
							|  |  |  |         'BUILD_CLASS': -2, | 
					
						
							|  |  |  |         'STORE_NAME': -1, | 
					
						
							|  |  |  |         'STORE_ATTR': -2, | 
					
						
							|  |  |  |         'DELETE_ATTR': -1, | 
					
						
							|  |  |  |         'STORE_GLOBAL': -1, | 
					
						
							|  |  |  |         'BUILD_MAP': 1, | 
					
						
							|  |  |  |         'COMPARE_OP': -1, | 
					
						
							|  |  |  |         'STORE_FAST': -1, | 
					
						
							| 
									
										
										
										
											2000-10-12 20:23:23 +00:00
										 |  |  |         'IMPORT_STAR': -1, | 
					
						
							|  |  |  |         'IMPORT_NAME': 0, | 
					
						
							|  |  |  |         'IMPORT_FROM': 1, | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |     # use pattern match | 
					
						
							|  |  |  |     patterns = [ | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |         ('BINARY_', -1), | 
					
						
							|  |  |  |         ('LOAD_', 1), | 
					
						
							|  |  |  |         ] | 
					
						
							| 
									
										
										
										
											2000-03-06 18:53:14 +00:00
										 |  |  |      | 
					
						
							|  |  |  |     # special cases: | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |     # UNPACK_SEQUENCE, BUILD_TUPLE, | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |     # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |     def UNPACK_SEQUENCE(self, count): | 
					
						
							| 
									
										
										
										
											2001-08-29 20:55:17 +00:00
										 |  |  |         return count-1 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |     def BUILD_TUPLE(self, count): | 
					
						
							| 
									
										
										
										
											2001-08-29 20:55:17 +00:00
										 |  |  |         return -count+1 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |     def BUILD_LIST(self, count): | 
					
						
							| 
									
										
										
										
											2001-08-29 20:55:17 +00:00
										 |  |  |         return -count+1 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |     def CALL_FUNCTION(self, argc): | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |         hi, lo = divmod(argc, 256) | 
					
						
							|  |  |  |         return lo + hi * 2 | 
					
						
							| 
									
										
										
										
											2000-05-02 22:32:59 +00:00
										 |  |  |     def CALL_FUNCTION_VAR(self, argc): | 
					
						
							|  |  |  |         return self.CALL_FUNCTION(argc)+1 | 
					
						
							|  |  |  |     def CALL_FUNCTION_KW(self, argc): | 
					
						
							|  |  |  |         return self.CALL_FUNCTION(argc)+1 | 
					
						
							|  |  |  |     def CALL_FUNCTION_VAR_KW(self, argc): | 
					
						
							|  |  |  |         return self.CALL_FUNCTION(argc)+2 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |     def MAKE_FUNCTION(self, argc): | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |         return -argc | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |     def BUILD_SLICE(self, argc): | 
					
						
							| 
									
										
										
										
											2000-02-21 22:46:00 +00:00
										 |  |  |         if argc == 2: | 
					
						
							|  |  |  |             return -1 | 
					
						
							|  |  |  |         elif argc == 3: | 
					
						
							|  |  |  |             return -2 | 
					
						
							| 
									
										
										
										
											2000-02-14 14:14:29 +00:00
										 |  |  |      | 
					
						
							|  |  |  | findDepth = StackDepthTracker().findDepth |