/* valagdbusservermodule.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.GDBusServerModule : GDBusClientModule { string generate_dbus_wrapper (Method m, ObjectTypeSymbol sym, bool ready = false) { string wrapper_name = "_dbus_%s".printf (get_ccode_name (m)); bool need_goto_label = ready; if (m.base_method != null) { m = m.base_method; } else if (m.base_interface_method != null) { m = m.base_interface_method; } if (ready) { // async ready function wrapper_name += "_ready"; } var function = new CCodeFunction (wrapper_name); function.modifiers = CCodeModifiers.STATIC; if (!ready) { function.add_parameter (new CCodeParameter ("self", get_ccode_name (sym) + "*")); function.add_parameter (new CCodeParameter ("_parameters_", "GVariant*")); function.add_parameter (new CCodeParameter ("invocation", "GDBusMethodInvocation*")); } else { function.add_parameter (new CCodeParameter ("source_object", "GObject *")); function.add_parameter (new CCodeParameter ("_res_", "GAsyncResult *")); function.add_parameter (new CCodeParameter ("_user_data_", "gpointer")); } push_function (function); CCodeIdentifier ready_data_expr = m.coroutine ? new CCodeIdentifier ("_ready_data") : null; string ready_data_struct_name = Symbol.lower_case_to_camel_case (get_ccode_name (m)) + "ReadyData"; if (ready) { ccode.add_declaration (ready_data_struct_name + "*", new CCodeVariableDeclarator ("_ready_data", new CCodeIdentifier ("_user_data_"))); ccode.add_declaration ("GDBusMethodInvocation*", new CCodeVariableDeclarator ("invocation", new CCodeMemberAccess.pointer (ready_data_expr, "_invocation_"))); } var connection = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_method_invocation_get_connection")); connection.add_argument (new CCodeIdentifier ("invocation")); bool no_reply = is_dbus_no_reply (m); bool uses_fd = dbus_method_uses_file_descriptor (m); if (uses_fd) { cfile.add_include ("gio/gunixfdlist.h"); ccode.add_declaration ("GUnixFDList*", new CCodeVariableDeclarator ("_fd_list")); } bool uses_error = false; if (!m.coroutine || ready) { ccode.add_declaration ("GError*", new CCodeVariableDeclarator.zero ("error", new CCodeConstant ("NULL"))); uses_error = true; } if (!ready) { ccode.add_declaration ("GVariantIter", new CCodeVariableDeclarator ("_arguments_iter")); var iter_init = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_init")); iter_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_arguments_iter"))); iter_init.add_argument (new CCodeIdentifier ("_parameters_")); ccode.add_expression (iter_init); } CCodeFunctionCall ccall; if (!ready) { ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (m))); ccall.add_argument (new CCodeIdentifier ("self")); } else { ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_finish_name (m))); ccall.add_argument (new CCodeCastExpression (new CCodeIdentifier ("source_object"), get_ccode_name (sym) + "*")); ccall.add_argument (new CCodeIdentifier ("_res_")); } if (!ready) { if (uses_fd) { ccode.add_declaration ("gint", new CCodeVariableDeclarator.zero ("_fd_index", new CCodeConstant ("0"))); ccode.add_declaration ("gint", new CCodeVariableDeclarator ("_fd")); } CCodeStruct? ready_data_struct = null; if (m.coroutine) { ready_data_struct = new CCodeStruct ("_" + ready_data_struct_name); ready_data_struct.add_field ("GDBusMethodInvocation*", "_invocation_"); append_struct (ready_data_struct); var ready_data_alloc = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0")); ready_data_alloc.add_argument (new CCodeIdentifier (ready_data_struct_name)); ccode.add_declaration (ready_data_struct_name + "*", new CCodeVariableDeclarator ("_ready_data")); ccode.add_assignment (ready_data_expr, ready_data_alloc); ccode.add_assignment (new CCodeMemberAccess.pointer (ready_data_expr, "_invocation_"), new CCodeIdentifier ("invocation")); } foreach (Parameter param in m.get_parameters ()) { string param_name = get_variable_cname (param.name); if (param.direction != ParameterDirection.IN) { continue; } if (param.variable_type is ObjectType && param.variable_type.type_symbol.get_full_name () == "GLib.Cancellable") { continue; } if (param.variable_type is ObjectType && param.variable_type.type_symbol.get_full_name () == "GLib.BusName") { // ignore BusName sender parameters continue; } CCodeExpression param_expr; if (ready_data_expr != null) { param_expr = new CCodeMemberAccess.pointer (ready_data_expr, param_name); } else { param_expr = new CCodeIdentifier (param_name); } var owned_type = param.variable_type.copy (); owned_type.value_owned = true; if (ready_data_struct != null) { ready_data_struct.add_field (get_ccode_name (owned_type), param_name); } else { ccode.add_declaration (get_ccode_name (owned_type), new CCodeVariableDeclarator.zero (param_name, default_value_for_type (param.variable_type, true))); } var array_type = param.variable_type as ArrayType; if (array_type != null) { var length_ctype = get_ccode_array_length_type (array_type); for (int dim = 1; dim <= array_type.rank; dim++) { string length_cname = get_variable_array_length_cname (param, dim); if (ready_data_struct != null) { ready_data_struct.add_field (length_ctype, length_cname); } else { ccode.add_declaration (length_ctype, new CCodeVariableDeclarator.zero (length_cname, new CCodeConstant ("0"))); } } } var message_expr = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_method_invocation_get_message")); message_expr.add_argument (new CCodeIdentifier ("invocation")); bool may_fail; receive_dbus_value (param.variable_type, message_expr, new CCodeIdentifier ("_arguments_iter"), param_expr, param, new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("error")), out may_fail); if (may_fail) { if (!uses_error) { ccode.add_declaration ("GError*", new CCodeVariableDeclarator.zero ("error", new CCodeConstant ("NULL"))); uses_error = true; } ccode.open_if (new CCodeIdentifier ("error")); var return_error = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_method_invocation_take_error")); return_error.add_argument (new CCodeIdentifier ("invocation")); return_error.add_argument (new CCodeIdentifier ("error")); ccode.add_expression (return_error); if (need_goto_label || requires_destroy (owned_type)) { ccode.add_goto ("_error"); need_goto_label = true; } else { ccode.add_return (); } ccode.close (); } else if (!need_goto_label && m.tree_can_fail && requires_destroy (owned_type)) { need_goto_label = true; } } } foreach (Parameter param in m.get_parameters ()) { string param_name = get_variable_cname (param.name); CCodeExpression param_expr; if (ready_data_expr != null && param.direction == ParameterDirection.IN) { param_expr = new CCodeMemberAccess.pointer (ready_data_expr, param_name); } else { param_expr = new CCodeIdentifier (param_name); } if (param.direction == ParameterDirection.IN && !ready) { if (param.variable_type is ObjectType && param.variable_type.type_symbol.get_full_name () == "GLib.Cancellable") { ccall.add_argument (new CCodeConstant ("NULL")); continue; } if (param.variable_type is ObjectType && param.variable_type.type_symbol.get_full_name () == "GLib.BusName") { // ignore BusName sender parameters var sender = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_method_invocation_get_sender")); sender.add_argument (new CCodeIdentifier ("invocation")); ccall.add_argument (sender); continue; } unowned Struct? st = param.variable_type.type_symbol as Struct; if (st != null && !st.is_simple_type ()) { ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, param_expr)); } else { ccall.add_argument (param_expr); } } else if (param.direction == ParameterDirection.OUT && (!m.coroutine || ready)) { ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, param_expr)); } var array_type = param.variable_type as ArrayType; if (array_type != null) { for (int dim = 1; dim <= array_type.rank; dim++) { string length_cname = get_variable_array_length_cname (param, dim); CCodeExpression length_expr; if (ready_data_expr != null && param.direction == ParameterDirection.IN) length_expr = new CCodeMemberAccess.pointer (ready_data_expr, length_cname); else length_expr = new CCodeIdentifier (length_cname); if (param.direction == ParameterDirection.IN && !ready) { ccall.add_argument (length_expr); } else if (param.direction == ParameterDirection.OUT && !no_reply && (!m.coroutine || ready)) { ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, length_expr)); } } } } if (!m.coroutine || ready) { if (!(m.return_type is VoidType)) { if (m.return_type.is_real_non_null_struct_type ()) { ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("result"))); } else { var array_type = m.return_type as ArrayType; if (array_type != null) { for (int dim = 1; dim <= array_type.rank; dim++) { string length_cname = get_array_length_cname ("result", dim); ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (length_cname))); } } } } } if (m.coroutine && !ready) { ccall.add_argument (new CCodeCastExpression (new CCodeIdentifier (wrapper_name + "_ready"), "GAsyncReadyCallback")); ccall.add_argument (ready_data_expr); } if (!m.coroutine || ready) { if (m.tree_can_fail) { ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("error"))); } } if (!no_reply && (!m.coroutine || ready)) { if (m.return_type is VoidType || m.return_type.is_real_non_null_struct_type ()) { ccode.add_expression (ccall); } else { ccode.add_assignment (new CCodeIdentifier ("result"), ccall); } if (m.tree_can_fail) { ccode.open_if (new CCodeIdentifier ("error")); var return_error = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_method_invocation_take_error")); return_error.add_argument (new CCodeIdentifier ("invocation")); return_error.add_argument (new CCodeIdentifier ("error")); ccode.add_expression (return_error); if (need_goto_label) { ccode.add_goto ("_error"); } else { ccode.add_return (); } ccode.close (); } ccode.add_declaration ("GDBusMessage*", new CCodeVariableDeclarator.zero ("_reply_message", new CCodeConstant ("NULL"))); var message_expr = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_method_invocation_get_message")); message_expr.add_argument (new CCodeIdentifier ("invocation")); ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_message_new_method_reply")); ccall.add_argument (message_expr); ccode.add_assignment (new CCodeIdentifier ("_reply_message"), ccall); ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator ("_reply")); ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator ("_reply_builder")); var builder_init = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init")); builder_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_reply_builder"))); builder_init.add_argument (new CCodeIdentifier ("G_VARIANT_TYPE_TUPLE")); ccode.add_expression (builder_init); if (uses_fd) { ccode.add_assignment (new CCodeIdentifier ("_fd_list"), new CCodeFunctionCall (new CCodeIdentifier ("g_unix_fd_list_new"))); } foreach (Parameter param in m.get_parameters ()) { if (param.direction != ParameterDirection.OUT) { continue; } string param_name = get_variable_cname (param.name); var owned_type = param.variable_type.copy (); owned_type.value_owned = true; ccode.add_declaration (get_ccode_name (owned_type), new CCodeVariableDeclarator.zero (param_name, default_value_for_type (param.variable_type, true))); var array_type = param.variable_type as ArrayType; if (array_type != null) { var length_ctype = get_ccode_array_length_type (array_type); for (int dim = 1; dim <= array_type.rank; dim++) { string length_cname = get_variable_array_length_cname (param, dim); ccode.add_declaration (length_ctype, new CCodeVariableDeclarator.zero (length_cname, new CCodeConstant ("0"))); } } send_dbus_value (param.variable_type, new CCodeIdentifier ("_reply_builder"), new CCodeIdentifier (param_name), param); } if (!(m.return_type is VoidType)) { if (m.return_type.is_real_non_null_struct_type ()) { ccode.add_declaration (get_ccode_name (m.return_type), new CCodeVariableDeclarator.zero ("result", default_value_for_type (m.return_type, true))); send_dbus_value (m.return_type, new CCodeIdentifier ("_reply_builder"), new CCodeIdentifier ("result"), m); if (requires_destroy (m.return_type)) { // keep local alive (symbol_reference is weak) var local = new LocalVariable (m.return_type, ".result"); ccode.add_expression (destroy_local (local)); } } else { ccode.add_declaration (get_ccode_name (m.return_type), new CCodeVariableDeclarator ("result")); var array_type = m.return_type as ArrayType; if (array_type != null) { var length_ctype = get_ccode_array_length_type (array_type); for (int dim = 1; dim <= array_type.rank; dim++) { string length_cname = get_array_length_cname ("result", dim); ccode.add_declaration (length_ctype, new CCodeVariableDeclarator.zero (length_cname, new CCodeConstant ("0"))); } } send_dbus_value (m.return_type, new CCodeIdentifier ("_reply_builder"), new CCodeIdentifier ("result"), m); if (requires_destroy (m.return_type)) { // keep local alive (symbol_reference is weak) var local = new LocalVariable (m.return_type, ".result"); ccode.add_expression (destroy_local (local)); } } } var builder_end = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end")); builder_end.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_reply_builder"))); ccode.add_assignment (new CCodeIdentifier ("_reply"), builder_end); var set_body = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_message_set_body")); set_body.add_argument (new CCodeIdentifier ("_reply_message")); set_body.add_argument (new CCodeIdentifier ("_reply")); ccode.add_expression (set_body); if (uses_fd) { ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_message_set_unix_fd_list")); ccall.add_argument (new CCodeIdentifier ("_reply_message")); ccall.add_argument (new CCodeIdentifier ("_fd_list")); ccode.add_expression (ccall); ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref")); ccall.add_argument (new CCodeIdentifier ("_fd_list")); ccode.add_expression (ccall); } } else { ccode.add_expression (ccall); } if (!no_reply && (!m.coroutine || ready)) { var return_value = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_connection_send_message")); return_value.add_argument (connection); return_value.add_argument (new CCodeIdentifier ("_reply_message")); return_value.add_argument (new CCodeConstant ("G_DBUS_SEND_MESSAGE_FLAGS_NONE")); return_value.add_argument (new CCodeConstant ("NULL")); return_value.add_argument (new CCodeConstant ("NULL")); ccode.add_expression (return_value); // free invocation like g_dbus_method_invocation_return_* var unref_call = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref")); unref_call.add_argument (new CCodeIdentifier ("invocation")); ccode.add_expression (unref_call); unref_call = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref")); unref_call.add_argument (new CCodeIdentifier ("_reply_message")); ccode.add_expression (unref_call); } if (need_goto_label) { ccode.add_label ("_error"); } foreach (Parameter param in m.get_parameters ()) { if ((param.direction == ParameterDirection.IN && (ready_data_expr == null || ready)) || (param.direction == ParameterDirection.OUT && !no_reply && (!m.coroutine || ready))) { if (param.variable_type is ObjectType && param.variable_type.type_symbol.get_full_name () == "GLib.Cancellable") { continue; } if (param.variable_type is ObjectType && param.variable_type.type_symbol.get_full_name () == "GLib.BusName") { // ignore BusName sender parameters continue; } var owned_type = param.variable_type.copy (); owned_type.value_owned = true; if (requires_destroy (owned_type)) { if (ready_data_expr != null && param.direction == ParameterDirection.IN) { var target = new GLibValue (owned_type, new CCodeMemberAccess.pointer (ready_data_expr, param.name), true); var array_type = owned_type as ArrayType; if (array_type != null) { for (int dim = 1; dim <= array_type.rank; dim++) { string length_cname = get_variable_array_length_cname (param, dim); target.append_array_length_cvalue (new CCodeMemberAccess.pointer (ready_data_expr, length_cname)); } } ccode.add_expression (destroy_value (target)); } else { // keep local alive (symbol_reference is weak) var local = new LocalVariable (owned_type, get_variable_cname (param.name)); ccode.add_expression (destroy_local (local)); } } } } if (ready) { var freecall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_free")); freecall.add_argument (new CCodeIdentifier (ready_data_struct_name)); freecall.add_argument (ready_data_expr); ccode.add_expression (freecall); } else if (need_goto_label) { ccode.add_statement (new CCodeEmptyStatement ()); } pop_function (); cfile.add_function_declaration (function); cfile.add_function (function); if (m.coroutine && !ready) { // generate ready function generate_dbus_wrapper (m, sym, true); } return wrapper_name; } string generate_dbus_signal_wrapper (Signal sig, ObjectTypeSymbol sym, string dbus_iface_name) { string wrapper_name = "_dbus_%s_%s".printf (get_ccode_lower_case_name (sym), get_ccode_lower_case_name (sig)); var function = new CCodeFunction (wrapper_name, "void"); function.modifiers = CCodeModifiers.STATIC; function.add_parameter (new CCodeParameter ("_sender", "GObject*")); foreach (var param in sig.get_parameters ()) { // ensure ccodenode of parameter is set var cparam = generate_parameter (param, cfile, new HashMap (), null); function.add_parameter (cparam); if (param.variable_type is ArrayType) { var array_type = (ArrayType) param.variable_type; var length_ctype = get_ccode_array_length_type (array_type); for (int dim = 1; dim <= array_type.rank; dim++) { function.add_parameter (new CCodeParameter (get_variable_array_length_cname (param, dim), length_ctype)); } } } function.add_parameter (new CCodeParameter ("_data", "gpointer*")); push_function (function); ccode.add_declaration ("GDBusConnection *", new CCodeVariableDeclarator ("_connection", new CCodeElementAccess (new CCodeIdentifier ("_data"), new CCodeConstant ("1")))); ccode.add_declaration ("const gchar *", new CCodeVariableDeclarator ("_path", new CCodeElementAccess (new CCodeIdentifier ("_data"), new CCodeConstant ("2")))); ccode.add_declaration ("GVariant", new CCodeVariableDeclarator ("*_arguments")); ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator ("_arguments_builder")); var builder_init = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init")); builder_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_arguments_builder"))); builder_init.add_argument (new CCodeIdentifier ("G_VARIANT_TYPE_TUPLE")); ccode.add_expression (builder_init); foreach (Parameter param in sig.get_parameters ()) { string param_name = get_variable_cname (param.name); CCodeExpression expr = new CCodeIdentifier (param_name); if (param.variable_type.is_real_struct_type ()) { expr = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, expr); } write_expression (param.variable_type, new CCodeIdentifier ("_arguments_builder"), expr, param); } var builder_end = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end")); builder_end.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_arguments_builder"))); ccode.add_assignment (new CCodeIdentifier ("_arguments"), builder_end); var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_connection_emit_signal")); ccall.add_argument (new CCodeIdentifier ("_connection")); ccall.add_argument (new CCodeConstant ("NULL")); ccall.add_argument (new CCodeIdentifier ("_path")); ccall.add_argument (new CCodeConstant ("\"%s\"".printf (dbus_iface_name))); ccall.add_argument (new CCodeConstant ("\"%s\"".printf (get_dbus_name_for_member (sig)))); ccall.add_argument (new CCodeIdentifier ("_arguments")); ccall.add_argument (new CCodeConstant ("NULL")); ccode.add_expression (ccall); pop_function (); cfile.add_function_declaration (function); cfile.add_function (function); return wrapper_name; } string generate_dbus_property_get_wrapper (Property prop, ObjectTypeSymbol sym) { string wrapper_name = "_dbus_%s".printf (get_ccode_name (prop.get_accessor)); var function = new CCodeFunction (wrapper_name, "GVariant*"); function.modifiers = CCodeModifiers.STATIC; function.add_parameter (new CCodeParameter ("self", get_ccode_name (sym) + "*")); push_function (function); var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (prop.get_accessor))); ccall.add_argument (new CCodeIdentifier ("self")); if (prop.get_accessor.value_type.is_real_non_null_struct_type ()) { ccode.add_declaration (get_ccode_name (prop.get_accessor.value_type), new CCodeVariableDeclarator.zero ("result", default_value_for_type (prop.get_accessor.value_type, true))); ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("result"))); ccode.add_expression (ccall); } else { ccode.add_declaration (get_ccode_name (prop.get_accessor.value_type), new CCodeVariableDeclarator ("result")); ccode.add_assignment (new CCodeIdentifier ("result"), ccall); var array_type = prop.get_accessor.value_type as ArrayType; if (array_type != null) { var length_ctype = get_ccode_array_length_type (array_type); for (int dim = 1; dim <= array_type.rank; dim++) { string length_cname = get_array_length_cname ("result", dim); ccode.add_declaration (length_ctype, new CCodeVariableDeclarator.zero (length_cname, new CCodeConstant ("0"))); ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (length_cname))); } } } ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator ("_reply")); if (get_dbus_signature (prop) != null) { // raw GVariant ccode.add_assignment (new CCodeIdentifier ("_reply"), new CCodeIdentifier("result")); } else { var reply_expr = serialize_expression (prop.get_accessor.value_type, new CCodeIdentifier ("result")); ccode.add_assignment (new CCodeIdentifier ("_reply"), reply_expr); if (requires_destroy (prop.get_accessor.value_type)) { // keep local alive (symbol_reference is weak) var local = new LocalVariable (prop.get_accessor.value_type, ".result"); ccode.add_expression (destroy_local (local)); } } ccode.add_return (new CCodeIdentifier ("_reply")); pop_function (); cfile.add_function_declaration (function); cfile.add_function (function); return wrapper_name; } string generate_dbus_property_set_wrapper (Property prop, ObjectTypeSymbol sym) { string wrapper_name = "_dbus_%s".printf (get_ccode_name (prop.set_accessor)); var function = new CCodeFunction (wrapper_name); function.modifiers = CCodeModifiers.STATIC; function.add_parameter (new CCodeParameter ("self", get_ccode_name (sym) + "*")); function.add_parameter (new CCodeParameter ("_value", "GVariant*")); push_function (function); var ccall = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_name (prop.set_accessor))); ccall.add_argument (new CCodeIdentifier ("self")); var owned_type = prop.property_type.copy (); owned_type.value_owned = true; ccode.add_declaration (get_ccode_name (owned_type), new CCodeVariableDeclarator.zero ("value", default_value_for_type (prop.property_type, true))); unowned Struct? st = prop.property_type.type_symbol as Struct; if (st != null && !st.is_simple_type ()) { ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("value"))); } else { ccall.add_argument (new CCodeIdentifier ("value")); var array_type = prop.property_type as ArrayType; if (array_type != null) { var length_ctype = get_ccode_array_length_type (array_type); for (int dim = 1; dim <= array_type.rank; dim++) { ccode.add_declaration (length_ctype, new CCodeVariableDeclarator (get_array_length_cname ("value", dim))); ccall.add_argument (new CCodeIdentifier (get_array_length_cname ("value", dim))); } } } var target = new CCodeIdentifier ("value"); if (get_dbus_signature (prop) != null) { ccode.add_assignment (target, new CCodeIdentifier("_value")); ccode.add_expression (ccall); } else { var expr = deserialize_expression (prop.property_type, new CCodeIdentifier ("_value"), target); ccode.add_assignment (target, expr); ccode.add_expression (ccall); if (requires_destroy (owned_type)) { // keep local alive (symbol_reference is weak) var local = new LocalVariable (owned_type, "value"); ccode.add_expression (destroy_local (local)); } } pop_function (); cfile.add_function_declaration (function); cfile.add_function (function); return wrapper_name; } void handle_signals (ObjectTypeSymbol sym, bool connect) { string dbus_iface_name = get_dbus_name (sym); if (dbus_iface_name == null) { return; } foreach (Signal sig in sym.get_signals ()) { if (sig.access != SymbolAccessibility.PUBLIC) { continue; } if (!is_dbus_visible (sig)) { continue; } if (connect) { var connect_call = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_connect")); connect_call.add_argument (new CCodeIdentifier ("object")); connect_call.add_argument (get_signal_canonical_constant (sig)); connect_call.add_argument (new CCodeCastExpression (new CCodeIdentifier (generate_dbus_signal_wrapper (sig, sym, dbus_iface_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 ("_dbus_%s_%s".printf (get_ccode_lower_case_name (sym), get_ccode_lower_case_name (sig)))); 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*")); cfunc.add_parameter (new CCodeParameter ("sender", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("object_path", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("interface_name", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("method_name", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("parameters", "GVariant*")); cfunc.add_parameter (new CCodeParameter ("invocation", "GDBusMethodInvocation*")); cfunc.add_parameter (new CCodeParameter ("user_data", "gpointer")); cfunc.modifiers |= CCodeModifiers.STATIC; push_function (cfunc); ccode.add_declaration ("gpointer*", new CCodeVariableDeclarator ("data", new CCodeIdentifier ("user_data"))); ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("object", new CCodeElementAccess (new CCodeIdentifier ("data"), new CCodeConstant ("0")))); bool first = true; foreach (Method m in sym.get_methods ()) { if (m is CreationMethod || m.binding != MemberBinding.INSTANCE || m.overrides || m.access != SymbolAccessibility.PUBLIC) { continue; } if (!is_dbus_visible (m)) { continue; } cfile.add_include ("string.h"); var ccheck = new CCodeFunctionCall (new CCodeIdentifier ("strcmp")); ccheck.add_argument (new CCodeIdentifier ("method_name")); ccheck.add_argument (new CCodeConstant ("\"%s\"".printf (get_dbus_name_for_member (m)))); if (first) { ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ccheck, new CCodeConstant ("0"))); first = false; } else { ccode.else_if (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ccheck, new CCodeConstant ("0"))); } var ccall = new CCodeFunctionCall (new CCodeIdentifier (generate_dbus_wrapper (m, sym))); ccall.add_argument (new CCodeIdentifier ("object")); ccall.add_argument (new CCodeIdentifier ("parameters")); ccall.add_argument (new CCodeIdentifier ("invocation")); ccode.add_expression (ccall); } if (!first) { ccode.add_else (); } var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref")); ccall.add_argument (new CCodeIdentifier ("invocation")); ccode.add_expression (ccall); if (!first) { ccode.close (); } pop_function (); cfile.add_function_declaration (cfunc); cfile.add_function (cfunc); } void generate_interface_get_property_function (ObjectTypeSymbol sym) { var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + "dbus_interface_get_property", "GVariant*"); cfunc.add_parameter (new CCodeParameter ("connection", "GDBusConnection*")); cfunc.add_parameter (new CCodeParameter ("sender", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("object_path", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("interface_name", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("property_name", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("error", "GError**")); cfunc.add_parameter (new CCodeParameter ("user_data", "gpointer")); cfunc.modifiers |= CCodeModifiers.STATIC; cfile.add_function_declaration (cfunc); push_function (cfunc); ccode.add_declaration ("gpointer*", new CCodeVariableDeclarator ("data", new CCodeIdentifier ("user_data"))); ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("object", new CCodeElementAccess (new CCodeIdentifier ("data"), new CCodeConstant ("0")))); bool firstif = true; foreach (Property prop in sym.get_properties ()) { if (prop.binding != MemberBinding.INSTANCE || prop.overrides || prop.access != SymbolAccessibility.PUBLIC) { continue; } if (!is_dbus_visible (prop)) { continue; } if (prop.get_accessor == null) { continue; } cfile.add_include ("string.h"); var ccheck = new CCodeFunctionCall (new CCodeIdentifier ("strcmp")); ccheck.add_argument (new CCodeIdentifier ("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 ccall = new CCodeFunctionCall (new CCodeIdentifier (generate_dbus_property_get_wrapper (prop, sym))); ccall.add_argument (new CCodeIdentifier ("object")); ccode.add_return (ccall); } if (!firstif) { ccode.close (); } ccode.add_return (new CCodeConstant ("NULL")); pop_function (); cfile.add_function (cfunc); } void generate_interface_set_property_function (ObjectTypeSymbol sym) { var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + "dbus_interface_set_property", "gboolean"); cfunc.add_parameter (new CCodeParameter ("connection", "GDBusConnection*")); cfunc.add_parameter (new CCodeParameter ("sender", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("object_path", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("interface_name", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("property_name", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("value", "GVariant*")); cfunc.add_parameter (new CCodeParameter ("error", "GError**")); cfunc.add_parameter (new CCodeParameter ("user_data", "gpointer")); cfunc.modifiers |= CCodeModifiers.STATIC; cfile.add_function_declaration (cfunc); push_function (cfunc); ccode.add_declaration ("gpointer*", new CCodeVariableDeclarator ("data", new CCodeIdentifier ("user_data"))); ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("object", new CCodeElementAccess (new CCodeIdentifier ("data"), new CCodeConstant ("0")))); bool firstif = true; foreach (Property prop in sym.get_properties ()) { if (prop.binding != MemberBinding.INSTANCE || prop.overrides || prop.access != SymbolAccessibility.PUBLIC) { continue; } if (!is_dbus_visible (prop)) { continue; } if (prop.set_accessor == null) { continue; } cfile.add_include ("string.h"); var ccheck = new CCodeFunctionCall (new CCodeIdentifier ("strcmp")); ccheck.add_argument (new CCodeIdentifier ("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 ccall = new CCodeFunctionCall (new CCodeIdentifier (generate_dbus_property_set_wrapper (prop, sym))); ccall.add_argument (new CCodeIdentifier ("object")); ccall.add_argument (new CCodeIdentifier ("value")); ccode.add_expression (ccall); ccode.add_return (new CCodeConstant ("TRUE")); } if (!firstif) { ccode.close (); } ccode.add_return (new CCodeConstant ("FALSE")); pop_function (); cfile.add_function (cfunc); } CCodeExpression get_interface_vtable (ObjectTypeSymbol sym) { var vtable = new CCodeInitializerList (); vtable.append (new CCodeIdentifier (get_ccode_lower_case_prefix (sym) + "dbus_interface_method_call")); vtable.append (new CCodeIdentifier (get_ccode_lower_case_prefix (sym) + "dbus_interface_get_property")); vtable.append (new CCodeIdentifier (get_ccode_lower_case_prefix (sym) + "dbus_interface_set_property")); generate_interface_method_call_function (sym); generate_interface_get_property_function (sym); generate_interface_set_property_function (sym); var cdecl = new CCodeDeclaration ("const GDBusInterfaceVTable"); cdecl.add_declarator (new CCodeVariableDeclarator ("_" + get_ccode_lower_case_prefix (sym) + "dbus_interface_vtable", vtable)); cdecl.modifiers = CCodeModifiers.STATIC; cfile.add_constant_declaration (cdecl); return new CCodeIdentifier ("_" + get_ccode_lower_case_prefix (sym) + "dbus_interface_vtable"); } string generate_register_object_function () { string register_object_func = "_vala_g_dbus_connection_register_object"; if (!add_wrapper (register_object_func)) { return register_object_func; } cfile.add_include ("gio/gio.h"); var function = new CCodeFunction (register_object_func, "guint"); function.modifiers = CCodeModifiers.STATIC; function.add_parameter (new CCodeParameter ("type", "GType")); function.add_parameter (new CCodeParameter ("object", "void*")); function.add_parameter (new CCodeParameter ("connection", "GDBusConnection*")); function.add_parameter (new CCodeParameter ("path", "const gchar*")); function.add_parameter (new CCodeParameter ("error", "GError**")); push_function (function); var quark = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string")); quark.add_argument (new CCodeConstant ("\"vala-dbus-register-object\"")); var get_qdata = new CCodeFunctionCall (new CCodeIdentifier ("g_type_get_qdata")); get_qdata.add_argument (new CCodeIdentifier ("type")); get_qdata.add_argument (quark); ccode.add_declaration ("void", new CCodeVariableDeclarator ("*func")); ccode.add_assignment (new CCodeIdentifier ("func"), get_qdata); ccode.open_if (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new CCodeIdentifier ("func"))); // no D-Bus interface // return error var set_error = new CCodeFunctionCall (new CCodeIdentifier ("g_set_error_literal")); set_error.add_argument (new CCodeIdentifier ("error")); set_error.add_argument (new CCodeIdentifier ("G_IO_ERROR")); set_error.add_argument (new CCodeIdentifier ("G_IO_ERROR_FAILED")); set_error.add_argument (new CCodeConstant ("\"The specified type does not support D-Bus registration\"")); ccode.add_expression (set_error); ccode.add_return (new CCodeConstant ("0")); ccode.close (); var register_object = new CCodeCastExpression (new CCodeIdentifier ("func"), "guint (*) (void *, GDBusConnection *, const gchar *, GError **)"); var ccall = new CCodeFunctionCall (register_object); ccall.add_argument (new CCodeIdentifier ("object")); ccall.add_argument (new CCodeIdentifier ("connection")); ccall.add_argument (new CCodeIdentifier ("path")); ccall.add_argument (new CCodeIdentifier ("error")); ccode.add_return (ccall); pop_function (); cfile.add_function_declaration (function); cfile.add_function (function); return register_object_func; } public override void visit_method_call (MethodCall expr) { var mtype = expr.call.value_type as MethodType; if (mtype == null || get_ccode_name (mtype.method_symbol) != "g_dbus_connection_register_object") { base.visit_method_call (expr); return; } var ma = (MemberAccess) expr.call; var type_arg = ma.get_type_arguments ().get (0); CCodeFunctionCall cregister; var object_type = type_arg as ObjectType; if (object_type != null) { if (get_dbus_name (object_type.type_symbol) == null) { Report.error (expr.source_reference, "DBusConnection.register_object requires type argument with [DBus (name = ...)] attribute"); return; } cregister = new CCodeFunctionCall (new CCodeIdentifier ("%sregister_object".printf (get_ccode_lower_case_prefix (object_type.type_symbol)))); } else { // use runtime type information for generic methods cregister = new CCodeFunctionCall (new CCodeIdentifier (generate_register_object_function ())); cregister.add_argument (get_type_id_expression (type_arg)); } var args = expr.get_argument_list (); var path_arg = args[0]; var obj_arg = args[1]; // method can fail current_method_inner_error = true; cregister.add_argument (get_cvalue (obj_arg)); cregister.add_argument (get_cvalue (ma.inner)); cregister.add_argument (get_cvalue (path_arg)); cregister.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_inner_error_cexpression ())); if (expr.parent_node is ExpressionStatement) { ccode.add_expression (cregister); } else { var temp_var = get_temp_variable (expr.value_type, expr.value_type.value_owned); var temp_ref = get_variable_cexpression (temp_var.name); emit_temp_var (temp_var); ccode.add_assignment (temp_ref, cregister); set_cvalue (expr, temp_ref); } } public override void generate_class_declaration (Class cl, CCodeFile decl_space) { base.generate_class_declaration (cl, decl_space); generate_object_type_symbol_declaration (cl, decl_space); } public override void generate_interface_declaration (Interface iface, CCodeFile decl_space) { base.generate_interface_declaration (iface, decl_space); generate_object_type_symbol_declaration (iface, decl_space); } public override void visit_class (Class cl) { base.visit_class (cl); visit_object_type_symbol (cl); } public override void visit_interface (Interface iface) { base.visit_interface (iface); visit_object_type_symbol (iface); } void generate_object_type_symbol_declaration (ObjectTypeSymbol sym, CCodeFile decl_space) { string dbus_iface_name = get_dbus_name (sym); if (dbus_iface_name == null) { return; } string register_object_name = "%sregister_object".printf (get_ccode_lower_case_prefix (sym)); if (add_symbol_declaration (decl_space, sym, register_object_name)) { return; } decl_space.add_include ("gio/gio.h"); // declare register_object function var cfunc = new CCodeFunction (register_object_name, "guint"); cfunc.add_parameter (new CCodeParameter ("object", "void*")); cfunc.add_parameter (new CCodeParameter ("connection", "GDBusConnection*")); cfunc.add_parameter (new CCodeParameter ("path", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("error", "GError**")); if (sym.is_private_symbol ()) { cfunc.modifiers |= CCodeModifiers.STATIC; } else if (context.hide_internal && sym.is_internal_symbol ()) { cfunc.modifiers |= CCodeModifiers.INTERNAL; } else { cfunc.modifiers |= CCodeModifiers.EXTERN; requires_vala_extern = true; } decl_space.add_function_declaration (cfunc); } void visit_object_type_symbol (ObjectTypeSymbol sym) { // only support registering a single D-Bus interface at a time (unlike old D-Bus support) // however, register_object can be invoked multiple times for the same object path with different interfaces string dbus_iface_name = get_dbus_name (sym); if (dbus_iface_name == null) { return; } cfile.add_include ("gio/gio.h"); var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + "register_object", "guint"); cfunc.add_parameter (new CCodeParameter ("object", "gpointer")); cfunc.add_parameter (new CCodeParameter ("connection", "GDBusConnection*")); cfunc.add_parameter (new CCodeParameter ("path", "const gchar*")); cfunc.add_parameter (new CCodeParameter ("error", "GError**")); if (sym.is_private_symbol ()) { cfunc.modifiers |= CCodeModifiers.STATIC; } else if (context.hide_internal && sym.is_internal_symbol ()) { cfunc.modifiers |= CCodeModifiers.INTERNAL; } push_function (cfunc); ccode.add_declaration ("guint", new CCodeVariableDeclarator ("result")); // data consists of 3 pointers: object, connection, path ccode.add_declaration ("gpointer", new CCodeVariableDeclarator ("*data")); var alloc_data = new CCodeFunctionCall (new CCodeIdentifier ("g_new")); alloc_data.add_argument (new CCodeIdentifier ("gpointer")); alloc_data.add_argument (new CCodeConstant ("3")); ccode.add_assignment (new CCodeIdentifier ("data"), alloc_data); var ref_function = get_ccode_ref_function (sym); if (sym is Interface && ref_function == null) { Report.error (sym.source_reference, "missing class prerequisite for interface `%s', add GLib.Object to interface declaration if unsure", sym.get_full_name ()); return; } var ref_object = new CCodeFunctionCall (new CCodeIdentifier (ref_function)); ref_object.add_argument (new CCodeIdentifier ("object")); ccode.add_assignment (new CCodeElementAccess (new CCodeIdentifier ("data"), new CCodeConstant ("0")), ref_object); ref_object = new CCodeFunctionCall (new CCodeIdentifier ("g_object_ref")); ref_object.add_argument (new CCodeIdentifier ("connection")); ccode.add_assignment (new CCodeElementAccess (new CCodeIdentifier ("data"), new CCodeConstant ("1")), ref_object); var dup_path = new CCodeFunctionCall (new CCodeIdentifier ("g_strdup")); dup_path.add_argument (new CCodeIdentifier ("path")); ccode.add_assignment (new CCodeElementAccess (new CCodeIdentifier ("data"), new CCodeConstant ("2")), dup_path); var cregister = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_connection_register_object")); cregister.add_argument (new CCodeIdentifier ("connection")); cregister.add_argument (new CCodeIdentifier ("path")); cregister.add_argument (new CCodeCastExpression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_interface_info (sym)), "GDBusInterfaceInfo *")); cregister.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_interface_vtable (sym))); cregister.add_argument (new CCodeIdentifier ("data")); cregister.add_argument (new CCodeIdentifier ("_" + get_ccode_lower_case_prefix (sym) + "unregister_object")); cregister.add_argument (new CCodeIdentifier ("error")); ccode.add_assignment (new CCodeIdentifier ("result"), cregister); ccode.open_if (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new CCodeIdentifier ("result"))); ccode.add_return (new CCodeConstant ("0")); ccode.close (); handle_signals (sym, true); ccode.add_return (new CCodeIdentifier ("result")); pop_function (); cfile.add_function (cfunc); cfunc = new CCodeFunction ("_" + get_ccode_lower_case_prefix (sym) + "unregister_object"); cfunc.add_parameter (new CCodeParameter ("user_data", "gpointer")); cfunc.modifiers |= CCodeModifiers.STATIC; push_function (cfunc); ccode.add_declaration ("gpointer*", new CCodeVariableDeclarator ("data", new CCodeIdentifier ("user_data"))); handle_signals (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"))); ccode.add_expression (unref_object); unref_object = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref")); unref_object.add_argument (new CCodeElementAccess (new CCodeIdentifier ("data"), new CCodeConstant ("1"))); ccode.add_expression (unref_object); var free_path = new CCodeFunctionCall (new CCodeIdentifier ("g_free")); free_path.add_argument (new CCodeElementAccess (new CCodeIdentifier ("data"), new CCodeConstant ("2"))); ccode.add_expression (free_path); var free_data = new CCodeFunctionCall (new CCodeIdentifier ("g_free")); free_data.add_argument (new CCodeIdentifier ("data")); ccode.add_expression (free_data); pop_function (); cfile.add_function_declaration (cfunc); cfile.add_function (cfunc); } public override void register_dbus_info (CCodeBlock block, ObjectTypeSymbol sym) { string dbus_iface_name = get_dbus_name (sym); if (dbus_iface_name == null) { return; } base.register_dbus_info (block, sym); var quark = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_from_static_string")); quark.add_argument (new CCodeConstant ("\"vala-dbus-register-object\"")); var set_qdata = new CCodeFunctionCall (new CCodeIdentifier ("g_type_set_qdata")); set_qdata.add_argument (new CCodeIdentifier ("%s_type_id".printf (get_ccode_lower_case_name (sym, null)))); set_qdata.add_argument (quark); set_qdata.add_argument (new CCodeCastExpression (new CCodeIdentifier (get_ccode_lower_case_prefix (sym) + "register_object"), "void*")); block.add_statement (new CCodeExpressionStatement (set_qdata)); } }