valagsignalmodule.vala 28.6 KB
Newer Older
1
/* valagsignalmodule.vala
Jürg Billeter's avatar
Jürg Billeter committed
2
 *
3
 * Copyright (C) 2006-2010  Jürg Billeter
4
 * Copyright (C) 2006-2008  Raffaele Sandrini
Jürg Billeter's avatar
Jürg Billeter committed
5 6 7 8
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
Jürg Billeter's avatar
Jürg Billeter committed
10 11 12 13 14 15 16 17 18 19 20 21

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Jürg Billeter <j@bitron.ch>
22
 *	Raffaele Sandrini <raffaele@sandrini.ch>
Jürg Billeter's avatar
Jürg Billeter committed
23 24 25
 */


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) {
Jürg Billeter's avatar
Jürg Billeter committed
29
			return ("POINTER");
30 31
		} else if (t is ErrorType) {
			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 38 39 40
					return ("BOXED_INT");
				} else {
					return ("POINTER_INT");
				}
41
			}
42
		} else if (t is VoidType) {
Jürg Billeter's avatar
Jürg Billeter committed
43
			return ("VOID");
44
		} else if (dbus && DBusModule.get_type_signature (t).has_prefix ("(")) {
45
			return ("BOXED");
46 47 48 49 50 51 52 53 54 55 56
		} else if (t.data_type is Enum) {
			var en = (Enum) t.data_type;
			if (dbus) {
				if (en.is_flags) {
					return ("UINT");
				} else {
					return ("INT");
				}
			} else {
				return en.get_marshaller_type_name ();
			}
Jürg Billeter's avatar
Jürg Billeter committed
57 58 59 60 61
		} else {
			return t.data_type.get_marshaller_type_name ();
		}
	}
	
62
	private string get_marshaller_type_name_for_parameter (FormalParameter param, bool dbus = false) {
63 64 65
		if (param.direction != ParameterDirection.IN) {
			return ("POINTER");
		} else {
Jürg Billeter's avatar
Jürg Billeter committed
66
			return get_marshaller_type_name (param.variable_type, dbus);
67 68 69
		}
	}
	
70
	public override string get_marshaller_function (List<FormalParameter> params, DataType return_type, string? prefix = null, bool dbus = false) {
71
		var signature = get_marshaller_signature (params, return_type, dbus);
Jürg Billeter's avatar
Jürg Billeter committed
72
		string ret;
73

Jürg Billeter's avatar
Jürg Billeter committed
74
		if (prefix == null) {
75
			if (predefined_marshal_set.contains (signature)) {
Jürg Billeter's avatar
Jürg Billeter committed
76 77 78 79 80 81
				prefix = "g_cclosure_marshal";
			} else {
				prefix = "g_cclosure_user_marshal";
			}
		}
		
82
		ret = "%s_%s_".printf (prefix, get_marshaller_type_name (return_type, dbus));
Jürg Billeter's avatar
Jürg Billeter committed
83
		
84
		if (params == null || params.size == 0) {
Jürg Billeter's avatar
Jürg Billeter committed
85 86 87
			ret = ret + "_VOID";
		} else {
			foreach (FormalParameter p in params) {
88
				ret = "%s_%s".printf (ret, get_marshaller_type_name_for_parameter (p, dbus));
Jürg Billeter's avatar
Jürg Billeter committed
89 90 91 92 93 94
			}
		}
		
		return ret;
	}
	
95
	private string? get_value_type_name_from_type_reference (DataType t) {
96
		if (t is PointerType || t.type_parameter != null) {
Jürg Billeter's avatar
Jürg Billeter committed
97
			return "gpointer";
98
		} else if (t is VoidType) {
Jürg Billeter's avatar
Jürg Billeter committed
99
			return "void";
100
		} else if (t.data_type == string_type.data_type) {
101
			return "const char*";
Jürg Billeter's avatar
Jürg Billeter committed
102
		} else if (t.data_type is Class || t.data_type is Interface) {
103
			return "gpointer";
Jürg Billeter's avatar
Jürg Billeter committed
104
		} else if (t.data_type is Struct) {
105 106 107 108 109 110
			var st = (Struct) t.data_type;
			if (st.is_simple_type ()) {
				return t.data_type.get_cname ();
			} else {
				return "gpointer";
			}
Jürg Billeter's avatar
Jürg Billeter committed
111 112
		} else if (t.data_type is Enum) {
			return "gint";
Jürg Billeter's avatar
Jürg Billeter committed
113
		} else if (t is ArrayType) {
Jürg Billeter's avatar
Jürg Billeter committed
114
			return "gpointer";
115 116
		} else if (t is ErrorType) {
			return "gpointer";
Jürg Billeter's avatar
Jürg Billeter committed
117 118 119 120 121
		}
		
		return null;
	}
	
122 123 124 125
	private string? get_value_type_name_from_parameter (FormalParameter p) {
		if (p.direction != ParameterDirection.IN) {
			return "gpointer";
		} else {
Jürg Billeter's avatar
Jürg Billeter committed
126
			return get_value_type_name_from_type_reference (p.variable_type);
127 128 129
		}
	}
	
130
	private string get_marshaller_signature (List<FormalParameter> params, DataType return_type, bool dbus = false) {
Jürg Billeter's avatar
Jürg Billeter committed
131 132
		string signature;
		
133
		signature = "%s:".printf (get_marshaller_type_name (return_type, dbus));
134
		if (params == null || params.size == 0) {
Jürg Billeter's avatar
Jürg Billeter committed
135 136 137 138 139
			signature = signature + "VOID";
		} else {
			bool first = true;
			foreach (FormalParameter p in params) {
				if (first) {
140
					signature = signature + get_marshaller_type_name_for_parameter (p, dbus);
Jürg Billeter's avatar
Jürg Billeter committed
141 142
					first = false;
				} else {
143
					signature = "%s,%s".printf (signature, get_marshaller_type_name_for_parameter (p, dbus));
Jürg Billeter's avatar
Jürg Billeter committed
144 145 146 147 148 149
				}
			}
		}
		
		return signature;
	}
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

	private CCodeExpression? get_signal_name_cexpression (Signal sig, Expression? detail_expr, CodeNode node) {
		if (detail_expr == null) {
			return sig.get_canonical_cconstant ();
		}

		if (detail_expr.value_type is NullType || !detail_expr.value_type.compatible (string_type)) {
			node.error = true;
			Report.error (detail_expr.source_reference, "only string details are supported");
			return null;
		}

		if (detail_expr is StringLiteral) {
			return sig.get_canonical_cconstant (((StringLiteral) detail_expr).eval ());
		}

		var detail_decl = get_temp_variable (detail_expr.value_type, true, node);
		temp_vars.insert (0, detail_decl);
		temp_ref_vars.insert (0, detail_decl);

		var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_strconcat"));
		ccall.add_argument (sig.get_canonical_cconstant (""));
		ccall.add_argument ((CCodeExpression) detail_expr.ccodenode);
		ccall.add_argument (new CCodeConstant ("NULL"));

		var ccomma = new CCodeCommaExpression ();
		ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (detail_decl.name), ccall));
		ccomma.append_expression (get_variable_cexpression (detail_decl.name));
		return ccomma;
	}

181
	public override void visit_signal (Signal sig) {
182 183 184 185 186 187 188
		// parent_symbol may be null for dynamic signals

		var cl = sig.parent_symbol as Class;
		if (cl != null && cl.is_compact) {
			sig.error = true;
			Report.error (sig.source_reference, "Signals are not supported in compact classes");
			return;
189 190
		}

191 192 193 194 195 196 197 198 199 200
		if (cl != null) {
			foreach (DataType base_type in cl.get_base_types ()) {
				if (SemanticAnalyzer.symbol_lookup_inherited (base_type.data_type, sig.name) is Signal) {
					sig.error = true;
					Report.error (sig.source_reference, "Signals with the same name as a signal in a base type are not supported");
					return;
				}
			}
		}

Jürg Billeter's avatar
Jürg Billeter committed
201
		sig.accept_children (this);
202

203 204 205 206 207
		// declare parameter type
		foreach (FormalParameter p in sig.get_parameters ()) {
			generate_parameter (p, source_declarations, new HashMap<int,CCodeFormalParameter> (), null);
		}

208 209 210
		generate_marshaller (sig.get_parameters (), sig.return_type);
	}

211
	public override void generate_marshaller (List<FormalParameter> params, DataType return_type, bool dbus = false) {
Jürg Billeter's avatar
Jürg Billeter committed
212 213 214 215
		string signature;
		int n_params, i;
		
		/* 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)) {
Jürg Billeter's avatar
Jürg Billeter committed
218 219 220
			return;
		}
		
221
		var signal_marshaller = new CCodeFunction (get_marshaller_function (params, return_type, null, dbus), "void");
Jürg Billeter's avatar
Jürg Billeter committed
222 223 224 225 226 227 228 229 230
		signal_marshaller.modifiers = CCodeModifiers.STATIC;
		
		signal_marshaller.add_parameter (new CCodeFormalParameter ("closure", "GClosure *"));
		signal_marshaller.add_parameter (new CCodeFormalParameter ("return_value", "GValue *"));
		signal_marshaller.add_parameter (new CCodeFormalParameter ("n_param_values", "guint"));
		signal_marshaller.add_parameter (new CCodeFormalParameter ("param_values", "const GValue *"));
		signal_marshaller.add_parameter (new CCodeFormalParameter ("invocation_hint", "gpointer"));
		signal_marshaller.add_parameter (new CCodeFormalParameter ("marshal_data", "gpointer"));
		
231
		source_signal_marshaller_declaration.append (signal_marshaller.copy ());
Jürg Billeter's avatar
Jürg Billeter committed
232 233 234
		
		var marshaller_body = new CCodeBlock ();
		
235
		var callback_decl = new CCodeFunctionDeclarator (get_marshaller_function (params, return_type, "GMarshalFunc", dbus));
Jürg Billeter's avatar
Jürg Billeter committed
236 237 238
		callback_decl.add_parameter (new CCodeFormalParameter ("data1", "gpointer"));
		n_params = 1;
		foreach (FormalParameter p in params) {
239
			callback_decl.add_parameter (new CCodeFormalParameter ("arg_%d".printf (n_params), get_value_type_name_from_parameter (p)));
Jürg Billeter's avatar
Jürg Billeter committed
240
			n_params++;
Jürg Billeter's avatar
Jürg Billeter committed
241
			if (p.variable_type.is_array () && !dbus) {
242 243 244
				callback_decl.add_parameter (new CCodeFormalParameter ("arg_%d".printf (n_params), "gint"));
				n_params++;
			}
Jürg Billeter's avatar
Jürg Billeter committed
245 246
		}
		callback_decl.add_parameter (new CCodeFormalParameter ("data2", "gpointer"));
247
		marshaller_body.add_statement (new CCodeTypeDefinition (get_value_type_name_from_type_reference (return_type), callback_decl));
Jürg Billeter's avatar
Jürg Billeter committed
248
		
249
		var var_decl = new CCodeDeclaration (get_marshaller_function (params, return_type, "GMarshalFunc", dbus));
Jürg Billeter's avatar
Jürg Billeter committed
250 251 252 253 254 255
		var_decl.modifiers = CCodeModifiers.REGISTER;
		var_decl.add_declarator (new CCodeVariableDeclarator ("callback"));
		marshaller_body.add_statement (var_decl);
		
		var_decl = new CCodeDeclaration ("GCClosure *");
		var_decl.modifiers = CCodeModifiers.REGISTER;
256
		var_decl.add_declarator (new CCodeVariableDeclarator ("cc", new CCodeCastExpression (new CCodeIdentifier ("closure"), "GCClosure *")));
Jürg Billeter's avatar
Jürg Billeter committed
257 258 259 260 261 262 263 264 265 266
		marshaller_body.add_statement (var_decl);
		
		var_decl = new CCodeDeclaration ("gpointer");
		var_decl.modifiers = CCodeModifiers.REGISTER;
		var_decl.add_declarator (new CCodeVariableDeclarator ("data1"));
		var_decl.add_declarator (new CCodeVariableDeclarator ("data2"));
		marshaller_body.add_statement (var_decl);
		
		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));
Jürg Billeter's avatar
Jürg Billeter committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
			var_decl.add_declarator (new CCodeVariableDeclarator ("v_return"));
			marshaller_body.add_statement (var_decl);
			
			fc = new CCodeFunctionCall (new CCodeIdentifier ("g_return_if_fail"));
			fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("return_value"), new CCodeConstant ("NULL")));
			marshaller_body.add_statement (new CCodeExpressionStatement (fc));
		}
		
		fc = new CCodeFunctionCall (new CCodeIdentifier ("g_return_if_fail"));
		fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("n_param_values"), new CCodeConstant (n_params.to_string())));
		marshaller_body.add_statement (new CCodeExpressionStatement (fc));
		
		var data = new CCodeMemberAccess (new CCodeIdentifier ("closure"), "data", true);
		var param = new CCodeMemberAccess (new CCodeMemberAccess (new CCodeIdentifier ("param_values"), "data[0]", true), "v_pointer");
		var cond = new CCodeFunctionCall (new CCodeConstant ("G_CCLOSURE_SWAP_DATA"));
		cond.add_argument (new CCodeIdentifier ("closure"));
		var true_block = new CCodeBlock ();
		true_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data1"), data)));
		true_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data2"), param)));
		var false_block = new CCodeBlock ();
		false_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data1"), param)));
		false_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("data2"), data)));
		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)));
Jürg Billeter's avatar
Jürg Billeter committed
294 295 296 297 298 299 300
		marshaller_body.add_statement (new CCodeExpressionStatement (c_assign));
		
		fc = new CCodeFunctionCall (new CCodeIdentifier ("callback"));
		fc.add_argument (new CCodeIdentifier ("data1"));
		i = 1;
		foreach (FormalParameter p in params) {
			string get_value_function;
Jürg Billeter's avatar
Jürg Billeter committed
301
			bool is_array = p.variable_type.is_array ();
302 303 304
			if (p.direction != ParameterDirection.IN) {
				get_value_function = "g_value_get_pointer";
			} else if (is_array) {
305
				if (dbus) {
306 307
					get_value_function = "g_value_get_boxed";
				} else {
Jürg Billeter's avatar
Jürg Billeter committed
308
					if (((ArrayType) p.variable_type).element_type.data_type == string_type.data_type) {
309 310 311 312
						get_value_function = "g_value_get_boxed";
					} else {
						get_value_function = "g_value_get_pointer";
					}
313
				}
Jürg Billeter's avatar
Jürg Billeter committed
314
			} else if (p.variable_type is PointerType || p.variable_type.type_parameter != null) {
Jürg Billeter's avatar
Jürg Billeter committed
315
				get_value_function = "g_value_get_pointer";
Jürg Billeter's avatar
Jürg Billeter committed
316
			} else if (p.variable_type is ErrorType) {
317
				get_value_function = "g_value_get_pointer";
Jürg Billeter's avatar
Jürg Billeter committed
318
			} else if (dbus && DBusModule.get_type_signature (p.variable_type).has_prefix ("(")) {
319
				get_value_function = "g_value_get_boxed";
Jürg Billeter's avatar
Jürg Billeter committed
320 321
			} else if (dbus && p.variable_type.data_type is Enum) {
				var en = (Enum) p.variable_type.data_type;
322 323 324 325 326
				if (en.is_flags) {
					get_value_function = "g_value_get_uint";
				} else {
					get_value_function = "g_value_get_int";
				}
Jürg Billeter's avatar
Jürg Billeter committed
327
			} else {
Jürg Billeter's avatar
Jürg Billeter committed
328
				get_value_function = p.variable_type.data_type.get_get_value_function ();
Jürg Billeter's avatar
Jürg Billeter committed
329 330 331 332 333
			}
			var inner_fc = new CCodeFunctionCall (new CCodeIdentifier (get_value_function));
			inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
			fc.add_argument (inner_fc);
			i++;
334 335 336 337 338 339
			if (is_array && !dbus) {
				inner_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_int"));
				inner_fc.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("param_values"), new CCodeIdentifier (i.to_string ())));
				fc.add_argument (inner_fc);
				i++;
			}
Jürg Billeter's avatar
Jürg Billeter committed
340 341 342
		}
		fc.add_argument (new CCodeIdentifier ("data2"));
		
343
		if (return_type.data_type != null || return_type.is_array ()) {
Jürg Billeter's avatar
Jürg Billeter committed
344 345 346
			marshaller_body.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("v_return"), fc)));
			
			CCodeFunctionCall set_fc;
347
			if (return_type.is_array ()) {
348
				if (dbus) {
349 350
					set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_boxed"));
				} else {
351
					if (((ArrayType) return_type).element_type.data_type == string_type.data_type) {
352 353 354 355
						set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_boxed"));
					} else {
						set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_pointer"));
					}
356 357
				}
			} else if (return_type.type_parameter != null) {
Jürg Billeter's avatar
Jürg Billeter committed
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) {
Jürg Billeter's avatar
Jürg Billeter committed
362
				set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_take_string"));
363 364
			} else if (return_type.data_type is Class || return_type.data_type is Interface) {
				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 368 369 370 371 372 373
			} else if (dbus && return_type.data_type is Enum) {
				var en = (Enum) return_type.data_type;
				if (en.is_flags) {
					set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_uint"));
				} else {
					set_fc = new CCodeFunctionCall (new CCodeIdentifier ("g_value_set_int"));
				}
Jürg Billeter's avatar
Jürg Billeter committed
374
			} else {
375
				set_fc = new CCodeFunctionCall (new CCodeIdentifier (return_type.data_type.get_set_value_function ()));
Jürg Billeter's avatar
Jürg Billeter committed
376 377 378 379 380 381 382 383 384 385 386
			}
			set_fc.add_argument (new CCodeIdentifier ("return_value"));
			set_fc.add_argument (new CCodeIdentifier ("v_return"));
			
			marshaller_body.add_statement (new CCodeExpressionStatement (set_fc));
		} else {
			marshaller_body.add_statement (new CCodeExpressionStatement (fc));
		}
		
		signal_marshaller.block = marshaller_body;
		
387 388
		source_signal_marshaller_definition.append (signal_marshaller);
		user_marshal_set.add (signature);
Jürg Billeter's avatar
Jürg Billeter committed
389
	}
390 391 392

	public override CCodeFunctionCall get_signal_creation (Signal sig, TypeSymbol type) {	
		var csignew = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_new"));
393
		var cl = sig.parent_symbol as Class;
394 395
		csignew.add_argument (new CCodeConstant ("\"%s\"".printf (sig.get_cname ())));
		csignew.add_argument (new CCodeIdentifier (type.get_type_id ()));
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
		string[] flags = new string[0];
		if (sig.run_type == "first") {
			flags += "G_SIGNAL_RUN_FIRST";
		} else if (sig.run_type == "cleanup") {
			flags += "G_SIGNAL_RUN_CLEANUP";
		} else {
			flags += "G_SIGNAL_RUN_LAST";
		}
		if (sig.is_detailed) {
			flags += "G_SIGNAL_DETAILED";
		}

		if (sig.no_recurse) {
			flags += "G_SIGNAL_NO_RECURSE";
		}

		if (sig.is_action) {
			flags += "G_SIGNAL_ACTION";
		}

		if (sig.no_hooks) {
			flags += "G_SIGNAL_NO_HOOKS";
		}

		csignew.add_argument (new CCodeConstant (string.joinv (" | ", flags)));

422 423 424 425 426 427 428 429
		if (sig.default_handler == null) {
			csignew.add_argument (new CCodeConstant ("0"));
		} else {
			var struct_offset = new CCodeFunctionCall (new CCodeIdentifier ("G_STRUCT_OFFSET"));
			struct_offset.add_argument (new CCodeIdentifier ("%sClass".printf (cl.get_cname ())));
			struct_offset.add_argument (new CCodeIdentifier (sig.default_handler.vfunc_name));
			csignew.add_argument (struct_offset);
		}
430 431 432
		csignew.add_argument (new CCodeConstant ("NULL"));
		csignew.add_argument (new CCodeConstant ("NULL"));

Jürg Billeter's avatar
Jürg Billeter committed
433
		string marshaller = get_marshaller_function (sig.get_parameters (), sig.return_type);
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451

		var marshal_arg = new CCodeIdentifier (marshaller);
		csignew.add_argument (marshal_arg);

		var params = sig.get_parameters ();
		if (sig.return_type is PointerType || sig.return_type.type_parameter != null) {
			csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
		} else if (sig.return_type is ErrorType) {
			csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
		} else if (sig.return_type.data_type == null) {
			csignew.add_argument (new CCodeConstant ("G_TYPE_NONE"));
		} else {
			csignew.add_argument (new CCodeConstant (sig.return_type.data_type.get_type_id ()));
		}

		int params_len = 0;
		foreach (FormalParameter param in params) {
			params_len++;
Jürg Billeter's avatar
Jürg Billeter committed
452
			if (param.variable_type.is_array ()) {
453 454 455 456 457 458
				params_len++;
			}
		}

		csignew.add_argument (new CCodeConstant ("%d".printf (params_len)));
		foreach (FormalParameter param in params) {
Jürg Billeter's avatar
Jürg Billeter committed
459 460
			if (param.variable_type.is_array ()) {
				if (((ArrayType) param.variable_type).element_type.data_type == string_type.data_type) {
461 462 463 464 465
					csignew.add_argument (new CCodeConstant ("G_TYPE_STRV"));
				} else {
					csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
				}
				csignew.add_argument (new CCodeConstant ("G_TYPE_INT"));
Jürg Billeter's avatar
Jürg Billeter committed
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"));
Jürg Billeter's avatar
Jürg Billeter committed
468
			} else if (param.variable_type is ErrorType) {
469 470
				csignew.add_argument (new CCodeConstant ("G_TYPE_POINTER"));
			} else {
Jürg Billeter's avatar
Jürg Billeter committed
471
				csignew.add_argument (new CCodeConstant (param.variable_type.data_type.get_type_id ()));
472 473 474 475 476 477 478
			}
		}

		marshal_arg.name = marshaller;

		return csignew;
	}
479

480 481 482 483
	public virtual CCodeExpression get_dbus_g_type (DataType data_type) {
		return new CCodeConstant (data_type.data_type.get_type_id ());
	}

484 485 486 487 488 489
	public override void visit_element_access (ElementAccess expr) {
		if (expr.container is MemberAccess && expr.container.symbol_reference is Signal) {
			// detailed signal emission
			var sig = (Signal) expr.symbol_reference;
			var ma = (MemberAccess) expr.container;

490 491
			var detail_expr = expr.get_indices ().get (0);
			var signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
492 493
			
			var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit_by_name"));
494
			ccall.add_argument ((CCodeExpression) ma.inner.ccodenode);
495 496 497
			if (signal_name_cexpr != null) {
				ccall.add_argument (signal_name_cexpr);
			}
498 499 500 501 502
			expr.ccodenode = ccall;
		} else {
			base.visit_element_access (expr);
		}
	}
503

504 505 506
	bool in_gobject_instance (Method m) {
		bool result = false;
		if (m.binding == MemberBinding.INSTANCE) {
Jürg Billeter's avatar
Jürg Billeter committed
507
			result = m.this_parameter.variable_type.data_type.is_subtype_of (gobject_type);
508 509 510 511
		}
		return result;
	}

512 513 514 515 516 517
	CCodeExpression? emit_signal_assignment (Assignment assignment) {
		var sig = (Signal) assignment.left.symbol_reference;

		bool disconnect = false;

		if (assignment.operator == AssignmentOperator.ADD) {
518
			// connect
519
		} else if (assignment.operator == AssignmentOperator.SUB) {
520
			// disconnect
521 522 523 524 525 526 527
			disconnect = true;
		} else {
			assignment.error = true;
			Report.error (assignment.source_reference, "Specified compound assignment type for signals not supported.");
			return null;
		}

528
		return connect_signal (sig, assignment.left, assignment.right, disconnect, false, assignment);
529 530 531 532 533 534 535 536 537 538 539 540 541 542
	}

	public override void visit_assignment (Assignment assignment) {
		if (assignment.left.symbol_reference is Signal) {
			if (assignment.left.error || assignment.right.error) {
				assignment.error = true;
				return;
			}

			assignment.ccodenode = emit_signal_assignment (assignment);
		} else {
			base.visit_assignment (assignment);
		}
	}
543 544

	public override void visit_member_access (MemberAccess expr) {
545 546
		if (expr.symbol_reference is Signal) {
			CCodeExpression pub_inst = null;
547
	
548 549 550
			if (expr.inner != null) {
				pub_inst = (CCodeExpression) expr.inner.ccodenode;
			}
551 552 553 554 555

			var sig = (Signal) expr.symbol_reference;
			var cl = (TypeSymbol) sig.parent_symbol;
			
			if (expr.inner is BaseAccess && sig.is_virtual) {
556
				var m = sig.default_handler;
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
				var base_class = (Class) m.parent_symbol;
				var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (base_class.get_upper_case_cname (null))));
				vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (current_class.get_lower_case_cname (null))));
				
				expr.ccodenode = new CCodeMemberAccess.pointer (vcast, m.name);
				return;
			}

			if (sig.has_emitter) {
				var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_%s".printf (cl.get_lower_case_cname (null), sig.name)));

				ccall.add_argument (pub_inst);
				expr.ccodenode = ccall;
			} else {
				var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit_by_name"));
572
				ccall.add_argument (pub_inst);
573 574 575 576 577 578 579 580 581

				ccall.add_argument (sig.get_canonical_cconstant ());
				
				expr.ccodenode = ccall;
			}
		} else {
			base.visit_member_access (expr);
		}
	}
582 583 584 585 586 587 588 589 590 591 592 593 594 595

	public override void visit_method_call (MethodCall expr) {
		var method_type = expr.call.value_type as MethodType;

		if (method_type == null || !(method_type.method_symbol.parent_symbol is Signal)) {
			// no signal connect/disconnect call
			base.visit_method_call (expr);
			return;
		}

		var sig = (Signal) method_type.method_symbol.parent_symbol;
		var signal_access = ((MemberAccess) expr.call).inner;
		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");
598

599
		expr.ccodenode = connect_signal (sig, signal_access, handler, disconnect, after, expr);
600 601
	}

602
	CCodeExpression? connect_signal (Signal sig, Expression signal_access, Expression handler, bool disconnect, bool after, CodeNode expr) {
603
		string connect_func;
604 605

		var m = (Method) handler.symbol_reference;
606 607 608 609

		if (!disconnect) {
			// connect
			if (sig is DynamicSignal) {
610
				if (!after)
Jürg Billeter's avatar
Jürg Billeter committed
611
					connect_func = get_dynamic_signal_connect_wrapper_name ((DynamicSignal) sig);
612
				else
Jürg Billeter's avatar
Jürg Billeter committed
613
					connect_func = get_dynamic_signal_connect_after_wrapper_name ((DynamicSignal) sig);
614
			} else {
615 616 617
				if (m.closure) {
					connect_func = "g_signal_connect_data";
				} else if (in_gobject_instance (m)) {
618
					connect_func = "g_signal_connect_object";
619
				} else if (!after) {
620
					connect_func = "g_signal_connect";
621 622
				} else
					connect_func = "g_signal_connect_after";
623 624 625 626
			}
		} else {
			// disconnect
			if (sig is DynamicSignal) {
Jürg Billeter's avatar
Jürg Billeter committed
627
				connect_func = get_dynamic_signal_disconnect_wrapper_name ((DynamicSignal) sig);
628 629 630 631 632 633 634
			} else {
				connect_func = "g_signal_handlers_disconnect_matched";
			}
		}

		var ccall = new CCodeFunctionCall (new CCodeIdentifier (connect_func));

635
		CCodeExpression signal_name_cexpr = null;
636 637 638 639 640 641

		// first argument: instance of sender
		MemberAccess ma;
		if (signal_access is ElementAccess) {
			var ea = (ElementAccess) signal_access;
			ma = (MemberAccess) ea.container;
642 643 644
			var detail_expr = ea.get_indices ().get (0);
			signal_name_cexpr = get_signal_name_cexpression (sig, detail_expr, expr);
			if (signal_name_cexpr == null) {
645
				return null;
646 647 648
			}
		} else {
			ma = (MemberAccess) signal_access;
649
			signal_name_cexpr = get_signal_name_cexpression (sig, null, expr);
650 651 652 653
		}
		if (ma.inner != null) {
			ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner));
		} else {
654
			ccall.add_argument (get_result_cexpression ("self"));
655 656
		}

657 658
		CCodeCommaExpression? ccomma = null;

659 660 661 662 663 664 665 666 667
		if (sig is DynamicSignal) {
			// dynamic_signal_connect or dynamic_signal_disconnect

			// second argument: signal name
			ccall.add_argument (new CCodeConstant ("\"%s\"".printf (sig.name)));
		} else if (!disconnect) {
			// g_signal_connect_object or g_signal_connect

			// second argument: signal name
668
			ccall.add_argument (signal_name_cexpr);
669 670 671 672
		} else {
			// g_signal_handlers_disconnect_matched

			// second argument: mask
673
			if (!(signal_access is ElementAccess)) {
674 675 676 677 678 679
				ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
			} else {
				ccall.add_argument (new CCodeConstant ("G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA"));
			}

			// get signal id
680
			ccomma = new CCodeCommaExpression ();
681
			var temp_decl = get_temp_variable (uint_type);
682
			temp_vars.add (temp_decl);
683
			var parse_call = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_parse_name"));
684
			parse_call.add_argument (signal_name_cexpr);
685 686
			var decl_type = (TypeSymbol) sig.parent_symbol;
			parse_call.add_argument (new CCodeIdentifier (decl_type.get_type_id ()));
687
			parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_decl.name)));
688
			LocalVariable? detail_temp_decl = null;
689
			if (!(signal_access is ElementAccess)) {
690
				parse_call.add_argument (new CCodeConstant ("NULL"));
691
				parse_call.add_argument (new CCodeConstant ("FALSE"));
692
			} else {
693
				detail_temp_decl = get_temp_variable (gquark_type);
694
				temp_vars.add (detail_temp_decl);
695
				parse_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (detail_temp_decl.name)));
696
				parse_call.add_argument (new CCodeConstant ("TRUE"));
697 698 699 700
			}
			ccomma.append_expression (parse_call);

			// third argument: signal_id
701
			ccall.add_argument (get_variable_cexpression (temp_decl.name));
702 703

			// fourth argument: detail
704 705 706 707 708
			if (detail_temp_decl == null) {
				ccall.add_argument (new CCodeConstant ("0"));
			} else {
				ccall.add_argument (get_variable_cexpression (detail_temp_decl.name));
			}
709 710 711 712 713 714 715
			// fifth argument: closure
			ccall.add_argument (new CCodeConstant ("NULL"));
		}

		// third resp. sixth argument: handler
		ccall.add_argument (new CCodeCastExpression ((CCodeExpression) handler.ccodenode, "GCallback"));

716 717 718 719
		if (m.closure) {
			// g_signal_connect_data

			// fourth argument: user_data
720 721
			CCodeExpression handler_destroy_notify;
			ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
722 723

			// fifth argument: destroy_notify
724
			ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
725 726

			// sixth argument: connect_flags
727 728 729 730
			if (!after)
				ccall.add_argument (new CCodeConstant ("0"));
			else
				ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
731
		} else if (m.binding == MemberBinding.INSTANCE) {
732 733 734 735 736 737 738 739 740
			// g_signal_connect_object or g_signal_handlers_disconnect_matched
			// or dynamic_signal_connect or dynamic_signal_disconnect

			// fourth resp. seventh argument: object/user_data
			if (handler is MemberAccess) {
				var right_ma = (MemberAccess) handler;
				if (right_ma.inner != null) {
					ccall.add_argument ((CCodeExpression) right_ma.inner.ccodenode);
				} else {
741
					ccall.add_argument (get_result_cexpression ("self"));
742 743
				}
			} else if (handler is LambdaExpression) {
744
				ccall.add_argument (get_result_cexpression ("self"));
745 746
			}
			if (!disconnect && !(sig is DynamicSignal)
747
			    && in_gobject_instance (m)) {
748 749 750
				// g_signal_connect_object

				// fifth argument: connect_flags
751 752 753 754
				if (!after)
					ccall.add_argument (new CCodeConstant ("0"));
				else
					ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
755 756
			}
		} else {
757
			// g_signal_connect or g_signal_connect_after or g_signal_handlers_disconnect_matched
758 759 760 761 762 763
			// or dynamic_signal_connect or dynamic_signal_disconnect

			// fourth resp. seventh argument: user_data
			ccall.add_argument (new CCodeConstant ("NULL"));
		}

764 765 766 767 768 769
		if (ccomma != null) {
			ccomma.append_expression (ccall);
			return ccomma;
		} else {
			return ccall;
		}
770
	}
Jürg Billeter's avatar
Jürg Billeter committed
771 772
}