From 3ec400b09fb15c9f7392b77b0c3fb324ee08beed Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 16 Jun 2021 19:17:27 +0100 Subject: Add new annotations for property accessors We introduce two new annotations: - (set-property PROPERTY_NAME) - (get-property PROPERTY_NAME) These annotations are valid inside function blocks for methods on objects and interfaces, and define whether a function is a property accessor, e.g.: /** * gtk_widget_set_name: (set-property name) * @self: ... * @name: ... * * ... */ /** * gtk_widget_get_name: (get-property name) * @self: ... * * ... * * Returns: ... */ The annotations are transformed into the GIR data as attributes: - glib:set-property="PROPERTY_NAME" - glib:get-property="PROPERTY_NAME" The underlying typelib data has had flags for setter and getter functions for a while, but they have never been plugged into the GIR data or the introspection scanner. Now they are; you can retrieve the GIPropertyInfo from a GIFunctionInfo that has the GI_FUNCTION_IS_SETTER or GI_FUNCTION_IS_GETTER flags set. Fixes: #13 --- girepository/girnode.c | 20 ++++++++++++++++++-- girepository/girnode.h | 1 + girepository/girparser.c | 23 +++++++++++++++++++++++ girepository/girwriter.c | 20 ++++++++++++++++---- giscanner/annotationparser.py | 35 ++++++++++++++++++++++++++++++++--- giscanner/ast.py | 2 ++ giscanner/girparser.py | 2 ++ giscanner/girwriter.py | 4 ++++ giscanner/maintransformer.py | 17 +++++++++++++---- 9 files changed, 111 insertions(+), 13 deletions(-) diff --git a/girepository/girnode.c b/girepository/girnode.c index 796f2001..5e74b0b0 100644 --- a/girepository/girnode.c +++ b/girepository/girnode.c @@ -218,6 +218,7 @@ _g_ir_node_free (GIrNode *node) g_free (node->name); g_free (function->symbol); + g_free (function->property); _g_ir_node_free ((GIrNode *)function->result); for (l = function->parameters; l; l = l->next) _g_ir_node_free ((GIrNode *)l->data); @@ -1648,8 +1649,8 @@ _g_ir_node_build_typelib (GIrNode *node, blob->blob_type = BLOB_TYPE_FUNCTION; blob->deprecated = function->deprecated; blob->is_static = !function->is_method; - blob->setter = function->is_setter; - blob->getter = function->is_getter; + blob->setter = FALSE; + blob->getter = FALSE; blob->constructor = function->is_constructor; blob->wraps_vfunc = function->wraps_vfunc; blob->throws = function->throws; /* Deprecated. Also stored in SignatureBlob. */ @@ -1658,6 +1659,21 @@ _g_ir_node_build_typelib (GIrNode *node, blob->symbol = _g_ir_write_string (function->symbol, strings, data, offset2); blob->signature = signature; + if (function->is_setter || function->is_getter) + { + int index = get_index_of_member_type ((GIrNodeInterface*)parent, + G_IR_NODE_PROPERTY, + function->property); + if (index == -1) + { + g_error ("Unknown property %s for accessor %s", function->property, node->name); + } + + blob->setter = function->is_setter; + blob->getter = function->is_getter; + blob->index = (guint) index; + } + /* function->result is special since it doesn't appear in the serialized format but * we do want the attributes for it to appear */ diff --git a/girepository/girnode.h b/girepository/girnode.h index 9b42accd..f2645b68 100644 --- a/girepository/girnode.h +++ b/girepository/girnode.h @@ -103,6 +103,7 @@ struct _GIrNodeFunction gboolean instance_transfer_full; gchar *symbol; + char *property; GIrNodeParam *result; GList *parameters; diff --git a/girepository/girparser.c b/girepository/girparser.c index b6983d1a..2d30f201 100644 --- a/girepository/girparser.c +++ b/girepository/girparser.c @@ -807,6 +807,8 @@ start_function (GMarkupParseContext *context, const gchar *symbol; const gchar *deprecated; const gchar *throws; + const gchar *set_property; + const gchar *get_property; GIrNodeFunction *function; gboolean found = FALSE; ParseState in_embedded_state = STATE_NONE; @@ -854,6 +856,8 @@ start_function (GMarkupParseContext *context, symbol = find_attribute ("c:identifier", attribute_names, attribute_values); deprecated = find_attribute ("deprecated", attribute_names, attribute_values); throws = find_attribute ("throws", attribute_names, attribute_values); + set_property = find_attribute ("glib:set-property", attribute_names, attribute_values); + get_property = find_attribute ("glib:get-property", attribute_names, attribute_values); if (name == NULL) { @@ -889,6 +893,25 @@ start_function (GMarkupParseContext *context, function->is_constructor = TRUE; else function->is_constructor = FALSE; + + if (set_property != NULL) + { + function->is_setter = TRUE; + function->is_getter = FALSE; + function->property = g_strdup (set_property); + } + else if (get_property != NULL) + { + function->is_setter = FALSE; + function->is_getter = TRUE; + function->property = g_strdup (get_property); + } + else + { + function->is_setter = FALSE; + function->is_getter = FALSE; + function->property = NULL; + } } else { diff --git a/girepository/girwriter.c b/girepository/girwriter.c index 104ee633..2393e7d7 100644 --- a/girepository/girwriter.c +++ b/girepository/girwriter.c @@ -586,10 +586,22 @@ write_function_info (const gchar *namespace, xml_printf (file, " name=\"%s\" c:identifier=\"%s\"", name, symbol); - if (flags & GI_FUNCTION_IS_SETTER) - xml_printf (file, " type=\"setter\""); - else if (flags & GI_FUNCTION_IS_GETTER) - xml_printf (file, " type=\"getter\""); + if ((flags & GI_FUNCTION_IS_SETTER) || (flags & GI_FUNCTION_IS_GETTER)) + { + GIPropertyInfo *property = g_function_info_get_property (info); + + if (property != NULL) + { + const char *property_name = g_base_info_get_name ((GIBaseInfo *)property); + + if (flags & GI_FUNCTION_IS_SETTER) + xml_printf (file, " glib:set-property=\"%s\"", property_name); + else if (flags & GI_FUNCTION_IS_GETTER) + xml_printf (file, " glib:get-property=\"%s\"", property_name); + + g_base_info_unref (property); + } + } if (deprecated) xml_printf (file, " deprecated=\"1\""); diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py index c0b30581..bf475d4c 100644 --- a/giscanner/annotationparser.py +++ b/giscanner/annotationparser.py @@ -196,6 +196,7 @@ ANN_CONSTRUCTOR = 'constructor' ANN_DESTROY = 'destroy' ANN_ELEMENT_TYPE = 'element-type' ANN_FOREIGN = 'foreign' +ANN_GET_PROPERTY = 'get-property' ANN_GET_VALUE_FUNC = 'get-value-func' ANN_IN = 'in' ANN_INOUT = 'inout' @@ -207,6 +208,7 @@ ANN_OUT = 'out' ANN_REF_FUNC = 'ref-func' ANN_RENAME_TO = 'rename-to' ANN_SCOPE = 'scope' +ANN_SET_PROPERTY = 'set-property' ANN_SET_VALUE_FUNC = 'set-value-func' ANN_SKIP = 'skip' ANN_TRANSFER = 'transfer' @@ -226,6 +228,7 @@ GI_ANNS = [ANN_ALLOW_NONE, ANN_DESTROY, ANN_ELEMENT_TYPE, ANN_FOREIGN, + ANN_GET_PROPERTY, ANN_GET_VALUE_FUNC, ANN_IN, ANN_INOUT, @@ -234,6 +237,7 @@ GI_ANNS = [ANN_ALLOW_NONE, ANN_REF_FUNC, ANN_RENAME_TO, ANN_SCOPE, + ANN_SET_PROPERTY, ANN_SET_VALUE_FUNC, ANN_SKIP, ANN_TRANSFER, @@ -812,6 +816,18 @@ class GtkDocAnnotatable(object): self._validate_annotation(position, ann_name, options, exact_n_options=0) + def _do_validate_get_property(self, position, ann_name, options): + ''' + Validate the ``(get-property)`` 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_value_func(self, position, ann_name, options): ''' Validate the ``(value-func)`` annotation. @@ -947,6 +963,18 @@ class GtkDocAnnotatable(object): self._validate_annotation(position, ann_name, options, exact_n_options=1, choices=SCOPE_OPTIONS) + def _do_validate_set_property(self, position, ann_name, options): + ''' + Validate the ``(set-property)`` 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_set_value_func(self, position, ann_name, options): ''' Validate the ``(value-func)`` annotation. @@ -1092,9 +1120,10 @@ class GtkDocCommentBlock(GtkDocAnnotatable): 'name', 'params', 'description', 'tags') #: Valid annotation names for the GTK-Doc comment block identifier part. - valid_annotations = (ANN_ATTRIBUTES, ANN_CONSTRUCTOR, ANN_FOREIGN, ANN_GET_VALUE_FUNC, - ANN_METHOD, ANN_REF_FUNC, ANN_RENAME_TO, ANN_SET_VALUE_FUNC, - ANN_SKIP, ANN_TRANSFER, ANN_TYPE, ANN_UNREF_FUNC, ANN_VALUE, ANN_VFUNC) + valid_annotations = (ANN_ATTRIBUTES, ANN_CONSTRUCTOR, ANN_FOREIGN, ANN_GET_PROPERTY, + ANN_GET_VALUE_FUNC, ANN_METHOD, ANN_REF_FUNC, ANN_RENAME_TO, + ANN_SET_PROPERTY, ANN_SET_VALUE_FUNC, ANN_SKIP, ANN_TRANSFER, + ANN_TYPE, ANN_UNREF_FUNC, ANN_VALUE, ANN_VFUNC) def __init__(self, name, position=None): GtkDocAnnotatable.__init__(self, position) diff --git a/giscanner/ast.py b/giscanner/ast.py index e11fc988..a85e8879 100644 --- a/giscanner/ast.py +++ b/giscanner/ast.py @@ -752,6 +752,8 @@ class Function(Callable): self.shadows = None # C symbol string self.moved_to = None # namespaced function name string self.internal_skipped = False # if True, this func will not be written to GIR + self.set_property = None # Property name + self.get_property = None # Property name def clone(self): clone = copy.copy(self) diff --git a/giscanner/girparser.py b/giscanner/girparser.py index 9f124d89..a20de210 100644 --- a/giscanner/girparser.py +++ b/giscanner/girparser.py @@ -382,6 +382,8 @@ class GIRParser(object): else: assert False + func.set_property = node.attrib.get(_glibns('set-property'), None) + func.get_property = node.attrib.get(_glibns('get-property'), None) func.shadows = node.attrib.get('shadows', None) func.shadowed_by = node.attrib.get('shadowed-by', None) func.moved_to = node.attrib.get('moved-to', None) diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py index d276e923..9551f18f 100644 --- a/giscanner/girwriter.py +++ b/giscanner/girwriter.py @@ -224,6 +224,10 @@ class GIRWriter(XMLWriter): attrs.append(('shadows', func.shadows)) if func.moved_to is not None: attrs.append(('moved-to', func.moved_to)) + if func.set_property is not None: + attrs.append(('glib:set-property', func.set_property)) + if func.get_property is not None: + attrs.append(('glib:get-property', func.get_property)) self._write_callable(func, tag_name, attrs) def _write_function_macro(self, macro): diff --git a/giscanner/maintransformer.py b/giscanner/maintransformer.py index 9077a1d0..23073b4e 100644 --- a/giscanner/maintransformer.py +++ b/giscanner/maintransformer.py @@ -24,10 +24,11 @@ from . import message from .annotationparser import (TAG_DEPRECATED, TAG_SINCE, TAG_STABILITY, TAG_RETURNS) from .annotationparser import (ANN_ALLOW_NONE, ANN_ARRAY, ANN_ATTRIBUTES, ANN_CLOSURE, ANN_CONSTRUCTOR, ANN_DESTROY, ANN_ELEMENT_TYPE, ANN_FOREIGN, - ANN_GET_VALUE_FUNC, ANN_IN, ANN_INOUT, ANN_METHOD, ANN_OUT, - ANN_REF_FUNC, ANN_RENAME_TO, ANN_SCOPE, ANN_SET_VALUE_FUNC, - ANN_SKIP, ANN_TRANSFER, ANN_TYPE, ANN_UNREF_FUNC, ANN_VALUE, - ANN_VFUNC, ANN_NULLABLE, ANN_OPTIONAL, ANN_NOT) + ANN_GET_PROPERTY, ANN_GET_VALUE_FUNC, ANN_IN, ANN_INOUT, + ANN_METHOD, ANN_OUT, ANN_REF_FUNC, ANN_RENAME_TO, ANN_SCOPE, + ANN_SET_PROPERTY, ANN_SET_VALUE_FUNC, ANN_SKIP, ANN_TRANSFER, + ANN_TYPE, ANN_UNREF_FUNC, ANN_VALUE, ANN_VFUNC, ANN_NULLABLE, + ANN_OPTIONAL, ANN_NOT) from .annotationparser import (OPT_ARRAY_FIXED_SIZE, OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED, OPT_OUT_CALLEE_ALLOCATES, OPT_OUT_CALLER_ALLOCATES, OPT_TRANSFER_CONTAINER, OPT_TRANSFER_FLOATING, OPT_TRANSFER_NONE) @@ -773,6 +774,14 @@ class MainTransformer(object): if ANN_METHOD in block.annotations: node.is_method = True + set_property = block.annotations.get(ANN_SET_PROPERTY) + if set_property is not None and isinstance(node, ast.Function): + node.set_property = set_property[0] + + get_property = block.annotations.get(ANN_GET_PROPERTY) + if get_property is not None and isinstance(node, ast.Function): + node.get_property = get_property[0] + def _apply_annotations_alias(self, node, chain): block = self._get_block(node) self._apply_annotations_annotated(node, block) -- cgit v1.2.1