From: Jürg Billeter Date: Sun, 24 Jun 2012 18:50:44 +0000 (+0200) Subject: GAsync: Support async creation methods X-Git-Tag: 0.17.3~19 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6be0b6a71b3fa53a38eb0fc055d1ea952ec973f7;p=thirdparty%2Fvala.git GAsync: Support async creation methods Fixes bug 659886. --- diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala index ccaca21ee..7c4d28bc7 100644 --- a/codegen/valaccodebasemodule.vala +++ b/codegen/valaccodebasemodule.vala @@ -2129,6 +2129,14 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { } } + public CCodeExpression get_this_cexpression () { + if (is_in_coroutine ()) { + return new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "self"); + } else { + return new CCodeIdentifier ("self"); + } + } + public string get_local_cname (LocalVariable local) { var cname = get_variable_cname (local.name); if (is_in_coroutine ()) { @@ -3532,9 +3540,9 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { // do not call return as member cleanup and chain up to base finalizer // stil need to be executed ccode.add_goto ("_return"); + } else if (is_in_coroutine ()) { } else if (current_method is CreationMethod) { ccode.add_return (new CCodeIdentifier ("self")); - } else if (is_in_coroutine ()) { } else if (current_return_type is VoidType || current_return_type.is_real_non_null_struct_type ()) { // structs are returned via out parameter ccode.add_return (); @@ -4332,6 +4340,9 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { var params = m.get_parameters (); CCodeFunctionCall creation_call; + CCodeFunctionCall async_call = null; + CCodeFunctionCall finish_call = null; + generate_method_declaration (m, cfile); var cl = expr.type_reference.data_type as Class; @@ -4362,17 +4373,32 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { generate_type_declaration (expr.type_reference, cfile); - var carg_map = new HashMap (direct_hash, direct_equal); + var in_arg_map = new HashMap (direct_hash, direct_equal); + var out_arg_map = in_arg_map; + + if (m != null && m.coroutine) { + // async call + + async_call = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (m))); + finish_call = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_finish_name (m))); + + creation_call = finish_call; + + // output arguments used separately + out_arg_map = new HashMap (direct_hash, direct_equal); + // pass GAsyncResult stored in closure to finish function + out_arg_map.set (get_param_pos (0.1), new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_res_")); + } if (cl != null && !cl.is_compact) { - add_generic_type_arguments (carg_map, expr.type_reference.get_type_arguments (), expr); + add_generic_type_arguments (in_arg_map, expr.type_reference.get_type_arguments (), expr); } else if (cl != null && get_ccode_simple_generics (m)) { int type_param_index = 0; foreach (var type_arg in expr.type_reference.get_type_arguments ()) { if (requires_copy (type_arg)) { - carg_map.set (get_param_pos (-1 + 0.1 * type_param_index + 0.03), get_destroy0_func_expression (type_arg)); + in_arg_map.set (get_param_pos (-1 + 0.1 * type_param_index + 0.03), get_destroy0_func_expression (type_arg)); } else { - carg_map.set (get_param_pos (-1 + 0.1 * type_param_index + 0.03), new CCodeConstant ("NULL")); + in_arg_map.set (get_param_pos (-1 + 0.1 * type_param_index + 0.03), new CCodeConstant ("NULL")); } type_param_index++; } @@ -4385,11 +4411,18 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { Iterator params_it = params.iterator (); foreach (Expression arg in expr.get_argument_list ()) { CCodeExpression cexpr = get_cvalue (arg); + + var carg_map = in_arg_map; + Parameter param = null; if (params_it.next ()) { param = params_it.get (); ellipsis = param.ellipsis; if (!ellipsis) { + if (param.direction == ParameterDirection.OUT) { + carg_map = out_arg_map; + } + // g_array_new: element size if (cl == garray_type && param.name == "element_size") { var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof")); @@ -4450,39 +4483,80 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { // method can fail current_method_inner_error = true; // add &inner_error before the ellipsis arguments - carg_map.set (get_param_pos (-1), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression ("_inner_error_"))); + out_arg_map.set (get_param_pos (-1), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression ("_inner_error_"))); } if (ellipsis) { /* ensure variable argument list ends with NULL * except when using printf-style arguments */ if (m == null) { - carg_map.set (get_param_pos (-1, true), new CCodeConstant ("NULL")); + in_arg_map.set (get_param_pos (-1, true), new CCodeConstant ("NULL")); } else if (!m.printf_format && !m.scanf_format && get_ccode_sentinel (m) != "") { - carg_map.set (get_param_pos (-1, true), new CCodeConstant (get_ccode_sentinel (m))); + in_arg_map.set (get_param_pos (-1, true), new CCodeConstant (get_ccode_sentinel (m))); } } if ((st != null && !st.is_simple_type ()) && get_ccode_instance_pos (m) < 0) { // instance parameter is at the end in a struct creation method - carg_map.set (get_param_pos (-3), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance)); + out_arg_map.set (get_param_pos (-3), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance)); + } + + if (m != null && m.coroutine) { + if (expr.is_yield_expression) { + // asynchronous call + in_arg_map.set (get_param_pos (-1), new CCodeIdentifier (generate_ready_function (current_method))); + in_arg_map.set (get_param_pos (-0.9), new CCodeIdentifier ("_data_")); + } } // append C arguments in the right order - int last_pos = -1; + + int last_pos; int min_pos; - while (true) { - min_pos = -1; - foreach (int pos in carg_map.get_keys ()) { - if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { - min_pos = pos; + + if (async_call != creation_call) { + // don't append out arguments for .begin() calls + last_pos = -1; + while (true) { + min_pos = -1; + foreach (int pos in out_arg_map.get_keys ()) { + if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { + min_pos = pos; + } } + if (min_pos == -1) { + break; + } + creation_call.add_argument (out_arg_map.get (min_pos)); + last_pos = min_pos; } - if (min_pos == -1) { - break; + } + + if (async_call != null) { + last_pos = -1; + while (true) { + min_pos = -1; + foreach (int pos in in_arg_map.get_keys ()) { + if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { + min_pos = pos; + } + } + if (min_pos == -1) { + break; + } + async_call.add_argument (in_arg_map.get (min_pos)); + last_pos = min_pos; } - creation_call.add_argument (carg_map.get (min_pos)); - last_pos = min_pos; + } + + if (expr.is_yield_expression) { + // set state before calling async function to support immediate callbacks + int state = next_coroutine_state++; + + ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"), new CCodeConstant (state.to_string ())); + ccode.add_expression (async_call); + ccode.add_return (new CCodeConstant ("FALSE")); + ccode.add_label ("_state_%d".printf (state)); } creation_expr = creation_call; diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala index bb2704078..f164ae1c6 100644 --- a/codegen/valaccodemethodcallmodule.vala +++ b/codegen/valaccodemethodcallmodule.vala @@ -125,10 +125,10 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { if (m is CreationMethod && m.parent_symbol is Class) { if (context.profile == Profile.GOBJECT) { if (!((Class) m.parent_symbol).is_compact) { - ccall.add_argument (new CCodeIdentifier ("object_type")); + ccall.add_argument (get_variable_cexpression ("object_type")); } } else { - ccall.add_argument (new CCodeIdentifier ("self")); + ccall.add_argument (get_this_cexpression ()); } if (!current_class.is_compact) { @@ -158,7 +158,7 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { } } } else if (m is CreationMethod && m.parent_symbol is Struct) { - ccall.add_argument (new CCodeIdentifier ("self")); + ccall.add_argument (get_this_cexpression ()); } else if (m != null && m.get_type_parameters ().size > 0 && !get_ccode_has_generic_type_parameter (m) && !get_ccode_simple_generics (m) && (ccall != finish_call || expr.is_yield_expression)) { // generic method // don't add generic arguments for .end() calls @@ -217,7 +217,7 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { } else { // Accessing the method from within an instance method var k = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_GET_CLASS")); - k.add_argument (new CCodeIdentifier ("self")); + k.add_argument (get_this_cexpression ()); klass = k; } } else { @@ -282,12 +282,12 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { } generate_dynamic_method_wrapper ((DynamicMethod) m); } else if (m is CreationMethod && context.profile == Profile.GOBJECT && m.parent_symbol is Class) { - ccode.add_assignment (new CCodeIdentifier ("self"), new CCodeCastExpression (ccall, CCodeBaseModule.get_ccode_name (current_class) + "*")); + ccode.add_assignment (get_this_cexpression (), new CCodeCastExpression (ccall, CCodeBaseModule.get_ccode_name (current_class) + "*")); if (current_method.body.captured) { // capture self after setting it var ref_call = new CCodeFunctionCall (get_dup_func_expression (new ObjectType (current_class), expr.source_reference)); - ref_call.add_argument (new CCodeIdentifier ("self")); + ref_call.add_argument (get_this_cexpression ()); ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (current_method.body))), "self"), ref_call); } diff --git a/codegen/valaccodemethodmodule.vala b/codegen/valaccodemethodmodule.vala index 24085d280..5559898b2 100644 --- a/codegen/valaccodemethodmodule.vala +++ b/codegen/valaccodemethodmodule.vala @@ -552,15 +552,17 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { if (m is CreationMethod) { if (in_gobject_creation_method) { - ccode.add_declaration ("%s *".printf (get_ccode_name (current_type_symbol)), new CCodeVariableDeclarator.zero ("self", new CCodeConstant ("NULL"))); + if (!m.coroutine) { + ccode.add_declaration ("%s *".printf (get_ccode_name (current_type_symbol)), new CCodeVariableDeclarator.zero ("self", new CCodeConstant ("NULL"))); + } } else if (is_gtypeinstance_creation_method (m)) { var cl = (Class) m.parent_symbol; ccode.add_declaration (get_ccode_name (cl) + "*", new CCodeVariableDeclarator.zero ("self", new CCodeConstant ("NULL"))); if (cl.is_fundamental ()) { var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_type_create_instance")); - ccall.add_argument (new CCodeIdentifier ("object_type")); - ccode.add_assignment (new CCodeIdentifier ("self"), new CCodeCastExpression (ccall, get_ccode_name (cl) + "*")); + ccall.add_argument (get_variable_cexpression ("object_type")); + ccode.add_assignment (get_this_cexpression (), new CCodeCastExpression (ccall, get_ccode_name (cl) + "*")); /* type, dup func, and destroy func fields for generic types */ foreach (TypeParameter type_param in current_class.get_type_parameters ()) { @@ -584,19 +586,21 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { } } else if (current_type_symbol is Class) { var cl = (Class) m.parent_symbol; - ccode.add_declaration (get_ccode_name (cl) + "*", new CCodeVariableDeclarator ("self")); + if (!m.coroutine) { + ccode.add_declaration (get_ccode_name (cl) + "*", new CCodeVariableDeclarator ("self")); + } if (!((CreationMethod) m).chain_up) { // TODO implicitly chain up to base class as in add_object_creation var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0")); ccall.add_argument (new CCodeIdentifier (get_ccode_name (cl))); - ccode.add_assignment (new CCodeIdentifier ("self"), ccall); + ccode.add_assignment (get_this_cexpression (), ccall); } if (cl.base_class == null) { // derived compact classes do not have fields var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (get_ccode_lower_case_name (cl, null)))); - cinitcall.add_argument (new CCodeIdentifier ("self")); + cinitcall.add_argument (get_this_cexpression ()); ccode.add_expression (cinitcall); } } else { @@ -714,7 +718,7 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { ccode.add_expression (cfreeparams); } - if (current_type_symbol is Class) { + if (current_type_symbol is Class && !m.coroutine) { CCodeExpression cresult = new CCodeIdentifier ("self"); if (get_ccode_type (m) != null) { cresult = new CCodeCastExpression (cresult, get_ccode_type (m)); @@ -859,7 +863,7 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { cparam_map.set (get_param_pos (get_ccode_instance_pos (m)), instance_param); } else if (m.parent_symbol is Class && m is CreationMethod) { var cl = (Class) m.parent_symbol; - if (!cl.is_compact && vcall == null) { + if (!cl.is_compact && vcall == null && (direction & 1) == 1) { cparam_map.set (get_param_pos (get_ccode_instance_pos (m)), new CCodeParameter ("object_type", "GType")); } } else if (m.binding == MemberBinding.INSTANCE || (m.parent_symbol is Struct && m is CreationMethod)) { diff --git a/codegen/valagasyncmodule.vala b/codegen/valagasyncmodule.vala index bee5cebfa..05d1e422b 100644 --- a/codegen/valagasyncmodule.vala +++ b/codegen/valagasyncmodule.vala @@ -32,6 +32,10 @@ public class Vala.GAsyncModule : GSignalModule { data.add_field ("GAsyncResult*", "_res_"); data.add_field ("GSimpleAsyncResult*", "_async_result"); + if (m is CreationMethod) { + data.add_field ("GType", "object_type"); + } + if (m.binding == MemberBinding.INSTANCE) { var type_sym = (TypeSymbol) m.parent_symbol; if (type_sym is ObjectTypeSymbol) { @@ -205,7 +209,7 @@ public class Vala.GAsyncModule : GSignalModule { var create_result = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_new")); var cl = m.parent_symbol as Class; - if (m.binding == MemberBinding.INSTANCE && + if (!(m is CreationMethod) && m.binding == MemberBinding.INSTANCE && cl != null && cl.is_subtype_of (gobject_type)) { var gobject_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT")); gobject_cast.add_argument (new CCodeIdentifier ("self")); @@ -236,7 +240,9 @@ public class Vala.GAsyncModule : GSignalModule { set_op_res_call.add_argument (new CCodeIdentifier (get_ccode_real_name (m) + "_data_free")); ccode.add_expression (set_op_res_call); - if (m.binding == MemberBinding.INSTANCE) { + if (m is CreationMethod) { + ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "object_type"), new CCodeIdentifier ("object_type")); + } else if (m.binding == MemberBinding.INSTANCE) { var this_type = m.this_parameter.variable_type.copy (); this_type.value_owned = true; @@ -308,30 +314,62 @@ public class Vala.GAsyncModule : GSignalModule { return; } + var cl = m.parent_symbol as Class; + var asyncfunc = new CCodeFunction (get_ccode_name (m), "void"); var cparam_map = new HashMap (direct_hash, direct_equal); - cparam_map.set (get_param_pos (-1), new CCodeParameter ("_callback_", "GAsyncReadyCallback")); - cparam_map.set (get_param_pos (-0.9), new CCodeParameter ("_user_data_", "gpointer")); - - generate_cparameters (m, decl_space, cparam_map, asyncfunc, null, null, null, 1); + var carg_map = new HashMap (direct_hash, direct_equal); if (m.is_private_symbol ()) { asyncfunc.modifiers |= CCodeModifiers.STATIC; } - decl_space.add_function_declaration (asyncfunc); + // do not generate _new functions for creation methods of abstract classes + if (!(m is CreationMethod && cl != null && cl.is_abstract)) { + generate_cparameters (m, decl_space, cparam_map, asyncfunc, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")), 1); + + decl_space.add_function_declaration (asyncfunc); + } var finishfunc = new CCodeFunction (get_ccode_finish_name (m)); cparam_map = new HashMap (direct_hash, direct_equal); - cparam_map.set (get_param_pos (0.1), new CCodeParameter ("_res_", "GAsyncResult*")); - - generate_cparameters (m, decl_space, cparam_map, finishfunc, null, null, null, 2); + carg_map = new HashMap (direct_hash, direct_equal); if (m.is_private_symbol ()) { finishfunc.modifiers |= CCodeModifiers.STATIC; } - decl_space.add_function_declaration (finishfunc); + // do not generate _new functions for creation methods of abstract classes + if (!(m is CreationMethod && cl != null && cl.is_abstract)) { + generate_cparameters (m, decl_space, cparam_map, finishfunc, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")), 2); + + decl_space.add_function_declaration (finishfunc); + } + + if (m is CreationMethod && cl != null) { + // _construct function + var function = new CCodeFunction (get_ccode_real_name (m)); + + if (m.is_private_symbol ()) { + function.modifiers |= CCodeModifiers.STATIC; + } + + cparam_map = new HashMap (direct_hash, direct_equal); + generate_cparameters (m, decl_space, cparam_map, function, null, null, null, 1); + + decl_space.add_function_declaration (function); + + function = new CCodeFunction (get_ccode_finish_real_name (m)); + + if (m.is_private_symbol ()) { + function.modifiers |= CCodeModifiers.STATIC; + } + + cparam_map = new HashMap (direct_hash, direct_equal); + generate_cparameters (m, decl_space, cparam_map, function, null, null, null, 2); + + decl_space.add_function_declaration (function); + } } else { base.generate_method_declaration (m, decl_space); } @@ -386,6 +424,69 @@ public class Vala.GAsyncModule : GSignalModule { } } + public override void visit_creation_method (CreationMethod m) { + if (!m.coroutine) { + base.visit_creation_method (m); + } else { + push_line (m.source_reference); + + bool visible = !m.is_private_symbol (); + + visit_method (m); + + if (m.source_type == SourceFileType.FAST) { + return; + } + + // do not generate _new functions for creation methods of abstract classes + if (current_type_symbol is Class && !current_class.is_compact && !current_class.is_abstract) { + var vfunc = new CCodeFunction (get_ccode_name (m)); + + var cparam_map = new HashMap (direct_hash, direct_equal); + var carg_map = new HashMap (direct_hash, direct_equal); + + push_function (vfunc); + + var vcall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m))); + vcall.add_argument (new CCodeIdentifier (get_ccode_type_id (current_class))); + + generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall, 1); + ccode.add_expression (vcall); + + if (!visible) { + vfunc.modifiers |= CCodeModifiers.STATIC; + } + + pop_function (); + + cfile.add_function (vfunc); + + + vfunc = new CCodeFunction (get_ccode_finish_name (m)); + + cparam_map = new HashMap (direct_hash, direct_equal); + carg_map = new HashMap (direct_hash, direct_equal); + + push_function (vfunc); + + vcall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_finish_real_name (m))); + + generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall, 2); + ccode.add_return (vcall); + + if (!visible) { + vfunc.modifiers |= CCodeModifiers.STATIC; + } + + pop_function (); + + cfile.add_function (vfunc); + } + + pop_line (); + } + } + void generate_finish_function (Method m) { push_context (new EmitContext ()); @@ -406,7 +507,12 @@ public class Vala.GAsyncModule : GSignalModule { push_function (finishfunc); var return_type = m.return_type; - if (!(return_type is VoidType) && !return_type.is_real_non_null_struct_type ()) { + if (m is CreationMethod) { + var type_sym = (TypeSymbol) m.parent_symbol; + if (type_sym is ObjectTypeSymbol) { + ccode.add_declaration (get_ccode_name (type_sym) + "*", new CCodeVariableDeclarator ("result")); + } + } else if (!(return_type is VoidType) && !return_type.is_real_non_null_struct_type ()) { ccode.add_declaration (get_ccode_name (m.return_type), new CCodeVariableDeclarator ("result")); } @@ -443,7 +549,11 @@ public class Vala.GAsyncModule : GSignalModule { } emit_context.pop_symbol (); - if (return_type.is_real_non_null_struct_type ()) { + if (m is CreationMethod) { + ccode.add_assignment (new CCodeIdentifier ("result"), new CCodeMemberAccess.pointer (data_var, "self")); + ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "self"), new CCodeConstant ("NULL")); + ccode.add_return (new CCodeIdentifier ("result")); + } else if (return_type.is_real_non_null_struct_type ()) { // structs are returned via out parameter CCodeExpression cexpr = new CCodeMemberAccess.pointer (data_var, "result"); if (requires_copy (return_type)) { diff --git a/vala/valaobjectcreationexpression.vala b/vala/valaobjectcreationexpression.vala index a274de013..d96fb6503 100644 --- a/vala/valaobjectcreationexpression.vala +++ b/vala/valaobjectcreationexpression.vala @@ -43,6 +43,8 @@ public class Vala.ObjectCreationExpression : Expression { */ public MemberAccess member_name { get; set; } + public bool is_yield_expression { get; set; } + public bool struct_creation { get; set; } private List argument_list = new ArrayList (); diff --git a/vala/valaparser.vala b/vala/valaparser.vala index 099ed073a..caecb62da 100644 --- a/vala/valaparser.vala +++ b/vala/valaparser.vala @@ -978,16 +978,23 @@ public class Vala.Parser : CodeVisitor { Expression parse_yield_expression () throws ParseError { expect (TokenType.YIELD); + var expr = parse_expression (); var call = expr as MethodCall; - if (call == null) { + var object_creation = expr as ObjectCreationExpression; + if (call == null && object_creation == null) { Report.error (expr.source_reference, "syntax error, expected method call"); throw new ParseError.SYNTAX ("expected method call"); } - call.is_yield_expression = true; - return call; + if (call != null) { + call.is_yield_expression = true; + } else if (object_creation != null) { + object_creation.is_yield_expression = true; + } + + return expr; } Expression parse_sizeof_expression () throws ParseError {