>From 08720290486096fa4d225fab7cd10da98549f06a Mon Sep 17 00:00:00 2001
From: Stef Walter <stefw@redhat.com>
Date: Fri, 10 Jan 2014 08:54:41 +0100
Subject: [PATCH 2/6] sbus: Add sbus_vtable and update codegen to support it

Each interface is a vtable structure derived from
sbus_vtable, in the sense that it has an sbus_vtable
struct as its first argument. This lets us upcast the
interface vtable structure to an sbus_vtable and dispatch
to it dynamically and cleanly.

The interface metadata contains information about which
vtable offset in the interface metadata should be dispatched
to for a given function. This is a common scheme, not only
among dbus implementations, but also compiled languages.

Currently all the vtable functions are of type
sbus_msg_handler_fn. These are the handlers we are familiar
with and perform raw processing of the message. Later commits
will introduce type safe handlers that levelage compile checking
and automatic argument packing/unpacking.

Although this may seem contrived now, the remainder of the
dbus infrastructure work will build on this, including
ofd.Properties, ofd.ObjectManager, ofd.Introspect, compiler
checked type safe unpacking/packing, etc.

The codegen now generates vtable structures for each interface
along-side the metadata, and fills in vtable offsets
appropriately.

It is obviously still possible to hand-craft such vtables and
metadata if needed for a special case.

Once again examples output can be found at:

  src/tests/sbus_codegen_tests_generated.h
---
 Makefile.am                              |  2 +-
 src/sbus/sbus_codegen                    | 41 ++++++++++++++++++++++++++++++--
 src/sbus/sssd_dbus.h                     | 15 ++++++++++++
 src/sbus/sssd_dbus_meta.h                |  1 +
 src/tests/sbus_codegen_tests.c           | 33 +++++++++++++++++++++++++
 src/tests/sbus_codegen_tests.xml         |  5 ++++
 src/tests/sbus_codegen_tests_generated.c |  8 +++++++
 src/tests/sbus_codegen_tests_generated.h | 19 +++++++++++++++
 8 files changed, 121 insertions(+), 3 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 2652e8f..40a0058 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -669,7 +669,7 @@ SUFFIXES = .xml _generated.h _generated.c
 .xml_generated.h:
 	$(srcdir)/$(SBUS_CODEGEN) --mode=header --output=$@ $<
 .xml_generated.c:
-	$(srcdir)/$(SBUS_CODEGEN) --mode=source --output=$@ $<
+	$(srcdir)/$(SBUS_CODEGEN) --mode=source --include=$(@:.c=.h) --output=$@ $<
 
 # Regenerate when codegen changes
 CODEGEN_CODE = \
diff --git a/src/sbus/sbus_codegen b/src/sbus/sbus_codegen
index a627f2b..e770c83 100755
--- a/src/sbus/sbus_codegen
+++ b/src/sbus/sbus_codegen
@@ -181,6 +181,7 @@ def source_methods(iface, methods):
             print "        %s__out," % meth.fq_c_name()
         else:
             print "        NULL, /* no out_args */"
+        print "        offsetof(struct %s, %s)," % (iface.c_name(), meth.c_name())
         print "    },"
     print "    { NULL, }"
     print "};"
@@ -249,7 +250,7 @@ def source_interface(iface):
     print "};"
     print
 
-def generate_source(ifaces, filename):
+def generate_source(ifaces, filename, include_header=None):
     basename = os.path.basename(filename)
 
     print "/* The following definitions are auto-generated from %s */" % basename
@@ -258,6 +259,8 @@ def generate_source(ifaces, filename):
     print "#include \"util/util.h\""
     print "#include \"sbus/sssd_dbus.h\""
     print "#include \"sbus/sssd_dbus_meta.h\""
+    if include_header:
+        print "#include \"%s\"" % include_header
     print
 
     for iface in ifaces:
@@ -277,6 +280,20 @@ def generate_source(ifaces, filename):
         # The sbus_interface structure
         source_interface(iface)
 
+def header_vtable(iface, methods):
+    print "/* vtable for %s */" % iface.name
+    print "struct %s {" % iface.c_name()
+    print "    struct sbus_vtable vtable; /* derive from sbus_vtable */"
+
+    # All methods
+    for meth in iface.methods:
+        print "    sbus_msg_handler_fn %s;" % meth.c_name()
+
+    # TODO: Property getters and setters will go here
+
+    print "};"
+    print
+
 def generate_header(ifaces, filename):
     basename = os.path.basename(filename)
     guard = "__%s__" % re.sub(r'([^_A-Z0-9])', "_", basename.upper())
@@ -290,6 +307,22 @@ def generate_header(ifaces, filename):
     print
 
     print "/* ------------------------------------------------------------------------"
+    print " * DBus Vtable handler structures"
+    print " *"
+    print " * These structures are filled in by implementors of the different"
+    print " * dbus interfaces to handle method calls."
+    print " *"
+    print " * Handler functions of type sbus_msg_handler_fn accept raw messages,"
+    print " * other handlers will be typed appropriately. If a handler that is"
+    print " * set to NULL is invoked it will result in a"
+    print " * org.freedesktop.DBus.Error.NotSupported error for the caller."
+    print " */"
+    print
+    for iface in ifaces:
+        if iface.methods:
+            header_vtable(iface, iface.methods)
+
+    print "/* ------------------------------------------------------------------------"
     print " * DBus Interface Metadata"
     print " *"
     print " * These structure definitions are filled in with the information about"
@@ -465,6 +498,10 @@ def parse_options():
                       dest="output", default=None,
                       help="Set output file name (default: stdout)",
                       metavar="FILE")
+    parser.add_option("--include",
+                      dest="include", default=None,
+                      help="name of a header to #include",
+                      metavar="HEADER")
     (options, args) = parser.parse_args()
 
     if not args:
@@ -488,7 +525,7 @@ def main():
         if options.mode == "header":
             generate_header(parser.parsed_interfaces, filename)
         elif options.mode == "source":
-            generate_source(parser.parsed_interfaces, filename)
+            generate_source(parser.parsed_interfaces, filename, options.include)
         else:
             assert False, "should not be reached"
 
diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h
index 7604797..e9e7c98 100644
--- a/src/sbus/sssd_dbus.h
+++ b/src/sbus/sssd_dbus.h
@@ -55,6 +55,21 @@ enum {
     SBUS_RECONNECT_ERROR
 };
 
+/*
+ * This represents vtable of interface handlers for methods and
+ * properties and so on. The actual vtable structs derive from this struct
+ * (ie: have this struct as their first member).
+ *
+ * The offsets for matching vtable function pointers are in sbus_method_meta
+ * These are used to dynamically dispatch the method invocations.
+ */
+struct sbus_vtable {
+    const struct sbus_interface_meta *meta;
+    int flags; /* unused for now */
+
+    /* derived structs place function pointers here. */
+};
+
 /* Special interface and method for D-BUS introspection */
 #define DBUS_INTROSPECT_INTERFACE "org.freedesktop.DBus.Introspectable"
 #define DBUS_INTROSPECT_METHOD "Introspect"
diff --git a/src/sbus/sssd_dbus_meta.h b/src/sbus/sssd_dbus_meta.h
index 17ea7a5..abc9921 100644
--- a/src/sbus/sssd_dbus_meta.h
+++ b/src/sbus/sssd_dbus_meta.h
@@ -42,6 +42,7 @@ struct sbus_method_meta {
     const char *name;
     const struct sbus_arg_meta *in_args;
     const struct sbus_arg_meta *out_args;
+    size_t vtable_offset;
 };
 
 enum {
diff --git a/src/tests/sbus_codegen_tests.c b/src/tests/sbus_codegen_tests.c
index 6b2abf8..39856fa 100644
--- a/src/tests/sbus_codegen_tests.c
+++ b/src/tests/sbus_codegen_tests.c
@@ -112,6 +112,38 @@ START_TEST(test_signals)
 }
 END_TEST
 
+static int
+mock_move_universe(DBusMessage *msg, struct sbus_connection *conn)
+{
+    /* not called */
+    return 0;
+}
+
+static int
+mock_crash_now(DBusMessage *msg, struct sbus_connection *conn)
+{
+    /* not called */
+    return 0;
+}
+
+START_TEST(test_vtable)
+{
+    struct com_planetexpress_Ship vtable = {
+        { &com_planetexpress_Ship_meta, 0 },
+        mock_move_universe,
+        mock_crash_now,
+    };
+
+    /*
+     * These are not silly tests:
+     * - Will fail compilation if c-symbol name was not respected
+     * - Will fail if method order was not respected
+     */
+    ck_assert(vtable.crash_now == mock_crash_now);
+    ck_assert(vtable.MoveUniverse == mock_move_universe);
+}
+END_TEST
+
 Suite *create_suite(void)
 {
     Suite *s = suite_create("sbus_codegen");
@@ -123,6 +155,7 @@ Suite *create_suite(void)
     tcase_add_test(tc, test_methods);
     tcase_add_test(tc, test_properties);
     tcase_add_test(tc, test_signals);
+    tcase_add_test(tc, test_vtable);
 
     /* Add all test cases to the test suite */
     suite_add_tcase(s, tc);
diff --git a/src/tests/sbus_codegen_tests.xml b/src/tests/sbus_codegen_tests.xml
index 0def358..e571dbc 100755
--- a/src/tests/sbus_codegen_tests.xml
+++ b/src/tests/sbus_codegen_tests.xml
@@ -29,6 +29,11 @@
         <signal name="BecameSentient">
             <arg name="gender" type="s"/>
         </signal>
+
+        <!-- A method with a specific c name -->
+        <method name="Crash">
+            <annotation value="crash_now" name="org.freedesktop.DBus.GLib.CSymbol"/>
+        </method>
     </interface>
 
     <!--
diff --git a/src/tests/sbus_codegen_tests_generated.c b/src/tests/sbus_codegen_tests_generated.c
index 4890e2e..a12a6c7 100644
--- a/src/tests/sbus_codegen_tests_generated.c
+++ b/src/tests/sbus_codegen_tests_generated.c
@@ -3,6 +3,7 @@
 #include "util/util.h"
 #include "sbus/sssd_dbus.h"
 #include "sbus/sssd_dbus_meta.h"
+#include "../src/tests/sbus_codegen_tests_generated.h"
 
 /* arguments for com.planetexpress.Ship.MoveUniverse */
 const struct sbus_arg_meta com_planetexpress_Ship_MoveUniverse__in[] = {
@@ -23,6 +24,13 @@ const struct sbus_method_meta com_planetexpress_Ship__methods[] = {
         "MoveUniverse", /* name */
         com_planetexpress_Ship_MoveUniverse__in,
         com_planetexpress_Ship_MoveUniverse__out,
+        offsetof(struct com_planetexpress_Ship, MoveUniverse),
+    },
+    {
+        "Crash", /* name */
+        NULL, /* no in_args */
+        NULL, /* no out_args */
+        offsetof(struct com_planetexpress_Ship, crash_now),
     },
     { NULL, }
 };
diff --git a/src/tests/sbus_codegen_tests_generated.h b/src/tests/sbus_codegen_tests_generated.h
index 56911dd..c3ff78d 100644
--- a/src/tests/sbus_codegen_tests_generated.h
+++ b/src/tests/sbus_codegen_tests_generated.h
@@ -6,6 +6,25 @@
 #include "sbus/sssd_dbus.h"
 
 /* ------------------------------------------------------------------------
+ * DBus Vtable handler structures
+ *
+ * These structures are filled in by implementors of the different
+ * dbus interfaces to handle method calls.
+ *
+ * Handler functions of type sbus_msg_handler_fn accept raw messages,
+ * other handlers will be typed appropriately. If a handler that is
+ * set to NULL is invoked it will result in a
+ * org.freedesktop.DBus.Error.NotSupported error for the caller.
+ */
+
+/* vtable for com.planetexpress.Ship */
+struct com_planetexpress_Ship {
+    struct sbus_vtable vtable; /* derive from sbus_vtable */
+    sbus_msg_handler_fn MoveUniverse;
+    sbus_msg_handler_fn crash_now;
+};
+
+/* ------------------------------------------------------------------------
  * DBus Interface Metadata
  *
  * These structure definitions are filled in with the information about
-- 
1.8.4.2

