tests: Add invalid "method" tests to increase coverage
[vala-gnome.git] / codegen / valaccodemethodcallmodule.vala
blob1beaa8ba436f68459643f63ac5484bf367af0c96
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 (expr.is_constructv_chainup) {
57 ccall.call = new CCodeIdentifier (get_ccode_constructv_name ((CreationMethod) m));
59 } else if (itype is SignalType) {
60 var sig_type = (SignalType) itype;
61 if (ma != null && ma.inner is BaseAccess && sig_type.signal_symbol.is_virtual) {
62 m = sig_type.signal_symbol.default_handler;
63 } else {
64 ccall = (CCodeFunctionCall) get_cvalue (expr.call);
66 } else if (itype is ObjectType) {
67 // constructor
68 var cl = (Class) ((ObjectType) itype).type_symbol;
69 m = cl.default_construction_method;
70 generate_method_declaration (m, cfile);
71 var real_name = get_ccode_real_name (m);
72 if (expr.is_constructv_chainup) {
73 real_name = get_ccode_constructv_name ((CreationMethod) m);
75 ccall = new CCodeFunctionCall (new CCodeIdentifier (real_name));
76 } else if (itype is StructValueType) {
77 // constructor
78 var st = (Struct) ((StructValueType) itype).type_symbol;
79 m = st.default_construction_method;
80 generate_method_declaration (m, cfile);
81 ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_real_name (m)));
82 } else if (itype is DelegateType) {
83 deleg = ((DelegateType) itype).delegate_symbol;
86 var in_arg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
87 var out_arg_map = in_arg_map;
89 if (m != null && m.coroutine) {
90 // async call
92 async_call = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (m)));
93 finish_call = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_finish_name (m)));
95 if (ma.inner is BaseAccess) {
96 if (m.base_method != null) {
97 var base_class = (Class) m.base_method.parent_symbol;
98 var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (get_ccode_upper_case_name (base_class, null))));
99 vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (get_ccode_lower_case_name (current_class, null))));
101 async_call.call = new CCodeMemberAccess.pointer (vcast, get_ccode_vfunc_name (m));
102 finish_call.call = new CCodeMemberAccess.pointer (vcast, get_ccode_finish_vfunc_name (m));
103 } else if (m.base_interface_method != null) {
104 var base_iface = (Interface) m.base_interface_method.parent_symbol;
105 string parent_iface_var = "%s_%s_parent_iface".printf (get_ccode_lower_case_name (current_class), get_ccode_lower_case_name (base_iface));
107 async_call.call = new CCodeMemberAccess.pointer (new CCodeIdentifier (parent_iface_var), get_ccode_vfunc_name (m));
108 finish_call.call = new CCodeMemberAccess.pointer (new CCodeIdentifier (parent_iface_var), get_ccode_finish_vfunc_name (m));
112 if (ma.member_name == "begin" && ma.inner.symbol_reference == ma.symbol_reference) {
113 // no finish call
114 ccall = async_call;
115 params = m.get_async_begin_parameters ();
116 } else if (ma.member_name == "end" && ma.inner.symbol_reference == ma.symbol_reference) {
117 // no async call
118 ccall = finish_call;
119 params = m.get_async_end_parameters ();
120 } else if (!expr.is_yield_expression) {
121 // same as .begin, backwards compatible to bindings without async methods
122 ccall = async_call;
123 params = m.get_async_begin_parameters ();
124 } else {
125 ccall = finish_call;
127 // output arguments used separately
128 out_arg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
129 // pass GAsyncResult stored in closure to finish function
130 out_arg_map.set (get_param_pos (0.1), new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_res_"));
134 if (m is CreationMethod && m.parent_symbol is Class) {
135 if (!((Class) m.parent_symbol).is_compact) {
136 ccall.add_argument (get_variable_cexpression ("object_type"));
139 if (!current_class.is_compact) {
140 if (current_class != m.parent_symbol) {
141 // chain up to base class
142 foreach (DataType base_type in current_class.get_base_types ()) {
143 if (base_type.data_type is Class) {
144 List<TypeParameter> type_parameters = null;
145 if (get_ccode_real_name (m) == "g_object_new") {
146 // gobject-style chainup
147 type_parameters = ((Class) base_type.data_type).get_type_parameters ();
149 add_generic_type_arguments (in_arg_map, base_type.get_type_arguments (), expr, true, type_parameters);
150 break;
153 } else {
154 // chain up to other constructor in same class
155 int type_param_index = 0;
156 var cl = (Class) m.parent_symbol;
157 foreach (TypeParameter type_param in cl.get_type_parameters ()) {
158 in_arg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ())));
159 in_arg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ())));
160 in_arg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ())));
161 type_param_index++;
164 } else if (current_class.base_class == gsource_type) {
165 // g_source_new
167 string class_prefix = get_ccode_lower_case_name (current_class);
169 var funcs = new CCodeDeclaration ("const GSourceFuncs");
170 funcs.modifiers = CCodeModifiers.STATIC;
171 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))));
172 ccode.add_statement (funcs);
174 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_source_funcs")));
176 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
177 csizeof.add_argument (new CCodeIdentifier (get_ccode_name (current_class)));
178 ccall.add_argument (csizeof);
180 } else if (m is CreationMethod && m.parent_symbol is Struct) {
181 ccall.add_argument (get_this_cexpression ());
182 } 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)) {
183 // generic method
184 // don't add generic arguments for .end() calls
185 add_generic_type_arguments (in_arg_map, ma.get_type_arguments (), expr);
188 // the complete call expression, might include casts, comma expressions, and/or assignments
189 CCodeExpression ccall_expr = ccall;
191 if (m is ArrayResizeMethod) {
192 var array_type = (ArrayType) ma.inner.value_type;
193 in_arg_map.set (get_param_pos (0), new CCodeIdentifier (get_ccode_name (array_type.element_type)));
194 } else if (m is ArrayMoveMethod) {
195 requires_array_move = true;
196 } else if (m is ArrayCopyMethod) {
197 expr.target_value = copy_value (ma.inner.target_value, expr);
198 return;
201 CCodeExpression instance = null;
202 if (m != null && m.is_async_callback) {
203 if (current_method.closure) {
204 var block = ((Method) m.parent_symbol).body;
205 instance = new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (block))), "_async_data_");
206 } else {
207 instance = new CCodeIdentifier ("_data_");
210 in_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
211 out_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
212 } else if (m != null && m.binding == MemberBinding.INSTANCE && !(m is CreationMethod)) {
213 var instance_value = ma.inner.target_value;
214 if ((ma.member_name == "begin" || ma.member_name == "end") && ma.inner.symbol_reference == ma.symbol_reference) {
215 var inner_ma = (MemberAccess) ma.inner;
216 instance_value = inner_ma.inner.target_value;
218 instance = get_cvalue_ (instance_value);
220 var st = m.parent_symbol as Struct;
221 if (st != null && !st.is_simple_type ()) {
222 // we need to pass struct instance by reference
223 if (!get_lvalue (instance_value)) {
224 instance_value = store_temp_value (instance_value, expr);
226 instance = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue_ (instance_value));
229 if (expr.is_yield_expression) {
230 in_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
231 if (get_ccode_finish_instance (m)) {
232 out_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
234 } else if (ma.member_name != "end" || get_ccode_finish_instance (m)) {
235 out_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
236 in_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), instance);
238 } else if (m != null && m.binding == MemberBinding.CLASS) {
239 var cl = (Class) m.parent_symbol;
240 var cast = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_upper_case_name (cl, null) + "_CLASS"));
242 CCodeExpression klass;
243 if (ma.inner == null) {
244 if (get_this_type () == null) {
245 // Accessing the method from a static or class constructor
246 klass = new CCodeIdentifier ("klass");
247 } else {
248 // Accessing the method from within an instance method
249 var k = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_GET_CLASS"));
250 k.add_argument (get_this_cexpression ());
251 klass = k;
253 } else {
254 // Accessing the method of an instance
255 var k = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_GET_CLASS"));
256 k.add_argument (get_cvalue (ma.inner));
257 klass = k;
260 cast.add_argument (klass);
261 in_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), cast);
262 out_arg_map.set (get_param_pos (get_ccode_instance_pos (m)), cast);
265 if (m != null && get_ccode_has_generic_type_parameter (m)) {
266 // insert type argument for macros
267 if (m.get_type_parameters ().size > 0) {
268 // generic method
269 int type_param_index = 0;
270 foreach (var type_arg in ma.get_type_arguments ()) {
271 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)));
272 type_param_index++;
274 } else {
275 // method in generic type
276 int type_param_index = 0;
277 foreach (var type_arg in ma.inner.value_type.get_type_arguments ()) {
278 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)));
279 type_param_index++;
284 if (m is ArrayMoveMethod) {
285 var array_type = (ArrayType) ma.inner.value_type;
286 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
287 csizeof.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
288 in_arg_map.set (get_param_pos (0.1), csizeof);
289 } else if (m is DynamicMethod) {
290 m.clear_parameters ();
291 int param_nr = 1;
292 foreach (Expression arg in expr.get_argument_list ()) {
293 var unary = arg as UnaryExpression;
294 if (unary != null && unary.operator == UnaryOperator.OUT) {
295 // out argument
296 var param = new Parameter ("param%d".printf (param_nr), unary.inner.value_type);
297 param.direction = ParameterDirection.OUT;
298 m.add_parameter (param);
299 } else if (unary != null && unary.operator == UnaryOperator.REF) {
300 // ref argument
301 var param = new Parameter ("param%d".printf (param_nr), unary.inner.value_type);
302 param.direction = ParameterDirection.REF;
303 m.add_parameter (param);
304 } else {
305 // in argument
306 m.add_parameter (new Parameter ("param%d".printf (param_nr), arg.value_type));
308 param_nr++;
310 foreach (Parameter param in m.get_parameters ()) {
311 param.accept (this);
313 generate_dynamic_method_wrapper ((DynamicMethod) m);
314 } else if (m is CreationMethod && m.parent_symbol is Class) {
315 ccode.add_assignment (get_this_cexpression (), new CCodeCastExpression (ccall, get_ccode_name (current_class) + "*"));
317 if (current_method.body.captured) {
318 // capture self after setting it
319 var ref_call = new CCodeFunctionCall (get_dup_func_expression (new ObjectType (current_class), expr.source_reference));
320 ref_call.add_argument (get_this_cexpression ());
322 ccode.add_assignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (current_method.body))), "self"), ref_call);
325 if (!current_class.is_compact && current_class.get_type_parameters ().size > 0) {
326 /* type, dup func, and destroy func fields for generic types */
327 var suffices = new string[] {"type", "dup_func", "destroy_func"};
328 foreach (TypeParameter type_param in current_class.get_type_parameters ()) {
329 var priv_access = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv");
331 foreach (string suffix in suffices) {
332 var param_name = new CCodeIdentifier ("%s_%s".printf (type_param.name.down (), suffix));
333 ccode.add_assignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
337 // object chainup can't be used as expression
338 ccall_expr = null;
341 bool ellipsis = false;
343 int i = 1;
344 int arg_pos;
345 Iterator<Parameter> params_it = params.iterator ();
346 foreach (Expression arg in expr.get_argument_list ()) {
347 CCodeExpression cexpr = get_cvalue (arg);
349 var carg_map = in_arg_map;
351 if (params_it.next ()) {
352 var param = params_it.get ();
353 ellipsis = param.params_array || param.ellipsis;
354 if (!ellipsis) {
355 if (param.direction == ParameterDirection.OUT) {
356 carg_map = out_arg_map;
359 var unary = arg as UnaryExpression;
360 if (unary == null || unary.operator != UnaryOperator.OUT) {
361 if (get_ccode_array_length (param) && param.variable_type is ArrayType) {
362 var array_type = (ArrayType) param.variable_type;
363 for (int dim = 1; dim <= array_type.rank; dim++) {
364 CCodeExpression? array_length_expr = null;
365 if (get_ccode_array_length_type (param) != null) {
366 array_length_expr = new CCodeCastExpression (get_array_length_cexpression (arg, dim), get_ccode_array_length_type (param));
367 } else {
368 array_length_expr = get_array_length_cexpression (arg, dim);
370 carg_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), array_length_expr);
372 } else if (param.variable_type is DelegateType) {
373 var deleg_type = (DelegateType) param.variable_type;
374 var d = deleg_type.delegate_symbol;
375 if (d.has_target) {
376 CCodeExpression delegate_target_destroy_notify;
377 var delegate_target = get_delegate_target_cexpression (arg, out delegate_target_destroy_notify);
378 assert (delegate_target != null);
379 if (get_ccode_type (param) == "GClosure*") {
380 // one single GClosure parameter
381 var closure_new = new CCodeFunctionCall (new CCodeIdentifier ("g_cclosure_new"));
382 closure_new.add_argument (new CCodeCastExpression (cexpr, "GCallback"));
383 closure_new.add_argument (delegate_target);
384 closure_new.add_argument (new CCodeCastExpression (delegate_target_destroy_notify, "GClosureNotify"));
385 cexpr = new CCodeConditionalExpression (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cexpr, new CCodeIdentifier ("NULL")), new CCodeIdentifier ("NULL"), closure_new);
386 } else {
387 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), delegate_target);
388 if (deleg_type.is_disposable ()) {
389 assert (delegate_target_destroy_notify != null);
390 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param) + 0.01), delegate_target_destroy_notify);
394 } else if (param.variable_type is MethodType) {
395 // callbacks in dynamic method calls
396 CCodeExpression delegate_target_destroy_notify;
397 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), get_delegate_target_cexpression (arg, out delegate_target_destroy_notify));
398 } else if (param.variable_type is GenericType) {
399 if (m != null && get_ccode_simple_generics (m)) {
400 var generic_type = (GenericType) param.variable_type;
401 int type_param_index = m.get_type_parameter_index (generic_type.type_parameter.name);
402 var type_arg = ma.get_type_arguments ().get (type_param_index);
403 if (param.variable_type.value_owned) {
404 if (requires_copy (type_arg)) {
405 carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (param)), get_destroy_func_expression (type_arg));
406 } else {
407 carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (param)), new CCodeConstant ("NULL"));
413 cexpr = handle_struct_argument (param, arg, cexpr);
414 } else {
415 arg.target_value = null;
417 var temp_var = get_temp_variable (param.variable_type, param.variable_type.value_owned, null, true);
418 emit_temp_var (temp_var);
419 set_cvalue (arg, get_variable_cexpression (temp_var.name));
420 arg.target_value.value_type = arg.target_type;
422 cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue (arg));
424 if (get_ccode_array_length (param) && param.variable_type is ArrayType) {
425 var array_type = (ArrayType) param.variable_type;
426 var array_length_type = int_type;
427 if (get_ccode_array_length_type (param) != null) {
428 array_length_type = new CType (get_ccode_array_length_type (param));
430 for (int dim = 1; dim <= array_type.rank; dim++) {
431 var temp_array_length = get_temp_variable (array_length_type);
432 emit_temp_var (temp_array_length);
433 append_array_length (arg, get_variable_cexpression (temp_array_length.name));
434 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)));
436 } else if (param.variable_type is DelegateType) {
437 var deleg_type = (DelegateType) param.variable_type;
438 var d = deleg_type.delegate_symbol;
439 if (d.has_target) {
440 temp_var = get_temp_variable (new PointerType (new VoidType ()), true, null, true);
441 emit_temp_var (temp_var);
442 set_delegate_target (arg, get_variable_cexpression (temp_var.name));
443 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_delegate_target (arg)));
444 if (deleg_type.is_disposable ()) {
445 temp_var = get_temp_variable (gdestroynotify_type, true, null, true);
446 emit_temp_var (temp_var);
447 set_delegate_target_destroy_notify (arg, get_variable_cexpression (temp_var.name));
448 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)));
454 if (get_ccode_type (param) != null) {
455 cexpr = new CCodeCastExpression (cexpr, get_ccode_type (param));
457 } else {
458 cexpr = handle_struct_argument (null, arg, cexpr);
460 arg_pos = get_param_pos (get_ccode_pos (param), ellipsis);
461 } else {
462 // default argument position
463 cexpr = handle_struct_argument (null, arg, cexpr);
464 arg_pos = get_param_pos (i, ellipsis);
467 carg_map.set (arg_pos, cexpr);
469 if (arg is NamedArgument && ellipsis) {
470 var named_arg = (NamedArgument) arg;
471 string name = string.joinv ("-", named_arg.name.split ("_"));
472 carg_map.set (get_param_pos (i - 0.1, ellipsis), new CCodeConstant ("\"%s\"".printf (name)));
475 i++;
477 if (params_it.next ()) {
478 var param = params_it.get ();
480 /* if there are more parameters than arguments,
481 * the additional parameter is an ellipsis parameter
482 * otherwise there is a bug in the semantic analyzer
484 assert (param.params_array || param.ellipsis);
485 ellipsis = true;
488 /* add length argument for methods returning arrays */
489 if (m != null && m.return_type is ArrayType && async_call != ccall) {
490 var array_type = (ArrayType) m.return_type;
491 for (int dim = 1; dim <= array_type.rank; dim++) {
492 if (get_ccode_array_null_terminated (m)) {
493 // handle calls to methods returning null-terminated arrays
494 var temp_var = get_temp_variable (itype.get_return_type (), true, null, false);
495 var temp_ref = get_variable_cexpression (temp_var.name);
497 emit_temp_var (temp_var);
499 ccall_expr = new CCodeAssignment (temp_ref, ccall_expr);
501 requires_array_length = true;
502 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
503 len_call.add_argument (temp_ref);
505 append_array_length (expr, len_call);
506 } else if (get_ccode_array_length (m)) {
507 LocalVariable temp_var;
509 if (get_ccode_array_length_type (m) == null) {
510 temp_var = get_temp_variable (int_type);
511 } else {
512 temp_var = get_temp_variable (new CType (get_ccode_array_length_type (m)));
514 var temp_ref = get_variable_cexpression (temp_var.name);
516 emit_temp_var (temp_var);
518 out_arg_map.set (get_param_pos (get_ccode_array_length_pos (m) + 0.01 * dim), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
520 append_array_length (expr, temp_ref);
521 } else if (get_ccode_array_length_expr (m) != null) {
522 append_array_length (expr, new CCodeConstant (get_ccode_array_length_expr (m)));
523 } else {
524 append_array_length (expr, new CCodeConstant ("-1"));
527 } else if (m != null && m.return_type is DelegateType && async_call != ccall) {
528 var deleg_type = (DelegateType) m.return_type;
529 var d = deleg_type.delegate_symbol;
530 if (d.has_target) {
531 var temp_var = get_temp_variable (new PointerType (new VoidType ()));
532 var temp_ref = get_variable_cexpression (temp_var.name);
534 emit_temp_var (temp_var);
536 out_arg_map.set (get_param_pos (get_ccode_delegate_target_pos (m)), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
538 set_delegate_target (expr, temp_ref);
540 if (deleg_type.is_disposable ()) {
541 temp_var = get_temp_variable (gdestroynotify_type);
542 temp_ref = get_variable_cexpression (temp_var.name);
544 emit_temp_var (temp_var);
546 out_arg_map.set (get_param_pos (get_ccode_delegate_target_pos (m) + 0.01), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
548 set_delegate_target_destroy_notify (expr, temp_ref);
549 } else {
550 set_delegate_target_destroy_notify (expr, new CCodeConstant ("NULL"));
552 } else {
553 set_delegate_target (expr, new CCodeConstant ("NULL"));
557 // add length argument for delegates returning arrays
558 // TODO: avoid code duplication with methods returning arrays, see above
559 if (deleg != null && deleg.return_type is ArrayType) {
560 var array_type = (ArrayType) deleg.return_type;
561 for (int dim = 1; dim <= array_type.rank; dim++) {
562 if (get_ccode_array_null_terminated (deleg)) {
563 // handle calls to methods returning null-terminated arrays
564 var temp_var = get_temp_variable (itype.get_return_type (), true, null, false);
565 var temp_ref = get_variable_cexpression (temp_var.name);
567 emit_temp_var (temp_var);
569 ccall_expr = new CCodeAssignment (temp_ref, ccall_expr);
571 requires_array_length = true;
572 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
573 len_call.add_argument (temp_ref);
575 append_array_length (expr, len_call);
576 } else if (get_ccode_array_length (deleg)) {
577 var temp_var = get_temp_variable (int_type);
578 var temp_ref = get_variable_cexpression (temp_var.name);
580 emit_temp_var (temp_var);
582 out_arg_map.set (get_param_pos (get_ccode_array_length_pos (deleg) + 0.01 * dim), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
584 append_array_length (expr, temp_ref);
585 } else {
586 append_array_length (expr, new CCodeConstant ("-1"));
589 } else if (deleg != null && deleg.return_type is DelegateType) {
590 var deleg_type = (DelegateType) deleg.return_type;
591 var d = deleg_type.delegate_symbol;
592 if (d.has_target) {
593 var temp_var = get_temp_variable (new PointerType (new VoidType ()));
594 var temp_ref = get_variable_cexpression (temp_var.name);
596 emit_temp_var (temp_var);
598 out_arg_map.set (get_param_pos (get_ccode_delegate_target_pos (deleg)), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
600 set_delegate_target (expr, temp_ref);
602 if (deleg_type.is_disposable ()) {
603 temp_var = get_temp_variable (gdestroynotify_type);
604 temp_ref = get_variable_cexpression (temp_var.name);
606 emit_temp_var (temp_var);
608 out_arg_map.set (get_param_pos (get_ccode_delegate_target_pos (deleg) + 0.01), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, temp_ref));
610 set_delegate_target_destroy_notify (expr, temp_ref);
615 if (m != null && m.coroutine) {
616 if (expr.is_yield_expression) {
617 // asynchronous call
618 in_arg_map.set (get_param_pos (-1), new CCodeIdentifier (generate_ready_function (current_method)));
619 in_arg_map.set (get_param_pos (-0.9), new CCodeIdentifier ("_data_"));
623 if (expr.tree_can_fail) {
624 // method can fail
625 current_method_inner_error = true;
626 // add &inner_error before the ellipsis arguments
627 out_arg_map.set (get_param_pos (-1), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression ("_inner_error_")));
628 } else if (m != null && m.has_error_type_parameter () && async_call != ccall) {
629 // inferred error argument from base method
630 out_arg_map.set (get_param_pos (-1), new CCodeConstant ("NULL"));
633 if (ellipsis) {
634 /* ensure variable argument list ends with NULL
635 * except when using printf-style arguments */
636 if (m == null) {
637 in_arg_map.set (get_param_pos (-1, true), new CCodeConstant ("NULL"));
638 } else if (!m.printf_format && !m.scanf_format && get_ccode_sentinel (m) != "" && !expr.is_constructv_chainup) {
639 in_arg_map.set (get_param_pos (-1, true), new CCodeConstant (get_ccode_sentinel (m)));
643 if (itype is DelegateType) {
644 var deleg_type = (DelegateType) itype;
645 var d = deleg_type.delegate_symbol;
646 if (d.has_target) {
647 CCodeExpression delegate_target_destroy_notify;
648 in_arg_map.set (get_param_pos (get_ccode_instance_pos (d)), get_delegate_target_cexpression (expr.call, out delegate_target_destroy_notify));
649 out_arg_map.set (get_param_pos (get_ccode_instance_pos (d)), get_delegate_target_cexpression (expr.call, out delegate_target_destroy_notify));
653 // structs are returned via out parameter
654 bool return_result_via_out_param = itype.get_return_type ().is_real_non_null_struct_type ();
656 // pass address for the return value of non-void signals without emitter functions
657 if (itype is SignalType && !(itype.get_return_type () is VoidType)) {
658 var sig = ((SignalType) itype).signal_symbol;
660 if (ma != null && ma.inner is BaseAccess && sig.is_virtual) {
661 // normal return value for base access
662 } else if (!get_signal_has_emitter (sig) || ma.source_reference.file == sig.source_reference.file) {
663 return_result_via_out_param = true;
667 if (async_call == ccall) {
668 // skip out parameter for .begin() calls
669 return_result_via_out_param = false;
672 CCodeExpression out_param_ref = null;
674 if (return_result_via_out_param) {
675 var out_param_var = get_temp_variable (itype.get_return_type (), true, null, true);
676 out_param_ref = get_variable_cexpression (out_param_var.name);
677 emit_temp_var (out_param_var);
678 out_arg_map.set (get_param_pos (-3), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, out_param_ref));
681 // append C arguments in the right order
683 int last_pos;
684 int min_pos;
686 if (async_call != ccall) {
687 // don't append out arguments for .begin() calls
688 last_pos = -1;
689 while (true) {
690 min_pos = -1;
691 foreach (int pos in out_arg_map.get_keys ()) {
692 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
693 min_pos = pos;
696 if (min_pos == -1) {
697 break;
699 ccall.add_argument (out_arg_map.get (min_pos));
700 last_pos = min_pos;
704 if (async_call != null) {
705 last_pos = -1;
706 while (true) {
707 min_pos = -1;
708 foreach (int pos in in_arg_map.get_keys ()) {
709 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
710 min_pos = pos;
713 if (min_pos == -1) {
714 break;
716 async_call.add_argument (in_arg_map.get (min_pos));
717 last_pos = min_pos;
721 if (expr.is_yield_expression) {
722 // set state before calling async function to support immediate callbacks
723 int state = emit_context.next_coroutine_state++;
725 ccode.add_assignment (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data_"), "_state_"), new CCodeConstant (state.to_string ()));
726 ccode.add_expression (async_call);
727 ccode.add_return (new CCodeConstant ("FALSE"));
728 ccode.add_label ("_state_%d".printf (state));
731 if (expr.is_assert) {
732 string message = ((string) expr.source_reference.begin.pos).substring (0, (int) (expr.source_reference.end.pos - expr.source_reference.begin.pos));
733 ccall.call = new CCodeIdentifier ("_vala_assert");
734 ccall.add_argument (new CCodeConstant ("\"%s\"".printf (message.replace ("\n", " ").escape (""))));
735 requires_assert = true;
739 if (return_result_via_out_param) {
740 ccode.add_expression (ccall_expr);
741 ccall_expr = out_param_ref;
744 if (m != null && m.binding == MemberBinding.INSTANCE && m.returns_modified_pointer) {
745 if (ma != null && ma.inner.symbol_reference is Property && ma.inner is MemberAccess) {
746 var prop = (Property) ma.inner.symbol_reference;
747 store_property (prop, ((MemberAccess) ma.inner).inner, new GLibValue (expr.value_type, ccall_expr));
748 ccall_expr = null;
749 } else {
750 ccall_expr = new CCodeAssignment (instance, ccall_expr);
754 if (m != null && get_ccode_type (m) != null && get_ccode_type (m) != get_ccode_name (m.return_type)) {
755 // Bug 699956: Implement cast for method return type if [CCode type=] annotation is specified
756 ccall_expr = new CCodeCastExpression (ccall_expr, get_ccode_name (m.return_type));
759 if (m is ArrayResizeMethod) {
760 // FIXME: size expression must not be evaluated twice at runtime (potential side effects)
761 Iterator<Expression> arg_it = expr.get_argument_list ().iterator ();
762 arg_it.next ();
763 var new_size = get_cvalue (arg_it.get ());
765 var temp_decl = get_temp_variable (int_type);
766 var temp_ref = get_variable_cexpression (temp_decl.name);
768 emit_temp_var (temp_decl);
770 /* memset needs string.h */
771 cfile.add_include ("string.h");
773 var clen = get_array_length_cexpression (ma.inner, 1);
774 var celems = get_cvalue (ma.inner);
775 var array_type = (ArrayType) ma.inner.value_type;
776 var csizeof = new CCodeIdentifier ("sizeof (%s)".printf (get_ccode_name (array_type.element_type)));
777 var cdelta = new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, temp_ref, clen);
778 var ccheck = new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, temp_ref, clen);
780 var czero = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
781 czero.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, celems, clen));
782 czero.add_argument (new CCodeConstant ("0"));
783 czero.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, csizeof, cdelta));
785 ccode.add_assignment (temp_ref, new_size);
786 ccode.add_expression (ccall_expr);
787 ccode.add_expression (new CCodeConditionalExpression (ccheck, czero, new CCodeConstant ("NULL")));
788 ccode.add_assignment (get_array_length_cexpression (ma.inner, 1), temp_ref);
790 var array_var = ma.inner.symbol_reference;
791 var array_local = array_var as LocalVariable;
792 if (array_var != null && array_var.is_internal_symbol ()
793 && ((array_var is LocalVariable && !array_local.captured) || array_var is Field)) {
794 ccode.add_assignment (get_array_size_cvalue (ma.inner.target_value), temp_ref);
797 return;
800 if (expr.parent_node is ExpressionStatement && !expr.value_type.is_disposable ()) {
801 if (ccall_expr != null && !return_result_via_out_param) {
802 ccode.add_expression (ccall_expr);
804 } else {
805 var result_type = itype.get_return_type ();
807 if (expr.formal_value_type is GenericType && !(expr.value_type is GenericType)) {
808 var type_parameter = ((GenericType) expr.formal_value_type).type_parameter;
809 var st = type_parameter.parent_symbol.parent_symbol as Struct;
810 if (type_parameter.parent_symbol == garray_type ||
811 (st != null && get_ccode_name (st) == "va_list")) {
812 // GArray and va_list don't use pointer-based generics
813 // above logic copied from visit_expression ()
814 // TODO avoid code duplication
815 result_type = expr.value_type;
819 if (m != null && m.get_format_arg_index () >= 0) {
820 set_cvalue (expr, ccall_expr);
821 } else if (m != null && m.get_attribute_bool ("CCode", "use_inplace", false)) {
822 set_cvalue (expr, ccall_expr);
823 } else if (!return_result_via_out_param
824 && ((m != null && !has_ref_out_param (m)) || (deleg != null && !has_ref_out_param (deleg)))
825 && (result_type is ValueType && !result_type.is_disposable ())) {
826 set_cvalue (expr, ccall_expr);
827 } else if (!return_result_via_out_param) {
828 var temp_var = get_temp_variable (result_type, result_type.value_owned, null, false);
829 var temp_ref = get_variable_cexpression (temp_var.name);
831 emit_temp_var (temp_var);
833 ccode.add_assignment (temp_ref, ccall_expr);
834 set_cvalue (expr, temp_ref);
835 ((GLibValue) expr.target_value).lvalue = true;
836 } else {
837 set_cvalue (expr, ccall_expr);
838 ((GLibValue) expr.target_value).lvalue = true;
842 params_it = params.iterator ();
843 foreach (Expression arg in expr.get_argument_list ()) {
844 Parameter param = null;
846 if (params_it.next ()) {
847 param = params_it.get ();
848 if (param.params_array || param.ellipsis) {
849 // ignore ellipsis arguments as we currently don't use temporary variables for them
850 break;
854 var unary = arg as UnaryExpression;
855 if (unary == null || unary.operator != UnaryOperator.OUT) {
856 continue;
859 if (requires_destroy (unary.inner.value_type)) {
860 // unref old value
861 ccode.add_expression (destroy_value (unary.inner.target_value));
864 // assign new value
865 store_value (unary.inner.target_value, transform_value (unary.target_value, unary.inner.value_type, arg), expr.source_reference);
867 // handle out null terminated arrays
868 if (param != null && get_ccode_array_null_terminated (param)) {
869 requires_array_length = true;
870 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
871 len_call.add_argument (get_cvalue_ (unary.inner.target_value));
873 ccode.add_assignment (get_array_length_cvalue (unary.inner.target_value, 1), len_call);
877 if (m is CreationMethod && m.parent_symbol is Class && ((current_class.is_compact && current_class.base_class != null) || current_class.base_class == gsource_type)) {
878 var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (get_ccode_lower_case_name (current_class, null))));
879 cinitcall.add_argument (get_this_cexpression ());
880 ccode.add_expression (cinitcall);
884 private string generate_enum_tostring_function (Enum en) {
885 var to_string_func = "_%s_to_string".printf (get_ccode_lower_case_name (en));
887 if (!add_wrapper (to_string_func)) {
888 // wrapper already defined
889 return to_string_func;
891 // declaration
893 var function = new CCodeFunction (to_string_func, "const char*");
894 function.modifiers = CCodeModifiers.STATIC;
896 function.add_parameter (new CCodeParameter ("value", get_ccode_name (en)));
898 // definition
899 push_context (new EmitContext ());
900 push_function (function);
902 ccode.open_switch (new CCodeConstant ("value"));
903 foreach (var enum_value in en.get_values ()) {
904 ccode.add_case (new CCodeIdentifier (get_ccode_name (enum_value)));
905 ccode.add_return (new CCodeConstant ("\""+get_ccode_name (enum_value)+"\""));
907 ccode.close ();
908 ccode.add_return (new CCodeConstant ("NULL"));
910 // append to file
911 cfile.add_function_declaration (function);
912 cfile.add_function (function);
914 pop_context ();
916 return to_string_func;
919 bool has_ref_out_param (Callable c) {
920 foreach (var param in c.get_parameters ()) {
921 if (param.direction != ParameterDirection.IN) {
922 return true;
925 return false;