Commit c6b07f36 authored by Robert Bradshaw's avatar Robert Bradshaw

Arbitrary nested closure support.

parent a504dae0
...@@ -83,6 +83,8 @@ pymoduledef_cname = pyrex_prefix + "moduledef" ...@@ -83,6 +83,8 @@ pymoduledef_cname = pyrex_prefix + "moduledef"
optional_args_cname = pyrex_prefix + "optional_args" optional_args_cname = pyrex_prefix + "optional_args"
import_star = pyrex_prefix + "import_star" import_star = pyrex_prefix + "import_star"
import_star_set = pyrex_prefix + "import_star_set" import_star_set = pyrex_prefix + "import_star_set"
cur_scope_cname = pyrex_prefix + "scope"
outer_scope_cname= pyrex_prefix + "outer_scope"
line_c_macro = "__LINE__" line_c_macro = "__LINE__"
......
...@@ -992,6 +992,12 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -992,6 +992,12 @@ class FuncDefNode(StatNode, BlockNode):
import Buffer import Buffer
lenv = self.local_scope lenv = self.local_scope
if lenv.is_closure_scope:
outer_scope_cname = "%s->%s" % (Naming.cur_scope_cname,
Naming.outer_scope_cname)
else:
outer_scope_cname = Naming.outer_scope_cname
lenv.mangle_closure_cnames(outer_scope_cname)
# Generate closure function definitions # Generate closure function definitions
self.body.generate_function_definitions(lenv, code) self.body.generate_function_definitions(lenv, code)
...@@ -1014,18 +1020,16 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1014,18 +1020,16 @@ class FuncDefNode(StatNode, BlockNode):
self.generate_function_header(code, self.generate_function_header(code,
with_pymethdef = env.is_py_class_scope or env.is_closure_scope) with_pymethdef = env.is_py_class_scope or env.is_closure_scope)
# ----- Local variable declarations # ----- Local variable declarations
# lenv.mangle_closure_cnames(Naming.cur_scope_cname) if lenv.is_closure_scope:
if self.needs_closure: code.put(lenv.scope_class.type.declaration_code(Naming.cur_scope_cname))
code.put(lenv.scope_class.type.declaration_code(lenv.closure_cname))
code.putln(";") code.putln(";")
else: if env.is_closure_scope and not lenv.is_closure_scope:
self.generate_argument_declarations(lenv, code) code.put(env.scope_class.type.declaration_code(Naming.outer_scope_cname))
code.put_var_declarations(lenv.var_entries) code.putln(";")
if env.is_closure_scope: self.generate_argument_declarations(lenv, code)
code.putln("%s = (%s)%s;" % ( for entry in lenv.var_entries:
env.scope_class.type.declaration_code(env.closure_cname), if not entry.in_closure:
env.scope_class.type.declaration_code(''), code.put_var_declaration(entry)
Naming.self_cname))
init = "" init = ""
if not self.return_type.is_void: if not self.return_type.is_void:
if self.return_type.is_pyobject: if self.return_type.is_pyobject:
...@@ -1052,7 +1056,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1052,7 +1056,7 @@ class FuncDefNode(StatNode, BlockNode):
# ----- Create closure scope object # ----- Create closure scope object
if self.needs_closure: if self.needs_closure:
code.putln("%s = (%s)%s->tp_new(%s, %s, NULL);" % ( code.putln("%s = (%s)%s->tp_new(%s, %s, NULL);" % (
lenv.closure_cname, Naming.cur_scope_cname,
lenv.scope_class.type.declaration_code(''), lenv.scope_class.type.declaration_code(''),
lenv.scope_class.type.typeptr_cname, lenv.scope_class.type.typeptr_cname,
lenv.scope_class.type.typeptr_cname, lenv.scope_class.type.typeptr_cname,
...@@ -1061,9 +1065,15 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1061,9 +1065,15 @@ class FuncDefNode(StatNode, BlockNode):
# The code below assumes the local variables are innitially NULL # The code below assumes the local variables are innitially NULL
# Note that it is unsafe to decref the scope at this point. # Note that it is unsafe to decref the scope at this point.
for entry in lenv.arg_entries + lenv.var_entries: for entry in lenv.arg_entries + lenv.var_entries:
if entry.type.is_pyobject: if entry.in_closure and entry.type.is_pyobject:
code.put_var_decref(entry) code.put_var_decref_clear(entry)
code.putln("%s = NULL;" % entry.cname) if env.is_closure_scope:
if lenv.is_closure_scope:
code.put_decref(outer_scope_cname, env.scope_class.type)
code.putln("%s = (%s)%s;" % (
outer_scope_cname,
env.scope_class.type.declaration_code(''),
Naming.self_cname))
# ----- Fetch arguments # ----- Fetch arguments
self.generate_argument_parsing_code(env, code) self.generate_argument_parsing_code(env, code)
# If an argument is assigned to in the body, we must # If an argument is assigned to in the body, we must
...@@ -1167,13 +1177,16 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1167,13 +1177,16 @@ class FuncDefNode(StatNode, BlockNode):
entry.xdecref_cleanup = 1 entry.xdecref_cleanup = 1
if self.needs_closure: if self.needs_closure:
code.put_decref(lenv.closure_cname, lenv.scope_class.type) code.put_decref(Naming.cur_scope_cname, lenv.scope_class.type)
else: for entry in lenv.var_entries:
code.put_var_decrefs(lenv.var_entries, used_only = 1) if entry.used and not entry.in_closure:
# Decref any increfed args code.put_var_decref(entry)
for entry in lenv.arg_entries: # Decref any increfed args
if entry.type.is_pyobject and lenv.control_flow.get_state((entry.name, 'source')) != 'arg': for entry in lenv.arg_entries:
code.put_var_decref(entry) if (entry.type.is_pyobject
and not entry.in_closure
and lenv.control_flow.get_state((entry.name, 'source')) != 'arg'):
code.put_var_decref(entry)
# ----- Return # ----- Return
# This code is duplicated in ModuleNode.generate_module_init_func # This code is duplicated in ModuleNode.generate_module_init_func
...@@ -1815,7 +1828,7 @@ class DefNode(FuncDefNode): ...@@ -1815,7 +1828,7 @@ class DefNode(FuncDefNode):
pymethdef_cname = self.entry.pymethdef_cname)) pymethdef_cname = self.entry.pymethdef_cname))
elif env.is_closure_scope: elif env.is_closure_scope:
self_object = ExprNodes.TempNode(self.pos, env.scope_class.type, env) self_object = ExprNodes.TempNode(self.pos, env.scope_class.type, env)
self_object.temp_cname = "((PyObject*)%s)" % env.closure_cname self_object.temp_cname = "((PyObject*)%s)" % Naming.cur_scope_cname
rhs = ExprNodes.PyCFunctionNode(self.pos, rhs = ExprNodes.PyCFunctionNode(self.pos,
self_object = self_object, self_object = self_object,
pymethdef_cname = self.entry.pymethdef_cname) pymethdef_cname = self.entry.pymethdef_cname)
...@@ -1870,7 +1883,7 @@ class DefNode(FuncDefNode): ...@@ -1870,7 +1883,7 @@ class DefNode(FuncDefNode):
if arg.is_generic: # or arg.needs_conversion: if arg.is_generic: # or arg.needs_conversion:
if arg.needs_conversion: if arg.needs_conversion:
code.putln("PyObject *%s = 0;" % arg.hdr_cname) code.putln("PyObject *%s = 0;" % arg.hdr_cname)
else: elif not entry.in_closure:
code.put_var_declaration(arg.entry) code.put_var_declaration(arg.entry)
def generate_keyword_list(self, code): def generate_keyword_list(self, code):
......
...@@ -864,6 +864,9 @@ class CreateClosureClasses(CythonTransform): ...@@ -864,6 +864,9 @@ class CreateClosureClasses(CythonTransform):
return node return node
def create_class_from_scope(self, node, target_module_scope): def create_class_from_scope(self, node, target_module_scope):
print node.entry.scope.is_closure_scope
as_name = "%s%s" % (Naming.closure_class_prefix, node.entry.cname) as_name = "%s%s" % (Naming.closure_class_prefix, node.entry.cname)
func_scope = node.local_scope func_scope = node.local_scope
...@@ -871,9 +874,17 @@ class CreateClosureClasses(CythonTransform): ...@@ -871,9 +874,17 @@ class CreateClosureClasses(CythonTransform):
pos = node.pos, defining = True, implementing = True) pos = node.pos, defining = True, implementing = True)
func_scope.scope_class = entry func_scope.scope_class = entry
class_scope = entry.type.scope class_scope = entry.type.scope
for entry in func_scope.entries.values(): if node.entry.scope.is_closure_scope:
cname = entry.cname[entry.cname.index('->')+2:] # everywhere but here they're attached to this class print "yes", class_scope
class_scope.declare_var(pos=node.pos, class_scope.declare_var(pos=node.pos,
name=Naming.outer_scope_cname, # this could conflict?
cname=Naming.outer_scope_cname,
type=node.entry.scope.scope_class.type,
is_cdef=True)
for entry in func_scope.entries.values():
# This is wasteful--we should do this later when we know which vars are actually being used inside...
cname = entry.cname
class_scope.declare_var(pos=entry.pos,
name=entry.name, name=entry.name,
cname=cname, cname=cname,
type=entry.type, type=entry.type,
...@@ -882,6 +893,7 @@ class CreateClosureClasses(CythonTransform): ...@@ -882,6 +893,7 @@ class CreateClosureClasses(CythonTransform):
def visit_FuncDefNode(self, node): def visit_FuncDefNode(self, node):
if node.needs_closure: if node.needs_closure:
self.create_class_from_scope(node, self.module_scope) self.create_class_from_scope(node, self.module_scope)
self.visitchildren(node)
return node return node
......
...@@ -19,6 +19,7 @@ try: ...@@ -19,6 +19,7 @@ try:
set set
except NameError: except NameError:
from sets import Set as set from sets import Set as set
import copy
possible_identifier = re.compile(ur"(?![0-9])\w+$", re.U).match possible_identifier = re.compile(ur"(?![0-9])\w+$", re.U).match
nice_identifier = re.compile('^[a-zA-Z0-0_]+$').match nice_identifier = re.compile('^[a-zA-Z0-0_]+$').match
...@@ -140,6 +141,7 @@ class Entry(object): ...@@ -140,6 +141,7 @@ class Entry(object):
is_arg = 0 is_arg = 0
is_local = 0 is_local = 0
in_closure = 0 in_closure = 0
from_closure = 0
is_declared_generic = 0 is_declared_generic = 0
is_readonly = 0 is_readonly = 0
func_cname = None func_cname = None
...@@ -499,14 +501,7 @@ class Scope(object): ...@@ -499,14 +501,7 @@ class Scope(object):
# Look up name in this scope or an enclosing one. # Look up name in this scope or an enclosing one.
# Return None if not found. # Return None if not found.
return (self.lookup_here(name) return (self.lookup_here(name)
or (self.outer_scope and self.outer_scope.lookup_from_inner(name)) or (self.outer_scope and self.outer_scope.lookup(name))
or None)
def lookup_from_inner(self, name):
# Look up name in this scope or an enclosing one.
# This is only called from enclosing scopes.
return (self.lookup_here(name)
or (self.outer_scope and self.outer_scope.lookup_from_inner(name))
or None) or None)
def lookup_here(self, name): def lookup_here(self, name):
...@@ -1013,7 +1008,7 @@ class ModuleScope(Scope): ...@@ -1013,7 +1008,7 @@ class ModuleScope(Scope):
var_entry.is_readonly = 1 var_entry.is_readonly = 1
entry.as_variable = var_entry entry.as_variable = var_entry
class LocalScope(Scope): class LocalScope(Scope):
def __init__(self, name, outer_scope): def __init__(self, name, outer_scope):
Scope.__init__(self, name, outer_scope, outer_scope) Scope.__init__(self, name, outer_scope, outer_scope)
...@@ -1056,21 +1051,39 @@ class LocalScope(Scope): ...@@ -1056,21 +1051,39 @@ class LocalScope(Scope):
entry = self.global_scope().lookup_target(name) entry = self.global_scope().lookup_target(name)
self.entries[name] = entry self.entries[name] = entry
def lookup_from_inner(self, name): def lookup(self, name):
entry = self.lookup_here(name) # Look up name in this scope or an enclosing one.
if entry: # Return None if not found.
entry.in_closure = 1 entry = Scope.lookup(self, name)
return entry if entry is not None:
else: if entry.scope is not self and entry.scope.is_closure_scope:
return (self.outer_scope and self.outer_scope.lookup_from_inner(name)) or None print "making new entry for", entry.cname, "in", self
# The actual c fragment for the different scopes differs
# on the outside and inside, so we make a new entry
entry.in_closure = True
# Would it be better to declare_var here?
inner_entry = Entry(entry.name, entry.cname, entry.type, entry.pos)
inner_entry.scope = self
inner_entry.is_variable = True
inner_entry.outer_entry = entry
inner_entry.from_closure = True
self.entries[name] = inner_entry
return inner_entry
return entry
def mangle_closure_cnames(self, scope_var): def mangle_closure_cnames(self, outer_scope_cname):
print "mangling", self
for entry in self.entries.values(): for entry in self.entries.values():
if entry.in_closure: print entry.name, entry.in_closure, entry.from_closure
if not hasattr(entry, 'orig_cname'): if entry.from_closure:
entry.orig_cname = entry.cname cname = entry.outer_entry.cname
entry.cname = scope_var + "->" + entry.cname if cname.startswith(Naming.cur_scope_cname):
cname = cname[len(Naming.cur_scope_cname)+2:]
entry.cname = "%s->%s" % (outer_scope_cname, cname)
elif entry.in_closure:
entry.original_cname = entry.cname
entry.cname = "%s->%s" % (Naming.cur_scope_cname, entry.cname)
print entry.cname
class ClosureScope(LocalScope): class ClosureScope(LocalScope):
...@@ -1085,8 +1098,9 @@ class ClosureScope(LocalScope): ...@@ -1085,8 +1098,9 @@ class ClosureScope(LocalScope):
# entry.in_closure = 1 # entry.in_closure = 1
# LocalScope.mangle_closure_cnames(self, scope_var) # LocalScope.mangle_closure_cnames(self, scope_var)
def mangle(self, prefix, name): # def mangle(self, prefix, name):
return "%s->%s" % (self.closure_cname, name) # return "%s->%s" % (self.cur_scope_cname, name)
# return "%s->%s" % (self.closure_cname, name)
def declare_pyfunction(self, name, pos): def declare_pyfunction(self, name, pos):
# Add an entry for a Python function. # Add an entry for a Python function.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment