Improve string tests
[vala-lang.git] / vala / valamethodcall.vala
blob670464d1db9ff4a0cceb437a7a7342390fb8e613
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 CCodeExpression delegate_target { get; set; }
43 public Expression _call;
45 private Gee.List<Expression> argument_list = new ArrayList<Expression> ();
46 private Gee.List<CCodeExpression> array_sizes = new ArrayList<CCodeExpression> ();
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 Gee.List<Expression> get_argument_list () {
76 return new ReadOnlyList<Expression> (argument_list);
79 /**
80 * Add an array size C code expression.
82 public void append_array_size (CCodeExpression size) {
83 array_sizes.add (size);
86 /**
87 * Get the C code expression for array sizes for all dimensions
88 * ascending from left to right.
90 public Gee.List<CCodeExpression> get_array_sizes () {
91 return new ReadOnlyList<CCodeExpression> (array_sizes);
94 public override void accept (CodeVisitor visitor) {
95 visitor.visit_method_call (this);
97 visitor.visit_expression (this);
100 public override void accept_children (CodeVisitor visitor) {
101 call.accept (visitor);
103 foreach (Expression expr in argument_list) {
104 expr.accept (visitor);
108 public override void replace_expression (Expression old_node, Expression new_node) {
109 if (call == old_node) {
110 call = new_node;
113 int index = argument_list.index_of (old_node);
114 if (index >= 0 && new_node.parent_node == null) {
115 argument_list[index] = new_node;
116 new_node.parent_node = this;
120 public override bool is_pure () {
121 return false;
124 public override bool check (SemanticAnalyzer analyzer) {
125 if (checked) {
126 return !error;
129 checked = true;
131 if (!call.check (analyzer)) {
132 /* if method resolving didn't succeed, skip this check */
133 error = true;
134 return false;
137 // type of target object
138 DataType target_object_type = null;
140 if (call is MemberAccess) {
141 var ma = (MemberAccess) call;
142 if (ma.prototype_access) {
143 error = true;
144 Report.error (source_reference, "Access to instance member `%s' denied".printf (call.symbol_reference.get_full_name ()));
145 return false;
148 if (ma.inner != null) {
149 target_object_type = ma.inner.value_type;
153 var mtype = call.value_type;
155 if (mtype is ObjectType) {
156 // constructor chain-up
157 var cm = analyzer.find_current_method () as CreationMethod;
158 if (cm == null) {
159 error = true;
160 Report.error (source_reference, "use `new' operator to create new objects");
161 return false;
162 } else if (cm.chain_up) {
163 error = true;
164 Report.error (source_reference, "Multiple constructor calls in the same constructor are not permitted");
165 return false;
167 cm.chain_up = true;
170 // check for struct construction
171 if (call is MemberAccess &&
172 ((call.symbol_reference is CreationMethod
173 && call.symbol_reference.parent_symbol is Struct)
174 || call.symbol_reference is Struct)) {
175 var struct_creation_expression = new ObjectCreationExpression ((MemberAccess) call, source_reference);
176 struct_creation_expression.struct_creation = true;
177 foreach (Expression arg in get_argument_list ()) {
178 struct_creation_expression.add_argument (arg);
180 struct_creation_expression.target_type = target_type;
181 analyzer.replaced_nodes.add (this);
182 parent_node.replace_expression (this, struct_creation_expression);
183 struct_creation_expression.check (analyzer);
184 return true;
185 } else if (call is MemberAccess
186 && call.symbol_reference is CreationMethod) {
187 // constructor chain-up
188 var cm = analyzer.find_current_method () as CreationMethod;
189 if (cm == null) {
190 error = true;
191 Report.error (source_reference, "use `new' operator to create new objects");
192 return false;
193 } else if (cm.chain_up) {
194 error = true;
195 Report.error (source_reference, "Multiple constructor calls in the same constructor are not permitted");
196 return false;
198 cm.chain_up = true;
201 Gee.List<FormalParameter> params;
203 if (mtype != null && mtype.is_invokable ()) {
204 params = mtype.get_parameters ();
205 } else if (call.symbol_reference is Class) {
206 error = true;
207 Report.error (source_reference, "use `new' operator to create new objects");
208 return false;
209 } else {
210 error = true;
211 Report.error (source_reference, "invocation not supported in this context");
212 return false;
215 Expression last_arg = null;
217 var args = get_argument_list ();
218 Iterator<Expression> arg_it = args.iterator ();
219 foreach (FormalParameter param in params) {
220 if (param.ellipsis) {
221 break;
224 if (param.params_array) {
225 var array_type = (ArrayType) param.parameter_type;
226 while (arg_it.next ()) {
227 Expression arg = arg_it.get ();
229 /* store expected type for callback parameters */
230 arg.target_type = array_type.element_type;
231 arg.target_type.value_owned = array_type.value_owned;
233 break;
236 if (arg_it.next ()) {
237 Expression arg = arg_it.get ();
239 /* store expected type for callback parameters */
240 arg.formal_target_type = param.parameter_type;
241 arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, this);
243 last_arg = arg;
247 // printf arguments
248 if (mtype is MethodType && ((MethodType) mtype).method_symbol.printf_format) {
249 StringLiteral format_literal = null;
250 if (last_arg != null) {
251 // use last argument as format string
252 format_literal = last_arg as StringLiteral;
253 } else {
254 // use instance as format string for string.printf (...)
255 var ma = call as MemberAccess;
256 if (ma != null) {
257 format_literal = ma.inner as StringLiteral;
260 if (format_literal != null) {
261 string format = format_literal.eval ();
263 bool unsupported_format = false;
265 weak string format_it = format;
266 unichar c = format_it.get_char ();
267 while (c != '\0') {
268 if (c != '%') {
269 format_it = format_it.next_char ();
270 c = format_it.get_char ();
271 continue;
274 format_it = format_it.next_char ();
275 c = format_it.get_char ();
276 // flags
277 while (c == '#' || c == '0' || c == '-' || c == ' ' || c == '+') {
278 format_it = format_it.next_char ();
279 c = format_it.get_char ();
281 // field width
282 while (c >= '0' && c <= '9') {
283 format_it = format_it.next_char ();
284 c = format_it.get_char ();
286 // precision
287 if (c == '.') {
288 format_it = format_it.next_char ();
289 c = format_it.get_char ();
290 while (c >= '0' && c <= '9') {
291 format_it = format_it.next_char ();
292 c = format_it.get_char ();
295 // length modifier
296 int length = 0;
297 if (c == 'h') {
298 length = -1;
299 format_it = format_it.next_char ();
300 c = format_it.get_char ();
301 if (c == 'h') {
302 length = -2;
303 format_it = format_it.next_char ();
304 c = format_it.get_char ();
306 } else if (c == 'l') {
307 length = 1;
308 format_it = format_it.next_char ();
309 c = format_it.get_char ();
310 } else if (c == 'z') {
311 length = 2;
312 format_it = format_it.next_char ();
313 c = format_it.get_char ();
315 // conversion specifier
316 DataType param_type = null;
317 if (c == 'd' || c == 'i' || c == 'c') {
318 // integer
319 if (length == -2) {
320 param_type = analyzer.int8_type;
321 } else if (length == -1) {
322 param_type = analyzer.short_type;
323 } else if (length == 0) {
324 param_type = analyzer.int_type;
325 } else if (length == 1) {
326 param_type = analyzer.long_type;
327 } else if (length == 2) {
328 param_type = analyzer.ssize_t_type;
330 } else if (c == 'o' || c == 'u' || c == 'x' || c == 'X') {
331 // unsigned integer
332 if (length == -2) {
333 param_type = analyzer.uchar_type;
334 } else if (length == -1) {
335 param_type = analyzer.ushort_type;
336 } else if (length == 0) {
337 param_type = analyzer.uint_type;
338 } else if (length == 1) {
339 param_type = analyzer.ulong_type;
340 } else if (length == 2) {
341 param_type = analyzer.size_t_type;
343 } else if (c == 'e' || c == 'E' || c == 'f' || c == 'F'
344 || c == 'g' || c == 'G' || c == 'a' || c == 'A') {
345 // double
346 param_type = analyzer.double_type;
347 } else if (c == 's') {
348 // string
349 param_type = analyzer.string_type;
350 } else if (c == 'p') {
351 // pointer
352 param_type = new PointerType (new VoidType ());
353 } else if (c == '%') {
354 // literal %
355 } else {
356 unsupported_format = true;
357 break;
359 if (c != '\0') {
360 format_it = format_it.next_char ();
361 c = format_it.get_char ();
363 if (param_type != null) {
364 if (arg_it.next ()) {
365 Expression arg = arg_it.get ();
367 arg.target_type = param_type;
368 } else {
369 Report.error (source_reference, "Too few arguments for specified format");
370 return false;
374 if (!unsupported_format && arg_it.next ()) {
375 Report.error (source_reference, "Too many arguments for specified format");
376 return false;
381 foreach (Expression arg in get_argument_list ()) {
382 arg.check (analyzer);
385 DataType ret_type = mtype.get_return_type ();
386 params = mtype.get_parameters ();
388 if (ret_type is VoidType) {
389 // void return type
390 if (!(parent_node is ExpressionStatement)
391 && !(parent_node is ForStatement)
392 && !(parent_node is YieldStatement)) {
393 // A void method invocation can be in the initializer or
394 // iterator of a for statement
395 error = true;
396 Report.error (source_reference, "invocation of void method not allowed as expression");
397 return false;
401 formal_value_type = ret_type;
402 value_type = formal_value_type.get_actual_type (target_object_type, this);
404 bool may_throw = false;
406 if (mtype is MethodType) {
407 var m = ((MethodType) mtype).method_symbol;
408 foreach (DataType error_type in m.get_error_types ()) {
409 may_throw = true;
411 // ensure we can trace back which expression may throw errors of this type
412 var call_error_type = error_type.copy ();
413 call_error_type.source_reference = source_reference;
415 add_error_type (call_error_type);
417 } else if (mtype is DelegateType) {
418 var d = ((DelegateType) mtype).delegate_symbol;
419 foreach (DataType error_type in d.get_error_types ()) {
420 may_throw = true;
422 // ensure we can trace back which expression may throw errors of this type
423 var call_error_type = error_type.copy ();
424 call_error_type.source_reference = source_reference;
426 add_error_type (call_error_type);
430 if (!analyzer.check_arguments (this, mtype, params, get_argument_list ())) {
431 error = true;
432 return false;
435 if (may_throw) {
436 if (parent_node is LocalVariable || parent_node is ExpressionStatement) {
437 // simple statements, no side effects after method call
438 } else {
439 var old_insert_block = analyzer.insert_block;
440 analyzer.insert_block = prepare_condition_split (analyzer);
442 // store parent_node as we need to replace the expression in the old parent node later on
443 var old_parent_node = parent_node;
445 var local = new LocalVariable (value_type, get_temp_name (), null, source_reference);
446 // use floating variable to avoid unnecessary (and sometimes impossible) copies
447 local.floating = true;
448 var decl = new DeclarationStatement (local, source_reference);
450 insert_statement (analyzer.insert_block, decl);
452 Expression temp_access = new MemberAccess.simple (local.name, source_reference);
453 temp_access.target_type = target_type;
455 // don't set initializer earlier as this changes parent_node and parent_statement
456 local.initializer = this;
457 decl.check (analyzer);
458 temp_access.check (analyzer);
460 // move temp variable to insert block to ensure the
461 // variable is in the same block as the declaration
462 // otherwise there will be scoping issues in the generated code
463 var block = (Block) analyzer.current_symbol;
464 block.remove_local_variable (local);
465 analyzer.insert_block.add_local_variable (local);
467 analyzer.insert_block = old_insert_block;
469 old_parent_node.replace_expression (this, temp_access);
473 return !error;
476 public override void get_defined_variables (Collection<LocalVariable> collection) {
477 call.get_defined_variables (collection);
479 foreach (Expression arg in argument_list) {
480 arg.get_defined_variables (collection);
484 public override void get_used_variables (Collection<LocalVariable> collection) {
485 call.get_used_variables (collection);
487 foreach (Expression arg in argument_list) {
488 arg.get_used_variables (collection);