64ad460c3570548a6d9b0e0f5caae4e0806b37fb
[roobuilder] / src / codegen / valaccodedelegatemodule.vala
1 /* valaccodedelegatemodule.vala
2  *
3  * Copyright (C) 2006-2010  Jürg Billeter
4  * Copyright (C) 2006-2008  Raffaele Sandrini
5  *
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.
10
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.
15
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
19  *
20  * Author:
21  *      Jürg Billeter <j@bitron.ch>
22  *      Raffaele Sandrini <raffaele@sandrini.ch>
23  */
24
25
26 /**
27  * The link between an assignment and generated code.
28  */
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;
33                 }
34
35                 // internally generated delegates don't require a typedef
36                 if (d.sender_type != null) {
37                         return;
38                 }
39
40                 var creturn_type = get_callable_creturn_type (d);
41                 if (creturn_type is DelegateType && ((DelegateType) creturn_type).delegate_symbol == d) {
42                         // recursive delegate
43                         creturn_type = new DelegateType ((Delegate) context.root.scope.lookup ("GLib").scope.lookup ("Callback"));
44                 }
45
46                 generate_type_declaration (creturn_type, decl_space);
47
48                 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
49
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);
53                 }
54
55                 // FIXME partial code duplication with CCodeMethodModule.generate_cparameters
56
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) + "*";
65
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);
69                         }
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);
81                                 }
82                         }
83                 }
84
85                 if (d.has_target) {
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);
89                 }
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);
94                 }
95
96                 // append C parameters in the right order
97                 int last_pos = -1;
98                 int min_pos;
99                 while (true) {
100                         min_pos = -1;
101                         foreach (int pos in cparam_map.get_keys ()) {
102                                 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
103                                         min_pos = pos;
104                                 }
105                         }
106                         if (min_pos == -1) {
107                                 break;
108                         }
109                         cfundecl.add_parameter (cparam_map.get (min_pos));
110                         last_pos = min_pos;
111                 }
112
113                 var ctypedef = new CCodeTypeDefinition (get_ccode_name (creturn_type), cfundecl);
114
115                 if (d.version.deprecated) {
116                         if (context.profile == Profile.GOBJECT) {
117                                 decl_space.add_include ("glib.h");
118                         }
119                         ctypedef.modifiers |= CCodeModifiers.DEPRECATED;
120                 }
121
122                 decl_space.add_type_declaration (ctypedef);
123         }
124
125         public override void visit_delegate (Delegate d) {
126                 generate_delegate_declaration (d, cfile);
127
128                 if (!d.is_internal_symbol ()) {
129                         generate_delegate_declaration (d, header_file);
130                 }
131                 if (!d.is_private_symbol ()) {
132                         generate_delegate_declaration (d, internal_header_file);
133                 }
134
135                 d.accept_children (this);
136         }
137
138         public override string get_delegate_target_cname (string delegate_cname) {
139                 return "%s_target".printf (delegate_cname);
140         }
141
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);
145         }
146
147         public override CCodeExpression get_delegate_target_cvalue (TargetValue value) {
148                 return ((GLibValue) value).delegate_target_cvalue;
149         }
150
151         public override CCodeExpression get_delegate_target_destroy_notify_cvalue (TargetValue value) {
152                 return ((GLibValue) value).delegate_target_destroy_notify_cvalue;
153         }
154
155         public override string get_delegate_target_destroy_notify_cname (string delegate_cname) {
156                 return "%s_target_destroy_notify".printf (delegate_cname);
157         }
158
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;
163
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;
169                         }
170
171                         if (method.is_variadic ()) {
172                                 Report.warning (node.source_reference, "internal: Variadic method requires a direct cast to delegate");
173                         } else {
174                                 return new CCodeIdentifier (generate_delegate_wrapper (method, dt, node));
175                         }
176                 }
177
178                 return base.get_implicit_cast_expression (source_cexpr, expression_type, target_type, node);
179         }
180
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));
192                 }
193
194                 string wrapper_name = "_%s_%s".printf (get_ccode_name (m), delegate_name);
195
196                 if (!add_wrapper (wrapper_name)) {
197                         // wrapper already defined
198                         return wrapper_name;
199                 }
200
201                 // declaration
202                 var creturn_type = get_callable_creturn_type (d);
203
204                 var function = new CCodeFunction (wrapper_name, get_ccode_name (creturn_type));
205                 function.modifiers = CCodeModifiers.STATIC;
206
207                 push_function (function);
208
209                 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
210
211                 if (d.has_target) {
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);
214                 }
215
216                 if (d.sender_type != null) {
217                         var param = new Parameter ("_sender", d.sender_type);
218                         generate_parameter (param, cfile, cparam_map, null);
219                 }
220
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);
229                         }
230
231                         generate_parameter (param, cfile, cparam_map, null);
232                 }
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) + "*";
237
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);
241                         }
242                 } else if (d.return_type is DelegateType) {
243                         // return delegate target if appropriate
244                         var deleg_type = (DelegateType) d.return_type;
245
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);
252                                 }
253                         }
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);
257                 }
258
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);
262                 }
263
264                 // append C parameters in the right order
265                 int last_pos = -1;
266                 int min_pos;
267                 while (true) {
268                         min_pos = -1;
269                         foreach (int pos in cparam_map.get_keys ()) {
270                                 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
271                                         min_pos = pos;
272                                 }
273                         }
274                         if (min_pos == -1) {
275                                 break;
276                         }
277                         function.add_parameter (cparam_map.get (min_pos));
278                         last_pos = min_pos;
279                 }
280
281
282                 // definition
283
284                 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
285
286                 int i = 0;
287                 if (m.binding == MemberBinding.INSTANCE || m.closure) {
288                         CCodeExpression arg;
289                         if (d.has_target) {
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);
293                                 }
294                         } else {
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 ();
299                                 } else {
300                                         arg = new CCodeIdentifier (get_ccode_name (d_params.get (0)));
301                                         i = 1;
302                                 }
303                         }
304                         carg_map.set (get_param_pos (get_ccode_instance_pos (m)), arg);
305                 }
306
307                 bool first = true;
308
309                 foreach (Parameter param in m.get_parameters ()) {
310                         if (first && d.sender_type != null && m.get_parameters ().size == d.get_parameters ().size + 1) {
311                                 // sender parameter
312                                 carg_map.set (get_param_pos (get_ccode_pos (param)), new CCodeIdentifier ("_sender"));
313
314                                 first = false;
315                                 continue;
316                         }
317
318                         CCodeExpression arg;
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);
322                         }
323                         carg_map.set (get_param_pos (get_ccode_pos (param)), arg);
324
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));
334                                                 clength = len_call;
335                                         } else if (!get_ccode_array_length (d_params.get (i))) {
336                                                 clength = new CCodeConstant ("-1");
337                                         } else {
338                                                 clength = new CCodeIdentifier (get_variable_array_length_cname (d_params.get (i), dim));
339                                         }
340                                         carg_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), clength);
341                                 }
342                         } else if (get_ccode_delegate_target (param) && param.variable_type is DelegateType) {
343                                 var deleg_type = (DelegateType) param.variable_type;
344
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);
351                                         }
352                                 }
353                         }
354
355                         i++;
356                 }
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");
363                                 } else {
364                                         clength = new CCodeIdentifier (get_array_length_cname ("result", dim));
365                                 }
366                                 carg_map.set (get_param_pos (get_ccode_array_length_pos (m) + 0.01 * dim), clength);
367                         }
368                 } else if (get_ccode_delegate_target (m) && m.return_type is DelegateType) {
369                         var deleg_type = (DelegateType) m.return_type;
370
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);
377                                 }
378                         }
379                 } else if (m.return_type.is_real_non_null_struct_type ()) {
380                         carg_map.set (get_param_pos (-3), new CCodeIdentifier ("result"));
381                 }
382
383                 if (m.tree_can_fail) {
384                         carg_map.set (get_param_pos (get_ccode_error_pos (m)), new CCodeIdentifier ("error"));
385                 }
386
387                 var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (m)));
388
389                 // append C arguments in the right order
390                 last_pos = -1;
391                 while (true) {
392                         min_pos = -1;
393                         foreach (int pos in carg_map.get_keys ()) {
394                                 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
395                                         min_pos = pos;
396                                 }
397                         }
398                         if (min_pos == -1) {
399                                 break;
400                         }
401                         ccall.add_argument (carg_map.get (min_pos));
402                         last_pos = min_pos;
403                 }
404
405                 if (m.coroutine) {
406                         ccall.add_argument (new CCodeConstant ("NULL"));
407                         ccall.add_argument (new CCodeConstant ("NULL"));
408                 }
409
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)));
415                         }
416                 } else {
417                         CCodeExpression result = ccall;
418                         if (d.return_type is GenericType) {
419                                 result = convert_to_generic_pointer (result, m.return_type);
420                         }
421                         ccode.add_declaration (get_ccode_name (creturn_type), new CCodeVariableDeclarator ("result", result));
422                 }
423
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;
427                         if (m.closure) {
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);
432                         }
433
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);
438                         }
439                 }
440
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"));
444                 }
445
446                 pop_function ();
447
448                 // append to file
449                 cfile.add_function_declaration (function);
450                 cfile.add_function (function);
451
452                 return wrapper_name;
453         }
454
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);
458                 }
459
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"));
464                 }
465
466                 generate_type_declaration (param_type, decl_space);
467
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);
471
472                 if (param.direction != ParameterDirection.IN) {
473                         ctypename += "*";
474                         target_ctypename += "*";
475                         target_destroy_notify_ctypename += "*";
476                 }
477
478                 var main_cparam = new CCodeParameter (get_ccode_name (param), ctypename);
479
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));
483                 }
484
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));
492                                 }
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));
498                                         }
499                                 }
500                         }
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));
506                         }
507                 }
508
509                 return main_cparam;
510         }
511 }