[gjs: 1/2] object: Cache known unresolvable properties
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs: 1/2] object: Cache known unresolvable properties
- Date: Mon, 17 Feb 2020 19:47:35 +0000 (UTC)
commit e41aa04050374bba0d752160200b88aad7c18153
Author: Philip Chimento <philip chimento gmail com>
Date: Fri Feb 14 22:12:12 2020 -0800
object: Cache known unresolvable properties
Based on a patch by Daniel van Vugt. This adds a negative cache to the
resolve mechanism. Since on any particular GObject class's prototype we
know whether a name is resolved or not, by consulting the introspection
information, we can cache the name when it is not resolved, so that the
resolve operation can quickly move on to the next parent class's
prototype.
The situation we are trying to avoid here, is looking up a property or
method belonging to an ancestor class (e.g., GObject.prototype.notify)
on an instance of a class that is many levels of inheritance deeper
(e.g. some custom Gtk widget.) If you do myWidget.notify('foo'), then
the name 'notify' will first be resolved on the instance (a no-op), then
on the custom widget class prototype (with resolve_no_info), then on
each parent (with resolve), consulting all implemented interfaces at
each level, and finally resolve on GObject.prototype. Subsequent lookups
will still walk through all the intermediate classes and interfaces
every time, even though GObject.prototype now has the property. By using
the negative cache, all the resolves on intermediate classes return
early, and the interfaces are not consulted.
I don't believe it's necessary to ever invalidate the cache, since new
methods and properties cannot be added to introspection information at
runtime.
Closes: #302
gi/object.cpp | 20 ++++++++++++++++++++
gi/object.h | 19 +++++++++++++++++++
2 files changed, 39 insertions(+)
---
diff --git a/gi/object.cpp b/gi/object.cpp
index 3b1e87a7..195bb4a7 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -752,6 +752,25 @@ bool ObjectBase::id_is_never_lazy(jsid name, const GjsAtoms& atoms) {
bool ObjectPrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
JS::HandleId id, const char* name,
bool* resolved) {
+ if (m_unresolvable_cache.has(id)) {
+ *resolved = false;
+ return true;
+ }
+
+ if (!uncached_resolve(context, obj, id, name, resolved))
+ return false;
+
+ if (!*resolved && !m_unresolvable_cache.putNew(id)) {
+ JS_ReportOutOfMemory(context);
+ return false;
+ }
+
+ return true;
+}
+
+bool ObjectPrototype::uncached_resolve(JSContext* context, JS::HandleObject obj,
+ JS::HandleId id, const char* name,
+ bool* resolved) {
/* If we have no GIRepository information (we're a JS GObject subclass),
* we need to look at exposing interfaces. Look up our interfaces through
* GType data, and then hope that *those* are introspectable. */
@@ -1578,6 +1597,7 @@ void ObjectBase::trace_impl(JSTracer* tracer) {
void ObjectPrototype::trace_impl(JSTracer* tracer) {
m_property_cache.trace(tracer);
m_field_cache.trace(tracer);
+ m_unresolvable_cache.trace(tracer);
}
void ObjectInstance::finalize_impl(JSFreeOp* fop, JSObject* obj) {
diff --git a/gi/object.h b/gi/object.h
index 8995e437..29bc9932 100644
--- a/gi/object.h
+++ b/gi/object.h
@@ -211,6 +211,19 @@ class ObjectBase
GJS_USE static GQuark custom_property_quark(void);
};
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1614220
+struct IdHasher {
+ typedef jsid Lookup;
+ static mozilla::HashNumber hash(jsid id) {
+ if (MOZ_LIKELY(JSID_IS_ATOM(id)))
+ return js::DefaultHasher<JSAtom*>::hash(JSID_TO_ATOM(id));
+ if (JSID_IS_SYMBOL(id))
+ return js::DefaultHasher<JS::Symbol*>::hash(JSID_TO_SYMBOL(id));
+ return mozilla::HashGeneric(JSID_BITS(id));
+ }
+ static bool match(jsid id1, jsid id2) { return id1 == id2; }
+};
+
class ObjectPrototype
: public GIWrapperPrototype<ObjectBase, ObjectPrototype, ObjectInstance> {
friend class GIWrapperPrototype<ObjectBase, ObjectPrototype,
@@ -223,9 +236,12 @@ class ObjectPrototype
using FieldCache =
JS::GCHashMap<JS::Heap<JSString*>, GjsAutoInfo<GI_INFO_TYPE_FIELD>,
js::DefaultHasher<JSString*>, js::SystemAllocPolicy>;
+ using NegativeLookupCache =
+ JS::GCHashSet<jsid, IdHasher, js::SystemAllocPolicy>;
PropertyCache m_property_cache;
FieldCache m_field_cache;
+ NegativeLookupCache m_unresolvable_cache;
ObjectPrototype(GIObjectInfo* info, GType gtype);
~ObjectPrototype();
@@ -253,6 +269,9 @@ class ObjectPrototype
bool resolve_no_info(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
bool* resolved, const char* name,
ResolveWhat resolve_props);
+ GJS_JSAPI_RETURN_CONVENTION
+ bool uncached_resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+ const char* name, bool* resolved);
public:
void set_type_qdata(void);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]