Release 0.41.92
[vala-gnome.git] / codegen / valagasyncmodule.vala
blob9d63ab7adfc19e7cbb407826f60b22f237e78532
1 /* valagasyncmodule.vala
3 * Copyright (C) 2008-2012 Jürg Billeter
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.
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.
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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 using GLib;
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);
30 data.add_field ("int", "_state_");
31 data.add_field ("GObject*", "_source_object_");
32 data.add_field ("GAsyncResult*", "_res_");
34 data.add_field ("GTask*", "_async_result");
35 if (!context.require_glib_version (2, 44)) {
36 data.add_field ("GAsyncReadyCallback", "_callback_");
37 data.add_field ("gboolean", "_task_complete_");
40 if (m is CreationMethod) {
41 data.add_field ("GType", "object_type");
44 if (m.binding == MemberBinding.INSTANCE) {
45 var type_sym = (TypeSymbol) m.parent_symbol;
46 if (type_sym is ObjectTypeSymbol) {
47 data.add_field (get_ccode_name (type_sym) + "*", "self");
48 } else {
49 data.add_field (get_ccode_name (type_sym), "self");
53 foreach (Parameter param in m.get_parameters ()) {
54 var param_type = param.variable_type.copy ();
55 param_type.value_owned = true;
56 data.add_field (get_ccode_name (param_type), get_variable_cname (param.name));
58 if (param.variable_type is ArrayType) {
59 var array_type = (ArrayType) param.variable_type;
60 if (get_ccode_array_length (param)) {
61 for (int dim = 1; dim <= array_type.rank; dim++) {
62 data.add_field ("gint", get_parameter_array_length_cname (param, dim));
65 } else if (param.variable_type is DelegateType) {
66 var deleg_type = (DelegateType) param.variable_type;
67 if (deleg_type.delegate_symbol.has_target) {
68 data.add_field ("gpointer", get_ccode_delegate_target_name (param));
69 if (deleg_type.is_disposable ()) {
70 data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
76 foreach (var type_param in m.get_type_parameters ()) {
77 data.add_field ("GType", "%s_type".printf (type_param.name.down ()));
78 data.add_field ("GBoxedCopyFunc", "%s_dup_func".printf (type_param.name.down ()));
79 data.add_field ("GDestroyNotify", "%s_destroy_func".printf (type_param.name.down ()));
82 if (!(m.return_type is VoidType)) {
83 data.add_field (get_ccode_name (m.return_type), "result");
84 if (m.return_type is ArrayType) {
85 var array_type = (ArrayType) m.return_type;
86 if (get_ccode_array_length (m)) {
87 for (int dim = 1; dim <= array_type.rank; dim++) {
88 data.add_field ("gint", get_array_length_cname ("result", dim));
91 } else if (m.return_type is DelegateType) {
92 var deleg_type = (DelegateType) m.return_type;
93 if (deleg_type.delegate_symbol.has_target) {
94 data.add_field ("gpointer", get_delegate_target_cname ("result"));
95 data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname ("result"));
100 return data;
103 CCodeFunction generate_free_function (Method m) {
104 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
106 var freefunc = new CCodeFunction (get_ccode_real_name (m) + "_data_free", "void");
107 freefunc.modifiers = CCodeModifiers.STATIC;
108 freefunc.add_parameter (new CCodeParameter ("_data", "gpointer"));
110 push_context (new EmitContext (m));
111 push_function (freefunc);
113 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_", new CCodeIdentifier ("_data")));
115 foreach (Parameter param in m.get_parameters ()) {
116 if (!param.captured && param.direction != ParameterDirection.OUT) {
117 var param_type = param.variable_type.copy ();
118 if (!param_type.value_owned) {
119 param_type.value_owned = !no_implicit_copy (param_type);
122 if (requires_destroy (param_type)) {
123 ccode.add_expression (destroy_parameter (param));
128 if (requires_destroy (m.return_type)) {
129 if (get_ccode_array_length (m) || !(m.return_type is ArrayType)) {
130 /* this is very evil. */
131 var v = new LocalVariable (m.return_type, ".result");
132 ccode.add_expression (destroy_local (v));
133 } else {
134 var v = new GLibValue (m.return_type, new CCodeIdentifier ("_data_->result"), true);
135 v.array_null_terminated = get_ccode_array_null_terminated (m);
136 ccode.add_expression (destroy_value (v));
140 if (m.binding == MemberBinding.INSTANCE) {
141 var this_type = m.this_parameter.variable_type.copy ();
142 this_type.value_owned = true;
144 if (requires_destroy (this_type)) {
145 ccode.add_expression (destroy_parameter (m.this_parameter));
149 var freecall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_free"));
150 freecall.add_argument (new CCodeIdentifier (dataname));
151 freecall.add_argument (new CCodeIdentifier ("_data_"));
152 ccode.add_expression (freecall);
154 pop_context ();
156 cfile.add_function_declaration (freefunc);
157 cfile.add_function (freefunc);
159 return freefunc;
162 void generate_async_ready_callback_wrapper (Method m, string function_name) {
163 var function = new CCodeFunction (function_name, "void");
164 function.modifiers = CCodeModifiers.STATIC;
166 function.add_parameter (new CCodeParameter ("*source_object", "GObject"));
167 function.add_parameter (new CCodeParameter ("*res", "GAsyncResult"));
168 function.add_parameter (new CCodeParameter ("*user_data", "void"));
170 push_function (function);
172 // Set _task_complete_ to false after calling back to the real func
173 var async_result_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_TASK"));
174 async_result_cast.add_argument (new CCodeIdentifier ("res"));
176 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
177 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_task_data_"));
179 var get_data_call = new CCodeFunctionCall (new CCodeIdentifier ("g_task_get_task_data"));
180 get_data_call.add_argument (async_result_cast);
182 var data_var = new CCodeIdentifier ("_task_data_");
183 ccode.add_assignment (data_var, get_data_call);
185 var task_inner_callback = new CCodeMemberAccess.pointer (data_var, "_callback_");
186 var callback_is_nonnull = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, task_inner_callback, new CCodeConstant ("NULL"));
188 ccode.open_if (callback_is_nonnull);
189 var nested_callback = new CCodeFunctionCall (task_inner_callback);
190 nested_callback.add_argument (new CCodeIdentifier ("source_object"));
191 nested_callback.add_argument (new CCodeIdentifier ("res"));
192 nested_callback.add_argument (new CCodeIdentifier ("user_data"));
193 ccode.add_expression (nested_callback);
194 ccode.close ();
196 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_task_complete_"), new CCodeConstant ("TRUE"));
198 pop_function ();
200 cfile.add_function_declaration (function);
201 cfile.add_function (function);
204 void generate_async_function (Method m) {
205 push_context (new EmitContext ());
207 string? callback_wrapper = null;
209 if (!context.require_glib_version (2, 44)) {
210 callback_wrapper = get_ccode_real_name (m) + "_async_ready_wrapper";
211 generate_async_ready_callback_wrapper (m, callback_wrapper);
214 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
215 var asyncfunc = new CCodeFunction (get_ccode_real_name (m), "void");
216 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
218 cparam_map.set (get_param_pos (-1), new CCodeParameter ("_callback_", "GAsyncReadyCallback"));
219 cparam_map.set (get_param_pos (-0.9), new CCodeParameter ("_user_data_", "gpointer"));
221 generate_cparameters (m, cfile, cparam_map, asyncfunc, null, null, null, 1);
223 if (m.base_method != null || m.base_interface_method != null) {
224 // declare *_real_* function
225 asyncfunc.modifiers |= CCodeModifiers.STATIC;
226 cfile.add_function_declaration (asyncfunc);
227 } else if (m.is_private_symbol ()) {
228 asyncfunc.modifiers |= CCodeModifiers.STATIC;
229 } else if (context.hide_internal && m.is_internal_symbol ()) {
230 asyncfunc.modifiers |= CCodeModifiers.INTERNAL;
233 push_function (asyncfunc);
235 // logic copied from valaccodemethodmodule
236 if (m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
237 Method base_method;
239 if (m.overrides) {
240 base_method = m.base_method;
241 } else {
242 base_method = m.base_interface_method;
245 var base_expression_type = new ObjectType ((ObjectTypeSymbol) base_method.parent_symbol);
246 var type_symbol = m.parent_symbol as ObjectTypeSymbol;
248 var self_target_type = new ObjectType (type_symbol);
249 var cself = get_cvalue_ (transform_value (new GLibValue (base_expression_type, new CCodeIdentifier ("base"), true), self_target_type, m));
250 ccode.add_declaration ("%s *".printf (get_ccode_name (type_symbol)), new CCodeVariableDeclarator ("self"));
251 ccode.add_assignment (new CCodeIdentifier ("self"), cself);
254 var dataalloc = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
255 dataalloc.add_argument (new CCodeIdentifier (dataname));
257 var data_var = new CCodeIdentifier ("_data_");
259 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_"));
260 ccode.add_assignment (data_var, dataalloc);
262 if (!context.require_glib_version (2, 44)) {
263 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_callback_"), new CCodeConstant ("_callback_"));
266 var create_result = new CCodeFunctionCall (new CCodeIdentifier ("g_task_new"));
268 var t = m.parent_symbol as TypeSymbol;
269 if (!(m is CreationMethod) && m.binding == MemberBinding.INSTANCE &&
270 t != null && t.is_subtype_of (gobject_type)) {
271 var gobject_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT"));
272 gobject_cast.add_argument (new CCodeIdentifier ("self"));
274 create_result.add_argument (gobject_cast);
275 } else {
276 create_result.add_argument (new CCodeConstant ("NULL"));
279 Parameter cancellable_param = null;
281 foreach (Parameter param in m.get_parameters ()) {
282 if (param.variable_type is ObjectType && param.variable_type.data_type.get_full_name () == "GLib.Cancellable") {
283 cancellable_param = param;
284 break;
288 if (cancellable_param == null) {
289 create_result.add_argument (new CCodeConstant ("NULL"));
290 } else {
291 create_result.add_argument (new CCodeIdentifier (get_variable_cname (cancellable_param.name)));
294 if (context.require_glib_version (2, 44)) {
295 create_result.add_argument (new CCodeIdentifier ("_callback_"));
296 } else {
297 create_result.add_argument (new CCodeIdentifier (callback_wrapper));
299 create_result.add_argument (new CCodeIdentifier ("_user_data_"));
301 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_async_result"), create_result);
303 if (!context.require_glib_version (2, 44)) {
304 var task_completed_var = new CCodeMemberAccess.pointer (data_var, "_task_complete_");
305 var callback = new CCodeIdentifier ("_callback_");
306 var callback_is_null = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, callback, new CCodeConstant ("NULL"));
308 ccode.open_if (callback_is_null);
309 ccode.add_assignment (task_completed_var, new CCodeConstant ("TRUE"));
310 ccode.close ();
313 var attach_data_call = new CCodeFunctionCall (new CCodeIdentifier ("g_task_set_task_data"));
315 attach_data_call.add_argument (new CCodeMemberAccess.pointer (data_var, "_async_result"));
316 attach_data_call.add_argument (data_var);
317 attach_data_call.add_argument (new CCodeIdentifier (get_ccode_real_name (m) + "_data_free"));
318 ccode.add_expression (attach_data_call);
320 if (m is CreationMethod) {
321 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "object_type"), new CCodeIdentifier ("object_type"));
322 } else if (m.binding == MemberBinding.INSTANCE) {
323 var this_type = m.this_parameter.variable_type.copy ();
324 this_type.value_owned = true;
326 // create copy if necessary as variables in async methods may need to be kept alive
327 CCodeExpression cself = new CCodeIdentifier ("self");
328 if (this_type.is_real_non_null_struct_type ()) {
329 cself = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cself);
331 if (requires_copy (this_type)) {
332 cself = get_cvalue_ (copy_value (new GLibValue (m.this_parameter.variable_type, cself, true), m.this_parameter));
335 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "self"), cself);
338 emit_context.push_symbol (m);
339 foreach (Parameter param in m.get_parameters ()) {
340 if (param.direction != ParameterDirection.OUT) {
341 // create copy if necessary as variables in async methods may need to be kept alive
342 var old_captured = param.captured;
343 param.captured = false;
344 current_method.coroutine = false;
346 TargetValue value;
347 if (param.variable_type.value_owned) {
348 // do not use load_parameter for reference/ownership transfer
349 // otherwise delegate destroy notify will not be moved
350 value = get_parameter_cvalue (param);
351 } else {
352 value = load_parameter (param);
355 current_method.coroutine = true;
357 store_parameter (param, value);
359 param.captured = old_captured;
362 emit_context.pop_symbol ();
364 foreach (var type_param in m.get_type_parameters ()) {
365 var type = "%s_type".printf (type_param.name.down ());
366 var dup_func = "%s_dup_func".printf (type_param.name.down ());
367 var destroy_func = "%s_destroy_func".printf (type_param.name.down ());
368 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, type), new CCodeIdentifier (type));
369 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, dup_func), new CCodeIdentifier (dup_func));
370 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, destroy_func), new CCodeIdentifier (destroy_func));
373 var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m) + "_co"));
374 ccall.add_argument (data_var);
375 ccode.add_expression (ccall);
377 cfile.add_function (asyncfunc);
379 pop_context ();
382 public void append_struct (CCodeStruct structure) {
383 var typename = new CCodeVariableDeclarator (structure.name.substring (1));
384 var typedef = new CCodeTypeDefinition ("struct " + structure.name, typename);
385 cfile.add_type_declaration (typedef);
386 cfile.add_type_definition (structure);
389 public override void generate_method_declaration (Method m, CCodeFile decl_space) {
390 if (m.coroutine) {
391 if (add_symbol_declaration (decl_space, m, get_ccode_name (m))) {
392 return;
395 var cl = m.parent_symbol as Class;
397 var asyncfunc = new CCodeFunction (get_ccode_name (m), "void");
398 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
399 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
401 if (m.is_private_symbol ()) {
402 asyncfunc.modifiers |= CCodeModifiers.STATIC;
403 } else if (context.hide_internal && m.is_internal_symbol ()) {
404 asyncfunc.modifiers |= CCodeModifiers.INTERNAL;
407 // do not generate _new functions for creation methods of abstract classes
408 if (!(m is CreationMethod && cl != null && cl.is_abstract)) {
409 generate_cparameters (m, decl_space, cparam_map, asyncfunc, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")), 1);
411 decl_space.add_function_declaration (asyncfunc);
414 var finishfunc = new CCodeFunction (get_ccode_finish_name (m));
415 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
416 carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
418 if (m.is_private_symbol ()) {
419 finishfunc.modifiers |= CCodeModifiers.STATIC;
420 } else if (context.hide_internal && m.is_internal_symbol ()) {
421 finishfunc.modifiers |= CCodeModifiers.INTERNAL;
424 // do not generate _new functions for creation methods of abstract classes
425 if (!(m is CreationMethod && cl != null && cl.is_abstract)) {
426 generate_cparameters (m, decl_space, cparam_map, finishfunc, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")), 2);
428 decl_space.add_function_declaration (finishfunc);
431 if (m is CreationMethod && cl != null) {
432 // _construct function
433 var function = new CCodeFunction (get_ccode_real_name (m));
435 if (m.is_private_symbol ()) {
436 function.modifiers |= CCodeModifiers.STATIC;
437 } else if (context.hide_internal && m.is_internal_symbol ()) {
438 function.modifiers |= CCodeModifiers.INTERNAL;
441 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
442 generate_cparameters (m, decl_space, cparam_map, function, null, null, null, 1);
444 decl_space.add_function_declaration (function);
446 function = new CCodeFunction (get_ccode_finish_real_name (m));
448 if (m.is_private_symbol ()) {
449 function.modifiers |= CCodeModifiers.STATIC;
450 } else if (context.hide_internal && m.is_internal_symbol ()) {
451 function.modifiers |= CCodeModifiers.INTERNAL;
454 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
455 generate_cparameters (m, decl_space, cparam_map, function, null, null, null, 2);
457 decl_space.add_function_declaration (function);
459 } else {
460 base.generate_method_declaration (m, decl_space);
464 public override void visit_method (Method m) {
465 if (m.coroutine) {
466 cfile.add_include ("gio/gio.h");
467 if (!m.is_internal_symbol ()) {
468 header_file.add_include ("gio/gio.h");
471 if (!m.is_abstract && m.body != null) {
472 var data = generate_data_struct (m);
474 closure_struct = data;
476 generate_free_function (m);
477 generate_async_function (m);
478 generate_finish_function (m);
480 // append the _co function
481 base.visit_method (m);
482 closure_struct = null;
484 // only append data struct here to make sure all struct member
485 // types are declared before the struct definition
486 append_struct (data);
487 } else {
488 generate_method_declaration (m, cfile);
490 if (!m.is_internal_symbol ()) {
491 generate_method_declaration (m, header_file);
493 if (!m.is_private_symbol ()) {
494 generate_method_declaration (m, internal_header_file);
498 if (m.is_abstract || m.is_virtual) {
499 // generate virtual function wrappers
500 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
501 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
502 generate_vfunc (m, new VoidType (), cparam_map, carg_map, "", 1);
504 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
505 carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
506 generate_vfunc (m, m.return_type, cparam_map, carg_map, "_finish", 2);
508 } else {
509 base.visit_method (m);
513 public override void visit_creation_method (CreationMethod m) {
514 if (!m.coroutine) {
515 base.visit_creation_method (m);
516 } else {
517 push_line (m.source_reference);
519 bool visible = !m.is_private_symbol ();
521 visit_method (m);
523 if (m.source_type == SourceFileType.FAST) {
524 return;
527 // do not generate _new functions for creation methods of abstract classes
528 if (current_type_symbol is Class && !current_class.is_compact && !current_class.is_abstract) {
529 var vfunc = new CCodeFunction (get_ccode_name (m));
531 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
532 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
534 push_function (vfunc);
536 var vcall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m)));
537 vcall.add_argument (new CCodeIdentifier (get_ccode_type_id (current_class)));
539 generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall, 1);
540 ccode.add_expression (vcall);
542 if (!visible) {
543 vfunc.modifiers |= CCodeModifiers.STATIC;
546 pop_function ();
548 cfile.add_function (vfunc);
551 vfunc = new CCodeFunction (get_ccode_finish_name (m));
553 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
554 carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
556 push_function (vfunc);
558 vcall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_finish_real_name (m)));
560 generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall, 2);
561 ccode.add_return (vcall);
563 if (!visible) {
564 vfunc.modifiers |= CCodeModifiers.STATIC;
567 pop_function ();
569 cfile.add_function (vfunc);
572 pop_line ();
576 void generate_finish_function (Method m) {
577 push_context (new EmitContext ());
579 string dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
581 var finishfunc = new CCodeFunction (get_ccode_finish_real_name (m));
583 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
585 cparam_map.set (get_param_pos (0.1), new CCodeParameter ("_res_", "GAsyncResult*"));
587 generate_cparameters (m, cfile, cparam_map, finishfunc, null, null, null, 2);
589 if (m.is_private_symbol () || m.base_method != null || m.base_interface_method != null) {
590 finishfunc.modifiers |= CCodeModifiers.STATIC;
591 } else if (context.hide_internal && m.is_internal_symbol ()) {
592 finishfunc.modifiers |= CCodeModifiers.INTERNAL;
595 push_function (finishfunc);
597 var return_type = m.return_type;
598 if (m is CreationMethod) {
599 var type_sym = (TypeSymbol) m.parent_symbol;
600 if (type_sym is ObjectTypeSymbol) {
601 ccode.add_declaration (get_ccode_name (type_sym) + "*", new CCodeVariableDeclarator ("result"));
602 return_type = ((ObjectTypeSymbol) type_sym).get_this_type ();
604 } else if (!(return_type is VoidType) && !return_type.is_real_non_null_struct_type ()) {
605 ccode.add_declaration (get_ccode_name (m.return_type), new CCodeVariableDeclarator ("result"));
608 var data_var = new CCodeIdentifier ("_data_");
610 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_"));
612 var async_result_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_TASK"));
613 async_result_cast.add_argument (new CCodeIdentifier ("_res_"));
615 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_task_propagate_pointer"));
616 ccall.add_argument (async_result_cast);
618 if (m.get_error_types ().size > 0) {
619 ccall.add_argument (new CCodeIdentifier ("error"));
620 } else {
621 ccall.add_argument (new CCodeConstant ("NULL"));
624 ccode.add_assignment (data_var, ccall);
626 bool has_cancellable = false;
628 foreach (Parameter param in m.get_parameters ()) {
629 if (param.variable_type is ObjectType && param.variable_type.data_type.get_full_name () == "GLib.Cancellable") {
630 has_cancellable = true;
631 break;
635 // If a task is cancelled, g_task_propagate_pointer returns NULL
636 if (m.get_error_types ().size > 0 || has_cancellable) {
637 var is_null = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeConstant ("NULL"), data_var);
639 ccode.open_if (is_null);
640 return_default_value (return_type);
641 ccode.close ();
644 emit_context.push_symbol (m);
645 foreach (Parameter param in m.get_parameters ()) {
646 if (param.direction != ParameterDirection.IN) {
647 return_out_parameter (param);
648 if (!(param.variable_type is ValueType) || param.variable_type.nullable) {
649 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, get_variable_cname (param.name)), new CCodeConstant ("NULL"));
653 emit_context.pop_symbol ();
655 if (m is CreationMethod) {
656 ccode.add_assignment (new CCodeIdentifier ("result"), new CCodeMemberAccess.pointer (data_var, "self"));
657 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "self"), new CCodeConstant ("NULL"));
658 ccode.add_return (new CCodeIdentifier ("result"));
659 } else if (return_type.is_real_non_null_struct_type ()) {
660 // structs are returned via out parameter
661 CCodeExpression cexpr = new CCodeMemberAccess.pointer (data_var, "result");
662 if (requires_copy (return_type)) {
663 cexpr = get_cvalue_ (copy_value (new GLibValue (return_type, cexpr, true), return_type));
665 ccode.add_assignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("result")), cexpr);
666 } else if (!(return_type is VoidType)) {
667 ccode.add_assignment (new CCodeIdentifier ("result"), new CCodeMemberAccess.pointer (data_var, "result"));
668 if (return_type is ArrayType) {
669 var array_type = (ArrayType) return_type;
670 if (get_ccode_array_length (m)) {
671 for (int dim = 1; dim <= array_type.rank; dim++) {
672 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)));
675 } else if (return_type is DelegateType && ((DelegateType) return_type).delegate_symbol.has_target) {
676 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")));
678 if (!(return_type is ValueType) || return_type.nullable) {
679 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "result"), new CCodeConstant ("NULL"));
681 ccode.add_return (new CCodeIdentifier ("result"));
684 pop_function ();
686 cfile.add_function (finishfunc);
688 pop_context ();
691 public override string generate_ready_function (Method m) {
692 // generate ready callback handler
694 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
696 var readyfunc = new CCodeFunction (get_ccode_name (m) + "_ready", "void");
698 if (!add_wrapper (readyfunc.name)) {
699 // wrapper already defined
700 return readyfunc.name;
703 readyfunc.add_parameter (new CCodeParameter ("source_object", "GObject*"));
704 readyfunc.add_parameter (new CCodeParameter ("_res_", "GAsyncResult*"));
705 readyfunc.add_parameter (new CCodeParameter ("_user_data_", "gpointer"));
707 push_function (readyfunc);
709 var data_var = new CCodeIdentifier ("_data_");
711 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_"));
712 ccode.add_assignment (data_var, new CCodeIdentifier ("_user_data_"));
713 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_source_object_"), new CCodeIdentifier ("source_object"));
714 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_res_"), new CCodeIdentifier ("_res_"));
716 if (!context.require_glib_version (2, 44)) {
717 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_task_complete_"), new CCodeConstant ("TRUE"));
720 var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m) + "_co"));
721 ccall.add_argument (data_var);
722 ccode.add_expression (ccall);
724 readyfunc.modifiers |= CCodeModifiers.STATIC;
726 pop_function ();
728 cfile.add_function_declaration (readyfunc);
729 cfile.add_function (readyfunc);
731 return readyfunc.name;
734 public override void generate_virtual_method_declaration (Method m, CCodeFile decl_space, CCodeStruct type_struct) {
735 if (!m.coroutine) {
736 base.generate_virtual_method_declaration (m, decl_space, type_struct);
737 return;
740 if (!m.is_abstract && !m.is_virtual) {
741 return;
744 var creturn_type = m.return_type;
745 if (m.return_type.is_real_non_null_struct_type ()) {
746 // structs are returned via out parameter
747 creturn_type = new VoidType ();
750 // add vfunc field to the type struct
751 var vdeclarator = new CCodeFunctionDeclarator (get_ccode_vfunc_name (m));
752 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
754 generate_cparameters (m, decl_space, cparam_map, new CCodeFunction ("fake"), vdeclarator, null, null, 1);
756 var vdecl = new CCodeDeclaration ("void");
757 vdecl.add_declarator (vdeclarator);
758 type_struct.add_declaration (vdecl);
760 // add vfunc field to the type struct
761 vdeclarator = new CCodeFunctionDeclarator (get_ccode_finish_vfunc_name (m));
762 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
764 generate_cparameters (m, decl_space, cparam_map, new CCodeFunction ("fake"), vdeclarator, null, null, 2);
766 vdecl = new CCodeDeclaration (get_ccode_name (creturn_type));
767 vdecl.add_declarator (vdeclarator);
768 type_struct.add_declaration (vdecl);
771 public override void visit_yield_statement (YieldStatement stmt) {
772 if (!is_in_coroutine ()) {
773 return;
776 if (stmt.yield_expression == null) {
777 int state = emit_context.next_coroutine_state++;
779 ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"), new CCodeConstant (state.to_string ()));
780 ccode.add_return (new CCodeConstant ("FALSE"));
781 ccode.add_label ("_state_%d".printf (state));
782 ccode.add_statement (new CCodeEmptyStatement ());
784 return;
787 if (stmt.yield_expression.error) {
788 stmt.error = true;
789 return;
792 ccode.add_expression (get_cvalue (stmt.yield_expression));
794 if (stmt.tree_can_fail && stmt.yield_expression.tree_can_fail) {
795 // simple case, no node breakdown necessary
797 add_simple_check (stmt.yield_expression);
800 /* free temporary objects */
802 foreach (var value in temp_ref_values) {
803 ccode.add_expression (destroy_value (value));
806 temp_ref_values.clear ();
809 public override void return_with_exception (CCodeExpression error_expr)
811 if (!is_in_coroutine ()) {
812 base.return_with_exception (error_expr);
813 return;
816 var async_result_expr = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_async_result");
817 CCodeFunctionCall set_error = null;
819 set_error = new CCodeFunctionCall (new CCodeIdentifier ("g_task_return_error"));
820 set_error.add_argument (async_result_expr);
821 set_error.add_argument (error_expr);
822 ccode.add_expression (set_error);
824 append_local_free (current_symbol, false);
826 // We already returned the error above, we must not return anything else here.
827 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
828 unref.add_argument (async_result_expr);
829 ccode.add_expression (unref);
831 ccode.add_return (new CCodeConstant ("FALSE"));
834 public override void visit_return_statement (ReturnStatement stmt) {
835 base.visit_return_statement (stmt);
837 if (!is_in_coroutine ()) {
838 return;
841 complete_async ();
844 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) {
845 if (m.coroutine) {
846 decl_space.add_include ("gio/gio.h");
848 if (direction == 1) {
849 cparam_map.set (get_param_pos (-1), new CCodeParameter ("_callback_", "GAsyncReadyCallback"));
850 cparam_map.set (get_param_pos (-0.9), new CCodeParameter ("_user_data_", "gpointer"));
851 if (carg_map != null) {
852 carg_map.set (get_param_pos (-1), new CCodeIdentifier ("_callback_"));
853 carg_map.set (get_param_pos (-0.9), new CCodeIdentifier ("_user_data_"));
855 } else if (direction == 2) {
856 cparam_map.set (get_param_pos (0.1), new CCodeParameter ("_res_", "GAsyncResult*"));
857 if (carg_map != null) {
858 carg_map.set (get_param_pos (0.1), new CCodeIdentifier ("_res_"));
862 base.generate_cparameters (m, decl_space, cparam_map, func, vdeclarator, carg_map, vcall, direction);
865 public string generate_async_callback_wrapper () {
866 string async_callback_wrapper_func = "_vala_g_async_ready_callback";
868 if (!add_wrapper (async_callback_wrapper_func)) {
869 return async_callback_wrapper_func;
872 var function = new CCodeFunction (async_callback_wrapper_func, "void");
873 function.modifiers = CCodeModifiers.STATIC;
875 function.add_parameter (new CCodeParameter ("*source_object", "GObject"));
876 function.add_parameter (new CCodeParameter ("*res", "GAsyncResult"));
877 function.add_parameter (new CCodeParameter ("*user_data", "void"));
879 push_function (function);
881 var res_ref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_ref"));
882 res_ref.add_argument (new CCodeIdentifier ("res"));
884 CCodeFunctionCall ccall = null;
886 // store reference to async result of inner async function in out async result
887 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_task_return_pointer"));
888 ccall.add_argument (new CCodeIdentifier ("user_data"));
889 ccall.add_argument (res_ref);
890 ccall.add_argument (new CCodeIdentifier ("g_object_unref"));
891 ccode.add_expression (ccall);
893 // free async result
894 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
895 ccall.add_argument (new CCodeIdentifier ("user_data"));
896 ccode.add_expression (ccall);
898 pop_function ();
900 cfile.add_function_declaration (function);
901 cfile.add_function (function);
903 return async_callback_wrapper_func;