change run to use meson/ninja and then exec. - remove libvala code from application...
[roobuilder] / src / codegen / valagsignalmodule.vala
1 /* valagsignalmodule.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 public class Vala.GSignalModule : GObjectModule {
27         string get_marshaller_function (Signal sig, List<Parameter> params, DataType return_type, string? prefix = null) {
28                 var signature = get_marshaller_signature (sig, params, return_type);
29                 string ret;
30
31                 if (prefix == null) {
32                         if (predefined_marshal_set.contains (signature)) {
33                                 prefix = "g_cclosure_marshal";
34                         } else {
35                                 prefix = "g_cclosure_user_marshal";
36                         }
37                 }
38
39                 ret = "%s_%s_".printf (prefix, get_ccode_marshaller_type_name (return_type));
40
41                 foreach (Parameter p in params) {
42                         ret = "%s_%s".printf (ret, get_ccode_marshaller_type_name (p).replace (",", "_"));
43                 }
44                 if (sig.return_type.is_real_non_null_struct_type ()) {
45                         ret = ret + "_POINTER";
46                 } else if (params.size == 0) {
47                         ret = ret + "_VOID";
48                 }
49
50                 return ret;
51         }
52
53         private string? get_value_type_name_from_type_reference (DataType t) {
54                 if (t is PointerType || t is GenericType) {
55                         return "gpointer";
56                 } else if (t is VoidType) {
57                         return "void";
58                 } else if (get_ccode_type_id (t) == get_ccode_type_id (string_type)) {
59                         return "const char*";
60                 } else if (t.type_symbol is Class || t.type_symbol is Interface) {
61                         return "gpointer";
62                 } else if (t is ValueType && t.nullable) {
63                         return "gpointer";
64                 } else if (t.type_symbol is Struct) {
65                         unowned Struct st = (Struct) t.type_symbol;
66                         if (st.is_simple_type ()) {
67                                 return get_ccode_name (t.type_symbol);
68                         } else {
69                                 return "gpointer";
70                         }
71                 } else if (t.type_symbol is Enum) {
72                         unowned Enum en = (Enum) t.type_symbol;
73                         if (en.is_flags) {
74                                 return "guint";
75                         } else {
76                                 return "gint";
77                         }
78                 } else if (t is ArrayType) {
79                         return "gpointer";
80                 } else if (t is DelegateType) {
81                         return "gpointer";
82                 } else if (t is ErrorType) {
83                         return "gpointer";
84                 }
85
86                 return null;
87         }
88
89         private string? get_value_type_name_from_parameter (Parameter p) {
90                 if (p.direction != ParameterDirection.IN) {
91                         return "gpointer";
92                 } else {
93                         return get_value_type_name_from_type_reference (p.variable_type);
94                 }
95         }
96
97         private string get_marshaller_signature (Signal sig, List<Parameter> params, DataType return_type) {
98                 string signature;
99
100                 signature = "%s:".printf (get_ccode_marshaller_type_name (return_type));
101                 bool first = true;
102                 foreach (Parameter p in params) {
103                         if (first) {
104                                 signature = signature + get_ccode_marshaller_type_name (p);
105                                 first = false;
106                         } else {
107                                 signature = "%s,%s".printf (signature, get_ccode_marshaller_type_name (p));
108                         }
109                 }
110                 if (sig.return_type.is_real_non_null_struct_type ()) {
111                         signature = signature + (first ? "POINTER" : ",POINTER");
112                 } else if (params.size == 0) {
113                         signature = signature + "VOID";
114                 }
115
116                 return signature;
117         }
118
119         private CCodeExpression get_signal_name_cexpression (Signal sig, Expression? detail_expr, CodeNode node) {
120                 if (detail_expr == null) {
121                         return get_signal_canonical_constant (sig);
122                 }
123
124                 if (detail_expr is StringLiteral) {
125                         return get_signal_canonical_constant (sig, ((StringLiteral) detail_expr).eval ());
126                 }
127
128                 var detail_value = create_temp_value (detail_expr.value_type, false, node, true);
129                 temp_ref_values.insert (0, detail_value);
130
131                 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strconcat"));
132                 ccall.add_argument (get_signal_canonical_constant (sig, ""));
133                 ccall.add_argument (get_cvalue (detail_expr));
134                 ccall.add_argument (new CCodeConstant ("NULL"));
135
136                 ccode.add_assignment (get_cvalue_ (detail_value), ccall);
137                 return get_cvalue_ (detail_value);
138         }
139
140         private CCodeExpression get_signal_id_cexpression (Signal sig) {
141                 var cl = (TypeSymbol) sig.parent_symbol;
142                 var signal_array = new CCodeIdentifier ("%s_signals".printf (get_ccode_lower_case_name (cl)));
143                 var signal_enum_value = new CCodeIdentifier ("%s_%s_SIGNAL".printf (get_ccode_upper_case_name (cl), get_ccode_upper_case_name (sig)));
144
145                 return new CCodeElementAccess (signal_array, signal_enum_value);
146         }
147
148         private CCodeExpression get_detail_cexpression (Expression detail_expr, CodeNode node) {
149                 var detail_cexpr = get_cvalue (detail_expr);
150                 CCodeFunctionCall detail_ccall;
151                 if (is_constant_ccode_expression (detail_cexpr)) {
152                         detail_ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string"));
153                 } else {
154                         detail_ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_string"));
155                 }
156                 detail_ccall.add_argument (detail_cexpr);
157
158                 return detail_ccall;
159         }
160
161         public override void visit_signal (Signal sig) {
162                 if (signal_enum != null && sig.parent_symbol is TypeSymbol) {
163                         signal_enum.add_value (new CCodeEnumValue ("%s_%s_SIGNAL".printf (get_ccode_upper_case_name ((TypeSymbol) sig.parent_symbol), get_ccode_upper_case_name (sig))));
164                 }
165
166                 sig.accept_children (this);
167
168                 // declare parameter type
169                 unowned List<Parameter> params = sig.get_parameters ();
170                 foreach (Parameter p in params) {
171                         generate_parameter (p, cfile, new HashMap<int,CCodeParameter> (), null);
172                 }
173
174                 if (sig.return_type.is_real_non_null_struct_type ()) {
175                         generate_marshaller (sig, params, new VoidType ());
176                 } else {
177                         generate_marshaller (sig, params, sig.return_type);
178                 }
179         }
180
181         void generate_marshaller (Signal sig, List<Parameter> params, DataType return_type) {
182                 string signature;
183                 int n_params, i;
184
185                 /* check whether a signal with the same signature already exists for this source file (or predefined) */
186                 signature = get_marshaller_signature (sig, params, return_type);
187                 if (predefined_marshal_set.contains (signature) || user_marshal_set.contains (signature)) {
188                         return;
189                 }
190
191                 var signal_marshaller = new CCodeFunction (get_marshaller_function (sig, params, return_type, null), "void");
192                 signal_marshaller.modifiers = CCodeModifiers.STATIC;
193
194                 signal_marshaller.add_parameter (new CCodeParameter ("closure", "GClosure *"));
195                 signal_marshaller.add_parameter (new CCodeParameter ("return_value", "GValue *"));
196                 signal_marshaller.add_parameter (new CCodeParameter ("n_param_values", "guint"));
197                 signal_marshaller.add_parameter (new CCodeParameter ("param_values", "const GValue *"));
198                 signal_marshaller.add_parameter (new CCodeParameter ("invocation_hint", "gpointer"));
199                 signal_marshaller.add_parameter (new CCodeParameter ("marshal_data", "gpointer"));
200
201                 push_function (signal_marshaller);
202
203                 var callback_decl = new CCodeFunctionDeclarator (get_marshaller_function (sig, params, return_type, "GMarshalFunc"));
204                 callback_decl.add_parameter (new CCodeParameter ("data1", "gpointer"));
205                 n_params = 1;
206                 foreach (Parameter p in params) {
207                         callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), get_value_type_name_from_parameter (p)));
208                         n_params++;
209                         if (p.variable_type is ArrayType) {
210                                 var array_type = (ArrayType) p.variable_type;
211                                 var length_ctype = get_ccode_array_length_type (p);
212                                 for (var j = 0; j < array_type.rank; j++) {
213                                         callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), length_ctype));
214                                         n_params++;
215                                 }
216                         } else if (p.variable_type is DelegateType) {
217                                 unowned DelegateType delegate_type = (DelegateType) p.variable_type;
218                                 if (delegate_type.delegate_symbol.has_target) {
219                                         callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), get_ccode_name (delegate_target_type)));
220                                         n_params++;
221                                         if (delegate_type.is_disposable ()) {
222                                                 callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), get_ccode_name (delegate_target_destroy_type)));
223                                                 n_params++;
224                                         }
225                                 }
226                         }
227                 }
228                 if (sig.return_type.is_real_non_null_struct_type ()) {
229                         callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), "gpointer"));
230                         n_params++;
231                 }
232
233                 callback_decl.add_parameter (new CCodeParameter ("data2", "gpointer"));
234                 ccode.add_statement (new CCodeTypeDefinition (get_value_type_name_from_type_reference (return_type), callback_decl));
235
236                 ccode.add_declaration (get_marshaller_function (sig, params, return_type, "GMarshalFunc"), new CCodeVariableDeclarator ("callback"), CCodeModifiers.REGISTER);
237
238                 ccode.add_declaration ("GCClosure *", new CCodeVariableDeclarator ("cc", new CCodeCastExpression (new CCodeIdentifier ("closure"), "GCClosure *")), CCodeModifiers.REGISTER);
239
240                 ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("data1"), CCodeModifiers.REGISTER);
241                 ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("data2"), CCodeModifiers.REGISTER);
242
243                 CCodeFunctionCall fc;
244
245                 if (return_type.type_symbol != null || return_type is ArrayType) {
246                         ccode.add_declaration (get_value_type_name_from_type_reference (return_type), new CCodeVariableDeclarator ("v_return"));
247
248                         fc = new CCodeFunctionCall (new CCodeIdentifier ("g_return_if_fail"));
249                         fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("return_value"), new CCodeConstant ("NULL")));
250                         ccode.add_expression (fc);
251                 }
252
253                 fc = new CCodeFunctionCall (new CCodeIdentifier ("g_return_if_fail"));
254                 fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("n_param_values"), new CCodeConstant (n_params.to_string())));
255                 ccode.add_expression (fc);
256
257                 var data = new CCodeMemberAccess (new CCodeIdentifier ("closure"), "data", true);
258                 var param = new CCodeMemberAccess (new CCodeMemberAccess (new CCodeIdentifier ("param_values"), "data[0]", true), "v_pointer");
259                 var cond = new CCodeFunctionCall (new CCodeConstant ("G_CCLOSURE_SWAP_DATA"));
260                 cond.add_argument (new CCodeIdentifier ("closure"));
261                 ccode.open_if (cond);
262                 ccode.add_assignment (new CCodeIdentifier ("data1"), data);
263                 ccode.add_assignment (new CCodeIdentifier ("data2"), param);
264                 ccode.add_else ();
265                 ccode.add_assignment (new CCodeIdentifier ("data1"), param);
266                 ccode.add_assignment (new CCodeIdentifier ("data2"), data);
267                 ccode.close ();
268
269                 var c_assign_rhs =  new CCodeCastExpression (new CCodeConditionalExpression (new CCodeIdentifier ("marshal_data"), new CCodeIdentifier ("marshal_data"), new CCodeMemberAccess (new CCodeIdentifier ("cc"), "callback", true)), get_marshaller_function (sig, params, return_type, "GMarshalFunc"));
270                 ccode.add_assignment (new CCodeIdentifier ("callback"), c_assign_rhs);
271
272                 fc = new CCodeFunctionCall (new CCodeIdentifier ("callback"));
273                 fc.add_argument (new CCodeIdentifier ("data1"));
274                 i = 1;
275                 foreach (Parameter p in params) {
276                         CCodeFunctionCall inner_fc;
277                         if (p.direction != ParameterDirection.IN) {
278                                 inner_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_pointer"));
279                         } else if (p.variable_type is ValueType && p.variable_type.nullable) {
280                                 inner_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_pointer"));
281                         } else {
282                                 inner_fc = new CCodeFunctionCall (get_value_getter_function (p.variable_type));
283                         }
284                         inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
285                         fc.add_argument (inner_fc);
286                         i++;
287                         if (p.variable_type is ArrayType) {
288                                 var array_type = (ArrayType) p.variable_type;
289                                 var length_value_function = get_ccode_get_value_function (array_type.length_type.type_symbol);
290                                 assert (length_value_function != null && length_value_function != "");
291                                 for (var j = 0; j < array_type.rank; j++) {
292                                         inner_fc = new CCodeFunctionCall (new CCodeIdentifier (length_value_function));
293                                         inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
294                                         fc.add_argument (inner_fc);
295                                         i++;
296                                 }
297                         } else if (p.variable_type is DelegateType) {
298                                 unowned DelegateType delegate_type = (DelegateType) p.variable_type;
299                                 if (delegate_type.delegate_symbol.has_target) {
300                                         inner_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_pointer"));
301                                         inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
302                                         fc.add_argument (inner_fc);
303                                         i++;
304                                         if (delegate_type.is_disposable ()) {
305                                                 inner_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_pointer"));
306                                                 inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
307                                                 fc.add_argument (inner_fc);
308                                                 i++;
309                                         }
310                                 }
311                         }
312                 }
313                 if (sig.return_type.is_real_non_null_struct_type ()) {
314                         var inner_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_pointer"));
315                         inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
316                         fc.add_argument (inner_fc);
317                         i++;
318                 }
319                 fc.add_argument (new CCodeIdentifier ("data2"));
320
321                 if (return_type.type_symbol != null || return_type is ArrayType) {
322                         ccode.add_assignment (new CCodeIdentifier ("v_return"), fc);
323
324                         CCodeFunctionCall set_fc;
325                         if (return_type is ValueType) {
326                                 if (return_type.nullable) {
327                                         set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
328                                 } else {
329                                         set_fc = new CCodeFunctionCall (get_value_setter_function (return_type));
330                                 }
331                         } else {
332                                 set_fc = new CCodeFunctionCall (get_value_taker_function (return_type));
333                         }
334                         set_fc.add_argument (new CCodeIdentifier ("return_value"));
335                         set_fc.add_argument (new CCodeIdentifier ("v_return"));
336
337                         ccode.add_expression (set_fc);
338                 } else {
339                         ccode.add_expression (fc);
340                 }
341
342                 pop_function ();
343
344                 cfile.add_function_declaration (signal_marshaller);
345                 cfile.add_function (signal_marshaller);
346                 user_marshal_set.add (signature);
347         }
348
349         public override CCodeExpression get_signal_creation (Signal sig, ObjectTypeSymbol type) {
350                 CCodeFunctionCall csignew;
351                 if (sig.default_handler == null || sig.is_virtual) {
352                         csignew = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_new"));
353                 } else {
354                         csignew = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_new_class_handler"));
355                 }
356                 csignew.add_argument (new CCodeConstant ("\"%s\"".printf (get_ccode_name (sig))));
357                 csignew.add_argument (new CCodeIdentifier (get_ccode_type_id (type)));
358                 string[] flags = new string[0];
359                 var run_type = sig.get_attribute_string ("Signal", "run");
360                 if (run_type == "first") {
361                         flags += "G_SIGNAL_RUN_FIRST";
362                 } else if (run_type == "cleanup") {
363                         flags += "G_SIGNAL_RUN_CLEANUP";
364                 } else {
365                         flags += "G_SIGNAL_RUN_LAST";
366                 }
367                 if (sig.get_attribute_bool ("Signal", "detailed")) {
368                         flags += "G_SIGNAL_DETAILED";
369                 }
370
371                 if (sig.get_attribute_bool ("Signal", "no_recurse")) {
372                         flags += "G_SIGNAL_NO_RECURSE";
373                 }
374
375                 if (sig.get_attribute_bool ("Signal", "action")) {
376                         flags += "G_SIGNAL_ACTION";
377                 }
378
379                 if (sig.get_attribute_bool ("Signal", "no_hooks")) {
380                         flags += "G_SIGNAL_NO_HOOKS";
381                 }
382
383                 if (sig.version.deprecated) {
384                         flags += "G_SIGNAL_DEPRECATED";
385                 }
386
387                 csignew.add_argument (new CCodeConstant (string.joinv (" | ", flags)));
388
389                 if (sig.default_handler == null) {
390                         csignew.add_argument (new CCodeConstant ("0"));
391                 } else if (sig.is_virtual) {
392                         var struct_offset = new CCodeFunctionCall (new CCodeIdentifier ("G_STRUCT_OFFSET"));
393                         struct_offset.add_argument (new CCodeIdentifier (get_ccode_type_name (type)));
394                         struct_offset.add_argument (new CCodeIdentifier (get_ccode_vfunc_name (sig.default_handler)));
395                         csignew.add_argument (struct_offset);
396                 } else {
397                         csignew.add_argument (new CCodeCastExpression (new CCodeIdentifier (get_ccode_real_name (sig.default_handler)), "GCallback"));
398                 }
399                 csignew.add_argument (new CCodeConstant ("NULL"));
400                 csignew.add_argument (new CCodeConstant ("NULL"));
401
402                 unowned List<Parameter> params = sig.get_parameters ();
403                 string marshaller;
404                 if (sig.return_type.is_real_non_null_struct_type ()) {
405                         marshaller = get_marshaller_function (sig, params, new VoidType ());
406                 } else {
407                         marshaller = get_marshaller_function (sig, params, sig.return_type);
408                 }
409
410                 var marshal_arg = new CCodeIdentifier (marshaller);
411                 csignew.add_argument (marshal_arg);
412
413                 if (sig.return_type is PointerType || sig.return_type is GenericType) {
414                         csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
415                 } else if (sig.return_type is ErrorType) {
416                         csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
417                 } else if (sig.return_type is ValueType && sig.return_type.nullable) {
418                         csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
419                 } else if (sig.return_type.type_symbol == null) {
420                         csignew.add_argument (new CCodeConstant ("G_TYPE_NONE"));
421                 } else if (sig.return_type.is_real_non_null_struct_type ()) {
422                         csignew.add_argument (new CCodeConstant ("G_TYPE_NONE"));
423                 } else {
424                         csignew.add_argument (new CCodeConstant (get_ccode_type_id (sig.return_type.type_symbol)));
425                 }
426
427                 int params_len = 0;
428                 foreach (Parameter param in params) {
429                         params_len++;
430                         if (param.variable_type is ArrayType) {
431                                 params_len += ((ArrayType) param.variable_type).rank;
432                         } else if (param.variable_type is DelegateType) {
433                                 unowned DelegateType delegate_type = (DelegateType) param.variable_type;
434                                 if (delegate_type.delegate_symbol.has_target) {
435                                         params_len++;
436                                         if (delegate_type.is_disposable ()) {
437                                                 params_len++;
438                                         }
439                                 }
440                         }
441                 }
442                 if (sig.return_type.is_real_non_null_struct_type ()) {
443                         params_len++;
444                 }
445
446                 csignew.add_argument (new CCodeConstant ("%d".printf (params_len)));
447                 foreach (Parameter param in params) {
448                         if (param.variable_type is ArrayType) {
449                                 var array_type = (ArrayType) param.variable_type;
450                                 if (array_type.element_type.type_symbol == string_type.type_symbol) {
451                                         csignew.add_argument (new CCodeConstant ("G_TYPE_STRV"));
452                                 } else {
453                                         csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
454                                 }
455                                 assert (get_ccode_has_type_id (array_type.length_type.type_symbol));
456                                 var length_type_id = get_ccode_type_id (array_type.length_type.type_symbol);
457                                 for (var i = 0; i < array_type.rank; i++) {
458                                         csignew.add_argument (new CCodeConstant (length_type_id));
459                                 }
460                         } else if (param.variable_type is DelegateType) {
461                                 unowned DelegateType delegate_type = (DelegateType) param.variable_type;
462                                 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
463                                 if (delegate_type.delegate_symbol.has_target) {
464                                         csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
465                                         if (delegate_type.is_disposable ()) {
466                                                 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
467                                         }
468                                 }
469                         } else if (param.variable_type is PointerType || param.variable_type is GenericType || param.direction != ParameterDirection.IN) {
470                                 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
471                         } else if (param.variable_type is ErrorType) {
472                                 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
473                         } else if (param.variable_type is ValueType && param.variable_type.nullable) {
474                                 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
475                         } else {
476                                 csignew.add_argument (new CCodeConstant (get_ccode_type_id (param.variable_type.type_symbol)));
477                         }
478                 }
479                 if (sig.return_type.is_real_non_null_struct_type ()) {
480                         csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
481                 }
482
483                 marshal_arg.name = marshaller;
484
485                 return new CCodeAssignment (get_signal_id_cexpression (sig), csignew);
486         }
487
488         public override void visit_element_access (ElementAccess expr) {
489                 if (!(expr.container is MemberAccess && expr.container.symbol_reference is Signal)) {
490                         base.visit_element_access (expr);
491                         return;
492                 }
493
494                 if (expr.parent_node is MethodCall) {
495                         // detailed signal emission
496                         unowned Signal sig = (Signal) expr.symbol_reference;
497                         unowned MemberAccess ma = (MemberAccess) expr.container;
498                         var detail_expr = expr.get_indices ().get (0);
499
500                         set_cvalue (expr, emit_signal (sig, ma, detail_expr));
501                 } else {
502                         // signal connect or disconnect
503                 }
504         }
505
506         bool in_gobject_instance (Method m) {
507                 bool result = false;
508                 if (m.binding == MemberBinding.INSTANCE) {
509                         result = m.this_parameter.variable_type.type_symbol.is_subtype_of (gobject_type);
510                 }
511                 return result;
512         }
513
514         public override void visit_member_access (MemberAccess expr) {
515                 if (!(expr.symbol_reference is Signal)) {
516                         base.visit_member_access (expr);
517                         return;
518                 }
519
520                 unowned Signal sig = (Signal) expr.symbol_reference;
521
522                 set_cvalue (expr, emit_signal (sig, expr));
523         }
524
525         CCodeExpression emit_signal (Signal sig, MemberAccess expr, Expression? detail_expr = null) {
526                 CCodeExpression pub_inst = null;
527
528                 if (expr.inner != null) {
529                         pub_inst = get_cvalue (expr.inner);
530                 }
531
532                 if (expr.inner is BaseAccess && sig.is_virtual) {
533                         var m = sig.default_handler;
534                         var base_class = (Class) m.parent_symbol;
535                         var vcast = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_class_type_function (base_class)));
536                         vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (get_ccode_lower_case_name (current_class))));
537                         return new CCodeMemberAccess.pointer (vcast, m.name);
538                 }
539
540                 if (!sig.external_package && expr.source_reference.file == sig.source_reference.file && !(sig is DynamicSignal)) {
541                         var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit"));
542                         ccall.add_argument (pub_inst);
543                         ccall.add_argument (get_signal_id_cexpression (sig));
544                         if (detail_expr == null) {
545                                 ccall.add_argument (new CCodeConstant ("0"));
546                         } else {
547                                 ccall.add_argument (get_detail_cexpression (detail_expr, expr));
548                         }
549                         return ccall;
550                 } else if (get_ccode_has_emitter (sig)) {
551                         string emitter_func;
552                         if (sig.emitter != null) {
553                                 if (!sig.external_package && expr.source_reference.file != sig.source_reference.file) {
554                                         generate_method_declaration (sig.emitter, cfile);
555                                 }
556                                 emitter_func = get_ccode_lower_case_name (sig.emitter);
557                         } else {
558                                 unowned TypeSymbol sym = (TypeSymbol) sig.parent_symbol;
559                                 emitter_func = "%s_%s".printf (get_ccode_lower_case_name (sym), get_ccode_lower_case_name (sig));
560                         }
561                         var ccall = new CCodeFunctionCall (new CCodeIdentifier (emitter_func));
562                         ccall.add_argument (pub_inst);
563                         return ccall;
564                 } else {
565                         var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit_by_name"));
566                         ccall.add_argument (pub_inst);
567                         if (detail_expr == null) {
568                                 ccall.add_argument (get_signal_canonical_constant (sig));
569                         } else {
570                                 ccall.add_argument (get_signal_name_cexpression (sig, detail_expr, expr));
571                         }
572                         return ccall;
573                 }
574         }
575
576         public override void visit_method_call (MethodCall expr) {
577                 var method_type = expr.call.value_type as MethodType;
578
579                 if (method_type == null || !(method_type.method_symbol.parent_symbol is Signal)) {
580                         // no signal connect/disconnect call
581                         base.visit_method_call (expr);
582                         return;
583                 }
584
585                 var sig = (Signal) method_type.method_symbol.parent_symbol;
586                 var signal_access = ((MemberAccess) expr.call).inner;
587                 var handler = expr.get_argument_list ().get (0);
588
589                 bool disconnect = (method_type.method_symbol.name == "disconnect");
590                 bool after = (method_type.method_symbol.name == "connect_after");
591
592                 var cexpr = connect_signal (sig, signal_access, handler, disconnect, after, expr);
593                 set_cvalue (expr, cexpr);
594         }
595
596         CCodeExpression? connect_signal (Signal sig, Expression signal_access, Expression handler, bool disconnect, bool after, CodeNode expr) {
597                 string connect_func;
598
599                 DelegateType? dt = null;
600                 if (handler.symbol_reference is Variable) {
601                         dt = ((Variable) handler.symbol_reference).variable_type as DelegateType;
602                         if (dt != null && !context.experimental) {
603                                 Report.warning (handler.source_reference, "Connecting delegates to signals is experimental");
604                         }
605                         // Use actual lambda expression if available for proper target/destroy handling
606                         if (((Variable) handler.symbol_reference).initializer is LambdaExpression) {
607                                 handler = ((Variable) handler.symbol_reference).initializer;
608                         }
609                 }
610                 var m = handler.symbol_reference as Method;
611
612                 if (!disconnect) {
613                         // connect
614                         if (!(sig is DynamicSignal) && ((m != null && m.closure) || (dt != null && dt.value_owned))) {
615                                 connect_func = "g_signal_connect_data";
616                         } else if (m != null && in_gobject_instance (m)) {
617                                 connect_func = "g_signal_connect_object";
618                         } else if (!after) {
619                                 connect_func = "g_signal_connect";
620                         } else {
621                                 connect_func = "g_signal_connect_after";
622                         }
623                 } else {
624                         // disconnect
625                         if (sig is DynamicSignal) {
626                                 connect_func = "VALA_UNSUPPORTED";
627                         } else {
628                                 connect_func = "g_signal_handlers_disconnect_matched";
629                         }
630                 }
631
632                 var ccall = new CCodeFunctionCall (new CCodeIdentifier (connect_func));
633
634                 CCodeExpression signal_name_cexpr = null;
635
636                 // first argument: instance of sender
637                 MemberAccess ma;
638                 if (signal_access is ElementAccess) {
639                         var ea = (ElementAccess) signal_access;
640                         ma = (MemberAccess) ea.container;
641                         var detail_expr = ea.get_indices ().get (0);
642                         signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
643                 } else {
644                         ma = (MemberAccess) signal_access;
645                         signal_name_cexpr = get_signal_name_cexpression (sig, null, expr);
646                 }
647                 if (ma.inner != null) {
648                         ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
649                 } else {
650                         ccall.add_argument (get_this_cexpression ());
651                 }
652
653                 if (sig is DynamicSignal) {
654                         // dynamic_signal_connect or dynamic_signal_disconnect
655
656                         // second argument: signal name
657                         ccall.add_argument (new CCodeConstant ("\"%s\"".printf (get_ccode_name (sig))));
658                 } else if (!disconnect) {
659                         // g_signal_connect_object or g_signal_connect
660
661                         // second argument: signal name
662                         ccall.add_argument (signal_name_cexpr);
663                 } else {
664                         // g_signal_handlers_disconnect_matched
665
666                         // second argument: mask
667                         if (!(signal_access is ElementAccess)) {
668                                 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
669                         } else {
670                                 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
671                         }
672
673                         // get signal id
674                         var temp_decl = get_temp_variable (uint_type);
675                         emit_temp_var (temp_decl);
676                         var parse_call = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_parse_name"));
677                         parse_call.add_argument (signal_name_cexpr);
678                         var decl_type = (TypeSymbol) sig.parent_symbol;
679                         parse_call.add_argument (new CCodeIdentifier (get_ccode_type_id (decl_type)));
680                         parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_decl.name)));
681                         LocalVariable? detail_temp_decl = null;
682                         if (!(signal_access is ElementAccess)) {
683                                 parse_call.add_argument (new CCodeConstant ("NULL"));
684                                 parse_call.add_argument (new CCodeConstant ("FALSE"));
685                         } else {
686                                 detail_temp_decl = get_temp_variable (gquark_type);
687                                 emit_temp_var (detail_temp_decl);
688                                 parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (detail_temp_decl.name)));
689                                 parse_call.add_argument (new CCodeConstant ("TRUE"));
690                         }
691                         ccode.add_expression (parse_call);
692
693                         // third argument: signal_id
694                         ccall.add_argument (get_variable_cexpression (temp_decl.name));
695
696                         // fourth argument: detail
697                         if (detail_temp_decl == null) {
698                                 ccall.add_argument (new CCodeConstant ("0"));
699                         } else {
700                                 ccall.add_argument (get_variable_cexpression (detail_temp_decl.name));
701                         }
702                         // fifth argument: closure
703                         ccall.add_argument (new CCodeConstant ("NULL"));
704                 }
705
706                 // third resp. sixth argument: handler
707                 ccall.add_argument (new CCodeCastExpression (get_cvalue (handler), "GCallback"));
708
709                 if (m != null && m.closure) {
710                         // g_signal_connect_data
711
712                         // fourth argument: user_data
713                         CCodeExpression handler_destroy_notify;
714                         ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
715
716                         // fifth argument: destroy_notify
717                         ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
718
719                         // sixth argument: connect_flags
720                         if (!after)
721                                 ccall.add_argument (new CCodeConstant ("0"));
722                         else
723                                 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
724                 } else if (m != null && m.binding == MemberBinding.INSTANCE) {
725                         // g_signal_connect_object or g_signal_handlers_disconnect_matched
726                         // or dynamic_signal_connect or dynamic_signal_disconnect
727
728                         // fourth resp. seventh argument: object/user_data
729                         if (handler is MemberAccess) {
730                                 var right_ma = (MemberAccess) handler;
731                                 if (right_ma.inner != null) {
732                                         ccall.add_argument (get_cvalue (right_ma.inner));
733                                 } else {
734                                         ccall.add_argument (get_this_cexpression ());
735                                 }
736                         } else if (handler is LambdaExpression) {
737                                 ccall.add_argument (get_this_cexpression ());
738                         }
739                         if (!disconnect && in_gobject_instance (m)) {
740                                 // g_signal_connect_object
741
742                                 // fifth argument: connect_flags
743                                 if (!after)
744                                         ccall.add_argument (new CCodeConstant ("0"));
745                                 else
746                                         ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
747                         }
748                 } else if (dt != null && dt.delegate_symbol.has_target) {
749                         // fourth argument: user_data
750                         CCodeExpression handler_destroy_notify;
751                         ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
752                         if (!disconnect && dt.value_owned) {
753                                 // fifth argument: destroy_notify
754                                 //FIXME handler_destroy_notify is NULL
755                                 ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
756                                 // sixth argument: connect_flags
757                                 if (!after)
758                                         ccall.add_argument (new CCodeConstant ("0"));
759                                 else
760                                         ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
761                         }
762                 } else {
763                         // g_signal_connect or g_signal_connect_after or g_signal_handlers_disconnect_matched
764                         // or dynamic_signal_connect or dynamic_signal_disconnect
765
766                         // fourth resp. seventh argument: user_data
767                         ccall.add_argument (new CCodeConstant ("NULL"));
768                 }
769
770                 if (disconnect || expr.parent_node is ExpressionStatement) {
771                         ccode.add_expression (ccall);
772                         return null;
773                 } else {
774                         var temp_var = get_temp_variable (ulong_type);
775                         var temp_ref = get_variable_cexpression (temp_var.name);
776
777                         emit_temp_var (temp_var);
778
779                         ccode.add_assignment (temp_ref, ccall);
780
781                         return temp_ref;
782                 }
783         }
784 }
785