codegen: Use temporary variable for string concatenation
[vala-lang.git] / vala / valamethod.vala
blob8ef46b0336a0107b6a3bd9f3d4871bcceff8752a
1 /* valamethod.vala
3 * Copyright (C) 2006-2010 Jürg Billeter
4 * Copyright (C) 2006-2008 Raffaele Sandrini
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Author:
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
25 using GLib;
27 /**
28 * Represents a type or namespace method.
30 public class Vala.Method : Subroutine {
31 List<TypeParameter> type_parameters;
33 public const string DEFAULT_SENTINEL = "NULL";
35 /**
36 * The return type of this method.
38 public DataType return_type {
39 get { return _return_type; }
40 set {
41 _return_type = value;
42 _return_type.parent_node = this;
46 public override bool has_result {
47 get { return !(return_type is VoidType); }
50 /**
51 * Specifies whether this method may only be called with an instance of
52 * the contained type.
54 public MemberBinding binding { get; set; default = MemberBinding.INSTANCE; }
56 /**
57 * The name of the vfunc of this method as it is used in C code.
59 public string vfunc_name {
60 get {
61 if (_vfunc_name == null) {
62 _vfunc_name = this.name;
64 return _vfunc_name;
66 set {
67 _vfunc_name = value;
71 /**
72 * The sentinel to use for terminating variable length argument lists.
74 public string sentinel {
75 get {
76 if (_sentinel == null) {
77 return DEFAULT_SENTINEL;
80 return _sentinel;
83 set {
84 _sentinel = value;
88 /**
89 * Specifies whether this method is abstract. Abstract methods have no
90 * body, may only be specified within abstract classes, and must be
91 * overriden by derived non-abstract classes.
93 public bool is_abstract { get; set; }
95 /**
96 * Specifies whether this method is virtual. Virtual methods may be
97 * overridden by derived classes.
99 public bool is_virtual { get; set; }
102 * Specifies whether this method overrides a virtual or abstract method
103 * of a base type.
105 public bool overrides { get; set; }
108 * Specifies whether this method should be inlined.
110 public bool is_inline { get; set; }
112 public bool returns_floating_reference { get; set; }
115 * Specifies whether the C method returns a new instance pointer which
116 * may be different from the previous instance pointer. Only valid for
117 * imported methods.
119 public bool returns_modified_pointer { get; set; }
122 * Specifies the virtual or abstract method this method overrides.
123 * Reference must be weak as virtual and abstract methods set
124 * base_method to themselves.
126 public Method base_method {
127 get {
128 find_base_methods ();
129 return _base_method;
134 * Specifies the abstract interface method this method implements.
136 public Method base_interface_method {
137 get {
138 find_base_methods ();
139 return _base_interface_method;
143 public bool entry_point { get; private set; }
146 * Specifies the generated `this` parameter for instance methods.
148 public Parameter this_parameter { get; set; }
151 * Specifies the position of the instance parameter in the C function.
153 public double cinstance_parameter_position { get; set; }
156 * Specifies the position of the array length out parameter in the C
157 * function.
159 public double carray_length_parameter_position { get; set; }
162 * Specifies the position of the delegate target out parameter in the C
163 * function.
165 public double cdelegate_target_parameter_position { get; set; }
168 * Specifies whether the array length should be returned implicitly
169 * if the return type is an array.
171 public bool no_array_length { get; set; }
174 * Specifies whether the array is null terminated.
176 public bool array_null_terminated { get; set; }
179 * Specified a custom type for the array length parameter.
181 public string? array_length_type { get; set; default = null; }
184 * Specifies whether this method expects printf-style format arguments.
186 public bool printf_format { get; set; }
189 * Specifies whether this method expects scanf-style format arguments.
191 public bool scanf_format { get; set; }
194 * Specifies whether a new function without a GType parameter is
195 * available. This is only applicable to creation methods.
197 public bool has_new_function { get; set; default = true; }
200 * Specifies whether a construct function with a GType parameter is
201 * available. This is only applicable to creation methods.
203 public bool has_construct_function { get; set; default = true; }
205 public bool has_generic_type_parameter { get; set; }
207 public double generic_type_parameter_position { get; set; }
209 public bool simple_generics { get; set; }
211 public weak Signal signal_reference { get; set; }
213 public bool closure { get; set; }
215 public bool coroutine { get; set; }
217 public bool is_async_callback { get; set; }
219 public int yield_count { get; set; }
221 private List<Parameter> parameters = new ArrayList<Parameter> ();
222 private string cname;
223 private string finish_name;
224 private string _vfunc_name;
225 private string _sentinel;
226 private List<Expression> preconditions;
227 private List<Expression> postconditions;
228 private DataType _return_type;
230 private weak Method _base_method;
231 private Method _base_interface_method;
232 private bool base_methods_valid;
234 Method? callback_method;
236 // only valid for closures
237 List<LocalVariable> captured_variables;
239 static List<Expression> _empty_expression_list;
240 static List<TypeParameter> _empty_type_parameter_list;
243 * Creates a new method.
245 * @param name method name
246 * @param return_type method return type
247 * @param source reference to source code
248 * @return newly created method
250 public Method (string? name, DataType return_type, SourceReference? source_reference = null, Comment? comment = null) {
251 base (name, source_reference, comment);
252 this.return_type = return_type;
254 carray_length_parameter_position = -3;
255 cdelegate_target_parameter_position = -3;
259 * Appends parameter to this method.
261 * @param param a formal parameter
263 public void add_parameter (Parameter param) {
264 // default C parameter position
265 param.cparameter_position = parameters.size + 1;
266 param.carray_length_parameter_position = param.cparameter_position + 0.1;
267 param.cdelegate_target_parameter_position = param.cparameter_position + 0.1;
268 param.cdestroy_notify_parameter_position = param.cparameter_position + 0.1;
270 parameters.add (param);
271 if (!param.ellipsis) {
272 scope.add (param.name, param);
276 public List<Parameter> get_parameters () {
277 return parameters;
281 * Remove all parameters from this method.
283 public void clear_parameters () {
284 foreach (Parameter param in parameters) {
285 if (!param.ellipsis) {
286 scope.remove (param.name);
289 parameters.clear ();
292 public override void accept (CodeVisitor visitor) {
293 visitor.visit_method (this);
296 public override void accept_children (CodeVisitor visitor) {
297 foreach (TypeParameter p in get_type_parameters ()) {
298 p.accept (visitor);
301 if (return_type != null) {
302 return_type.accept (visitor);
305 foreach (Parameter param in parameters) {
306 param.accept (visitor);
309 foreach (DataType error_type in get_error_types ()) {
310 error_type.accept (visitor);
313 if (result_var != null) {
314 result_var.accept (visitor);
317 if (preconditions != null) {
318 foreach (Expression precondition in preconditions) {
319 precondition.accept (visitor);
323 if (postconditions != null) {
324 foreach (Expression postcondition in postconditions) {
325 postcondition.accept (visitor);
329 if (body != null) {
330 body.accept (visitor);
335 * Returns the interface name of this method as it is used in C code.
337 * @return the name to be used in C code
339 public string get_cname () {
340 if (cname == null) {
341 cname = get_default_cname ();
343 return cname;
346 public string get_finish_cname () {
347 assert (coroutine);
348 if (finish_name == null) {
349 finish_name = get_default_finish_cname ();
351 return finish_name;
354 public void set_finish_cname (string name) {
355 finish_name = name;
359 * Returns the default interface name of this method as it is used in C
360 * code.
362 * @return the name to be used in C code by default
364 public virtual string get_default_cname () {
365 if (name == "main" && parent_symbol.name == null) {
366 // avoid conflict with generated main function
367 return "_vala_main";
368 } else if (name.has_prefix ("_")) {
369 return "_%s%s".printf (parent_symbol.get_lower_case_cprefix (), name.substring (1));
370 } else {
371 return "%s%s".printf (parent_symbol.get_lower_case_cprefix (), name);
376 * Returns the implementation name of this data type as it is used in C
377 * code.
379 * @return the name to be used in C code
381 public virtual string get_real_cname () {
382 if (base_method != null || base_interface_method != null) {
383 return "%sreal_%s".printf (parent_symbol.get_lower_case_cprefix (), name);
384 } else {
385 return get_cname ();
389 protected string get_finish_name_for_basename (string basename) {
390 string result = basename;
391 if (result.has_suffix ("_async")) {
392 result = result.substring (0, result.length - "_async".length);
394 result += "_finish";
395 return result;
398 public string get_finish_real_cname () {
399 assert (coroutine);
400 return get_finish_name_for_basename (get_real_cname ());
403 public string get_finish_vfunc_name () {
404 assert (coroutine);
405 return get_finish_name_for_basename (vfunc_name);
408 public string get_default_finish_cname () {
409 return get_finish_name_for_basename (get_cname ());
413 * Sets the name of this method as it is used in C code.
415 * @param cname the name to be used in C code
417 public void set_cname (string cname) {
418 this.cname = cname;
421 private void process_ccode_attribute (Attribute a) {
422 if (a.has_argument ("cname")) {
423 set_cname (a.get_string ("cname"));
425 if (a.has_argument ("cheader_filename")) {
426 var val = a.get_string ("cheader_filename");
427 foreach (string filename in val.split (",")) {
428 add_cheader_filename (filename);
431 if (a.has_argument ("vfunc_name")) {
432 this.vfunc_name = a.get_string ("vfunc_name");
434 if (a.has_argument ("finish_name")) {
435 this.finish_name = a.get_string ("finish_name");
437 if (a.has_argument ("sentinel")) {
438 this.sentinel = a.get_string ("sentinel");
440 if (a.has_argument ("instance_pos")) {
441 cinstance_parameter_position = a.get_double ("instance_pos");
443 if (a.has_argument ("array_length")) {
444 no_array_length = !a.get_bool ("array_length");
446 if (a.has_argument ("array_length_type")) {
447 array_length_type = a.get_string ("array_length_type");
449 if (a.has_argument ("array_null_terminated")) {
450 array_null_terminated = a.get_bool ("array_null_terminated");
452 if (a.has_argument ("array_length_pos")) {
453 carray_length_parameter_position = a.get_double ("array_length_pos");
455 if (a.has_argument ("delegate_target_pos")) {
456 cdelegate_target_parameter_position = a.get_double ("delegate_target_pos");
458 if (a.has_argument ("has_new_function")) {
459 has_new_function = a.get_bool ("has_new_function");
461 if (a.has_argument ("has_construct_function")) {
462 has_construct_function = a.get_bool ("has_construct_function");
464 if (a.has_argument ("generic_type_pos")) {
465 has_generic_type_parameter = true;
466 generic_type_parameter_position = a.get_double ("generic_type_pos");
468 if (a.has_argument ("simple_generics")) {
469 simple_generics = a.get_bool ("simple_generics");
471 if (a.has_argument ("returns_floating_reference")) {
472 returns_floating_reference = a.get_bool ("returns_floating_reference");
477 * Process all associated attributes.
479 public void process_attributes () {
480 foreach (Attribute a in attributes) {
481 if (a.name == "CCode") {
482 process_ccode_attribute (a);
483 } else if (a.name == "ReturnsModifiedPointer") {
484 returns_modified_pointer = true;
485 } else if (a.name == "FloatingReference") {
486 return_type.floating_reference = true;
487 } else if (a.name == "PrintfFormat") {
488 printf_format = true;
489 } else if (a.name == "ScanfFormat") {
490 scanf_format = true;
491 } else if (a.name == "NoArrayLength") {
492 Report.warning (source_reference, "NoArrayLength attribute is deprecated, use [CCode (array_length = false)] instead.");
493 no_array_length = true;
494 } else if (a.name == "Deprecated") {
495 process_deprecated_attribute (a);
496 } else if (a.name == "NoThrow") {
497 get_error_types ().clear ();
503 * Checks whether the parameters and return type of this method are
504 * compatible with the specified method
506 * @param base_method a method
507 * @param invalid_match error string about which check failed
508 * @return true if the specified method is compatible to this method
510 public bool compatible (Method base_method, out string? invalid_match) {
511 if (binding != base_method.binding) {
512 invalid_match = "incompatible binding";
513 return false;
516 ObjectType object_type = null;
517 if (parent_symbol is ObjectTypeSymbol) {
518 object_type = new ObjectType ((ObjectTypeSymbol) parent_symbol);
519 foreach (TypeParameter type_parameter in object_type.type_symbol.get_type_parameters ()) {
520 var type_arg = new GenericType (type_parameter);
521 type_arg.value_owned = true;
522 object_type.add_type_argument (type_arg);
526 var actual_base_type = base_method.return_type.get_actual_type (object_type, null, this);
527 if (!return_type.equals (actual_base_type)) {
528 invalid_match = "incompatible return type";
529 return false;
532 Iterator<Parameter> method_params_it = parameters.iterator ();
533 int param_index = 1;
534 foreach (Parameter base_param in base_method.parameters) {
535 /* this method may not expect less arguments */
536 if (!method_params_it.next ()) {
537 invalid_match = "too few parameters";
538 return false;
541 var param = method_params_it.get ();
542 if (base_param.ellipsis != param.ellipsis) {
543 invalid_match = "ellipsis parameter mismatch";
544 return false;
546 if (!base_param.ellipsis) {
547 actual_base_type = base_param.variable_type.get_actual_type (object_type, null, this);
548 if (!actual_base_type.equals (param.variable_type)) {
549 invalid_match = "incompatible type of parameter %d".printf (param_index);
550 return false;
553 param_index++;
556 /* this method may not expect more arguments */
557 if (method_params_it.next ()) {
558 invalid_match = "too many parameters";
559 return false;
562 /* this method may throw less but not more errors than the base method */
563 foreach (DataType method_error_type in get_error_types ()) {
564 bool match = false;
565 foreach (DataType base_method_error_type in base_method.get_error_types ()) {
566 if (method_error_type.compatible (base_method_error_type)) {
567 match = true;
568 break;
572 if (!match) {
573 invalid_match = "incompatible error type `%s'".printf (method_error_type.to_string ());
574 return false;
577 if (base_method.coroutine != this.coroutine) {
578 invalid_match = "async mismatch";
579 return false;
582 return true;
586 * Appends the specified parameter to the list of type parameters.
588 * @param p a type parameter
590 public void add_type_parameter (TypeParameter p) {
591 if (type_parameters == null) {
592 type_parameters = new ArrayList<TypeParameter> ();
594 type_parameters.add (p);
595 scope.add (p.name, p);
599 * Returns a copy of the type parameter list.
601 * @return list of type parameters
603 public List<TypeParameter> get_type_parameters () {
604 if (type_parameters != null) {
605 return type_parameters;
607 if (_empty_type_parameter_list == null) {
608 _empty_type_parameter_list = new ArrayList<TypeParameter> ();
610 return _empty_type_parameter_list;
613 public int get_type_parameter_index (string name) {
614 if (type_parameters == null) {
615 return -1;
618 int i = 0;
619 foreach (TypeParameter parameter in type_parameters) {
620 if (parameter.name == name) {
621 return i;
623 i++;
625 return -1;
629 * Adds a precondition to this method.
631 * @param precondition a boolean precondition expression
633 public void add_precondition (Expression precondition) {
634 if (preconditions == null) {
635 preconditions = new ArrayList<Expression> ();
637 preconditions.add (precondition);
638 precondition.parent_node = this;
642 * Returns a copy of the list of preconditions of this method.
644 * @return list of preconditions
646 public List<Expression> get_preconditions () {
647 if (preconditions != null) {
648 return preconditions;
650 if (_empty_expression_list == null) {
651 _empty_expression_list = new ArrayList<Expression> ();
653 return _empty_expression_list;
657 * Adds a postcondition to this method.
659 * @param postcondition a boolean postcondition expression
661 public void add_postcondition (Expression postcondition) {
662 if (postconditions == null) {
663 postconditions = new ArrayList<Expression> ();
665 postconditions.add (postcondition);
666 postcondition.parent_node = this;
670 * Returns a copy of the list of postconditions of this method.
672 * @return list of postconditions
674 public List<Expression> get_postconditions () {
675 if (postconditions != null) {
676 return postconditions;
678 if (_empty_expression_list == null) {
679 _empty_expression_list = new ArrayList<Expression> ();
681 return _empty_expression_list;
684 public override void replace_type (DataType old_type, DataType new_type) {
685 if (return_type == old_type) {
686 return_type = new_type;
687 return;
689 var error_types = get_error_types ();
690 for (int i = 0; i < error_types.size; i++) {
691 if (error_types[i] == old_type) {
692 error_types[i] = new_type;
693 return;
698 private void find_base_methods () {
699 if (base_methods_valid) {
700 return;
703 if (parent_symbol is Class) {
704 if (!(this is CreationMethod)) {
705 find_base_interface_method ((Class) parent_symbol);
706 if (is_virtual || is_abstract || overrides) {
707 find_base_class_method ((Class) parent_symbol);
710 } else if (parent_symbol is Interface) {
711 if (is_virtual || is_abstract) {
712 _base_interface_method = this;
716 base_methods_valid = true;
719 private void find_base_class_method (Class cl) {
720 var sym = cl.scope.lookup (name);
721 if (sym is Method) {
722 var base_method = (Method) sym;
723 if (base_method.is_abstract || base_method.is_virtual) {
724 string invalid_match;
725 if (!compatible (base_method, out invalid_match)) {
726 error = true;
727 Report.error (source_reference, "overriding method `%s' is incompatible with base method `%s': %s.".printf (get_full_name (), base_method.get_full_name (), invalid_match));
728 return;
731 _base_method = base_method;
732 return;
734 } else if (sym is Signal) {
735 var sig = (Signal) sym;
736 if (sig.is_virtual) {
737 var base_method = sig.default_handler;
738 string invalid_match;
739 if (!compatible (base_method, out invalid_match)) {
740 error = true;
741 Report.error (source_reference, "overriding method `%s' is incompatible with base method `%s': %s.".printf (get_full_name (), base_method.get_full_name (), invalid_match));
742 return;
745 _base_method = base_method;
746 return;
750 if (cl.base_class != null) {
751 find_base_class_method (cl.base_class);
755 private void find_base_interface_method (Class cl) {
756 // FIXME report error if multiple possible base methods are found
757 foreach (DataType type in cl.get_base_types ()) {
758 if (type.data_type is Interface) {
759 var sym = type.data_type.scope.lookup (name);
760 if (sym is Method) {
761 var base_method = (Method) sym;
762 if (base_method.is_abstract || base_method.is_virtual) {
763 string invalid_match;
764 if (!compatible (base_method, out invalid_match)) {
765 error = true;
766 Report.error (source_reference, "overriding method `%s' is incompatible with base method `%s': %s.".printf (get_full_name (), base_method.get_full_name (), invalid_match));
767 return;
770 _base_interface_method = base_method;
771 return;
778 public override bool check (CodeContext context) {
779 if (checked) {
780 return !error;
783 checked = true;
785 process_attributes ();
787 if (is_abstract) {
788 if (parent_symbol is Class) {
789 var cl = (Class) parent_symbol;
790 if (!cl.is_abstract) {
791 error = true;
792 Report.error (source_reference, "Abstract methods may not be declared in non-abstract classes");
793 return false;
795 } else if (!(parent_symbol is Interface)) {
796 error = true;
797 Report.error (source_reference, "Abstract methods may not be declared outside of classes and interfaces");
798 return false;
800 } else if (is_virtual) {
801 if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
802 error = true;
803 Report.error (source_reference, "Virtual methods may not be declared outside of classes and interfaces");
804 return false;
807 if (parent_symbol is Class) {
808 var cl = (Class) parent_symbol;
809 if (cl.is_compact) {
810 Report.error (source_reference, "Virtual methods may not be declared in compact classes");
811 return false;
814 } else if (overrides) {
815 if (!(parent_symbol is Class)) {
816 error = true;
817 Report.error (source_reference, "Methods may not be overridden outside of classes");
818 return false;
820 } else if (access == SymbolAccessibility.PROTECTED) {
821 if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
822 error = true;
823 Report.error (source_reference, "Protected methods may not be declared outside of classes and interfaces");
824 return false;
828 if (is_abstract && body != null) {
829 Report.error (source_reference, "Abstract methods cannot have bodies");
830 } else if ((is_abstract || is_virtual) && external && !external_package && !parent_symbol.external) {
831 Report.error (source_reference, "Extern methods cannot be abstract or virtual");
832 } else if (external && body != null) {
833 Report.error (source_reference, "Extern methods cannot have bodies");
834 } else if (!is_abstract && !external && source_type == SourceFileType.SOURCE && body == null) {
835 Report.error (source_reference, "Non-abstract, non-extern methods must have bodies");
838 if (coroutine && !external_package && !context.has_package ("gio-2.0")) {
839 error = true;
840 Report.error (source_reference, "gio-2.0 package required for async methods");
841 return false;
844 var old_source_file = context.analyzer.current_source_file;
845 var old_symbol = context.analyzer.current_symbol;
847 if (source_reference != null) {
848 context.analyzer.current_source_file = source_reference.file;
850 context.analyzer.current_symbol = this;
852 return_type.check (context);
854 var init_attr = get_attribute ("ModuleInit");
855 if (init_attr != null) {
856 source_reference.file.context.module_init_method = this;
859 if (return_type != null) {
860 return_type.check (context);
863 if (parameters.size == 1 && parameters[0].ellipsis && body != null) {
864 // accept just `...' for external methods for convenience
865 error = true;
866 Report.error (parameters[0].source_reference, "Named parameter required before `...'");
869 foreach (Parameter param in parameters) {
870 param.check (context);
871 if (coroutine && param.direction == ParameterDirection.REF) {
872 error = true;
873 Report.error (param.source_reference, "Reference parameters are not supported for async methods");
877 foreach (DataType error_type in get_error_types ()) {
878 error_type.check (context);
880 // check whether error type is at least as accessible as the method
881 if (!context.analyzer.is_type_accessible (this, error_type)) {
882 error = true;
883 Report.error (source_reference, "error type `%s` is less accessible than method `%s`".printf (error_type.to_string (), get_full_name ()));
884 return false;
888 if (result_var != null) {
889 result_var.check (context);
892 if (preconditions != null) {
893 foreach (Expression precondition in preconditions) {
894 precondition.check (context);
898 if (postconditions != null) {
899 foreach (Expression postcondition in postconditions) {
900 postcondition.check (context);
904 if (body != null) {
905 body.check (context);
908 context.analyzer.current_source_file = old_source_file;
909 context.analyzer.current_symbol = old_symbol;
911 if (context.analyzer.current_struct != null) {
912 if (is_abstract || is_virtual || overrides) {
913 error = true;
914 Report.error (source_reference, "A struct member `%s' cannot be marked as override, virtual, or abstract".printf (get_full_name ()));
915 return false;
917 } else if (overrides && base_method == null) {
918 Report.error (source_reference, "%s: no suitable method found to override".printf (get_full_name ()));
919 } else if ((is_abstract || is_virtual || overrides) && access == SymbolAccessibility.PRIVATE) {
920 error = true;
921 Report.error (source_reference, "Private member `%s' cannot be marked as override, virtual, or abstract".printf (get_full_name ()));
922 return false;
925 if (!external_package && !overrides && !hides && get_hidden_member () != null) {
926 Report.warning (source_reference, "%s hides inherited method `%s'. Use the `new' keyword if hiding was intentional".printf (get_full_name (), get_hidden_member ().get_full_name ()));
929 // check whether return type is at least as accessible as the method
930 if (!context.analyzer.is_type_accessible (this, return_type)) {
931 error = true;
932 Report.error (source_reference, "return type `%s` is less accessible than method `%s`".printf (return_type.to_string (), get_full_name ()));
933 return false;
936 foreach (Expression precondition in get_preconditions ()) {
937 if (precondition.error) {
938 // if there was an error in the precondition, skip this check
939 error = true;
940 return false;
943 if (!precondition.value_type.compatible (context.analyzer.bool_type)) {
944 error = true;
945 Report.error (precondition.source_reference, "Precondition must be boolean");
946 return false;
950 foreach (Expression postcondition in get_postconditions ()) {
951 if (postcondition.error) {
952 // if there was an error in the postcondition, skip this check
953 error = true;
954 return false;
957 if (!postcondition.value_type.compatible (context.analyzer.bool_type)) {
958 error = true;
959 Report.error (postcondition.source_reference, "Postcondition must be boolean");
960 return false;
964 // check that all errors that can be thrown in the method body are declared
965 if (body != null) {
966 foreach (DataType body_error_type in body.get_error_types ()) {
967 bool can_propagate_error = false;
968 foreach (DataType method_error_type in get_error_types ()) {
969 if (body_error_type.compatible (method_error_type)) {
970 can_propagate_error = true;
973 bool is_dynamic_error = body_error_type is ErrorType && ((ErrorType) body_error_type).dynamic_error;
974 if (!can_propagate_error && !is_dynamic_error) {
975 Report.warning (body_error_type.source_reference, "unhandled error `%s'".printf (body_error_type.to_string()));
980 if (is_possible_entry_point (context)) {
981 if (context.entry_point != null) {
982 error = true;
983 Report.error (source_reference, "program already has an entry point `%s'".printf (context.entry_point.get_full_name ()));
984 return false;
986 entry_point = true;
987 context.entry_point = this;
989 if (tree_can_fail && context.profile != Profile.DOVA) {
990 Report.error (source_reference, "\"main\" method cannot throw errors");
994 return !error;
997 bool is_possible_entry_point (CodeContext context) {
998 if (external_package) {
999 return false;
1002 if (context.entry_point_name == null) {
1003 if (name == null || name != "main") {
1004 // method must be called "main"
1005 return false;
1007 } else {
1008 // custom entry point name
1009 if (get_full_name () != context.entry_point_name) {
1010 return false;
1014 if (binding == MemberBinding.INSTANCE) {
1015 // method must be static
1016 return false;
1019 if (return_type is VoidType) {
1020 } else if (return_type.data_type == context.analyzer.int_type.data_type) {
1021 } else {
1022 // return type must be void or int
1023 return false;
1026 var params = get_parameters ();
1027 if (params.size == 0) {
1028 // method may have no parameters
1029 return true;
1032 if (params.size > 1) {
1033 // method must not have more than one parameter
1034 return false;
1037 Iterator<Parameter> params_it = params.iterator ();
1038 params_it.next ();
1039 var param = params_it.get ();
1041 if (param.direction == ParameterDirection.OUT) {
1042 // parameter must not be an out parameter
1043 return false;
1046 if (!(param.variable_type is ArrayType)) {
1047 // parameter must be an array
1048 return false;
1051 var array_type = (ArrayType) param.variable_type;
1052 if (array_type.element_type.data_type != context.analyzer.string_type.data_type) {
1053 // parameter must be an array of strings
1054 return false;
1057 return true;
1060 public int get_required_arguments () {
1061 int n = 0;
1062 foreach (var param in parameters) {
1063 if (param.initializer != null || param.ellipsis) {
1064 // optional argument
1065 break;
1067 n++;
1069 return n;
1072 public Method get_callback_method () {
1073 assert (this.coroutine);
1075 if (callback_method == null) {
1076 var bool_type = new BooleanType ((Struct) CodeContext.get ().root.scope.lookup ("bool"));
1077 bool_type.value_owned = true;
1078 callback_method = new Method ("callback", bool_type, source_reference);
1079 callback_method.access = SymbolAccessibility.PUBLIC;
1080 callback_method.external = true;
1081 callback_method.binding = MemberBinding.INSTANCE;
1082 callback_method.owner = scope;
1083 callback_method.is_async_callback = true;
1084 callback_method.set_cname (get_real_cname () + "_co");
1086 return callback_method;
1089 public List<Parameter> get_async_begin_parameters () {
1090 assert (this.coroutine);
1092 var glib_ns = CodeContext.get ().root.scope.lookup ("GLib");
1094 var params = new ArrayList<Parameter> ();
1095 foreach (var param in parameters) {
1096 if (param.direction == ParameterDirection.IN) {
1097 params.add (param);
1101 var callback_type = new DelegateType ((Delegate) glib_ns.scope.lookup ("AsyncReadyCallback"));
1102 callback_type.nullable = true;
1103 callback_type.is_called_once = true;
1105 var callback_param = new Parameter ("_callback_", callback_type);
1106 callback_param.initializer = new NullLiteral (source_reference);
1107 callback_param.initializer.target_type = callback_type.copy ();
1108 callback_param.cparameter_position = -1;
1109 callback_param.cdelegate_target_parameter_position = -0.9;
1111 params.add (callback_param);
1113 return params;
1116 public List<Parameter> get_async_end_parameters () {
1117 assert (this.coroutine);
1119 var params = new ArrayList<Parameter> ();
1121 var glib_ns = CodeContext.get ().root.scope.lookup ("GLib");
1122 var result_type = new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("AsyncResult"));
1124 var result_param = new Parameter ("_res_", result_type);
1125 result_param.cparameter_position = 0.1;
1126 params.add (result_param);
1128 foreach (var param in parameters) {
1129 if (param.direction == ParameterDirection.OUT) {
1130 params.add (param);
1134 return params;
1137 public void add_captured_variable (LocalVariable local) {
1138 assert (this.closure);
1140 if (captured_variables == null) {
1141 captured_variables = new ArrayList<LocalVariable> ();
1143 captured_variables.add (local);
1146 public void get_captured_variables (Collection<LocalVariable> variables) {
1147 if (captured_variables != null) {
1148 foreach (var local in captured_variables) {
1149 variables.add (local);
1154 public override void get_defined_variables (Collection<LocalVariable> collection) {
1155 // capturing variables is only supported if they are initialized
1156 // therefore assume that captured variables are initialized
1157 if (closure) {
1158 get_captured_variables (collection);
1163 // vim:sw=8 noet