gtk+-4.0: Update to 3.93.0+6aeae2c8
[vala-gnome.git] / codegen / valagsignalmodule.vala
blob15a18cb7b0a20c78e8adf741cb9387938054e677
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 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))));
186 sig.accept_children (this);
188 // declare parameter type
189 foreach (Parameter p in sig.get_parameters ()) {
190 generate_parameter (p, cfile, new HashMap<int,CCodeParameter> (), null);
193 generate_marshaller (sig.get_parameters (), sig.return_type);
196 void generate_marshaller (List<Parameter> params, DataType return_type) {
197 string signature;
198 int n_params, i;
200 /* check whether a signal with the same signature already exists for this source file (or predefined) */
201 signature = get_marshaller_signature (params, return_type);
202 if (predefined_marshal_set.contains (signature) || user_marshal_set.contains (signature)) {
203 return;
206 var signal_marshaller = new CCodeFunction (get_marshaller_function (params, return_type, null), "void");
207 signal_marshaller.modifiers = CCodeModifiers.STATIC;
209 signal_marshaller.add_parameter (new CCodeParameter ("closure", "GClosure *"));
210 signal_marshaller.add_parameter (new CCodeParameter ("return_value", "GValue *"));
211 signal_marshaller.add_parameter (new CCodeParameter ("n_param_values", "guint"));
212 signal_marshaller.add_parameter (new CCodeParameter ("param_values", "const GValue *"));
213 signal_marshaller.add_parameter (new CCodeParameter ("invocation_hint", "gpointer"));
214 signal_marshaller.add_parameter (new CCodeParameter ("marshal_data", "gpointer"));
216 push_function (signal_marshaller);
218 var callback_decl = new CCodeFunctionDeclarator (get_marshaller_function (params, return_type, "GMarshalFunc"));
219 callback_decl.add_parameter (new CCodeParameter ("data1", "gpointer"));
220 n_params = 1;
221 foreach (Parameter p in params) {
222 callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), get_value_type_name_from_parameter (p)));
223 n_params++;
224 if (p.variable_type.is_array ()) {
225 for (var j = 0; j < ((ArrayType) p.variable_type).rank; j++) {
226 callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), "gint"));
227 n_params++;
231 callback_decl.add_parameter (new CCodeParameter ("data2", "gpointer"));
232 ccode.add_statement (new CCodeTypeDefinition (get_value_type_name_from_type_reference (return_type), callback_decl));
234 ccode.add_declaration (get_marshaller_function (params, return_type, "GMarshalFunc"), new CCodeVariableDeclarator ("callback"), CCodeModifiers.REGISTER);
236 ccode.add_declaration ("GCClosure *", new CCodeVariableDeclarator ("cc", new CCodeCastExpression (new CCodeIdentifier ("closure"), "GCClosure *")), CCodeModifiers.REGISTER);
238 ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("data1"), CCodeModifiers.REGISTER);
239 ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("data2"), CCodeModifiers.REGISTER);
241 CCodeFunctionCall fc;
243 if (return_type.data_type != null || return_type.is_array ()) {
244 ccode.add_declaration (get_value_type_name_from_type_reference (return_type), new CCodeVariableDeclarator ("v_return"));
246 fc = new CCodeFunctionCall (new CCodeIdentifier ("g_return_if_fail"));
247 fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("return_value"), new CCodeConstant ("NULL")));
248 ccode.add_expression (fc);
251 fc = new CCodeFunctionCall (new CCodeIdentifier ("g_return_if_fail"));
252 fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("n_param_values"), new CCodeConstant (n_params.to_string())));
253 ccode.add_expression (fc);
255 var data = new CCodeMemberAccess (new CCodeIdentifier ("closure"), "data", true);
256 var param = new CCodeMemberAccess (new CCodeMemberAccess (new CCodeIdentifier ("param_values"), "data[0]", true), "v_pointer");
257 var cond = new CCodeFunctionCall (new CCodeConstant ("G_CCLOSURE_SWAP_DATA"));
258 cond.add_argument (new CCodeIdentifier ("closure"));
259 ccode.open_if (cond);
260 ccode.add_assignment (new CCodeIdentifier ("data1"), data);
261 ccode.add_assignment (new CCodeIdentifier ("data2"), param);
262 ccode.add_else ();
263 ccode.add_assignment (new CCodeIdentifier ("data1"), param);
264 ccode.add_assignment (new CCodeIdentifier ("data2"), data);
265 ccode.close ();
267 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"));
268 ccode.add_assignment (new CCodeIdentifier ("callback"), c_assign_rhs);
270 fc = new CCodeFunctionCall (new CCodeIdentifier ("callback"));
271 fc.add_argument (new CCodeIdentifier ("data1"));
272 i = 1;
273 foreach (Parameter p in params) {
274 string get_value_function;
275 bool is_array = p.variable_type.is_array ();
276 if (p.direction != ParameterDirection.IN) {
277 get_value_function = "g_value_get_pointer";
278 } else if (is_array) {
279 if (((ArrayType) p.variable_type).element_type.data_type == string_type.data_type) {
280 get_value_function = "g_value_get_boxed";
281 } else {
282 get_value_function = "g_value_get_pointer";
284 } else if (p.variable_type is PointerType || p.variable_type is GenericType) {
285 get_value_function = "g_value_get_pointer";
286 } else if (p.variable_type is ErrorType) {
287 get_value_function = "g_value_get_pointer";
288 } else if (p.variable_type is ValueType && p.variable_type.nullable) {
289 get_value_function = "g_value_get_pointer";
290 } else {
291 get_value_function = get_ccode_get_value_function (p.variable_type.data_type);
293 var inner_fc = new CCodeFunctionCall (new CCodeIdentifier (get_value_function));
294 inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
295 fc.add_argument (inner_fc);
296 i++;
297 if (is_array) {
298 for (var j = 0; j < ((ArrayType) p.variable_type).rank; j++) {
299 inner_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_int"));
300 inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
301 fc.add_argument (inner_fc);
302 i++;
306 fc.add_argument (new CCodeIdentifier ("data2"));
308 if (return_type.data_type != null || return_type.is_array ()) {
309 ccode.add_assignment (new CCodeIdentifier ("v_return"), fc);
311 CCodeFunctionCall set_fc;
312 if (return_type.is_array ()) {
313 if (((ArrayType) return_type).element_type.data_type == string_type.data_type) {
314 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_boxed"));
315 } else {
316 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
318 } else if (return_type is GenericType) {
319 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
320 } else if (return_type is ErrorType) {
321 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
322 } else if (return_type.data_type == string_type.data_type) {
323 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_string"));
324 } else if (return_type.data_type is Class || return_type.data_type is Interface) {
325 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_object"));
326 } else if (return_type is ValueType && return_type.nullable) {
327 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
328 } else {
329 set_fc = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_set_value_function (return_type.data_type)));
331 set_fc.add_argument (new CCodeIdentifier ("return_value"));
332 set_fc.add_argument (new CCodeIdentifier ("v_return"));
334 ccode.add_expression (set_fc);
335 } else {
336 ccode.add_expression (fc);
339 pop_function ();
341 cfile.add_function_declaration (signal_marshaller);
342 cfile.add_function (signal_marshaller);
343 user_marshal_set.add (signature);
346 public override CCodeExpression get_signal_creation (Signal sig, TypeSymbol type) {
347 var csignew = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_new"));
348 csignew.add_argument (new CCodeConstant ("\"%s\"".printf (get_ccode_name (sig))));
349 csignew.add_argument (new CCodeIdentifier (get_ccode_type_id (type)));
350 string[] flags = new string[0];
351 var run_type = sig.get_attribute_string ("Signal", "run");
352 if (run_type == "first") {
353 flags += "G_SIGNAL_RUN_FIRST";
354 } else if (run_type == "cleanup") {
355 flags += "G_SIGNAL_RUN_CLEANUP";
356 } else {
357 flags += "G_SIGNAL_RUN_LAST";
359 if (sig.get_attribute_bool ("Signal", "detailed")) {
360 flags += "G_SIGNAL_DETAILED";
363 if (sig.get_attribute_bool ("Signal", "no_recurse")) {
364 flags += "G_SIGNAL_NO_RECURSE";
367 if (sig.get_attribute_bool ("Signal", "action")) {
368 flags += "G_SIGNAL_ACTION";
371 if (sig.get_attribute_bool ("Signal", "no_hooks")) {
372 flags += "G_SIGNAL_NO_HOOKS";
375 if (sig.version.deprecated) {
376 flags += "G_SIGNAL_DEPRECATED";
379 csignew.add_argument (new CCodeConstant (string.joinv (" | ", flags)));
381 if (sig.default_handler == null) {
382 csignew.add_argument (new CCodeConstant ("0"));
383 } else {
384 var struct_offset = new CCodeFunctionCall (new CCodeIdentifier ("G_STRUCT_OFFSET"));
385 if (type is Class) {
386 struct_offset.add_argument (new CCodeIdentifier ("%sClass".printf (get_ccode_name (type))));
387 } else {
388 // interface
389 struct_offset.add_argument (new CCodeIdentifier (get_ccode_type_name ((Interface) type)));
391 struct_offset.add_argument (new CCodeIdentifier (get_ccode_vfunc_name (sig.default_handler)));
392 csignew.add_argument (struct_offset);
394 csignew.add_argument (new CCodeConstant ("NULL"));
395 csignew.add_argument (new CCodeConstant ("NULL"));
397 string marshaller = get_marshaller_function (sig.get_parameters (), sig.return_type);
399 var marshal_arg = new CCodeIdentifier (marshaller);
400 csignew.add_argument (marshal_arg);
402 var params = sig.get_parameters ();
403 if (sig.return_type is PointerType || sig.return_type is GenericType) {
404 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
405 } else if (sig.return_type is ErrorType) {
406 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
407 } else if (sig.return_type is ValueType && sig.return_type.nullable) {
408 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
409 } else if (sig.return_type.data_type == null) {
410 csignew.add_argument (new CCodeConstant ("G_TYPE_NONE"));
411 } else {
412 csignew.add_argument (new CCodeConstant (get_ccode_type_id (sig.return_type.data_type)));
415 int params_len = 0;
416 foreach (Parameter param in params) {
417 params_len++;
418 if (param.variable_type.is_array ()) {
419 params_len += ((ArrayType) param.variable_type).rank;
423 csignew.add_argument (new CCodeConstant ("%d".printf (params_len)));
424 foreach (Parameter param in params) {
425 if (param.variable_type.is_array ()) {
426 if (((ArrayType) param.variable_type).element_type.data_type == string_type.data_type) {
427 csignew.add_argument (new CCodeConstant ("G_TYPE_STRV"));
428 } else {
429 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
431 for (var i = 0; i < ((ArrayType) param.variable_type).rank; i++) {
432 csignew.add_argument (new CCodeConstant ("G_TYPE_INT"));
434 } else if (param.variable_type is PointerType || param.variable_type is GenericType || param.direction != ParameterDirection.IN) {
435 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
436 } else if (param.variable_type is ErrorType) {
437 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
438 } else if (param.variable_type is ValueType && param.variable_type.nullable) {
439 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
440 } else {
441 csignew.add_argument (new CCodeConstant (get_ccode_type_id (param.variable_type.data_type)));
445 marshal_arg.name = marshaller;
447 return new CCodeAssignment (get_signal_id_cexpression (sig), csignew);
450 public override void visit_element_access (ElementAccess expr) {
451 if (expr.container is MemberAccess && expr.container.symbol_reference is Signal) {
452 if (expr.parent_node is MethodCall) {
453 // detailed signal emission
454 var sig = (Signal) expr.symbol_reference;
455 var ma = (MemberAccess) expr.container;
457 var detail_expr = expr.get_indices ().get (0);
459 CCodeFunctionCall ccall;
460 if (!sig.external_package && expr.source_reference.file == sig.source_reference.file) {
461 var detail_cexpr = get_detail_cexpression (detail_expr, expr);
463 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit"));
464 ccall.add_argument (get_cvalue (ma.inner));
465 ccall.add_argument (get_signal_id_cexpression (sig));
466 if (detail_cexpr != null) {
467 ccall.add_argument (detail_cexpr);
469 } else {
470 var signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
472 ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit_by_name"));
473 ccall.add_argument (get_cvalue (ma.inner));
474 if (signal_name_cexpr != null) {
475 ccall.add_argument (signal_name_cexpr);
479 set_cvalue (expr, ccall);
480 } else {
481 // signal connect or disconnect
483 } else {
484 base.visit_element_access (expr);
488 bool in_gobject_instance (Method m) {
489 bool result = false;
490 if (m.binding == MemberBinding.INSTANCE) {
491 result = m.this_parameter.variable_type.data_type.is_subtype_of (gobject_type);
493 return result;
496 void emit_signal_assignment (Assignment assignment) {
497 var sig = (Signal) assignment.left.symbol_reference;
499 bool disconnect = false;
501 if (assignment.operator == AssignmentOperator.ADD) {
502 // connect
503 } else if (assignment.operator == AssignmentOperator.SUB) {
504 // disconnect
505 disconnect = true;
506 } else {
507 assignment.error = true;
508 Report.error (assignment.source_reference, "Specified compound assignment type for signals not supported.");
509 return;
512 connect_signal (sig, assignment.left, assignment.right, disconnect, false, assignment);
515 public override void visit_assignment (Assignment assignment) {
516 if (assignment.left.symbol_reference is Signal) {
517 if (assignment.left.error || assignment.right.error) {
518 assignment.error = true;
519 return;
522 emit_signal_assignment (assignment);
523 } else {
524 base.visit_assignment (assignment);
528 public override void visit_member_access (MemberAccess expr) {
529 if (expr.symbol_reference is Signal) {
530 CCodeExpression pub_inst = null;
532 if (expr.inner != null) {
533 pub_inst = get_cvalue (expr.inner);
536 var sig = (Signal) expr.symbol_reference;
537 var cl = (TypeSymbol) sig.parent_symbol;
539 if (expr.inner is BaseAccess && sig.is_virtual) {
540 var m = sig.default_handler;
541 var base_class = (Class) m.parent_symbol;
542 var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (get_ccode_upper_case_name (base_class, null))));
543 vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (get_ccode_lower_case_name (current_class))));
545 set_cvalue (expr, new CCodeMemberAccess.pointer (vcast, m.name));
546 return;
549 if (!sig.external_package && expr.source_reference.file == sig.source_reference.file) {
550 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit"));
551 ccall.add_argument (pub_inst);
552 ccall.add_argument (get_signal_id_cexpression (sig));
553 ccall.add_argument (new CCodeConstant ("0"));
555 set_cvalue (expr, ccall);
556 } else if (get_signal_has_emitter (sig)) {
557 string emitter_func;
558 if (sig.emitter != null) {
559 if (!sig.external_package && expr.source_reference.file != sig.source_reference.file) {
560 generate_method_declaration (sig.emitter, cfile);
562 emitter_func = get_ccode_lower_case_name (sig.emitter);
563 } else {
564 emitter_func = "%s_%s".printf (get_ccode_lower_case_name (cl), get_ccode_lower_case_name (sig));
566 var ccall = new CCodeFunctionCall (new CCodeIdentifier (emitter_func));
568 ccall.add_argument (pub_inst);
569 set_cvalue (expr, ccall);
570 } else {
571 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit_by_name"));
572 ccall.add_argument (pub_inst);
574 ccall.add_argument (get_signal_canonical_constant (sig));
576 set_cvalue (expr, ccall);
578 } else {
579 base.visit_member_access (expr);
583 public override void visit_method_call (MethodCall expr) {
584 var method_type = expr.call.value_type as MethodType;
586 if (method_type == null || !(method_type.method_symbol.parent_symbol is Signal)) {
587 // no signal connect/disconnect call
588 base.visit_method_call (expr);
589 return;
592 var sig = (Signal) method_type.method_symbol.parent_symbol;
593 var signal_access = ((MemberAccess) expr.call).inner;
594 var handler = expr.get_argument_list ().get (0);
596 bool disconnect = (method_type.method_symbol.name == "disconnect");
597 bool after = (method_type.method_symbol.name == "connect_after");
599 var cexpr = connect_signal (sig, signal_access, handler, disconnect, after, expr);
600 set_cvalue (expr, cexpr);
603 CCodeExpression? connect_signal (Signal sig, Expression signal_access, Expression handler, bool disconnect, bool after, CodeNode expr) {
604 string connect_func;
606 DelegateType? dt = null;
607 var p = handler.symbol_reference as Parameter;
608 if (p != null) {
609 dt = p.variable_type as DelegateType;
610 if (dt != null && !context.experimental) {
611 Report.warning (dt.source_reference, "Connecting delegates to signals is experimental");
614 var m = handler.symbol_reference as Method;
616 if (!disconnect) {
617 // connect
618 if (sig is DynamicSignal) {
619 if (!after)
620 connect_func = get_dynamic_signal_connect_wrapper_name ((DynamicSignal) sig);
621 else
622 connect_func = get_dynamic_signal_connect_after_wrapper_name ((DynamicSignal) sig);
623 } else {
624 if ((m != null && m.closure) || (dt != null && dt.value_owned)) {
625 connect_func = "g_signal_connect_data";
626 } else if (m != null && in_gobject_instance (m)) {
627 connect_func = "g_signal_connect_object";
628 } else if (!after) {
629 connect_func = "g_signal_connect";
630 } else
631 connect_func = "g_signal_connect_after";
633 } else {
634 // disconnect
635 if (handler is LambdaExpression) {
636 Report.error (handler.source_reference, "Cannot disconnect lambda expression from signal. Use Object.disconnect.");
638 if (sig is DynamicSignal) {
639 connect_func = get_dynamic_signal_disconnect_wrapper_name ((DynamicSignal) sig);
640 } else {
641 connect_func = "g_signal_handlers_disconnect_matched";
645 var ccall = new CCodeFunctionCall (new CCodeIdentifier (connect_func));
647 CCodeExpression signal_name_cexpr = null;
649 // first argument: instance of sender
650 MemberAccess ma;
651 if (signal_access is ElementAccess) {
652 var ea = (ElementAccess) signal_access;
653 ma = (MemberAccess) ea.container;
654 var detail_expr = ea.get_indices ().get (0);
655 signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
656 if (signal_name_cexpr == null) {
657 return null;
659 } else {
660 ma = (MemberAccess) signal_access;
661 signal_name_cexpr = get_signal_name_cexpression (sig, null, expr);
663 if (ma.inner != null) {
664 ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
665 } else {
666 ccall.add_argument (get_result_cexpression ("self"));
669 if (sig is DynamicSignal) {
670 // dynamic_signal_connect or dynamic_signal_disconnect
672 // second argument: signal name
673 ccall.add_argument (new CCodeConstant ("\"%s\"".printf (get_ccode_name (sig))));
674 } else if (!disconnect) {
675 // g_signal_connect_object or g_signal_connect
677 // second argument: signal name
678 ccall.add_argument (signal_name_cexpr);
679 } else {
680 // g_signal_handlers_disconnect_matched
682 // second argument: mask
683 if (!(signal_access is ElementAccess)) {
684 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
685 } else {
686 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
689 // get signal id
690 var temp_decl = get_temp_variable (uint_type);
691 emit_temp_var (temp_decl);
692 var parse_call = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_parse_name"));
693 parse_call.add_argument (signal_name_cexpr);
694 var decl_type = (TypeSymbol) sig.parent_symbol;
695 parse_call.add_argument (new CCodeIdentifier (get_ccode_type_id (decl_type)));
696 parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_decl.name)));
697 LocalVariable? detail_temp_decl = null;
698 if (!(signal_access is ElementAccess)) {
699 parse_call.add_argument (new CCodeConstant ("NULL"));
700 parse_call.add_argument (new CCodeConstant ("FALSE"));
701 } else {
702 detail_temp_decl = get_temp_variable (gquark_type);
703 emit_temp_var (detail_temp_decl);
704 parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (detail_temp_decl.name)));
705 parse_call.add_argument (new CCodeConstant ("TRUE"));
707 ccode.add_expression (parse_call);
709 // third argument: signal_id
710 ccall.add_argument (get_variable_cexpression (temp_decl.name));
712 // fourth argument: detail
713 if (detail_temp_decl == null) {
714 ccall.add_argument (new CCodeConstant ("0"));
715 } else {
716 ccall.add_argument (get_variable_cexpression (detail_temp_decl.name));
718 // fifth argument: closure
719 ccall.add_argument (new CCodeConstant ("NULL"));
722 // third resp. sixth argument: handler
723 ccall.add_argument (new CCodeCastExpression (get_cvalue (handler), "GCallback"));
725 if (m != null && m.closure) {
726 // g_signal_connect_data
728 // fourth argument: user_data
729 CCodeExpression handler_destroy_notify;
730 ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
732 // fifth argument: destroy_notify
733 ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
735 // sixth argument: connect_flags
736 if (!after)
737 ccall.add_argument (new CCodeConstant ("0"));
738 else
739 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
740 } else if (m != null && m.binding == MemberBinding.INSTANCE) {
741 // g_signal_connect_object or g_signal_handlers_disconnect_matched
742 // or dynamic_signal_connect or dynamic_signal_disconnect
744 // fourth resp. seventh argument: object/user_data
745 if (handler is MemberAccess) {
746 var right_ma = (MemberAccess) handler;
747 if (right_ma.inner != null) {
748 ccall.add_argument (get_cvalue (right_ma.inner));
749 } else {
750 ccall.add_argument (get_result_cexpression ("self"));
752 } else if (handler is LambdaExpression) {
753 ccall.add_argument (get_result_cexpression ("self"));
755 if (!disconnect && !(sig is DynamicSignal)
756 && in_gobject_instance (m)) {
757 // g_signal_connect_object
759 // fifth argument: connect_flags
760 if (!after)
761 ccall.add_argument (new CCodeConstant ("0"));
762 else
763 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
765 } else if (dt != null && dt.delegate_symbol.has_target) {
766 // fourth argument: user_data
767 CCodeExpression handler_destroy_notify;
768 ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
769 if (!disconnect && dt.value_owned) {
770 // fifth argument: destroy_notify
771 //FIXME handler_destroy_notify is NULL
772 ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
773 // sixth argument: connect_flags
774 if (!after)
775 ccall.add_argument (new CCodeConstant ("0"));
776 else
777 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
779 } else {
780 // g_signal_connect or g_signal_connect_after or g_signal_handlers_disconnect_matched
781 // or dynamic_signal_connect or dynamic_signal_disconnect
783 // fourth resp. seventh argument: user_data
784 ccall.add_argument (new CCodeConstant ("NULL"));
787 if (disconnect || expr.parent_node is ExpressionStatement) {
788 ccode.add_expression (ccall);
789 return null;
790 } else {
791 var temp_var = get_temp_variable (ulong_type);
792 var temp_ref = get_variable_cexpression (temp_var.name);
794 emit_temp_var (temp_var);
796 ccode.add_assignment (temp_ref, ccall);
798 return temp_ref;