3 * Copyright (C) 2006-2009 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 () {
76 return new ReadOnlyList
<Expression
> (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_pure () {
109 public override bool check (SemanticAnalyzer analyzer
) {
116 if (!call
.check (analyzer
)) {
117 /* if method resolving didn't succeed, skip this check */
122 // type of target object
123 DataType target_object_type
= null;
125 if (call is MemberAccess
) {
126 var ma
= (MemberAccess
) call
;
127 if (ma
.prototype_access
) {
129 Report
.error (source_reference
, "Access to instance member `%s' denied".printf (call
.symbol_reference
.get_full_name ()));
133 if (ma
.inner
!= null) {
134 target_object_type
= ma
.inner
.value_type
;
137 if (ma
.symbol_reference
!= null && ma
.symbol_reference
.get_attribute ("Assert") != null) {
138 this
.is_assert
= true;
142 var mtype
= call
.value_type
;
144 if (mtype is ObjectType
|| (analyzer
.context
.profile
== Profile
.GOBJECT
&& call
.symbol_reference
== analyzer
.object_type
)) {
145 // constructor chain-up
146 var cm
= analyzer
.find_current_method () as CreationMethod
;
149 Report
.error (source_reference
, "invocation not supported in this context");
151 } else if (cm
.chain_up
) {
153 Report
.error (source_reference
, "Multiple constructor calls in the same constructor are not permitted");
158 if (mtype is ObjectType
) {
159 var otype
= (ObjectType
) mtype
;
160 var cl
= (Class
) otype
.type_symbol
;
161 var base_cm
= cl
.default_construction_method
;
162 if (base_cm
== null) {
164 Report
.error (source_reference
, "chain up to `%s' not supported".printf (cl
.get_full_name ()));
166 } else if (!base_cm
.has_construct_function
) {
168 Report
.error (source_reference
, "chain up to `%s' not supported".printf (base_cm
.get_full_name ()));
173 var cl
= cm
.parent_symbol as Class
;
174 if (cl
== null || !cl
.is_subtype_of (analyzer
.object_type
)) {
176 Report
.error (source_reference
, "chain up to `GLib.Object' not supported");
179 call
.value_type
= new
ObjectType (analyzer
.object_type
);
180 mtype
= call
.value_type
;
184 // check for struct construction
185 if (call is MemberAccess
&&
186 ((call
.symbol_reference is CreationMethod
187 && call
.symbol_reference
.parent_symbol is Struct
)
188 || call
.symbol_reference is Struct
)) {
189 var struct_creation_expression
= new
ObjectCreationExpression ((MemberAccess
) call
, source_reference
);
190 struct_creation_expression
.struct_creation
= true;
191 foreach (Expression arg
in get_argument_list ()) {
192 struct_creation_expression
.add_argument (arg
);
194 struct_creation_expression
.target_type
= target_type
;
195 analyzer
.replaced_nodes
.add (this
);
196 parent_node
.replace_expression (this
, struct_creation_expression
);
197 struct_creation_expression
.check (analyzer
);
199 } else if (call is MemberAccess
200 && call
.symbol_reference is CreationMethod
) {
201 // constructor chain-up
202 var cm
= analyzer
.find_current_method () as CreationMethod
;
205 Report
.error (source_reference
, "use `new' operator to create new objects");
207 } else if (cm
.chain_up
) {
209 Report
.error (source_reference
, "Multiple constructor calls in the same constructor are not permitted");
214 var base_cm
= (CreationMethod
) call
.symbol_reference
;
215 if (!base_cm
.has_construct_function
) {
217 Report
.error (source_reference
, "chain up to `%s' not supported".printf (base_cm
.get_full_name ()));
222 if (mtype
!= null && mtype
.is_invokable ()) {
223 // call ok, expression is invokable
224 } else if (call
.symbol_reference is Class
) {
226 Report
.error (source_reference
, "use `new' operator to create new objects");
230 Report
.error (source_reference
, "invocation not supported in this context");
234 var ret_type
= mtype
.get_return_type ();
235 var params
= mtype
.get_parameters ();
237 if (mtype is MethodType
) {
238 var m
= ((MethodType
) mtype
).method_symbol
;
239 if (m
!= null && m
.coroutine
&& !is_yield_expression
) {
240 // begin or end call of async method
241 var ma
= (MemberAccess
) call
;
242 if (ma
.member_name
!= "end") {
243 // begin (possibly implicit)
244 params
= m
.get_async_begin_parameters ();
245 ret_type
= new
VoidType ();
248 params
= m
.get_async_end_parameters ();
253 Expression last_arg
= null;
255 var args
= get_argument_list ();
256 Iterator
<Expression
> arg_it
= args
.iterator ();
257 foreach (FormalParameter param
in params
) {
258 if (param
.ellipsis
) {
262 if (param
.params_array
) {
263 var array_type
= (ArrayType
) param
.parameter_type
;
264 while (arg_it
.next ()) {
265 Expression arg
= arg_it
.get ();
267 /* store expected type for callback parameters */
268 arg
.target_type
= array_type
.element_type
;
269 arg
.target_type
.value_owned
= array_type
.value_owned
;
274 if (arg_it
.next ()) {
275 Expression arg
= arg_it
.get ();
277 /* store expected type for callback parameters */
278 arg
.formal_target_type
= param
.parameter_type
;
279 arg
.target_type
= arg
.formal_target_type
.get_actual_type (target_object_type
, call as MemberAccess
, this
);
286 if (mtype is MethodType
&& ((MethodType
) mtype
).method_symbol
.printf_format
) {
287 StringLiteral format_literal
= null;
288 if (last_arg
!= null) {
289 // use last argument as format string
290 format_literal
= last_arg as StringLiteral
;
291 if (format_literal
== null && args
.size
== params
.size
- 1) {
292 // insert "%s" to avoid issues with embedded %
293 format_literal
= new
StringLiteral ("\"%s\"");
294 format_literal
.target_type
= analyzer
.string_type
.copy ();
295 argument_list
.insert (args
.size
- 1, format_literal
);
297 // recreate iterator and skip to right position
298 arg_it
= argument_list
.iterator ();
299 foreach (FormalParameter param
in params
) {
300 if (param
.ellipsis
) {
307 // use instance as format string for string.printf (...)
308 var ma
= call as MemberAccess
;
310 format_literal
= ma
.inner as StringLiteral
;
313 if (format_literal
!= null) {
314 string format
= format_literal
.eval ();
316 bool unsupported_format
= false;
318 weak string format_it
= format
;
319 unichar c
= format_it
.get_char ();
322 format_it
= format_it
.next_char ();
323 c
= format_it
.get_char ();
327 format_it
= format_it
.next_char ();
328 c
= format_it
.get_char ();
330 while (c
== '#' || c
== '0' || c
== '-' || c
== ' ' || c
== '+') {
331 format_it
= format_it
.next_char ();
332 c
= format_it
.get_char ();
335 while (c
>= '0' && c
<= '9') {
336 format_it
= format_it
.next_char ();
337 c
= format_it
.get_char ();
341 format_it
= format_it
.next_char ();
342 c
= format_it
.get_char ();
343 while (c
>= '0' && c
<= '9') {
344 format_it
= format_it
.next_char ();
345 c
= format_it
.get_char ();
352 format_it
= format_it
.next_char ();
353 c
= format_it
.get_char ();
356 format_it
= format_it
.next_char ();
357 c
= format_it
.get_char ();
359 } else if (c
== 'l') {
361 format_it
= format_it
.next_char ();
362 c
= format_it
.get_char ();
363 } else if (c
== 'z') {
365 format_it
= format_it
.next_char ();
366 c
= format_it
.get_char ();
368 // conversion specifier
369 DataType param_type
= null;
370 if (c
== 'd' || c
== 'i' || c
== 'c') {
373 param_type
= analyzer
.int8_type
;
374 } else if (length
== -1) {
375 param_type
= analyzer
.short_type
;
376 } else if (length
== 0) {
377 param_type
= analyzer
.int_type
;
378 } else if (length
== 1) {
379 param_type
= analyzer
.long_type
;
380 } else if (length
== 2) {
381 param_type
= analyzer
.ssize_t_type
;
383 } else if (c
== 'o' || c
== 'u' || c
== 'x' || c
== 'X') {
386 param_type
= analyzer
.uchar_type
;
387 } else if (length
== -1) {
388 param_type
= analyzer
.ushort_type
;
389 } else if (length
== 0) {
390 param_type
= analyzer
.uint_type
;
391 } else if (length
== 1) {
392 param_type
= analyzer
.ulong_type
;
393 } else if (length
== 2) {
394 param_type
= analyzer
.size_t_type
;
396 } else if (c
== 'e' || c
== 'E' || c
== 'f' || c
== 'F'
397 || c
== 'g' || c
== 'G' || c
== 'a' || c
== 'A') {
399 param_type
= analyzer
.double_type
;
400 } else if (c
== 's') {
402 param_type
= analyzer
.string_type
;
403 } else if (c
== 'p') {
405 param_type
= new
PointerType (new
VoidType ());
406 } else if (c
== '%') {
409 unsupported_format
= true;
413 format_it
= format_it
.next_char ();
414 c
= format_it
.get_char ();
416 if (param_type
!= null) {
417 if (arg_it
.next ()) {
418 Expression arg
= arg_it
.get ();
420 arg
.target_type
= param_type
;
422 Report
.error (source_reference
, "Too few arguments for specified format");
427 if (!unsupported_format
&& arg_it
.next ()) {
428 Report
.error (source_reference
, "Too many arguments for specified format");
434 foreach (Expression arg
in get_argument_list ()) {
435 arg
.check (analyzer
);
438 if (ret_type is VoidType
) {
440 if (!(parent_node is ExpressionStatement
)
441 && !(parent_node is ForStatement
)
442 && !(parent_node is YieldStatement
)) {
443 // A void method invocation can be in the initializer or
444 // iterator of a for statement
446 Report
.error (source_reference
, "invocation of void method not allowed as expression");
451 formal_value_type
= ret_type
;
452 value_type
= formal_value_type
.get_actual_type (target_object_type
, call as MemberAccess
, this
);
454 bool may_throw
= false;
456 if (mtype is MethodType
) {
457 var m
= ((MethodType
) mtype
).method_symbol
;
458 if (is_yield_expression
) {
461 Report
.error (source_reference
, "yield expression requires async method");
463 if (analyzer
.current_method
== null || !analyzer
.current_method
.coroutine
) {
465 Report
.error (source_reference
, "yield expression not available outside async method");
468 if (m
!= null && m
.coroutine
&& !is_yield_expression
&& ((MemberAccess
) call
).member_name
!= "end") {
469 // .begin call of async method, no error can happen here
471 foreach (DataType error_type
in m
.get_error_types ()) {
474 // ensure we can trace back which expression may throw errors of this type
475 var call_error_type
= error_type
.copy ();
476 call_error_type
.source_reference
= source_reference
;
478 add_error_type (call_error_type
);
481 } else if (mtype is DelegateType
) {
482 var d
= ((DelegateType
) mtype
).delegate_symbol
;
483 foreach (DataType error_type
in d
.get_error_types ()) {
486 // ensure we can trace back which expression may throw errors of this type
487 var call_error_type
= error_type
.copy ();
488 call_error_type
.source_reference
= source_reference
;
490 add_error_type (call_error_type
);
494 if (!analyzer
.check_arguments (this
, mtype
, params
, get_argument_list ())) {
500 if (parent_node is LocalVariable
|| parent_node is ExpressionStatement
) {
501 // simple statements, no side effects after method call
503 // store parent_node as we need to replace the expression in the old parent node later on
504 var old_parent_node
= parent_node
;
506 var local
= new
LocalVariable (value_type
, get_temp_name (), null, source_reference
);
507 // use floating variable to avoid unnecessary (and sometimes impossible) copies
508 local
.floating
= true;
509 var decl
= new
DeclarationStatement (local
, source_reference
);
511 insert_statement (analyzer
.insert_block
, decl
);
513 Expression temp_access
= new MemberAccess
.simple (local
.name
, source_reference
);
514 temp_access
.target_type
= target_type
;
516 // don't set initializer earlier as this changes parent_node and parent_statement
517 local
.initializer
= this
;
518 decl
.check (analyzer
);
519 temp_access
.check (analyzer
);
521 // move temp variable to insert block to ensure the
522 // variable is in the same block as the declaration
523 // otherwise there will be scoping issues in the generated code
524 var block
= (Block
) analyzer
.current_symbol
;
525 block
.remove_local_variable (local
);
526 analyzer
.insert_block
.add_local_variable (local
);
528 old_parent_node
.replace_expression (this
, temp_access
);
535 public override void get_defined_variables (Collection
<LocalVariable
> collection
) {
536 call
.get_defined_variables (collection
);
538 foreach (Expression arg
in argument_list
) {
539 arg
.get_defined_variables (collection
);
543 public override void get_used_variables (Collection
<LocalVariable
> collection
) {
544 call
.get_used_variables (collection
);
546 foreach (Expression arg
in argument_list
) {
547 arg
.get_used_variables (collection
);