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
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; }
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
> ();
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
;
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
;
78 * Returns a copy of the argument list.
80 * @return argument list
82 public List
<Expression
> get_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
) {
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 ();
131 public override bool is_pure () {
135 public override bool is_accessible (Symbol sym
) {
136 foreach (var arg
in argument_list
) {
137 if (!arg
.is_accessible (sym
)) {
142 return call
.is_accessible (sym
);
145 public override bool check (CodeContext context
) {
152 if (!call
.check (context
)) {
153 /* if method resolving didn't succeed, skip this check */
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
) {
170 Report
.error (source_reference
, "Access to instance member `%s' denied".printf (call
.symbol_reference
.get_full_name ()));
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
;
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
;
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
) {
219 var ma
= expr as MemberAccess
;
220 if (ma
!= null && ma
.symbol_reference is CreationMethod
) {
222 ma
= expr as MemberAccess
;
224 if (ma
!= null && ma
.member_name
== "this") {
225 // this[.with_foo] ()
227 } else if (expr is BaseAccess
) {
228 // base[.with_foo] ()
233 CreationMethod base_cm
= null;
236 var cm
= context
.analyzer
.find_current_method () as CreationMethod
;
239 Report
.error (source_reference
, "invocation not supported in this context");
241 } else if (cm
.chain_up
) {
243 Report
.error (source_reference
, "Multiple constructor calls in the same constructor are not permitted");
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) {
254 Report
.error (source_reference
, "chain up to `%s' not supported".printf (cl
.get_full_name ()));
256 } else if (!base_cm
.has_construct_function
) {
258 Report
.error (source_reference
, "chain up to `%s' not supported".printf (base_cm
.get_full_name ()));
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
) {
265 Report
.error (source_reference
, "chain up to `%s' not supported".printf (base_cm
.get_full_name ()));
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
)) {
272 Report
.error (source_reference
, "chain up to `GLib.Object' not supported");
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 ())) {
288 Report
.error (source_reference
, "invocation not supported in this context");
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
);
302 } else if (!is_chainup
&& call is MemberAccess
&& call
.symbol_reference is CreationMethod
) {
304 Report
.error (source_reference
, "use `new' operator to create new objects");
308 if (!is_chainup
&& mtype is ObjectType
) {
309 // prevent funny stuff like (new Object ()) ()
311 Report
.error (source_reference
, "invocation not supported in this context");
313 } else if (mtype
!= null && mtype
.is_invokable ()) {
314 // call ok, expression is invokable
315 } else if (call
.symbol_reference is Class
) {
317 Report
.error (source_reference
, "use `new' operator to create new objects");
321 Report
.error (source_reference
, "invocation not supported in this context");
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 ();
343 params
= m
.get_async_end_parameters ();
345 } else if (ma
.member_name
== "begin" || ma
.member_name
== "end") {
347 Report
.error (ma
.source_reference
, "use of `%s' not allowed in yield statement".printf (ma
.member_name
));
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
) {
357 Report
.error (ma
.source_reference
, "too few type arguments");
359 } else if (n_type_args
> 0 && n_type_args
> n_type_params
) {
361 Report
.error (ma
.source_reference
, "too many type arguments");
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
)) {
378 if (param
.ellipsis
) {
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
;
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
);
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
);
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
) {
440 // use instance as format string for string.printf (...)
441 var ma
= call as MemberAccess
;
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
)) {
454 foreach (Expression arg
in get_argument_list ()) {
458 if (ret_type is VoidType
) {
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
466 Report
.error (source_reference
, "invocation of void method not allowed as expression");
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
) {
481 Report
.error (source_reference
, "yield expression requires async method");
483 if (context
.analyzer
.current_method
== null || !context
.analyzer
.current_method
.coroutine
) {
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
491 foreach (DataType error_type
in m
.get_error_types ()) {
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 ();
516 foreach (Parameter param
in dynamic_sig
.handler
.value_type
.get_parameters ()) {
518 // skip sender parameter
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
) {
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) {
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) {
560 Report
.error (ma
.source_reference
, "cannot infer generic type argument for type parameter `%s'".printf (type_param
.get_full_name ()));
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
) {
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
) {
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 ()) {
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 ()) {
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 ())) {
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;
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");
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
);
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
);
678 foreach (Expression expr
in argument_list
) {
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
;
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
]);