Fix crash when using out parameters in delegates, fixes bug 563705
[vala-lang.git] / vala / valadelegate.vala
blob329c25888a101f624f75ab5705402f885172a647
1 /* valadelegate.vala
3 * Copyright (C) 2006-2008 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 a function callback type.
29 public class Vala.Delegate : TypeSymbol {
30 /**
31 * The return type of this callback.
33 public DataType return_type {
34 get { return _return_type; }
35 set {
36 _return_type = value;
37 _return_type.parent_node = this;
41 /**
42 * Specifies whether callback supports calling instance methods.
43 * The reference to the object instance will be appended to the end of
44 * the argument list in the generated C code.
46 public bool has_target { get; set; }
48 /**
49 * Specifies the position of the instance parameter in the C function.
51 public double cinstance_parameter_position { get; set; }
53 /**
54 * Specifies the position of the array length out parameter in the C
55 * function.
57 public double carray_length_parameter_position { get; set; }
59 /**
60 * Specifies the position of the delegate target out parameter in the C
61 * function.
63 public double cdelegate_target_parameter_position { get; set; }
65 /**
66 * Specifies whether the array length should implicitly be passed
67 * if the parameter type is an array.
69 public bool no_array_length {
70 get {
71 return _no_array_length;
73 set {
74 _no_array_length = value;
75 foreach (FormalParameter param in parameters) {
76 param.no_array_length = value;
81 private Gee.List<TypeParameter> type_parameters = new ArrayList<TypeParameter> ();
83 private Gee.List<FormalParameter> parameters = new ArrayList<FormalParameter> ();
84 private string cname;
86 private DataType _return_type;
87 private bool _no_array_length;
89 /**
90 * Creates a new delegate.
92 * @param name delegate type name
93 * @param return_type return type
94 * @param source reference to source code
95 * @return newly created delegate
97 public Delegate (string? name, DataType return_type, SourceReference? source_reference = null) {
98 base (name, source_reference);
99 this.return_type = return_type;
101 // error is -1 (right of user_data)
102 cinstance_parameter_position = -2;
103 carray_length_parameter_position = -3;
104 cdelegate_target_parameter_position = -3;
108 * Appends the specified parameter to the list of type parameters.
110 * @param p a type parameter
112 public void add_type_parameter (TypeParameter p) {
113 type_parameters.add (p);
114 p.type = this;
115 scope.add (p.name, p);
119 * Appends paramater to this callback function.
121 * @param param a formal parameter
123 public void add_parameter (FormalParameter param) {
124 if (no_array_length) {
125 param.no_array_length = true;
127 // default C parameter position
128 param.cparameter_position = parameters.size + 1;
129 param.carray_length_parameter_position = param.cparameter_position + 0.1;
130 param.cdelegate_target_parameter_position = param.cparameter_position + 0.1;
132 parameters.add (param);
133 scope.add (param.name, param);
137 * Return copy of parameter list.
139 * @return parameter list
141 public Gee.List<FormalParameter> get_parameters () {
142 return new ReadOnlyList<FormalParameter> (parameters);
146 * Checks whether the arguments and return type of the specified method
147 * matches this callback.
149 * @param m a method
150 * @return true if the specified method is compatible to this callback
152 public bool matches_method (Method m) {
153 // method is allowed to ensure stricter return type (stronger postcondition)
154 if (!m.return_type.stricter (return_type)) {
155 return false;
158 var method_params = m.get_parameters ();
159 Iterator<FormalParameter> method_params_it = method_params.iterator ();
160 bool first = true;
161 foreach (FormalParameter param in parameters) {
162 /* use first callback parameter as instance parameter if
163 * an instance method is being compared to a static
164 * callback
166 if (first && m.binding == MemberBinding.INSTANCE && !has_target) {
167 first = false;
168 continue;
171 /* method is allowed to accept less arguments */
172 if (!method_params_it.next ()) {
173 break;
176 // method is allowed to accept arguments of looser types (weaker precondition)
177 var method_param = method_params_it.get ();
178 if (!param.parameter_type.stricter (method_param.parameter_type)) {
179 return false;
183 /* method may not expect more arguments */
184 if (method_params_it.next ()) {
185 return false;
188 return true;
191 public override void accept (CodeVisitor visitor) {
192 visitor.visit_delegate (this);
195 public override void accept_children (CodeVisitor visitor) {
196 foreach (TypeParameter p in type_parameters) {
197 p.accept (visitor);
200 return_type.accept (visitor);
202 foreach (FormalParameter param in parameters) {
203 param.accept (visitor);
207 public override string get_cname (bool const_type = false) {
208 if (cname == null) {
209 cname = "%s%s".printf (parent_symbol.get_cprefix (), name);
211 return cname;
215 * Sets the name of this callback as it is used in C code.
217 * @param cname the name to be used in C code
219 public void set_cname (string cname) {
220 this.cname = cname;
223 private void process_ccode_attribute (Attribute a) {
224 if (a.has_argument ("cname")) {
225 set_cname (a.get_string ("cname"));
227 if (a.has_argument ("instance_pos")) {
228 cinstance_parameter_position = a.get_double ("instance_pos");
230 if (a.has_argument ("array_length_pos")) {
231 carray_length_parameter_position = a.get_double ("array_length_pos");
233 if (a.has_argument ("delegate_target_pos")) {
234 cdelegate_target_parameter_position = a.get_double ("delegate_target_pos");
236 if (a.has_argument ("cheader_filename")) {
237 var val = a.get_string ("cheader_filename");
238 foreach (string filename in val.split (",")) {
239 add_cheader_filename (filename);
245 * Process all associated attributes.
247 public void process_attributes () {
248 foreach (Attribute a in attributes) {
249 if (a.name == "CCode") {
250 process_ccode_attribute (a);
251 } else if (a.name == "NoArrayLength") {
252 no_array_length = true;
257 public override bool is_reference_type () {
258 return false;
261 public override string? get_type_id () {
262 return "G_TYPE_POINTER";
265 public override string? get_marshaller_type_name () {
266 return "POINTER";
269 public override string? get_get_value_function () {
270 return "g_value_get_pointer";
273 public override string? get_set_value_function () {
274 return "g_value_set_pointer";
277 public override void replace_type (DataType old_type, DataType new_type) {
278 if (return_type == old_type) {
279 return_type = new_type;
280 return;
282 var error_types = get_error_types ();
283 for (int i = 0; i < error_types.size; i++) {
284 if (error_types[i] == old_type) {
285 error_types[i] = new_type;
286 return;
291 public string get_prototype_string (string name) {
292 return "%s %s %s".printf (get_return_type_string (), name, get_parameters_string ());
295 string get_return_type_string () {
296 string str = "";
297 if (!return_type.value_owned && return_type is ReferenceType) {
298 str = "weak ";
300 str += return_type.to_string ();
302 return str;
305 string get_parameters_string () {
306 string str = "(";
308 int i = 1;
309 foreach (FormalParameter param in parameters) {
310 if (i > 1) {
311 str += ", ";
314 if (param.direction != ParameterDirection.IN) {
315 if (param.direction == ParameterDirection.REF) {
316 str += "ref ";
317 } else if (param.direction == ParameterDirection.OUT) {
318 str += "out ";
320 if (!param.parameter_type.value_owned && param.parameter_type is ReferenceType) {
321 str += "weak ";
325 str += param.parameter_type.to_string ();
327 if (param.direction == ParameterDirection.IN && param.parameter_type.value_owned) {
328 str += "#";
331 i++;
334 str += ")";
336 return str;
339 public override bool check (SemanticAnalyzer analyzer) {
340 if (checked) {
341 return !error;
344 checked = true;
346 process_attributes ();
348 var old_source_file = analyzer.current_source_file;
350 if (source_reference != null) {
351 analyzer.current_source_file = source_reference.file;
354 foreach (TypeParameter p in type_parameters) {
355 p.check (analyzer);
358 return_type.check (analyzer);
360 foreach (FormalParameter param in parameters) {
361 param.check (analyzer);
364 foreach (DataType error_type in get_error_types ()) {
365 error_type.check (analyzer);
368 analyzer.current_source_file = old_source_file;
370 return !error;