[vala/tintou/gdbus-properties: 27/27] dbus: Bind properties with the GDBusProxy directly




commit cd9dc9cb228309a6e3354c3a8ef5c09e5e2620ec
Author: Corentin Noël <corentin elementary io>
Date:   Tue Apr 7 23:50:55 2020 +0200

    dbus: Bind properties with the GDBusProxy directly

 codegen/valagdbusclientmodule.vala | 259 ++++++++++++++++++++++++++++++++++++-
 codegen/valagdbusservermodule.vala | 101 +++++++++++++++
 codegen/valagobjectmodule.vala     | 172 ++++++++++++------------
 tests/Makefile.am                  |   1 +
 tests/dbus/properties.test         |  98 ++++++++++++++
 vala/valasemanticanalyzer.vala     |   5 -
 6 files changed, 548 insertions(+), 88 deletions(-)
---
diff --git a/codegen/valagdbusclientmodule.vala b/codegen/valagdbusclientmodule.vala
index af833f749..60b825657 100644
--- a/codegen/valagdbusclientmodule.vala
+++ b/codegen/valagdbusclientmodule.vala
@@ -205,17 +205,51 @@ public class Vala.GDBusClientModule : GDBusModule {
 
                cfile.add_type_member_definition (define_type);
 
+               generate_properties_enums (iface);
+
                var proxy_class_init = new CCodeFunction (lower_cname + "_class_init", "void");
                proxy_class_init.add_parameter (new CCodeParameter ("klass", cname + "Class*"));
                proxy_class_init.modifiers = CCodeModifiers.STATIC;
                push_function (proxy_class_init);
                var proxy_class = new CCodeFunctionCall (new CCodeIdentifier ("G_DBUS_PROXY_CLASS"));
                proxy_class.add_argument (new CCodeIdentifier ("klass"));
-               ccode.add_assignment (new CCodeMemberAccess.pointer (proxy_class, "g_signal"), new 
CCodeIdentifier (lower_cname + "_g_signal"));
+               ccode.add_declaration ("GDBusProxyClass *", new CCodeVariableDeclarator.zero ("proxy_class", 
proxy_class));
+               var proxy_class_identifier = new CCodeIdentifier ("proxy_class");
+
+               var object_class = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_CLASS"));
+               object_class.add_argument (new CCodeIdentifier ("klass"));
+               ccode.add_declaration ("GObjectClass *", new CCodeVariableDeclarator.zero ("object_class", 
object_class));
+               var object_class_identifier = new CCodeIdentifier ("object_class");
+
+               ccode.add_assignment (new CCodeMemberAccess.pointer (proxy_class_identifier, "g_signal"), new 
CCodeIdentifier (lower_cname + "_g_signal"));
+               ccode.add_assignment (new CCodeMemberAccess.pointer (proxy_class_identifier, 
"g_properties_changed"), new CCodeIdentifier (lower_cname + "_g_properties_changed"));
+
+               ccode.add_assignment (new CCodeMemberAccess.pointer (object_class_identifier, 
"get_property"), new CCodeIdentifier (lower_cname + "_get_property"));
+               ccode.add_assignment (new CCodeMemberAccess.pointer (object_class_identifier, 
"set_property"), new CCodeIdentifier (lower_cname + "_set_property"));
+
+               var sym_name = get_ccode_upper_case_name (iface) + "_PROXY";
+               var pspecs = new CCodeIdentifier ("%s_proxy_properties".printf (get_ccode_lower_case_name 
(iface)));
+               foreach (Property prop in iface.get_properties ()) {
+                       var upper_identifier = new CCodeIdentifier ("%s_%s_PROPERTY".printf (sym_name, 
Symbol.camel_case_to_lower_case (prop.name).ascii_up ()));
+                       var override_func = new CCodeFunctionCall (new CCodeIdentifier 
("g_object_class_override_property"));
+                       override_func.add_argument (object_class_identifier);
+                       override_func.add_argument (upper_identifier);
+                       override_func.add_argument (get_property_canonical_cconstant (prop));
+                       ccode.add_expression (override_func);
+                       var find_prop = new CCodeFunctionCall (new CCodeIdentifier 
("g_object_class_find_property"));
+                       find_prop.add_argument (object_class_identifier);
+                       find_prop.add_argument (get_property_canonical_cconstant (prop));
+                       ccode.add_assignment (new CCodeElementAccess (pspecs, upper_identifier), find_prop);
+               }
+
                pop_function ();
                cfile.add_function (proxy_class_init);
 
+               generate_pspec_from_dbus_property (iface);
+               generate_get_property_function (iface);
+               generate_set_property_function (iface);
                generate_signal_handler_function (iface);
+               generate_properties_changed_handler_function (iface);
 
                if (in_plugin) {
                        var proxy_class_finalize = new CCodeFunction (lower_cname + "_class_finalize", 
"void");
@@ -501,6 +535,117 @@ public class Vala.GDBusClientModule : GDBusModule {
                return wrapper_name;
        }
 
+       void generate_properties_enums (ObjectTypeSymbol sym) {
+               var prop_enum = new CCodeEnum ();
+               var sym_name = get_ccode_upper_case_name (sym) + "_PROXY";
+               prop_enum.add_value (new CCodeEnumValue ("%s_0_PROPERTY".printf (sym_name)));
+               var properties = sym.get_properties ();
+               foreach (Property prop in properties) {
+                          prop_enum.add_value (new CCodeEnumValue ("%s_%s_PROPERTY".printf (sym_name, 
Symbol.camel_case_to_lower_case (prop.name).ascii_up ())));
+               }
+
+               var last_prop = "%s_NUM_PROPERTIES".printf (sym_name);
+               prop_enum.add_value (new CCodeEnumValue (last_prop));
+               cfile.add_type_declaration (prop_enum);
+
+               var prop_array_decl = new CCodeDeclaration ("GParamSpec*");
+               prop_array_decl.modifiers |= CCodeModifiers.STATIC;
+               prop_array_decl.add_declarator (new CCodeVariableDeclarator ("%s_proxy_properties".printf 
(get_ccode_lower_case_name (sym)), null, new CCodeDeclaratorSuffix.with_array (new CCodeIdentifier 
(last_prop))));
+               cfile.add_type_declaration (prop_array_decl);
+       }
+
+       void generate_get_property_function (ObjectTypeSymbol sym) {
+               var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + "proxy_get_property", 
"void");
+               cfunc.add_parameter (new CCodeParameter ("object", "GObject*"));
+               cfunc.add_parameter (new CCodeParameter ("property_id", "guint"));
+               cfunc.add_parameter (new CCodeParameter ("value", "GValue*"));
+               cfunc.add_parameter (new CCodeParameter ("pspec", "GParamSpec*"));
+
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+
+               cfile.add_function_declaration (cfunc);
+
+               push_function (cfunc);
+
+               ccode.add_declaration ("%s *".printf (get_ccode_name (sym)), new CCodeVariableDeclarator 
("self"));
+               ccode.add_declaration ("GVariant *", new CCodeVariableDeclarator ("variant", null));
+               ccode.add_declaration ("const gchar *", new CCodeVariableDeclarator.zero 
("dbus_property_name", new CCodeConstant ("NULL")));
+
+               var cast_call = generate_instance_cast (new CCodeIdentifier ("object"), sym);
+               ccode.add_assignment (new CCodeIdentifier ("self"), cast_call);
+
+               ccode.open_switch (new CCodeIdentifier ("property_id"));
+
+               var sym_name = get_ccode_upper_case_name (sym) + "_PROXY";
+               var dbus_proxy_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_DBUS_PROXY"));
+               dbus_proxy_cast.add_argument (new CCodeIdentifier ("object"));
+               foreach (Property prop in sym.get_properties ()) {
+                       if (prop.access != SymbolAccessibility.PUBLIC) {
+                               continue;
+                       }
+
+                       var upper_identifier = new CCodeIdentifier ("%s_%s_PROPERTY".printf (sym_name, 
Symbol.camel_case_to_lower_case (prop.name).ascii_up ()));
+                       ccode.add_case (upper_identifier);
+
+                       string proxy_name = "%sdbus_proxy_get_%s".printf (get_ccode_lower_case_prefix (sym), 
prop.name);
+                       property_to_value (prop, new CCodeIdentifier (proxy_name), new CCodeIdentifier 
("self"));
+
+                       ccode.add_break ();
+               }
+
+               ccode.add_default ();
+               ccode.add_statement (new CCodeReturnStatement (null));
+               ccode.close ();
+
+               pop_function ();
+
+               cfile.add_function (cfunc);
+       }
+
+       void generate_set_property_function (ObjectTypeSymbol sym) {
+               // Create the function
+               var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + "proxy_set_property", 
"void");
+               cfunc.add_parameter (new CCodeParameter ("object", "GObject*"));
+               cfunc.add_parameter (new CCodeParameter ("property_id", "guint"));
+               cfunc.add_parameter (new CCodeParameter ("value", "const GValue*"));
+               cfunc.add_parameter (new CCodeParameter ("pspec", "GParamSpec*"));
+
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+
+               cfile.add_function_declaration (cfunc);
+
+               push_function (cfunc);
+
+               var cast_call = generate_instance_cast (new CCodeIdentifier ("object"), sym);
+               ccode.add_declaration ("%s *".printf (get_ccode_name (sym)), new CCodeVariableDeclarator 
("self"));
+               ccode.add_assignment (new CCodeIdentifier ("self"), cast_call);
+
+               ccode.open_switch (new CCodeIdentifier ("property_id"));
+
+               var sym_name = get_ccode_upper_case_name (sym) + "_PROXY";
+               foreach (Property prop in sym.get_properties ()) {
+                       if (prop.access != SymbolAccessibility.PUBLIC) {
+                               continue;
+                       }
+
+                       var upper_identifier = new CCodeIdentifier ("%s_%s_PROPERTY".printf (sym_name, 
Symbol.camel_case_to_lower_case (prop.name).ascii_up ()));
+                       ccode.add_case (upper_identifier);
+
+                       string proxy_name = "%sdbus_proxy_set_%s".printf (get_ccode_lower_case_prefix (sym), 
prop.name);
+                       property_from_value (prop, new CCodeIdentifier (proxy_name), new CCodeIdentifier 
("self"));
+
+                       ccode.add_break ();
+               }
+
+               ccode.add_default ();
+               ccode.add_statement (new CCodeReturnStatement (null));
+               ccode.close ();
+
+               pop_function ();
+
+               cfile.add_function (cfunc);
+       }
+
        void generate_signal_handler_function (ObjectTypeSymbol sym) {
                var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + "proxy_g_signal", "void");
                cfunc.add_parameter (new CCodeParameter ("proxy", "GDBusProxy*"));
@@ -550,6 +695,118 @@ public class Vala.GDBusClientModule : GDBusModule {
                cfile.add_function (cfunc);
        }
 
+       void generate_pspec_from_dbus_property (ObjectTypeSymbol sym) {
+               var cfunc = new CCodeFunction ("_vala_%sfind_property_from_dbus_name".printf 
(get_ccode_lower_case_prefix (sym)), "GParamSpec *");
+               cfunc.add_parameter (new CCodeParameter ("dbus_property_name", "const gchar *"));
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+               
+               cfile.add_function_declaration (cfunc);
+               
+               push_function (cfunc);
+               
+               bool firstif = true;
+
+               var pspecs = new CCodeIdentifier ("%s_proxy_properties".printf (get_ccode_lower_case_name 
(sym)));
+               var sym_name = get_ccode_upper_case_name (sym) + "_PROXY";
+               foreach (Property prop in sym.get_properties ()) {
+                       if (prop.access != SymbolAccessibility.PUBLIC) {
+                               continue;
+                       }
+
+                       var ccheck = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
+                       ccheck.add_argument (new CCodeIdentifier ("dbus_property_name"));
+                       ccheck.add_argument (new CCodeConstant ("\"%s\"".printf (get_dbus_name_for_member 
(prop))));
+
+                       var cond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ccheck, new 
CCodeConstant ("0"));
+                       if (firstif) {
+                               ccode.open_if (cond);
+                               firstif = false;
+                       } else {
+                               ccode.else_if (cond);
+                       }
+
+                       var upper_identifier = new CCodeIdentifier ("%s_%s_PROPERTY".printf (sym_name, 
Symbol.camel_case_to_lower_case (prop.name).ascii_up ()));
+                       ccode.add_statement (new CCodeReturnStatement (new CCodeElementAccess (pspecs, 
upper_identifier)));
+               }
+
+               if (!firstif) {
+                       ccode.close ();
+               }
+
+               ccode.add_statement (new CCodeReturnStatement (new CCodeConstant ("NULL")));
+               
+               pop_function ();
+               cfile.add_function (cfunc);
+       }
+
+       void generate_properties_changed_handler_function (ObjectTypeSymbol sym) {
+               var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + 
"proxy_g_properties_changed", "void");
+               cfunc.add_parameter (new CCodeParameter ("proxy", "GDBusProxy*"));
+               cfunc.add_parameter (new CCodeParameter ("changed_properties", "GVariant*"));
+               cfunc.add_parameter (new CCodeParameter ("invalidated_properties", "const gchar* const*"));
+
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+
+               cfile.add_function_declaration (cfunc);
+
+               push_function (cfunc);
+
+               ccode.add_declaration ("GVariantIter *", new CCodeVariableDeclarator ("iter", null));
+               ccode.add_declaration ("const gchar *", new CCodeVariableDeclarator ("key", null));
+               ccode.add_declaration ("GParamSpec *", new CCodeVariableDeclarator ("pspec", null));
+               ccode.add_declaration ("guint", new CCodeVariableDeclarator ("n", null));
+               var pspec_identifier = new CCodeIdentifier ("pspec");
+               var n_identifier = new CCodeIdentifier ("n");
+               var variant_get = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get"));
+               variant_get.add_argument (new CCodeIdentifier ("changed_properties"));
+               variant_get.add_argument (new CCodeConstant.string ("\"a{sv}\""));
+               variant_get.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("iter")));
+               ccode.add_expression (variant_get);
+
+               var variant_iter_next = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_next"));
+               variant_iter_next.add_argument (new CCodeIdentifier ("iter"));
+               variant_iter_next.add_argument (new CCodeConstant.string ("\"{&sv}\""));
+               variant_iter_next.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("key")));
+               variant_iter_next.add_argument (new CCodeConstant ("NULL"));
+
+               ccode.open_while (variant_iter_next);
+               var find_property_from_dbus_name = new CCodeFunctionCall (new CCodeIdentifier 
("_vala_%sfind_property_from_dbus_name".printf (get_ccode_lower_case_prefix (sym))));
+               find_property_from_dbus_name.add_argument (new CCodeIdentifier ("key"));
+               ccode.add_assignment (pspec_identifier, find_property_from_dbus_name);
+
+               ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, pspec_identifier, 
new CCodeConstant ("NULL")));
+
+               var notify_by_pspec = new CCodeFunctionCall (new CCodeIdentifier 
("g_object_notify_by_pspec"));
+               notify_by_pspec.add_argument (new CCodeCastExpression (new CCodeIdentifier ("proxy"), 
"GObject *"));
+               notify_by_pspec.add_argument (pspec_identifier);
+               ccode.add_expression (notify_by_pspec);
+
+               ccode.close ();
+               ccode.close ();
+
+               var variant_iter_free = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_free"));
+               variant_iter_free.add_argument (new CCodeIdentifier ("iter"));
+               ccode.add_expression (variant_iter_free);
+
+               var for_init = new CCodeAssignment (n_identifier, new CCodeConstant ("0"));
+               var for_increment = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, 
n_identifier);
+               var for_condition = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new 
CCodeElementAccess (new CCodeIdentifier ("invalidated_properties"), n_identifier), new CCodeConstant 
("NULL"));
+               ccode.open_for (for_init, for_condition, for_increment);
+               var find_invalidated_property_from_dbus_name = new CCodeFunctionCall (new CCodeIdentifier 
("_vala_%sfind_property_from_dbus_name".printf (get_ccode_lower_case_prefix (sym))));
+               find_invalidated_property_from_dbus_name.add_argument (new CCodeElementAccess (new 
CCodeIdentifier ("invalidated_properties"), n_identifier));
+               ccode.add_assignment (pspec_identifier, find_invalidated_property_from_dbus_name);
+
+               ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, pspec_identifier, 
new CCodeConstant ("NULL")));
+               ccode.add_expression (notify_by_pspec);
+               ccode.close ();
+               ccode.close ();
+
+               pop_function ();
+
+               cfile.add_function (cfunc);
+       }
+
+
        void generate_marshalling (Method m, CallType call_type, string? iface_name, string? method_name, int 
method_timeout) {
                var gdbusproxy = new CCodeCastExpression (new CCodeIdentifier ("self"), "GDBusProxy *");
 
diff --git a/codegen/valagdbusservermodule.vala b/codegen/valagdbusservermodule.vala
index 06166772e..0790cff88 100644
--- a/codegen/valagdbusservermodule.vala
+++ b/codegen/valagdbusservermodule.vala
@@ -710,6 +710,105 @@ public class Vala.GDBusServerModule : GDBusClientModule {
                }
        }
 
+       void generate_property_notify (ObjectTypeSymbol sym, Property prop) {
+               var cfunc = new CCodeFunction ("_%snotify_%s".printf (get_ccode_lower_case_prefix (sym), 
prop.name), "void");
+               cfunc.add_parameter (new CCodeParameter ("gobject", "GObject *"));
+               cfunc.add_parameter (new CCodeParameter ("pspec", "GParamSpec *"));
+               cfunc.add_parameter (new CCodeParameter ("user_data", "gpointer"));
+
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+
+               push_function (cfunc);
+
+               ccode.add_declaration ("gpointer*", new CCodeVariableDeclarator.zero ("data", new 
CCodeIdentifier ("user_data")));
+               ccode.add_declaration ("GError*", new CCodeVariableDeclarator.zero ("error", new 
CCodeConstant ("NULL")));
+               ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator.zero ("parameters", new 
CCodeConstant ("NULL")));
+               ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator.zero ("variant", new 
CCodeConstant ("NULL")));
+               ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator ("changed_builder", 
null));
+               ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator ("invalidated_builder", 
null));
+
+               var variant_builder_init = new CCodeFunctionCall (new CCodeIdentifier 
("g_variant_builder_init"));
+               variant_builder_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, 
new CCodeIdentifier ("changed_builder")));
+               variant_builder_init.add_argument (new CCodeConstant ("G_VARIANT_TYPE_VARDICT"));
+               ccode.add_expression (variant_builder_init);
+
+               var variant_builder_init2 = new CCodeFunctionCall (new CCodeIdentifier 
("g_variant_builder_init"));
+               variant_builder_init2.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, 
new CCodeIdentifier ("invalidated_builder")));
+               variant_builder_init2.add_argument (new CCodeConstant ("G_VARIANT_TYPE_STRING_ARRAY"));
+               ccode.add_expression (variant_builder_init2);
+
+               var cast_call = generate_instance_cast (new CCodeIdentifier ("gobject"), sym);
+               ccode.add_declaration ("%s *".printf (get_ccode_name (sym)), new CCodeVariableDeclarator 
("self"));
+               ccode.add_assignment (new CCodeIdentifier ("self"), cast_call);
+
+               var dbus_get_property = new CCodeFunctionCall (new CCodeIdentifier ("_dbus_%s".printf 
(get_ccode_name (prop.get_accessor))));
+               dbus_get_property.add_argument (new CCodeIdentifier ("self"));
+               ccode.add_assignment (new CCodeIdentifier ("variant"), dbus_get_property);
+
+               var variant_builder_add = new CCodeFunctionCall (new CCodeIdentifier 
("g_variant_builder_add"));
+               variant_builder_add.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, 
new CCodeIdentifier ("changed_builder")));
+               variant_builder_add.add_argument (new CCodeConstant.string ("\"{sv}\""));
+               variant_builder_add.add_argument (new CCodeConstant.string ("\"%s\"".printf 
(get_dbus_name_for_member (prop))));
+               variant_builder_add.add_argument (new CCodeIdentifier ("variant"));
+               ccode.add_expression (variant_builder_add);
+
+               var variant_new = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new"));
+               variant_new.add_argument (new CCodeConstant.string ("\"(sa{sv}as)\""));
+               variant_new.add_argument (new CCodeConstant.string ("\"%s\"".printf (get_dbus_name (sym))));
+               variant_new.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("changed_builder")));
+               variant_new.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("invalidated_builder")));
+               ccode.add_assignment (new CCodeIdentifier ("parameters"), variant_new);
+
+               var disconnect_call = new CCodeFunctionCall (new CCodeIdentifier 
("g_dbus_connection_emit_signal"));
+               disconnect_call.add_argument (new CCodeCastExpression (new CCodeElementAccess (new 
CCodeIdentifier ("data"), new CCodeConstant ("1")), "GDBusConnection *"));
+               disconnect_call.add_argument (new CCodeConstant ("NULL"));
+               disconnect_call.add_argument (new CCodeCastExpression (new CCodeElementAccess (new 
CCodeIdentifier ("data"), new CCodeConstant ("2")), "const gchar *"));
+               disconnect_call.add_argument (new CCodeConstant.string 
("\"org.freedesktop.DBus.Properties\""));
+               disconnect_call.add_argument (new CCodeConstant.string ("\"PropertiesChanged\""));
+               disconnect_call.add_argument (new CCodeIdentifier ("parameters"));
+               disconnect_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("error")));
+               ccode.add_expression (disconnect_call);
+
+               pop_function ();
+
+               cfile.add_function_declaration (cfunc);
+               cfile.add_function (cfunc);
+       }
+
+       void handle_properties (ObjectTypeSymbol sym, bool connect) {
+               string dbus_iface_name = get_dbus_name (sym);
+               if (dbus_iface_name == null) {
+                       return;
+               }
+
+               foreach (Property prop in sym.get_properties ()) {
+                       if (prop.access != SymbolAccessibility.PUBLIC) {
+                               continue;
+                       }
+                       if (!is_dbus_visible (prop)) {
+                               continue;
+                       }
+
+                       var notify_name = "_%snotify_%s".printf (get_ccode_lower_case_prefix (sym), 
prop.name);
+                       if (connect) {
+                               generate_property_notify (sym, prop);
+                               var connect_call = new CCodeFunctionCall (new CCodeIdentifier 
("g_signal_connect"));
+                               connect_call.add_argument (new CCodeIdentifier ("object"));
+                               connect_call.add_argument (new CCodeConstant.string ("\"notify::%s\"".printf 
(get_ccode_name (prop))));
+                               connect_call.add_argument (new CCodeCastExpression (new CCodeIdentifier 
(notify_name), "GCallback"));
+                               connect_call.add_argument (new CCodeIdentifier ("data"));
+                               ccode.add_expression (connect_call);
+                       } else {
+                               // disconnect the signals
+                               var disconnect_call = new CCodeFunctionCall (new CCodeIdentifier 
("g_signal_handlers_disconnect_by_func"));
+                               disconnect_call.add_argument (new CCodeElementAccess (new CCodeIdentifier 
("data"), new CCodeConstant ("0")));
+                               disconnect_call.add_argument (new CCodeIdentifier (notify_name));
+                               disconnect_call.add_argument (new CCodeIdentifier ("data"));
+                               ccode.add_expression (disconnect_call);
+                       }
+               }
+       }
+
        void generate_interface_method_call_function (ObjectTypeSymbol sym) {
                var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + 
"dbus_interface_method_call", "void");
                cfunc.add_parameter (new CCodeParameter ("connection", "GDBusConnection*"));
@@ -1160,6 +1259,7 @@ public class Vala.GDBusServerModule : GDBusClientModule {
                ccode.close ();
 
                handle_signals (sym, true);
+               handle_properties (sym, true);
 
                ccode.add_return (new CCodeIdentifier ("result"));
 
@@ -1176,6 +1276,7 @@ public class Vala.GDBusServerModule : GDBusClientModule {
                ccode.add_declaration ("gpointer*", new CCodeVariableDeclarator ("data", new CCodeIdentifier 
("user_data")));
 
                handle_signals (sym, false);
+               handle_properties (sym, false);
 
                var unref_object = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_unref_function 
(sym)));
                unref_object.add_argument (new CCodeElementAccess (new CCodeIdentifier ("data"), new 
CCodeConstant ("0")));
diff --git a/codegen/valagobjectmodule.vala b/codegen/valagobjectmodule.vala
index 54b1d4401..3a5bd7971 100644
--- a/codegen/valagobjectmodule.vala
+++ b/codegen/valagobjectmodule.vala
@@ -232,56 +232,7 @@ public class Vala.GObjectModule : GTypeModule {
                        }
 
                        ccode.add_case (new CCodeIdentifier ("%s_PROPERTY".printf (get_ccode_upper_case_name 
(prop))));
-                       if (prop.property_type.is_real_struct_type ()) {
-                               ccode.open_block ();
-                               ccode.add_declaration (get_ccode_name (prop.property_type), new 
CCodeVariableDeclarator ("boxed"));
-
-                               ccall = new CCodeFunctionCall (cfunc);
-                               ccall.add_argument (cself);
-                               if (prop.property_type.nullable) {
-                                       ccode.add_assignment (new CCodeIdentifier ("boxed"), ccall);
-                               } else {
-                                       ccall.add_argument (new CCodeUnaryExpression 
(CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("boxed")));
-                                       ccode.add_expression (ccall);
-                               }
-
-                               var csetcall = new CCodeFunctionCall ();
-                               csetcall.call = get_value_setter_function (prop.property_type);
-                               csetcall.add_argument (new CCodeIdentifier ("value"));
-                               if (prop.property_type.nullable) {
-                                       csetcall.add_argument (new CCodeIdentifier ("boxed"));
-                               } else {
-                                       csetcall.add_argument (new CCodeUnaryExpression 
(CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("boxed")));
-                               }
-                               add_guarded_expression (prop, csetcall);
-
-                               if (requires_destroy (prop.get_accessor.value_type)) {
-                                       ccode.add_expression (destroy_value (new GLibValue 
(prop.get_accessor.value_type, new CCodeIdentifier ("boxed"), true)));
-                               }
-                               ccode.close ();
-                       } else {
-                               ccall = new CCodeFunctionCall (cfunc);
-                               ccall.add_argument (cself);
-                               var array_type = prop.property_type as ArrayType;
-                               if (array_type != null && get_ccode_array_length (prop) && 
array_type.element_type.type_symbol == string_type.type_symbol) {
-                                       // G_TYPE_STRV
-                                       ccode.open_block ();
-                                       ccode.add_declaration ("int", new CCodeVariableDeclarator ("length"));
-                                       ccall.add_argument (new CCodeUnaryExpression 
(CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("length")));
-                               }
-                               var csetcall = new CCodeFunctionCall ();
-                               if (prop.get_accessor.value_type.value_owned) {
-                                       csetcall.call = get_value_taker_function (prop.property_type);
-                               } else {
-                                       csetcall.call = get_value_setter_function (prop.property_type);
-                               }
-                               csetcall.add_argument (new CCodeIdentifier ("value"));
-                               csetcall.add_argument (ccall);
-                               add_guarded_expression (prop, csetcall);
-                               if (array_type != null && get_ccode_array_length (prop) && 
array_type.element_type.type_symbol == string_type.type_symbol) {
-                                       ccode.close ();
-                               }
-                       }
+                       property_to_value (prop, cfunc, cself);
                        ccode.add_break ();
                }
 
@@ -333,6 +284,59 @@ public class Vala.GObjectModule : GTypeModule {
                cfile.add_function (get_prop);
        }
 
+       protected void property_to_value (Property prop, CCodeExpression get_prop_func, CCodeExpression 
cself) {
+               if (prop.property_type.is_real_struct_type ()) {
+                       ccode.open_block ();
+                       ccode.add_declaration (get_ccode_name (prop.property_type), new 
CCodeVariableDeclarator ("boxed"));
+
+                       var ccall = new CCodeFunctionCall (get_prop_func);
+                       ccall.add_argument (cself);
+                       if (prop.property_type.nullable) {
+                               ccode.add_assignment (new CCodeIdentifier ("boxed"), ccall);
+                       } else {
+                               ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, 
new CCodeIdentifier ("boxed")));
+                               ccode.add_expression (ccall);
+                       }
+
+                       var csetcall = new CCodeFunctionCall ();
+                       csetcall.call = get_value_setter_function (prop.property_type);
+                       csetcall.add_argument (new CCodeIdentifier ("value"));
+                       if (prop.property_type.nullable) {
+                               csetcall.add_argument (new CCodeIdentifier ("boxed"));
+                       } else {
+                               csetcall.add_argument (new CCodeUnaryExpression 
(CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("boxed")));
+                       }
+                       add_guarded_expression (prop, csetcall);
+
+                       if (requires_destroy (prop.get_accessor.value_type)) {
+                               ccode.add_expression (destroy_value (new GLibValue 
(prop.get_accessor.value_type, new CCodeIdentifier ("boxed"), true)));
+                       }
+                       ccode.close ();
+               } else {
+                       var ccall = new CCodeFunctionCall (get_prop_func);
+                       ccall.add_argument (cself);
+                       var array_type = prop.property_type as ArrayType;
+                       if (array_type != null && get_ccode_array_length (prop) && 
array_type.element_type.type_symbol == string_type.type_symbol) {
+                               // G_TYPE_STRV
+                               ccode.open_block ();
+                               ccode.add_declaration ("int", new CCodeVariableDeclarator ("length"));
+                               ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, 
new CCodeIdentifier ("length")));
+                       }
+                       var csetcall = new CCodeFunctionCall ();
+                       if (prop.get_accessor.value_type.value_owned) {
+                               csetcall.call = get_value_taker_function (prop.property_type);
+                       } else {
+                               csetcall.call = get_value_setter_function (prop.property_type);
+                       }
+                       csetcall.add_argument (new CCodeIdentifier ("value"));
+                       csetcall.add_argument (ccall);
+                       add_guarded_expression (prop, csetcall);
+                       if (array_type != null && get_ccode_array_length (prop) && 
array_type.element_type.type_symbol == string_type.type_symbol) {
+                               ccode.close ();
+                       }
+               }
+       }
+
        private void add_set_property_function (Class cl) {
                var set_prop = new CCodeFunction ("_vala_%s_set_property".printf (get_ccode_lower_case_name 
(cl, null)), "void");
                set_prop.modifiers = CCodeModifiers.STATIC;
@@ -381,38 +385,7 @@ public class Vala.GObjectModule : GTypeModule {
                        }
 
                        ccode.add_case (new CCodeIdentifier ("%s_PROPERTY".printf (get_ccode_upper_case_name 
(prop))));
-                       ccall = new CCodeFunctionCall (cfunc);
-                       ccall.add_argument (cself);
-                       if (prop.property_type is ArrayType && 
((ArrayType)prop.property_type).element_type.type_symbol == string_type.type_symbol) {
-                               ccode.open_block ();
-                               ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("boxed"));
-
-                               var cgetcall = new CCodeFunctionCall (new CCodeIdentifier 
("g_value_get_boxed"));
-                               cgetcall.add_argument (new CCodeIdentifier ("value"));
-                               ccode.add_assignment (new CCodeIdentifier ("boxed"), cgetcall);
-                               ccall.add_argument (new CCodeIdentifier ("boxed"));
-
-                               if (get_ccode_array_length (prop)) {
-                                       var cisnull = new CCodeBinaryExpression 
(CCodeBinaryOperator.EQUALITY, new CCodeIdentifier ("boxed"), new CCodeConstant ("NULL"));
-                                       var cstrvlen = new CCodeFunctionCall (new CCodeIdentifier 
("g_strv_length"));
-                                       cstrvlen.add_argument (new CCodeIdentifier ("boxed"));
-                                       var ccond = new CCodeConditionalExpression (cisnull, new 
CCodeConstant ("0"), cstrvlen);
-
-                                       ccall.add_argument (ccond);
-                               }
-                               add_guarded_expression (prop, ccall);
-                               ccode.close ();
-                       } else {
-                               var cgetcall = new CCodeFunctionCall ();
-                               if (prop.property_type.type_symbol != null) {
-                                       cgetcall.call = new CCodeIdentifier (get_ccode_get_value_function 
(prop.property_type.type_symbol));
-                               } else {
-                                       cgetcall.call = new CCodeIdentifier ("g_value_get_pointer");
-                               }
-                               cgetcall.add_argument (new CCodeIdentifier ("value"));
-                               ccall.add_argument (cgetcall);
-                               add_guarded_expression (prop, ccall);
-                       }
+                       property_from_value (prop, cfunc, cself);
                        ccode.add_break ();
                }
 
@@ -461,6 +434,41 @@ public class Vala.GObjectModule : GTypeModule {
                cfile.add_function (set_prop);
        }
 
+       protected void property_from_value (Property prop, CCodeExpression set_prop_func, CCodeExpression 
cself) {
+               var ccall = new CCodeFunctionCall (set_prop_func);
+               ccall.add_argument (cself);
+               if (prop.property_type is ArrayType && 
((ArrayType)prop.property_type).element_type.type_symbol == string_type.type_symbol) {
+                       ccode.open_block ();
+                       ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("boxed"));
+
+                       var cgetcall = new CCodeFunctionCall (new CCodeIdentifier ("g_value_get_boxed"));
+                       cgetcall.add_argument (new CCodeIdentifier ("value"));
+                       ccode.add_assignment (new CCodeIdentifier ("boxed"), cgetcall);
+                       ccall.add_argument (new CCodeIdentifier ("boxed"));
+
+                       if (get_ccode_array_length (prop)) {
+                               var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new 
CCodeIdentifier ("boxed"), new CCodeConstant ("NULL"));
+                               var cstrvlen = new CCodeFunctionCall (new CCodeIdentifier ("g_strv_length"));
+                               cstrvlen.add_argument (new CCodeIdentifier ("boxed"));
+                               var ccond = new CCodeConditionalExpression (cisnull, new CCodeConstant ("0"), 
cstrvlen);
+
+                               ccall.add_argument (ccond);
+                       }
+                       add_guarded_expression (prop, ccall);
+                       ccode.close ();
+               } else {
+                       var cgetcall = new CCodeFunctionCall ();
+                       if (prop.property_type.type_symbol != null) {
+                               cgetcall.call = new CCodeIdentifier (get_ccode_get_value_function 
(prop.property_type.type_symbol));
+                       } else {
+                               cgetcall.call = new CCodeIdentifier ("g_value_get_pointer");
+                       }
+                       cgetcall.add_argument (new CCodeIdentifier ("value"));
+                       ccall.add_argument (cgetcall);
+                       add_guarded_expression (prop, ccall);
+               }
+       }
+
        private void emit_invalid_property_id_warn () {
                // warn on invalid property id
                var cwarn = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_WARN_INVALID_PROPERTY_ID"));
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b620a847f..00ecf4e46 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -662,6 +662,7 @@ TESTS = \
        dbus/connection.test \
        dbus/dynamic-method.test \
        dbus/enum-string-marshalling.vala \
+       dbus/properties.test \
        dbus/signals.test \
        dbus/filedescriptor.test \
        dbus/filedescriptor-async.test \
diff --git a/tests/dbus/properties.test b/tests/dbus/properties.test
new file mode 100644
index 000000000..78b2df521
--- /dev/null
+++ b/tests/dbus/properties.test
@@ -0,0 +1,98 @@
+Packages: gio-2.0
+D-Bus
+
+Program: client
+
+[DBus (name = "org.example.Test")]
+interface Test : Object {
+       public abstract string test_property { owned get; set; }
+       public abstract int test_int_property { get; set; }
+       public abstract void change_everything () throws GLib.Error;
+       public abstract void check_everything () throws GLib.Error;
+}
+
+class Test2 : Object {
+       public string test_property { get; set; }
+       public int test_int_property { get; set; }
+}
+
+void main () {
+       // client
+       Test test = Bus.get_proxy_sync (BusType.SESSION, "org.example.Test", "/org/example/test", 
DBusProxyFlags.NONE);
+       assert (test.test_property == "foo");
+       assert (test.test_int_property == 17);
+
+       MainLoop main_loop = new MainLoop ();
+       Test2 test2 = new Test2 ();
+       test.bind_property ("test-property", test2, "test-property", 
GLib.BindingFlags.SYNC_CREATE|GLib.BindingFlags.BIDIRECTIONAL);
+       test.bind_property ("test-int-property", test2, "test-int-property", 
GLib.BindingFlags.SYNC_CREATE|GLib.BindingFlags.BIDIRECTIONAL);
+
+       assert (test2.test_property == "foo");
+       assert (test2.test_int_property == 17);
+
+       test2.notify["test-int-property"].connect (() => {
+               main_loop.quit ();
+       });
+
+       test.change_everything ();
+       main_loop.run ();
+       assert (test.test_property == "bar");
+       assert (test.test_int_property == 53);
+
+       assert (test2.test_property == "bar");
+       assert (test2.test_int_property == 53);
+
+       test2.test_property = "baz";
+       test2.test_int_property = 765;
+       main_loop = new MainLoop ();
+       main_loop.run ();
+
+       assert (test.test_property == "baz");
+       assert (test.test_int_property == 765);
+
+       test.check_everything ();
+}
+
+Program: server
+
+[DBus (name = "org.example.Test")]
+class Test : Object {
+       public string test_property { owned get; set; default = "foo";}
+       public int test_int_property { get; set; default = 17; }
+
+       public void change_everything () throws GLib.Error {
+               test_property = "bar";
+               test_int_property = 53;
+       }
+
+       public void check_everything () throws GLib.Error {
+               assert (test_property == "baz");
+               assert (test_int_property == 765);
+       }
+}
+
+MainLoop main_loop;
+
+void client_exit (Pid pid, int status) {
+       // client finished, terminate server
+       assert (status == 0);
+       main_loop.quit ();
+}
+
+void main () {
+       var conn = Bus.get_sync (BusType.SESSION);
+       conn.register_object ("/org/example/test", new Test ());
+
+       // try to register service in session bus
+       var request_result = conn.call_sync ("org.freedesktop.DBus", "/org/freedesktop/DBus", 
"org.freedesktop.DBus", "RequestName",
+                                             new Variant ("(su)", "org.example.Test", 0x4), null, 0, -1);
+       assert ((uint) request_result.get_child_value (0) == 1);
+
+       // server ready, spawn client
+       Pid client_pid;
+       Process.spawn_async (null, { "dbus_properties_client" }, null, SpawnFlags.DO_NOT_REAP_CHILD, null, 
out client_pid);
+       ChildWatch.add (client_pid, client_exit);
+
+       main_loop = new MainLoop ();
+       main_loop.run ();
+}
diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala
index 2c8db80c0..0232e7abf 100644
--- a/vala/valasemanticanalyzer.vala
+++ b/vala/valasemanticanalyzer.vala
@@ -476,11 +476,6 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        return false;
                }
 
-               if (type_sym is Interface && type_sym.get_attribute ("DBus") != null) {
-                       // GObject properties not currently supported in D-Bus interfaces
-                       return false;
-               }
-
                return true;
        }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]