[libpeas] Make PeasExtension implement extension interfaces.



commit 4f24c6c687dd2e132e927d5cd84bdb2b0c111845
Author: Steve Frécinaux <code istique net>
Date:   Tue Aug 31 21:07:16 2010 +0200

    Make PeasExtension implement extension interfaces.
    
    With this commit, we make it possible to use PeasExtension instances
    directly with the extension interface methods.
    
    This makes the overal use of libpeas nicer and more typesafe, make it
    feel like we're using the real underlying extension instance, and make
    it easier to port old code over without changing all the API calls.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=627338

 libpeas/Makefile.am                    |   42 +++--
 libpeas/peas-extension-subclasses.c    |  296 ++++++++++++++++++++++++++++++++
 libpeas/peas-extension-subclasses.h    |   34 ++++
 libpeas/peas-introspection.c           |   64 +++++++
 libpeas/peas-introspection.h           |    3 +
 loaders/c/peas-extension-c.c           |    5 +-
 loaders/python/peas-extension-python.c |    5 +-
 loaders/seed/peas-extension-seed.c     |    6 +-
 peas-demo/peas-demo-window.c           |    4 +-
 9 files changed, 434 insertions(+), 25 deletions(-)
---
diff --git a/libpeas/Makefile.am b/libpeas/Makefile.am
index c0861ab..f7c96db 100644
--- a/libpeas/Makefile.am
+++ b/libpeas/Makefile.am
@@ -26,28 +26,30 @@ INST_H_FILES =			\
 	peas-engine.h		\
 	peas.h
 
-NOINST_H_FILES =		\
-	peas-debug.h		\
-	peas-dirs.h		\
-	peas-helpers.h		\
-	peas-i18n.h		\
-	peas-introspection.h	\
-	peas-plugin-info-priv.h	\
+NOINST_H_FILES =			\
+	peas-debug.h			\
+	peas-dirs.h			\
+	peas-extension-subclasses.h	\
+	peas-helpers.h			\
+	peas-i18n.h			\
+	peas-introspection.h		\
+	peas-plugin-info-priv.h		\
 	peas-plugin-loader.h
 
-C_FILES =			\
-	peas-debug.c		\
-	peas-dirs.c		\
-	peas-helpers.c		\
-	peas-i18n.c		\
-	peas-object-module.c	\
-	peas-introspection.c	\
-	peas-plugin-info.c	\
-	peas-plugin-loader.c	\
-	peas-extension-base.c	\
-	peas-extension.c	\
-	peas-extension-set.c	\
-	peas-activatable.c	\
+C_FILES =				\
+	peas-debug.c			\
+	peas-dirs.c			\
+	peas-helpers.c			\
+	peas-i18n.c			\
+	peas-object-module.c		\
+	peas-introspection.c		\
+	peas-plugin-info.c		\
+	peas-plugin-loader.c		\
+	peas-extension-base.c		\
+	peas-extension.c		\
+	peas-extension-set.c		\
+	peas-extension-subclasses.c	\
+	peas-activatable.c		\
 	peas-engine.c
 
 BUILT_SOURCES = \
diff --git a/libpeas/peas-extension-subclasses.c b/libpeas/peas-extension-subclasses.c
new file mode 100644
index 0000000..a6cf6eb
--- /dev/null
+++ b/libpeas/peas-extension-subclasses.c
@@ -0,0 +1,296 @@
+/*
+ * peas-extension-subclasses.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2010 - Steve Frécinaux
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Library General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <girepository.h>
+#include <girffi.h>
+#include "peas-extension.h"
+#include "peas-extension-subclasses.h"
+#include "peas-introspection.h"
+
+typedef struct _MethodImpl {
+  GICallableInfo *info;
+  gchar *method_name;
+  ffi_cif cif;
+  ffi_closure *closure;
+  guint struct_offset;
+} MethodImpl;
+
+static GQuark
+method_impl_quark (void)
+{
+  static GQuark quark = 0;
+
+  if (quark == 0)
+    quark = g_quark_from_static_string ("PeasExtensionInterfaceImplementation");
+
+  return quark;
+}
+
+static void
+handle_method_impl (ffi_cif  *cif,
+                    gpointer  result,
+                    gpointer *args,
+                    gpointer  data)
+{
+  MethodImpl *impl = (MethodImpl *) data;
+  GIArgInfo *arg_info;
+  GITypeInfo *type_info;
+  GITypeInfo *return_type_info;
+  guint n_args, i;
+  PeasExtension *instance;
+  GArgument *arguments;
+  GArgument return_value;
+
+  instance = *((PeasExtension **) args[0]);
+  g_assert (PEAS_IS_EXTENSION (instance));
+
+  n_args = g_callable_info_get_n_args (impl->info);
+  arguments = g_newa (GArgument, n_args);
+
+  for (i = 1; i < n_args; i++)
+    {
+      arg_info = g_callable_info_get_arg (impl->info, i);
+      type_info = g_arg_info_get_type (arg_info);
+
+      peas_gi_pointer_to_argument (type_info, args[i + 1], &arguments[i]);
+
+      g_base_info_unref (type_info);
+      g_base_info_unref (arg_info);
+    }
+
+  peas_extension_callv (instance, impl->method_name, arguments, &return_value);
+
+  for (i = 1; i < n_args; i++)
+    {
+      GIDirection direction;
+
+      arg_info = g_callable_info_get_arg (impl->info, i);
+      direction = g_arg_info_get_direction (arg_info);
+
+      if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT)
+        {
+          type_info = g_arg_info_get_type (arg_info);
+          peas_gi_argument_to_pointer (type_info, &arguments[i], args[i + 1]);
+          g_base_info_unref (type_info);
+        }
+
+      g_base_info_unref (arg_info);
+    }
+
+  return_type_info = g_callable_info_get_return_type (impl->info);
+
+  if (g_type_info_get_tag (return_type_info) != GI_TYPE_TAG_VOID)
+    peas_gi_argument_to_pointer (return_type_info, &return_value, result);
+
+  g_base_info_unref (return_type_info);
+}
+
+static void
+create_native_closure (GIInterfaceInfo *iface_info,
+                       GIVFuncInfo     *vfunc_info,
+                       MethodImpl      *impl)
+{
+  GIFunctionInfo *invoker_info;
+  GIStructInfo *struct_info;
+  GIFieldInfo *field_info;
+  GITypeInfo *type_info;
+  GICallbackInfo *callback_info;
+  guint n_fields, i;
+  gboolean found_field_info;
+
+  invoker_info = g_vfunc_info_get_invoker (vfunc_info);
+  if (invoker_info == NULL)
+    {
+      g_debug ("No invoker for VFunc '%s.%s'",
+               g_base_info_get_name (iface_info),
+               g_base_info_get_name (vfunc_info));
+      g_base_info_unref (vfunc_info);
+      return;
+    }
+
+  struct_info = g_interface_info_get_iface_struct (iface_info);
+  n_fields = g_struct_info_get_n_fields (struct_info);
+
+  found_field_info = FALSE;
+  for (i = 0; i < n_fields; i++)
+    {
+      field_info = g_struct_info_get_field (struct_info, i);
+
+      if (strcmp (g_base_info_get_name (field_info),
+                  g_base_info_get_name (vfunc_info)) == 0)
+        {
+          found_field_info = TRUE;
+          break;
+        }
+
+      g_base_info_unref (field_info);
+    }
+
+  if (!found_field_info)
+    {
+      g_debug ("No struct field for VFunc '%s.%s'",
+               g_base_info_get_name (iface_info),
+               g_base_info_get_name (vfunc_info));
+      g_base_info_unref (struct_info);
+      g_base_info_unref (invoker_info);
+      return;
+    }
+
+  type_info = g_field_info_get_type (field_info);
+  g_assert (g_type_info_get_tag (type_info) == GI_TYPE_TAG_INTERFACE);
+
+  callback_info = g_type_info_get_interface (type_info);
+  g_assert (g_base_info_get_type (callback_info) == GI_INFO_TYPE_CALLBACK);
+
+  impl->info = g_base_info_ref (callback_info);
+  impl->method_name = g_strdup (g_base_info_get_name (invoker_info));
+  impl->closure = g_callable_info_prepare_closure (callback_info, &impl->cif,
+                                                   handle_method_impl, impl);
+  impl->struct_offset = g_field_info_get_offset (field_info);
+
+  g_base_info_unref (callback_info);
+  g_base_info_unref (type_info);
+  g_base_info_unref (field_info);
+  g_base_info_unref (struct_info);
+  g_base_info_unref (invoker_info);
+}
+
+static void
+implement_interface_methods (gpointer iface,
+                             GType    proxy_type)
+{
+  GType exten_type = G_TYPE_FROM_INTERFACE (iface);
+  GIInterfaceInfo *iface_info;
+  guint n_vfuncs, i;
+  MethodImpl *impls;
+
+  g_debug ("Implementing interface '%s' for proxy type '%s'",
+           g_type_name (exten_type), g_type_name (proxy_type));
+
+  iface_info = g_irepository_find_by_gtype (NULL, exten_type);
+  g_return_if_fail (g_base_info_get_type (iface_info) == GI_INFO_TYPE_INTERFACE);
+
+  n_vfuncs = g_interface_info_get_n_vfuncs (iface_info);
+
+  impls = g_type_get_qdata (exten_type, method_impl_quark ());
+
+  if (impls == NULL)
+    {
+      impls = g_new0 (MethodImpl, n_vfuncs);
+
+      for (i = 0; i < n_vfuncs; i++)
+        {
+          GIVFuncInfo *vfunc_info;
+          vfunc_info = g_interface_info_get_vfunc (iface_info, i);
+          create_native_closure (iface_info, vfunc_info, &impls[i]);
+        }
+
+      g_type_set_qdata (exten_type, method_impl_quark (), impls);
+    }
+
+  for (i = 0; i < n_vfuncs; i++)
+    {
+      gpointer *method_ptr;
+
+      if (impls[i].closure == NULL)
+        continue;
+
+      method_ptr = G_STRUCT_MEMBER_P (iface, impls[i].struct_offset);
+      *method_ptr = impls[i].closure;
+
+      g_debug ("Implemented '%s.%s' at %d (%p) with %p",
+               g_type_name (exten_type), impls[i].method_name,
+               impls[i].struct_offset, method_ptr, impls[i].closure);
+    }
+
+  g_base_info_unref (iface_info);
+
+  g_debug ("Implemented interface '%s' for '%s' proxy",
+           g_type_name (exten_type), g_type_name (proxy_type));
+}
+
+static void
+extension_subclass_init (GObjectClass *klass)
+{
+  g_debug ("Initializing class '%s'", G_OBJECT_CLASS_NAME (klass));
+}
+
+static void
+extension_subclass_instance_init (GObject *instance)
+{
+  g_debug ("Initializing new instance of '%s'", G_OBJECT_TYPE_NAME (instance));
+}
+
+GType
+peas_extension_register_subclass (GType parent_type,
+                                  GType extension_type)
+{
+  gchar *type_name;
+  GType the_type;
+
+  type_name = g_strdup_printf ("%s+%s",
+                               g_type_name (parent_type),
+                               g_type_name (extension_type));
+
+  the_type = g_type_from_name (type_name);
+
+  if (the_type == G_TYPE_INVALID)
+    {
+      GTypeQuery query;
+      GTypeInfo type_info = {
+        0,
+        (GBaseInitFunc) NULL,
+        (GBaseFinalizeFunc) NULL,
+        (GClassInitFunc) extension_subclass_init,
+        (GClassFinalizeFunc) NULL,
+        NULL,
+        0,
+        0,
+        (GInstanceInitFunc) extension_subclass_instance_init
+      };
+      GInterfaceInfo iface_info = {
+        (GInterfaceInitFunc) implement_interface_methods,
+        (GInterfaceFinalizeFunc) NULL,
+        NULL
+      };
+
+      g_debug ("Registering new type '%s'", type_name);
+
+      g_type_query (parent_type, &query);
+      type_info.class_size = query.class_size;
+      type_info.instance_size = query.instance_size;
+
+      the_type = g_type_register_static (parent_type, type_name, &type_info, 0);
+
+      iface_info.interface_data = GSIZE_TO_POINTER (the_type);
+
+      g_type_add_interface_static (the_type, extension_type, &iface_info);
+    }
+
+  g_free (type_name);
+
+  return the_type;
+}
diff --git a/libpeas/peas-extension-subclasses.h b/libpeas/peas-extension-subclasses.h
new file mode 100644
index 0000000..e3fac1e
--- /dev/null
+++ b/libpeas/peas-extension-subclasses.h
@@ -0,0 +1,34 @@
+/*
+ * peas-extension-subclasses.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2010 - Steve Frécinaux
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Library General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PEAS_EXTENSION_SUBCLASSES_H__
+#define __PEAS_EXTENSION_SUBCLASSES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+GType         peas_extension_register_subclass      (GType parent_type,
+                                                     GType extension_type);
+
+G_END_DECLS
+
+#endif /* __PEAS_EXTENSION_SUBCLASSES_H__ */
diff --git a/libpeas/peas-introspection.c b/libpeas/peas-introspection.c
index 1ef4689..040d83c 100644
--- a/libpeas/peas-introspection.c
+++ b/libpeas/peas-introspection.c
@@ -233,6 +233,70 @@ peas_gi_argument_to_pointer (GITypeInfo     *type_info,
     }
 }
 
+void
+peas_gi_pointer_to_argument (GITypeInfo     *type_info,
+                             gpointer        ptr,
+                             GArgument      *arg)
+{
+  g_return_if_fail (ptr != NULL);
+
+  switch (g_type_info_get_tag (type_info))
+    {
+    case GI_TYPE_TAG_VOID:
+    case GI_TYPE_TAG_BOOLEAN:
+      arg->v_boolean = *((gboolean *) ptr);
+      break;
+    case GI_TYPE_TAG_INT8:
+      arg->v_int8 = *((gint8 *) ptr);
+      break;
+    case GI_TYPE_TAG_UINT8:
+      arg->v_uint8 = *((guint8 *) ptr);
+      break;
+    case GI_TYPE_TAG_INT16:
+      arg->v_int16 = *((gint16 *) ptr);
+      break;
+    case GI_TYPE_TAG_UINT16:
+      arg->v_uint16 = *((guint16 *) ptr);
+      break;
+    case GI_TYPE_TAG_INT32:
+      arg->v_int32 = *((gint32 *) ptr);
+      break;
+    case GI_TYPE_TAG_UINT32:
+      arg->v_uint32 = *((guint32 *) ptr);
+      break;
+    case GI_TYPE_TAG_INT64:
+      arg->v_int64 = *((gint64 *) ptr);
+      break;
+    case GI_TYPE_TAG_UINT64:
+      arg->v_uint64 = *((guint64 *) ptr);
+      break;
+    case GI_TYPE_TAG_FLOAT:
+      arg->v_float = *((gfloat *) ptr);
+      break;
+    case GI_TYPE_TAG_DOUBLE:
+      arg->v_double = *((gdouble *) ptr);
+      break;
+    case GI_TYPE_TAG_GTYPE:
+      /* apparently, GType is meant to be a gsize, from gobject/gtype.h in glib */
+      arg->v_size = *((gsize *) ptr);
+      break;
+    case GI_TYPE_TAG_UTF8:
+    case GI_TYPE_TAG_FILENAME:
+      arg->v_string = *((gchar **) ptr);
+      break;
+    case GI_TYPE_TAG_ARRAY:
+    case GI_TYPE_TAG_INTERFACE:
+    case GI_TYPE_TAG_GLIST:
+    case GI_TYPE_TAG_GSLIST:
+    case GI_TYPE_TAG_GHASH:
+    case GI_TYPE_TAG_ERROR:
+      arg->v_pointer = *((gpointer **) ptr);
+      break;
+    default:
+      g_return_if_reached ();
+    }
+}
+
 GICallableInfo *
 peas_gi_get_method_info (GType        iface_type,
                          const gchar *method_name)
diff --git a/libpeas/peas-introspection.h b/libpeas/peas-introspection.h
index 679f9cf..dc15e45 100644
--- a/libpeas/peas-introspection.h
+++ b/libpeas/peas-introspection.h
@@ -37,6 +37,9 @@ void             peas_gi_valist_to_arguments      (GICallableInfo *callable_info
 void             peas_gi_argument_to_pointer      (GITypeInfo     *type_info,
                                                    GArgument      *arg,
                                                    gpointer        ptr);
+void             peas_gi_pointer_to_argument      (GITypeInfo     *type_info,
+                                                   gpointer        ptr,
+                                                   GArgument      *arg);
 gboolean         peas_method_apply                (GObject     *instance,
                                                    GType        iface_type,
                                                    const gchar *method_name,
diff --git a/loaders/c/peas-extension-c.c b/loaders/c/peas-extension-c.c
index 8a9fe5c..136c17f 100644
--- a/loaders/c/peas-extension-c.c
+++ b/loaders/c/peas-extension-c.c
@@ -25,6 +25,7 @@
 
 #include  <girepository.h>
 #include <libpeas/peas-introspection.h>
+#include <libpeas/peas-extension-subclasses.h>
 #include "peas-extension-c.h"
 
 G_DEFINE_TYPE (PeasExtensionC, peas_extension_c, PEAS_TYPE_EXTENSION);
@@ -75,8 +76,10 @@ peas_extension_c_new (GType    gtype,
                       GObject *instance)
 {
   PeasExtensionC *cexten;
+  GType real_type;
 
-  cexten = PEAS_EXTENSION_C (g_object_new (PEAS_TYPE_EXTENSION_C,
+  real_type = peas_extension_register_subclass (PEAS_TYPE_EXTENSION_C, gtype);
+  cexten = PEAS_EXTENSION_C (g_object_new (real_type,
                                            "extension-type", gtype,
                                            NULL));
   cexten->instance = instance;
diff --git a/loaders/python/peas-extension-python.c b/loaders/python/peas-extension-python.c
index d104112..ef147a7 100644
--- a/loaders/python/peas-extension-python.c
+++ b/loaders/python/peas-extension-python.c
@@ -30,6 +30,7 @@
 #include <Python.h>
 #include <pygobject.h>
 #include <libpeas/peas-introspection.h>
+#include <libpeas/peas-extension-subclasses.h>
 #include "peas-extension-python.h"
 
 G_DEFINE_TYPE (PeasExtensionPython, peas_extension_python, PEAS_TYPE_EXTENSION);
@@ -84,8 +85,10 @@ peas_extension_python_new (GType     gtype,
                            PyObject *instance)
 {
   PeasExtensionPython *pyexten;
+  GType real_type;
 
-  pyexten = PEAS_EXTENSION_PYTHON (g_object_new (PEAS_TYPE_EXTENSION_PYTHON,
+  real_type = peas_extension_register_subclass (PEAS_TYPE_EXTENSION_PYTHON, gtype);
+  pyexten = PEAS_EXTENSION_PYTHON (g_object_new (real_type,
                                                  "extension-type", gtype,
                                                  NULL));
   pyexten->instance = instance;
diff --git a/loaders/seed/peas-extension-seed.c b/loaders/seed/peas-extension-seed.c
index 50e3532..ad3d933 100644
--- a/loaders/seed/peas-extension-seed.c
+++ b/loaders/seed/peas-extension-seed.c
@@ -25,6 +25,7 @@
 
 #include "peas-extension-seed.h"
 #include <libpeas/peas-introspection.h>
+#include <libpeas/peas-extension-subclasses.h>
 #include <girepository.h>
 
 G_DEFINE_TYPE (PeasExtensionSeed, peas_extension_seed, PEAS_TYPE_EXTENSION);
@@ -399,10 +400,13 @@ peas_extension_seed_new (GType       exten_type,
                          SeedContext js_context,
                          SeedObject  js_object)
 {
+  GType real_type;
+
   g_return_val_if_fail (js_context != NULL, NULL);
   g_return_val_if_fail (js_object != NULL, NULL);
 
-  return PEAS_EXTENSION (g_object_new (PEAS_TYPE_EXTENSION_SEED,
+  real_type = peas_extension_register_subclass (PEAS_TYPE_EXTENSION_SEED, exten_type);
+  return PEAS_EXTENSION (g_object_new (real_type,
                                        "extension-type", exten_type,
                                        "js-context", js_context,
                                        "js-object", js_object,
diff --git a/peas-demo/peas-demo-window.c b/peas-demo/peas-demo-window.c
index 9efd57c..35cc2cb 100644
--- a/peas-demo/peas-demo-window.c
+++ b/peas-demo/peas-demo-window.c
@@ -47,7 +47,7 @@ on_extension_added (PeasExtensionSet *set,
                     PeasExtension    *exten,
                     DemoWindow       *dw)
 {
-  peas_extension_call (exten, "activate", dw);
+  peas_activatable_activate (PEAS_ACTIVATABLE (exten));
 }
 
 static void
@@ -56,7 +56,7 @@ on_extension_removed (PeasExtensionSet *set,
                       PeasExtension    *exten,
                       DemoWindow       *dw)
 {
-  peas_extension_call (exten, "deactivate");
+  peas_activatable_deactivate (PEAS_ACTIVATABLE (exten));
 }
 
 static gboolean



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