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 (List
<Parameter
> params
, DataType return_type
, string? prefix
= null) {
28 var signature
= get_marshaller_signature (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 if (params
== null || params
.size
== 0) {
44 foreach (Parameter p
in params
) {
45 ret
= "%s_%s".printf (ret
, get_ccode_marshaller_type_name (p
).replace (",", "_"));
52 private string?
get_value_type_name_from_type_reference (DataType t
) {
53 if (t is PointerType
|| t is GenericType
) {
55 } else if (t is VoidType
) {
57 } else if (get_ccode_type_id (t
) == get_ccode_type_id (string_type
)) {
59 } else if (t
.data_type is Class
|| t
.data_type is Interface
) {
61 } else if (t is ValueType
&& t
.nullable
) {
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
);
70 } else if (t
.data_type is Enum
) {
72 } else if (t is ArrayType
) {
74 } else if (t is ErrorType
) {
81 private string?
get_value_type_name_from_parameter (Parameter p
) {
82 if (p
.direction
!= ParameterDirection
.IN
) {
85 return get_value_type_name_from_type_reference (p
.variable_type
);
89 private string get_marshaller_signature (List
<Parameter
> params
, DataType return_type
) {
92 signature
= "%s:".printf (get_ccode_marshaller_type_name (return_type
));
93 if (params
== null || params
.size
== 0) {
94 signature
= signature
+ "VOID";
97 foreach (Parameter p
in params
) {
99 signature
= signature
+ get_ccode_marshaller_type_name (p
);
102 signature
= "%s,%s".printf (signature
, get_ccode_marshaller_type_name (p
));
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
)) {
117 Report
.error (detail_expr
.source_reference
, "only string details are supported");
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
)) {
148 Report
.error (detail_expr
.source_reference
, "only string details are supported");
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"));
157 detail_ccall
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_quark_from_string"));
159 detail_ccall
.add_argument (detail_cexpr
);
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
) {
170 Report
.error (sig
.source_reference
, "Signals are not supported in compact classes");
175 foreach (DataType base_type
in cl
.get_base_types ()) {
176 if (SemanticAnalyzer
.symbol_lookup_inherited (base_type
.data_type
, sig
.name
) is Signal
) {
178 Report
.error (sig
.source_reference
, "Signals with the same name as a signal in a base type are not supported");
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
) {
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
)) {
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"));
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
)));
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"));
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
);
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 (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 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";
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";
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
);
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
);
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"));
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"));
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
);
338 ccode
.add_expression (fc
);
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";
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"));
386 var struct_offset
= new
CCodeFunctionCall (new
CCodeIdentifier ("G_STRUCT_OFFSET"));
388 struct_offset
.add_argument (new
CCodeIdentifier ("%sClass".printf (get_ccode_name (type
))));
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"));
414 csignew
.add_argument (new
CCodeConstant (get_ccode_type_id (sig
.return_type
.data_type
)));
418 foreach (Parameter param
in params
) {
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"));
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"));
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
);
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
);
483 // signal connect or disconnect
486 base.visit_element_access (expr
);
490 bool in_gobject_instance (Method m
) {
492 if (m
.binding
== MemberBinding
.INSTANCE
) {
493 result
= m
.this_parameter
.variable_type
.data_type
.is_subtype_of (gobject_type
);
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
) {
505 } else if (assignment
.operator
== AssignmentOperator
.SUB
) {
509 assignment
.error
= true;
510 Report
.error (assignment
.source_reference
, "Specified compound assignment type for signals not supported.");
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;
524 emit_signal_assignment (assignment
);
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
));
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
)) {
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
);
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
);
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
);
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
);
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
) {
608 DelegateType? dt
= null;
609 var p
= handler
.symbol_reference as Parameter
;
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
;
620 if (sig is DynamicSignal
) {
622 connect_func
= get_dynamic_signal_connect_wrapper_name ((DynamicSignal
) sig
);
624 connect_func
= get_dynamic_signal_connect_after_wrapper_name ((DynamicSignal
) sig
);
626 if ((m
!= null && m
.closure
) && in_gobject_instance (m
)) {
627 connect_func
= "g_signal_connect_closure";
628 } else if ((m
!= null && m
.closure
) || (dt
!= null && dt
.value_owned
)) {
629 connect_func
= "g_signal_connect_data";
630 } else if (m
!= null && in_gobject_instance (m
)) {
631 connect_func
= "g_signal_connect_object";
633 connect_func
= "g_signal_connect";
635 connect_func
= "g_signal_connect_after";
639 if (handler is LambdaExpression
) {
640 Report
.error (handler
.source_reference
, "Cannot disconnect lambda expression from signal. Use Object.disconnect.");
642 if (sig is DynamicSignal
) {
643 connect_func
= get_dynamic_signal_disconnect_wrapper_name ((DynamicSignal
) sig
);
645 connect_func
= "g_signal_handlers_disconnect_matched";
649 var ccall
= new
CCodeFunctionCall (new
CCodeIdentifier (connect_func
));
651 CCodeExpression signal_name_cexpr
= null;
653 // first argument: instance of sender
655 if (signal_access is ElementAccess
) {
656 var ea
= (ElementAccess
) signal_access
;
657 ma
= (MemberAccess
) ea
.container
;
658 var detail_expr
= ea
.get_indices ().get (0);
659 signal_name_cexpr
= get_signal_name_cexpression (sig
, detail_expr
, expr
);
660 if (signal_name_cexpr
== null) {
664 ma
= (MemberAccess
) signal_access
;
665 signal_name_cexpr
= get_signal_name_cexpression (sig
, null, expr
);
667 if (ma
.inner
!= null) {
668 ccall
.add_argument ((CCodeExpression
) get_ccodenode (ma
.inner
));
670 ccall
.add_argument (get_result_cexpression ("self"));
673 if (sig is DynamicSignal
) {
674 // dynamic_signal_connect or dynamic_signal_disconnect
676 // second argument: signal name
677 ccall
.add_argument (new
CCodeConstant ("\"%s\"".printf (get_ccode_name (sig
))));
678 } else if (!disconnect
) {
679 // g_signal_connect_object or g_signal_connect
681 // second argument: signal name
682 ccall
.add_argument (signal_name_cexpr
);
684 // g_signal_handlers_disconnect_matched
686 // second argument: mask
687 if (!(signal_access is ElementAccess
)) {
688 ccall
.add_argument (new
CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
690 ccall
.add_argument (new
CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
694 var temp_decl
= get_temp_variable (uint_type
);
695 emit_temp_var (temp_decl
);
696 var parse_call
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_signal_parse_name"));
697 parse_call
.add_argument (signal_name_cexpr
);
698 var decl_type
= (TypeSymbol
) sig
.parent_symbol
;
699 parse_call
.add_argument (new
CCodeIdentifier (get_ccode_type_id (decl_type
)));
700 parse_call
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, get_variable_cexpression (temp_decl
.name
)));
701 LocalVariable? detail_temp_decl
= null;
702 if (!(signal_access is ElementAccess
)) {
703 parse_call
.add_argument (new
CCodeConstant ("NULL"));
704 parse_call
.add_argument (new
CCodeConstant ("FALSE"));
706 detail_temp_decl
= get_temp_variable (gquark_type
);
707 emit_temp_var (detail_temp_decl
);
708 parse_call
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, get_variable_cexpression (detail_temp_decl
.name
)));
709 parse_call
.add_argument (new
CCodeConstant ("TRUE"));
711 ccode
.add_expression (parse_call
);
713 // third argument: signal_id
714 ccall
.add_argument (get_variable_cexpression (temp_decl
.name
));
716 // fourth argument: detail
717 if (detail_temp_decl
== null) {
718 ccall
.add_argument (new
CCodeConstant ("0"));
720 ccall
.add_argument (get_variable_cexpression (detail_temp_decl
.name
));
722 // fifth argument: closure
723 ccall
.add_argument (new
CCodeConstant ("NULL"));
726 if ((m
!= null && m
.closure
) && in_gobject_instance (m
)) {
727 // g_signal_connect_closure
729 // third argument: closure
730 var closure_var
= get_temp_variable (new
CType ("GClosure*"), false, null, false);
731 var closure_ref
= get_variable_cexpression (closure_var
.name
);
732 emit_temp_var (closure_var
);
734 var closure_call
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_cclosure_new"));
736 closure_call
.add_argument (new
CCodeCastExpression (get_cvalue (handler
), "GCallback"));
738 CCodeExpression handler_destroy_notify
;
739 closure_call
.add_argument (get_delegate_target_cexpression (handler
, out handler_destroy_notify
));
741 closure_call
.add_argument (new
CCodeCastExpression (handler_destroy_notify
, "GClosureNotify"));
743 ccode
.add_assignment (closure_ref
, closure_call
);
745 var watch_call
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_object_watch_closure"));
746 watch_call
.add_argument (new
CCodeCastExpression (get_result_cexpression ("self"), "GObject *"));
747 watch_call
.add_argument (closure_ref
);
749 ccode
.add_expression (watch_call
);
751 ccall
.add_argument (closure_ref
);
753 // fourth argument: after?
754 ccall
.add_argument (new
CCodeConstant (after ?
"TRUE" : "FALSE"));
755 } else if (m
!= null && m
.closure
) {
756 // g_signal_connect_data
758 // third argument: handler
759 ccall
.add_argument (new
CCodeCastExpression (get_cvalue (handler
), "GCallback"));
761 // fourth argument: user_data
762 CCodeExpression handler_destroy_notify
;
763 ccall
.add_argument (get_delegate_target_cexpression (handler
, out handler_destroy_notify
));
765 // fifth argument: destroy_notify
766 ccall
.add_argument (new
CCodeCastExpression (handler_destroy_notify
, "GClosureNotify"));
768 // sixth argument: connect_flags
770 ccall
.add_argument (new
CCodeConstant ("0"));
772 ccall
.add_argument (new
CCodeConstant ("G_CONNECT_AFTER"));
773 } else if (m
!= null && m
.binding
== MemberBinding
.INSTANCE
) {
774 // g_signal_connect_object or g_signal_handlers_disconnect_matched
775 // or dynamic_signal_connect or dynamic_signal_disconnect
777 // third resp. sixth argument: handler
778 ccall
.add_argument (new
CCodeCastExpression (get_cvalue (handler
), "GCallback"));
780 // fourth resp. seventh argument: object/user_data
781 if (handler is MemberAccess
) {
782 var right_ma
= (MemberAccess
) handler
;
783 if (right_ma
.inner
!= null) {
784 ccall
.add_argument (get_cvalue (right_ma
.inner
));
786 ccall
.add_argument (get_result_cexpression ("self"));
788 } else if (handler is LambdaExpression
) {
789 ccall
.add_argument (get_result_cexpression ("self"));
791 if (!disconnect
&& !(sig is DynamicSignal
)
792 && in_gobject_instance (m
)) {
793 // g_signal_connect_object
795 // fifth argument: connect_flags
797 ccall
.add_argument (new
CCodeConstant ("0"));
799 ccall
.add_argument (new
CCodeConstant ("G_CONNECT_AFTER"));
801 } else if (dt
!= null && dt
.delegate_symbol
.has_target
) {
802 // third argument: handler
803 ccall
.add_argument (new
CCodeCastExpression (get_cvalue (handler
), "GCallback"));
805 // fourth argument: user_data
806 CCodeExpression handler_destroy_notify
;
807 ccall
.add_argument (get_delegate_target_cexpression (handler
, out handler_destroy_notify
));
808 if (!disconnect
&& dt
.value_owned
) {
809 // fifth argument: destroy_notify
810 //FIXME handler_destroy_notify is NULL
811 ccall
.add_argument (new
CCodeCastExpression (handler_destroy_notify
, "GClosureNotify"));
812 // sixth argument: connect_flags
814 ccall
.add_argument (new
CCodeConstant ("0"));
816 ccall
.add_argument (new
CCodeConstant ("G_CONNECT_AFTER"));
819 // g_signal_connect or g_signal_connect_after or g_signal_handlers_disconnect_matched
820 // or dynamic_signal_connect or dynamic_signal_disconnect
822 // third resp. sixth argument: handler
823 ccall
.add_argument (new
CCodeCastExpression (get_cvalue (handler
), "GCallback"));
825 // fourth resp. seventh argument: user_data
826 ccall
.add_argument (new
CCodeConstant ("NULL"));
829 if (disconnect
|| expr
.parent_node is ExpressionStatement
) {
830 ccode
.add_expression (ccall
);
833 var temp_var
= get_temp_variable (ulong_type
);
834 var temp_ref
= get_variable_cexpression (temp_var
.name
);
836 emit_temp_var (temp_var
);
838 ccode
.add_assignment (temp_ref
, ccall
);