Release 0.41.92
[vala-gnome.git] / codegen / valagsignalmodule.vala
blobf9a3b9faff9a392d1b46741c1aa02dce7474caad
1 /* valagsignalmodule.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 public class Vala.GSignalModule : GObjectModule {
27 string get_marshaller_function (List<Parameter> params, DataType return_type, string? prefix = null) {
28 var signature = get_marshaller_signature (params, return_type);
29 string ret;
31 if (prefix == null) {
32 if (predefined_marshal_set.contains (signature)) {
33 prefix = "g_cclosure_marshal";
34 } else {
35 prefix = "g_cclosure_user_marshal";
39 ret = "%s_%s_".printf (prefix, get_ccode_marshaller_type_name (return_type));
41 if (params == null || params.size == 0) {
42 ret = ret + "_VOID";
43 } else {
44 foreach (Parameter p in params) {
45 ret = "%s_%s".printf (ret, get_ccode_marshaller_type_name (p).replace (",", "_"));
49 return ret;
52 private string? get_value_type_name_from_type_reference (DataType t) {
53 if (t is PointerType || t is GenericType) {
54 return "gpointer";
55 } else if (t is VoidType) {
56 return "void";
57 } else if (get_ccode_type_id (t) == get_ccode_type_id (string_type)) {
58 return "const char*";
59 } else if (t.data_type is Class || t.data_type is Interface) {
60 return "gpointer";
61 } else if (t is ValueType && t.nullable) {
62 return "gpointer";
63 } else if (t.data_type is Struct) {
64 var st = (Struct) t.data_type;
65 if (st.is_simple_type ()) {
66 return get_ccode_name (t.data_type);
67 } else {
68 return "gpointer";
70 } else if (t.data_type is Enum) {
71 return "gint";
72 } else if (t is ArrayType) {
73 return "gpointer";
74 } else if (t is ErrorType) {
75 return "gpointer";
78 return null;
81 private string? get_value_type_name_from_parameter (Parameter p) {
82 if (p.direction != ParameterDirection.IN) {
83 return "gpointer";
84 } else {
85 return get_value_type_name_from_type_reference (p.variable_type);
89 private string get_marshaller_signature (List<Parameter> params, DataType return_type) {
90 string signature;
92 signature = "%s:".printf (get_ccode_marshaller_type_name (return_type));
93 if (params == null || params.size == 0) {
94 signature = signature + "VOID";
95 } else {
96 bool first = true;
97 foreach (Parameter p in params) {
98 if (first) {
99 signature = signature + get_ccode_marshaller_type_name (p);
100 first = false;
101 } else {
102 signature = "%s,%s".printf (signature, get_ccode_marshaller_type_name (p));
107 return signature;
110 private CCodeExpression? get_signal_name_cexpression (Signal sig, Expression? detail_expr, CodeNode node) {
111 if (detail_expr == null) {
112 return get_signal_canonical_constant (sig);
115 if (detail_expr.value_type is NullType || !detail_expr.value_type.compatible (string_type)) {
116 node.error = true;
117 Report.error (detail_expr.source_reference, "only string details are supported");
118 return null;
121 if (detail_expr is StringLiteral) {
122 return get_signal_canonical_constant (sig, ((StringLiteral) detail_expr).eval ());
125 var detail_value = create_temp_value (detail_expr.value_type, false, node, true);
126 temp_ref_values.insert (0, detail_value);
128 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strconcat"));
129 ccall.add_argument (get_signal_canonical_constant (sig, ""));
130 ccall.add_argument (get_cvalue (detail_expr));
131 ccall.add_argument (new CCodeConstant ("NULL"));
133 ccode.add_assignment (get_cvalue_ (detail_value), ccall);
134 return get_cvalue_ (detail_value);
137 private CCodeExpression get_signal_id_cexpression (Signal sig) {
138 var cl = (TypeSymbol) sig.parent_symbol;
139 var signal_array = new CCodeIdentifier ("%s_signals".printf (get_ccode_lower_case_name (cl)));
140 var signal_enum_value = new CCodeIdentifier ("%s_%s_SIGNAL".printf (get_ccode_upper_case_name (cl), get_ccode_upper_case_name (sig)));
142 return new CCodeElementAccess (signal_array, signal_enum_value);
145 private CCodeExpression? get_detail_cexpression (Expression detail_expr, CodeNode node) {
146 if (detail_expr.value_type is NullType || !detail_expr.value_type.compatible (string_type)) {
147 node.error = true;
148 Report.error (detail_expr.source_reference, "only string details are supported");
149 return null;
152 var detail_cexpr = get_cvalue (detail_expr);
153 CCodeFunctionCall detail_ccall;
154 if (is_constant_ccode_expression (detail_cexpr)) {
155 detail_ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string"));
156 } else {
157 detail_ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_string"));
159 detail_ccall.add_argument (detail_cexpr);
161 return detail_ccall;
164 public override void visit_signal (Signal sig) {
165 // parent_symbol may be null for dynamic signals
167 var cl = sig.parent_symbol as Class;
168 if (cl != null && cl.is_compact) {
169 sig.error = true;
170 Report.error (sig.source_reference, "Signals are not supported in compact classes");
171 return;
174 if (cl != null) {
175 foreach (DataType base_type in cl.get_base_types ()) {
176 if (SemanticAnalyzer.symbol_lookup_inherited (base_type.data_type, sig.name) is Signal) {
177 sig.error = true;
178 Report.error (sig.source_reference, "Signals with the same name as a signal in a base type are not supported");
179 return;
184 if (signal_enum != null && sig.parent_symbol is TypeSymbol) {
185 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))));
188 sig.accept_children (this);
190 // declare parameter type
191 foreach (Parameter p in sig.get_parameters ()) {
192 generate_parameter (p, cfile, new HashMap<int,CCodeParameter> (), null);
195 generate_marshaller (sig.get_parameters (), sig.return_type);
198 void generate_marshaller (List<Parameter> params, DataType return_type) {
199 string signature;
200 int n_params, i;
202 /* check whether a signal with the same signature already exists for this source file (or predefined) */
203 signature = get_marshaller_signature (params, return_type);
204 if (predefined_marshal_set.contains (signature) || user_marshal_set.contains (signature)) {
205 return;
208 var signal_marshaller = new CCodeFunction (get_marshaller_function (params, return_type, null), "void");
209 signal_marshaller.modifiers = CCodeModifiers.STATIC;
211 signal_marshaller.add_parameter (new CCodeParameter ("closure", "GClosure *"));
212 signal_marshaller.add_parameter (new CCodeParameter ("return_value", "GValue *"));
213 signal_marshaller.add_parameter (new CCodeParameter ("n_param_values", "guint"));
214 signal_marshaller.add_parameter (new CCodeParameter ("param_values", "const GValue *"));
215 signal_marshaller.add_parameter (new CCodeParameter ("invocation_hint", "gpointer"));
216 signal_marshaller.add_parameter (new CCodeParameter ("marshal_data", "gpointer"));
218 push_function (signal_marshaller);
220 var callback_decl = new CCodeFunctionDeclarator (get_marshaller_function (params, return_type, "GMarshalFunc"));
221 callback_decl.add_parameter (new CCodeParameter ("data1", "gpointer"));
222 n_params = 1;
223 foreach (Parameter p in params) {
224 callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), get_value_type_name_from_parameter (p)));
225 n_params++;
226 if (p.variable_type.is_array ()) {
227 for (var j = 0; j < ((ArrayType) p.variable_type).rank; j++) {
228 callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), "gint"));
229 n_params++;
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));
236 ccode.add_declaration (get_marshaller_function (params, return_type, "GMarshalFunc"), new CCodeVariableDeclarator ("callback"), CCodeModifiers.REGISTER);
238 ccode.add_declaration ("GCClosure *", new CCodeVariableDeclarator ("cc", new CCodeCastExpression (new CCodeIdentifier ("closure"), "GCClosure *")), CCodeModifiers.REGISTER);
240 ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("data1"), CCodeModifiers.REGISTER);
241 ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("data2"), CCodeModifiers.REGISTER);
243 CCodeFunctionCall fc;
245 if (return_type.data_type != null || return_type.is_array ()) {
246 ccode.add_declaration (get_value_type_name_from_type_reference (return_type), new CCodeVariableDeclarator ("v_return"));
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);
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);
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 ();
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 (params, return_type, "GMarshalFunc"));
270 ccode.add_assignment (new CCodeIdentifier ("callback"), c_assign_rhs);
272 fc = new CCodeFunctionCall (new CCodeIdentifier ("callback"));
273 fc.add_argument (new CCodeIdentifier ("data1"));
274 i = 1;
275 foreach (Parameter p in params) {
276 string get_value_function;
277 bool is_array = p.variable_type.is_array ();
278 if (p.direction != ParameterDirection.IN) {
279 get_value_function = "g_value_get_pointer";
280 } else if (is_array) {
281 if (((ArrayType) p.variable_type).element_type.data_type == string_type.data_type) {
282 get_value_function = "g_value_get_boxed";
283 } else {
284 get_value_function = "g_value_get_pointer";
286 } else if (p.variable_type is PointerType || p.variable_type is GenericType) {
287 get_value_function = "g_value_get_pointer";
288 } else if (p.variable_type is ErrorType) {
289 get_value_function = "g_value_get_pointer";
290 } else if (p.variable_type is ValueType && p.variable_type.nullable) {
291 get_value_function = "g_value_get_pointer";
292 } else {
293 get_value_function = get_ccode_get_value_function (p.variable_type.data_type);
295 var inner_fc = new CCodeFunctionCall (new CCodeIdentifier (get_value_function));
296 inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
297 fc.add_argument (inner_fc);
298 i++;
299 if (is_array) {
300 for (var j = 0; j < ((ArrayType) p.variable_type).rank; j++) {
301 inner_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_int"));
302 inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
303 fc.add_argument (inner_fc);
304 i++;
308 fc.add_argument (new CCodeIdentifier ("data2"));
310 if (return_type.data_type != null || return_type.is_array ()) {
311 ccode.add_assignment (new CCodeIdentifier ("v_return"), fc);
313 CCodeFunctionCall set_fc;
314 if (return_type.is_array ()) {
315 if (((ArrayType) return_type).element_type.data_type == string_type.data_type) {
316 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_boxed"));
317 } else {
318 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
320 } else if (return_type is GenericType) {
321 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
322 } else if (return_type is ErrorType) {
323 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
324 } else if (return_type.data_type == string_type.data_type) {
325 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_string"));
326 } else if (return_type.data_type is Class || return_type.data_type is Interface) {
327 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_object"));
328 } else if (return_type is ValueType && return_type.nullable) {
329 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
330 } else {
331 set_fc = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_set_value_function (return_type.data_type)));
333 set_fc.add_argument (new CCodeIdentifier ("return_value"));
334 set_fc.add_argument (new CCodeIdentifier ("v_return"));
336 ccode.add_expression (set_fc);
337 } else {
338 ccode.add_expression (fc);
341 pop_function ();
343 cfile.add_function_declaration (signal_marshaller);
344 cfile.add_function (signal_marshaller);
345 user_marshal_set.add (signature);
348 public override CCodeExpression get_signal_creation (Signal sig, TypeSymbol type) {
349 var csignew = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_new"));
350 csignew.add_argument (new CCodeConstant ("\"%s\"".printf (get_ccode_name (sig))));
351 csignew.add_argument (new CCodeIdentifier (get_ccode_type_id (type)));
352 string[] flags = new string[0];
353 var run_type = sig.get_attribute_string ("Signal", "run");
354 if (run_type == "first") {
355 flags += "G_SIGNAL_RUN_FIRST";
356 } else if (run_type == "cleanup") {
357 flags += "G_SIGNAL_RUN_CLEANUP";
358 } else {
359 flags += "G_SIGNAL_RUN_LAST";
361 if (sig.get_attribute_bool ("Signal", "detailed")) {
362 flags += "G_SIGNAL_DETAILED";
365 if (sig.get_attribute_bool ("Signal", "no_recurse")) {
366 flags += "G_SIGNAL_NO_RECURSE";
369 if (sig.get_attribute_bool ("Signal", "action")) {
370 flags += "G_SIGNAL_ACTION";
373 if (sig.get_attribute_bool ("Signal", "no_hooks")) {
374 flags += "G_SIGNAL_NO_HOOKS";
377 if (sig.version.deprecated) {
378 flags += "G_SIGNAL_DEPRECATED";
381 csignew.add_argument (new CCodeConstant (string.joinv (" | ", flags)));
383 if (sig.default_handler == null) {
384 csignew.add_argument (new CCodeConstant ("0"));
385 } else {
386 var struct_offset = new CCodeFunctionCall (new CCodeIdentifier ("G_STRUCT_OFFSET"));
387 if (type is Class) {
388 struct_offset.add_argument (new CCodeIdentifier ("%sClass".printf (get_ccode_name (type))));
389 } else {
390 // interface
391 struct_offset.add_argument (new CCodeIdentifier (get_ccode_type_name ((Interface) type)));
393 struct_offset.add_argument (new CCodeIdentifier (get_ccode_vfunc_name (sig.default_handler)));
394 csignew.add_argument (struct_offset);
396 csignew.add_argument (new CCodeConstant ("NULL"));
397 csignew.add_argument (new CCodeConstant ("NULL"));
399 string marshaller = get_marshaller_function (sig.get_parameters (), sig.return_type);
401 var marshal_arg = new CCodeIdentifier (marshaller);
402 csignew.add_argument (marshal_arg);
404 var params = sig.get_parameters ();
405 if (sig.return_type is PointerType || sig.return_type is GenericType) {
406 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
407 } else if (sig.return_type is ErrorType) {
408 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
409 } else if (sig.return_type is ValueType && sig.return_type.nullable) {
410 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
411 } else if (sig.return_type.data_type == null) {
412 csignew.add_argument (new CCodeConstant ("G_TYPE_NONE"));
413 } else {
414 csignew.add_argument (new CCodeConstant (get_ccode_type_id (sig.return_type.data_type)));
417 int params_len = 0;
418 foreach (Parameter param in params) {
419 params_len++;
420 if (param.variable_type.is_array ()) {
421 params_len += ((ArrayType) param.variable_type).rank;
425 csignew.add_argument (new CCodeConstant ("%d".printf (params_len)));
426 foreach (Parameter param in params) {
427 if (param.variable_type.is_array ()) {
428 if (((ArrayType) param.variable_type).element_type.data_type == string_type.data_type) {
429 csignew.add_argument (new CCodeConstant ("G_TYPE_STRV"));
430 } else {
431 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
433 for (var i = 0; i < ((ArrayType) param.variable_type).rank; i++) {
434 csignew.add_argument (new CCodeConstant ("G_TYPE_INT"));
436 } else if (param.variable_type is PointerType || param.variable_type is GenericType || param.direction != ParameterDirection.IN) {
437 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
438 } else if (param.variable_type is ErrorType) {
439 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
440 } else if (param.variable_type is ValueType && param.variable_type.nullable) {
441 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
442 } else {
443 csignew.add_argument (new CCodeConstant (get_ccode_type_id (param.variable_type.data_type)));
447 marshal_arg.name = marshaller;
449 return new CCodeAssignment (get_signal_id_cexpression (sig), csignew);
452 public override void visit_element_access (ElementAccess expr) {
453 if (expr.container is MemberAccess && expr.container.symbol_reference is Signal) {
454 if (expr.parent_node is MethodCall) {
455 // detailed signal emission
456 var sig = (Signal) expr.symbol_reference;
457 var ma = (MemberAccess) expr.container;
459 var detail_expr = expr.get_indices ().get (0);
461 CCodeFunctionCall ccall;
462 if (!sig.external_package && expr.source_reference.file == sig.source_reference.file) {
463 var detail_cexpr = get_detail_cexpression (detail_expr, expr);
465 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit"));
466 ccall.add_argument (get_cvalue (ma.inner));
467 ccall.add_argument (get_signal_id_cexpression (sig));
468 if (detail_cexpr != null) {
469 ccall.add_argument (detail_cexpr);
471 } else {
472 var signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
474 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit_by_name"));
475 ccall.add_argument (get_cvalue (ma.inner));
476 if (signal_name_cexpr != null) {
477 ccall.add_argument (signal_name_cexpr);
481 set_cvalue (expr, ccall);
482 } else {
483 // signal connect or disconnect
485 } else {
486 base.visit_element_access (expr);
490 bool in_gobject_instance (Method m) {
491 bool result = false;
492 if (m.binding == MemberBinding.INSTANCE) {
493 result = m.this_parameter.variable_type.data_type.is_subtype_of (gobject_type);
495 return result;
498 void emit_signal_assignment (Assignment assignment) {
499 var sig = (Signal) assignment.left.symbol_reference;
501 bool disconnect = false;
503 if (assignment.operator == AssignmentOperator.ADD) {
504 // connect
505 } else if (assignment.operator == AssignmentOperator.SUB) {
506 // disconnect
507 disconnect = true;
508 } else {
509 assignment.error = true;
510 Report.error (assignment.source_reference, "Specified compound assignment type for signals not supported.");
511 return;
514 connect_signal (sig, assignment.left, assignment.right, disconnect, false, assignment);
517 public override void visit_assignment (Assignment assignment) {
518 if (assignment.left.symbol_reference is Signal) {
519 if (assignment.left.error || assignment.right.error) {
520 assignment.error = true;
521 return;
524 emit_signal_assignment (assignment);
525 } else {
526 base.visit_assignment (assignment);
530 public override void visit_member_access (MemberAccess expr) {
531 if (expr.symbol_reference is Signal) {
532 CCodeExpression pub_inst = null;
534 if (expr.inner != null) {
535 pub_inst = get_cvalue (expr.inner);
538 var sig = (Signal) expr.symbol_reference;
539 var cl = (TypeSymbol) sig.parent_symbol;
541 if (expr.inner is BaseAccess && sig.is_virtual) {
542 var m = sig.default_handler;
543 var base_class = (Class) m.parent_symbol;
544 var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (get_ccode_upper_case_name (base_class, null))));
545 vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (get_ccode_lower_case_name (current_class))));
547 set_cvalue (expr, new CCodeMemberAccess.pointer (vcast, m.name));
548 return;
551 if (!sig.external_package && expr.source_reference.file == sig.source_reference.file) {
552 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit"));
553 ccall.add_argument (pub_inst);
554 ccall.add_argument (get_signal_id_cexpression (sig));
555 ccall.add_argument (new CCodeConstant ("0"));
557 set_cvalue (expr, ccall);
558 } else if (get_signal_has_emitter (sig)) {
559 string emitter_func;
560 if (sig.emitter != null) {
561 if (!sig.external_package && expr.source_reference.file != sig.source_reference.file) {
562 generate_method_declaration (sig.emitter, cfile);
564 emitter_func = get_ccode_lower_case_name (sig.emitter);
565 } else {
566 emitter_func = "%s_%s".printf (get_ccode_lower_case_name (cl), get_ccode_lower_case_name (sig));
568 var ccall = new CCodeFunctionCall (new CCodeIdentifier (emitter_func));
570 ccall.add_argument (pub_inst);
571 set_cvalue (expr, ccall);
572 } else {
573 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit_by_name"));
574 ccall.add_argument (pub_inst);
576 ccall.add_argument (get_signal_canonical_constant (sig));
578 set_cvalue (expr, ccall);
580 } else {
581 base.visit_member_access (expr);
585 public override void visit_method_call (MethodCall expr) {
586 var method_type = expr.call.value_type as MethodType;
588 if (method_type == null || !(method_type.method_symbol.parent_symbol is Signal)) {
589 // no signal connect/disconnect call
590 base.visit_method_call (expr);
591 return;
594 var sig = (Signal) method_type.method_symbol.parent_symbol;
595 var signal_access = ((MemberAccess) expr.call).inner;
596 var handler = expr.get_argument_list ().get (0);
598 bool disconnect = (method_type.method_symbol.name == "disconnect");
599 bool after = (method_type.method_symbol.name == "connect_after");
601 var cexpr = connect_signal (sig, signal_access, handler, disconnect, after, expr);
602 set_cvalue (expr, cexpr);
605 CCodeExpression? connect_signal (Signal sig, Expression signal_access, Expression handler, bool disconnect, bool after, CodeNode expr) {
606 string connect_func;
608 DelegateType? dt = null;
609 var p = handler.symbol_reference as Parameter;
610 if (p != null) {
611 dt = p.variable_type as DelegateType;
612 if (dt != null && !context.experimental) {
613 Report.warning (dt.source_reference, "Connecting delegates to signals is experimental");
616 var m = handler.symbol_reference as Method;
618 if (!disconnect) {
619 // connect
620 if (sig is DynamicSignal) {
621 if (!after)
622 connect_func = get_dynamic_signal_connect_wrapper_name ((DynamicSignal) sig);
623 else
624 connect_func = get_dynamic_signal_connect_after_wrapper_name ((DynamicSignal) sig);
625 } else {
626 if ((m != null && m.closure) || (dt != null && dt.value_owned)) {
627 connect_func = "g_signal_connect_data";
628 } else if (m != null && in_gobject_instance (m)) {
629 connect_func = "g_signal_connect_object";
630 } else if (!after) {
631 connect_func = "g_signal_connect";
632 } else
633 connect_func = "g_signal_connect_after";
635 } else {
636 // disconnect
637 if (handler is LambdaExpression) {
638 Report.error (handler.source_reference, "Cannot disconnect lambda expression from signal. Use Object.disconnect.");
640 if (sig is DynamicSignal) {
641 connect_func = get_dynamic_signal_disconnect_wrapper_name ((DynamicSignal) sig);
642 } else {
643 connect_func = "g_signal_handlers_disconnect_matched";
647 var ccall = new CCodeFunctionCall (new CCodeIdentifier (connect_func));
649 CCodeExpression signal_name_cexpr = null;
651 // first argument: instance of sender
652 MemberAccess ma;
653 if (signal_access is ElementAccess) {
654 var ea = (ElementAccess) signal_access;
655 ma = (MemberAccess) ea.container;
656 var detail_expr = ea.get_indices ().get (0);
657 signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
658 if (signal_name_cexpr == null) {
659 return null;
661 } else {
662 ma = (MemberAccess) signal_access;
663 signal_name_cexpr = get_signal_name_cexpression (sig, null, expr);
665 if (ma.inner != null) {
666 ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
667 } else {
668 ccall.add_argument (get_result_cexpression ("self"));
671 if (sig is DynamicSignal) {
672 // dynamic_signal_connect or dynamic_signal_disconnect
674 // second argument: signal name
675 ccall.add_argument (new CCodeConstant ("\"%s\"".printf (get_ccode_name (sig))));
676 } else if (!disconnect) {
677 // g_signal_connect_object or g_signal_connect
679 // second argument: signal name
680 ccall.add_argument (signal_name_cexpr);
681 } else {
682 // g_signal_handlers_disconnect_matched
684 // second argument: mask
685 if (!(signal_access is ElementAccess)) {
686 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
687 } else {
688 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
691 // get signal id
692 var temp_decl = get_temp_variable (uint_type);
693 emit_temp_var (temp_decl);
694 var parse_call = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_parse_name"));
695 parse_call.add_argument (signal_name_cexpr);
696 var decl_type = (TypeSymbol) sig.parent_symbol;
697 parse_call.add_argument (new CCodeIdentifier (get_ccode_type_id (decl_type)));
698 parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_decl.name)));
699 LocalVariable? detail_temp_decl = null;
700 if (!(signal_access is ElementAccess)) {
701 parse_call.add_argument (new CCodeConstant ("NULL"));
702 parse_call.add_argument (new CCodeConstant ("FALSE"));
703 } else {
704 detail_temp_decl = get_temp_variable (gquark_type);
705 emit_temp_var (detail_temp_decl);
706 parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (detail_temp_decl.name)));
707 parse_call.add_argument (new CCodeConstant ("TRUE"));
709 ccode.add_expression (parse_call);
711 // third argument: signal_id
712 ccall.add_argument (get_variable_cexpression (temp_decl.name));
714 // fourth argument: detail
715 if (detail_temp_decl == null) {
716 ccall.add_argument (new CCodeConstant ("0"));
717 } else {
718 ccall.add_argument (get_variable_cexpression (detail_temp_decl.name));
720 // fifth argument: closure
721 ccall.add_argument (new CCodeConstant ("NULL"));
724 // third resp. sixth argument: handler
725 ccall.add_argument (new CCodeCastExpression (get_cvalue (handler), "GCallback"));
727 if (m != null && m.closure) {
728 // g_signal_connect_data
730 // fourth argument: user_data
731 CCodeExpression handler_destroy_notify;
732 ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
734 // fifth argument: destroy_notify
735 ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
737 // sixth argument: connect_flags
738 if (!after)
739 ccall.add_argument (new CCodeConstant ("0"));
740 else
741 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
742 } else if (m != null && m.binding == MemberBinding.INSTANCE) {
743 // g_signal_connect_object or g_signal_handlers_disconnect_matched
744 // or dynamic_signal_connect or dynamic_signal_disconnect
746 // fourth resp. seventh argument: object/user_data
747 if (handler is MemberAccess) {
748 var right_ma = (MemberAccess) handler;
749 if (right_ma.inner != null) {
750 ccall.add_argument (get_cvalue (right_ma.inner));
751 } else {
752 ccall.add_argument (get_result_cexpression ("self"));
754 } else if (handler is LambdaExpression) {
755 ccall.add_argument (get_result_cexpression ("self"));
757 if (!disconnect && !(sig is DynamicSignal)
758 && in_gobject_instance (m)) {
759 // g_signal_connect_object
761 // fifth argument: connect_flags
762 if (!after)
763 ccall.add_argument (new CCodeConstant ("0"));
764 else
765 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
767 } else if (dt != null && dt.delegate_symbol.has_target) {
768 // fourth argument: user_data
769 CCodeExpression handler_destroy_notify;
770 ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
771 if (!disconnect && dt.value_owned) {
772 // fifth argument: destroy_notify
773 //FIXME handler_destroy_notify is NULL
774 ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
775 // sixth argument: connect_flags
776 if (!after)
777 ccall.add_argument (new CCodeConstant ("0"));
778 else
779 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
781 } else {
782 // g_signal_connect or g_signal_connect_after or g_signal_handlers_disconnect_matched
783 // or dynamic_signal_connect or dynamic_signal_disconnect
785 // fourth resp. seventh argument: user_data
786 ccall.add_argument (new CCodeConstant ("NULL"));
789 if (disconnect || expr.parent_node is ExpressionStatement) {
790 ccode.add_expression (ccall);
791 return null;
792 } else {
793 var temp_var = get_temp_variable (ulong_type);
794 var temp_ref = get_variable_cexpression (temp_var.name);
796 emit_temp_var (temp_var);
798 ccode.add_assignment (temp_ref, ccall);
800 return temp_ref;