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 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
)) {
49 return_type_cname
= "GCallback";
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
);
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
);
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
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
);
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
276 foreach (int pos
in cparam_map
.get_keys ()) {
277 if (pos
> last_pos
&& (min_pos
== -1 || pos
< min_pos
)) {
284 function
.add_parameter (cparam_map
.get (min_pos
));
291 var carg_map
= new HashMap
<int,CCodeExpression
> (direct_hash
, direct_equal
);
294 if (m
.binding
== MemberBinding
.INSTANCE
|| m
.closure
) {
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
);
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");
307 arg
= new
CCodeIdentifier (get_variable_cname (d_params
.get (0).name
));
311 carg_map
.set (get_param_pos (get_ccode_instance_pos (m
)), arg
);
316 foreach (Parameter param
in m
.get_parameters ()) {
317 if (first
&& d
.sender_type
!= null && m
.get_parameters ().size
== d
.get_parameters ().size
+ 1) {
319 carg_map
.set (get_param_pos (get_ccode_pos (param
)), new
CCodeIdentifier ("_sender"));
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
));
342 } else if (!get_ccode_array_length (d_params
.get (i
))) {
343 clength
= new
CCodeConstant ("-1");
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
);
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");
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
400 foreach (int pos
in carg_map
.get_keys ()) {
401 if (pos
> last_pos
&& (min_pos
== -1 || pos
< min_pos
)) {
408 ccall
.add_argument (carg_map
.get (min_pos
));
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)));
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;
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"));
456 cfile
.add_function_declaration (function
);
457 cfile
.add_function (function
);
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
) {
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
));