[gjs: 6/9] interface: Split private struct into InterfacePrototype/InterfaceInstance
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs: 6/9] interface: Split private struct into InterfacePrototype/InterfaceInstance
- Date: Tue, 8 Jan 2019 06:54:21 +0000 (UTC)
commit 0fa00d49c4f063c94f225d3c8f711c41d44ee1e0
Author: Philip Chimento <philip chimento gmail com>
Date: Fri Dec 21 17:11:21 2018 -0700
interface: Split private struct into InterfacePrototype/InterfaceInstance
This continues the ports of GObject introspection wrappers to the new
framework introduced in wrapperutils.h. This file is a bit unusual as no
InterfaceInstance instances should ever be constructed, so we override
InterfaceInstance::constructor() to throw an exception.
This also adds a debug topic for interfaces, since there wasn't one
previously.
gi/interface.cpp | 162 +++++++++++++++++--------------------------------------
gi/interface.h | 86 +++++++++++++++++++++++++++++
util/log.cpp | 3 ++
util/log.h | 1 +
4 files changed, 139 insertions(+), 113 deletions(-)
---
diff --git a/gi/interface.cpp b/gi/interface.cpp
index c6c8b6ed..0d10061c 100644
--- a/gi/interface.cpp
+++ b/gi/interface.cpp
@@ -37,74 +37,36 @@
#include <girepository.h>
-typedef struct {
- GIInterfaceInfo *info;
- GType gtype;
- /* the GTypeInterface vtable wrapped by this JS Object (only used for
- prototypes) */
- GTypeInterface *vtable;
-} Interface;
-
-extern struct JSClass gjs_interface_class;
-
-GJS_DEFINE_PRIV_FROM_JS(Interface, gjs_interface_class)
-
-GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(interface)
-
-static void
-interface_finalize(JSFreeOp *fop,
- JSObject *obj)
-{
- Interface *priv;
-
- priv = (Interface*) JS_GetPrivate(obj);
-
- if (priv == NULL)
- return;
-
- if (priv->info != NULL)
- g_base_info_unref((GIBaseInfo*)priv->info);
-
- g_clear_pointer(&priv->vtable, (GDestroyNotify)g_type_default_interface_unref);
+InterfacePrototype::InterfacePrototype(GIInterfaceInfo* info, GType gtype)
+ : GIWrapperPrototype(info, gtype),
+ m_vtable(
+ static_cast<GTypeInterface*>(g_type_default_interface_ref(gtype))) {
+ GJS_INC_COUNTER(interface);
+}
+InterfacePrototype::~InterfacePrototype(void) {
+ g_clear_pointer(&m_vtable, g_type_default_interface_unref);
GJS_DEC_COUNTER(interface);
- g_slice_free(Interface, priv);
}
-GJS_JSAPI_RETURN_CONVENTION
-static bool
-interface_resolve(JSContext *context,
- JS::HandleObject obj,
- JS::HandleId id,
- bool *resolved)
-{
- Interface* priv = priv_from_js(context, obj);
-
- if (priv == nullptr)
- return false;
-
+// See GIWrapperBase::resolve().
+bool InterfacePrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
+ JS::HandleId id, const char* name,
+ bool* resolved) {
/* If we have no GIRepository information then this interface was defined
* from within GJS. In that case, it has no properties that need to be
* resolved from within C code, as interfaces cannot inherit. */
- if (priv->info == NULL) {
- *resolved = false;
- return true;
- }
-
- JS::UniqueChars name;
- if (!gjs_get_string_id(context, id, &name))
- return false;
- if (!name) {
+ if (is_custom_js_class()) {
*resolved = false;
return true;
}
GjsAutoFunctionInfo method_info =
- g_interface_info_find_method(priv->info, name.get());
+ g_interface_info_find_method(m_info, name);
if (method_info) {
if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) {
- if (!gjs_define_function(context, obj, priv->gtype, method_info))
+ if (!gjs_define_function(context, obj, m_gtype, method_info))
return false;
*resolved = true;
@@ -118,18 +80,16 @@ interface_resolve(JSContext *context,
return true;
}
-GJS_JSAPI_RETURN_CONVENTION
-static bool
-interface_has_instance_func(JSContext *cx,
- unsigned argc,
- JS::Value *vp)
-{
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- /* This method is not called directly, so no need for error messages */
+/*
+ * InterfaceBase::has_instance:
+ *
+ * JSNative implementation of `[Symbol.hasInstance]()`. This method is never
+ * called directly, but instead is called indirectly by the JS engine as part of
+ * an `instanceof` expression.
+ */
+bool InterfaceBase::has_instance(JSContext* cx, unsigned argc, JS::Value* vp) {
+ GJS_GET_THIS(cx, argc, vp, args, interface_constructor);
- JS::RootedValue interface(cx, args.computeThis(cx));
- g_assert(interface.isObject());
- JS::RootedObject interface_constructor(cx, &interface.toObject());
JS::RootedObject interface_proto(cx);
const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
if (!gjs_object_require_property(cx, interface_constructor,
@@ -137,45 +97,47 @@ interface_has_instance_func(JSContext *cx,
&interface_proto))
return false;
- Interface *priv;
- if (!priv_from_js_with_typecheck(cx, interface_proto, &priv))
+ InterfaceBase* priv = InterfaceBase::for_js_typecheck(cx, interface_proto);
+ if (!priv)
return false;
+ return priv->to_prototype()->has_instance_impl(cx, args);
+}
+
+// See InterfaceBase::has_instance().
+bool InterfacePrototype::has_instance_impl(JSContext* cx,
+ const JS::CallArgs& args) {
+ // This method is never called directly, so no need for error messages.
g_assert(args.length() == 1);
g_assert(args[0].isObject());
JS::RootedObject instance(cx, &args[0].toObject());
- bool isinstance = gjs_typecheck_object(cx, instance, priv->gtype, false);
+ bool isinstance = gjs_typecheck_object(cx, instance, m_gtype, false);
args.rval().setBoolean(isinstance);
return true;
}
-static const struct JSClassOps gjs_interface_class_ops = {
+// clang-format off
+const struct JSClassOps InterfaceBase::class_ops = {
nullptr, // addProperty
nullptr, // deleteProperty
nullptr, // enumerate
nullptr, // newEnumerate
- interface_resolve,
+ &InterfaceBase::resolve,
nullptr, // mayResolve
- interface_finalize};
+ &InterfaceBase::finalize,
+};
-struct JSClass gjs_interface_class = {
+const struct JSClass InterfaceBase::klass = {
"GObject_Interface",
JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
- &gjs_interface_class_ops
-};
-
-JSPropertySpec gjs_interface_proto_props[] = {
- JS_PS_END
+ &InterfaceBase::class_ops
};
-JSFunctionSpec gjs_interface_proto_funcs[] = {
- JS_FS_END
-};
-
-JSFunctionSpec gjs_interface_static_funcs[] = {
- JS_SYM_FN(hasInstance, interface_has_instance_func, 1, 0),
+JSFunctionSpec InterfaceBase::static_methods[] = {
+ JS_SYM_FN(hasInstance, &InterfaceBase::has_instance, 1, 0),
JS_FS_END
};
+// clang-format on
bool
gjs_define_interface_class(JSContext *context,
@@ -184,47 +146,21 @@ gjs_define_interface_class(JSContext *context,
GType gtype,
JS::MutableHandleObject constructor)
{
- Interface *priv;
- const char *constructor_name;
- const char *ns;
JS::RootedObject prototype(context);
- ns = gjs_get_names_from_gtype_and_gi_info(gtype, (GIBaseInfo *) info,
- &constructor_name);
-
- if (!gjs_init_class_dynamic(context, in_object, nullptr, ns,
- constructor_name,
- &gjs_interface_class,
- gjs_interface_constructor, 0,
- /* props of prototype */
- &gjs_interface_proto_props[0],
- /* funcs of prototype */
- &gjs_interface_proto_funcs[0],
- /* props of constructor, MyConstructor.myprop */
- NULL,
- /* funcs of constructor, MyConstructor.myfunc() */
- gjs_interface_static_funcs,
- &prototype,
- constructor)) {
+ if (!InterfacePrototype::create_class(context, in_object, info, gtype,
+ constructor, &prototype))
return false;
- }
-
- GJS_INC_COUNTER(interface);
- priv = g_slice_new0(Interface);
- priv->info = info == NULL ? NULL : g_base_info_ref((GIBaseInfo *) info);
- priv->gtype = gtype;
- priv->vtable = (GTypeInterface *) g_type_default_interface_ref(gtype);
- JS_SetPrivate(prototype, priv);
/* If we have no GIRepository information, then this interface was defined
* from within GJS and therefore has no C static methods to be defined. */
- if (priv->info) {
+ if (info) {
if (!gjs_define_static_methods<InfoType::Interface>(
- context, constructor, priv->gtype, priv->info))
+ context, constructor, gtype, info))
return false;
}
- return gjs_wrapper_define_gtype_prop(context, constructor, priv->gtype);
+ return true;
}
bool
diff --git a/gi/interface.h b/gi/interface.h
index d34d7862..e91197ef 100644
--- a/gi/interface.h
+++ b/gi/interface.h
@@ -29,9 +29,95 @@
#include <glib.h>
#include <girepository.h>
+#include "gi/wrapperutils.h"
#include "gjs/jsapi-util.h"
#include "gjs/macros.h"
+class InterfacePrototype;
+class InterfaceInstance;
+
+/* For more information on this Base/Prototype/Interface scheme, see the notes
+ * in wrapperutils.h.
+ *
+ * What's unusual about this subclass is that InterfaceInstance should never
+ * actually be instantiated. Interfaces can't be constructed, and
+ * GIWrapperBase::constructor() is overridden to just throw an exception and not
+ * create any JS wrapper object.
+ *
+ * We use the template classes from wrapperutils.h anyway, because there is
+ * still a lot of common code.
+ */
+
+class InterfaceBase : public GIWrapperBase<InterfaceBase, InterfacePrototype,
+ InterfaceInstance> {
+ friend class GIWrapperBase;
+
+ protected:
+ explicit InterfaceBase(InterfacePrototype* proto = nullptr)
+ : GIWrapperBase(proto) {}
+ ~InterfaceBase(void) {}
+
+ static const GjsDebugTopic debug_topic = GJS_DEBUG_GINTERFACE;
+ static constexpr const char* debug_tag = "GInterface";
+
+ static const struct JSClassOps class_ops;
+ static const struct JSClass klass;
+ static JSFunctionSpec static_methods[];
+
+ GJS_USE const char* to_string_kind(void) const { return "interface"; }
+
+ // JSNative methods
+
+ // Overrides GIWrapperBase::constructor().
+ GJS_JSAPI_RETURN_CONVENTION
+ static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ gjs_throw_abstract_constructor_error(cx, args);
+ return false;
+ }
+
+ GJS_JSAPI_RETURN_CONVENTION
+ static bool has_instance(JSContext* cx, unsigned argc, JS::Value* vp);
+};
+
+class InterfacePrototype
+ : public GIWrapperPrototype<InterfaceBase, InterfacePrototype,
+ InterfaceInstance, GIInterfaceInfo> {
+ friend class GIWrapperPrototype;
+ friend class GIWrapperBase;
+ friend class InterfaceBase; // for has_instance_impl
+
+ // the GTypeInterface vtable wrapped by this JS object
+ GTypeInterface* m_vtable;
+
+ explicit InterfacePrototype(GIInterfaceInfo* info, GType gtype);
+ ~InterfacePrototype(void);
+
+ // JSClass operations
+
+ GJS_JSAPI_RETURN_CONVENTION
+ bool resolve_impl(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+ const char* name, bool* resolved);
+
+ // JS methods
+
+ GJS_JSAPI_RETURN_CONVENTION
+ bool has_instance_impl(JSContext* cx, const JS::CallArgs& args);
+};
+
+class InterfaceInstance
+ : public GIWrapperInstance<InterfaceBase, InterfacePrototype,
+ InterfaceInstance> {
+ friend class GIWrapperInstance;
+ friend class GIWrapperBase;
+
+ G_GNUC_NORETURN InterfaceInstance(JSContext* cx, JS::HandleObject obj)
+ : GIWrapperInstance(cx, obj) {
+ g_assert_not_reached();
+ }
+ G_GNUC_NORETURN ~InterfaceInstance(void) { g_assert_not_reached(); }
+};
+
G_BEGIN_DECLS
GJS_JSAPI_RETURN_CONVENTION
diff --git a/util/log.cpp b/util/log.cpp
index 213be8ea..410913e4 100644
--- a/util/log.cpp
+++ b/util/log.cpp
@@ -221,6 +221,9 @@ _Pragma("GCC diagnostic pop")
case GJS_DEBUG_GERROR:
prefix = "JS G ERR";
break;
+ case GJS_DEBUG_GINTERFACE:
+ prefix = "JS G IFACE";
+ break;
default:
prefix = "???";
break;
diff --git a/util/log.h b/util/log.h
index 00b9d458..a88b8786 100644
--- a/util/log.h
+++ b/util/log.h
@@ -50,6 +50,7 @@ typedef enum {
GJS_DEBUG_GPARAM,
GJS_DEBUG_GERROR,
GJS_DEBUG_GFUNDAMENTAL,
+ GJS_DEBUG_GINTERFACE,
} GjsDebugTopic;
/* These defines are because we have some pretty expensive and
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]