3 * Copyright (C) 2006-2010 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
20 * Jürg Billeter <j@bitron.ch>
26 * Represents a function callback type.
28 public class Vala
.Delegate
: TypeSymbol
, Callable
{
30 * The return type of this callback.
32 public DataType return_type
{
33 get { return _return_type
; }
36 _return_type
.parent_node
= this
;
41 * Specifies whether callback supports calling instance methods.
42 * The reference to the object instance will be appended to the end of
43 * the argument list in the generated C code.
45 public bool has_target
{
47 if (_has_target
== null) {
48 _has_target
= get_attribute_bool ("CCode", "has_target", true);
55 remove_attribute_argument ("CCode", "has_target");
57 set_attribute_bool ("CCode", "has_target", false);
62 public DataType? sender_type
{ get; set; }
64 private List
<TypeParameter
> type_parameters
= new ArrayList
<TypeParameter
> ();
66 private List
<Parameter
> parameters
= new ArrayList
<Parameter
> ();
68 private DataType _return_type
;
69 private bool? _has_target
;
72 * Creates a new delegate.
74 * @param name delegate type name
75 * @param return_type return type
76 * @param source_reference reference to source code
77 * @return newly created delegate
79 public Delegate (string? name
, DataType return_type
, SourceReference? source_reference
= null, Comment? comment
= null) {
80 base (name
, source_reference
, comment
);
81 this
.return_type
= return_type
;
85 * Appends the specified parameter to the list of type parameters.
87 * @param p a type parameter
89 public void add_type_parameter (TypeParameter p
) {
90 type_parameters
.add (p
);
91 scope
.add (p
.name
, p
);
94 public List
<TypeParameter
> get_type_parameters () {
95 return type_parameters
;
98 public override int get_type_parameter_index (string name
) {
100 foreach (TypeParameter parameter
in type_parameters
) {
101 if (parameter
.name
== name
) {
110 * Appends paramater to this callback function.
112 * @param param a formal parameter
114 public void add_parameter (Parameter param
) {
115 parameters
.add (param
);
116 scope
.add (param
.name
, param
);
120 * Return copy of parameter list.
122 * @return parameter list
124 public List
<Parameter
> get_parameters () {
129 * Checks whether the arguments and return type of the specified method
130 * matches this callback.
133 * @return true if the specified method is compatible to this callback
135 public bool matches_method (Method m
, DataType dt
) {
136 if (m
.coroutine
&& !(parent_symbol is Signal
)) {
137 // async delegates are not yet supported
141 // method is allowed to ensure stricter return type (stronger postcondition)
142 if (!m
.return_type
.stricter (return_type
.get_actual_type (dt
, null, this
))) {
146 var method_params
= m
.get_parameters ();
147 Iterator
<Parameter
> method_params_it
= method_params
.iterator ();
149 if (sender_type
!= null && method_params
.size
== parameters
.size
+ 1) {
150 // method has sender parameter
151 method_params_it
.next ();
153 // method is allowed to accept arguments of looser types (weaker precondition)
154 var method_param
= method_params_it
.get ();
155 if (!sender_type
.stricter (method_param
.variable_type
)) {
161 foreach (Parameter param
in parameters
) {
162 /* use first callback parameter as instance parameter if
163 * an instance method is being compared to a static
166 if (first
&& m
.binding
== MemberBinding
.INSTANCE
&& !has_target
) {
171 /* method is allowed to accept less arguments */
172 if (!method_params_it
.next ()) {
176 // method is allowed to accept arguments of looser types (weaker precondition)
177 var method_param
= method_params_it
.get ();
178 if (!param
.variable_type
.get_actual_type (dt
, null, this
).stricter (method_param
.variable_type
)) {
183 /* method may not expect more arguments */
184 if (method_params_it
.next ()) {
188 var error_types
= get_error_types ();
189 var method_error_types
= m
.get_error_types ();
191 // method must throw error if the delegate does
192 if (error_types
.size
> 0 && method_error_types
.size
== 0) {
196 // method may throw less but not more errors than the delegate
197 foreach (DataType method_error_type
in method_error_types
) {
199 foreach (DataType delegate_error_type
in error_types
) {
200 if (method_error_type
.compatible (delegate_error_type
)) {
214 public override void accept (CodeVisitor visitor
) {
215 visitor
.visit_delegate (this
);
218 public override void accept_children (CodeVisitor visitor
) {
219 foreach (TypeParameter p
in type_parameters
) {
223 return_type
.accept (visitor
);
225 foreach (Parameter param
in parameters
) {
226 param
.accept (visitor
);
229 foreach (DataType error_type
in get_error_types ()) {
230 error_type
.accept (visitor
);
234 public override bool is_reference_type () {
238 public override void replace_type (DataType old_type
, DataType new_type
) {
239 if (return_type
== old_type
) {
240 return_type
= new_type
;
243 var error_types
= get_error_types ();
244 for (int i
= 0; i
< error_types
.size
; i
++) {
245 if (error_types
[i
] == old_type
) {
246 error_types
[i
] = new_type
;
252 public override bool check (CodeContext context
) {
259 var old_source_file
= context
.analyzer
.current_source_file
;
261 if (source_reference
!= null) {
262 context
.analyzer
.current_source_file
= source_reference
.file
;
265 foreach (TypeParameter p
in type_parameters
) {
269 return_type
.check (context
);
271 foreach (Parameter param
in parameters
) {
272 param
.check (context
);
275 foreach (DataType error_type
in get_error_types ()) {
276 error_type
.check (context
);
279 context
.analyzer
.current_source_file
= old_source_file
;