Release 0.7.8
[vala-lang.git] / vala / valamethodcall.vala
blobab4702432cca1612c4bda27d8f03733a78099fe3
1 /* valamethodcall.vala
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
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 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) {
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_pure () {
106 return false;
109 public override bool check (SemanticAnalyzer analyzer) {
110 if (checked) {
111 return !error;
114 checked = true;
116 if (!call.check (analyzer)) {
117 /* if method resolving didn't succeed, skip this check */
118 error = true;
119 return false;
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) {
128 error = true;
129 Report.error (source_reference, "Access to instance member `%s' denied".printf (call.symbol_reference.get_full_name ()));
130 return false;
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;
147 if (cm == null) {
148 error = true;
149 Report.error (source_reference, "invocation not supported in this context");
150 return false;
151 } else if (cm.chain_up) {
152 error = true;
153 Report.error (source_reference, "Multiple constructor calls in the same constructor are not permitted");
154 return false;
156 cm.chain_up = true;
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) {
163 error = true;
164 Report.error (source_reference, "chain up to `%s' not supported".printf (cl.get_full_name ()));
165 return false;
166 } else if (!base_cm.has_construct_function) {
167 error = true;
168 Report.error (source_reference, "chain up to `%s' not supported".printf (base_cm.get_full_name ()));
169 return false;
171 } else {
172 // GObject chain up
173 var cl = cm.parent_symbol as Class;
174 if (cl == null || !cl.is_subtype_of (analyzer.object_type)) {
175 error = true;
176 Report.error (source_reference, "chain up to `GLib.Object' not supported");
177 return false;
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);
198 return true;
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;
203 if (cm == null) {
204 error = true;
205 Report.error (source_reference, "use `new' operator to create new objects");
206 return false;
207 } else if (cm.chain_up) {
208 error = true;
209 Report.error (source_reference, "Multiple constructor calls in the same constructor are not permitted");
210 return false;
212 cm.chain_up = true;
214 var base_cm = (CreationMethod) call.symbol_reference;
215 if (!base_cm.has_construct_function) {
216 error = true;
217 Report.error (source_reference, "chain up to `%s' not supported".printf (base_cm.get_full_name ()));
218 return false;
222 if (mtype != null && mtype.is_invokable ()) {
223 // call ok, expression is invokable
224 } else if (call.symbol_reference is Class) {
225 error = true;
226 Report.error (source_reference, "use `new' operator to create new objects");
227 return false;
228 } else {
229 error = true;
230 Report.error (source_reference, "invocation not supported in this context");
231 return false;
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 ();
246 } else {
247 // end
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) {
259 break;
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;
271 break;
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);
281 last_arg = arg;
285 // printf arguments
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) {
301 break;
303 arg_it.next ();
306 } else {
307 // use instance as format string for string.printf (...)
308 var ma = call as MemberAccess;
309 if (ma != null) {
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 ();
320 while (c != '\0') {
321 if (c != '%') {
322 format_it = format_it.next_char ();
323 c = format_it.get_char ();
324 continue;
327 format_it = format_it.next_char ();
328 c = format_it.get_char ();
329 // flags
330 while (c == '#' || c == '0' || c == '-' || c == ' ' || c == '+') {
331 format_it = format_it.next_char ();
332 c = format_it.get_char ();
334 // field width
335 while (c >= '0' && c <= '9') {
336 format_it = format_it.next_char ();
337 c = format_it.get_char ();
339 // precision
340 if (c == '.') {
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 ();
348 // length modifier
349 int length = 0;
350 if (c == 'h') {
351 length = -1;
352 format_it = format_it.next_char ();
353 c = format_it.get_char ();
354 if (c == 'h') {
355 length = -2;
356 format_it = format_it.next_char ();
357 c = format_it.get_char ();
359 } else if (c == 'l') {
360 length = 1;
361 format_it = format_it.next_char ();
362 c = format_it.get_char ();
363 } else if (c == 'z') {
364 length = 2;
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') {
371 // integer
372 if (length == -2) {
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') {
384 // unsigned integer
385 if (length == -2) {
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') {
398 // double
399 param_type = analyzer.double_type;
400 } else if (c == 's') {
401 // string
402 param_type = analyzer.string_type;
403 } else if (c == 'p') {
404 // pointer
405 param_type = new PointerType (new VoidType ());
406 } else if (c == '%') {
407 // literal %
408 } else {
409 unsupported_format = true;
410 break;
412 if (c != '\0') {
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;
421 } else {
422 Report.error (source_reference, "Too few arguments for specified format");
423 return false;
427 if (!unsupported_format && arg_it.next ()) {
428 Report.error (source_reference, "Too many arguments for specified format");
429 return false;
434 foreach (Expression arg in get_argument_list ()) {
435 arg.check (analyzer);
438 if (ret_type is VoidType) {
439 // void return type
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
445 error = true;
446 Report.error (source_reference, "invocation of void method not allowed as expression");
447 return false;
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) {
459 if (!m.coroutine) {
460 error = true;
461 Report.error (source_reference, "yield expression requires async method");
463 if (analyzer.current_method == null || !analyzer.current_method.coroutine) {
464 error = true;
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
470 } else {
471 foreach (DataType error_type in m.get_error_types ()) {
472 may_throw = true;
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 ()) {
484 may_throw = true;
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 ())) {
495 error = true;
496 return false;
499 if (may_throw) {
500 if (parent_node is LocalVariable || parent_node is ExpressionStatement) {
501 // simple statements, no side effects after method call
502 } else {
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);
532 return !error;
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);