[gjs/ewlsh/register-type] Implement GObject.registerType
- From: Evan Welsh <ewlsh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/ewlsh/register-type] Implement GObject.registerType
- Date: Thu, 1 Apr 2021 02:46:44 +0000 (UTC)
commit 544daa6a7f9a000d9a33517b567b2131c31de6bc
Author: Evan Welsh <contact evanwelsh com>
Date: Wed Mar 31 19:41:19 2021 -0700
Implement GObject.registerType
gi/cwrapper.cpp | 4 ++-
gi/gtype.cpp | 3 ++
gi/object.cpp | 26 +++++++++++++--
gi/private.cpp | 4 +++
gi/wrapperutils.h | 35 ++++++++++++++++++--
gjs/atoms.h | 2 ++
modules/core/overrides/GObject.js | 68 ++++++++++++++++++++++++++++++++++++++-
7 files changed, 135 insertions(+), 7 deletions(-)
---
diff --git a/gi/cwrapper.cpp b/gi/cwrapper.cpp
index f5fbff8a..a9fd6557 100644
--- a/gi/cwrapper.cpp
+++ b/gi/cwrapper.cpp
@@ -24,5 +24,7 @@ bool gjs_wrapper_define_gtype_prop(JSContext* cx, JS::HandleObject constructor,
const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
return JS_DefinePropertyById(cx, constructor, atoms.gtype(), gtype_obj,
- JSPROP_PERMANENT);
+ JSPROP_PERMANENT) &&
+ JS_DefinePropertyById(cx, constructor, atoms.gobject_type(),
+ gtype_obj, JSPROP_PERMANENT);
}
diff --git a/gi/gtype.cpp b/gi/gtype.cpp
index 7ccb8099..aa2d665a 100644
--- a/gi/gtype.cpp
+++ b/gi/gtype.cpp
@@ -129,6 +129,9 @@ class GTypeObj : public CWrapper<GTypeObj, void> {
// property on that and hope it's a GType wrapper object
if (!JS_GetPropertyById(cx, object, atoms.gtype(), &v_gtype))
return false;
+ if (!v_gtype.isObject() &&
+ !JS_GetPropertyById(cx, object, atoms.gobject_type(), &v_gtype))
+ return false;
if (!v_gtype.isObject()) {
// OK, so we're not a class. But maybe we're an instance. Check for
// "constructor" and recurse on that.
diff --git a/gi/object.cpp b/gi/object.cpp
index 39ce7f5f..294d77d3 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -1622,6 +1622,10 @@ bool ObjectInstance::constructor_impl(JSContext* context,
if (!JS_HasOwnPropertyById(context, rooted_target, gjs->atoms().gtype(),
&has_gtype))
return false;
+ if (!has_gtype &&
+ !JS_HasOwnPropertyById(context, rooted_target,
+ gjs->atoms().gobject_type(), &has_gtype))
+ return false;
if (!has_gtype) {
gjs_throw(context,
@@ -1631,9 +1635,25 @@ bool ObjectInstance::constructor_impl(JSContext* context,
return false;
}
- return gjs_object_require_property(context, object, "GObject instance",
- gjs->atoms().init(), &initer) &&
- gjs->call_function(object, initer, argv, argv.rval());
+ // If our instance is an instance of &klass it is an instance of a native
+ // class.
+ if (JS_InstanceOf(context, object, &klass, nullptr)) {
+ return gjs_object_require_property(context, object, "GObject instance",
+ gjs->atoms().init(), &initer) &&
+ gjs->call_function(object, initer, argv, argv.rval());
+ // Otherwise it is a JS-based class and we should call the constructor
+ // directly.
+ } else {
+ // TODO: This needs error guards.
+ ObjectBase* priv = ObjectBase::for_js(context, object);
+
+ if (!priv->check_is_instance(context, "initialize"))
+ return false;
+
+ JS::RootedObject obj(context, object);
+
+ return priv->to_instance()->init_impl(context, argv, &obj);
+ }
}
void ObjectInstance::trace_impl(JSTracer* tracer) {
diff --git a/gi/private.cpp b/gi/private.cpp
index 935295bb..0cdfbb81 100644
--- a/gi/private.cpp
+++ b/gi/private.cpp
@@ -413,6 +413,10 @@ static JSFunctionSpec module_funcs[] = {
};
static JSPropertySpec module_props[] = {
+ JS_PSG("gobject_prototype_symbol",
+ symbol_getter<&GjsAtoms::gobject_prototype>, GJS_MODULE_PROP_FLAGS),
+ JS_PSG("gobject_type_symbol", symbol_getter<&GjsAtoms::gobject_type>,
+ GJS_MODULE_PROP_FLAGS),
JS_PSG("hook_up_vfunc_symbol", symbol_getter<&GjsAtoms::hook_up_vfunc>,
GJS_MODULE_PROP_FLAGS),
JS_PSG("signal_find_symbol", symbol_getter<&GjsAtoms::signal_find>,
diff --git a/gi/wrapperutils.h b/gi/wrapperutils.h
index 0febd1bc..9ad32474 100644
--- a/gi/wrapperutils.h
+++ b/gi/wrapperutils.h
@@ -429,8 +429,23 @@ class GIWrapperBase : public CWrapperPointerOps<Base> {
JS::RootedObject proto(cx);
if (!JS_GetPrototype(cx, obj, &proto))
return false;
- if (JS_GetClass(proto) != &Base::klass) {
- gjs_throw(cx, "Tried to construct an object without a GType");
+
+ JS::RootedValue gproto(cx);
+
+ bool has_property = false;
+
+ auto gjs_cx = GjsContextPrivate::from_cx(cx);
+ auto atoms = gjs_cx->atoms();
+ if (!JS_HasOwnPropertyById(cx, proto, atoms.gobject_prototype(),
+ &has_property))
+ return false;
+
+ if (JS_GetClass(proto) != &Base::klass &&
+ (!has_property ||
+ !JS_GetPropertyById(cx, proto, atoms.gobject_prototype(),
+ &gproto) ||
+ !gproto.isObject())) {
+ gjs_throw(cx, "Tried to construct an object without a GType!");
return false;
}
@@ -896,6 +911,22 @@ class GIWrapperPrototype : public Base {
JS::HandleObject wrapper) {
JS::RootedObject proto(cx);
JS_GetPrototype(cx, wrapper, &proto);
+
+ if (JS_GetClass(proto) != &Base::klass) {
+ JS::RootedValue gproto(cx);
+
+ auto priv = GjsContextPrivate::from_cx(cx);
+ auto atoms = priv->atoms();
+
+ if (JS_GetPropertyById(cx, proto, atoms.gobject_prototype(),
+ &gproto) &&
+ gproto.isObject()) {
+ proto.set(&gproto.toObject());
+ }
+
+ // TODO(ewlsh): Handle assertions with errors instead.
+ }
+
Base* retval = Base::for_js(cx, proto);
g_assert(retval);
return retval->to_prototype();
diff --git a/gjs/atoms.h b/gjs/atoms.h
index 25c9904d..e33a66e0 100644
--- a/gjs/atoms.h
+++ b/gjs/atoms.h
@@ -71,6 +71,8 @@ class JSTracer;
macro(y, "y")
#define FOR_EACH_SYMBOL_ATOM(macro) \
+ macro(gobject_type, "__GObject__type") \
+ macro(gobject_prototype, "__GObject__prototype") \
macro(hook_up_vfunc, "__GObject__hook_up_vfunc") \
macro(private_ns_marker, "__gjsPrivateNS") \
macro(signal_find, "__GObject__signal_find") \
diff --git a/modules/core/overrides/GObject.js b/modules/core/overrides/GObject.js
index 8058a2ec..8aecf13b 100644
--- a/modules/core/overrides/GObject.js
+++ b/modules/core/overrides/GObject.js
@@ -78,6 +78,60 @@ function registerClass(...args) {
return initclass._classInit(klass);
}
+function registerType(klass) {
+ let gtypename = _createGTypeName(klass);
+ let gflags = klass.hasOwnProperty(GTypeFlags) ? klass[GTypeFlags] : 0;
+ let gobjectInterfaces = klass.hasOwnProperty(interfaces) ? klass[interfaces] : [];
+ let propertiesArray = _propertiesAsArray(klass);
+ let parent = Object.getPrototypeOf(klass);
+ let gobjectSignals = klass.hasOwnProperty(signals) ? klass[signals] : [];
+
+ propertiesArray.forEach(pspec => _checkAccessors(klass.prototype, pspec, GObject));
+
+ let registered_type = Gi.register_type(parent[Gi.gobject_prototype_symbol] ?? parent.prototype,
gtypename, gflags,
+ gobjectInterfaces, propertiesArray);
+
+ // TODO: Construct a better "wrapper" than the registered prototype
+ // TODO: Avoid discarding a constructed constructor
+ klass[Gi.gobject_prototype_symbol] = registered_type.prototype;
+ klass.prototype[Gi.gobject_prototype_symbol] = registered_type.prototype;
+ klass[Gi.gobject_type_symbol] = registered_type[Gi.gobject_type_symbol];
+ // klass.prototype[Gi.gobject_type_symbol] = registered_type[Gi.gobject_type_symbol];
+
+ _createSignals(klass[Gi.gobject_type_symbol], gobjectSignals);
+
+ gobjectInterfaces.forEach(iface => _copyAllDescriptorsNoOverwrite(klass.prototype, iface.prototype));
+
+ Object.getOwnPropertyNames(klass.prototype)
+ .filter(name => name.startsWith('vfunc_') || name.startsWith('on_'))
+ .forEach(name => {
+ let descr = Object.getOwnPropertyDescriptor(klass.prototype, name);
+ if (typeof descr.value !== 'function')
+ return;
+
+ let func = klass.prototype[name];
+
+ if (name.startsWith('vfunc_')) {
+ klass[Gi.gobject_prototype_symbol][Gi.hook_up_vfunc_symbol](name.slice(6), func);
+ } else if (name.startsWith('on_')) {
+ let id = GObject.signal_lookup(name.slice(3).replace('_', '-'),
+ klass[Gi.gobject_type_symbol]);
+ if (id !== 0) {
+ GObject.signal_override_class_closure(id, klass[Gi.gobject_type_symbol], function
(...argArray) {
+ let emitter = argArray.shift();
+
+ return func.apply(emitter, argArray);
+ });
+ }
+ }
+ });
+
+ gobjectInterfaces.forEach(iface =>
+ _checkInterface(iface, klass.prototype));
+
+ return registered_type[Gi.gobject_type_symbol];
+}
+
// Some common functions between GObject.Class and GObject.Interface
function _createSignals(gtype, sigs) {
@@ -176,6 +230,17 @@ function _copyAllDescriptors(target, source, filter) {
});
}
+function _copyAllDescriptorsNoOverwrite(target, source) {
+ [...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source)]
+ .forEach(key => {
+ if (!target[key]) {
+ let descriptor = Object.getOwnPropertyDescriptor(source, key);
+
+ Object.defineProperty(target, key, descriptor);
+ }
+ });
+}
+
function _interfacePresent(required, klass) {
if (!klass[interfaces])
return false;
@@ -430,6 +495,7 @@ function _init() {
};
GObject.registerClass = registerClass;
+ GObject.registerType = registerType;
GObject.Object._classInit = function (klass) {
let gtypename = _createGTypeName(klass);
@@ -441,7 +507,7 @@ function _init() {
propertiesArray.forEach(pspec => _checkAccessors(klass.prototype, pspec, GObject));
- let newClass = Gi.register_type(parent.prototype, gtypename, gflags,
+ let newClass = Gi.register_type(parent[Gi.gobject_prototype_symbol] ?? parent.prototype, gtypename,
gflags,
gobjectInterfaces, propertiesArray);
Object.setPrototypeOf(newClass, parent);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]