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
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
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);
32 if (predefined_marshal_set.contains (signature)) {
33 prefix = "g_cclosure_marshal";
35 prefix = "g_cclosure_user_marshal";
39 ret = "%s_%s_".printf (prefix, get_ccode_marshaller_type_name (return_type));
41 foreach (Parameter p in params) {
42 ret = "%s_%s".printf (ret, get_ccode_marshaller_type_name (p).replace (",", "_"));
44 if (sig.return_type.is_real_non_null_struct_type ()) {
45 ret = ret + "_POINTER";
46 } else if (params.size == 0) {
53 private string? get_value_type_name_from_type_reference (DataType t) {
54 if (t is PointerType || t is GenericType) {
56 } else if (t is VoidType) {
58 } else if (get_ccode_type_id (t) == get_ccode_type_id (string_type)) {
60 } else if (t.type_symbol is Class || t.type_symbol is Interface) {
62 } else if (t is ValueType && t.nullable) {
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);
71 } else if (t.type_symbol is Enum) {
72 unowned Enum en = (Enum) t.type_symbol;
78 } else if (t is ArrayType) {
80 } else if (t is DelegateType) {
82 } else if (t is ErrorType) {
89 private string? get_value_type_name_from_parameter (Parameter p) {
90 if (p.direction != ParameterDirection.IN) {
93 return get_value_type_name_from_type_reference (p.variable_type);
97 private string get_marshaller_signature (Signal sig, List<Parameter> params, DataType return_type) {
100 signature = "%s:".printf (get_ccode_marshaller_type_name (return_type));
102 foreach (Parameter p in params) {
104 signature = signature + get_ccode_marshaller_type_name (p);
107 signature = "%s,%s".printf (signature, get_ccode_marshaller_type_name (p));
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";
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);
124 if (detail_expr is StringLiteral) {
125 return get_signal_canonical_constant (sig, ((StringLiteral) detail_expr).eval ());
128 var detail_value = create_temp_value (detail_expr.value_type, false, node, true);
129 temp_ref_values.insert (0, detail_value);
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"));
136 ccode.add_assignment (get_cvalue_ (detail_value), ccall);
137 return get_cvalue_ (detail_value);
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)));
145 return new CCodeElementAccess (signal_array, signal_enum_value);
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"));
154 detail_ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_string"));
156 detail_ccall.add_argument (detail_cexpr);
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))));
166 sig.accept_children (this);
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);
174 if (sig.return_type.is_real_non_null_struct_type ()) {
175 generate_marshaller (sig, params, new VoidType ());
177 generate_marshaller (sig, params, sig.return_type);
181 void generate_marshaller (Signal sig, List<Parameter> params, DataType return_type) {
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)) {
191 var signal_marshaller = new CCodeFunction (get_marshaller_function (sig, params, return_type, null), "void");
192 signal_marshaller.modifiers = CCodeModifiers.STATIC;
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"));
201 push_function (signal_marshaller);
203 var callback_decl = new CCodeFunctionDeclarator (get_marshaller_function (sig, params, return_type, "GMarshalFunc"));
204 callback_decl.add_parameter (new CCodeParameter ("data1", "gpointer"));
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)));
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));
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)));
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)));
228 if (sig.return_type.is_real_non_null_struct_type ()) {
229 callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), "gpointer"));
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 (sig, 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.type_symbol != null || return_type is ArrayType) {
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);
265 ccode.add_assignment (new CCodeIdentifier ("data1"), param);
266 ccode.add_assignment (new CCodeIdentifier ("data2"), data);
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);
272 fc = new CCodeFunctionCall (new CCodeIdentifier ("callback"));
273 fc.add_argument (new CCodeIdentifier ("data1"));
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"));
282 inner_fc = new CCodeFunctionCall (get_value_getter_function (p.variable_type));
284 inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
285 fc.add_argument (inner_fc);
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);
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);
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);
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);
319 fc.add_argument (new CCodeIdentifier ("data2"));
321 if (return_type.type_symbol != null || return_type is ArrayType) {
322 ccode.add_assignment (new CCodeIdentifier ("v_return"), fc);
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"));
329 set_fc = new CCodeFunctionCall (get_value_setter_function (return_type));
332 set_fc = new CCodeFunctionCall (get_value_taker_function (return_type));
334 set_fc.add_argument (new CCodeIdentifier ("return_value"));
335 set_fc.add_argument (new CCodeIdentifier ("v_return"));
337 ccode.add_expression (set_fc);
339 ccode.add_expression (fc);
344 cfile.add_function_declaration (signal_marshaller);
345 cfile.add_function (signal_marshaller);
346 user_marshal_set.add (signature);
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"));
354 csignew = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_new_class_handler"));
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";
365 flags += "G_SIGNAL_RUN_LAST";
367 if (sig.get_attribute_bool ("Signal", "detailed")) {
368 flags += "G_SIGNAL_DETAILED";
371 if (sig.get_attribute_bool ("Signal", "no_recurse")) {
372 flags += "G_SIGNAL_NO_RECURSE";
375 if (sig.get_attribute_bool ("Signal", "action")) {
376 flags += "G_SIGNAL_ACTION";
379 if (sig.get_attribute_bool ("Signal", "no_hooks")) {
380 flags += "G_SIGNAL_NO_HOOKS";
383 if (sig.version.deprecated) {
384 flags += "G_SIGNAL_DEPRECATED";
387 csignew.add_argument (new CCodeConstant (string.joinv (" | ", flags)));
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);
397 csignew.add_argument (new CCodeCastExpression (new CCodeIdentifier (get_ccode_real_name (sig.default_handler)), "GCallback"));
399 csignew.add_argument (new CCodeConstant ("NULL"));
400 csignew.add_argument (new CCodeConstant ("NULL"));
402 unowned List<Parameter> params = sig.get_parameters ();
404 if (sig.return_type.is_real_non_null_struct_type ()) {
405 marshaller = get_marshaller_function (sig, params, new VoidType ());
407 marshaller = get_marshaller_function (sig, params, sig.return_type);
410 var marshal_arg = new CCodeIdentifier (marshaller);
411 csignew.add_argument (marshal_arg);
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"));
424 csignew.add_argument (new CCodeConstant (get_ccode_type_id (sig.return_type.type_symbol)));
428 foreach (Parameter param in params) {
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) {
436 if (delegate_type.is_disposable ()) {
442 if (sig.return_type.is_real_non_null_struct_type ()) {
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"));
453 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
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));
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"));
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"));
476 csignew.add_argument (new CCodeConstant (get_ccode_type_id (param.variable_type.type_symbol)));
479 if (sig.return_type.is_real_non_null_struct_type ()) {
480 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
483 marshal_arg.name = marshaller;
485 return new CCodeAssignment (get_signal_id_cexpression (sig), csignew);
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);
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);
500 set_cvalue (expr, emit_signal (sig, ma, detail_expr));
502 // signal connect or disconnect
506 bool in_gobject_instance (Method m) {
508 if (m.binding == MemberBinding.INSTANCE) {
509 result = m.this_parameter.variable_type.type_symbol.is_subtype_of (gobject_type);
514 public override void visit_member_access (MemberAccess expr) {
515 if (!(expr.symbol_reference is Signal)) {
516 base.visit_member_access (expr);
520 unowned Signal sig = (Signal) expr.symbol_reference;
522 set_cvalue (expr, emit_signal (sig, expr));
525 CCodeExpression emit_signal (Signal sig, MemberAccess expr, Expression? detail_expr = null) {
526 CCodeExpression pub_inst = null;
528 if (expr.inner != null) {
529 pub_inst = get_cvalue (expr.inner);
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);
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"));
547 ccall.add_argument (get_detail_cexpression (detail_expr, expr));
550 } else if (get_ccode_has_emitter (sig)) {
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);
556 emitter_func = get_ccode_lower_case_name (sig.emitter);
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));
561 var ccall = new CCodeFunctionCall (new CCodeIdentifier (emitter_func));
562 ccall.add_argument (pub_inst);
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));
570 ccall.add_argument (get_signal_name_cexpression (sig, detail_expr, expr));
576 public override void visit_method_call (MethodCall expr) {
577 var method_type = expr.call.value_type as MethodType;
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);
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);
589 bool disconnect = (method_type.method_symbol.name == "disconnect");
590 bool after = (method_type.method_symbol.name == "connect_after");
592 var cexpr = connect_signal (sig, signal_access, handler, disconnect, after, expr);
593 set_cvalue (expr, cexpr);
596 CCodeExpression? connect_signal (Signal sig, Expression signal_access, Expression handler, bool disconnect, bool after, CodeNode expr) {
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");
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;
610 var m = handler.symbol_reference as Method;
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";
619 connect_func = "g_signal_connect";
621 connect_func = "g_signal_connect_after";
625 if (sig is DynamicSignal) {
626 connect_func = "VALA_UNSUPPORTED";
628 connect_func = "g_signal_handlers_disconnect_matched";
632 var ccall = new CCodeFunctionCall (new CCodeIdentifier (connect_func));
634 CCodeExpression signal_name_cexpr = null;
636 // first argument: instance of sender
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);
644 ma = (MemberAccess) signal_access;
645 signal_name_cexpr = get_signal_name_cexpression (sig, null, expr);
647 if (ma.inner != null) {
648 ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
650 ccall.add_argument (get_this_cexpression ());
653 if (sig is DynamicSignal) {
654 // dynamic_signal_connect or dynamic_signal_disconnect
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
661 // second argument: signal name
662 ccall.add_argument (signal_name_cexpr);
664 // g_signal_handlers_disconnect_matched
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"));
670 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
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"));
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"));
691 ccode.add_expression (parse_call);
693 // third argument: signal_id
694 ccall.add_argument (get_variable_cexpression (temp_decl.name));
696 // fourth argument: detail
697 if (detail_temp_decl == null) {
698 ccall.add_argument (new CCodeConstant ("0"));
700 ccall.add_argument (get_variable_cexpression (detail_temp_decl.name));
702 // fifth argument: closure
703 ccall.add_argument (new CCodeConstant ("NULL"));
706 // third resp. sixth argument: handler
707 ccall.add_argument (new CCodeCastExpression (get_cvalue (handler), "GCallback"));
709 if (m != null && m.closure) {
710 // g_signal_connect_data
712 // fourth argument: user_data
713 CCodeExpression handler_destroy_notify;
714 ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
716 // fifth argument: destroy_notify
717 ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
719 // sixth argument: connect_flags
721 ccall.add_argument (new CCodeConstant ("0"));
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
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));
734 ccall.add_argument (get_this_cexpression ());
736 } else if (handler is LambdaExpression) {
737 ccall.add_argument (get_this_cexpression ());
739 if (!disconnect && in_gobject_instance (m)) {
740 // g_signal_connect_object
742 // fifth argument: connect_flags
744 ccall.add_argument (new CCodeConstant ("0"));
746 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
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
758 ccall.add_argument (new CCodeConstant ("0"));
760 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
763 // g_signal_connect or g_signal_connect_after or g_signal_handlers_disconnect_matched
764 // or dynamic_signal_connect or dynamic_signal_disconnect
766 // fourth resp. seventh argument: user_data
767 ccall.add_argument (new CCodeConstant ("NULL"));
770 if (disconnect || expr.parent_node is ExpressionStatement) {
771 ccode.add_expression (ccall);
774 var temp_var = get_temp_variable (ulong_type);
775 var temp_ref = get_variable_cexpression (temp_var.name);
777 emit_temp_var (temp_var);
779 ccode.add_assignment (temp_ref, ccall);