[gjs: 3/7] object: Split file into smaller logical units



commit 44ed1dc035cd46eedf22c703c2f6015d712c5af3
Author: Philip Chimento <philip chimento gmail com>
Date:   Wed Jun 13 22:35:36 2018 -0700

    object: Split file into smaller logical units
    
    We now have object.cpp dealing only with ObjectInstance, the JSClass for
    wrapper JS objects. Two new files are created:
    
    - gobject.cpp, which is the dynamic GObject types for classes and
      interfaces created in JS code.
    
    - private.cpp, which is the private GI native module, accessible from JS
      as `imports._gi`.

 gi/gobject.cpp  | 271 ++++++++++++++++
 gi/gobject.h    |  41 +++
 gi/object.cpp   | 991 +-------------------------------------------------------
 gi/object.h     | 307 +++++++++++++++++-
 gi/private.cpp  | 414 +++++++++++++++++++++++
 gi/private.h    |  31 ++
 gjs-srcs.mk     |   4 +
 gjs/context.cpp |   7 +-
 8 files changed, 1070 insertions(+), 996 deletions(-)
---
diff --git a/gi/gobject.cpp b/gi/gobject.cpp
new file mode 100644
index 00000000..ae36e5d0
--- /dev/null
+++ b/gi/gobject.cpp
@@ -0,0 +1,271 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2008  litl, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <glib-object.h>
+
+#include <unordered_map>
+
+#include "gjs/context.h"
+#include "gjs/jsapi-util.h"
+#include "gjs/jsapi-wrapper.h"
+#include "gobject.h"
+#include "object.h"
+#include "value.h"
+
+static std::unordered_map<GType, AutoParamArray> class_init_properties;
+
+static JSContext* current_context(void) {
+    GjsContext* gjs = gjs_context_get_current();
+    return static_cast<JSContext*>(gjs_context_get_native_context(gjs));
+}
+
+void push_class_init_properties(GType gtype, AutoParamArray* params) {
+    class_init_properties[gtype] = std::move(*params);
+}
+
+bool pop_class_init_properties(GType gtype, AutoParamArray* params_out) {
+    auto found = class_init_properties.find(gtype);
+    if (found == class_init_properties.end())
+        return false;
+
+    *params_out = std::move(found->second);
+    class_init_properties.erase(found);
+    return true;
+}
+
+static char* hyphen_to_underscore(const char* str) {
+    char *s = g_strdup(str);
+    char *retval = s;
+    while (*(s++) != '\0') {
+        if (*s == '-')
+            *s = '_';
+    }
+    return retval;
+}
+
+static void jsobj_set_gproperty(JSContext* cx, JS::HandleObject object,
+                                const GValue* value, GParamSpec* pspec) {
+    JS::RootedValue jsvalue(cx);
+    if (!gjs_value_from_g_value(cx, &jsvalue, value))
+        return;
+
+    GjsAutoChar underscore_name = hyphen_to_underscore(pspec->name);
+    if (!JS_SetProperty(cx, object, underscore_name, jsvalue))
+        gjs_log_exception(cx);
+}
+
+static void gjs_object_base_init(void* klass) {
+    auto* priv = ObjectInstance::for_gtype(G_OBJECT_CLASS_TYPE(klass));
+    if (priv)
+        priv->ref_closures();
+}
+
+static void gjs_object_base_finalize(void* klass) {
+    auto* priv = ObjectInstance::for_gtype(G_OBJECT_CLASS_TYPE(klass));
+    if (priv)
+        priv->unref_closures();
+}
+
+static GObject* gjs_object_constructor(
+    GType type, unsigned n_construct_properties,
+    GObjectConstructParam* construct_properties) {
+    if (!ObjectInstance::object_init_list.empty()) {
+        GType parent_type = g_type_parent(type);
+
+        /* The object is being constructed from JS:
+         * Simply chain up to the first non-gjs constructor
+         */
+        while (G_OBJECT_CLASS(g_type_class_peek(parent_type))->constructor ==
+               gjs_object_constructor)
+            parent_type = g_type_parent(parent_type);
+
+        return G_OBJECT_CLASS(g_type_class_peek(parent_type))
+            ->constructor(type, n_construct_properties, construct_properties);
+    }
+
+    /* The object is being constructed from native code (e.g. GtkBuilder):
+     * Construct the JS object from the constructor, then use the GObject
+     * that was associated in gjs_object_custom_init()
+     */
+    JSContext *cx = current_context();
+    JSAutoRequest ar(cx);
+    JSAutoCompartment ac(cx, gjs_get_import_global(cx));
+
+    JS::RootedObject constructor(
+        cx, gjs_lookup_object_constructor_from_info(cx, nullptr, type));
+    if (!constructor)
+        return nullptr;
+
+    JSObject* object;
+    if (n_construct_properties) {
+        JS::RootedObject props_hash(cx, JS_NewPlainObject(cx));
+
+        for (unsigned i = 0; i < n_construct_properties; i++)
+            jsobj_set_gproperty(cx, props_hash, construct_properties[i].value,
+                                construct_properties[i].pspec);
+
+        JS::AutoValueArray<1> args(cx);
+        args[0].set(JS::ObjectValue(*props_hash));
+        object = JS_New(cx, constructor, args);
+    } else {
+        object = JS_New(cx, constructor, JS::HandleValueArray::empty());
+    }
+
+    if (!object)
+        return nullptr;
+
+    auto* priv = ObjectInstance::for_js_nocheck(object);
+    /* Should have been set in init_impl() and pushed into object_init_list,
+     * then popped from object_init_list in gjs_object_custom_init() */
+    g_assert(priv);
+    /* We only hold a toggle ref at this point, add back a ref that the
+     * native code can own.
+     */
+    return G_OBJECT(g_object_ref(priv->gobj()));
+}
+
+static void gjs_object_set_gproperty(GObject* object, unsigned property_id,
+                                     const GValue* value, GParamSpec* pspec) {
+    auto* priv = ObjectInstance::for_gobject(object);
+    JSContext *cx = current_context();
+
+    JS::RootedObject js_obj(cx, priv->wrapper());
+    jsobj_set_gproperty(cx, js_obj, value, pspec);
+}
+
+static void gjs_object_get_gproperty(GObject* object, unsigned property_id,
+                                     GValue* value, GParamSpec* pspec) {
+    auto* priv = ObjectInstance::for_gobject(object);
+    JSContext *cx = current_context();
+
+    JS::RootedObject js_obj(cx, priv->wrapper());
+    JS::RootedValue jsvalue(cx);
+
+    GjsAutoChar underscore_name = hyphen_to_underscore(pspec->name);
+    if (!JS_GetProperty(cx, js_obj, underscore_name, &jsvalue) ||
+        !gjs_value_to_g_value(cx, jsvalue, value))
+        gjs_log_exception(cx);
+}
+
+static void gjs_object_class_init(void* class_pointer, void* user_data) {
+    GObjectClass* klass = G_OBJECT_CLASS(class_pointer);
+    GType gtype = G_OBJECT_CLASS_TYPE(klass);
+
+    klass->constructor = gjs_object_constructor;
+    klass->set_property = gjs_object_set_gproperty;
+    klass->get_property = gjs_object_get_gproperty;
+
+    AutoParamArray properties;
+    if (!pop_class_init_properties(gtype, &properties))
+        return;
+
+    unsigned i = 0;
+    for (GjsAutoParam& pspec : properties) {
+        g_param_spec_set_qdata(pspec, ObjectInstance::custom_property_quark(),
+                               GINT_TO_POINTER(1));
+        g_object_class_install_property(klass, ++i, pspec);
+    }
+}
+
+static void gjs_object_custom_init(GTypeInstance* instance, void* klass) {
+    if (ObjectInstance::object_init_list.empty())
+        return;
+
+    JSContext *cx = current_context();
+
+    JS::RootedObject object(cx, ObjectInstance::object_init_list.top());
+    auto* priv = ObjectInstance::for_js_nocheck(object);
+    g_assert(priv);  // Should have been set in init_impl()
+
+    if (priv->gtype() != G_TYPE_FROM_INSTANCE(instance)) {
+        /* This is not the most derived instance_init function,
+           do nothing.
+         */
+        return;
+    }
+
+    ObjectInstance::object_init_list.pop();
+
+    priv->associate_js_gobject(cx, object, G_OBJECT(instance));
+
+    /* Custom JS objects will most likely have visible state, so
+     * just do this from the start */
+    priv->ensure_uses_toggle_ref(cx);
+
+    JS::RootedValue v(cx);
+    if (!gjs_object_get_property(cx, object, GJS_STRING_INSTANCE_INIT, &v)) {
+        gjs_log_exception(cx);
+        return;
+    }
+
+    if (!v.isObject())
+        return;
+
+    JS::RootedValue r(cx);
+    if (!JS_CallFunctionValue(cx, object, v, JS::HandleValueArray::empty(), &r))
+        gjs_log_exception(cx);
+}
+
+static void gjs_interface_init(void* g_iface, void* iface_data) {
+    GType gtype = G_TYPE_FROM_INTERFACE(g_iface);
+
+    AutoParamArray properties;
+    if (!pop_class_init_properties(gtype, &properties))
+        return;
+
+    for (GjsAutoParam& pspec : properties) {
+        g_param_spec_set_qdata(pspec, ObjectInstance::custom_property_quark(),
+                               GINT_TO_POINTER(1));
+        g_object_interface_install_property(g_iface, pspec);
+    }
+}
+
+constexpr GTypeInfo gjs_gobject_class_info = {
+    0,  // class_size
+
+    gjs_object_base_init,
+    gjs_object_base_finalize,
+
+    gjs_object_class_init,
+    GClassFinalizeFunc(nullptr),
+    nullptr,  // class_data
+
+    0,  // instance_size
+    0,  // n_preallocs
+    gjs_object_custom_init,
+};
+
+constexpr GTypeInfo gjs_gobject_interface_info = {
+    sizeof(GTypeInterface),  // class_size
+
+    GBaseInitFunc(nullptr),
+    GBaseFinalizeFunc(nullptr),
+
+    gjs_interface_init,
+    GClassFinalizeFunc(nullptr),
+    nullptr,  // class_data
+
+    0,        // instance_size
+    0,        // n_preallocs
+    nullptr,  // instance_init
+};
diff --git a/gi/gobject.h b/gi/gobject.h
new file mode 100644
index 00000000..1d925e05
--- /dev/null
+++ b/gi/gobject.h
@@ -0,0 +1,41 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2018  Philip Chimento <philip chimento gmail com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef GI_GOBJECT_H_
+#define GI_GOBJECT_H_
+
+#include <glib-object.h>
+
+#include <unordered_map>
+#include <vector>
+
+#include "gjs/jsapi-util.h"
+
+using AutoParamArray = std::vector<GjsAutoParam>;
+
+extern const GTypeInfo gjs_gobject_class_info;
+extern const GTypeInfo gjs_gobject_interface_info;
+
+void push_class_init_properties(GType gtype, AutoParamArray* params);
+bool pop_class_init_properties(GType gtype, AutoParamArray* params_out);
+
+#endif  // GI_GOBJECT_H_
diff --git a/gi/object.cpp b/gi/object.cpp
index 28e3faad..4917dc9a 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -24,10 +24,7 @@
 
 #include <config.h>
 
-#include <functional>
 #include <memory>
-#include <set>
-#include <stack>
 #include <string.h>
 #include <tuple>
 #include <unordered_map>
@@ -57,295 +54,7 @@
 
 #include "js/GCHashTable.h"
 
-class ObjectInstance;
-
-class GjsListLink {
- private:
-    ObjectInstance *m_prev;
-    ObjectInstance *m_next;
-
- public:
-    ObjectInstance *prev(void) const { return m_prev; }
-    ObjectInstance *next(void) const { return m_next; }
-
-    void prepend(ObjectInstance *this_instance, ObjectInstance *head);
-    void unlink(void);
-    size_t size(void) const;
-};
-
-struct AutoGValueVector : public std::vector<GValue> {
-    ~AutoGValueVector() {
-        for (GValue value : *this)
-            g_value_unset(&value);
-    }
-};
-
-class ObjectInstance {
-    GIObjectInfo *m_info;
-    GObject *m_gobj;  /* nullptr if we are the prototype and not an instance */
-    GjsMaybeOwned<JSObject *> m_wrapper;
-    GType m_gtype;
-
-    /* a list of all GClosures installed on this object (from
-     * signals, trampolines and explicit GClosures), used when tracing */
-    std::set<GClosure *> m_closures;
-
-    /* the GObjectClass wrapped by this JS Object (only used for
-       prototypes) */
-    GTypeClass *m_class;
-
-    GjsListLink m_instance_link;
-
-    bool m_wrapper_finalized : 1;
-    bool m_gobj_disposed : 1;
-
-    /* True if this object has visible JS state, and thus its lifecycle is
-     * managed using toggle references. False if this object just keeps a
-     * hard ref on the underlying GObject, and may be finalized at will. */
-    bool m_uses_toggle_ref : 1;
-
- public:
-    static std::stack<JS::PersistentRootedObject> object_init_list;
-
-    /* Static methods to get an existing ObjectInstance */
-
- private:
-    static ObjectInstance *for_js_prototype(JSContext *cx, JS::HandleObject obj);
-
- public:
-    static ObjectInstance *for_gobject(GObject *gobj);
-    static ObjectInstance *for_js(JSContext *cx, JS::HandleObject obj);
-    static ObjectInstance *for_js_nocheck(JSObject *obj);
-    static ObjectInstance *for_gtype(GType gtype);  /* Prototype-only */
-
-    /* Constructors */
-
- private:
-    /* Constructor for instances */
-    ObjectInstance(JSContext *cx, JS::HandleObject obj);
-
- public:
-    /* Public constructor for instances (uses GSlice allocator) */
-    static ObjectInstance *new_for_js_object(JSContext *cx, JS::HandleObject obj);
-
-    /* Constructor for prototypes (only called from gjs_define_object_class) */
-    ObjectInstance(JSObject *prototype, GIObjectInfo *info, GType gtype);
-
-    /* Accessors */
-
- private:
-    bool is_prototype(void) const { return !m_gobj; }
-    bool is_custom_js_class(void) const { return !m_info; }
-    bool has_wrapper(void) const { return !!m_wrapper; }
-    const char *ns(void) const {
-        return m_info ? g_base_info_get_namespace(m_info) : "";
-    }
-    const char *name(void) const {
-        return m_info ? g_base_info_get_name(m_info) : type_name();
-    }
-    const char *type_name(void) const { return g_type_name(m_gtype); }
-
-    using PropertyCache =
-        JS::GCHashMap<JS::Heap<JSString*>, GjsAutoParam,
-                      js::DefaultHasher<JSString*>, js::SystemAllocPolicy>;
-    using FieldCache =
-        JS::GCHashMap<JS::Heap<JSString*>, GjsAutoInfo<GIFieldInfo>,
-                      js::DefaultHasher<JSString*>, js::SystemAllocPolicy>;
-    PropertyCache *get_property_cache(void);  /* Prototype-only */
-    FieldCache *get_field_cache(void);  /* Prototype-only */
-
- public:
-    GType gtype(void) const { return m_gtype; }
-    GObject *gobj(void) const { return m_gobj; }
-    JSObject *wrapper(void) const { return m_wrapper; }
-
-    /* Methods to manipulate the JS object wrapper */
-
- private:
-    void discard_wrapper(void) { m_wrapper.reset(); }
-    void switch_to_rooted(JSContext *cx) { m_wrapper.switch_to_rooted(cx); }
-    void switch_to_unrooted(void) { m_wrapper.switch_to_unrooted(); }
-    bool update_after_gc(void) { return m_wrapper.update_after_gc(); }
-
- public:
-    bool wrapper_is_rooted(void) const { return m_wrapper.rooted(); }
-    void release_native_object(void);
-    void associate_js_gobject(JSContext *cx, JS::HandleObject obj, GObject *gobj);
-    void disassociate_js_gobject(void);
-    bool weak_pointer_was_finalized(void);
-    void toggle_down(void);
-    void toggle_up(void);
-
-    /* Methods to manipulate the list of closures */
-
- private:
-    void invalidate_all_closures(void);
-
- public:
-    void associate_closure(JSContext *cx, GClosure *closure);
-    void ref_closures(void) {
-        for (GClosure *closure : m_closures)
-            g_closure_ref(closure);
-    }
-    void unref_closures(void) {
-        for (GClosure *closure : m_closures)
-            g_closure_unref(closure);
-    }
-
-    /* Helper methods for both prototypes and instances */
-
- private:
-    bool check_is_instance(JSContext* cx, const char* for_what) const;
-    void debug_lifecycle(const char *message) const {
-        gjs_debug_lifecycle(GJS_DEBUG_GOBJECT,
-                            "[%p: GObject %p JS wrapper %p %s.%s (%s)] %s",
-                            this, m_gobj, m_wrapper.get(), ns(), name(),
-                            type_name(), message);
-    }
-    void debug_jsprop_base(const char *message, const char *id, JSObject *obj) const {
-        gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
-                         "[%p: GObject %p JS object %p %s.%s (%s)] %s '%s'",
-                         this, m_gobj, obj, ns(), name(), type_name(),
-                         message, id);
-    }
-    void debug_jsprop(const char *message, jsid id, JSObject *obj) const {
-        debug_jsprop_base(message, gjs_debug_id(id).c_str(), obj);
-    }
-    void debug_jsprop(const char *message, JSString *id, JSObject *obj) const {
-        debug_jsprop_base(message, gjs_debug_string(id).c_str(), obj);
-    }
-    static void debug_jsprop_static(const char *message, jsid id, JSObject *obj) {
-        gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
-                         "[JS object %p] %s '%s', no instance associated",
-                         obj, message, gjs_debug_id(id).c_str());
-    }
-
- public:
-    void type_query_dynamic_safe(GTypeQuery* query);
-
-    /* Instance-only helper methods */
-
- private:
-    void set_object_qdata(void);
-    void unset_object_qdata(void);
-    void check_js_object_finalized(void);
-
- public:
-    void ensure_uses_toggle_ref(JSContext *cx);
-    bool check_gobject_disposed(const char* for_what) const;
-
-    /* Prototype-only helper methods */
-
- private:
-    GParamSpec *find_param_spec_from_id(JSContext *cx, JS::HandleString key);
-    GIFieldInfo *find_field_info_from_id(JSContext *cx, JS::HandleString key);
-    bool props_to_g_parameters(JSContext *cx, const JS::HandleValueArray& args,
-                               std::vector<const char *> *names,
-                               AutoGValueVector *values);
-    bool is_vfunc_unchanged(GIVFuncInfo *info);
-    bool resolve_no_info(JSContext *cx, JS::HandleObject obj, bool *resolved,
-                         const char *name);
-
- public:
-    void set_type_qdata(void);
-
-    /* Methods to manipulate the linked list of instances */
-
- private:
-    static ObjectInstance *wrapped_gobject_list;
-    ObjectInstance *next(void) const { return m_instance_link.next(); }
-    void link(void);
-    void unlink(void);
-
- public:
-    GjsListLink *get_link(void) { return &m_instance_link; }
-    static size_t num_wrapped_gobjects(void) {
-        return wrapped_gobject_list ?
-            wrapped_gobject_list->m_instance_link.size() : 0;
-    }
-    using Action = std::function<void(ObjectInstance *)>;
-    using Predicate = std::function<bool(ObjectInstance *)>;
-    static void iterate_wrapped_gobjects(Action action);
-    static void remove_wrapped_gobjects_if(Predicate predicate, Action action);
-
-    /* JSClass operations */
-
- private:
-    bool add_property_impl(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
-                           JS::HandleValue value);
-    bool resolve_impl(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
-                      bool *resolved);
-    void trace_impl(JSTracer *tracer);
-    void finalize_impl(JSFreeOp *fop, JSObject *obj);
-
- public:
-    static bool add_property(JSContext *cx, JS::HandleObject obj,
-                             JS::HandleId id, JS::HandleValue value);
-    static bool resolve(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
-                        bool *resolved);
-    static void finalize(JSFreeOp *fop, JSObject *obj);
-    static void trace(JSTracer *tracer, JSObject *obj);
-
-    /* JS property getters/setters */
-
- private:
-    bool prop_getter_impl(JSContext *cx, JS::HandleObject obj,
-                          JS::HandleString name, JS::MutableHandleValue rval);
-    bool field_getter_impl(JSContext *cx, JS::HandleObject obj,
-                           JS::HandleString name, JS::MutableHandleValue rval);
-    bool prop_setter_impl(JSContext *cx, JS::HandleObject obj,
-                          JS::HandleString name, JS::HandleValue value);
-    bool field_setter_impl(JSContext *cx, JS::HandleObject obj,
-                           JS::HandleString name, JS::HandleValue value);
-    static bool prop_getter(JSContext *cx, unsigned argc, JS::Value *vp);
-    static bool field_getter(JSContext *cx, unsigned argc, JS::Value *vp);
-    static bool prop_setter(JSContext *cx, unsigned argc, JS::Value *vp);
-    static bool field_setter(JSContext *cx, unsigned argc, JS::Value *vp);
-
-    /* JS methods */
-
- private:
-    bool connect_impl(JSContext *cx, const JS::CallArgs& args, bool after);
-    bool emit_impl(JSContext *cx, const JS::CallArgs& args);
-    bool to_string_impl(JSContext *cx, const JS::CallArgs& args);
-    bool init_impl(JSContext *cx, const JS::CallArgs& args,
-                   JS::MutableHandleObject obj);
-    bool hook_up_vfunc_impl(JSContext *cx, const JS::CallArgs& args);
-
- public:
-    static bool connect(JSContext *cx, unsigned argc, JS::Value *vp);
-    static bool connect_after(JSContext *cx, unsigned argc, JS::Value *vp);
-    static bool emit(JSContext *cx, unsigned argc, JS::Value *vp);
-    static bool to_string(JSContext *cx, unsigned argc, JS::Value *vp);
-    static bool init(JSContext *cx, unsigned argc, JS::Value *vp);
-    static bool hook_up_vfunc(JSContext *cx, unsigned argc, JS::Value *vp);
-
-    /* Methods connected to "public" API */
- private:
-    static JS::PersistentRootedSymbol hook_up_vfunc_root;
-
- public:
-    bool typecheck_object(JSContext *cx, GType expected_type, bool throw_error);
-    static JS::Symbol* hook_up_vfunc_symbol(JSContext *cx);
-
-    /* Notification callbacks */
-
-    void gobj_dispose_notify(void);
-    void context_dispose_notify(void);
-    static void closure_invalidated_notify(void *data, GClosure *closure) {
-        static_cast<ObjectInstance *>(data)->m_closures.erase(closure);
-    }
-
-    /* Quarks */
-    static GQuark custom_type_quark(void);
-    static GQuark custom_property_quark(void);
-};
-
 std::stack<JS::PersistentRootedObject> ObjectInstance::object_init_list{};
-
-using AutoParamArray = std::vector<GjsAutoParam>;
-static std::unordered_map<GType, AutoParamArray> class_init_properties;
-
 static bool weak_pointer_callback = false;
 ObjectInstance *ObjectInstance::wrapped_gobject_list = nullptr;
 JS::PersistentRootedSymbol ObjectInstance::hook_up_vfunc_root;
@@ -1981,11 +1690,9 @@ ObjectInstance::finalize_impl(JSFreeOp  *fop,
     GJS_DEC_COUNTER(object);
 }
 
-static JSObject *
-gjs_lookup_object_constructor_from_info(JSContext    *context,
-                                        GIObjectInfo *info,
-                                        GType         gtype)
-{
+JSObject* gjs_lookup_object_constructor_from_info(JSContext* context,
+                                                  GIObjectInfo* info,
+                                                  GType gtype) {
     JS::RootedObject in_object(context);
     const char *constructor_name;
 
@@ -2771,698 +2478,6 @@ ObjectInstance::hook_up_vfunc_impl(JSContext          *cx,
     return true;
 }
 
-static gchar *
-hyphen_to_underscore (gchar *string)
-{
-    gchar *str, *s;
-    str = s = g_strdup(string);
-    while (*(str++) != '\0') {
-        if (*str == '-')
-            *str = '_';
-    }
-    return s;
-}
-
-static void
-gjs_object_get_gproperty (GObject    *object,
-                          guint       property_id,
-                          GValue     *value,
-                          GParamSpec *pspec)
-{
-    GjsContext *gjs_context;
-    JSContext *context;
-    gchar *underscore_name;
-    auto *priv = ObjectInstance::for_gobject(object);
-
-    gjs_context = gjs_context_get_current();
-    context = (JSContext*) gjs_context_get_native_context(gjs_context);
-
-    JS::RootedObject js_obj(context, priv->wrapper());
-    JS::RootedValue jsvalue(context);
-
-    underscore_name = hyphen_to_underscore((gchar *)pspec->name);
-    if (!JS_GetProperty(context, js_obj, underscore_name, &jsvalue) ||
-        !gjs_value_to_g_value(context, jsvalue, value))
-        gjs_log_exception(context);
-    g_free (underscore_name);
-}
-
-static void
-jsobj_set_gproperty(JSContext       *context,
-                    JS::HandleObject object,
-                    const GValue    *value,
-                    GParamSpec      *pspec)
-{
-    JS::RootedValue jsvalue(context);
-    gchar *underscore_name;
-
-    if (!gjs_value_from_g_value(context, &jsvalue, value))
-        return;
-
-    underscore_name = hyphen_to_underscore((gchar *)pspec->name);
-    if (!JS_SetProperty(context, object, underscore_name, jsvalue))
-        gjs_log_exception(context);
-    g_free (underscore_name);
-}
-
-static GObject *
-gjs_object_constructor (GType                  type,
-                        guint                  n_construct_properties,
-                        GObjectConstructParam *construct_properties)
-{
-    if (!ObjectInstance::object_init_list.empty()) {
-        GType parent_type = g_type_parent(type);
-
-        /* The object is being constructed from JS:
-         * Simply chain up to the first non-gjs constructor
-         */
-        while (G_OBJECT_CLASS(g_type_class_peek(parent_type))->constructor == gjs_object_constructor)
-            parent_type = g_type_parent(parent_type);
-
-        return G_OBJECT_CLASS(g_type_class_peek(parent_type))->constructor(type, n_construct_properties, 
construct_properties);
-    }
-
-    GjsContext *gjs_context;
-    JSContext *context;
-    JSObject *object;
-
-    /* The object is being constructed from native code (e.g. GtkBuilder):
-     * Construct the JS object from the constructor, then use the GObject
-     * that was associated in gjs_object_custom_init()
-     */
-    gjs_context = gjs_context_get_current();
-    context = (JSContext*) gjs_context_get_native_context(gjs_context);
-
-    JSAutoRequest ar(context);
-    JSAutoCompartment ac(context, gjs_get_import_global(context));
-
-    JS::RootedObject constructor(context,
-        gjs_lookup_object_constructor_from_info(context, NULL, type));
-    if (!constructor)
-        return NULL;
-
-    if (n_construct_properties) {
-        guint i;
-
-        JS::RootedObject props_hash(context, JS_NewPlainObject(context));
-
-        for (i = 0; i < n_construct_properties; i++)
-            jsobj_set_gproperty(context, props_hash,
-                                construct_properties[i].value,
-                                construct_properties[i].pspec);
-
-        JS::AutoValueArray<1> args(context);
-        args[0].set(JS::ObjectValue(*props_hash));
-        object = JS_New(context, constructor, args);
-    } else {
-        object = JS_New(context, constructor, JS::HandleValueArray::empty());
-    }
-
-    if (!object)
-        return NULL;
-
-    auto *priv = ObjectInstance::for_js_nocheck(object);
-    /* Should have been set in init_impl() and pushed into object_init_list,
-     * then popped from object_init_list in gjs_object_custom_init() */
-    g_assert(priv);
-    /* We only hold a toggle ref at this point, add back a ref that the
-     * native code can own.
-     */
-    return G_OBJECT(g_object_ref(priv->gobj()));
-}
-
-static void
-gjs_object_set_gproperty (GObject      *object,
-                          guint         property_id,
-                          const GValue *value,
-                          GParamSpec   *pspec)
-{
-    GjsContext *gjs_context;
-    JSContext *context;
-    auto *priv = ObjectInstance::for_gobject(object);
-
-    gjs_context = gjs_context_get_current();
-    context = (JSContext*) gjs_context_get_native_context(gjs_context);
-
-    JS::RootedObject js_obj(context, priv->wrapper());
-    jsobj_set_gproperty(context, js_obj, value, pspec);
-}
-
-static bool
-gjs_override_property(JSContext *cx,
-                      unsigned   argc,
-                      JS::Value *vp)
-{
-    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-    GjsAutoJSChar name;
-    JS::RootedObject type(cx);
-    GParamSpec *pspec;
-    GParamSpec *new_pspec;
-    GType gtype;
-
-    if (!gjs_parse_call_args(cx, "override_property", args, "so",
-                             "name", &name,
-                             "type", &type))
-        return false;
-
-    if ((gtype = gjs_gtype_get_actual_gtype(cx, type)) == G_TYPE_INVALID) {
-        gjs_throw(cx, "Invalid parameter type was not a GType");
-        return false;
-    }
-
-    if (g_type_is_a(gtype, G_TYPE_INTERFACE)) {
-        GTypeInterface *interface_type =
-            (GTypeInterface *) g_type_default_interface_ref(gtype);
-        pspec = g_object_interface_find_property(interface_type, name);
-        g_type_default_interface_unref(interface_type);
-    } else {
-        GTypeClass *class_type = (GTypeClass *) g_type_class_ref(gtype);
-        pspec = g_object_class_find_property(G_OBJECT_CLASS(class_type), name);
-        g_type_class_unref(class_type);
-    }
-
-    if (pspec == NULL) {
-        gjs_throw(cx, "No such property '%s' to override on type '%s'",
-                  name.get(), g_type_name(gtype));
-        return false;
-    }
-
-    new_pspec = g_param_spec_override(name, pspec);
-
-    g_param_spec_set_qdata(new_pspec, ObjectInstance::custom_property_quark(),
-                           GINT_TO_POINTER(1));
-
-    args.rval().setObject(*gjs_param_from_g_param(cx, new_pspec));
-    g_param_spec_unref(new_pspec);
-
-    return true;
-}
-
-static void
-gjs_interface_init(GTypeInterface *g_iface,
-                   gpointer        iface_data)
-{
-    GType gtype = G_TYPE_FROM_INTERFACE(g_iface);
-
-    auto found = class_init_properties.find(gtype);
-    if (found == class_init_properties.end())
-        return;
-
-    AutoParamArray& properties = found->second;
-    for (GjsAutoParam& pspec : properties) {
-        g_param_spec_set_qdata(pspec, ObjectInstance::custom_property_quark(),
-                               GINT_TO_POINTER(1));
-        g_object_interface_install_property(g_iface, pspec);
-    }
-
-    class_init_properties.erase(found);
-}
-
-static void
-gjs_object_class_init(GObjectClass *klass,
-                      gpointer      user_data)
-{
-    GType gtype = G_OBJECT_CLASS_TYPE(klass);
-
-    klass->constructor = gjs_object_constructor;
-    klass->set_property = gjs_object_set_gproperty;
-    klass->get_property = gjs_object_get_gproperty;
-
-    auto found = class_init_properties.find(gtype);
-    if (found == class_init_properties.end())
-        return;
-
-    AutoParamArray& properties = found->second;
-    unsigned i = 0;
-    for (GjsAutoParam& pspec : properties) {
-        g_param_spec_set_qdata(pspec, ObjectInstance::custom_property_quark(),
-                               GINT_TO_POINTER(1));
-        g_object_class_install_property(klass, ++i, pspec);
-    }
-
-    class_init_properties.erase(found);
-}
-
-static void
-gjs_object_custom_init(GTypeInstance *instance,
-                       gpointer       klass)
-{
-    GjsContext *gjs_context;
-    JSContext *context;
-    ObjectInstance *priv;
-
-    if (ObjectInstance::object_init_list.empty())
-        return;
-
-    gjs_context = gjs_context_get_current();
-    context = (JSContext*) gjs_context_get_native_context(gjs_context);
-
-    JS::RootedObject object(context,
-                            ObjectInstance::object_init_list.top().get());
-    priv = ObjectInstance::for_js_nocheck(object);
-    g_assert(priv);  /* Should have been set in init_impl() */
-
-    if (priv->gtype() != G_TYPE_FROM_INSTANCE(instance)) {
-        /* This is not the most derived instance_init function,
-           do nothing.
-         */
-        return;
-    }
-
-    ObjectInstance::object_init_list.pop();
-
-    priv->associate_js_gobject(context, object, G_OBJECT(instance));
-
-    /* Custom JS objects will most likely have visible state, so
-     * just do this from the start */
-    priv->ensure_uses_toggle_ref(context);
-
-    JS::RootedValue v(context);
-    if (!gjs_object_get_property(context, object,
-                                 GJS_STRING_INSTANCE_INIT, &v)) {
-        gjs_log_exception(context);
-        return;
-    }
-
-    if (!v.isObject())
-        return;
-
-    JS::RootedValue r(context);
-    if (!JS_CallFunctionValue(context, object, v,
-                              JS::HandleValueArray::empty(), &r))
-        gjs_log_exception(context);
-}
-
-static inline void
-gjs_add_interface(GType instance_type,
-                  GType interface_type)
-{
-    static GInterfaceInfo interface_vtable = { NULL, NULL, NULL };
-
-    g_type_add_interface_static(instance_type,
-                                interface_type,
-                                &interface_vtable);
-}
-
-static bool
-validate_interfaces_and_properties_args(JSContext       *cx,
-                                        JS::HandleObject interfaces,
-                                        JS::HandleObject properties,
-                                        uint32_t        *n_interfaces,
-                                        uint32_t        *n_properties)
-{
-    guint32 n_int, n_prop;
-    bool is_array;
-
-    if (!JS_IsArrayObject(cx, interfaces, &is_array))
-        return false;
-    if (!is_array) {
-        gjs_throw(cx, "Invalid parameter interfaces (expected Array)");
-        return false;
-    }
-
-    if (!JS_GetArrayLength(cx, interfaces, &n_int))
-        return false;
-
-    if (!JS_IsArrayObject(cx, properties, &is_array))
-        return false;
-    if (!is_array) {
-        gjs_throw(cx, "Invalid parameter properties (expected Array)");
-        return false;
-    }
-
-    if (!JS_GetArrayLength(cx, properties, &n_prop))
-        return false;
-
-    if (n_interfaces != NULL)
-        *n_interfaces = n_int;
-    if (n_properties != NULL)
-        *n_properties = n_prop;
-    return true;
-}
-
-static bool
-get_interface_gtypes(JSContext       *cx,
-                     JS::HandleObject interfaces,
-                     uint32_t         n_interfaces,
-                     GType           *iface_types)
-{
-    guint32 i;
-
-    for (i = 0; i < n_interfaces; i++) {
-        JS::RootedValue iface_val(cx);
-        GType iface_type;
-
-        if (!JS_GetElement(cx, interfaces, i, &iface_val))
-            return false;
-
-        if (!iface_val.isObject()) {
-            gjs_throw (cx, "Invalid parameter interfaces (element %d was not a GType)", i);
-            return false;
-        }
-
-        JS::RootedObject iface(cx, &iface_val.toObject());
-        iface_type = gjs_gtype_get_actual_gtype(cx, iface);
-        if (iface_type == G_TYPE_INVALID) {
-            gjs_throw (cx, "Invalid parameter interfaces (element %d was not a GType)", i);
-            return false;
-        }
-
-        iface_types[i] = iface_type;
-    }
-    return true;
-}
-
-static bool
-save_properties_for_class_init(JSContext       *cx,
-                               JS::HandleObject properties,
-                               uint32_t         n_properties,
-                               GType            gtype)
-{
-    AutoParamArray properties_native;
-    JS::RootedValue prop_val(cx);
-    JS::RootedObject prop_obj(cx);
-    for (uint32_t i = 0; i < n_properties; i++) {
-        if (!JS_GetElement(cx, properties, i, &prop_val))
-            return false;
-
-        if (!prop_val.isObject()) {
-            gjs_throw(cx, "Invalid parameter, expected object");
-            return false;
-        }
-
-        prop_obj = &prop_val.toObject();
-        if (!gjs_typecheck_param(cx, prop_obj, G_TYPE_NONE, true))
-            return false;
-
-        properties_native.emplace_back(
-            g_param_spec_ref(gjs_g_param_from_param(cx, prop_obj)));
-    }
-    class_init_properties[gtype] = std::move(properties_native);
-    return true;
-}
-
-static bool
-gjs_register_interface(JSContext *cx,
-                       unsigned   argc,
-                       JS::Value *vp)
-{
-    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-    GjsAutoJSChar name;
-    guint32 i, n_interfaces, n_properties;
-    GType *iface_types;
-    GType interface_type;
-    GTypeInfo type_info = {
-        sizeof (GTypeInterface), /* class_size */
-
-        (GBaseInitFunc) NULL,
-        (GBaseFinalizeFunc) NULL,
-
-        (GClassInitFunc) gjs_interface_init,
-        (GClassFinalizeFunc) NULL,
-        NULL, /* class_data */
-
-        0,    /* instance_size */
-        0,    /* n_preallocs */
-        NULL, /* instance_init */
-    };
-
-    JS::RootedObject interfaces(cx), properties(cx);
-    if (!gjs_parse_call_args(cx, "register_interface", args, "soo",
-                             "name", &name,
-                             "interfaces", &interfaces,
-                             "properties", &properties))
-        return false;
-
-    if (!validate_interfaces_and_properties_args(cx, interfaces, properties,
-                                                 &n_interfaces, &n_properties))
-        return false;
-
-    iface_types = (GType *) g_alloca(sizeof(GType) * n_interfaces);
-
-    /* We do interface addition in two passes so that any failure
-       is caught early, before registering the GType (which we can't undo) */
-    if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types))
-        return false;
-
-    if (g_type_from_name(name) != G_TYPE_INVALID) {
-        gjs_throw(cx, "Type name %s is already registered", name.get());
-        return false;
-    }
-
-    interface_type = g_type_register_static(G_TYPE_INTERFACE, name, &type_info,
-                                            (GTypeFlags) 0);
-
-    g_type_set_qdata(interface_type, ObjectInstance::custom_type_quark(),
-                     GINT_TO_POINTER(1));
-
-    if (!save_properties_for_class_init(cx, properties, n_properties, interface_type))
-        return false;
-
-    for (i = 0; i < n_interfaces; i++)
-        g_type_interface_add_prerequisite(interface_type, iface_types[i]);
-
-    /* create a custom JSClass */
-    JS::RootedObject module(cx, gjs_lookup_private_namespace(cx));
-    if (!module)
-        return false;  /* error will have been thrown already */
-
-    JS::RootedObject constructor(cx);
-    gjs_define_interface_class(cx, module, NULL, interface_type, &constructor);
-
-    args.rval().setObject(*constructor);
-    return true;
-}
-
-static void
-gjs_object_base_init(void *klass)
-{
-    auto *priv = ObjectInstance::for_gtype(G_OBJECT_CLASS_TYPE(klass));
-    if (priv)
-        priv->ref_closures();
-}
-
-static void
-gjs_object_base_finalize(void *klass)
-{
-    auto *priv = ObjectInstance::for_gtype(G_OBJECT_CLASS_TYPE(klass));
-    if (priv)
-        priv->unref_closures();
-}
-
-static bool
-gjs_register_type(JSContext *cx,
-                  unsigned   argc,
-                  JS::Value *vp)
-{
-    JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
-    GjsAutoJSChar name;
-    GType instance_type;
-    GTypeQuery query;
-    ObjectInstance *parent_priv;
-    GTypeInfo type_info = {
-        0, /* class_size */
-
-        gjs_object_base_init,
-        gjs_object_base_finalize,
-
-       (GClassInitFunc) gjs_object_class_init,
-       (GClassFinalizeFunc) NULL,
-       NULL, /* class_data */
-
-       0,    /* instance_size */
-       0,    /* n_preallocs */
-       gjs_object_custom_init,
-    };
-    guint32 i, n_interfaces, n_properties;
-    GType *iface_types;
-
-    JSAutoRequest ar(cx);
-
-    JS::RootedObject parent(cx), interfaces(cx), properties(cx);
-    if (!gjs_parse_call_args(cx, "register_type", argv, "osoo",
-                             "parent", &parent,
-                             "name", &name,
-                             "interfaces", &interfaces,
-                             "properties", &properties))
-        return false;
-
-    if (!parent)
-        return false;
-
-    if (!do_base_typecheck(cx, parent, true))
-        return false;
-
-    if (!validate_interfaces_and_properties_args(cx, interfaces, properties,
-                                                 &n_interfaces, &n_properties))
-        return false;
-
-    iface_types = (GType*) g_alloca(sizeof(GType) * n_interfaces);
-
-    /* We do interface addition in two passes so that any failure
-       is caught early, before registering the GType (which we can't undo) */
-    if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types))
-        return false;
-
-    if (g_type_from_name(name) != G_TYPE_INVALID) {
-        gjs_throw(cx, "Type name %s is already registered", name.get());
-        return false;
-    }
-
-    parent_priv = ObjectInstance::for_js(cx, parent);
-
-    /* We checked parent above, in do_base_typecheck() */
-    g_assert(parent_priv != NULL);
-
-    parent_priv->type_query_dynamic_safe(&query);
-    if (G_UNLIKELY (query.type == 0)) {
-        gjs_throw (cx, "Cannot inherit from a non-gjs dynamic type [bug 687184]");
-        return false;
-    }
-
-    type_info.class_size = query.class_size;
-    type_info.instance_size = query.instance_size;
-
-    instance_type = g_type_register_static(parent_priv->gtype(), name,
-                                           &type_info, GTypeFlags(0));
-
-    g_type_set_qdata(instance_type, ObjectInstance::custom_type_quark(),
-                     GINT_TO_POINTER(1));
-
-    if (!save_properties_for_class_init(cx, properties, n_properties, instance_type))
-        return false;
-
-    for (i = 0; i < n_interfaces; i++)
-        gjs_add_interface(instance_type, iface_types[i]);
-
-    /* create a custom JSClass */
-    JS::RootedObject module(cx, gjs_lookup_private_namespace(cx));
-    JS::RootedObject constructor(cx), prototype(cx);
-    if (!gjs_define_object_class(cx, module, nullptr, instance_type, &constructor,
-                                 &prototype))
-        return false;
-
-    ObjectInstance *priv = priv_from_js(cx, prototype);
-    g_type_set_qdata(instance_type, gjs_object_priv_quark(), priv);
-
-    argv.rval().setObject(*constructor);
-
-    return true;
-}
-
-static bool
-gjs_signal_new(JSContext *cx,
-               unsigned   argc,
-               JS::Value *vp)
-{
-    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-    GjsAutoJSChar signal_name;
-    GSignalAccumulator accumulator;
-    gint signal_id;
-    guint i, n_parameters;
-    int32_t flags, accumulator_enum;
-
-    JSAutoRequest ar(cx);
-
-    JS::RootedObject gtype_obj(cx), return_gtype_obj(cx), params_obj(cx);
-    if (!gjs_parse_call_args(cx, "signal_new", args, "osiioo",
-                             "gtype", &gtype_obj,
-                             "signal name", &signal_name,
-                             "flags", &flags,
-                             "accumulator", &accumulator_enum,
-                             "return gtype", &return_gtype_obj,
-                             "params", &params_obj))
-        return false;
-
-    if (!gjs_typecheck_gtype(cx, gtype_obj, true))
-        return false;
-
-    /* we only support standard accumulators for now */
-    switch (accumulator_enum) {
-    case 1:
-        accumulator = g_signal_accumulator_first_wins;
-        break;
-    case 2:
-        accumulator = g_signal_accumulator_true_handled;
-        break;
-    case 0:
-    default:
-        accumulator = NULL;
-    }
-
-    GType return_type = gjs_gtype_get_actual_gtype(cx, return_gtype_obj);
-
-    if (accumulator == g_signal_accumulator_true_handled && return_type != G_TYPE_BOOLEAN) {
-        gjs_throw (cx, "GObject.SignalAccumulator.TRUE_HANDLED can only be used with boolean signals");
-        return false;
-    }
-
-    if (!JS_GetArrayLength(cx, params_obj, &n_parameters))
-        return false;
-
-    GType *params = g_newa(GType, n_parameters);
-    JS::RootedValue gtype_val(cx);
-    for (i = 0; i < n_parameters; i++) {
-        if (!JS_GetElement(cx, params_obj, i, &gtype_val) ||
-            !gtype_val.isObject()) {
-            gjs_throw(cx, "Invalid signal parameter number %d", i);
-            return false;
-        }
-
-        JS::RootedObject gjs_gtype(cx, &gtype_val.toObject());
-        params[i] = gjs_gtype_get_actual_gtype(cx, gjs_gtype);
-    }
-
-    GType gtype = gjs_gtype_get_actual_gtype(cx, gtype_obj);
-
-    signal_id = g_signal_newv(signal_name,
-                              gtype,
-                              GSignalFlags(flags),
-                              NULL, /* class closure */
-                              accumulator,
-                              NULL, /* accu_data */
-                              g_cclosure_marshal_generic,
-                              return_type, /* return type */
-                              n_parameters,
-                              params);
-
-    args.rval().setInt32(signal_id);
-    return true;
-}
-
-static bool
-hook_up_vfunc_symbol_getter(JSContext *cx,
-                            unsigned   argc,
-                            JS::Value *vp)
-{
-    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-    args.rval().setSymbol(ObjectInstance::hook_up_vfunc_symbol(cx));
-    return true;
-}
-
-static JSFunctionSpec module_funcs[] = {
-    JS_FS("override_property", gjs_override_property, 2, GJS_MODULE_PROP_FLAGS),
-    JS_FS("register_interface", gjs_register_interface, 3, GJS_MODULE_PROP_FLAGS),
-    JS_FS("register_type", gjs_register_type, 4, GJS_MODULE_PROP_FLAGS),
-    JS_FS("signal_new", gjs_signal_new, 6, GJS_MODULE_PROP_FLAGS),
-    JS_FS_END,
-};
-
-static JSPropertySpec module_props[] = {
-    JS_PSG("hook_up_vfunc_symbol", hook_up_vfunc_symbol_getter,
-           GJS_MODULE_PROP_FLAGS),
-    JS_PS_END
-};
-
-bool
-gjs_define_private_gi_stuff(JSContext              *cx,
-                            JS::MutableHandleObject module)
-{
-    module.set(JS_NewPlainObject(cx));
-    return JS_DefineFunctions(cx, module, module_funcs) &&
-        JS_DefineProperties(cx, module, module_props);
-}
-
 bool
 gjs_lookup_object_constructor(JSContext             *context,
                               GType                  gtype,
diff --git a/gi/object.h b/gi/object.h
index a7e855b0..cbc66ee9 100644
--- a/gi/object.h
+++ b/gi/object.h
@@ -24,11 +24,308 @@
 #ifndef __GJS_OBJECT_H__
 #define __GJS_OBJECT_H__
 
-#include <stdbool.h>
-
 #include <glib-object.h>
 #include <girepository.h>
+
+#include <functional>
+#include <set>
+#include <stack>
+#include <vector>
+
+#include "gjs/jsapi-util-root.h"
 #include "gjs/jsapi-util.h"
+#include "gjs/jsapi-wrapper.h"
+
+#include "js/GCHashTable.h"
+
+class ObjectInstance;
+
+class GjsListLink {
+ private:
+    ObjectInstance* m_prev;
+    ObjectInstance* m_next;
+
+ public:
+    ObjectInstance* prev(void) const { return m_prev; }
+    ObjectInstance* next(void) const { return m_next; }
+
+    void prepend(ObjectInstance* this_instance, ObjectInstance* head);
+    void unlink(void);
+    size_t size(void) const;
+};
+
+struct AutoGValueVector : public std::vector<GValue> {
+    ~AutoGValueVector() {
+        for (GValue value : *this)
+            g_value_unset(&value);
+    }
+};
+
+class ObjectInstance {
+    GIObjectInfo* m_info;
+    GObject* m_gobj;  // nullptr if we are the prototype and not an instance
+    GjsMaybeOwned<JSObject*> m_wrapper;
+    GType m_gtype;
+
+    /* a list of all GClosures installed on this object (from
+     * signals, trampolines and explicit GClosures), used when tracing */
+    std::set<GClosure*> m_closures;
+
+    /* the GObjectClass wrapped by this JS Object (only used for
+       prototypes) */
+    GTypeClass* m_class;
+
+    GjsListLink m_instance_link;
+
+    bool m_wrapper_finalized : 1;
+    bool m_gobj_disposed : 1;
+
+    /* True if this object has visible JS state, and thus its lifecycle is
+     * managed using toggle references. False if this object just keeps a
+     * hard ref on the underlying GObject, and may be finalized at will. */
+    bool m_uses_toggle_ref : 1;
+
+ public:
+    static std::stack<JS::PersistentRootedObject> object_init_list;
+
+    /* Static methods to get an existing ObjectInstance */
+
+ private:
+    static ObjectInstance* for_js_prototype(JSContext* cx,
+                                            JS::HandleObject obj);
+
+ public:
+    static ObjectInstance* for_gobject(GObject* gobj);
+    static ObjectInstance* for_js(JSContext* cx, JS::HandleObject obj);
+    static ObjectInstance* for_js_nocheck(JSObject* obj);
+    static ObjectInstance* for_gtype(GType gtype);  // Prototype-only
+
+    /* Constructors */
+
+ private:
+    /* Constructor for instances */
+    ObjectInstance(JSContext* cx, JS::HandleObject obj);
+
+ public:
+    /* Public constructor for instances (uses GSlice allocator) */
+    static ObjectInstance* new_for_js_object(JSContext* cx,
+                                             JS::HandleObject obj);
+
+    /* Constructor for prototypes (only called from gjs_define_object_class) */
+    ObjectInstance(JSObject* prototype, GIObjectInfo* info, GType gtype);
+
+    /* Accessors */
+
+ private:
+    bool is_prototype(void) const { return !m_gobj; }
+    bool is_custom_js_class(void) const { return !m_info; }
+    bool has_wrapper(void) const { return !!m_wrapper; }
+    const char* ns(void) const {
+        return m_info ? g_base_info_get_namespace(m_info) : "";
+    }
+    const char* name(void) const {
+        return m_info ? g_base_info_get_name(m_info) : type_name();
+    }
+    const char* type_name(void) const { return g_type_name(m_gtype); }
+
+    using PropertyCache =
+        JS::GCHashMap<JS::Heap<JSString*>, GjsAutoParam,
+                      js::DefaultHasher<JSString*>, js::SystemAllocPolicy>;
+    using FieldCache =
+        JS::GCHashMap<JS::Heap<JSString*>, GjsAutoInfo<GIFieldInfo>,
+                      js::DefaultHasher<JSString*>, js::SystemAllocPolicy>;
+    PropertyCache* get_property_cache(void);  // Prototype-only
+    FieldCache* get_field_cache(void);        // Prototype-only
+
+ public:
+    GType gtype(void) const { return m_gtype; }
+    GObject* gobj(void) const { return m_gobj; }
+    JSObject* wrapper(void) const { return m_wrapper; }
+
+    /* Methods to manipulate the JS object wrapper */
+
+ private:
+    void discard_wrapper(void) { m_wrapper.reset(); }
+    void switch_to_rooted(JSContext* cx) { m_wrapper.switch_to_rooted(cx); }
+    void switch_to_unrooted(void) { m_wrapper.switch_to_unrooted(); }
+    bool update_after_gc(void) { return m_wrapper.update_after_gc(); }
+
+ public:
+    bool wrapper_is_rooted(void) const { return m_wrapper.rooted(); }
+    void release_native_object(void);
+    void associate_js_gobject(JSContext* cx, JS::HandleObject obj,
+                              GObject* gobj);
+    void disassociate_js_gobject(void);
+    bool weak_pointer_was_finalized(void);
+    void toggle_down(void);
+    void toggle_up(void);
+
+    /* Methods to manipulate the list of closures */
+
+ private:
+    void invalidate_all_closures(void);
+
+ public:
+    void associate_closure(JSContext* cx, GClosure* closure);
+    void ref_closures(void) {
+        for (GClosure* closure : m_closures)
+            g_closure_ref(closure);
+    }
+    void unref_closures(void) {
+        for (GClosure* closure : m_closures)
+            g_closure_unref(closure);
+    }
+
+    /* Helper methods for both prototypes and instances */
+
+ private:
+    bool check_is_instance(JSContext* cx, const char* for_what) const;
+    void debug_lifecycle(const char* message) const {
+        gjs_debug_lifecycle(
+            GJS_DEBUG_GOBJECT, "[%p: GObject %p JS wrapper %p %s.%s (%s)] %s",
+            this, m_gobj, m_wrapper.get(), ns(), name(), type_name(), message);
+    }
+    void debug_jsprop_base(const char* message, const char* id,
+                           JSObject* obj) const {
+        gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
+                         "[%p: GObject %p JS object %p %s.%s (%s)] %s '%s'",
+                         this, m_gobj, obj, ns(), name(), type_name(), message,
+                         id);
+    }
+    void debug_jsprop(const char* message, jsid id, JSObject* obj) const {
+        debug_jsprop_base(message, gjs_debug_id(id).c_str(), obj);
+    }
+    void debug_jsprop(const char* message, JSString* id, JSObject* obj) const {
+        debug_jsprop_base(message, gjs_debug_string(id).c_str(), obj);
+    }
+    static void debug_jsprop_static(const char* message, jsid id,
+                                    JSObject* obj) {
+        gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
+                         "[JS object %p] %s '%s', no instance associated", obj,
+                         message, gjs_debug_id(id).c_str());
+    }
+
+ public:
+    void type_query_dynamic_safe(GTypeQuery* query);
+
+    /* Instance-only helper methods */
+
+ private:
+    void set_object_qdata(void);
+    void unset_object_qdata(void);
+    void check_js_object_finalized(void);
+
+ public:
+    void ensure_uses_toggle_ref(JSContext* cx);
+    bool check_gobject_disposed(const char* for_what) const;
+
+    /* Prototype-only helper methods */
+
+ private:
+    GParamSpec* find_param_spec_from_id(JSContext* cx, JS::HandleString key);
+    GIFieldInfo* find_field_info_from_id(JSContext* cx, JS::HandleString key);
+    bool props_to_g_parameters(JSContext* cx, const JS::HandleValueArray& args,
+                               std::vector<const char*>* names,
+                               AutoGValueVector* values);
+    bool is_vfunc_unchanged(GIVFuncInfo* info);
+    bool resolve_no_info(JSContext* cx, JS::HandleObject obj, bool* resolved,
+                         const char* name);
+
+ public:
+    void set_type_qdata(void);
+
+    /* Methods to manipulate the linked list of instances */
+
+ private:
+    static ObjectInstance* wrapped_gobject_list;
+    ObjectInstance* next(void) const { return m_instance_link.next(); }
+    void link(void);
+    void unlink(void);
+
+ public:
+    GjsListLink* get_link(void) { return &m_instance_link; }
+    static size_t num_wrapped_gobjects(void) {
+        return wrapped_gobject_list
+                   ? wrapped_gobject_list->m_instance_link.size()
+                   : 0;
+    }
+    using Action = std::function<void(ObjectInstance*)>;
+    using Predicate = std::function<bool(ObjectInstance*)>;
+    static void iterate_wrapped_gobjects(Action action);
+    static void remove_wrapped_gobjects_if(Predicate predicate, Action action);
+
+    /* JSClass operations */
+
+ private:
+    bool add_property_impl(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                           JS::HandleValue value);
+    bool resolve_impl(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                      bool* resolved);
+    void trace_impl(JSTracer* tracer);
+    void finalize_impl(JSFreeOp* fop, JSObject* obj);
+
+ public:
+    static bool add_property(JSContext* cx, JS::HandleObject obj,
+                             JS::HandleId id, JS::HandleValue value);
+    static bool resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                        bool* resolved);
+    static void finalize(JSFreeOp* fop, JSObject* obj);
+    static void trace(JSTracer* tracer, JSObject* obj);
+
+    /* JS property getters/setters */
+
+ private:
+    bool prop_getter_impl(JSContext* cx, JS::HandleObject obj,
+                          JS::HandleString name, JS::MutableHandleValue rval);
+    bool field_getter_impl(JSContext* cx, JS::HandleObject obj,
+                           JS::HandleString name, JS::MutableHandleValue rval);
+    bool prop_setter_impl(JSContext* cx, JS::HandleObject obj,
+                          JS::HandleString name, JS::HandleValue value);
+    bool field_setter_impl(JSContext* cx, JS::HandleObject obj,
+                           JS::HandleString name, JS::HandleValue value);
+    static bool prop_getter(JSContext* cx, unsigned argc, JS::Value* vp);
+    static bool field_getter(JSContext* cx, unsigned argc, JS::Value* vp);
+    static bool prop_setter(JSContext* cx, unsigned argc, JS::Value* vp);
+    static bool field_setter(JSContext* cx, unsigned argc, JS::Value* vp);
+
+    /* JS methods */
+
+ private:
+    bool connect_impl(JSContext* cx, const JS::CallArgs& args, bool after);
+    bool emit_impl(JSContext* cx, const JS::CallArgs& args);
+    bool to_string_impl(JSContext* cx, const JS::CallArgs& args);
+    bool init_impl(JSContext* cx, const JS::CallArgs& args,
+                   JS::MutableHandleObject obj);
+    bool hook_up_vfunc_impl(JSContext* cx, const JS::CallArgs& args);
+
+ public:
+    static bool connect(JSContext* cx, unsigned argc, JS::Value* vp);
+    static bool connect_after(JSContext* cx, unsigned argc, JS::Value* vp);
+    static bool emit(JSContext* cx, unsigned argc, JS::Value* vp);
+    static bool to_string(JSContext* cx, unsigned argc, JS::Value* vp);
+    static bool init(JSContext* cx, unsigned argc, JS::Value* vp);
+    static bool hook_up_vfunc(JSContext* cx, unsigned argc, JS::Value* vp);
+
+    /* Methods connected to "public" API */
+ private:
+    static JS::PersistentRootedSymbol hook_up_vfunc_root;
+
+ public:
+    bool typecheck_object(JSContext* cx, GType expected_type, bool throw_error);
+    static JS::Symbol* hook_up_vfunc_symbol(JSContext* cx);
+
+    /* Notification callbacks */
+
+    void gobj_dispose_notify(void);
+    void context_dispose_notify(void);
+    static void closure_invalidated_notify(void* data, GClosure* closure) {
+        static_cast<ObjectInstance*>(data)->m_closures.erase(closure);
+    }
+
+    /* Quarks */
+    static GQuark custom_type_quark(void);
+    static GQuark custom_property_quark(void);
+};
 
 G_BEGIN_DECLS
 
@@ -42,6 +339,9 @@ bool gjs_define_object_class(JSContext              *cx,
 bool gjs_lookup_object_constructor(JSContext             *context,
                                    GType                  gtype,
                                    JS::MutableHandleValue value_p);
+JSObject* gjs_lookup_object_constructor_from_info(JSContext* cx,
+                                                  GIObjectInfo* info,
+                                                  GType gtype);
 
 JSObject* gjs_object_from_g_object      (JSContext     *context,
                                          GObject       *gobj);
@@ -69,9 +369,6 @@ bool gjs_object_define_static_methods(JSContext       *context,
                                       GType            gtype,
                                       GIObjectInfo    *object_info);
 
-bool gjs_define_private_gi_stuff(JSContext              *cx,
-                                 JS::MutableHandleObject module);
-
 bool gjs_object_associate_closure(JSContext       *cx,
                                   JS::HandleObject obj,
                                   GClosure        *closure);
diff --git a/gi/private.cpp b/gi/private.cpp
new file mode 100644
index 00000000..8f266c32
--- /dev/null
+++ b/gi/private.cpp
@@ -0,0 +1,414 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2008  litl, LLC
+ * Copyright (c) 2018  Philip Chimento <philip chimento gmail com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <glib-object.h>
+
+#include <unordered_map>
+
+#include "gjs/jsapi-util-args.h"
+#include "gjs/jsapi-util.h"
+#include "gjs/jsapi-wrapper.h"
+#include "gobject.h"
+#include "interface.h"
+#include "object.h"
+#include "param.h"
+#include "private.h"
+#include "repo.h"
+
+/* gi/private.cpp - private "imports._gi" module with operations that we need
+ * to use from JS in order to create GObject classes, but should not be exposed
+ * to client code.
+ */
+
+static bool gjs_override_property(JSContext* cx, unsigned argc, JS::Value* vp) {
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    GjsAutoJSChar name;
+    JS::RootedObject type(cx);
+
+    if (!gjs_parse_call_args(cx, "override_property", args, "so", "name", &name,
+                             "type", &type))
+        return false;
+
+    GType gtype = gjs_gtype_get_actual_gtype(cx, type);
+    if (gtype == G_TYPE_INVALID) {
+        gjs_throw(cx, "Invalid parameter type was not a GType");
+        return false;
+    }
+
+    GParamSpec* pspec;
+    if (g_type_is_a(gtype, G_TYPE_INTERFACE)) {
+        auto* interface_type =
+            static_cast<GTypeInterface*>(g_type_default_interface_ref(gtype));
+        pspec = g_object_interface_find_property(interface_type, name);
+        g_type_default_interface_unref(interface_type);
+    } else {
+        auto* class_type = static_cast<GTypeClass*>(g_type_class_ref(gtype));
+        pspec = g_object_class_find_property(G_OBJECT_CLASS(class_type), name);
+        g_type_class_unref(class_type);
+    }
+
+    if (!pspec) {
+        gjs_throw(cx, "No such property '%s' to override on type '%s'",
+                  name.get(), g_type_name(gtype));
+        return false;
+    }
+
+    GjsAutoParam new_pspec = g_param_spec_override(name, pspec);
+
+    g_param_spec_set_qdata(new_pspec, ObjectInstance::custom_property_quark(),
+                           GINT_TO_POINTER(1));
+
+    args.rval().setObject(*gjs_param_from_g_param(cx, new_pspec.get()));
+
+    return true;
+}
+
+static bool validate_interfaces_and_properties_args(JSContext* cx,
+                                                    JS::HandleObject interfaces,
+                                                    JS::HandleObject properties,
+                                                    uint32_t* n_interfaces,
+                                                    uint32_t* n_properties) {
+    bool is_array;
+    if (!JS_IsArrayObject(cx, interfaces, &is_array))
+        return false;
+    if (!is_array) {
+        gjs_throw(cx, "Invalid parameter interfaces (expected Array)");
+        return false;
+    }
+
+    uint32_t n_int;
+    if (!JS_GetArrayLength(cx, interfaces, &n_int))
+        return false;
+
+    if (!JS_IsArrayObject(cx, properties, &is_array))
+        return false;
+    if (!is_array) {
+        gjs_throw(cx, "Invalid parameter properties (expected Array)");
+        return false;
+    }
+
+    uint32_t n_prop;
+    if (!JS_GetArrayLength(cx, properties, &n_prop))
+        return false;
+
+    if (n_interfaces)
+        *n_interfaces = n_int;
+    if (n_properties)
+        *n_properties = n_prop;
+    return true;
+}
+
+static bool save_properties_for_class_init(JSContext* cx,
+                                           JS::HandleObject properties,
+                                           uint32_t n_properties, GType gtype) {
+    AutoParamArray properties_native;
+    JS::RootedValue prop_val(cx);
+    JS::RootedObject prop_obj(cx);
+    for (uint32_t i = 0; i < n_properties; i++) {
+        if (!JS_GetElement(cx, properties, i, &prop_val))
+            return false;
+
+        if (!prop_val.isObject()) {
+            gjs_throw(cx, "Invalid parameter, expected object");
+            return false;
+        }
+
+        prop_obj = &prop_val.toObject();
+        if (!gjs_typecheck_param(cx, prop_obj, G_TYPE_NONE, true))
+            return false;
+
+        properties_native.emplace_back(
+            g_param_spec_ref(gjs_g_param_from_param(cx, prop_obj)));
+    }
+    push_class_init_properties(gtype, &properties_native);
+    return true;
+}
+
+static bool get_interface_gtypes(JSContext* cx, JS::HandleObject interfaces,
+                                 uint32_t n_interfaces, GType* iface_types) {
+    for (uint32_t ix = 0; ix < n_interfaces; ix++) {
+        JS::RootedValue iface_val(cx);
+        if (!JS_GetElement(cx, interfaces, ix, &iface_val))
+            return false;
+
+        if (!iface_val.isObject()) {
+            gjs_throw(
+                cx, "Invalid parameter interfaces (element %d was not a GType)",
+                ix);
+            return false;
+        }
+
+        JS::RootedObject iface(cx, &iface_val.toObject());
+        GType iface_type = gjs_gtype_get_actual_gtype(cx, iface);
+        if (iface_type == G_TYPE_INVALID) {
+            gjs_throw(
+                cx, "Invalid parameter interfaces (element %d was not a GType)",
+                ix);
+            return false;
+        }
+
+        iface_types[ix] = iface_type;
+    }
+    return true;
+}
+
+static bool gjs_register_interface(JSContext* cx, unsigned argc,
+                                   JS::Value* vp) {
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+    GjsAutoJSChar name;
+    JS::RootedObject interfaces(cx), properties(cx);
+    if (!gjs_parse_call_args(cx, "register_interface", args, "soo", "name",
+                             &name, "interfaces", &interfaces, "properties",
+                             &properties))
+        return false;
+
+    uint32_t n_interfaces, n_properties;
+    if (!validate_interfaces_and_properties_args(cx, interfaces, properties,
+                                                 &n_interfaces, &n_properties))
+        return false;
+
+    GType* iface_types = g_newa(GType, n_interfaces);
+
+    /* We do interface addition in two passes so that any failure
+       is caught early, before registering the GType (which we can't undo) */
+    if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types))
+        return false;
+
+    if (g_type_from_name(name) != G_TYPE_INVALID) {
+        gjs_throw(cx, "Type name %s is already registered", name.get());
+        return false;
+    }
+
+    GTypeInfo type_info = gjs_gobject_interface_info;
+    GType interface_type = g_type_register_static(G_TYPE_INTERFACE, name,
+        &type_info, GTypeFlags(0));
+
+    g_type_set_qdata(interface_type, ObjectInstance::custom_type_quark(),
+                     GINT_TO_POINTER(1));
+
+    if (!save_properties_for_class_init(cx, properties, n_properties,
+                                        interface_type))
+        return false;
+
+    for (uint32_t ix = 0; ix < n_interfaces; ix++)
+        g_type_interface_add_prerequisite(interface_type, iface_types[ix]);
+
+    /* create a custom JSClass */
+    JS::RootedObject module(cx, gjs_lookup_private_namespace(cx));
+    if (!module)
+        return false;  // error will have been thrown already
+
+    JS::RootedObject constructor(cx);
+    gjs_define_interface_class(cx, module, nullptr, interface_type,
+                               &constructor);
+
+    args.rval().setObject(*constructor);
+    return true;
+}
+
+static inline void gjs_add_interface(GType instance_type,
+                                     GType interface_type) {
+    static GInterfaceInfo interface_vtable{nullptr, nullptr, nullptr};
+    g_type_add_interface_static(instance_type, interface_type,
+                                &interface_vtable);
+}
+
+static bool gjs_register_type(JSContext* cx, unsigned argc, JS::Value* vp) {
+    JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
+
+    JSAutoRequest ar(cx);
+
+    GjsAutoJSChar name;
+    JS::RootedObject parent(cx), interfaces(cx), properties(cx);
+    if (!gjs_parse_call_args(cx, "register_type", argv, "osoo", "parent",
+                             &parent, "name", &name, "interfaces", &interfaces,
+                             "properties", &properties))
+        return false;
+
+    if (!parent)
+        return false;
+
+    if (!gjs_typecheck_is_object(cx, parent, true))
+        return false;
+
+    uint32_t n_interfaces, n_properties;
+    if (!validate_interfaces_and_properties_args(cx, interfaces, properties,
+                                                 &n_interfaces, &n_properties))
+        return false;
+
+    auto* iface_types =
+        static_cast<GType*>(g_alloca(sizeof(GType) * n_interfaces));
+
+    /* We do interface addition in two passes so that any failure
+       is caught early, before registering the GType (which we can't undo) */
+    if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types))
+        return false;
+
+    if (g_type_from_name(name) != G_TYPE_INVALID) {
+        gjs_throw(cx, "Type name %s is already registered", name.get());
+        return false;
+    }
+
+    auto* parent_priv = ObjectInstance::for_js(cx, parent);
+    /* We checked parent above, in gjs_typecheck_is_object() */
+    g_assert(parent_priv);
+
+    GTypeQuery query;
+    parent_priv->type_query_dynamic_safe(&query);
+    if (G_UNLIKELY(query.type == 0)) {
+        gjs_throw(cx,
+                  "Cannot inherit from a non-gjs dynamic type [bug 687184]");
+        return false;
+    }
+
+    GTypeInfo type_info = gjs_gobject_class_info;
+    type_info.class_size = query.class_size;
+    type_info.instance_size = query.instance_size;
+
+    GType instance_type = g_type_register_static(parent_priv->gtype(), name,
+                                                 &type_info, GTypeFlags(0));
+
+    g_type_set_qdata(instance_type, ObjectInstance::custom_type_quark(),
+                     GINT_TO_POINTER(1));
+
+    if (!save_properties_for_class_init(cx, properties, n_properties,
+                                        instance_type))
+        return false;
+
+    for (uint32_t ix = 0; ix < n_interfaces; ix++)
+        gjs_add_interface(instance_type, iface_types[ix]);
+
+    /* create a custom JSClass */
+    JS::RootedObject module(cx, gjs_lookup_private_namespace(cx));
+    JS::RootedObject constructor(cx), prototype(cx);
+    gjs_define_object_class(cx, module, nullptr, instance_type, &constructor,
+                            &prototype);
+
+    ObjectInstance* priv = ObjectInstance::for_js(cx, prototype);
+    priv->set_type_qdata();
+
+    argv.rval().setObject(*constructor);
+
+    return true;
+}
+
+static bool gjs_signal_new(JSContext* cx, unsigned argc, JS::Value* vp) {
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+    JSAutoRequest ar(cx);
+
+    GjsAutoJSChar signal_name;
+    int32_t flags, accumulator_enum;
+    JS::RootedObject gtype_obj(cx), return_gtype_obj(cx), params_obj(cx);
+    if (!gjs_parse_call_args(cx, "signal_new", args, "osiioo", "gtype",
+                             &gtype_obj, "signal name", &signal_name, "flags",
+                             &flags, "accumulator", &accumulator_enum,
+                             "return gtype", &return_gtype_obj, "params",
+                             &params_obj))
+        return false;
+
+    if (!gjs_typecheck_gtype(cx, gtype_obj, true))
+        return false;
+
+    /* we only support standard accumulators for now */
+    GSignalAccumulator accumulator;
+    switch (accumulator_enum) {
+        case 1:
+            accumulator = g_signal_accumulator_first_wins;
+            break;
+        case 2:
+            accumulator = g_signal_accumulator_true_handled;
+            break;
+        case 0:
+        default:
+            accumulator = NULL;
+    }
+
+    GType return_type = gjs_gtype_get_actual_gtype(cx, return_gtype_obj);
+
+    if (accumulator == g_signal_accumulator_true_handled &&
+        return_type != G_TYPE_BOOLEAN) {
+        gjs_throw(cx,
+                  "GObject.SignalAccumulator.TRUE_HANDLED can only be used "
+                  "with boolean signals");
+        return false;
+    }
+
+    uint32_t n_parameters;
+    if (!JS_GetArrayLength(cx, params_obj, &n_parameters))
+        return false;
+
+    GType* params = g_newa(GType, n_parameters);
+    JS::RootedValue gtype_val(cx);
+    for (uint32_t ix = 0; ix < n_parameters; ix++) {
+        if (!JS_GetElement(cx, params_obj, ix, &gtype_val) ||
+            !gtype_val.isObject()) {
+            gjs_throw(cx, "Invalid signal parameter number %d", ix);
+            return false;
+        }
+
+        JS::RootedObject gjs_gtype(cx, &gtype_val.toObject());
+        params[ix] = gjs_gtype_get_actual_gtype(cx, gjs_gtype);
+    }
+
+    GType gtype = gjs_gtype_get_actual_gtype(cx, gtype_obj);
+
+    unsigned signal_id = g_signal_newv(
+        signal_name, gtype, GSignalFlags(flags), nullptr, /* class closure */
+        accumulator, nullptr,                             /* accu_data */
+        g_cclosure_marshal_generic, return_type, n_parameters, params);
+
+    // FIXME: what if ID is greater than int32 max?
+    args.rval().setInt32(signal_id);
+    return true;
+}
+
+static bool hook_up_vfunc_symbol_getter(JSContext* cx, unsigned argc,
+                                        JS::Value* vp) {
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    args.rval().setSymbol(ObjectInstance::hook_up_vfunc_symbol(cx));
+    return true;
+}
+
+static JSFunctionSpec module_funcs[] = {
+    JS_FS("override_property", gjs_override_property, 2, GJS_MODULE_PROP_FLAGS),
+    JS_FS("register_interface", gjs_register_interface, 3,
+          GJS_MODULE_PROP_FLAGS),
+    JS_FS("register_type", gjs_register_type, 4, GJS_MODULE_PROP_FLAGS),
+    JS_FS("signal_new", gjs_signal_new, 6, GJS_MODULE_PROP_FLAGS),
+    JS_FS_END,
+};
+
+static JSPropertySpec module_props[] = {
+    JS_PSG("hook_up_vfunc_symbol", hook_up_vfunc_symbol_getter,
+           GJS_MODULE_PROP_FLAGS),
+    JS_PS_END};
+
+bool gjs_define_private_gi_stuff(JSContext* cx,
+                                 JS::MutableHandleObject module) {
+    module.set(JS_NewPlainObject(cx));
+    return JS_DefineFunctions(cx, module, module_funcs) &&
+           JS_DefineProperties(cx, module, module_props);
+}
diff --git a/gi/private.h b/gi/private.h
new file mode 100644
index 00000000..16e8fee7
--- /dev/null
+++ b/gi/private.h
@@ -0,0 +1,31 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2008  litl, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef GI_PRIVATE_H_
+#define GI_PRIVATE_H_
+
+#include "gjs/jsapi-wrapper.h"
+
+bool gjs_define_private_gi_stuff(JSContext* cx, JS::MutableHandleObject module);
+
+#endif  // GI_PRIVATE_H_
diff --git a/gjs-srcs.mk b/gjs-srcs.mk
index c7600bef..00ea0bdd 100644
--- a/gjs-srcs.mk
+++ b/gjs-srcs.mk
@@ -29,6 +29,8 @@ gjs_srcs =                            \
        gi/gerror.cpp                   \
        gi/gerror.h                     \
        gi/gjs_gi_trace.h               \
+       gi/gobject.cpp                  \
+       gi/gobject.h                    \
        gi/gtype.cpp                    \
        gi/gtype.h                      \
        gi/interface.cpp                \
@@ -39,6 +41,8 @@ gjs_srcs =                            \
        gi/object.h                     \
        gi/param.cpp                    \
        gi/param.h                      \
+       gi/private.cpp                  \
+       gi/private.h                    \
        gi/proxyutils.cpp               \
        gi/proxyutils.h                 \
        gi/repo.cpp                     \
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 9d42df64..0dc3883d 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -32,8 +32,12 @@
 
 #include <gio/gio.h>
 
+#include "byteArray.h"
 #include "context-private.h"
 #include "engine.h"
+#include "gi/object.h"
+#include "gi/private.h"
+#include "gi/repo.h"
 #include "global.h"
 #include "importer.h"
 #include "jsapi-util.h"
@@ -41,9 +45,6 @@
 #include "mem.h"
 #include "native.h"
 #include "profiler-private.h"
-#include "byteArray.h"
-#include "gi/object.h"
-#include "gi/repo.h"
 
 #include <modules/modules.h>
 


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