Release 0.41.92
[vala-gnome.git] / codegen / valaccodedelegatemodule.vala
blob042e2be0538e52e826d7a475ca6f8e097ea3bda8
1 /* valaccodedelegatemodule.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>
26 /**
27 * The link between an assignment and generated code.
29 public class Vala.CCodeDelegateModule : CCodeArrayModule {
30 public override void generate_delegate_declaration (Delegate d, CCodeFile decl_space) {
31 if (add_symbol_declaration (decl_space, d, get_ccode_name (d))) {
32 return;
35 // internally generated delegates don't require a typedef
36 if (d.sender_type != null) {
37 return;
40 string return_type_cname = get_ccode_name (d.return_type);
42 if (d.return_type.is_real_non_null_struct_type ()) {
43 // structs are returned via out parameter
44 return_type_cname = "void";
47 if (return_type_cname == get_ccode_name (d)) {
48 // recursive delegate
49 return_type_cname = "GCallback";
50 } else {
51 generate_type_declaration (d.return_type, decl_space);
54 var cfundecl = new CCodeFunctionDeclarator (get_ccode_name (d));
55 foreach (Parameter param in d.get_parameters ()) {
56 var cparam = generate_parameter (param, decl_space, new HashMap<int,CCodeParameter> (), null);
58 cfundecl.add_parameter (cparam);
60 // handle array parameters
61 if (get_ccode_array_length (param) && param.variable_type is ArrayType) {
62 var array_type = (ArrayType) param.variable_type;
64 var length_ctype = "int";
65 if (param.direction != ParameterDirection.IN) {
66 length_ctype = "int*";
69 for (int dim = 1; dim <= array_type.rank; dim++) {
70 cparam = new CCodeParameter (get_parameter_array_length_cname (param, dim), length_ctype);
71 cfundecl.add_parameter (cparam);
74 // handle delegate parameters
75 if (param.variable_type is DelegateType) {
76 var deleg_type = (DelegateType) param.variable_type;
77 var param_d = deleg_type.delegate_symbol;
78 if (param_d.has_target) {
79 cparam = new CCodeParameter (get_delegate_target_cname (get_variable_cname (param.name)), "gpointer");
80 cfundecl.add_parameter (cparam);
81 if (deleg_type.is_disposable ()) {
82 cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)), "GDestroyNotify");
83 cfundecl.add_parameter (cparam);
88 if (get_ccode_array_length (d) && d.return_type is ArrayType) {
89 // return array length if appropriate
90 var array_type = (ArrayType) d.return_type;
91 var array_length_type = get_ccode_array_length_type (d) != null ? get_ccode_array_length_type (d) : "int";
92 array_length_type += "*";
94 for (int dim = 1; dim <= array_type.rank; dim++) {
95 var cparam = new CCodeParameter (get_array_length_cname ("result", dim), array_length_type);
96 cfundecl.add_parameter (cparam);
98 } else if (d.return_type is DelegateType) {
99 // return delegate target if appropriate
100 var deleg_type = (DelegateType) d.return_type;
101 var result_d = deleg_type.delegate_symbol;
102 if (result_d.has_target) {
103 var cparam = new CCodeParameter (get_delegate_target_cname ("result"), "gpointer*");
104 cfundecl.add_parameter (cparam);
105 if (deleg_type.is_disposable ()) {
106 cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), "GDestroyNotify*");
107 cfundecl.add_parameter (cparam);
110 } else if (d.return_type.is_real_non_null_struct_type ()) {
111 var cparam = new CCodeParameter ("result", "%s*".printf (get_ccode_name (d.return_type)));
112 cfundecl.add_parameter (cparam);
114 if (d.has_target) {
115 var cparam = new CCodeParameter ("user_data", "gpointer");
116 cfundecl.add_parameter (cparam);
118 if (d.get_error_types ().size > 0) {
119 var cparam = new CCodeParameter ("error", "GError**");
120 cfundecl.add_parameter (cparam);
123 var ctypedef = new CCodeTypeDefinition (return_type_cname, cfundecl);
124 ctypedef.modifiers |= (d.version.deprecated ? CCodeModifiers.DEPRECATED : 0);
126 decl_space.add_type_definition (ctypedef);
129 public override void visit_delegate (Delegate d) {
130 d.accept_children (this);
132 generate_delegate_declaration (d, cfile);
134 if (!d.is_internal_symbol ()) {
135 generate_delegate_declaration (d, header_file);
137 if (!d.is_private_symbol ()) {
138 generate_delegate_declaration (d, internal_header_file);
142 public override string get_delegate_target_cname (string delegate_cname) {
143 return "%s_target".printf (delegate_cname);
146 public override CCodeExpression get_delegate_target_cexpression (Expression delegate_expr, out CCodeExpression delegate_target_destroy_notify) {
147 delegate_target_destroy_notify = get_delegate_target_destroy_notify_cvalue (delegate_expr.target_value);
148 return get_delegate_target_cvalue (delegate_expr.target_value);
151 public override CCodeExpression get_delegate_target_cvalue (TargetValue value) {
152 return ((GLibValue) value).delegate_target_cvalue;
155 public override CCodeExpression get_delegate_target_destroy_notify_cvalue (TargetValue value) {
156 return ((GLibValue) value).delegate_target_destroy_notify_cvalue;
159 public override string get_delegate_target_destroy_notify_cname (string delegate_cname) {
160 return "%s_target_destroy_notify".printf (delegate_cname);
163 public override CCodeExpression get_implicit_cast_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, CodeNode? node) {
164 if (target_type is DelegateType && expression_type is MethodType) {
165 var dt = (DelegateType) target_type;
166 var mt = (MethodType) expression_type;
168 var method = mt.method_symbol;
169 if (method.base_method != null) {
170 method = method.base_method;
171 } else if (method.base_interface_method != null) {
172 method = method.base_interface_method;
175 return new CCodeIdentifier (generate_delegate_wrapper (method, dt, node));
178 return base.get_implicit_cast_expression (source_cexpr, expression_type, target_type, node);
181 public string generate_delegate_wrapper (Method m, DelegateType dt, CodeNode? node) {
182 var d = dt.delegate_symbol;
183 string delegate_name;
184 var sig = d.parent_symbol as Signal;
185 var dynamic_sig = sig as DynamicSignal;
186 if (dynamic_sig != null) {
187 delegate_name = get_dynamic_signal_cname (dynamic_sig);
188 } else if (sig != null) {
189 delegate_name = get_ccode_lower_case_prefix (sig.parent_symbol) + get_ccode_lower_case_name (sig);
190 } else {
191 delegate_name = Symbol.camel_case_to_lower_case (get_ccode_name (d));
194 string wrapper_name = "_%s_%s".printf (get_ccode_name (m), delegate_name);
196 if (!add_wrapper (wrapper_name)) {
197 // wrapper already defined
198 return wrapper_name;
201 // declaration
203 string return_type_cname = get_ccode_name (d.return_type);
205 if (d.return_type.is_real_non_null_struct_type ()) {
206 // structs are returned via out parameter
207 return_type_cname = "void";
210 var function = new CCodeFunction (wrapper_name, return_type_cname);
211 function.modifiers = CCodeModifiers.STATIC;
213 push_function (function);
215 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
217 if (d.has_target) {
218 var cparam = new CCodeParameter ("self", "gpointer");
219 cparam_map.set (get_param_pos (get_ccode_instance_pos (d)), cparam);
222 if (d.sender_type != null) {
223 var param = new Parameter ("_sender", d.sender_type);
224 generate_parameter (param, cfile, cparam_map, null);
227 var d_params = d.get_parameters ();
228 foreach (Parameter param in d_params) {
229 if (dynamic_sig != null
230 && param.variable_type is ArrayType
231 && ((ArrayType) param.variable_type).element_type.data_type == string_type.data_type) {
232 // use null-terminated string arrays for dynamic signals for compatibility reasons
233 param.set_attribute_bool ("CCode", "array_length", false);
234 param.set_attribute_bool ("CCode", "array_null_terminated", true);
237 generate_parameter (param, cfile, cparam_map, null);
239 if (get_ccode_array_length (d) && d.return_type is ArrayType) {
240 // return array length if appropriate
241 var array_type = (ArrayType) d.return_type;
242 var array_length_type = get_ccode_array_length_type (d) != null ? get_ccode_array_length_type (d) : "int";
243 array_length_type += "*";
245 for (int dim = 1; dim <= array_type.rank; dim++) {
246 var cparam = new CCodeParameter (get_array_length_cname ("result", dim), array_length_type);
247 cparam_map.set (get_param_pos (get_ccode_array_length_pos (d) + 0.01 * dim), cparam);
249 } else if (d.return_type is DelegateType) {
250 // return delegate target if appropriate
251 var deleg_type = (DelegateType) d.return_type;
253 if (deleg_type.delegate_symbol.has_target) {
254 var cparam = new CCodeParameter (get_delegate_target_cname ("result"), "gpointer*");
255 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (d)), cparam);
256 if (deleg_type.is_disposable ()) {
257 cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), "GDestroyNotify*");
258 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (d) + 0.01), cparam);
261 } else if (d.return_type.is_real_non_null_struct_type ()) {
262 var cparam = new CCodeParameter ("result", "%s*".printf (get_ccode_name (d.return_type)));
263 cparam_map.set (get_param_pos (-3), cparam);
266 if (m.get_error_types ().size > 0) {
267 var cparam = new CCodeParameter ("error", "GError**");
268 cparam_map.set (get_param_pos (-1), cparam);
271 // append C parameters in the right order
272 int last_pos = -1;
273 int min_pos;
274 while (true) {
275 min_pos = -1;
276 foreach (int pos in cparam_map.get_keys ()) {
277 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
278 min_pos = pos;
281 if (min_pos == -1) {
282 break;
284 function.add_parameter (cparam_map.get (min_pos));
285 last_pos = min_pos;
289 // definition
291 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
293 int i = 0;
294 if (m.binding == MemberBinding.INSTANCE || m.closure) {
295 CCodeExpression arg;
296 if (d.has_target) {
297 arg = new CCodeIdentifier ("self");
298 if (!m.closure && m.this_parameter != null) {
299 arg = convert_from_generic_pointer (arg, m.this_parameter.variable_type);
301 } else {
302 // use first delegate parameter as instance
303 if (d_params.size == 0 || m.closure) {
304 Report.error (node != null ? node.source_reference : null, "Cannot create delegate without target for instance method or closure");
305 arg = new CCodeConstant ("NULL");
306 } else {
307 arg = new CCodeIdentifier (get_variable_cname (d_params.get (0).name));
308 i = 1;
311 carg_map.set (get_param_pos (get_ccode_instance_pos (m)), arg);
314 bool first = true;
316 foreach (Parameter param in m.get_parameters ()) {
317 if (first && d.sender_type != null && m.get_parameters ().size == d.get_parameters ().size + 1) {
318 // sender parameter
319 carg_map.set (get_param_pos (get_ccode_pos (param)), new CCodeIdentifier ("_sender"));
321 first = false;
322 continue;
325 CCodeExpression arg;
326 arg = new CCodeIdentifier (get_variable_cname (d_params.get (i).name));
327 if (d_params.get (i).variable_type is GenericType) {
328 arg = convert_from_generic_pointer (arg, param.variable_type);
330 carg_map.set (get_param_pos (get_ccode_pos (param)), arg);
332 // handle array arguments
333 if (get_ccode_array_length (param) && param.variable_type is ArrayType) {
334 var array_type = (ArrayType) param.variable_type;
335 for (int dim = 1; dim <= array_type.rank; dim++) {
336 CCodeExpression clength;
337 if (get_ccode_array_null_terminated (d_params.get (i))) {
338 requires_array_length = true;
339 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
340 len_call.add_argument (new CCodeIdentifier (d_params.get (i).name));
341 clength = len_call;
342 } else if (!get_ccode_array_length (d_params.get (i))) {
343 clength = new CCodeConstant ("-1");
344 } else {
345 clength = new CCodeIdentifier (get_parameter_array_length_cname (d_params.get (i), dim));
347 carg_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), clength);
349 } else if (param.variable_type is DelegateType) {
350 var deleg_type = (DelegateType) param.variable_type;
352 if (deleg_type.delegate_symbol.has_target) {
353 var ctarget = new CCodeIdentifier (get_ccode_delegate_target_name (d_params.get (i)));
354 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), ctarget);
355 if (deleg_type.is_disposable ()) {
356 var ctarget_destroy_notify = new CCodeIdentifier (get_delegate_target_destroy_notify_cname (d_params.get (i).name));
357 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (m) + 0.01), ctarget_destroy_notify);
362 i++;
364 if (get_ccode_array_length (m) && m.return_type is ArrayType) {
365 var array_type = (ArrayType) m.return_type;
366 for (int dim = 1; dim <= array_type.rank; dim++) {
367 CCodeExpression clength;
368 if (!get_ccode_array_length (d)) {
369 clength = new CCodeConstant ("NULL");
370 } else {
371 clength = new CCodeIdentifier (get_array_length_cname ("result", dim));
373 carg_map.set (get_param_pos (get_ccode_array_length_pos (m) + 0.01 * dim), clength);
375 } else if (m.return_type is DelegateType) {
376 var deleg_type = (DelegateType) m.return_type;
378 if (deleg_type.delegate_symbol.has_target) {
379 var ctarget = new CCodeIdentifier (get_delegate_target_cname ("result"));
380 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (m)), ctarget);
381 if (deleg_type.is_disposable ()) {
382 var ctarget_destroy_notify = new CCodeIdentifier (get_delegate_target_destroy_notify_cname ("result"));
383 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (m) + 0.01), ctarget_destroy_notify);
386 } else if (m.return_type.is_real_non_null_struct_type ()) {
387 carg_map.set (get_param_pos (-3), new CCodeIdentifier ("result"));
390 if (m.get_error_types ().size > 0) {
391 carg_map.set (get_param_pos (-1), new CCodeIdentifier ("error"));
394 var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (m)));
396 // append C arguments in the right order
397 last_pos = -1;
398 while (true) {
399 min_pos = -1;
400 foreach (int pos in carg_map.get_keys ()) {
401 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
402 min_pos = pos;
405 if (min_pos == -1) {
406 break;
408 ccall.add_argument (carg_map.get (min_pos));
409 last_pos = min_pos;
412 if (m.coroutine) {
413 ccall.add_argument (new CCodeConstant ("NULL"));
414 ccall.add_argument (new CCodeConstant ("NULL"));
417 if (m.return_type is VoidType || m.return_type.is_real_non_null_struct_type ()) {
418 ccode.add_expression (ccall);
419 if (!(d.return_type is VoidType || d.return_type.is_real_non_null_struct_type ())) {
420 // return a default value
421 ccode.add_declaration (return_type_cname, new CCodeVariableDeclarator ("result", default_value_for_type (d.return_type, true)));
423 } else {
424 CCodeExpression result = ccall;
425 if (d.return_type is GenericType) {
426 result = convert_to_generic_pointer (result, m.return_type);
428 ccode.add_declaration (return_type_cname, new CCodeVariableDeclarator ("result", result));
431 if (d.has_target /* TODO: && dt.value_owned */ && dt.is_called_once) {
432 // destroy notify "self" after the call
433 CCodeExpression? destroy_notify = null;
434 if (m.closure) {
435 int block_id = get_block_id (current_closure_block);
436 destroy_notify = new CCodeIdentifier ("block%d_data_unref".printf (block_id));
437 } else if (get_this_type () != null && m.binding != MemberBinding.STATIC && !m.is_async_callback && is_reference_counting (m.this_parameter.variable_type.data_type)) {
438 destroy_notify = get_destroy_func_expression (m.this_parameter.variable_type);
441 if (destroy_notify != null) {
442 var unref_call = new CCodeFunctionCall (destroy_notify);
443 unref_call.add_argument (new CCodeIdentifier ("self"));
444 ccode.add_expression (unref_call);
448 if (!(m.return_type is VoidType || m.return_type.is_real_non_null_struct_type ()) ||
449 !(d.return_type is VoidType || d.return_type.is_real_non_null_struct_type ())) {
450 ccode.add_return (new CCodeIdentifier ("result"));
453 pop_function ();
455 // append to file
456 cfile.add_function_declaration (function);
457 cfile.add_function (function);
459 return wrapper_name;
462 public override CCodeParameter generate_parameter (Parameter param, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
463 if (!(param.variable_type is DelegateType || param.variable_type is MethodType)) {
464 return base.generate_parameter (param, decl_space, cparam_map, carg_map);
467 string ctypename = get_ccode_name (param.variable_type);
468 string target_ctypename = "gpointer";
469 string target_destroy_notify_ctypename = "GDestroyNotify";
471 if (param.parent_symbol is Delegate
472 && get_ccode_name (param.variable_type) == get_ccode_name (param.parent_symbol)) {
473 // recursive delegate
474 ctypename = "GCallback";
477 if (param.direction != ParameterDirection.IN) {
478 ctypename += "*";
479 target_ctypename += "*";
480 target_destroy_notify_ctypename += "*";
483 var main_cparam = new CCodeParameter (get_variable_cname (param.name), ctypename);
485 cparam_map.set (get_param_pos (get_ccode_pos (param)), main_cparam);
486 if (carg_map != null) {
487 carg_map.set (get_param_pos (get_ccode_pos (param)), get_variable_cexpression (param.name));
490 if (param.variable_type is DelegateType) {
491 var deleg_type = (DelegateType) param.variable_type;
493 generate_delegate_declaration (deleg_type.delegate_symbol, decl_space);
495 if (deleg_type.delegate_symbol.has_target) {
496 var cparam = new CCodeParameter (get_ccode_delegate_target_name (param), target_ctypename);
497 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), cparam);
498 if (carg_map != null) {
499 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), get_variable_cexpression (cparam.name));
501 if (deleg_type.is_disposable ()) {
502 cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)), target_destroy_notify_ctypename);
503 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (param) + 0.01), cparam);
504 if (carg_map != null) {
505 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param) + 0.01), get_variable_cexpression (cparam.name));
509 } else if (param.variable_type is MethodType) {
510 var cparam = new CCodeParameter (get_ccode_delegate_target_name (param), target_ctypename);
511 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), cparam);
512 if (carg_map != null) {
513 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), get_variable_cexpression (cparam.name));
517 return main_cparam;