Fix `this' access in default signal handlers
[vala-lang.git] / vala / valamethodcall.vala
blob44e9ee686f183daec8ef77ca37303bfe5ca93c98
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;
24 using Gee;
26 /**
27 * Represents an invocation expression in the source code.
29 public class Vala.MethodCall : Expression {
30 /**
31 * The method to call.
33 public Expression call {
34 get { return _call; }
35 set {
36 _call = value;
37 _call.parent_node = this;
41 public bool is_yield_expression { get; set; }
43 public Expression _call;
45 private Gee.List<Expression> argument_list = new ArrayList<Expression> ();
47 /**
48 * Creates a new invocation expression.
50 * @param call method to call
51 * @param source_reference reference to source code
52 * @return newly created invocation expression
54 public MethodCall (Expression call, SourceReference? source_reference = null) {
55 this.source_reference = source_reference;
56 this.call = call;
59 /**
60 * Appends the specified expression to the list of arguments.
62 * @param arg an argument
64 public void add_argument (Expression arg) {
65 argument_list.add (arg);
66 arg.parent_node = this;
69 /**
70 * Returns a copy of the argument list.
72 * @return argument list
74 public Gee.List<Expression> get_argument_list () {
75 return new ReadOnlyList<Expression> (argument_list);
78 public override void accept (CodeVisitor visitor) {
79 visitor.visit_method_call (this);
81 visitor.visit_expression (this);
84 public override void accept_children (CodeVisitor visitor) {
85 call.accept (visitor);
87 foreach (Expression expr in argument_list) {
88 expr.accept (visitor);
92 public override void replace_expression (Expression old_node, Expression new_node) {
93 if (call == old_node) {
94 call = new_node;
97 int index = argument_list.index_of (old_node);
98 if (index >= 0 && new_node.parent_node == null) {
99 argument_list[index] = new_node;
100 new_node.parent_node = this;
104 public override bool is_pure () {
105 return false;
108 public override bool check (SemanticAnalyzer analyzer) {
109 if (checked) {
110 return !error;
113 checked = true;
115 if (!call.check (analyzer)) {
116 /* if method resolving didn't succeed, skip this check */
117 error = true;
118 return false;
121 // type of target object
122 DataType target_object_type = null;
124 if (call is MemberAccess) {
125 var ma = (MemberAccess) call;
126 if (ma.prototype_access) {
127 error = true;
128 Report.error (source_reference, "Access to instance member `%s' denied".printf (call.symbol_reference.get_full_name ()));
129 return false;
132 if (ma.inner != null) {
133 target_object_type = ma.inner.value_type;
137 var mtype = call.value_type;
139 if (mtype is ObjectType) {
140 // constructor chain-up
141 var cm = analyzer.find_current_method () as CreationMethod;
142 if (cm == null) {
143 error = true;
144 Report.error (source_reference, "invocation not supported in this context");
145 return false;
146 } else if (cm.chain_up) {
147 error = true;
148 Report.error (source_reference, "Multiple constructor calls in the same constructor are not permitted");
149 return false;
151 cm.chain_up = true;
153 var otype = (ObjectType) mtype;
154 var cl = (Class) otype.type_symbol;
155 var base_cm = cl.default_construction_method;
156 if (!base_cm.has_construct_function) {
157 error = true;
158 Report.error (source_reference, "chain up to `%s' not supported".printf (base_cm.get_full_name ()));
159 return false;
163 // check for struct construction
164 if (call is MemberAccess &&
165 ((call.symbol_reference is CreationMethod
166 && call.symbol_reference.parent_symbol is Struct)
167 || call.symbol_reference is Struct)) {
168 var struct_creation_expression = new ObjectCreationExpression ((MemberAccess) call, source_reference);
169 struct_creation_expression.struct_creation = true;
170 foreach (Expression arg in get_argument_list ()) {
171 struct_creation_expression.add_argument (arg);
173 struct_creation_expression.target_type = target_type;
174 analyzer.replaced_nodes.add (this);
175 parent_node.replace_expression (this, struct_creation_expression);
176 struct_creation_expression.check (analyzer);
177 return true;
178 } else if (call is MemberAccess
179 && call.symbol_reference is CreationMethod) {
180 // constructor chain-up
181 var cm = analyzer.find_current_method () as CreationMethod;
182 if (cm == null) {
183 error = true;
184 Report.error (source_reference, "use `new' operator to create new objects");
185 return false;
186 } else if (cm.chain_up) {
187 error = true;
188 Report.error (source_reference, "Multiple constructor calls in the same constructor are not permitted");
189 return false;
191 cm.chain_up = true;
193 var base_cm = (CreationMethod) call.symbol_reference;
194 if (!base_cm.has_construct_function) {
195 error = true;
196 Report.error (source_reference, "chain up to `%s' not supported".printf (base_cm.get_full_name ()));
197 return false;
201 if (mtype != null && mtype.is_invokable ()) {
202 // call ok, expression is invokable
203 } else if (call.symbol_reference is Class) {
204 error = true;
205 Report.error (source_reference, "use `new' operator to create new objects");
206 return false;
207 } else {
208 error = true;
209 Report.error (source_reference, "invocation not supported in this context");
210 return false;
213 var ret_type = mtype.get_return_type ();
214 var params = mtype.get_parameters ();
216 if (mtype is MethodType) {
217 var m = ((MethodType) mtype).method_symbol;
218 if (m != null && m.coroutine && !is_yield_expression) {
219 // begin or end call of async method
220 var ma = (MemberAccess) call;
221 if (ma.member_name != "end") {
222 // begin (possibly implicit)
223 params = m.get_async_begin_parameters ();
224 ret_type = new VoidType ();
225 } else {
226 // end
227 params = m.get_async_end_parameters ();
232 Expression last_arg = null;
234 var args = get_argument_list ();
235 Iterator<Expression> arg_it = args.iterator ();
236 foreach (FormalParameter param in params) {
237 if (param.ellipsis) {
238 break;
241 if (param.params_array) {
242 var array_type = (ArrayType) param.parameter_type;
243 while (arg_it.next ()) {
244 Expression arg = arg_it.get ();
246 /* store expected type for callback parameters */
247 arg.target_type = array_type.element_type;
248 arg.target_type.value_owned = array_type.value_owned;
250 break;
253 if (arg_it.next ()) {
254 Expression arg = arg_it.get ();
256 /* store expected type for callback parameters */
257 arg.formal_target_type = param.parameter_type;
258 arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, call as MemberAccess, this);
260 last_arg = arg;
264 // printf arguments
265 if (mtype is MethodType && ((MethodType) mtype).method_symbol.printf_format) {
266 StringLiteral format_literal = null;
267 if (last_arg != null) {
268 // use last argument as format string
269 format_literal = last_arg as StringLiteral;
270 } else {
271 // use instance as format string for string.printf (...)
272 var ma = call as MemberAccess;
273 if (ma != null) {
274 format_literal = ma.inner as StringLiteral;
277 if (format_literal != null) {
278 string format = format_literal.eval ();
280 bool unsupported_format = false;
282 weak string format_it = format;
283 unichar c = format_it.get_char ();
284 while (c != '\0') {
285 if (c != '%') {
286 format_it = format_it.next_char ();
287 c = format_it.get_char ();
288 continue;
291 format_it = format_it.next_char ();
292 c = format_it.get_char ();
293 // flags
294 while (c == '#' || c == '0' || c == '-' || c == ' ' || c == '+') {
295 format_it = format_it.next_char ();
296 c = format_it.get_char ();
298 // field width
299 while (c >= '0' && c <= '9') {
300 format_it = format_it.next_char ();
301 c = format_it.get_char ();
303 // precision
304 if (c == '.') {
305 format_it = format_it.next_char ();
306 c = format_it.get_char ();
307 while (c >= '0' && c <= '9') {
308 format_it = format_it.next_char ();
309 c = format_it.get_char ();
312 // length modifier
313 int length = 0;
314 if (c == 'h') {
315 length = -1;
316 format_it = format_it.next_char ();
317 c = format_it.get_char ();
318 if (c == 'h') {
319 length = -2;
320 format_it = format_it.next_char ();
321 c = format_it.get_char ();
323 } else if (c == 'l') {
324 length = 1;
325 format_it = format_it.next_char ();
326 c = format_it.get_char ();
327 } else if (c == 'z') {
328 length = 2;
329 format_it = format_it.next_char ();
330 c = format_it.get_char ();
332 // conversion specifier
333 DataType param_type = null;
334 if (c == 'd' || c == 'i' || c == 'c') {
335 // integer
336 if (length == -2) {
337 param_type = analyzer.int8_type;
338 } else if (length == -1) {
339 param_type = analyzer.short_type;
340 } else if (length == 0) {
341 param_type = analyzer.int_type;
342 } else if (length == 1) {
343 param_type = analyzer.long_type;
344 } else if (length == 2) {
345 param_type = analyzer.ssize_t_type;
347 } else if (c == 'o' || c == 'u' || c == 'x' || c == 'X') {
348 // unsigned integer
349 if (length == -2) {
350 param_type = analyzer.uchar_type;
351 } else if (length == -1) {
352 param_type = analyzer.ushort_type;
353 } else if (length == 0) {
354 param_type = analyzer.uint_type;
355 } else if (length == 1) {
356 param_type = analyzer.ulong_type;
357 } else if (length == 2) {
358 param_type = analyzer.size_t_type;
360 } else if (c == 'e' || c == 'E' || c == 'f' || c == 'F'
361 || c == 'g' || c == 'G' || c == 'a' || c == 'A') {
362 // double
363 param_type = analyzer.double_type;
364 } else if (c == 's') {
365 // string
366 param_type = analyzer.string_type;
367 } else if (c == 'p') {
368 // pointer
369 param_type = new PointerType (new VoidType ());
370 } else if (c == '%') {
371 // literal %
372 } else {
373 unsupported_format = true;
374 break;
376 if (c != '\0') {
377 format_it = format_it.next_char ();
378 c = format_it.get_char ();
380 if (param_type != null) {
381 if (arg_it.next ()) {
382 Expression arg = arg_it.get ();
384 arg.target_type = param_type;
385 } else {
386 Report.error (source_reference, "Too few arguments for specified format");
387 return false;
391 if (!unsupported_format && arg_it.next ()) {
392 Report.error (source_reference, "Too many arguments for specified format");
393 return false;
398 foreach (Expression arg in get_argument_list ()) {
399 arg.check (analyzer);
402 if (ret_type is VoidType) {
403 // void return type
404 if (!(parent_node is ExpressionStatement)
405 && !(parent_node is ForStatement)
406 && !(parent_node is YieldStatement)) {
407 // A void method invocation can be in the initializer or
408 // iterator of a for statement
409 error = true;
410 Report.error (source_reference, "invocation of void method not allowed as expression");
411 return false;
415 formal_value_type = ret_type;
416 value_type = formal_value_type.get_actual_type (target_object_type, call as MemberAccess, this);
418 bool may_throw = false;
420 if (mtype is MethodType) {
421 var m = ((MethodType) mtype).method_symbol;
422 if (is_yield_expression) {
423 if (!m.coroutine) {
424 error = true;
425 Report.error (source_reference, "yield expression requires async method");
427 if (analyzer.current_method == null || !analyzer.current_method.coroutine) {
428 error = true;
429 Report.error (source_reference, "yield expression not available outside async method");
432 if (m != null && m.coroutine && !is_yield_expression && ((MemberAccess) call).member_name != "end") {
433 // .begin call of async method, no error can happen here
434 } else {
435 foreach (DataType error_type in m.get_error_types ()) {
436 may_throw = true;
438 // ensure we can trace back which expression may throw errors of this type
439 var call_error_type = error_type.copy ();
440 call_error_type.source_reference = source_reference;
442 add_error_type (call_error_type);
445 } else if (mtype is DelegateType) {
446 var d = ((DelegateType) mtype).delegate_symbol;
447 foreach (DataType error_type in d.get_error_types ()) {
448 may_throw = true;
450 // ensure we can trace back which expression may throw errors of this type
451 var call_error_type = error_type.copy ();
452 call_error_type.source_reference = source_reference;
454 add_error_type (call_error_type);
458 if (!analyzer.check_arguments (this, mtype, params, get_argument_list ())) {
459 error = true;
460 return false;
463 if (may_throw) {
464 if (parent_node is LocalVariable || parent_node is ExpressionStatement) {
465 // simple statements, no side effects after method call
466 } else {
467 // store parent_node as we need to replace the expression in the old parent node later on
468 var old_parent_node = parent_node;
470 var local = new LocalVariable (value_type, get_temp_name (), null, source_reference);
471 // use floating variable to avoid unnecessary (and sometimes impossible) copies
472 local.floating = true;
473 var decl = new DeclarationStatement (local, source_reference);
475 insert_statement (analyzer.insert_block, decl);
477 Expression temp_access = new MemberAccess.simple (local.name, source_reference);
478 temp_access.target_type = target_type;
480 // don't set initializer earlier as this changes parent_node and parent_statement
481 local.initializer = this;
482 decl.check (analyzer);
483 temp_access.check (analyzer);
485 // move temp variable to insert block to ensure the
486 // variable is in the same block as the declaration
487 // otherwise there will be scoping issues in the generated code
488 var block = (Block) analyzer.current_symbol;
489 block.remove_local_variable (local);
490 analyzer.insert_block.add_local_variable (local);
492 old_parent_node.replace_expression (this, temp_access);
496 return !error;
499 public override void get_defined_variables (Collection<LocalVariable> collection) {
500 call.get_defined_variables (collection);
502 foreach (Expression arg in argument_list) {
503 arg.get_defined_variables (collection);
507 public override void get_used_variables (Collection<LocalVariable> collection) {
508 call.get_used_variables (collection);
510 foreach (Expression arg in argument_list) {
511 arg.get_used_variables (collection);