From eb3b8f9830b69502e381deea0d59f4660c1d6738 Mon Sep 17 00:00:00 2001 From: Rico Tzschichholz Date: Thu, 21 Mar 2019 11:38:28 +0100 Subject: [PATCH] codegen: Implement silent-cast for GLib.Variant Fixes https://gitlab.gnome.org/GNOME/vala/issues/767 --- codegen/valagvariantmodule.vala | 51 ++++++++++++- tests/Makefile.am | 2 + .../basic-types/gvariants-unboxing-safe.vala | 74 +++++++++++++++++++ tests/semantic/cast-gvariant-unsupported.test | 6 ++ vala/valacastexpression.vala | 3 + 5 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 tests/basic-types/gvariants-unboxing-safe.vala create mode 100644 tests/semantic/cast-gvariant-unsupported.test diff --git a/codegen/valagvariantmodule.vala b/codegen/valagvariantmodule.vala index edd98b702..ddbab1b88 100644 --- a/codegen/valagvariantmodule.vala +++ b/codegen/valagvariantmodule.vala @@ -160,8 +160,57 @@ public class Vala.GVariantModule : GAsyncModule { 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 (target_type.is_real_non_null_struct_type ()) { + + 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); diff --git a/tests/Makefile.am b/tests/Makefile.am index 0e1472ffb..ce19144f7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -34,6 +34,7 @@ TESTS = \ basic-types/glists.vala \ basic-types/gptrarray.vala \ basic-types/gvariants.vala \ + basic-types/gvariants-unboxing-safe.vala \ basic-types/bug570846.test \ basic-types/bug571486.vala \ basic-types/bug591552.vala \ @@ -596,6 +597,7 @@ TESTS = \ semantic/assignment-same-variable.vala \ semantic/assignment-signal-incompatible-method.test \ semantic/assignment-signal-incompatible-type.test \ + semantic/cast-gvariant-unsupported.test \ semantic/chainup-gobject-incompatible-type-property.test \ semantic/chainup-gobject-unknown-property.test \ semantic/chainup-gobject-unsupported-type-property.test \ diff --git a/tests/basic-types/gvariants-unboxing-safe.vala b/tests/basic-types/gvariants-unboxing-safe.vala new file mode 100644 index 000000000..03cc73b71 --- /dev/null +++ b/tests/basic-types/gvariants-unboxing-safe.vala @@ -0,0 +1,74 @@ +struct Foo { + public string s; + public uint64 u64; + public bool b; +} + +void main () { + Variant v; + + v = new Variant.int32 (4711); + { + bool? b = v as bool; + assert (b == null); + } + { + int16? i16 = v as int16; + assert (i16 == null); + } + { + int32? i32 = v as int32; + assert (i32 == 4711); + } + { + string? s = v as string; + assert (s == null); + } + + v = new Variant.boolean (true); + { + bool? b = v as bool; + assert (b == true); + } + { + int32? i32 = v as int32; + assert (i32 == null); + } + + v = new Variant.strv ({ "foo", "bar", "manam" }); + { + string[]? sa = v as string[]; + assert (sa != null); + assert (sa[2] == "manam"); + } + + Foo vsrc = { "foo", uint64.MAX, true }; + v = vsrc; + assert ("(stb)" == v.get_type_string ()); + { + Foo real_st = (Foo) v; + assert (real_st.s == "foo"); + assert (real_st.u64 == uint64.MAX); + assert (real_st.b == true); + + Foo? st = v as Foo; + assert (st != null); + assert (st.s == "foo"); + assert (st.u64 == uint64.MAX); + assert (st.b == true); + } + + HashTable vsrc2 = new HashTable (str_hash, str_equal); + vsrc2.insert ("foo", "bar"); + vsrc2.insert ("bar", "manam"); + v = vsrc2; + { + HashTable dict = v as HashTable; + assert (dict.lookup ("foo") == "bar"); + assert (dict.lookup ("bar") == "manam"); + } + { + HashTable? dict = v as HashTable; + assert (dict == null); + } +} diff --git a/tests/semantic/cast-gvariant-unsupported.test b/tests/semantic/cast-gvariant-unsupported.test new file mode 100644 index 000000000..2e4ed2d3e --- /dev/null +++ b/tests/semantic/cast-gvariant-unsupported.test @@ -0,0 +1,6 @@ +Invalid Code + +void main () { + Variant? v = null; + Object? array = v as Object; +} diff --git a/vala/valacastexpression.vala b/vala/valacastexpression.vala index 1dc357764..63e356155 100644 --- a/vala/valacastexpression.vala +++ b/vala/valacastexpression.vala @@ -184,6 +184,9 @@ public class Vala.CastExpression : Expression { && is_gvariant (context, inner.value_type) && !is_gvariant (context, value_type)) { // GVariant unboxing returns owned value value_type.value_owned = true; + if (value_type.get_type_signature () == null) { + Report.error (source_reference, "Casting of `GLib.Variant' to `%s' is not supported".printf (value_type.to_qualified_string ())); + } } inner.target_type = inner.value_type.copy (); -- 2.47.2