vala: Update list of used attributes
[vala-gnome.git] / codegen / valagasyncmodule.vala
blobbfae24a5feb7ead112e947537ea986cc88e4bfe8
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 if (context.require_glib_version (2, 36)) {
35 data.add_field ("GTask*", "_async_result");
36 if (!context.require_glib_version (2, 44)) {
37 data.add_field ("GAsyncReadyCallback", "_callback_");
38 data.add_field ("gboolean", "_task_complete_");
40 } else {
41 data.add_field ("GSimpleAsyncResult*", "_async_result");
44 if (m is CreationMethod) {
45 data.add_field ("GType", "object_type");
48 if (m.binding == MemberBinding.INSTANCE) {
49 var type_sym = (TypeSymbol) m.parent_symbol;
50 if (type_sym is ObjectTypeSymbol) {
51 data.add_field (get_ccode_name (type_sym) + "*", "self");
52 } else {
53 data.add_field (get_ccode_name (type_sym), "self");
57 foreach (Parameter param in m.get_parameters ()) {
58 bool is_unowned_delegate = param.variable_type is DelegateType && !param.variable_type.value_owned;
60 var param_type = param.variable_type.copy ();
61 param_type.value_owned = true;
62 data.add_field (get_ccode_name (param_type), get_variable_cname (param.name));
64 if (param.variable_type is ArrayType) {
65 var array_type = (ArrayType) param.variable_type;
66 if (get_ccode_array_length (param)) {
67 for (int dim = 1; dim <= array_type.rank; dim++) {
68 data.add_field ("gint", get_parameter_array_length_cname (param, dim));
71 } else if (param.variable_type is DelegateType) {
72 var deleg_type = (DelegateType) param.variable_type;
73 if (deleg_type.delegate_symbol.has_target) {
74 data.add_field ("gpointer", get_ccode_delegate_target_name (param));
75 if (!is_unowned_delegate) {
76 data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
82 foreach (var type_param in m.get_type_parameters ()) {
83 data.add_field ("GType", "%s_type".printf (type_param.name.down ()));
84 data.add_field ("GBoxedCopyFunc", "%s_dup_func".printf (type_param.name.down ()));
85 data.add_field ("GDestroyNotify", "%s_destroy_func".printf (type_param.name.down ()));
88 if (!(m.return_type is VoidType)) {
89 data.add_field (get_ccode_name (m.return_type), "result");
90 if (m.return_type is ArrayType) {
91 var array_type = (ArrayType) m.return_type;
92 if (get_ccode_array_length (m)) {
93 for (int dim = 1; dim <= array_type.rank; dim++) {
94 data.add_field ("gint", get_array_length_cname ("result", dim));
97 } else if (m.return_type is DelegateType) {
98 var deleg_type = (DelegateType) m.return_type;
99 if (deleg_type.delegate_symbol.has_target) {
100 data.add_field ("gpointer", get_delegate_target_cname ("result"));
101 data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname ("result"));
106 return data;
109 CCodeFunction generate_free_function (Method m) {
110 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
112 var freefunc = new CCodeFunction (get_ccode_real_name (m) + "_data_free", "void");
113 freefunc.modifiers = CCodeModifiers.STATIC;
114 freefunc.add_parameter (new CCodeParameter ("_data", "gpointer"));
116 push_context (new EmitContext (m));
117 push_function (freefunc);
119 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_", new CCodeIdentifier ("_data")));
121 foreach (Parameter param in m.get_parameters ()) {
122 if (!param.captured && param.direction != ParameterDirection.OUT) {
123 var param_type = param.variable_type.copy ();
124 if (!param_type.value_owned) {
125 param_type.value_owned = !no_implicit_copy (param_type);
128 if (requires_destroy (param_type)) {
129 ccode.add_expression (destroy_parameter (param));
134 if (requires_destroy (m.return_type)) {
135 if (get_ccode_array_length (m) || !(m.return_type is ArrayType)) {
136 /* this is very evil. */
137 var v = new LocalVariable (m.return_type, ".result");
138 ccode.add_expression (destroy_local (v));
139 } else {
140 var v = new GLibValue (m.return_type, new CCodeIdentifier ("_data_->result"), true);
141 v.array_null_terminated = get_ccode_array_null_terminated (m);
142 ccode.add_expression (destroy_value (v));
146 if (m.binding == MemberBinding.INSTANCE) {
147 var this_type = m.this_parameter.variable_type.copy ();
148 this_type.value_owned = true;
150 if (requires_destroy (this_type)) {
151 ccode.add_expression (destroy_parameter (m.this_parameter));
155 var freecall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_free"));
156 freecall.add_argument (new CCodeIdentifier (dataname));
157 freecall.add_argument (new CCodeIdentifier ("_data_"));
158 ccode.add_expression (freecall);
160 pop_context ();
162 cfile.add_function_declaration (freefunc);
163 cfile.add_function (freefunc);
165 return freefunc;
168 void generate_async_ready_callback_wrapper (Method m, string function_name) {
169 var function = new CCodeFunction (function_name, "void");
170 function.modifiers = CCodeModifiers.STATIC;
172 function.add_parameter (new CCodeParameter ("*source_object", "GObject"));
173 function.add_parameter (new CCodeParameter ("*res", "GAsyncResult"));
174 function.add_parameter (new CCodeParameter ("*user_data", "void"));
176 push_function (function);
178 // Set _task_complete_ to false after calling back to the real func
179 var async_result_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_TASK"));
180 async_result_cast.add_argument (new CCodeIdentifier ("res"));
182 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
183 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_task_data_"));
185 var get_data_call = new CCodeFunctionCall (new CCodeIdentifier ("g_task_get_task_data"));
186 get_data_call.add_argument (async_result_cast);
188 var data_var = new CCodeIdentifier ("_task_data_");
189 ccode.add_assignment (data_var, get_data_call);
191 var task_inner_callback = new CCodeMemberAccess.pointer (data_var, "_callback_");
192 var callback_is_nonnull = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, task_inner_callback, new CCodeConstant ("NULL"));
194 ccode.open_if (callback_is_nonnull);
195 var nested_callback = new CCodeFunctionCall (task_inner_callback);
196 nested_callback.add_argument (new CCodeIdentifier ("source_object"));
197 nested_callback.add_argument (new CCodeIdentifier ("res"));
198 nested_callback.add_argument (new CCodeIdentifier ("user_data"));
199 ccode.add_expression (nested_callback);
200 ccode.close ();
202 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_task_complete_"), new CCodeConstant ("TRUE"));
204 pop_function ();
206 cfile.add_function_declaration (function);
207 cfile.add_function (function);
210 void generate_async_function (Method m) {
211 push_context (new EmitContext ());
213 string? callback_wrapper = null;
215 if (context.require_glib_version (2, 36) && !context.require_glib_version (2, 44)) {
216 callback_wrapper = get_ccode_real_name (m) + "_async_ready_wrapper";
217 generate_async_ready_callback_wrapper (m, callback_wrapper);
220 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
221 var asyncfunc = new CCodeFunction (get_ccode_real_name (m), "void");
222 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
224 cparam_map.set (get_param_pos (-1), new CCodeParameter ("_callback_", "GAsyncReadyCallback"));
225 cparam_map.set (get_param_pos (-0.9), new CCodeParameter ("_user_data_", "gpointer"));
227 generate_cparameters (m, cfile, cparam_map, asyncfunc, null, null, null, 1);
229 if (m.base_method != null || m.base_interface_method != null) {
230 // declare *_real_* function
231 asyncfunc.modifiers |= CCodeModifiers.STATIC;
232 cfile.add_function_declaration (asyncfunc);
233 } else if (m.is_private_symbol ()) {
234 asyncfunc.modifiers |= CCodeModifiers.STATIC;
235 } else if (context.hide_internal && m.is_internal_symbol ()) {
236 asyncfunc.modifiers |= CCodeModifiers.INTERNAL;
239 push_function (asyncfunc);
241 // logic copied from valaccodemethodmodule
242 if (m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
243 Method base_method;
245 if (m.overrides) {
246 base_method = m.base_method;
247 } else {
248 base_method = m.base_interface_method;
251 var base_expression_type = new ObjectType ((ObjectTypeSymbol) base_method.parent_symbol);
252 var type_symbol = m.parent_symbol as ObjectTypeSymbol;
254 var self_target_type = new ObjectType (type_symbol);
255 var cself = get_cvalue_ (transform_value (new GLibValue (base_expression_type, new CCodeIdentifier ("base"), true), self_target_type, m));
256 ccode.add_declaration ("%s *".printf (get_ccode_name (type_symbol)), new CCodeVariableDeclarator ("self"));
257 ccode.add_assignment (new CCodeIdentifier ("self"), cself);
260 var dataalloc = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
261 dataalloc.add_argument (new CCodeIdentifier (dataname));
263 var data_var = new CCodeIdentifier ("_data_");
265 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_"));
266 ccode.add_assignment (data_var, dataalloc);
268 CCodeFunctionCall? create_result = null;
270 if (context.require_glib_version (2, 36)) {
271 if (!context.require_glib_version (2, 44)) {
272 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_callback_"), new CCodeConstant ("_callback_"));
274 create_result = new CCodeFunctionCall (new CCodeIdentifier ("g_task_new"));
275 } else {
276 create_result = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_new"));
279 var t = m.parent_symbol as TypeSymbol;
280 if (!(m is CreationMethod) && m.binding == MemberBinding.INSTANCE &&
281 t != null && t.is_subtype_of (gobject_type)) {
282 var gobject_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT"));
283 gobject_cast.add_argument (new CCodeIdentifier ("self"));
285 create_result.add_argument (gobject_cast);
286 } else {
287 create_result.add_argument (new CCodeConstant ("NULL"));
290 if (context.require_glib_version (2, 36)) {
291 Parameter cancellable_param = null;
293 foreach (Parameter param in m.get_parameters ()) {
294 if (param.variable_type is ObjectType && param.variable_type.data_type.get_full_name () == "GLib.Cancellable") {
295 cancellable_param = param;
296 break;
300 if (cancellable_param == null) {
301 create_result.add_argument (new CCodeConstant ("NULL"));
302 } else {
303 create_result.add_argument (new CCodeIdentifier (get_variable_cname (cancellable_param.name)));
307 if (!context.require_glib_version (2, 36) || context.require_glib_version (2, 44)) {
308 create_result.add_argument (new CCodeIdentifier ("_callback_"));
309 } else {
310 create_result.add_argument (new CCodeIdentifier (callback_wrapper));
312 create_result.add_argument (new CCodeIdentifier ("_user_data_"));
314 if (!context.require_glib_version (2, 36)) {
315 create_result.add_argument (new CCodeIdentifier (get_ccode_real_name (m)));
318 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_async_result"), create_result);
320 CCodeFunctionCall attach_data_call;
322 if (!context.require_glib_version (2, 36)) {
323 attach_data_call = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_set_op_res_gpointer"));
324 } else {
325 if (!context.require_glib_version (2, 44)) {
326 var task_completed_var = new CCodeMemberAccess.pointer (data_var, "_task_complete_");
327 var callback = new CCodeIdentifier ("_callback_");
328 var callback_is_null = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, callback, new CCodeConstant ("NULL"));
330 ccode.open_if (callback_is_null);
331 ccode.add_assignment (task_completed_var, new CCodeConstant ("TRUE"));
332 ccode.close ();
335 attach_data_call = new CCodeFunctionCall (new CCodeIdentifier ("g_task_set_task_data"));
338 attach_data_call.add_argument (new CCodeMemberAccess.pointer (data_var, "_async_result"));
339 attach_data_call.add_argument (data_var);
340 attach_data_call.add_argument (new CCodeIdentifier (get_ccode_real_name (m) + "_data_free"));
341 ccode.add_expression (attach_data_call);
343 if (m is CreationMethod) {
344 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "object_type"), new CCodeIdentifier ("object_type"));
345 } else if (m.binding == MemberBinding.INSTANCE) {
346 var this_type = m.this_parameter.variable_type.copy ();
347 this_type.value_owned = true;
349 // create copy if necessary as variables in async methods may need to be kept alive
350 CCodeExpression cself = new CCodeIdentifier ("self");
351 if (this_type.is_real_non_null_struct_type ()) {
352 cself = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cself);
354 if (requires_copy (this_type)) {
355 cself = get_cvalue_ (copy_value (new GLibValue (m.this_parameter.variable_type, cself, true), m.this_parameter));
358 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "self"), cself);
361 emit_context.push_symbol (m);
362 foreach (Parameter param in m.get_parameters ()) {
363 if (param.direction != ParameterDirection.OUT) {
364 // create copy if necessary as variables in async methods may need to be kept alive
365 var old_captured = param.captured;
366 param.captured = false;
367 current_method.coroutine = false;
369 TargetValue value;
370 if (param.variable_type.value_owned) {
371 // do not use load_parameter for reference/ownership transfer
372 // otherwise delegate destroy notify will not be moved
373 value = get_parameter_cvalue (param);
374 } else {
375 value = load_parameter (param);
378 current_method.coroutine = true;
380 store_parameter (param, value);
382 param.captured = old_captured;
385 emit_context.pop_symbol ();
387 foreach (var type_param in m.get_type_parameters ()) {
388 var type = "%s_type".printf (type_param.name.down ());
389 var dup_func = "%s_dup_func".printf (type_param.name.down ());
390 var destroy_func = "%s_destroy_func".printf (type_param.name.down ());
391 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, type), new CCodeIdentifier (type));
392 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, dup_func), new CCodeIdentifier (dup_func));
393 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, destroy_func), new CCodeIdentifier (destroy_func));
396 var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m) + "_co"));
397 ccall.add_argument (data_var);
398 ccode.add_expression (ccall);
400 cfile.add_function (asyncfunc);
402 pop_context ();
405 void append_struct (CCodeStruct structure) {
406 var typename = new CCodeVariableDeclarator (structure.name.substring (1));
407 var typedef = new CCodeTypeDefinition ("struct " + structure.name, typename);
408 cfile.add_type_declaration (typedef);
409 cfile.add_type_definition (structure);
412 public override void generate_method_declaration (Method m, CCodeFile decl_space) {
413 if (m.coroutine) {
414 if (add_symbol_declaration (decl_space, m, get_ccode_name (m))) {
415 return;
418 var cl = m.parent_symbol as Class;
420 var asyncfunc = new CCodeFunction (get_ccode_name (m), "void");
421 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
422 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
424 if (m.is_private_symbol ()) {
425 asyncfunc.modifiers |= CCodeModifiers.STATIC;
426 } else if (context.hide_internal && m.is_internal_symbol ()) {
427 asyncfunc.modifiers |= CCodeModifiers.INTERNAL;
430 // do not generate _new functions for creation methods of abstract classes
431 if (!(m is CreationMethod && cl != null && cl.is_abstract)) {
432 generate_cparameters (m, decl_space, cparam_map, asyncfunc, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")), 1);
434 decl_space.add_function_declaration (asyncfunc);
437 var finishfunc = new CCodeFunction (get_ccode_finish_name (m));
438 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
439 carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
441 if (m.is_private_symbol ()) {
442 finishfunc.modifiers |= CCodeModifiers.STATIC;
443 } else if (context.hide_internal && m.is_internal_symbol ()) {
444 finishfunc.modifiers |= CCodeModifiers.INTERNAL;
447 // do not generate _new functions for creation methods of abstract classes
448 if (!(m is CreationMethod && cl != null && cl.is_abstract)) {
449 generate_cparameters (m, decl_space, cparam_map, finishfunc, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")), 2);
451 decl_space.add_function_declaration (finishfunc);
454 if (m is CreationMethod && cl != null) {
455 // _construct function
456 var function = new CCodeFunction (get_ccode_real_name (m));
458 if (m.is_private_symbol ()) {
459 function.modifiers |= CCodeModifiers.STATIC;
460 } else if (context.hide_internal && m.is_internal_symbol ()) {
461 function.modifiers |= CCodeModifiers.INTERNAL;
464 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
465 generate_cparameters (m, decl_space, cparam_map, function, null, null, null, 1);
467 decl_space.add_function_declaration (function);
469 function = new CCodeFunction (get_ccode_finish_real_name (m));
471 if (m.is_private_symbol ()) {
472 function.modifiers |= CCodeModifiers.STATIC;
473 } else if (context.hide_internal && m.is_internal_symbol ()) {
474 function.modifiers |= CCodeModifiers.INTERNAL;
477 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
478 generate_cparameters (m, decl_space, cparam_map, function, null, null, null, 2);
480 decl_space.add_function_declaration (function);
482 } else {
483 base.generate_method_declaration (m, decl_space);
487 public override void visit_method (Method m) {
488 if (m.coroutine) {
489 cfile.add_include ("gio/gio.h");
490 if (!m.is_internal_symbol ()) {
491 header_file.add_include ("gio/gio.h");
494 if (!m.is_abstract && m.body != null) {
495 var data = generate_data_struct (m);
497 closure_struct = data;
499 generate_free_function (m);
500 generate_async_function (m);
501 generate_finish_function (m);
503 // append the _co function
504 base.visit_method (m);
505 closure_struct = null;
507 // only append data struct here to make sure all struct member
508 // types are declared before the struct definition
509 append_struct (data);
510 } else {
511 generate_method_declaration (m, cfile);
513 if (!m.is_internal_symbol ()) {
514 generate_method_declaration (m, header_file);
516 if (!m.is_private_symbol ()) {
517 generate_method_declaration (m, internal_header_file);
521 if (m.is_abstract || m.is_virtual) {
522 // generate virtual function wrappers
523 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
524 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
525 generate_vfunc (m, new VoidType (), cparam_map, carg_map, "", 1);
527 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
528 carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
529 generate_vfunc (m, m.return_type, cparam_map, carg_map, "_finish", 2);
531 } else {
532 base.visit_method (m);
536 public override void visit_creation_method (CreationMethod m) {
537 if (!m.coroutine) {
538 base.visit_creation_method (m);
539 } else {
540 push_line (m.source_reference);
542 bool visible = !m.is_private_symbol ();
544 visit_method (m);
546 if (m.source_type == SourceFileType.FAST) {
547 return;
550 // do not generate _new functions for creation methods of abstract classes
551 if (current_type_symbol is Class && !current_class.is_compact && !current_class.is_abstract) {
552 var vfunc = new CCodeFunction (get_ccode_name (m));
554 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
555 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
557 push_function (vfunc);
559 var vcall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m)));
560 vcall.add_argument (new CCodeIdentifier (get_ccode_type_id (current_class)));
562 generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall, 1);
563 ccode.add_expression (vcall);
565 if (!visible) {
566 vfunc.modifiers |= CCodeModifiers.STATIC;
569 pop_function ();
571 cfile.add_function (vfunc);
574 vfunc = new CCodeFunction (get_ccode_finish_name (m));
576 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
577 carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
579 push_function (vfunc);
581 vcall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_finish_real_name (m)));
583 generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall, 2);
584 ccode.add_return (vcall);
586 if (!visible) {
587 vfunc.modifiers |= CCodeModifiers.STATIC;
590 pop_function ();
592 cfile.add_function (vfunc);
595 pop_line ();
599 void generate_finish_function (Method m) {
600 push_context (new EmitContext ());
602 string dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
604 var finishfunc = new CCodeFunction (get_ccode_finish_real_name (m));
606 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
608 cparam_map.set (get_param_pos (0.1), new CCodeParameter ("_res_", "GAsyncResult*"));
610 generate_cparameters (m, cfile, cparam_map, finishfunc, null, null, null, 2);
612 if (m.is_private_symbol () || m.base_method != null || m.base_interface_method != null) {
613 finishfunc.modifiers |= CCodeModifiers.STATIC;
614 } else if (context.hide_internal && m.is_internal_symbol ()) {
615 finishfunc.modifiers |= CCodeModifiers.INTERNAL;
618 push_function (finishfunc);
620 var return_type = m.return_type;
621 if (m is CreationMethod) {
622 var type_sym = (TypeSymbol) m.parent_symbol;
623 if (type_sym is ObjectTypeSymbol) {
624 ccode.add_declaration (get_ccode_name (type_sym) + "*", new CCodeVariableDeclarator ("result"));
626 } else if (!(return_type is VoidType) && !return_type.is_real_non_null_struct_type ()) {
627 ccode.add_declaration (get_ccode_name (m.return_type), new CCodeVariableDeclarator ("result"));
630 var data_var = new CCodeIdentifier ("_data_");
632 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_"));
634 if (context.require_glib_version (2, 36)) {
635 var async_result_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_TASK"));
636 async_result_cast.add_argument (new CCodeIdentifier ("_res_"));
638 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_task_propagate_pointer"));
639 ccall.add_argument (async_result_cast);
641 if (m.get_error_types ().size > 0) {
642 ccall.add_argument (new CCodeIdentifier ("error"));
643 } else {
644 ccall.add_argument (new CCodeConstant ("NULL"));
647 ccode.add_assignment (data_var, ccall);
649 bool has_cancellable = false;
651 foreach (Parameter param in m.get_parameters ()) {
652 if (param.variable_type is ObjectType && param.variable_type.data_type.get_full_name () == "GLib.Cancellable") {
653 has_cancellable = true;
654 break;
658 // If a task is cancelled, g_task_propagate_pointer returns NULL
659 if (m.get_error_types ().size > 0 || has_cancellable) {
660 var is_null = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeConstant ("NULL"), data_var);
662 ccode.open_if (is_null);
663 return_default_value (return_type);
664 ccode.close ();
666 } else {
667 var simple_async_result_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_SIMPLE_ASYNC_RESULT"));
668 simple_async_result_cast.add_argument (new CCodeIdentifier ("_res_"));
670 if (m.get_error_types ().size > 0) {
671 // propagate error from async method
672 var propagate_error = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_propagate_error"));
673 propagate_error.add_argument (simple_async_result_cast);
674 propagate_error.add_argument (new CCodeIdentifier ("error"));
676 ccode.open_if (propagate_error);
677 return_default_value (return_type);
678 ccode.close ();
681 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_get_op_res_gpointer"));
682 ccall.add_argument (simple_async_result_cast);
683 ccode.add_assignment (data_var, ccall);
686 emit_context.push_symbol (m);
687 foreach (Parameter param in m.get_parameters ()) {
688 if (param.direction != ParameterDirection.IN) {
689 return_out_parameter (param);
690 if (!(param.variable_type is ValueType) || param.variable_type.nullable) {
691 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, get_variable_cname (param.name)), new CCodeConstant ("NULL"));
695 emit_context.pop_symbol ();
697 if (m is CreationMethod) {
698 ccode.add_assignment (new CCodeIdentifier ("result"), new CCodeMemberAccess.pointer (data_var, "self"));
699 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "self"), new CCodeConstant ("NULL"));
700 ccode.add_return (new CCodeIdentifier ("result"));
701 } else if (return_type.is_real_non_null_struct_type ()) {
702 // structs are returned via out parameter
703 CCodeExpression cexpr = new CCodeMemberAccess.pointer (data_var, "result");
704 if (requires_copy (return_type)) {
705 cexpr = get_cvalue_ (copy_value (new GLibValue (return_type, cexpr, true), return_type));
707 ccode.add_assignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("result")), cexpr);
708 } else if (!(return_type is VoidType)) {
709 ccode.add_assignment (new CCodeIdentifier ("result"), new CCodeMemberAccess.pointer (data_var, "result"));
710 if (return_type is ArrayType) {
711 var array_type = (ArrayType) return_type;
712 if (get_ccode_array_length (m)) {
713 for (int dim = 1; dim <= array_type.rank; dim++) {
714 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)));
717 } else if (return_type is DelegateType && ((DelegateType) return_type).delegate_symbol.has_target) {
718 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")));
720 if (!(return_type is ValueType) || return_type.nullable) {
721 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "result"), new CCodeConstant ("NULL"));
723 ccode.add_return (new CCodeIdentifier ("result"));
726 pop_function ();
728 cfile.add_function (finishfunc);
730 pop_context ();
733 public override string generate_ready_function (Method m) {
734 // generate ready callback handler
736 var dataname = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "Data";
738 var readyfunc = new CCodeFunction (get_ccode_name (m) + "_ready", "void");
740 if (!add_wrapper (readyfunc.name)) {
741 // wrapper already defined
742 return readyfunc.name;
745 readyfunc.add_parameter (new CCodeParameter ("source_object", "GObject*"));
746 readyfunc.add_parameter (new CCodeParameter ("_res_", "GAsyncResult*"));
747 readyfunc.add_parameter (new CCodeParameter ("_user_data_", "gpointer"));
749 push_function (readyfunc);
751 var data_var = new CCodeIdentifier ("_data_");
753 ccode.add_declaration (dataname + "*", new CCodeVariableDeclarator ("_data_"));
754 ccode.add_assignment (data_var, new CCodeIdentifier ("_user_data_"));
755 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_source_object_"), new CCodeIdentifier ("source_object"));
756 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_res_"), new CCodeIdentifier ("_res_"));
758 if (context.require_glib_version (2, 36) && !context.require_glib_version (2, 44)) {
759 ccode.add_assignment (new CCodeMemberAccess.pointer (data_var, "_task_complete_"), new CCodeConstant ("TRUE"));
762 var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m) + "_co"));
763 ccall.add_argument (data_var);
764 ccode.add_expression (ccall);
766 readyfunc.modifiers |= CCodeModifiers.STATIC;
768 pop_function ();
770 cfile.add_function_declaration (readyfunc);
771 cfile.add_function (readyfunc);
773 return readyfunc.name;
776 public override void generate_virtual_method_declaration (Method m, CCodeFile decl_space, CCodeStruct type_struct) {
777 if (!m.coroutine) {
778 base.generate_virtual_method_declaration (m, decl_space, type_struct);
779 return;
782 if (!m.is_abstract && !m.is_virtual) {
783 return;
786 var creturn_type = m.return_type;
787 if (m.return_type.is_real_non_null_struct_type ()) {
788 // structs are returned via out parameter
789 creturn_type = new VoidType ();
792 // add vfunc field to the type struct
793 var vdeclarator = new CCodeFunctionDeclarator (get_ccode_vfunc_name (m));
794 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
796 generate_cparameters (m, decl_space, cparam_map, new CCodeFunction ("fake"), vdeclarator, null, null, 1);
798 var vdecl = new CCodeDeclaration ("void");
799 vdecl.add_declarator (vdeclarator);
800 type_struct.add_declaration (vdecl);
802 // add vfunc field to the type struct
803 vdeclarator = new CCodeFunctionDeclarator (get_ccode_finish_vfunc_name (m));
804 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
806 generate_cparameters (m, decl_space, cparam_map, new CCodeFunction ("fake"), vdeclarator, null, null, 2);
808 vdecl = new CCodeDeclaration (get_ccode_name (creturn_type));
809 vdecl.add_declarator (vdeclarator);
810 type_struct.add_declaration (vdecl);
813 public override void visit_yield_statement (YieldStatement stmt) {
814 if (!is_in_coroutine ()) {
815 return;
818 if (stmt.yield_expression == null) {
819 int state = emit_context.next_coroutine_state++;
821 ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"), new CCodeConstant (state.to_string ()));
822 ccode.add_return (new CCodeConstant ("FALSE"));
823 ccode.add_label ("_state_%d".printf (state));
824 ccode.add_statement (new CCodeEmptyStatement ());
826 return;
829 if (stmt.yield_expression.error) {
830 stmt.error = true;
831 return;
834 ccode.add_expression (get_cvalue (stmt.yield_expression));
836 if (stmt.tree_can_fail && stmt.yield_expression.tree_can_fail) {
837 // simple case, no node breakdown necessary
839 add_simple_check (stmt.yield_expression);
842 /* free temporary objects */
844 foreach (var value in temp_ref_values) {
845 ccode.add_expression (destroy_value (value));
848 temp_ref_values.clear ();
851 public override void return_with_exception (CCodeExpression error_expr)
853 if (!is_in_coroutine ()) {
854 base.return_with_exception (error_expr);
855 return;
858 var async_result_expr = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_async_result");
859 CCodeFunctionCall set_error = null;
861 if (context.require_glib_version (2, 36)) {
862 set_error = new CCodeFunctionCall (new CCodeIdentifier ("g_task_return_error"));
863 } else {
864 set_error = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_take_error"));
866 set_error.add_argument (async_result_expr);
867 set_error.add_argument (error_expr);
868 ccode.add_expression (set_error);
870 append_local_free (current_symbol, false);
872 if (context.require_glib_version (2, 36)) {
873 // We already returned the error above, we must not return anything else here.
874 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
875 unref.add_argument (async_result_expr);
876 ccode.add_expression (unref);
878 ccode.add_return (new CCodeConstant ("FALSE"));
879 } else {
880 complete_async ();
884 public override void visit_return_statement (ReturnStatement stmt) {
885 base.visit_return_statement (stmt);
887 if (!is_in_coroutine ()) {
888 return;
891 complete_async ();
894 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) {
895 if (m.coroutine) {
896 decl_space.add_include ("gio/gio.h");
898 if (direction == 1) {
899 cparam_map.set (get_param_pos (-1), new CCodeParameter ("_callback_", "GAsyncReadyCallback"));
900 cparam_map.set (get_param_pos (-0.9), new CCodeParameter ("_user_data_", "gpointer"));
901 if (carg_map != null) {
902 carg_map.set (get_param_pos (-1), new CCodeIdentifier ("_callback_"));
903 carg_map.set (get_param_pos (-0.9), new CCodeIdentifier ("_user_data_"));
905 } else if (direction == 2) {
906 cparam_map.set (get_param_pos (0.1), new CCodeParameter ("_res_", "GAsyncResult*"));
907 if (carg_map != null) {
908 carg_map.set (get_param_pos (0.1), new CCodeIdentifier ("_res_"));
912 base.generate_cparameters (m, decl_space, cparam_map, func, vdeclarator, carg_map, vcall, direction);
915 public string generate_async_callback_wrapper () {
916 string async_callback_wrapper_func = "_vala_g_async_ready_callback";
918 if (!add_wrapper (async_callback_wrapper_func)) {
919 return async_callback_wrapper_func;
922 var function = new CCodeFunction (async_callback_wrapper_func, "void");
923 function.modifiers = CCodeModifiers.STATIC;
925 function.add_parameter (new CCodeParameter ("*source_object", "GObject"));
926 function.add_parameter (new CCodeParameter ("*res", "GAsyncResult"));
927 function.add_parameter (new CCodeParameter ("*user_data", "void"));
929 push_function (function);
931 var res_ref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_ref"));
932 res_ref.add_argument (new CCodeIdentifier ("res"));
934 CCodeFunctionCall ccall = null;
936 // store reference to async result of inner async function in out async result
937 if (context.require_glib_version (2, 36)) {
938 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_task_return_pointer"));
939 } else {
940 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_set_op_res_gpointer"));
943 ccall.add_argument (new CCodeIdentifier ("user_data"));
944 ccall.add_argument (res_ref);
945 ccall.add_argument (new CCodeIdentifier ("g_object_unref"));
946 ccode.add_expression (ccall);
948 if (!context.require_glib_version (2, 36)) {
949 // call user-provided callback
950 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_complete"));
951 ccall.add_argument (new CCodeIdentifier ("user_data"));
952 ccode.add_expression (ccall);
955 // free async result
956 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
957 ccall.add_argument (new CCodeIdentifier ("user_data"));
958 ccode.add_expression (ccall);
960 pop_function ();
962 cfile.add_function_declaration (function);
963 cfile.add_function (function);
965 return async_callback_wrapper_func;