[gobject-introspection] Allow enums and bitfields to have static methods



commit 169b206cbb4b347e4b17854e8f0c62a40404f803
Author: Torsten SchÃnfeld <kaffeetisch gmx de>
Date:   Sat Aug 13 17:28:30 2011 +0200

    Allow enums and bitfields to have static methods
    
    This uses the same backcompat machinery that was introduced for static
    methods for non-class types, so this change does not break users of the
    existing presentations.
    
    New libgirepository API:
    
        g_enum_info_get_n_methods
        g_enum_info_get_method
    
    https://bugzilla.gnome.org/show_bug.cgi?id=656499

 docs/reference/gi-sections.txt         |    2 +
 girepository/gienuminfo.c              |   55 ++++++++++++++++++++++++++++++
 girepository/gienuminfo.h              |    3 ++
 girepository/girnode.c                 |   18 +++++++++-
 girepository/girnode.h                 |    1 +
 girepository/girparser.c               |   17 +++++++++
 girepository/gitypelib-internal.h      |    9 ++++-
 girepository/gitypelib.c               |   22 ++++++++----
 giscanner/ast.py                       |   10 +++++
 giscanner/girparser.py                 |    3 ++
 giscanner/girwriter.py                 |    4 ++
 giscanner/maintransformer.py           |    2 +-
 tests/repository/gitypelibtest.c       |   57 ++++++++++++++++++++++++++++++-
 tests/scanner/Foo-1.0-expected.gir     |   37 +++++++++++++++++++--
 tests/scanner/Regress-1.0-expected.gir |   14 +++++++-
 15 files changed, 237 insertions(+), 17 deletions(-)
---
diff --git a/docs/reference/gi-sections.txt b/docs/reference/gi-sections.txt
index 6d796f0..7db8378 100644
--- a/docs/reference/gi-sections.txt
+++ b/docs/reference/gi-sections.txt
@@ -191,6 +191,8 @@ GIEnumInfo
 GIValueInfo
 g_enum_info_get_n_values
 g_enum_info_get_value
+g_enum_info_get_n_methods
+g_enum_info_get_method
 g_enum_info_get_storage_type
 g_value_info_get_value
 </SECTION>
diff --git a/girepository/gienuminfo.c b/girepository/gienuminfo.c
index 338a46e..90b19a9 100644
--- a/girepository/gienuminfo.c
+++ b/girepository/gienuminfo.c
@@ -112,6 +112,61 @@ g_enum_info_get_value (GIEnumInfo *info,
 }
 
 /**
+ * g_enum_info_get_n_methods:
+ * @info: a #GIEnumInfo
+ *
+ * Obtain the number of methods that this enum type has.
+ *
+ * Returns: number of methods
+ */
+gint
+g_enum_info_get_n_methods (GIEnumInfo *info)
+{
+  GIRealInfo *rinfo = (GIRealInfo *)info;
+  EnumBlob *blob;
+
+  g_return_val_if_fail (info != NULL, 0);
+  g_return_val_if_fail (GI_IS_ENUM_INFO (info), 0);
+
+  blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset];
+
+  return blob->n_methods;
+}
+
+/**
+ * g_enum_info_get_method:
+ * @info: a #GIEnumInfo
+ * @n: index of method to get
+ *
+ * Obtain an enum type method at index @n.
+ *
+ * Returns: (transfer full): the #GIFunctionInfo. Free the struct by calling
+ * g_base_info_unref() when done.
+ */
+GIFunctionInfo *
+g_enum_info_get_method (GIEnumInfo *info,
+			gint        n)
+{
+  gint offset;
+  GIRealInfo *rinfo = (GIRealInfo *)info;
+  Header *header;
+  EnumBlob *blob;
+
+  g_return_val_if_fail (info != NULL, NULL);
+  g_return_val_if_fail (GI_IS_ENUM_INFO (info), NULL);
+
+  header = (Header *)rinfo->typelib->data;
+  blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset];
+
+  offset = rinfo->offset + header->enum_blob_size
+    + blob->n_values * header->value_blob_size
+    + n * header->function_blob_size;
+
+  return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info,
+					rinfo->typelib, offset);
+}
+
+/**
  * g_enum_info_get_storage_type:
  * @info: a #GIEnumInfo
  *
diff --git a/girepository/gienuminfo.h b/girepository/gienuminfo.h
index fc0f3c8..def97f9 100644
--- a/girepository/gienuminfo.h
+++ b/girepository/gienuminfo.h
@@ -40,6 +40,9 @@ G_BEGIN_DECLS
 gint           g_enum_info_get_n_values      (GIEnumInfo  *info);
 GIValueInfo  * g_enum_info_get_value         (GIEnumInfo  *info,
 					      gint         n);
+gint              g_enum_info_get_n_methods     (GIEnumInfo  *info);
+GIFunctionInfo  * g_enum_info_get_method        (GIEnumInfo  *info,
+						 gint         n);
 GITypeTag      g_enum_info_get_storage_type  (GIEnumInfo  *info);
 const gchar *  g_enum_info_get_error_domain  (GIEnumInfo  *info);
 
diff --git a/girepository/girnode.c b/girepository/girnode.c
index 166ca30..fcc8ce5 100644
--- a/girepository/girnode.c
+++ b/girepository/girnode.c
@@ -333,6 +333,10 @@ _g_ir_node_free (GIrNode *node)
 	for (l = enum_->values; l; l = l->next)
 	  _g_ir_node_free ((GIrNode *)l->data);
 	g_list_free (enum_->values);
+
+	for (l = enum_->methods; l; l = l->next)
+	  _g_ir_node_free ((GIrNode *)l->data);
+	g_list_free (enum_->methods);
       }
       break;
 
@@ -467,6 +471,8 @@ _g_ir_node_get_size (GIrNode *node)
 	size = sizeof (EnumBlob);
 	for (l = enum_->values; l; l = l->next)
 	  size += _g_ir_node_get_size ((GIrNode *)l->data);
+	for (l = enum_->methods; l; l = l->next)
+	  size += _g_ir_node_get_size ((GIrNode *)l->data);
       }
       break;
 
@@ -718,6 +724,8 @@ _g_ir_node_get_full_size_internal (GIrNode *parent,
 
 	for (l = enum_->values; l; l = l->next)
 	  size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data);
+	for (l = enum_->methods; l; l = l->next)
+	  size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data);
       }
       break;
 
@@ -2030,7 +2038,7 @@ _g_ir_node_build_typelib (GIrNode         *node,
 	  blob->error_domain = 0;
 
 	blob->n_values = 0;
-	blob->reserved2 = 0;
+	blob->n_methods = 0;
 
 	for (l = enum_->values; l; l = l->next)
 	  {
@@ -2039,6 +2047,14 @@ _g_ir_node_build_typelib (GIrNode         *node,
 	    blob->n_values++;
 	    _g_ir_node_build_typelib (value, node, build, offset, offset2);
 	  }
+
+	for (l = enum_->methods; l; l = l->next)
+	  {
+	    GIrNode *method = (GIrNode *)l->data;
+
+	    blob->n_methods++;
+	    _g_ir_node_build_typelib (method, node, build, offset, offset2);
+	  }
       }
       break;
 
diff --git a/girepository/girnode.h b/girepository/girnode.h
index 6e03821..3286345 100644
--- a/girepository/girnode.h
+++ b/girepository/girnode.h
@@ -288,6 +288,7 @@ struct _GIrNodeEnum
   gchar *error_domain;
 
   GList *values;
+  GList *methods;
 };
 
 struct _GIrNodeBoxed
diff --git a/girepository/girparser.c b/girepository/girparser.c
index c9b7d62..6984e82 100644
--- a/girepository/girparser.c
+++ b/girepository/girparser.c
@@ -805,6 +805,9 @@ start_function (GMarkupParseContext *context,
 	       strcmp (element_name, "method") == 0 ||
 	       strcmp (element_name, "callback") == 0);
       break;
+    case STATE_ENUM:
+      found = strcmp (element_name, "function") == 0;
+      break;
     case STATE_STRUCT_FIELD:
       found = (found || strcmp (element_name, "callback") == 0);
       break;
@@ -925,6 +928,15 @@ start_function (GMarkupParseContext *context,
 	  union_->members = g_list_append (union_->members, function);
 	}
 	break;
+      case G_IR_NODE_ENUM:
+      case G_IR_NODE_FLAGS:
+	{
+	  GIrNodeEnum *enum_;
+
+	  enum_ = (GIrNodeEnum *)CURRENT_NODE (ctx);
+	  enum_->methods = g_list_append (enum_->methods, function);
+	}
+	break;
       default:
 	g_assert_not_reached ();
       }
@@ -3186,6 +3198,9 @@ end_element_handler (GMarkupParseContext *context,
 	      state_switch (ctx, STATE_STRUCT);
 	    else if (CURRENT_NODE (ctx)->type == G_IR_NODE_UNION)
 	      state_switch (ctx, STATE_UNION);
+	    else if (CURRENT_NODE (ctx)->type == G_IR_NODE_ENUM ||
+		     CURRENT_NODE (ctx)->type == G_IR_NODE_FLAGS)
+	      state_switch (ctx, STATE_ENUM);
 	    else
 	      {
 		int line_number, char_number;
@@ -3256,6 +3271,8 @@ end_element_handler (GMarkupParseContext *context,
     case STATE_ENUM:
       if (strcmp ("member", element_name) == 0)
 	break;
+      else if (strcmp ("function", element_name) == 0)
+	break;
       else if (require_one_of_end_elements (context, ctx,
 					    element_name, error, "enumeration",
 					    "bitfield", NULL))
diff --git a/girepository/gitypelib-internal.h b/girepository/gitypelib-internal.h
index 1dde516..f450a2f 100644
--- a/girepository/gitypelib-internal.h
+++ b/girepository/gitypelib-internal.h
@@ -800,8 +800,10 @@ typedef struct {
  * @gtype_init: String naming the symbol which gets the runtime #GType
  * @error_domain: String naming the #GError domain this enum is
  *   associated with
- * @n_values: The lengths of the values arrays.
+ * @n_values: The length of the values array.
+ * @n_methods: The length of the methods array.
  * @values: Describes the enum values.
+ * @methods: Describes the enum methods.
  */
 typedef struct {
   guint16   blob_type;
@@ -817,11 +819,14 @@ typedef struct {
   guint32   gtype_init;
 
   guint16   n_values;
-  guint16   reserved2;
+  guint16   n_methods;
 
   guint32   error_domain;
 
   ValueBlob values[];
+#if 0
+  FunctionBlob methods[];
+#endif
 } EnumBlob;
 
 /**
diff --git a/girepository/gitypelib.c b/girepository/gitypelib.c
index f610d45..2e5b0e1 100644
--- a/girepository/gitypelib.c
+++ b/girepository/gitypelib.c
@@ -1428,6 +1428,7 @@ validate_enum_blob (ValidateContext *ctx,
   GITypelib *typelib = ctx->typelib;
   EnumBlob *blob;
   gint i;
+  guint32 offset2;
 
   if (typelib->len < offset + sizeof (EnumBlob))
     {
@@ -1473,7 +1474,8 @@ validate_enum_blob (ValidateContext *ctx,
     return FALSE;
 
   if (typelib->len < offset + sizeof (EnumBlob) +
-      blob->n_values * sizeof (ValueBlob))
+      blob->n_values * sizeof (ValueBlob) +
+      blob->n_methods * sizeof (FunctionBlob))
     {
       g_set_error (error,
 		   G_TYPELIB_ERROR,
@@ -1482,22 +1484,22 @@ validate_enum_blob (ValidateContext *ctx,
       return FALSE;
     }
 
+  offset2 = offset + sizeof (EnumBlob);
+
   push_context (ctx, get_string_nofail (typelib, blob->name));
 
-  for (i = 0; i < blob->n_values; i++)
+  for (i = 0; i < blob->n_values; i++, offset2 += sizeof (ValueBlob))
     {
       if (!validate_value_blob (typelib,
-				offset + sizeof (EnumBlob) +
-				i * sizeof (ValueBlob),
+				offset2,
 				error))
 	return FALSE;
 
 #if 0
-      v1 = (ValueBlob *)&typelib->data[offset + sizeof (EnumBlob) +
-                                        i * sizeof (ValueBlob)];
+      v1 = (ValueBlob *)&typelib->data[offset2];
       for (j = 0; j < i; j++)
 	{
-	  v2 = (ValueBlob *)&typelib->data[offset + sizeof (EnumBlob) +
+	  v2 = (ValueBlob *)&typelib->data[offset2 +
                                             j * sizeof (ValueBlob)];
 
 	  if (v1->value == v2->value)
@@ -1514,6 +1516,12 @@ validate_enum_blob (ValidateContext *ctx,
 #endif
     }
 
+  for (i = 0; i < blob->n_methods; i++, offset2 += sizeof (FunctionBlob))
+    {
+      if (!validate_function_blob (ctx, offset2, BLOB_TYPE_ENUM, error))
+	return FALSE;
+    }
+
   pop_context (ctx);
 
   return TRUE;
diff --git a/giscanner/ast.py b/giscanner/ast.py
index 1433422..373daa9 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -736,6 +736,11 @@ class Enum(Node, Registered):
         self.members = members
         # Associated error domain name
         self.error_domain = None
+        self.static_methods = []
+
+    def _walk(self, callback, chain):
+        for meth in self.static_methods:
+            meth.walk(callback, chain)
 
 
 class Bitfield(Node, Registered):
@@ -750,6 +755,11 @@ class Bitfield(Node, Registered):
         self.ctype = ctype
         self.c_symbol_prefix = c_symbol_prefix
         self.members = members
+        self.static_methods = []
+
+    def _walk(self, callback, chain):
+        for meth in self.static_methods:
+            meth.walk(callback, chain)
 
 
 class Member(Annotated):
diff --git a/giscanner/girparser.py b/giscanner/girparser.py
index 51ef934..8568fea 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -572,3 +572,6 @@ class GIRParser(object):
             return
         for member in self._find_children(node, _corens('member')):
             members.append(self._parse_member(member))
+        for func_node in self._find_children(node, _corens('function')):
+            func = self._parse_function_common(func_node, ast.Function)
+            obj.static_methods.append(func)
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
index f993729..f1e150d 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -337,6 +337,8 @@ and/or use gtk-doc annotations. ''')
             self._write_generic(enum)
             for member in enum.members:
                 self._write_member(member)
+            for method in sorted(enum.static_methods):
+                self._write_static_method(method)
 
     def _write_bitfield(self, bitfield):
         attrs = [('name', bitfield.name)]
@@ -348,6 +350,8 @@ and/or use gtk-doc annotations. ''')
             self._write_generic(bitfield)
             for member in bitfield.members:
                 self._write_member(member)
+            for method in sorted(bitfield.static_methods):
+                self._write_static_method(method)
 
     def _write_member(self, member):
         attrs = [('name', member.name),
diff --git a/giscanner/maintransformer.py b/giscanner/maintransformer.py
index 8c07fda..7900419 100644
--- a/giscanner/maintransformer.py
+++ b/giscanner/maintransformer.py
@@ -1006,7 +1006,7 @@ method or constructor of some type."""
             node.static_methods.append(func)
             return True
         elif isinstance(node, (ast.Interface, ast.Record, ast.Union,
-                               ast.Boxed)):
+                               ast.Boxed, ast.Enum, ast.Bitfield)):
             # prior to the introduction of this part of the code, only
             # ast.Class could have static methods.  so for backwards
             # compatibility, instead of removing the func from the namespace,
diff --git a/tests/repository/gitypelibtest.c b/tests/repository/gitypelibtest.c
index 9e9b8d2..2896846 100644
--- a/tests/repository/gitypelibtest.c
+++ b/tests/repository/gitypelibtest.c
@@ -23,10 +23,8 @@ test_enum_and_flags_cidentifier(GIRepository *repo)
 
     for (i = 0; i < n_infos; i++) {
         GIBaseInfo *info;
-        GIInfoType type;
 
         info = g_irepository_get_info (repo, "GIMarshallingTests", i);
-        type = g_base_info_get_type (info);
 
         /* both GI_INFO_TYPE_ENUM and GI_INFO_TYPE_FLAGS use GIEnumInfo */
         if (GI_IS_ENUM_INFO (info)) {
@@ -54,6 +52,60 @@ test_enum_and_flags_cidentifier(GIRepository *repo)
     }
 }
 
+static void
+_check_enum_methods (GIBaseInfo *info, const gchar *name, const gchar *prefix)
+{
+    gint n_methods, i;
+
+    n_methods = g_enum_info_get_n_methods ((GIEnumInfo *) info);
+    if (n_methods <= 0)
+        g_error ("%s should have methods", name);
+
+    for (i = 0; i < n_methods; i += n_methods-1) {
+        GIBaseInfo *function_info;
+        GIFunctionInfoFlags flags;
+        const gchar *symbol;
+        function_info = g_enum_info_get_method ((GIEnumInfo *) info, i);
+        if (!function_info)
+            g_error ("Could not find %s method nr. %d", name, i+1);
+        flags = g_function_info_get_flags ((GIFunctionInfo *) function_info);
+        if (flags != 0)
+            g_error ("%s methods should be static", name);
+        symbol = g_function_info_get_symbol ((GIFunctionInfo *) function_info);
+        if (!symbol || !g_str_has_prefix (symbol, prefix))
+            g_error ("Could not find valid function symbol");
+        g_base_info_unref (function_info);
+    }
+}
+
+static void
+test_enum_and_flags_static_methods(GIRepository *repo)
+{
+    GITypelib *ret;
+    GError *error = NULL;
+    GIBaseInfo *enum_info;
+
+    ret = g_irepository_require (repo, "GIMarshallingTests", NULL, 0, &error);
+    if (!ret)
+        g_error ("%s", error->message);
+
+    enum_info = g_irepository_find_by_name (repo, "GIMarshallingTests", "GEnum");
+    if (!enum_info)
+        g_error ("Could not find GIMarshallingTests.GEnum");
+    _check_enum_methods (enum_info,
+                         "GIMarshallingTests.GEnum",
+                         "gi_marshalling_tests_genum_");
+    g_base_info_unref (enum_info);
+
+    enum_info = g_irepository_find_by_name (repo, "GIMarshallingTests", "Flags");
+    if (!enum_info)
+        g_error ("Could not find GIMarshallingTests.Flags");
+    _check_enum_methods (enum_info,
+                         "GIMarshallingTests.Flags",
+                         "gi_marshalling_tests_flags_");
+    g_base_info_unref (enum_info);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -65,6 +117,7 @@ main(int argc, char **argv)
 
     /* do tests */
     test_enum_and_flags_cidentifier (repo);
+    test_enum_and_flags_static_methods (repo);
 
     exit(0);
 }
diff --git a/tests/scanner/Foo-1.0-expected.gir b/tests/scanner/Foo-1.0-expected.gir
index f18ed56..ffcf216 100644
--- a/tests/scanner/Foo-1.0-expected.gir
+++ b/tests/scanner/Foo-1.0-expected.gir
@@ -186,6 +186,26 @@ and/or use gtk-doc annotations.  -->
               value="2"
               c:identifier="FOO_ENUM_DELTA"
               glib:nick="delta"/>
+      <function name="method" c:identifier="foo_enum_type_method">
+        <return-value transfer-ownership="none">
+          <type name="gint" c:type="int"/>
+        </return-value>
+        <parameters>
+          <parameter name="foo_enum" transfer-ownership="none">
+            <type name="EnumType" c:type="FooEnumType"/>
+          </parameter>
+        </parameters>
+      </function>
+      <function name="returnv" c:identifier="foo_enum_type_returnv">
+        <return-value transfer-ownership="none">
+          <type name="EnumType" c:type="FooEnumType"/>
+        </return-value>
+        <parameters>
+          <parameter name="x" transfer-ownership="none">
+            <type name="gint" c:type="int"/>
+          </parameter>
+        </parameters>
+      </function>
     </enumeration>
     <enumeration name="Error"
                  glib:type-name="FooError"
@@ -204,6 +224,11 @@ and/or use gtk-doc annotations.  -->
               value="2"
               c:identifier="FOO_ERROR_UGLY"
               glib:nick="ugly"/>
+      <function name="quark" c:identifier="foo_error_quark">
+        <return-value transfer-ownership="none">
+          <type name="GLib.Quark" c:type="GQuark"/>
+        </return-value>
+      </function>
     </enumeration>
     <union name="Event" c:type="FooEvent">
       <field name="type" writable="1">
@@ -954,7 +979,9 @@ exposed to language bindings.</doc>
         </parameter>
       </parameters>
     </function>
-    <function name="enum_type_method" c:identifier="foo_enum_type_method">
+    <function name="enum_type_method"
+              c:identifier="foo_enum_type_method"
+              moved-to="EnumType.method">
       <return-value transfer-ownership="none">
         <type name="gint" c:type="int"/>
       </return-value>
@@ -964,7 +991,9 @@ exposed to language bindings.</doc>
         </parameter>
       </parameters>
     </function>
-    <function name="enum_type_returnv" c:identifier="foo_enum_type_returnv">
+    <function name="enum_type_returnv"
+              c:identifier="foo_enum_type_returnv"
+              moved-to="EnumType.returnv">
       <return-value transfer-ownership="none">
         <type name="EnumType" c:type="FooEnumType"/>
       </return-value>
@@ -974,7 +1003,9 @@ exposed to language bindings.</doc>
         </parameter>
       </parameters>
     </function>
-    <function name="error_quark" c:identifier="foo_error_quark">
+    <function name="error_quark"
+              c:identifier="foo_error_quark"
+              moved-to="Error.quark">
       <return-value transfer-ownership="none">
         <type name="GLib.Quark" c:type="GQuark"/>
       </return-value>
diff --git a/tests/scanner/Regress-1.0-expected.gir b/tests/scanner/Regress-1.0-expected.gir
index 80b0c68..ce5af5c 100644
--- a/tests/scanner/Regress-1.0-expected.gir
+++ b/tests/scanner/Regress-1.0-expected.gir
@@ -188,6 +188,16 @@ use it should be.</doc>
               value="48"
               c:identifier="REGRESS_TEST_VALUE4"
               glib:nick="value4"/>
+      <function name="param" c:identifier="regress_test_enum_param">
+        <return-value transfer-ownership="none">
+          <type name="utf8" c:type="gchar*"/>
+        </return-value>
+        <parameters>
+          <parameter name="e" transfer-ownership="none">
+            <type name="TestEnum" c:type="RegressTestEnum"/>
+          </parameter>
+        </parameters>
+      </function>
     </enumeration>
     <enumeration name="TestEnumNoGEnum" c:type="RegressTestEnumNoGEnum">
       <member name="evalue1" value="0" c:identifier="REGRESS_TEST_EVALUE1"/>
@@ -1887,7 +1897,9 @@ call and can be released on return.</doc>
         </parameter>
       </parameters>
     </function>
-    <function name="test_enum_param" c:identifier="regress_test_enum_param">
+    <function name="test_enum_param"
+              c:identifier="regress_test_enum_param"
+              moved-to="TestEnum.param">
       <return-value transfer-ownership="none">
         <type name="utf8" c:type="gchar*"/>
       </return-value>



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