Commit 22ae017f authored by Tomeu Vizoso's avatar Tomeu Vizoso

Support the (transfer) annotation for properties.

* girepository/*: Add g_property_info_get_ownership_transfer() and write
  the transfer attribute of properties into the typelib.

* giscanner/*: Parse the (transfer) annotation and write it into the .gir.

* tools/generate.c: Read the transfer annotation for properties and write
  to the .tgir.

https://bugzilla.gnome.org/show_bug.cgi?id=620484
parent 862cdbe9
......@@ -108,6 +108,7 @@ g_signal_info_true_stops_emit
GIPropertyInfo
g_property_info_get_flags
g_property_info_get_type
g_property_info_get_ownership_transfer
</SECTION>
<SECTION>
......
......@@ -361,12 +361,23 @@ case.">
</parameter>
</parameters>
</method>
<property name="bare" writable="1">
<property name="bare" writable="1" transfer-ownership="none">
<type name="GObject.Object" c:type="GObject"/>
</property>
<property name="boxed" writable="1">
<property name="boxed" writable="1" transfer-ownership="none">
<type name="TestBoxed" c:type="TestBoxed"/>
</property>
<property name="hash-table" writable="1" transfer-ownership="container">
<type name="GLib.HashTable" c:type="GHashTable">
<type name="utf8"/>
<type name="int8"/>
</type>
</property>
<property name="list" writable="1" transfer-ownership="none">
<type name="GLib.List" c:type="gpointer">
<type name="utf8"/>
</type>
</property>
<field name="parent_instance">
<type name="GObject.Object" c:type="GObject"/>
</field>
......@@ -376,6 +387,12 @@ case.">
<field name="boxed">
<type name="TestBoxed" c:type="TestBoxed*"/>
</field>
<field name="hash_table">
<type name="GLib.HashTable" c:type="GHashTable*"/>
</field>
<field name="list">
<type name="GLib.List" c:type="GList*"/>
</field>
<glib:signal name="test">
<return-value transfer-ownership="full">
<type name="none" c:type="void"/>
......@@ -605,7 +622,7 @@ case.">
</parameter>
</parameters>
</method>
<property name="testbool" writable="1">
<property name="testbool" writable="1" transfer-ownership="none">
<type name="boolean" c:type="gboolean"/>
</property>
<field name="parent_instance">
......
......@@ -324,7 +324,10 @@ and/or use gtk-doc annotations. -->
</parameter>
</parameters>
</method>
<property name="int" writable="1" construct="1">
<property name="int"
writable="1"
construct="1"
transfer-ownership="none">
<type name="int" c:type="gint"/>
</property>
<field name="parent_instance">
......
......@@ -1524,7 +1524,9 @@ G_DEFINE_TYPE(TestObj, test_obj, G_TYPE_OBJECT);
enum
{
PROP_TEST_OBJ_BARE = 1,
PROP_TEST_OBJ_BOXED
PROP_TEST_OBJ_BOXED,
PROP_TEST_OBJ_HASH_TABLE,
PROP_TEST_OBJ_LIST,
};
static void
......@@ -1534,6 +1536,7 @@ test_obj_set_property (GObject *object,
GParamSpec *pspec)
{
TestObj *self = TEST_OBJECT (object);
GList *list;
switch (property_id)
{
......@@ -1546,7 +1549,25 @@ test_obj_set_property (GObject *object,
test_boxed_free (self->boxed);
self->boxed = g_value_dup_boxed (value);
break;
case PROP_TEST_OBJ_HASH_TABLE:
if (self->hash_table)
g_hash_table_unref (self->hash_table);
self->hash_table = g_hash_table_ref (g_value_get_boxed (value));
break;
case PROP_TEST_OBJ_LIST:
if (self->list != NULL)
{
for (list = self->list; list != NULL; list = g_list_next (list))
g_free (list->data);
g_list_free (self->list);
}
self->list = NULL;
for (list = g_value_get_pointer (value); list != NULL; list = g_list_next (list))
self->list = g_list_append (self->list, g_strdup (list->data));
break;
default:
/* We don't have any other property... */
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
......@@ -1571,6 +1592,16 @@ test_obj_get_property (GObject *object,
case PROP_TEST_OBJ_BOXED:
g_value_set_boxed (value, self->boxed);
break;
case PROP_TEST_OBJ_HASH_TABLE:
if (self->hash_table != NULL)
g_hash_table_ref (self->hash_table);
g_value_set_boxed (value, self->hash_table);
break;
case PROP_TEST_OBJ_LIST:
g_value_set_pointer (value, self->list);
break;
default:
/* We don't have any other property... */
......@@ -1660,6 +1691,35 @@ test_obj_class_init (TestObjClass *klass)
g_object_class_install_property (gobject_class,
PROP_TEST_OBJ_BOXED,
pspec);
/**
* TestObj:hash-table:
*
* Type: GLib.HashTable<utf8,int8>
* Transfer: container
*/
pspec = g_param_spec_boxed ("hash-table",
"GHashTable property",
"A contained GHashTable",
G_TYPE_HASH_TABLE,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_TEST_OBJ_HASH_TABLE,
pspec);
/**
* TestObj:list:
*
* Type: GLib.List<utf8>
* Transfer: none
*/
pspec = g_param_spec_pointer ("list",
"GList property",
"A contained GList",
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_TEST_OBJ_LIST,
pspec);
klass->matrix = test_obj_default_matrix;
}
......@@ -1669,6 +1729,7 @@ test_obj_init (TestObj *obj)
{
obj->bare = NULL;
obj->boxed = NULL;
obj->hash_table = NULL;
}
TestObj *
......
......@@ -268,6 +268,8 @@ struct _TestObj
GObject *bare;
TestBoxed *boxed;
GHashTable *hash_table;
GList *list;
};
struct _TestObjClass
......
......@@ -57,3 +57,30 @@ g_property_info_get_type (GIPropertyInfo *info)
return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, rinfo->offset + G_STRUCT_OFFSET (PropertyBlob, type));
}
/**
* g_property_info_get_ownership_transfer:
* @info: a #GIPropertyInfo
*
* Obtain the ownership transfer for this property. See #GITransfer for more
* information about transfer values.
*
* Returns: the transfer
*/
GITransfer
g_property_info_get_ownership_transfer (GIPropertyInfo *info)
{
GIRealInfo *rinfo = (GIRealInfo *)info;
PropertyBlob *blob;
g_return_val_if_fail (info != NULL, -1);
g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), -1);
blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset];
if (blob->transfer_ownership)
return GI_TRANSFER_EVERYTHING;
else if (blob->transfer_container_ownership)
return GI_TRANSFER_CONTAINER;
else
return GI_TRANSFER_NOTHING;
}
......@@ -35,6 +35,7 @@ G_BEGIN_DECLS
GParamFlags g_property_info_get_flags (GIPropertyInfo *info);
GITypeInfo * g_property_info_get_type (GIPropertyInfo *info);
GITransfer g_property_info_get_ownership_transfer (GIPropertyInfo *info);
G_END_DECLS
......
......@@ -1648,6 +1648,8 @@ g_ir_node_build_typelib (GIrNode *node,
blob->writable = prop->writable;
blob->construct = prop->construct;
blob->construct_only = prop->construct_only;
blob->transfer_ownership = prop->transfer;
blob->transfer_container_ownership = prop->shallow_transfer;
blob->reserved = 0;
g_ir_node_build_typelib ((GIrNode *)prop->type,
......
......@@ -178,6 +178,8 @@ struct _GIrNodeProperty
gboolean writable;
gboolean construct;
gboolean construct_only;
gboolean transfer;
gboolean shallow_transfer;
GIrNodeType *type;
};
......
......@@ -840,6 +840,44 @@ start_function (GMarkupParseContext *context,
return TRUE;
}
static void
parse_property_transfer (GIrNodeProperty *property,
const gchar *transfer,
ParseContext *ctx)
{
if (transfer == NULL)
{
GIrNodeInterface *iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
g_warning ("required attribute 'transfer-ownership' for property '%s' in "
"type '%s.%s'", property->node.name, ctx->namespace,
iface->node.name);
}
else if (strcmp (transfer, "none") == 0)
{
property->transfer = FALSE;
property->shallow_transfer = FALSE;
}
else if (strcmp (transfer, "container") == 0)
{
property->transfer = FALSE;
property->shallow_transfer = TRUE;
}
else if (strcmp (transfer, "full") == 0)
{
property->transfer = TRUE;
property->shallow_transfer = FALSE;
}
else
{
GIrNodeInterface *iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
g_warning ("Unknown transfer-ownership value: '%s' for property '%s' in "
"type '%s.%s'", transfer, property->node.name, ctx->namespace,
iface->node.name);
}
}
static void
parse_param_transfer (GIrNodeParam *param, const gchar *transfer, const gchar *name)
{
......@@ -1237,12 +1275,14 @@ start_property (GMarkupParseContext *context,
const gchar *writable;
const gchar *construct;
const gchar *construct_only;
const gchar *transfer;
name = find_attribute ("name", attribute_names, attribute_values);
readable = find_attribute ("readable", attribute_names, attribute_values);
writable = find_attribute ("writable", attribute_names, attribute_values);
construct = find_attribute ("construct", attribute_names, attribute_values);
construct_only = find_attribute ("construct-only", attribute_names, attribute_values);
transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values);
if (name == NULL)
MISSING_ATTRIBUTE (context, error, element_name, "name");
......@@ -1274,6 +1314,8 @@ start_property (GMarkupParseContext *context,
else
property->construct_only = FALSE;
parse_property_transfer (property, transfer, ctx);
iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, property);
......
......@@ -187,6 +187,26 @@ write_type_name_attribute (const gchar *namespace,
xml_printf (file, "\"");
}
static void
write_ownership_transfer (GITransfer transfer,
Xml *file)
{
switch (transfer)
{
case GI_TRANSFER_NOTHING:
xml_printf (file, " transfer-ownership=\"none\"");
break;
case GI_TRANSFER_CONTAINER:
xml_printf (file, " transfer-ownership=\"container\"");
break;
case GI_TRANSFER_EVERYTHING:
xml_printf (file, " transfer-ownership=\"full\"");
break;
default:
g_assert_not_reached ();
}
}
static void
write_type_info (const gchar *namespace,
GITypeInfo *info,
......@@ -443,20 +463,7 @@ write_callable_info (const gchar *namespace,
xml_start_element (file, "return-value");
switch (g_callable_info_get_caller_owns (info))
{
case GI_TRANSFER_NOTHING:
xml_printf (file, " transfer-ownership=\"none\"");
break;
case GI_TRANSFER_CONTAINER:
xml_printf (file, " transfer-ownership=\"container\"");
break;
case GI_TRANSFER_EVERYTHING:
xml_printf (file, " transfer-ownership=\"full\"");
break;
default:
g_assert_not_reached ();
}
write_ownership_transfer (g_callable_info_get_caller_owns (info), file);
if (g_callable_info_may_return_null (info))
xml_printf (file, " allow-none=\"1\"");
......@@ -477,20 +484,7 @@ write_callable_info (const gchar *namespace,
xml_printf (file, " name=\"%s\"",
g_base_info_get_name ((GIBaseInfo *) arg));
switch (g_arg_info_get_ownership_transfer (arg))
{
case GI_TRANSFER_NOTHING:
xml_printf (file, " transfer-ownership=\"none\"");
break;
case GI_TRANSFER_CONTAINER:
xml_printf (file, " transfer-ownership=\"container\"");
break;
case GI_TRANSFER_EVERYTHING:
xml_printf (file, " transfer-ownership=\"full\"");
break;
default:
g_assert_not_reached ();
}
write_ownership_transfer (g_arg_info_get_ownership_transfer (arg), file);
switch (g_arg_info_get_direction (arg))
{
......@@ -968,6 +962,8 @@ write_property_info (const gchar *namespace,
if (flags & G_PARAM_CONSTRUCT_ONLY)
xml_printf (file, " construct-only=\"1\"");
write_ownership_transfer (g_property_info_get_ownership_transfer (info), file);
write_attributes (file, (GIBaseInfo*) info);
type = g_property_info_get_type (info);
......
......@@ -815,17 +815,25 @@ typedef struct {
* @writable:
* @construct:
* @construct_only: The ParamFlags used when registering the property.
* @transfer_ownership: When writing, the type containing the property takes
* ownership of the value. When reading, the returned value needs to be released
* by the caller.
* @transfer_container_ownership: For container types indicates that the
* ownership of the container, but not of its contents, is transferred. This is
* typically the case when reading lists of statically allocated things.
* @type: Describes the type of the property.
*/
typedef struct {
guint32 name;
guint32 deprecated : 1;
guint32 readable : 1;
guint32 writable : 1;
guint32 construct : 1;
guint32 construct_only : 1;
guint32 reserved :27;
guint32 deprecated : 1;
guint32 readable : 1;
guint32 writable : 1;
guint32 construct : 1;
guint32 construct_only : 1;
guint32 transfer_ownership : 1;
guint32 transfer_container_ownership : 1;
guint32 reserved :25;
guint32 reserved2;
......
......@@ -241,13 +241,29 @@ typedef enum
/**
* GITransfer:
* @GI_TRANSFER_NOTHING: transfer nothing to the caller
* @GI_TRANSFER_CONTAINER: transfer the container (eg list, array,
* hashtable), but no the contents to the caller.
* @GI_TRANSFER_EVERYTHING: transfer everything to the caller.
*
* Represent the transfer ownership information of a #GICallableInfo or
* a #GIArgInfo.
* @GI_TRANSFER_NOTHING: transfer nothing from the callee (function or the type
* instance the property belongs to) to the caller. The callee retains the
* ownership of the transfer and the caller doesn't need to do anything to free
* up the resources of this transfer.
* @GI_TRANSFER_CONTAINER: transfer the container (list, array, hash table) from
* the callee to the caller. The callee retains the ownership of the individual
* items in the container and the caller has to free up the container resources
* (g_list_free()/g_hash_table_destroy() etc) of this transfer.
* @GI_TRANSFER_EVERYTHING: transfer everything, eg the container and its
* contents from the callee to the caller. This is the case when the callee
* creates a copy of all the data it returns. The caller is responsible for
* cleaning up the container and item resources of this transfer.
*
* The transfer is the exchange of data between two parts, from the callee to
* the caller. The callee is either a function/method/signal or an
* object/interface where a property is defined. The caller is the side
* accessing a property or calling a function.
* #GITransfer specifies who's responsible for freeing the resources after the
* ownership transfer is complete. In case of a containing type such as a list,
* an array or a hash table the container itself is specified differently from
* the items within the container itself. Each container is freed differently,
* check the documentation for the types themselves for information on how to
* free them.
*/
typedef enum {
GI_TRANSFER_NOTHING,
......
......@@ -24,8 +24,8 @@ import re
import sys
from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
Interface, List, Map, Parameter, Record, Return, Type, Union,
Varargs,
Interface, List, Map, Parameter, Property, Record, Return,
Type, Union, Varargs,
default_array_types,
BASIC_GIR_TYPES,
PARAM_DIRECTION_INOUT,
......@@ -53,6 +53,7 @@ TAG_RETURNS_ALT = 'return value'
TAG_ATTRIBUTES = 'attributes'
TAG_RENAME_TO = 'rename to'
TAG_TYPE = 'type'
TAG_TRANSFER = 'transfer'
# Options - annotations for parameters and return values
OPT_ALLOW_NONE = 'allow-none'
......@@ -418,6 +419,14 @@ class AnnotationApplier(object):
self._parse_node_common(prop, block)
if block:
prop.doc = block.comment
transfer_tag = self._get_tag(block, TAG_TRANSFER)
if transfer_tag is not None:
options = {OPT_TRANSFER: Option(transfer_tag.value)}
else:
options = {}
prop.transfer = self._extract_transfer(parent, prop, options)
type_tag = self._get_tag(block, TAG_TYPE)
if type_tag:
prop.type = self._resolve(type_tag.value, prop.type)
......@@ -946,5 +955,7 @@ class AnnotationApplier(object):
return PARAM_TRANSFER_FULL
elif isinstance(node, Field):
return PARAM_TRANSFER_NONE
elif isinstance(node, Property):
return PARAM_TRANSFER_NONE
else:
raise AssertionError(node)
......@@ -521,12 +521,12 @@ class Constant(Node):
self.name, self.type, self.value)
class Property(Node):
class Property(TypeContainer):
def __init__(self, name, type_name, readable, writable,
construct, construct_only, ctype=None):
Node.__init__(self, name)
construct, construct_only, ctype=None, transfer=None):
self.type = Type(type_name, ctype)
TypeContainer.__init__(self, name, self.type, transfer)
self.readable = readable
self.writable = writable
self.construct = construct
......
......@@ -400,6 +400,7 @@ and/or use gtk-doc annotations. ''')
attrs.append(('construct', '1'))
if prop.construct_only:
attrs.append(('construct-only', '1'))
attrs.append(('transfer-ownership', prop.transfer))
if prop.doc:
attrs.append(('doc', prop.doc))
with self.tagcontext('property', attrs):
......
......@@ -484,7 +484,10 @@ type.">
<type name="none" c:type="void"/>
</return-value>
</method>
<property name="function-property" writable="1" construct="1">
<property name="function-property"
writable="1"
construct="1"
transfer-ownership="none">
<type name="Callback" c:type="gpointer"/>
</property>
<property name="string-property"
......@@ -493,6 +496,7 @@ type.">
deprecated-version="1.2"
writable="1"
construct="1"
transfer-ownership="none"
doc="This is a property which is a string">
<type name="utf8" c:type="gchararray"/>
</property>
......
......@@ -356,10 +356,10 @@
<type name="none"/>
</return-value>
</method>
<property name="function-property" writable="1" construct="1">
<property name="function-property" writable="1" construct="1" transfer-ownership="none">
<type name="Callback"/>
</property>
<property name="string-property" writable="1" construct="1">
<property name="string-property" writable="1" construct="1" transfer-ownership="none">
<type name="utf8"/>
</property>
<glib:signal name="doc-empty-arg-parsing" when="LAST">
......
......@@ -449,7 +449,10 @@ uses a C sugar return type.">
</parameter>
</parameters>
</method>
<property name="string" writable="1" construct="1">
<property name="string"
writable="1"
construct="1"
transfer-ownership="none">
<type name="utf8" c:type="gchararray"/>
</property>
<field name="parent_instance">
......
......@@ -333,7 +333,7 @@
</parameter>
</parameters>
</method>
<property name="string" writable="1" construct="1">
<property name="string" writable="1" construct="1" transfer-ownership="none">
<type name="utf8"/>
</property>
<glib:signal name="signal" when="LAST">
......
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