[gjs: 7/9] union: Split private struct into UnionPrototype/UnionInstance



commit cc2e68cb1f91621d8ed1a189153f780058f75aee
Author: Philip Chimento <philip chimento gmail com>
Date:   Tue Dec 11 22:11:21 2018 -0800

    union: Split private struct into UnionPrototype/UnionInstance
    
    This continues the ports of GObject introspection wrappers to the new
    framework introduced in wrapperutils.h.

 gi/union.cpp      | 307 ++++++++++--------------------------------------------
 gi/union.h        |  60 +++++++++++
 gjs/mem-private.h |   3 +-
 gjs/mem.cpp       |   6 +-
 4 files changed, 122 insertions(+), 254 deletions(-)
---
diff --git a/gi/union.cpp b/gi/union.cpp
index b8c1355d..c0639d39 100644
--- a/gi/union.cpp
+++ b/gi/union.cpp
@@ -41,57 +41,33 @@
 #include "gjs/mem-private.h"
 #include "repo.h"
 
-typedef struct {
-    GIUnionInfo *info;
-    void *gboxed; /* NULL if we are the prototype and not an instance */
-    GType gtype;
-} Union;
-
-extern struct JSClass gjs_union_class;
+UnionPrototype::UnionPrototype(GIUnionInfo* info, GType gtype)
+    : GIWrapperPrototype(info, gtype) {
+    GJS_INC_COUNTER(union_prototype);
+}
 
-GJS_DEFINE_PRIV_FROM_JS(Union, gjs_union_class)
+UnionPrototype::~UnionPrototype(void) { GJS_DEC_COUNTER(union_prototype); }
 
-/*
- * The *resolved out parameter, on success, should be false to indicate that id
- * was not resolved; and true if id was resolved.
- */
-GJS_JSAPI_RETURN_CONVENTION
-static bool
-union_resolve(JSContext       *context,
-              JS::HandleObject obj,
-              JS::HandleId     id,
-              bool            *resolved)
-{
-    Union *priv = priv_from_js(context, obj);
-    gjs_debug_jsprop(GJS_DEBUG_GBOXED, "Resolve prop '%s' hook, obj %s, priv %p",
-                     gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv);
-
-    if (priv == nullptr)
-        return false; /* wrong class */
-
-    if (priv->gboxed != NULL) {
-        /* We are an instance, not a prototype, so look for
-         * per-instance props that we want to define on the
-         * JSObject. Generally we do not want to cache these in JS, we
-         * want to always pull them from the C object, or JS would not
-         * see any changes made from C. So we use the get/set prop
-         * hooks, not this resolve hook.
-         */
-        *resolved = false;
-        return true;
-    }
+UnionInstance::UnionInstance(JSContext* cx, JS::HandleObject obj)
+    : GIWrapperInstance(cx, obj) {
+    GJS_INC_COUNTER(union_instance);
+}
 
-    JS::UniqueChars name;
-    if (!gjs_get_string_id(context, id, &name))
-        return false;
-    if (!name) {
-        *resolved = false;
-        return true; /* not resolved, but no error */
+UnionInstance::~UnionInstance(void) {
+    if (m_ptr) {
+        g_boxed_free(g_registered_type_info_get_g_type(info()), m_ptr);
+        m_ptr = nullptr;
     }
+    GJS_DEC_COUNTER(union_instance);
+}
 
-    /* We are the prototype, so look for methods and other class properties */
+// See GIWrapperBase::resolve().
+bool UnionPrototype::resolve_impl(JSContext* context, JS::HandleObject obj,
+                                  JS::HandleId id, const char* prop_name,
+                                  bool* resolved) {
+    // Look for methods and other class properties
     GjsAutoFunctionInfo method_info =
-        g_union_info_find_method(priv->info, name.get());
+        g_union_info_find_method(info(), prop_name);
 
     if (method_info) {
 #if GJS_VERBOSE_ENABLE_GI_USAGE
@@ -100,14 +76,12 @@ union_resolve(JSContext       *context,
         if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) {
             gjs_debug(GJS_DEBUG_GBOXED,
                       "Defining method %s in prototype for %s.%s",
-                      method_info.name(),
-                      g_base_info_get_namespace( (GIBaseInfo*) priv->info),
-                      g_base_info_get_name( (GIBaseInfo*) priv->info));
+                      method_info.name(), ns(), name());
 
             /* obj is union proto */
-            if (!gjs_define_function(
-                    context, obj, g_registered_type_info_get_g_type(priv->info),
-                    method_info))
+            if (!gjs_define_function(context, obj,
+                                     g_registered_type_info_get_g_type(info()),
+                                     method_info))
                 return false;
 
             *resolved = true; /* we defined the prop in object_proto */
@@ -173,52 +147,16 @@ union_new(JSContext       *context,
     return NULL;
 }
 
-GJS_NATIVE_CONSTRUCTOR_DECLARE(union)
-{
-    GJS_NATIVE_CONSTRUCTOR_VARIABLES(union)
-    Union *priv;
-    Union *proto_priv;
-    JS::RootedObject proto(context);
-    void *gboxed;
-
-    GJS_NATIVE_CONSTRUCTOR_PRELUDE(union);
-
-    priv = g_slice_new0(Union);
-
-    GJS_INC_COUNTER(union);
-
-    g_assert(priv_from_js(context, object) == NULL);
-    JS_SetPrivate(object, priv);
-
-    gjs_debug_lifecycle(GJS_DEBUG_GBOXED,
-                        "union constructor, obj %p priv %p",
-                        object.get(), priv);
-
-    JS_GetPrototype(context, object, &proto);
-    gjs_debug_lifecycle(GJS_DEBUG_GBOXED, "union instance __proto__ is %p",
-                        proto.get());
-
-    /* If we're the prototype, then post-construct we'll fill in priv->info.
-     * If we are not the prototype, though, then we'll get ->info from the
-     * prototype and then create a GObject if we don't have one already.
-     */
-    proto_priv = priv_from_js(context, proto);
-    if (proto_priv == NULL) {
-        gjs_debug(GJS_DEBUG_GBOXED,
-                  "Bad prototype set on union? Must match JSClass of object. JS error should have been 
reported.");
-        return false;
-    }
-
-    priv->info = proto_priv->info;
-    g_base_info_ref( (GIBaseInfo*) priv->info);
-    priv->gtype = proto_priv->gtype;
-
+// See GIWrapperBase::constructor().
+bool UnionInstance::constructor_impl(JSContext* context,
+                                     JS::HandleObject object,
+                                     const JS::CallArgs& args) {
     /* union_new happens to be implemented by calling
      * gjs_invoke_c_function(), which returns a JS::Value.
      * The returned "gboxed" here is owned by that JS::Value,
      * not by us.
      */
-    gboxed = union_new(context, object, priv->info);
+    void* gboxed = union_new(context, object, info());
 
     if (gboxed == NULL) {
         return false;
@@ -228,89 +166,34 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(union)
      * be garbage collected, we make a copy here to be
      * owned by us.
      */
-    priv->gboxed = g_boxed_copy(priv->gtype, gboxed);
-
-    gjs_debug_lifecycle(GJS_DEBUG_GBOXED,
-                        "JSObject created with union instance %p type %s",
-                        priv->gboxed, g_type_name(priv->gtype));
-
-    GJS_NATIVE_CONSTRUCTOR_FINISH(union);
+    copy_union(gboxed);
 
     return true;
 }
 
-static void
-union_finalize(JSFreeOp *fop,
-               JSObject *obj)
-{
-    Union *priv;
-
-    priv = (Union*) JS_GetPrivate(obj);
-    gjs_debug_lifecycle(GJS_DEBUG_GBOXED,
-                        "finalize, obj %p priv %p", obj, priv);
-    if (priv == NULL)
-        return; /* wrong class? */
-
-    if (priv->gboxed) {
-        g_boxed_free(g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) priv->info),
-                     priv->gboxed);
-        priv->gboxed = NULL;
-    }
-
-    if (priv->info) {
-        g_base_info_unref( (GIBaseInfo*) priv->info);
-        priv->info = NULL;
-    }
-
-    GJS_DEC_COUNTER(union);
-    g_slice_free(Union, priv);
-}
-
-GJS_JSAPI_RETURN_CONVENTION
-static bool
-to_string_func(JSContext *context,
-               unsigned   argc,
-               JS::Value *vp)
-{
-    GJS_GET_PRIV(context, argc, vp, rec, obj, Union, priv);
-    return gjs_wrapper_to_string_func(context, obj, "union", priv->info,
-                                      priv->gtype, priv->gboxed, rec.rval());
-}
-
-/* The bizarre thing about this vtable is that it applies to both
- * instances of the object, and to the prototype that instances of the
- * class have.
- */
-static const struct JSClassOps gjs_union_class_ops = {
+// clang-format off
+const struct JSClassOps UnionBase::class_ops = {
     nullptr,  // addProperty
     nullptr,  // deleteProperty
     nullptr,  // enumerate
     nullptr,  // newEnumerate
-    union_resolve,
+    &UnionBase::resolve,
     nullptr,  // mayResolve
-    union_finalize};
+    &UnionBase::finalize,
+};
 
-struct JSClass gjs_union_class = {
+const struct JSClass UnionBase::klass = {
     "GObject_Union",
     JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,
-    &gjs_union_class_ops
-};
-
-JSPropertySpec gjs_union_proto_props[] = {
-    JS_PS_END
+    &UnionBase::class_ops
 };
-
-JSFunctionSpec gjs_union_proto_funcs[] = {
-    JS_FN("toString", to_string_func, 0, 0),
-    JS_FS_END};
+// clang-format on
 
 bool
 gjs_define_union_class(JSContext       *context,
                        JS::HandleObject in_object,
                        GIUnionInfo     *info)
 {
-    const char *constructor_name;
-    Union *priv;
     GType gtype;
     JS::RootedObject prototype(context), constructor(context);
 
@@ -323,43 +206,8 @@ gjs_define_union_class(JSContext       *context,
         return false;
     }
 
-    /* See the comment in gjs_define_object_class() for an
-     * explanation of how this all works; Union is pretty much the
-     * same as Object.
-     */
-
-    constructor_name = g_base_info_get_name( (GIBaseInfo*) info);
-
-    if (!gjs_init_class_dynamic(context, in_object, nullptr,
-                                g_base_info_get_namespace( (GIBaseInfo*) info),
-                                constructor_name,
-                                &gjs_union_class,
-                                gjs_union_constructor, 0,
-                                /* props of prototype */
-                                &gjs_union_proto_props[0],
-                                /* funcs of prototype */
-                                &gjs_union_proto_funcs[0],
-                                /* props of constructor, MyConstructor.myprop */
-                                NULL,
-                                /* funcs of constructor, MyConstructor.myfunc() */
-                                NULL,
-                                &prototype,
-                                &constructor)) {
-        return false;
-    }
-
-    GJS_INC_COUNTER(union);
-    priv = g_slice_new0(Union);
-    priv->info = info;
-    g_base_info_ref( (GIBaseInfo*) priv->info);
-    priv->gtype = gtype;
-    JS_SetPrivate(prototype, priv);
-
-    gjs_debug(GJS_DEBUG_GBOXED, "Defined class %s prototype is %p class %p in object %p",
-              constructor_name, prototype.get(), JS_GetClass(prototype),
-              in_object.get());
-
-    return gjs_wrapper_define_gtype_prop(context, constructor, gtype);
+    return !!UnionPrototype::create_class(context, in_object, info, gtype,
+                                          &constructor, &prototype);
 }
 
 JSObject*
@@ -367,8 +215,6 @@ gjs_union_from_c_union(JSContext    *context,
                        GIUnionInfo  *info,
                        void         *gboxed)
 {
-    JSObject *obj;
-    Union *priv;
     GType gtype;
 
     if (gboxed == NULL)
@@ -392,15 +238,13 @@ gjs_union_from_c_union(JSContext    *context,
     if (!proto)
         return nullptr;
 
-    obj = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto);
+    JS::RootedObject obj(
+        context, JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto));
+    if (!obj)
+        return nullptr;
 
-    GJS_INC_COUNTER(union);
-    priv = g_slice_new0(Union);
-    JS_SetPrivate(obj, priv);
-    priv->info = info;
-    g_base_info_ref( (GIBaseInfo *) priv->info);
-    priv->gtype = gtype;
-    priv->gboxed = g_boxed_copy(gtype, gboxed);
+    UnionInstance* priv = UnionInstance::new_for_js_object(context, obj);
+    priv->copy_union(gboxed);
 
     return obj;
 }
@@ -409,14 +253,14 @@ void*
 gjs_c_union_from_union(JSContext       *context,
                        JS::HandleObject obj)
 {
-    Union *priv;
-
     if (!obj)
         return NULL;
 
-    priv = priv_from_js(context, obj);
+    UnionInstance* priv = UnionInstance::for_js(context, obj);
+    if (!priv)
+        return nullptr;
 
-    return priv->gboxed;
+    return priv->ptr();
 }
 
 bool
@@ -426,48 +270,9 @@ gjs_typecheck_union(JSContext       *context,
                     GType            expected_type,
                     bool             throw_error)
 {
-    Union *priv;
-    bool result;
-
-    if (!do_base_typecheck(context, object, throw_error))
-        return false;
-
-    priv = priv_from_js(context, object);
-
-    if (priv->gboxed == NULL) {
-        if (throw_error) {
-            gjs_throw_custom(context, JSProto_TypeError, nullptr,
-                             "Object is %s.%s.prototype, not an object instance - cannot convert to a union 
instance",
-                             g_base_info_get_namespace( (GIBaseInfo*) priv->info),
-                             g_base_info_get_name( (GIBaseInfo*) priv->info));
-        }
-
-        return false;
-    }
-
-    if (expected_type != G_TYPE_NONE)
-        result = g_type_is_a (priv->gtype, expected_type);
-    else if (expected_info != NULL)
-        result = g_base_info_equal((GIBaseInfo*) priv->info, (GIBaseInfo*) expected_info);
-    else
-        result = true;
-
-    if (!result && throw_error) {
-        if (expected_info != NULL) {
-            gjs_throw_custom(context, JSProto_TypeError, nullptr,
-                             "Object is of type %s.%s - cannot convert to %s.%s",
-                             g_base_info_get_namespace((GIBaseInfo*) priv->info),
-                             g_base_info_get_name((GIBaseInfo*) priv->info),
-                             g_base_info_get_namespace((GIBaseInfo*) expected_info),
-                             g_base_info_get_name((GIBaseInfo*) expected_info));
-        } else {
-            gjs_throw_custom(context, JSProto_TypeError, nullptr,
-                             "Object is of type %s.%s - cannot convert to %s",
-                             g_base_info_get_namespace((GIBaseInfo*) priv->info),
-                             g_base_info_get_name((GIBaseInfo*) priv->info),
-                             g_type_name(expected_type));
-        }
-    }
-
-    return result;
+    if (throw_error)
+        return UnionBase::typecheck(context, object, expected_info,
+                                    expected_type);
+    return UnionBase::typecheck(context, object, expected_info, expected_type,
+                                UnionBase::TypecheckNoThrow());
 }
diff --git a/gi/union.h b/gi/union.h
index cf338e7f..2d1665e6 100644
--- a/gi/union.h
+++ b/gi/union.h
@@ -28,9 +28,69 @@
 #include <glib.h>
 #include <girepository.h>
 
+#include "gi/wrapperutils.h"
 #include "gjs/jsapi-util.h"
 #include "gjs/macros.h"
 
+class UnionPrototype;
+class UnionInstance;
+
+class UnionBase
+    : public GIWrapperBase<UnionBase, UnionPrototype, UnionInstance> {
+    friend class GIWrapperBase;
+
+ protected:
+    explicit UnionBase(UnionPrototype* proto = nullptr)
+        : GIWrapperBase(proto) {}
+    ~UnionBase(void) {}
+
+    static const GjsDebugTopic debug_topic = GJS_DEBUG_GBOXED;
+    static constexpr const char* debug_tag = "union";
+
+    static const JSClassOps class_ops;
+    static const JSClass klass;
+
+    GJS_USE static const char* to_string_kind(void) { return "union"; }
+};
+
+class UnionPrototype : public GIWrapperPrototype<UnionBase, UnionPrototype,
+                                                 UnionInstance, GIUnionInfo> {
+    friend class GIWrapperPrototype;
+    friend class GIWrapperBase;
+
+    explicit UnionPrototype(GIUnionInfo* info, GType gtype);
+    ~UnionPrototype(void);
+
+    GJS_JSAPI_RETURN_CONVENTION
+    bool resolve_impl(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                      const char* prop_name, bool* resolved);
+
+    // Overrides GIWrapperPrototype::constructor_nargs().
+    GJS_USE unsigned constructor_nargs(void) const { return 0; }
+};
+
+class UnionInstance
+    : public GIWrapperInstance<UnionBase, UnionPrototype, UnionInstance> {
+    friend class GIWrapperInstance;
+    friend class GIWrapperBase;
+
+    explicit UnionInstance(JSContext* cx, JS::HandleObject obj);
+    ~UnionInstance(void);
+
+    GJS_JSAPI_RETURN_CONVENTION
+    bool constructor_impl(JSContext* cx, JS::HandleObject obj,
+                          const JS::CallArgs& args);
+
+ public:
+    /*
+     * UnionInstance::copy_union:
+     *
+     * Allocate a new union pointer using g_boxed_copy(), from a raw union
+     * pointer.
+     */
+    void copy_union(void* ptr) { m_ptr = g_boxed_copy(gtype(), ptr); }
+};
+
 G_BEGIN_DECLS
 
 GJS_JSAPI_RETURN_CONVENTION
diff --git a/gjs/mem-private.h b/gjs/mem-private.h
index 75da541b..e9819ca0 100644
--- a/gjs/mem-private.h
+++ b/gjs/mem-private.h
@@ -51,7 +51,8 @@ GJS_DECLARE_COUNTER(object_instance)
 GJS_DECLARE_COUNTER(object_prototype)
 GJS_DECLARE_COUNTER(param)
 GJS_DECLARE_COUNTER(repo)
-GJS_DECLARE_COUNTER(union)
+GJS_DECLARE_COUNTER(union_instance)
+GJS_DECLARE_COUNTER(union_prototype)
 
 #define GJS_INC_COUNTER(name)                               \
     do {                                                    \
diff --git a/gjs/mem.cpp b/gjs/mem.cpp
index 2e3a3e9b..2f54c30d 100644
--- a/gjs/mem.cpp
+++ b/gjs/mem.cpp
@@ -51,7 +51,8 @@ GJS_DEFINE_COUNTER(object_instance)
 GJS_DEFINE_COUNTER(object_prototype)
 GJS_DEFINE_COUNTER(param)
 GJS_DEFINE_COUNTER(repo)
-GJS_DEFINE_COUNTER(union)
+GJS_DEFINE_COUNTER(union_instance)
+GJS_DEFINE_COUNTER(union_prototype)
 
 #define GJS_LIST_COUNTER(name) \
     & gjs_counter_ ## name
@@ -75,7 +76,8 @@ static GjsMemCounter* counters[] = {
     GJS_LIST_COUNTER(object_prototype),
     GJS_LIST_COUNTER(param),
     GJS_LIST_COUNTER(repo),
-    GJS_LIST_COUNTER(union),
+    GJS_LIST_COUNTER(union_instance),
+    GJS_LIST_COUNTER(union_prototype),
 };
 // clang-format on
 


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