Release 0.41.92
[vala-gnome.git] / vala / valamethodcall.vala
blob8f68dc4bd40e4aff4b9464277b34f6c80d0e7595
1 /* valamethodcall.vala
3 * Copyright (C) 2006-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 /**
26 * Represents an invocation expression in the source code.
28 public class Vala.MethodCall : Expression {
29 /**
30 * The method to call.
32 public Expression call {
33 get { return _call; }
34 set {
35 _call = value;
36 _call.parent_node = this;
40 public bool is_yield_expression { get; set; }
42 public bool is_assert { get; private set; }
44 /**
45 * Whether this chain up uses the constructv function with va_list.
47 public bool is_constructv_chainup { get; private set; }
49 public bool is_chainup { get; private set; }
51 private Expression _call;
53 private List<Expression> argument_list = new ArrayList<Expression> ();
55 /**
56 * Creates a new invocation expression.
58 * @param call method to call
59 * @param source_reference reference to source code
60 * @return newly created invocation expression
62 public MethodCall (Expression call, SourceReference? source_reference = null) {
63 this.source_reference = source_reference;
64 this.call = call;
67 /**
68 * Appends the specified expression to the list of arguments.
70 * @param arg an argument
72 public void add_argument (Expression arg) {
73 argument_list.add (arg);
74 arg.parent_node = this;
77 /**
78 * Returns a copy of the argument list.
80 * @return argument list
82 public List<Expression> get_argument_list () {
83 return argument_list;
86 public override void accept (CodeVisitor visitor) {
87 visitor.visit_method_call (this);
89 visitor.visit_expression (this);
92 public override void accept_children (CodeVisitor visitor) {
93 call.accept (visitor);
95 foreach (Expression expr in argument_list) {
96 expr.accept (visitor);
100 public override void replace_expression (Expression old_node, Expression new_node) {
101 if (call == old_node) {
102 call = new_node;
105 int index = argument_list.index_of (old_node);
106 if (index >= 0 && new_node.parent_node == null) {
107 argument_list[index] = new_node;
108 new_node.parent_node = this;
112 public override bool is_constant () {
113 var method_type = call.value_type as MethodType;
115 if (method_type != null) {
116 // N_ and NC_ do not have any effect on the C code,
117 // they are only interpreted by xgettext
118 // this means that it is ok to use them in constant initializers
119 if (method_type.method_symbol.get_full_name () == "GLib.N_") {
120 // first argument is string
121 return argument_list[0].is_constant ();
122 } else if (method_type.method_symbol.get_full_name () == "GLib.NC_") {
123 // second argument is string
124 return argument_list[1].is_constant ();
128 return false;
131 public override bool is_pure () {
132 return false;
135 public override bool is_accessible (Symbol sym) {
136 foreach (var arg in argument_list) {
137 if (!arg.is_accessible (sym)) {
138 return false;
142 return call.is_accessible (sym);
145 public override bool check (CodeContext context) {
146 if (checked) {
147 return !error;
150 checked = true;
152 if (!call.check (context)) {
153 /* if method resolving didn't succeed, skip this check */
154 error = true;
155 return false;
158 // type of target object
159 DataType target_object_type = null;
161 List<DataType> method_type_args = null;
163 if (call.value_type is DelegateType) {
164 // delegate invocation, resolve generic types relative to delegate
165 target_object_type = call.value_type;
166 } else if (call is MemberAccess) {
167 var ma = (MemberAccess) call;
168 if (ma.prototype_access) {
169 error = true;
170 Report.error (source_reference, "Access to instance member `%s' denied".printf (call.symbol_reference.get_full_name ()));
171 return false;
174 method_type_args = ma.get_type_arguments ();
176 if (ma.inner != null) {
177 target_object_type = ma.inner.value_type;
179 // foo is relevant instance in foo.bar.connect (on_bar)
180 if (ma.inner.symbol_reference is Signal) {
181 var sig = ma.inner as MemberAccess;
182 if (sig != null) {
183 target_object_type = sig.inner.value_type;
187 // foo is relevant instance in foo.bar.begin (bar_ready) and foo.bar.end (result)
188 var m = ma.symbol_reference as Method;
189 if (m != null && m.coroutine) {
190 // begin or end call of async method
191 if (ma.member_name == "begin" || ma.member_name == "end") {
192 var method_access = ma.inner as MemberAccess;
193 if (method_access != null && method_access.inner != null) {
194 target_object_type = method_access.inner.value_type;
195 } else {
196 // static method
197 target_object_type = null;
203 if (ma.symbol_reference != null && ma.symbol_reference.get_attribute ("Assert") != null) {
204 this.is_assert = true;
206 var args = get_argument_list ();
207 if (args.size == 1) {
208 this.source_reference = args[0].source_reference;
213 var mtype = call.value_type;
214 var gobject_chainup = (context.profile == Profile.GOBJECT && call.symbol_reference == context.analyzer.object_type);
215 is_chainup = gobject_chainup;
217 if (!gobject_chainup) {
218 var expr = call;
219 var ma = expr as MemberAccess;
220 if (ma != null && ma.symbol_reference is CreationMethod) {
221 expr = ma.inner;
222 ma = expr as MemberAccess;
224 if (ma != null && ma.member_name == "this") {
225 // this[.with_foo] ()
226 is_chainup = true;
227 } else if (expr is BaseAccess) {
228 // base[.with_foo] ()
229 is_chainup = true;
233 CreationMethod base_cm = null;
235 if (is_chainup) {
236 var cm = context.analyzer.find_current_method () as CreationMethod;
237 if (cm == null) {
238 error = true;
239 Report.error (source_reference, "invocation not supported in this context");
240 return false;
241 } else if (cm.chain_up) {
242 error = true;
243 Report.error (source_reference, "Multiple constructor calls in the same constructor are not permitted");
244 return false;
246 cm.chain_up = true;
248 if (mtype is ObjectType) {
249 var otype = (ObjectType) mtype;
250 var cl = (Class) otype.type_symbol;
251 base_cm = cl.default_construction_method;
252 if (base_cm == null) {
253 error = true;
254 Report.error (source_reference, "chain up to `%s' not supported".printf (cl.get_full_name ()));
255 return false;
256 } else if (!base_cm.has_construct_function) {
257 error = true;
258 Report.error (source_reference, "chain up to `%s' not supported".printf (base_cm.get_full_name ()));
259 return false;
261 } else if (call.symbol_reference is CreationMethod && call.symbol_reference.parent_symbol is Class) {
262 base_cm = (CreationMethod) call.symbol_reference;
263 if (!base_cm.has_construct_function) {
264 error = true;
265 Report.error (source_reference, "chain up to `%s' not supported".printf (base_cm.get_full_name ()));
266 return false;
268 } else if (gobject_chainup) {
269 var cl = cm.parent_symbol as Class;
270 if (cl == null || !cl.is_subtype_of (context.analyzer.object_type)) {
271 error = true;
272 Report.error (source_reference, "chain up to `GLib.Object' not supported");
273 return false;
275 call.value_type = new ObjectType (context.analyzer.object_type);
276 mtype = call.value_type;
280 // check for struct construction
281 if (call is MemberAccess &&
282 ((call.symbol_reference is CreationMethod
283 && call.symbol_reference.parent_symbol is Struct)
284 || call.symbol_reference is Struct)) {
285 var st = call.symbol_reference as Struct;
286 if (st != null && st.default_construction_method == null && (st.is_boolean_type () || st.is_integer_type () || st.is_floating_type ())) {
287 error = true;
288 Report.error (source_reference, "invocation not supported in this context");
289 return false;
292 var struct_creation_expression = new ObjectCreationExpression ((MemberAccess) call, source_reference);
293 struct_creation_expression.struct_creation = true;
294 foreach (Expression arg in get_argument_list ()) {
295 struct_creation_expression.add_argument (arg);
297 struct_creation_expression.target_type = target_type;
298 context.analyzer.replaced_nodes.add (this);
299 parent_node.replace_expression (this, struct_creation_expression);
300 struct_creation_expression.check (context);
301 return true;
302 } else if (!is_chainup && call is MemberAccess && call.symbol_reference is CreationMethod) {
303 error = true;
304 Report.error (source_reference, "use `new' operator to create new objects");
305 return false;
308 if (!is_chainup && mtype is ObjectType) {
309 // prevent funny stuff like (new Object ()) ()
310 error = true;
311 Report.error (source_reference, "invocation not supported in this context");
312 return false;
313 } else if (mtype != null && mtype.is_invokable ()) {
314 // call ok, expression is invokable
315 } else if (call.symbol_reference is Class) {
316 error = true;
317 Report.error (source_reference, "use `new' operator to create new objects");
318 return false;
319 } else {
320 error = true;
321 Report.error (source_reference, "invocation not supported in this context");
322 return false;
325 var ret_type = mtype.get_return_type ();
326 var params = mtype.get_parameters ();
328 if (mtype is MethodType) {
329 var m = ((MethodType) mtype).method_symbol;
330 if (m != null && m.coroutine) {
331 var ma = (MemberAccess) call;
332 if (!is_yield_expression) {
333 // begin or end call of async method
334 if (ma.member_name != "end") {
335 // begin (possibly implicit)
336 if (ma.member_name != "begin") {
337 Report.deprecated (ma.source_reference, "implicit .begin is deprecated");
339 params = m.get_async_begin_parameters ();
340 ret_type = new VoidType ();
341 } else {
342 // end
343 params = m.get_async_end_parameters ();
345 } else if (ma.member_name == "begin" || ma.member_name == "end") {
346 error = true;
347 Report.error (ma.source_reference, "use of `%s' not allowed in yield statement".printf (ma.member_name));
351 if (m != null) {
352 var ma = (MemberAccess) call;
353 int n_type_params = m.get_type_parameters ().size;
354 int n_type_args = ma.get_type_arguments ().size;
355 if (n_type_args > 0 && n_type_args < n_type_params) {
356 error = true;
357 Report.error (ma.source_reference, "too few type arguments");
358 return false;
359 } else if (n_type_args > 0 && n_type_args > n_type_params) {
360 error = true;
361 Report.error (ma.source_reference, "too many type arguments");
362 return false;
367 // FIXME partial code duplication in ObjectCreationExpression.check
369 Expression last_arg = null;
371 var args = get_argument_list ();
372 Iterator<Expression> arg_it = args.iterator ();
373 foreach (Parameter param in params) {
374 if (!param.check (context)) {
375 error = true;
378 if (param.ellipsis) {
379 break;
382 if (param.params_array) {
383 var array_type = (ArrayType) param.variable_type;
384 while (arg_it.next ()) {
385 Expression arg = arg_it.get ();
387 /* store expected type for callback parameters */
388 arg.target_type = array_type.element_type;
389 arg.target_type.value_owned = array_type.value_owned;
391 break;
394 if (arg_it.next ()) {
395 Expression arg = arg_it.get ();
397 /* store expected type for callback parameters */
398 arg.formal_target_type = param.variable_type;
399 arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, method_type_args, this);
401 last_arg = arg;
405 // concatenate stringified arguments for methods with attribute [Print]
406 if (mtype is MethodType && ((MethodType) mtype).method_symbol.get_attribute ("Print") != null) {
407 var template = new Template (source_reference);
408 foreach (Expression arg in argument_list) {
409 arg.parent_node = null;
410 template.add_expression (arg);
412 argument_list.clear ();
413 add_argument (template);
416 // printf arguments
417 if (mtype is MethodType && ((MethodType) mtype).method_symbol.printf_format) {
418 StringLiteral format_literal = null;
419 if (last_arg is NullLiteral) {
420 // do not replace explicit null
421 } else if (last_arg != null) {
422 // use last argument as format string
423 format_literal = StringLiteral.get_format_literal (last_arg);
424 if (format_literal == null && args.size == params.size - 1) {
425 // insert "%s" to avoid issues with embedded %
426 format_literal = new StringLiteral ("\"%s\"");
427 format_literal.target_type = context.analyzer.string_type.copy ();
428 argument_list.insert (args.size - 1, format_literal);
430 // recreate iterator and skip to right position
431 arg_it = argument_list.iterator ();
432 foreach (Parameter param in params) {
433 if (param.ellipsis) {
434 break;
436 arg_it.next ();
439 } else {
440 // use instance as format string for string.printf (...)
441 var ma = call as MemberAccess;
442 if (ma != null) {
443 format_literal = StringLiteral.get_format_literal (ma.inner);
446 if (format_literal != null) {
447 string format = format_literal.eval ();
448 if (!context.analyzer.check_print_format (format, arg_it, source_reference)) {
449 return false;
454 foreach (Expression arg in get_argument_list ()) {
455 arg.check (context);
458 if (ret_type is VoidType) {
459 // void return type
460 if (!(parent_node is ExpressionStatement)
461 && !(parent_node is ForStatement)
462 && !(parent_node is YieldStatement)) {
463 // A void method invocation can be in the initializer or
464 // iterator of a for statement
465 error = true;
466 Report.error (source_reference, "invocation of void method not allowed as expression");
467 return false;
471 formal_value_type = ret_type.copy ();
472 value_type = formal_value_type.get_actual_type (target_object_type, method_type_args, this);
474 bool may_throw = false;
476 if (mtype is MethodType) {
477 var m = ((MethodType) mtype).method_symbol;
478 if (is_yield_expression) {
479 if (!m.coroutine) {
480 error = true;
481 Report.error (source_reference, "yield expression requires async method");
483 if (context.analyzer.current_method == null || !context.analyzer.current_method.coroutine) {
484 error = true;
485 Report.error (source_reference, "yield expression not available outside async method");
488 if (m != null && m.coroutine && !is_yield_expression && ((MemberAccess) call).member_name != "end") {
489 // .begin call of async method, no error can happen here
490 } else {
491 foreach (DataType error_type in m.get_error_types ()) {
492 may_throw = true;
494 // ensure we can trace back which expression may throw errors of this type
495 var call_error_type = error_type.copy ();
496 call_error_type.source_reference = source_reference;
498 add_error_type (call_error_type);
501 if (m.returns_floating_reference) {
502 value_type.floating_reference = true;
504 if (m.returns_modified_pointer) {
505 ((MemberAccess) call).inner.lvalue = true;
507 // avoid passing possible null to ref_sink_function without checking
508 if (may_throw && !value_type.nullable && value_type.floating_reference && ret_type is ObjectType) {
509 value_type.nullable = true;
512 var dynamic_sig = m.parent_symbol as DynamicSignal;
513 if (dynamic_sig != null && dynamic_sig.handler != null) {
514 dynamic_sig.return_type = dynamic_sig.handler.value_type.get_return_type ().copy ();
515 bool first = true;
516 foreach (Parameter param in dynamic_sig.handler.value_type.get_parameters ()) {
517 if (first) {
518 // skip sender parameter
519 first = false;
520 } else {
521 dynamic_sig.add_parameter (param.copy ());
524 dynamic_sig.handler.target_type = new DelegateType (dynamic_sig.get_delegate (new ObjectType ((ObjectTypeSymbol) dynamic_sig.parent_symbol), this));
527 if (m != null && m.get_type_parameters ().size > 0) {
528 var ma = (MemberAccess) call;
529 if (ma.get_type_arguments ().size == 0) {
530 // infer type arguments
531 foreach (var type_param in m.get_type_parameters ()) {
532 DataType type_arg = null;
534 // infer type arguments from arguments
535 arg_it = args.iterator ();
536 foreach (Parameter param in params) {
537 if (param.ellipsis || param.params_array) {
538 break;
541 if (arg_it.next ()) {
542 Expression arg = arg_it.get ();
544 type_arg = param.variable_type.infer_type_argument (type_param, arg.value_type);
545 if (type_arg != null) {
546 break;
549 arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, method_type_args, this);
553 // infer type arguments from expected return type
554 if (type_arg == null && target_type != null) {
555 type_arg = m.return_type.infer_type_argument (type_param, target_type);
558 if (type_arg == null) {
559 error = true;
560 Report.error (ma.source_reference, "cannot infer generic type argument for type parameter `%s'".printf (type_param.get_full_name ()));
561 return false;
564 ma.add_type_argument (type_arg);
567 // recalculate argument target types with new information
568 arg_it = args.iterator ();
569 foreach (Parameter param in params) {
570 if (param.ellipsis || param.params_array) {
571 break;
574 if (arg_it.next ()) {
575 Expression arg = arg_it.get ();
577 arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, method_type_args, this);
581 // recalculate return value type with new information
582 value_type = formal_value_type.get_actual_type (target_object_type, method_type_args, this);
585 // replace method-type if needed for proper argument-check in semantic-analyser
586 if (m != null && m.coroutine) {
587 var ma = (MemberAccess) call;
588 if (ma.member_name == "end") {
589 mtype = new MethodType (m.get_end_method ());
592 } else if (mtype is ObjectType) {
593 // constructor
594 var cl = (Class) ((ObjectType) mtype).type_symbol;
595 var m = cl.default_construction_method;
596 foreach (DataType error_type in m.get_error_types ()) {
597 may_throw = true;
599 // ensure we can trace back which expression may throw errors of this type
600 var call_error_type = error_type.copy ();
601 call_error_type.source_reference = source_reference;
603 add_error_type (call_error_type);
605 } else if (mtype is DelegateType) {
606 var d = ((DelegateType) mtype).delegate_symbol;
607 foreach (DataType error_type in d.get_error_types ()) {
608 may_throw = true;
610 // ensure we can trace back which expression may throw errors of this type
611 var call_error_type = error_type.copy ();
612 call_error_type.source_reference = source_reference;
614 add_error_type (call_error_type);
618 if (!context.analyzer.check_arguments (this, mtype, params, get_argument_list ())) {
619 error = true;
620 return false;
623 /* Check for constructv chain up */
624 if (base_cm != null && base_cm.is_variadic () && args.size == base_cm.get_parameters ().size) {
625 var this_last_arg = args[args.size-1];
626 if (this_last_arg.value_type is StructValueType && this_last_arg.value_type.data_type == context.analyzer.va_list_type.data_type) {
627 is_constructv_chainup = true;
631 if (may_throw) {
632 if (parent_node is LocalVariable || parent_node is ExpressionStatement) {
633 // simple statements, no side effects after method call
634 } else if (!(context.analyzer.current_symbol is Block)) {
635 // can't handle errors in field initializers
636 Report.error (source_reference, "Field initializers must not throw errors");
637 } else {
638 // store parent_node as we need to replace the expression in the old parent node later on
639 var old_parent_node = parent_node;
641 var local = new LocalVariable (value_type.copy (), get_temp_name (), null, source_reference);
642 var decl = new DeclarationStatement (local, source_reference);
644 insert_statement (context.analyzer.insert_block, decl);
646 var temp_access = SemanticAnalyzer.create_temp_access (local, target_type);
648 // don't set initializer earlier as this changes parent_node and parent_statement
649 local.initializer = this;
650 decl.check (context);
653 // move temp variable to insert block to ensure the
654 // variable is in the same block as the declaration
655 // otherwise there will be scoping issues in the generated code
656 var block = (Block) context.analyzer.current_symbol;
657 block.remove_local_variable (local);
658 context.analyzer.insert_block.add_local_variable (local);
660 old_parent_node.replace_expression (this, temp_access);
661 temp_access.check (context);
665 return !error;
668 public override void emit (CodeGenerator codegen) {
669 var method_type = call.value_type as MethodType;
671 if (method_type != null && method_type.method_symbol.parent_symbol is Signal) {
672 var signal_access = ((MemberAccess) call).inner;
673 signal_access.emit (codegen);
674 } else {
675 call.emit (codegen);
678 foreach (Expression expr in argument_list) {
679 expr.emit (codegen);
682 codegen.visit_method_call (this);
684 codegen.visit_expression (this);
687 public override void get_defined_variables (Collection<Variable> collection) {
688 call.get_defined_variables (collection);
690 foreach (Expression arg in argument_list) {
691 arg.get_defined_variables (collection);
695 public override void get_used_variables (Collection<Variable> collection) {
696 call.get_used_variables (collection);
698 foreach (Expression arg in argument_list) {
699 arg.get_used_variables (collection);
703 public StringLiteral? get_format_literal () {
704 var mtype = this.call.value_type as MethodType;
705 if (mtype != null) {
706 int format_arg = mtype.method_symbol.get_format_arg_index ();
707 if (format_arg >= 0 && format_arg < argument_list.size) {
708 return StringLiteral.get_format_literal (argument_list[format_arg]);
712 return null;