summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvan Welsh <contact@evanwelsh.com>2021-09-12 00:26:06 -0700
committerEvan Welsh <contact@evanwelsh.com>2021-09-19 19:05:34 -0700
commit0a9298a65abdfd78c28dcfdbfdd58d33f66acc45 (patch)
tree32a2684aef5ecd6f7bd417d67b52f9f61e1fda18
parentea45ca9140cb6f2ed25fc47b4ee326074c36093f (diff)
downloadgjs-ewlsh/chain-static.tar.gz
Correctly chain constructor prototypes to enable static inheritanceewlsh/chain-static
In ES2015+ classes it is expected for constructors to inherit the static methods of their parent class, our implementation of classes previously did not do this as it was uncommon in ES5 era classes.
-rw-r--r--gi/object.cpp47
-rw-r--r--gi/object.h3
-rw-r--r--installed-tests/js/testGtk3.js4
-rw-r--r--installed-tests/js/testGtk4.js4
4 files changed, 52 insertions, 6 deletions
diff --git a/gi/object.cpp b/gi/object.cpp
index cc945d6e..376832b2 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -2375,11 +2375,35 @@ JSPropertySpec ObjectBase::proto_properties[] = {
bool ObjectPrototype::get_parent_proto(JSContext* cx,
JS::MutableHandleObject proto) const {
GType parent_type = g_type_parent(gtype());
- if (parent_type != G_TYPE_INVALID) {
- proto.set(gjs_lookup_object_prototype(cx, parent_type));
- if (!proto)
- return false;
+ if (parent_type == G_TYPE_INVALID) {
+ proto.set(nullptr);
+ return true;
}
+
+ JSObject* prototype = gjs_lookup_object_prototype(cx, parent_type);
+ if (!prototype)
+ return false;
+
+ proto.set(prototype);
+ return true;
+}
+
+bool ObjectPrototype::get_parent_constructor(
+ JSContext* cx, JS::MutableHandleObject constructor) const {
+ GType parent_type = g_type_parent(gtype());
+
+ if (parent_type == G_TYPE_INVALID) {
+ constructor.set(nullptr);
+ return true;
+ }
+
+ JS::RootedValue v_constructor(cx);
+ if (!gjs_lookup_object_constructor(cx, parent_type, &v_constructor))
+ return false;
+
+ // gjs_lookup_object_constructor should always return an object.
+ g_assert(v_constructor.isObject());
+ constructor.set(&v_constructor.toObject());
return true;
}
@@ -2401,10 +2425,21 @@ bool ObjectPrototype::define_class(JSContext* context,
GIObjectInfo* info, GType gtype,
JS::MutableHandleObject constructor,
JS::MutableHandleObject prototype) {
- if (!ObjectPrototype::create_class(context, in_object, info, gtype,
- constructor, prototype))
+ ObjectPrototype* priv = ObjectPrototype::create_class(
+ context, in_object, info, gtype, constructor, prototype);
+ if (!priv)
return false;
+ JS::RootedObject parent_constructor(context);
+ if (!priv->get_parent_constructor(context, &parent_constructor))
+ return false;
+ // If this is a fundamental constructor (e.g. GObject.Object) the
+ // parent constructor may be null.
+ if (parent_constructor) {
+ if (!JS_SetPrototype(context, constructor, parent_constructor))
+ return false;
+ }
+
// hook_up_vfunc and the signal handler matcher functions can't be included
// in gjs_object_instance_proto_funcs because they are custom symbols.
const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
diff --git a/gi/object.h b/gi/object.h
index e988189c..044a3e23 100644
--- a/gi/object.h
+++ b/gi/object.h
@@ -211,6 +211,9 @@ class ObjectPrototype
private:
GJS_JSAPI_RETURN_CONVENTION
bool get_parent_proto(JSContext* cx, JS::MutableHandleObject proto) const;
+ GJS_JSAPI_RETURN_CONVENTION
+ bool get_parent_constructor(JSContext* cx,
+ JS::MutableHandleObject constructor) const;
[[nodiscard]] bool is_vfunc_unchanged(GIVFuncInfo* info);
static void vfunc_invalidated_notify(void* data, GClosure* closure);
diff --git a/installed-tests/js/testGtk3.js b/installed-tests/js/testGtk3.js
index 27450fea..83ca8247 100644
--- a/installed-tests/js/testGtk3.js
+++ b/installed-tests/js/testGtk3.js
@@ -174,6 +174,10 @@ describe('Gtk overrides', function () {
expect(Gtk.Widget.get_css_name.call(MyComplexGtkSubclass)).toEqual('complex-subclass');
});
+ it('static inheritance works', function () {
+ expect(MyComplexGtkSubclass.get_css_name()).toEqual('complex-subclass');
+ });
+
it('avoid crashing when GTK vfuncs are called in garbage collection', function () {
GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_CRITICAL,
'*during garbage collection*');
diff --git a/installed-tests/js/testGtk4.js b/installed-tests/js/testGtk4.js
index 2d458610..d7fc0028 100644
--- a/installed-tests/js/testGtk4.js
+++ b/installed-tests/js/testGtk4.js
@@ -184,6 +184,10 @@ describe('Gtk overrides', function () {
expect(Gtk.Widget.get_css_name.call(MyComplexGtkSubclass)).toEqual('complex-subclass');
});
+ it('static inheritance works', function () {
+ expect(MyComplexGtkSubclass.get_css_name()).toEqual('complex-subclass');
+ });
+
it('can create a Gtk.TreeIter with accessible stamp field', function () {
const iter = new Gtk.TreeIter();
iter.stamp = 42;