3 * Copyright (C) 2006-2009 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
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
29 * Represents a type or namespace method.
31 public class Vala
.Method
: Member
{
32 public const string DEFAULT_SENTINEL
= "NULL";
35 * The return type of this method.
37 public DataType return_type
{
38 get { return _return_type
; }
41 _return_type
.parent_node
= this
;
55 public BasicBlock entry_block
{ get; set; }
57 public BasicBlock exit_block
{ get; set; }
60 * Specifies whether this method may only be called with an instance of
63 public MemberBinding binding
{ get; set; default = MemberBinding
.INSTANCE
; }
66 * The name of the vfunc of this method as it is used in C code.
68 public string vfunc_name
{
70 if (_vfunc_name
== null) {
71 _vfunc_name
= this
.name
;
81 * The sentinel to use for terminating variable length argument lists.
83 public string sentinel
{
85 if (_sentinel
== null) {
86 return DEFAULT_SENTINEL
;
98 * Specifies whether this method is abstract. Abstract methods have no
99 * body, may only be specified within abstract classes, and must be
100 * overriden by derived non-abstract classes.
102 public bool is_abstract
{ get; set; }
105 * Specifies whether this method is virtual. Virtual methods may be
106 * overridden by derived classes.
108 public bool is_virtual
{ get; set; }
111 * Specifies whether this method overrides a virtual or abstract method
114 public bool overrides
{ get; set; }
117 * Specifies whether this method should be inlined.
119 public bool is_inline
{ get; set; }
122 * Specifies whether the C method returns a new instance pointer which
123 * may be different from the previous instance pointer. Only valid for
126 public bool returns_modified_pointer
{ get; set; }
129 * Specifies the virtual or abstract method this method overrides.
130 * Reference must be weak as virtual and abstract methods set
131 * base_method to themselves.
133 public Method base_method
{
135 find_base_methods ();
141 * Specifies the abstract interface method this method implements.
143 public Method base_interface_method
{
145 find_base_methods ();
146 return _base_interface_method
;
150 public bool entry_point
{ get; private set; }
153 * Specifies the generated `this' parameter for instance methods.
155 public FormalParameter this_parameter
{ get; set; }
158 * Specifies the generated `result' variable for postconditions.
160 public LocalVariable result_var
{ get; set; }
163 * Specifies the position of the instance parameter in the C function.
165 public double cinstance_parameter_position
{ get; set; }
168 * Specifies the position of the array length out parameter in the C
171 public double carray_length_parameter_position
{ get; set; }
174 * Specifies the position of the delegate target out parameter in the C
177 public double cdelegate_target_parameter_position
{ get; set; }
180 * Specifies whether the array length should be returned implicitly
181 * if the return type is an array.
183 public bool no_array_length
{ get; set; }
186 * Specifies whether the array is null terminated.
188 public bool array_null_terminated
{ get; set; }
191 * Specifies whether this method expects printf-style format arguments.
193 public bool printf_format
{ get; set; }
196 * Specifies whether a construct function with a GType parameter is
197 * available. This is only applicable to creation methods.
199 public bool has_construct_function
{ get; set; default = true; }
201 public bool coroutine
{ get; set; }
203 private Gee
.List
<FormalParameter
> parameters
= new ArrayList
<FormalParameter
> ();
204 private string cname
;
205 private string _vfunc_name
;
206 private string _sentinel
;
207 private Gee
.List
<Expression
> preconditions
= new ArrayList
<Expression
> ();
208 private Gee
.List
<Expression
> postconditions
= new ArrayList
<Expression
> ();
209 private DataType _return_type
;
212 private weak Method _base_method
;
213 private Method _base_interface_method
;
214 private bool base_methods_valid
;
217 * Creates a new method.
219 * @param name method name
220 * @param return_type method return type
221 * @param source reference to source code
222 * @return newly created method
224 public Method (string? name
, DataType return_type
, SourceReference? source_reference
= null) {
225 base (name
, source_reference
);
226 this
.return_type
= return_type
;
228 carray_length_parameter_position
= -3;
229 cdelegate_target_parameter_position
= -3;
233 * Appends parameter to this method.
235 * @param param a formal parameter
237 public void add_parameter (FormalParameter param
) {
238 // default C parameter position
239 param
.cparameter_position
= parameters
.size
+ 1;
240 param
.carray_length_parameter_position
= param
.cparameter_position
+ 0.1;
241 param
.cdelegate_target_parameter_position
= param
.cparameter_position
+ 0.1;
243 parameters
.add (param
);
244 if (!param
.ellipsis
) {
245 scope
.add (param
.name
, param
);
249 public Gee
.List
<FormalParameter
> get_parameters () {
250 return new ReadOnlyList
<FormalParameter
> (parameters
);
254 * Remove all parameters from this method.
256 public void clear_parameters () {
257 foreach (FormalParameter param
in parameters
) {
258 if (!param
.ellipsis
) {
259 scope
.remove (param
.name
);
265 public override void accept (CodeVisitor visitor
) {
266 visitor
.visit_method (this
);
269 public override void accept_children (CodeVisitor visitor
) {
270 if (return_type
!= null) {
271 return_type
.accept (visitor
);
274 foreach (FormalParameter param
in parameters
) {
275 param
.accept (visitor
);
278 foreach (DataType error_type
in get_error_types ()) {
279 error_type
.accept (visitor
);
282 if (result_var
!= null) {
283 result_var
.accept (visitor
);
286 foreach (Expression precondition
in preconditions
) {
287 precondition
.accept (visitor
);
290 foreach (Expression postcondition
in postconditions
) {
291 postcondition
.accept (visitor
);
295 body
.accept (visitor
);
300 * Returns the interface name of this method as it is used in C code.
302 * @return the name to be used in C code
304 public string get_cname () {
306 cname
= get_default_cname ();
312 * Returns the default interface name of this method as it is used in C
315 * @return the name to be used in C code by default
317 public virtual string get_default_cname () {
318 if (name
== "main" && parent_symbol
.name
== null) {
319 // avoid conflict with generated main function
321 } else if (name
.has_prefix ("_")) {
322 return "_%s%s".printf (parent_symbol
.get_lower_case_cprefix (), name
.offset (1));
324 return "%s%s".printf (parent_symbol
.get_lower_case_cprefix (), name
);
329 * Returns the implementation name of this data type as it is used in C
332 * @return the name to be used in C code
334 public virtual string get_real_cname () {
335 if (base_method
!= null || base_interface_method
!= null) {
336 return "%sreal_%s".printf (parent_symbol
.get_lower_case_cprefix (), name
);
343 * Sets the name of this method as it is used in C code.
345 * @param cname the name to be used in C code
347 public void set_cname (string cname
) {
351 private void process_ccode_attribute (Attribute a
) {
352 if (a
.has_argument ("cname")) {
353 set_cname (a
.get_string ("cname"));
355 if (a
.has_argument ("cheader_filename")) {
356 var val
= a
.get_string ("cheader_filename");
357 foreach (string filename
in val
.split (",")) {
358 add_cheader_filename (filename
);
361 if (a
.has_argument ("vfunc_name")) {
362 this
.vfunc_name
= a
.get_string ("vfunc_name");
364 if (a
.has_argument ("sentinel")) {
365 this
.sentinel
= a
.get_string ("sentinel");
367 if (a
.has_argument ("instance_pos")) {
368 cinstance_parameter_position
= a
.get_double ("instance_pos");
370 if (a
.has_argument ("array_length")) {
371 no_array_length
= !a
.get_bool ("array_length");
373 if (a
.has_argument ("array_null_terminated")) {
374 array_null_terminated
= a
.get_bool ("array_null_terminated");
376 if (a
.has_argument ("array_length_pos")) {
377 carray_length_parameter_position
= a
.get_double ("array_length_pos");
379 if (a
.has_argument ("delegate_target_pos")) {
380 cdelegate_target_parameter_position
= a
.get_double ("delegate_target_pos");
382 if (a
.has_argument ("has_construct_function")) {
383 has_construct_function
= a
.get_bool ("has_construct_function");
388 * Process all associated attributes.
390 public void process_attributes () {
391 foreach (Attribute a
in attributes
) {
392 if (a
.name
== "CCode") {
393 process_ccode_attribute (a
);
394 } else if (a
.name
== "ReturnsModifiedPointer") {
395 returns_modified_pointer
= true;
396 } else if (a
.name
== "FloatingReference") {
397 return_type
.floating_reference
= true;
398 } else if (a
.name
== "PrintfFormat") {
399 printf_format
= true;
405 * Checks whether the parameters and return type of this method are
406 * compatible with the specified method
408 * @param base_method a method
409 * @param invalid_match error string about which check failed
410 * @return true if the specified method is compatible to this method
412 public bool compatible (Method base_method
, out string? invalid_match
) {
413 ObjectType object_type
= null;
414 if (parent_symbol is ObjectTypeSymbol
) {
415 object_type
= new
ObjectType ((ObjectTypeSymbol
) parent_symbol
);
416 foreach (TypeParameter type_parameter
in object_type
.type_symbol
.get_type_parameters ()) {
417 var type_arg
= new
GenericType (type_parameter
);
418 type_arg
.value_owned
= true;
419 object_type
.add_type_argument (type_arg
);
423 var actual_base_type
= base_method
.return_type
.get_actual_type (object_type
, this
);
424 if (!return_type
.equals (actual_base_type
)) {
425 invalid_match
= "incompatible return type";
429 Iterator
<FormalParameter
> method_params_it
= parameters
.iterator ();
431 foreach (FormalParameter base_param
in base_method
.parameters
) {
432 /* this method may not expect less arguments */
433 if (!method_params_it
.next ()) {
434 invalid_match
= "too few parameters";
438 actual_base_type
= base_param
.parameter_type
.get_actual_type (object_type
, this
);
439 if (!actual_base_type
.equals (method_params_it
.get ().parameter_type
)) {
440 invalid_match
= "incompatible type of parameter %d".printf (param_index
);
446 /* this method may not expect more arguments */
447 if (method_params_it
.next ()) {
448 invalid_match
= "too many parameters";
452 /* this method may throw more but not less errors than the base method */
453 foreach (DataType method_error_type
in get_error_types ()) {
455 foreach (DataType base_method_error_type
in base_method
.get_error_types ()) {
456 if (method_error_type
.compatible (base_method_error_type
)) {
463 invalid_match
= "incompatible error type `%s'".printf (method_error_type
.to_string ());
472 * Adds a precondition to this method.
474 * @param precondition a boolean precondition expression
476 public void add_precondition (Expression precondition
) {
477 preconditions
.add (precondition
);
478 precondition
.parent_node
= this
;
482 * Returns a copy of the list of preconditions of this method.
484 * @return list of preconditions
486 public Gee
.List
<Expression
> get_preconditions () {
487 return new ReadOnlyList
<Expression
> (preconditions
);
491 * Adds a postcondition to this method.
493 * @param postcondition a boolean postcondition expression
495 public void add_postcondition (Expression postcondition
) {
496 postconditions
.add (postcondition
);
497 postcondition
.parent_node
= this
;
501 * Returns a copy of the list of postconditions of this method.
503 * @return list of postconditions
505 public Gee
.List
<Expression
> get_postconditions () {
506 return new ReadOnlyList
<Expression
> (postconditions
);
509 public override void replace_type (DataType old_type
, DataType new_type
) {
510 if (return_type
== old_type
) {
511 return_type
= new_type
;
514 var error_types
= get_error_types ();
515 for (int i
= 0; i
< error_types
.size
; i
++) {
516 if (error_types
[i
] == old_type
) {
517 error_types
[i
] = new_type
;
523 private void find_base_methods () {
524 if (base_methods_valid
) {
528 if (parent_symbol is Class
) {
529 /* VAPI classes don't specify overridden methods */
530 if (!parent_symbol
.external_package
) {
531 if (!(this is CreationMethod
)) {
532 find_base_interface_method ((Class
) parent_symbol
);
533 if (is_virtual
|| is_abstract
|| overrides
) {
534 find_base_class_method ((Class
) parent_symbol
);
537 } else if (is_virtual
|| is_abstract
) {
540 } else if (parent_symbol is Interface
) {
541 if (is_virtual
|| is_abstract
) {
542 _base_interface_method
= this
;
546 base_methods_valid
= true;
549 private void find_base_class_method (Class cl
) {
550 var sym
= cl
.scope
.lookup (name
);
552 var base_method
= (Method
) sym
;
553 if (base_method
.is_abstract
|| base_method
.is_virtual
) {
554 string invalid_match
;
555 if (!compatible (base_method
, out invalid_match
)) {
557 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
));
561 _base_method
= base_method
;
564 } else if (sym is Signal
) {
565 var sig
= (Signal
) sym
;
566 if (sig
.is_virtual
) {
567 var base_method
= sig
.get_method_handler ();
568 string invalid_match
;
569 if (!compatible (base_method
, out invalid_match
)) {
571 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
));
575 _base_method
= base_method
;
580 if (cl
.base_class
!= null) {
581 find_base_class_method (cl
.base_class
);
585 private void find_base_interface_method (Class cl
) {
586 // FIXME report error if multiple possible base methods are found
587 foreach (DataType type
in cl
.get_base_types ()) {
588 if (type
.data_type is Interface
) {
589 var sym
= type
.data_type
.scope
.lookup (name
);
591 var base_method
= (Method
) sym
;
592 if (base_method
.is_abstract
|| base_method
.is_virtual
) {
593 string invalid_match
;
594 if (!compatible (base_method
, out invalid_match
)) {
596 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
));
600 _base_interface_method
= base_method
;
608 public override bool check (SemanticAnalyzer analyzer
) {
615 process_attributes ();
618 if (parent_symbol is Class
) {
619 var cl
= (Class
) parent_symbol
;
620 if (!cl
.is_abstract
) {
622 Report
.error (source_reference
, "Abstract methods may not be declared in non-abstract classes");
625 } else if (!(parent_symbol is Interface
)) {
627 Report
.error (source_reference
, "Abstract methods may not be declared outside of classes and interfaces");
630 } else if (is_virtual
) {
631 if (!(parent_symbol is Class
) && !(parent_symbol is Interface
)) {
633 Report
.error (source_reference
, "Virtual methods may not be declared outside of classes and interfaces");
637 if (parent_symbol is Class
) {
638 var cl
= (Class
) parent_symbol
;
640 Report
.error (source_reference
, "Virtual methods may not be declared in compact classes");
644 } else if (overrides
) {
645 if (!(parent_symbol is Class
)) {
647 Report
.error (source_reference
, "Methods may not be overridden outside of classes");
652 if (is_abstract
&& body
!= null) {
653 Report
.error (source_reference
, "Abstract methods cannot have bodies");
654 } else if (external
&& body
!= null) {
655 Report
.error (source_reference
, "Extern methods cannot have bodies");
656 } else if (!is_abstract
&& !external
&& !external_package
&& body
== null) {
657 Report
.error (source_reference
, "Non-abstract, non-extern methods must have bodies");
660 var old_source_file
= analyzer
.current_source_file
;
661 var old_symbol
= analyzer
.current_symbol
;
662 var old_class
= analyzer
.current_class
;
663 var old_struct
= analyzer
.current_struct
;
664 var old_return_type
= analyzer
.current_return_type
;
666 if (source_reference
!= null) {
667 analyzer
.current_source_file
= source_reference
.file
;
669 analyzer
.current_symbol
= this
;
670 if (parent_symbol is Class
) {
671 analyzer
.current_class
= (Class
) parent_symbol
;
672 } else if (parent_symbol is Struct
) {
673 analyzer
.current_struct
= (Struct
) parent_symbol
;
675 analyzer
.current_return_type
= return_type
;
677 return_type
.check (analyzer
);
679 var init_attr
= get_attribute ("ModuleInit");
680 if (init_attr
!= null) {
681 source_reference
.file
.context
.module_init_method
= this
;
684 if (!is_internal_symbol ()) {
685 if (return_type is ValueType
) {
686 analyzer
.current_source_file
.add_type_dependency (return_type
, SourceFileDependencyType
.HEADER_FULL
);
688 analyzer
.current_source_file
.add_type_dependency (return_type
, SourceFileDependencyType
.HEADER_SHALLOW
);
691 analyzer
.current_source_file
.add_type_dependency (return_type
, SourceFileDependencyType
.SOURCE
);
693 if (return_type
!= null) {
694 return_type
.check (analyzer
);
697 foreach (FormalParameter param
in parameters
) {
698 param
.check (analyzer
);
701 foreach (DataType error_type
in get_error_types ()) {
702 error_type
.check (analyzer
);
705 if (result_var
!= null) {
706 result_var
.check (analyzer
);
709 foreach (Expression precondition
in preconditions
) {
710 precondition
.check (analyzer
);
713 foreach (Expression postcondition
in postconditions
) {
714 postcondition
.check (analyzer
);
718 body
.check (analyzer
);
721 analyzer
.current_source_file
= old_source_file
;
722 analyzer
.current_symbol
= old_symbol
;
723 analyzer
.current_class
= old_class
;
724 analyzer
.current_struct
= old_struct
;
725 analyzer
.current_return_type
= old_return_type
;
727 if (analyzer
.current_symbol
.parent_symbol is Method
) {
728 /* lambda expressions produce nested methods */
729 var up_method
= (Method
) analyzer
.current_symbol
.parent_symbol
;
730 analyzer
.current_return_type
= up_method
.return_type
;
733 if (analyzer
.current_symbol is Struct
) {
734 if (is_abstract
|| is_virtual
|| overrides
) {
735 Report
.error (source_reference
, "A struct member `%s' cannot be marked as override, virtual, or abstract".printf (get_full_name ()));
738 } else if (overrides
&& base_method
== null) {
739 Report
.error (source_reference
, "%s: no suitable method found to override".printf (get_full_name ()));
742 // check whether return type is at least as accessible as the method
743 if (!analyzer
.is_type_accessible (this
, return_type
)) {
745 Report
.error (source_reference
, "return type `%s` is less accessible than method `%s`".printf (return_type
.to_string (), get_full_name ()));
749 foreach (Expression precondition
in get_preconditions ()) {
750 if (precondition
.error
) {
751 // if there was an error in the precondition, skip this check
756 if (!precondition
.value_type
.compatible (analyzer
.bool_type
)) {
758 Report
.error (precondition
.source_reference
, "Precondition must be boolean");
763 foreach (Expression postcondition
in get_postconditions ()) {
764 if (postcondition
.error
) {
765 // if there was an error in the postcondition, skip this check
770 if (!postcondition
.value_type
.compatible (analyzer
.bool_type
)) {
772 Report
.error (postcondition
.source_reference
, "Postcondition must be boolean");
777 if (tree_can_fail
&& name
== "main") {
778 Report
.error (source_reference
, "\"main\" method cannot throw errors");
781 // check that all errors that can be thrown in the method body are declared
783 foreach (DataType body_error_type
in body
.get_error_types ()) {
784 bool can_propagate_error
= false;
785 foreach (DataType method_error_type
in get_error_types ()) {
786 if (body_error_type
.compatible (method_error_type
)) {
787 can_propagate_error
= true;
790 if (!can_propagate_error
) {
791 Report
.warning (body_error_type
.source_reference
, "unhandled error `%s'".printf (body_error_type
.to_string()));
796 if (is_possible_entry_point (analyzer
)) {
803 bool is_possible_entry_point (SemanticAnalyzer analyzer
) {
804 if (name
== null || name
!= "main") {
805 // method must be called "main"
809 if (binding
== MemberBinding
.INSTANCE
) {
810 // method must be static
814 if (return_type is VoidType
) {
815 } else if (return_type
.data_type
== analyzer
.int_type
.data_type
) {
817 // return type must be void or int
821 var params
= get_parameters ();
822 if (params
.size
== 0) {
823 // method may have no parameters
827 if (params
.size
> 1) {
828 // method must not have more than one parameter
832 Iterator
<FormalParameter
> params_it
= params
.iterator ();
834 var param
= params_it
.get ();
836 if (param
.direction
== ParameterDirection
.OUT
) {
837 // parameter must not be an out parameter
841 if (!(param
.parameter_type is ArrayType
)) {
842 // parameter must be an array
846 var array_type
= (ArrayType
) param
.parameter_type
;
847 if (array_type
.element_type
.data_type
!= analyzer
.string_type
.data_type
) {
848 // parameter must be an array of strings