[gjs] object: Support accessing fields on GObjects



commit 8ebb803ef9223a8ddefe158bf6cd34fce15952e0
Author: Philip Chimento <philip chimento gmail com>
Date:   Sun Dec 18 19:28:19 2016 -0800

    object: Support accessing fields on GObjects
    
    This adds support for accessing fields in GObject instance structures as
    if they were JS properties.
    
    There are a few caveats:
    
    - If there is a GObject property of the same name, the field is
      inaccessible.
    
    - GObject Introspection doesn't support reading fields that are of a
      complex type, so we throw an exception if you try to read one of those.
    
    - Unfortunately the /*< private >*/ annotation doesn't seem to show up in
      the GIR file, so there is no difference between private and public
      fields (I think that only gtk-doc uses that annotation.)
    
    https://bugzilla.gnome.org/show_bug.cgi?id=563391

 gi/object.cpp                                    |   75 ++++++++++++++++++++++
 installed-tests/js/testEverythingEncapsulated.js |   25 +++++++
 2 files changed, 100 insertions(+), 0 deletions(-)
---
diff --git a/gi/object.cpp b/gi/object.cpp
index f742fc2..be3abde 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -298,6 +298,73 @@ get_prop_from_g_param(JSContext             *context,
     return true;
 }
 
+static bool
+get_prop_from_field(JSContext             *cx,
+                    JS::HandleObject       obj,
+                    ObjectInstance        *priv,
+                    const char            *name,
+                    JS::MutableHandleValue value_p)
+{
+    if (priv->info == NULL)
+        return true;  /* Not resolved, but no error; leave value_p untouched */
+
+    int n_fields = g_object_info_get_n_fields(priv->info);
+    int ix;
+    GIFieldInfo *field = NULL;
+    for (ix = 0; ix < n_fields; ix++) {
+        field = g_object_info_get_field(priv->info, ix);
+        const char *field_name = g_base_info_get_name((GIBaseInfo *) field);
+        if (strcmp(name, field_name) == 0)
+            break;
+        g_clear_pointer(&field, g_base_info_unref);
+    }
+
+    if (field == NULL)
+        return true;
+
+    bool retval = true;
+    GITypeInfo *type = NULL;
+    GITypeTag tag;
+    GIArgument arg = { 0 };
+
+    if (!(g_field_info_get_flags(field) & GI_FIELD_IS_READABLE))
+        goto out;
+
+    gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Overriding %s with GObject field",
+                     name);
+
+    type = g_field_info_get_type(field);
+    tag = g_type_info_get_tag(type);
+    if (tag == GI_TYPE_TAG_ARRAY ||
+        tag == GI_TYPE_TAG_INTERFACE ||
+        tag == GI_TYPE_TAG_GLIST ||
+        tag == GI_TYPE_TAG_GSLIST ||
+        tag == GI_TYPE_TAG_GHASH ||
+        tag == GI_TYPE_TAG_ERROR) {
+        gjs_throw(cx, "Can't get field %s; GObject introspection supports only "
+                  "fields with simple types, not %s", name,
+                  g_type_tag_to_string(tag));
+        retval = false;
+        goto out;
+    }
+
+    retval = g_field_info_get_field(field, priv->gobj, &arg);
+    if (!retval) {
+        gjs_throw(cx, "Error getting field %s from object", name);
+        goto out;
+    }
+
+    retval = gjs_value_from_g_argument(cx, value_p, type, &arg, true);
+    /* copy_structs is irrelevant because g_field_info_get_field() doesn't
+     * handle boxed types */
+
+out:
+    if (type != NULL)
+        g_base_info_unref((GIBaseInfo *) type);
+    g_base_info_unref((GIBaseInfo *) field);
+    return retval;
+}
+
 /* a hook on getting a property; set value_p to override property's value.
  * Return value is false on OOM/exception.
  */
@@ -329,6 +396,14 @@ object_instance_get_prop(JSContext              *context,
         goto out;
 
     ret = get_prop_from_g_param(context, obj, priv, name, value_p);
+    if (!ret)
+        goto out;
+
+    if (!value_p.isUndefined())
+        goto out;
+
+    /* Fall back to fields */
+    ret = get_prop_from_field(context, obj, priv, name, value_p);
 
  out:
     g_free(name);
diff --git a/installed-tests/js/testEverythingEncapsulated.js 
b/installed-tests/js/testEverythingEncapsulated.js
index ee09b79..4349a3e 100644
--- a/installed-tests/js/testEverythingEncapsulated.js
+++ b/installed-tests/js/testEverythingEncapsulated.js
@@ -189,3 +189,28 @@ describe('Introspected boxed types', function () {
         expect(boxed.some_long).toEqual(5);
     });
 });
+
+describe('Introspected GObject', function () {
+    let obj;
+    beforeEach(function () {
+        obj = new Regress.TestObj({
+            // These properties have backing public fields with different names
+            int: 42,
+            float: 3.1416,
+            double: 2.71828,
+        });
+    });
+
+    it('can access fields with simple types', function () {
+        // Compare the values gotten through the GObject property getters to the
+        // values of the backing fields
+        expect(obj.some_int8).toEqual(obj.int);
+        expect(obj.some_float).toEqual(obj.float);
+        expect(obj.some_double).toEqual(obj.double);
+    });
+
+    it('cannot access fields with complex types (GI limitation)', function () {
+        expect(() => obj.parent_instance).toThrow();
+        expect(() => obj.function_ptr).toThrow();
+    });
+});


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