bbe810496897d385e91dcce9eb567cfa308d813b
[roobuilder] / src / codegen / valagasyncmodule.vala
1 /* valagasyncmodule.vala
2  *
3  * Copyright (C) 2008-2012  Jürg Billeter
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18  *
19  * Author:
20  *      Jürg Billeter <j@bitron.ch>
21  */
22
23 using GLib;
24
25 public class Vala.GAsyncModule : GtkModule {
26         CCodeStruct generate_data_struct (Method m) {
27                 string dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
28                 var data = new CCodeStruct ("_" + dataname);
29
30                 data.add_field ("int", "_state_");
31                 data.add_field ("GObject*", "_source_object_");
32                 data.add_field ("GAsyncResult*", "_res_");
33                 data.add_field ("GTask*", "_async_result");
34
35                 if (m is CreationMethod) {
36                         data.add_field ("GType", "object_type");
37                 }
38
39                 if (m.binding == MemberBinding.INSTANCE) {
40                         var type_sym = (TypeSymbol) m.parent_symbol;
41                         if (type_sym is ObjectTypeSymbol) {
42                                 data.add_field (get_ccode_name (type_sym) + "*", "self");
43                         } else {
44                                 data.add_field (get_ccode_name (type_sym), "self");
45                         }
46                 }
47
48                 foreach (Parameter param in m.get_parameters ()) {
49                         var param_type = param.variable_type.copy ();
50                         param_type.value_owned = true;
51                         data.add_field (get_ccode_name (param_type), get_ccode_name (param), 0, get_ccode_declarator_suffix (param_type));
52
53                         if (param.variable_type is ArrayType) {
54                                 var array_type = (ArrayType) param.variable_type;
55                                 if (get_ccode_array_length (param) && !((ArrayType) array_type).fixed_length) {
56                                         var length_ctype = get_ccode_array_length_type (param);
57                                         for (int dim = 1; dim <= array_type.rank; dim++) {
58                                                 data.add_field (length_ctype, get_variable_array_length_cname (param, dim));
59                                         }
60                                 }
61                         } else if (param.variable_type is DelegateType) {
62                                 var deleg_type = (DelegateType) param.variable_type;
63                                 if (deleg_type.delegate_symbol.has_target) {
64                                         data.add_field (get_ccode_name (delegate_target_type), get_ccode_delegate_target_name (param));
65                                         if (deleg_type.is_disposable ()) {
66                                                 data.add_field (get_ccode_name (delegate_target_destroy_type), get_ccode_delegate_target_destroy_notify_name (param));
67                                         }
68                                 }
69                         }
70                 }
71
72                 foreach (var type_param in m.get_type_parameters ()) {
73                         data.add_field ("GType", get_ccode_type_id (type_param));
74                         data.add_field ("GBoxedCopyFunc", get_ccode_copy_function (type_param));
75                         data.add_field ("GDestroyNotify", get_ccode_destroy_function (type_param));
76                 }
77
78                 if (!(m.return_type is VoidType)) {
79                         data.add_field (get_ccode_name (m.return_type), "result");
80                         if (m.return_type is ArrayType) {
81                                 var array_type = (ArrayType) m.return_type;
82                                 if (get_ccode_array_length (m)) {
83                                         var length_ctype = get_ccode_array_length_type (m);
84                                         for (int dim = 1; dim <= array_type.rank; dim++) {
85                                                 data.add_field (length_ctype, get_array_length_cname ("result", dim));
86                                         }
87                                 }
88                         } else if (m.return_type is DelegateType) {
89                                 var deleg_type = (DelegateType) m.return_type;
90                                 if (deleg_type.delegate_symbol.has_target) {
91                                         data.add_field (get_ccode_name (delegate_target_type), get_delegate_target_cname ("result"));
92                                         data.add_field (get_ccode_name (delegate_target_destroy_type), get_delegate_target_destroy_notify_cname ("result"));
93                                 }
94                         }
95                 }
96
97                 return data;
98         }
99
100         CCodeFunction generate_free_function (Method m) {
101                 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
102
103                 var freefunc = new CCodeFunction (get_ccode_real_name (m) + "_data_free", "void");
104                 freefunc.modifiers = CCodeModifiers.STATIC;
105                 freefunc.add_parameter (new CCodeParameter ("_data", "gpointer"));
106
107                 push_context (new EmitContext (m));
108                 push_function (freefunc);
109
110                 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_", new CCodeIdentifier ("_data")));
111
112                 foreach (Parameter param in m.get_parameters ()) {
113                         if (!param.captured && param.direction != ParameterDirection.OUT) {
114                                 var param_type = param.variable_type.copy ();
115                                 if (!param_type.value_owned) {
116                                         param_type.value_owned = !no_implicit_copy (param_type);
117                                 }
118
119                                 if (requires_destroy (param_type)) {
120                                         ccode.add_expression (destroy_parameter (param));
121                                 }
122                         }
123                 }
124
125                 if (requires_destroy (m.return_type)) {
126                         if (get_ccode_array_length (m) || !(m.return_type is ArrayType)) {
127                                 /* this is very evil. */
128                                 var v = new LocalVariable (m.return_type, ".result");
129                                 ccode.add_expression (destroy_local (v));
130                         } else {
131                                 var v = new GLibValue (m.return_type, new CCodeIdentifier ("_data_->result"), true);
132                                 v.array_null_terminated = get_ccode_array_null_terminated (m);
133                                 ccode.add_expression (destroy_value (v));
134                         }
135                 }
136
137                 if (m.binding == MemberBinding.INSTANCE) {
138                         var this_type = m.this_parameter.variable_type.copy ();
139                         this_type.value_owned = true;
140
141                         if (requires_destroy (this_type)) {
142                                 ccode.add_expression (destroy_parameter (m.this_parameter));
143                         }
144                 }
145
146                 var freecall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_free"));
147                 freecall.add_argument (new CCodeIdentifier (dataname));
148                 freecall.add_argument (new CCodeIdentifier ("_data_"));
149                 ccode.add_expression (freecall);
150
151                 pop_context ();
152
153                 cfile.add_function_declaration (freefunc);
154                 cfile.add_function (freefunc);
155
156                 return freefunc;
157         }
158
159         void generate_async_function (Method m) {
160                 push_context (new EmitContext ());
161
162                 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
163                 var asyncfunc = new CCodeFunction (get_ccode_real_name (m), "void");
164                 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
165
166                 cparam_map.set (get_param_pos (-1), new CCodeParameter ("_callback_", "GAsyncReadyCallback"));
167                 cparam_map.set (get_param_pos (-0.9), new CCodeParameter ("_user_data_", "gpointer"));
168
169                 generate_cparameters (m, cfile, cparam_map, asyncfunc, null, null, null, 1);
170
171                 if (m.base_method != null || m.base_interface_method != null) {
172                         // declare *_real_* function
173                         asyncfunc.modifiers |= CCodeModifiers.STATIC;
174                         cfile.add_function_declaration (asyncfunc);
175                 } else if (m.is_private_symbol ()) {
176                         asyncfunc.modifiers |= CCodeModifiers.STATIC;
177                 } else if (context.hide_internal && m.is_internal_symbol ()) {
178                         asyncfunc.modifiers |= CCodeModifiers.INTERNAL;
179                 }
180
181                 push_function (asyncfunc);
182
183                 // FIXME partial code duplication with CCodeMethodModule.visit_method
184                 unowned Class? cl = m.parent_symbol as Class;
185                 if (cl != null) {
186                         if (m.binding == MemberBinding.INSTANCE && !(m is CreationMethod)
187                             && m.base_method == null && m.base_interface_method == null) {
188                                 create_type_check_statement (m, new VoidType (), cl, true, "self");
189                         }
190                 }
191                 foreach (Parameter param in m.get_parameters ()) {
192                         if (param.ellipsis || param.params_array) {
193                                 break;
194                         }
195
196                         if (param.direction == ParameterDirection.IN) {
197                                 unowned TypeSymbol? t = param.variable_type.type_symbol;
198                                 if (t != null && (t.is_reference_type () || param.variable_type.is_real_struct_type ())) {
199                                         create_type_check_statement (m, new VoidType (), t, !param.variable_type.nullable, get_ccode_name (param));
200                                 }
201                         }
202                 }
203
204                 // logic copied from valaccodemethodmodule
205                 if (m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
206                         Method base_method;
207
208                         if (m.overrides && m.base_method != null) {
209                                 base_method = m.base_method;
210                         } else {
211                                 base_method = m.base_interface_method;
212                         }
213
214                         var base_expression_type = new ObjectType ((ObjectTypeSymbol) base_method.parent_symbol);
215                         var type_symbol = m.parent_symbol as ObjectTypeSymbol;
216
217                         var self_target_type = new ObjectType (type_symbol);
218                         var cself = get_cvalue_ (transform_value (new GLibValue (base_expression_type, new CCodeIdentifier ("base"), true), self_target_type, m));
219                         ccode.add_declaration ("%s *".printf (get_ccode_name (type_symbol)), new CCodeVariableDeclarator ("self"));
220                         ccode.add_assignment (new CCodeIdentifier ("self"), cself);
221                 }
222
223                 var dataalloc = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
224                 dataalloc.add_argument (new CCodeIdentifier (dataname));
225
226                 var data_var = new CCodeIdentifier ("_data_");
227
228                 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_"));
229                 ccode.add_assignment (data_var, dataalloc);
230
231                 var create_result = new CCodeFunctionCall (new CCodeIdentifier ("g_task_new"));
232
233                 var t = m.parent_symbol as TypeSymbol;
234                 if (!(m is CreationMethod) && m.binding == MemberBinding.INSTANCE &&
235                     t != null && t.is_subtype_of (gobject_type)) {
236                         var gobject_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT"));
237                         gobject_cast.add_argument (new CCodeIdentifier ("self"));
238
239                         create_result.add_argument (gobject_cast);
240                 } else {
241                         create_result.add_argument (new CCodeConstant ("NULL"));
242                 }
243
244                 Parameter cancellable_param = null;
245
246                 foreach (Parameter param in m.get_parameters ()) {
247                         if (param.variable_type is ObjectType && param.variable_type.type_symbol.get_full_name () == "GLib.Cancellable") {
248                                 cancellable_param = param;
249                                 break;
250                         }
251                 }
252
253                 if (cancellable_param == null) {
254                         create_result.add_argument (new CCodeConstant ("NULL"));
255                 } else {
256                         create_result.add_argument (new CCodeIdentifier (get_ccode_name (cancellable_param)));
257                 }
258
259                 create_result.add_argument (new CCodeIdentifier ("_callback_"));
260                 create_result.add_argument (new CCodeIdentifier ("_user_data_"));
261
262                 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_async_result"), create_result);
263
264                 var attach_data_call = new CCodeFunctionCall (new CCodeIdentifier ("g_task_set_task_data"));
265
266                 attach_data_call.add_argument (new CCodeMemberAccess.pointer (data_var, "_async_result"));
267                 attach_data_call.add_argument (data_var);
268                 attach_data_call.add_argument (new CCodeIdentifier (get_ccode_real_name (m) + "_data_free"));
269                 ccode.add_expression (attach_data_call);
270
271                 if (m is CreationMethod) {
272                         ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "object_type"), new CCodeIdentifier ("object_type"));
273                 } else if (m.binding == MemberBinding.INSTANCE) {
274                         var this_type = m.this_parameter.variable_type.copy ();
275                         this_type.value_owned = true;
276
277                         // create copy if necessary as variables in async methods may need to be kept alive
278                         CCodeExpression cself = new CCodeIdentifier ("self");
279                         if (this_type.is_real_non_null_struct_type ()) {
280                                 cself = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cself);
281                         }
282                         if (requires_copy (this_type))  {
283                                 cself = get_cvalue_ (copy_value (new GLibValue (m.this_parameter.variable_type, cself, true), m.this_parameter));
284                         }
285
286                         ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "self"), cself);
287                 }
288
289                 emit_context.push_symbol (m);
290                 foreach (Parameter param in m.get_parameters ()) {
291                         if (param.direction != ParameterDirection.OUT) {
292                                 // create copy if necessary as variables in async methods may need to be kept alive
293                                 var old_captured = param.captured;
294                                 param.captured = false;
295                                 current_method.coroutine = false;
296
297                                 TargetValue value;
298                                 if (param.variable_type.value_owned) {
299                                         // do not use load_parameter for reference/ownership transfer
300                                         // otherwise delegate destroy notify will not be moved
301                                         value = get_parameter_cvalue (param);
302                                 } else  {
303                                         value = load_parameter (param);
304                                 }
305
306                                 current_method.coroutine = true;
307
308                                 store_parameter (param, value);
309
310                                 param.captured = old_captured;
311                         }
312                 }
313                 emit_context.pop_symbol ();
314
315                 foreach (var type_param in m.get_type_parameters ()) {
316                         var type = get_ccode_type_id (type_param);
317                         var dup_func = get_ccode_copy_function (type_param);
318                         var destroy_func = get_ccode_destroy_function (type_param);
319                         ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, type), new CCodeIdentifier (type));
320                         ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, dup_func), new CCodeIdentifier (dup_func));
321                         ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, destroy_func), new CCodeIdentifier (destroy_func));
322                 }
323
324                 var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m) + "_co"));
325                 ccall.add_argument (data_var);
326                 ccode.add_expression (ccall);
327
328                 cfile.add_function (asyncfunc);
329
330                 pop_context ();
331         }
332
333         public void append_struct (CCodeStruct structure) {
334                 var typename = new CCodeVariableDeclarator (structure.name.substring (1));
335                 var typedef = new CCodeTypeDefinition ("struct " + structure.name, typename);
336                 cfile.add_type_declaration (typedef);
337                 cfile.add_type_definition (structure);
338         }
339
340         public override bool generate_method_declaration (Method m, CCodeFile decl_space) {
341                 if (m.coroutine) {
342                         if ((m.is_abstract || m.is_virtual) && get_ccode_no_wrapper (m)) {
343                                 return false;
344                         }
345                         if (add_symbol_declaration (decl_space, m, get_ccode_name (m))) {
346                                 return false;
347                         }
348
349                         generate_type_declaration (new MethodType (m), decl_space);
350
351                         var cl = m.parent_symbol as Class;
352
353                         var asyncfunc = new CCodeFunction (get_ccode_name (m), "void");
354                         var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
355                         var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
356
357                         if (m.is_private_symbol () || m.entry_point) {
358                                 asyncfunc.modifiers |= CCodeModifiers.STATIC;
359                         } else if (context.hide_internal && m.is_internal_symbol ()) {
360                                 asyncfunc.modifiers |= CCodeModifiers.INTERNAL;
361                         } else {
362                                 asyncfunc.modifiers |= CCodeModifiers.EXTERN;
363                                 requires_vala_extern = true;
364                         }
365
366                         // do not generate _new functions for creation methods of abstract classes
367                         if (!(m is CreationMethod && cl != null && cl.is_abstract)) {
368                                 generate_cparameters (m, decl_space, cparam_map, asyncfunc, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")), 1);
369
370                                 decl_space.add_function_declaration (asyncfunc);
371                         }
372
373                         var finishfunc = new CCodeFunction (get_ccode_finish_name (m));
374                         cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
375                         carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
376
377                         if (m.is_private_symbol () || m.entry_point) {
378                                 finishfunc.modifiers |= CCodeModifiers.STATIC;
379                         } else if (context.hide_internal && m.is_internal_symbol ()) {
380                                 finishfunc.modifiers |= CCodeModifiers.INTERNAL;
381                         } else {
382                                 finishfunc.modifiers |= CCodeModifiers.EXTERN;
383                                 requires_vala_extern = true;
384                         }
385
386                         // do not generate _new functions for creation methods of abstract classes
387                         if (!(m is CreationMethod && cl != null && cl.is_abstract)) {
388                                 generate_cparameters (m, decl_space, cparam_map, finishfunc, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")), 2);
389
390                                 decl_space.add_function_declaration (finishfunc);
391                         }
392
393                         if (m is CreationMethod && cl != null) {
394                                 // _construct function
395                                 var function = new CCodeFunction (get_ccode_real_name (m));
396
397                                 if (m.is_private_symbol ()) {
398                                         function.modifiers |= CCodeModifiers.STATIC;
399                                 } else if (context.hide_internal && m.is_internal_symbol ()) {
400                                         function.modifiers |= CCodeModifiers.INTERNAL;
401                                 } else {
402                                         function.modifiers |= CCodeModifiers.EXTERN;
403                                         requires_vala_extern = true;
404                                 }
405
406                                 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
407                                 generate_cparameters (m, decl_space, cparam_map, function, null, null, null, 1);
408
409                                 decl_space.add_function_declaration (function);
410
411                                 function = new CCodeFunction (get_ccode_finish_real_name (m));
412
413                                 if (m.is_private_symbol ()) {
414                                         function.modifiers |= CCodeModifiers.STATIC;
415                                 } else if (context.hide_internal && m.is_internal_symbol ()) {
416                                         function.modifiers |= CCodeModifiers.INTERNAL;
417                                 } else {
418                                         function.modifiers |= CCodeModifiers.EXTERN;
419                                         requires_vala_extern = true;
420                                 }
421
422                                 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
423                                 generate_cparameters (m, decl_space, cparam_map, function, null, null, null, 2);
424
425                                 decl_space.add_function_declaration (function);
426                         }
427
428                         return true;
429                 } else {
430                         return base.generate_method_declaration (m, decl_space);
431                 }
432         }
433
434         public override void visit_method (Method m) {
435                 if (m.coroutine) {
436                         cfile.add_include ("gio/gio.h");
437                         if (!m.is_internal_symbol ()) {
438                                 header_file.add_include ("gio/gio.h");
439                         }
440
441                         if (!m.is_abstract && m.body != null) {
442                                 var data = generate_data_struct (m);
443
444                                 closure_struct = data;
445
446                                 generate_free_function (m);
447                                 generate_async_function (m);
448                                 generate_finish_function (m);
449
450                                 // append the _co function
451                                 base.visit_method (m);
452                                 closure_struct = null;
453
454                                 // only append data struct here to make sure all struct member
455                                 // types are declared before the struct definition
456                                 append_struct (data);
457                         } else {
458                                 generate_method_declaration (m, cfile);
459
460                                 if (!m.is_internal_symbol ()) {
461                                         generate_method_declaration (m, header_file);
462                                 }
463                                 if (!m.is_private_symbol ()) {
464                                         generate_method_declaration (m, internal_header_file);
465                                 }
466                         }
467
468                         if ((m.is_abstract || m.is_virtual) && !get_ccode_no_wrapper (m)) {
469                                 // generate virtual function wrappers
470                                 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
471                                 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
472                                 generate_vfunc (m, new VoidType (), cparam_map, carg_map, "", 1);
473
474                                 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
475                                 carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
476                                 generate_vfunc (m, m.return_type, cparam_map, carg_map, "_finish", 2);
477                         }
478                 } else {
479                         base.visit_method (m);
480                 }
481         }
482
483         public override void visit_creation_method (CreationMethod m) {
484                 if (!m.coroutine) {
485                         base.visit_creation_method (m);
486                 } else {
487                         push_line (m.source_reference);
488
489                         bool visible = !m.is_private_symbol ();
490
491                         visit_method (m);
492
493                         if (m.source_type == SourceFileType.FAST) {
494                                 return;
495                         }
496
497                         // do not generate _new functions for creation methods of abstract classes
498                         if (current_type_symbol is Class && !current_class.is_compact && !current_class.is_abstract) {
499                                 var vfunc = new CCodeFunction (get_ccode_name (m));
500
501                                 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
502                                 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
503
504                                 push_function (vfunc);
505
506                                 var vcall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m)));
507                                 vcall.add_argument (new CCodeIdentifier (get_ccode_type_id (current_class)));
508
509                                 generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall, 1);
510                                 ccode.add_expression (vcall);
511
512                                 if (!visible) {
513                                         vfunc.modifiers |= CCodeModifiers.STATIC;
514                                 }
515
516                                 pop_function ();
517
518                                 cfile.add_function (vfunc);
519
520
521                                 vfunc = new CCodeFunction (get_ccode_finish_name (m));
522
523                                 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
524                                 carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
525
526                                 push_function (vfunc);
527
528                                 vcall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_finish_real_name (m)));
529
530                                 generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall, 2);
531                                 ccode.add_return (vcall);
532
533                                 if (!visible) {
534                                         vfunc.modifiers |= CCodeModifiers.STATIC;
535                                 }
536
537                                 pop_function ();
538
539                                 cfile.add_function (vfunc);
540                         }
541
542                         pop_line ();
543                 }
544         }
545
546         void generate_finish_function (Method m) {
547                 push_context (new EmitContext ());
548
549                 string dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
550
551                 var finishfunc = new CCodeFunction (get_ccode_finish_real_name (m));
552
553                 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
554
555                 cparam_map.set (get_param_pos (get_ccode_async_result_pos (m)), new CCodeParameter ("_res_", "GAsyncResult*"));
556
557                 generate_cparameters (m, cfile, cparam_map, finishfunc, null, null, null, 2);
558
559                 if (m.is_private_symbol () || m.base_method != null || m.base_interface_method != null) {
560                         finishfunc.modifiers |= CCodeModifiers.STATIC;
561                 } else if (context.hide_internal && m.is_internal_symbol ()) {
562                         finishfunc.modifiers |= CCodeModifiers.INTERNAL;
563                 }
564
565                 push_function (finishfunc);
566
567                 var return_type = m.return_type;
568                 if (m is CreationMethod) {
569                         var type_sym = (TypeSymbol) m.parent_symbol;
570                         if (type_sym is ObjectTypeSymbol) {
571                                 ccode.add_declaration (get_ccode_name (type_sym) + "*", new CCodeVariableDeclarator ("result"));
572                                 return_type = SemanticAnalyzer.get_this_type (m, type_sym);
573                         }
574                 } else if (!(return_type is VoidType) && !return_type.is_real_non_null_struct_type ()) {
575                         ccode.add_declaration (get_ccode_name (m.return_type), new CCodeVariableDeclarator ("result"));
576                 }
577
578                 var data_var = new CCodeIdentifier ("_data_");
579
580                 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_"));
581
582                 var async_result_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_TASK"));
583                 async_result_cast.add_argument (new CCodeIdentifier ("_res_"));
584
585                 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_task_propagate_pointer"));
586                 ccall.add_argument (async_result_cast);
587
588                 if (m.tree_can_fail) {
589                         ccall.add_argument (new CCodeIdentifier ("error"));
590                 } else {
591                         ccall.add_argument (new CCodeConstant ("NULL"));
592                 }
593
594                 ccode.add_assignment (data_var, ccall);
595
596                 bool has_cancellable = false;
597
598                 foreach (Parameter param in m.get_parameters ()) {
599                         if (param.variable_type is ObjectType && param.variable_type.type_symbol.get_full_name () == "GLib.Cancellable") {
600                                 has_cancellable = true;
601                                 break;
602                         }
603                 }
604
605                 // If a task is cancelled, g_task_propagate_pointer returns NULL
606                 if (m.tree_can_fail || has_cancellable) {
607                         var is_null = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeConstant ("NULL"), data_var);
608
609                         ccode.open_if (is_null);
610                         return_default_value (return_type);
611                         ccode.close ();
612                 }
613
614                 emit_context.push_symbol (m);
615                 foreach (Parameter param in m.get_parameters ()) {
616                         if (param.direction != ParameterDirection.IN) {
617                                 return_out_parameter (param);
618                                 if (!(param.variable_type is ValueType) || param.variable_type.nullable) {
619                                         ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, get_ccode_name (param)), new CCodeConstant ("NULL"));
620                                 }
621                         }
622                 }
623                 emit_context.pop_symbol ();
624
625                 if (m is CreationMethod) {
626                         ccode.add_assignment (new CCodeIdentifier ("result"), new CCodeMemberAccess.pointer (data_var, "self"));
627                         ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "self"), new CCodeConstant ("NULL"));
628                         ccode.add_return (new CCodeIdentifier ("result"));
629                 } else if (return_type.is_real_non_null_struct_type ()) {
630                         // structs are returned via out parameter
631                         CCodeExpression cexpr = new CCodeMemberAccess.pointer (data_var, "result");
632                         if (requires_copy (return_type)) {
633                                 cexpr = get_cvalue_ (copy_value (new GLibValue (return_type, cexpr, true), return_type));
634                         }
635                         ccode.add_assignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("result")), cexpr);
636                 } else if (!(return_type is VoidType)) {
637                         ccode.add_assignment (new CCodeIdentifier ("result"), new CCodeMemberAccess.pointer (data_var, "result"));
638                         if (return_type is ArrayType) {
639                                 var array_type = (ArrayType) return_type;
640                                 if (get_ccode_array_length (m)) {
641                                         for (int dim = 1; dim <= array_type.rank; dim++) {
642                                                 ccode.add_assignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier (get_array_length_cname ("result", dim))), new CCodeMemberAccess.pointer (data_var, get_array_length_cname ("result", dim)));
643                                         }
644                                 }
645                         } else if (return_type is DelegateType && ((DelegateType) return_type).delegate_symbol.has_target) {
646                                 ccode.add_assignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier (get_delegate_target_cname ("result"))), new CCodeMemberAccess.pointer (data_var, get_delegate_target_cname ("result")));
647                         }
648                         if (!(return_type is ValueType) || return_type.nullable) {
649                                 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "result"), new CCodeConstant ("NULL"));
650                         }
651                         ccode.add_return (new CCodeIdentifier ("result"));
652                 }
653
654                 pop_function ();
655
656                 cfile.add_function (finishfunc);
657
658                 pop_context ();
659         }
660
661         public override string generate_ready_function (Method m) {
662                 // generate ready callback handler
663
664                 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
665
666                 var readyfunc = new CCodeFunction (get_ccode_name (m) + "_ready", "void");
667
668                 if (!add_wrapper (readyfunc.name)) {
669                         // wrapper already defined
670                         return readyfunc.name;
671                 }
672
673                 readyfunc.add_parameter (new CCodeParameter ("source_object", "GObject*"));
674                 readyfunc.add_parameter (new CCodeParameter ("_res_", "GAsyncResult*"));
675                 readyfunc.add_parameter (new CCodeParameter ("_user_data_", "gpointer"));
676
677                 push_function (readyfunc);
678
679                 var data_var = new CCodeIdentifier ("_data_");
680
681                 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_"));
682                 ccode.add_assignment (data_var, new CCodeIdentifier ("_user_data_"));
683                 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_source_object_"), new CCodeIdentifier ("source_object"));
684                 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_res_"), new CCodeIdentifier ("_res_"));
685
686                 var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m) + "_co"));
687                 ccall.add_argument (data_var);
688                 ccode.add_expression (ccall);
689
690                 readyfunc.modifiers |= CCodeModifiers.STATIC;
691
692                 pop_function ();
693
694                 cfile.add_function_declaration (readyfunc);
695                 cfile.add_function (readyfunc);
696
697                 return readyfunc.name;
698         }
699
700         public override void generate_virtual_method_declaration (Method m, CCodeFile decl_space, CCodeStruct type_struct) {
701                 if (!m.coroutine) {
702                         base.generate_virtual_method_declaration (m, decl_space, type_struct);
703                         return;
704                 }
705
706                 if (!m.is_abstract && !m.is_virtual) {
707                         return;
708                 }
709
710                 var creturn_type = get_callable_creturn_type (m);
711
712                 // add vfunc field to the type struct
713                 var vdeclarator = new CCodeFunctionDeclarator (get_ccode_vfunc_name (m));
714                 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
715
716                 generate_cparameters (m, decl_space, cparam_map, new CCodeFunction ("fake"), vdeclarator, null, null, 1);
717
718                 var vdecl = new CCodeDeclaration ("void");
719                 vdecl.add_declarator (vdeclarator);
720                 type_struct.add_declaration (vdecl);
721
722                 // add vfunc field to the type struct
723                 vdeclarator = new CCodeFunctionDeclarator (get_ccode_finish_vfunc_name (m));
724                 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
725
726                 generate_cparameters (m, decl_space, cparam_map, new CCodeFunction ("fake"), vdeclarator, null, null, 2);
727
728                 vdecl = new CCodeDeclaration (get_ccode_name (creturn_type));
729                 vdecl.add_declarator (vdeclarator);
730                 type_struct.add_declaration (vdecl);
731         }
732
733         public override void visit_yield_statement (YieldStatement stmt) {
734                 if (!is_in_coroutine ()) {
735                         return;
736                 }
737
738                 int state = emit_context.next_coroutine_state++;
739
740                 ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"), new CCodeConstant (state.to_string ()));
741                 ccode.add_return (new CCodeConstant ("FALSE"));
742                 ccode.add_label ("_state_%d".printf (state));
743                 ccode.add_statement (new CCodeEmptyStatement ());
744         }
745
746         public override void return_with_exception (CCodeExpression error_expr)
747         {
748                 if (!is_in_coroutine ()) {
749                         base.return_with_exception (error_expr);
750                         return;
751                 }
752
753                 var async_result_expr = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_async_result");
754                 CCodeFunctionCall set_error = null;
755
756                 set_error = new CCodeFunctionCall (new CCodeIdentifier ("g_task_return_error"));
757                 set_error.add_argument (async_result_expr);
758                 set_error.add_argument (error_expr);
759                 ccode.add_expression (set_error);
760
761                 // free local variables
762                 append_local_free (current_symbol);
763
764                 // free possibly already assigned out-parameter
765                 append_out_param_free (current_method);
766
767                 // We already returned the error above, we must not return anything else here.
768                 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
769                 unref.add_argument (async_result_expr);
770                 ccode.add_expression (unref);
771
772                 ccode.add_return (new CCodeConstant ("FALSE"));
773         }
774
775         public override void visit_return_statement (ReturnStatement stmt) {
776                 base.visit_return_statement (stmt);
777
778                 if (!is_in_coroutine ()) {
779                         return;
780                 }
781
782                 complete_async ();
783         }
784
785         public override void generate_cparameters (Method m, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, CCodeFunction func, CCodeFunctionDeclarator? vdeclarator = null, Map<int,CCodeExpression>? carg_map = null, CCodeFunctionCall? vcall = null, int direction = 3) {
786                 if (m.coroutine) {
787                         decl_space.add_include ("gio/gio.h");
788
789                         if (direction == 1) {
790                                 cparam_map.set (get_param_pos (-1), new CCodeParameter ("_callback_", "GAsyncReadyCallback"));
791                                 cparam_map.set (get_param_pos (-0.9), new CCodeParameter ("_user_data_", "gpointer"));
792                                 if (carg_map != null) {
793                                         carg_map.set (get_param_pos (-1), new CCodeIdentifier ("_callback_"));
794                                         carg_map.set (get_param_pos (-0.9), new CCodeIdentifier ("_user_data_"));
795                                 }
796                         } else if (direction == 2) {
797                                 cparam_map.set (get_param_pos (get_ccode_async_result_pos (m)), new CCodeParameter ("_res_", "GAsyncResult*"));
798                                 if (carg_map != null) {
799                                         carg_map.set (get_param_pos (get_ccode_async_result_pos (m)), new CCodeIdentifier ("_res_"));
800                                 }
801                         }
802                 }
803                 base.generate_cparameters (m, decl_space, cparam_map, func, vdeclarator, carg_map, vcall, direction);
804         }
805
806         public string generate_async_callback_wrapper () {
807                 string async_callback_wrapper_func = "_vala_g_async_ready_callback";
808
809                 if (!add_wrapper (async_callback_wrapper_func)) {
810                         return async_callback_wrapper_func;
811                 }
812
813                 var function = new CCodeFunction (async_callback_wrapper_func, "void");
814                 function.modifiers = CCodeModifiers.STATIC;
815
816                 function.add_parameter (new CCodeParameter ("*source_object", "GObject"));
817                 function.add_parameter (new CCodeParameter ("*res", "GAsyncResult"));
818                 function.add_parameter (new CCodeParameter ("*user_data", "void"));
819
820                 push_function (function);
821
822                 var res_ref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_ref"));
823                 res_ref.add_argument (new CCodeIdentifier ("res"));
824
825                 CCodeFunctionCall ccall = null;
826
827                 // store reference to async result of inner async function in out async result
828                 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_task_return_pointer"));
829                 ccall.add_argument (new CCodeIdentifier ("user_data"));
830                 ccall.add_argument (res_ref);
831                 ccall.add_argument (new CCodeIdentifier ("g_object_unref"));
832                 ccode.add_expression (ccall);
833
834                 // free async result
835                 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
836                 ccall.add_argument (new CCodeIdentifier ("user_data"));
837                 ccode.add_expression (ccall);
838
839                 pop_function ();
840
841                 cfile.add_function_declaration (function);
842                 cfile.add_function (function);
843
844                 return async_callback_wrapper_func;
845         }
846 }