codegen: Avoid critical in get_basic_type_info() when there is no signature
[vala-gnome.git] / codegen / valagvariantmodule.vala
blob3fa5ab50738a455ebf43aa7baa00405f4b6cf5ec
1 /* valagvariantmodule.vala
3 * Copyright (C) 2010-2011 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 public class Vala.GVariantModule : GAsyncModule {
24 struct BasicTypeInfo {
25 public unowned string signature;
26 public unowned string type_name;
27 public bool is_string;
30 const BasicTypeInfo[] basic_types = {
31 { "y", "byte", false },
32 { "b", "boolean", false },
33 { "n", "int16", false },
34 { "q", "uint16", false },
35 { "i", "int32", false },
36 { "u", "uint32", false },
37 { "x", "int64", false },
38 { "t", "uint64", false },
39 { "d", "double", false },
40 { "s", "string", true },
41 { "o", "object_path", true },
42 { "g", "signature", true }
45 static bool is_string_marshalled_enum (TypeSymbol? symbol) {
46 if (symbol != null && symbol is Enum) {
47 return symbol.get_attribute_bool ("DBus", "use_string_marshalling");
49 return false;
52 string get_dbus_value (EnumValue value, string default_value) {
53 var dbus_value = value.get_attribute_string ("DBus", "value");
54 if (dbus_value != null) {
55 return dbus_value;;
57 return default_value;
60 public static string? get_dbus_signature (Symbol symbol) {
61 return symbol.get_attribute_string ("DBus", "signature");
64 bool get_basic_type_info (string? signature, out BasicTypeInfo basic_type) {
65 if (signature != null) {
66 foreach (BasicTypeInfo info in basic_types) {
67 if (info.signature == signature) {
68 basic_type = info;
69 return true;
73 basic_type = BasicTypeInfo ();
74 return false;
77 public static string? get_type_signature (DataType datatype, Symbol? symbol = null) {
78 if (symbol != null) {
79 string sig = get_dbus_signature (symbol);
80 if (sig != null) {
81 // allow overriding signature in attribute, used for raw GVariants
82 return sig;
86 var array_type = datatype as ArrayType;
88 if (array_type != null) {
89 string element_type_signature = get_type_signature (array_type.element_type);
91 if (element_type_signature == null) {
92 return null;
95 return string.nfill (array_type.rank, 'a') + element_type_signature;
96 } else if (is_string_marshalled_enum (datatype.data_type)) {
97 return "s";
98 } else if (datatype.data_type != null) {
99 string sig = datatype.data_type.get_attribute_string ("CCode", "type_signature");
101 var st = datatype.data_type as Struct;
102 var en = datatype.data_type as Enum;
103 if (sig == null && st != null) {
104 var str = new StringBuilder ();
105 str.append_c ('(');
106 foreach (Field f in st.get_fields ()) {
107 if (f.binding == MemberBinding.INSTANCE) {
108 str.append (get_type_signature (f.variable_type, f));
111 str.append_c (')');
112 sig = str.str;
113 } else if (sig == null && en != null) {
114 if (en.is_flags) {
115 return "u";
116 } else {
117 return "i";
121 var type_args = datatype.get_type_arguments ();
122 if (sig != null && "%s" in sig && type_args.size > 0) {
123 string element_sig = "";
124 foreach (DataType type_arg in type_args) {
125 var s = get_type_signature (type_arg);
126 if (s != null) {
127 element_sig += s;
131 sig = sig.replace ("%s", element_sig);
134 if (sig == null &&
135 (datatype.data_type.get_full_name () == "GLib.UnixInputStream" ||
136 datatype.data_type.get_full_name () == "GLib.UnixOutputStream" ||
137 datatype.data_type.get_full_name () == "GLib.Socket")) {
138 return "h";
141 return sig;
142 } else {
143 return null;
147 public override void visit_enum (Enum en) {
148 base.visit_enum (en);
150 if (is_string_marshalled_enum (en)) {
151 // strcmp
152 cfile.add_include ("string.h");
154 // for G_DBUS_ERROR
155 cfile.add_include ("gio/gio.h");
157 cfile.add_function (generate_enum_from_string_function (en));
158 cfile.add_function (generate_enum_to_string_function (en));
162 public override bool generate_enum_declaration (Enum en, CCodeFile decl_space) {
163 if (base.generate_enum_declaration (en, decl_space)) {
164 if (is_string_marshalled_enum (en)) {
165 decl_space.add_function_declaration (generate_enum_from_string_function_declaration (en));
166 decl_space.add_function_declaration (generate_enum_to_string_function_declaration (en));
168 return true;
170 return false;
173 CCodeExpression? get_array_length (CCodeExpression expr, int dim) {
174 var id = expr as CCodeIdentifier;
175 var ma = expr as CCodeMemberAccess;
176 if (id != null) {
177 return new CCodeIdentifier ("%s_length%d".printf (id.name, dim));
178 } else if (ma != null) {
179 if (ma.is_pointer) {
180 return new CCodeMemberAccess.pointer (ma.inner, "%s_length%d".printf (ma.member_name, dim));
181 } else {
182 return new CCodeMemberAccess (ma.inner, "%s_length%d".printf (ma.member_name, dim));
184 } else {
185 // must be NULL-terminated
186 var len_call = new CCodeFunctionCall (new CCodeIdentifier ("g_strv_length"));
187 len_call.add_argument (expr);
188 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));
196 var from_string_call = new CCodeFunctionCall (new CCodeIdentifier (from_string_name));
197 from_string_call.add_argument (expr);
198 from_string_call.add_argument (error_expr != null ? error_expr : new CCodeConstant ("NULL"));
200 return from_string_call;
203 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));
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**"));
210 return from_string_func;
213 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));
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**"));
220 push_function (from_string_func);
222 ccode.add_declaration (get_ccode_name (en), new CCodeVariableDeclarator.zero ("value", new CCodeConstant ("0")));
224 bool firstif = true;
225 foreach (EnumValue enum_value in en.get_values ()) {
226 string dbus_value = get_dbus_value (enum_value, enum_value.name);
227 var string_comparison = new CCodeFunctionCall (new CCodeIdentifier ("strcmp"));
228 string_comparison.add_argument (new CCodeIdentifier ("str"));
229 string_comparison.add_argument (new CCodeConstant ("\"%s\"".printf (dbus_value)));
230 var cond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, string_comparison, new CCodeConstant ("0"));
231 if (firstif) {
232 ccode.open_if (cond);
233 firstif = false;
234 } else {
235 ccode.else_if (cond);
237 ccode.add_assignment (new CCodeIdentifier ("value"), new CCodeIdentifier (get_ccode_name (enum_value)));
240 ccode.add_else ();
241 var set_error = new CCodeFunctionCall (new CCodeIdentifier ("g_set_error"));
242 set_error.add_argument (new CCodeIdentifier ("error"));
243 set_error.add_argument (new CCodeIdentifier ("G_DBUS_ERROR"));
244 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 ccode.add_expression (set_error);
247 ccode.close ();
249 ccode.add_return (new CCodeIdentifier ("value"));
251 pop_function ();
252 return from_string_func;
255 CCodeExpression deserialize_basic (BasicTypeInfo basic_type, CCodeExpression variant_expr, bool transfer = false) {
256 var get_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_" + basic_type.type_name));
257 get_call.add_argument (variant_expr);
259 if (basic_type.is_string) {
260 if (transfer) {
261 get_call.call = new CCodeIdentifier ("g_variant_get_string");
262 } else {
263 get_call.call = new CCodeIdentifier ("g_variant_dup_string");
265 get_call.add_argument (new CCodeConstant ("NULL"));
268 return get_call;
271 CCodeExpression deserialize_array (ArrayType array_type, CCodeExpression variant_expr, CCodeExpression? expr) {
272 if (array_type.rank == 1 && get_type_signature (array_type) == "ay") {
273 return deserialize_buffer_array (array_type, variant_expr, expr);
276 string temp_name = "_tmp%d_".printf (next_temp_var_id++);
278 var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_new"));
279 new_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
280 // add one extra element for NULL-termination
281 new_call.add_argument (new CCodeConstant ("5"));
283 ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (temp_name, new_call));
284 ccode.add_declaration ("int", new CCodeVariableDeclarator (temp_name + "_length", new CCodeConstant ("0")));
285 ccode.add_declaration ("int", new CCodeVariableDeclarator (temp_name + "_size", new CCodeConstant ("4")));
287 deserialize_array_dim (array_type, 1, temp_name, variant_expr, expr);
289 if (array_type.element_type.is_reference_type_or_type_parameter ()) {
290 // NULL terminate array
291 var length = new CCodeIdentifier (temp_name + "_length");
292 var element_access = new CCodeElementAccess (new CCodeIdentifier (temp_name), length);
293 ccode.add_assignment (element_access, new CCodeIdentifier ("NULL"));
296 return new CCodeIdentifier (temp_name);
299 void deserialize_array_dim (ArrayType array_type, int dim, string temp_name, CCodeExpression variant_expr, CCodeExpression? expr) {
300 string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
301 string element_name = "_tmp%d_".printf (next_temp_var_id++);
303 ccode.add_declaration ("int", new CCodeVariableDeclarator ("%s_length%d".printf (temp_name, dim), new CCodeConstant ("0")));
304 ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name));
305 ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (element_name));
307 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_init"));
308 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
309 iter_call.add_argument (variant_expr);
310 ccode.add_expression (iter_call);
312 iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_next_value"));
313 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
315 var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeAssignment (new CCodeIdentifier (element_name), iter_call), new CCodeConstant ("NULL"));
316 var cforiter = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("%s_length%d".printf (temp_name, dim)));
317 ccode.open_for (null, cforcond, cforiter);
319 if (dim < array_type.rank) {
320 deserialize_array_dim (array_type, dim + 1, temp_name, new CCodeIdentifier (element_name), expr);
321 } else {
322 var size_check = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier (temp_name + "_size"), new CCodeIdentifier (temp_name + "_length"));
324 ccode.open_if (size_check);
326 // tmp_size = (2 * tmp_size);
327 var new_size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("2"), new CCodeIdentifier (temp_name + "_size"));
328 ccode.add_assignment (new CCodeIdentifier (temp_name + "_size"), new_size);
330 var renew_call = new CCodeFunctionCall (new CCodeIdentifier ("g_renew"));
331 renew_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
332 renew_call.add_argument (new CCodeIdentifier (temp_name));
333 // add one extra element for NULL-termination
334 renew_call.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier (temp_name + "_size"), new CCodeConstant ("1")));
335 ccode.add_assignment (new CCodeIdentifier (temp_name), renew_call);
337 ccode.close ();
339 var element_access = new CCodeElementAccess (new CCodeIdentifier (temp_name), new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier (temp_name + "_length")));
340 var element_expr = deserialize_expression (array_type.element_type, new CCodeIdentifier (element_name), null);
341 ccode.add_assignment (element_access, element_expr);
344 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
345 unref.add_argument (new CCodeIdentifier (element_name));
346 ccode.add_expression (unref);
348 ccode.close ();
350 if (expr != null) {
351 ccode.add_assignment (get_array_length (expr, dim), new CCodeIdentifier ("%s_length%d".printf (temp_name, dim)));
355 CCodeExpression deserialize_buffer_array (ArrayType array_type, CCodeExpression variant_expr, CCodeExpression? expr) {
356 string temp_name = "_tmp%d_".printf (next_temp_var_id++);
358 var get_data_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_data"));
359 get_data_call.add_argument (variant_expr);
361 var get_size_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_size"));
362 get_size_call.add_argument (variant_expr);
363 ccode.add_declaration ("gsize", new CCodeVariableDeclarator (temp_name + "_length", get_size_call));
364 var length = new CCodeIdentifier (temp_name + "_length");
366 var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup"));
367 dup_call.add_argument (get_data_call);
368 dup_call.add_argument (length);
370 ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (temp_name, dup_call));
371 if (expr != null) {
372 ccode.add_assignment (get_array_length (expr, 1), length);
375 return new CCodeIdentifier (temp_name);
378 CCodeExpression? deserialize_struct (Struct st, CCodeExpression variant_expr) {
379 string temp_name = "_tmp%d_".printf (next_temp_var_id++);
380 string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
382 ccode.add_declaration (get_ccode_name (st), new CCodeVariableDeclarator (temp_name));
383 ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name));
385 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_init"));
386 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
387 iter_call.add_argument (variant_expr);
388 ccode.add_expression (iter_call);
390 bool field_found = false;;
392 foreach (Field f in st.get_fields ()) {
393 if (f.binding != MemberBinding.INSTANCE) {
394 continue;
397 field_found = true;
399 read_expression (f.variable_type, new CCodeIdentifier (subiter_name), new CCodeMemberAccess (new CCodeIdentifier (temp_name), get_ccode_name (f)), f);
402 if (!field_found) {
403 return null;
406 return new CCodeIdentifier (temp_name);
409 CCodeExpression? deserialize_hash_table (ObjectType type, CCodeExpression variant_expr) {
410 string temp_name = "_tmp%d_".printf (next_temp_var_id++);
411 string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
412 string key_name = "_tmp%d_".printf (next_temp_var_id++);
413 string value_name = "_tmp%d_".printf (next_temp_var_id++);
415 var type_args = type.get_type_arguments ();
416 assert (type_args.size == 2);
417 var key_type = type_args.get (0);
418 var value_type = type_args.get (1);
420 ccode.add_declaration ("GHashTable*", new CCodeVariableDeclarator (temp_name));
421 ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name));
422 ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (key_name));
423 ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (value_name));
425 var hash_table_new = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_new_full"));
426 if (key_type.data_type.is_subtype_of (string_type.data_type)) {
427 hash_table_new.add_argument (new CCodeIdentifier ("g_str_hash"));
428 hash_table_new.add_argument (new CCodeIdentifier ("g_str_equal"));
429 } else if (key_type.data_type == gvariant_type) {
430 hash_table_new.add_argument (new CCodeIdentifier ("g_variant_hash"));
431 hash_table_new.add_argument (new CCodeIdentifier ("g_variant_equal"));
432 } else {
433 hash_table_new.add_argument (new CCodeIdentifier ("g_direct_hash"));
434 hash_table_new.add_argument (new CCodeIdentifier ("g_direct_equal"));
437 if (key_type.data_type.is_subtype_of (string_type.data_type)) {
438 hash_table_new.add_argument (new CCodeIdentifier ("g_free"));
439 } else if (key_type.data_type == gvariant_type) {
440 hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_variant_unref"), "GDestroyNotify"));
441 } else if (key_type.data_type.get_full_name () == "GLib.HashTable") {
442 hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_hash_table_unref"), "GDestroyNotify"));
443 } else {
444 hash_table_new.add_argument (new CCodeIdentifier ("NULL"));
447 if (value_type.data_type.is_subtype_of (string_type.data_type)) {
448 hash_table_new.add_argument (new CCodeIdentifier ("g_free"));
449 } else if (value_type.data_type == gvariant_type) {
450 hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_variant_unref"), "GDestroyNotify"));
451 } else if (value_type.data_type.get_full_name () == "GLib.HashTable") {
452 hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_hash_table_unref"), "GDestroyNotify"));
453 } else {
454 hash_table_new.add_argument (new CCodeIdentifier ("NULL"));
456 ccode.add_assignment (new CCodeIdentifier (temp_name), hash_table_new);
458 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_init"));
459 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
460 iter_call.add_argument (variant_expr);
461 ccode.add_expression (iter_call);
463 iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_loop"));
464 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
465 iter_call.add_argument (new CCodeConstant ("\"{?*}\""));
466 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (key_name)));
467 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (value_name)));
469 ccode.open_while (iter_call);
471 var key_expr = deserialize_expression (key_type, new CCodeIdentifier (key_name), null);
472 var value_expr = deserialize_expression (value_type, new CCodeIdentifier (value_name), null);
473 if (key_expr == null || value_expr == null) {
474 return null;
477 var hash_table_insert = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_insert"));
478 hash_table_insert.add_argument (new CCodeIdentifier (temp_name));
479 hash_table_insert.add_argument (convert_to_generic_pointer (key_expr, key_type));
480 hash_table_insert.add_argument (convert_to_generic_pointer (value_expr, value_type));
481 ccode.add_expression (hash_table_insert);
483 ccode.close ();
485 return new CCodeIdentifier (temp_name);
488 public override CCodeExpression? deserialize_expression (DataType type, CCodeExpression variant_expr, CCodeExpression? expr, CCodeExpression? error_expr = null, out bool may_fail = null) {
489 BasicTypeInfo basic_type;
490 CCodeExpression result = null;
491 may_fail = false;
492 if (is_string_marshalled_enum (type.data_type)) {
493 get_basic_type_info ("s", out basic_type);
494 result = deserialize_basic (basic_type, variant_expr, true);
495 result = generate_enum_value_from_string (type as EnumValueType, result, error_expr);
496 may_fail = true;
497 } else if (get_basic_type_info (get_type_signature (type), out basic_type)) {
498 result = deserialize_basic (basic_type, variant_expr);
499 } else if (type is ArrayType) {
500 result = deserialize_array ((ArrayType) type, variant_expr, expr);
501 } else if (type.data_type is Struct) {
502 var st = (Struct) type.data_type;
503 result = deserialize_struct (st, variant_expr);
504 if (result != null && type.nullable) {
505 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
506 csizeof.add_argument (new CCodeIdentifier (get_ccode_name (st)));
507 var cdup = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup"));
508 cdup.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, result));
509 cdup.add_argument (csizeof);
510 result = cdup;
512 } else if (type is ObjectType) {
513 if (type.data_type.get_full_name () == "GLib.Variant") {
514 var variant_get = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_variant"));
515 variant_get.add_argument (variant_expr);
516 result = variant_get;
517 } else if (type.data_type.get_full_name () == "GLib.HashTable") {
518 result = deserialize_hash_table ((ObjectType) type, variant_expr);
522 if (result == null) {
523 Report.error (type.source_reference, "GVariant deserialization of type `%s' is not supported".printf (type.to_string ()));
526 return result;
529 public void read_expression (DataType type, CCodeExpression iter_expr, CCodeExpression target_expr, Symbol? sym, CCodeExpression? error_expr = null, out bool may_fail = null) {
530 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_next_value"));
531 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, iter_expr));
533 if (sym != null && get_dbus_signature (sym) != null) {
534 // raw GVariant
535 ccode.add_assignment (target_expr, iter_call);
536 may_fail = false;
537 return;
540 string temp_name = "_tmp%d_".printf (next_temp_var_id++);
542 ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (temp_name));
544 var variant_expr = new CCodeIdentifier (temp_name);
546 ccode.add_assignment (variant_expr, iter_call);
548 var result = deserialize_expression (type, variant_expr, target_expr, error_expr, out may_fail);
549 if (result == null) {
550 // error already reported
551 return;
554 ccode.add_assignment (target_expr, result);
556 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
557 unref.add_argument (variant_expr);
558 ccode.add_expression (unref);
561 CCodeExpression? generate_enum_value_to_string (EnumValueType type, CCodeExpression? expr) {
562 var en = type.type_symbol as Enum;
563 var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null));
565 var to_string_call = new CCodeFunctionCall (new CCodeIdentifier (to_string_name));
566 to_string_call.add_argument (expr);
568 return to_string_call;
571 public CCodeFunction generate_enum_to_string_function_declaration (Enum en) {
572 var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null));
574 var to_string_func = new CCodeFunction (to_string_name, "const char*");
575 to_string_func.add_parameter (new CCodeParameter ("value", get_ccode_name (en)));
577 return to_string_func;
580 public CCodeFunction generate_enum_to_string_function (Enum en) {
581 var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null));
583 var to_string_func = new CCodeFunction (to_string_name, "const char*");
584 to_string_func.add_parameter (new CCodeParameter ("value", get_ccode_name (en)));
586 push_function (to_string_func);
588 ccode.add_declaration ("const char *", new CCodeVariableDeclarator ("str"));
590 ccode.open_switch (new CCodeIdentifier ("value"));
591 foreach (EnumValue enum_value in en.get_values ()) {
592 string dbus_value = get_dbus_value (enum_value, enum_value.name);
593 ccode.add_case (new CCodeIdentifier (get_ccode_name (enum_value)));
594 ccode.add_assignment (new CCodeIdentifier ("str"), new CCodeConstant ("\"%s\"".printf (dbus_value)));
595 ccode.add_break ();
598 ccode.close();
600 ccode.add_return (new CCodeIdentifier ("str"));
602 pop_function ();
603 return to_string_func;
606 CCodeExpression? serialize_basic (BasicTypeInfo basic_type, CCodeExpression expr) {
607 var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new_" + basic_type.type_name));
608 new_call.add_argument (expr);
609 return new_call;
612 CCodeExpression? serialize_array (ArrayType array_type, CCodeExpression array_expr) {
613 if (array_type.rank == 1 && get_type_signature (array_type) == "ay") {
614 return serialize_buffer_array (array_type, array_expr);
617 string array_iter_name = "_tmp%d_".printf (next_temp_var_id++);
619 ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (array_iter_name));
620 ccode.add_assignment (new CCodeIdentifier (array_iter_name), array_expr);
622 return serialize_array_dim (array_type, 1, array_expr, new CCodeIdentifier (array_iter_name));
625 CCodeExpression? serialize_array_dim (ArrayType array_type, int dim, CCodeExpression array_expr, CCodeExpression array_iter_expr) {
626 string builder_name = "_tmp%d_".printf (next_temp_var_id++);
627 string index_name = "_tmp%d_".printf (next_temp_var_id++);
629 ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (builder_name));
630 ccode.add_declaration ("int", new CCodeVariableDeclarator (index_name));
632 var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE"));
633 ArrayType array_type_copy = (ArrayType) array_type.copy ();
634 array_type_copy.rank -= dim - 1;
635 gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (get_type_signature (array_type_copy))));
637 var builder_init = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init"));
638 builder_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
639 builder_init.add_argument (gvariant_type);
640 ccode.add_expression (builder_init);
642 var cforinit = new CCodeAssignment (new CCodeIdentifier (index_name), new CCodeConstant ("0"));
643 var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier (index_name), get_array_length (array_expr, dim));
644 var cforiter = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier (index_name));
645 ccode.open_for (cforinit, cforcond, cforiter);
647 CCodeExpression element_variant;
648 if (dim < array_type.rank) {
649 element_variant = serialize_array_dim (array_type, dim + 1, array_expr, array_iter_expr);
650 } else {
651 var element_expr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, array_iter_expr);
652 element_variant = serialize_expression (array_type.element_type, element_expr);
655 var builder_add = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_add_value"));
656 builder_add.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
657 builder_add.add_argument (element_variant);
658 ccode.add_expression (builder_add);
660 if (dim == array_type.rank) {
661 var array_iter_incr = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, array_iter_expr);
662 ccode.add_expression (array_iter_incr);
665 ccode.close ();
667 var builder_end = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end"));
668 builder_end.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
669 return builder_end;
672 CCodeExpression serialize_buffer_array (ArrayType array_type, CCodeExpression array_expr) {
673 string buffer_name = "_tmp%d_".printf (next_temp_var_id++);
675 var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE"));
676 gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (get_type_signature (array_type))));
678 var dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup"));
679 dup_call.add_argument (array_expr);
680 dup_call.add_argument (get_array_length (array_expr, 1));
681 ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (buffer_name, dup_call));
683 var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new_from_data"));
684 new_call.add_argument (gvariant_type);
685 new_call.add_argument (new CCodeIdentifier (buffer_name));
686 new_call.add_argument (get_array_length (array_expr, 1));
687 new_call.add_argument (new CCodeConstant ("TRUE"));
688 new_call.add_argument (new CCodeIdentifier ("g_free"));
689 new_call.add_argument (new CCodeIdentifier (buffer_name));
691 return new_call;
694 CCodeExpression? serialize_struct (Struct st, CCodeExpression struct_expr) {
695 string builder_name = "_tmp%d_".printf (next_temp_var_id++);
697 ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (builder_name));
699 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init"));
700 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
701 iter_call.add_argument (new CCodeIdentifier ("G_VARIANT_TYPE_TUPLE"));
702 ccode.add_expression (iter_call);
704 bool field_found = false;;
706 foreach (Field f in st.get_fields ()) {
707 if (f.binding != MemberBinding.INSTANCE) {
708 continue;
711 field_found = true;
713 write_expression (f.variable_type, new CCodeIdentifier (builder_name), new CCodeMemberAccess (struct_expr, get_ccode_name (f)), f);
716 if (!field_found) {
717 return null;
720 var builder_end = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end"));
721 builder_end.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
722 return builder_end;
725 CCodeExpression? serialize_hash_table (ObjectType type, CCodeExpression hash_table_expr) {
726 string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
727 string tableiter_name = "_tmp%d_".printf (next_temp_var_id++);
728 string key_name = "_tmp%d_".printf (next_temp_var_id++);
729 string value_name = "_tmp%d_".printf (next_temp_var_id++);
731 var type_args = type.get_type_arguments ();
732 assert (type_args.size == 2);
733 var key_type = type_args.get (0);
734 var value_type = type_args.get (1);
736 ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (subiter_name));
737 ccode.add_declaration ("GHashTableIter", new CCodeVariableDeclarator (tableiter_name));
738 ccode.add_declaration ("gpointer", new CCodeVariableDeclarator (key_name));
739 ccode.add_declaration ("gpointer", new CCodeVariableDeclarator (value_name));
741 var iter_init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_iter_init"));
742 iter_init_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (tableiter_name)));
743 iter_init_call.add_argument (hash_table_expr);
744 ccode.add_expression (iter_init_call);
746 var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE"));
747 gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (get_type_signature (type))));
749 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init"));
750 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
751 iter_call.add_argument (gvariant_type);
752 ccode.add_expression (iter_call);
754 var iter_next_call = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_iter_next"));
755 iter_next_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (tableiter_name)));
756 iter_next_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (key_name)));
757 iter_next_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (value_name)));
759 ccode.open_while (iter_next_call);
761 ccode.add_declaration (get_ccode_name (key_type), new CCodeVariableDeclarator ("_key"));
762 ccode.add_declaration (get_ccode_name (value_type), new CCodeVariableDeclarator ("_value"));
764 ccode.add_assignment (new CCodeIdentifier ("_key"), convert_from_generic_pointer (new CCodeIdentifier (key_name), key_type));
765 ccode.add_assignment (new CCodeIdentifier ("_value"), convert_from_generic_pointer (new CCodeIdentifier (value_name), value_type));
767 var serialized_key = serialize_expression (key_type, new CCodeIdentifier ("_key"));
768 var serialized_value = serialize_expression (value_type, new CCodeIdentifier ("_value"));
769 if (serialized_key == null || serialized_value == null) {
770 return null;
773 iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_add"));
774 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
775 iter_call.add_argument (new CCodeConstant ("\"{?*}\""));
776 iter_call.add_argument (serialized_key);
777 iter_call.add_argument (serialized_value);
778 ccode.add_expression (iter_call);
780 ccode.close ();
782 iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end"));
783 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
784 return iter_call;
787 public override CCodeExpression? serialize_expression (DataType type, CCodeExpression expr) {
788 BasicTypeInfo basic_type;
789 CCodeExpression result = null;
790 if (is_string_marshalled_enum (type.data_type)) {
791 get_basic_type_info ("s", out basic_type);
792 result = generate_enum_value_to_string (type as EnumValueType, expr);
793 result = serialize_basic (basic_type, result);
794 } else if (get_basic_type_info (get_type_signature (type), out basic_type)) {
795 result = serialize_basic (basic_type, expr);
796 } else if (type is ArrayType) {
797 result = serialize_array ((ArrayType) type, expr);
798 } else if (type.data_type is Struct) {
799 var st_expr = expr;
800 if (type.nullable) {
801 st_expr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, st_expr);
803 result = serialize_struct ((Struct) type.data_type, st_expr);
804 } else if (type is ObjectType) {
805 if (type.data_type.get_full_name () == "GLib.Variant") {
806 var variant_new = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new_variant"));
807 variant_new.add_argument (expr);
808 result = variant_new;
809 } else if (type.data_type.get_full_name () == "GLib.HashTable") {
810 result = serialize_hash_table ((ObjectType) type, expr);
814 if (result == null) {
815 Report.error (type.source_reference, "GVariant serialization of type `%s' is not supported".printf (type.to_string ()));
818 return result;
821 public void write_expression (DataType type, CCodeExpression builder_expr, CCodeExpression expr, Symbol? sym) {
822 var variant_expr = expr;
823 if (sym == null || get_dbus_signature (sym) == null) {
824 // perform boxing
825 variant_expr = serialize_expression (type, expr);
827 if (variant_expr != null) {
828 var builder_add = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_add_value"));
829 builder_add.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, builder_expr));
830 builder_add.add_argument (variant_expr);
831 ccode.add_expression (builder_add);