[gjs] object: Don't let a method shadow a property
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs] object: Don't let a method shadow a property
- Date: Sat, 29 Jul 2017 08:51:59 +0000 (UTC)
commit 40b7b1767dcd5176d3c493cb02e2a3ff1fca7eae
Author: Philip Chimento <philip chimento gmail com>
Date: Wed Jul 19 23:48:16 2017 -0700
object: Don't let a method shadow a property
This changed due to a change in property access code in SpiderMonkey 45.
Previously, the getProperty hook was apparently called for every property
access. Now, it's never called if the resolve hook defines a lazy
property first.
This is unfortunate; we may still change this behaviour (see
https://bugzilla.gnome.org/show_bug.cgi?id=690450#c30), but if we do
change it then we want it to be intentional.
https://bugzilla.gnome.org/show_bug.cgi?id=785091
configure.ac | 2 +-
gi/object.cpp | 82 +++++++++++++++++++++-
installed-tests/js/testEverythingEncapsulated.js | 10 +++
3 files changed, 90 insertions(+), 4 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b4bc63a..25903af 100644
--- a/configure.ac
+++ b/configure.ac
@@ -254,7 +254,7 @@ AC_CONFIG_LINKS([
AC_OUTPUT
# Warn about conditions that affect runtime
-PKG_CHECK_EXISTS([gobject-introspection-1.0 >= 1.53.1], [], [
+PKG_CHECK_EXISTS([gobject-introspection-1.0 >= 1.53.4], [], [
AC_MSG_WARN([You do not have a new enough version of
gobject-introspection to run the tests. You can still build GJS, but some
tests will fail.])])
diff --git a/gi/object.cpp b/gi/object.cpp
index 76f204e..ab19dcd 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -322,6 +322,14 @@ lookup_field_info(GIObjectInfo *info,
g_clear_pointer(&retval, g_base_info_unref);
}
+ if (!retval)
+ return nullptr;
+
+ if (!(g_field_info_get_flags(retval) & GI_FIELD_IS_READABLE)) {
+ g_base_info_unref(retval);
+ return nullptr;
+ }
+
return retval;
}
@@ -345,9 +353,6 @@ get_prop_from_field(JSContext *cx,
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);
@@ -650,6 +655,65 @@ object_instance_resolve_no_info(JSContext *context,
return true;
}
+/* Taken from GLib */
+static void
+canonicalize_key(char *key)
+{
+ for (char *p = key; *p != 0; p++) {
+ char c = *p;
+
+ if (c != '-' &&
+ (c < '0' || c > '9') &&
+ (c < 'A' || c > 'Z') &&
+ (c < 'a' || c > 'z'))
+ *p = '-';
+ }
+}
+
+static bool
+is_gobject_property_name(GIObjectInfo *info,
+ const char *name)
+{
+ int n_props = g_object_info_get_n_properties(info);
+ int ix;
+ GIPropertyInfo *prop_info = nullptr;
+
+ char *canonical_name = gjs_hyphen_from_camel(name);
+ canonicalize_key(canonical_name);
+
+ for (ix = 0; ix < n_props; ix++) {
+ prop_info = g_object_info_get_property(info, ix);
+ const char *prop_name = g_base_info_get_name(prop_info);
+ if (strcmp(canonical_name, prop_name) == 0)
+ break;
+ g_clear_pointer(&prop_info, g_base_info_unref);
+ }
+
+ g_free(canonical_name);
+
+ if (!prop_info)
+ return false;
+
+ if (!(g_property_info_get_flags(prop_info) & G_PARAM_READABLE)) {
+ g_base_info_unref(prop_info);
+ return false;
+ }
+
+ g_base_info_unref(prop_info);
+ return true;
+}
+
+static bool
+is_gobject_field_name(GIObjectInfo *info,
+ const char *name)
+{
+ GIFieldInfo *field_info = lookup_field_info(info, name);
+ if (!field_info)
+ return false;
+ g_base_info_unref(field_info);
+ return true;
+}
+
/*
* The *objp out parameter, on success, should be null to indicate that id
* was not resolved; and non-null, referring to obj or one of its prototypes,
@@ -748,6 +812,18 @@ object_instance_resolve(JSContext *context,
* method resolution. */
}
+ /* If the name refers to a GObject property or field, don't resolve.
+ * Instead, let the getProperty hook handle fetching the property from
+ * GObject. */
+ if (is_gobject_property_name(priv->info, name) ||
+ is_gobject_field_name(priv->info, name)) {
+ gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
+ "Breaking out of %p resolve, '%s' is a GObject prop",
+ obj.get(), name.get());
+ *resolved = false;
+ return true;
+ }
+
/* find_method does not look at methods on parent classes,
* we rely on javascript to walk up the __proto__ chain
* and find those and define them in the right prototype.
diff --git a/installed-tests/js/testEverythingEncapsulated.js
b/installed-tests/js/testEverythingEncapsulated.js
index 0142160..1039f0e 100644
--- a/installed-tests/js/testEverythingEncapsulated.js
+++ b/installed-tests/js/testEverythingEncapsulated.js
@@ -231,6 +231,16 @@ describe('Introspected GObject', function () {
obj.ownprop = 'foo';
expect(obj.hasOwnProperty('ownprop')).toBeTruthy();
});
+
+ // This test is not meant to be normative; a GObject behaving like this is
+ // doing something unsupported. However, we have been handling this so far
+ // in a certain way, and we don't want to break user code because of badly
+ // behaved libraries. This test ensures that any change to the behaviour
+ // must be intentional.
+ it('resolves properties when they are shadowed by methods', function () {
+ expect(obj.name_conflict).toEqual(42);
+ expect(obj.name_conflict instanceof Function).toBeFalsy();
+ });
});
describe('Introspected function length', function () {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]