Commit 9f182bd6 authored by Philip Chimento's avatar Philip Chimento

object: Support fields defined in ancestor classes

We define a JS property with getter and setter for a GObject field in
resolve_impl() (where lazy properties are defined) when JS code tries to
access a field. The getter and setter retrieve the field's GIFieldInfo
information from a cache, using a key that is built into the getter and
setter.

Because this is done during the resolve operation, and the resolve
operation can occur multiple times going up the prototype chain, the JS
property may end up on a prototype that is an ancestor of the object's
direct prototype. So the GIFieldInfo may end up on any prototype in the
prototype chain, and so we have to search through the prototype chain
for it.

Closes: #223.
parent dce837ce
......@@ -375,19 +375,6 @@ static GjsAutoFieldInfo lookup_field_info(GIObjectInfo* info,
return retval;
}
// Retrieves a GIFieldInfo for a field named @key. This is for use in
// field_getter_impl() and field_setter_impl(), where the field info *must* have
// been cached previously in resolve_impl(). This will fail an assertion if
// there is no cached field info.
//
// The caller does not own the return value, and it can never be null.
GIFieldInfo* ObjectPrototype::lookup_cached_field_info(JSContext* cx,
JS::HandleString key) {
auto entry = m_field_cache.lookupForAdd(key);
g_assert(entry && "Field info should have been cached in resolve_impl()");
return entry->value().get();
}
bool ObjectBase::field_getter(JSContext* cx, unsigned argc, JS::Value* vp) {
GJS_GET_WRAPPER_PRIV(cx, argc, vp, args, obj, ObjectBase, priv);
......@@ -1668,6 +1655,44 @@ gjs_lookup_object_prototype(JSContext *context,
return gjs_lookup_object_prototype_from_info(context, info, gtype);
}
// Retrieves a GIFieldInfo for a field named @key. This is for use in
// field_getter_impl() and field_setter_impl(), where the field info *must* have
// been cached previously in resolve_impl() on this ObjectPrototype or one of
// its parent ObjectPrototypes. This will fail an assertion if there is no
// cached field info.
//
// The caller does not own the return value, and it can never be null.
GIFieldInfo* ObjectPrototype::lookup_cached_field_info(JSContext* cx,
JS::HandleString key) {
if (is_custom_js_class()) {
// Custom JS classes can't have fields. We must be looking up a field on
// a GObject-introspected parent.
GType parent_gtype = g_type_parent(m_gtype);
g_assert(parent_gtype != G_TYPE_INVALID &&
"Custom JS class must have parent");
ObjectPrototype* parent_proto =
ObjectPrototype::for_gtype(parent_gtype);
g_assert(parent_proto &&
"Custom JS class's parent must have been accessed in JS");
return parent_proto->lookup_cached_field_info(cx, key);
}
gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
"Looking up cached field info for '%s' in '%s' prototype",
gjs_debug_string(key).c_str(), g_type_name(m_gtype));
auto entry = m_field_cache.lookupForAdd(key);
if (entry)
return entry->value().get();
// We must be looking up a field defined on a parent. Look up the prototype
// object via its GIObjectInfo.
GjsAutoObjectInfo parent_info = g_object_info_get_parent(m_info);
JS::RootedObject parent_proto(cx, gjs_lookup_object_prototype_from_info(
cx, parent_info, G_TYPE_INVALID));
ObjectPrototype* parent = ObjectPrototype::for_js(cx, parent_proto);
return parent->lookup_cached_field_info(cx, key);
}
void ObjectBase::associate_closure(JSContext* cx, GClosure* closure) {
if (!is_prototype())
to_instance()->ensure_uses_toggle_ref(cx);
......
......@@ -243,6 +243,19 @@ describe('Introspected GObject', function () {
it('gives undefined for write-only properties', function () {
expect(obj.write_only).not.toBeDefined();
});
it('can read fields from a parent class', function () {
let subobj = new Regress.TestSubObj({
int: 42,
float: 3.1416,
double: 2.71828,
});
// see "can access fields with simple types" above
expect(subobj.some_int8).toEqual(subobj.int);
expect(subobj.some_float).toEqual(subobj.float);
expect(subobj.some_double).toEqual(subobj.double);
});
});
describe('Introspected function length', function () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment