From d5215b23315e7c9c22c6a32218bb6f8027a9dd4c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 20 Feb 2009 02:48:51 +0000 Subject: 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. --- 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. + * + * + * Iterating over attributes + * + * 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); + * } + * } + * + * + * + * 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"> + @@ -426,6 +427,12 @@ type."> + + + + + + + @@ -323,6 +324,12 @@ + + + + + + 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; -- cgit v0.9.1