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
, d
.get_cname ())) {
35 string return_type_cname
= d
.return_type
.get_cname ();
37 if (d
.return_type
.is_real_non_null_struct_type ()) {
38 // structs are returned via out parameter
39 return_type_cname
= "void";
42 if (return_type_cname
== d
.get_cname ()) {
44 return_type_cname
= "GCallback";
46 generate_type_declaration (d
.return_type
, decl_space
);
49 var cfundecl
= new
CCodeFunctionDeclarator (d
.get_cname ());
50 foreach (Parameter param
in d
.get_parameters ()) {
51 var cparam
= generate_parameter (param
, decl_space
, new HashMap
<int,CCodeParameter
> (), null);
53 cfundecl
.add_parameter (cparam
);
55 // handle array parameters
56 if (!param
.no_array_length
&& param
.variable_type is ArrayType
) {
57 var array_type
= (ArrayType
) param
.variable_type
;
59 var length_ctype
= "int";
60 if (param
.direction
!= ParameterDirection
.IN
) {
61 length_ctype
= "int*";
64 for (int dim
= 1; dim
<= array_type
.rank
; dim
++) {
65 cparam
= new
CCodeParameter (get_parameter_array_length_cname (param
, dim
), length_ctype
);
66 cfundecl
.add_parameter (cparam
);
69 // handle delegate parameters
70 if (param
.variable_type is DelegateType
) {
71 var deleg_type
= (DelegateType
) param
.variable_type
;
72 var param_d
= deleg_type
.delegate_symbol
;
73 if (param_d
.has_target
) {
74 cparam
= new
CCodeParameter (get_delegate_target_cname (get_variable_cname (param
.name
)), "void*");
75 cfundecl
.add_parameter (cparam
);
76 if (deleg_type
.value_owned
) {
77 cparam
= new
CCodeParameter (get_delegate_target_destroy_notify_cname (get_variable_cname (param
.name
)), "GDestroyNotify*");
78 cfundecl
.add_parameter (cparam
);
83 if (!d
.no_array_length
&& d
.return_type is ArrayType
) {
84 // return array length if appropriate
85 var array_type
= (ArrayType
) d
.return_type
;
87 for (int dim
= 1; dim
<= array_type
.rank
; dim
++) {
88 var cparam
= new
CCodeParameter (get_array_length_cname ("result", dim
), "int*");
89 cfundecl
.add_parameter (cparam
);
91 } else if (d
.return_type is DelegateType
) {
92 // return delegate target if appropriate
93 var deleg_type
= (DelegateType
) d
.return_type
;
94 var result_d
= deleg_type
.delegate_symbol
;
95 if (result_d
.has_target
) {
96 var cparam
= new
CCodeParameter (get_delegate_target_cname ("result"), "void**");
97 cfundecl
.add_parameter (cparam
);
98 if (deleg_type
.value_owned
) {
99 cparam
= new
CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), "GDestroyNotify*");
100 cfundecl
.add_parameter (cparam
);
103 } else if (d
.return_type
.is_real_non_null_struct_type ()) {
104 var cparam
= new
CCodeParameter ("result", "%s*".printf (d
.return_type
.get_cname ()));
105 cfundecl
.add_parameter (cparam
);
108 var cparam
= new
CCodeParameter ("user_data", "void*");
109 cfundecl
.add_parameter (cparam
);
111 if (d
.get_error_types ().size
> 0) {
112 var cparam
= new
CCodeParameter ("error", "GError**");
113 cfundecl
.add_parameter (cparam
);
116 var ctypedef
= new
CCodeTypeDefinition (return_type_cname
, cfundecl
);
117 ctypedef
.deprecated
= d
.deprecated
;
119 decl_space
.add_type_definition (ctypedef
);
122 public override void visit_delegate (Delegate d
) {
123 d
.accept_children (this
);
125 generate_delegate_declaration (d
, cfile
);
127 if (!d
.is_internal_symbol ()) {
128 generate_delegate_declaration (d
, header_file
);
130 if (!d
.is_private_symbol ()) {
131 generate_delegate_declaration (d
, internal_header_file
);
135 public override string get_delegate_target_cname (string delegate_cname
) {
136 return "%s_target".printf (delegate_cname
);
139 public override CCodeExpression
get_delegate_target_cexpression (Expression delegate_expr
, out CCodeExpression delegate_target_destroy_notify
) {
140 delegate_target_destroy_notify
= get_delegate_target_destroy_notify_cvalue (delegate_expr
.target_value
);
141 return get_delegate_target_cvalue (delegate_expr
.target_value
);
144 public override CCodeExpression
get_delegate_target_cvalue (TargetValue value
) {
145 return ((GLibValue
) value
).delegate_target_cvalue
;
148 public override CCodeExpression
get_delegate_target_destroy_notify_cvalue (TargetValue value
) {
149 return ((GLibValue
) value
).delegate_target_destroy_notify_cvalue
;
152 public override string get_delegate_target_destroy_notify_cname (string delegate_cname
) {
153 return "%s_target_destroy_notify".printf (delegate_cname
);
156 public override CCodeExpression
get_implicit_cast_expression (CCodeExpression source_cexpr
, DataType? expression_type
, DataType? target_type
, Expression? expr
= null) {
157 if (target_type is DelegateType
&& expression_type is MethodType
) {
158 var dt
= (DelegateType
) target_type
;
159 var mt
= (MethodType
) expression_type
;
161 var method
= mt
.method_symbol
;
162 if (method
.base_method
!= null) {
163 method
= method
.base_method
;
164 } else if (method
.base_interface_method
!= null) {
165 method
= method
.base_interface_method
;
168 return new
CCodeIdentifier (generate_delegate_wrapper (method
, dt
, expr
));
171 return base.get_implicit_cast_expression (source_cexpr
, expression_type
, target_type
, expr
);
174 private string generate_delegate_wrapper (Method m
, DelegateType dt
, Expression? expr
) {
175 var d
= dt
.delegate_symbol
;
176 string delegate_name
;
177 var sig
= d
.parent_symbol as Signal
;
178 var dynamic_sig
= sig as DynamicSignal
;
179 if (dynamic_sig
!= null) {
180 delegate_name
= get_dynamic_signal_cname (dynamic_sig
);
181 } else if (sig
!= null) {
182 delegate_name
= sig
.parent_symbol
.get_lower_case_cprefix () + sig
.get_cname ();
184 delegate_name
= Symbol
.camel_case_to_lower_case (d
.get_cname ());
187 string wrapper_name
= "_%s_%s".printf (m
.get_cname (), delegate_name
);
189 if (!add_wrapper (wrapper_name
)) {
190 // wrapper already defined
196 string return_type_cname
= d
.return_type
.get_cname ();
198 if (d
.return_type
.is_real_non_null_struct_type ()) {
199 // structs are returned via out parameter
200 return_type_cname
= "void";
203 var function
= new
CCodeFunction (wrapper_name
, return_type_cname
);
204 function
.modifiers
= CCodeModifiers
.STATIC
;
206 var cparam_map
= new HashMap
<int,CCodeParameter
> (direct_hash
, direct_equal
);
209 var cparam
= new
CCodeParameter ("self", "gpointer");
210 cparam_map
.set (get_param_pos (d
.cinstance_parameter_position
), cparam
);
213 if (d
.sender_type
!= null) {
214 var param
= new
Parameter ("_sender", d
.sender_type
);
215 generate_parameter (param
, cfile
, cparam_map
, null);
218 var d_params
= d
.get_parameters ();
219 foreach (Parameter param
in d_params
) {
220 if (dynamic_sig
!= null
221 && param
.variable_type is ArrayType
222 && ((ArrayType
) param
.variable_type
).element_type
.data_type
== string_type
.data_type
) {
223 // use null-terminated string arrays for dynamic signals for compatibility reasons
224 param
.no_array_length
= true;
225 param
.array_null_terminated
= true;
228 generate_parameter (param
, cfile
, cparam_map
, null);
230 if (!d
.no_array_length
&& d
.return_type is ArrayType
) {
231 // return array length if appropriate
232 var array_type
= (ArrayType
) d
.return_type
;
234 for (int dim
= 1; dim
<= array_type
.rank
; dim
++) {
235 var cparam
= new
CCodeParameter (get_array_length_cname ("result", dim
), "int*");
236 cparam_map
.set (get_param_pos (d
.carray_length_parameter_position
+ 0.01 * dim
), cparam
);
238 } else if (d
.return_type is DelegateType
) {
239 // return delegate target if appropriate
240 var deleg_type
= (DelegateType
) d
.return_type
;
242 if (deleg_type
.delegate_symbol
.has_target
) {
243 var cparam
= new
CCodeParameter (get_delegate_target_cname ("result"), "void**");
244 cparam_map
.set (get_param_pos (d
.cdelegate_target_parameter_position
), cparam
);
245 if (deleg_type
.value_owned
) {
246 cparam
= new
CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), "GDestroyNotify*");
247 cparam_map
.set (get_param_pos (d
.cdelegate_target_parameter_position
+ 0.01), cparam
);
250 } else if (d
.return_type
.is_real_non_null_struct_type ()) {
251 var cparam
= new
CCodeParameter ("result", "%s*".printf (d
.return_type
.get_cname ()));
252 cparam_map
.set (get_param_pos (-3), cparam
);
255 if (m
.get_error_types ().size
> 0) {
256 var cparam
= new
CCodeParameter ("error", "GError**");
257 cparam_map
.set (get_param_pos (-1), cparam
);
260 // append C parameters in the right order
265 foreach (int pos
in cparam_map
.get_keys ()) {
266 if (pos
> last_pos
&& (min_pos
== -1 || pos
< min_pos
)) {
273 function
.add_parameter (cparam_map
.get (min_pos
));
280 var carg_map
= new HashMap
<int,CCodeExpression
> (direct_hash
, direct_equal
);
283 if (m
.binding
== MemberBinding
.INSTANCE
|| m
.closure
) {
286 arg
= new
CCodeIdentifier ("self");
288 // use first delegate parameter as instance
289 if (d_params
.size
== 0) {
290 Report
.error (expr
!= null ? expr
.source_reference
: null, "Cannot create delegate without target for instance method or closure");
291 arg
= new
CCodeConstant ("NULL");
293 arg
= new
CCodeIdentifier (get_variable_cname (d_params
.get (0).name
));
297 carg_map
.set (get_param_pos (m
.cinstance_parameter_position
), arg
);
302 foreach (Parameter param
in m
.get_parameters ()) {
303 if (first
&& d
.sender_type
!= null && m
.get_parameters ().size
== d
.get_parameters ().size
+ 1) {
305 carg_map
.set (get_param_pos (param
.cparameter_position
), new
CCodeIdentifier ("_sender"));
312 arg
= new
CCodeIdentifier (get_variable_cname (d_params
.get (i
).name
));
313 carg_map
.set (get_param_pos (param
.cparameter_position
), arg
);
315 // handle array arguments
316 if (!param
.no_array_length
&& param
.variable_type is ArrayType
) {
317 var array_type
= (ArrayType
) param
.variable_type
;
318 for (int dim
= 1; dim
<= array_type
.rank
; dim
++) {
319 CCodeExpression clength
;
320 if (d_params
.get (i
).array_null_terminated
) {
321 requires_array_length
= true;
322 var len_call
= new
CCodeFunctionCall (new
CCodeIdentifier ("_vala_array_length"));
323 len_call
.add_argument (new
CCodeIdentifier (d_params
.get (i
).name
));
325 } else if (d_params
.get (i
).no_array_length
) {
326 clength
= new
CCodeConstant ("-1");
328 clength
= new
CCodeIdentifier (get_parameter_array_length_cname (d_params
.get (i
), dim
));
330 carg_map
.set (get_param_pos (param
.carray_length_parameter_position
+ 0.01 * dim
), clength
);
332 } else if (param
.variable_type is DelegateType
) {
333 var deleg_type
= (DelegateType
) param
.variable_type
;
335 if (deleg_type
.delegate_symbol
.has_target
) {
336 var ctarget
= new
CCodeIdentifier (get_delegate_target_cname (d_params
.get (i
).name
));
337 carg_map
.set (get_param_pos (param
.cdelegate_target_parameter_position
), ctarget
);
338 if (deleg_type
.value_owned
) {
339 var ctarget_destroy_notify
= new
CCodeIdentifier (get_delegate_target_destroy_notify_cname (d_params
.get (i
).name
));
340 carg_map
.set (get_param_pos (m
.cdelegate_target_parameter_position
+ 0.01), ctarget_destroy_notify
);
347 if (!m
.no_array_length
&& m
.return_type is ArrayType
) {
348 var array_type
= (ArrayType
) m
.return_type
;
349 for (int dim
= 1; dim
<= array_type
.rank
; dim
++) {
350 CCodeExpression clength
;
351 if (d
.no_array_length
) {
352 clength
= new
CCodeConstant ("NULL");
354 clength
= new
CCodeIdentifier (get_array_length_cname ("result", dim
));
356 carg_map
.set (get_param_pos (m
.carray_length_parameter_position
+ 0.01 * dim
), clength
);
358 } else if (m
.return_type is DelegateType
) {
359 var deleg_type
= (DelegateType
) m
.return_type
;
361 if (deleg_type
.delegate_symbol
.has_target
) {
362 var ctarget
= new
CCodeIdentifier (get_delegate_target_cname ("result"));
363 carg_map
.set (get_param_pos (m
.cdelegate_target_parameter_position
), ctarget
);
364 if (deleg_type
.value_owned
) {
365 var ctarget_destroy_notify
= new
CCodeIdentifier (get_delegate_target_destroy_notify_cname ("result"));
366 carg_map
.set (get_param_pos (m
.cdelegate_target_parameter_position
+ 0.01), ctarget_destroy_notify
);
369 } else if (m
.return_type
.is_real_non_null_struct_type ()) {
370 carg_map
.set (get_param_pos (-3), new
CCodeIdentifier ("result"));
373 if (m
.get_error_types ().size
> 0) {
374 carg_map
.set (get_param_pos (-1), new
CCodeIdentifier ("error"));
377 var ccall
= new
CCodeFunctionCall (new
CCodeIdentifier (m
.get_cname ()));
379 // append C arguments in the right order
383 foreach (int pos
in carg_map
.get_keys ()) {
384 if (pos
> last_pos
&& (min_pos
== -1 || pos
< min_pos
)) {
391 ccall
.add_argument (carg_map
.get (min_pos
));
396 ccall
.add_argument (new
CCodeConstant ("NULL"));
397 ccall
.add_argument (new
CCodeConstant ("NULL"));
399 var block
= new
CCodeBlock ();
400 if (m
.return_type is VoidType
|| m
.return_type
.is_real_non_null_struct_type ()) {
401 block
.add_statement (new
CCodeExpressionStatement (ccall
));
403 var cdecl
= new
CCodeDeclaration (return_type_cname
);
404 cdecl
.add_declarator (new
CCodeVariableDeclarator ("result", ccall
));
405 block
.add_statement (cdecl
);
408 if (d
.has_target
&& !dt
.value_owned
&& dt
.is_called_once
&& expr
!= null) {
409 // destroy notify "self" after the call, only support lambda expressions and methods
410 CCodeExpression? destroy_notify
= null;
411 if (expr is LambdaExpression
) {
412 var lambda
= (LambdaExpression
) expr
;
413 if (lambda
.method
.closure
) {
414 int block_id
= get_block_id (current_closure_block
);
415 destroy_notify
= new
CCodeIdentifier ("block%d_data_unref".printf (block_id
));
416 } else if (get_this_type () != null) {
417 destroy_notify
= get_destroy_func_expression (get_this_type ());
418 } else if (in_constructor
) {
419 destroy_notify
= new
CCodeIdentifier ("g_object_unref");
421 } else if (expr
.symbol_reference is Method
) {
422 var ma
= (MemberAccess
) expr
;
423 if (m
.binding
!= MemberBinding
.STATIC
&&
424 !m
.is_async_callback
&&
425 ma
.inner
.value_type
.data_type
!= null && ma
.inner
.value_type
.data_type
.is_reference_counting ()) {
426 destroy_notify
= get_destroy_func_expression (ma
.inner
.value_type
);
430 if (destroy_notify
!= null) {
431 var unref_call
= new
CCodeFunctionCall (destroy_notify
);
432 unref_call
.add_argument (new
CCodeIdentifier ("self"));
433 block
.add_statement (new
CCodeExpressionStatement (unref_call
));
437 if (!(m
.return_type is VoidType
|| m
.return_type
.is_real_non_null_struct_type ())) {
438 block
.add_statement (new
CCodeReturnStatement (new
CCodeIdentifier ("result")));
443 cfile
.add_function_declaration (function
);
445 function
.block
= block
;
446 cfile
.add_function (function
);
451 public override CCodeParameter
generate_parameter (Parameter param
, CCodeFile decl_space
, Map
<int,CCodeParameter
> cparam_map
, Map
<int,CCodeExpression
>? carg_map
) {
452 if (!(param
.variable_type is DelegateType
|| param
.variable_type is MethodType
)) {
453 return base.generate_parameter (param
, decl_space
, cparam_map
, carg_map
);
456 string ctypename
= param
.variable_type
.get_cname ();
457 string target_ctypename
= "void*";
458 string target_destroy_notify_ctypename
= "GDestroyNotify";
460 if (param
.parent_symbol is Delegate
461 && param
.variable_type
.get_cname () == ((Delegate
) param
.parent_symbol
).get_cname ()) {
462 // recursive delegate
463 ctypename
= "GCallback";
466 if (param
.direction
!= ParameterDirection
.IN
) {
468 target_ctypename
+= "*";
469 target_destroy_notify_ctypename
+= "*";
472 var main_cparam
= new
CCodeParameter (get_variable_cname (param
.name
), ctypename
);
474 cparam_map
.set (get_param_pos (param
.cparameter_position
), main_cparam
);
475 if (carg_map
!= null) {
476 carg_map
.set (get_param_pos (param
.cparameter_position
), get_variable_cexpression (param
.name
));
479 if (param
.variable_type is DelegateType
) {
480 var deleg_type
= (DelegateType
) param
.variable_type
;
481 var d
= deleg_type
.delegate_symbol
;
483 generate_delegate_declaration (d
, decl_space
);
486 var cparam
= new
CCodeParameter (get_delegate_target_cname (get_variable_cname (param
.name
)), target_ctypename
);
487 cparam_map
.set (get_param_pos (param
.cdelegate_target_parameter_position
), cparam
);
488 if (carg_map
!= null) {
489 carg_map
.set (get_param_pos (param
.cdelegate_target_parameter_position
), get_variable_cexpression (cparam
.name
));
491 if (deleg_type
.value_owned
) {
492 cparam
= new
CCodeParameter (get_delegate_target_destroy_notify_cname (get_variable_cname (param
.name
)), target_destroy_notify_ctypename
);
493 cparam_map
.set (get_param_pos (param
.cdelegate_target_parameter_position
+ 0.01), cparam
);
494 if (carg_map
!= null) {
495 carg_map
.set (get_param_pos (param
.cdelegate_target_parameter_position
+ 0.01), get_variable_cexpression (cparam
.name
));
499 } else if (param
.variable_type is MethodType
) {
500 var cparam
= new
CCodeParameter (get_delegate_target_cname (get_variable_cname (param
.name
)), target_ctypename
);
501 cparam_map
.set (get_param_pos (param
.cdelegate_target_parameter_position
), cparam
);
502 if (carg_map
!= null) {
503 carg_map
.set (get_param_pos (param
.cdelegate_target_parameter_position
), get_variable_cexpression (cparam
.name
));