[gjs: 3/7] object: Split file into smaller logical units
- From: Cosimo Cecchi <cosimoc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs: 3/7] object: Split file into smaller logical units
- Date: Sun, 1 Jul 2018 17:09:10 +0000 (UTC)
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", >ype_obj,
- "signal name", &signal_name,
- "flags", &flags,
- "accumulator", &accumulator_enum,
- "return gtype", &return_gtype_obj,
- "params", ¶ms_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, >ype_val) ||
- !gtype_val.isObject()) {
- gjs_throw(cx, "Invalid signal parameter number %d", i);
- return false;
- }
-
- JS::RootedObject gjs_gtype(cx, >ype_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",
+ >ype_obj, "signal name", &signal_name, "flags",
+ &flags, "accumulator", &accumulator_enum,
+ "return gtype", &return_gtype_obj, "params",
+ ¶ms_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, >ype_val) ||
+ !gtype_val.isObject()) {
+ gjs_throw(cx, "Invalid signal parameter number %d", ix);
+ return false;
+ }
+
+ JS::RootedObject gjs_gtype(cx, >ype_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]