Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2009-02-20 02:48:51 (GMT)
committer Colin Walters <walters@verbum.org>2009-03-03 22:26:37 (GMT)
commitd5215b23315e7c9c22c6a32218bb6f8027a9dd4c (patch)
treee8d9e360099c756677ad23a1039f4d05533008b9
parent2bdd2bf9303ae0e718c00ec84078336e722138f1 (diff)
Bug 571548 - Generic attributes
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.
-rw-r--r--docs/reference/gi-sections.txt3
-rw-r--r--girepository/ginfo.c143
-rw-r--r--girepository/girepository.h14
-rw-r--r--girepository/girmodule.c88
-rw-r--r--girepository/girnode.c38
-rw-r--r--girepository/girnode.h7
-rw-r--r--girepository/girparser.c52
-rw-r--r--girepository/gtypelib.c22
-rw-r--r--girepository/gtypelib.h31
-rw-r--r--giscanner/annotationparser.py60
-rw-r--r--giscanner/ast.py1
-rw-r--r--giscanner/girwriter.py15
-rw-r--r--giscanner/xmlwriter.py2
-rw-r--r--tests/scanner/annotation-1.0-expected.gir7
-rw-r--r--tests/scanner/annotation-1.0-expected.tgir7
-rw-r--r--tests/scanner/annotation.c9
-rw-r--r--tests/scanner/annotation.h4
-rw-r--r--tools/generate.c42
18 files changed, 450 insertions, 95 deletions
diff --git a/docs/reference/gi-sections.txt b/docs/reference/gi-sections.txt
index 96ef8fb..6177d10 100644
--- a/docs/reference/gi-sections.txt
+++ b/docs/reference/gi-sections.txt
@@ -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
diff --git a/girepository/ginfo.c b/girepository/ginfo.c
index 1c34ee2..fcc5f09 100644
--- a/girepository/ginfo.c
+++ b/girepository/ginfo.c
@@ -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 *
diff --git a/girepository/girepository.h b/girepository/girepository.h
index 61a9116..4059adc 100644
--- a/girepository/girepository.h
+++ b/girepository/girepository.h
@@ -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);
diff --git a/girepository/girmodule.c b/girepository/girmodule.c
index 5abd31f..96ee9ce 100644
--- a/girepository/girmodule.c
+++ b/girepository/girmodule.c
@@ -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;
}
diff --git a/girepository/girnode.c b/girepository/girnode.c
index fb96d8d..22c0aee 100644
--- a/girepository/girnode.c
+++ b/girepository/girnode.c
@@ -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
diff --git a/girepository/girnode.h b/girepository/girnode.h
index 45c2bb0..2a1f6b2 100644
--- a/girepository/girnode.h
+++ b/girepository/girnode.h
@@ -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,
diff --git a/girepository/girparser.c b/girepository/girparser.c
index e08b3fc..006ed3b 100644
--- a/girepository/girparser.c
+++ b/girepository/girparser.c
@@ -68,6 +68,7 @@ typedef enum
STATE_INTERFACE_CONSTANT,
STATE_ALIAS,
STATE_TYPE,
+ STATE_ATTRIBUTE,
STATE_UNKNOWN
} ParseState;
@@ -1865,6 +1866,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,
const gchar **attribute_names,
@@ -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)
diff --git a/girepository/gtypelib.c b/girepository/gtypelib.c
index 6ff00bf..a578c67 100644
--- a/girepository/gtypelib.c
+++ b/girepository/gtypelib.c
@@ -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;
}
diff --git a/girepository/gtypelib.h b/girepository/gtypelib.h
index 7a2838f..db5fe11 100644
--- a/girepository/gtypelib.h
+++ b/girepository/gtypelib.h
@@ -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;
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 003ff2c..0798b80 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -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
diff --git a/giscanner/ast.py b/giscanner/ast.py
index e708258..d2bae87 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -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
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
index df52709..88510b0 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -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)
diff --git a/giscanner/xmlwriter.py b/giscanner/xmlwriter.py
index 9f222e9..3068e62 100644
--- a/giscanner/xmlwriter.py
+++ b/giscanner/xmlwriter.py
@@ -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,
diff --git a/tests/scanner/annotation-1.0-expected.gir b/tests/scanner/annotation-1.0-expected.gir
index 053d459..f427345 100644
--- a/tests/scanner/annotation-1.0-expected.gir
+++ b/tests/scanner/annotation-1.0-expected.gir
@@ -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"
diff --git a/tests/scanner/annotation-1.0-expected.tgir b/tests/scanner/annotation-1.0-expected.tgir
index 643ffb2..2075143 100644
--- a/tests/scanner/annotation-1.0-expected.tgir
+++ b/tests/scanner/annotation-1.0-expected.tgir
@@ -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>
@@ -323,6 +324,12 @@
<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>
diff --git a/tests/scanner/annotation.c b/tests/scanner/annotation.c
index 20c2729..41508e2 100644
--- a/tests/scanner/annotation.c
+++ b/tests/scanner/annotation.c
@@ -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 = '\\';
diff --git a/tests/scanner/annotation.h b/tests/scanner/annotation.h
index 6904f15..79a686d 100644
--- a/tests/scanner/annotation.h
+++ b/tests/scanner/annotation.h
@@ -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:
*
diff --git a/tools/generate.c b/tools/generate.c
index b95e052..c4d7291 100644
--- a/tools/generate.c
+++ b/tools/generate.c
@@ -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
@@ -318,6 +319,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,
GArgument *argument,
@@ -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;