Commit 46cc7c0f authored by Thomas Wouters's avatar Thomas Wouters

Bring Tools/compiler almost up to date. Specifically:

- fix tab space issues (SF patch #101167 by Neil Schemenauer)
- fix co_flags for classes to include CO_NEWLOCALS (SF patch #101145 by Neil)
- fix for merger of UNPACK_LIST and UNPACK_TUPLE into UNPACK_SEQUENCE,
  (SF patch #101168 by, well, Neil :)
- Adjust bytecode MAGIC to current bytecode.

TODO: teach compile.py about list comprehensions.
parent 81f7eb6c
...@@ -9,71 +9,71 @@ from compiler import misc ...@@ -9,71 +9,71 @@ from compiler import misc
class FlowGraph: class FlowGraph:
def __init__(self): def __init__(self):
self.current = self.entry = Block() self.current = self.entry = Block()
self.exit = Block("exit") self.exit = Block("exit")
self.blocks = misc.Set() self.blocks = misc.Set()
self.blocks.add(self.entry) self.blocks.add(self.entry)
self.blocks.add(self.exit) self.blocks.add(self.exit)
def startBlock(self, block): def startBlock(self, block):
self.current = block self.current = block
def nextBlock(self, block=None): def nextBlock(self, block=None):
if block is None: if block is None:
block = self.newBlock() block = self.newBlock()
# XXX think we need to specify when there is implicit transfer # XXX think we need to specify when there is implicit transfer
# from one block to the next # from one block to the next
# #
# I think this strategy works: each block has a child # I think this strategy works: each block has a child
# designated as "next" which is returned as the last of the # designated as "next" which is returned as the last of the
# children. because the nodes in a graph are emitted in # children. because the nodes in a graph are emitted in
# reverse post order, the "next" block will always be emitted # reverse post order, the "next" block will always be emitted
# immediately after its parent. # immediately after its parent.
# Worry: maintaining this invariant could be tricky # Worry: maintaining this invariant could be tricky
self.current.addNext(block) self.current.addNext(block)
self.startBlock(block) self.startBlock(block)
def newBlock(self): def newBlock(self):
b = Block() b = Block()
self.blocks.add(b) self.blocks.add(b)
return b return b
def startExitBlock(self): def startExitBlock(self):
self.startBlock(self.exit) self.startBlock(self.exit)
def emit(self, *inst): def emit(self, *inst):
# XXX should jump instructions implicitly call nextBlock? # XXX should jump instructions implicitly call nextBlock?
if inst[0] == 'RETURN_VALUE': if inst[0] == 'RETURN_VALUE':
self.current.addOutEdge(self.exit) self.current.addOutEdge(self.exit)
self.current.emit(inst) self.current.emit(inst)
def getBlocks(self): def getBlocks(self):
"""Return the blocks in reverse postorder """Return the blocks in reverse postorder
i.e. each node appears before all of its successors i.e. each node appears before all of its successors
""" """
# XXX make sure every node that doesn't have an explicit next # XXX make sure every node that doesn't have an explicit next
# is set so that next points to exit # is set so that next points to exit
for b in self.blocks.elements(): for b in self.blocks.elements():
if b is self.exit: if b is self.exit:
continue continue
if not b.next: if not b.next:
b.addNext(self.exit) b.addNext(self.exit)
order = dfs_postorder(self.entry, {}) order = dfs_postorder(self.entry, {})
order.reverse() order.reverse()
# hack alert # hack alert
if not self.exit in order: if not self.exit in order:
order.append(self.exit) order.append(self.exit)
return order return order
def dfs_postorder(b, seen): def dfs_postorder(b, seen):
"""Depth-first search of tree rooted at b, return in postorder""" """Depth-first search of tree rooted at b, return in postorder"""
order = [] order = []
seen[b] = b seen[b] = b
for c in b.children(): for c in b.children():
if seen.has_key(c): if seen.has_key(c):
continue continue
order = order + dfs_postorder(c, seen) order = order + dfs_postorder(c, seen)
order.append(b) order.append(b)
return order return order
...@@ -81,47 +81,47 @@ class Block: ...@@ -81,47 +81,47 @@ class Block:
_count = 0 _count = 0
def __init__(self, label=''): def __init__(self, label=''):
self.insts = [] self.insts = []
self.inEdges = misc.Set() self.inEdges = misc.Set()
self.outEdges = misc.Set() self.outEdges = misc.Set()
self.label = label self.label = label
self.bid = Block._count self.bid = Block._count
self.next = [] self.next = []
Block._count = Block._count + 1 Block._count = Block._count + 1
def __repr__(self): def __repr__(self):
if self.label: if self.label:
return "<block %s id=%d len=%d>" % (self.label, self.bid, return "<block %s id=%d len=%d>" % (self.label, self.bid,
len(self.insts)) len(self.insts))
else: else:
return "<block id=%d len=%d>" % (self.bid, len(self.insts)) return "<block id=%d len=%d>" % (self.bid, len(self.insts))
def __str__(self): def __str__(self):
insts = map(str, self.insts) insts = map(str, self.insts)
return "<block %s %d:\n%s>" % (self.label, self.bid, return "<block %s %d:\n%s>" % (self.label, self.bid,
string.join(insts, '\n')) string.join(insts, '\n'))
def emit(self, inst): def emit(self, inst):
op = inst[0] op = inst[0]
if op[:4] == 'JUMP': if op[:4] == 'JUMP':
self.outEdges.add(inst[1]) self.outEdges.add(inst[1])
self.insts.append(inst) self.insts.append(inst)
def getInstructions(self): def getInstructions(self):
return self.insts return self.insts
def addInEdge(self, block): def addInEdge(self, block):
self.inEdges.add(block) self.inEdges.add(block)
def addOutEdge(self, block): def addOutEdge(self, block):
self.outEdges.add(block) self.outEdges.add(block)
def addNext(self, block): def addNext(self, block):
self.next.append(block) self.next.append(block)
assert len(self.next) == 1, map(str, self.next) assert len(self.next) == 1, map(str, self.next)
def children(self): def children(self):
return self.outEdges.elements() + self.next return self.outEdges.elements() + self.next
# flags for code objects # flags for code objects
CO_OPTIMIZED = 0x0001 CO_OPTIMIZED = 0x0001
...@@ -139,18 +139,18 @@ class PyFlowGraph(FlowGraph): ...@@ -139,18 +139,18 @@ class PyFlowGraph(FlowGraph):
super_init = FlowGraph.__init__ super_init = FlowGraph.__init__
def __init__(self, name, filename, args=(), optimized=0): def __init__(self, name, filename, args=(), optimized=0):
self.super_init() self.super_init()
self.name = name self.name = name
self.filename = filename self.filename = filename
self.docstring = None self.docstring = None
self.args = args # XXX self.args = args # XXX
self.argcount = getArgCount(args) self.argcount = getArgCount(args)
if optimized: if optimized:
self.flags = CO_OPTIMIZED | CO_NEWLOCALS self.flags = CO_OPTIMIZED | CO_NEWLOCALS
else: else:
self.flags = 0 self.flags = 0
self.consts = [] self.consts = []
self.names = [] self.names = []
self.varnames = list(args) or [] self.varnames = list(args) or []
for i in range(len(self.varnames)): for i in range(len(self.varnames)):
var = self.varnames[i] var = self.varnames[i]
...@@ -163,13 +163,13 @@ class PyFlowGraph(FlowGraph): ...@@ -163,13 +163,13 @@ class PyFlowGraph(FlowGraph):
self.consts.insert(0, doc) self.consts.insert(0, doc)
def setFlag(self, flag): def setFlag(self, flag):
self.flags = self.flags | flag self.flags = self.flags | flag
if flag == CO_VARARGS: if flag == CO_VARARGS:
self.argcount = self.argcount - 1 self.argcount = self.argcount - 1
def getCode(self): def getCode(self):
"""Get a Python code object""" """Get a Python code object"""
if self.stage == RAW: if self.stage == RAW:
self.flattenGraph() self.flattenGraph()
if self.stage == FLAT: if self.stage == FLAT:
self.convertArgs() self.convertArgs()
...@@ -198,38 +198,38 @@ class PyFlowGraph(FlowGraph): ...@@ -198,38 +198,38 @@ class PyFlowGraph(FlowGraph):
sys.stdout = save sys.stdout = save
def flattenGraph(self): def flattenGraph(self):
"""Arrange the blocks in order and resolve jumps""" """Arrange the blocks in order and resolve jumps"""
assert self.stage == RAW assert self.stage == RAW
self.insts = insts = [] self.insts = insts = []
pc = 0 pc = 0
begin = {} begin = {}
end = {} end = {}
for b in self.getBlocks(): for b in self.getBlocks():
begin[b] = pc begin[b] = pc
for inst in b.getInstructions(): for inst in b.getInstructions():
insts.append(inst) insts.append(inst)
if len(inst) == 1: if len(inst) == 1:
pc = pc + 1 pc = pc + 1
else: else:
# arg takes 2 bytes # arg takes 2 bytes
pc = pc + 3 pc = pc + 3
end[b] = pc end[b] = pc
pc = 0 pc = 0
for i in range(len(insts)): for i in range(len(insts)):
inst = insts[i] inst = insts[i]
if len(inst) == 1: if len(inst) == 1:
pc = pc + 1 pc = pc + 1
else: else:
pc = pc + 3 pc = pc + 3
opname = inst[0] opname = inst[0]
if self.hasjrel.has_elt(opname): if self.hasjrel.has_elt(opname):
oparg = inst[1] oparg = inst[1]
offset = begin[oparg] - pc offset = begin[oparg] - pc
insts[i] = opname, offset insts[i] = opname, offset
elif self.hasjabs.has_elt(opname): elif self.hasjabs.has_elt(opname):
insts[i] = opname, begin[inst[1]] insts[i] = opname, begin[inst[1]]
self.stacksize = findDepth(self.insts) self.stacksize = findDepth(self.insts)
self.stage = FLAT self.stage = FLAT
hasjrel = misc.Set() hasjrel = misc.Set()
for i in dis.hasjrel: for i in dis.hasjrel:
...@@ -292,7 +292,7 @@ class PyFlowGraph(FlowGraph): ...@@ -292,7 +292,7 @@ class PyFlowGraph(FlowGraph):
_cmp = list(dis.cmp_op) _cmp = list(dis.cmp_op)
def _convert_COMPARE_OP(self, arg): def _convert_COMPARE_OP(self, arg):
return self._cmp.index(arg) return self._cmp.index(arg)
# similarly for other opcodes... # similarly for other opcodes...
...@@ -314,12 +314,12 @@ class PyFlowGraph(FlowGraph): ...@@ -314,12 +314,12 @@ class PyFlowGraph(FlowGraph):
if opname == "SET_LINENO": if opname == "SET_LINENO":
lnotab.nextLine(oparg) lnotab.nextLine(oparg)
hi, lo = twobyte(oparg) hi, lo = twobyte(oparg)
try: try:
lnotab.addCode(self.opnum[opname], lo, hi) lnotab.addCode(self.opnum[opname], lo, hi)
except ValueError: except ValueError:
print opname, oparg print opname, oparg
print self.opnum[opname], lo, hi print self.opnum[opname], lo, hi
raise raise
self.stage = DONE self.stage = DONE
opnum = {} opnum = {}
...@@ -354,10 +354,10 @@ class PyFlowGraph(FlowGraph): ...@@ -354,10 +354,10 @@ class PyFlowGraph(FlowGraph):
elt = elt.getCode() elt = elt.getCode()
l.append(elt) l.append(elt)
return tuple(l) return tuple(l)
def isJump(opname): def isJump(opname):
if opname[:4] == 'JUMP': if opname[:4] == 'JUMP':
return 1 return 1
class TupleArg: class TupleArg:
"""Helper for marking func defs with nested tuples in arglist""" """Helper for marking func defs with nested tuples in arglist"""
...@@ -372,10 +372,10 @@ class TupleArg: ...@@ -372,10 +372,10 @@ class TupleArg:
def getArgCount(args): def getArgCount(args):
argcount = len(args) argcount = len(args)
if args: if args:
for arg in args: for arg in args:
if isinstance(arg, TupleArg): if isinstance(arg, TupleArg):
numNames = len(misc.flatten(arg.names)) numNames = len(misc.flatten(arg.names))
argcount = argcount - numNames argcount = argcount - numNames
return argcount return argcount
def twobyte(val): def twobyte(val):
...@@ -513,11 +513,9 @@ class StackDepthTracker: ...@@ -513,11 +513,9 @@ class StackDepthTracker:
] ]
# special cases: # special cases:
# UNPACK_TUPLE, UNPACK_LIST, BUILD_TUPLE, # UNPACK_SEQUENCE, BUILD_TUPLE,
# BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
def UNPACK_TUPLE(self, count): def UNPACK_SEQUENCE(self, count):
return count
def UNPACK_LIST(self, count):
return count return count
def BUILD_TUPLE(self, count): def BUILD_TUPLE(self, count):
return -count return -count
......
...@@ -7,7 +7,7 @@ from cStringIO import StringIO ...@@ -7,7 +7,7 @@ from cStringIO import StringIO
from compiler import ast, parse, walk from compiler import ast, parse, walk
from compiler import pyassem, misc from compiler import pyassem, misc
from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, TupleArg from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS, TupleArg
callfunc_opcode_info = { callfunc_opcode_info = {
# (Have *args, Have **args) : opcode # (Have *args, Have **args) : opcode
...@@ -29,32 +29,32 @@ def compile(filename): ...@@ -29,32 +29,32 @@ def compile(filename):
class Module: class Module:
def __init__(self, source, filename): def __init__(self, source, filename):
self.filename = filename self.filename = filename
self.source = source self.source = source
self.code = None self.code = None
def compile(self): def compile(self):
ast = parse(self.source) ast = parse(self.source)
root, filename = os.path.split(self.filename) root, filename = os.path.split(self.filename)
gen = ModuleCodeGenerator(filename) gen = ModuleCodeGenerator(filename)
walk(ast, gen, 1) walk(ast, gen, 1)
self.code = gen.getCode() self.code = gen.getCode()
def dump(self, f): def dump(self, f):
f.write(self.getPycHeader()) f.write(self.getPycHeader())
marshal.dump(self.code, f) marshal.dump(self.code, f)
MAGIC = (20121 | (ord('\r')<<16) | (ord('\n')<<24)) MAGIC = (50811 | (ord('\r')<<16) | (ord('\n')<<24))
def getPycHeader(self): def getPycHeader(self):
# compile.c uses marshal to write a long directly, with # compile.c uses marshal to write a long directly, with
# calling the interface that would also generate a 1-byte code # calling the interface that would also generate a 1-byte code
# to indicate the type of the value. simplest way to get the # to indicate the type of the value. simplest way to get the
# same effect is to call marshal and then skip the code. # same effect is to call marshal and then skip the code.
magic = marshal.dumps(self.MAGIC)[1:] magic = marshal.dumps(self.MAGIC)[1:]
mtime = os.stat(self.filename)[stat.ST_MTIME] mtime = os.stat(self.filename)[stat.ST_MTIME]
mtime = struct.pack('i', mtime) mtime = struct.pack('i', mtime)
return magic + mtime return magic + mtime
class CodeGenerator: class CodeGenerator:
...@@ -63,24 +63,24 @@ class CodeGenerator: ...@@ -63,24 +63,24 @@ class CodeGenerator:
def __init__(self, filename): def __init__(self, filename):
## Subclasses must define a constructor that intializes self.graph ## Subclasses must define a constructor that intializes self.graph
## before calling this init function ## before calling this init function
## self.graph = pyassem.PyFlowGraph() ## self.graph = pyassem.PyFlowGraph()
self.filename = filename self.filename = filename
self.locals = misc.Stack() self.locals = misc.Stack()
self.loops = misc.Stack() self.loops = misc.Stack()
self.curStack = 0 self.curStack = 0
self.maxStack = 0 self.maxStack = 0
self._setupGraphDelegation() self._setupGraphDelegation()
def _setupGraphDelegation(self): def _setupGraphDelegation(self):
self.emit = self.graph.emit self.emit = self.graph.emit
self.newBlock = self.graph.newBlock self.newBlock = self.graph.newBlock
self.startBlock = self.graph.startBlock self.startBlock = self.graph.startBlock
self.nextBlock = self.graph.nextBlock self.nextBlock = self.graph.nextBlock
self.setDocstring = self.graph.setDocstring self.setDocstring = self.graph.setDocstring
def getCode(self): def getCode(self):
"""Return a code object""" """Return a code object"""
return self.graph.getCode() return self.graph.getCode()
# Next five methods handle name access # Next five methods handle name access
...@@ -97,11 +97,11 @@ class CodeGenerator: ...@@ -97,11 +97,11 @@ class CodeGenerator:
self._nameOp('DELETE', name) self._nameOp('DELETE', name)
def _nameOp(self, prefix, name): def _nameOp(self, prefix, name):
if not self.optimized: if not self.optimized:
self.emit(prefix + '_NAME', name) self.emit(prefix + '_NAME', name)
return return
if self.isLocalName(name): if self.isLocalName(name):
self.emit(prefix + '_FAST', name) self.emit(prefix + '_FAST', name)
else: else:
self.emit(prefix + '_GLOBAL', name) self.emit(prefix + '_GLOBAL', name)
...@@ -125,25 +125,25 @@ class CodeGenerator: ...@@ -125,25 +125,25 @@ class CodeGenerator:
# code objects # code objects
def visitModule(self, node): def visitModule(self, node):
lnf = walk(node.node, LocalNameFinder(), 0) lnf = walk(node.node, LocalNameFinder(), 0)
self.locals.push(lnf.getLocals()) self.locals.push(lnf.getLocals())
self.setDocstring(node.doc) self.setDocstring(node.doc)
self.visit(node.node) self.visit(node.node)
self.emit('LOAD_CONST', None) self.emit('LOAD_CONST', None)
self.emit('RETURN_VALUE') self.emit('RETURN_VALUE')
def visitFunction(self, node): def visitFunction(self, node):
self._visitFuncOrLambda(node, isLambda=0) self._visitFuncOrLambda(node, isLambda=0)
self.storeName(node.name) self.storeName(node.name)
def visitLambda(self, node): def visitLambda(self, node):
self._visitFuncOrLambda(node, isLambda=1) self._visitFuncOrLambda(node, isLambda=1)
## self.storeName("<lambda>") ## self.storeName("<lambda>")
def _visitFuncOrLambda(self, node, isLambda): def _visitFuncOrLambda(self, node, isLambda):
gen = FunctionCodeGenerator(node, self.filename, isLambda) gen = FunctionCodeGenerator(node, self.filename, isLambda)
walk(node.code, gen) walk(node.code, gen)
gen.finish() gen.finish()
self.set_lineno(node) self.set_lineno(node)
for default in node.defaults: for default in node.defaults:
self.visit(default) self.visit(default)
...@@ -170,158 +170,158 @@ class CodeGenerator: ...@@ -170,158 +170,158 @@ class CodeGenerator:
# The next few implement control-flow statements # The next few implement control-flow statements
def visitIf(self, node): def visitIf(self, node):
end = self.newBlock() end = self.newBlock()
numtests = len(node.tests) numtests = len(node.tests)
for i in range(numtests): for i in range(numtests):
test, suite = node.tests[i] test, suite = node.tests[i]
self.set_lineno(test) self.set_lineno(test)
self.visit(test) self.visit(test)
## if i == numtests - 1 and not node.else_: ## if i == numtests - 1 and not node.else_:
## nextTest = end ## nextTest = end
## else: ## else:
## nextTest = self.newBlock() ## nextTest = self.newBlock()
nextTest = self.newBlock() nextTest = self.newBlock()
self.emit('JUMP_IF_FALSE', nextTest) self.emit('JUMP_IF_FALSE', nextTest)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
self.visit(suite) self.visit(suite)
self.emit('JUMP_FORWARD', end) self.emit('JUMP_FORWARD', end)
self.nextBlock(nextTest) self.nextBlock(nextTest)
self.emit('POP_TOP') self.emit('POP_TOP')
if node.else_: if node.else_:
self.visit(node.else_) self.visit(node.else_)
self.nextBlock(end) self.nextBlock(end)
def visitWhile(self, node): def visitWhile(self, node):
self.set_lineno(node) self.set_lineno(node)
loop = self.newBlock() loop = self.newBlock()
else_ = self.newBlock() else_ = self.newBlock()
after = self.newBlock() after = self.newBlock()
self.emit('SETUP_LOOP', after) self.emit('SETUP_LOOP', after)
self.nextBlock(loop) self.nextBlock(loop)
self.loops.push(loop) self.loops.push(loop)
self.set_lineno(node) self.set_lineno(node)
self.visit(node.test) self.visit(node.test)
self.emit('JUMP_IF_FALSE', else_ or after) self.emit('JUMP_IF_FALSE', else_ or after)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
self.visit(node.body) self.visit(node.body)
self.emit('JUMP_ABSOLUTE', loop) self.emit('JUMP_ABSOLUTE', loop)
self.startBlock(else_) # or just the POPs if not else clause self.startBlock(else_) # or just the POPs if not else clause
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
if node.else_: if node.else_:
self.visit(node.else_) self.visit(node.else_)
self.loops.pop() self.loops.pop()
self.nextBlock(after) self.nextBlock(after)
def visitFor(self, node): def visitFor(self, node):
start = self.newBlock() start = self.newBlock()
anchor = self.newBlock() anchor = self.newBlock()
after = self.newBlock() after = self.newBlock()
self.loops.push(start) self.loops.push(start)
self.set_lineno(node) self.set_lineno(node)
self.emit('SETUP_LOOP', after) self.emit('SETUP_LOOP', after)
self.visit(node.list) self.visit(node.list)
self.visit(ast.Const(0)) self.visit(ast.Const(0))
self.nextBlock(start) self.nextBlock(start)
self.set_lineno(node) self.set_lineno(node)
self.emit('FOR_LOOP', anchor) self.emit('FOR_LOOP', anchor)
self.visit(node.assign) self.visit(node.assign)
self.visit(node.body) self.visit(node.body)
self.emit('JUMP_ABSOLUTE', start) self.emit('JUMP_ABSOLUTE', start)
self.nextBlock(anchor) self.nextBlock(anchor)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
if node.else_: if node.else_:
self.visit(node.else_) self.visit(node.else_)
self.loops.pop() self.loops.pop()
self.nextBlock(after) self.nextBlock(after)
def visitBreak(self, node): def visitBreak(self, node):
if not self.loops: if not self.loops:
raise SyntaxError, "'break' outside loop (%s, %d)" % \ raise SyntaxError, "'break' outside loop (%s, %d)" % \
(self.filename, node.lineno) (self.filename, node.lineno)
self.set_lineno(node) self.set_lineno(node)
self.emit('BREAK_LOOP') self.emit('BREAK_LOOP')
def visitContinue(self, node): def visitContinue(self, node):
if not self.loops: if not self.loops:
raise SyntaxError, "'continue' outside loop (%s, %d)" % \ raise SyntaxError, "'continue' outside loop (%s, %d)" % \
(self.filename, node.lineno) (self.filename, node.lineno)
l = self.loops.top() l = self.loops.top()
self.set_lineno(node) self.set_lineno(node)
self.emit('JUMP_ABSOLUTE', l) self.emit('JUMP_ABSOLUTE', l)
self.nextBlock() self.nextBlock()
def visitTest(self, node, jump): def visitTest(self, node, jump):
end = self.newBlock() end = self.newBlock()
for child in node.nodes[:-1]: for child in node.nodes[:-1]:
self.visit(child) self.visit(child)
self.emit(jump, end) self.emit(jump, end)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
self.visit(node.nodes[-1]) self.visit(node.nodes[-1])
self.nextBlock(end) self.nextBlock(end)
def visitAnd(self, node): def visitAnd(self, node):
self.visitTest(node, 'JUMP_IF_FALSE') self.visitTest(node, 'JUMP_IF_FALSE')
def visitOr(self, node): def visitOr(self, node):
self.visitTest(node, 'JUMP_IF_TRUE') self.visitTest(node, 'JUMP_IF_TRUE')
def visitCompare(self, node): def visitCompare(self, node):
self.visit(node.expr) self.visit(node.expr)
cleanup = self.newBlock() cleanup = self.newBlock()
for op, code in node.ops[:-1]: for op, code in node.ops[:-1]:
self.visit(code) self.visit(code)
self.emit('DUP_TOP') self.emit('DUP_TOP')
self.emit('ROT_THREE') self.emit('ROT_THREE')
self.emit('COMPARE_OP', op) self.emit('COMPARE_OP', op)
self.emit('JUMP_IF_FALSE', cleanup) self.emit('JUMP_IF_FALSE', cleanup)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
# now do the last comparison # now do the last comparison
if node.ops: if node.ops:
op, code = node.ops[-1] op, code = node.ops[-1]
self.visit(code) self.visit(code)
self.emit('COMPARE_OP', op) self.emit('COMPARE_OP', op)
if len(node.ops) > 1: if len(node.ops) > 1:
end = self.newBlock() end = self.newBlock()
self.emit('JUMP_FORWARD', end) self.emit('JUMP_FORWARD', end)
self.nextBlock(cleanup) self.nextBlock(cleanup)
self.emit('ROT_TWO') self.emit('ROT_TWO')
self.emit('POP_TOP') self.emit('POP_TOP')
self.nextBlock(end) self.nextBlock(end)
# exception related # exception related
def visitAssert(self, node): def visitAssert(self, node):
# XXX would be interesting to implement this via a # XXX would be interesting to implement this via a
# transformation of the AST before this stage # transformation of the AST before this stage
end = self.newBlock() end = self.newBlock()
self.set_lineno(node) self.set_lineno(node)
# XXX __debug__ and AssertionError appear to be special cases # XXX __debug__ and AssertionError appear to be special cases
# -- they are always loaded as globals even if there are local # -- they are always loaded as globals even if there are local
# names. I guess this is a sort of renaming op. # names. I guess this is a sort of renaming op.
self.emit('LOAD_GLOBAL', '__debug__') self.emit('LOAD_GLOBAL', '__debug__')
self.emit('JUMP_IF_FALSE', end) self.emit('JUMP_IF_FALSE', end)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
self.visit(node.test) self.visit(node.test)
self.emit('JUMP_IF_TRUE', end) self.emit('JUMP_IF_TRUE', end)
self.nextBlock() self.nextBlock()
self.emit('LOAD_GLOBAL', 'AssertionError') self.emit('LOAD_GLOBAL', 'AssertionError')
self.visit(node.fail) self.visit(node.fail)
self.emit('RAISE_VARARGS', 2) self.emit('RAISE_VARARGS', 2)
self.nextBlock(end) self.nextBlock(end)
self.emit('POP_TOP') self.emit('POP_TOP')
def visitRaise(self, node): def visitRaise(self, node):
self.set_lineno(node) self.set_lineno(node)
...@@ -349,7 +349,7 @@ class CodeGenerator: ...@@ -349,7 +349,7 @@ class CodeGenerator:
self.visit(node.body) self.visit(node.body)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.emit('JUMP_FORWARD', lElse) self.emit('JUMP_FORWARD', lElse)
self.nextBlock(handlers) self.nextBlock(handlers)
last = len(node.handlers) - 1 last = len(node.handlers) - 1
for i in range(len(node.handlers)): for i in range(len(node.handlers)):
...@@ -361,7 +361,7 @@ class CodeGenerator: ...@@ -361,7 +361,7 @@ class CodeGenerator:
self.emit('COMPARE_OP', 'exception match') self.emit('COMPARE_OP', 'exception match')
next = self.newBlock() next = self.newBlock()
self.emit('JUMP_IF_FALSE', next) self.emit('JUMP_IF_FALSE', next)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('POP_TOP') self.emit('POP_TOP')
if target: if target:
...@@ -372,13 +372,13 @@ class CodeGenerator: ...@@ -372,13 +372,13 @@ class CodeGenerator:
self.visit(body) self.visit(body)
self.emit('JUMP_FORWARD', end) self.emit('JUMP_FORWARD', end)
if expr: if expr:
self.nextBlock(next) self.nextBlock(next)
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('END_FINALLY') self.emit('END_FINALLY')
if node.else_: if node.else_:
self.nextBlock(lElse) self.nextBlock(lElse)
self.visit(node.else_) self.visit(node.else_)
self.nextBlock(end) self.nextBlock(end)
def visitTryFinally(self, node): def visitTryFinally(self, node):
final = self.newBlock() final = self.newBlock()
...@@ -387,15 +387,15 @@ class CodeGenerator: ...@@ -387,15 +387,15 @@ class CodeGenerator:
self.visit(node.body) self.visit(node.body)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.emit('LOAD_CONST', None) self.emit('LOAD_CONST', None)
self.nextBlock(final) self.nextBlock(final)
self.visit(node.final) self.visit(node.final)
self.emit('END_FINALLY') self.emit('END_FINALLY')
# misc # misc
## def visitStmt(self, node): ## def visitStmt(self, node):
## # nothing to do except walk the children ## # nothing to do except walk the children
## pass ## pass
def visitDiscard(self, node): def visitDiscard(self, node):
self.visit(node.expr) self.visit(node.expr)
...@@ -405,12 +405,12 @@ class CodeGenerator: ...@@ -405,12 +405,12 @@ class CodeGenerator:
self.emit('LOAD_CONST', node.value) self.emit('LOAD_CONST', node.value)
def visitKeyword(self, node): def visitKeyword(self, node):
self.emit('LOAD_CONST', node.name) self.emit('LOAD_CONST', node.name)
self.visit(node.expr) self.visit(node.expr)
def visitGlobal(self, node): def visitGlobal(self, node):
# no code to generate # no code to generate
pass pass
def visitName(self, node): def visitName(self, node):
self.loadName(node.name) self.loadName(node.name)
...@@ -470,7 +470,7 @@ class CodeGenerator: ...@@ -470,7 +470,7 @@ class CodeGenerator:
def visitAssTuple(self, node): def visitAssTuple(self, node):
if findOp(node) != 'OP_DELETE': if findOp(node) != 'OP_DELETE':
self.emit('UNPACK_TUPLE', len(node.nodes)) self.emit('UNPACK_SEQUENCE', len(node.nodes))
for child in node.nodes: for child in node.nodes:
self.visit(child) self.visit(child)
...@@ -655,10 +655,10 @@ class CodeGenerator: ...@@ -655,10 +655,10 @@ class CodeGenerator:
set.emit('SET_LINENO', lineno) set.emit('SET_LINENO', lineno)
self.emit('BUILD_MAP', 0) self.emit('BUILD_MAP', 0)
for k, v in node.items: for k, v in node.items:
lineno2 = getattr(node, 'lineno', None) lineno2 = getattr(node, 'lineno', None)
if lineno2 is not None and lineno != lineno2: if lineno2 is not None and lineno != lineno2:
self.emit('SET_LINENO', lineno2) self.emit('SET_LINENO', lineno2)
lineno = lineno2 lineno = lineno2
self.emit('DUP_TOP') self.emit('DUP_TOP')
self.visit(v) self.visit(v)
self.emit('ROT_TWO') self.emit('ROT_TWO')
...@@ -669,9 +669,9 @@ class ModuleCodeGenerator(CodeGenerator): ...@@ -669,9 +669,9 @@ class ModuleCodeGenerator(CodeGenerator):
super_init = CodeGenerator.__init__ super_init = CodeGenerator.__init__
def __init__(self, filename): def __init__(self, filename):
# XXX <module> is ? in compile.c # XXX <module> is ? in compile.c
self.graph = pyassem.PyFlowGraph("<module>", filename) self.graph = pyassem.PyFlowGraph("<module>", filename)
self.super_init(filename) self.super_init(filename)
class FunctionCodeGenerator(CodeGenerator): class FunctionCodeGenerator(CodeGenerator):
super_init = CodeGenerator.__init__ super_init = CodeGenerator.__init__
...@@ -686,27 +686,27 @@ class FunctionCodeGenerator(CodeGenerator): ...@@ -686,27 +686,27 @@ class FunctionCodeGenerator(CodeGenerator):
klass.lambdaCount = klass.lambdaCount + 1 klass.lambdaCount = klass.lambdaCount + 1
else: else:
name = func.name name = func.name
args, hasTupleArg = generateArgList(func.argnames) args, hasTupleArg = generateArgList(func.argnames)
self.graph = pyassem.PyFlowGraph(name, filename, args, self.graph = pyassem.PyFlowGraph(name, filename, args,
optimized=1) optimized=1)
self.isLambda = isLambda self.isLambda = isLambda
self.super_init(filename) self.super_init(filename)
lnf = walk(func.code, LocalNameFinder(args), 0) lnf = walk(func.code, LocalNameFinder(args), 0)
self.locals.push(lnf.getLocals()) self.locals.push(lnf.getLocals())
if func.varargs: if func.varargs:
self.graph.setFlag(CO_VARARGS) self.graph.setFlag(CO_VARARGS)
if func.kwargs: if func.kwargs:
self.graph.setFlag(CO_VARKEYWORDS) self.graph.setFlag(CO_VARKEYWORDS)
self.set_lineno(func) self.set_lineno(func)
if hasTupleArg: if hasTupleArg:
self.generateArgUnpack(func.argnames) self.generateArgUnpack(func.argnames)
def finish(self): def finish(self):
self.graph.startExitBlock() self.graph.startExitBlock()
if not self.isLambda: if not self.isLambda:
self.emit('LOAD_CONST', None) self.emit('LOAD_CONST', None)
self.emit('RETURN_VALUE') self.emit('RETURN_VALUE')
def generateArgUnpack(self, args): def generateArgUnpack(self, args):
count = 0 count = 0
...@@ -714,30 +714,33 @@ class FunctionCodeGenerator(CodeGenerator): ...@@ -714,30 +714,33 @@ class FunctionCodeGenerator(CodeGenerator):
if type(arg) == types.TupleType: if type(arg) == types.TupleType:
self.emit('LOAD_FAST', '.nested%d' % count) self.emit('LOAD_FAST', '.nested%d' % count)
count = count + 1 count = count + 1
self.unpackTuple(arg) self.unpackSequence(arg)
def unpackTuple(self, tup): def unpackSequence(self, tup):
self.emit('UNPACK_TUPLE', len(tup)) self.emit('UNPACK_SEQUENCE', len(tup))
for elt in tup: for elt in tup:
if type(elt) == types.TupleType: if type(elt) == types.TupleType:
self.unpackTuple(elt) self.unpackSequence(elt)
else: else:
self.emit('STORE_FAST', elt) self.emit('STORE_FAST', elt)
unpackTuple = unpackSequence
class ClassCodeGenerator(CodeGenerator): class ClassCodeGenerator(CodeGenerator):
super_init = CodeGenerator.__init__ super_init = CodeGenerator.__init__
def __init__(self, klass, filename): def __init__(self, klass, filename):
self.graph = pyassem.PyFlowGraph(klass.name, filename, self.graph = pyassem.PyFlowGraph(klass.name, filename,
optimized=0) optimized=0)
self.super_init(filename) self.super_init(filename)
lnf = walk(klass.code, LocalNameFinder(), 0) lnf = walk(klass.code, LocalNameFinder(), 0)
self.locals.push(lnf.getLocals()) self.locals.push(lnf.getLocals())
self.graph.setFlag(CO_NEWLOCALS)
def finish(self): def finish(self):
self.graph.startExitBlock() self.graph.startExitBlock()
self.emit('LOAD_LOCALS') self.emit('LOAD_LOCALS')
self.emit('RETURN_VALUE') self.emit('RETURN_VALUE')
def generateArgList(arglist): def generateArgList(arglist):
...@@ -746,14 +749,14 @@ def generateArgList(arglist): ...@@ -746,14 +749,14 @@ def generateArgList(arglist):
extra = [] extra = []
count = 0 count = 0
for elt in arglist: for elt in arglist:
if type(elt) == types.StringType: if type(elt) == types.StringType:
args.append(elt) args.append(elt)
elif type(elt) == types.TupleType: elif type(elt) == types.TupleType:
args.append(TupleArg(count, elt)) args.append(TupleArg(count, elt))
count = count + 1 count = count + 1
extra.extend(misc.flatten(elt)) extra.extend(misc.flatten(elt))
else: else:
raise ValueError, "unexpect argument type:", elt raise ValueError, "unexpect argument type:", elt
return args + extra, count return args + extra, count
class LocalNameFinder: class LocalNameFinder:
...@@ -771,7 +774,7 @@ class LocalNameFinder: ...@@ -771,7 +774,7 @@ class LocalNameFinder:
return self.names return self.names
def visitDict(self, node): def visitDict(self, node):
pass pass
def visitGlobal(self, node): def visitGlobal(self, node):
for name in node.names: for name in node.names:
...@@ -781,7 +784,7 @@ class LocalNameFinder: ...@@ -781,7 +784,7 @@ class LocalNameFinder:
self.names.add(node.name) self.names.add(node.name)
def visitLambda(self, node): def visitLambda(self, node):
pass pass
def visitImport(self, node): def visitImport(self, node):
for name in node.names: for name in node.names:
...@@ -816,4 +819,4 @@ if __name__ == "__main__": ...@@ -816,4 +819,4 @@ if __name__ == "__main__":
import sys import sys
for file in sys.argv[1:]: for file in sys.argv[1:]:
compile(file) compile(file)
...@@ -38,7 +38,7 @@ class ASTVisitor: ...@@ -38,7 +38,7 @@ class ASTVisitor:
def __init__(self): def __init__(self):
self.node = None self.node = None
self._cache = {} self._cache = {}
def preorder(self, tree, visitor): def preorder(self, tree, visitor):
"""Do preorder walk of tree using visitor""" """Do preorder walk of tree using visitor"""
...@@ -47,7 +47,7 @@ class ASTVisitor: ...@@ -47,7 +47,7 @@ class ASTVisitor:
self._preorder(tree) self._preorder(tree)
def _preorder(self, node, *args): def _preorder(self, node, *args):
return apply(self.dispatch, (node,) + args) return apply(self.dispatch, (node,) + args)
def default(self, node, *args): def default(self, node, *args):
for child in node.getChildren(): for child in node.getChildren():
...@@ -56,18 +56,18 @@ class ASTVisitor: ...@@ -56,18 +56,18 @@ class ASTVisitor:
def dispatch(self, node, *args): def dispatch(self, node, *args):
self.node = node self.node = node
meth = self._cache.get(node.__class__, None) meth = self._cache.get(node.__class__, None)
className = node.__class__.__name__ className = node.__class__.__name__
if meth is None: if meth is None:
meth = getattr(self.visitor, 'visit' + className, self.default) meth = getattr(self.visitor, 'visit' + className, self.default)
self._cache[node.__class__] = meth self._cache[node.__class__] = meth
if self.VERBOSE > 0: if self.VERBOSE > 0:
if self.VERBOSE == 1: if self.VERBOSE == 1:
if meth == 0: if meth == 0:
print "dispatch", className print "dispatch", className
else: else:
print "dispatch", className, (meth and meth.__name__ or '') print "dispatch", className, (meth and meth.__name__ or '')
return apply(meth, (node,) + args) return apply(meth, (node,) + args)
class ExampleASTVisitor(ASTVisitor): class ExampleASTVisitor(ASTVisitor):
"""Prints examples of the nodes that aren't visited """Prints examples of the nodes that aren't visited
...@@ -80,11 +80,11 @@ class ExampleASTVisitor(ASTVisitor): ...@@ -80,11 +80,11 @@ class ExampleASTVisitor(ASTVisitor):
def dispatch(self, node, *args): def dispatch(self, node, *args):
self.node = node self.node = node
meth = self._cache.get(node.__class__, None) meth = self._cache.get(node.__class__, None)
className = node.__class__.__name__ className = node.__class__.__name__
if meth is None: if meth is None:
meth = getattr(self.visitor, 'visit' + className, 0) meth = getattr(self.visitor, 'visit' + className, 0)
self._cache[node.__class__] = meth self._cache[node.__class__] = meth
if self.VERBOSE > 1: if self.VERBOSE > 1:
print "dispatch", className, (meth and meth.__name__ or '') print "dispatch", className, (meth and meth.__name__ or '')
if meth: if meth:
...@@ -92,15 +92,15 @@ class ExampleASTVisitor(ASTVisitor): ...@@ -92,15 +92,15 @@ class ExampleASTVisitor(ASTVisitor):
elif self.VERBOSE > 0: elif self.VERBOSE > 0:
klass = node.__class__ klass = node.__class__
if not self.examples.has_key(klass): if not self.examples.has_key(klass):
self.examples[klass] = klass self.examples[klass] = klass
print print
print self.visitor print self.visitor
print klass print klass
for attr in dir(node): for attr in dir(node):
if attr[0] != '_': if attr[0] != '_':
print "\t", "%-12.12s" % attr, getattr(node, attr) print "\t", "%-12.12s" % attr, getattr(node, attr)
print print
return apply(self.default, (node,) + args) return apply(self.default, (node,) + args)
_walker = ASTVisitor _walker = ASTVisitor
def walk(tree, visitor, verbose=None): def walk(tree, visitor, verbose=None):
......
...@@ -9,71 +9,71 @@ from compiler import misc ...@@ -9,71 +9,71 @@ from compiler import misc
class FlowGraph: class FlowGraph:
def __init__(self): def __init__(self):
self.current = self.entry = Block() self.current = self.entry = Block()
self.exit = Block("exit") self.exit = Block("exit")
self.blocks = misc.Set() self.blocks = misc.Set()
self.blocks.add(self.entry) self.blocks.add(self.entry)
self.blocks.add(self.exit) self.blocks.add(self.exit)
def startBlock(self, block): def startBlock(self, block):
self.current = block self.current = block
def nextBlock(self, block=None): def nextBlock(self, block=None):
if block is None: if block is None:
block = self.newBlock() block = self.newBlock()
# XXX think we need to specify when there is implicit transfer # XXX think we need to specify when there is implicit transfer
# from one block to the next # from one block to the next
# #
# I think this strategy works: each block has a child # I think this strategy works: each block has a child
# designated as "next" which is returned as the last of the # designated as "next" which is returned as the last of the
# children. because the nodes in a graph are emitted in # children. because the nodes in a graph are emitted in
# reverse post order, the "next" block will always be emitted # reverse post order, the "next" block will always be emitted
# immediately after its parent. # immediately after its parent.
# Worry: maintaining this invariant could be tricky # Worry: maintaining this invariant could be tricky
self.current.addNext(block) self.current.addNext(block)
self.startBlock(block) self.startBlock(block)
def newBlock(self): def newBlock(self):
b = Block() b = Block()
self.blocks.add(b) self.blocks.add(b)
return b return b
def startExitBlock(self): def startExitBlock(self):
self.startBlock(self.exit) self.startBlock(self.exit)
def emit(self, *inst): def emit(self, *inst):
# XXX should jump instructions implicitly call nextBlock? # XXX should jump instructions implicitly call nextBlock?
if inst[0] == 'RETURN_VALUE': if inst[0] == 'RETURN_VALUE':
self.current.addOutEdge(self.exit) self.current.addOutEdge(self.exit)
self.current.emit(inst) self.current.emit(inst)
def getBlocks(self): def getBlocks(self):
"""Return the blocks in reverse postorder """Return the blocks in reverse postorder
i.e. each node appears before all of its successors i.e. each node appears before all of its successors
""" """
# XXX make sure every node that doesn't have an explicit next # XXX make sure every node that doesn't have an explicit next
# is set so that next points to exit # is set so that next points to exit
for b in self.blocks.elements(): for b in self.blocks.elements():
if b is self.exit: if b is self.exit:
continue continue
if not b.next: if not b.next:
b.addNext(self.exit) b.addNext(self.exit)
order = dfs_postorder(self.entry, {}) order = dfs_postorder(self.entry, {})
order.reverse() order.reverse()
# hack alert # hack alert
if not self.exit in order: if not self.exit in order:
order.append(self.exit) order.append(self.exit)
return order return order
def dfs_postorder(b, seen): def dfs_postorder(b, seen):
"""Depth-first search of tree rooted at b, return in postorder""" """Depth-first search of tree rooted at b, return in postorder"""
order = [] order = []
seen[b] = b seen[b] = b
for c in b.children(): for c in b.children():
if seen.has_key(c): if seen.has_key(c):
continue continue
order = order + dfs_postorder(c, seen) order = order + dfs_postorder(c, seen)
order.append(b) order.append(b)
return order return order
...@@ -81,47 +81,47 @@ class Block: ...@@ -81,47 +81,47 @@ class Block:
_count = 0 _count = 0
def __init__(self, label=''): def __init__(self, label=''):
self.insts = [] self.insts = []
self.inEdges = misc.Set() self.inEdges = misc.Set()
self.outEdges = misc.Set() self.outEdges = misc.Set()
self.label = label self.label = label
self.bid = Block._count self.bid = Block._count
self.next = [] self.next = []
Block._count = Block._count + 1 Block._count = Block._count + 1
def __repr__(self): def __repr__(self):
if self.label: if self.label:
return "<block %s id=%d len=%d>" % (self.label, self.bid, return "<block %s id=%d len=%d>" % (self.label, self.bid,
len(self.insts)) len(self.insts))
else: else:
return "<block id=%d len=%d>" % (self.bid, len(self.insts)) return "<block id=%d len=%d>" % (self.bid, len(self.insts))
def __str__(self): def __str__(self):
insts = map(str, self.insts) insts = map(str, self.insts)
return "<block %s %d:\n%s>" % (self.label, self.bid, return "<block %s %d:\n%s>" % (self.label, self.bid,
string.join(insts, '\n')) string.join(insts, '\n'))
def emit(self, inst): def emit(self, inst):
op = inst[0] op = inst[0]
if op[:4] == 'JUMP': if op[:4] == 'JUMP':
self.outEdges.add(inst[1]) self.outEdges.add(inst[1])
self.insts.append(inst) self.insts.append(inst)
def getInstructions(self): def getInstructions(self):
return self.insts return self.insts
def addInEdge(self, block): def addInEdge(self, block):
self.inEdges.add(block) self.inEdges.add(block)
def addOutEdge(self, block): def addOutEdge(self, block):
self.outEdges.add(block) self.outEdges.add(block)
def addNext(self, block): def addNext(self, block):
self.next.append(block) self.next.append(block)
assert len(self.next) == 1, map(str, self.next) assert len(self.next) == 1, map(str, self.next)
def children(self): def children(self):
return self.outEdges.elements() + self.next return self.outEdges.elements() + self.next
# flags for code objects # flags for code objects
CO_OPTIMIZED = 0x0001 CO_OPTIMIZED = 0x0001
...@@ -139,18 +139,18 @@ class PyFlowGraph(FlowGraph): ...@@ -139,18 +139,18 @@ class PyFlowGraph(FlowGraph):
super_init = FlowGraph.__init__ super_init = FlowGraph.__init__
def __init__(self, name, filename, args=(), optimized=0): def __init__(self, name, filename, args=(), optimized=0):
self.super_init() self.super_init()
self.name = name self.name = name
self.filename = filename self.filename = filename
self.docstring = None self.docstring = None
self.args = args # XXX self.args = args # XXX
self.argcount = getArgCount(args) self.argcount = getArgCount(args)
if optimized: if optimized:
self.flags = CO_OPTIMIZED | CO_NEWLOCALS self.flags = CO_OPTIMIZED | CO_NEWLOCALS
else: else:
self.flags = 0 self.flags = 0
self.consts = [] self.consts = []
self.names = [] self.names = []
self.varnames = list(args) or [] self.varnames = list(args) or []
for i in range(len(self.varnames)): for i in range(len(self.varnames)):
var = self.varnames[i] var = self.varnames[i]
...@@ -163,13 +163,13 @@ class PyFlowGraph(FlowGraph): ...@@ -163,13 +163,13 @@ class PyFlowGraph(FlowGraph):
self.consts.insert(0, doc) self.consts.insert(0, doc)
def setFlag(self, flag): def setFlag(self, flag):
self.flags = self.flags | flag self.flags = self.flags | flag
if flag == CO_VARARGS: if flag == CO_VARARGS:
self.argcount = self.argcount - 1 self.argcount = self.argcount - 1
def getCode(self): def getCode(self):
"""Get a Python code object""" """Get a Python code object"""
if self.stage == RAW: if self.stage == RAW:
self.flattenGraph() self.flattenGraph()
if self.stage == FLAT: if self.stage == FLAT:
self.convertArgs() self.convertArgs()
...@@ -198,38 +198,38 @@ class PyFlowGraph(FlowGraph): ...@@ -198,38 +198,38 @@ class PyFlowGraph(FlowGraph):
sys.stdout = save sys.stdout = save
def flattenGraph(self): def flattenGraph(self):
"""Arrange the blocks in order and resolve jumps""" """Arrange the blocks in order and resolve jumps"""
assert self.stage == RAW assert self.stage == RAW
self.insts = insts = [] self.insts = insts = []
pc = 0 pc = 0
begin = {} begin = {}
end = {} end = {}
for b in self.getBlocks(): for b in self.getBlocks():
begin[b] = pc begin[b] = pc
for inst in b.getInstructions(): for inst in b.getInstructions():
insts.append(inst) insts.append(inst)
if len(inst) == 1: if len(inst) == 1:
pc = pc + 1 pc = pc + 1
else: else:
# arg takes 2 bytes # arg takes 2 bytes
pc = pc + 3 pc = pc + 3
end[b] = pc end[b] = pc
pc = 0 pc = 0
for i in range(len(insts)): for i in range(len(insts)):
inst = insts[i] inst = insts[i]
if len(inst) == 1: if len(inst) == 1:
pc = pc + 1 pc = pc + 1
else: else:
pc = pc + 3 pc = pc + 3
opname = inst[0] opname = inst[0]
if self.hasjrel.has_elt(opname): if self.hasjrel.has_elt(opname):
oparg = inst[1] oparg = inst[1]
offset = begin[oparg] - pc offset = begin[oparg] - pc
insts[i] = opname, offset insts[i] = opname, offset
elif self.hasjabs.has_elt(opname): elif self.hasjabs.has_elt(opname):
insts[i] = opname, begin[inst[1]] insts[i] = opname, begin[inst[1]]
self.stacksize = findDepth(self.insts) self.stacksize = findDepth(self.insts)
self.stage = FLAT self.stage = FLAT
hasjrel = misc.Set() hasjrel = misc.Set()
for i in dis.hasjrel: for i in dis.hasjrel:
...@@ -292,7 +292,7 @@ class PyFlowGraph(FlowGraph): ...@@ -292,7 +292,7 @@ class PyFlowGraph(FlowGraph):
_cmp = list(dis.cmp_op) _cmp = list(dis.cmp_op)
def _convert_COMPARE_OP(self, arg): def _convert_COMPARE_OP(self, arg):
return self._cmp.index(arg) return self._cmp.index(arg)
# similarly for other opcodes... # similarly for other opcodes...
...@@ -314,12 +314,12 @@ class PyFlowGraph(FlowGraph): ...@@ -314,12 +314,12 @@ class PyFlowGraph(FlowGraph):
if opname == "SET_LINENO": if opname == "SET_LINENO":
lnotab.nextLine(oparg) lnotab.nextLine(oparg)
hi, lo = twobyte(oparg) hi, lo = twobyte(oparg)
try: try:
lnotab.addCode(self.opnum[opname], lo, hi) lnotab.addCode(self.opnum[opname], lo, hi)
except ValueError: except ValueError:
print opname, oparg print opname, oparg
print self.opnum[opname], lo, hi print self.opnum[opname], lo, hi
raise raise
self.stage = DONE self.stage = DONE
opnum = {} opnum = {}
...@@ -354,10 +354,10 @@ class PyFlowGraph(FlowGraph): ...@@ -354,10 +354,10 @@ class PyFlowGraph(FlowGraph):
elt = elt.getCode() elt = elt.getCode()
l.append(elt) l.append(elt)
return tuple(l) return tuple(l)
def isJump(opname): def isJump(opname):
if opname[:4] == 'JUMP': if opname[:4] == 'JUMP':
return 1 return 1
class TupleArg: class TupleArg:
"""Helper for marking func defs with nested tuples in arglist""" """Helper for marking func defs with nested tuples in arglist"""
...@@ -372,10 +372,10 @@ class TupleArg: ...@@ -372,10 +372,10 @@ class TupleArg:
def getArgCount(args): def getArgCount(args):
argcount = len(args) argcount = len(args)
if args: if args:
for arg in args: for arg in args:
if isinstance(arg, TupleArg): if isinstance(arg, TupleArg):
numNames = len(misc.flatten(arg.names)) numNames = len(misc.flatten(arg.names))
argcount = argcount - numNames argcount = argcount - numNames
return argcount return argcount
def twobyte(val): def twobyte(val):
...@@ -513,11 +513,9 @@ class StackDepthTracker: ...@@ -513,11 +513,9 @@ class StackDepthTracker:
] ]
# special cases: # special cases:
# UNPACK_TUPLE, UNPACK_LIST, BUILD_TUPLE, # UNPACK_SEQUENCE, BUILD_TUPLE,
# BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE # BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
def UNPACK_TUPLE(self, count): def UNPACK_SEQUENCE(self, count):
return count
def UNPACK_LIST(self, count):
return count return count
def BUILD_TUPLE(self, count): def BUILD_TUPLE(self, count):
return -count return -count
......
...@@ -7,7 +7,7 @@ from cStringIO import StringIO ...@@ -7,7 +7,7 @@ from cStringIO import StringIO
from compiler import ast, parse, walk from compiler import ast, parse, walk
from compiler import pyassem, misc from compiler import pyassem, misc
from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, TupleArg from compiler.pyassem import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS, TupleArg
callfunc_opcode_info = { callfunc_opcode_info = {
# (Have *args, Have **args) : opcode # (Have *args, Have **args) : opcode
...@@ -29,32 +29,32 @@ def compile(filename): ...@@ -29,32 +29,32 @@ def compile(filename):
class Module: class Module:
def __init__(self, source, filename): def __init__(self, source, filename):
self.filename = filename self.filename = filename
self.source = source self.source = source
self.code = None self.code = None
def compile(self): def compile(self):
ast = parse(self.source) ast = parse(self.source)
root, filename = os.path.split(self.filename) root, filename = os.path.split(self.filename)
gen = ModuleCodeGenerator(filename) gen = ModuleCodeGenerator(filename)
walk(ast, gen, 1) walk(ast, gen, 1)
self.code = gen.getCode() self.code = gen.getCode()
def dump(self, f): def dump(self, f):
f.write(self.getPycHeader()) f.write(self.getPycHeader())
marshal.dump(self.code, f) marshal.dump(self.code, f)
MAGIC = (20121 | (ord('\r')<<16) | (ord('\n')<<24)) MAGIC = (50811 | (ord('\r')<<16) | (ord('\n')<<24))
def getPycHeader(self): def getPycHeader(self):
# compile.c uses marshal to write a long directly, with # compile.c uses marshal to write a long directly, with
# calling the interface that would also generate a 1-byte code # calling the interface that would also generate a 1-byte code
# to indicate the type of the value. simplest way to get the # to indicate the type of the value. simplest way to get the
# same effect is to call marshal and then skip the code. # same effect is to call marshal and then skip the code.
magic = marshal.dumps(self.MAGIC)[1:] magic = marshal.dumps(self.MAGIC)[1:]
mtime = os.stat(self.filename)[stat.ST_MTIME] mtime = os.stat(self.filename)[stat.ST_MTIME]
mtime = struct.pack('i', mtime) mtime = struct.pack('i', mtime)
return magic + mtime return magic + mtime
class CodeGenerator: class CodeGenerator:
...@@ -63,24 +63,24 @@ class CodeGenerator: ...@@ -63,24 +63,24 @@ class CodeGenerator:
def __init__(self, filename): def __init__(self, filename):
## Subclasses must define a constructor that intializes self.graph ## Subclasses must define a constructor that intializes self.graph
## before calling this init function ## before calling this init function
## self.graph = pyassem.PyFlowGraph() ## self.graph = pyassem.PyFlowGraph()
self.filename = filename self.filename = filename
self.locals = misc.Stack() self.locals = misc.Stack()
self.loops = misc.Stack() self.loops = misc.Stack()
self.curStack = 0 self.curStack = 0
self.maxStack = 0 self.maxStack = 0
self._setupGraphDelegation() self._setupGraphDelegation()
def _setupGraphDelegation(self): def _setupGraphDelegation(self):
self.emit = self.graph.emit self.emit = self.graph.emit
self.newBlock = self.graph.newBlock self.newBlock = self.graph.newBlock
self.startBlock = self.graph.startBlock self.startBlock = self.graph.startBlock
self.nextBlock = self.graph.nextBlock self.nextBlock = self.graph.nextBlock
self.setDocstring = self.graph.setDocstring self.setDocstring = self.graph.setDocstring
def getCode(self): def getCode(self):
"""Return a code object""" """Return a code object"""
return self.graph.getCode() return self.graph.getCode()
# Next five methods handle name access # Next five methods handle name access
...@@ -97,11 +97,11 @@ class CodeGenerator: ...@@ -97,11 +97,11 @@ class CodeGenerator:
self._nameOp('DELETE', name) self._nameOp('DELETE', name)
def _nameOp(self, prefix, name): def _nameOp(self, prefix, name):
if not self.optimized: if not self.optimized:
self.emit(prefix + '_NAME', name) self.emit(prefix + '_NAME', name)
return return
if self.isLocalName(name): if self.isLocalName(name):
self.emit(prefix + '_FAST', name) self.emit(prefix + '_FAST', name)
else: else:
self.emit(prefix + '_GLOBAL', name) self.emit(prefix + '_GLOBAL', name)
...@@ -125,25 +125,25 @@ class CodeGenerator: ...@@ -125,25 +125,25 @@ class CodeGenerator:
# code objects # code objects
def visitModule(self, node): def visitModule(self, node):
lnf = walk(node.node, LocalNameFinder(), 0) lnf = walk(node.node, LocalNameFinder(), 0)
self.locals.push(lnf.getLocals()) self.locals.push(lnf.getLocals())
self.setDocstring(node.doc) self.setDocstring(node.doc)
self.visit(node.node) self.visit(node.node)
self.emit('LOAD_CONST', None) self.emit('LOAD_CONST', None)
self.emit('RETURN_VALUE') self.emit('RETURN_VALUE')
def visitFunction(self, node): def visitFunction(self, node):
self._visitFuncOrLambda(node, isLambda=0) self._visitFuncOrLambda(node, isLambda=0)
self.storeName(node.name) self.storeName(node.name)
def visitLambda(self, node): def visitLambda(self, node):
self._visitFuncOrLambda(node, isLambda=1) self._visitFuncOrLambda(node, isLambda=1)
## self.storeName("<lambda>") ## self.storeName("<lambda>")
def _visitFuncOrLambda(self, node, isLambda): def _visitFuncOrLambda(self, node, isLambda):
gen = FunctionCodeGenerator(node, self.filename, isLambda) gen = FunctionCodeGenerator(node, self.filename, isLambda)
walk(node.code, gen) walk(node.code, gen)
gen.finish() gen.finish()
self.set_lineno(node) self.set_lineno(node)
for default in node.defaults: for default in node.defaults:
self.visit(default) self.visit(default)
...@@ -170,158 +170,158 @@ class CodeGenerator: ...@@ -170,158 +170,158 @@ class CodeGenerator:
# The next few implement control-flow statements # The next few implement control-flow statements
def visitIf(self, node): def visitIf(self, node):
end = self.newBlock() end = self.newBlock()
numtests = len(node.tests) numtests = len(node.tests)
for i in range(numtests): for i in range(numtests):
test, suite = node.tests[i] test, suite = node.tests[i]
self.set_lineno(test) self.set_lineno(test)
self.visit(test) self.visit(test)
## if i == numtests - 1 and not node.else_: ## if i == numtests - 1 and not node.else_:
## nextTest = end ## nextTest = end
## else: ## else:
## nextTest = self.newBlock() ## nextTest = self.newBlock()
nextTest = self.newBlock() nextTest = self.newBlock()
self.emit('JUMP_IF_FALSE', nextTest) self.emit('JUMP_IF_FALSE', nextTest)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
self.visit(suite) self.visit(suite)
self.emit('JUMP_FORWARD', end) self.emit('JUMP_FORWARD', end)
self.nextBlock(nextTest) self.nextBlock(nextTest)
self.emit('POP_TOP') self.emit('POP_TOP')
if node.else_: if node.else_:
self.visit(node.else_) self.visit(node.else_)
self.nextBlock(end) self.nextBlock(end)
def visitWhile(self, node): def visitWhile(self, node):
self.set_lineno(node) self.set_lineno(node)
loop = self.newBlock() loop = self.newBlock()
else_ = self.newBlock() else_ = self.newBlock()
after = self.newBlock() after = self.newBlock()
self.emit('SETUP_LOOP', after) self.emit('SETUP_LOOP', after)
self.nextBlock(loop) self.nextBlock(loop)
self.loops.push(loop) self.loops.push(loop)
self.set_lineno(node) self.set_lineno(node)
self.visit(node.test) self.visit(node.test)
self.emit('JUMP_IF_FALSE', else_ or after) self.emit('JUMP_IF_FALSE', else_ or after)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
self.visit(node.body) self.visit(node.body)
self.emit('JUMP_ABSOLUTE', loop) self.emit('JUMP_ABSOLUTE', loop)
self.startBlock(else_) # or just the POPs if not else clause self.startBlock(else_) # or just the POPs if not else clause
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
if node.else_: if node.else_:
self.visit(node.else_) self.visit(node.else_)
self.loops.pop() self.loops.pop()
self.nextBlock(after) self.nextBlock(after)
def visitFor(self, node): def visitFor(self, node):
start = self.newBlock() start = self.newBlock()
anchor = self.newBlock() anchor = self.newBlock()
after = self.newBlock() after = self.newBlock()
self.loops.push(start) self.loops.push(start)
self.set_lineno(node) self.set_lineno(node)
self.emit('SETUP_LOOP', after) self.emit('SETUP_LOOP', after)
self.visit(node.list) self.visit(node.list)
self.visit(ast.Const(0)) self.visit(ast.Const(0))
self.nextBlock(start) self.nextBlock(start)
self.set_lineno(node) self.set_lineno(node)
self.emit('FOR_LOOP', anchor) self.emit('FOR_LOOP', anchor)
self.visit(node.assign) self.visit(node.assign)
self.visit(node.body) self.visit(node.body)
self.emit('JUMP_ABSOLUTE', start) self.emit('JUMP_ABSOLUTE', start)
self.nextBlock(anchor) self.nextBlock(anchor)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
if node.else_: if node.else_:
self.visit(node.else_) self.visit(node.else_)
self.loops.pop() self.loops.pop()
self.nextBlock(after) self.nextBlock(after)
def visitBreak(self, node): def visitBreak(self, node):
if not self.loops: if not self.loops:
raise SyntaxError, "'break' outside loop (%s, %d)" % \ raise SyntaxError, "'break' outside loop (%s, %d)" % \
(self.filename, node.lineno) (self.filename, node.lineno)
self.set_lineno(node) self.set_lineno(node)
self.emit('BREAK_LOOP') self.emit('BREAK_LOOP')
def visitContinue(self, node): def visitContinue(self, node):
if not self.loops: if not self.loops:
raise SyntaxError, "'continue' outside loop (%s, %d)" % \ raise SyntaxError, "'continue' outside loop (%s, %d)" % \
(self.filename, node.lineno) (self.filename, node.lineno)
l = self.loops.top() l = self.loops.top()
self.set_lineno(node) self.set_lineno(node)
self.emit('JUMP_ABSOLUTE', l) self.emit('JUMP_ABSOLUTE', l)
self.nextBlock() self.nextBlock()
def visitTest(self, node, jump): def visitTest(self, node, jump):
end = self.newBlock() end = self.newBlock()
for child in node.nodes[:-1]: for child in node.nodes[:-1]:
self.visit(child) self.visit(child)
self.emit(jump, end) self.emit(jump, end)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
self.visit(node.nodes[-1]) self.visit(node.nodes[-1])
self.nextBlock(end) self.nextBlock(end)
def visitAnd(self, node): def visitAnd(self, node):
self.visitTest(node, 'JUMP_IF_FALSE') self.visitTest(node, 'JUMP_IF_FALSE')
def visitOr(self, node): def visitOr(self, node):
self.visitTest(node, 'JUMP_IF_TRUE') self.visitTest(node, 'JUMP_IF_TRUE')
def visitCompare(self, node): def visitCompare(self, node):
self.visit(node.expr) self.visit(node.expr)
cleanup = self.newBlock() cleanup = self.newBlock()
for op, code in node.ops[:-1]: for op, code in node.ops[:-1]:
self.visit(code) self.visit(code)
self.emit('DUP_TOP') self.emit('DUP_TOP')
self.emit('ROT_THREE') self.emit('ROT_THREE')
self.emit('COMPARE_OP', op) self.emit('COMPARE_OP', op)
self.emit('JUMP_IF_FALSE', cleanup) self.emit('JUMP_IF_FALSE', cleanup)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
# now do the last comparison # now do the last comparison
if node.ops: if node.ops:
op, code = node.ops[-1] op, code = node.ops[-1]
self.visit(code) self.visit(code)
self.emit('COMPARE_OP', op) self.emit('COMPARE_OP', op)
if len(node.ops) > 1: if len(node.ops) > 1:
end = self.newBlock() end = self.newBlock()
self.emit('JUMP_FORWARD', end) self.emit('JUMP_FORWARD', end)
self.nextBlock(cleanup) self.nextBlock(cleanup)
self.emit('ROT_TWO') self.emit('ROT_TWO')
self.emit('POP_TOP') self.emit('POP_TOP')
self.nextBlock(end) self.nextBlock(end)
# exception related # exception related
def visitAssert(self, node): def visitAssert(self, node):
# XXX would be interesting to implement this via a # XXX would be interesting to implement this via a
# transformation of the AST before this stage # transformation of the AST before this stage
end = self.newBlock() end = self.newBlock()
self.set_lineno(node) self.set_lineno(node)
# XXX __debug__ and AssertionError appear to be special cases # XXX __debug__ and AssertionError appear to be special cases
# -- they are always loaded as globals even if there are local # -- they are always loaded as globals even if there are local
# names. I guess this is a sort of renaming op. # names. I guess this is a sort of renaming op.
self.emit('LOAD_GLOBAL', '__debug__') self.emit('LOAD_GLOBAL', '__debug__')
self.emit('JUMP_IF_FALSE', end) self.emit('JUMP_IF_FALSE', end)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
self.visit(node.test) self.visit(node.test)
self.emit('JUMP_IF_TRUE', end) self.emit('JUMP_IF_TRUE', end)
self.nextBlock() self.nextBlock()
self.emit('LOAD_GLOBAL', 'AssertionError') self.emit('LOAD_GLOBAL', 'AssertionError')
self.visit(node.fail) self.visit(node.fail)
self.emit('RAISE_VARARGS', 2) self.emit('RAISE_VARARGS', 2)
self.nextBlock(end) self.nextBlock(end)
self.emit('POP_TOP') self.emit('POP_TOP')
def visitRaise(self, node): def visitRaise(self, node):
self.set_lineno(node) self.set_lineno(node)
...@@ -349,7 +349,7 @@ class CodeGenerator: ...@@ -349,7 +349,7 @@ class CodeGenerator:
self.visit(node.body) self.visit(node.body)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.emit('JUMP_FORWARD', lElse) self.emit('JUMP_FORWARD', lElse)
self.nextBlock(handlers) self.nextBlock(handlers)
last = len(node.handlers) - 1 last = len(node.handlers) - 1
for i in range(len(node.handlers)): for i in range(len(node.handlers)):
...@@ -361,7 +361,7 @@ class CodeGenerator: ...@@ -361,7 +361,7 @@ class CodeGenerator:
self.emit('COMPARE_OP', 'exception match') self.emit('COMPARE_OP', 'exception match')
next = self.newBlock() next = self.newBlock()
self.emit('JUMP_IF_FALSE', next) self.emit('JUMP_IF_FALSE', next)
self.nextBlock() self.nextBlock()
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('POP_TOP') self.emit('POP_TOP')
if target: if target:
...@@ -372,13 +372,13 @@ class CodeGenerator: ...@@ -372,13 +372,13 @@ class CodeGenerator:
self.visit(body) self.visit(body)
self.emit('JUMP_FORWARD', end) self.emit('JUMP_FORWARD', end)
if expr: if expr:
self.nextBlock(next) self.nextBlock(next)
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('END_FINALLY') self.emit('END_FINALLY')
if node.else_: if node.else_:
self.nextBlock(lElse) self.nextBlock(lElse)
self.visit(node.else_) self.visit(node.else_)
self.nextBlock(end) self.nextBlock(end)
def visitTryFinally(self, node): def visitTryFinally(self, node):
final = self.newBlock() final = self.newBlock()
...@@ -387,15 +387,15 @@ class CodeGenerator: ...@@ -387,15 +387,15 @@ class CodeGenerator:
self.visit(node.body) self.visit(node.body)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.emit('LOAD_CONST', None) self.emit('LOAD_CONST', None)
self.nextBlock(final) self.nextBlock(final)
self.visit(node.final) self.visit(node.final)
self.emit('END_FINALLY') self.emit('END_FINALLY')
# misc # misc
## def visitStmt(self, node): ## def visitStmt(self, node):
## # nothing to do except walk the children ## # nothing to do except walk the children
## pass ## pass
def visitDiscard(self, node): def visitDiscard(self, node):
self.visit(node.expr) self.visit(node.expr)
...@@ -405,12 +405,12 @@ class CodeGenerator: ...@@ -405,12 +405,12 @@ class CodeGenerator:
self.emit('LOAD_CONST', node.value) self.emit('LOAD_CONST', node.value)
def visitKeyword(self, node): def visitKeyword(self, node):
self.emit('LOAD_CONST', node.name) self.emit('LOAD_CONST', node.name)
self.visit(node.expr) self.visit(node.expr)
def visitGlobal(self, node): def visitGlobal(self, node):
# no code to generate # no code to generate
pass pass
def visitName(self, node): def visitName(self, node):
self.loadName(node.name) self.loadName(node.name)
...@@ -470,7 +470,7 @@ class CodeGenerator: ...@@ -470,7 +470,7 @@ class CodeGenerator:
def visitAssTuple(self, node): def visitAssTuple(self, node):
if findOp(node) != 'OP_DELETE': if findOp(node) != 'OP_DELETE':
self.emit('UNPACK_TUPLE', len(node.nodes)) self.emit('UNPACK_SEQUENCE', len(node.nodes))
for child in node.nodes: for child in node.nodes:
self.visit(child) self.visit(child)
...@@ -655,10 +655,10 @@ class CodeGenerator: ...@@ -655,10 +655,10 @@ class CodeGenerator:
set.emit('SET_LINENO', lineno) set.emit('SET_LINENO', lineno)
self.emit('BUILD_MAP', 0) self.emit('BUILD_MAP', 0)
for k, v in node.items: for k, v in node.items:
lineno2 = getattr(node, 'lineno', None) lineno2 = getattr(node, 'lineno', None)
if lineno2 is not None and lineno != lineno2: if lineno2 is not None and lineno != lineno2:
self.emit('SET_LINENO', lineno2) self.emit('SET_LINENO', lineno2)
lineno = lineno2 lineno = lineno2
self.emit('DUP_TOP') self.emit('DUP_TOP')
self.visit(v) self.visit(v)
self.emit('ROT_TWO') self.emit('ROT_TWO')
...@@ -669,9 +669,9 @@ class ModuleCodeGenerator(CodeGenerator): ...@@ -669,9 +669,9 @@ class ModuleCodeGenerator(CodeGenerator):
super_init = CodeGenerator.__init__ super_init = CodeGenerator.__init__
def __init__(self, filename): def __init__(self, filename):
# XXX <module> is ? in compile.c # XXX <module> is ? in compile.c
self.graph = pyassem.PyFlowGraph("<module>", filename) self.graph = pyassem.PyFlowGraph("<module>", filename)
self.super_init(filename) self.super_init(filename)
class FunctionCodeGenerator(CodeGenerator): class FunctionCodeGenerator(CodeGenerator):
super_init = CodeGenerator.__init__ super_init = CodeGenerator.__init__
...@@ -686,27 +686,27 @@ class FunctionCodeGenerator(CodeGenerator): ...@@ -686,27 +686,27 @@ class FunctionCodeGenerator(CodeGenerator):
klass.lambdaCount = klass.lambdaCount + 1 klass.lambdaCount = klass.lambdaCount + 1
else: else:
name = func.name name = func.name
args, hasTupleArg = generateArgList(func.argnames) args, hasTupleArg = generateArgList(func.argnames)
self.graph = pyassem.PyFlowGraph(name, filename, args, self.graph = pyassem.PyFlowGraph(name, filename, args,
optimized=1) optimized=1)
self.isLambda = isLambda self.isLambda = isLambda
self.super_init(filename) self.super_init(filename)
lnf = walk(func.code, LocalNameFinder(args), 0) lnf = walk(func.code, LocalNameFinder(args), 0)
self.locals.push(lnf.getLocals()) self.locals.push(lnf.getLocals())
if func.varargs: if func.varargs:
self.graph.setFlag(CO_VARARGS) self.graph.setFlag(CO_VARARGS)
if func.kwargs: if func.kwargs:
self.graph.setFlag(CO_VARKEYWORDS) self.graph.setFlag(CO_VARKEYWORDS)
self.set_lineno(func) self.set_lineno(func)
if hasTupleArg: if hasTupleArg:
self.generateArgUnpack(func.argnames) self.generateArgUnpack(func.argnames)
def finish(self): def finish(self):
self.graph.startExitBlock() self.graph.startExitBlock()
if not self.isLambda: if not self.isLambda:
self.emit('LOAD_CONST', None) self.emit('LOAD_CONST', None)
self.emit('RETURN_VALUE') self.emit('RETURN_VALUE')
def generateArgUnpack(self, args): def generateArgUnpack(self, args):
count = 0 count = 0
...@@ -714,30 +714,33 @@ class FunctionCodeGenerator(CodeGenerator): ...@@ -714,30 +714,33 @@ class FunctionCodeGenerator(CodeGenerator):
if type(arg) == types.TupleType: if type(arg) == types.TupleType:
self.emit('LOAD_FAST', '.nested%d' % count) self.emit('LOAD_FAST', '.nested%d' % count)
count = count + 1 count = count + 1
self.unpackTuple(arg) self.unpackSequence(arg)
def unpackTuple(self, tup): def unpackSequence(self, tup):
self.emit('UNPACK_TUPLE', len(tup)) self.emit('UNPACK_SEQUENCE', len(tup))
for elt in tup: for elt in tup:
if type(elt) == types.TupleType: if type(elt) == types.TupleType:
self.unpackTuple(elt) self.unpackSequence(elt)
else: else:
self.emit('STORE_FAST', elt) self.emit('STORE_FAST', elt)
unpackTuple = unpackSequence
class ClassCodeGenerator(CodeGenerator): class ClassCodeGenerator(CodeGenerator):
super_init = CodeGenerator.__init__ super_init = CodeGenerator.__init__
def __init__(self, klass, filename): def __init__(self, klass, filename):
self.graph = pyassem.PyFlowGraph(klass.name, filename, self.graph = pyassem.PyFlowGraph(klass.name, filename,
optimized=0) optimized=0)
self.super_init(filename) self.super_init(filename)
lnf = walk(klass.code, LocalNameFinder(), 0) lnf = walk(klass.code, LocalNameFinder(), 0)
self.locals.push(lnf.getLocals()) self.locals.push(lnf.getLocals())
self.graph.setFlag(CO_NEWLOCALS)
def finish(self): def finish(self):
self.graph.startExitBlock() self.graph.startExitBlock()
self.emit('LOAD_LOCALS') self.emit('LOAD_LOCALS')
self.emit('RETURN_VALUE') self.emit('RETURN_VALUE')
def generateArgList(arglist): def generateArgList(arglist):
...@@ -746,14 +749,14 @@ def generateArgList(arglist): ...@@ -746,14 +749,14 @@ def generateArgList(arglist):
extra = [] extra = []
count = 0 count = 0
for elt in arglist: for elt in arglist:
if type(elt) == types.StringType: if type(elt) == types.StringType:
args.append(elt) args.append(elt)
elif type(elt) == types.TupleType: elif type(elt) == types.TupleType:
args.append(TupleArg(count, elt)) args.append(TupleArg(count, elt))
count = count + 1 count = count + 1
extra.extend(misc.flatten(elt)) extra.extend(misc.flatten(elt))
else: else:
raise ValueError, "unexpect argument type:", elt raise ValueError, "unexpect argument type:", elt
return args + extra, count return args + extra, count
class LocalNameFinder: class LocalNameFinder:
...@@ -771,7 +774,7 @@ class LocalNameFinder: ...@@ -771,7 +774,7 @@ class LocalNameFinder:
return self.names return self.names
def visitDict(self, node): def visitDict(self, node):
pass pass
def visitGlobal(self, node): def visitGlobal(self, node):
for name in node.names: for name in node.names:
...@@ -781,7 +784,7 @@ class LocalNameFinder: ...@@ -781,7 +784,7 @@ class LocalNameFinder:
self.names.add(node.name) self.names.add(node.name)
def visitLambda(self, node): def visitLambda(self, node):
pass pass
def visitImport(self, node): def visitImport(self, node):
for name in node.names: for name in node.names:
...@@ -816,4 +819,4 @@ if __name__ == "__main__": ...@@ -816,4 +819,4 @@ if __name__ == "__main__":
import sys import sys
for file in sys.argv[1:]: for file in sys.argv[1:]:
compile(file) compile(file)
...@@ -38,7 +38,7 @@ class ASTVisitor: ...@@ -38,7 +38,7 @@ class ASTVisitor:
def __init__(self): def __init__(self):
self.node = None self.node = None
self._cache = {} self._cache = {}
def preorder(self, tree, visitor): def preorder(self, tree, visitor):
"""Do preorder walk of tree using visitor""" """Do preorder walk of tree using visitor"""
...@@ -47,7 +47,7 @@ class ASTVisitor: ...@@ -47,7 +47,7 @@ class ASTVisitor:
self._preorder(tree) self._preorder(tree)
def _preorder(self, node, *args): def _preorder(self, node, *args):
return apply(self.dispatch, (node,) + args) return apply(self.dispatch, (node,) + args)
def default(self, node, *args): def default(self, node, *args):
for child in node.getChildren(): for child in node.getChildren():
...@@ -56,18 +56,18 @@ class ASTVisitor: ...@@ -56,18 +56,18 @@ class ASTVisitor:
def dispatch(self, node, *args): def dispatch(self, node, *args):
self.node = node self.node = node
meth = self._cache.get(node.__class__, None) meth = self._cache.get(node.__class__, None)
className = node.__class__.__name__ className = node.__class__.__name__
if meth is None: if meth is None:
meth = getattr(self.visitor, 'visit' + className, self.default) meth = getattr(self.visitor, 'visit' + className, self.default)
self._cache[node.__class__] = meth self._cache[node.__class__] = meth
if self.VERBOSE > 0: if self.VERBOSE > 0:
if self.VERBOSE == 1: if self.VERBOSE == 1:
if meth == 0: if meth == 0:
print "dispatch", className print "dispatch", className
else: else:
print "dispatch", className, (meth and meth.__name__ or '') print "dispatch", className, (meth and meth.__name__ or '')
return apply(meth, (node,) + args) return apply(meth, (node,) + args)
class ExampleASTVisitor(ASTVisitor): class ExampleASTVisitor(ASTVisitor):
"""Prints examples of the nodes that aren't visited """Prints examples of the nodes that aren't visited
...@@ -80,11 +80,11 @@ class ExampleASTVisitor(ASTVisitor): ...@@ -80,11 +80,11 @@ class ExampleASTVisitor(ASTVisitor):
def dispatch(self, node, *args): def dispatch(self, node, *args):
self.node = node self.node = node
meth = self._cache.get(node.__class__, None) meth = self._cache.get(node.__class__, None)
className = node.__class__.__name__ className = node.__class__.__name__
if meth is None: if meth is None:
meth = getattr(self.visitor, 'visit' + className, 0) meth = getattr(self.visitor, 'visit' + className, 0)
self._cache[node.__class__] = meth self._cache[node.__class__] = meth
if self.VERBOSE > 1: if self.VERBOSE > 1:
print "dispatch", className, (meth and meth.__name__ or '') print "dispatch", className, (meth and meth.__name__ or '')
if meth: if meth:
...@@ -92,15 +92,15 @@ class ExampleASTVisitor(ASTVisitor): ...@@ -92,15 +92,15 @@ class ExampleASTVisitor(ASTVisitor):
elif self.VERBOSE > 0: elif self.VERBOSE > 0:
klass = node.__class__ klass = node.__class__
if not self.examples.has_key(klass): if not self.examples.has_key(klass):
self.examples[klass] = klass self.examples[klass] = klass
print print
print self.visitor print self.visitor
print klass print klass
for attr in dir(node): for attr in dir(node):
if attr[0] != '_': if attr[0] != '_':
print "\t", "%-12.12s" % attr, getattr(node, attr) print "\t", "%-12.12s" % attr, getattr(node, attr)
print print
return apply(self.default, (node,) + args) return apply(self.default, (node,) + args)
_walker = ASTVisitor _walker = ASTVisitor
def walk(tree, visitor, verbose=None): def walk(tree, visitor, verbose=None):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment