/* valagvariantmodule.vala * * Copyright (C) 2010-2011 Jürg Billeter * * 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 */ public class Vala.GVariantModule : GValueModule { 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) { return symbol.get_attribute_bool ("DBus", "use_string_marshalling"); } return false; } string get_dbus_value (EnumValue value, string default_value) { var dbus_value = value.get_attribute_string ("DBus", "value"); if (dbus_value != null) { return dbus_value;; } return default_value; } public static string? get_dbus_signature (Symbol symbol) { return symbol.get_attribute_string ("DBus", "signature"); } 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; } } } basic_type = BasicTypeInfo (); return false; } public override void visit_enum (Enum en) { base.visit_enum (en); if (is_string_marshalled_enum (en)) { // strcmp cfile.add_include ("string.h"); // for G_DBUS_ERROR cfile.add_include ("gio/gio.h"); cfile.add_function (generate_enum_from_string_function (en)); cfile.add_function (generate_enum_to_string_function (en)); } } public override bool generate_enum_declaration (Enum en, CCodeFile decl_space) { if (base.generate_enum_declaration (en, decl_space)) { if (is_string_marshalled_enum (en)) { decl_space.add_function_declaration (generate_enum_from_string_function_declaration (en)); decl_space.add_function_declaration (generate_enum_to_string_function_declaration (en)); } return true; } return false; } int next_variant_function_id = 0; public override void visit_cast_expression (CastExpression expr) { var value = expr.inner.target_value; var target_type = expr.type_reference; if (expr.is_non_null_cast || value.value_type == null || gvariant_type == null || value.value_type.type_symbol != gvariant_type) { base.visit_cast_expression (expr); return; } generate_type_declaration (expr.type_reference, cfile); string variant_func = "_variant_get%d".printf (++next_variant_function_id); var variant = value; if (value.value_type.value_owned) { // value leaked, destroy it var temp_value = store_temp_value (value, expr); temp_ref_values.insert (0, ((GLibValue) temp_value).copy ()); variant = temp_value; } var ccall = new CCodeFunctionCall (new CCodeIdentifier (variant_func)); ccall.add_argument (get_cvalue_ (variant)); var needs_init = (target_type is ArrayType); var result = create_temp_value (target_type, needs_init, expr); var cfunc = new CCodeFunction (variant_func); cfunc.modifiers = CCodeModifiers.STATIC; cfunc.add_parameter (new CCodeParameter ("value", "GVariant*")); if (!target_type.is_real_non_null_struct_type ()) { cfunc.return_type = get_ccode_name (target_type); } if (target_type.is_real_non_null_struct_type ()) { // structs are returned via out parameter cfunc.add_parameter (new CCodeParameter ("result", "%s *".printf (get_ccode_name (target_type)))); ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_cvalue_ (result))); } else if (target_type is ArrayType) { // return array length if appropriate // tmp = _variant_get (variant, &tmp_length); unowned ArrayType array_type = (ArrayType) target_type; var length_ctype = get_ccode_array_length_type (array_type); for (int dim = 1; dim <= array_type.rank; dim++) { ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_array_length_cvalue (result, dim))); cfunc.add_parameter (new CCodeParameter (get_array_length_cname ("result", dim), length_ctype + "*")); } } if (!target_type.is_real_non_null_struct_type ()) { ccode.add_assignment (get_cvalue_ (result), ccall); } else { ccode.add_expression (ccall); } push_context (new EmitContext ()); push_function (cfunc); CCodeExpression type_expr = null; BasicTypeInfo basic_type = {}; bool is_basic_type = false; if (expr.is_silent_cast) { var signature = target_type.get_type_signature (); is_basic_type = get_basic_type_info (signature, out basic_type); var ccheck = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_is_of_type")); ccheck.add_argument (new CCodeIdentifier ("value")); if (is_basic_type) { type_expr = new CCodeIdentifier ("G_VARIANT_TYPE_" + basic_type.type_name.ascii_up ()); } else { var gvariant_type_type = new ObjectType ((Class) root_symbol.scope.lookup ("GLib").scope.lookup ("VariantType")); var type_temp = get_temp_variable (gvariant_type_type, true, expr, true); emit_temp_var (type_temp); type_expr = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_type_new")); ((CCodeFunctionCall) type_expr).add_argument (new CCodeIdentifier ("\"%s\"".printf (signature))); store_value (get_local_cvalue (type_temp), new GLibValue (gvariant_type_type, type_expr), expr.source_reference); type_expr = get_variable_cexpression (type_temp.name); } ccheck.add_argument (type_expr); ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.AND, new CCodeIdentifier ("value"), ccheck)); } CCodeExpression func_result = deserialize_expression (target_type, new CCodeIdentifier ("value"), new CCodeIdentifier ("*result")); if (expr.is_silent_cast) { if (is_basic_type && basic_type.is_string) { ccode.add_return (func_result); } else { if (!is_basic_type) { var type_free = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_type_free")); type_free.add_argument (type_expr); ccode.add_expression (type_free); } var temp_type = expr.target_type.copy (); if (!expr.target_type.is_real_struct_type ()) { temp_type.nullable = false; } var temp_value = create_temp_value (temp_type, false, expr); store_value (temp_value, new GLibValue (temp_type, func_result), expr.source_reference); ccode.add_return (get_cvalue_ (transform_value (temp_value, expr.target_type, expr))); } ccode.add_else (); if (!is_basic_type) { var type_free = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_type_free")); type_free.add_argument (type_expr); ccode.add_expression (type_free); } ccode.add_return (new CCodeConstant ("NULL")); ccode.close (); } else if (target_type.is_real_non_null_struct_type ()) { ccode.add_assignment (new CCodeIdentifier ("*result"), func_result); } else { ccode.add_return (func_result); } pop_function (); pop_context (); cfile.add_function_declaration (cfunc); cfile.add_function (cfunc); expr.target_value = load_temp_value (result); } 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; } } CCodeExpression? generate_enum_value_from_string (EnumValueType type, CCodeExpression? expr, CCodeExpression? error_expr) { var en = type.type_symbol as Enum; var from_string_name = "%s_from_string".printf (get_ccode_lower_case_name (en, null)); var from_string_call = new CCodeFunctionCall (new CCodeIdentifier (from_string_name)); from_string_call.add_argument (expr); from_string_call.add_argument (error_expr != null ? error_expr : new CCodeConstant ("NULL")); return from_string_call; } public CCodeFunction generate_enum_from_string_function_declaration (Enum en) { var from_string_name = "%s_from_string".printf (get_ccode_lower_case_name (en, null)); var from_string_func = new CCodeFunction (from_string_name, get_ccode_name (en)); from_string_func.add_parameter (new CCodeParameter ("str", "const char*")); from_string_func.add_parameter (new CCodeParameter ("error", "GError**")); from_string_func.modifiers |= CCodeModifiers.EXTERN; requires_vala_extern = true; return from_string_func; } public CCodeFunction generate_enum_from_string_function (Enum en) { var from_string_name = "%s_from_string".printf (get_ccode_lower_case_name (en, null)); var from_string_func = new CCodeFunction (from_string_name, get_ccode_name (en)); from_string_func.add_parameter (new CCodeParameter ("str", "const char*")); from_string_func.add_parameter (new CCodeParameter ("error", "GError**")); push_function (from_string_func); ccode.add_declaration (get_ccode_name (en), new CCodeVariableDeclarator.zero ("value", new CCodeConstant ("0"))); bool firstif = true; 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))); var cond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, string_comparison, new CCodeConstant ("0")); if (firstif) { ccode.open_if (cond); firstif = false; } else { ccode.else_if (cond); } ccode.add_assignment (new CCodeIdentifier ("value"), new CCodeIdentifier (get_ccode_name (enum_value))); } ccode.add_else (); 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")); set_error.add_argument (new CCodeConstant ("\"Invalid value for enum `%s'\"".printf (get_ccode_name (en)))); ccode.add_expression (set_error); ccode.close (); ccode.add_return (new CCodeIdentifier ("value")); pop_function (); return from_string_func; } CCodeExpression deserialize_basic (BasicTypeInfo basic_type, CCodeExpression variant_expr, bool transfer = false) { 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; } CCodeExpression deserialize_array (ArrayType array_type, CCodeExpression variant_expr, CCodeExpression? expr) { if (array_type.rank == 1 && array_type.get_type_signature () == "ay") { return deserialize_buffer_array (array_type, variant_expr, expr); } string temp_name = "_tmp%d_".printf (next_temp_var_id++); var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_new")); new_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type))); // add one extra element for NULL-termination new_call.add_argument (new CCodeConstant ("5")); var length_ctype = get_ccode_array_length_type (array_type); ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (temp_name, new_call)); 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"))); deserialize_array_dim (array_type, 1, temp_name, variant_expr, expr); 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); ccode.add_assignment (element_access, new CCodeConstant ("NULL")); } return new CCodeIdentifier (temp_name); } void deserialize_array_dim (ArrayType array_type, int dim, string temp_name, CCodeExpression variant_expr, CCodeExpression? expr) { string subiter_name = "_tmp%d_".printf (next_temp_var_id++); string element_name = "_tmp%d_".printf (next_temp_var_id++); ccode.add_declaration (get_ccode_array_length_type (array_type), new CCodeVariableDeclarator ("%s_length%d".printf (temp_name, dim), new CCodeConstant ("0"))); ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name)); ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (element_name)); 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); ccode.add_expression (iter_call); iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_next_value")); iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name))); var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeAssignment (new CCodeIdentifier (element_name), iter_call), new CCodeConstant ("NULL")); var cforiter = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("%s_length%d".printf (temp_name, dim))); ccode.open_for (null, cforcond, cforiter); if (dim < array_type.rank) { deserialize_array_dim (array_type, dim + 1, temp_name, new CCodeIdentifier (element_name), expr); } else { var size_check = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier (temp_name + "_size"), new CCodeIdentifier (temp_name + "_length")); ccode.open_if (size_check); // tmp_size = (2 * tmp_size); var new_size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("2"), new CCodeIdentifier (temp_name + "_size")); ccode.add_assignment (new CCodeIdentifier (temp_name + "_size"), new_size); var renew_call = new CCodeFunctionCall (new CCodeIdentifier ("g_renew")); renew_call.add_argument (new CCodeIdentifier (get_ccode_name (array_type.element_type))); 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"))); ccode.add_assignment (new CCodeIdentifier (temp_name), renew_call); ccode.close (); var element_access = new CCodeElementAccess (new CCodeIdentifier (temp_name), new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier (temp_name + "_length"))); var element_expr = deserialize_expression (array_type.element_type, new CCodeIdentifier (element_name), null); ccode.add_assignment (element_access, element_expr); } var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref")); unref.add_argument (new CCodeIdentifier (element_name)); ccode.add_expression (unref); ccode.close (); if (expr != null) { ccode.add_assignment (get_array_length (expr, dim), new CCodeIdentifier ("%s_length%d".printf (temp_name, dim))); } } 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"); CCodeFunctionCall dup_call; if (context.require_glib_version (2, 68)) { dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup2")); } else { requires_memdup2 = true; dup_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_memdup2")); } 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); } CCodeExpression? deserialize_struct (Struct st, CCodeExpression variant_expr) { string temp_name = "_tmp%d_".printf (next_temp_var_id++); string subiter_name = "_tmp%d_".printf (next_temp_var_id++); ccode.add_declaration (get_ccode_name (st), new CCodeVariableDeclarator (temp_name)); ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator (subiter_name)); 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); ccode.add_expression (iter_call); bool field_found = false;; foreach (Field f in st.get_fields ()) { if (f.binding != MemberBinding.INSTANCE) { continue; } field_found = true; read_expression (f.variable_type, new CCodeIdentifier (subiter_name), new CCodeMemberAccess (new CCodeIdentifier (temp_name), get_ccode_name (f)), f); } if (!field_found) { return null; } return new CCodeIdentifier (temp_name); } CCodeExpression? deserialize_hash_table (ObjectType type, CCodeExpression variant_expr) { 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 (); if (type_args.size != 2) { Report.error (type.source_reference, "Missing type-arguments for GVariant deserialization of `%s'", type.type_symbol.get_full_name ()); return new CCodeInvalidExpression (); } var key_type = type_args.get (0); var value_type = type_args.get (1); 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)); var hash_table_new = new CCodeFunctionCall (new CCodeIdentifier ("g_hash_table_new_full")); if (key_type.type_symbol.is_subtype_of (string_type.type_symbol)) { hash_table_new.add_argument (new CCodeIdentifier ("g_str_hash")); hash_table_new.add_argument (new CCodeIdentifier ("g_str_equal")); } else if (key_type.type_symbol == gvariant_type) { hash_table_new.add_argument (new CCodeIdentifier ("g_variant_hash")); hash_table_new.add_argument (new CCodeIdentifier ("g_variant_equal")); } else { hash_table_new.add_argument (new CCodeIdentifier ("g_direct_hash")); hash_table_new.add_argument (new CCodeIdentifier ("g_direct_equal")); } if (key_type.type_symbol.is_subtype_of (string_type.type_symbol)) { hash_table_new.add_argument (new CCodeIdentifier ("g_free")); } else if (key_type.type_symbol == gvariant_type) { hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_variant_unref"), "GDestroyNotify")); } else if (key_type.type_symbol.get_full_name () == "GLib.HashTable") { hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_hash_table_unref"), "GDestroyNotify")); } else { hash_table_new.add_argument (new CCodeConstant ("NULL")); } if (value_type.type_symbol.is_subtype_of (string_type.type_symbol)) { hash_table_new.add_argument (new CCodeIdentifier ("g_free")); } else if (value_type.type_symbol == gvariant_type) { hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_variant_unref"), "GDestroyNotify")); } else if (value_type.type_symbol.get_full_name () == "GLib.HashTable") { hash_table_new.add_argument (new CCodeCastExpression (new CCodeIdentifier ("g_hash_table_unref"), "GDestroyNotify")); } else { hash_table_new.add_argument (new CCodeConstant ("NULL")); } ccode.add_assignment (new CCodeIdentifier (temp_name), hash_table_new); 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); ccode.add_expression (iter_call); 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))); ccode.open_while (iter_call); var key_expr = deserialize_expression (key_type, new CCodeIdentifier (key_name), null); var value_expr = deserialize_expression (value_type, new CCodeIdentifier (value_name), null); if (key_expr == null || value_expr == null) { return null; } 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)); ccode.add_expression (hash_table_insert); ccode.close (); return new CCodeIdentifier (temp_name); } public override CCodeExpression? deserialize_expression (DataType type, CCodeExpression variant_expr, CCodeExpression? expr, CCodeExpression? error_expr = null, out bool may_fail = null) { BasicTypeInfo basic_type; CCodeExpression result = null; may_fail = false; if (is_string_marshalled_enum (type.type_symbol)) { get_basic_type_info ("s", out basic_type); result = deserialize_basic (basic_type, variant_expr, true); result = generate_enum_value_from_string (type as EnumValueType, result, error_expr); may_fail = true; } else if (get_basic_type_info (type.get_type_signature (), out basic_type)) { result = deserialize_basic (basic_type, variant_expr); } else if (type is ArrayType) { result = deserialize_array ((ArrayType) type, variant_expr, expr); } else if (type.type_symbol is Struct) { unowned Struct st = (Struct) type.type_symbol; result = deserialize_struct (st, variant_expr); if (result != null && type.nullable) { var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof")); csizeof.add_argument (new CCodeIdentifier (get_ccode_name (st))); CCodeFunctionCall cdup; if (context.require_glib_version (2, 68)) { cdup = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup2")); } else { requires_memdup2 = true; cdup = new CCodeFunctionCall (new CCodeIdentifier ("_vala_memdup2")); } cdup.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, result)); cdup.add_argument (csizeof); result = cdup; } } else if (type is ObjectType) { if (type.type_symbol.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.type_symbol.get_full_name () == "GLib.HashTable") { result = deserialize_hash_table ((ObjectType) type, variant_expr); } } if (result == null) { Report.error (type.source_reference, "GVariant deserialization of type `%s' is not supported", type.to_string ()); return new CCodeInvalidExpression (); } return result; } public void read_expression (DataType type, CCodeExpression iter_expr, CCodeExpression target_expr, Symbol? sym, CCodeExpression? error_expr = null, out bool may_fail = null) { 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 ccode.add_assignment (target_expr, iter_call); may_fail = false; return; } string temp_name = "_tmp%d_".printf (next_temp_var_id++); ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator (temp_name)); var variant_expr = new CCodeIdentifier (temp_name); ccode.add_assignment (variant_expr, iter_call); var result = deserialize_expression (type, variant_expr, target_expr, error_expr, out may_fail); if (result == null) { // error already reported return; } ccode.add_assignment (target_expr, result); var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref")); unref.add_argument (variant_expr); ccode.add_expression (unref); } CCodeExpression? generate_enum_value_to_string (EnumValueType type, CCodeExpression? expr) { var en = type.type_symbol as Enum; var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null)); 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) { var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null)); var to_string_func = new CCodeFunction (to_string_name, "const char*"); to_string_func.add_parameter (new CCodeParameter ("value", get_ccode_name (en))); to_string_func.modifiers |= CCodeModifiers.EXTERN; requires_vala_extern = true; return to_string_func; } public CCodeFunction generate_enum_to_string_function (Enum en) { var to_string_name = "%s_to_string".printf (get_ccode_lower_case_name (en, null)); var to_string_func = new CCodeFunction (to_string_name, "const char*"); to_string_func.add_parameter (new CCodeParameter ("value", get_ccode_name (en))); push_function (to_string_func); ccode.add_declaration ("const char *", new CCodeVariableDeclarator ("str")); ccode.open_switch (new CCodeIdentifier ("value")); foreach (EnumValue enum_value in en.get_values ()) { string dbus_value = get_dbus_value (enum_value, enum_value.name); ccode.add_case (new CCodeIdentifier (get_ccode_name (enum_value))); ccode.add_assignment (new CCodeIdentifier ("str"), new CCodeConstant ("\"%s\"".printf (dbus_value))); ccode.add_break (); } ccode.close(); ccode.add_return (new CCodeIdentifier ("str")); pop_function (); return to_string_func; } CCodeExpression? serialize_basic (BasicTypeInfo basic_type, CCodeExpression expr) { var new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new_" + basic_type.type_name)); new_call.add_argument (expr); return new_call; } CCodeExpression? serialize_array (ArrayType array_type, CCodeExpression array_expr) { if (array_type.rank == 1 && array_type.get_type_signature () == "ay") { return serialize_buffer_array (array_type, array_expr); } string array_iter_name = "_tmp%d_".printf (next_temp_var_id++); ccode.add_declaration (get_ccode_name (array_type), new CCodeVariableDeclarator (array_iter_name)); ccode.add_assignment (new CCodeIdentifier (array_iter_name), array_expr); return serialize_array_dim (array_type, 1, array_expr, new CCodeIdentifier (array_iter_name)); } CCodeExpression? serialize_array_dim (ArrayType array_type, int dim, CCodeExpression array_expr, CCodeExpression array_iter_expr) { string builder_name = "_tmp%d_".printf (next_temp_var_id++); string index_name = "_tmp%d_".printf (next_temp_var_id++); ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (builder_name)); ccode.add_declaration (get_ccode_array_length_type (array_type), new CCodeVariableDeclarator (index_name)); var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE")); ArrayType array_type_copy = (ArrayType) array_type.copy (); array_type_copy.rank -= dim - 1; gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (array_type_copy.get_type_signature ()))); var builder_init = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init")); builder_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (builder_name))); builder_init.add_argument (gvariant_type); ccode.add_expression (builder_init); 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); CCodeExpression element_variant; if (dim < array_type.rank) { element_variant = serialize_array_dim (array_type, dim + 1, array_expr, array_iter_expr); } else { var element_expr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, array_iter_expr); element_variant = serialize_expression (array_type.element_type, element_expr); } 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); ccode.add_expression (builder_add); if (dim == array_type.rank) { var array_iter_incr = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, array_iter_expr); ccode.add_expression (array_iter_incr); } ccode.close (); 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; } 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 (array_type.get_type_signature ()))); CCodeFunctionCall dup_call; if (context.require_glib_version (2, 68)) { dup_call = new CCodeFunctionCall (new CCodeIdentifier ("g_memdup2")); } else { requires_memdup2 = true; dup_call = new CCodeFunctionCall (new CCodeIdentifier ("_vala_memdup2")); } 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; } CCodeExpression? serialize_struct (Struct st, CCodeExpression struct_expr) { string builder_name = "_tmp%d_".printf (next_temp_var_id++); ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator (builder_name)); 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")); ccode.add_expression (iter_call); bool field_found = false;; foreach (Field f in st.get_fields ()) { if (f.binding != MemberBinding.INSTANCE) { continue; } field_found = true; write_expression (f.variable_type, new CCodeIdentifier (builder_name), new CCodeMemberAccess (struct_expr, get_ccode_name (f)), f); } 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; } CCodeExpression? serialize_hash_table (ObjectType type, CCodeExpression hash_table_expr) { 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 (); if (type_args.size != 2) { Report.error (type.source_reference, "Missing type-arguments for GVariant serialization of `%s'", type.type_symbol.get_full_name ()); return new CCodeInvalidExpression (); } var key_type = type_args.get (0); var value_type = type_args.get (1); 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)); 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); ccode.add_expression (iter_init_call); var gvariant_type = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE")); gvariant_type.add_argument (new CCodeConstant ("\"%s\"".printf (type.get_type_signature ()))); var iter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init")); iter_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (subiter_name))); iter_call.add_argument (gvariant_type); ccode.add_expression (iter_call); 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))); ccode.open_while (iter_next_call); ccode.add_declaration (get_ccode_name (key_type), new CCodeVariableDeclarator ("_key")); ccode.add_declaration (get_ccode_name (value_type), new CCodeVariableDeclarator ("_value")); 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)); 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; } 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 ("\"{?*}\"")); iter_call.add_argument (serialized_key); iter_call.add_argument (serialized_value); ccode.add_expression (iter_call); ccode.close (); 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; } public override CCodeExpression? serialize_expression (DataType type, CCodeExpression expr) { BasicTypeInfo basic_type; CCodeExpression result = null; if (is_string_marshalled_enum (type.type_symbol)) { get_basic_type_info ("s", out basic_type); result = generate_enum_value_to_string (type as EnumValueType, expr); result = serialize_basic (basic_type, result); } else if (get_basic_type_info (type.get_type_signature (), out basic_type)) { result = serialize_basic (basic_type, expr); } else if (type is ArrayType) { result = serialize_array ((ArrayType) type, expr); } else if (type.type_symbol is Struct) { var st_expr = expr; if (type.nullable) { st_expr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, st_expr); } result = serialize_struct ((Struct) type.type_symbol, st_expr); } else if (type is ObjectType) { if (type.type_symbol.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.type_symbol.get_full_name () == "GLib.HashTable") { result = serialize_hash_table ((ObjectType) type, expr); } } if (result == null) { Report.error (type.source_reference, "GVariant serialization of type `%s' is not supported", type.to_string ()); return new CCodeInvalidExpression (); } return result; } public void write_expression (DataType type, CCodeExpression builder_expr, CCodeExpression expr, Symbol? sym) { var variant_expr = expr; if (sym == null || get_dbus_signature (sym) == null) { // perform boxing variant_expr = serialize_expression (type, expr); } 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); ccode.add_expression (builder_add); } } }