D-Bus: Remove extra semicolon to avoid C warning
[vala-lang.git] / vala / valamethodcall.vala
blob23329bdcbebd760c51bb9936f954128024581eef
1 /* valamethodcall.vala
3 * Copyright (C) 2006-2011 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 public Expression _call;
46 private List<Expression> argument_list = new ArrayList<Expression> ();
48 /**
49 * Creates a new invocation expression.
51 * @param call method to call
52 * @param source_reference reference to source code
53 * @return newly created invocation expression
55 public MethodCall (Expression call, SourceReference? source_reference = null) {
56 this.source_reference = source_reference;
57 this.call = call;
60 /**
61 * Appends the specified expression to the list of arguments.
63 * @param arg an argument
65 public void add_argument (Expression arg) {
66 argument_list.add (arg);
67 arg.parent_node = this;
70 /**
71 * Returns a copy of the argument list.
73 * @return argument list
75 public List<Expression> get_argument_list () {
76 return argument_list;
79 public override void accept (CodeVisitor visitor) {
80 visitor.visit_method_call (this);
82 visitor.visit_expression (this);
85 public override void accept_children (CodeVisitor visitor) {
86 call.accept (visitor);
88 foreach (Expression expr in argument_list) {
89 expr.accept (visitor);
93 public override void replace_expression (Expression old_node, Expression new_node) {
94 if (call == old_node) {
95 call = new_node;
98 int index = argument_list.index_of (old_node);
99 if (index >= 0 && new_node.parent_node == null) {
100 argument_list[index] = new_node;
101 new_node.parent_node = this;
105 public override bool is_constant () {
106 var method_type = call.value_type as MethodType;
108 if (method_type != null) {
109 // N_ and NC_ do not have any effect on the C code,
110 // they are only interpreted by xgettext
111 // this means that it is ok to use them in constant initializers
112 if (method_type.method_symbol.get_full_name () == "GLib.N_") {
113 // first argument is string
114 return argument_list[0].is_constant ();
115 } else if (method_type.method_symbol.get_full_name () == "GLib.NC_") {
116 // second argument is string
117 return argument_list[1].is_constant ();
121 return false;
124 public override bool is_pure () {
125 return false;
128 bool is_chainup () {
129 if (!(call.symbol_reference is CreationMethod)) {
130 return false;
133 var expr = call;
135 var ma = (MemberAccess) call;
136 if (ma.inner != null) {
137 expr = ma.inner;
140 ma = expr as MemberAccess;
141 if (ma != null && ma.member_name == "this") {
142 return true;
143 } else if (expr is BaseAccess) {
144 return true;
145 } else {
146 return false;
150 public override bool check (CodeContext context) {
151 if (checked) {
152 return !error;
155 checked = true;
157 if (!call.check (context)) {
158 /* if method resolving didn't succeed, skip this check */
159 error = true;
160 return false;
163 // type of target object
164 DataType target_object_type = null;
166 if (call.value_type is DelegateType) {
167 // delegate invocation, resolve generic types relative to delegate
168 target_object_type = call.value_type;
169 } else if (call is MemberAccess) {
170 var ma = (MemberAccess) call;
171 if (ma.prototype_access) {
172 error = true;
173 Report.error (source_reference, "Access to instance member `%s' denied".printf (call.symbol_reference.get_full_name ()));
174 return false;
177 if (ma.inner != null) {
178 target_object_type = ma.inner.value_type;
180 // foo is relevant instance in foo.bar.connect (on_bar)
181 if (ma.inner.symbol_reference is Signal) {
182 var sig = ma.inner as MemberAccess;
183 if (sig != null) {
184 target_object_type = sig.inner.value_type;
189 if (ma.symbol_reference != null && ma.symbol_reference.get_attribute ("Assert") != null) {
190 this.is_assert = true;
194 var mtype = call.value_type;
196 if (mtype is ObjectType || (context.profile == Profile.GOBJECT && call.symbol_reference == context.analyzer.object_type)) {
197 // constructor chain-up
198 var cm = context.analyzer.find_current_method () as CreationMethod;
199 if (cm == null) {
200 error = true;
201 Report.error (source_reference, "invocation not supported in this context");
202 return false;
203 } else if (cm.chain_up) {
204 error = true;
205 Report.error (source_reference, "Multiple constructor calls in the same constructor are not permitted");
206 return false;
208 cm.chain_up = true;
210 if (mtype is ObjectType) {
211 var otype = (ObjectType) mtype;
212 var cl = (Class) otype.type_symbol;
213 var base_cm = cl.default_construction_method;
214 if (base_cm == null) {
215 error = true;
216 Report.error (source_reference, "chain up to `%s' not supported".printf (cl.get_full_name ()));
217 return false;
218 } else if (!base_cm.has_construct_function) {
219 error = true;
220 Report.error (source_reference, "chain up to `%s' not supported".printf (base_cm.get_full_name ()));
221 return false;
223 } else {
224 // GObject chain up
225 var cl = cm.parent_symbol as Class;
226 if (cl == null || !cl.is_subtype_of (context.analyzer.object_type)) {
227 error = true;
228 Report.error (source_reference, "chain up to `GLib.Object' not supported");
229 return false;
231 call.value_type = new ObjectType (context.analyzer.object_type);
232 mtype = call.value_type;
236 // check for struct construction
237 if (call is MemberAccess &&
238 ((call.symbol_reference is CreationMethod
239 && call.symbol_reference.parent_symbol is Struct)
240 || call.symbol_reference is Struct)) {
241 var st = call.symbol_reference as Struct;
242 if (st != null && st.default_construction_method == null && (st.is_boolean_type () || st.is_integer_type () || st.is_floating_type ())) {
243 error = true;
244 Report.error (source_reference, "invocation not supported in this context");
245 return false;
248 if (is_chainup ()) {
249 var cm = context.analyzer.find_current_method () as CreationMethod;
250 if (cm != null) {
251 if (cm.chain_up) {
252 error = true;
253 Report.error (source_reference, "Multiple constructor calls in the same constructor are not permitted");
254 return false;
256 cm.chain_up = true;
259 var struct_creation_expression = new ObjectCreationExpression ((MemberAccess) call, source_reference);
260 struct_creation_expression.struct_creation = true;
261 foreach (Expression arg in get_argument_list ()) {
262 struct_creation_expression.add_argument (arg);
264 struct_creation_expression.target_type = target_type;
265 context.analyzer.replaced_nodes.add (this);
266 parent_node.replace_expression (this, struct_creation_expression);
267 struct_creation_expression.check (context);
268 return true;
269 } else if (call is MemberAccess
270 && call.symbol_reference is CreationMethod) {
271 // constructor chain-up
272 var cm = context.analyzer.find_current_method () as CreationMethod;
273 if (cm == null) {
274 error = true;
275 Report.error (source_reference, "use `new' operator to create new objects");
276 return false;
277 } else if (cm.chain_up) {
278 error = true;
279 Report.error (source_reference, "Multiple constructor calls in the same constructor are not permitted");
280 return false;
282 cm.chain_up = true;
284 var base_cm = (CreationMethod) call.symbol_reference;
285 if (!base_cm.has_construct_function) {
286 error = true;
287 Report.error (source_reference, "chain up to `%s' not supported".printf (base_cm.get_full_name ()));
288 return false;
292 if (mtype != null && mtype.is_invokable ()) {
293 // call ok, expression is invokable
294 } else if (call.symbol_reference is Class) {
295 error = true;
296 Report.error (source_reference, "use `new' operator to create new objects");
297 return false;
298 } else {
299 error = true;
300 Report.error (source_reference, "invocation not supported in this context");
301 return false;
304 var ret_type = mtype.get_return_type ();
305 var params = mtype.get_parameters ();
307 if (mtype is MethodType) {
308 var m = ((MethodType) mtype).method_symbol;
309 if (m != null && m.coroutine) {
310 var ma = (MemberAccess) call;
311 if (!is_yield_expression) {
312 // begin or end call of async method
313 if (ma.member_name != "end") {
314 // begin (possibly implicit)
315 params = m.get_async_begin_parameters ();
316 ret_type = new VoidType ();
317 } else {
318 // end
319 params = m.get_async_end_parameters ();
321 } else if (ma.member_name == "begin" || ma.member_name == "end") {
322 error = true;
323 Report.error (ma.source_reference, "use of `%s' not allowed in yield statement".printf (ma.member_name));
327 if (m != null && m.get_type_parameters ().size > 0) {
328 var ma = (MemberAccess) call;
329 int n_type_params = m.get_type_parameters ().size;
330 int n_type_args = ma.get_type_arguments ().size;
331 if (n_type_args > 0 && n_type_args < n_type_params) {
332 error = true;
333 Report.error (ma.source_reference, "too few type arguments");
334 return false;
335 } else if (n_type_args > 0 && n_type_args > n_type_params) {
336 error = true;
337 Report.error (ma.source_reference, "too many type arguments");
338 return false;
343 Expression last_arg = null;
345 var args = get_argument_list ();
346 Iterator<Expression> arg_it = args.iterator ();
347 foreach (Parameter param in params) {
348 if (param.ellipsis) {
349 break;
352 if (param.params_array) {
353 var array_type = (ArrayType) param.variable_type;
354 while (arg_it.next ()) {
355 Expression arg = arg_it.get ();
357 /* store expected type for callback parameters */
358 arg.target_type = array_type.element_type;
359 arg.target_type.value_owned = array_type.value_owned;
361 break;
364 if (arg_it.next ()) {
365 Expression arg = arg_it.get ();
367 /* store expected type for callback parameters */
368 arg.formal_target_type = param.variable_type;
369 arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, call as MemberAccess, this);
371 last_arg = arg;
375 // concatenate stringified arguments for methods with attribute [Print]
376 if (mtype is MethodType && ((MethodType) mtype).method_symbol.get_attribute ("Print") != null) {
377 var template = new Template (source_reference);
378 foreach (Expression arg in argument_list) {
379 arg.parent_node = null;
380 template.add_expression (arg);
382 argument_list.clear ();
383 add_argument (template);
386 // printf arguments
387 if (mtype is MethodType && ((MethodType) mtype).method_symbol.printf_format) {
388 StringLiteral format_literal = null;
389 if (last_arg != null) {
390 // use last argument as format string
391 format_literal = last_arg as StringLiteral;
392 if (format_literal == null && args.size == params.size - 1) {
393 // insert "%s" to avoid issues with embedded %
394 format_literal = new StringLiteral ("\"%s\"");
395 format_literal.target_type = context.analyzer.string_type.copy ();
396 argument_list.insert (args.size - 1, format_literal);
398 // recreate iterator and skip to right position
399 arg_it = argument_list.iterator ();
400 foreach (Parameter param in params) {
401 if (param.ellipsis) {
402 break;
404 arg_it.next ();
407 } else {
408 // use instance as format string for string.printf (...)
409 var ma = call as MemberAccess;
410 if (ma != null) {
411 format_literal = ma.inner as StringLiteral;
414 if (format_literal != null) {
415 string format = format_literal.eval ();
417 bool unsupported_format = false;
419 weak string format_it = format;
420 unichar c = format_it.get_char ();
421 while (c != '\0') {
422 if (c != '%') {
423 format_it = format_it.next_char ();
424 c = format_it.get_char ();
425 continue;
428 format_it = format_it.next_char ();
429 c = format_it.get_char ();
430 // flags
431 while (c == '#' || c == '0' || c == '-' || c == ' ' || c == '+') {
432 format_it = format_it.next_char ();
433 c = format_it.get_char ();
435 // field width
436 while (c >= '0' && c <= '9') {
437 format_it = format_it.next_char ();
438 c = format_it.get_char ();
440 // precision
441 if (c == '.') {
442 format_it = format_it.next_char ();
443 c = format_it.get_char ();
444 while (c >= '0' && c <= '9') {
445 format_it = format_it.next_char ();
446 c = format_it.get_char ();
449 // length modifier
450 int length = 0;
451 if (c == 'h') {
452 length = -1;
453 format_it = format_it.next_char ();
454 c = format_it.get_char ();
455 if (c == 'h') {
456 length = -2;
457 format_it = format_it.next_char ();
458 c = format_it.get_char ();
460 } else if (c == 'l') {
461 length = 1;
462 format_it = format_it.next_char ();
463 c = format_it.get_char ();
464 } else if (c == 'z') {
465 length = 2;
466 format_it = format_it.next_char ();
467 c = format_it.get_char ();
469 // conversion specifier
470 DataType param_type = null;
471 if (c == 'd' || c == 'i' || c == 'c') {
472 // integer
473 if (length == -2) {
474 param_type = context.analyzer.int8_type;
475 } else if (length == -1) {
476 param_type = context.analyzer.short_type;
477 } else if (length == 0) {
478 param_type = context.analyzer.int_type;
479 } else if (length == 1) {
480 param_type = context.analyzer.long_type;
481 } else if (length == 2) {
482 param_type = context.analyzer.ssize_t_type;
484 } else if (c == 'o' || c == 'u' || c == 'x' || c == 'X') {
485 // unsigned integer
486 if (length == -2) {
487 param_type = context.analyzer.uchar_type;
488 } else if (length == -1) {
489 param_type = context.analyzer.ushort_type;
490 } else if (length == 0) {
491 param_type = context.analyzer.uint_type;
492 } else if (length == 1) {
493 param_type = context.analyzer.ulong_type;
494 } else if (length == 2) {
495 param_type = context.analyzer.size_t_type;
497 } else if (c == 'e' || c == 'E' || c == 'f' || c == 'F'
498 || c == 'g' || c == 'G' || c == 'a' || c == 'A') {
499 // double
500 param_type = context.analyzer.double_type;
501 } else if (c == 's') {
502 // string
503 param_type = context.analyzer.string_type;
504 } else if (c == 'p') {
505 // pointer
506 param_type = new PointerType (new VoidType ());
507 } else if (c == '%') {
508 // literal %
509 } else {
510 unsupported_format = true;
511 break;
513 if (c != '\0') {
514 format_it = format_it.next_char ();
515 c = format_it.get_char ();
517 if (param_type != null) {
518 if (arg_it.next ()) {
519 Expression arg = arg_it.get ();
521 arg.target_type = param_type;
522 } else {
523 Report.error (source_reference, "Too few arguments for specified format");
524 return false;
528 if (!unsupported_format && arg_it.next ()) {
529 Report.error (source_reference, "Too many arguments for specified format");
530 return false;
535 foreach (Expression arg in get_argument_list ()) {
536 arg.check (context);
539 if (ret_type is VoidType) {
540 // void return type
541 if (!(parent_node is ExpressionStatement)
542 && !(parent_node is ForStatement)
543 && !(parent_node is YieldStatement)) {
544 // A void method invocation can be in the initializer or
545 // iterator of a for statement
546 error = true;
547 Report.error (source_reference, "invocation of void method not allowed as expression");
548 return false;
552 formal_value_type = ret_type;
553 value_type = formal_value_type.get_actual_type (target_object_type, call as MemberAccess, this);
555 bool may_throw = false;
557 if (mtype is MethodType) {
558 var m = ((MethodType) mtype).method_symbol;
559 if (is_yield_expression) {
560 if (!m.coroutine) {
561 error = true;
562 Report.error (source_reference, "yield expression requires async method");
564 if (context.analyzer.current_method == null || !context.analyzer.current_method.coroutine) {
565 error = true;
566 Report.error (source_reference, "yield expression not available outside async method");
568 context.analyzer.current_method.yield_count++;
570 if (m != null && m.coroutine && !is_yield_expression && ((MemberAccess) call).member_name != "end") {
571 // .begin call of async method, no error can happen here
572 } else {
573 foreach (DataType error_type in m.get_error_types ()) {
574 may_throw = true;
576 // ensure we can trace back which expression may throw errors of this type
577 var call_error_type = error_type.copy ();
578 call_error_type.source_reference = source_reference;
580 add_error_type (call_error_type);
583 if (m.returns_floating_reference) {
584 value_type.floating_reference = true;
587 var dynamic_sig = m.parent_symbol as DynamicSignal;
588 if (dynamic_sig != null && dynamic_sig.handler != null) {
589 dynamic_sig.return_type = dynamic_sig.handler.value_type.get_return_type ().copy ();
590 bool first = true;
591 foreach (Parameter param in dynamic_sig.handler.value_type.get_parameters ()) {
592 if (first) {
593 // skip sender parameter
594 first = false;
595 } else {
596 dynamic_sig.add_parameter (param.copy ());
599 dynamic_sig.handler.target_type = new DelegateType (dynamic_sig.get_delegate (new ObjectType ((ObjectTypeSymbol) dynamic_sig.parent_symbol), this));
602 if (m != null && m.get_type_parameters ().size > 0) {
603 var ma = (MemberAccess) call;
604 if (ma.get_type_arguments ().size == 0) {
605 // infer type arguments
606 foreach (var type_param in m.get_type_parameters ()) {
607 DataType type_arg = null;
609 // infer type arguments from arguments
610 arg_it = args.iterator ();
611 foreach (Parameter param in params) {
612 if (param.ellipsis || param.params_array) {
613 break;
616 if (arg_it.next ()) {
617 Expression arg = arg_it.get ();
619 var generic_type = param.variable_type as GenericType;
620 if (generic_type != null && generic_type.type_parameter == type_param) {
621 type_arg = arg.value_type.copy ();
622 type_arg.value_owned = true;
623 break;
626 arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, call as MemberAccess, this);
630 // infer type arguments from expected return type
631 if (type_arg == null && target_type != null) {
632 var generic_type = m.return_type as GenericType;
633 if (generic_type != null && generic_type.type_parameter == type_param) {
634 type_arg = target_type.copy ();
635 type_arg.value_owned = true;
639 if (type_arg == null) {
640 error = true;
641 Report.error (ma.source_reference, "cannot infer generic type argument for type parameter `%s'".printf (type_param.get_full_name ()));
642 return false;
645 ma.add_type_argument (type_arg);
648 // recalculate argument target types with new information
649 arg_it = args.iterator ();
650 foreach (Parameter param in params) {
651 if (param.ellipsis || param.params_array) {
652 break;
655 if (arg_it.next ()) {
656 Expression arg = arg_it.get ();
658 arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, call as MemberAccess, this);
662 // recalculate return value type with new information
663 value_type = formal_value_type.get_actual_type (target_object_type, call as MemberAccess, this);
666 } else if (mtype is ObjectType) {
667 // constructor
668 var cl = (Class) ((ObjectType) mtype).type_symbol;
669 var m = cl.default_construction_method;
670 foreach (DataType error_type in m.get_error_types ()) {
671 may_throw = true;
673 // ensure we can trace back which expression may throw errors of this type
674 var call_error_type = error_type.copy ();
675 call_error_type.source_reference = source_reference;
677 add_error_type (call_error_type);
679 } else if (mtype is DelegateType) {
680 var d = ((DelegateType) mtype).delegate_symbol;
681 foreach (DataType error_type in d.get_error_types ()) {
682 may_throw = true;
684 // ensure we can trace back which expression may throw errors of this type
685 var call_error_type = error_type.copy ();
686 call_error_type.source_reference = source_reference;
688 add_error_type (call_error_type);
692 if (!context.analyzer.check_arguments (this, mtype, params, get_argument_list ())) {
693 error = true;
694 return false;
697 if (may_throw) {
698 if (parent_node is LocalVariable || parent_node is ExpressionStatement) {
699 // simple statements, no side effects after method call
700 } else if (!(context.analyzer.current_symbol is Block)) {
701 if (context.profile != Profile.DOVA) {
702 // can't handle errors in field initializers
703 Report.error (source_reference, "Field initializers must not throw errors");
705 } else {
706 // store parent_node as we need to replace the expression in the old parent node later on
707 var old_parent_node = parent_node;
709 var local = new LocalVariable (value_type, get_temp_name (), null, source_reference);
710 // use floating variable to avoid unnecessary (and sometimes impossible) copies
711 local.floating = true;
712 var decl = new DeclarationStatement (local, source_reference);
714 insert_statement (context.analyzer.insert_block, decl);
716 Expression temp_access = new MemberAccess.simple (local.name, source_reference);
717 temp_access.target_type = target_type;
719 // don't set initializer earlier as this changes parent_node and parent_statement
720 local.initializer = this;
721 decl.check (context);
722 temp_access.check (context);
724 // move temp variable to insert block to ensure the
725 // variable is in the same block as the declaration
726 // otherwise there will be scoping issues in the generated code
727 var block = (Block) context.analyzer.current_symbol;
728 block.remove_local_variable (local);
729 context.analyzer.insert_block.add_local_variable (local);
731 old_parent_node.replace_expression (this, temp_access);
735 return !error;
738 public override void emit (CodeGenerator codegen) {
739 var method_type = call.value_type as MethodType;
741 if (method_type != null) {
742 // N_ and NC_ do not have any effect on the C code,
743 // they are only interpreted by xgettext
744 // this means that it is ok to use them in constant initializers
745 // however, we must avoid generating regular method call code
746 // as that may include temporary variables
747 if (method_type.method_symbol.get_full_name () == "GLib.N_") {
748 // first argument is string
749 argument_list[0].emit (codegen);
750 this.target_value = argument_list[0].target_value;
751 return;
752 } else if (method_type.method_symbol.get_full_name () == "GLib.NC_") {
753 // second argument is string
754 argument_list[1].emit (codegen);
755 this.target_value = argument_list[1].target_value;
756 return;
760 if (method_type != null && method_type.method_symbol.parent_symbol is Signal) {
761 var signal_access = ((MemberAccess) call).inner;
762 signal_access.emit (codegen);
763 } else {
764 call.emit (codegen);
767 foreach (Expression expr in argument_list) {
768 expr.emit (codegen);
771 codegen.visit_method_call (this);
773 codegen.visit_expression (this);
776 public override void get_defined_variables (Collection<LocalVariable> collection) {
777 call.get_defined_variables (collection);
779 foreach (Expression arg in argument_list) {
780 arg.get_defined_variables (collection);
784 public override void get_used_variables (Collection<LocalVariable> collection) {
785 call.get_used_variables (collection);
787 foreach (Expression arg in argument_list) {
788 arg.get_used_variables (collection);