[gobject-introspection] Allow attributes on parameters and return values



commit 11cfe386c37ced44a8e3efb5556bde3a43a11171
Author: David Zeuthen <davidz redhat com>
Date:   Tue Jun 15 11:01:37 2010 -0400

    Allow attributes on parameters and return values
    
    Any annotation where the key has a dot in the name will go into the
    attribute list. For example
    
      * @arg: (foo.bar baz): some arg
    
    the parameter @arg will get the attribute with key foo.bar and value
    baz. This also works for.
    
      * Returns: (foo.bar2 baz2): the return value
    
    Also add tests for this new feature.
    
    See https://bugzilla.gnome.org/show_bug.cgi?id=571548
    
    Signed-off-by: David Zeuthen <davidz redhat com>

 docs/reference/gi-sections.txt             |    2 +
 girepository/gibaseinfo.c                  |   24 +++++++--
 girepository/gicallableinfo.c              |   76 ++++++++++++++++++++++++++++
 girepository/gicallableinfo.h              |    6 ++
 girepository/girnode.c                     |   16 ++++++
 girepository/girparser.c                   |    9 +++-
 girepository/girwriter.c                   |   21 +++++++-
 girepository/gitypelib-internal.h          |    4 ++
 giscanner/annotationparser.py              |    6 ++
 giscanner/girwriter.py                     |    2 +
 tests/scanner/annotation-1.0-expected.gir  |   34 ++++++++++++
 tests/scanner/annotation-1.0-expected.tgir |   33 ++++++++++++
 tests/scanner/annotation.c                 |   37 +++++++++++++
 tests/scanner/annotation.h                 |    3 +
 14 files changed, 265 insertions(+), 8 deletions(-)
---
diff --git a/docs/reference/gi-sections.txt b/docs/reference/gi-sections.txt
index 6b1cd07..261afd8 100644
--- a/docs/reference/gi-sections.txt
+++ b/docs/reference/gi-sections.txt
@@ -74,6 +74,8 @@ GICallableInfo
 g_callable_info_get_return_type
 g_callable_info_get_caller_owns
 g_callable_info_may_return_null
+g_callable_info_get_return_attribute
+g_callable_info_iterate_return_attributes
 g_callable_info_get_n_args
 g_callable_info_get_arg
 g_callable_info_load_arg
diff --git a/girepository/gibaseinfo.c b/girepository/gibaseinfo.c
index c8444a5..81b936d 100644
--- a/girepository/gibaseinfo.c
+++ b/girepository/gibaseinfo.c
@@ -475,7 +475,7 @@ g_base_info_get_attribute (GIBaseInfo   *info,
 
 static int
 cmp_attribute (const void *av,
-                const void *bv)
+               const void *bv)
 {
   const AttributeBlob *a = av;
   const AttributeBlob *b = bv;
@@ -488,13 +488,25 @@ cmp_attribute (const void *av,
     return 1;
 }
 
-static AttributeBlob *
-find_first_attribute (GIRealInfo *rinfo)
+/*
+ * _attribute_blob_find_first:
+ * @GIBaseInfo: A #GIBaseInfo.
+ * @blob_offset: The offset for the blob to find the first attribute for.
+ *
+ * Searches for the first #AttributeBlob for @blob_offset and returns
+ * it if found.
+ *
+ * Returns: A pointer to #AttributeBlob or %NULL if not found.
+ */
+AttributeBlob *
+_attribute_blob_find_first (GIBaseInfo *info,
+                            guint32     blob_offset)
 {
+  GIRealInfo *rinfo = (GIRealInfo *) info;
   Header *header = (Header *)rinfo->typelib->data;
   AttributeBlob blob, *first, *res, *previous;
 
-  blob.offset = rinfo->offset;
+  blob.offset = blob_offset;
 
   first = (AttributeBlob *) &rinfo->typelib->data[header->attributes];
 
@@ -505,7 +517,7 @@ find_first_attribute (GIRealInfo *rinfo)
     return NULL;
 
   previous = res - 1;
-  while (previous >= first && previous->offset == rinfo->offset)
+  while (previous >= first && previous->offset == blob_offset)
     {
       res = previous;
       previous = res - 1;
@@ -563,7 +575,7 @@ g_base_info_iterate_attributes (GIBaseInfo      *info,
   if (iterator->data != NULL)
     next = (AttributeBlob *) iterator->data;
   else
-    next = find_first_attribute (rinfo);
+    next = _attribute_blob_find_first (info, rinfo->offset);
 
   if (next == NULL || next->offset != rinfo->offset || next >= after)
     return FALSE;
diff --git a/girepository/gicallableinfo.c b/girepository/gicallableinfo.c
index 6097cb4..6b79e62 100644
--- a/girepository/gicallableinfo.c
+++ b/girepository/gicallableinfo.c
@@ -19,6 +19,8 @@
  * Boston, MA 02111-1307, USA.
  */
 
+#include <stdlib.h>
+
 #include <glib.h>
 
 #include <girepository.h>
@@ -259,3 +261,77 @@ g_callable_info_load_arg (GICallableInfo *info,
   _g_info_init ((GIRealInfo*)arg, GI_INFO_TYPE_ARG, rinfo->repository, (GIBaseInfo*)info, rinfo->typelib,
                 offset + header->signature_blob_size + n * header->arg_blob_size);
 }
+
+/**
+ * g_callable_info_get_return_attribute:
+ * @info: a #GICallableInfo
+ * @name: a freeform string naming an attribute
+ *
+ * Retrieve an arbitrary attribute associated with the return value.
+ *
+ * Returns: The value of the attribute, or %NULL if no such attribute exists
+ */
+const gchar *
+g_callable_info_get_return_attribute (GICallableInfo  *info,
+                                      const gchar     *name)
+{
+  GIAttributeIter iter = { 0, };
+  gchar *curname, *curvalue;
+  while (g_callable_info_iterate_return_attributes (info, &iter, &curname, &curvalue))
+    {
+      if (g_strcmp0 (name, curname) == 0)
+        return (const gchar*) curvalue;
+    }
+
+  return NULL;
+}
+
+/**
+ * g_callable_info_iterate_return_attributes:
+ * @info: a #GICallableInfo
+ * @iterator: a #GIAttributeIter structure, must be initialized; see below
+ * @name: (out) (transfer none): Returned name, must not be freed
+ * @value: (out) (transfer none): Returned name, must not be freed
+ *
+ * Iterate over all attributes associated with the return value.  The
+ * iterator structure is typically stack allocated, and must have its
+ * first member initialized to %NULL.
+ *
+ * Both the @name and @value should be treated as constants
+ * and must not be freed.
+ *
+ * See g_base_info_iterate_attributes() for an example of how to use a
+ * similar API.
+ *
+ * Returns: %TRUE if there are more attributes
+ */
+gboolean
+g_callable_info_iterate_return_attributes (GICallableInfo  *info,
+                                           GIAttributeIter *iterator,
+                                           char           **name,
+                                           char          **value)
+{
+  GIRealInfo *rinfo = (GIRealInfo *)info;
+  Header *header = (Header *)rinfo->typelib->data;
+  AttributeBlob *next, *after;
+  guint32 blob_offset;
+
+  after = (AttributeBlob *) &rinfo->typelib->data[header->attributes +
+                                                  header->n_attributes * header->attribute_blob_size];
+
+  blob_offset = signature_offset (info);
+
+  if (iterator->data != NULL)
+    next = (AttributeBlob *) iterator->data;
+  else
+    next = _attribute_blob_find_first (info, blob_offset);
+
+  if (next == NULL || next->offset != blob_offset || next >= after)
+    return FALSE;
+
+  *name = (gchar*) g_typelib_get_string (rinfo->typelib, next->name);
+  *value = (gchar*) g_typelib_get_string (rinfo->typelib, next->value);
+  iterator->data = next + 1;
+
+  return TRUE;
+}
diff --git a/girepository/gicallableinfo.h b/girepository/gicallableinfo.h
index 479baa2..54d2cac 100644
--- a/girepository/gicallableinfo.h
+++ b/girepository/gicallableinfo.h
@@ -39,6 +39,12 @@ G_BEGIN_DECLS
 GITypeInfo *           g_callable_info_get_return_type (GICallableInfo *info);
 void                   g_callable_info_load_return_type (GICallableInfo *info,
                                                          GITypeInfo     *type);
+const gchar *          g_callable_info_get_return_attribute (GICallableInfo *info,
+                                                             const gchar    *name);
+gboolean               g_callable_info_iterate_return_attributes (GICallableInfo  *info,
+                                                                  GIAttributeIter *iterator,
+                                                                  char           **name,
+                                                                  char          **value);
 GITransfer             g_callable_info_get_caller_owns (GICallableInfo *info);
 gboolean               g_callable_info_may_return_null (GICallableInfo *info);
 gint                   g_callable_info_get_n_args      (GICallableInfo *info);
diff --git a/girepository/girnode.c b/girepository/girnode.c
index fbdcdf9..576ac10 100644
--- a/girepository/girnode.c
+++ b/girepository/girnode.c
@@ -1678,6 +1678,14 @@ g_ir_node_build_typelib (GIrNode         *node,
 	blob->symbol = write_string (function->symbol, strings, data, offset2);
 	blob->signature = signature;
 
+        /* function->result is special since it doesn't appear in the serialized format but
+         * we do want the attributes for it to appear
+         */
+        build->nodes_with_attributes = g_list_prepend (build->nodes_with_attributes, function->result);
+        build->n_attributes += g_hash_table_size (((GIrNode *) function->result)->attributes);
+        g_assert (((GIrNode *) function->result)->offset == 0);
+        ((GIrNode *) function->result)->offset = signature;
+
 	g_debug ("building function '%s'", function->symbol);
 
         g_ir_node_build_typelib ((GIrNode *)function->result->type,
@@ -1770,6 +1778,14 @@ g_ir_node_build_typelib (GIrNode         *node,
 	blob->name = write_string (node->name, strings, data, offset2);
 	blob->signature = signature;
 
+        /* signal->result is special since it doesn't appear in the serialized format but
+         * we do want the attributes for it to appear
+         */
+        build->nodes_with_attributes = g_list_prepend (build->nodes_with_attributes, signal->result);
+        build->n_attributes += g_hash_table_size (((GIrNode *) signal->result)->attributes);
+        g_assert (((GIrNode *) signal->result)->offset == 0);
+        ((GIrNode *) signal->result)->offset = signature;
+
         g_ir_node_build_typelib ((GIrNode *)signal->result->type,
 				 node, build, &signature, offset2);
 
diff --git a/girepository/girparser.c b/girepository/girparser.c
index 2713bb4..5efe7fd 100644
--- a/girepository/girparser.c
+++ b/girepository/girparser.c
@@ -1990,7 +1990,14 @@ start_attribute (GMarkupParseContext *context,
 
   curnode = CURRENT_NODE (ctx);
 
-  g_hash_table_insert (curnode->attributes, g_strdup (name), g_strdup (value));
+  if (ctx->current_typed && ctx->current_typed->type == G_IR_NODE_PARAM)
+    {
+      g_hash_table_insert (ctx->current_typed->attributes, g_strdup (name), g_strdup (value));
+    }
+  else
+    {
+      g_hash_table_insert (curnode->attributes, g_strdup (name), g_strdup (value));
+    }
 
   return TRUE;
 }
diff --git a/girepository/girwriter.c b/girepository/girwriter.c
index b123a13..19862b0 100644
--- a/girepository/girwriter.c
+++ b/girepository/girwriter.c
@@ -358,7 +358,7 @@ write_type_info (const gchar *namespace,
 
 static void
 write_attributes (Xml *file,
-                   GIBaseInfo *info)
+                  GIBaseInfo *info)
 {
   GIAttributeIter iter = { 0, };
   char *name, *value;
@@ -372,6 +372,21 @@ write_attributes (Xml *file,
 }
 
 static void
+write_return_value_attributes (Xml *file,
+                               GICallableInfo *info)
+{
+  GIAttributeIter iter = { 0, };
+  char *name, *value;
+
+  while (g_callable_info_iterate_return_attributes (info, &iter, &name, &value))
+    {
+      xml_start_element (file, "attribute");
+      xml_printf (file, " name=\"%s\" value=\"%s\"", name, value);
+      xml_end_element (file, "attribute");
+    }
+}
+
+static void
 write_constant_value (const gchar *namespace,
 		      GITypeInfo *info,
 		      GArgument *argument,
@@ -467,6 +482,8 @@ write_callable_info (const gchar    *namespace,
   if (g_callable_info_may_return_null (info))
     xml_printf (file, " allow-none=\"1\"");
 
+  write_return_value_attributes (file, info);
+
   write_type_info (namespace, type, file);
 
   xml_end_element (file, "return-value");
@@ -528,6 +545,8 @@ write_callable_info (const gchar    *namespace,
       if (g_arg_info_get_destroy (arg) >= 0)
         xml_printf (file, " destroy=\"%d\"", g_arg_info_get_destroy (arg));
 
+      write_attributes (file, (GIBaseInfo*) arg);
+
       type = g_arg_info_get_type (arg);
       write_type_info (namespace, type, file);
 
diff --git a/girepository/gitypelib-internal.h b/girepository/gitypelib-internal.h
index 90113ac..d4577ac 100644
--- a/girepository/gitypelib-internal.h
+++ b/girepository/gitypelib-internal.h
@@ -1110,6 +1110,10 @@ gboolean g_typelib_validate (GTypelib  *typelib,
 			     GError    **error);
 
 
+/* defined in gibaseinfo.c */
+AttributeBlob *_attribute_blob_find_first (GIBaseInfo *info,
+                                           guint32     blob_offset);
+
 G_END_DECLS
 
 #endif  /* __G_TYPELIB_H__ */
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 43ca03c..01cbb1a 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -635,6 +635,12 @@ class AnnotationApplier(object):
         if tag is not None and tag.comment is not None:
             node.doc = tag.comment
 
+        for key in options:
+            if '.' in key:
+                value = options.get(key)
+                if value:
+                    node.attributes.append((key, value.one()))
+
     def _extract_direction(self, node, options):
         caller_allocates = False
         if (OPT_INOUT in options or
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
index 9b48d4e..59b4b49 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -192,6 +192,7 @@ and/or use gtk-doc annotations. ''')
         if return_.doc:
             attrs.append(('doc', return_.doc))
         with self.tagcontext('return-value', attrs):
+            self._write_attributes(return_)
             self._write_type(return_.type)
 
     def _write_parameters(self, parameters):
@@ -224,6 +225,7 @@ and/or use gtk-doc annotations. ''')
         if parameter.doc:
             attrs.append(('doc', parameter.doc))
         with self.tagcontext('parameter', attrs):
+            self._write_attributes(parameter)
             self._write_type(parameter.type)
 
     def _type_to_string(self, ntype):
diff --git a/tests/scanner/annotation-1.0-expected.gir b/tests/scanner/annotation-1.0-expected.gir
index 2599cf3..12025ed 100644
--- a/tests/scanner/annotation-1.0-expected.gir
+++ b/tests/scanner/annotation-1.0-expected.gir
@@ -503,6 +503,23 @@ type.">
       <field name="parent_instance">
         <type name="GObject.Object" c:type="GObject"/>
       </field>
+      <glib:signal name="attribute-signal"
+                   doc="This signal tests a signal with attributes.">
+        <return-value transfer-ownership="full" doc="the return value">
+          <attribute name="some.annotation.foo3" value="val3"/>
+          <type name="utf8" c:type="gchararray"/>
+        </return-value>
+        <parameters>
+          <parameter name="arg1" transfer-ownership="none" doc="a value">
+            <attribute name="some.annotation.foo1" value="val1"/>
+            <type name="utf8" c:type="gchararray"/>
+          </parameter>
+          <parameter name="arg2" transfer-ownership="none" doc="another value">
+            <attribute name="some.annotation.foo2" value="val2"/>
+            <type name="utf8" c:type="gchararray"/>
+          </parameter>
+        </parameters>
+      </glib:signal>
       <glib:signal name="doc-empty-arg-parsing"
                    doc="This signal tests an empty document argument (@arg1)">
         <return-value transfer-ownership="full">
@@ -562,6 +579,23 @@ it says it&apos;s pointer but it&apos;s actually a string."
         </array>
       </field>
     </record>
+    <function name="attribute_func" c:identifier="annotation_attribute_func">
+      <return-value transfer-ownership="none" doc="The return value.">
+        <attribute name="yet.another.annotation" value="another_value"/>
+        <attribute name="some.other.annotation" value="value2"/>
+        <type name="int" c:type="gint"/>
+      </return-value>
+      <parameters>
+        <parameter name="object" transfer-ownership="none">
+          <type name="Object" c:type="AnnotationObject*"/>
+        </parameter>
+        <parameter name="data" transfer-ownership="none" doc="Some data.">
+          <attribute name="another.annotation" value="blahvalue"/>
+          <attribute name="some.annotation" value="value"/>
+          <type name="utf8" c:type="gchar*"/>
+        </parameter>
+      </parameters>
+    </function>
     <function name="custom_destroy"
               c:identifier="annotation_custom_destroy"
               doc="Test messing up the heuristic of closure/destroy-notification
diff --git a/tests/scanner/annotation-1.0-expected.tgir b/tests/scanner/annotation-1.0-expected.tgir
index ba56be6..0f8a5fe 100644
--- a/tests/scanner/annotation-1.0-expected.tgir
+++ b/tests/scanner/annotation-1.0-expected.tgir
@@ -362,6 +362,22 @@
       <property name="string-property" writable="1" construct="1" transfer-ownership="none">
         <type name="utf8"/>
       </property>
+      <glib:signal name="attribute-signal" when="LAST">
+        <return-value transfer-ownership="full">
+          <attribute name="some.annotation.foo3" value="val3"/>
+          <type name="utf8"/>
+        </return-value>
+        <parameters>
+          <parameter name="arg1" transfer-ownership="none">
+            <attribute name="some.annotation.foo1" value="val1"/>
+            <type name="utf8"/>
+          </parameter>
+          <parameter name="arg2" transfer-ownership="none">
+            <attribute name="some.annotation.foo2" value="val2"/>
+            <type name="utf8"/>
+          </parameter>
+        </parameters>
+      </glib:signal>
       <glib:signal name="doc-empty-arg-parsing" when="LAST">
         <return-value transfer-ownership="full">
           <type name="none"/>
@@ -407,6 +423,23 @@
         </array>
       </field>
     </record>
+    <function name="attribute_func" c:identifier="annotation_attribute_func">
+      <return-value transfer-ownership="none">
+        <attribute name="yet.another.annotation" value="another_value"/>
+        <attribute name="some.other.annotation" value="value2"/>
+        <type name="int"/>
+      </return-value>
+      <parameters>
+        <parameter name="object" transfer-ownership="none">
+          <type name="Object"/>
+        </parameter>
+        <parameter name="data" transfer-ownership="none">
+          <attribute name="another.annotation" value="blahvalue"/>
+          <attribute name="some.annotation" value="value"/>
+          <type name="utf8"/>
+        </parameter>
+      </parameters>
+    </function>
     <function name="custom_destroy" c:identifier="annotation_custom_destroy">
       <return-value transfer-ownership="none">
         <type name="none"/>
diff --git a/tests/scanner/annotation.c b/tests/scanner/annotation.c
index 9795263..64f9cc3 100644
--- a/tests/scanner/annotation.c
+++ b/tests/scanner/annotation.c
@@ -14,6 +14,7 @@ enum {
   STRING_SIGNAL,
   LIST_SIGNAL,
   DOC_EMPTY_ARG_PARSING,
+  ATTRIBUTE_SIGNAL,
   LAST_SIGNAL
 };
 
@@ -119,6 +120,28 @@ annotation_object_class_init (AnnotationObjectClass *klass)
 		  G_TYPE_NONE, 1, G_TYPE_POINTER);
 
   /**
+   * AnnotationObject::attribute-signal:
+   * @annotation: the annotation object
+   * @arg1: (some.annotation.foo1 val1): a value
+   * @arg2: (some.annotation.foo2 val2): another value
+   *
+   * This signal tests a signal with attributes.
+   *
+   * Returns: (some.annotation.foo3 val3): the return value
+   */
+  annotation_object_signals[ATTRIBUTE_SIGNAL] =
+    g_signal_new ("attribute-signal",
+		  G_OBJECT_CLASS_TYPE (gobject_class),
+		  G_SIGNAL_RUN_LAST,
+		  0,
+		  NULL, NULL,
+		  NULL, /* marshaller */
+		  G_TYPE_STRING,
+                  2,
+                  G_TYPE_STRING,
+                  G_TYPE_STRING);
+
+  /**
    * AnnotationObject:string-property:
    *
    * This is a property which is a string
@@ -700,4 +723,18 @@ annotation_ptr_array (GPtrArray *array)
 {
 }
 
+/**
+ * annotation_attribute_func:
+ * @object: A #AnnotationObject.
+ * @data: (some.annotation value) (another.annotation blahvalue): Some data.
+ *
+ * Returns: (some.other.annotation value2) (yet.another.annotation another_value): The return value.
+ */
+gint
+annotation_attribute_func (AnnotationObject *object,
+                           const gchar      *data)
+{
+  return 42;
+}
+
 char backslash_parsing_tester_2 = '\\';
diff --git a/tests/scanner/annotation.h b/tests/scanner/annotation.h
index 0f47d22..ace3651 100644
--- a/tests/scanner/annotation.h
+++ b/tests/scanner/annotation.h
@@ -139,6 +139,9 @@ void     annotation_custom_destroy (AnnotationCallback callback,
 char *   annotation_get_source_file (void);
 void     annotation_set_source_file (const char *fname);
 
+gint     annotation_attribute_func (AnnotationObject *object,
+                                    const gchar      *data);
+
 /**
  * AnnotationStruct:
  *



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