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
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
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))) {
35 // internally generated delegates don't require a typedef
36 if (d.sender_type != null) {
40 var creturn_type = get_callable_creturn_type (d);
41 if (creturn_type is DelegateType && ((DelegateType) creturn_type).delegate_symbol == d) {
43 creturn_type = new DelegateType ((Delegate) context.root.scope.lookup ("GLib").scope.lookup ("Callback"));
46 generate_type_declaration (creturn_type, decl_space);
48 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
50 var cfundecl = new CCodeFunctionDeclarator (get_ccode_name (d));
51 foreach (Parameter param in d.get_parameters ()) {
52 generate_parameter (param, decl_space, cparam_map, null);
55 // FIXME partial code duplication with CCodeMethodModule.generate_cparameters
57 if (d.return_type.is_real_non_null_struct_type ()) {
58 // structs are returned via out parameter
59 var cparam = new CCodeParameter ("result", get_ccode_name (d.return_type) + "*");
60 cparam_map.set (get_param_pos (-3), cparam);
61 } else if (get_ccode_array_length (d) && d.return_type is ArrayType) {
62 // return array length if appropriate
63 var array_type = (ArrayType) d.return_type;
64 var length_ctype = get_ccode_array_length_type (d) + "*";
66 for (int dim = 1; dim <= array_type.rank; dim++) {
67 var cparam = new CCodeParameter (get_array_length_cname ("result", dim), length_ctype);
68 cparam_map.set (get_param_pos (get_ccode_array_length_pos (d) + 0.01 * dim), cparam);
70 } else if (get_ccode_delegate_target (d) && d.return_type is DelegateType) {
71 // return delegate target if appropriate
72 var deleg_type = (DelegateType) d.return_type;
73 if (deleg_type.delegate_symbol.has_target) {
74 generate_type_declaration (delegate_target_type, decl_space);
75 var cparam = new CCodeParameter (get_delegate_target_cname ("result"), get_ccode_name (delegate_target_type) + "*");
76 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (d)), cparam);
77 if (deleg_type.is_disposable ()) {
78 generate_type_declaration (delegate_target_destroy_type, decl_space);
79 cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), get_ccode_name (delegate_target_destroy_type) + "*");
80 cparam_map.set (get_param_pos (get_ccode_destroy_notify_pos (d)), cparam);
86 generate_type_declaration (delegate_target_type, decl_space);
87 var cparam = new CCodeParameter ("user_data", get_ccode_name (delegate_target_type));
88 cparam_map.set (get_param_pos (get_ccode_instance_pos (d)), cparam);
90 if (d.tree_can_fail) {
91 generate_type_declaration (gerror_type, decl_space);
92 var cparam = new CCodeParameter ("error", "GError**");
93 cparam_map.set (get_param_pos (get_ccode_error_pos (d)), cparam);
96 // append C parameters in the right order
101 foreach (int pos in cparam_map.get_keys ()) {
102 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
109 cfundecl.add_parameter (cparam_map.get (min_pos));
113 var ctypedef = new CCodeTypeDefinition (get_ccode_name (creturn_type), cfundecl);
115 if (d.version.deprecated) {
116 if (context.profile == Profile.GOBJECT) {
117 decl_space.add_include ("glib.h");
119 ctypedef.modifiers |= CCodeModifiers.DEPRECATED;
122 decl_space.add_type_declaration (ctypedef);
125 public override void visit_delegate (Delegate d) {
126 generate_delegate_declaration (d, cfile);
128 if (!d.is_internal_symbol ()) {
129 generate_delegate_declaration (d, header_file);
131 if (!d.is_private_symbol ()) {
132 generate_delegate_declaration (d, internal_header_file);
135 d.accept_children (this);
138 public override string get_delegate_target_cname (string delegate_cname) {
139 return "%s_target".printf (delegate_cname);
142 public override CCodeExpression get_delegate_target_cexpression (Expression delegate_expr, out CCodeExpression delegate_target_destroy_notify) {
143 delegate_target_destroy_notify = get_delegate_target_destroy_notify_cvalue (delegate_expr.target_value);
144 return get_delegate_target_cvalue (delegate_expr.target_value);
147 public override CCodeExpression get_delegate_target_cvalue (TargetValue value) {
148 return ((GLibValue) value).delegate_target_cvalue;
151 public override CCodeExpression get_delegate_target_destroy_notify_cvalue (TargetValue value) {
152 return ((GLibValue) value).delegate_target_destroy_notify_cvalue;
155 public override string get_delegate_target_destroy_notify_cname (string delegate_cname) {
156 return "%s_target_destroy_notify".printf (delegate_cname);
159 public override CCodeExpression get_implicit_cast_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, CodeNode? node) {
160 if (target_type is DelegateType && expression_type is MethodType) {
161 var dt = (DelegateType) target_type;
162 var mt = (MethodType) expression_type;
164 var method = mt.method_symbol;
165 if (method.base_method != null) {
166 method = method.base_method;
167 } else if (method.base_interface_method != null) {
168 method = method.base_interface_method;
171 if (method.is_variadic ()) {
172 Report.warning (node.source_reference, "internal: Variadic method requires a direct cast to delegate");
174 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);
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
202 var creturn_type = get_callable_creturn_type (d);
204 var function = new CCodeFunction (wrapper_name, get_ccode_name (creturn_type));
205 function.modifiers = CCodeModifiers.STATIC;
207 push_function (function);
209 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
212 var cparam = new CCodeParameter ("self", get_ccode_name (delegate_target_type));
213 cparam_map.set (get_param_pos (get_ccode_instance_pos (d)), cparam);
216 if (d.sender_type != null) {
217 var param = new Parameter ("_sender", d.sender_type);
218 generate_parameter (param, cfile, cparam_map, null);
221 var d_params = d.get_parameters ();
222 foreach (Parameter param in d_params) {
223 if (dynamic_sig != null
224 && param.variable_type is ArrayType
225 && ((ArrayType) param.variable_type).element_type.type_symbol == string_type.type_symbol) {
226 // use null-terminated string arrays for dynamic signals for compatibility reasons
227 param.set_attribute_bool ("CCode", "array_length", false);
228 param.set_attribute_bool ("CCode", "array_null_terminated", true);
231 generate_parameter (param, cfile, cparam_map, null);
233 if (get_ccode_array_length (d) && d.return_type is ArrayType) {
234 // return array length if appropriate
235 var array_type = (ArrayType) d.return_type;
236 var length_ctype = get_ccode_array_length_type (d) + "*";
238 for (int dim = 1; dim <= array_type.rank; dim++) {
239 var cparam = new CCodeParameter (get_array_length_cname ("result", dim), length_ctype);
240 cparam_map.set (get_param_pos (get_ccode_array_length_pos (d) + 0.01 * dim), cparam);
242 } else if (d.return_type is DelegateType) {
243 // return delegate target if appropriate
244 var deleg_type = (DelegateType) d.return_type;
246 if (get_ccode_delegate_target (d) && deleg_type.delegate_symbol.has_target) {
247 var cparam = new CCodeParameter (get_delegate_target_cname ("result"), get_ccode_name (delegate_target_type) + "*");
248 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (d)), cparam);
249 if (deleg_type.is_disposable ()) {
250 cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), get_ccode_name (delegate_target_destroy_type) + "*");
251 cparam_map.set (get_param_pos (get_ccode_destroy_notify_pos (d)), cparam);
254 } else if (d.return_type.is_real_non_null_struct_type ()) {
255 var cparam = new CCodeParameter ("result", "%s*".printf (get_ccode_name (d.return_type)));
256 cparam_map.set (get_param_pos (-3), cparam);
259 if (m.tree_can_fail) {
260 var cparam = new CCodeParameter ("error", "GError**");
261 cparam_map.set (get_param_pos (get_ccode_error_pos (d)), cparam);
264 // append C parameters in the right order
269 foreach (int pos in cparam_map.get_keys ()) {
270 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
277 function.add_parameter (cparam_map.get (min_pos));
284 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
287 if (m.binding == MemberBinding.INSTANCE || m.closure) {
290 arg = new CCodeIdentifier ("self");
291 if (!m.closure && m.this_parameter != null) {
292 arg = convert_from_generic_pointer (arg, m.this_parameter.variable_type);
295 // use first delegate parameter as instance
296 if (d_params.size == 0 || m.closure) {
297 Report.error (node != null ? node.source_reference : null, "internal: Cannot create delegate wrapper");
298 arg = new CCodeInvalidExpression ();
300 arg = new CCodeIdentifier (get_ccode_name (d_params.get (0)));
304 carg_map.set (get_param_pos (get_ccode_instance_pos (m)), arg);
309 foreach (Parameter param in m.get_parameters ()) {
310 if (first && d.sender_type != null && m.get_parameters ().size == d.get_parameters ().size + 1) {
312 carg_map.set (get_param_pos (get_ccode_pos (param)), new CCodeIdentifier ("_sender"));
319 arg = new CCodeIdentifier (get_ccode_name (d_params.get (i)));
320 if (d_params.get (i).variable_type is GenericType) {
321 arg = convert_from_generic_pointer (arg, param.variable_type);
323 carg_map.set (get_param_pos (get_ccode_pos (param)), arg);
325 // handle array arguments
326 if (get_ccode_array_length (param) && param.variable_type is ArrayType) {
327 var array_type = (ArrayType) param.variable_type;
328 for (int dim = 1; dim <= array_type.rank; dim++) {
329 CCodeExpression clength;
330 if (get_ccode_array_null_terminated (d_params.get (i))) {
331 requires_array_length = true;
332 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
333 len_call.add_argument (new CCodeIdentifier (d_params.get (i).name));
335 } else if (!get_ccode_array_length (d_params.get (i))) {
336 clength = new CCodeConstant ("-1");
338 clength = new CCodeIdentifier (get_variable_array_length_cname (d_params.get (i), dim));
340 carg_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), clength);
342 } else if (get_ccode_delegate_target (param) && param.variable_type is DelegateType) {
343 var deleg_type = (DelegateType) param.variable_type;
345 if (deleg_type.delegate_symbol.has_target) {
346 var ctarget = new CCodeIdentifier (get_ccode_delegate_target_name (d_params.get (i)));
347 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), ctarget);
348 if (deleg_type.is_disposable ()) {
349 var ctarget_destroy_notify = new CCodeIdentifier (get_ccode_delegate_target_destroy_notify_name (d_params.get (i)));
350 carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (m)), ctarget_destroy_notify);
357 if (get_ccode_array_length (m) && m.return_type is ArrayType) {
358 var array_type = (ArrayType) m.return_type;
359 for (int dim = 1; dim <= array_type.rank; dim++) {
360 CCodeExpression clength;
361 if (!get_ccode_array_length (d)) {
362 clength = new CCodeConstant ("NULL");
364 clength = new CCodeIdentifier (get_array_length_cname ("result", dim));
366 carg_map.set (get_param_pos (get_ccode_array_length_pos (m) + 0.01 * dim), clength);
368 } else if (get_ccode_delegate_target (m) && m.return_type is DelegateType) {
369 var deleg_type = (DelegateType) m.return_type;
371 if (deleg_type.delegate_symbol.has_target) {
372 var ctarget = new CCodeIdentifier (get_delegate_target_cname ("result"));
373 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (m)), ctarget);
374 if (deleg_type.is_disposable ()) {
375 var ctarget_destroy_notify = new CCodeIdentifier (get_delegate_target_destroy_notify_cname ("result"));
376 carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (m)), ctarget_destroy_notify);
379 } else if (m.return_type.is_real_non_null_struct_type ()) {
380 carg_map.set (get_param_pos (-3), new CCodeIdentifier ("result"));
383 if (m.tree_can_fail) {
384 carg_map.set (get_param_pos (get_ccode_error_pos (m)), new CCodeIdentifier ("error"));
387 var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (m)));
389 // append C arguments in the right order
393 foreach (int pos in carg_map.get_keys ()) {
394 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
401 ccall.add_argument (carg_map.get (min_pos));
406 ccall.add_argument (new CCodeConstant ("NULL"));
407 ccall.add_argument (new CCodeConstant ("NULL"));
410 if (m.return_type is VoidType || m.return_type.is_real_non_null_struct_type ()) {
411 ccode.add_expression (ccall);
412 if (!(d.return_type is VoidType || d.return_type.is_real_non_null_struct_type ())) {
413 // return a default value
414 ccode.add_declaration (get_ccode_name (creturn_type), new CCodeVariableDeclarator ("result", default_value_for_type (d.return_type, true)));
417 CCodeExpression result = ccall;
418 if (d.return_type is GenericType) {
419 result = convert_to_generic_pointer (result, m.return_type);
421 ccode.add_declaration (get_ccode_name (creturn_type), new CCodeVariableDeclarator ("result", result));
424 if (d.has_target /* TODO: && dt.value_owned */ && dt.is_called_once) {
425 // destroy notify "self" after the call
426 CCodeExpression? destroy_notify = null;
428 int block_id = get_block_id (current_closure_block);
429 destroy_notify = new CCodeIdentifier ("block%d_data_unref".printf (block_id));
430 } else if (get_this_type () != null && m.binding != MemberBinding.STATIC && !m.is_async_callback && is_reference_counting (m.this_parameter.variable_type.type_symbol)) {
431 destroy_notify = get_destroy_func_expression (m.this_parameter.variable_type);
434 if (destroy_notify != null) {
435 var unref_call = new CCodeFunctionCall (destroy_notify);
436 unref_call.add_argument (new CCodeIdentifier ("self"));
437 ccode.add_expression (unref_call);
441 if (!(m.return_type is VoidType || m.return_type.is_real_non_null_struct_type ()) ||
442 !(d.return_type is VoidType || d.return_type.is_real_non_null_struct_type ())) {
443 ccode.add_return (new CCodeIdentifier ("result"));
449 cfile.add_function_declaration (function);
450 cfile.add_function (function);
455 public override CCodeParameter generate_parameter (Parameter param, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
456 if (!(param.variable_type is DelegateType || param.variable_type is MethodType)) {
457 return base.generate_parameter (param, decl_space, cparam_map, carg_map);
460 var param_type = param.variable_type;
461 if (param_type is DelegateType && ((DelegateType) param_type).delegate_symbol == param.parent_symbol) {
462 // recursive delegate
463 param_type = new DelegateType ((Delegate) context.root.scope.lookup ("GLib").scope.lookup ("Callback"));
466 generate_type_declaration (param_type, decl_space);
468 string ctypename = get_ccode_name (param_type);
469 string target_ctypename = get_ccode_name (delegate_target_type);
470 string target_destroy_notify_ctypename = get_ccode_name (delegate_target_destroy_type);
472 if (param.direction != ParameterDirection.IN) {
474 target_ctypename += "*";
475 target_destroy_notify_ctypename += "*";
478 var main_cparam = new CCodeParameter (get_ccode_name (param), ctypename);
480 cparam_map.set (get_param_pos (get_ccode_pos (param)), main_cparam);
481 if (carg_map != null) {
482 carg_map.set (get_param_pos (get_ccode_pos (param)), get_parameter_cexpression (param));
485 if (param_type is DelegateType) {
486 unowned DelegateType deleg_type = (DelegateType) param_type;
487 if (get_ccode_delegate_target (param) && deleg_type.delegate_symbol.has_target) {
488 var cparam = new CCodeParameter (get_ccode_delegate_target_name (param), target_ctypename);
489 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), cparam);
490 if (carg_map != null) {
491 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), get_cexpression (cparam.name));
493 if (deleg_type.is_disposable ()) {
494 cparam = new CCodeParameter (get_ccode_delegate_target_destroy_notify_name (param), target_destroy_notify_ctypename);
495 cparam_map.set (get_param_pos (get_ccode_destroy_notify_pos (param)), cparam);
496 if (carg_map != null) {
497 carg_map.set (get_param_pos (get_ccode_destroy_notify_pos (param)), get_cexpression (cparam.name));
501 } else if (param_type is MethodType) {
502 var cparam = new CCodeParameter (get_ccode_delegate_target_name (param), target_ctypename);
503 cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), cparam);
504 if (carg_map != null) {
505 carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), get_cexpression (cparam.name));