Commit 11cfe386 authored by David Zeuthen's avatar David Zeuthen

Allow attributes on parameters and return values

Any annotation where the key has a dot in the name will go into the
attribute list. For example

  * @arg: (foo.bar baz): some arg

the parameter @arg will get the attribute with key foo.bar and value
baz. This also works for.

  * Returns: (foo.bar2 baz2): the return value

Also add tests for this new feature.

See https://bugzilla.gnome.org/show_bug.cgi?id=571548Signed-off-by: default avatarDavid Zeuthen <davidz@redhat.com>
parent 751ffa01
......@@ -74,6 +74,8 @@ GICallableInfo
g_callable_info_get_return_type
g_callable_info_get_caller_owns
g_callable_info_may_return_null
g_callable_info_get_return_attribute
g_callable_info_iterate_return_attributes
g_callable_info_get_n_args
g_callable_info_get_arg
g_callable_info_load_arg
......
......@@ -475,7 +475,7 @@ g_base_info_get_attribute (GIBaseInfo *info,
static int
cmp_attribute (const void *av,
const void *bv)
const void *bv)
{
const AttributeBlob *a = av;
const AttributeBlob *b = bv;
......@@ -488,13 +488,25 @@ cmp_attribute (const void *av,
return 1;
}
static AttributeBlob *
find_first_attribute (GIRealInfo *rinfo)
/*
* _attribute_blob_find_first:
* @GIBaseInfo: A #GIBaseInfo.
* @blob_offset: The offset for the blob to find the first attribute for.
*
* Searches for the first #AttributeBlob for @blob_offset and returns
* it if found.
*
* Returns: A pointer to #AttributeBlob or %NULL if not found.
*/
AttributeBlob *
_attribute_blob_find_first (GIBaseInfo *info,
guint32 blob_offset)
{
GIRealInfo *rinfo = (GIRealInfo *) info;
Header *header = (Header *)rinfo->typelib->data;
AttributeBlob blob, *first, *res, *previous;
blob.offset = rinfo->offset;
blob.offset = blob_offset;
first = (AttributeBlob *) &rinfo->typelib->data[header->attributes];
......@@ -505,7 +517,7 @@ find_first_attribute (GIRealInfo *rinfo)
return NULL;
previous = res - 1;
while (previous >= first && previous->offset == rinfo->offset)
while (previous >= first && previous->offset == blob_offset)
{
res = previous;
previous = res - 1;
......@@ -563,7 +575,7 @@ g_base_info_iterate_attributes (GIBaseInfo *info,
if (iterator->data != NULL)
next = (AttributeBlob *) iterator->data;
else
next = find_first_attribute (rinfo);
next = _attribute_blob_find_first (info, rinfo->offset);
if (next == NULL || next->offset != rinfo->offset || next >= after)
return FALSE;
......
......@@ -19,6 +19,8 @@
* Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <glib.h>
#include <girepository.h>
......@@ -259,3 +261,77 @@ g_callable_info_load_arg (GICallableInfo *info,
_g_info_init ((GIRealInfo*)arg, GI_INFO_TYPE_ARG, rinfo->repository, (GIBaseInfo*)info, rinfo->typelib,
offset + header->signature_blob_size + n * header->arg_blob_size);
}
/**
* g_callable_info_get_return_attribute:
* @info: a #GICallableInfo
* @name: a freeform string naming an attribute
*
* Retrieve an arbitrary attribute associated with the return value.
*
* Returns: The value of the attribute, or %NULL if no such attribute exists
*/
const gchar *
g_callable_info_get_return_attribute (GICallableInfo *info,
const gchar *name)
{
GIAttributeIter iter = { 0, };
gchar *curname, *curvalue;
while (g_callable_info_iterate_return_attributes (info, &iter, &curname, &curvalue))
{
if (g_strcmp0 (name, curname) == 0)
return (const gchar*) curvalue;
}
return NULL;
}
/**
* g_callable_info_iterate_return_attributes:
* @info: a #GICallableInfo
* @iterator: a #GIAttributeIter structure, must be initialized; see below
* @name: (out) (transfer none): Returned name, must not be freed
* @value: (out) (transfer none): Returned name, must not be freed
*
* Iterate over all attributes associated with the return value. The
* iterator structure is typically stack allocated, and must have its
* first member initialized to %NULL.
*
* Both the @name and @value should be treated as constants
* and must not be freed.
*
* See g_base_info_iterate_attributes() for an example of how to use a
* similar API.
*
* Returns: %TRUE if there are more attributes
*/
gboolean
g_callable_info_iterate_return_attributes (GICallableInfo *info,
GIAttributeIter *iterator,
char **name,
char **value)
{
GIRealInfo *rinfo = (GIRealInfo *)info;
Header *header = (Header *)rinfo->typelib->data;
AttributeBlob *next, *after;
guint32 blob_offset;
after = (AttributeBlob *) &rinfo->typelib->data[header->attributes +
header->n_attributes * header->attribute_blob_size];
blob_offset = signature_offset (info);
if (iterator->data != NULL)
next = (AttributeBlob *) iterator->data;
else
next = _attribute_blob_find_first (info, blob_offset);
if (next == NULL || next->offset != blob_offset || next >= after)
return FALSE;
*name = (gchar*) g_typelib_get_string (rinfo->typelib, next->name);
*value = (gchar*) g_typelib_get_string (rinfo->typelib, next->value);
iterator->data = next + 1;
return TRUE;
}
......@@ -39,6 +39,12 @@ G_BEGIN_DECLS
GITypeInfo * g_callable_info_get_return_type (GICallableInfo *info);
void g_callable_info_load_return_type (GICallableInfo *info,
GITypeInfo *type);
const gchar * g_callable_info_get_return_attribute (GICallableInfo *info,
const gchar *name);
gboolean g_callable_info_iterate_return_attributes (GICallableInfo *info,
GIAttributeIter *iterator,
char **name,
char **value);
GITransfer g_callable_info_get_caller_owns (GICallableInfo *info);
gboolean g_callable_info_may_return_null (GICallableInfo *info);
gint g_callable_info_get_n_args (GICallableInfo *info);
......
......@@ -1678,6 +1678,14 @@ g_ir_node_build_typelib (GIrNode *node,
blob->symbol = write_string (function->symbol, strings, data, offset2);
blob->signature = signature;
/* function->result is special since it doesn't appear in the serialized format but
* we do want the attributes for it to appear
*/
build->nodes_with_attributes = g_list_prepend (build->nodes_with_attributes, function->result);
build->n_attributes += g_hash_table_size (((GIrNode *) function->result)->attributes);
g_assert (((GIrNode *) function->result)->offset == 0);
((GIrNode *) function->result)->offset = signature;
g_debug ("building function '%s'", function->symbol);
g_ir_node_build_typelib ((GIrNode *)function->result->type,
......@@ -1770,6 +1778,14 @@ g_ir_node_build_typelib (GIrNode *node,
blob->name = write_string (node->name, strings, data, offset2);
blob->signature = signature;
/* signal->result is special since it doesn't appear in the serialized format but
* we do want the attributes for it to appear
*/
build->nodes_with_attributes = g_list_prepend (build->nodes_with_attributes, signal->result);
build->n_attributes += g_hash_table_size (((GIrNode *) signal->result)->attributes);
g_assert (((GIrNode *) signal->result)->offset == 0);
((GIrNode *) signal->result)->offset = signature;
g_ir_node_build_typelib ((GIrNode *)signal->result->type,
node, build, &signature, offset2);
......
......@@ -1990,7 +1990,14 @@ start_attribute (GMarkupParseContext *context,
curnode = CURRENT_NODE (ctx);
g_hash_table_insert (curnode->attributes, g_strdup (name), g_strdup (value));
if (ctx->current_typed && ctx->current_typed->type == G_IR_NODE_PARAM)
{
g_hash_table_insert (ctx->current_typed->attributes, g_strdup (name), g_strdup (value));
}
else
{
g_hash_table_insert (curnode->attributes, g_strdup (name), g_strdup (value));
}
return TRUE;
}
......
......@@ -358,7 +358,7 @@ write_type_info (const gchar *namespace,
static void
write_attributes (Xml *file,
GIBaseInfo *info)
GIBaseInfo *info)
{
GIAttributeIter iter = { 0, };
char *name, *value;
......@@ -371,6 +371,21 @@ write_attributes (Xml *file,
}
}
static void
write_return_value_attributes (Xml *file,
GICallableInfo *info)
{
GIAttributeIter iter = { 0, };
char *name, *value;
while (g_callable_info_iterate_return_attributes (info, &iter, &name, &value))
{
xml_start_element (file, "attribute");
xml_printf (file, " name=\"%s\" value=\"%s\"", name, value);
xml_end_element (file, "attribute");
}
}
static void
write_constant_value (const gchar *namespace,
GITypeInfo *info,
......@@ -467,6 +482,8 @@ write_callable_info (const gchar *namespace,
if (g_callable_info_may_return_null (info))
xml_printf (file, " allow-none=\"1\"");
write_return_value_attributes (file, info);
write_type_info (namespace, type, file);
xml_end_element (file, "return-value");
......@@ -528,6 +545,8 @@ write_callable_info (const gchar *namespace,
if (g_arg_info_get_destroy (arg) >= 0)
xml_printf (file, " destroy=\"%d\"", g_arg_info_get_destroy (arg));
write_attributes (file, (GIBaseInfo*) arg);
type = g_arg_info_get_type (arg);
write_type_info (namespace, type, file);
......
......@@ -1110,6 +1110,10 @@ gboolean g_typelib_validate (GTypelib *typelib,
GError **error);
/* defined in gibaseinfo.c */
AttributeBlob *_attribute_blob_find_first (GIBaseInfo *info,
guint32 blob_offset);
G_END_DECLS
#endif /* __G_TYPELIB_H__ */
......
......@@ -635,6 +635,12 @@ class AnnotationApplier(object):
if tag is not None and tag.comment is not None:
node.doc = tag.comment
for key in options:
if '.' in key:
value = options.get(key)
if value:
node.attributes.append((key, value.one()))
def _extract_direction(self, node, options):
caller_allocates = False
if (OPT_INOUT in options or
......
......@@ -192,6 +192,7 @@ and/or use gtk-doc annotations. ''')
if return_.doc:
attrs.append(('doc', return_.doc))
with self.tagcontext('return-value', attrs):
self._write_attributes(return_)
self._write_type(return_.type)
def _write_parameters(self, parameters):
......@@ -224,6 +225,7 @@ and/or use gtk-doc annotations. ''')
if parameter.doc:
attrs.append(('doc', parameter.doc))
with self.tagcontext('parameter', attrs):
self._write_attributes(parameter)
self._write_type(parameter.type)
def _type_to_string(self, ntype):
......
......@@ -503,6 +503,23 @@ type.">
<field name="parent_instance">
<type name="GObject.Object" c:type="GObject"/>
</field>
<glib:signal name="attribute-signal"
doc="This signal tests a signal with attributes.">
<return-value transfer-ownership="full" doc="the return value">
<attribute name="some.annotation.foo3" value="val3"/>
<type name="utf8" c:type="gchararray"/>
</return-value>
<parameters>
<parameter name="arg1" transfer-ownership="none" doc="a value">
<attribute name="some.annotation.foo1" value="val1"/>
<type name="utf8" c:type="gchararray"/>
</parameter>
<parameter name="arg2" transfer-ownership="none" doc="another value">
<attribute name="some.annotation.foo2" value="val2"/>
<type name="utf8" c:type="gchararray"/>
</parameter>
</parameters>
</glib:signal>
<glib:signal name="doc-empty-arg-parsing"
doc="This signal tests an empty document argument (@arg1)">
<return-value transfer-ownership="full">
......@@ -562,6 +579,23 @@ it says it&apos;s pointer but it&apos;s actually a string."
</array>
</field>
</record>
<function name="attribute_func" c:identifier="annotation_attribute_func">
<return-value transfer-ownership="none" doc="The return value.">
<attribute name="yet.another.annotation" value="another_value"/>
<attribute name="some.other.annotation" value="value2"/>
<type name="int" c:type="gint"/>
</return-value>
<parameters>
<parameter name="object" transfer-ownership="none">
<type name="Object" c:type="AnnotationObject*"/>
</parameter>
<parameter name="data" transfer-ownership="none" doc="Some data.">
<attribute name="another.annotation" value="blahvalue"/>
<attribute name="some.annotation" value="value"/>
<type name="utf8" c:type="gchar*"/>
</parameter>
</parameters>
</function>
<function name="custom_destroy"
c:identifier="annotation_custom_destroy"
doc="Test messing up the heuristic of closure/destroy-notification
......
......@@ -362,6 +362,22 @@
<property name="string-property" writable="1" construct="1" transfer-ownership="none">
<type name="utf8"/>
</property>
<glib:signal name="attribute-signal" when="LAST">
<return-value transfer-ownership="full">
<attribute name="some.annotation.foo3" value="val3"/>
<type name="utf8"/>
</return-value>
<parameters>
<parameter name="arg1" transfer-ownership="none">
<attribute name="some.annotation.foo1" value="val1"/>
<type name="utf8"/>
</parameter>
<parameter name="arg2" transfer-ownership="none">
<attribute name="some.annotation.foo2" value="val2"/>
<type name="utf8"/>
</parameter>
</parameters>
</glib:signal>
<glib:signal name="doc-empty-arg-parsing" when="LAST">
<return-value transfer-ownership="full">
<type name="none"/>
......@@ -407,6 +423,23 @@
</array>
</field>
</record>
<function name="attribute_func" c:identifier="annotation_attribute_func">
<return-value transfer-ownership="none">
<attribute name="yet.another.annotation" value="another_value"/>
<attribute name="some.other.annotation" value="value2"/>
<type name="int"/>
</return-value>
<parameters>
<parameter name="object" transfer-ownership="none">
<type name="Object"/>
</parameter>
<parameter name="data" transfer-ownership="none">
<attribute name="another.annotation" value="blahvalue"/>
<attribute name="some.annotation" value="value"/>
<type name="utf8"/>
</parameter>
</parameters>
</function>
<function name="custom_destroy" c:identifier="annotation_custom_destroy">
<return-value transfer-ownership="none">
<type name="none"/>
......
......@@ -14,6 +14,7 @@ enum {
STRING_SIGNAL,
LIST_SIGNAL,
DOC_EMPTY_ARG_PARSING,
ATTRIBUTE_SIGNAL,
LAST_SIGNAL
};
......@@ -118,6 +119,28 @@ annotation_object_class_init (AnnotationObjectClass *klass)
(GSignalCMarshaller)g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
/**
* AnnotationObject::attribute-signal:
* @annotation: the annotation object
* @arg1: (some.annotation.foo1 val1): a value
* @arg2: (some.annotation.foo2 val2): another value
*
* This signal tests a signal with attributes.
*
* Returns: (some.annotation.foo3 val3): the return value
*/
annotation_object_signals[ATTRIBUTE_SIGNAL] =
g_signal_new ("attribute-signal",
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL, /* marshaller */
G_TYPE_STRING,
2,
G_TYPE_STRING,
G_TYPE_STRING);
/**
* AnnotationObject:string-property:
*
......@@ -700,4 +723,18 @@ annotation_ptr_array (GPtrArray *array)
{
}
/**
* annotation_attribute_func:
* @object: A #AnnotationObject.
* @data: (some.annotation value) (another.annotation blahvalue): Some data.
*
* Returns: (some.other.annotation value2) (yet.another.annotation another_value): The return value.
*/
gint
annotation_attribute_func (AnnotationObject *object,
const gchar *data)
{
return 42;
}
char backslash_parsing_tester_2 = '\\';
......@@ -139,6 +139,9 @@ void annotation_custom_destroy (AnnotationCallback callback,
char * annotation_get_source_file (void);
void annotation_set_source_file (const char *fname);
gint annotation_attribute_func (AnnotationObject *object,
const gchar *data);
/**
* AnnotationStruct:
*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment