[perl-Glib-Object-Introspection] Register unregistered enums



commit f463b9eeca4d6edb862e1ac3edb1b97e4a7bd80f
Author: Torsten Schönfeld <kaffeetisch gmx de>
Date:   Mon Dec 2 22:21:41 2013 +0100

    Register unregistered enums
    
    Using the introspection information, create a custom GType for unregistered
    enums/flags and register it with perl-Glib.
    
    Relevant for, e.g., GSpawnFlags and VtePtyFlags.

 GObjectIntrospection.xs        |   20 ++++++++++++-
 MANIFEST                       |    1 +
 NEWS                           |    6 ++++
 gperl-i11n-enums.c             |   51 ++++++++++++++++++++++++++++++++++
 gperl-i11n-info.c              |   59 +++++++++++++++++++++++++++++-----------
 gperl-i11n-marshal-interface.c |   16 +++++++++++
 t/enums.t                      |   10 ++++++-
 7 files changed, 144 insertions(+), 19 deletions(-)
---
diff --git a/GObjectIntrospection.xs b/GObjectIntrospection.xs
index 69f60f1..4ba39da 100644
--- a/GObjectIntrospection.xs
+++ b/GObjectIntrospection.xs
@@ -172,7 +172,11 @@ static GIFieldInfo * get_field_info (GIBaseInfo *info,
                                      const gchar *field_name);
 static GISignalInfo * get_signal_info (GIBaseInfo *container_info,
                                        const gchar *signal_name);
+
+static gchar * sythesize_gtype_name (GIBaseInfo *info);
+static gchar * sythesize_prefixed_gtype_name (GIBaseInfo *info);
 static GType get_gtype (GIRegisteredTypeInfo *info);
+
 static const gchar * get_package_for_basename (const gchar *basename);
 static gboolean is_forbidden_sub_name (const gchar *name);
 
@@ -231,6 +235,9 @@ static gsize size_of_type_tag (GITypeTag type_tag);
 static gsize size_of_interface (GITypeInfo *type_info);
 static gsize size_of_type_info (GITypeInfo *type_info);
 
+/* enums/flags */
+static GType register_unregistered_enum (GIEnumInfo *info);
+
 /* fields */
 static void store_fields (HV *fields, GIBaseInfo *info, GIInfoType info_type);
 static SV * get_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer);
@@ -273,6 +280,7 @@ static void call_carp_carp (const char *msg);
 
 #include "gperl-i11n-callback.c"
 #include "gperl-i11n-croak.c"
+#include "gperl-i11n-enums.c"
 #include "gperl-i11n-field.c"
 #include "gperl-i11n-gvalue.c"
 #include "gperl-i11n-info.c"
@@ -410,8 +418,16 @@ _register_types (class, namespace, package)
                               namespace, name);
                }
                if (type == G_TYPE_NONE) {
-                       g_base_info_unref ((GIBaseInfo *) info);
-                       continue;
+                       /* Try registering unregistered enums/flags. */
+                       if (info_type == GI_INFO_TYPE_ENUM || info_type == GI_INFO_TYPE_FLAGS) {
+                               type = register_unregistered_enum (info);
+                       }
+                       /* If there is still no GType, stop this iteration and
+                        * go to the next item. */
+                       if (!type || type == G_TYPE_NONE) {
+                               g_base_info_unref ((GIBaseInfo *) info);
+                               continue;
+                       }
                }
 
                full_package = g_strconcat (package, "::", name, NULL);
diff --git a/MANIFEST b/MANIFEST
index e3a7515..a21311f 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,6 +1,7 @@
 GObjectIntrospection.xs
 gperl-i11n-callback.c
 gperl-i11n-croak.c
+gperl-i11n-enums.c
 gperl-i11n-field.c
 gperl-i11n-gvalue.c
 gperl-i11n-info.c
diff --git a/NEWS b/NEWS
index ce1a142..2268c74 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+Overview of changes in Glib::Object::Introspection <next>
+========================================================
+
+* Register unregistered enums so that, e.g., GSpawnFlags and VtePtyFlags become
+  usable.
+
 Overview of changes in Glib::Object::Introspection 0.016
 ========================================================
 
diff --git a/gperl-i11n-enums.c b/gperl-i11n-enums.c
new file mode 100644
index 0000000..0a471ef
--- /dev/null
+++ b/gperl-i11n-enums.c
@@ -0,0 +1,51 @@
+/* -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; -*- */
+
+#define FILL_VALUES(values) \
+       for (i = 0; i < n_values; i++) { \
+               GIValueInfo *value_info = g_enum_info_get_value (info, i); \
+               (values)[i].value = g_value_info_get_value (value_info); \
+               /* FIXME: Can we assume that the strings will stick around long enough? */ \
+               (values)[i].value_nick = g_base_info_get_name (value_info); \
+               (values)[i].value_name = g_base_info_get_attribute (value_info, "c:identifier"); \
+               if (!(values)[i].value_name) \
+                       (values)[i].value_name = (values)[i].value_nick; \
+               g_base_info_unref (value_info); \
+       }
+
+static GType
+register_unregistered_enum (GIEnumInfo *info)
+{
+       GType gtype = G_TYPE_NONE;
+       gchar *full_name;
+       GIInfoType info_type;
+       void *values;
+
+       /* Abort if there already is a GType under this name. */
+       full_name = sythesize_prefixed_gtype_name (info);
+       if (g_type_from_name (full_name)) {
+               g_free (full_name);
+               return gtype;
+       }
+
+       /* We have to leak enum_values as g_enum_register_static and
+        * g_flags_register_static assume that what we pass in will be valid
+        * throughout the lifetime of the program. */
+       gint i, n_values = g_enum_info_get_n_values (info);
+       if (info_type ==  GI_INFO_TYPE_ENUM) {
+               values = g_new0 (GEnumValue, n_values+1); /* zero-terminated */
+               FILL_VALUES ((GEnumValue *) values);
+       } else {
+               values = g_new0 (GFlagsValue, n_values+1); /* zero-terminated */
+               FILL_VALUES ((GFlagsValue *) values);
+       }
+
+       info_type = g_base_info_get_type(info);
+       if (info_type ==  GI_INFO_TYPE_ENUM) {
+               gtype = g_enum_register_static (full_name, (GEnumValue *) values);
+       } else {
+               gtype = g_flags_register_static (full_name, (GFlagsValue *) values);
+       }
+
+       g_free (full_name);
+       return gtype;
+}
diff --git a/gperl-i11n-info.c b/gperl-i11n-info.c
index 2a4926a..922a8f4 100644
--- a/gperl-i11n-info.c
+++ b/gperl-i11n-info.c
@@ -164,31 +164,58 @@ get_signal_info (GIBaseInfo *container_info, const gchar *signal_name)
        return NULL;
 }
 
+/* Caller owns return value. */
+static gchar *
+sythesize_gtype_name (GIBaseInfo *info)
+{
+       const gchar *namespace = g_base_info_get_namespace (info);
+       const gchar *name = g_base_info_get_name (info);
+       if (0 == strncmp (namespace, "GObject", 8) ||
+           0 == strncmp (namespace, "GLib", 4))
+       {
+               namespace = "G";
+       }
+       return g_strconcat (namespace, name, NULL);
+}
+
+/* Caller owns return value. */
+static gchar *
+sythesize_prefixed_gtype_name (GIBaseInfo *info)
+{
+       const gchar *namespace = g_base_info_get_namespace (info);
+       const gchar *name = g_base_info_get_name (info);
+       if (0 == strncmp (namespace, "GObject", 8) ||
+           0 == strncmp (namespace, "GLib", 4))
+       {
+               namespace = "G";
+       }
+       return g_strconcat ("GPerlI11n", namespace, name, NULL);
+}
+
 static GType
 get_gtype (GIRegisteredTypeInfo *info)
 {
        GType gtype = g_registered_type_info_get_g_type (info);
-       if (gtype == G_TYPE_NONE) {
-               /* Fall back to the registered type name, and if that doesn't
-                * work either, construct the full name and try that. */
+       /* Fall back to the registered type name, and if that doesn't work
+        * either, construct the full name and the prefixed full name and try
+        * them. */
+       if (!gtype || gtype == G_TYPE_NONE) {
                const gchar *type_name = g_registered_type_info_get_type_name (info);
                if (type_name) {
                        gtype = g_type_from_name (type_name);
-                       return gtype ? gtype : G_TYPE_NONE;
-               } else {
-                       gchar *full_name;
-                       const gchar *namespace = g_base_info_get_namespace (info);
-                       const gchar *name = g_base_info_get_name (info);
-                       if (0 == strncmp (namespace, "GObject", 8)) {
-                               namespace = "G";
-                       }
-                       full_name = g_strconcat (namespace, name, NULL);
-                       gtype = g_type_from_name (full_name);
-                       g_free (full_name);
-                       return gtype ? gtype : G_TYPE_NONE;
                }
        }
-       return gtype;
+       if (!gtype || gtype == G_TYPE_NONE) {
+               gchar *full_name = sythesize_gtype_name (info);
+               gtype = g_type_from_name (full_name);
+               g_free (full_name);
+       }
+       if (!gtype || gtype == G_TYPE_NONE) {
+               gchar *full_name = sythesize_prefixed_gtype_name (info);
+               gtype = g_type_from_name (full_name);
+               g_free (full_name);
+       }
+       return gtype ? gtype : G_TYPE_NONE;
 }
 
 static const gchar *
diff --git a/gperl-i11n-marshal-interface.c b/gperl-i11n-marshal-interface.c
index 517bfee..614841f 100644
--- a/gperl-i11n-marshal-interface.c
+++ b/gperl-i11n-marshal-interface.c
@@ -227,6 +227,10 @@ sv_to_interface (GIArgInfo * arg_info,
            case GI_INFO_TYPE_ENUM:
            {
                GType type = get_gtype ((GIRegisteredTypeInfo *) interface);
+               if (G_TYPE_NONE == type) {
+                       ccroak ("Could not handle unknown enum type %s",
+                               g_base_info_get_name (interface));
+               }
                /* FIXME: Check storage type? */
                arg->v_long = gperl_convert_enum (type, sv);
                break;
@@ -235,6 +239,10 @@ sv_to_interface (GIArgInfo * arg_info,
            case GI_INFO_TYPE_FLAGS:
            {
                GType type = get_gtype ((GIRegisteredTypeInfo *) interface);
+               if (G_TYPE_NONE == type) {
+                       ccroak ("Could not handle unknown flags type %s",
+                               g_base_info_get_name (interface));
+               }
                /* FIXME: Check storage type? */
                arg->v_long = gperl_convert_flags (type, sv);
                break;
@@ -305,6 +313,10 @@ interface_to_sv (GITypeInfo* info, GIArgument *arg, gboolean own, GPerlI11nInvoc
            case GI_INFO_TYPE_ENUM:
            {
                GType type = get_gtype ((GIRegisteredTypeInfo *) interface);
+               if (G_TYPE_NONE == type) {
+                       ccroak ("Could not handle unknown enum type %s",
+                               g_base_info_get_name (interface));
+               }
                /* FIXME: Is it right to just use v_long here? */
                sv = gperl_convert_back_enum (type, arg->v_long);
                break;
@@ -313,6 +325,10 @@ interface_to_sv (GITypeInfo* info, GIArgument *arg, gboolean own, GPerlI11nInvoc
            case GI_INFO_TYPE_FLAGS:
            {
                GType type = get_gtype ((GIRegisteredTypeInfo *) interface);
+               if (G_TYPE_NONE == type) {
+                       ccroak ("Could not handle unknown flags type %s",
+                               g_base_info_get_name (interface));
+               }
                /* FIXME: Is it right to just use v_long here? */
                sv = gperl_convert_back_flags (type, arg->v_long);
                break;
diff --git a/t/enums.t b/t/enums.t
index b412800..e5cecdd 100644
--- a/t/enums.t
+++ b/t/enums.t
@@ -5,8 +5,16 @@ BEGIN { require './t/inc/setup.pl' };
 use strict;
 use warnings;
 
-plan tests => 3;
+plan tests => 4;
 
 is (Regress::test_enum_param ('value1'), 'value1');
 is (Regress::test_unsigned_enum_param ('value2'), 'value2');
 ok (Regress::global_get_flags_out () == ['flag1', 'flag3']);
+
+SKIP: {
+  skip 'non-GType flags tests', 1
+    unless (check_gi_version (0, 10, 3));
+
+  GI::no_type_flags_in ([qw/value2/]);
+  is (GI::no_type_flags_returnv (), [qw/value2/]);
+}


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