mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
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.
This commit is contained in:
parent
81f7eb6c6b
commit
46cc7c0f7b
6 changed files with 692 additions and 690 deletions
|
@ -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:
|
||||||
_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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
|
|
||||||
_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):
|
||||||
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):
|
||||||
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:
|
||||||
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:
|
||||||
]
|
]
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
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):
|
||||||
|
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
# 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:
|
||||||
# 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:
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
|
|
||||||
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:
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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:
|
||||||
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:
|
||||||
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__":
|
||||||
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:
|
||||||
|
|
||||||
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:
|
||||||
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:
|
||||||
|
|
||||||
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):
|
||||||
|
|
||||||
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):
|
||||||
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
|
||||||
|
|
||||||
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:
|
||||||
_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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
|
|
||||||
_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):
|
||||||
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):
|
||||||
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:
|
||||||
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:
|
||||||
]
|
]
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
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):
|
||||||
|
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
# 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:
|
||||||
# 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:
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
|
|
||||||
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:
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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:
|
||||||
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:
|
||||||
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__":
|
||||||
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:
|
||||||
|
|
||||||
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:
|
||||||
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:
|
||||||
|
|
||||||
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):
|
||||||
|
|
||||||
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):
|
||||||
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):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue