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
20 * Jürg Billeter <j@bitron.ch>
26 * Represents an invocation expression in the source code.
28 public class Vala
.MethodCall
: Expression
{
32 public Expression call
{
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
> ();
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
;
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
;
71 * Returns a copy of the argument list.
73 * @return argument list
75 public List
<Expression
> get_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
) {
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 ();
124 public override bool is_pure () {
129 if (!(call
.symbol_reference is CreationMethod
)) {
135 var ma
= (MemberAccess
) call
;
136 if (ma
.inner
!= null) {
140 ma
= expr as MemberAccess
;
141 if (ma
!= null && ma
.member_name
== "this") {
143 } else if (expr is BaseAccess
) {
150 public override bool check (CodeContext context
) {
157 if (!call
.check (context
)) {
158 /* if method resolving didn't succeed, skip this check */
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
) {
173 Report
.error (source_reference
, "Access to instance member `%s' denied".printf (call
.symbol_reference
.get_full_name ()));
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
;
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
;
201 Report
.error (source_reference
, "invocation not supported in this context");
203 } else if (cm
.chain_up
) {
205 Report
.error (source_reference
, "Multiple constructor calls in the same constructor are not permitted");
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) {
216 Report
.error (source_reference
, "chain up to `%s' not supported".printf (cl
.get_full_name ()));
218 } else if (!base_cm
.has_construct_function
) {
220 Report
.error (source_reference
, "chain up to `%s' not supported".printf (base_cm
.get_full_name ()));
225 var cl
= cm
.parent_symbol as Class
;
226 if (cl
== null || !cl
.is_subtype_of (context
.analyzer
.object_type
)) {
228 Report
.error (source_reference
, "chain up to `GLib.Object' not supported");
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 ())) {
244 Report
.error (source_reference
, "invocation not supported in this context");
249 var cm
= context
.analyzer
.find_current_method () as CreationMethod
;
253 Report
.error (source_reference
, "Multiple constructor calls in the same constructor are not permitted");
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
);
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
;
275 Report
.error (source_reference
, "use `new' operator to create new objects");
277 } else if (cm
.chain_up
) {
279 Report
.error (source_reference
, "Multiple constructor calls in the same constructor are not permitted");
284 var base_cm
= (CreationMethod
) call
.symbol_reference
;
285 if (!base_cm
.has_construct_function
) {
287 Report
.error (source_reference
, "chain up to `%s' not supported".printf (base_cm
.get_full_name ()));
292 if (mtype
!= null && mtype
.is_invokable ()) {
293 // call ok, expression is invokable
294 } else if (call
.symbol_reference is Class
) {
296 Report
.error (source_reference
, "use `new' operator to create new objects");
300 Report
.error (source_reference
, "invocation not supported in this context");
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 ();
319 params
= m
.get_async_end_parameters ();
321 } else if (ma
.member_name
== "begin" || ma
.member_name
== "end") {
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
) {
333 Report
.error (ma
.source_reference
, "too few type arguments");
335 } else if (n_type_args
> 0 && n_type_args
> n_type_params
) {
337 Report
.error (ma
.source_reference
, "too many type arguments");
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
) {
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
;
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
);
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
);
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
) {
408 // use instance as format string for string.printf (...)
409 var ma
= call as MemberAccess
;
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 ();
423 format_it
= format_it
.next_char ();
424 c
= format_it
.get_char ();
428 format_it
= format_it
.next_char ();
429 c
= format_it
.get_char ();
431 while (c
== '#' || c
== '0' || c
== '-' || c
== ' ' || c
== '+') {
432 format_it
= format_it
.next_char ();
433 c
= format_it
.get_char ();
436 while (c
>= '0' && c
<= '9') {
437 format_it
= format_it
.next_char ();
438 c
= format_it
.get_char ();
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 ();
453 format_it
= format_it
.next_char ();
454 c
= format_it
.get_char ();
457 format_it
= format_it
.next_char ();
458 c
= format_it
.get_char ();
460 } else if (c
== 'l') {
462 format_it
= format_it
.next_char ();
463 c
= format_it
.get_char ();
464 } else if (c
== 'z') {
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') {
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') {
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') {
500 param_type
= context
.analyzer
.double_type
;
501 } else if (c
== 's') {
503 param_type
= context
.analyzer
.string_type
;
504 } else if (c
== 'p') {
506 param_type
= new
PointerType (new
VoidType ());
507 } else if (c
== '%') {
510 unsupported_format
= true;
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
;
523 Report
.error (source_reference
, "Too few arguments for specified format");
528 if (!unsupported_format
&& arg_it
.next ()) {
529 Report
.error (source_reference
, "Too many arguments for specified format");
535 foreach (Expression arg
in get_argument_list ()) {
539 if (ret_type is VoidType
) {
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
547 Report
.error (source_reference
, "invocation of void method not allowed as expression");
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
) {
562 Report
.error (source_reference
, "yield expression requires async method");
564 if (context
.analyzer
.current_method
== null || !context
.analyzer
.current_method
.coroutine
) {
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
573 foreach (DataType error_type
in m
.get_error_types ()) {
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 ();
591 foreach (Parameter param
in dynamic_sig
.handler
.value_type
.get_parameters ()) {
593 // skip sender parameter
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
) {
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;
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) {
641 Report
.error (ma
.source_reference
, "cannot infer generic type argument for type parameter `%s'".printf (type_param
.get_full_name ()));
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
) {
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
) {
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 ()) {
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 ()) {
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 ())) {
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");
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
);
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
;
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
;
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
);
767 foreach (Expression expr
in argument_list
) {
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
);