codegen: Fix floating reference regression with Variants
[vala-gnome.git] / codegen / valaccodemethodcallmodule.vala
blob0e800cba87cc9c2aed1bd1102a2d9cccb296abcd
1 /* valaccodemethodcallmodule.vala
3 * Copyright (C) 2006-2010 Jürg Billeter
4 * Copyright (C) 2006-2008 Raffaele Sandrini
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Author:
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
25 using GLib;
27 public class Vala.CCodeMethodCallModule : CCodeAssignmentModule {
28 public override void visit_method_call (MethodCall expr) {
29 // the bare function call
30 var ccall = new CCodeFunctionCall (get_cvalue (expr.call));
32 CCodeFunctionCall async_call = null;
33 CCodeFunctionCall finish_call = null;
35 Method m = null;
36 Delegate deleg = null;
37 List<Parameter> params;
39 var ma = expr.call as MemberAccess;
41 var itype = expr.call.value_type;
42 params = itype.get_parameters ();
44 if (itype is MethodType) {
45 assert (ma != null);
46 m = ((MethodType) itype).method_symbol;
48 if (!get_ccode_simple_generics (m)) {
49 check_type_arguments (ma);
52 if (ma.inner != null && ma.inner.value_type is EnumValueType && ((EnumValueType) ma.inner.value_type).get_to_string_method() == m) {
53 // Enum.VALUE.to_string()
54 var en = (Enum) ma.inner.value_type.data_type;
55 ccall.call = new CCodeIdentifier (generate_enum_tostring_function (en));
56 } else if (context.profile == Profile.POSIX && ma.inner != null && ma.inner.value_type != null && ma.inner.value_type.data_type == string_type.data_type && ma.member_name == "printf") {
57 ccall.call = new CCodeIdentifier (generate_string_printf_function ());
58 } else if (expr.is_constructv_chainup) {
59 ccall.call = new CCodeIdentifier (get_ccode_constructv_name ((CreationMethod) m));
61 } else if (itype is SignalType) {
62 var sig_type = (SignalType) itype;
63 if (ma != null && ma.inner is BaseAccess && sig_type.signal_symbol.is_virtual) {
64 m = sig_type.signal_symbol.default_handler;
65 } else {
66 ccall = (CCodeFunctionCall) get_cvalue (expr.call);
68 } else if (itype is ObjectType) {
69 // constructor
70 var cl = (Class) ((ObjectType) itype).type_symbol;
71 m = cl.default_construction_method;
72 generate_method_declaration (m, cfile);
73 var real_name = get_ccode_real_name (m);
74 if (expr.is_constructv_chainup) {
75 real_name = get_ccode_constructv_name ((CreationMethod) m);
77 ccall = new CCodeFunctionCall (new CCodeIdentifier (real_name));
78 } else if (itype is StructValueType) {
79 // constructor
80 var st = (Struct) ((StructValueType) itype).type_symbol;
81 m = st.default_construction_method;
82 generate_method_declaration (m, cfile);
83 ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m)));
84 } else if (itype is DelegateType) {
85 deleg = ((DelegateType) itype).delegate_symbol;
88 var in_arg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
89 var out_arg_map = in_arg_map;
91 if (m != null && m.coroutine) {
92 // async call
94 async_call = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (m)));
95 finish_call = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_finish_name (m)));
97 if (ma.inner is BaseAccess) {
98 if (m.base_method != null) {
99 var base_class = (Class) m.base_method.parent_symbol;
100 var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (get_ccode_upper_case_name (base_class, null))));
101 vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (get_ccode_lower_case_name (current_class, null))));
103 async_call.call = new CCodeMemberAccess.pointer (vcast, get_ccode_vfunc_name (m));
104 finish_call.call = new CCodeMemberAccess.pointer (vcast, get_ccode_finish_vfunc_name (m));
105 } else if (m.base_interface_method != null) {
106 var base_iface = (Interface) m.base_interface_method.parent_symbol;
107 string parent_iface_var = "%s_%s_parent_iface".printf (get_ccode_lower_case_name (current_class), get_ccode_lower_case_name (base_iface));
109 async_call.call = new CCodeMemberAccess.pointer (new CCodeIdentifier (parent_iface_var), get_ccode_vfunc_name (m));
110 finish_call.call = new CCodeMemberAccess.pointer (new CCodeIdentifier (parent_iface_var), get_ccode_finish_vfunc_name (m));
114 if (ma.member_name == "begin" && ma.inner.symbol_reference == ma.symbol_reference) {
115 // no finish call
116 ccall = async_call;
117 params = m.get_async_begin_parameters ();
118 } else if (ma.member_name == "end" && ma.inner.symbol_reference == ma.symbol_reference) {
119 // no async call
120 ccall = finish_call;
121 params = m.get_async_end_parameters ();
122 } else if (!expr.is_yield_expression) {
123 // same as .begin, backwards compatible to bindings without async methods
124 ccall = async_call;
125 params = m.get_async_begin_parameters ();
126 } else {
127 ccall = finish_call;
129 // output arguments used separately
130 out_arg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
131 // pass GAsyncResult stored in closure to finish function
132 out_arg_map.set (get_param_pos (0.1), new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_res_"));
136 if (m is CreationMethod && m.parent_symbol is Class) {
137 if (context.profile == Profile.GOBJECT) {
138 if (!((Class) m.parent_symbol).is_compact) {
139 ccall.add_argument (get_variable_cexpression ("object_type"));
141 } else {
142 ccall.add_argument (get_this_cexpression ());
145 if (!current_class.is_compact) {
146 if (current_class != m.parent_symbol) {
147 // chain up to base class
148 foreach (DataType base_type in current_class.get_base_types ()) {
149 if (base_type.data_type is Class) {
150 List<TypeParameter> type_parameters = null;
151 if (get_ccode_real_name (m) == "g_object_new") {
152 // gobject-style chainup
153 type_parameters = ((Class) base_type.data_type).get_type_parameters ();
155 add_generic_type_arguments (in_arg_map, base_type.get_type_arguments (), expr, true, type_parameters);
156 break;
159 } else {
160 // chain up to other constructor in same class
161 int type_param_index = 0;
162 var cl = (Class) m.parent_symbol;
163 foreach (TypeParameter type_param in cl.get_type_parameters ()) {
164 in_arg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ())));
165 in_arg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ())));
166 in_arg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ())));
167 type_param_index++;
170 } else if (current_class.base_class == gsource_type) {
171 // g_source_new
173 string class_prefix = get_ccode_lower_case_name (current_class);
175 var funcs = new CCodeDeclaration ("const GSourceFuncs");
176 funcs.modifiers = CCodeModifiers.STATIC;
177 funcs.add_declarator (new CCodeVariableDeclarator ("_source_funcs", new CCodeConstant ("{ %s_real_prepare, %s_real_check, %s_real_dispatch, %s_finalize}".printf (class_prefix, class_prefix, class_prefix, class_prefix))));
178 ccode.add_statement (funcs);
180 ccall.add_argument (new CCodeCastExpression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_source_funcs")), "GSourceFuncs *"));
182 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
183 csizeof.add_argument (new CCodeIdentifier (get_ccode_name (current_class)));
184 ccall.add_argument (csizeof);
186 } else if (m is CreationMethod && m.parent_symbol is Struct) {
187 ccall.add_argument (get_this_cexpression ());
188 } else if (m != null && m.get_type_parameters ().size > 0 && !get_ccode_has_generic_type_parameter (m) && !get_ccode_simple_generics (m) && (ccall != finish_call || expr.is_yield_expression)) {
189 // generic method
190 // don't add generic arguments for .end() calls
191 add_generic_type_arguments (in_arg_map, ma.get_type_arguments (), expr);
194 // the complete call expression, might include casts, comma expressions, and/or assignments
195 CCodeExpression ccall_expr = ccall;
197 if (m is ArrayResizeMethod) {
198 var array_type = (ArrayType) ma.inner.value_type;
199 in_arg_map.set (get_param_pos (0), new CCodeIdentifier (get_ccode_name (array_type.element_type)));
200 } else if (m is ArrayMoveMethod) {
201 requires_array_move = true;
202 } else if (m is ArrayCopyMethod) {
203 expr.target_value = copy_value (ma.inner.target_value, expr);
204 return;
207 CCodeExpression instance = null;
208 if (m != null && m.is_async_callback) {
209 if (current_method.closure) {
210 var block = ((Method) m.parent_symbol).body;
211 instance = new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (block))), "_async_data_");
212 } else {
213 instance = new CCodeIdentifier ("_data_");
216 in_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
217 out_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
218 } else if (m != null && m.binding == MemberBinding.INSTANCE && !(m is CreationMethod)) {
219 var instance_value = ma.inner.target_value;
220 if ((ma.member_name == "begin" || ma.member_name == "end") && ma.inner.symbol_reference == ma.symbol_reference) {
221 var inner_ma = (MemberAccess) ma.inner;
222 instance_value = inner_ma.inner.target_value;
224 instance = get_cvalue_ (instance_value);
226 var st = m.parent_symbol as Struct;
227 if (st != null && !st.is_simple_type ()) {
228 // we need to pass struct instance by reference
229 if (!get_lvalue (instance_value)) {
230 instance_value = store_temp_value (instance_value, expr);
232 instance = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue_ (instance_value));
235 if (expr.is_yield_expression) {
236 in_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
237 if (get_ccode_finish_instance (m)) {
238 out_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
240 } else if (ma.member_name != "end" || get_ccode_finish_instance (m)) {
241 out_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
242 in_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
244 } else if (m != null && m.binding == MemberBinding.CLASS) {
245 var cl = (Class) m.parent_symbol;
246 var cast = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_upper_case_name (cl, null) + "_CLASS"));
248 CCodeExpression klass;
249 if (ma.inner == null) {
250 if (get_this_type () == null) {
251 // Accessing the method from a static or class constructor
252 klass = new CCodeIdentifier ("klass");
253 } else {
254 // Accessing the method from within an instance method
255 var k = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_GET_CLASS"));
256 k.add_argument (get_this_cexpression ());
257 klass = k;
259 } else {
260 // Accessing the method of an instance
261 var k = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_GET_CLASS"));
262 k.add_argument (get_cvalue (ma.inner));
263 klass = k;
266 cast.add_argument (klass);
267 in_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), cast);
268 out_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), cast);
271 if (m != null && get_ccode_has_generic_type_parameter (m)) {
272 // insert type argument for macros
273 if (m.get_type_parameters ().size > 0) {
274 // generic method
275 int type_param_index = 0;
276 foreach (var type_arg in ma.get_type_arguments ()) {
277 in_arg_map.set (get_param_pos (get_ccode_generic_type_pos (m) + 0.01 * type_param_index), new CCodeIdentifier (get_ccode_name (type_arg)));
278 type_param_index++;
280 } else {
281 // method in generic type
282 int type_param_index = 0;
283 foreach (var type_arg in ma.inner.value_type.get_type_arguments ()) {
284 in_arg_map.set (get_param_pos (get_ccode_generic_type_pos (m) + 0.01 * type_param_index), new CCodeIdentifier (get_ccode_name (type_arg)));
285 type_param_index++;
290 if (m is ArrayMoveMethod) {
291 var array_type = (ArrayType) ma.inner.value_type;
292 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
293 csizeof.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
294 in_arg_map.set (get_param_pos (0.1), csizeof);
295 } else if (m is DynamicMethod) {
296 m.clear_parameters ();
297 int param_nr = 1;
298 foreach (Expression arg in expr.get_argument_list ()) {
299 var unary = arg as UnaryExpression;
300 if (unary != null && unary.operator == UnaryOperator.OUT) {
301 // out argument
302 var param = new Parameter ("param%d".printf (param_nr), unary.inner.value_type);
303 param.direction = ParameterDirection.OUT;
304 m.add_parameter (param);
305 } else if (unary != null && unary.operator == UnaryOperator.REF) {
306 // ref argument
307 var param = new Parameter ("param%d".printf (param_nr), unary.inner.value_type);
308 param.direction = ParameterDirection.REF;
309 m.add_parameter (param);
310 } else {
311 // in argument
312 m.add_parameter (new Parameter ("param%d".printf (param_nr), arg.value_type));
314 param_nr++;
316 foreach (Parameter param in m.get_parameters ()) {
317 param.accept (this);
319 generate_dynamic_method_wrapper ((DynamicMethod) m);
320 } else if (m is CreationMethod && context.profile == Profile.GOBJECT && m.parent_symbol is Class) {
321 ccode.add_assignment (get_this_cexpression (), new CCodeCastExpression (ccall, get_ccode_name (current_class) + "*"));
323 if (current_method.body.captured) {
324 // capture self after setting it
325 var ref_call = new CCodeFunctionCall (get_dup_func_expression (new ObjectType (current_class), expr.source_reference));
326 ref_call.add_argument (get_this_cexpression ());
328 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (current_method.body))), "self"), ref_call);
331 if (!current_class.is_compact && current_class.get_type_parameters ().size > 0) {
332 /* type, dup func, and destroy func fields for generic types */
333 var suffices = new string[] {"type", "dup_func", "destroy_func"};
334 foreach (TypeParameter type_param in current_class.get_type_parameters ()) {
335 var priv_access = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv");
337 foreach (string suffix in suffices) {
338 var param_name = new CCodeIdentifier ("%s_%s".printf (type_param.name.down (), suffix));
339 ccode.add_assignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
343 // object chainup can't be used as expression
344 ccall_expr = null;
347 bool ellipsis = false;
349 int i = 1;
350 int arg_pos;
351 Iterator<Parameter> params_it = params.iterator ();
352 foreach (Expression arg in expr.get_argument_list ()) {
353 CCodeExpression cexpr = get_cvalue (arg);
355 var carg_map = in_arg_map;
357 if (params_it.next ()) {
358 var param = params_it.get ();
359 ellipsis = param.params_array || param.ellipsis;
360 if (!ellipsis) {
361 if (param.direction == ParameterDirection.OUT) {
362 carg_map = out_arg_map;
365 var unary = arg as UnaryExpression;
366 if (unary == null || unary.operator != UnaryOperator.OUT) {
367 if (get_ccode_array_length (param) && param.variable_type is ArrayType) {
368 var array_type = (ArrayType) param.variable_type;
369 for (int dim = 1; dim <= array_type.rank; dim++) {
370 CCodeExpression? array_length_expr = null;
371 if (get_ccode_array_length_type (param) != null) {
372 string length_ctype = get_ccode_array_length_type (param);
373 if (unary != null && unary.operator == UnaryOperator.REF) {
374 length_ctype = "%s*".printf (length_ctype);
376 array_length_expr = new CCodeCastExpression (get_array_length_cexpression (arg, dim), length_ctype);
377 } else {
378 array_length_expr = get_array_length_cexpression (arg, dim);
380 carg_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), array_length_expr);
382 } else if (param.variable_type is DelegateType) {
383 var deleg_type = (DelegateType) param.variable_type;
384 if (deleg_type.delegate_symbol.has_target) {
385 CCodeExpression delegate_target_destroy_notify;
386 var delegate_target = get_delegate_target_cexpression (arg, out delegate_target_destroy_notify);
387 assert (delegate_target != null);
388 if (get_ccode_type (param) == "GClosure*") {
389 // one single GClosure parameter
390 var closure_new = new CCodeFunctionCall (new CCodeIdentifier ("g_cclosure_new"));
391 closure_new.add_argument (new CCodeCastExpression (cexpr, "GCallback"));
392 closure_new.add_argument (delegate_target);
393 closure_new.add_argument (new CCodeCastExpression (delegate_target_destroy_notify, "GClosureNotify"));
394 cexpr = new CCodeConditionalExpression (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cexpr, new CCodeIdentifier ("NULL")), new CCodeIdentifier ("NULL"), closure_new);
395 } else {
396 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), delegate_target);
397 if (deleg_type.is_disposable ()) {
398 assert (delegate_target_destroy_notify != null);
399 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param) + 0.01), delegate_target_destroy_notify);
403 } else if (param.variable_type is MethodType) {
404 // callbacks in dynamic method calls
405 CCodeExpression delegate_target_destroy_notify;
406 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), get_delegate_target_cexpression (arg, out delegate_target_destroy_notify));
407 } else if (param.variable_type is GenericType) {
408 if (m != null && get_ccode_simple_generics (m)) {
409 var generic_type = (GenericType) param.variable_type;
410 int type_param_index = m.get_type_parameter_index (generic_type.type_parameter.name);
411 var type_arg = ma.get_type_arguments ().get (type_param_index);
412 if (param.variable_type.value_owned) {
413 if (requires_copy (type_arg)) {
414 carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (param)), get_destroy_func_expression (type_arg));
415 } else {
416 carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (param)), new CCodeConstant ("NULL"));
422 cexpr = handle_struct_argument (param, arg, cexpr);
423 } else {
424 arg.target_value = null;
426 var temp_var = get_temp_variable (param.variable_type, param.variable_type.value_owned, null, true);
427 emit_temp_var (temp_var);
428 set_cvalue (arg, get_variable_cexpression (temp_var.name));
429 arg.target_value.value_type = arg.target_type;
431 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue (arg));
433 if (get_ccode_array_length (param) && param.variable_type is ArrayType) {
434 var array_type = (ArrayType) param.variable_type;
435 var array_length_type = int_type;
436 if (get_ccode_array_length_type (param) != null) {
437 array_length_type = new CType (get_ccode_array_length_type (param));
439 for (int dim = 1; dim <= array_type.rank; dim++) {
440 var temp_array_length = get_temp_variable (array_length_type);
441 emit_temp_var (temp_array_length);
442 append_array_length (arg, get_variable_cexpression (temp_array_length.name));
443 carg_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_array_lengths (arg).get (dim - 1)));
445 } else if (param.variable_type is DelegateType) {
446 var deleg_type = (DelegateType) param.variable_type;
447 if (deleg_type.delegate_symbol.has_target) {
448 temp_var = get_temp_variable (new PointerType (new VoidType ()), true, null, true);
449 emit_temp_var (temp_var);
450 set_delegate_target (arg, get_variable_cexpression (temp_var.name));
451 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_delegate_target (arg)));
452 if (deleg_type.is_disposable ()) {
453 temp_var = get_temp_variable (gdestroynotify_type, true, null, true);
454 emit_temp_var (temp_var);
455 set_delegate_target_destroy_notify (arg, get_variable_cexpression (temp_var.name));
456 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param) + 0.01), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_delegate_target_destroy_notify (arg)));
462 if (get_ccode_type (param) != null) {
463 cexpr = new CCodeCastExpression (cexpr, get_ccode_type (param));
465 } else {
466 cexpr = handle_struct_argument (null, arg, cexpr);
468 arg_pos = get_param_pos (get_ccode_pos (param), ellipsis);
469 } else {
470 // default argument position
471 cexpr = handle_struct_argument (null, arg, cexpr);
472 arg_pos = get_param_pos (i, ellipsis);
475 carg_map.set (arg_pos, cexpr);
477 if (arg is NamedArgument && ellipsis) {
478 var named_arg = (NamedArgument) arg;
479 string name = string.joinv ("-", named_arg.name.split ("_"));
480 carg_map.set (get_param_pos (i - 0.1, ellipsis), new CCodeConstant ("\"%s\"".printf (name)));
483 i++;
485 if (params_it.next ()) {
486 var param = params_it.get ();
488 /* if there are more parameters than arguments,
489 * the additional parameter is an ellipsis parameter
490 * otherwise there is a bug in the semantic analyzer
492 assert (param.params_array || param.ellipsis);
493 ellipsis = true;
496 /* add length argument for methods returning arrays */
497 if (m != null && m.return_type is ArrayType && async_call != ccall) {
498 var array_type = (ArrayType) m.return_type;
499 for (int dim = 1; dim <= array_type.rank; dim++) {
500 if (get_ccode_array_null_terminated (m)) {
501 // handle calls to methods returning null-terminated arrays
502 var temp_var = get_temp_variable (itype.get_return_type (), true, null, false);
503 var temp_ref = get_variable_cexpression (temp_var.name);
505 emit_temp_var (temp_var);
507 ccall_expr = new CCodeAssignment (temp_ref, ccall_expr);
509 requires_array_length = true;
510 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
511 len_call.add_argument (temp_ref);
513 append_array_length (expr, len_call);
514 } else if (get_ccode_array_length (m)) {
515 LocalVariable temp_var;
517 if (get_ccode_array_length_type (m) == null) {
518 temp_var = get_temp_variable (int_type);
519 } else {
520 temp_var = get_temp_variable (new CType (get_ccode_array_length_type (m)));
522 var temp_ref = get_variable_cexpression (temp_var.name);
524 emit_temp_var (temp_var);
526 out_arg_map.set (get_param_pos (get_ccode_array_length_pos (m) + 0.01 * dim), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
528 append_array_length (expr, temp_ref);
529 } else if (get_ccode_array_length_expr (m) != null) {
530 append_array_length (expr, new CCodeConstant (get_ccode_array_length_expr (m)));
531 } else {
532 append_array_length (expr, new CCodeConstant ("-1"));
535 } else if (m != null && m.return_type is DelegateType && async_call != ccall) {
536 var deleg_type = (DelegateType) m.return_type;
537 if (deleg_type.delegate_symbol.has_target) {
538 var temp_var = get_temp_variable (new PointerType (new VoidType ()));
539 var temp_ref = get_variable_cexpression (temp_var.name);
541 emit_temp_var (temp_var);
543 out_arg_map.set (get_param_pos (get_ccode_delegate_target_pos (m)), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
545 set_delegate_target (expr, temp_ref);
547 if (deleg_type.is_disposable ()) {
548 temp_var = get_temp_variable (gdestroynotify_type);
549 temp_ref = get_variable_cexpression (temp_var.name);
551 emit_temp_var (temp_var);
553 out_arg_map.set (get_param_pos (get_ccode_delegate_target_pos (m) + 0.01), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
555 set_delegate_target_destroy_notify (expr, temp_ref);
556 } else {
557 set_delegate_target_destroy_notify (expr, new CCodeConstant ("NULL"));
559 } else {
560 set_delegate_target (expr, new CCodeConstant ("NULL"));
564 // add length argument for delegates returning arrays
565 // TODO: avoid code duplication with methods returning arrays, see above
566 if (deleg != null && deleg.return_type is ArrayType) {
567 var array_type = (ArrayType) deleg.return_type;
568 for (int dim = 1; dim <= array_type.rank; dim++) {
569 if (get_ccode_array_null_terminated (deleg)) {
570 // handle calls to methods returning null-terminated arrays
571 var temp_var = get_temp_variable (itype.get_return_type (), true, null, false);
572 var temp_ref = get_variable_cexpression (temp_var.name);
574 emit_temp_var (temp_var);
576 ccall_expr = new CCodeAssignment (temp_ref, ccall_expr);
578 requires_array_length = true;
579 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
580 len_call.add_argument (temp_ref);
582 append_array_length (expr, len_call);
583 } else if (get_ccode_array_length (deleg)) {
584 var temp_var = get_temp_variable (int_type);
585 var temp_ref = get_variable_cexpression (temp_var.name);
587 emit_temp_var (temp_var);
589 out_arg_map.set (get_param_pos (get_ccode_array_length_pos (deleg) + 0.01 * dim), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
591 append_array_length (expr, temp_ref);
592 } else {
593 append_array_length (expr, new CCodeConstant ("-1"));
596 } else if (deleg != null && deleg.return_type is DelegateType) {
597 var deleg_type = (DelegateType) deleg.return_type;
598 if (deleg_type.delegate_symbol.has_target) {
599 var temp_var = get_temp_variable (new PointerType (new VoidType ()));
600 var temp_ref = get_variable_cexpression (temp_var.name);
602 emit_temp_var (temp_var);
604 out_arg_map.set (get_param_pos (get_ccode_delegate_target_pos (deleg)), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
606 set_delegate_target (expr, temp_ref);
608 if (deleg_type.is_disposable ()) {
609 temp_var = get_temp_variable (gdestroynotify_type);
610 temp_ref = get_variable_cexpression (temp_var.name);
612 emit_temp_var (temp_var);
614 out_arg_map.set (get_param_pos (get_ccode_delegate_target_pos (deleg) + 0.01), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
616 set_delegate_target_destroy_notify (expr, temp_ref);
621 if (m != null && m.coroutine) {
622 if (expr.is_yield_expression) {
623 // asynchronous call
624 in_arg_map.set (get_param_pos (-1), new CCodeIdentifier (generate_ready_function (current_method)));
625 in_arg_map.set (get_param_pos (-0.9), new CCodeIdentifier ("_data_"));
629 if (expr.tree_can_fail) {
630 // method can fail
631 current_method_inner_error = true;
632 // add &inner_error before the ellipsis arguments
633 out_arg_map.set (get_param_pos (-1), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression ("_inner_error_")));
634 } else if (m != null && m.has_error_type_parameter () && async_call != ccall) {
635 // inferred error argument from base method
636 out_arg_map.set (get_param_pos (-1), new CCodeConstant ("NULL"));
639 if (ellipsis) {
640 /* ensure variable argument list ends with NULL
641 * except when using printf-style arguments */
642 if (m == null) {
643 in_arg_map.set (get_param_pos (-1, true), new CCodeConstant ("NULL"));
644 } else if (!m.printf_format && !m.scanf_format && get_ccode_sentinel (m) != "" && !expr.is_constructv_chainup) {
645 in_arg_map.set (get_param_pos (-1, true), new CCodeConstant (get_ccode_sentinel (m)));
649 if (itype is DelegateType) {
650 var deleg_type = (DelegateType) itype;
651 var d = deleg_type.delegate_symbol;
652 if (d.has_target) {
653 CCodeExpression delegate_target_destroy_notify;
654 in_arg_map.set (get_param_pos (get_ccode_instance_pos (d)), get_delegate_target_cexpression (expr.call, out delegate_target_destroy_notify));
655 out_arg_map.set (get_param_pos (get_ccode_instance_pos (d)), get_delegate_target_cexpression (expr.call, out delegate_target_destroy_notify));
659 // structs are returned via out parameter
660 bool return_result_via_out_param = itype.get_return_type ().is_real_non_null_struct_type ();
662 // pass address for the return value of non-void signals without emitter functions
663 if (itype is SignalType && !(itype.get_return_type () is VoidType)) {
664 var sig = ((SignalType) itype).signal_symbol;
666 if (ma != null && ma.inner is BaseAccess && sig.is_virtual) {
667 // normal return value for base access
668 } else if (!get_signal_has_emitter (sig) || ma.source_reference.file == sig.source_reference.file) {
669 return_result_via_out_param = true;
673 if (async_call == ccall) {
674 // skip out parameter for .begin() calls
675 return_result_via_out_param = false;
678 CCodeExpression out_param_ref = null;
680 if (return_result_via_out_param) {
681 var out_param_var = get_temp_variable (itype.get_return_type (), true, null, true);
682 out_param_ref = get_variable_cexpression (out_param_var.name);
683 emit_temp_var (out_param_var);
684 out_arg_map.set (get_param_pos (-3), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, out_param_ref));
687 // append C arguments in the right order
689 int last_pos;
690 int min_pos;
692 if (async_call != ccall) {
693 // don't append out arguments for .begin() calls
694 last_pos = -1;
695 while (true) {
696 min_pos = -1;
697 foreach (int pos in out_arg_map.get_keys ()) {
698 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
699 min_pos = pos;
702 if (min_pos == -1) {
703 break;
705 ccall.add_argument (out_arg_map.get (min_pos));
706 last_pos = min_pos;
710 if (async_call != null) {
711 last_pos = -1;
712 while (true) {
713 min_pos = -1;
714 foreach (int pos in in_arg_map.get_keys ()) {
715 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
716 min_pos = pos;
719 if (min_pos == -1) {
720 break;
722 async_call.add_argument (in_arg_map.get (min_pos));
723 last_pos = min_pos;
727 if (expr.is_yield_expression) {
728 // set state before calling async function to support immediate callbacks
729 int state = emit_context.next_coroutine_state++;
731 ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"), new CCodeConstant (state.to_string ()));
732 ccode.add_expression (async_call);
733 ccode.add_return (new CCodeConstant ("FALSE"));
734 ccode.add_label ("_state_%d".printf (state));
737 if (expr.is_assert) {
738 string message = ((string) expr.source_reference.begin.pos).substring (0, (int) (expr.source_reference.end.pos - expr.source_reference.begin.pos));
739 ccall.call = new CCodeIdentifier ("_vala_assert");
740 ccall.add_argument (new CCodeConstant ("\"%s\"".printf (message.replace ("\n", " ").escape (""))));
741 requires_assert = true;
745 if (return_result_via_out_param) {
746 ccode.add_expression (ccall_expr);
747 ccall_expr = out_param_ref;
750 if (m != null && m.binding == MemberBinding.INSTANCE && m.returns_modified_pointer) {
751 if (ma != null && ma.inner.symbol_reference is Property && ma.inner is MemberAccess) {
752 var prop = (Property) ma.inner.symbol_reference;
753 store_property (prop, ((MemberAccess) ma.inner).inner, new GLibValue (expr.value_type, ccall_expr));
754 ccall_expr = null;
755 } else {
756 ccall_expr = new CCodeAssignment (instance, ccall_expr);
760 if (m != null && get_ccode_type (m) != null && get_ccode_type (m) != get_ccode_name (m.return_type)) {
761 // Bug 699956: Implement cast for method return type if [CCode type=] annotation is specified
762 ccall_expr = new CCodeCastExpression (ccall_expr, get_ccode_name (m.return_type));
765 if (m is ArrayResizeMethod) {
766 // FIXME: size expression must not be evaluated twice at runtime (potential side effects)
767 Iterator<Expression> arg_it = expr.get_argument_list ().iterator ();
768 arg_it.next ();
769 var new_size = get_cvalue (arg_it.get ());
771 var temp_decl = get_temp_variable (int_type);
772 var temp_ref = get_variable_cexpression (temp_decl.name);
774 emit_temp_var (temp_decl);
776 /* memset needs string.h */
777 cfile.add_include ("string.h");
779 var clen = get_array_length_cexpression (ma.inner, 1);
780 var celems = get_cvalue (ma.inner);
781 var array_type = (ArrayType) ma.inner.value_type;
782 var csizeof = new CCodeIdentifier ("sizeof (%s)".printf (get_ccode_name (array_type.element_type)));
783 var cdelta = new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, temp_ref, clen);
784 var ccheck = new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, temp_ref, clen);
786 var czero = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
787 czero.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, celems, clen));
788 czero.add_argument (new CCodeConstant ("0"));
789 czero.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, csizeof, cdelta));
791 ccode.add_assignment (temp_ref, new_size);
792 ccode.add_expression (ccall_expr);
793 ccode.add_expression (new CCodeConditionalExpression (ccheck, czero, new CCodeConstant ("NULL")));
794 ccode.add_assignment (get_array_length_cexpression (ma.inner, 1), temp_ref);
796 var array_var = ma.inner.symbol_reference;
797 var array_local = array_var as LocalVariable;
798 if (array_var != null && array_var.is_internal_symbol ()
799 && ((array_var is LocalVariable && !array_local.captured) || array_var is Field)) {
800 ccode.add_assignment (get_array_size_cvalue (ma.inner.target_value), temp_ref);
803 return;
806 if (expr.parent_node is ExpressionStatement && !expr.value_type.is_disposable ()) {
807 if (ccall_expr != null && !return_result_via_out_param) {
808 ccode.add_expression (ccall_expr);
810 } else {
811 var result_type = itype.get_return_type ();
813 if (expr.formal_value_type is GenericType && !(expr.value_type is GenericType)) {
814 var type_parameter = ((GenericType) expr.formal_value_type).type_parameter;
815 var st = type_parameter.parent_symbol.parent_symbol as Struct;
816 if (type_parameter.parent_symbol == garray_type ||
817 (st != null && get_ccode_name (st) == "va_list")) {
818 // GArray and va_list don't use pointer-based generics
819 // above logic copied from visit_expression ()
820 // TODO avoid code duplication
821 result_type = expr.value_type;
825 if (m != null && m.get_format_arg_index () >= 0) {
826 set_cvalue (expr, ccall_expr);
827 } else if (m != null && m.get_attribute_bool ("CCode", "use_inplace", false)) {
828 set_cvalue (expr, ccall_expr);
829 } else if (!return_result_via_out_param
830 && ((m != null && !has_ref_out_param (m)) || (deleg != null && !has_ref_out_param (deleg)))
831 && (result_type is ValueType && !result_type.is_disposable ())) {
832 set_cvalue (expr, ccall_expr);
833 } else if (!return_result_via_out_param) {
834 var temp_var = get_temp_variable (result_type, result_type.value_owned, null, false);
835 var temp_ref = get_variable_cexpression (temp_var.name);
837 emit_temp_var (temp_var);
839 ccode.add_assignment (temp_ref, ccall_expr);
840 set_cvalue (expr, temp_ref);
841 ((GLibValue) expr.target_value).lvalue = true;
842 } else {
843 set_cvalue (expr, ccall_expr);
844 ((GLibValue) expr.target_value).lvalue = true;
848 params_it = params.iterator ();
849 foreach (Expression arg in expr.get_argument_list ()) {
850 Parameter param = null;
852 if (params_it.next ()) {
853 param = params_it.get ();
854 if (param.params_array || param.ellipsis) {
855 // ignore ellipsis arguments as we currently don't use temporary variables for them
856 break;
860 var unary = arg as UnaryExpression;
861 if (unary == null || unary.operator != UnaryOperator.OUT) {
862 continue;
865 if (requires_destroy (unary.inner.value_type)) {
866 // unref old value
867 ccode.add_expression (destroy_value (unary.inner.target_value));
870 // assign new value
871 store_value (unary.inner.target_value, transform_value (unary.target_value, unary.inner.value_type, arg), expr.source_reference);
873 // handle out null terminated arrays
874 if (param != null && get_ccode_array_null_terminated (param)) {
875 requires_array_length = true;
876 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
877 len_call.add_argument (get_cvalue_ (unary.inner.target_value));
879 ccode.add_assignment (get_array_length_cvalue (unary.inner.target_value, 1), len_call);
883 if (m is CreationMethod && m.parent_symbol is Class && ((current_class.is_compact && current_class.base_class != null) || current_class.base_class == gsource_type)) {
884 var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (get_ccode_lower_case_name (current_class, null))));
885 cinitcall.add_argument (get_this_cexpression ());
886 ccode.add_expression (cinitcall);
890 private string generate_enum_tostring_function (Enum en) {
891 var to_string_func = "_%s_to_string".printf (get_ccode_lower_case_name (en));
893 if (!add_wrapper (to_string_func)) {
894 // wrapper already defined
895 return to_string_func;
897 // declaration
899 var function = new CCodeFunction (to_string_func, "const char*");
900 function.modifiers = CCodeModifiers.STATIC;
902 function.add_parameter (new CCodeParameter ("value", get_ccode_name (en)));
904 // definition
905 push_context (new EmitContext ());
906 push_function (function);
908 ccode.open_switch (new CCodeConstant ("value"));
909 foreach (var enum_value in en.get_values ()) {
910 ccode.add_case (new CCodeIdentifier (get_ccode_name (enum_value)));
911 ccode.add_return (new CCodeConstant ("\""+get_ccode_name (enum_value)+"\""));
913 ccode.close ();
914 ccode.add_return (new CCodeConstant ("NULL"));
916 // append to file
917 cfile.add_function_declaration (function);
918 cfile.add_function (function);
920 pop_context ();
922 return to_string_func;
925 bool has_ref_out_param (Callable c) {
926 foreach (var param in c.get_parameters ()) {
927 if (param.direction != ParameterDirection.IN) {
928 return true;
931 return false;
934 string generate_string_printf_function () {
935 if (!add_wrapper ("string_printf")) {
936 // wrapper already defined
937 return "string_printf";
940 // declaration
941 var function = new CCodeFunction ("string_printf", "char*");
942 function.add_parameter (new CCodeParameter ("format", "const char*"));
943 function.add_parameter (new CCodeParameter.with_ellipsis ());
944 function.modifiers = CCodeModifiers.STATIC;
946 // definition
947 push_context (new EmitContext ());
948 push_function (function);
950 ccode.add_declaration ("int", new CCodeVariableDeclarator ("length"));
951 ccode.add_declaration ("va_list", new CCodeVariableDeclarator ("ap"));
952 ccode.add_declaration ("char*", new CCodeVariableDeclarator ("result"));
954 var va_start = new CCodeFunctionCall (new CCodeIdentifier ("va_start"));
955 va_start.add_argument (new CCodeIdentifier ("ap"));
956 va_start.add_argument (new CCodeIdentifier ("format"));
958 ccode.add_expression (va_start);
960 var vsnprintf = new CCodeFunctionCall (new CCodeIdentifier ("vsnprintf"));
961 vsnprintf.add_argument (new CCodeConstant ("NULL"));
962 vsnprintf.add_argument (new CCodeConstant ("0"));
963 vsnprintf.add_argument (new CCodeIdentifier ("format"));
964 vsnprintf.add_argument (new CCodeIdentifier ("ap"));
966 ccode.add_assignment (new CCodeIdentifier ("length"), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, vsnprintf, new CCodeConstant ("1")));
968 var va_end = new CCodeFunctionCall (new CCodeIdentifier ("va_end"));
969 va_end.add_argument (new CCodeIdentifier ("ap"));
971 ccode.add_expression (va_end);
973 var malloc = new CCodeFunctionCall (new CCodeIdentifier ("malloc"));
974 malloc.add_argument (new CCodeIdentifier ("length"));
976 ccode.add_assignment (new CCodeIdentifier ("result"), malloc);
978 va_start = new CCodeFunctionCall (new CCodeIdentifier ("va_start"));
979 va_start.add_argument (new CCodeIdentifier ("ap"));
980 va_start.add_argument (new CCodeIdentifier ("format"));
982 ccode.add_expression (va_start);
984 vsnprintf = new CCodeFunctionCall (new CCodeIdentifier ("vsnprintf"));
985 vsnprintf.add_argument (new CCodeIdentifier ("result"));
986 vsnprintf.add_argument (new CCodeIdentifier ("length"));
987 vsnprintf.add_argument (new CCodeIdentifier ("format"));
988 vsnprintf.add_argument (new CCodeIdentifier ("ap"));
990 ccode.add_expression (vsnprintf);
992 va_end = new CCodeFunctionCall (new CCodeIdentifier ("va_end"));
993 va_end.add_argument (new CCodeIdentifier ("ap"));
995 ccode.add_expression (va_end);
997 ccode.add_return (new CCodeIdentifier ("result"));
999 // append to file
1000 cfile.add_include ("stdarg.h");
1001 cfile.add_function_declaration (function);
1002 cfile.add_function (function);
1004 pop_context ();
1006 return "string_printf";