[gjs: 1/2] object: Resolve properties in resolve_no_info
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs: 1/2] object: Resolve properties in resolve_no_info
- Date: Tue, 17 Jul 2018 03:03:51 +0000 (UTC)
commit 12ace06a6f1ae2a2896f0a37027734ede7c89750
Author: Philip Chimento <philip chimento gmail com>
Date: Mon Jul 16 21:29:47 2018 -0400
object: Resolve properties in resolve_no_info
A regression from the property cache refactor caused properties like
Gio.NetworkMonitor.network_available to disappear. This was because
NetworkMonitor is an interface, implemented by a non-introspectable
class, i.e. Gio.NetworkMonitor.get_default() gives you an instance of
GNetworkMonitorBase which does not have introspection information.
To fix this, we iterate through all the interfaces implemented by a
non-introspectable type, and check if the ID to be resolved is a property
of one of them, and define it on the prototype if so. For this we factor
out some code into separate functions, and change resolve_no_info() to
have a parameter specifying whether to consider only methods or both
methods and properties.
Closes: #182
gi/object.cpp | 126 ++++++++++++++++++++++-----------
gi/object.h | 9 ++-
installed-tests/js/testGObjectClass.js | 7 ++
3 files changed, 98 insertions(+), 44 deletions(-)
---
diff --git a/gi/object.cpp b/gi/object.cpp
index b7cab446..5c82fa92 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -667,11 +667,77 @@ find_vfunc_on_parents(GIObjectInfo *info,
return vfunc;
}
+/* 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 = '-';
+ }
+}
+
+/* @name must already be canonicalized */
+static bool is_ginterface_property_name(GIInterfaceInfo* info,
+ const char* name) {
+ int n_props = g_interface_info_get_n_properties(info);
+ GjsAutoInfo<GIPropertyInfo> prop_info;
+
+ for (int ix = 0; ix < n_props; ix++) {
+ prop_info = g_interface_info_get_property(info, ix);
+ if (strcmp(name, prop_info.name()) == 0)
+ break;
+ prop_info.reset();
+ }
+
+ if (!prop_info)
+ return false;
+
+ return g_property_info_get_flags(prop_info) & G_PARAM_READABLE;
+}
+
+bool ObjectPrototype::lazy_define_gobject_property(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ bool* resolved,
+ const char* name) {
+ bool found = false;
+ if (!JS_AlreadyHasOwnPropertyById(cx, obj, id, &found))
+ return false;
+ if (found) {
+ /* Already defined, so *resolved = false because we didn't just
+ * define it */
+ *resolved = false;
+ return true;
+ }
+
+ debug_jsprop("Defining lazy GObject property", id, obj);
+
+ JS::RootedValue private_id(cx, JS::StringValue(JSID_TO_STRING(id)));
+ if (!gjs_define_property_dynamic(
+ cx, obj, name, "gobject_prop", &ObjectBase::prop_getter,
+ &ObjectBase::prop_setter, private_id, GJS_MODULE_PROP_FLAGS))
+ return false;
+
+ *resolved = true;
+ return true;
+}
+
bool ObjectPrototype::resolve_no_info(JSContext* cx, JS::HandleObject obj,
- bool* resolved, const char* name) {
+ JS::HandleId id, bool* resolved,
+ const char* name,
+ ResolveWhat resolve_props) {
guint n_interfaces;
guint i;
+ GjsAutoChar canonical_name;
+ if (resolve_props == ConsiderMethodsAndProperties) {
+ char* canonical_name_unowned = gjs_hyphen_from_camel(name);
+ canonicalize_key(canonical_name_unowned);
+ canonical_name.reset(canonical_name_unowned);
+ }
+
GType *interfaces = g_type_interfaces(m_gtype, &n_interfaces);
for (i = 0; i < n_interfaces; i++) {
GjsAutoInfo<GIInterfaceInfo> iface_info =
@@ -696,6 +762,17 @@ bool ObjectPrototype::resolve_no_info(JSContext* cx, JS::HandleObject obj,
return true;
}
}
+
+ if (resolve_props == ConsiderOnlyMethods)
+ continue;
+
+ /* If the name refers to a GObject property, lazily define the property
+ * in JS as we do below in the real resolve hook. We ignore fields here
+ * because I don't think interfaces can have fields */
+ if (is_ginterface_property_name(iface_info, canonical_name)) {
+ g_free(interfaces);
+ return lazy_define_gobject_property(cx, obj, id, resolved, name);
+ }
}
*resolved = false;
@@ -703,21 +780,6 @@ bool ObjectPrototype::resolve_no_info(JSContext* cx, JS::HandleObject obj,
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)
@@ -784,7 +846,8 @@ bool ObjectPrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
* we need to look at exposing interfaces. Look up our interfaces through
* GType data, and then hope that *those* are introspectable. */
if (is_custom_js_class())
- return resolve_no_info(context, obj, resolved, name);
+ return resolve_no_info(context, obj, id, resolved, name,
+ ConsiderMethodsAndProperties);
if (g_str_has_prefix (name, "vfunc_")) {
/* The only time we find a vfunc info is when we're the base
@@ -824,30 +887,8 @@ bool ObjectPrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
* method resolution. */
}
- /* If the name refers to a GObject property or field, lazily define the
- * property in JS, on the prototype. */
- if (is_gobject_property_name(m_info, name)) {
- bool found = false;
- if (!JS_AlreadyHasOwnPropertyById(context, obj, id, &found))
- return false;
- if (found) {
- /* Already defined, so *resolved = false because we didn't just
- * define it */
- *resolved = false;
- return true;
- }
-
- debug_jsprop("Defining lazy GObject property", id, obj);
-
- JS::RootedValue private_id(context, JS::StringValue(JSID_TO_STRING(id)));
- if (!gjs_define_property_dynamic(
- context, obj, name, "gobject_prop", &ObjectBase::prop_getter,
- &ObjectBase::prop_setter, private_id, GJS_MODULE_PROP_FLAGS))
- return false;
-
- *resolved = true;
- return true;
- }
+ if (is_gobject_property_name(m_info, name))
+ return lazy_define_gobject_property(context, obj, id, resolved, name);
GjsAutoInfo<GIFieldInfo> field_info = lookup_field_info(m_info, name);
if (field_info) {
@@ -896,7 +937,8 @@ bool ObjectPrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
* https://bugzilla.gnome.org/show_bug.cgi?id=632922
*/
if (!method_info)
- return resolve_no_info(context, obj, resolved, name);
+ return resolve_no_info(context, obj, id, resolved, name,
+ ConsiderOnlyMethods);
#if GJS_VERBOSE_ENABLE_GI_USAGE
_gjs_log_info_usage((GIBaseInfo*) method_info);
diff --git a/gi/object.h b/gi/object.h
index 4c6cd59f..7c5ea35e 100644
--- a/gi/object.h
+++ b/gi/object.h
@@ -272,8 +272,13 @@ class ObjectPrototype : public ObjectBase {
/* Helper methods */
private:
bool is_vfunc_unchanged(GIVFuncInfo* info);
- bool resolve_no_info(JSContext* cx, JS::HandleObject obj, bool* resolved,
- const char* name);
+ bool lazy_define_gobject_property(JSContext* cx, JS::HandleObject obj,
+ JS::HandleId id, bool* resolved,
+ const char* name);
+ enum ResolveWhat { ConsiderOnlyMethods, ConsiderMethodsAndProperties };
+ bool resolve_no_info(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+ bool* resolved, const char* name,
+ ResolveWhat resolve_props);
public:
void set_type_qdata(void);
diff --git a/installed-tests/js/testGObjectClass.js b/installed-tests/js/testGObjectClass.js
index fe215134..4371f598 100644
--- a/installed-tests/js/testGObjectClass.js
+++ b/installed-tests/js/testGObjectClass.js
@@ -352,4 +352,11 @@ describe('GObject class with decorator', function () {
expect (() => new MyCustomCharset() && new MySecondCustomCharset()).not.toThrow();
});
+
+ it('resolves properties from interfaces', function() {
+ const mon = Gio.NetworkMonitor.get_default();
+ expect(mon.network_available).toBeDefined();
+ expect(mon.networkAvailable).toBeDefined();
+ expect(mon['network-available']).toBeDefined();
+ });
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]