be13d35b92da7c52aeaf590ed94ca7bb6751f4a3
[roobuilder] / src / codegen / valagvariantmodule.vala
1 /* valagvariantmodule.vala
2  *
3  * Copyright (C) 2010-2011  Jürg Billeter
4  *
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.
9
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.
14
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
18  *
19  * Author:
20  *      Jürg Billeter <j@bitron.ch>
21  */
22
23 public class Vala.GVariantModule : GValueModule {
24         struct BasicTypeInfo {
25                 public unowned string signature;
26                 public unowned string type_name;
27                 public bool is_string;
28         }
29
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 }
43         };
44
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");
48                 }
49                 return false;
50         }
51
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;;
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         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;
70                                 }
71                         }
72                 }
73                 basic_type = BasicTypeInfo ();
74                 return false;
75         }
76
77         public override void visit_enum (Enum en) {
78                 base.visit_enum (en);
79
80                 if (is_string_marshalled_enum (en)) {
81                         // strcmp
82                         cfile.add_include ("string.h");
83
84                         // for G_DBUS_ERROR
85                         cfile.add_include ("gio/gio.h");
86
87                         cfile.add_function (generate_enum_from_string_function (en));
88                         cfile.add_function (generate_enum_to_string_function (en));
89                 }
90         }
91
92         public override bool generate_enum_declaration (Enum en, CCodeFile decl_space) {
93                 if (base.generate_enum_declaration (en, decl_space)) {
94                         if (is_string_marshalled_enum (en)) {
95                                 decl_space.add_function_declaration (generate_enum_from_string_function_declaration (en));
96                                 decl_space.add_function_declaration (generate_enum_to_string_function_declaration (en));
97                         }
98                         return true;
99                 }
100                 return false;
101         }
102
103         int next_variant_function_id = 0;
104
105         public override void visit_cast_expression (CastExpression expr) {
106                 var value = expr.inner.target_value;
107                 var target_type = expr.type_reference;
108
109                 if (expr.is_non_null_cast || value.value_type == null || gvariant_type == null || value.value_type.type_symbol != gvariant_type) {
110                         base.visit_cast_expression (expr);
111                         return;
112                 }
113
114                 generate_type_declaration (expr.type_reference, cfile);
115
116                 string variant_func = "_variant_get%d".printf (++next_variant_function_id);
117
118                 var variant = value;
119                 if (value.value_type.value_owned) {
120                         // value leaked, destroy it
121                         var temp_value = store_temp_value (value, expr);
122                         temp_ref_values.insert (0, ((GLibValue) temp_value).copy ());
123                         variant = temp_value;
124                 }
125
126                 var ccall = new CCodeFunctionCall (new CCodeIdentifier (variant_func));
127                 ccall.add_argument (get_cvalue_ (variant));
128
129                 var needs_init = (target_type is ArrayType);
130                 var result = create_temp_value (target_type, needs_init, expr);
131
132                 var cfunc = new CCodeFunction (variant_func);
133                 cfunc.modifiers = CCodeModifiers.STATIC;
134                 cfunc.add_parameter (new CCodeParameter ("value", "GVariant*"));
135
136                 if (!target_type.is_real_non_null_struct_type ()) {
137                         cfunc.return_type = get_ccode_name (target_type);
138                 }
139
140                 if (target_type.is_real_non_null_struct_type ()) {
141                         // structs are returned via out parameter
142                         cfunc.add_parameter (new CCodeParameter ("result", "%s *".printf (get_ccode_name (target_type))));
143                         ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue_ (result)));
144                 } else if (target_type is ArrayType) {
145                         // return array length if appropriate
146                         // tmp = _variant_get (variant, &tmp_length);
147                         unowned ArrayType array_type = (ArrayType) target_type;
148                         var length_ctype = get_ccode_array_length_type (array_type);
149                         for (int dim = 1; dim <= array_type.rank; dim++) {
150                                 ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_array_length_cvalue (result, dim)));
151                                 cfunc.add_parameter (new CCodeParameter (get_array_length_cname ("result", dim), length_ctype + "*"));
152                         }
153                 }
154
155                 if (!target_type.is_real_non_null_struct_type ()) {
156                         ccode.add_assignment (get_cvalue_ (result), ccall);
157                 } else {
158                         ccode.add_expression (ccall);
159                 }
160
161                 push_context (new EmitContext ());
162                 push_function (cfunc);
163
164                 CCodeExpression type_expr = null;
165                 BasicTypeInfo basic_type = {};
166                 bool is_basic_type = false;
167                 if (expr.is_silent_cast) {
168                         var signature = target_type.get_type_signature ();
169                         is_basic_type = get_basic_type_info (signature, out basic_type);
170                         var ccheck = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_is_of_type"));
171                         ccheck.add_argument (new CCodeIdentifier ("value"));
172                         if (is_basic_type) {
173                                 type_expr = new CCodeIdentifier ("G_VARIANT_TYPE_" + basic_type.type_name.ascii_up ());
174                         } else {
175                                 var gvariant_type_type = new ObjectType ((Class) root_symbol.scope.lookup ("GLib").scope.lookup ("VariantType"));
176                                 var type_temp = get_temp_variable (gvariant_type_type, true, expr, true);
177                                 emit_temp_var (type_temp);
178                                 type_expr = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_type_new"));
179                                 ((CCodeFunctionCall) type_expr).add_argument (new CCodeIdentifier ("\"%s\"".printf (signature)));
180                                 store_value (get_local_cvalue (type_temp), new GLibValue (gvariant_type_type, type_expr), expr.source_reference);
181                                 type_expr = get_variable_cexpression (type_temp.name);
182                         }
183                         ccheck.add_argument (type_expr);
184                         ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.AND, new CCodeIdentifier ("value"), ccheck));
185                 }
186
187                 CCodeExpression func_result = deserialize_expression (target_type, new CCodeIdentifier ("value"), new CCodeIdentifier ("*result"));
188
189                 if (expr.is_silent_cast) {
190                         if (is_basic_type && basic_type.is_string) {
191                                 ccode.add_return (func_result);
192                         } else {
193                                 if (!is_basic_type) {
194                                         var type_free = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_type_free"));
195                                         type_free.add_argument (type_expr);
196                                         ccode.add_expression (type_free);
197                                 }
198                                 var temp_type = expr.target_type.copy ();
199                                 if (!expr.target_type.is_real_struct_type ()) {
200                                         temp_type.nullable = false;
201                                 }
202                                 var temp_value = create_temp_value (temp_type, false, expr);
203                                 store_value (temp_value, new GLibValue (temp_type, func_result), expr.source_reference);
204                                 ccode.add_return (get_cvalue_ (transform_value (temp_value, expr.target_type, expr)));
205                         }
206                         ccode.add_else ();
207                         if (!is_basic_type) {
208                                 var type_free = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_type_free"));
209                                 type_free.add_argument (type_expr);
210                                 ccode.add_expression (type_free);
211                         }
212                         ccode.add_return (new CCodeConstant ("NULL"));
213                         ccode.close ();
214                 } else if (target_type.is_real_non_null_struct_type ()) {
215                         ccode.add_assignment (new CCodeIdentifier ("*result"), func_result);
216                 } else {
217                         ccode.add_return (func_result);
218                 }
219
220                 pop_function ();
221                 pop_context ();
222
223                 cfile.add_function_declaration (cfunc);
224                 cfile.add_function (cfunc);
225
226                 expr.target_value = load_temp_value (result);
227         }
228
229         CCodeExpression? get_array_length (CCodeExpression expr, int dim) {
230                 var id = expr as CCodeIdentifier;
231                 var ma = expr as CCodeMemberAccess;
232                 if (id != null) {
233                         return new CCodeIdentifier ("%s_length%d".printf (id.name, dim));
234                 } else if (ma != null) {
235                         if (ma.is_pointer) {
236                                 return new CCodeMemberAccess.pointer (ma.inner, "%s_length%d".printf (ma.member_name, dim));
237                         } else {
238                                 return new CCodeMemberAccess (ma.inner, "%s_length%d".printf (ma.member_name, dim));
239                         }
240                 } else {
241                         // must be NULL-terminated
242                         var len_call = new CCodeFunctionCall (new CCodeIdentifier ("g_strv_length"));
243                         len_call.add_argument (expr);
244                         return len_call;
245                 }
246         }
247
248         CCodeExpression? generate_enum_value_from_string (EnumValueType type, CCodeExpression? expr, CCodeExpression? error_expr) {
249                 var en = type.type_symbol as Enum;
250                 var from_string_name = "%s_from_string".printf (get_ccode_lower_case_name (en, null));
251
252                 var from_string_call = new CCodeFunctionCall (new CCodeIdentifier (from_string_name));
253                 from_string_call.add_argument (expr);
254                 from_string_call.add_argument (error_expr != null ? error_expr : new CCodeConstant ("NULL"));
255
256                 return from_string_call;
257         }
258
259         public CCodeFunction generate_enum_from_string_function_declaration (Enum en) {
260                 var from_string_name = "%s_from_string".printf (get_ccode_lower_case_name (en, null));
261
262                 var from_string_func = new CCodeFunction (from_string_name, get_ccode_name (en));
263                 from_string_func.add_parameter (new CCodeParameter ("str", "const char*"));
264                 from_string_func.add_parameter (new CCodeParameter ("error", "GError**"));
265                 from_string_func.modifiers |= CCodeModifiers.EXTERN;
266                 requires_vala_extern = true;
267
268                 return from_string_func;
269         }
270
271         public CCodeFunction generate_enum_from_string_function (Enum en) {
272                 var from_string_name = "%s_from_string".printf (get_ccode_lower_case_name (en, null));
273
274                 var from_string_func = new CCodeFunction (from_string_name, get_ccode_name (en));
275                 from_string_func.add_parameter (new CCodeParameter ("str", "const char*"));
276                 from_string_func.add_parameter (new CCodeParameter ("error", "GError**"));
277
278                 push_function (from_string_func);
279
280                 ccode.add_declaration (get_ccode_name (en), new CCodeVariableDeclarator.zero ("value", new CCodeConstant ("0")));
281
282                 bool firstif = true;
283                 foreach (EnumValue enum_value in en.get_values ()) {
284                         string dbus_value = get_dbus_value (enum_value, enum_value.name);
285                         var string_comparison = new CCodeFunctionCall (new CCodeIdentifier ("strcmp"));
286                         string_comparison.add_argument (new CCodeIdentifier ("str"));
287                         string_comparison.add_argument (new CCodeConstant ("\"%s\"".printf (dbus_value)));
288                         var cond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, string_comparison, new CCodeConstant ("0"));
289                         if (firstif) {
290                                 ccode.open_if (cond);
291                                 firstif = false;
292                         } else {
293                                 ccode.else_if (cond);
294                         }
295                         ccode.add_assignment (new CCodeIdentifier ("value"), new CCodeIdentifier (get_ccode_name (enum_value)));
296                 }
297
298                 ccode.add_else ();
299                 var set_error = new CCodeFunctionCall (new CCodeIdentifier ("g_set_error"));
300                 set_error.add_argument (new CCodeIdentifier ("error"));
301                 set_error.add_argument (new CCodeIdentifier ("G_DBUS_ERROR"));
302                 set_error.add_argument (new CCodeIdentifier ("G_DBUS_ERROR_INVALID_ARGS"));
303                 set_error.add_argument (new CCodeConstant ("\"Invalid value for enum `%s'\"".printf (get_ccode_name (en))));
304                 ccode.add_expression (set_error);
305                 ccode.close ();
306
307                 ccode.add_return (new CCodeIdentifier ("value"));
308
309                 pop_function ();
310                 return from_string_func;
311         }
312
313         CCodeExpression deserialize_basic (BasicTypeInfo basic_type, CCodeExpression variant_expr, bool transfer = false) {
314                 var get_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_" + basic_type.type_name));
315                 get_call.add_argument (variant_expr);
316
317                 if (basic_type.is_string) {
318                         if (transfer) {
319                                 get_call.call = new CCodeIdentifier ("g_variant_get_string");
320                         } else {
321                                 get_call.call = new CCodeIdentifier ("g_variant_dup_string");
322                         }
323                         get_call.add_argument (new CCodeConstant ("NULL"));
324                 }
325
326                 return get_call;
327         }
328
329         CCodeExpression deserialize_array (ArrayType array_type, CCodeExpression variant_expr, CCodeExpression? expr) {
330                 if (array_type.rank == 1 && array_type.get_type_signature () == "ay") {
331                         return deserialize_buffer_array (array_type, variant_expr, expr);
332                 }
333
334                 string temp_name = "_tmp%d_".printf (next_temp_var_id++);
335
336                 var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_new"));
337                 new_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
338                 // add one extra element for NULL-termination
339                 new_call.add_argument (new CCodeConstant ("5"));
340
341                 var length_ctype = get_ccode_array_length_type (array_type);
342                 ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (temp_name, new_call));
343                 ccode.add_declaration (length_ctype, new CCodeVariableDeclarator (temp_name + "_length", new CCodeConstant ("0")));
344                 ccode.add_declaration (length_ctype, new CCodeVariableDeclarator (temp_name + "_size", new CCodeConstant ("4")));
345
346                 deserialize_array_dim (array_type, 1, temp_name, variant_expr, expr);
347
348                 if (array_type.element_type.is_reference_type_or_type_parameter ()) {
349                         // NULL terminate array
350                         var length = new CCodeIdentifier (temp_name + "_length");
351                         var element_access = new CCodeElementAccess (new CCodeIdentifier (temp_name), length);
352                         ccode.add_assignment (element_access, new CCodeConstant ("NULL"));
353                 }
354
355                 return new CCodeIdentifier (temp_name);
356         }
357
358         void deserialize_array_dim (ArrayType array_type, int dim, string temp_name, CCodeExpression variant_expr, CCodeExpression? expr) {
359                 string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
360                 string element_name = "_tmp%d_".printf (next_temp_var_id++);
361
362                 ccode.add_declaration (get_ccode_array_length_type (array_type), new CCodeVariableDeclarator ("%s_length%d".printf (temp_name, dim), new CCodeConstant ("0")));
363                 ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name));
364                 ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (element_name));
365
366                 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_init"));
367                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
368                 iter_call.add_argument (variant_expr);
369                 ccode.add_expression (iter_call);
370
371                 iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_next_value"));
372                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
373
374                 var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeAssignment (new CCodeIdentifier (element_name), iter_call), new CCodeConstant ("NULL"));
375                 var cforiter = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("%s_length%d".printf (temp_name, dim)));
376                 ccode.open_for (null, cforcond, cforiter);
377
378                 if (dim < array_type.rank) {
379                         deserialize_array_dim (array_type, dim + 1, temp_name, new CCodeIdentifier (element_name), expr);
380                 } else {
381                         var size_check = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier (temp_name + "_size"), new CCodeIdentifier (temp_name + "_length"));
382
383                         ccode.open_if (size_check);
384
385                         // tmp_size = (2 * tmp_size);
386                         var new_size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("2"), new CCodeIdentifier (temp_name + "_size"));
387                         ccode.add_assignment (new CCodeIdentifier (temp_name + "_size"), new_size);
388
389                         var renew_call = new CCodeFunctionCall (new CCodeIdentifier ("g_renew"));
390                         renew_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type)));
391                         renew_call.add_argument (new CCodeIdentifier (temp_name));
392                         // add one extra element for NULL-termination
393                         renew_call.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier (temp_name + "_size"), new CCodeConstant ("1")));
394                         ccode.add_assignment (new CCodeIdentifier (temp_name), renew_call);
395
396                         ccode.close ();
397
398                         var element_access = new CCodeElementAccess (new CCodeIdentifier (temp_name), new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier (temp_name + "_length")));
399                         var element_expr = deserialize_expression (array_type.element_type, new CCodeIdentifier (element_name), null);
400                         ccode.add_assignment (element_access, element_expr);
401                 }
402
403                 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
404                 unref.add_argument (new CCodeIdentifier (element_name));
405                 ccode.add_expression (unref);
406
407                 ccode.close ();
408
409                 if (expr != null) {
410                         ccode.add_assignment (get_array_length (expr, dim), new CCodeIdentifier ("%s_length%d".printf (temp_name, dim)));
411                 }
412         }
413
414         CCodeExpression deserialize_buffer_array (ArrayType array_type, CCodeExpression variant_expr, CCodeExpression? expr) {
415                 string temp_name = "_tmp%d_".printf (next_temp_var_id++);
416
417                 var get_data_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_data"));
418                 get_data_call.add_argument (variant_expr);
419
420                 var get_size_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_size"));
421                 get_size_call.add_argument (variant_expr);
422                 ccode.add_declaration ("gsize", new CCodeVariableDeclarator (temp_name + "_length", get_size_call));
423                 var length = new CCodeIdentifier (temp_name + "_length");
424
425                 CCodeFunctionCall dup_call;
426                 if (context.require_glib_version (2, 68)) {
427                         dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup2"));
428                 } else {
429                         requires_memdup2 = true;
430                         dup_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_memdup2"));
431                 }
432                 dup_call.add_argument (get_data_call);
433                 dup_call.add_argument (length);
434
435                 ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (temp_name, dup_call));
436                 if (expr != null) {
437                         ccode.add_assignment (get_array_length (expr, 1), length);
438                 }
439
440                 return new CCodeIdentifier (temp_name);
441         }
442
443         CCodeExpression? deserialize_struct (Struct st, CCodeExpression variant_expr) {
444                 string temp_name = "_tmp%d_".printf (next_temp_var_id++);
445                 string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
446
447                 ccode.add_declaration (get_ccode_name (st), new CCodeVariableDeclarator (temp_name));
448                 ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name));
449
450                 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_init"));
451                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
452                 iter_call.add_argument (variant_expr);
453                 ccode.add_expression (iter_call);
454
455                 bool field_found = false;;
456
457                 foreach (Field f in st.get_fields ()) {
458                         if (f.binding != MemberBinding.INSTANCE) {
459                                 continue;
460                         }
461
462                         field_found = true;
463
464                         read_expression (f.variable_type, new CCodeIdentifier (subiter_name), new CCodeMemberAccess (new CCodeIdentifier (temp_name), get_ccode_name (f)), f);
465                 }
466
467                 if (!field_found) {
468                         return null;
469                 }
470
471                 return new CCodeIdentifier (temp_name);
472         }
473
474         CCodeExpression? deserialize_hash_table (ObjectType type, CCodeExpression variant_expr) {
475                 string temp_name = "_tmp%d_".printf (next_temp_var_id++);
476                 string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
477                 string key_name = "_tmp%d_".printf (next_temp_var_id++);
478                 string value_name = "_tmp%d_".printf (next_temp_var_id++);
479
480                 var type_args = type.get_type_arguments ();
481                 if (type_args.size != 2) {
482                         Report.error (type.source_reference, "Missing type-arguments for GVariant deserialization of `%s'", type.type_symbol.get_full_name ());
483                         return new CCodeInvalidExpression ();
484                 }
485                 var key_type = type_args.get (0);
486                 var value_type = type_args.get (1);
487
488                 ccode.add_declaration ("GHashTable*", new CCodeVariableDeclarator (temp_name));
489                 ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name));
490                 ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (key_name));
491                 ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (value_name));
492
493                 var hash_table_new = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_new_full"));
494                 if (key_type.type_symbol.is_subtype_of (string_type.type_symbol)) {
495                         hash_table_new.add_argument (new CCodeIdentifier ("g_str_hash"));
496                         hash_table_new.add_argument (new CCodeIdentifier ("g_str_equal"));
497                 } else if (key_type.type_symbol == gvariant_type) {
498                         hash_table_new.add_argument (new CCodeIdentifier ("g_variant_hash"));
499                         hash_table_new.add_argument (new CCodeIdentifier ("g_variant_equal"));
500                 } else {
501                         hash_table_new.add_argument (new CCodeIdentifier ("g_direct_hash"));
502                         hash_table_new.add_argument (new CCodeIdentifier ("g_direct_equal"));
503                 }
504
505                 if (key_type.type_symbol.is_subtype_of (string_type.type_symbol)) {
506                         hash_table_new.add_argument (new CCodeIdentifier ("g_free"));
507                 } else if (key_type.type_symbol == gvariant_type) {
508                         hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_variant_unref"), "GDestroyNotify"));
509                 } else if (key_type.type_symbol.get_full_name () == "GLib.HashTable") {
510                         hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_hash_table_unref"), "GDestroyNotify"));
511                 } else {
512                         hash_table_new.add_argument (new CCodeConstant ("NULL"));
513                 }
514
515                 if (value_type.type_symbol.is_subtype_of (string_type.type_symbol)) {
516                         hash_table_new.add_argument (new CCodeIdentifier ("g_free"));
517                 } else if (value_type.type_symbol == gvariant_type) {
518                         hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_variant_unref"), "GDestroyNotify"));
519                 } else if (value_type.type_symbol.get_full_name () == "GLib.HashTable") {
520                         hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_hash_table_unref"), "GDestroyNotify"));
521                 } else {
522                         hash_table_new.add_argument (new CCodeConstant ("NULL"));
523                 }
524                 ccode.add_assignment (new CCodeIdentifier (temp_name), hash_table_new);
525
526                 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_init"));
527                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
528                 iter_call.add_argument (variant_expr);
529                 ccode.add_expression (iter_call);
530
531                 iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_loop"));
532                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
533                 iter_call.add_argument (new CCodeConstant ("\"{?*}\""));
534                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (key_name)));
535                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (value_name)));
536
537                 ccode.open_while (iter_call);
538
539                 var key_expr = deserialize_expression (key_type, new CCodeIdentifier (key_name), null);
540                 var value_expr = deserialize_expression (value_type, new CCodeIdentifier (value_name), null);
541                 if (key_expr == null || value_expr == null) {
542                         return null;
543                 }
544
545                 var hash_table_insert = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_insert"));
546                 hash_table_insert.add_argument (new CCodeIdentifier (temp_name));
547                 hash_table_insert.add_argument (convert_to_generic_pointer (key_expr, key_type));
548                 hash_table_insert.add_argument (convert_to_generic_pointer (value_expr, value_type));
549                 ccode.add_expression (hash_table_insert);
550
551                 ccode.close ();
552
553                 return new CCodeIdentifier (temp_name);
554         }
555
556         public override CCodeExpression? deserialize_expression (DataType type, CCodeExpression variant_expr, CCodeExpression? expr, CCodeExpression? error_expr = null, out bool may_fail = null) {
557                 BasicTypeInfo basic_type;
558                 CCodeExpression result = null;
559                 may_fail = false;
560                 if (is_string_marshalled_enum (type.type_symbol)) {
561                         get_basic_type_info ("s", out basic_type);
562                         result = deserialize_basic (basic_type, variant_expr, true);
563                         result = generate_enum_value_from_string (type as EnumValueType, result, error_expr);
564                         may_fail = true;
565                 } else if (get_basic_type_info (type.get_type_signature (), out basic_type)) {
566                         result = deserialize_basic (basic_type, variant_expr);
567                 } else if (type is ArrayType) {
568                         result = deserialize_array ((ArrayType) type, variant_expr, expr);
569                 } else if (type.type_symbol is Struct) {
570                         unowned Struct st = (Struct) type.type_symbol;
571                         result = deserialize_struct (st, variant_expr);
572                         if (result != null && type.nullable) {
573                                 var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof"));
574                                 csizeof.add_argument (new CCodeIdentifier (get_ccode_name (st)));
575                                 CCodeFunctionCall cdup;
576                                 if (context.require_glib_version (2, 68)) {
577                                         cdup = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup2"));
578                                 } else {
579                                         requires_memdup2 = true;
580                                         cdup = new CCodeFunctionCall (new CCodeIdentifier ("_vala_memdup2"));
581                                 }
582                                 cdup.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, result));
583                                 cdup.add_argument (csizeof);
584                                 result = cdup;
585                         }
586                 } else if (type is ObjectType) {
587                         if (type.type_symbol.get_full_name () == "GLib.Variant") {
588                                 var variant_get = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get_variant"));
589                                 variant_get.add_argument (variant_expr);
590                                 result = variant_get;
591                         } else if (type.type_symbol.get_full_name () == "GLib.HashTable") {
592                                 result = deserialize_hash_table ((ObjectType) type, variant_expr);
593                         }
594                 }
595
596                 if (result == null) {
597                         Report.error (type.source_reference, "GVariant deserialization of type `%s' is not supported", type.to_string ());
598                         return new CCodeInvalidExpression ();
599                 }
600
601                 return result;
602         }
603
604         public void read_expression (DataType type, CCodeExpression iter_expr, CCodeExpression target_expr, Symbol? sym, CCodeExpression? error_expr = null, out bool may_fail = null) {
605                 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_next_value"));
606                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, iter_expr));
607
608                 if (sym != null && get_dbus_signature (sym) != null) {
609                         // raw GVariant
610                         ccode.add_assignment (target_expr, iter_call);
611                         may_fail = false;
612                         return;
613                 }
614
615                 string temp_name = "_tmp%d_".printf (next_temp_var_id++);
616
617                 ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (temp_name));
618
619                 var variant_expr = new CCodeIdentifier (temp_name);
620
621                 ccode.add_assignment (variant_expr, iter_call);
622
623                 var result = deserialize_expression (type, variant_expr, target_expr, error_expr, out may_fail);
624                 if (result == null) {
625                         // error already reported
626                         return;
627                 }
628
629                 ccode.add_assignment (target_expr, result);
630
631                 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
632                 unref.add_argument (variant_expr);
633                 ccode.add_expression (unref);
634         }
635
636         CCodeExpression? generate_enum_value_to_string (EnumValueType type, CCodeExpression? expr) {
637                 var en = type.type_symbol as Enum;
638                 var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null));
639
640                 var to_string_call = new CCodeFunctionCall (new CCodeIdentifier (to_string_name));
641                 to_string_call.add_argument (expr);
642
643                 return to_string_call;
644         }
645
646         public CCodeFunction generate_enum_to_string_function_declaration (Enum en) {
647                 var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null));
648
649                 var to_string_func = new CCodeFunction (to_string_name, "const char*");
650                 to_string_func.add_parameter (new CCodeParameter ("value", get_ccode_name (en)));
651                 to_string_func.modifiers |= CCodeModifiers.EXTERN;
652                 requires_vala_extern = true;
653
654                 return to_string_func;
655         }
656
657         public CCodeFunction generate_enum_to_string_function (Enum en) {
658                 var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null));
659
660                 var to_string_func = new CCodeFunction (to_string_name, "const char*");
661                 to_string_func.add_parameter (new CCodeParameter ("value", get_ccode_name (en)));
662
663                 push_function (to_string_func);
664
665                 ccode.add_declaration ("const char *", new CCodeVariableDeclarator ("str"));
666
667                 ccode.open_switch (new CCodeIdentifier ("value"));
668                 foreach (EnumValue enum_value in en.get_values ()) {
669                         string dbus_value = get_dbus_value (enum_value, enum_value.name);
670                         ccode.add_case (new CCodeIdentifier (get_ccode_name (enum_value)));
671                         ccode.add_assignment (new CCodeIdentifier ("str"), new CCodeConstant ("\"%s\"".printf (dbus_value)));
672                         ccode.add_break ();
673                 }
674
675                 ccode.close();
676
677                 ccode.add_return (new CCodeIdentifier ("str"));
678
679                 pop_function ();
680                 return to_string_func;
681         }
682
683         CCodeExpression? serialize_basic (BasicTypeInfo basic_type, CCodeExpression expr) {
684                 var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new_" + basic_type.type_name));
685                 new_call.add_argument (expr);
686                 return new_call;
687         }
688
689         CCodeExpression? serialize_array (ArrayType array_type, CCodeExpression array_expr) {
690                 if (array_type.rank == 1 && array_type.get_type_signature () == "ay") {
691                         return serialize_buffer_array (array_type, array_expr);
692                 }
693
694                 string array_iter_name = "_tmp%d_".printf (next_temp_var_id++);
695
696                 ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (array_iter_name));
697                 ccode.add_assignment (new CCodeIdentifier (array_iter_name), array_expr);
698
699                 return serialize_array_dim (array_type, 1, array_expr, new CCodeIdentifier (array_iter_name));
700         }
701
702         CCodeExpression? serialize_array_dim (ArrayType array_type, int dim, CCodeExpression array_expr, CCodeExpression array_iter_expr) {
703                 string builder_name = "_tmp%d_".printf (next_temp_var_id++);
704                 string index_name = "_tmp%d_".printf (next_temp_var_id++);
705
706                 ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (builder_name));
707                 ccode.add_declaration (get_ccode_array_length_type (array_type), new CCodeVariableDeclarator (index_name));
708
709                 var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE"));
710                 ArrayType array_type_copy = (ArrayType) array_type.copy ();
711                 array_type_copy.rank -= dim - 1;
712                 gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (array_type_copy.get_type_signature ())));
713
714                 var builder_init = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init"));
715                 builder_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
716                 builder_init.add_argument (gvariant_type);
717                 ccode.add_expression (builder_init);
718
719                 var cforinit = new CCodeAssignment (new CCodeIdentifier (index_name), new CCodeConstant ("0"));
720                 var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier (index_name), get_array_length (array_expr, dim));
721                 var cforiter = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier (index_name));
722                 ccode.open_for (cforinit, cforcond, cforiter);
723
724                 CCodeExpression element_variant;
725                 if (dim < array_type.rank) {
726                         element_variant = serialize_array_dim (array_type, dim + 1, array_expr, array_iter_expr);
727                 } else {
728                         var element_expr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, array_iter_expr);
729                         element_variant = serialize_expression (array_type.element_type, element_expr);
730                 }
731
732                 var builder_add = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_add_value"));
733                 builder_add.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
734                 builder_add.add_argument (element_variant);
735                 ccode.add_expression (builder_add);
736
737                 if (dim == array_type.rank) {
738                         var array_iter_incr = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, array_iter_expr);
739                         ccode.add_expression (array_iter_incr);
740                 }
741
742                 ccode.close ();
743
744                 var builder_end = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end"));
745                 builder_end.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
746                 return builder_end;
747         }
748
749         CCodeExpression serialize_buffer_array (ArrayType array_type, CCodeExpression array_expr) {
750                 string buffer_name = "_tmp%d_".printf (next_temp_var_id++);
751
752                 var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE"));
753                 gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (array_type.get_type_signature ())));
754
755                 CCodeFunctionCall dup_call;
756                 if (context.require_glib_version (2, 68)) {
757                         dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup2"));
758                 } else {
759                         requires_memdup2 = true;
760                         dup_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_memdup2"));
761                 }
762                 dup_call.add_argument (array_expr);
763                 dup_call.add_argument (get_array_length (array_expr, 1));
764                 ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (buffer_name, dup_call));
765
766                 var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new_from_data"));
767                 new_call.add_argument (gvariant_type);
768                 new_call.add_argument (new CCodeIdentifier (buffer_name));
769                 new_call.add_argument (get_array_length (array_expr, 1));
770                 new_call.add_argument (new CCodeConstant ("TRUE"));
771                 new_call.add_argument (new CCodeIdentifier ("g_free"));
772                 new_call.add_argument (new CCodeIdentifier (buffer_name));
773
774                 return new_call;
775         }
776
777         CCodeExpression? serialize_struct (Struct st, CCodeExpression struct_expr) {
778                 string builder_name = "_tmp%d_".printf (next_temp_var_id++);
779
780                 ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (builder_name));
781
782                 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init"));
783                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
784                 iter_call.add_argument (new CCodeIdentifier ("G_VARIANT_TYPE_TUPLE"));
785                 ccode.add_expression (iter_call);
786
787                 bool field_found = false;;
788
789                 foreach (Field f in st.get_fields ()) {
790                         if (f.binding != MemberBinding.INSTANCE) {
791                                 continue;
792                         }
793
794                         field_found = true;
795
796                         write_expression (f.variable_type, new CCodeIdentifier (builder_name), new CCodeMemberAccess (struct_expr, get_ccode_name (f)), f);
797                 }
798
799                 if (!field_found) {
800                         return null;
801                 }
802
803                 var builder_end = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end"));
804                 builder_end.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name)));
805                 return builder_end;
806         }
807
808         CCodeExpression? serialize_hash_table (ObjectType type, CCodeExpression hash_table_expr) {
809                 string subiter_name = "_tmp%d_".printf (next_temp_var_id++);
810                 string tableiter_name = "_tmp%d_".printf (next_temp_var_id++);
811                 string key_name = "_tmp%d_".printf (next_temp_var_id++);
812                 string value_name = "_tmp%d_".printf (next_temp_var_id++);
813
814                 var type_args = type.get_type_arguments ();
815                 if (type_args.size != 2) {
816                         Report.error (type.source_reference, "Missing type-arguments for GVariant serialization of `%s'", type.type_symbol.get_full_name ());
817                         return new CCodeInvalidExpression ();
818                 }
819                 var key_type = type_args.get (0);
820                 var value_type = type_args.get (1);
821
822                 ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (subiter_name));
823                 ccode.add_declaration ("GHashTableIter", new CCodeVariableDeclarator (tableiter_name));
824                 ccode.add_declaration ("gpointer", new CCodeVariableDeclarator (key_name));
825                 ccode.add_declaration ("gpointer", new CCodeVariableDeclarator (value_name));
826
827                 var iter_init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_iter_init"));
828                 iter_init_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (tableiter_name)));
829                 iter_init_call.add_argument (hash_table_expr);
830                 ccode.add_expression (iter_init_call);
831
832                 var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE"));
833                 gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (type.get_type_signature ())));
834
835                 var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init"));
836                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
837                 iter_call.add_argument (gvariant_type);
838                 ccode.add_expression (iter_call);
839
840                 var iter_next_call = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_iter_next"));
841                 iter_next_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (tableiter_name)));
842                 iter_next_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (key_name)));
843                 iter_next_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (value_name)));
844
845                 ccode.open_while (iter_next_call);
846
847                 ccode.add_declaration (get_ccode_name (key_type), new CCodeVariableDeclarator ("_key"));
848                 ccode.add_declaration (get_ccode_name (value_type), new CCodeVariableDeclarator ("_value"));
849
850                 ccode.add_assignment (new CCodeIdentifier ("_key"), convert_from_generic_pointer (new CCodeIdentifier (key_name), key_type));
851                 ccode.add_assignment (new CCodeIdentifier ("_value"), convert_from_generic_pointer (new CCodeIdentifier (value_name), value_type));
852
853                 var serialized_key =  serialize_expression (key_type, new CCodeIdentifier ("_key"));
854                 var serialized_value = serialize_expression (value_type, new CCodeIdentifier ("_value"));
855                 if (serialized_key == null || serialized_value == null) {
856                         return null;
857                 }
858
859                 iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_add"));
860                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
861                 iter_call.add_argument (new CCodeConstant ("\"{?*}\""));
862                 iter_call.add_argument (serialized_key);
863                 iter_call.add_argument (serialized_value);
864                 ccode.add_expression (iter_call);
865
866                 ccode.close ();
867
868                 iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end"));
869                 iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name)));
870                 return iter_call;
871         }
872
873         public override CCodeExpression? serialize_expression (DataType type, CCodeExpression expr) {
874                 BasicTypeInfo basic_type;
875                 CCodeExpression result = null;
876                 if (is_string_marshalled_enum (type.type_symbol)) {
877                         get_basic_type_info ("s", out basic_type);
878                         result = generate_enum_value_to_string (type as EnumValueType, expr);
879                         result = serialize_basic (basic_type, result);
880                 } else if (get_basic_type_info (type.get_type_signature (), out basic_type)) {
881                         result = serialize_basic (basic_type, expr);
882                 } else if (type is ArrayType) {
883                         result = serialize_array ((ArrayType) type, expr);
884                 } else if (type.type_symbol is Struct) {
885                         var st_expr = expr;
886                         if (type.nullable) {
887                                 st_expr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, st_expr);
888                         }
889                         result = serialize_struct ((Struct) type.type_symbol, st_expr);
890                 } else if (type is ObjectType) {
891                         if (type.type_symbol.get_full_name () == "GLib.Variant") {
892                                 var variant_new = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new_variant"));
893                                 variant_new.add_argument (expr);
894                                 result = variant_new;
895                         } else if (type.type_symbol.get_full_name () == "GLib.HashTable") {
896                                 result = serialize_hash_table ((ObjectType) type, expr);
897                         }
898                 }
899
900                 if (result == null) {
901                         Report.error (type.source_reference, "GVariant serialization of type `%s' is not supported", type.to_string ());
902                         return new CCodeInvalidExpression ();
903                 }
904
905                 return result;
906         }
907
908         public void write_expression (DataType type, CCodeExpression builder_expr, CCodeExpression expr, Symbol? sym) {
909                 var variant_expr = expr;
910                 if (sym == null || get_dbus_signature (sym) == null) {
911                         // perform boxing
912                         variant_expr = serialize_expression (type, expr);
913                 }
914                 if (variant_expr != null) {
915                         var builder_add = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_add_value"));
916                         builder_add.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, builder_expr));
917                         builder_add.add_argument (variant_expr);
918                         ccode.add_expression (builder_add);
919                 }
920         }
921 }