D/BUS IDL compiler ...
Michael Meeks
michael@ximian.com
Fri, 05 Mar 2004 16:23:15 +0000
--=-Ja3LglJd6Mr4gsObEil7
Content-Type: text/plain
Content-Transfer-Encoding: 7bit
Hi guys,
So - attached is my 1st pass at a dbus IDL compiler; so far it parses a
chunk of (what looks like) pseudo-IDL pinched from the HAL spec, and
there is an embedded test that generates working code for (at least)
oneway methods, implemented by test-service.c (couldn't find much more
meat to throw it at).
There are a number of issues / problems which I'd like to preempt with
it's introduction however:
* It's written in perl - I don't like perl - perl, being a
pre-requisite for autotools is available on every developer
platform; the IDL only needs re-compiling by developers. QED.
Indeed, perl is (empirically) more portable than autotools,
it's also an excellent language for string processing, and
made it very fast to write this. It's also not that bad if
you think for a few seconds before typing.
* It appears to re-implement dbus_message_append_valist etc.
however - it needs to do this to return the advanced va_list
to allow return value de-marshalling.
* It doesn't implement the '[in],out' specifiers for parameters
- a simple limitation, to be fixed.
* It doesn't implement the array types - correct, the lack of a
decent recursive type description is sufficiently annoying to
me that I can't stomach it yet.
* The pre-processor doesn't implement /* */ comments - and/or it
doesn't fork right - again, fixable bugs.
* The license is LGPL/X11 - I'm still deeply concerned with the
unresolved AFL legal issue.
* It doesn't generate stubs/skels - true, clearly we don't need
stubs (since we have control of the calling convention), but
skel support [ in the form of easy-to-use glib-genmarshal type
macros ] is planned & should be easy enough.
* It handles error conditions as leakily as the existing _valist
methods: true enough ;-) this should be easy enough to fix
though.
There are a number of other issues that this throws up with D/BUS that
I hadn't noticed before, and it'd be nice to understand:
* Why do we have a 'service' and a 'path' concept ? surely a
given service will bind to a given path ? someone must know
this ? it's slightly surreal having a method taking three
strings which are ~mostly identical in ~every case. Is it
really worth having this extra dimension to the space ? is
it really orthogonal ?
* 'signals' - I'm confused as to what to generate for these;
it seems to me that there are 2 needs:
a) marshalling type information for the emitter
b) proxying type information for the listener
of course - we could duplicate this information in some
way - but given that a 'signal' seems to be (simply) a
oneway method call; it would seem more sensible (perhaps)
to use a 'Listener' pattern to allow a simpler
implementation.
* signal emission - in Bonobo we discovered that often
signals are emitted with no listener :-) thus it makes
sense to be able to detect this eventuality from the
emitter and avoid the cost - is that something that makes
sense with dbus ? and/or that is done already.
More questions to come I guess;
My hope is - that this will be something useful; I guess it'll be more
useful on the server-side though - at least, judging from my quick
skimming of HAL there is too much hand-coded error-prone
marshal/de-marshal logic around that's verbose & hard to audit.
I hope to do a little work on this in the next few days, so flames,
outrage, constructive comments etc. appreciated :-) and/or is this
something that could live in dbus CVS ?
Thanks,
Michael.
--
michael@ximian.com <><, Pseudo Engineer, itinerant idiot
--=-Ja3LglJd6Mr4gsObEil7
Content-Disposition: attachment; filename=diddle.diff
Content-Type: text/x-patch; name=diddle.diff; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Index: Makefile.am
===================================================================
RCS file: /cvs/dbus/dbus/Makefile.am,v
retrieving revision 1.18
diff -u -p -u -r1.18 Makefile.am
--- a/Makefile.am 17 Oct 2003 16:23:19 -0000 1.18
+++ b/Makefile.am 5 Mar 2004 16:00:06 -0000
@@ -27,7 +27,7 @@ dist-local:
echo "You have to build with Qt and GLib to make dist" ; \
fi
-SUBDIRS=dbus bus doc $(GLIB_SUBDIR) $(GCJ_SUBDIR) $(MONO_SUBDIR) $(QT_SUBDIR) $(PYTHON_SUBDIR) test tools
+SUBDIRS=dbus bus doc $(GLIB_SUBDIR) $(GCJ_SUBDIR) $(MONO_SUBDIR) $(QT_SUBDIR) $(PYTHON_SUBDIR) test tools idl
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = dbus-1.pc $(GLIB_PC)
Index: configure.in
===================================================================
RCS file: /cvs/dbus/dbus/configure.in,v
retrieving revision 1.76
diff -u -p -u -r1.76 configure.in
--- a/configure.in 19 Nov 2003 21:51:09 -0000 1.76
+++ b/configure.in 5 Mar 2004 16:00:06 -0000
@@ -935,6 +935,7 @@ bus/session.conf
bus/messagebus
bus/dbus-daemon-1.1
Makefile
+idl/Makefile
dbus/Makefile
glib/Makefile
python/Makefile
--- /dev/null 2003-09-23 18:59:22.000000000 +0100
+++ idl/Makefile.am 2004-03-05 14:32:18.000000000 +0000
@@ -0,0 +1,21 @@
+bin_SCRIPTS = diddle
+
+lib_LTLIBRARIES=libtbus-1.la
+
+diddleincludedir=$(includedir)/libtbus-1.0/tbusa
+diddleinclude_HEADERS = tbus.h
+
+INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) -DDBUS_COMPILATION
+
+libtbus_1_la_SOURCES = tbus.c
+libtbus_1_la_LIBADD= $(DBUS_CLIENT_LIBS) -ldbus-1
+
+check_PROGRAMS = test-idl
+
+test_idl_SOURCES = test-idl.c
+test_idl_LDADD = $(DBUS_CLIENT_LIBS) -ltbus-1
+
+test.h : test.idl $(srcdir)/diddle
+ $(srcdir)/diddle -otest.h test.idl
+test-idl.c : test.h
+
--- /dev/null 2003-09-23 18:59:22.000000000 +0100
+++ idl/test.idl 2004-03-05 15:48:01.000000000 +0000
@@ -0,0 +1,12 @@
+module org.freedesktop {
+ interface TestSuite {
+ oneway void Echo (string arg);
+ oneway void EmitFoo ();
+
+// Signals on interfaces seems to me to badly
+// confuse the whole client/server scheme. We should
+// have a listener interface surely.
+ signal Foo(double length);
+ };
+};
+
--- /dev/null 2003-09-23 18:59:22.000000000 +0100
+++ idl/test-idl.c 2004-03-05 15:25:49.000000000 +0000
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <test.h>
+#include <dbus/dbus.h>
+
+static void
+error_exit (DBusError *error)
+{
+ fprintf (stderr, "Failed to invoke Echo: '%s'\n",
+ error ? error->name : "<NoEx>");
+ exit (1);
+}
+
+int main (int argc, char **argv)
+{
+ DBusConnection *connection;
+ DBusError error[1];
+
+ dbus_error_init (error);
+ connection = dbus_bus_get (DBUS_BUS_ACTIVATION, error);
+ if (!connection) {
+ fprintf (stderr, "Failed to get connection\n");
+ return 1;
+ }
+
+ if (!org_freedesktop_TestSuite_Echo (connection,
+ "org.freedesktop.DBus.TestSuiteEchoService",
+ "/org/freedesktop/TestSuite",
+ "Hello World", error))
+ error_exit (error);
+
+ if (!org_freedesktop_TestSuite_EmitFoo (connection,
+ "org.freedesktop.DBus.TestSuiteEchoService",
+ "/org/freedesktop/TestSuite", error))
+ error_exit (error);
+
+ dbus_connection_flush (connection);
+ dbus_connection_disconnect (connection);
+ dbus_connection_unref (connection);
+
+ printf ("All tests succeded\n");
+
+ dbus_shutdown();
+
+ return 0;
+}
--- /dev/null 2003-09-23 18:59:22.000000000 +0100
+++ idl/tbus.c 2004-03-05 15:54:51.000000000 +0000
@@ -0,0 +1,184 @@
+#include <stdio.h>
+#include <tbus.h>
+#include <dbus/dbus-protocol.h>
+#include <dbus/dbus-internals.h>
+
+#define ARG_DELIMITER '_'
+
+static dbus_bool_t
+marshal_args (DBusMessage *message,
+ char **method_descr,
+ DBusError *error,
+ va_list *args)
+{
+ char *p;
+ dbus_bool_t err = FALSE;
+ DBusMessageIter iter;
+
+ dbus_message_append_iter_init (message, &iter);
+
+ for (p = *method_descr; !err && *p != ARG_DELIMITER; p++) {
+
+# define MAP(dbus_type, method, c_type) \
+ case DBUS_TYPE_##dbus_type: \
+ err |= !dbus_message_iter_append##method (&iter, va_arg (*args, c_type)); \
+ break
+
+ switch (*p) {
+ case DBUS_TYPE_NIL:
+ err |= !dbus_message_iter_append_nil (&iter);
+ break;
+ MAP (BYTE, _byte, unsigned char);
+ MAP (BOOLEAN, _boolean, dbus_bool_t);
+ MAP (INT32, _int32, dbus_int32_t);
+ MAP (UINT32, _uint32, dbus_uint32_t);
+#ifdef DBUS_HAVE_INT64
+ MAP (INT64, _int64, dbus_int64_t);
+ MAP (UINT64, _uint64, dbus_uint64_t);
+#endif
+ MAP (DOUBLE, _double, double);
+ MAP (STRING, _string, const char *);
+ case DBUS_TYPE_ARRAY:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_CUSTOM:
+ case DBUS_TYPE_DICT:
+ default:
+ _dbus_warn ("More design / thought required %c", *p);
+ err = 1;
+ break;
+ }
+# undef MAP
+ }
+
+ *method_descr = p;
+
+ return !err;
+}
+
+static dbus_bool_t
+demarshal_args (DBusMessage *message,
+ char **method_descr,
+ DBusError *error,
+ va_list *args)
+{
+ char *p;
+ dbus_bool_t err = FALSE;
+ DBusMessageIter iter;
+
+ dbus_message_iter_init (message, &iter);
+
+ for (p = *method_descr; !err && *p != ARG_DELIMITER; p++) {
+ int type;
+
+ type = dbus_message_iter_get_arg_type (&iter);
+ if (type != *p) {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Argument type mismatch %s should be %s",
+ _dbus_type_to_string (type),
+ _dbus_type_to_string (*p));
+ err = TRUE;
+ break;
+ }
+
+# define MAP(dbus_type, method, c_type) \
+ case DBUS_TYPE_##dbus_type: \
+ *(c_type *)va_arg(*args, c_type *) = dbus_message_iter_get##method (&iter); \
+ break
+ switch (*p) {
+ case DBUS_TYPE_NIL:
+ break;
+ MAP (BYTE, _byte, unsigned char);
+ MAP (BOOLEAN, _boolean, dbus_bool_t);
+ MAP (INT32, _int32, dbus_int32_t);
+ MAP (UINT32, _uint32, dbus_uint32_t);
+#ifdef DBUS_HAVE_INT64
+ MAP (INT64, _int64, dbus_int64_t);
+ MAP (UINT64, _uint64, dbus_uint64_t);
+#endif
+ MAP (DOUBLE, _double, double);
+ MAP (STRING, _string, const char *);
+ case DBUS_TYPE_ARRAY:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_CUSTOM:
+ case DBUS_TYPE_DICT:
+ default:
+ _dbus_warn ("More design / thought for demarshalling '%c'", *p);
+ err = 1;
+ break;
+ }
+# undef MAP
+ if (p[1] != ARG_DELIMITER && !dbus_message_iter_next (&iter)) {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "More return values arrived than expected");
+ err = TRUE;
+ }
+ }
+
+ if (p[1] != ARG_DELIMITER) {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "More arguments specified than arrived");
+ err = TRUE;
+ }
+
+ return !err;
+}
+
+dbus_bool_t
+tbus_invoke (DBusConnection *cnx,
+ const char *service,
+ const char *path,
+ const char *iface,
+ DBusError *error,
+ const char *method_descr,
+ ...)
+
+{
+ va_list args;
+ char *argp;
+ dbus_bool_t success;
+ DBusMessage *message;
+ const char *method_name;
+
+ method_name = strrchr (method_descr, '_') + 1;
+
+ va_start (args, method_descr);
+
+ message = dbus_message_new_method_call (service, path, iface, method_name);
+ if (!message)
+ return FALSE;
+
+ argp = (char *) method_descr;
+ if (!(success = marshal_args (message, &argp, error, &args))) {
+ fprintf (stderr, "Failed to marshal\n");
+ goto out;
+ }
+
+ argp++;
+ if (*argp++ == 'o') { /* oneway */
+ dbus_message_set_no_reply (message, TRUE);
+
+ success = dbus_connection_send (cnx, message, NULL);
+ } else {
+ DBusMessage *reply;
+
+ reply = dbus_connection_send_with_reply_and_block (cnx, message, -1, error);
+ if (!reply) {
+ fprintf (stderr, "Failed to get reply\n");
+ success = FALSE;
+ goto out;
+ }
+
+ success = demarshal_args (message, &argp, error, &args);
+ if (!success)
+ fprintf (stderr, "Failed to get demarshal args\n");
+
+ dbus_message_unref (reply);
+ }
+
+ out:
+ dbus_message_unref (message);
+
+ va_end (args);
+
+ return success;
+}
--- /dev/null 2003-09-23 18:59:22.000000000 +0100
+++ idl/tbus.h 2004-03-05 15:00:05.000000000 +0000
@@ -0,0 +1,15 @@
+#ifndef TBUS_H
+#define TBUS_H
+
+#include <dbus/dbus-connection.h>
+
+/* Called by generated macros only */
+dbus_bool_t tbus_invoke (DBusConnection *cnx,
+ const char *service,
+ const char *path,
+ const char *iface,
+ DBusError *error,
+ const char *method_descr,
+ ...);
+
+#endif /* TBUS_H */
--- /dev/null 2003-09-23 18:59:22.000000000 +0100
+++ idl/bus.idl 2004-02-17 16:19:28.000000000 +0000
@@ -0,0 +1,89 @@
+interface Empty {
+
+}
+
+interface Devices {
+
+ #
+ # Return a list of all devices in the GDL
+ #
+ # @return List of UDI's
+ #
+ array{string} GetAllDevices();
+
+ # Determine if a device with a given Unique Device Id exists in the GDL
+ #
+ # @param udi Device UDI, for example '/org/freedesktop/Hal/devices/pci_8086_7111'
+ # @return TRUE iff the device with the given UDI exists
+ #
+ bool DeviceExists(string udi);
+
+ # Find the set of devices in the GDL that has a given property matching
+ # a given value
+ #
+ # @param key Key, for example 'block.fstype'
+ # @param value Value, for example 'ext3'
+ # @return List of UDI's
+ #
+ array{string} FindDeviceStringMatch(string key, string value);
+
+ # Find the set of devices in the GDL that has a given capability
+ #
+ # @param capability Capability, for example 'volume'
+ # @return List of UDI's
+ #
+ array{string} FindDeviceByCapability(string capability);
+
+ # Notification that a new device have been added to the GDL
+ #
+ # @param udi Unique Device Id
+ #
+ void DeviceAdded(string udi);
+
+ # Notification that a new device have been removed from the GDL. The
+ # application cannot use this UDI anymore.
+ #
+ # @param udi Unique Device Id
+ #
+ void DeviceRemoved(string udi);
+
+ # Notification that a device in the GDL have got a new capability. Note that
+ # this is emitted even though the device already had the old capability
+ #
+ # @param udi Unique Device Id
+ #
+ void NewCapability(string udi, string capability);
+};
+
+interface PropertyBag {
+ # Set property
+ #
+ # @param key Property to set
+ # @param value Value to set
+ # @raises org.freedesktop.Hal.(NoSuchDevice|TypeMismatch)
+ #
+ void SetProperty(string key, any value);
+ void SetPropertyString(string key, string value);
+ void SetPropertyInteger(string key, int32 value);
+ void SetPropertyBoolean(string key, bool value);
+ void SetPropertyDouble(string key, double value);
+
+ # Get property
+ #
+ # @param key Property to get
+ # @return The value of the property
+ # @raises org.freedesktop.Hal.(NoSuchDevice|NoSuchProperty|TypeMismatch)
+ #
+ any GetProperty(string key);
+ string GetPropertyString(string key);
+ int32 GetPropertyInteger(string key);
+ bool GetPropertyBoolean(string key);
+ double GetPropertyDouble(string key);
+
+ # Get all properties
+ #
+ # @return Dictionary from key to value
+ # @raises org.freedesktop.Hal.NoSuchDevice
+ #
+ map{string, any} GetAllProperties();
+};
--=-Ja3LglJd6Mr4gsObEil7--