Add support for async signal handlers
[vala-lang.git] / codegen / valaccodedelegatemodule.vala
blobcef9219288cec6d14f2f4e3aee44123bd57e3df2
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, d.get_cname ())) {
32 return;
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 ()) {
43 // recursive delegate
44 return_type_cname = "GCallback";
45 } else {
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);
107 if (d.has_target) {
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 ();
183 } else {
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
191 return wrapper_name;
194 // declaration
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);
208 if (d.has_target) {
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
261 int last_pos = -1;
262 int min_pos;
263 while (true) {
264 min_pos = -1;
265 foreach (int pos in cparam_map.get_keys ()) {
266 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
267 min_pos = pos;
270 if (min_pos == -1) {
271 break;
273 function.add_parameter (cparam_map.get (min_pos));
274 last_pos = min_pos;
278 // definition
280 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
282 int i = 0;
283 if (m.binding == MemberBinding.INSTANCE || m.closure) {
284 CCodeExpression arg;
285 if (d.has_target) {
286 arg = new CCodeIdentifier ("self");
287 } else {
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");
292 } else {
293 arg = new CCodeIdentifier (get_variable_cname (d_params.get (0).name));
294 i = 1;
297 carg_map.set (get_param_pos (m.cinstance_parameter_position), arg);
300 bool first = true;
302 foreach (Parameter param in m.get_parameters ()) {
303 if (first && d.sender_type != null && m.get_parameters ().size == d.get_parameters ().size + 1) {
304 // sender parameter
305 carg_map.set (get_param_pos (param.cparameter_position), new CCodeIdentifier ("_sender"));
307 first = false;
308 continue;
311 CCodeExpression arg;
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));
324 clength = len_call;
325 } else if (d_params.get (i).no_array_length) {
326 clength = new CCodeConstant ("-1");
327 } else {
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);
345 i++;
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");
353 } else {
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
380 last_pos = -1;
381 while (true) {
382 min_pos = -1;
383 foreach (int pos in carg_map.get_keys ()) {
384 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
385 min_pos = pos;
388 if (min_pos == -1) {
389 break;
391 ccall.add_argument (carg_map.get (min_pos));
392 last_pos = min_pos;
395 if (m.coroutine) {
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));
402 } else {
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")));
441 // append to file
443 cfile.add_function_declaration (function);
445 function.block = block;
446 cfile.add_function (function);
448 return wrapper_name;
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) {
467 ctypename += "*";
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);
485 if (d.has_target) {
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));
507 return main_cparam;