[gjs/ewlsh/chain-static] Correctly chain constructor prototypes to enable static inheritance




commit 65e6925cf9df721ce9e8a6fd03cb70f4759f2ae4
Author: Evan Welsh <contact evanwelsh com>
Date:   Fri Aug 6 06:20:40 2021 -0700

    Correctly chain constructor prototypes to enable static inheritance
    
    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.

 gi/object.cpp                 | 29 +++++++++++++++++++++++++++--
 gi/object.h                   |  3 +++
 modules/core/overrides/Gtk.js | 39 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 2 deletions(-)
---
diff --git a/gi/object.cpp b/gi/object.cpp
index 77acf3f4..aa81da95 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -2380,6 +2380,20 @@ bool ObjectPrototype::get_parent_proto(JSContext* cx,
     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) {
+        JS::RootedValue v_constructor(cx);
+        if (!gjs_lookup_object_constructor(cx, parent_type, &v_constructor))
+            return false;
+
+        constructor.set(&v_constructor.toObject());
+    }
+    return true;
+}
+
 /*
  * ObjectPrototype::define_class:
  * @in_object: Object where the constructor is stored, typically a repo object.
@@ -2398,9 +2412,20 @@ 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.
diff --git a/gi/object.h b/gi/object.h
index 0d9d40fe..8bef43b7 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/modules/core/overrides/Gtk.js b/modules/core/overrides/Gtk.js
index 306f7d3f..2db80a39 100644
--- a/modules/core/overrides/Gtk.js
+++ b/modules/core/overrides/Gtk.js
@@ -107,6 +107,45 @@ function _init() {
         return klass;
     };
 
+    Gtk.registerWidgetType = function registerWidgetType(klass) {
+        let template = klass[Gtk.template];
+        let cssName = klass[Gtk.cssName];
+        let children = klass[Gtk.children];
+        let internalChildren = klass[Gtk.internalChildren];
+
+        if (template) {
+            klass.prototype._instance_init = function () {
+                this.init_template();
+            };
+        }
+
+        GObject.registerType(klass);
+
+        if (cssName)
+            Gtk.Widget.set_css_name.call(klass, cssName);
+
+        if (template) {
+            if (typeof template === 'string')
+                Gtk.Widget.set_template_from_uri.call(klass, template);
+            else
+                Gtk.Widget.set_template.call(klass, template);
+
+
+            if (BuilderScope)
+                Gtk.Widget.set_template_scope.call(klass, new BuilderScope());
+        }
+
+        if (children) {
+            children.forEach(child =>
+                Gtk.Widget.bind_template_child_full.call(klass, child, false, 0));
+        }
+
+        if (internalChildren) {
+            internalChildren.forEach(child =>
+                Gtk.Widget.bind_template_child_full.call(klass, child, true, 0));
+        }
+    };
+
     if (Gtk.Widget.prototype.get_first_child) {
         Gtk.Widget.prototype[Symbol.iterator] = function* () {
             for (let c = this.get_first_child(); c; c = c.get_next_sibling())


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]