codegen: Add get_field_cvalue and load_field
[vala-lang.git] / codegen / valagsignalmodule.vala
blobfd55878f842fd530db0d0c38a600300727f133e7
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 private string get_marshaller_type_name (DataType t, bool dbus = false) {
28 if (t is PointerType || t.type_parameter != null) {
29 return ("POINTER");
30 } else if (t is ErrorType) {
31 return ("POINTER");
32 } else if (t is ArrayType) {
33 if (dbus) {
34 return ("BOXED");
35 } else {
36 if (((ArrayType) t).element_type.data_type == string_type.data_type) {
37 return ("BOXED,INT");
38 } else {
39 return ("POINTER,INT");
42 } else if (t is VoidType) {
43 return ("VOID");
44 } else if (dbus && DBusModule.get_type_signature (t).has_prefix ("(")) {
45 return ("BOXED");
46 } else if (t.data_type is Enum) {
47 var en = (Enum) t.data_type;
48 if (dbus) {
49 if (en.is_flags) {
50 return ("UINT");
51 } else {
52 return ("INT");
54 } else {
55 return en.get_marshaller_type_name ();
57 } else {
58 return t.data_type.get_marshaller_type_name ();
62 private string get_marshaller_type_name_for_parameter (Parameter param, bool dbus = false) {
63 if (param.direction != ParameterDirection.IN) {
64 return ("POINTER");
65 } else {
66 return get_marshaller_type_name (param.variable_type, dbus);
70 public override string get_marshaller_function (List<Parameter> params, DataType return_type, string? prefix = null, bool dbus = false) {
71 var signature = get_marshaller_signature (params, return_type, dbus);
72 string ret;
74 if (prefix == null) {
75 if (predefined_marshal_set.contains (signature)) {
76 prefix = "g_cclosure_marshal";
77 } else {
78 prefix = "g_cclosure_user_marshal";
82 ret = "%s_%s_".printf (prefix, get_marshaller_type_name (return_type, dbus));
84 if (params == null || params.size == 0) {
85 ret = ret + "_VOID";
86 } else {
87 foreach (Parameter p in params) {
88 ret = "%s_%s".printf (ret, get_marshaller_type_name_for_parameter (p, dbus).replace (",", "_"));
92 return ret;
95 private string? get_value_type_name_from_type_reference (DataType t) {
96 if (t is PointerType || t.type_parameter != null) {
97 return "gpointer";
98 } else if (t is VoidType) {
99 return "void";
100 } else if (t.data_type == string_type.data_type) {
101 return "const char*";
102 } else if (t.data_type is Class || t.data_type is Interface) {
103 return "gpointer";
104 } else if (t.data_type is Struct) {
105 var st = (Struct) t.data_type;
106 if (st.is_simple_type ()) {
107 return t.data_type.get_cname ();
108 } else {
109 return "gpointer";
111 } else if (t.data_type is Enum) {
112 return "gint";
113 } else if (t is ArrayType) {
114 return "gpointer";
115 } else if (t is ErrorType) {
116 return "gpointer";
119 return null;
122 private string? get_value_type_name_from_parameter (Parameter p) {
123 if (p.direction != ParameterDirection.IN) {
124 return "gpointer";
125 } else {
126 return get_value_type_name_from_type_reference (p.variable_type);
130 private string get_marshaller_signature (List<Parameter> params, DataType return_type, bool dbus = false) {
131 string signature;
133 signature = "%s:".printf (get_marshaller_type_name (return_type, dbus));
134 if (params == null || params.size == 0) {
135 signature = signature + "VOID";
136 } else {
137 bool first = true;
138 foreach (Parameter p in params) {
139 if (first) {
140 signature = signature + get_marshaller_type_name_for_parameter (p, dbus);
141 first = false;
142 } else {
143 signature = "%s,%s".printf (signature, get_marshaller_type_name_for_parameter (p, dbus));
148 return signature;
151 private CCodeExpression? get_signal_name_cexpression (Signal sig, Expression? detail_expr, CodeNode node) {
152 if (detail_expr == null) {
153 return sig.get_canonical_cconstant ();
156 if (detail_expr.value_type is NullType || !detail_expr.value_type.compatible (string_type)) {
157 node.error = true;
158 Report.error (detail_expr.source_reference, "only string details are supported");
159 return null;
162 if (detail_expr is StringLiteral) {
163 return sig.get_canonical_cconstant (((StringLiteral) detail_expr).eval ());
166 var detail_decl = get_temp_variable (detail_expr.value_type, true, node);
167 emit_temp_var (detail_decl);
168 temp_ref_vars.insert (0, detail_decl);
170 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strconcat"));
171 ccall.add_argument (sig.get_canonical_cconstant (""));
172 ccall.add_argument (get_cvalue (detail_expr));
173 ccall.add_argument (new CCodeConstant ("NULL"));
175 var ccomma = new CCodeCommaExpression ();
176 ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (detail_decl.name), ccall));
177 ccomma.append_expression (get_variable_cexpression (detail_decl.name));
178 return ccomma;
181 public override void visit_signal (Signal sig) {
182 // parent_symbol may be null for dynamic signals
184 var cl = sig.parent_symbol as Class;
185 if (cl != null && cl.is_compact) {
186 sig.error = true;
187 Report.error (sig.source_reference, "Signals are not supported in compact classes");
188 return;
191 if (cl != null) {
192 foreach (DataType base_type in cl.get_base_types ()) {
193 if (SemanticAnalyzer.symbol_lookup_inherited (base_type.data_type, sig.name) is Signal) {
194 sig.error = true;
195 Report.error (sig.source_reference, "Signals with the same name as a signal in a base type are not supported");
196 return;
201 sig.accept_children (this);
203 // declare parameter type
204 foreach (Parameter p in sig.get_parameters ()) {
205 generate_parameter (p, cfile, new HashMap<int,CCodeParameter> (), null);
208 generate_marshaller (sig.get_parameters (), sig.return_type);
211 public override void generate_marshaller (List<Parameter> params, DataType return_type, bool dbus = false) {
212 string signature;
213 int n_params, i;
215 /* check whether a signal with the same signature already exists for this source file (or predefined) */
216 signature = get_marshaller_signature (params, return_type, dbus);
217 if (predefined_marshal_set.contains (signature) || user_marshal_set.contains (signature)) {
218 return;
221 var signal_marshaller = new CCodeFunction (get_marshaller_function (params, return_type, null, dbus), "void");
222 signal_marshaller.modifiers = CCodeModifiers.STATIC;
224 signal_marshaller.add_parameter (new CCodeParameter ("closure", "GClosure *"));
225 signal_marshaller.add_parameter (new CCodeParameter ("return_value", "GValue *"));
226 signal_marshaller.add_parameter (new CCodeParameter ("n_param_values", "guint"));
227 signal_marshaller.add_parameter (new CCodeParameter ("param_values", "const GValue *"));
228 signal_marshaller.add_parameter (new CCodeParameter ("invocation_hint", "gpointer"));
229 signal_marshaller.add_parameter (new CCodeParameter ("marshal_data", "gpointer"));
231 cfile.add_function_declaration (signal_marshaller);
233 var marshaller_body = new CCodeBlock ();
235 var callback_decl = new CCodeFunctionDeclarator (get_marshaller_function (params, return_type, "GMarshalFunc", dbus));
236 callback_decl.add_parameter (new CCodeParameter ("data1", "gpointer"));
237 n_params = 1;
238 foreach (Parameter p in params) {
239 callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), get_value_type_name_from_parameter (p)));
240 n_params++;
241 if (p.variable_type.is_array () && !dbus) {
242 callback_decl.add_parameter (new CCodeParameter ("arg_%d".printf (n_params), "gint"));
243 n_params++;
246 callback_decl.add_parameter (new CCodeParameter ("data2", "gpointer"));
247 marshaller_body.add_statement (new CCodeTypeDefinition (get_value_type_name_from_type_reference (return_type), callback_decl));
249 var var_decl = new CCodeDeclaration (get_marshaller_function (params, return_type, "GMarshalFunc", dbus));
250 var_decl.modifiers = CCodeModifiers.REGISTER;
251 var_decl.add_declarator (new CCodeVariableDeclarator ("callback"));
252 marshaller_body.add_statement (var_decl);
254 var_decl = new CCodeDeclaration ("GCClosure *");
255 var_decl.modifiers = CCodeModifiers.REGISTER;
256 var_decl.add_declarator (new CCodeVariableDeclarator ("cc", new CCodeCastExpression (new CCodeIdentifier ("closure"), "GCClosure *")));
257 marshaller_body.add_statement (var_decl);
259 var_decl = new CCodeDeclaration ("gpointer");
260 var_decl.modifiers = CCodeModifiers.REGISTER;
261 var_decl.add_declarator (new CCodeVariableDeclarator ("data1"));
262 var_decl.add_declarator (new CCodeVariableDeclarator ("data2"));
263 marshaller_body.add_statement (var_decl);
265 CCodeFunctionCall fc;
267 if (return_type.data_type != null || return_type.is_array ()) {
268 var_decl = new CCodeDeclaration (get_value_type_name_from_type_reference (return_type));
269 var_decl.add_declarator (new CCodeVariableDeclarator ("v_return"));
270 marshaller_body.add_statement (var_decl);
272 fc = new CCodeFunctionCall (new CCodeIdentifier ("g_return_if_fail"));
273 fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("return_value"), new CCodeConstant ("NULL")));
274 marshaller_body.add_statement (new CCodeExpressionStatement (fc));
277 fc = new CCodeFunctionCall (new CCodeIdentifier ("g_return_if_fail"));
278 fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("n_param_values"), new CCodeConstant (n_params.to_string())));
279 marshaller_body.add_statement (new CCodeExpressionStatement (fc));
281 var data = new CCodeMemberAccess (new CCodeIdentifier ("closure"), "data", true);
282 var param = new CCodeMemberAccess (new CCodeMemberAccess (new CCodeIdentifier ("param_values"), "data[0]", true), "v_pointer");
283 var cond = new CCodeFunctionCall (new CCodeConstant ("G_CCLOSURE_SWAP_DATA"));
284 cond.add_argument (new CCodeIdentifier ("closure"));
285 var true_block = new CCodeBlock ();
286 true_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data1"), data)));
287 true_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data2"), param)));
288 var false_block = new CCodeBlock ();
289 false_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data1"), param)));
290 false_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data2"), data)));
291 marshaller_body.add_statement (new CCodeIfStatement (cond, true_block, false_block));
293 var c_assign = new CCodeAssignment (new CCodeIdentifier ("callback"), 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", dbus)));
294 marshaller_body.add_statement (new CCodeExpressionStatement (c_assign));
296 fc = new CCodeFunctionCall (new CCodeIdentifier ("callback"));
297 fc.add_argument (new CCodeIdentifier ("data1"));
298 i = 1;
299 foreach (Parameter p in params) {
300 string get_value_function;
301 bool is_array = p.variable_type.is_array ();
302 if (p.direction != ParameterDirection.IN) {
303 get_value_function = "g_value_get_pointer";
304 } else if (is_array) {
305 if (dbus) {
306 get_value_function = "g_value_get_boxed";
307 } else {
308 if (((ArrayType) p.variable_type).element_type.data_type == string_type.data_type) {
309 get_value_function = "g_value_get_boxed";
310 } else {
311 get_value_function = "g_value_get_pointer";
314 } else if (p.variable_type is PointerType || p.variable_type.type_parameter != null) {
315 get_value_function = "g_value_get_pointer";
316 } else if (p.variable_type is ErrorType) {
317 get_value_function = "g_value_get_pointer";
318 } else if (dbus && DBusModule.get_type_signature (p.variable_type).has_prefix ("(")) {
319 get_value_function = "g_value_get_boxed";
320 } else if (dbus && p.variable_type.data_type is Enum) {
321 var en = (Enum) p.variable_type.data_type;
322 if (en.is_flags) {
323 get_value_function = "g_value_get_uint";
324 } else {
325 get_value_function = "g_value_get_int";
327 } else {
328 get_value_function = p.variable_type.data_type.get_get_value_function ();
330 var inner_fc = new CCodeFunctionCall (new CCodeIdentifier (get_value_function));
331 inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
332 fc.add_argument (inner_fc);
333 i++;
334 if (is_array && !dbus) {
335 inner_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_int"));
336 inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
337 fc.add_argument (inner_fc);
338 i++;
341 fc.add_argument (new CCodeIdentifier ("data2"));
343 if (return_type.data_type != null || return_type.is_array ()) {
344 marshaller_body.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("v_return"), fc)));
346 CCodeFunctionCall set_fc;
347 if (return_type.is_array ()) {
348 if (dbus) {
349 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_boxed"));
350 } else {
351 if (((ArrayType) return_type).element_type.data_type == string_type.data_type) {
352 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_boxed"));
353 } else {
354 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
357 } else if (return_type.type_parameter != null) {
358 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
359 } else if (return_type is ErrorType) {
360 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
361 } else if (return_type.data_type == string_type.data_type) {
362 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_string"));
363 } else if (return_type.data_type is Class || return_type.data_type is Interface) {
364 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_object"));
365 } else if (dbus && DBusModule.get_type_signature (return_type).has_prefix ("(")) {
366 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_boxed"));
367 } else if (dbus && return_type.data_type is Enum) {
368 var en = (Enum) return_type.data_type;
369 if (en.is_flags) {
370 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_uint"));
371 } else {
372 set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_int"));
374 } else {
375 set_fc = new CCodeFunctionCall (new CCodeIdentifier (return_type.data_type.get_set_value_function ()));
377 set_fc.add_argument (new CCodeIdentifier ("return_value"));
378 set_fc.add_argument (new CCodeIdentifier ("v_return"));
380 marshaller_body.add_statement (new CCodeExpressionStatement (set_fc));
381 } else {
382 marshaller_body.add_statement (new CCodeExpressionStatement (fc));
385 signal_marshaller.block = marshaller_body;
387 cfile.add_function (signal_marshaller);
388 user_marshal_set.add (signature);
391 public override CCodeFunctionCall get_signal_creation (Signal sig, TypeSymbol type) {
392 var csignew = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_new"));
393 var cl = sig.parent_symbol as Class;
394 csignew.add_argument (new CCodeConstant ("\"%s\"".printf (sig.get_cname ())));
395 csignew.add_argument (new CCodeIdentifier (type.get_type_id ()));
396 string[] flags = new string[0];
397 if (sig.run_type == "first") {
398 flags += "G_SIGNAL_RUN_FIRST";
399 } else if (sig.run_type == "cleanup") {
400 flags += "G_SIGNAL_RUN_CLEANUP";
401 } else {
402 flags += "G_SIGNAL_RUN_LAST";
404 if (sig.is_detailed) {
405 flags += "G_SIGNAL_DETAILED";
408 if (sig.no_recurse) {
409 flags += "G_SIGNAL_NO_RECURSE";
412 if (sig.is_action) {
413 flags += "G_SIGNAL_ACTION";
416 if (sig.no_hooks) {
417 flags += "G_SIGNAL_NO_HOOKS";
420 csignew.add_argument (new CCodeConstant (string.joinv (" | ", flags)));
422 if (sig.default_handler == null) {
423 csignew.add_argument (new CCodeConstant ("0"));
424 } else {
425 var struct_offset = new CCodeFunctionCall (new CCodeIdentifier ("G_STRUCT_OFFSET"));
426 struct_offset.add_argument (new CCodeIdentifier ("%sClass".printf (cl.get_cname ())));
427 struct_offset.add_argument (new CCodeIdentifier (sig.default_handler.vfunc_name));
428 csignew.add_argument (struct_offset);
430 csignew.add_argument (new CCodeConstant ("NULL"));
431 csignew.add_argument (new CCodeConstant ("NULL"));
433 string marshaller = get_marshaller_function (sig.get_parameters (), sig.return_type);
435 var marshal_arg = new CCodeIdentifier (marshaller);
436 csignew.add_argument (marshal_arg);
438 var params = sig.get_parameters ();
439 if (sig.return_type is PointerType || sig.return_type.type_parameter != null) {
440 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
441 } else if (sig.return_type is ErrorType) {
442 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
443 } else if (sig.return_type.data_type == null) {
444 csignew.add_argument (new CCodeConstant ("G_TYPE_NONE"));
445 } else {
446 csignew.add_argument (new CCodeConstant (sig.return_type.data_type.get_type_id ()));
449 int params_len = 0;
450 foreach (Parameter param in params) {
451 params_len++;
452 if (param.variable_type.is_array ()) {
453 params_len++;
457 csignew.add_argument (new CCodeConstant ("%d".printf (params_len)));
458 foreach (Parameter param in params) {
459 if (param.variable_type.is_array ()) {
460 if (((ArrayType) param.variable_type).element_type.data_type == string_type.data_type) {
461 csignew.add_argument (new CCodeConstant ("G_TYPE_STRV"));
462 } else {
463 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
465 csignew.add_argument (new CCodeConstant ("G_TYPE_INT"));
466 } else if (param.variable_type is PointerType || param.variable_type.type_parameter != null || param.direction != ParameterDirection.IN) {
467 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
468 } else if (param.variable_type is ErrorType) {
469 csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
470 } else {
471 csignew.add_argument (new CCodeConstant (param.variable_type.data_type.get_type_id ()));
475 marshal_arg.name = marshaller;
477 return csignew;
480 public virtual CCodeExpression get_dbus_g_type (DataType data_type) {
481 return new CCodeConstant (data_type.data_type.get_type_id ());
484 public override void visit_element_access (ElementAccess expr) {
485 if (expr.container is MemberAccess && expr.container.symbol_reference is Signal) {
486 // detailed signal emission
487 var sig = (Signal) expr.symbol_reference;
488 var ma = (MemberAccess) expr.container;
490 var detail_expr = expr.get_indices ().get (0);
491 var signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
493 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit_by_name"));
494 ccall.add_argument (get_cvalue (ma.inner));
495 if (signal_name_cexpr != null) {
496 ccall.add_argument (signal_name_cexpr);
498 set_cvalue (expr, ccall);
499 } else {
500 base.visit_element_access (expr);
504 bool in_gobject_instance (Method m) {
505 bool result = false;
506 if (m.binding == MemberBinding.INSTANCE) {
507 result = m.this_parameter.variable_type.data_type.is_subtype_of (gobject_type);
509 return result;
512 void emit_signal_assignment (Assignment assignment) {
513 var sig = (Signal) assignment.left.symbol_reference;
515 bool disconnect = false;
517 if (assignment.operator == AssignmentOperator.ADD) {
518 // connect
519 } else if (assignment.operator == AssignmentOperator.SUB) {
520 // disconnect
521 disconnect = true;
522 } else {
523 assignment.error = true;
524 Report.error (assignment.source_reference, "Specified compound assignment type for signals not supported.");
525 return;
528 connect_signal (sig, assignment.left, assignment.right, disconnect, false, assignment);
531 public override void visit_assignment (Assignment assignment) {
532 if (assignment.left.symbol_reference is Signal) {
533 if (assignment.left.error || assignment.right.error) {
534 assignment.error = true;
535 return;
538 emit_signal_assignment (assignment);
539 } else {
540 base.visit_assignment (assignment);
544 public override void visit_member_access (MemberAccess expr) {
545 if (expr.symbol_reference is Signal) {
546 CCodeExpression pub_inst = null;
548 if (expr.inner != null) {
549 pub_inst = get_cvalue (expr.inner);
552 var sig = (Signal) expr.symbol_reference;
553 var cl = (TypeSymbol) sig.parent_symbol;
555 if (expr.inner is BaseAccess && sig.is_virtual) {
556 var m = sig.default_handler;
557 var base_class = (Class) m.parent_symbol;
558 var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (base_class.get_upper_case_cname (null))));
559 vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (current_class.get_lower_case_cname (null))));
561 set_cvalue (expr, new CCodeMemberAccess.pointer (vcast, m.name));
562 return;
565 if (sig.has_emitter) {
566 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_%s".printf (cl.get_lower_case_cname (null), sig.name)));
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 (sig.get_canonical_cconstant ());
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 var m = (Method) handler.symbol_reference;
608 if (!disconnect) {
609 // connect
610 if (sig is DynamicSignal) {
611 if (!after)
612 connect_func = get_dynamic_signal_connect_wrapper_name ((DynamicSignal) sig);
613 else
614 connect_func = get_dynamic_signal_connect_after_wrapper_name ((DynamicSignal) sig);
615 } else {
616 if (m.closure) {
617 connect_func = "g_signal_connect_data";
618 } else if (in_gobject_instance (m)) {
619 connect_func = "g_signal_connect_object";
620 } else if (!after) {
621 connect_func = "g_signal_connect";
622 } else
623 connect_func = "g_signal_connect_after";
625 } else {
626 // disconnect
627 if (handler is LambdaExpression) {
628 Report.error (handler.source_reference, "Cannot disconnect lambda expression from signal. Use Object.disconnect.");
630 if (sig is DynamicSignal) {
631 connect_func = get_dynamic_signal_disconnect_wrapper_name ((DynamicSignal) sig);
632 } else {
633 connect_func = "g_signal_handlers_disconnect_matched";
637 var ccall = new CCodeFunctionCall (new CCodeIdentifier (connect_func));
639 CCodeExpression signal_name_cexpr = null;
641 // first argument: instance of sender
642 MemberAccess ma;
643 if (signal_access is ElementAccess) {
644 var ea = (ElementAccess) signal_access;
645 ma = (MemberAccess) ea.container;
646 var detail_expr = ea.get_indices ().get (0);
647 signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
648 if (signal_name_cexpr == null) {
649 return null;
651 } else {
652 ma = (MemberAccess) signal_access;
653 signal_name_cexpr = get_signal_name_cexpression (sig, null, expr);
655 if (ma.inner != null) {
656 ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
657 } else {
658 ccall.add_argument (get_result_cexpression ("self"));
661 if (sig is DynamicSignal) {
662 // dynamic_signal_connect or dynamic_signal_disconnect
664 // second argument: signal name
665 ccall.add_argument (new CCodeConstant ("\"%s\"".printf (sig.name)));
666 } else if (!disconnect) {
667 // g_signal_connect_object or g_signal_connect
669 // second argument: signal name
670 ccall.add_argument (signal_name_cexpr);
671 } else {
672 // g_signal_handlers_disconnect_matched
674 // second argument: mask
675 if (!(signal_access is ElementAccess)) {
676 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
677 } else {
678 ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
681 // get signal id
682 var temp_decl = get_temp_variable (uint_type);
683 emit_temp_var (temp_decl);
684 var parse_call = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_parse_name"));
685 parse_call.add_argument (signal_name_cexpr);
686 var decl_type = (TypeSymbol) sig.parent_symbol;
687 parse_call.add_argument (new CCodeIdentifier (decl_type.get_type_id ()));
688 parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_decl.name)));
689 LocalVariable? detail_temp_decl = null;
690 if (!(signal_access is ElementAccess)) {
691 parse_call.add_argument (new CCodeConstant ("NULL"));
692 parse_call.add_argument (new CCodeConstant ("FALSE"));
693 } else {
694 detail_temp_decl = get_temp_variable (gquark_type);
695 emit_temp_var (detail_temp_decl);
696 parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (detail_temp_decl.name)));
697 parse_call.add_argument (new CCodeConstant ("TRUE"));
699 ccode.add_expression (parse_call);
701 // third argument: signal_id
702 ccall.add_argument (get_variable_cexpression (temp_decl.name));
704 // fourth argument: detail
705 if (detail_temp_decl == null) {
706 ccall.add_argument (new CCodeConstant ("0"));
707 } else {
708 ccall.add_argument (get_variable_cexpression (detail_temp_decl.name));
710 // fifth argument: closure
711 ccall.add_argument (new CCodeConstant ("NULL"));
714 // third resp. sixth argument: handler
715 ccall.add_argument (new CCodeCastExpression (get_cvalue (handler), "GCallback"));
717 if (m.closure) {
718 // g_signal_connect_data
720 // fourth argument: user_data
721 CCodeExpression handler_destroy_notify;
722 ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
724 // fifth argument: destroy_notify
725 ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
727 // sixth argument: connect_flags
728 if (!after)
729 ccall.add_argument (new CCodeConstant ("0"));
730 else
731 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
732 } else if (m.binding == MemberBinding.INSTANCE) {
733 // g_signal_connect_object or g_signal_handlers_disconnect_matched
734 // or dynamic_signal_connect or dynamic_signal_disconnect
736 // fourth resp. seventh argument: object/user_data
737 if (handler is MemberAccess) {
738 var right_ma = (MemberAccess) handler;
739 if (right_ma.inner != null) {
740 ccall.add_argument (get_cvalue (right_ma.inner));
741 } else {
742 ccall.add_argument (get_result_cexpression ("self"));
744 } else if (handler is LambdaExpression) {
745 ccall.add_argument (get_result_cexpression ("self"));
747 if (!disconnect && !(sig is DynamicSignal)
748 && in_gobject_instance (m)) {
749 // g_signal_connect_object
751 // fifth argument: connect_flags
752 if (!after)
753 ccall.add_argument (new CCodeConstant ("0"));
754 else
755 ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
757 } else {
758 // g_signal_connect or g_signal_connect_after or g_signal_handlers_disconnect_matched
759 // or dynamic_signal_connect or dynamic_signal_disconnect
761 // fourth resp. seventh argument: user_data
762 ccall.add_argument (new CCodeConstant ("NULL"));
765 if (disconnect || expr.parent_node is ExpressionStatement) {
766 ccode.add_expression (ccall);
767 return null;
768 } else {
769 var temp_var = get_temp_variable (ulong_type);
770 var temp_ref = get_variable_cexpression (temp_var.name);
772 emit_temp_var (temp_var);
774 ccode.add_assignment (temp_ref, ccall);
776 return temp_ref;