Bug 571548 - Generic attributes
authorColin Walters <walters@verbum.org>
Fri, 20 Feb 2009 02:48:51 +0000 (21:48 -0500)
committerColin Walters <walters@verbum.org>
Tue, 3 Mar 2009 22:26:37 +0000 (17:26 -0500)
We now support an extensible mechanism where arbitrary key-value
pairs may be associated with almost all items, including objects,
methods, and properties.

These attributes appear in both the .gir and the .typelib.

18 files changed:
docs/reference/gi-sections.txt
girepository/ginfo.c
girepository/girepository.h
girepository/girmodule.c
girepository/girnode.c
girepository/girnode.h
girepository/girparser.c
girepository/gtypelib.c
girepository/gtypelib.h
giscanner/annotationparser.py
giscanner/ast.py
giscanner/girwriter.py
giscanner/xmlwriter.py
tests/scanner/annotation-1.0-expected.gir
tests/scanner/annotation-1.0-expected.tgir
tests/scanner/annotation.c
tests/scanner/annotation.h
tools/generate.c

index 96ef8fb..6177d10 100644 (file)
@@ -61,7 +61,8 @@ g_base_info_get_type
 g_base_info_get_name
 g_base_info_get_namespace
 g_base_info_is_deprecated
-g_base_info_get_annotation
+g_base_info_get_attribute
+g_base_info_iterate_attributean
 g_base_info_get_container
 g_base_info_get_typelib
 g_info_new
index 1c34ee2..fcc5f09 100644 (file)
@@ -1,6 +1,7 @@
 /* GObject introspection: Repository implementation
  *
  * Copyright (C) 2005 Matthias Clasen
+ * Copyright (C) 2008,2009 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -402,65 +403,131 @@ g_base_info_is_deprecated (GIBaseInfo *info)
   return FALSE;
 }
 
+/**
+ * g_base_info_get_attribute:
+ * @info: A #GIBaseInfo
+ * @name: A freeform string naming an attribute
+ *
+ * Retrieve an arbitrary attribute associated with this node.
+ *
+ * Return value: The value of the attribute, or %NULL if no such attribute exists
+ */
+const gchar *
+g_base_info_get_attribute (GIBaseInfo   *info,
+                          const gchar *name)
+{
+  GIAttributeIter iter = { 0, };
+  gchar *curname, *curvalue;
+  while (g_base_info_iterate_attributes (info, &iter, &curname, &curvalue))
+    {
+      if (strcmp (name, curname) == 0)
+        return (const gchar*) curvalue;
+    }
+
+  return NULL;
+}
+
 static int
-cmp_annotation (const void *av,
-               const void *bv)
+cmp_attribute (const void *av,
+                const void *bv)
 {
-  const AnnotationBlob *a = av;
-  const AnnotationBlob *b = bv;
+  const AttributeBlob *a = av;
+  const AttributeBlob *b = bv;
  
-  if (b->offset < a->offset)
+  if (a->offset < b->offset)
     return -1;
-
-  if (b->offset > a->offset)
+  else if (a->offset == b->offset)
+    return 0;
+  else
     return 1;
-  
-  return 0;
 }
 
-const gchar *
-g_base_info_get_annotation (GIBaseInfo   *info,
-                           const gchar *name)
+static AttributeBlob *
+find_first_attribute (GIBaseInfo *info)
 {
   GIBaseInfo *base = (GIBaseInfo *)info;
   Header *header = (Header *)base->typelib->data;
-  AnnotationBlob blob, *first, *after, *res, *next;
-  const gchar *rname;
+  AttributeBlob blob, *first, *res, *previous;
 
   blob.offset = base->offset;
   
-  first = (AnnotationBlob *) &base->typelib->data[header->annotations];
-  after = (AnnotationBlob *) &base->typelib->data[header->annotations + 
-                                            header->n_annotations * header->annotation_blob_size];
+  first = (AttributeBlob *) &base->typelib->data[header->attributes];
+
+  res = bsearch (&blob, first, header->n_attributes,
+                 header->attribute_blob_size, cmp_attribute);
 
-  res = bsearch (&blob, first, header->n_annotations,
-                header->annotation_blob_size, cmp_annotation);
-  
   if (res == NULL)
     return NULL;
 
-  next = res;
-  do 
+  previous = res - 1;
+  while (previous >= first && previous->offset == base->offset)
     {
-      res = next;
-      next = res -= header->annotation_blob_size;
+      res = previous;
+      previous = res - 1;
     }
-  while (next >= first && next->offset == base->offset);
-    
-  next = res;
-  do 
-    {
-      res = next;
-      
-      rname = g_typelib_get_string (base->typelib, res->name);
-      if (strcmp (name, rname) == 0)
-       return g_typelib_get_string (base->typelib, res->value);
 
-      next = res += header->annotation_blob_size;
-    }
-  while (next < after && next->offset == base->offset);
+  return res;
+}
 
-  return NULL;
+/**
+ * g_base_info_iterate_attributes:
+ * @info: A #GIBaseInfo
+ * @iter: 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 this node.  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.
+ *
+ * <example>
+ * <title>Iterating over attributes</title>
+ * <programlisting>
+ * void
+ * print_attributes (GIBaseInfo *info)
+ * {
+ *   GIAttributeIter iter = { 0, };
+ *   char *name;
+ *   char *value;
+ *   while (g_base_info_iterate_attributes (info, &iter, &name, &value))
+ *     {
+ *       g_print ("attribute name: %s value: %s", name, value);
+ *     }
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * Return value: %TRUE if there are more attributes, %FALSE otherwise
+ */
+gboolean
+g_base_info_iterate_attributes (GIBaseInfo       *info,
+                                 GIAttributeIter *iter,
+                                 gchar           **name,
+                                 gchar           **value)
+{
+  GIBaseInfo *base = (GIBaseInfo *)info;
+  Header *header = (Header *)base->typelib->data;
+  AttributeBlob *next, *after;
+
+  after = (AttributeBlob *) &base->typelib->data[header->attributes +
+                                                  header->n_attributes * header->attribute_blob_size];
+
+  if (iter->data != NULL)
+    next = (AttributeBlob *) iter->data;
+  else
+    next = find_first_attribute (info);
+
+  if (next == NULL || next->offset != base->offset || next >= after)
+    return FALSE;
+
+  *name = (gchar*) g_typelib_get_string (base->typelib, next->name);
+  *value = (gchar*) g_typelib_get_string (base->typelib, next->value);
+  iter->data = next + 1;
+
+  return TRUE;
 }
 
 GIBaseInfo *
index 61a9116..4059adc 100644 (file)
@@ -1,6 +1,7 @@
 /* GObject introspection: Repository
  *
  * Copyright (C) 2005 Matthias Clasen
+ * Copyright (C) 2008,2009 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -179,14 +180,25 @@ typedef enum
 
 /* GIBaseInfo */
 
+typedef struct {
+  gpointer data;
+  gpointer data2;
+  gpointer data3;
+  gpointer data4;
+} GIAttributeIter;
+
 GIBaseInfo *           g_base_info_ref              (GIBaseInfo   *info);
 void                   g_base_info_unref            (GIBaseInfo   *info);
 GIInfoType             g_base_info_get_type         (GIBaseInfo   *info);
 const gchar *          g_base_info_get_name         (GIBaseInfo   *info);
 const gchar *          g_base_info_get_namespace    (GIBaseInfo   *info);
 gboolean               g_base_info_is_deprecated    (GIBaseInfo   *info);
-const gchar *          g_base_info_get_annotation   (GIBaseInfo   *info,
+const gchar *          g_base_info_get_attribute    (GIBaseInfo   *info,
                                                      const gchar  *name);
+gboolean               g_base_info_iterate_attributes (GIBaseInfo      *info,
+                                                       GIAttributeIter *iterator,
+                                                       char           **name,
+                                                       char          **value);
 GIBaseInfo *           g_base_info_get_container    (GIBaseInfo   *info);
 GTypelib *             g_base_info_get_typelib      (GIBaseInfo   *info);
 
index 5abd31f..96ee9ce 100644 (file)
@@ -109,6 +109,53 @@ g_ir_module_add_include_module (GIrModule  *module,
                        module);
 }
 
+struct AttributeWriteData
+{
+  guint count;
+  guchar *databuf;
+  GIrNode *node;
+  GHashTable *strings;
+  guint32 *offset;
+  guint32 *offset2;
+};
+
+static void
+write_attribute (gpointer key, gpointer value, gpointer datap)
+{
+  struct AttributeWriteData *data = datap;
+  guint32 old_offset = *(data->offset);
+  AttributeBlob *blob = (AttributeBlob*)&(data->databuf[old_offset]);
+
+  *(data->offset) += sizeof (AttributeBlob);
+
+  blob->offset = data->node->offset;
+  blob->name = write_string ((const char*) key, data->strings, data->databuf, data->offset2);
+  blob->value = write_string ((const char*) value, data->strings, data->databuf, data->offset2);
+
+  data->count++;
+}
+
+static guint
+write_attributes (GIrModule *module,
+                   GIrNode   *node,
+                   GHashTable *strings,
+                   guchar    *data,
+                   guint32   *offset,
+                   guint32   *offset2)
+{
+  struct AttributeWriteData wdata;
+  wdata.count = 0;
+  wdata.databuf = data;
+  wdata.node = node;
+  wdata.offset = offset;
+  wdata.offset2 = offset2;
+  wdata.strings = strings;
+
+  g_hash_table_foreach (node->attributes, write_attribute, &wdata);
+
+  return wdata.count;
+}
+
 GTypelib *
 g_ir_module_build_typelib (GIrModule  *module,
                             GList       *modules)
@@ -126,6 +173,7 @@ g_ir_module_build_typelib (GIrModule  *module,
   guint32 size, offset, offset2, old_offset;
   GHashTable *strings;
   GHashTable *types;
+  GList *offset_ordered_nodes;
   char *dependencies;
   guchar *data;
 
@@ -158,6 +206,7 @@ g_ir_module_build_typelib (GIrModule  *module,
   _g_irnode_init_stats ();
   strings = g_hash_table_new (g_str_hash, g_str_equal);
   types = g_hash_table_new (g_str_hash, g_str_equal);
+  offset_ordered_nodes = NULL;
   n_entries = g_list_length (module->entries);
 
   g_message ("%d entries (%d local), %d dependencies\n", n_entries, n_local_entries,
@@ -173,6 +222,10 @@ g_ir_module_build_typelib (GIrModule  *module,
       GIrNode *node = e->data;
       
       size += g_ir_node_get_full_size (node);
+      size += g_ir_node_get_attribute_size (node);
+
+      /* Also reset the offset here */
+      node->offset = 0;
     }
 
   /* Adjust size for strings allocated in header below specially */
@@ -195,8 +248,8 @@ g_ir_module_build_typelib (GIrModule  *module,
   header->reserved = 0;
   header->n_entries = n_entries;
   header->n_local_entries = n_local_entries;
-  header->n_annotations = 0;
-  header->annotations = 0; /* filled in later */
+  header->n_attributes = 0;
+  header->attributes = 0; /* filled in later */
   if (dependencies != NULL)
     header->dependencies = write_string (dependencies, strings, data, &header_size);
   else
@@ -219,7 +272,7 @@ g_ir_module_build_typelib (GIrModule  *module,
   header->value_blob_size = sizeof (ValueBlob);
   header->constant_blob_size = sizeof (ConstantBlob);
   header->error_domain_blob_size = sizeof (ErrorDomainBlob);
-  header->annotation_blob_size = sizeof (AnnotationBlob);
+  header->attribute_blob_size = sizeof (AttributeBlob);
   header->signature_blob_size = sizeof (SignatureBlob);
   header->enum_blob_size = sizeof (EnumBlob);
   header->struct_blob_size = sizeof (StructBlob);
@@ -245,10 +298,17 @@ g_ir_module_build_typelib (GIrModule  *module,
       /* we picked up implicit xref nodes, start over */
       if (i == n_entries)
        {
+         GList *link;
          g_message ("Found implicit cross references, starting over");
 
          g_hash_table_destroy (strings);
          g_hash_table_destroy (types);
+
+         /* Reset the cached offsets */
+         for (link = offset_ordered_nodes; link; link = link->next)
+           ((GIrNode *) link->data)->offset = 0;
+
+         g_list_free (offset_ordered_nodes);
          strings = NULL;
 
          g_free (data);
@@ -282,9 +342,14 @@ g_ir_module_build_typelib (GIrModule  *module,
          build.modules = modules;
          build.strings = strings;
          build.types = types;
+         build.offset_ordered_nodes = offset_ordered_nodes;
+         build.n_attributes = header->n_attributes;
          build.data = data;
          g_ir_node_build_typelib (node, NULL, &build, &offset, &offset2);
 
+         offset_ordered_nodes = build.offset_ordered_nodes;
+         header->n_attributes = build.n_attributes;
+
          if (offset2 > old_offset + g_ir_node_get_full_size (node))
            g_error ("left a hole of %d bytes\n", offset2 - old_offset - g_ir_node_get_full_size (node));
        }
@@ -292,9 +357,23 @@ g_ir_module_build_typelib (GIrModule  *module,
       entry++;
     }
 
+  offset_ordered_nodes = g_list_reverse (offset_ordered_nodes);
+
+  g_message ("header: %d entries, %d attributes", header->n_entries, header->n_attributes);
+
   _g_irnode_dump_stats ();
 
-  header->annotations = offset2;
+  /* Write attributes after the blobs */
+  offset = offset2;
+  header->attributes = offset;
+  offset2 = offset + header->n_attributes * header->attribute_blob_size;
+
+  for (e = offset_ordered_nodes; e; e = e->next)
+    {
+      GIrNode *node = e->data;
+
+      write_attributes (module, node, strings, data, &offset, &offset2);
+    }
   
   g_message ("reallocating to %d bytes", offset2);
 
@@ -305,6 +384,7 @@ g_ir_module_build_typelib (GIrModule  *module,
 
   g_hash_table_destroy (strings);
   g_hash_table_destroy (types);
+  g_list_free (offset_ordered_nodes);
 
   return typelib;
 }
index fb96d8d..22c0aee 100644 (file)
@@ -1,6 +1,7 @@
 /* GObject introspection: Typelib creation
  *
  * Copyright (C) 2005 Matthias Clasen
+ * Copyright (C) 2008,2009 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -184,6 +185,9 @@ g_ir_node_new (GIrNodeTypeId type)
     }
 
   node->type = type;
+  node->offset = 0;
+  node->attributes = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                            g_free, g_free);
 
   return node;
 }
@@ -400,6 +404,8 @@ g_ir_node_free (GIrNode *node)
       break;
     } 
 
+  g_hash_table_destroy (node->attributes);
+
   g_free (node);
 }
 
@@ -540,6 +546,18 @@ g_ir_node_get_size (GIrNode *node)
   return size;
 }
 
+static void
+add_attribute_size (gpointer key, gpointer value, gpointer data)
+{
+  const gchar *key_str = key;
+  const gchar *value_str = value;
+  gint *size_p = data;
+
+  *size_p += sizeof (AttributeBlob);
+  *size_p += ALIGN_VALUE (strlen (key_str) + 1, 4);
+  *size_p += ALIGN_VALUE (strlen (value_str) + 1, 4);
+}
+
 /* returns the full size of the blob including variable-size parts */
 static guint32
 g_ir_node_get_full_size_internal (GIrNode *parent,
@@ -851,6 +869,14 @@ g_ir_node_get_full_size (GIrNode *node)
   return g_ir_node_get_full_size_internal (NULL, node);
 }
 
+guint32
+g_ir_node_get_attribute_size (GIrNode *node)
+{
+  guint32 size = 0;
+  g_hash_table_foreach (node->attributes, add_attribute_size, &size);
+  return size;
+}
+
 int
 g_ir_node_cmp (GIrNode *node,
                GIrNode *other)
@@ -1364,6 +1390,15 @@ g_ir_node_build_typelib (GIrNode         *node,
 
   g_ir_node_compute_offsets (node, module, modules);
 
+  /* We should only be building each node once.  If we do a typelib expansion, we also
+   * reset the offset in girmodule.c.
+   */
+  g_assert (node->offset == 0);
+  node->offset = *offset;
+  build->offset_ordered_nodes = g_list_prepend (build->offset_ordered_nodes, node);
+
+  build->n_attributes += g_hash_table_size (node->attributes);
+
   switch (node->type)
     {
     case G_IR_NODE_TYPE:
@@ -2232,7 +2267,8 @@ g_ir_node_build_typelib (GIrNode         *node,
           old_offset, *offset, old_offset2, *offset2);
 
   if (*offset2 - old_offset2 + *offset - old_offset > g_ir_node_get_full_size (node))
-    g_error ("exceeding space reservation !!");
+    g_error ("exceeding space reservation; offset: %d (prev %d) offset2: %d (prev %d) nodesize: %d",
+             *offset, old_offset, *offset2, old_offset2, g_ir_node_get_full_size (node));
 }
 
 /* if str is already in the pool, return previous location, otherwise write str
index 45c2bb0..2a1f6b2 100644 (file)
@@ -51,6 +51,8 @@ struct _GIrTypelibBuild {
   GList       *modules;
   GHashTable  *strings;
   GHashTable  *types;
+  GList       *offset_ordered_nodes;
+  guint32      n_attributes;
   guchar      *data; 
 };
 
@@ -82,6 +84,10 @@ struct _GIrNode
 {
   GIrNodeTypeId type;
   gchar *name;
+
+  guint32 offset; /* Assigned as we build the typelib */
+
+  GHashTable *attributes;
 };
 
 struct _GIrNodeXRef
@@ -349,6 +355,7 @@ GIrNode * g_ir_node_new             (GIrNodeTypeId type);
 void      g_ir_node_free            (GIrNode    *node);
 guint32   g_ir_node_get_size        (GIrNode    *node);
 guint32   g_ir_node_get_full_size   (GIrNode    *node);
+guint32   g_ir_node_get_attribute_size (GIrNode *node);
 void      g_ir_node_build_typelib   (GIrNode         *node,
                                      GIrNode         *parent,
                                      GIrTypelibBuild *build,
index e08b3fc..006ed3b 100644 (file)
@@ -68,6 +68,7 @@ typedef enum
   STATE_INTERFACE_CONSTANT,
   STATE_ALIAS,
   STATE_TYPE,
+  STATE_ATTRIBUTE,
   STATE_UNKNOWN
 } ParseState;
 
@@ -1864,6 +1865,44 @@ end_type (ParseContext *ctx)
     }
 }
 
+static gboolean
+start_attribute (GMarkupParseContext *context,
+                 const gchar         *element_name,
+                 const gchar        **attribute_names,
+                 const gchar        **attribute_values,
+                 ParseContext       *ctx,
+                 GError             **error)
+{
+  const gchar *name;
+  const gchar *value;
+  GIrNode *curnode;
+
+  if (strcmp (element_name, "attribute") != 0 || ctx->node_stack == NULL)
+    return FALSE;
+
+  name = find_attribute ("name", attribute_names, attribute_values);
+  value = find_attribute ("value", attribute_names, attribute_values);
+
+  if (name == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "name");
+      return FALSE;
+    }
+  if (value == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "value");
+      return FALSE;
+    }
+
+  state_switch (ctx, STATE_ATTRIBUTE);
+
+  curnode = CURRENT_NODE (ctx);
+
+  g_hash_table_insert (curnode->attributes, g_strdup (name), g_strdup (value));
+
+  return TRUE;
+}
+
 static gboolean
 start_return_value (GMarkupParseContext *context,
                    const gchar         *element_name,
@@ -2383,6 +2422,10 @@ start_element_handler (GMarkupParseContext *context,
                      attribute_names, attribute_values,
                      ctx, error))
        goto out;
+      else if (start_attribute (context, element_name,
+                                attribute_names, attribute_values,
+                                ctx, error))
+        goto out;
       break;
     case 'b':
       if (start_enum (context, element_name, 
@@ -2662,7 +2705,7 @@ start_element_handler (GMarkupParseContext *context,
       ctx->unknown_depth += 1;
     }
   
- out: ;
+ out:
   if (*error) 
     {
       g_markup_parse_context_get_position (context, &line_number, &char_number);
@@ -3027,6 +3070,13 @@ end_element_handler (GMarkupParseContext *context,
          end_type (ctx);
          break;
        }
+    case STATE_ATTRIBUTE:
+      if (strcmp ("attribute", element_name) == 0)
+        {
+          state_switch (ctx, ctx->prev_state);
+        }
+      break;
+
     case STATE_UNKNOWN:
       ctx->unknown_depth -= 1;
       if (ctx->unknown_depth == 0)
index 6ff00bf..a578c67 100644 (file)
@@ -185,7 +185,7 @@ g_typelib_check_sanity (void)
   CHECK_SIZE (ObjectBlob, 44);
   CHECK_SIZE (InterfaceBlob, 40);
   CHECK_SIZE (ConstantBlob, 24);
-  CHECK_SIZE (AnnotationBlob, 12);
+  CHECK_SIZE (AttributeBlob, 12);
   CHECK_SIZE (UnionBlob, 40);
 #undef CHECK_SIZE
 
@@ -334,7 +334,7 @@ validate_header (ValidateContext  *ctx,
       header->value_blob_size != sizeof (ValueBlob) ||
       header->constant_blob_size != sizeof (ConstantBlob) ||
       header->error_domain_blob_size != sizeof (ErrorDomainBlob) ||
-      header->annotation_blob_size != sizeof (AnnotationBlob) ||
+      header->attribute_blob_size != sizeof (AttributeBlob) ||
       header->signature_blob_size != sizeof (SignatureBlob) ||
       header->enum_blob_size != sizeof (EnumBlob) ||
       header->struct_blob_size != sizeof (StructBlob) ||
@@ -358,21 +358,21 @@ validate_header (ValidateContext  *ctx,
       return FALSE; 
     }
 
-  if (!is_aligned (header->annotations))
+  if (!is_aligned (header->attributes))
     {
       g_set_error (error,
                   G_TYPELIB_ERROR,
                   G_TYPELIB_ERROR_INVALID_HEADER,
-                  "Misaligned annotations");
+                  "Misaligned attributes");
       return FALSE; 
     }
 
-  if (header->annotations == 0 && header->n_annotations > 0)
+  if (header->attributes == 0 && header->n_attributes > 0)
     {
       g_set_error (error,
                   G_TYPELIB_ERROR,
                   G_TYPELIB_ERROR_INVALID_HEADER,
-                  "Wrong number of annotations");
+                  "Wrong number of attributes");
       return FALSE; 
     }
 
@@ -1860,13 +1860,13 @@ validate_directory (ValidateContext   *ctx,
 }
 
 static gboolean
-validate_annotations (ValidateContext *ctx, 
-                     GError       **error)
+validate_attributes (ValidateContext *ctx,
+                    GError       **error)
 {
   GTypelib *typelib = ctx->typelib;
   Header *header = (Header *)typelib->data;
 
-  if (header->size < header->annotations + header->n_annotations * sizeof (AnnotationBlob))
+  if (header->size < header->attributes + header->n_attributes * sizeof (AttributeBlob))
     {
       g_set_error (error,
                   G_TYPELIB_ERROR,
@@ -1926,9 +1926,9 @@ g_typelib_validate (GTypelib     *typelib,
       return FALSE;
     }
 
-  if (!validate_annotations (&ctx, error))
+  if (!validate_attributes (&ctx, error))
     {
-      prefix_with_context (error, "annotations", &ctx);
+      prefix_with_context (error, "attributes", &ctx);
       return FALSE;
     }
 
index 7a2838f..db5fe11 100644 (file)
@@ -52,14 +52,15 @@ G_BEGIN_DECLS
  * 
  * The typelib has the following general format.
  *
- * typelib ::= header, directory, blobs, annotations
+ * typelib ::= header, directory, blobs, attributes, attributedata
  *
  * directory ::= list of entries
  *
  * entry ::= blob type, name, namespace, offset
  * blob ::= function|callback|struct|boxed|enum|flags|object|interface|constant|errordomain|union
- * annotations ::= list of annotations, sorted by offset 
- * annotation ::= offset, key, value
+ * attributes ::= list of attributes, sorted by offset
+ * attribute ::= offset, key, value
+ * attributedata ::= string data for attributes
  *
  * Details
  * 
@@ -189,8 +190,8 @@ typedef enum {
  * @n_local_entries: The number of entries referring to blobs in this typelib. The
  * local entries must occur before the unresolved entries.
  * @directory: Offset of the directory in the typelib.
- * @n_annotations: Number of annotation blocks
- * @annotations: Offset of the list of annotations in the typelib. 
+ * @n_attributes: Number of attribute blocks
+ * @attributes: Offset of the list of attributes in the typelib.
  * @dependencies: Offset of a single string, which is the list of
  * dependencies, separated by the '|' character.  The
  * dependencies are required in order to avoid having programs
@@ -212,7 +213,7 @@ typedef enum {
  * @property_blob_size: See above.
  * @field_blob_size: See above.
  * @value_blob_size: See above.
- * @annotation_blob_size: See above.
+ * @attribute_blob_size: See above.
  * @constant_blob_size: See above.
  * @object_blob_size: See above.
  * @union_blob_size: See above.
@@ -237,8 +238,8 @@ typedef struct {
   guint16 n_entries;
   guint16 n_local_entries;
   guint32 directory;
-  guint32 n_annotations;
-  guint32 annotations;
+  guint32 n_attributes;
+  guint32 attributes;
 
   guint32 dependencies;
 
@@ -256,7 +257,7 @@ typedef struct {
   guint16 property_blob_size;
   guint16 field_blob_size;
   guint16 value_blob_size;
-  guint16 annotation_blob_size;
+  guint16 attribute_blob_size;
   guint16 constant_blob_size;
   guint16 error_domain_blob_size;
 
@@ -1000,18 +1001,18 @@ typedef struct {
 } ConstantBlob;
 
 /**
- * AnnotationBlob:
- * @offset: The offset of the typelib entry to which this annotation refers.
- * Annotations are kept sorted by offset, so that the annotations
+ * AttributeBlob:
+ * @offset: The offset of the typelib entry to which this attribute refers.
+ * Attributes are kept sorted by offset, so that the attributes
  * of an entry can be found by a binary search.
- * @name: The name of the annotation, a string.
- * @value: The value of the annotation (also a string)
+ * @name: The name of the attribute, a string.
+ * @value: The value of the attribute (also a string)
  */
 typedef struct {
   guint32 offset;
   guint32 name;
   guint32 value;
-} AnnotationBlob;
+} AttributeBlob;
 
 struct _GTypelib {
   guchar *data;
index 003ff2c..0798b80 100644 (file)
@@ -20,6 +20,8 @@
 
 # AnnotationParser - parses gtk-doc annotations
 
+import sys
+
 from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
                   Interface, List, Map, Parameter, Record, Return, Type, Union,
                   Varargs,
@@ -43,6 +45,7 @@ TAG_SINCE = 'since'
 TAG_DEPRECATED = 'deprecated'
 TAG_RETURNS = 'returns'
 TAG_RETURNS_ALT = 'return value'
+TAG_ATTRIBUTES = 'attributes'
 
 # Options - annotations for parameters and return values
 OPT_ALLOW_NONE = 'allow-none'
@@ -92,7 +95,7 @@ class DocTag(object):
 
     def __init__(self, name):
         self.name = name
-        self.options = []
+        self.options = {}
         self.comment = None
 
 
@@ -178,23 +181,31 @@ class AnnotationParser(object):
             elif not ': ' in line:
                 comment_lines.append(line)
                 continue
-            tag = self._parse_tag(line)
-            block.tags[tag.name.lower()] = tag
+            tag_name, value = self._split_tag_namevalue(line)
+            canon_name = tag_name.lower()
+            if canon_name in block.tags:
+                print >>sys.stderr, "Multiple definition of tag %r" \
+                    % (canon_name, )
+            block.tags[canon_name] = self._create_tag(canon_name, value)
         block.comment = '\n'.join(comment_lines)
         self._blocks[block.name] = block
 
-    def _parse_tag(self, raw):
-        # Tag: bar
-        # Tag: bar opt1 opt2
+    def _split_tag_namevalue(self, raw):
+        """Split a line into tag name and value"""
         parts = raw.split(': ', 1)
         if len(parts) == 1:
             tag_name = parts[0]
             value = ''
         else:
             tag_name, value = parts
-        options, rest = self._parse_options(value)
+        return (tag_name, value)
+
+    def _create_tag(self, tag_name, value):
+        # Tag: bar
+        # Tag: bar opt1 opt2
         tag = DocTag(tag_name)
         tag.value = value
+        options, rest = self._parse_options(tag.value)
         tag.options = options
         tag.comment = rest
         return tag
@@ -271,7 +282,7 @@ class AnnotationApplier(object):
 
     def _parse_class(self, class_):
         block = self._blocks.get(class_.type_name)
-        self._parse_version(class_, block)
+        self._parse_node_common(class_, block)
         self._parse_constructors(class_.constructors)
         self._parse_methods(class_.methods)
         self._parse_methods(class_.static_methods)
@@ -283,7 +294,7 @@ class AnnotationApplier(object):
 
     def _parse_interface(self, interface):
         block = self._blocks.get(interface.type_name)
-        self._parse_version(interface, block)
+        self._parse_node_common(interface, block)
         self._parse_methods(interface.methods)
         self._parse_properties(interface, interface.properties)
         self._parse_signals(interface, interface.signals)
@@ -293,7 +304,7 @@ class AnnotationApplier(object):
 
     def _parse_record(self, record):
         block = self._blocks.get(record.symbol)
-        self._parse_version(record, block)
+        self._parse_node_common(record, block)
         self._parse_constructors(record.constructors)
         self._parse_methods(record.methods)
         self._parse_fields(record, record.fields)
@@ -302,7 +313,7 @@ class AnnotationApplier(object):
 
     def _parse_boxed(self, boxed):
         block = self._blocks.get(boxed.name)
-        self._parse_version(boxed, block)
+        self._parse_node_common(boxed, block)
         self._parse_constructors(boxed.constructors)
         self._parse_methods(boxed.methods)
         if block:
@@ -310,6 +321,7 @@ class AnnotationApplier(object):
 
     def _parse_union(self, union):
         block = self._blocks.get(union.name)
+        self._parse_node_common(union, block)
         self._parse_fields(union, union.fields)
         self._parse_constructors(union.constructors)
         self._parse_methods(union.methods)
@@ -318,13 +330,13 @@ class AnnotationApplier(object):
 
     def _parse_enum(self, enum):
         block = self._blocks.get(enum.symbol)
-        self._parse_version(enum, block)
+        self._parse_node_common(enum, block)
         if block:
             enum.doc = block.comment
 
     def _parse_bitfield(self, bitfield):
         block = self._blocks.get(bitfield.symbol)
-        self._parse_version(bitfield, block)
+        self._parse_node_common(bitfield, block)
         if block:
             bitfield.doc = block.comment
 
@@ -350,14 +362,13 @@ class AnnotationApplier(object):
 
     def _parse_property(self, parent, prop):
         block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
-        self._parse_version(prop, block)
-        self._parse_deprecated(prop, block)
+        self._parse_node_common(prop, block)
         if block:
             prop.doc = block.comment
 
     def _parse_callback(self, callback):
         block = self._blocks.get(callback.ctype)
-        self._parse_version(callback, block)
+        self._parse_node_common(callback, block)
         self._parse_params(callback, callback.parameters, block)
         self._parse_return(callback, callback.retval, block)
         if block:
@@ -365,8 +376,7 @@ class AnnotationApplier(object):
 
     def _parse_function(self, func):
         block = self._blocks.get(func.symbol)
-        self._parse_version(func, block)
-        self._parse_deprecated(func, block)
+        self._parse_node_common(func, block)
         self._parse_params(func, func.parameters, block)
         self._parse_return(func, func.retval, block)
         if block:
@@ -374,7 +384,7 @@ class AnnotationApplier(object):
 
     def _parse_signal(self, parent, signal):
         block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
-        self._parse_version(signal, block)
+        self._parse_node_common(signal, block)
         self._parse_deprecated(signal, block)
         # We're only attempting to name the signal parameters if
         # the number of parameter tags (@foo) is the same or greater
@@ -552,6 +562,11 @@ class AnnotationApplier(object):
                     "none, container, full." % (node, parent.name, transfer))
         return transfer
 
+    def _parse_node_common(self, node, block):
+        self._parse_version(node, block)
+        self._parse_deprecated(node, block)
+        self._parse_attributes(node, block)
+
     def _parse_version(self, node, block):
         since_tag = self._get_tag(block, TAG_SINCE)
         if since_tag is None:
@@ -572,6 +587,13 @@ class AnnotationApplier(object):
         if version is not None:
             node.deprecated_version = version
 
+    def _parse_attributes(self, node, block):
+        annos_tag = self._get_tag(block, TAG_ATTRIBUTES)
+        if annos_tag is None:
+            return
+        for key, value in annos_tag.options.iteritems():
+            node.attributes.append((key, value.one()))
+
     def _guess_direction(self, node):
         if node.direction:
             return node.direction
index e708258..d2bae87 100644 (file)
@@ -147,6 +147,7 @@ class Node(object):
 
     def __init__(self, name=None):
         self.name = name
+        self.attributes = [] # (key, value)*
         self.deprecated = None
         self.deprecated_version = None
         self.version = None
index df52709..88510b0 100644 (file)
@@ -137,6 +137,10 @@ and/or use gtk-doc annotations. ''')
         if node.version:
             attrs.append(('version', node.version))
 
+    def _write_attributes(self, node):
+        for key, value in node.attributes:
+            self.write_tag('attribute', [('name', key), ('value', value)])
+
     def _append_deprecated(self, node, attrs):
         if node.deprecated:
             attrs.append(('deprecated', node.deprecated))
@@ -163,6 +167,7 @@ and/or use gtk-doc annotations. ''')
         self._append_deprecated(func, attrs)
         self._append_throws(func, attrs)
         with self.tagcontext(tag_name, attrs):
+            self._write_attributes(func)
             self._write_return_type(func.retval)
             self._write_parameters(func.parameters)
 
@@ -280,6 +285,7 @@ and/or use gtk-doc annotations. ''')
             attrs.append(('c:type', enum.symbol))
 
         with self.tagcontext('enumeration', attrs):
+            self._write_attributes(enum)
             for member in enum.members:
                 self._write_member(member)
 
@@ -296,6 +302,7 @@ and/or use gtk-doc annotations. ''')
         else:
             attrs.append(('c:type', bitfield.symbol))
         with self.tagcontext('bitfield', attrs):
+            self._write_attributes(bitfield)
             for member in bitfield.members:
                 self._write_member(member)
 
@@ -335,6 +342,7 @@ and/or use gtk-doc annotations. ''')
             if node.glib_type_struct:
                 attrs.append(('glib:type-struct', node.glib_type_struct.name))
         with self.tagcontext(tag_name, attrs):
+            self._write_attributes(node)
             if isinstance(node, GLibObject):
                 for iface in node.interfaces:
                     self.write_tag('implements', [('name', iface)])
@@ -362,6 +370,7 @@ and/or use gtk-doc annotations. ''')
             attrs.append(('doc', boxed.doc))
         attrs.extend(self._boxed_attrs(boxed))
         with self.tagcontext('glib:boxed', attrs):
+            self._write_attributes(boxed)
             for method in boxed.constructors:
                 self._write_constructor(method)
             for method in boxed.methods:
@@ -383,6 +392,7 @@ and/or use gtk-doc annotations. ''')
         if prop.doc:
             attrs.append(('doc', prop.doc))
         with self.tagcontext('property', attrs):
+            self._write_attributes(prop)
             self._write_type(prop.type)
 
     def _write_callback(self, callback):
@@ -394,6 +404,7 @@ and/or use gtk-doc annotations. ''')
         self._append_deprecated(callback, attrs)
         self._append_throws(callback, attrs)
         with self.tagcontext('callback', attrs):
+            self._write_attributes(callback)
             self._write_return_type(callback.retval)
             self._write_parameters(callback.parameters)
 
@@ -420,6 +431,7 @@ and/or use gtk-doc annotations. ''')
         if isinstance(record, GLibBoxed):
             attrs.extend(self._boxed_attrs(record))
         with self.tagcontext('record', attrs):
+            self._write_attributes(record)
             if record.fields:
                 for field in record.fields:
                     self._write_field(field)
@@ -441,6 +453,7 @@ and/or use gtk-doc annotations. ''')
         if isinstance(union, GLibBoxed):
             attrs.extend(self._boxed_attrs(union))
         with self.tagcontext('union', attrs):
+            self._write_attributes(union)
             if union.fields:
                 for field in union.fields:
                     self._write_field(field)
@@ -471,6 +484,7 @@ and/or use gtk-doc annotations. ''')
             if field.bits:
                 attrs.append(('bits', str(field.bits)))
             with self.tagcontext('field', attrs):
+                self._write_attributes(field)
                 self._write_type(field.type)
 
     def _write_signal(self, signal):
@@ -480,5 +494,6 @@ and/or use gtk-doc annotations. ''')
         self._append_version(signal, attrs)
         self._append_deprecated(signal, attrs)
         with self.tagcontext('glib:signal', attrs):
+            self._write_attributes(signal)
             self._write_return_type(signal.retval)
             self._write_parameters(signal.parameters)
index 9f222e9..3068e62 100644 (file)
@@ -84,6 +84,8 @@ class XMLWriter(object):
     # Private
 
     def _open_tag(self, tag_name, attributes=None):
+        if attributes is None:
+            attributes = []
         attrs = collect_attributes(
             tag_name, attributes, self._indent,
             self._indent_char,
index 053d459..f427345 100644 (file)
@@ -62,6 +62,7 @@ and/or use gtk-doc annotations.  -->
            glib:type-name="AnnotationObject"
            glib:get-type="annotation_object_get_type"
            glib:type-struct="ObjectClass">
+      <attribute name="org.example.Test" value="cows"/>
       <method name="method" c:identifier="annotation_object_method">
         <return-value transfer-ownership="none">
           <type name="int" c:type="gint"/>
@@ -426,6 +427,12 @@ type.">
           <type name="GObject.Object" c:type="GObject*"/>
         </return-value>
       </method>
+      <method name="extra_annos" c:identifier="annotation_object_extra_annos">
+        <attribute name="org.foobar" value="testvalue"/>
+        <return-value transfer-ownership="none">
+          <type name="none" c:type="void"/>
+        </return-value>
+      </method>
       <property name="string-property"
                 version="1.0"
                 deprecated="Use better-string-property instead"
index 643ffb2..2075143 100644 (file)
@@ -48,6 +48,7 @@
       </parameters>
     </callback>
     <class name="Object" parent="GObject.Object" glib:type-struct="ObjectClass" glib:type-name="AnnotationObject" glib:get-type="annotation_object_get_type">
+      <attribute name="org.example.Test" value="cows"/>
       <field name="parent_instance">
         <type name="GObject.Object"/>
       </field>
           <type name="GObject.Object"/>
         </return-value>
       </method>
+      <method name="extra_annos" c:identifier="annotation_object_extra_annos">
+        <attribute name="org.foobar" value="testvalue"/>
+        <return-value transfer-ownership="none">
+          <type name="none"/>
+        </return-value>
+      </method>
       <property name="string-property" writable="1" construct="1">
         <type name="utf8"/>
       </property>
index 20c2729..41508e2 100644 (file)
@@ -565,5 +565,14 @@ annotation_versioned (void)
 {
 }
 
+/**
+ * annotation_object_extra_annos:
+ *
+ * Attributes: (org.foobar testvalue)
+ */
+void
+annotation_object_extra_annos (AnnotationObject *object)
+{
+}
 
 char backslash_parsing_tester_2 = '\\';
index 6904f15..79a686d 100644 (file)
@@ -25,6 +25,8 @@ typedef GList* (*AnnotationListCallback) (GList *in);
  * AnnotationObject:
  *
  * This is an object used to test annotations.
+ *
+ * Attributes: (org.example.Test cows)
  */
 typedef struct _AnnotationObject          AnnotationObject;
 typedef struct _AnnotationObjectClass     AnnotationObjectClass;
@@ -111,6 +113,8 @@ void     annotation_versioned           (void);
 char **  annotation_string_zero_terminated (void);
 void     annotation_string_zero_terminated_out (char ***out);
 
+void     annotation_object_extra_annos (AnnotationObject *object);
+
 /**
  * AnnotationStruct:
  *
index b95e052..c4d7291 100644 (file)
@@ -2,6 +2,7 @@
 /* GObject introspection: IDL generator
  *
  * Copyright (C) 2005 Matthias Clasen
+ * Copyright (C) 2008,2009 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -317,6 +318,21 @@ write_type_info (const gchar *namespace,
     }
 }
 
+static void
+write_attributes (Xml *file,
+                   GIBaseInfo *info)
+{
+  GIAttributeIter iter = { 0, };
+  char *name, *value;
+
+  while (g_base_info_iterate_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,
@@ -355,6 +371,8 @@ write_field_info (const gchar *namespace,
   if (size)
     xml_printf (file, " bits=\"%d\"", size);
 
+  write_attributes (file, (GIBaseInfo*) info);
+
   type = g_field_info_get_type (info);
 
   if (branch)
@@ -386,6 +404,8 @@ write_callable_info (const gchar    *namespace,
   GITypeInfo *type;
   gint i;
 
+  write_attributes (file, (GIBaseInfo*) info);
+
   type = g_callable_info_get_return_type (info);
 
   xml_start_element (file, "return-value");
@@ -604,6 +624,8 @@ write_struct_info (const gchar  *namespace,
   is_gtype_struct = g_struct_info_is_gtype_struct (info);
   if (is_gtype_struct)
     xml_printf (file, " glib:is-gtype-struct=\"1\"");
+
+  write_attributes (file, (GIBaseInfo*) info);
        
   size = g_struct_info_get_size (info);
   if (show_all && size >= 0)
@@ -650,6 +672,8 @@ write_value_info (const gchar *namespace,
   if (deprecated)
     xml_printf (file, " deprecated=\"1\"");
   
+  write_attributes (file, (GIBaseInfo*) info);
+
   xml_end_element (file, "member");
 }
 
@@ -746,6 +770,8 @@ write_constant_info (const gchar    *namespace,
 
   write_type_info (namespace, type, file);
 
+  write_attributes (file, (GIBaseInfo*) info);
+
   xml_end_element (file, "constant");
   
   g_base_info_unref ((GIBaseInfo *)type);
@@ -780,7 +806,8 @@ write_enum_info (const gchar *namespace,
   
   if (deprecated)
     xml_printf (file, " deprecated=\"1\"");
-       
+
+  write_attributes (file, (GIBaseInfo*) info);
 
   for (i = 0; i < g_enum_info_get_n_values (info); i++)
     {
@@ -902,7 +929,9 @@ write_property_info (const gchar    *namespace,
 
   if (flags & G_PARAM_CONSTRUCT_ONLY)
     xml_printf (file, " construct-only=\"1\"");
-    
+
+  write_attributes (file, (GIBaseInfo*) info);
+
   type = g_property_info_get_type (info);
 
   write_type_info (namespace, type, file);
@@ -954,7 +983,8 @@ write_object_info (const gchar  *namespace,
 
   if (deprecated)
     xml_printf (file, " deprecated=\"1\"");
-       
+
+  write_attributes (file, (GIBaseInfo*) info);
 
   if (g_object_info_get_n_interfaces (info) > 0)
     {
@@ -1044,6 +1074,8 @@ write_interface_info (const gchar     *namespace,
   if (deprecated)
     xml_printf (file, " deprecated=\"1\"");
 
+  write_attributes (file, (GIBaseInfo*) info);
+
   if (g_interface_info_get_n_prerequisites (info) > 0)
     {
       for (i = 0; i < g_interface_info_get_n_prerequisites (info); i++)
@@ -1141,11 +1173,13 @@ write_union_info (const gchar *namespace,
          
   if (deprecated)
     xml_printf (file, " deprecated=\"1\"");
-       
+
   size = g_union_info_get_size (info);
   if (show_all && size >= 0)
     xml_printf (file, " size=\"%d\"", size);
 
+  write_attributes (file, (GIBaseInfo*) info);
+
   if (g_union_info_is_discriminated (info))
     {
       gint offset;