[gobject-introspection/ebassi/property-annotation: 3/17] Add new annotations for property accessors




commit baa676e8139d973107f1957cae7980e67175f09f
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Wed Jun 16 19:17:27 2021 +0100

    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 fadfe56f..07e0d056 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 e4ce85a4..2cb5a960 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 f62b1de6..99ec91a9 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 d092df10..b913da00 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 267542f9..95ee1f97 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 9a0d95c7..7cb6f622 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 da200a65..0fe85ed4 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -379,6 +379,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 54c957c0..f8e61985 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)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]