valaccodedelegatemodule.vala 19.1 KB
Newer Older
1 2
/* valaccodedelegatemodule.vala
 *
3
 * Copyright (C) 2006-2010  Jürg Billeter
4
 * Copyright (C) 2006-2008  Raffaele Sandrini
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 * 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
 * version 2.1 of the License, or (at your option) any later version.

 * 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>
 *	Raffaele Sandrini <raffaele@sandrini.ch>
 */


/**
 * The link between an assignment and generated code.
 */
29
public class Vala.CCodeDelegateModule : CCodeArrayModule {
30
	public override void generate_delegate_declaration (Delegate d, CCodeFile decl_space) {
31
		if (add_symbol_declaration (decl_space, d, get_ccode_name (d))) {
32 33
			return;
		}
34

35 36 37 38 39
		// internally generated delegates don't require a typedef
		if (d.sender_type != null) {
			return;
		}

40 41
		generate_type_declaration (new DelegateType (d), decl_space);

42
		string return_type_cname = get_ccode_name (d.return_type);
43

44 45 46 47 48
		if (d.return_type.is_real_non_null_struct_type ()) {
			// structs are returned via out parameter
			return_type_cname = "void";
		}

49
		if (return_type_cname == get_ccode_name (d)) {
50 51 52 53 54
			// recursive delegate
			return_type_cname = "GCallback";
		} else {
			generate_type_declaration (d.return_type, decl_space);
		}
55

56 57
		var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);

58
		var cfundecl = new CCodeFunctionDeclarator (get_ccode_name (d));
59
		foreach (Parameter param in d.get_parameters ()) {
60 61
			generate_parameter (param, decl_space, cparam_map, null);
		}
62

63
		// FIXME partial code duplication with CCodeMethodModule.generate_cparameters
64

65 66 67 68 69
		if (d.return_type.is_real_non_null_struct_type ()) {
			// structs are returned via out parameter
			var cparam = new CCodeParameter ("result", get_ccode_name (d.return_type) + "*");
			cparam_map.set (get_param_pos (-3), cparam);
		} else if (get_ccode_array_length (d) && d.return_type is ArrayType) {
70 71
			// return array length if appropriate
			var array_type = (ArrayType) d.return_type;
72
			var length_ctype = (get_ccode_array_length_type (d) ?? get_ccode_array_length_type (array_type)) + "*";
73 74

			for (int dim = 1; dim <= array_type.rank; dim++) {
75
				var cparam = new CCodeParameter (get_array_length_cname ("result", dim), length_ctype);
76
				cparam_map.set (get_param_pos (get_ccode_array_length_pos (d) + 0.01 * dim), cparam);
77
			}
78 79 80
		} else if (d.return_type is DelegateType) {
			// return delegate target if appropriate
			var deleg_type = (DelegateType) d.return_type;
81
			if (deleg_type.delegate_symbol.has_target) {
82
				var cparam = new CCodeParameter (get_delegate_target_cname ("result"), get_ccode_name (delegate_target_type) + "*");
83
				cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (d)), cparam);
84
				if (deleg_type.is_disposable ()) {
85
					cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), get_ccode_name (delegate_target_destroy_type) + "*");
86
					cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (d) + 0.01), cparam);
87
				}
88
			}
89
		}
90

91
		if (d.has_target) {
92
			var cparam = new CCodeParameter ("user_data", get_ccode_name (delegate_target_type));
93
			cparam_map.set (get_param_pos (get_ccode_instance_pos (d)), cparam);
94
		}
95
		if (d.tree_can_fail) {
96
			var cparam = new CCodeParameter ("error", "GError**");
97
			cparam_map.set (get_param_pos (get_ccode_error_pos (d)), cparam);
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
		}

		// append C parameters in the right order
		int last_pos = -1;
		int min_pos;
		while (true) {
			min_pos = -1;
			foreach (int pos in cparam_map.get_keys ()) {
				if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
					min_pos = pos;
				}
			}
			if (min_pos == -1) {
				break;
			}
			cfundecl.add_parameter (cparam_map.get (min_pos));
			last_pos = min_pos;
115
		}
116

117
		var ctypedef = new CCodeTypeDefinition (return_type_cname, cfundecl);
118
		ctypedef.modifiers |= (d.version.deprecated ? CCodeModifiers.DEPRECATED : 0);
119

120
		decl_space.add_type_declaration (ctypedef);
121 122 123
	}

	public override void visit_delegate (Delegate d) {
124
		d.accept_children (this);
125

126
		generate_delegate_declaration (d, cfile);
127 128

		if (!d.is_internal_symbol ()) {
129
			generate_delegate_declaration (d, header_file);
130
		}
131
		if (!d.is_private_symbol ()) {
132
			generate_delegate_declaration (d, internal_header_file);
133
		}
134 135 136 137 138 139
	}

	public override string get_delegate_target_cname (string delegate_cname) {
		return "%s_target".printf (delegate_cname);
	}

140
	public override CCodeExpression get_delegate_target_cexpression (Expression delegate_expr, out CCodeExpression delegate_target_destroy_notify) {
141 142 143 144 145 146 147
		delegate_target_destroy_notify = get_delegate_target_destroy_notify_cvalue (delegate_expr.target_value);
		return get_delegate_target_cvalue (delegate_expr.target_value);
	}

	public override CCodeExpression get_delegate_target_cvalue (TargetValue value) {
		return ((GLibValue) value).delegate_target_cvalue;
	}
148

149 150
	public override CCodeExpression get_delegate_target_destroy_notify_cvalue (TargetValue value) {
		return ((GLibValue) value).delegate_target_destroy_notify_cvalue;
151 152 153 154 155
	}

	public override string get_delegate_target_destroy_notify_cname (string delegate_cname) {
		return "%s_target_destroy_notify".printf (delegate_cname);
	}
156

157
	public override CCodeExpression get_implicit_cast_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, CodeNode? node) {
158 159 160 161 162 163 164 165 166 167 168
		if (target_type is DelegateType && expression_type is MethodType) {
			var dt = (DelegateType) target_type;
			var mt = (MethodType) expression_type;

			var method = mt.method_symbol;
			if (method.base_method != null) {
				method = method.base_method;
			} else if (method.base_interface_method != null) {
				method = method.base_interface_method;
			}

169
			return new CCodeIdentifier (generate_delegate_wrapper (method, dt, node));
170
		}
171

172
		return base.get_implicit_cast_expression (source_cexpr, expression_type, target_type, node);
173 174
	}

175
	public string generate_delegate_wrapper (Method m, DelegateType dt, CodeNode? node) {
176
		var d = dt.delegate_symbol;
177 178 179 180
		string delegate_name;
		var sig = d.parent_symbol as Signal;
		var dynamic_sig = sig as DynamicSignal;
		if (dynamic_sig != null) {
181
			delegate_name = get_dynamic_signal_cname (dynamic_sig);
182
		} else if (sig != null) {
183
			delegate_name = get_ccode_lower_case_prefix (sig.parent_symbol) + get_ccode_lower_case_name (sig);
184
		} else {
185
			delegate_name = Symbol.camel_case_to_lower_case (get_ccode_name (d));
186 187
		}

188
		string wrapper_name = "_%s_%s".printf (get_ccode_name (m), delegate_name);
189 190 191 192 193 194 195 196

		if (!add_wrapper (wrapper_name)) {
			// wrapper already defined
			return wrapper_name;
		}

		// declaration

197
		string return_type_cname = get_ccode_name (d.return_type);
198 199 200 201 202 203 204

		if (d.return_type.is_real_non_null_struct_type ()) {
			// structs are returned via out parameter
			return_type_cname = "void";
		}

		var function = new CCodeFunction (wrapper_name, return_type_cname);
205 206
		function.modifiers = CCodeModifiers.STATIC;

207 208
		push_function (function);

209
		var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
210 211

		if (d.has_target) {
212
			var cparam = new CCodeParameter ("self", get_ccode_name (delegate_target_type));
213
			cparam_map.set (get_param_pos (get_ccode_instance_pos (d)), cparam);
214 215
		}

216
		if (d.sender_type != null) {
217
			var param = new Parameter ("_sender", d.sender_type);
218
			generate_parameter (param, cfile, cparam_map, null);
219 220
		}

221
		var d_params = d.get_parameters ();
222
		foreach (Parameter param in d_params) {
223
			if (dynamic_sig != null
Jürg Billeter's avatar
Jürg Billeter committed
224 225
			    && param.variable_type is ArrayType
			    && ((ArrayType) param.variable_type).element_type.data_type == string_type.data_type) {
226
				// use null-terminated string arrays for dynamic signals for compatibility reasons
227 228
				param.set_attribute_bool ("CCode", "array_length", false);
				param.set_attribute_bool ("CCode", "array_null_terminated", true);
229 230
			}

231
			generate_parameter (param, cfile, cparam_map, null);
232
		}
233
		if (get_ccode_array_length (d) && d.return_type is ArrayType) {
234 235
			// return array length if appropriate
			var array_type = (ArrayType) d.return_type;
236
			var length_ctype = (get_ccode_array_length_type (d) ?? get_ccode_array_length_type (array_type)) + "*";
237 238

			for (int dim = 1; dim <= array_type.rank; dim++) {
239
				var cparam = new CCodeParameter (get_array_length_cname ("result", dim), length_ctype);
240
				cparam_map.set (get_param_pos (get_ccode_array_length_pos (d) + 0.01 * dim), cparam);
241
			}
242 243 244 245 246
		} else if (d.return_type is DelegateType) {
			// return delegate target if appropriate
			var deleg_type = (DelegateType) d.return_type;

			if (deleg_type.delegate_symbol.has_target) {
247
				var cparam = new CCodeParameter (get_delegate_target_cname ("result"), get_ccode_name (delegate_target_type) + "*");
248
				cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (d)), cparam);
249
				if (deleg_type.is_disposable ()) {
250
					cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), get_ccode_name (delegate_target_destroy_type) + "*");
251
					cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (d) + 0.01), cparam);
252
				}
253
			}
254
		} else if (d.return_type.is_real_non_null_struct_type ()) {
255
			var cparam = new CCodeParameter ("result", "%s*".printf (get_ccode_name (d.return_type)));
256
			cparam_map.set (get_param_pos (-3), cparam);
257
		}
258

259
		if (m.tree_can_fail) {
260
			var cparam = new CCodeParameter ("error", "GError**");
261
			cparam_map.set (get_param_pos (get_ccode_error_pos (d)), cparam);
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
		}

		// append C parameters in the right order
		int last_pos = -1;
		int min_pos;
		while (true) {
			min_pos = -1;
			foreach (int pos in cparam_map.get_keys ()) {
				if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
					min_pos = pos;
				}
			}
			if (min_pos == -1) {
				break;
			}
			function.add_parameter (cparam_map.get (min_pos));
			last_pos = min_pos;
		}


		// definition

		var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);

		int i = 0;
287
		if (m.binding == MemberBinding.INSTANCE || m.closure) {
288 289 290
			CCodeExpression arg;
			if (d.has_target) {
				arg = new CCodeIdentifier ("self");
291 292 293
				if (!m.closure && m.this_parameter != null) {
					arg = convert_from_generic_pointer (arg, m.this_parameter.variable_type);
				}
294 295
			} else {
				// use first delegate parameter as instance
296
				if (d_params.size == 0 || m.closure) {
297
					Report.error (node != null ? node.source_reference : null, "Cannot create delegate without target for instance method or closure");
298 299
					arg = new CCodeConstant ("NULL");
				} else {
300
					arg = new CCodeIdentifier (get_ccode_name (d_params.get (0)));
301 302
					i = 1;
				}
303
			}
304
			carg_map.set (get_param_pos (get_ccode_instance_pos (m)), arg);
305 306
		}

307 308
		bool first = true;

309
		foreach (Parameter param in m.get_parameters ()) {
310 311
			if (first && d.sender_type != null && m.get_parameters ().size == d.get_parameters ().size + 1) {
				// sender parameter
312
				carg_map.set (get_param_pos (get_ccode_pos (param)), new CCodeIdentifier ("_sender"));
313 314 315 316 317

				first = false;
				continue;
			}

318
			CCodeExpression arg;
319
			arg = new CCodeIdentifier (get_ccode_name (d_params.get (i)));
320 321 322
			if (d_params.get (i).variable_type is GenericType) {
				arg = convert_from_generic_pointer (arg, param.variable_type);
			}
323
			carg_map.set (get_param_pos (get_ccode_pos (param)), arg);
324 325

			// handle array arguments
326
			if (get_ccode_array_length (param) && param.variable_type is ArrayType) {
Jürg Billeter's avatar
Jürg Billeter committed
327
				var array_type = (ArrayType) param.variable_type;
328 329
				for (int dim = 1; dim <= array_type.rank; dim++) {
					CCodeExpression clength;
330
					if (get_ccode_array_null_terminated (d_params.get (i))) {
331 332 333 334
						requires_array_length = true;
						var len_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_array_length"));
						len_call.add_argument (new CCodeIdentifier (d_params.get (i).name));
						clength = len_call;
335
					} else if (!get_ccode_array_length (d_params.get (i))) {
336 337
						clength = new CCodeConstant ("-1");
					} else {
338
						clength = new CCodeIdentifier (get_parameter_array_length_cname (d_params.get (i), dim));
339
					}
340
					carg_map.set (get_param_pos (get_ccode_array_length_pos (param) + 0.01 * dim), clength);
341
				}
Jürg Billeter's avatar
Jürg Billeter committed
342 343
			} else if (param.variable_type is DelegateType) {
				var deleg_type = (DelegateType) param.variable_type;
344 345

				if (deleg_type.delegate_symbol.has_target) {
346
					var ctarget = new CCodeIdentifier (get_ccode_delegate_target_name (d_params.get (i)));
347
					carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), ctarget);
348
					if (deleg_type.is_disposable ()) {
349
						var ctarget_destroy_notify = new CCodeIdentifier (get_ccode_delegate_target_destroy_notify_name (d_params.get (i)));
350
						carg_map.set (get_param_pos (get_ccode_delegate_target_pos (m) + 0.01), ctarget_destroy_notify);
351
					}
352
				}
353 354 355 356
			}

			i++;
		}
357
		if (get_ccode_array_length (m) && m.return_type is ArrayType) {
358 359 360
			var array_type = (ArrayType) m.return_type;
			for (int dim = 1; dim <= array_type.rank; dim++) {
				CCodeExpression clength;
361
				if (!get_ccode_array_length (d)) {
362 363
					clength = new CCodeConstant ("NULL");
				} else {
364
					clength = new CCodeIdentifier (get_array_length_cname ("result", dim));
365
				}
366
				carg_map.set (get_param_pos (get_ccode_array_length_pos (m) + 0.01 * dim), clength);
367
			}
368 369 370 371 372
		} else if (m.return_type is DelegateType) {
			var deleg_type = (DelegateType) m.return_type;

			if (deleg_type.delegate_symbol.has_target) {
				var ctarget = new CCodeIdentifier (get_delegate_target_cname ("result"));
373
				carg_map.set (get_param_pos (get_ccode_delegate_target_pos (m)), ctarget);
374
				if (deleg_type.is_disposable ()) {
375
					var ctarget_destroy_notify = new CCodeIdentifier (get_delegate_target_destroy_notify_cname ("result"));
376
					carg_map.set (get_param_pos (get_ccode_delegate_target_pos (m) + 0.01), ctarget_destroy_notify);
377
				}
378
			}
379 380
		} else if (m.return_type.is_real_non_null_struct_type ()) {
			carg_map.set (get_param_pos (-3), new CCodeIdentifier ("result"));
381
		}
382

383
		if (m.tree_can_fail) {
384
			carg_map.set (get_param_pos (get_ccode_error_pos (m)), new CCodeIdentifier ("error"));
385 386
		}

387
		var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (m)));
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404

		// append C arguments in the right order
		last_pos = -1;
		while (true) {
			min_pos = -1;
			foreach (int pos in carg_map.get_keys ()) {
				if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
					min_pos = pos;
				}
			}
			if (min_pos == -1) {
				break;
			}
			ccall.add_argument (carg_map.get (min_pos));
			last_pos = min_pos;
		}

405 406 407 408
		if (m.coroutine) {
			ccall.add_argument (new CCodeConstant ("NULL"));
			ccall.add_argument (new CCodeConstant ("NULL"));
		}
409

410
		if (m.return_type is VoidType || m.return_type.is_real_non_null_struct_type ()) {
411
			ccode.add_expression (ccall);
412 413 414 415
			if (!(d.return_type is VoidType || d.return_type.is_real_non_null_struct_type ())) {
				// return a default value
				ccode.add_declaration (return_type_cname, new CCodeVariableDeclarator ("result", default_value_for_type (d.return_type, true)));
			}
416
		} else {
417 418 419 420 421
			CCodeExpression result = ccall;
			if (d.return_type is GenericType) {
				result = convert_to_generic_pointer (result, m.return_type);
			}
			ccode.add_declaration (return_type_cname, new CCodeVariableDeclarator ("result", result));
422 423
		}

424
		if (d.has_target /* TODO: && dt.value_owned */ && dt.is_called_once) {
425
			// destroy notify "self" after the call
426
			CCodeExpression? destroy_notify = null;
427 428 429
			if (m.closure) {
				int block_id = get_block_id (current_closure_block);
				destroy_notify = new CCodeIdentifier ("block%d_data_unref".printf (block_id));
430
			} else if (get_this_type () != null && m.binding != MemberBinding.STATIC && !m.is_async_callback && is_reference_counting (m.this_parameter.variable_type.data_type)) {
431
				destroy_notify = get_destroy_func_expression (m.this_parameter.variable_type);
432 433 434 435 436
			}

			if (destroy_notify != null) {
				var unref_call = new CCodeFunctionCall (destroy_notify);
				unref_call.add_argument (new CCodeIdentifier ("self"));
437
				ccode.add_expression (unref_call);
438 439 440
			}
		}

441 442
		if (!(m.return_type is VoidType || m.return_type.is_real_non_null_struct_type ()) ||
			!(d.return_type is VoidType || d.return_type.is_real_non_null_struct_type ())) {
443
			ccode.add_return (new CCodeIdentifier ("result"));
444 445
		}

446
		pop_function ();
447

448
		// append to file
449
		cfile.add_function_declaration (function);
450
		cfile.add_function (function);
451 452 453

		return wrapper_name;
	}
454

455
	public override CCodeParameter generate_parameter (Parameter param, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
Jürg Billeter's avatar
Jürg Billeter committed
456
		if (!(param.variable_type is DelegateType || param.variable_type is MethodType)) {
Jürg Billeter's avatar
Jürg Billeter committed
457
			return base.generate_parameter (param, decl_space, cparam_map, carg_map);
458 459
		}

460 461
		generate_type_declaration (param.variable_type, decl_space);

462
		string ctypename = get_ccode_name (param.variable_type);
463 464
		string target_ctypename = get_ccode_name (delegate_target_type);
		string target_destroy_notify_ctypename = get_ccode_name (delegate_target_destroy_type);
465

466
		if (param.parent_symbol is Delegate
467
		    && get_ccode_name (param.variable_type) == get_ccode_name (param.parent_symbol)) {
468 469 470 471
			// recursive delegate
			ctypename = "GCallback";
		}

472 473
		if (param.direction != ParameterDirection.IN) {
			ctypename += "*";
Levi Bard's avatar
Levi Bard committed
474
			target_ctypename += "*";
475
			target_destroy_notify_ctypename += "*";
476 477
		}

478
		var main_cparam = new CCodeParameter (get_ccode_name (param), ctypename);
479

480
		cparam_map.set (get_param_pos (get_ccode_pos (param)), main_cparam);
481
		if (carg_map != null) {
482
			carg_map.set (get_param_pos (get_ccode_pos (param)), get_parameter_cexpression (param));
483 484
		}

Jürg Billeter's avatar
Jürg Billeter committed
485 486
		if (param.variable_type is DelegateType) {
			var deleg_type = (DelegateType) param.variable_type;
487

488
			generate_delegate_declaration (deleg_type.delegate_symbol, decl_space);
489

490
			if (deleg_type.delegate_symbol.has_target) {
491
				var cparam = new CCodeParameter (get_ccode_delegate_target_name (param), target_ctypename);
492
				cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), cparam);
493
				if (carg_map != null) {
494
					carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), get_cexpression (cparam.name));
495
				}
496
				if (deleg_type.is_disposable ()) {
497
					cparam = new CCodeParameter (get_ccode_delegate_target_destroy_notify_name (param), target_destroy_notify_ctypename);
498
					cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (param) + 0.01), cparam);
499
					if (carg_map != null) {
500
						carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param) + 0.01), get_cexpression (cparam.name));
501 502 503
					}
				}
			}
Jürg Billeter's avatar
Jürg Billeter committed
504
		} else if (param.variable_type is MethodType) {
505
			var cparam = new CCodeParameter (get_ccode_delegate_target_name (param), target_ctypename);
506
			cparam_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), cparam);
507
			if (carg_map != null) {
508
				carg_map.set (get_param_pos (get_ccode_delegate_target_pos (param)), get_cexpression (cparam.name));
509 510
			}
		}
Jürg Billeter's avatar
Jürg Billeter committed
511 512

		return main_cparam;
513
	}
514
}