Line data Source code
1 : /* valadelegate.vala
2 : *
3 : * Copyright (C) 2006-2010 Jürg Billeter
4 : *
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.
9 :
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.
14 :
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
18 : *
19 : * Author:
20 : * Jürg Billeter <j@bitron.ch>
21 : */
22 :
23 : using GLib;
24 :
25 : /**
26 : * Represents a function callback type.
27 : */
28 386930 : public class Vala.Delegate : TypeSymbol, Callable, GenericSymbol {
29 : /**
30 : * The return type of this callback.
31 : */
32 : public DataType return_type {
33 775346 : get { return _return_type; }
34 268822 : set {
35 282072 : _return_type = value;
36 268822 : _return_type.parent_node = this;
37 : }
38 : }
39 :
40 : /**
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.
44 : */
45 : public bool has_target {
46 54166 : get {
47 54166 : if (_has_target == null) {
48 44444 : _has_target = get_attribute_bool ("CCode", "has_target", true);
49 : }
50 54166 : return _has_target;
51 : }
52 338 : set {
53 338 : _has_target = value;
54 338 : if (value) {
55 103 : remove_attribute_argument ("CCode", "has_target");
56 : } else {
57 235 : set_attribute_bool ("CCode", "has_target", false);
58 : }
59 : }
60 : }
61 :
62 198590 : public DataType? sender_type { get; set; }
63 :
64 382316 : private List<TypeParameter> type_parameters = new ArrayList<TypeParameter> ();
65 :
66 382316 : private List<Parameter> parameters = new ArrayList<Parameter> ();
67 :
68 191158 : private DataType _return_type;
69 191158 : private bool? _has_target;
70 :
71 191158 : private List<DataType> error_types;
72 :
73 : /**
74 : * Creates a new delegate.
75 : *
76 : * @param name delegate type name
77 : * @param return_type return type
78 : * @param source_reference reference to source code
79 : * @return newly created delegate
80 : */
81 573474 : public Delegate (string? name, DataType return_type, SourceReference? source_reference = null, Comment? comment = null) {
82 191158 : base (name, source_reference, comment);
83 191158 : this.return_type = return_type;
84 : }
85 :
86 : /**
87 : * Appends the specified parameter to the list of type parameters.
88 : *
89 : * @param p a type parameter
90 : */
91 42866 : public void add_type_parameter (TypeParameter p) {
92 42866 : type_parameters.add (p);
93 42866 : scope.add (p.name, p);
94 : }
95 :
96 1090758 : public unowned List<TypeParameter> get_type_parameters () {
97 1090758 : return type_parameters;
98 : }
99 :
100 108 : public bool has_type_parameters () {
101 108 : return (type_parameters != null && type_parameters.size > 0);
102 : }
103 :
104 32593 : public override int get_type_parameter_index (string name) {
105 32593 : int i = 0;
106 38259 : foreach (TypeParameter parameter in type_parameters) {
107 35426 : if (parameter.name == name) {
108 32593 : return i;
109 : }
110 2833 : i++;
111 : }
112 32593 : return -1;
113 : }
114 :
115 : /**
116 : * Appends parameter to this callback function.
117 : *
118 : * @param param a formal parameter
119 : */
120 405983 : public void add_parameter (Parameter param) {
121 405983 : parameters.add (param);
122 405983 : scope.add (param.name, param);
123 : }
124 :
125 : /**
126 : * Return the parameter list.
127 : *
128 : * @return parameter list
129 : */
130 21873 : public unowned List<Parameter> get_parameters () {
131 21873 : return parameters;
132 : }
133 :
134 : /**
135 : * Checks whether the arguments and return type of the specified method
136 : * matches this callback.
137 : *
138 : * @param m a method
139 : * @return true if the specified method is compatible to this callback
140 : */
141 7740 : public bool matches_method (Method m, DataType dt) {
142 7740 : if (m.coroutine && !(parent_symbol is Signal)) {
143 : // async delegates are not yet supported
144 8 : return false;
145 : }
146 :
147 : // method is allowed to ensure stricter return type (stronger postcondition)
148 7740 : if (!m.return_type.stricter (return_type.get_actual_type (dt, null, this))) {
149 8 : return false;
150 : }
151 :
152 7740 : var method_params = m.get_parameters ();
153 7740 : Iterator<Parameter> method_params_it = method_params.iterator ();
154 :
155 7753 : if (sender_type != null && method_params.size == parameters.size + 1) {
156 : // method has sender parameter
157 14 : method_params_it.next ();
158 :
159 : // method is allowed to accept arguments of looser types (weaker precondition)
160 14 : var method_param = method_params_it.get ();
161 14 : if (!sender_type.stricter (method_param.variable_type)) {
162 1 : return false;
163 : }
164 : }
165 :
166 7739 : bool first = true;
167 33973 : foreach (Parameter param in parameters) {
168 13165 : Parameter? method_param = null;
169 : DataType method_param_type;
170 : /* use first callback parameter as instance parameter if
171 : * an instance method is being compared to a static
172 : * callback
173 : */
174 13165 : if (first && m.binding == MemberBinding.INSTANCE && !has_target) {
175 6 : first = false;
176 6 : method_param_type = SemanticAnalyzer.get_data_type_for_symbol (m.parent_symbol);
177 : } else {
178 : /* method is allowed to accept less arguments */
179 13159 : if (!method_params_it.next ()) {
180 23 : break;
181 : }
182 13136 : method_param = method_params_it.get ();
183 13136 : method_param_type = method_param.variable_type;
184 : }
185 :
186 13142 : if (method_param != null && (param.ellipsis || param.params_array)) {
187 23 : if (param.ellipsis != method_param.ellipsis || param.params_array != method_param.params_array) {
188 0 : return false;
189 : }
190 23 : break;
191 : }
192 :
193 : // method is allowed to accept arguments of looser types (weaker precondition)
194 13119 : if (!param.variable_type.get_actual_type (dt, null, this).stricter (method_param_type)) {
195 2 : return false;
196 : }
197 : }
198 :
199 : // delegate without target for instance method or closure
200 7737 : if (first && !has_target && (m.binding == MemberBinding.INSTANCE || m.closure) && (parameters.size == 0 || m.closure)) {
201 3 : return false;
202 : }
203 :
204 : /* method may not expect more arguments */
205 7734 : if (method_params_it.next ()) {
206 0 : return false;
207 : }
208 :
209 7734 : var method_error_types = new ArrayList<DataType> ();
210 7734 : m.get_error_types (method_error_types);
211 :
212 : // method must throw error if the delegate does
213 7734 : if (error_types != null && error_types.size > 0 && method_error_types.size == 0) {
214 1 : return false;
215 : }
216 :
217 : // method may throw less but not more errors than the delegate
218 8125 : foreach (DataType method_error_type in method_error_types) {
219 197 : bool match = false;
220 197 : if (error_types != null) {
221 199 : foreach (DataType delegate_error_type in error_types) {
222 197 : if (method_error_type.compatible (delegate_error_type)) {
223 196 : match = true;
224 196 : break;
225 : }
226 : }
227 : }
228 :
229 196 : if (!match) {
230 1 : return false;
231 : }
232 : }
233 :
234 7732 : return true;
235 : }
236 :
237 430342 : public override void accept (CodeVisitor visitor) {
238 430342 : visitor.visit_delegate (this);
239 : }
240 :
241 297579 : public override void accept_children (CodeVisitor visitor) {
242 430515 : foreach (TypeParameter p in type_parameters) {
243 66468 : p.accept (visitor);
244 : }
245 :
246 297579 : return_type.accept (visitor);
247 :
248 1563891 : foreach (Parameter param in parameters) {
249 633156 : param.accept (visitor);
250 : }
251 :
252 297579 : if (error_types != null) {
253 58341 : foreach (DataType error_type in error_types) {
254 19447 : error_type.accept (visitor);
255 : }
256 : }
257 : }
258 :
259 361 : public override bool is_reference_type () {
260 361 : return false;
261 : }
262 :
263 : /**
264 : * Adds an error type to the exceptions that can be
265 : * thrown by this delegate.
266 : */
267 12495 : public void add_error_type (DataType error_type) {
268 12495 : if (error_types == null) {
269 12495 : error_types = new ArrayList<DataType> ();
270 : }
271 12495 : error_types.add (error_type);
272 12495 : error_type.parent_node = this;
273 : }
274 :
275 45446 : public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
276 45446 : if (error_types != null) {
277 804 : foreach (var error_type in error_types) {
278 370 : if (source_reference != null) {
279 102 : var type = error_type.copy ();
280 102 : type.source_reference = source_reference;
281 102 : collection.add (type);
282 : } else {
283 166 : collection.add (error_type);
284 : }
285 : }
286 : }
287 : }
288 :
289 89247 : public override void replace_type (DataType old_type, DataType new_type) {
290 89247 : if (return_type == old_type) {
291 77621 : return_type = new_type;
292 77621 : return;
293 : }
294 11626 : if (error_types != null) {
295 11626 : for (int i = 0; i < error_types.size; i++) {
296 11626 : if (error_types[i] == old_type) {
297 11626 : error_types[i] = new_type;
298 11626 : return;
299 : }
300 : }
301 : }
302 : }
303 :
304 1090044 : public override bool check (CodeContext context) {
305 1090044 : if (checked) {
306 912862 : return !error;
307 : }
308 :
309 177182 : checked = true;
310 :
311 177182 : var old_source_file = context.analyzer.current_source_file;
312 :
313 177182 : if (source_reference != null) {
314 177182 : context.analyzer.current_source_file = source_reference.file;
315 : }
316 :
317 256530 : foreach (TypeParameter p in get_type_parameters ()) {
318 39674 : if (!p.check (context)) {
319 0 : error = true;
320 : }
321 : }
322 :
323 177182 : return_type.check (context);
324 177182 : if (!external_package) {
325 262 : context.analyzer.check_type (return_type);
326 262 : return_type.check_type_arguments (context, !(return_type is DelegateType));
327 : }
328 :
329 177182 : if (return_type.type_symbol == context.analyzer.va_list_type.type_symbol) {
330 1 : error = true;
331 1 : Report.error (source_reference, "`%s' not supported as return type", return_type.type_symbol.get_full_name ());
332 1 : return false;
333 : }
334 :
335 930381 : foreach (Parameter param in parameters) {
336 376600 : if (!param.check (context)) {
337 0 : error = true;
338 : }
339 : }
340 :
341 177181 : if (error_types != null) {
342 34738 : foreach (DataType error_type in error_types) {
343 11580 : if (!(error_type is ErrorType)) {
344 1 : error = true;
345 1 : Report.error (error_type.source_reference, "`%s' is not an error type", error_type.to_string ());
346 : }
347 11580 : error_type.check (context);
348 :
349 : // check whether error type is at least as accessible as the delegate
350 11580 : if (!error_type.is_accessible (this)) {
351 1 : error = true;
352 1 : Report.error (source_reference, "error type `%s' is less accessible than delegate `%s'", error_type.to_string (), get_full_name ());
353 1 : return false;
354 : }
355 : }
356 : }
357 :
358 177180 : context.analyzer.current_source_file = old_source_file;
359 :
360 177180 : return !error;
361 : }
362 : }
|