[vala] Add support for closures
- From: Jürg Billeter <juergbi src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [vala] Add support for closures
- Date: Tue, 15 Sep 2009 15:10:42 +0000 (UTC)
commit 1f14688e1d3508a2ba3c62322c362b468cea6bf8
Author: Jürg Billeter <j bitron ch>
Date: Tue Sep 15 17:09:02 2009 +0200
Add support for closures
Fixes bug 554781.
codegen/valaccodebasemodule.vala | 126 +++++++++++++++++++++++++++++-
codegen/valaccodedelegatemodule.vala | 22 ++++-
codegen/valaccodememberaccessmodule.vala | 4 +
codegen/valaccodemethodcallmodule.vala | 28 +++++--
codegen/valaccodemethodmodule.vala | 49 +++++++++++-
vala/valablock.vala | 2 +
vala/valalocalvariable.vala | 2 +
vala/valamemberaccess.vala | 10 ++-
vala/valamethod.vala | 2 +
vala/valasemanticanalyzer.vala | 7 ++
10 files changed, 234 insertions(+), 18 deletions(-)
---
diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala
index 3b60885..44b5e4b 100644
--- a/codegen/valaccodebasemodule.vala
+++ b/codegen/valaccodebasemodule.vala
@@ -132,6 +132,8 @@ internal class Vala.CCodeBaseModule : CCodeModule {
public bool in_static_or_class_context = false;
public bool current_method_inner_error = false;
public int next_coroutine_state = 1;
+ int next_block_id = 0;
+ Map<Block,int> block_map = new HashMap<Block,int> ();
public DataType void_type = new VoidType ();
public DataType bool_type;
@@ -1574,6 +1576,15 @@ internal class Vala.CCodeBaseModule : CCodeModule {
current_method_inner_error = old_method_inner_error;
}
+ public int get_block_id (Block b) {
+ int result = block_map[b];
+ if (result == 0) {
+ result = ++next_block_id;
+ block_map[b] = result;
+ }
+ return result;
+ }
+
public override void visit_block (Block b) {
var old_symbol = current_symbol;
current_symbol = b;
@@ -1586,7 +1597,104 @@ internal class Vala.CCodeBaseModule : CCodeModule {
}
var cblock = new CCodeBlock ();
-
+
+ if (b.captured) {
+ var parent_block = b.parent_symbol as Block;
+ while (parent_block != null && !parent_block.captured) {
+ parent_block = parent_block.parent_symbol as Block;
+ }
+
+ int block_id = get_block_id (b);
+ string struct_name = "Block%dData".printf (block_id);
+
+ var free_block = new CCodeBlock ();
+
+ var data = new CCodeStruct ("_" + struct_name);
+ data.add_field ("int", "ref_count");
+ if (parent_block != null) {
+ int parent_block_id = get_block_id (parent_block);
+
+ data.add_field ("Block%dData *".printf (parent_block_id), "_data%d_".printf (parent_block_id));
+
+ var unref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (parent_block_id)));
+ unref_call.add_argument (new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "_data%d_".printf (parent_block_id)));
+ free_block.add_statement (new CCodeExpressionStatement (unref_call));
+ } else if (current_method.binding == MemberBinding.INSTANCE) {
+ data.add_field ("%s *".printf (current_class.get_cname ()), "self");
+
+ var ma = new MemberAccess.simple ("this");
+ ma.symbol_reference = current_class;
+ free_block.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "self"), new ObjectType (current_class), ma)));
+ }
+ foreach (var local in local_vars) {
+ if (local.captured) {
+ data.add_field (local.variable_type.get_cname (), get_variable_cname (local.name) + local.variable_type.get_cdeclarator_suffix ());
+
+ if (local.variable_type is DelegateType) {
+ data.add_field ("gpointer", get_delegate_target_cname (get_variable_cname (local.name)));
+ }
+
+ if (requires_destroy (local.variable_type)) {
+ var ma = new MemberAccess.simple (local.name);
+ ma.symbol_reference = local;
+ free_block.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), get_variable_cname (local.name)), local.variable_type, ma)));
+ }
+ }
+ }
+
+ var typedef = new CCodeTypeDefinition ("struct _" + struct_name, new CCodeVariableDeclarator (struct_name));
+ source_declarations.add_type_declaration (typedef);
+ source_declarations.add_type_definition (data);
+
+ var data_alloc = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
+ data_alloc.add_argument (new CCodeIdentifier (struct_name));
+
+ var data_decl = new CCodeDeclaration (struct_name + "*");
+ data_decl.add_declarator (new CCodeVariableDeclarator ("_data%d_".printf (block_id), data_alloc));
+ cblock.add_statement (data_decl);
+
+ // initialize ref_count
+ cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "ref_count"), new CCodeIdentifier ("1"))));
+
+ if (parent_block != null) {
+ int parent_block_id = get_block_id (parent_block);
+
+ var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_ref".printf (parent_block_id)));
+ ref_call.add_argument (new CCodeIdentifier ("_data%d_".printf (parent_block_id)));
+
+ cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)), ref_call)));
+ } else if (current_method.binding == MemberBinding.INSTANCE) {
+ var ref_call = new CCodeFunctionCall (get_dup_func_expression (new ObjectType (current_class), b.source_reference));
+ ref_call.add_argument (new CCodeIdentifier ("self"));
+
+ cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "self"), ref_call)));
+ }
+
+ var data_free = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_free"));
+ data_free.add_argument (new CCodeIdentifier (struct_name));
+ data_free.add_argument (new CCodeIdentifier ("data"));
+ free_block.add_statement (new CCodeExpressionStatement (data_free));
+
+ // create ref/unref functions
+ var ref_fun = new CCodeFunction ("block%d_data_ref".printf (block_id), struct_name + "*");
+ ref_fun.add_parameter (new CCodeFormalParameter ("data", struct_name + "*"));
+ ref_fun.modifiers = CCodeModifiers.STATIC;
+ source_declarations.add_type_member_declaration (ref_fun.copy ());
+ ref_fun.block = new CCodeBlock ();
+ ref_fun.block.add_statement (new CCodeExpressionStatement (new CCodeUnaryExpression (CCodeUnaryOperator.PREFIX_INCREMENT, new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "ref_count"))));
+ ref_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("data")));
+ source_type_member_definition.append (ref_fun);
+
+ var unref_fun = new CCodeFunction ("block%d_data_unref".printf (block_id), struct_name + "*");
+ unref_fun.add_parameter (new CCodeFormalParameter ("data", struct_name + "*"));
+ unref_fun.modifiers = CCodeModifiers.STATIC;
+ source_declarations.add_type_member_declaration (unref_fun.copy ());
+ unref_fun.block = new CCodeBlock ();
+ var dec = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeUnaryExpression (CCodeUnaryOperator.PREFIX_DECREMENT, new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "ref_count")), new CCodeConstant ("0"));
+ unref_fun.block.add_statement (new CCodeIfStatement (dec, free_block));
+ source_type_member_definition.append (unref_fun);
+ }
+
foreach (CodeNode stmt in b.get_statements ()) {
if (stmt.error) {
continue;
@@ -1602,7 +1710,7 @@ internal class Vala.CCodeBaseModule : CCodeModule {
}
foreach (LocalVariable local in local_vars) {
- if (!local.floating && requires_destroy (local.variable_type)) {
+ if (!local.floating && !local.captured && requires_destroy (local.variable_type)) {
var ma = new MemberAccess.simple (local.name);
ma.symbol_reference = local;
cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma)));
@@ -1620,6 +1728,14 @@ internal class Vala.CCodeBaseModule : CCodeModule {
}
}
+ if (b.captured) {
+ int block_id = get_block_id (b);
+
+ var data_unref = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (block_id)));
+ data_unref.add_argument (new CCodeIdentifier ("_data%d_".printf (block_id)));
+ cblock.add_statement (new CCodeExpressionStatement (data_unref));
+ }
+
b.ccodenode = cblock;
current_symbol = old_symbol;
@@ -1778,7 +1894,11 @@ internal class Vala.CCodeBaseModule : CCodeModule {
pre_statement_fragment = null;
}
- if (current_method != null && current_method.coroutine) {
+ if (local.captured) {
+ if (local.initializer != null) {
+ cfrag.append (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (get_block_id ((Block) local.parent_symbol))), get_variable_cname (local.name)), rhs)));
+ }
+ } else if (current_method != null && current_method.coroutine) {
closure_struct.add_field (local.variable_type.get_cname (), get_variable_cname (local.name) + local.variable_type.get_cdeclarator_suffix ());
if (local.initializer != null) {
diff --git a/codegen/valaccodedelegatemodule.vala b/codegen/valaccodedelegatemodule.vala
index 7fb93a4..357e1dc 100644
--- a/codegen/valaccodedelegatemodule.vala
+++ b/codegen/valaccodedelegatemodule.vala
@@ -136,7 +136,13 @@ internal class Vala.CCodeDelegateModule : CCodeArrayModule {
var invocation_expr = (MethodCall) delegate_expr;
return invocation_expr.delegate_target;
} else if (delegate_expr is LambdaExpression) {
- if (get_this_type () != null || in_constructor) {
+ var closure_block = current_symbol as Block;
+ while (closure_block != null && !closure_block.captured) {
+ closure_block = closure_block.parent_symbol as Block;
+ }
+ if (closure_block != null) {
+ return new CCodeIdentifier ("_data%d_".printf (get_block_id (closure_block)));
+ } else if (get_this_type () != null || in_constructor) {
return new CCodeIdentifier ("self");
} else {
return new CCodeConstant ("NULL");
@@ -157,11 +163,17 @@ internal class Vala.CCodeDelegateModule : CCodeArrayModule {
}
} else if (delegate_expr.symbol_reference is LocalVariable) {
var local = (LocalVariable) delegate_expr.symbol_reference;
- var target_expr = new CCodeIdentifier (get_delegate_target_cname (local.name));
- if (is_out) {
- return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, target_expr);
+ if (local.captured) {
+ // captured variables are stored on the heap
+ var block = (Block) local.parent_symbol;
+ return new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (get_block_id (block))), get_delegate_target_cname (local.name));
} else {
- return target_expr;
+ var target_expr = new CCodeIdentifier (get_delegate_target_cname (local.name));
+ if (is_out) {
+ return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, target_expr);
+ } else {
+ return target_expr;
+ }
}
} else if (delegate_expr.symbol_reference is Field) {
var field = (Field) delegate_expr.symbol_reference;
diff --git a/codegen/valaccodememberaccessmodule.vala b/codegen/valaccodememberaccessmodule.vala
index d698a03..027d4a2 100644
--- a/codegen/valaccodememberaccessmodule.vala
+++ b/codegen/valaccodememberaccessmodule.vala
@@ -344,6 +344,10 @@ internal class Vala.CCodeMemberAccessModule : CCodeControlFlowModule {
if (local.is_result) {
// used in postconditions
expr.ccodenode = new CCodeIdentifier ("result");
+ } else if (local.captured) {
+ // captured variables are stored on the heap
+ var block = (Block) local.parent_symbol;
+ expr.ccodenode = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (get_block_id (block))), get_variable_cname (local.name));
} else {
expr.ccodenode = get_variable_cexpression (local.name);
}
diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala
index 619ac36..efff5c1 100644
--- a/codegen/valaccodemethodcallmodule.vala
+++ b/codegen/valaccodemethodcallmodule.vala
@@ -295,13 +295,27 @@ internal class Vala.CCodeMethodCallModule : CCodeAssignmentModule {
var delegate_method = arg.symbol_reference as Method;
var lambda = arg as LambdaExpression;
var arg_ma = arg as MemberAccess;
- if (lambda != null && get_this_type () != null) {
- // type of delegate target is same as `this'
- // for lambda expressions in instance methods
- var ref_call = new CCodeFunctionCall (get_dup_func_expression (get_this_type (), arg.source_reference));
- ref_call.add_argument (delegate_target);
- delegate_target = ref_call;
- delegate_target_destroy_notify = get_destroy_func_expression (get_this_type ());
+ if (lambda != null) {
+ if (delegate_method.closure) {
+ var closure_block = current_symbol as Block;
+ while (closure_block != null && !closure_block.captured) {
+ closure_block = closure_block.parent_symbol as Block;
+ }
+ int block_id = get_block_id (closure_block);
+ var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_ref".printf (block_id)));
+ ref_call.add_argument (delegate_target);
+ delegate_target = ref_call;
+ delegate_target_destroy_notify = new CCodeIdentifier ("block%d_data_unref".printf (block_id));
+ } else if (get_this_type () != null) {
+ // type of delegate target is same as `this'
+ // for lambda expressions in instance methods
+ var ref_call = new CCodeFunctionCall (get_dup_func_expression (get_this_type (), arg.source_reference));
+ ref_call.add_argument (delegate_target);
+ delegate_target = ref_call;
+ delegate_target_destroy_notify = get_destroy_func_expression (get_this_type ());
+ } else {
+ delegate_target_destroy_notify = new CCodeConstant ("NULL");
+ }
} else if (delegate_method != null && delegate_method.binding == MemberBinding.INSTANCE
&& arg_ma != null && arg_ma.inner != null && arg_ma.inner.value_type.data_type != null
&& arg_ma.inner.value_type.data_type.is_reference_counting ()) {
diff --git a/codegen/valaccodemethodmodule.vala b/codegen/valaccodemethodmodule.vala
index ff471c3..670a2a9 100644
--- a/codegen/valaccodemethodmodule.vala
+++ b/codegen/valaccodemethodmodule.vala
@@ -398,7 +398,44 @@ internal class Vala.CCodeMethodModule : CCodeStructModule {
source_type_member_definition.append (co_function);
}
- if (m.parent_symbol is Class && !m.coroutine) {
+ if (m.closure) {
+ // add variables for parent closure blocks
+ // as closures only have one parameter for the innermost closure block
+ var closure_block = m.parent_symbol as Block;
+ while (closure_block != null && !closure_block.captured) {
+ closure_block = closure_block.parent_symbol as Block;
+ }
+ int block_id = get_block_id (closure_block);
+ while (true) {
+ var parent_closure_block = closure_block.parent_symbol as Block;
+ while (parent_closure_block != null && !parent_closure_block.captured) {
+ parent_closure_block = parent_closure_block.parent_symbol as Block;
+ }
+ if (parent_closure_block == null) {
+ break;
+ }
+ int parent_block_id = get_block_id (parent_closure_block);
+
+ var parent_data = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id));
+ var cdecl = new CCodeDeclaration ("Block%dData*".printf (parent_block_id));
+ cdecl.add_declarator (new CCodeVariableDeclarator ("_data%d_".printf (parent_block_id), parent_data));
+
+ cinit.append (cdecl);
+
+ closure_block = parent_closure_block;
+ block_id = parent_block_id;
+ }
+
+ // add self variable for closures
+ // as closures have block data parameter
+ if (m.binding == MemberBinding.INSTANCE) {
+ var cself = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "self");
+ var cdecl = new CCodeDeclaration ("%s *".printf (current_class.get_cname ()));
+ cdecl.add_declarator (new CCodeVariableDeclarator ("self", cself));
+
+ cinit.append (cdecl);
+ }
+ } else if (m.parent_symbol is Class && !m.coroutine) {
var cl = (Class) m.parent_symbol;
if (m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
Method base_method;
@@ -720,7 +757,15 @@ internal class Vala.CCodeMethodModule : CCodeStructModule {
}
public override void generate_cparameters (Method m, CCodeDeclarationSpace decl_space, Map<int,CCodeFormalParameter> cparam_map, CCodeFunction func, CCodeFunctionDeclarator? vdeclarator = null, Map<int,CCodeExpression>? carg_map = null, CCodeFunctionCall? vcall = null, int direction = 3) {
- if (m.parent_symbol is Class && m is CreationMethod) {
+ if (m.closure) {
+ var closure_block = m.parent_symbol as Block;
+ while (closure_block != null && !closure_block.captured) {
+ closure_block = closure_block.parent_symbol as Block;
+ }
+ int block_id = get_block_id (closure_block);
+ var instance_param = new CCodeFormalParameter ("_data%d_".printf (block_id), "Block%dData*".printf (block_id));
+ cparam_map.set (get_param_pos (m.cinstance_parameter_position), instance_param);
+ } else if (m.parent_symbol is Class && m is CreationMethod) {
var cl = (Class) m.parent_symbol;
if (!cl.is_compact && vcall == null) {
cparam_map.set (get_param_pos (m.cinstance_parameter_position), new CCodeFormalParameter ("object_type", "GType"));
diff --git a/vala/valablock.vala b/vala/valablock.vala
index 9c99ec8..e9dfe2d 100644
--- a/vala/valablock.vala
+++ b/vala/valablock.vala
@@ -33,6 +33,8 @@ public class Vala.Block : Symbol, Statement {
*/
public bool contains_jump_statement { get; set; }
+ public bool captured { get; set; }
+
private Gee.List<Statement> statement_list = new ArrayList<Statement> ();
private Gee.List<LocalVariable> local_variables = new ArrayList<LocalVariable> ();
diff --git a/vala/valalocalvariable.vala b/vala/valalocalvariable.vala
index c404b2d..8d600cb 100644
--- a/vala/valalocalvariable.vala
+++ b/vala/valalocalvariable.vala
@@ -61,6 +61,8 @@ public class Vala.LocalVariable : Symbol {
*/
public bool floating { get; set; }
+ public bool captured { get; set; }
+
private Expression? _initializer;
private DataType? _variable_type;
diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala
index 8413f34..1e076e4 100644
--- a/vala/valamemberaccess.vala
+++ b/vala/valamemberaccess.vala
@@ -419,7 +419,15 @@ public class Vala.MemberAccess : Expression {
return false;
}
- if (member is Field) {
+ if (member is LocalVariable) {
+ var local = (LocalVariable) member;
+ var block = (Block) local.parent_symbol;
+ if (analyzer.find_parent_method (block) != analyzer.current_method) {
+ local.captured = true;
+ block.captured = true;
+ analyzer.current_method.closure = true;
+ }
+ } else if (member is Field) {
var f = (Field) member;
access = f.access;
instance = (f.binding == MemberBinding.INSTANCE);
diff --git a/vala/valamethod.vala b/vala/valamethod.vala
index a3cc995..b0ecc39 100644
--- a/vala/valamethod.vala
+++ b/vala/valamethod.vala
@@ -222,6 +222,8 @@ public class Vala.Method : Member {
public weak Signal signal_reference { get; set; }
+ public bool closure { get; set; }
+
public bool coroutine { get; set; }
public bool is_async_callback { get; set; }
diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala
index 1527d02..103344b 100644
--- a/vala/valasemanticanalyzer.vala
+++ b/vala/valasemanticanalyzer.vala
@@ -786,6 +786,13 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
return null;
}
+ public Method? find_parent_method (Symbol sym) {
+ while (sym is Block) {
+ sym = sym.parent_symbol;
+ }
+ return sym as Method;
+ }
+
public bool is_in_constructor () {
var sym = current_symbol;
while (sym != null) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]