From e9e3866133275cedcec6b9953adaf88fefcabbf8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sat, 29 Oct 2022 18:09:23 +0100 Subject: Add copy and free function annotations for POD types Plain Old Data (POD) types with or without a representation in the GType type system can still have a copy and/or a free function. We should allow annotating these types with their corresponding functions for copying their data into a new instance, and freeing their data. From a language bindings perspective, POD types should have a boxed GType wrapper around them, so they can use the generic GBoxed API to copy and free instances; from a documentation perspective, though, it'd be good to have a way to match a structured type, like a struct or a union, with its copy and free functions. In order to do that, we add two new header block annotations: - (copy-func function_name) - (free-func function_name) These annotations work exactly like ref-func and unref-func for typed instances: /** * GdkRGBA: (copy-func gdk_rgba_copy) * (free-func gdk_rgba_free) * @red: ... * @green: ... * @blue: ... * @alpha: ... * * ... */ The function is stored in the GIR data as two new attributes for the `` and `` elements: The annotations are not mandatory. See: #14 --- girepository/girnode.c | 22 ++++++++++++++ girepository/girnode.h | 6 ++++ girepository/girparser.c | 17 ++++++++++- girepository/girwriter.c | 18 +++++++++++ girepository/gistructinfo.c | 54 +++++++++++++++++++++++++++++++++ girepository/gistructinfo.h | 7 ++++- girepository/gitypelib-internal.h | 20 ++++++++----- girepository/giunioninfo.c | 54 +++++++++++++++++++++++++++++++++ girepository/giunioninfo.h | 6 ++++ giscanner/annotationparser.py | 63 +++++++++++++++++++++++++++++++++++---- giscanner/ast.py | 12 ++++++++ giscanner/girparser.py | 6 ++++ giscanner/girwriter.py | 8 +++++ giscanner/maintransformer.py | 9 ++++++ 14 files changed, 287 insertions(+), 15 deletions(-) diff --git a/girepository/girnode.c b/girepository/girnode.c index 34b7dea8..4a8d78b9 100644 --- a/girepository/girnode.c +++ b/girepository/girnode.c @@ -370,6 +370,8 @@ _g_ir_node_free (GIrNode *node) g_free (node->name); g_free (struct_->gtype_name); g_free (struct_->gtype_init); + g_free (struct_->copy_func); + g_free (struct_->free_func); for (l = struct_->members; l; l = l->next) _g_ir_node_free ((GIrNode *)l->data); @@ -403,6 +405,8 @@ _g_ir_node_free (GIrNode *node) g_free (node->name); g_free (union_->gtype_name); g_free (union_->gtype_init); + g_free (union_->copy_func); + g_free (union_->free_func); _g_ir_node_free ((GIrNode *)union_->discriminator_type); for (l = union_->members; l; l = l->next) @@ -755,6 +759,10 @@ _g_ir_node_get_full_size_internal (GIrNode *parent, size += ALIGN_VALUE (strlen (struct_->gtype_name) + 1, 4); if (struct_->gtype_init) size += ALIGN_VALUE (strlen (struct_->gtype_init) + 1, 4); + if (struct_->copy_func) + size += ALIGN_VALUE (strlen (struct_->copy_func) + 1, 4); + if (struct_->free_func) + size += ALIGN_VALUE (strlen (struct_->free_func) + 1, 4); for (l = struct_->members; l; l = l->next) size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); } @@ -855,6 +863,10 @@ _g_ir_node_get_full_size_internal (GIrNode *parent, size += ALIGN_VALUE (strlen (union_->gtype_name) + 1, 4); if (union_->gtype_init) size += ALIGN_VALUE (strlen (union_->gtype_init) + 1, 4); + if (union_->copy_func) + size += ALIGN_VALUE (strlen (union_->copy_func) + 1, 4); + if (union_->free_func) + size += ALIGN_VALUE (strlen (union_->free_func) + 1, 4); for (l = union_->members; l; l = l->next) size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); for (l = union_->discriminators; l; l = l->next) @@ -1956,6 +1968,11 @@ _g_ir_node_build_typelib (GIrNode *node, blob->gtype_init = 0; } + if (struct_->copy_func) + blob->copy_func = _g_ir_write_string (struct_->copy_func, strings, data, offset2); + if (struct_->free_func) + blob->free_func = _g_ir_write_string (struct_->free_func, strings, data, offset2); + blob->n_fields = 0; blob->n_methods = 0; @@ -2040,6 +2057,11 @@ _g_ir_node_build_typelib (GIrNode *node, blob->discriminator_offset = union_->discriminator_offset; + if (union_->copy_func) + blob->copy_func = _g_ir_write_string (union_->copy_func, strings, data, offset2); + if (union_->free_func) + blob->free_func = _g_ir_write_string (union_->free_func, strings, data, offset2); + /* We don't support Union discriminators right now. */ /* if (union_->discriminator_type) diff --git a/girepository/girnode.h b/girepository/girnode.h index 7b8c97f6..45a81477 100644 --- a/girepository/girnode.h +++ b/girepository/girnode.h @@ -328,6 +328,9 @@ struct _GIrNodeStruct gchar *gtype_name; gchar *gtype_init; + gchar *copy_func; + gchar *free_func; + gint alignment; gint size; @@ -346,6 +349,9 @@ struct _GIrNodeUnion gchar *gtype_name; gchar *gtype_init; + gchar *copy_func; + gchar *free_func; + gint alignment; gint size; diff --git a/girepository/girparser.c b/girepository/girparser.c index e5878b43..5ac4aefe 100644 --- a/girepository/girparser.c +++ b/girepository/girparser.c @@ -477,7 +477,9 @@ parse_basic (const char *str) static GIrNodeType * parse_type_internal (GIrModule *module, - const gchar *str, char **next, gboolean in_glib, + const gchar *str, + char **next, + gboolean in_glib, gboolean in_gobject) { const BasicTypeInfo *basic; @@ -2560,6 +2562,8 @@ start_struct (GMarkupParseContext *context, const gchar *gtype_init; const gchar *gtype_struct; const gchar *foreign; + const gchar *copy_func; + const gchar *free_func; GIrNodeStruct *struct_; if (!(strcmp (element_name, "record") == 0 && @@ -2579,6 +2583,8 @@ start_struct (GMarkupParseContext *context, gtype_init = find_attribute ("glib:get-type", attribute_names, attribute_values); gtype_struct = find_attribute ("glib:is-gtype-struct-for", attribute_names, attribute_values); foreign = find_attribute ("foreign", attribute_names, attribute_values); + copy_func = find_attribute ("copy-function", attribute_names, attribute_values); + free_func = find_attribute ("free-function", attribute_names, attribute_values); if (name == NULL && ctx->node_stack == NULL) { @@ -2615,6 +2621,9 @@ start_struct (GMarkupParseContext *context, struct_->foreign = (g_strcmp0 (foreign, "1") == 0); + struct_->copy_func = g_strdup (copy_func); + struct_->free_func = g_strdup (free_func); + if (ctx->node_stack == NULL) ctx->current_module->entries = g_list_append (ctx->current_module->entries, struct_); @@ -2634,6 +2643,8 @@ start_union (GMarkupParseContext *context, const gchar *deprecated; const gchar *typename; const gchar *typeinit; + const gchar *copy_func; + const gchar *free_func; GIrNodeUnion *union_; if (!(strcmp (element_name, "union") == 0 && @@ -2650,6 +2661,8 @@ start_union (GMarkupParseContext *context, deprecated = find_attribute ("deprecated", attribute_names, attribute_values); typename = find_attribute ("glib:type-name", attribute_names, attribute_values); typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + copy_func = find_attribute ("copy-function", attribute_names, attribute_values); + free_func = find_attribute ("free-function", attribute_names, attribute_values); if (name == NULL && ctx->node_stack == NULL) { @@ -2663,6 +2676,8 @@ start_union (GMarkupParseContext *context, ((GIrNode *)union_)->name = g_strdup (name ? name : ""); union_->gtype_name = g_strdup (typename); union_->gtype_init = g_strdup (typeinit); + union_->copy_func = g_strdup (copy_func); + union_->free_func = g_strdup (free_func); if (deprecated) union_->deprecated = TRUE; else diff --git a/girepository/girwriter.c b/girepository/girwriter.c index ea148f32..276bb676 100644 --- a/girepository/girwriter.c +++ b/girepository/girwriter.c @@ -642,6 +642,7 @@ write_struct_info (const gchar *namespace, const gchar *name; const gchar *type_name; const gchar *type_init; + const gchar *func; gboolean deprecated; gboolean is_gtype_struct; gboolean foreign; @@ -676,6 +677,14 @@ write_struct_info (const gchar *namespace, if (is_gtype_struct) xml_printf (file, " glib:is-gtype-struct=\"1\""); + func = g_struct_info_get_copy_function (info); + if (func) + xml_printf (file, " copy-function=\"%s\"", func); + + func = g_struct_info_get_free_function (info); + if (func) + xml_printf (file, " free-function=\"%s\"", func); + write_attributes (file, (GIBaseInfo*) info); size = g_struct_info_get_size (info); @@ -1237,6 +1246,7 @@ write_union_info (const gchar *namespace, const gchar *name; const gchar *type_name; const gchar *type_init; + const gchar *func; gboolean deprecated; gint i; gint size; @@ -1260,6 +1270,14 @@ write_union_info (const gchar *namespace, if (file->show_all && size >= 0) xml_printf (file, " size=\"%d\"", size); + func = g_union_info_get_copy_function (info); + if (func) + xml_printf (file, " copy-function=\"%s\"", func); + + func = g_union_info_get_free_function (info); + if (func) + xml_printf (file, " free-function=\"%s\"", func); + write_attributes (file, (GIBaseInfo*) info); if (g_union_info_is_discriminated (info)) diff --git a/girepository/gistructinfo.c b/girepository/gistructinfo.c index a9ad73f6..c13cb716 100644 --- a/girepository/gistructinfo.c +++ b/girepository/gistructinfo.c @@ -281,3 +281,57 @@ g_struct_info_is_gtype_struct (GIStructInfo *info) return blob->is_gtype_struct; } + +/** + * g_struct_info_get_copy_function: + * @info: a struct information blob + * + * Retrieves the name of the copy function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the copy function + * + * Since: 1.76 + */ +const char * +g_struct_info_get_copy_function (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_STRUCT_INFO (info), NULL); + + blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->copy_func) + return g_typelib_get_string (rinfo->typelib, blob->copy_func); + + return NULL; +} + +/** + * g_struct_info_get_free_function: + * @info: a struct information blob + * + * Retrieves the name of the free function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the free function + * + * Since: 1.76 + */ +const char * +g_struct_info_get_free_function (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_STRUCT_INFO (info), NULL); + + blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->free_func) + return g_typelib_get_string (rinfo->typelib, blob->free_func); + + return NULL; +} diff --git a/girepository/gistructinfo.h b/girepository/gistructinfo.h index 6e70fea7..8ff10d3c 100644 --- a/girepository/gistructinfo.h +++ b/girepository/gistructinfo.h @@ -75,7 +75,12 @@ gboolean g_struct_info_is_gtype_struct (GIStructInfo *info); GI_AVAILABLE_IN_ALL gboolean g_struct_info_is_foreign (GIStructInfo *info); -G_END_DECLS +GI_AVAILABLE_IN_1_76 +const char * g_struct_info_get_copy_function (GIStructInfo *info); + +GI_AVAILABLE_IN_1_76 +const char * g_struct_info_get_free_function (GIStructInfo *info); +G_END_DECLS #endif /* __GISTRUCTINFO_H__ */ diff --git a/girepository/gitypelib-internal.h b/girepository/gitypelib-internal.h index 77c4d6f3..494ab2b3 100644 --- a/girepository/gitypelib-internal.h +++ b/girepository/gitypelib-internal.h @@ -792,8 +792,10 @@ typedef struct { * @size: The size of the struct in bytes. * @n_fields: TODO * @n_methods: TODO - * @reserved2: Reserved for future use. - * @reserved3: Reserved for future use. + * @copy_func: String pointing to a function which can be called to copy + * the contents of this struct type + * @free_func: String pointing to a function which can be called to free + * the contents of this struct type * * TODO */ @@ -817,8 +819,8 @@ typedef struct { guint16 n_fields; guint16 n_methods; - guint32 reserved2; - guint32 reserved3; + guint32 copy_func; + guint32 free_func; } StructBlob; /** @@ -835,8 +837,10 @@ typedef struct { * @size: TODO * @n_fields: Length of the arrays * @n_functions: TODO - * @reserved2: Reserved for future use. - * @reserved3: Reserved for future use. + * @copy_func: String pointing to a function which can be called to copy + * the contents of this union type + * @free_func: String pointing to a function which can be called to free + * the contents of this union type * @discriminator_offset: Offset from the beginning of the union where the * discriminator of a discriminated union is located. The value 0xFFFF * indicates that the discriminator offset is unknown. @@ -861,8 +865,8 @@ typedef struct { guint16 n_fields; guint16 n_functions; - guint32 reserved2; - guint32 reserved3; + guint32 copy_func; + guint32 free_func; gint32 discriminator_offset; SimpleTypeBlob discriminator_type; diff --git a/girepository/giunioninfo.c b/girepository/giunioninfo.c index 0089f11c..32a2e307 100644 --- a/girepository/giunioninfo.c +++ b/girepository/giunioninfo.c @@ -267,3 +267,57 @@ g_union_info_get_alignment (GIUnionInfo *info) return blob->alignment; } + +/** + * g_union_info_get_copy_function: + * @info: a union information blob + * + * Retrieves the name of the copy function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the copy function + * + * Since: 1.76 + */ +const char * +g_union_info_get_copy_function (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_UNION_INFO (info), NULL); + + blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->copy_func) + return g_typelib_get_string (rinfo->typelib, blob->copy_func); + + return NULL; +} + +/** + * g_union_info_get_free_function: + * @info: a union information blob + * + * Retrieves the name of the free function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the free function + * + * Since: 1.76 + */ +const char * +g_union_info_get_free_function (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_UNION_INFO (info), NULL); + + blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->free_func) + return g_typelib_get_string (rinfo->typelib, blob->free_func); + + return NULL; +} diff --git a/girepository/giunioninfo.h b/girepository/giunioninfo.h index 62951b85..4087ed4d 100644 --- a/girepository/giunioninfo.h +++ b/girepository/giunioninfo.h @@ -77,6 +77,12 @@ gsize g_union_info_get_size (GIUnionInfo *info); GI_AVAILABLE_IN_ALL gsize g_union_info_get_alignment (GIUnionInfo *info); +GI_AVAILABLE_IN_1_76 +const char * g_union_info_get_copy_function (GIUnionInfo *info); + +GI_AVAILABLE_IN_1_76 +const char * g_union_info_get_free_function (GIUnionInfo *info); + G_END_DECLS diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py index 3952b24b..e567115a 100644 --- a/giscanner/annotationparser.py +++ b/giscanner/annotationparser.py @@ -193,11 +193,13 @@ ANN_ARRAY = 'array' ANN_ATTRIBUTES = 'attributes' ANN_CLOSURE = 'closure' ANN_CONSTRUCTOR = 'constructor' +ANN_COPY_FUNC = 'copy-func' ANN_DEFAULT_VALUE = 'default-value' ANN_DESTROY = 'destroy' ANN_ELEMENT_TYPE = 'element-type' ANN_EMITTER = 'emitter' ANN_FOREIGN = 'foreign' +ANN_FREE_FUNC = 'free-func' ANN_GET_PROPERTY = 'get-property' ANN_GET_VALUE_FUNC = 'get-value-func' ANN_GETTER = 'getter' @@ -790,6 +792,18 @@ class GtkDocAnnotatable(object): self._validate_annotation(position, ann_name, options, exact_n_options=0) + def _do_validate_copy_func(self, position, ann_name, options): + ''' + Validate the ``(copy-func)`` annotation. + + :param position: :class:`giscanner.message.Position` of the line in the source file + containing the annotation to be validated + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(position, ann_name, options, exact_n_options=1) + def _do_validate_default_value(self, position, ann_name, options): ''' Validate the ``(default-value)`` annotation. @@ -851,6 +865,18 @@ class GtkDocAnnotatable(object): self._validate_annotation(position, ann_name, options, exact_n_options=0) + def _do_validate_free_func(self, position, ann_name, options): + ''' + Validate the ``(free-func)`` annotation. + + :param position: :class:`giscanner.message.Position` of the line in the source file + containing the annotation to be validated + :param ann_name: name of the annotation holding the options to validate + :param options: annotation options to validate + ''' + + self._validate_annotation(position, ann_name, options, exact_n_options=1) + def _do_validate_get_property(self, position, ann_name, options): ''' Validate the ``(get-property)`` annotation. @@ -1127,9 +1153,24 @@ class GtkDocParameter(GtkDocAnnotatable): __slots__ = ('name', 'description') - valid_annotations = (ANN_ALLOW_NONE, ANN_ARRAY, ANN_ATTRIBUTES, ANN_CLOSURE, ANN_DESTROY, - ANN_ELEMENT_TYPE, ANN_IN, ANN_INOUT, ANN_OUT, ANN_SCOPE, ANN_SKIP, - ANN_TRANSFER, ANN_TYPE, ANN_OPTIONAL, ANN_NULLABLE, ANN_NOT) + valid_annotations = ( + ANN_ALLOW_NONE, + ANN_ARRAY, + ANN_ATTRIBUTES, + ANN_CLOSURE, + ANN_DESTROY, + ANN_ELEMENT_TYPE, + ANN_IN, + ANN_INOUT, + ANN_OUT, + ANN_SCOPE, + ANN_SKIP, + ANN_TRANSFER, + ANN_TYPE, + ANN_OPTIONAL, + ANN_NULLABLE, + ANN_NOT, + ) def __init__(self, name, position=None): GtkDocAnnotatable.__init__(self, position) @@ -1151,8 +1192,18 @@ class GtkDocTag(GtkDocAnnotatable): __slots__ = ('name', 'value', 'description') - valid_annotations = (ANN_ALLOW_NONE, ANN_ARRAY, ANN_ATTRIBUTES, ANN_ELEMENT_TYPE, ANN_SKIP, - ANN_TRANSFER, ANN_TYPE, ANN_NULLABLE, ANN_OPTIONAL, ANN_NOT) + valid_annotations = ( + ANN_ALLOW_NONE, + ANN_ARRAY, + ANN_ATTRIBUTES, + ANN_ELEMENT_TYPE, + ANN_SKIP, + ANN_TRANSFER, + ANN_TYPE, + ANN_NULLABLE, + ANN_OPTIONAL, + ANN_NOT, + ) def __init__(self, name, position=None): GtkDocAnnotatable.__init__(self, position) @@ -1182,9 +1233,11 @@ class GtkDocCommentBlock(GtkDocAnnotatable): valid_annotations = ( ANN_ATTRIBUTES, ANN_CONSTRUCTOR, + ANN_COPY_FUNC, ANN_DEFAULT_VALUE, ANN_EMITTER, ANN_FOREIGN, + ANN_FREE_FUNC, ANN_GET_PROPERTY, ANN_GET_VALUE_FUNC, ANN_GETTER, diff --git a/giscanner/ast.py b/giscanner/ast.py index dac326cb..bbfbe267 100644 --- a/giscanner/ast.py +++ b/giscanner/ast.py @@ -1127,6 +1127,12 @@ class Record(Compound): # If non-None, this record defines the FooClass C structure # for some Foo GObject (or similar for GInterface) self.is_gtype_struct_for = None + # If non-None, this record has a copy function for heap + # allocated instances + self.copy_func = None + # If non-None, this record has a free function for heap + # allocated instances + self.free_func = None class Union(Compound): @@ -1145,6 +1151,12 @@ class Union(Compound): c_symbol_prefix=c_symbol_prefix, disguised=disguised, tag_name=tag_name) + # If non-None, this union has a copy function for heap + # allocated instances + self.copy_func = None + # If non-None, this union has a free function for heap + # allocated instances + self.free_func = None class Boxed(Node, Registered): diff --git a/giscanner/girparser.py b/giscanner/girparser.py index 5fc22899..2f8c5f66 100644 --- a/giscanner/girparser.py +++ b/giscanner/girparser.py @@ -462,14 +462,20 @@ class GIRParser(object): def _parse_record(self, node, anonymous=False): struct = self._parse_compound(ast.Record, node) is_gtype_struct_for = node.attrib.get(_glibns('is-gtype-struct-for')) + copy_func = node.attrib.get('copy-function') + free_func = node.attrib.get('free-function') if is_gtype_struct_for is not None: struct.is_gtype_struct_for = self._namespace.type_from_name(is_gtype_struct_for) + struct.copy_func = copy_func + struct.free_func = free_func if not anonymous: self._namespace.append(struct) return struct def _parse_union(self, node, anonymous=False): union = self._parse_compound(ast.Union, node) + union.copy_func = node.attrib.get('copy-function') + union.free_func = node.attrib.get('free-function') if not anonymous: self._namespace.append(union) return union diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py index a3b3bc3e..810acad6 100644 --- a/giscanner/girwriter.py +++ b/giscanner/girwriter.py @@ -593,6 +593,10 @@ class GIRWriter(XMLWriter): is_gtype_struct = True attrs.append(('glib:is-gtype-struct-for', self._type_to_name(record.is_gtype_struct_for))) + if record.copy_func: + attrs.append(('copy-function', record.copy_func)) + if record.free_func: + attrs.append(('free-function', record.free_func)) self._append_version(record, attrs) self._append_node_generic(record, attrs) self._append_registered(record, attrs) @@ -621,6 +625,10 @@ class GIRWriter(XMLWriter): self._append_registered(union, attrs) if union.c_symbol_prefix: attrs.append(('c:symbol-prefix', union.c_symbol_prefix)) + if union.copy_func: + attrs.append(('copy-function', union.copy_func)) + if union.free_func: + attrs.append(('free-function', union.free_func)) with self.tagcontext('union', attrs): self._write_generic(union) if union.fields: diff --git a/giscanner/maintransformer.py b/giscanner/maintransformer.py index 285712c7..4393e160 100644 --- a/giscanner/maintransformer.py +++ b/giscanner/maintransformer.py @@ -28,11 +28,13 @@ from .annotationparser import ( ANN_ATTRIBUTES, ANN_CLOSURE, ANN_CONSTRUCTOR, + ANN_COPY_FUNC, ANN_DEFAULT_VALUE, ANN_DESTROY, ANN_ELEMENT_TYPE, ANN_EMITTER, ANN_FOREIGN, + ANN_FREE_FUNC, ANN_GET_PROPERTY, ANN_GET_VALUE_FUNC, ANN_GETTER, @@ -310,6 +312,13 @@ class MainTransformer(object): node.get_value_func = annotation[0] if annotation else None if isinstance(node, ast.Constant): self._apply_annotations_constant(node) + if isinstance(node, (ast.Record, ast.Union)): + block = self._get_block(node) + if block: + annotation = block.annotations.get(ANN_COPY_FUNC) + node.copy_func = annotation[0] if annotation else None + annotation = block.annotations.get(ANN_FREE_FUNC) + node.free_func = annotation[0] if annotation else None return True def _adjust_container_type(self, parent, node, annotations): -- cgit v1.2.1