Various sundry changes for 2.2 compatibility

Remove the option to have nested scopes or old LGB scopes.  This has a
large impact on the code base, by removing the need for two variants
of each CodeGenerator.

Add a get_module() method to CodeGenerator objects, used to get the
future features for the current module.

Set CO_GENERATOR, CO_GENERATOR_ALLOWED, and CO_FUTURE_DIVISION flags
as appropriate.

Attempt to fix the value of nlocals in newCodeObject(), assuming that
nlocals is 0 if CO_NEWLOCALS is not defined.
This commit is contained in:
Jeremy Hylton 2001-09-14 22:49:08 +00:00
parent 652a22437a
commit 1e99a77120
4 changed files with 188 additions and 274 deletions

View File

@ -361,6 +361,10 @@ class PyFlowGraph(FlowGraph):
if flag == CO_VARARGS: if flag == CO_VARARGS:
self.argcount = self.argcount - 1 self.argcount = self.argcount - 1
def checkFlag(self, flag):
if self.flags & flag:
return 1
def setFreeVars(self, names): def setFreeVars(self, names):
self.freevars = list(names) self.freevars = list(names)
@ -564,7 +568,7 @@ class PyFlowGraph(FlowGraph):
def newCodeObject(self): def newCodeObject(self):
assert self.stage == DONE assert self.stage == DONE
if self.flags == 0: if (self.flags & CO_NEWLOCALS) == 0:
nlocals = 0 nlocals = 0
else: else:
nlocals = len(self.varnames) nlocals = len(self.varnames)
@ -761,9 +765,6 @@ class StackDepthTracker:
('LOAD_', 1), ('LOAD_', 1),
] ]
# special cases:
# UNPACK_SEQUENCE, BUILD_TUPLE,
# BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
def UNPACK_SEQUENCE(self, count): def UNPACK_SEQUENCE(self, count):
return count-1 return count-1
def BUILD_TUPLE(self, count): def BUILD_TUPLE(self, count):

View File

@ -12,7 +12,7 @@ from compiler import ast, parse, walk
from compiler import pyassem, misc, future, symbols from compiler import pyassem, misc, future, symbols
from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL
from compiler.consts import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,\ from compiler.consts import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,\
CO_NESTED, CO_GENERATOR CO_NESTED, CO_GENERATOR, CO_GENERATOR_ALLOWED, CO_FUTURE_DIVISION
from compiler.pyassem import TupleArg from compiler.pyassem import TupleArg
# Do we have Python 1.x or Python 2.x? # Do we have Python 1.x or Python 2.x?
@ -34,6 +34,15 @@ EXCEPT = 2
TRY_FINALLY = 3 TRY_FINALLY = 3
END_FINALLY = 4 END_FINALLY = 4
class BlockStack(misc.Stack):
__super_init = misc.Stack.__init__
def __init__(self):
self.__super_init(self)
self.loop = None
def compile(filename, display=0): def compile(filename, display=0):
f = open(filename) f = open(filename)
buf = f.read() buf = f.read()
@ -52,8 +61,7 @@ class Module:
def compile(self, display=0): def compile(self, display=0):
tree = parse(self.source) tree = parse(self.source)
gen = NestedScopeModuleCodeGenerator(self.filename) gen = ModuleCodeGenerator(self.filename, tree)
walk(tree, gen, verbose=1)
if display: if display:
import pprint import pprint
print pprint.pprint(tree) print pprint.pprint(tree)
@ -154,6 +162,14 @@ class CodeGenerator:
self.last_lineno = None self.last_lineno = None
self._setupGraphDelegation() self._setupGraphDelegation()
# XXX set flags based on future features
futures = self.get_module().futures
for feature in futures:
if feature == "division":
self.graph.setFlag(CO_FUTURE_DIVISION)
elif feature == "generators":
self.graph.setFlag(CO_GENERATOR_ALLOWED)
def initClass(self): def initClass(self):
"""This method is called once for each class""" """This method is called once for each class"""
@ -185,6 +201,14 @@ class CodeGenerator:
else: else:
return name return name
def parseSymbols(self, tree):
s = symbols.SymbolVisitor()
walk(tree, s)
return s.scopes
def get_module(self):
raise RuntimeError, "should be implemented by subclasses"
# Next five methods handle name access # Next five methods handle name access
def isLocalName(self, name): def isLocalName(self, name):
@ -201,13 +225,22 @@ class CodeGenerator:
def _nameOp(self, prefix, name): def _nameOp(self, prefix, name):
name = self.mangle(name) name = self.mangle(name)
if not self.optimized: scope = self.scope.check_name(name)
self.emit(prefix + '_NAME', name) if scope == SC_LOCAL:
return if not self.optimized:
if self.isLocalName(name): self.emit(prefix + '_NAME', name)
self.emit(prefix + '_FAST', name) else:
self.emit(prefix + '_FAST', name)
elif scope == SC_GLOBAL:
if not self.optimized:
self.emit(prefix + '_NAME', name)
else:
self.emit(prefix + '_GLOBAL', name)
elif scope == SC_FREE or scope == SC_CELL:
self.emit(prefix + '_DEREF', name)
else: else:
self.emit(prefix + '_GLOBAL', name) raise RuntimeError, "unsupported scope for var %s: %d" % \
(name, scope)
def _implicitNameOp(self, prefix, name): def _implicitNameOp(self, prefix, name):
"""Emit name ops for names generated implicitly by for loops """Emit name ops for names generated implicitly by for loops
@ -249,6 +282,8 @@ class CodeGenerator:
ClassGen = None ClassGen = None
def visitModule(self, node): def visitModule(self, node):
self.scopes = self.parseSymbols(node)
self.scope = self.scopes[node]
self.emit('SET_LINENO', 0) self.emit('SET_LINENO', 0)
if node.doc: if node.doc:
self.emit('LOAD_CONST', node.doc) self.emit('LOAD_CONST', node.doc)
@ -270,17 +305,25 @@ class CodeGenerator:
def _visitFuncOrLambda(self, node, isLambda=0): def _visitFuncOrLambda(self, node, isLambda=0):
gen = self.FunctionGen(node, self.filename, self.scopes, isLambda, gen = self.FunctionGen(node, self.filename, self.scopes, isLambda,
self.class_name) self.class_name, self.get_module())
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)
self.emit('LOAD_CONST', gen) frees = gen.scope.get_free_vars()
self.emit('MAKE_FUNCTION', len(node.defaults)) if frees:
for name in frees:
self.emit('LOAD_CLOSURE', name)
self.emit('LOAD_CONST', gen)
self.emit('MAKE_CLOSURE', len(node.defaults))
else:
self.emit('LOAD_CONST', gen)
self.emit('MAKE_FUNCTION', len(node.defaults))
def visitClass(self, node): def visitClass(self, node):
gen = self.ClassGen(node, self.filename, self.scopes) gen = self.ClassGen(node, self.filename, self.scopes,
self.get_module())
if node.doc: if node.doc:
self.emit('LOAD_CONST', node.doc) self.emit('LOAD_CONST', node.doc)
self.storeName('__doc__') self.storeName('__doc__')
@ -291,8 +334,14 @@ class CodeGenerator:
for base in node.bases: for base in node.bases:
self.visit(base) self.visit(base)
self.emit('BUILD_TUPLE', len(node.bases)) self.emit('BUILD_TUPLE', len(node.bases))
frees = gen.scope.get_free_vars()
for name in frees:
self.emit('LOAD_CLOSURE', name)
self.emit('LOAD_CONST', gen) self.emit('LOAD_CONST', gen)
self.emit('MAKE_FUNCTION', 0) if frees:
self.emit('MAKE_CLOSURE', 0)
else:
self.emit('MAKE_FUNCTION', 0)
self.emit('CALL_FUNCTION', 0) self.emit('CALL_FUNCTION', 0)
self.emit('BUILD_CLASS') self.emit('BUILD_CLASS')
self.storeName(node.name) self.storeName(node.name)
@ -1017,121 +1066,34 @@ class CodeGenerator:
self.visit(k) self.visit(k)
self.emit('STORE_SUBSCR') self.emit('STORE_SUBSCR')
class NestedScopeCodeGenerator(CodeGenerator): class NestedScopeMixin:
__super_visitModule = CodeGenerator.visitModule """Defines initClass() for nested scoping (Python 2.2-compatible)"""
__super_visitClass = CodeGenerator.visitClass
__super__visitFuncOrLambda = CodeGenerator._visitFuncOrLambda
def parseSymbols(self, tree):
s = symbols.SymbolVisitor()
walk(tree, s)
return s.scopes
def visitModule(self, node):
self.scopes = self.parseSymbols(node)
self.scope = self.scopes[node]
self.__super_visitModule(node)
def _nameOp(self, prefix, name):
name = self.mangle(name)
scope = self.scope.check_name(name)
if scope == SC_LOCAL:
if not self.optimized:
self.emit(prefix + '_NAME', name)
else:
self.emit(prefix + '_FAST', name)
elif scope == SC_GLOBAL:
if not self.optimized:
self.emit(prefix + '_NAME', name)
else:
self.emit(prefix + '_GLOBAL', name)
elif scope == SC_FREE or scope == SC_CELL:
self.emit(prefix + '_DEREF', name)
else:
raise RuntimeError, "unsupported scope for var %s: %d" % \
(name, scope)
def _visitFuncOrLambda(self, node, isLambda=0):
gen = self.FunctionGen(node, self.filename, self.scopes, isLambda,
self.class_name)
walk(node.code, gen)
gen.finish()
self.set_lineno(node)
for default in node.defaults:
self.visit(default)
frees = gen.scope.get_free_vars()
if frees:
for name in frees:
self.emit('LOAD_CLOSURE', name)
self.emit('LOAD_CONST', gen)
self.emit('MAKE_CLOSURE', len(node.defaults))
else:
self.emit('LOAD_CONST', gen)
self.emit('MAKE_FUNCTION', len(node.defaults))
def visitClass(self, node):
gen = self.ClassGen(node, self.filename, self.scopes)
if node.doc:
self.emit('LOAD_CONST', node.doc)
self.storeName('__doc__')
walk(node.code, gen)
gen.finish()
self.set_lineno(node)
self.emit('LOAD_CONST', node.name)
for base in node.bases:
self.visit(base)
self.emit('BUILD_TUPLE', len(node.bases))
frees = gen.scope.get_free_vars()
for name in frees:
self.emit('LOAD_CLOSURE', name)
self.emit('LOAD_CONST', gen)
if frees:
self.emit('MAKE_CLOSURE', 0)
else:
self.emit('MAKE_FUNCTION', 0)
self.emit('CALL_FUNCTION', 0)
self.emit('BUILD_CLASS')
self.storeName(node.name)
class LGBScopeMixin:
"""Defines initClass() for Python 2.1-compatible scoping"""
def initClass(self): def initClass(self):
self.__class__.NameFinder = LocalNameFinder self.__class__.NameFinder = LocalNameFinder
self.__class__.FunctionGen = FunctionCodeGenerator self.__class__.FunctionGen = FunctionCodeGenerator
self.__class__.ClassGen = ClassCodeGenerator self.__class__.ClassGen = ClassCodeGenerator
class NestedScopeMixin: class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator):
"""Defines initClass() for nested scoping (Python 2.2-compatible)"""
def initClass(self):
self.__class__.NameFinder = LocalNameFinder
self.__class__.FunctionGen = NestedFunctionCodeGenerator
self.__class__.ClassGen = NestedClassCodeGenerator
class ModuleCodeGenerator(LGBScopeMixin, CodeGenerator):
__super_init = CodeGenerator.__init__ __super_init = CodeGenerator.__init__
scopes = None scopes = None
def __init__(self, filename): def __init__(self, filename, tree):
self.graph = pyassem.PyFlowGraph("<module>", filename) self.graph = pyassem.PyFlowGraph("<module>", filename)
self.futures = future.find_futures(tree)
self.__super_init(filename) self.__super_init(filename)
walk(tree, self)
class NestedScopeModuleCodeGenerator(NestedScopeMixin, def get_module(self):
NestedScopeCodeGenerator): return self
__super_init = CodeGenerator.__init__
def __init__(self, filename):
self.graph = pyassem.PyFlowGraph("<module>", filename)
self.__super_init(filename)
## self.graph.setFlag(CO_NESTED)
class AbstractFunctionCode: class AbstractFunctionCode:
optimized = 1 optimized = 1
lambdaCount = 0 lambdaCount = 0
def __init__(self, func, filename, scopes, isLambda, class_name): def __init__(self, func, filename, scopes, isLambda, class_name, mod):
self.class_name = class_name self.class_name = class_name
self.module = mod
if isLambda: if isLambda:
klass = FunctionCodeGenerator klass = FunctionCodeGenerator
name = "<lambda.%d>" % klass.lambdaCount name = "<lambda.%d>" % klass.lambdaCount
@ -1157,6 +1119,9 @@ class AbstractFunctionCode:
if hasTupleArg: if hasTupleArg:
self.generateArgUnpack(func.argnames) self.generateArgUnpack(func.argnames)
def get_module(self):
return self.module
def finish(self): def finish(self):
self.graph.startExitBlock() self.graph.startExitBlock()
if not self.isLambda: if not self.isLambda:
@ -1183,31 +1148,28 @@ class AbstractFunctionCode:
unpackTuple = unpackSequence unpackTuple = unpackSequence
class FunctionCodeGenerator(LGBScopeMixin, AbstractFunctionCode, class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode,
CodeGenerator): CodeGenerator):
super_init = CodeGenerator.__init__ # call be other init super_init = CodeGenerator.__init__ # call be other init
scopes = None scopes = None
class NestedFunctionCodeGenerator(AbstractFunctionCode,
NestedScopeMixin,
NestedScopeCodeGenerator):
super_init = NestedScopeCodeGenerator.__init__ # call be other init
__super_init = AbstractFunctionCode.__init__ __super_init = AbstractFunctionCode.__init__
def __init__(self, func, filename, scopes, isLambda, class_name): def __init__(self, func, filename, scopes, isLambda, class_name, mod):
self.scopes = scopes self.scopes = scopes
self.scope = scopes[func] self.scope = scopes[func]
self.__super_init(func, filename, scopes, isLambda, class_name) self.__super_init(func, filename, scopes, isLambda, class_name, mod)
self.graph.setFreeVars(self.scope.get_free_vars()) self.graph.setFreeVars(self.scope.get_free_vars())
self.graph.setCellVars(self.scope.get_cell_vars()) self.graph.setCellVars(self.scope.get_cell_vars())
if self.scope.generator is not None: if self.graph.checkFlag(CO_GENERATOR_ALLOWED):
self.graph.setFlag(CO_GENERATOR) if self.scope.generator is not None:
## self.graph.setFlag(CO_NESTED) self.graph.setFlag(CO_GENERATOR)
class AbstractClassCode: class AbstractClassCode:
def __init__(self, klass, filename, scopes): def __init__(self, klass, filename, scopes, module):
self.class_name = klass.name self.class_name = klass.name
self.module = module
self.graph = pyassem.PyFlowGraph(klass.name, filename, self.graph = pyassem.PyFlowGraph(klass.name, filename,
optimized=0, klass=1) optimized=0, klass=1)
self.super_init(filename) self.super_init(filename)
@ -1217,30 +1179,24 @@ class AbstractClassCode:
if klass.doc: if klass.doc:
self.setDocstring(klass.doc) self.setDocstring(klass.doc)
def _nameOp(self, prefix, name): def get_module(self):
name = self.mangle(name) return self.module
# Class namespaces are always unoptimized
self.emit(prefix + '_NAME', name)
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')
class ClassCodeGenerator(LGBScopeMixin, AbstractClassCode, CodeGenerator): class ClassCodeGenerator(NestedScopeMixin, AbstractClassCode, CodeGenerator):
super_init = CodeGenerator.__init__ super_init = CodeGenerator.__init__
scopes = None scopes = None
class NestedClassCodeGenerator(AbstractClassCode,
NestedScopeMixin,
NestedScopeCodeGenerator):
super_init = NestedScopeCodeGenerator.__init__ # call be other init
__super_init = AbstractClassCode.__init__ __super_init = AbstractClassCode.__init__
def __init__(self, klass, filename, scopes): def __init__(self, klass, filename, scopes, module):
self.scopes = scopes self.scopes = scopes
self.scope = scopes[klass] self.scope = scopes[klass]
self.__super_init(klass, filename, scopes) self.__super_init(klass, filename, scopes, module)
self.graph.setFreeVars(self.scope.get_free_vars()) self.graph.setFreeVars(self.scope.get_free_vars())
self.graph.setCellVars(self.scope.get_cell_vars()) self.graph.setCellVars(self.scope.get_cell_vars())
## self.graph.setFlag(CO_NESTED) ## self.graph.setFlag(CO_NESTED)

View File

@ -361,6 +361,10 @@ class PyFlowGraph(FlowGraph):
if flag == CO_VARARGS: if flag == CO_VARARGS:
self.argcount = self.argcount - 1 self.argcount = self.argcount - 1
def checkFlag(self, flag):
if self.flags & flag:
return 1
def setFreeVars(self, names): def setFreeVars(self, names):
self.freevars = list(names) self.freevars = list(names)
@ -564,7 +568,7 @@ class PyFlowGraph(FlowGraph):
def newCodeObject(self): def newCodeObject(self):
assert self.stage == DONE assert self.stage == DONE
if self.flags == 0: if (self.flags & CO_NEWLOCALS) == 0:
nlocals = 0 nlocals = 0
else: else:
nlocals = len(self.varnames) nlocals = len(self.varnames)
@ -761,9 +765,6 @@ class StackDepthTracker:
('LOAD_', 1), ('LOAD_', 1),
] ]
# special cases:
# UNPACK_SEQUENCE, BUILD_TUPLE,
# BUILD_LIST, CALL_FUNCTION, MAKE_FUNCTION, BUILD_SLICE
def UNPACK_SEQUENCE(self, count): def UNPACK_SEQUENCE(self, count):
return count-1 return count-1
def BUILD_TUPLE(self, count): def BUILD_TUPLE(self, count):

View File

@ -12,7 +12,7 @@ from compiler import ast, parse, walk
from compiler import pyassem, misc, future, symbols from compiler import pyassem, misc, future, symbols
from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL
from compiler.consts import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,\ from compiler.consts import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,\
CO_NESTED, CO_GENERATOR CO_NESTED, CO_GENERATOR, CO_GENERATOR_ALLOWED, CO_FUTURE_DIVISION
from compiler.pyassem import TupleArg from compiler.pyassem import TupleArg
# Do we have Python 1.x or Python 2.x? # Do we have Python 1.x or Python 2.x?
@ -34,6 +34,15 @@ EXCEPT = 2
TRY_FINALLY = 3 TRY_FINALLY = 3
END_FINALLY = 4 END_FINALLY = 4
class BlockStack(misc.Stack):
__super_init = misc.Stack.__init__
def __init__(self):
self.__super_init(self)
self.loop = None
def compile(filename, display=0): def compile(filename, display=0):
f = open(filename) f = open(filename)
buf = f.read() buf = f.read()
@ -52,8 +61,7 @@ class Module:
def compile(self, display=0): def compile(self, display=0):
tree = parse(self.source) tree = parse(self.source)
gen = NestedScopeModuleCodeGenerator(self.filename) gen = ModuleCodeGenerator(self.filename, tree)
walk(tree, gen, verbose=1)
if display: if display:
import pprint import pprint
print pprint.pprint(tree) print pprint.pprint(tree)
@ -154,6 +162,14 @@ class CodeGenerator:
self.last_lineno = None self.last_lineno = None
self._setupGraphDelegation() self._setupGraphDelegation()
# XXX set flags based on future features
futures = self.get_module().futures
for feature in futures:
if feature == "division":
self.graph.setFlag(CO_FUTURE_DIVISION)
elif feature == "generators":
self.graph.setFlag(CO_GENERATOR_ALLOWED)
def initClass(self): def initClass(self):
"""This method is called once for each class""" """This method is called once for each class"""
@ -185,6 +201,14 @@ class CodeGenerator:
else: else:
return name return name
def parseSymbols(self, tree):
s = symbols.SymbolVisitor()
walk(tree, s)
return s.scopes
def get_module(self):
raise RuntimeError, "should be implemented by subclasses"
# Next five methods handle name access # Next five methods handle name access
def isLocalName(self, name): def isLocalName(self, name):
@ -201,13 +225,22 @@ class CodeGenerator:
def _nameOp(self, prefix, name): def _nameOp(self, prefix, name):
name = self.mangle(name) name = self.mangle(name)
if not self.optimized: scope = self.scope.check_name(name)
self.emit(prefix + '_NAME', name) if scope == SC_LOCAL:
return if not self.optimized:
if self.isLocalName(name): self.emit(prefix + '_NAME', name)
self.emit(prefix + '_FAST', name) else:
self.emit(prefix + '_FAST', name)
elif scope == SC_GLOBAL:
if not self.optimized:
self.emit(prefix + '_NAME', name)
else:
self.emit(prefix + '_GLOBAL', name)
elif scope == SC_FREE or scope == SC_CELL:
self.emit(prefix + '_DEREF', name)
else: else:
self.emit(prefix + '_GLOBAL', name) raise RuntimeError, "unsupported scope for var %s: %d" % \
(name, scope)
def _implicitNameOp(self, prefix, name): def _implicitNameOp(self, prefix, name):
"""Emit name ops for names generated implicitly by for loops """Emit name ops for names generated implicitly by for loops
@ -249,6 +282,8 @@ class CodeGenerator:
ClassGen = None ClassGen = None
def visitModule(self, node): def visitModule(self, node):
self.scopes = self.parseSymbols(node)
self.scope = self.scopes[node]
self.emit('SET_LINENO', 0) self.emit('SET_LINENO', 0)
if node.doc: if node.doc:
self.emit('LOAD_CONST', node.doc) self.emit('LOAD_CONST', node.doc)
@ -270,17 +305,25 @@ class CodeGenerator:
def _visitFuncOrLambda(self, node, isLambda=0): def _visitFuncOrLambda(self, node, isLambda=0):
gen = self.FunctionGen(node, self.filename, self.scopes, isLambda, gen = self.FunctionGen(node, self.filename, self.scopes, isLambda,
self.class_name) self.class_name, self.get_module())
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)
self.emit('LOAD_CONST', gen) frees = gen.scope.get_free_vars()
self.emit('MAKE_FUNCTION', len(node.defaults)) if frees:
for name in frees:
self.emit('LOAD_CLOSURE', name)
self.emit('LOAD_CONST', gen)
self.emit('MAKE_CLOSURE', len(node.defaults))
else:
self.emit('LOAD_CONST', gen)
self.emit('MAKE_FUNCTION', len(node.defaults))
def visitClass(self, node): def visitClass(self, node):
gen = self.ClassGen(node, self.filename, self.scopes) gen = self.ClassGen(node, self.filename, self.scopes,
self.get_module())
if node.doc: if node.doc:
self.emit('LOAD_CONST', node.doc) self.emit('LOAD_CONST', node.doc)
self.storeName('__doc__') self.storeName('__doc__')
@ -291,8 +334,14 @@ class CodeGenerator:
for base in node.bases: for base in node.bases:
self.visit(base) self.visit(base)
self.emit('BUILD_TUPLE', len(node.bases)) self.emit('BUILD_TUPLE', len(node.bases))
frees = gen.scope.get_free_vars()
for name in frees:
self.emit('LOAD_CLOSURE', name)
self.emit('LOAD_CONST', gen) self.emit('LOAD_CONST', gen)
self.emit('MAKE_FUNCTION', 0) if frees:
self.emit('MAKE_CLOSURE', 0)
else:
self.emit('MAKE_FUNCTION', 0)
self.emit('CALL_FUNCTION', 0) self.emit('CALL_FUNCTION', 0)
self.emit('BUILD_CLASS') self.emit('BUILD_CLASS')
self.storeName(node.name) self.storeName(node.name)
@ -1017,121 +1066,34 @@ class CodeGenerator:
self.visit(k) self.visit(k)
self.emit('STORE_SUBSCR') self.emit('STORE_SUBSCR')
class NestedScopeCodeGenerator(CodeGenerator): class NestedScopeMixin:
__super_visitModule = CodeGenerator.visitModule """Defines initClass() for nested scoping (Python 2.2-compatible)"""
__super_visitClass = CodeGenerator.visitClass
__super__visitFuncOrLambda = CodeGenerator._visitFuncOrLambda
def parseSymbols(self, tree):
s = symbols.SymbolVisitor()
walk(tree, s)
return s.scopes
def visitModule(self, node):
self.scopes = self.parseSymbols(node)
self.scope = self.scopes[node]
self.__super_visitModule(node)
def _nameOp(self, prefix, name):
name = self.mangle(name)
scope = self.scope.check_name(name)
if scope == SC_LOCAL:
if not self.optimized:
self.emit(prefix + '_NAME', name)
else:
self.emit(prefix + '_FAST', name)
elif scope == SC_GLOBAL:
if not self.optimized:
self.emit(prefix + '_NAME', name)
else:
self.emit(prefix + '_GLOBAL', name)
elif scope == SC_FREE or scope == SC_CELL:
self.emit(prefix + '_DEREF', name)
else:
raise RuntimeError, "unsupported scope for var %s: %d" % \
(name, scope)
def _visitFuncOrLambda(self, node, isLambda=0):
gen = self.FunctionGen(node, self.filename, self.scopes, isLambda,
self.class_name)
walk(node.code, gen)
gen.finish()
self.set_lineno(node)
for default in node.defaults:
self.visit(default)
frees = gen.scope.get_free_vars()
if frees:
for name in frees:
self.emit('LOAD_CLOSURE', name)
self.emit('LOAD_CONST', gen)
self.emit('MAKE_CLOSURE', len(node.defaults))
else:
self.emit('LOAD_CONST', gen)
self.emit('MAKE_FUNCTION', len(node.defaults))
def visitClass(self, node):
gen = self.ClassGen(node, self.filename, self.scopes)
if node.doc:
self.emit('LOAD_CONST', node.doc)
self.storeName('__doc__')
walk(node.code, gen)
gen.finish()
self.set_lineno(node)
self.emit('LOAD_CONST', node.name)
for base in node.bases:
self.visit(base)
self.emit('BUILD_TUPLE', len(node.bases))
frees = gen.scope.get_free_vars()
for name in frees:
self.emit('LOAD_CLOSURE', name)
self.emit('LOAD_CONST', gen)
if frees:
self.emit('MAKE_CLOSURE', 0)
else:
self.emit('MAKE_FUNCTION', 0)
self.emit('CALL_FUNCTION', 0)
self.emit('BUILD_CLASS')
self.storeName(node.name)
class LGBScopeMixin:
"""Defines initClass() for Python 2.1-compatible scoping"""
def initClass(self): def initClass(self):
self.__class__.NameFinder = LocalNameFinder self.__class__.NameFinder = LocalNameFinder
self.__class__.FunctionGen = FunctionCodeGenerator self.__class__.FunctionGen = FunctionCodeGenerator
self.__class__.ClassGen = ClassCodeGenerator self.__class__.ClassGen = ClassCodeGenerator
class NestedScopeMixin: class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator):
"""Defines initClass() for nested scoping (Python 2.2-compatible)"""
def initClass(self):
self.__class__.NameFinder = LocalNameFinder
self.__class__.FunctionGen = NestedFunctionCodeGenerator
self.__class__.ClassGen = NestedClassCodeGenerator
class ModuleCodeGenerator(LGBScopeMixin, CodeGenerator):
__super_init = CodeGenerator.__init__ __super_init = CodeGenerator.__init__
scopes = None scopes = None
def __init__(self, filename): def __init__(self, filename, tree):
self.graph = pyassem.PyFlowGraph("<module>", filename) self.graph = pyassem.PyFlowGraph("<module>", filename)
self.futures = future.find_futures(tree)
self.__super_init(filename) self.__super_init(filename)
walk(tree, self)
class NestedScopeModuleCodeGenerator(NestedScopeMixin, def get_module(self):
NestedScopeCodeGenerator): return self
__super_init = CodeGenerator.__init__
def __init__(self, filename):
self.graph = pyassem.PyFlowGraph("<module>", filename)
self.__super_init(filename)
## self.graph.setFlag(CO_NESTED)
class AbstractFunctionCode: class AbstractFunctionCode:
optimized = 1 optimized = 1
lambdaCount = 0 lambdaCount = 0
def __init__(self, func, filename, scopes, isLambda, class_name): def __init__(self, func, filename, scopes, isLambda, class_name, mod):
self.class_name = class_name self.class_name = class_name
self.module = mod
if isLambda: if isLambda:
klass = FunctionCodeGenerator klass = FunctionCodeGenerator
name = "<lambda.%d>" % klass.lambdaCount name = "<lambda.%d>" % klass.lambdaCount
@ -1157,6 +1119,9 @@ class AbstractFunctionCode:
if hasTupleArg: if hasTupleArg:
self.generateArgUnpack(func.argnames) self.generateArgUnpack(func.argnames)
def get_module(self):
return self.module
def finish(self): def finish(self):
self.graph.startExitBlock() self.graph.startExitBlock()
if not self.isLambda: if not self.isLambda:
@ -1183,31 +1148,28 @@ class AbstractFunctionCode:
unpackTuple = unpackSequence unpackTuple = unpackSequence
class FunctionCodeGenerator(LGBScopeMixin, AbstractFunctionCode, class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode,
CodeGenerator): CodeGenerator):
super_init = CodeGenerator.__init__ # call be other init super_init = CodeGenerator.__init__ # call be other init
scopes = None scopes = None
class NestedFunctionCodeGenerator(AbstractFunctionCode,
NestedScopeMixin,
NestedScopeCodeGenerator):
super_init = NestedScopeCodeGenerator.__init__ # call be other init
__super_init = AbstractFunctionCode.__init__ __super_init = AbstractFunctionCode.__init__
def __init__(self, func, filename, scopes, isLambda, class_name): def __init__(self, func, filename, scopes, isLambda, class_name, mod):
self.scopes = scopes self.scopes = scopes
self.scope = scopes[func] self.scope = scopes[func]
self.__super_init(func, filename, scopes, isLambda, class_name) self.__super_init(func, filename, scopes, isLambda, class_name, mod)
self.graph.setFreeVars(self.scope.get_free_vars()) self.graph.setFreeVars(self.scope.get_free_vars())
self.graph.setCellVars(self.scope.get_cell_vars()) self.graph.setCellVars(self.scope.get_cell_vars())
if self.scope.generator is not None: if self.graph.checkFlag(CO_GENERATOR_ALLOWED):
self.graph.setFlag(CO_GENERATOR) if self.scope.generator is not None:
## self.graph.setFlag(CO_NESTED) self.graph.setFlag(CO_GENERATOR)
class AbstractClassCode: class AbstractClassCode:
def __init__(self, klass, filename, scopes): def __init__(self, klass, filename, scopes, module):
self.class_name = klass.name self.class_name = klass.name
self.module = module
self.graph = pyassem.PyFlowGraph(klass.name, filename, self.graph = pyassem.PyFlowGraph(klass.name, filename,
optimized=0, klass=1) optimized=0, klass=1)
self.super_init(filename) self.super_init(filename)
@ -1217,30 +1179,24 @@ class AbstractClassCode:
if klass.doc: if klass.doc:
self.setDocstring(klass.doc) self.setDocstring(klass.doc)
def _nameOp(self, prefix, name): def get_module(self):
name = self.mangle(name) return self.module
# Class namespaces are always unoptimized
self.emit(prefix + '_NAME', name)
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')
class ClassCodeGenerator(LGBScopeMixin, AbstractClassCode, CodeGenerator): class ClassCodeGenerator(NestedScopeMixin, AbstractClassCode, CodeGenerator):
super_init = CodeGenerator.__init__ super_init = CodeGenerator.__init__
scopes = None scopes = None
class NestedClassCodeGenerator(AbstractClassCode,
NestedScopeMixin,
NestedScopeCodeGenerator):
super_init = NestedScopeCodeGenerator.__init__ # call be other init
__super_init = AbstractClassCode.__init__ __super_init = AbstractClassCode.__init__
def __init__(self, klass, filename, scopes): def __init__(self, klass, filename, scopes, module):
self.scopes = scopes self.scopes = scopes
self.scope = scopes[klass] self.scope = scopes[klass]
self.__super_init(klass, filename, scopes) self.__super_init(klass, filename, scopes, module)
self.graph.setFreeVars(self.scope.get_free_vars()) self.graph.setFreeVars(self.scope.get_free_vars())
self.graph.setCellVars(self.scope.get_cell_vars()) self.graph.setCellVars(self.scope.get_cell_vars())
## self.graph.setFlag(CO_NESTED) ## self.graph.setFlag(CO_NESTED)