[libpeas] Allow extensions to be an Abstract Base Class



commit 7e7e850d24a930ffdaf04d0feaf6607fe797f30e
Author: Garrett Regier <garrettregier gmail com>
Date:   Fri Mar 24 12:41:09 2017 -0700

    Allow extensions to be an Abstract Base Class
    
    https://bugzilla.gnome.org/show_bug.cgi?id=767223

 libpeas/peas-engine.c                              |   33 ++++-
 libpeas/peas-extension-set.c                       |   18 +++-
 libpeas/peas-extension.c                           |   32 +++---
 libpeas/peas-introspection.c                       |   29 +++--
 libpeas/peas-introspection.h                       |    4 +-
 libpeas/peas-object-module.c                       |  116 +++++++++--------
 libpeas/peas-object-module.h                       |   10 +-
 libpeas/peas-utils.c                               |   40 +++++-
 libpeas/peas-utils.h                               |    2 +-
 tests/libpeas/introspection/Makefile.am            |    2 +
 .../libpeas/introspection/introspection-abstract.c |  132 ++++++++++++++++++++
 .../libpeas/introspection/introspection-abstract.h |   60 +++++++++
 tests/libpeas/plugins/extension-c/Makefile.am      |    4 +-
 .../plugins/extension-c/extension-c-abstract.c     |   55 ++++++++
 .../plugins/extension-c/extension-c-abstract.h     |   52 ++++++++
 .../plugins/extension-c/extension-c-plugin.c       |    7 +
 .../plugins/extension-lua/extension-lua51.lua      |    6 +-
 .../plugins/extension-python/extension-py.py.in    |    6 +-
 tests/libpeas/testing/testing-extension.c          |   51 ++++++++
 19 files changed, 548 insertions(+), 111 deletions(-)
---
diff --git a/libpeas/peas-engine.c b/libpeas/peas-engine.c
index 3b35145..5713cb0 100644
--- a/libpeas/peas-engine.c
+++ b/libpeas/peas-engine.c
@@ -57,6 +57,9 @@
  *     plugins and their extensions from within your application.</para>
  *   </listitem>
  * </itemizedlist>
+ *
+ * Since libpeas 1.22, @extension_type can be an Abstract #GType
+ * and not just an Interface #GType.
  **/
 
 /* Signals */
@@ -1274,6 +1277,9 @@ peas_engine_unload_plugin (PeasEngine     *engine,
  * Returns if @info provides an extension for @extension_type.
  * If the @info is not loaded than %FALSE will always be returned.
  *
+ * Since libpeas 1.22, @extension_type can be an Abstract #GType
+ * and not just an Interface #GType.
+ *
  * Returns: if @info provides an extension for @extension_type.
  */
 gboolean
@@ -1285,7 +1291,8 @@ peas_engine_provides_extension (PeasEngine     *engine,
 
   g_return_val_if_fail (PEAS_IS_ENGINE (engine), FALSE);
   g_return_val_if_fail (info != NULL, FALSE);
-  g_return_val_if_fail (G_TYPE_IS_INTERFACE (extension_type), FALSE);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (extension_type) ||
+                        G_TYPE_IS_ABSTRACT (extension_type), FALSE);
 
   if (!peas_plugin_info_is_loaded (info))
     return FALSE;
@@ -1303,10 +1310,13 @@ peas_engine_provides_extension (PeasEngine     *engine,
  * @parameters: (allow-none) (array length=n_parameters):
  *   an array of #GParameter.
  *
- * If the plugin identified by @info implements the @extension_type interface,
+ * If the plugin identified by @info implements the @extension_type,
  * then this function will return a new instance of this implementation,
  * wrapped in a new #PeasExtension instance. Otherwise, it will return %NULL.
  *
+ * Since libpeas 1.22, @extension_type can be an Abstract #GType
+ * and not just an Interface #GType.
+ *
  * See peas_engine_create_extension() for more information.
  *
  * Returns: (transfer full): a new instance of #PeasExtension wrapping
@@ -1324,8 +1334,9 @@ peas_engine_create_extensionv (PeasEngine     *engine,
 
   g_return_val_if_fail (PEAS_IS_ENGINE (engine), NULL);
   g_return_val_if_fail (info != NULL, NULL);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (extension_type) ||
+                        G_TYPE_IS_ABSTRACT (extension_type), NULL);
   g_return_val_if_fail (peas_plugin_info_is_loaded (info), NULL);
-  g_return_val_if_fail (G_TYPE_IS_INTERFACE (extension_type), FALSE);
 
   loader = get_plugin_loader (engine, info->loader_id);
   extension = peas_plugin_loader_create_extension (loader, info, extension_type,
@@ -1351,10 +1362,13 @@ peas_engine_create_extensionv (PeasEngine     *engine,
  * @var_args: the value of the first property, followed optionally by more
  *   name/value pairs, followed by %NULL.
  *
- * If the plugin identified by @info implements the @extension_type interface,
+ * If the plugin identified by @info implements the @extension_type,
  * then this function will return a new instance of this implementation,
  * wrapped in a new #PeasExtension instance. Otherwise, it will return %NULL.
  *
+ * Since libpeas 1.22, @extension_type can be an Abstract #GType
+ * and not just an Interface #GType.
+ *
  * See peas_engine_create_extension() for more information.
  *
  * Returns: a new instance of #PeasExtension wrapping
@@ -1374,7 +1388,8 @@ peas_engine_create_extension_valist (PeasEngine     *engine,
   g_return_val_if_fail (PEAS_IS_ENGINE (engine), NULL);
   g_return_val_if_fail (info != NULL, NULL);
   g_return_val_if_fail (peas_plugin_info_is_loaded (info), NULL);
-  g_return_val_if_fail (G_TYPE_IS_INTERFACE (extension_type), FALSE);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (extension_type) ||
+                        G_TYPE_IS_ABSTRACT (extension_type), FALSE);
 
   if (!peas_utils_valist_to_parameter_list (extension_type, first_property,
                                             var_args, &parameters,
@@ -1403,7 +1418,7 @@ peas_engine_create_extension_valist (PeasEngine     *engine,
  * @...: the value of the first property, followed optionally by more
  *   name/value pairs, followed by %NULL.
  *
- * If the plugin identified by @info implements the @extension_type interface,
+ * If the plugin identified by @info implements the @extension_type,
  * then this function will return a new instance of this implementation,
  * wrapped in a new #PeasExtension instance. Otherwise, it will return %NULL.
  *
@@ -1416,6 +1431,9 @@ peas_engine_create_extension_valist (PeasEngine     *engine,
  * principle of never giving you the actual object (also because it might as
  * well *not* be an actual object).
  *
+ * Since libpeas 1.22, @extension_type can be an Abstract #GType
+ * and not just an Interface #GType.
+ *
  * Returns: a new instance of #PeasExtension wrapping
  * the @extension_type instance, or %NULL.
  */
@@ -1432,7 +1450,8 @@ peas_engine_create_extension (PeasEngine     *engine,
   g_return_val_if_fail (PEAS_IS_ENGINE (engine), NULL);
   g_return_val_if_fail (info != NULL, NULL);
   g_return_val_if_fail (peas_plugin_info_is_loaded (info), NULL);
-  g_return_val_if_fail (G_TYPE_IS_INTERFACE (extension_type), FALSE);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (extension_type) ||
+                        G_TYPE_IS_ABSTRACT (extension_type), FALSE);
 
   va_start (var_args, first_property);
   exten = peas_engine_create_extension_valist (engine, info, extension_type,
diff --git a/libpeas/peas-extension-set.c b/libpeas/peas-extension-set.c
index cb3f9cc..a4e0f3f 100644
--- a/libpeas/peas-extension-set.c
+++ b/libpeas/peas-extension-set.c
@@ -615,6 +615,9 @@ peas_extension_set_foreach (PeasExtensionSet            *set,
  *
  * If @engine is %NULL, then the default engine will be used.
  *
+ * Since libpeas 1.22, @exten_type can be an Abstract #GType
+ * and not just an Interface #GType.
+ *
  * See peas_extension_set_new() for more information.
  *
  * Returns: (transfer full): a new instance of #PeasExtensionSet.
@@ -628,7 +631,8 @@ peas_extension_set_newv (PeasEngine *engine,
   PeasParameterArray construct_properties = { n_parameters, parameters };
 
   g_return_val_if_fail (engine == NULL || PEAS_IS_ENGINE (engine), NULL);
-  g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type), NULL);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
+                        G_TYPE_IS_ABSTRACT (exten_type), NULL);
 
   return PEAS_EXTENSION_SET (g_object_new (PEAS_TYPE_EXTENSION_SET,
                                            "engine", engine,
@@ -649,6 +653,9 @@ peas_extension_set_newv (PeasEngine *engine,
  *
  * If @engine is %NULL, then the default engine will be used.
  *
+ * Since libpeas 1.22, @exten_type can be an Abstract #GType
+ * and not just an Interface #GType.
+ *
  * See peas_extension_set_new() for more information.
  *
  * Returns: a new instance of #PeasExtensionSet.
@@ -664,7 +671,8 @@ peas_extension_set_new_valist (PeasEngine  *engine,
   PeasExtensionSet *set;
 
   g_return_val_if_fail (engine == NULL || PEAS_IS_ENGINE (engine), NULL);
-  g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type), NULL);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
+                        G_TYPE_IS_ABSTRACT (exten_type), NULL);
 
   if (!peas_utils_valist_to_parameter_list (exten_type, first_property,
                                             var_args, &parameters,
@@ -702,6 +710,9 @@ peas_extension_set_new_valist (PeasEngine  *engine,
  *
  * If @engine is %NULL, then the default engine will be used.
  *
+ * Since libpeas 1.22, @exten_type can be an Abstract #GType
+ * and not just an Interface #GType.
+ *
  * See peas_engine_create_extension() for more information.
  *
  * Returns: a new instance of #PeasExtensionSet.
@@ -716,7 +727,8 @@ peas_extension_set_new (PeasEngine  *engine,
   PeasExtensionSet *set;
 
   g_return_val_if_fail (engine == NULL || PEAS_IS_ENGINE (engine), NULL);
-  g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type), NULL);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
+                        G_TYPE_IS_ABSTRACT (exten_type), NULL);
 
   va_start (var_args, first_property);
   set = peas_extension_set_new_valist (engine, exten_type, first_property, var_args);
diff --git a/libpeas/peas-extension.c b/libpeas/peas-extension.c
index 773d9f2..8e46920 100644
--- a/libpeas/peas-extension.c
+++ b/libpeas/peas-extension.c
@@ -37,8 +37,8 @@
  * loaded plugins.
  *
  * To properly use the proxy instances, you will need GObject-introspection
- * data for the #GInterface or #GObjectClass you want to use as an extension
- * point.  For instance, if you wish to use #PeasActivatable, you will need to
+ * data for the #GType you want to use as an extension point.
+ * For instance, if you wish to use #PeasActivatable, you will need to
  * put the following code excerpt in the engine initialization code, in order
  * to load the required "Peas" typelib:
  *
@@ -47,7 +47,7 @@
  *                        "Peas", "1.0", 0, NULL);
  * ]|
  *
- * You should proceed the same way for any namespace which provides interfaces
+ * You should proceed the same way for any namespace which provides types
  * you want to use as extension points. GObject-introspection data is required
  * for all the supported languages, even for C.
  *
@@ -72,7 +72,7 @@ G_DEFINE_QUARK (peas-extension-type, extension_type)
 static GICallableInfo *
 get_method_info (PeasExtension *exten,
                  const gchar   *method_name,
-                 GType         *interface)
+                 GType         *gtype)
 {
   guint i;
   GType exten_type;
@@ -85,8 +85,8 @@ get_method_info (PeasExtension *exten,
 
   if (method_info != NULL)
     {
-      if (interface != NULL)
-        *interface = exten_type;
+      if (gtype != NULL)
+        *gtype = exten_type;
 
       return method_info;
     }
@@ -99,15 +99,15 @@ get_method_info (PeasExtension *exten,
 
       if (method_info != NULL)
         {
-          if (interface != NULL)
-            *interface = interfaces[i];
+          if (gtype != NULL)
+            *gtype = interfaces[i];
 
           break;
         }
     }
 
   if (method_info == NULL)
-    g_warning ("Could not find the interface for method '%s'", method_name);
+    g_warning ("Could not find the GType for method '%s'", method_name);
 
   g_free (interfaces);
   return method_info;
@@ -117,7 +117,7 @@ get_method_info (PeasExtension *exten,
  * peas_extension_get_extension_type:
  * @exten: A #PeasExtension.
  *
- * Get the type of the extension interface of the object proxied by @exten.
+ * Get the #GType of the extension proxied by @exten.
  *
  * Return value: The #GType proxied by @exten.
  *
@@ -154,7 +154,7 @@ peas_extension_get_extension_type (PeasExtension *exten)
  *
  * Return value: %TRUE on successful call.
  *
- * Deprecated: 1.2: Use the interface directly instead.
+ * Deprecated: 1.2: Use the object directly instead.
  */
 gboolean
 peas_extension_call (PeasExtension *exten,
@@ -186,7 +186,7 @@ peas_extension_call (PeasExtension *exten,
  *
  * Return value: %TRUE on successful call.
  *
- * Deprecated: 1.2: Use the interface directly instead.
+ * Deprecated: 1.2: Use the object directly instead.
  */
 gboolean
 peas_extension_call_valist (PeasExtension *exten,
@@ -241,7 +241,7 @@ peas_extension_call_valist (PeasExtension *exten,
  *
  * Return value: %TRUE on successful call.
  *
- * Deprecated: 1.2: Use the interface directly instead.
+ * Deprecated: 1.2: Use the object directly instead.
  */
 gboolean
 peas_extension_callv (PeasExtension *exten,
@@ -250,19 +250,19 @@ peas_extension_callv (PeasExtension *exten,
                       GIArgument    *return_value)
 {
   GICallableInfo *method_info;
-  GType interface;
+  GType gtype;
   gboolean success;
 
   g_return_val_if_fail (PEAS_IS_EXTENSION (exten), FALSE);
   g_return_val_if_fail (method_name != NULL, FALSE);
 
-  method_info = get_method_info (exten, method_name, &interface);
+  method_info = get_method_info (exten, method_name, &gtype);
 
   /* Already warned */
   if (method_info == NULL)
     return FALSE;
 
-  success = peas_gi_method_call (G_OBJECT (exten), method_info, interface,
+  success = peas_gi_method_call (G_OBJECT (exten), method_info, gtype,
                                  method_name, args, return_value);
 
   g_base_info_unref (method_info);
diff --git a/libpeas/peas-introspection.c b/libpeas/peas-introspection.c
index 89aa41e..49ac178 100644
--- a/libpeas/peas-introspection.c
+++ b/libpeas/peas-introspection.c
@@ -239,44 +239,44 @@ peas_gi_argument_to_pointer (GITypeInfo     *type_info,
 }
 
 GICallableInfo *
-peas_gi_get_method_info (GType        iface_type,
+peas_gi_get_method_info (GType        gtype,
                          const gchar *method_name)
 {
   GIRepository *repo;
-  GIBaseInfo *iface_info;
+  GIBaseInfo *type_info;
   GIFunctionInfo *func_info;
 
   repo = g_irepository_get_default ();
-  iface_info = g_irepository_find_by_gtype (repo, iface_type);
-  if (iface_info == NULL)
+  type_info = g_irepository_find_by_gtype (repo, gtype);
+  if (type_info == NULL)
     {
       g_warning ("Type not found in introspection: '%s'",
-                 g_type_name (iface_type));
+                 g_type_name (gtype));
       return NULL;
     }
 
-  switch (g_base_info_get_type (iface_info))
+  switch (g_base_info_get_type (type_info))
     {
     case GI_INFO_TYPE_OBJECT:
-      func_info = g_object_info_find_method ((GIObjectInfo *) iface_info,
+      func_info = g_object_info_find_method ((GIObjectInfo *) type_info,
                                              method_name);
       break;
     case GI_INFO_TYPE_INTERFACE:
-      func_info = g_interface_info_find_method ((GIInterfaceInfo *) iface_info,
+      func_info = g_interface_info_find_method ((GIInterfaceInfo *) type_info,
                                                 method_name);
       break;
     default:
       func_info = NULL;
     }
 
-  g_base_info_unref (iface_info);
+  g_base_info_unref (type_info);
   return (GICallableInfo *) func_info;
 }
 
 gboolean
 peas_gi_method_call (GObject        *instance,
                      GICallableInfo *func_info,
-                     GType           iface_type,
+                     GType           gtype,
                      const gchar    *method_name,
                      GIArgument     *args,
                      GIArgument     *return_value)
@@ -289,8 +289,9 @@ peas_gi_method_call (GObject        *instance,
 
   g_return_val_if_fail (G_IS_OBJECT (instance), FALSE);
   g_return_val_if_fail (func_info != NULL, FALSE);
-  g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_type), FALSE);
-  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (instance, iface_type),
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (gtype) ||
+                        G_TYPE_IS_ABSTRACT (gtype), FALSE);
+  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (instance, gtype),
                         FALSE);
   g_return_val_if_fail (method_name != NULL, FALSE);
 
@@ -311,14 +312,14 @@ peas_gi_method_call (GObject        *instance,
   n_in_args++;
 
   g_debug ("Calling '%s.%s' on '%p'",
-           g_type_name (iface_type), method_name, instance);
+           g_type_name (gtype), method_name, instance);
 
   ret = g_function_info_invoke (func_info, in_args, n_in_args, out_args,
                                 n_out_args, return_value, &error);
   if (!ret)
     {
       g_warning ("Error while calling '%s.%s': %s",
-                 g_type_name (iface_type), method_name, error->message);
+                 g_type_name (gtype), method_name, error->message);
       g_error_free (error);
     }
 
diff --git a/libpeas/peas-introspection.h b/libpeas/peas-introspection.h
index 53af722..e5f3bbd 100644
--- a/libpeas/peas-introspection.h
+++ b/libpeas/peas-introspection.h
@@ -27,7 +27,7 @@
 
 G_BEGIN_DECLS
 
-GICallableInfo  *peas_gi_get_method_info          (GType           iface_type,
+GICallableInfo  *peas_gi_get_method_info          (GType           gtype,
                                                    const gchar    *method_name);
 
 void             peas_gi_valist_to_arguments      (GICallableInfo *callable_info,
@@ -39,7 +39,7 @@ void             peas_gi_argument_to_pointer      (GITypeInfo     *type_info,
                                                    gpointer        ptr);
 gboolean         peas_gi_method_call              (GObject        *instance,
                                                    GICallableInfo *method_info,
-                                                   GType           iface_type,
+                                                   GType           gtype,
                                                    const gchar    *method_name,
                                                    GIArgument     *args,
                                                    GIArgument     *return_value);
diff --git a/libpeas/peas-object-module.c b/libpeas/peas-object-module.c
index fbd81f2..6711667 100644
--- a/libpeas/peas-object-module.c
+++ b/libpeas/peas-object-module.c
@@ -40,6 +40,9 @@
  * of extensions.  It will be used by C extensions implementors to register
  * extension implementations from within the peas_register_types module
  * function.
+ *
+ * Since libpeas 1.22, @extension_type can be an Abstract #GType
+ * and not just an Interface #GType.
  **/
 
 typedef void (*PeasObjectModuleRegisterFunc) (PeasObjectModule *module);
@@ -57,11 +60,11 @@ enum {
 static GParamSpec *properties[N_PROPERTIES] = { NULL };
 
 typedef struct {
-  GType iface_type;
+  GType exten_type;
   PeasFactoryFunc func;
   gpointer user_data;
   GDestroyNotify destroy_func;
-} InterfaceImplementation;
+} ExtensionImplementation;
 
 struct _PeasObjectModulePrivate {
   GModule *library;
@@ -171,7 +174,7 @@ peas_object_module_unload (GTypeModule *gmodule)
 {
   PeasObjectModule *module = PEAS_OBJECT_MODULE (gmodule);
   PeasObjectModulePrivate *priv = GET_PRIV (module);
-  InterfaceImplementation *impls;
+  ExtensionImplementation *impls;
   guint i;
 
   g_module_close (priv->library);
@@ -179,7 +182,7 @@ peas_object_module_unload (GTypeModule *gmodule)
   priv->library = NULL;
   priv->register_func = NULL;
 
-  impls = (InterfaceImplementation *) priv->implementations->data;
+  impls = (ExtensionImplementation *) priv->implementations->data;
   for (i = 0; i < priv->implementations->len; ++i)
     {
       if (impls[i].destroy_func != NULL)
@@ -196,7 +199,7 @@ peas_object_module_init (PeasObjectModule *module)
   PeasObjectModulePrivate *priv = GET_PRIV (module);
 
   priv->implementations = g_array_new (FALSE, FALSE,
-                                       sizeof (InterfaceImplementation));
+                                       sizeof (ExtensionImplementation));
 }
 
 static void
@@ -433,36 +436,38 @@ peas_object_module_new_embedded (const gchar *module_name,
 /**
  * peas_object_module_create_object: (skip)
  * @module: A #PeasObjectModule.
- * @interface: The #GType of the extension interface.
+ * @exten_type: The #GType of the extension.
  * @n_parameters: The number of paramteters.
  * @parameters: (array length=n_parameters): The parameters.
  *
- * Creates an object for the @interface passing @n_parameters
+ * Creates an object for the @exten_type passing @n_parameters
  * and @parameters to the #PeasFactoryFunc. If @module does
- * not provide a #PeasFactoryFunc for @interface then
+ * not provide a #PeasFactoryFunc for @exten_type then
  * %NULL is returned.
  *
+ * Since libpeas 1.22, @exten_type can be an Abstract #GType
+ * and not just an Interface #GType.
+ *
  * Return value: (transfer full): The created object, or %NULL.
  */
 GObject *
 peas_object_module_create_object (PeasObjectModule *module,
-                                  GType             interface,
+                                  GType             exten_type,
                                   guint             n_parameters,
                                   GParameter       *parameters)
 {
   PeasObjectModulePrivate *priv = GET_PRIV (module);
   guint i;
-  InterfaceImplementation *impls;
+  ExtensionImplementation *impls;
 
   g_return_val_if_fail (PEAS_IS_OBJECT_MODULE (module), NULL);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
+                        G_TYPE_IS_ABSTRACT (exten_type), NULL);
 
-  if (interface != PEAS_TYPE_PLUGIN_LOADER)
-    g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL);
-
-  impls = (InterfaceImplementation *) priv->implementations->data;
+  impls = (ExtensionImplementation *) priv->implementations->data;
   for (i = 0; i < priv->implementations->len; ++i)
     {
-      if (impls[i].iface_type == interface)
+      if (impls[i].exten_type == exten_type)
         return impls[i].func (n_parameters, parameters, impls[i].user_data);
     }
 
@@ -472,29 +477,31 @@ peas_object_module_create_object (PeasObjectModule *module,
 /**
  * peas_object_module_provides_object: (skip)
  * @module: A #PeasObjectModule.
- * @interface: The #GType of the extension interface.
+ * @exten_type: The #GType of the extension.
  *
- * Determines if the module provides an extension for @interface.
+ * Determines if the module provides an extension for @exten_type.
  *
- * Return value: if the module provides an extension for @interface.
+ * Since libpeas 1.22, @exten_type can be an Abstract #GType
+ * and not just an Interface #GType.
+ *
+ * Return value: if the module provides an extension for @exten_type.
  */
 gboolean
 peas_object_module_provides_object (PeasObjectModule *module,
-                                    GType             interface)
+                                    GType             exten_type)
 {
   PeasObjectModulePrivate *priv = GET_PRIV (module);
   guint i;
-  InterfaceImplementation *impls;
+  ExtensionImplementation *impls;
 
   g_return_val_if_fail (PEAS_IS_OBJECT_MODULE (module), FALSE);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
+                        G_TYPE_IS_ABSTRACT (exten_type), FALSE);
 
-  if (interface != PEAS_TYPE_PLUGIN_LOADER)
-    g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), FALSE);
-
-  impls = (InterfaceImplementation *) priv->implementations->data;
+  impls = (ExtensionImplementation *) priv->implementations->data;
   for (i = 0; i < priv->implementations->len; ++i)
     {
-      if (impls[i].iface_type == interface)
+      if (impls[i].exten_type == exten_type)
         return TRUE;
     }
 
@@ -578,8 +585,8 @@ peas_object_module_get_library (PeasObjectModule *module)
 /**
  * peas_object_module_register_extension_factory:
  * @module: Your plugin's #PeasObjectModule.
- * @iface_type: The #GType of the extension interface you implement.
- * @factory_func: The #PeasFactoryFunc that will create the @iface_type
+ * @exten_type: The #GType of the extension you implement.
+ * @factory_func: The #PeasFactoryFunc that will create the @exten_type
  *   instance when requested.
  * @user_data: Data to pass to @func calls.
  * @destroy_func: A #GDestroyNotify for @user_data.
@@ -592,27 +599,29 @@ peas_object_module_get_library (PeasObjectModule *module)
  * creating native types which cannot be instantiated correctly using
  * g_object_new().  For other uses, you will usually prefer relying on
  * peas_object_module_register_extension_type().
+ *
+ * Since libpeas 1.22, @exten_type can be an Abstract #GType
+ * and not just an Interface #GType.
  */
 void
 peas_object_module_register_extension_factory (PeasObjectModule *module,
-                                               GType             iface_type,
+                                               GType             exten_type,
                                                PeasFactoryFunc   factory_func,
                                                gpointer          user_data,
                                                GDestroyNotify    destroy_func)
 {
   PeasObjectModulePrivate *priv = GET_PRIV (module);
-  InterfaceImplementation impl = { iface_type, factory_func, user_data, destroy_func };
+  ExtensionImplementation impl = { exten_type, factory_func, user_data, destroy_func };
 
   g_return_if_fail (PEAS_IS_OBJECT_MODULE (module));
-  g_return_if_fail (!peas_object_module_provides_object (module, iface_type));
+  g_return_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
+                    G_TYPE_IS_ABSTRACT (exten_type));
+  g_return_if_fail (!peas_object_module_provides_object (module, exten_type));
   g_return_if_fail (factory_func != NULL);
 
-  if (iface_type != PEAS_TYPE_PLUGIN_LOADER)
-    g_return_if_fail (G_TYPE_IS_INTERFACE (iface_type));
-
   g_array_append_val (priv->implementations, impl);
 
-  g_debug ("Registered extension for type '%s'", g_type_name (iface_type));
+  g_debug ("Registered extension for type '%s'", g_type_name (exten_type));
 }
 
 static GObject *
@@ -620,15 +629,15 @@ create_gobject_from_type (guint       n_parameters,
                           GParameter *parameters,
                           gpointer    user_data)
 {
-  GType exten_type = GPOINTER_TO_SIZE (user_data);
+  GType impl_type = GPOINTER_TO_SIZE (user_data);
 
   /* We should be called with a "plugin-info" property appended
    * to the parameters. Let's get rid of it if the actual type
    * doesn't have such a property as it would cause a warning.
    */
-  if ((exten_type & TYPE_MISSING_PLUGIN_INFO_PROPERTY) != 0)
+  if ((impl_type & TYPE_MISSING_PLUGIN_INFO_PROPERTY) != 0)
     {
-      exten_type &= ~TYPE_MISSING_PLUGIN_INFO_PROPERTY;
+      impl_type &= ~TYPE_MISSING_PLUGIN_INFO_PROPERTY;
 
       if (n_parameters > 0)
         {
@@ -642,44 +651,45 @@ create_gobject_from_type (guint       n_parameters,
         }
     }
 
-  return G_OBJECT (g_object_newv (exten_type, n_parameters, parameters));
+  return G_OBJECT (g_object_newv (impl_type, n_parameters, parameters));
 }
 
 /**
  * peas_object_module_register_extension_type:
  * @module: Your plugin's #PeasObjectModule.
- * @iface_type: The #GType of the extension interface you implement.
- * @extension_type: The #GType of your implementation of @iface_type.
+ * @exten_type: The #GType of the extension you implement.
+ * @impl_type: The #GType of your implementation of @exten_type.
  *
- * Register an extension type which implements the extension interface
- * @iface_type.
+ * Register @impl_type as an extension which implements @extension_type.
+ *
+ * Since libpeas 1.22, @exten_type can be an Abstract #GType
+ * and not just an Interface #GType.
  */
 void
 peas_object_module_register_extension_type (PeasObjectModule *module,
-                                            GType             iface_type,
-                                            GType             extension_type)
+                                            GType             exten_type,
+                                            GType             impl_type)
 {
   GObjectClass *cls;
   GParamSpec *pspec;
 
   g_return_if_fail (PEAS_IS_OBJECT_MODULE (module));
-  g_return_if_fail (!peas_object_module_provides_object (module, iface_type));
-  g_return_if_fail (g_type_is_a (extension_type, iface_type));
-
-  if (iface_type != PEAS_TYPE_PLUGIN_LOADER)
-    g_return_if_fail (G_TYPE_IS_INTERFACE (iface_type));
+  g_return_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
+                    G_TYPE_IS_ABSTRACT (exten_type));
+  g_return_if_fail (!peas_object_module_provides_object (module, exten_type));
+  g_return_if_fail (g_type_is_a (impl_type, exten_type));
 
-  cls = g_type_class_ref (extension_type);
+  cls = g_type_class_ref (impl_type);
   pspec = g_object_class_find_property (cls, "plugin-info");
 
   /* Avoid checking for this each time in the factory function */
   if (pspec == NULL || pspec->value_type != PEAS_TYPE_PLUGIN_INFO)
-    extension_type |= TYPE_MISSING_PLUGIN_INFO_PROPERTY;
+    impl_type |= TYPE_MISSING_PLUGIN_INFO_PROPERTY;
 
   peas_object_module_register_extension_factory (module,
-                                                 iface_type,
+                                                 exten_type,
                                                  create_gobject_from_type,
-                                                 GSIZE_TO_POINTER (extension_type),
+                                                 GSIZE_TO_POINTER (impl_type),
                                                  NULL);
 
   g_type_class_unref (cls);
diff --git a/libpeas/peas-object-module.h b/libpeas/peas-object-module.h
index 70a0265..08c5d6b 100644
--- a/libpeas/peas-object-module.h
+++ b/libpeas/peas-object-module.h
@@ -95,11 +95,11 @@ PeasObjectModule   *peas_object_module_new_embedded           (const gchar
                                                                const gchar      *symbol);
 
 GObject            *peas_object_module_create_object          (PeasObjectModule *module,
-                                                               GType             interface,
+                                                               GType             exten_type,
                                                                guint             n_parameters,
                                                                GParameter       *parameters);
 gboolean            peas_object_module_provides_object        (PeasObjectModule *module,
-                                                               GType             interface);
+                                                               GType             exten_type);
 
 const gchar        *peas_object_module_get_path               (PeasObjectModule *module);
 const gchar        *peas_object_module_get_module_name        (PeasObjectModule *module);
@@ -109,14 +109,14 @@ GModule            *peas_object_module_get_library            (PeasObjectModule
 
 void                peas_object_module_register_extension_factory
                                                               (PeasObjectModule *module,
-                                                               GType             iface_type,
+                                                               GType             exten_type,
                                                                PeasFactoryFunc   factory_func,
                                                                gpointer          user_data,
                                                                GDestroyNotify    destroy_func);
 void                peas_object_module_register_extension_type
                                                               (PeasObjectModule *module,
-                                                               GType             iface_type,
-                                                               GType             extension_type);
+                                                               GType             exten_type,
+                                                               GType             impl_type);
 
 G_END_DECLS
 
diff --git a/libpeas/peas-utils.c b/libpeas/peas-utils.c
index 1051d20..7d3bd91 100644
--- a/libpeas/peas-utils.c
+++ b/libpeas/peas-utils.c
@@ -91,6 +91,34 @@ add_all_prerequisites (GType      iface_type,
   g_free (prereq);
 }
 
+static GPtrArray *
+find_base_type_and_interfaces (GType  exten_type,
+                               GType *base_type)
+{
+  GPtrArray *ifaces;
+  GType *interfaces;
+  gint i;
+
+  ifaces = g_ptr_array_new ();
+  g_ptr_array_set_free_func (ifaces,
+                             (GDestroyNotify) g_type_default_interface_unref);
+
+  if (G_TYPE_IS_INTERFACE (exten_type))
+    {
+      add_all_prerequisites (exten_type, base_type, ifaces);
+      return ifaces;
+    }
+
+  interfaces = g_type_interfaces (exten_type, NULL);
+  for (i = 0; interfaces[i] != G_TYPE_INVALID; ++i)
+    add_all_prerequisites (exten_type, base_type, ifaces);
+
+  *base_type = exten_type;
+
+  g_free (interfaces);
+  return ifaces;
+}
+
 static GParamSpec *
 find_param_spec_for_prerequisites (const gchar  *name,
                                    GObjectClass *klass,
@@ -113,7 +141,7 @@ find_param_spec_for_prerequisites (const gchar  *name,
 }
 
 gboolean
-peas_utils_valist_to_parameter_list (GType         iface_type,
+peas_utils_valist_to_parameter_list (GType         exten_type,
                                      const gchar  *first_property,
                                      va_list       args,
                                      GParameter  **params,
@@ -125,12 +153,10 @@ peas_utils_valist_to_parameter_list (GType         iface_type,
   const gchar *name;
   guint n_allocated_params;
 
-  g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_type), FALSE);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
+                        G_TYPE_IS_OBJECT (exten_type), FALSE);
 
-  ifaces = g_ptr_array_new ();
-  g_ptr_array_set_free_func (ifaces,
-                             (GDestroyNotify) g_type_default_interface_unref);
-  add_all_prerequisites (iface_type, &base_type, ifaces);
+  ifaces = find_base_type_and_interfaces (exten_type, &base_type);
 
   if (base_type != G_TYPE_INVALID)
     klass = g_type_class_ref (base_type);
@@ -150,7 +176,7 @@ peas_utils_valist_to_parameter_list (GType         iface_type,
       if (!pspec)
         {
           g_warning ("%s: type '%s' has no property named '%s'",
-                     G_STRFUNC, g_type_name (iface_type), name);
+                     G_STRFUNC, g_type_name (exten_type), name);
           goto error;
         }
 
diff --git a/libpeas/peas-utils.h b/libpeas/peas-utils.h
index adca711..5a19ba0 100644
--- a/libpeas/peas-utils.h
+++ b/libpeas/peas-utils.h
@@ -27,7 +27,7 @@
 #define PEAS_UTILS_C_LOADER_ID  0
 #define PEAS_UTILS_N_LOADERS    4
 
-gboolean  peas_utils_valist_to_parameter_list (GType         iface_type,
+gboolean  peas_utils_valist_to_parameter_list (GType         exten_type,
                                                const gchar  *first_property,
                                                va_list       var_args,
                                                GParameter  **params,
diff --git a/tests/libpeas/introspection/Makefile.am b/tests/libpeas/introspection/Makefile.am
index 930851b..3ea3758 100644
--- a/tests/libpeas/introspection/Makefile.am
+++ b/tests/libpeas/introspection/Makefile.am
@@ -13,6 +13,8 @@ libintrospection_1_0_la_LIBADD = \
        $(top_builddir)/libpeas/libpeas-1.0.la
 
 libintrospection_1_0_la_SOURCES = \
+       introspection-abstract.c                \
+       introspection-abstract.h                \
        introspection-base.c                    \
        introspection-base.h                    \
        introspection-callable.c                \
diff --git a/tests/libpeas/introspection/introspection-abstract.c 
b/tests/libpeas/introspection/introspection-abstract.c
new file mode 100644
index 0000000..0363d0a
--- /dev/null
+++ b/tests/libpeas/introspection/introspection-abstract.c
@@ -0,0 +1,132 @@
+/*
+ * introspection-abstract.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2017 Garrett Regier
+ *
+ * libpeas 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.
+ *
+ * libpeas 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 St, Fifth Floor, Boston, MA  02110-1301  USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "introspection-abstract.h"
+
+typedef struct {
+  gint value;
+} IntrospectionAbstractPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (IntrospectionAbstract,
+                                     introspection_abstract,
+                                     PEAS_TYPE_EXTENSION_BASE)
+
+#define GET_PRIV(o) \
+  (introspection_abstract_get_instance_private (o))
+
+enum {
+  PROP_0,
+  PROP_ABSTRACT_PROPERTY,
+  N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL };
+
+static void
+introspection_abstract_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  IntrospectionAbstract *abstract = INTROSPECTION_ABSTRACT (object);
+  IntrospectionAbstractPrivate *priv = GET_PRIV (abstract);
+
+  switch (prop_id)
+    {
+    case PROP_ABSTRACT_PROPERTY:
+      g_value_set_int (value, priv->value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+introspection_abstract_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  IntrospectionAbstract *abstract = INTROSPECTION_ABSTRACT (object);
+  IntrospectionAbstractPrivate *priv = GET_PRIV (abstract);
+
+  switch (prop_id)
+    {
+    case PROP_ABSTRACT_PROPERTY:
+      priv->value = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+introspection_abstract_class_init (IntrospectionAbstractClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = introspection_abstract_get_property;
+  object_class->set_property = introspection_abstract_set_property;
+
+  properties[PROP_ABSTRACT_PROPERTY] =
+      g_param_spec_int ("abstract-property",
+                        "Abstract Property",
+                        "The IntrospectionAbstract",
+                        G_MININT,
+                        G_MAXINT,
+                        -1,
+                        G_PARAM_READWRITE |
+                        G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+}
+
+static void
+introspection_abstract_init (IntrospectionAbstract *prereq)
+{
+}
+
+gint
+introspection_abstract_get_value (IntrospectionAbstract *abstract)
+{
+  IntrospectionAbstractPrivate *priv = GET_PRIV (abstract);
+
+  g_return_val_if_fail (INTROSPECTION_IS_ABSTRACT (abstract), -1);
+
+  return priv->value;
+}
+
+void
+introspection_abstract_set_value (IntrospectionAbstract *abstract,
+                                  gint                   value)
+{
+  IntrospectionAbstractPrivate *priv = GET_PRIV (abstract);
+
+  g_return_if_fail (INTROSPECTION_IS_ABSTRACT (abstract));
+
+  priv->value = value;
+}
diff --git a/tests/libpeas/introspection/introspection-abstract.h 
b/tests/libpeas/introspection/introspection-abstract.h
new file mode 100644
index 0000000..bfff2c4
--- /dev/null
+++ b/tests/libpeas/introspection/introspection-abstract.h
@@ -0,0 +1,60 @@
+/*
+ * introspection-abstract.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2017 Garrett Regier
+ *
+ * libpeas 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.
+ *
+ * libpeas 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 St, Fifth Floor, Boston, MA  02110-1301  USA.
+ */
+
+#ifndef __INTROSPECTION_ABSTRACT_H__
+#define __INTROSPECTION_ABSTRACT_H__
+
+#include <libpeas/peas.h>
+
+G_BEGIN_DECLS
+
+/*
+ * Type checking and casting macros
+ */
+#define INTROSPECTION_TYPE_ABSTRACT           (introspection_abstract_get_type ())
+#define INTROSPECTION_ABSTRACT(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
INTROSPECTION_TYPE_ABSTRACT, IntrospectionAbstract))
+#define INTROSPECTION_ABSTRACT_CLASS(obj)     (G_TYPE_CHECK_CLASS_CAST ((obj), INTROSPECTION_TYPE_ABSTRACT, 
IntrospectionAbstractClass))
+#define INTROSPECTION_IS_ABSTRACT(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
INTROSPECTION_TYPE_ABSTRACT))
+#define INTROSPECTION_ABSTRACT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), 
INTROSPECTION_TYPE_ABSTRACT, IntrospectionAbstractClass))
+
+typedef struct _IntrospectionAbstract         IntrospectionAbstract;
+typedef struct _IntrospectionAbstractClass    IntrospectionAbstractClass;
+
+struct _IntrospectionAbstract {
+  PeasExtensionBase parent;
+};
+
+struct _IntrospectionAbstractClass {
+  PeasExtensionBaseClass parent_class;
+};
+
+/*
+ * Public methods
+ */
+GType  introspection_abstract_get_type  (void) G_GNUC_CONST;
+
+gint   introspection_abstract_get_value (IntrospectionAbstract *abstract);
+void   introspection_abstract_set_value (IntrospectionAbstract *abstract,
+                                         gint                   value);
+
+G_END_DECLS
+
+#endif /* __INTROSPECTION_ABSTRACT_H__ */
diff --git a/tests/libpeas/plugins/extension-c/Makefile.am b/tests/libpeas/plugins/extension-c/Makefile.am
index 751b68b..24effac 100644
--- a/tests/libpeas/plugins/extension-c/Makefile.am
+++ b/tests/libpeas/plugins/extension-c/Makefile.am
@@ -12,7 +12,9 @@ noinst_LTLIBRARIES = \
        libextension-c-missing-symbol.la
 
 libextension_c_la_SOURCES = \
-       extension-c-plugin.c    \
+       extension-c-abstract.c  \
+       extension-c-abstract.h  \
+       extension-c-plugin.c    \
        extension-c-plugin.h
 
 libextension_c_la_LDFLAGS = $(TEST_PLUGIN_LIBTOOL_FLAGS)
diff --git a/tests/libpeas/plugins/extension-c/extension-c-abstract.c 
b/tests/libpeas/plugins/extension-c/extension-c-abstract.c
new file mode 100644
index 0000000..c6f767e
--- /dev/null
+++ b/tests/libpeas/plugins/extension-c/extension-c-abstract.c
@@ -0,0 +1,55 @@
+/*
+ * extension-c-abstract.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2017 - Garrett Regier
+ *
+ * libpeas 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.
+ *
+ * libpeas 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 St, Fifth Floor, Boston, MA  02110-1301  USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib-object.h>
+
+#include <libpeas/peas.h>
+
+#include "extension-c-abstract.h"
+
+G_DEFINE_DYNAMIC_TYPE (TestingExtensionCAbstract,
+                       testing_extension_c_abstract,
+                       INTROSPECTION_TYPE_ABSTRACT)
+
+static void
+testing_extension_c_abstract_init (TestingExtensionCAbstract *abstract)
+{
+}
+
+static void
+testing_extension_c_abstract_class_init (TestingExtensionCAbstractClass *klass)
+{
+}
+
+static void
+testing_extension_c_abstract_class_finalize (TestingExtensionCAbstractClass *klass)
+{
+}
+
+void
+testing_extension_c_abstract_register (GTypeModule *module)
+{
+  testing_extension_c_abstract_register_type (module);
+}
diff --git a/tests/libpeas/plugins/extension-c/extension-c-abstract.h 
b/tests/libpeas/plugins/extension-c/extension-c-abstract.h
new file mode 100644
index 0000000..57d35c3
--- /dev/null
+++ b/tests/libpeas/plugins/extension-c/extension-c-abstract.h
@@ -0,0 +1,52 @@
+/*
+ * extension-c-abstract.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2017 - Garrett Regier
+ *
+ * libpeas 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.
+ *
+ * libpeas 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 St, Fifth Floor, Boston, MA  02110-1301  USA.
+ */
+
+#ifndef __EXTENSION_C_ABSTRACT_H__
+#define __EXTENSION_C_ABSTRACT_H__
+
+#include "introspection-abstract.h"
+
+G_BEGIN_DECLS
+
+#define TESTING_TYPE_EXTENSION_C_ABSTRACT         (testing_extension_c_abstract_get_type ())
+#define TESTING_EXTENSION_C_ABSTRACT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
TESTING_TYPE_EXTENSION_C_ABSTRACT, TestingExtensionCAbstract))
+#define TESTING_EXTENSION_C_ABSTRACT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), 
TESTING_TYPE_EXTENSION_C_ABSTRACT, TestingExtensionCAbstract))
+#define TESTING_IS_EXTENSION_C_ABSTRACT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
TESTING_TYPE_EXTENSION_C_ABSTRACT))
+#define TESTING_IS_EXTENSION_C_ABSTRACT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), 
TESTING_TYPE_EXTENSION_C_ABSTRACT))
+#define TESTING_EXTENSION_C_ABSTRACT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), 
TESTING_TYPE_EXTENSION_C_ABSTRACT, TestingExtensionCAbstractClass))
+
+typedef struct _TestingExtensionCAbstract         TestingExtensionCAbstract;
+typedef struct _TestingExtensionCAbstractClass    TestingExtensionCAbstractClass;
+
+struct _TestingExtensionCAbstract {
+  IntrospectionAbstract parent_instance;
+};
+
+struct _TestingExtensionCAbstractClass {
+  IntrospectionAbstractClass parent_class;
+};
+
+GType testing_extension_c_abstract_get_type (void) G_GNUC_CONST;
+void  testing_extension_c_abstract_register (GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* __EXTENSION_C_ABSTRACT_H__ */
diff --git a/tests/libpeas/plugins/extension-c/extension-c-plugin.c 
b/tests/libpeas/plugins/extension-c/extension-c-plugin.c
index 3125de8..1e4b283 100644
--- a/tests/libpeas/plugins/extension-c/extension-c-plugin.c
+++ b/tests/libpeas/plugins/extension-c/extension-c-plugin.c
@@ -29,11 +29,13 @@
 
 #include <libpeas/peas.h>
 
+#include "introspection-abstract.h"
 #include "introspection-base.h"
 #include "introspection-callable.h"
 #include "introspection-has-prerequisite.h"
 #include "introspection-prerequisite.h"
 
+#include "extension-c-abstract.h"
 #include "extension-c-plugin.h"
 
 /* Used by the local linkage test */
@@ -175,9 +177,14 @@ testing_extension_c_plugin_class_finalize (TestingExtensionCPluginClass *klass)
 G_MODULE_EXPORT void
 peas_register_types (PeasObjectModule *module)
 {
+  testing_extension_c_abstract_register (G_TYPE_MODULE (module));
   testing_extension_c_plugin_register_type (G_TYPE_MODULE (module));
 
   peas_object_module_register_extension_type (module,
+                                              INTROSPECTION_TYPE_ABSTRACT,
+                                              TESTING_TYPE_EXTENSION_C_ABSTRACT);
+
+  peas_object_module_register_extension_type (module,
                                               INTROSPECTION_TYPE_BASE,
                                               TESTING_TYPE_EXTENSION_C_PLUGIN);
   peas_object_module_register_extension_type (module,
diff --git a/tests/libpeas/plugins/extension-lua/extension-lua51.lua 
b/tests/libpeas/plugins/extension-lua/extension-lua51.lua
index 69df5f7..66ee571 100644
--- a/tests/libpeas/plugins/extension-lua/extension-lua51.lua
+++ b/tests/libpeas/plugins/extension-lua/extension-lua51.lua
@@ -22,6 +22,10 @@ local Introspection = lgi.Introspection
 local Peas = lgi.Peas
 
 
+local ExtensionLuaAbstract =
+    Introspection.Abstract:derive('ExtensionLuaAbstract')
+
+
 local ExtensionLuaPlugin =
     Introspection.Prerequisite:derive('ExtensionLuaPlugin', {
                                       Peas.Activatable,
@@ -100,6 +104,6 @@ assert(pcall(function()
     __STRICT = true
 end))
 
-return { ExtensionLuaPlugin }
+return { ExtensionLuaAbstract, ExtensionLuaPlugin }
 
 -- ex:set ts=4 et sw=4 ai:
diff --git a/tests/libpeas/plugins/extension-python/extension-py.py.in 
b/tests/libpeas/plugins/extension-python/extension-py.py.in
index d5ea6fa..59070c4 100644
--- a/tests/libpeas/plugins/extension-python/extension-py.py.in
+++ b/tests/libpeas/plugins/extension-python/extension-py.py.in
@@ -24,7 +24,11 @@ import threading
 from gi.repository import GObject, Introspection, Peas
 
 
-__all__ = [ 'ExtensionPythonPlugin' ]
+__all__ = [ 'ExtensionPythonAbstract', 'ExtensionPythonPlugin' ]
+
+
+class ExtensionPythonAbstract(Introspection.Abstract):
+    pass
 
 
 class ExtensionPythonPlugin(Introspection.Prerequisite, Peas.Activatable,
diff --git a/tests/libpeas/testing/testing-extension.c b/tests/libpeas/testing/testing-extension.c
index 60e88f4..e00588b 100644
--- a/tests/libpeas/testing/testing-extension.c
+++ b/tests/libpeas/testing/testing-extension.c
@@ -33,6 +33,7 @@
 #include "testing.h"
 #include "testing-extension.h"
 
+#include "introspection-abstract.h"
 #include "introspection-base.h"
 #include "introspection-callable.h"
 #include "introspection-has-prerequisite.h"
@@ -264,6 +265,29 @@ test_extension_get_settings (PeasEngine     *engine,
   g_object_unref (extension);
 }
 
+static void
+test_extension_abstract (PeasEngine     *engine,
+                         PeasPluginInfo *info)
+{
+  PeasExtension *extension;
+  IntrospectionAbstract *abstract;
+
+  g_assert (peas_engine_load_plugin (engine, info));
+
+  extension = peas_engine_create_extension (engine, info,
+                                            INTROSPECTION_TYPE_ABSTRACT,
+                                            "abstract-property", 47,
+                                            NULL);
+
+  abstract = INTROSPECTION_ABSTRACT (extension);
+
+  g_assert_cmpint (introspection_abstract_get_value (abstract), ==, 47);
+  introspection_abstract_set_value (abstract, -22);
+  g_assert_cmpint (introspection_abstract_get_value (abstract), ==, -22);
+
+  g_object_unref (extension);
+}
+
 static gint
 run_in_multiple_threads (GFunc    func,
                          gpointer user_data)
@@ -479,6 +503,30 @@ test_extension_call_multi_args (PeasEngine     *engine,
   g_object_unref (extension);
 }
 
+static void
+test_extension_call_abstract (PeasEngine     *engine,
+                              PeasPluginInfo *info)
+{
+  PeasExtension *extension;
+  gint value = 0;
+
+  g_assert (peas_engine_load_plugin (engine, info));
+
+  extension = peas_engine_create_extension (engine, info,
+                                            INTROSPECTION_TYPE_ABSTRACT,
+                                            "abstract-property", 47,
+                                            NULL);
+
+  g_assert (peas_extension_call (extension, "get_value", &value));
+  g_assert_cmpint (value, ==, 47);
+
+  g_assert (peas_extension_call (extension, "set_value", -22));
+  g_assert (peas_extension_call (extension, "get_value", &value));
+  g_assert_cmpint (value, ==, -22);
+
+  g_object_unref (extension);
+}
+
 #define _EXTENSION_TEST(loader, path, ftest) \
   G_STMT_START { \
     gchar *full_path = g_strdup_printf (EXTENSION_TEST_NAME (%s, "%s"), \
@@ -533,6 +581,8 @@ testing_extension_basic (const gchar *loader_)
   _EXTENSION_TEST (loader, "plugin-info", plugin_info);
   _EXTENSION_TEST (loader, "get-settings", get_settings);
 
+  _EXTENSION_TEST (loader, "abstract", abstract);
+
   _EXTENSION_TEST (loader, "multiple-threads/global-loaders",
                    multiple_threads_global_loaders);
   _EXTENSION_TEST (loader, "multiple-threads/nonglobal-loaders",
@@ -553,6 +603,7 @@ testing_extension_callable (const gchar *loader)
   _EXTENSION_TEST (loader, "call-with-return", call_with_return);
   _EXTENSION_TEST (loader, "call-single-arg", call_single_arg);
   _EXTENSION_TEST (loader, "call-multi-args", call_multi_args);
+  _EXTENSION_TEST (loader, "call-abstract", call_abstract);
 }
 
 void


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