valagvariantmodule.vala 34.9 KB
Newer Older
1 2
/* valagvariantmodule.vala
 *
3
 * Copyright (C) 2010-2011  Jürg Billeter
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
 *
 * 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>
 */

public class Vala.GVariantModule : GAsyncModule {
	struct BasicTypeInfo {
		public unowned string signature;
		public unowned string type_name;
		public bool is_string;
	}

	const BasicTypeInfo[] basic_types = {
		{ "y", "byte", false },
		{ "b", "boolean", false },
		{ "n", "int16", false },
		{ "q", "uint16", false },
		{ "i", "int32", false },
		{ "u", "uint32", false },
		{ "x", "int64", false },
		{ "t", "uint64", false },
		{ "d", "double", false },
		{ "s", "string", true },
		{ "o", "object_path", true },
		{ "g", "signature", true }
	};

	static bool is_string_marshalled_enum (TypeSymbol? symbol) {
		if (symbol != null && symbol is Enum) {
47
			return symbol.get_attribute_bool ("DBus", "use_string_marshalling");
48 49 50 51 52
		}
		return false;
	}

	string get_dbus_value (EnumValue value, string default_value) {
53 54 55
		var dbus_value = value.get_attribute_string ("DBus", "value");
		if (dbus_value != null) {
			return dbus_value;;
56
		}
57
		return default_value;
58 59 60
	}

	public static string? get_dbus_signature (Symbol symbol) {
61
		return symbol.get_attribute_string ("DBus", "signature");
62 63
	}

64 65 66 67 68 69 70
	bool get_basic_type_info (string? signature, out BasicTypeInfo basic_type) {
		if (signature != null) {
			foreach (BasicTypeInfo info in basic_types) {
				if (info.signature == signature) {
					basic_type = info;
					return true;
				}
71 72
			}
		}
73
		basic_type = BasicTypeInfo ();
74 75 76
		return false;
	}

77 78 79 80 81 82 83 84 85
	public static string? get_type_signature (DataType datatype, Symbol? symbol = null) {
		if (symbol != null) {
			string sig = get_dbus_signature (symbol);
			if (sig != null) {
				// allow overriding signature in attribute, used for raw GVariants
				return sig;
			}
		}

86 87 88 89 90 91 92 93 94 95 96 97 98
		var array_type = datatype as ArrayType;

		if (array_type != null) {
			string element_type_signature = get_type_signature (array_type.element_type);

			if (element_type_signature == null) {
				return null;
			}

			return string.nfill (array_type.rank, 'a') + element_type_signature;
		} else if (is_string_marshalled_enum (datatype.data_type)) {
			return "s";
		} else if (datatype.data_type != null) {
99
			string sig = datatype.data_type.get_attribute_string ("CCode", "type_signature");
100 101 102 103 104 105 106 107

			var st = datatype.data_type as Struct;
			var en = datatype.data_type as Enum;
			if (sig == null && st != null) {
				var str = new StringBuilder ();
				str.append_c ('(');
				foreach (Field f in st.get_fields ()) {
					if (f.binding == MemberBinding.INSTANCE) {
108
						str.append (get_type_signature (f.variable_type, f));
109 110 111 112 113 114 115 116 117 118 119 120 121
					}
				}
				str.append_c (')');
				sig = str.str;
			} else if (sig == null && en != null) {
				if (en.is_flags) {
					return "u";
				} else {
					return "i";
				}
			}

			var type_args = datatype.get_type_arguments ();
122
			if (sig != null && "%s" in sig && type_args.size > 0) {
123 124 125 126 127 128 129 130
				string element_sig = "";
				foreach (DataType type_arg in type_args) {
					var s = get_type_signature (type_arg);
					if (s != null) {
						element_sig += s;
					}
				}

131
				sig = sig.replace ("%s", element_sig);
132 133
			}

134 135 136 137 138 139 140
			if (sig == null &&
			    (datatype.data_type.get_full_name () == "GLib.UnixInputStream" ||
			     datatype.data_type.get_full_name () == "GLib.UnixOutputStream" ||
			     datatype.data_type.get_full_name () == "GLib.Socket")) {
				return "h";
			}

141 142 143 144 145 146 147 148 149 150 151
			return sig;
		} else {
			return null;
		}
	}

	public override void visit_enum (Enum en) {
		base.visit_enum (en);

		if (is_string_marshalled_enum (en)) {
			// strcmp
152
			cfile.add_include ("string.h");
153

154 155 156
			// for G_DBUS_ERROR
			cfile.add_include ("gio/gio.h");

157 158
			cfile.add_function (generate_enum_from_string_function (en));
			cfile.add_function (generate_enum_to_string_function (en));
159 160 161
		}
	}

162
	public override bool generate_enum_declaration (Enum en, CCodeFile decl_space) {
163 164
		if (base.generate_enum_declaration (en, decl_space)) {
			if (is_string_marshalled_enum (en)) {
165 166
				decl_space.add_function_declaration (generate_enum_from_string_function_declaration (en));
				decl_space.add_function_declaration (generate_enum_to_string_function_declaration (en));
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
			}
			return true;
		}
		return false;
	}

	CCodeExpression? get_array_length (CCodeExpression expr, int dim) {
		var id = expr as CCodeIdentifier;
		var ma = expr as CCodeMemberAccess;
		if (id != null) {
			return new CCodeIdentifier ("%s_length%d".printf (id.name, dim));
		} else if (ma != null) {
			if (ma.is_pointer) {
				return new CCodeMemberAccess.pointer (ma.inner, "%s_length%d".printf (ma.member_name, dim));
			} else {
				return new CCodeMemberAccess (ma.inner, "%s_length%d".printf (ma.member_name, dim));
			}
		} else {
			// must be NULL-terminated
			var len_call = new CCodeFunctionCall (new CCodeIdentifier ("g_strv_length"));
			len_call.add_argument (expr);
			return len_call;
		}
	}

192
	CCodeExpression? generate_enum_value_from_string (EnumValueType type, CCodeExpression? expr, CCodeExpression? error_expr) {
193
		var en = type.type_symbol as Enum;
194
		var from_string_name = "%s_from_string".printf (get_ccode_lower_case_name (en, null));
195 196 197

		var from_string_call = new CCodeFunctionCall (new CCodeIdentifier (from_string_name));
		from_string_call.add_argument (expr);
198
		from_string_call.add_argument (error_expr != null ? error_expr : new CCodeConstant ("NULL"));
199 200 201 202 203

		return from_string_call;
	}

	public CCodeFunction generate_enum_from_string_function_declaration (Enum en) {
204
		var from_string_name = "%s_from_string".printf (get_ccode_lower_case_name (en, null));
205

206
		var from_string_func = new CCodeFunction (from_string_name, get_ccode_name (en));
207
		from_string_func.add_parameter (new CCodeParameter ("str", "const char*"));
208
		from_string_func.add_parameter (new CCodeParameter ("error", "GError**"));
209 210 211 212 213

		return from_string_func;
	}

	public CCodeFunction generate_enum_from_string_function (Enum en) {
214
		var from_string_name = "%s_from_string".printf (get_ccode_lower_case_name (en, null));
215

216
		var from_string_func = new CCodeFunction (from_string_name, get_ccode_name (en));
217
		from_string_func.add_parameter (new CCodeParameter ("str", "const char*"));
218
		from_string_func.add_parameter (new CCodeParameter ("error", "GError**"));
219

220
		push_function (from_string_func);
221

222
		ccode.add_declaration (get_ccode_name (en), new CCodeVariableDeclarator.zero ("value", new CCodeConstant ("0")));
223

224
		bool firstif = true;
225 226 227 228 229
		foreach (EnumValue enum_value in en.get_values ()) {
			string dbus_value = get_dbus_value (enum_value, enum_value.name);
			var string_comparison = new CCodeFunctionCall (new CCodeIdentifier ("strcmp"));
			string_comparison.add_argument (new CCodeIdentifier ("str"));
			string_comparison.add_argument (new CCodeConstant ("\"%s\"".printf (dbus_value)));
230 231 232 233
			var cond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, string_comparison, new CCodeConstant ("0"));
			if (firstif) {
				ccode.open_if (cond);
				firstif = false;
234
			} else {
235
				ccode.else_if (cond);
236
			}
237
			ccode.add_assignment (new CCodeIdentifier ("value"), new CCodeIdentifier (get_ccode_name (enum_value)));
238 239
		}

240
		ccode.add_else ();
241 242 243 244
		var set_error = new CCodeFunctionCall (new CCodeIdentifier ("g_set_error"));
		set_error.add_argument (new CCodeIdentifier ("error"));
		set_error.add_argument (new CCodeIdentifier ("G_DBUS_ERROR"));
		set_error.add_argument (new CCodeIdentifier ("G_DBUS_ERROR_INVALID_ARGS"));
245
		set_error.add_argument (new CCodeConstant ("\"Invalid value for enum `%s'\"".printf (get_ccode_name (en))));
246 247
		ccode.add_expression (set_error);
		ccode.close ();
248

249
		ccode.add_return (new CCodeIdentifier ("value"));
250

251
		pop_function ();
252 253 254
		return from_string_func;
	}

255
	CCodeExpression deserialize_basic (BasicTypeInfo basic_type, CCodeExpression variant_expr, bool transfer = false) {
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
		var get_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_" + basic_type.type_name));
		get_call.add_argument (variant_expr);

		if (basic_type.is_string) {
			if (transfer) {
				get_call.call = new CCodeIdentifier ("g_variant_get_string");
			} else {
				get_call.call = new CCodeIdentifier ("g_variant_dup_string");
			}
			get_call.add_argument (new CCodeConstant ("NULL"));
		}

		return get_call;
	}

271
	CCodeExpression deserialize_array (ArrayType array_type, CCodeExpression variant_expr, CCodeExpression? expr) {
272 273 274 275
		if (array_type.rank == 1 && get_type_signature (array_type) == "ay") {
			return deserialize_buffer_array (array_type, variant_expr, expr);
		}

276 277 278
		string temp_name = "_tmp%d_".printf (next_temp_var_id++);

		var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_new"));
279
		new_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
280 281 282
		// add one extra element for NULL-termination
		new_call.add_argument (new CCodeConstant ("5"));

283
		var length_ctype = get_ccode_array_length_type (array_type);
284
		ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (temp_name, new_call));
285 286
		ccode.add_declaration (length_ctype, new CCodeVariableDeclarator (temp_name + "_length", new CCodeConstant ("0")));
		ccode.add_declaration (length_ctype, new CCodeVariableDeclarator (temp_name + "_size", new CCodeConstant ("4")));
287

288
		deserialize_array_dim (array_type, 1, temp_name, variant_expr, expr);
289 290 291 292 293

		if (array_type.element_type.is_reference_type_or_type_parameter ()) {
			// NULL terminate array
			var length = new CCodeIdentifier (temp_name + "_length");
			var element_access = new CCodeElementAccess (new CCodeIdentifier (temp_name), length);
294
			ccode.add_assignment (element_access, new CCodeIdentifier ("NULL"));
295 296 297 298 299
		}

		return new CCodeIdentifier (temp_name);
	}

300
	void deserialize_array_dim (ArrayType array_type, int dim, string temp_name, CCodeExpression variant_expr, CCodeExpression? expr) {
301 302 303
		string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
		string element_name = "_tmp%d_".printf (next_temp_var_id++);

304
		ccode.add_declaration (get_ccode_array_length_type (array_type), new CCodeVariableDeclarator ("%s_length%d".printf (temp_name, dim), new CCodeConstant ("0")));
305 306
		ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name));
		ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (element_name));
307 308 309 310

		var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_init"));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
		iter_call.add_argument (variant_expr);
311
		ccode.add_expression (iter_call);
312 313 314 315

		iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_next_value"));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));

316
		var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeAssignment (new CCodeIdentifier (element_name), iter_call), new CCodeConstant ("NULL"));
317 318
		var cforiter = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("%s_length%d".printf (temp_name, dim)));
		ccode.open_for (null, cforcond, cforiter);
319 320

		if (dim < array_type.rank) {
321
			deserialize_array_dim (array_type, dim + 1, temp_name, new CCodeIdentifier (element_name), expr);
322 323
		} else {
			var size_check = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier (temp_name + "_size"), new CCodeIdentifier (temp_name + "_length"));
324 325

			ccode.open_if (size_check);
326 327 328

			// tmp_size = (2 * tmp_size);
			var new_size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("2"), new CCodeIdentifier (temp_name + "_size"));
329
			ccode.add_assignment (new CCodeIdentifier (temp_name + "_size"), new_size);
330 331

			var renew_call = new CCodeFunctionCall (new CCodeIdentifier ("g_renew"));
332
			renew_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
333 334 335
			renew_call.add_argument (new CCodeIdentifier (temp_name));
			// add one extra element for NULL-termination
			renew_call.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier (temp_name + "_size"), new CCodeConstant ("1")));
336
			ccode.add_assignment (new CCodeIdentifier (temp_name), renew_call);
337

338
			ccode.close ();
339 340

			var element_access = new CCodeElementAccess (new CCodeIdentifier (temp_name), new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier (temp_name + "_length")));
341
			var element_expr = deserialize_expression (array_type.element_type, new CCodeIdentifier (element_name), null);
342
			ccode.add_assignment (element_access, element_expr);
343 344 345 346
		}

		var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
		unref.add_argument (new CCodeIdentifier (element_name));
347
		ccode.add_expression (unref);
348

349
		ccode.close ();
350 351

		if (expr != null) {
352
			ccode.add_assignment (get_array_length (expr, dim), new CCodeIdentifier ("%s_length%d".printf (temp_name, dim)));
353 354 355
		}
	}

356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
	CCodeExpression deserialize_buffer_array (ArrayType array_type, CCodeExpression variant_expr, CCodeExpression? expr) {
		string temp_name = "_tmp%d_".printf (next_temp_var_id++);

		var get_data_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_data"));
		get_data_call.add_argument (variant_expr);

		var get_size_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_size"));
		get_size_call.add_argument (variant_expr);
		ccode.add_declaration ("gsize", new CCodeVariableDeclarator (temp_name + "_length", get_size_call));
		var length = new CCodeIdentifier (temp_name + "_length");

		var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup"));
		dup_call.add_argument (get_data_call);
		dup_call.add_argument (length);

		ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (temp_name, dup_call));
		if (expr != null) {
			ccode.add_assignment (get_array_length (expr, 1), length);
		}

		return new CCodeIdentifier (temp_name);
	}

379
	CCodeExpression? deserialize_struct (Struct st, CCodeExpression variant_expr) {
380 381 382
		string temp_name = "_tmp%d_".printf (next_temp_var_id++);
		string subiter_name = "_tmp%d_".printf (next_temp_var_id++);

383
		ccode.add_declaration (get_ccode_name (st), new CCodeVariableDeclarator (temp_name));
384
		ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name));
385 386 387 388

		var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_init"));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
		iter_call.add_argument (variant_expr);
389
		ccode.add_expression (iter_call);
390 391 392 393 394 395 396 397 398 399

		bool field_found = false;;

		foreach (Field f in st.get_fields ()) {
			if (f.binding != MemberBinding.INSTANCE) {
				continue;
			}

			field_found = true;

400
			read_expression (f.variable_type, new CCodeIdentifier (subiter_name), new CCodeMemberAccess (new CCodeIdentifier (temp_name), get_ccode_name (f)), f);
401 402 403 404 405 406 407 408 409
		}

		if (!field_found) {
			return null;
		}

		return new CCodeIdentifier (temp_name);
	}

410
	CCodeExpression? deserialize_hash_table (ObjectType type, CCodeExpression variant_expr) {
411 412 413 414 415 416 417 418 419 420
		string temp_name = "_tmp%d_".printf (next_temp_var_id++);
		string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
		string key_name = "_tmp%d_".printf (next_temp_var_id++);
		string value_name = "_tmp%d_".printf (next_temp_var_id++);

		var type_args = type.get_type_arguments ();
		assert (type_args.size == 2);
		var key_type = type_args.get (0);
		var value_type = type_args.get (1);

421 422 423 424
		ccode.add_declaration ("GHashTable*", new CCodeVariableDeclarator (temp_name));
		ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name));
		ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (key_name));
		ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (value_name));
425 426

		var hash_table_new = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_new_full"));
427
		if (key_type.data_type.is_subtype_of (string_type.data_type)) {
428 429
			hash_table_new.add_argument (new CCodeIdentifier ("g_str_hash"));
			hash_table_new.add_argument (new CCodeIdentifier ("g_str_equal"));
430 431 432
		} else if (key_type.data_type == gvariant_type) {
			hash_table_new.add_argument (new CCodeIdentifier ("g_variant_hash"));
			hash_table_new.add_argument (new CCodeIdentifier ("g_variant_equal"));
433 434 435 436
		} else {
			hash_table_new.add_argument (new CCodeIdentifier ("g_direct_hash"));
			hash_table_new.add_argument (new CCodeIdentifier ("g_direct_equal"));
		}
437

438
		if (key_type.data_type.is_subtype_of (string_type.data_type)) {
439
			hash_table_new.add_argument (new CCodeIdentifier ("g_free"));
440 441
		} else if (key_type.data_type == gvariant_type) {
			hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_variant_unref"), "GDestroyNotify"));
442 443
		} else if (key_type.data_type.get_full_name () == "GLib.HashTable") {
			hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_hash_table_unref"), "GDestroyNotify"));
444 445 446
		} else {
			hash_table_new.add_argument (new CCodeIdentifier ("NULL"));
		}
447

448
		if (value_type.data_type.is_subtype_of (string_type.data_type)) {
449
			hash_table_new.add_argument (new CCodeIdentifier ("g_free"));
450 451
		} else if (value_type.data_type == gvariant_type) {
			hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_variant_unref"), "GDestroyNotify"));
452 453
		} else if (value_type.data_type.get_full_name () == "GLib.HashTable") {
			hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_hash_table_unref"), "GDestroyNotify"));
454 455 456
		} else {
			hash_table_new.add_argument (new CCodeIdentifier ("NULL"));
		}
457
		ccode.add_assignment (new CCodeIdentifier (temp_name), hash_table_new);
458 459 460 461

		var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_init"));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
		iter_call.add_argument (variant_expr);
462
		ccode.add_expression (iter_call);
463 464 465 466 467 468 469

		iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_loop"));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
		iter_call.add_argument (new CCodeConstant ("\"{?*}\""));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (key_name)));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (value_name)));

470
		ccode.open_while (iter_call);
471

472 473
		var key_expr = deserialize_expression (key_type, new CCodeIdentifier (key_name), null);
		var value_expr = deserialize_expression (value_type, new CCodeIdentifier (value_name), null);
474 475 476
		if (key_expr == null || value_expr == null) {
			return null;
		}
477 478 479 480 481

		var hash_table_insert = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_insert"));
		hash_table_insert.add_argument (new CCodeIdentifier (temp_name));
		hash_table_insert.add_argument (convert_to_generic_pointer (key_expr, key_type));
		hash_table_insert.add_argument (convert_to_generic_pointer (value_expr, value_type));
482
		ccode.add_expression (hash_table_insert);
483

484
		ccode.close ();
485 486 487 488

		return new CCodeIdentifier (temp_name);
	}

489
	public override CCodeExpression? deserialize_expression (DataType type, CCodeExpression variant_expr, CCodeExpression? expr, CCodeExpression? error_expr = null, out bool may_fail = null) {
490 491
		BasicTypeInfo basic_type;
		CCodeExpression result = null;
492
		may_fail = false;
493 494
		if (is_string_marshalled_enum (type.data_type)) {
			get_basic_type_info ("s", out basic_type);
495
			result = deserialize_basic (basic_type, variant_expr, true);
496 497
			result = generate_enum_value_from_string (type as EnumValueType, result, error_expr);
			may_fail = true;
498
		} else if (get_basic_type_info (get_type_signature (type), out basic_type)) {
499
			result = deserialize_basic (basic_type, variant_expr);
500
		} else if (type is ArrayType) {
501
			result = deserialize_array ((ArrayType) type, variant_expr, expr);
502 503
		} else if (type.data_type is Struct) {
			var st = (Struct) type.data_type;
504
			result = deserialize_struct (st, variant_expr);
505 506
			if (result != null && type.nullable) {
				var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
507
				csizeof.add_argument (new CCodeIdentifier (get_ccode_name (st)));
508 509 510 511 512 513 514 515 516 517 518
				var cdup = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup"));
				cdup.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, result));
				cdup.add_argument (csizeof);
				result = cdup;
			}
		} else if (type is ObjectType) {
			if (type.data_type.get_full_name () == "GLib.Variant") {
				var variant_get = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_variant"));
				variant_get.add_argument (variant_expr);
				result = variant_get;
			} else if (type.data_type.get_full_name () == "GLib.HashTable") {
519
				result = deserialize_hash_table ((ObjectType) type, variant_expr);
520 521 522 523 524 525 526 527 528 529
			}
		}

		if (result == null) {
			Report.error (type.source_reference, "GVariant deserialization of type `%s' is not supported".printf (type.to_string ()));
		}

		return result;
	}

530
	public void read_expression (DataType type, CCodeExpression iter_expr, CCodeExpression target_expr, Symbol? sym, CCodeExpression? error_expr = null, out bool may_fail = null) {
531 532 533 534 535
		var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_next_value"));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, iter_expr));

		if (sym != null && get_dbus_signature (sym) != null) {
			// raw GVariant
536
			ccode.add_assignment (target_expr, iter_call);
537
			may_fail = false;
538 539 540
			return;
		}

541 542
		string temp_name = "_tmp%d_".printf (next_temp_var_id++);

543
		ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (temp_name));
544 545 546

		var variant_expr = new CCodeIdentifier (temp_name);

547
		ccode.add_assignment (variant_expr, iter_call);
548

549
		var result = deserialize_expression (type, variant_expr, target_expr, error_expr, out may_fail);
550 551 552 553 554
		if (result == null) {
			// error already reported
			return;
		}

555
		ccode.add_assignment (target_expr, result);
556 557 558

		var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
		unref.add_argument (variant_expr);
559
		ccode.add_expression (unref);
560 561
	}

562
	CCodeExpression? generate_enum_value_to_string (EnumValueType type, CCodeExpression? expr) {
563
		var en = type.type_symbol as Enum;
564
		var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null));
565 566 567 568 569 570 571 572

		var to_string_call = new CCodeFunctionCall (new CCodeIdentifier (to_string_name));
		to_string_call.add_argument (expr);

		return to_string_call;
	}

	public CCodeFunction generate_enum_to_string_function_declaration (Enum en) {
573
		var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null));
574 575

		var to_string_func = new CCodeFunction (to_string_name, "const char*");
576
		to_string_func.add_parameter (new CCodeParameter ("value", get_ccode_name (en)));
577 578 579 580 581

		return to_string_func;
	}

	public CCodeFunction generate_enum_to_string_function (Enum en) {
582
		var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null));
583 584

		var to_string_func = new CCodeFunction (to_string_name, "const char*");
585
		to_string_func.add_parameter (new CCodeParameter ("value", get_ccode_name (en)));
586

587
		push_function (to_string_func);
588

589
		ccode.add_declaration ("const char *", new CCodeVariableDeclarator ("str"));
590

591
		ccode.open_switch (new CCodeIdentifier ("value"));
592 593
		foreach (EnumValue enum_value in en.get_values ()) {
			string dbus_value = get_dbus_value (enum_value, enum_value.name);
594
			ccode.add_case (new CCodeIdentifier (get_ccode_name (enum_value)));
595 596
			ccode.add_assignment (new CCodeIdentifier ("str"), new CCodeConstant ("\"%s\"".printf (dbus_value)));
			ccode.add_break ();
597 598
		}

599 600
		ccode.close();

601
		ccode.add_return (new CCodeIdentifier ("str"));
602

603
		pop_function ();
604 605 606
		return to_string_func;
	}

607
	CCodeExpression? serialize_basic (BasicTypeInfo basic_type, CCodeExpression expr) {
608 609 610 611 612
		var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new_" + basic_type.type_name));
		new_call.add_argument (expr);
		return new_call;
	}

613
	CCodeExpression? serialize_array (ArrayType array_type, CCodeExpression array_expr) {
614 615 616 617
		if (array_type.rank == 1 && get_type_signature (array_type) == "ay") {
			return serialize_buffer_array (array_type, array_expr);
		}

618 619
		string array_iter_name = "_tmp%d_".printf (next_temp_var_id++);

620
		ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (array_iter_name));
621
		ccode.add_assignment (new CCodeIdentifier (array_iter_name), array_expr);
622

623
		return serialize_array_dim (array_type, 1, array_expr, new CCodeIdentifier (array_iter_name));
624 625
	}

626
	CCodeExpression? serialize_array_dim (ArrayType array_type, int dim, CCodeExpression array_expr, CCodeExpression array_iter_expr) {
627 628 629
		string builder_name = "_tmp%d_".printf (next_temp_var_id++);
		string index_name = "_tmp%d_".printf (next_temp_var_id++);

630
		ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (builder_name));
631
		ccode.add_declaration (get_ccode_array_length_type (array_type), new CCodeVariableDeclarator (index_name));
632

633
		var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE"));
634 635 636
		ArrayType array_type_copy = (ArrayType) array_type.copy ();
		array_type_copy.rank -= dim - 1;
		gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (get_type_signature (array_type_copy))));
637

638 639
		var builder_init = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init"));
		builder_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
640
		builder_init.add_argument (gvariant_type);
641
		ccode.add_expression (builder_init);
642

643 644 645 646
		var cforinit = new CCodeAssignment (new CCodeIdentifier (index_name), new CCodeConstant ("0"));
		var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier (index_name), get_array_length (array_expr, dim));
		var cforiter = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier (index_name));
		ccode.open_for (cforinit, cforcond, cforiter);
647 648 649

		CCodeExpression element_variant;
		if (dim < array_type.rank) {
650
			element_variant = serialize_array_dim (array_type, dim + 1, array_expr, array_iter_expr);
651 652
		} else {
			var element_expr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, array_iter_expr);
653
			element_variant = serialize_expression (array_type.element_type, element_expr);
654 655 656 657 658
		}

		var builder_add = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_add_value"));
		builder_add.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
		builder_add.add_argument (element_variant);
659
		ccode.add_expression (builder_add);
660 661 662

		if (dim == array_type.rank) {
			var array_iter_incr = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, array_iter_expr);
663
			ccode.add_expression (array_iter_incr);
664 665
		}

666
		ccode.close ();
667 668 669 670 671 672

		var builder_end = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end"));
		builder_end.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
		return builder_end;
	}

673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
	CCodeExpression serialize_buffer_array (ArrayType array_type, CCodeExpression array_expr) {
		string buffer_name = "_tmp%d_".printf (next_temp_var_id++);

		var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE"));
		gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (get_type_signature (array_type))));

		var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup"));
		dup_call.add_argument (array_expr);
		dup_call.add_argument (get_array_length (array_expr, 1));
		ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (buffer_name, dup_call));

		var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new_from_data"));
		new_call.add_argument (gvariant_type);
		new_call.add_argument (new CCodeIdentifier (buffer_name));
		new_call.add_argument (get_array_length (array_expr, 1));
		new_call.add_argument (new CCodeConstant ("TRUE"));
		new_call.add_argument (new CCodeIdentifier ("g_free"));
		new_call.add_argument (new CCodeIdentifier (buffer_name));

		return new_call;
	}

695
	CCodeExpression? serialize_struct (Struct st, CCodeExpression struct_expr) {
696 697
		string builder_name = "_tmp%d_".printf (next_temp_var_id++);

698
		ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (builder_name));
699 700 701 702

		var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init"));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
		iter_call.add_argument (new CCodeIdentifier ("G_VARIANT_TYPE_TUPLE"));
703
		ccode.add_expression (iter_call);
704 705 706 707 708 709 710 711 712 713

		bool field_found = false;;

		foreach (Field f in st.get_fields ()) {
			if (f.binding != MemberBinding.INSTANCE) {
				continue;
			}

			field_found = true;

714
			write_expression (f.variable_type, new CCodeIdentifier (builder_name), new CCodeMemberAccess (struct_expr, get_ccode_name (f)), f);
715 716 717 718 719 720 721 722 723 724 725
		}

		if (!field_found) {
			return null;
		}

		var builder_end = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end"));
		builder_end.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
		return builder_end;
	}

726
	CCodeExpression? serialize_hash_table (ObjectType type, CCodeExpression hash_table_expr) {
727 728 729 730 731 732 733 734 735 736
		string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
		string tableiter_name = "_tmp%d_".printf (next_temp_var_id++);
		string key_name = "_tmp%d_".printf (next_temp_var_id++);
		string value_name = "_tmp%d_".printf (next_temp_var_id++);

		var type_args = type.get_type_arguments ();
		assert (type_args.size == 2);
		var key_type = type_args.get (0);
		var value_type = type_args.get (1);

737 738 739 740
		ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (subiter_name));
		ccode.add_declaration ("GHashTableIter", new CCodeVariableDeclarator (tableiter_name));
		ccode.add_declaration ("gpointer", new CCodeVariableDeclarator (key_name));
		ccode.add_declaration ("gpointer", new CCodeVariableDeclarator (value_name));
741 742 743 744

		var iter_init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_iter_init"));
		iter_init_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (tableiter_name)));
		iter_init_call.add_argument (hash_table_expr);
745
		ccode.add_expression (iter_init_call);
746

747 748 749
		var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE"));
		gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (get_type_signature (type))));

750 751
		var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init"));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
752
		iter_call.add_argument (gvariant_type);
753
		ccode.add_expression (iter_call);
754 755 756 757 758 759

		var iter_next_call = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_iter_next"));
		iter_next_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (tableiter_name)));
		iter_next_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (key_name)));
		iter_next_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (value_name)));

760
		ccode.open_while (iter_next_call);
761

762 763
		ccode.add_declaration (get_ccode_name (key_type), new CCodeVariableDeclarator ("_key"));
		ccode.add_declaration (get_ccode_name (value_type), new CCodeVariableDeclarator ("_value"));
764

765 766
		ccode.add_assignment (new CCodeIdentifier ("_key"), convert_from_generic_pointer (new CCodeIdentifier (key_name), key_type));
		ccode.add_assignment (new CCodeIdentifier ("_value"), convert_from_generic_pointer (new CCodeIdentifier (value_name), value_type));
767

768 769 770 771 772 773
		var serialized_key =  serialize_expression (key_type, new CCodeIdentifier ("_key"));
		var serialized_value = serialize_expression (value_type, new CCodeIdentifier ("_value"));
		if (serialized_key == null || serialized_value == null) {
			return null;
		}

774 775 776
		iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_add"));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
		iter_call.add_argument (new CCodeConstant ("\"{?*}\""));
777 778
		iter_call.add_argument (serialized_key);
		iter_call.add_argument (serialized_value);
779
		ccode.add_expression (iter_call);
780

781
		ccode.close ();
782 783 784 785 786 787

		iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end"));
		iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
		return iter_call;
	}

788
	public override CCodeExpression? serialize_expression (DataType type, CCodeExpression expr) {
789 790 791 792
		BasicTypeInfo basic_type;
		CCodeExpression result = null;
		if (is_string_marshalled_enum (type.data_type)) {
			get_basic_type_info ("s", out basic_type);
793 794
			result = generate_enum_value_to_string (type as EnumValueType, expr);
			result = serialize_basic (basic_type, result);
795
		} else if (get_basic_type_info (get_type_signature (type), out basic_type)) {
796
			result = serialize_basic (basic_type, expr);
797
		} else if (type is ArrayType) {
798
			result = serialize_array ((ArrayType) type, expr);
799 800 801 802 803
		} else if (type.data_type is Struct) {
			var st_expr = expr;
			if (type.nullable) {
				st_expr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, st_expr);
			}
804
			result = serialize_struct ((Struct) type.data_type, st_expr);
805 806 807 808 809 810
		} else if (type is ObjectType) {
			if (type.data_type.get_full_name () == "GLib.Variant") {
				var variant_new = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new_variant"));
				variant_new.add_argument (expr);
				result = variant_new;
			} else if (type.data_type.get_full_name () == "GLib.HashTable") {
811
				result = serialize_hash_table ((ObjectType) type, expr);
812 813 814 815 816 817 818 819 820 821
			}
		}

		if (result == null) {
			Report.error (type.source_reference, "GVariant serialization of type `%s' is not supported".printf (type.to_string ()));
		}

		return result;
	}

822
	public void write_expression (DataType type, CCodeExpression builder_expr, CCodeExpression expr, Symbol? sym) {
823 824 825
		var variant_expr = expr;
		if (sym == null || get_dbus_signature (sym) == null) {
			// perform boxing
826
			variant_expr = serialize_expression (type, expr);
827
		}
828 829 830 831
		if (variant_expr != null) {
			var builder_add = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_add_value"));
			builder_add.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, builder_expr));
			builder_add.add_argument (variant_expr);
832
			ccode.add_expression (builder_add);
833 834 835
		}
	}
}