[gjs] Rework typechecking and private data access



commit 6ed5e40b4f62992c5b18ab8c3034072dd12e60c9
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Thu Jul 5 16:05:47 2012 +0200

    Rework typechecking and private data access
    
    Previously, all type safety checks were inside priv_from_js, which
    returned NULL if the object was of the wrong class. Except that a
    lot of code was not checking for NULL, causing potential crashes.
    Also, we were only checking the JSClass, not the actual type of
    object, so for example a function accepting a boxed would take in
    any boxed instance, not just the right one (and then that could
    crash JS code).
    Rework this by splitting typechecking and private data access.
    Internal API for typechecking is provided by all object classes
    (object, boxed, union, param, gerror) and it takes the necessary
    amount of information (GType, GIBaseInfo) to ensure type safety.
    priv_from_js becomes unsafe, and thus ok to call in dangerous
    data paths such as tracing and finalization.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=679688

 gi/arg.c                       |   87 +++++++++++++++++-----------
 gi/boxed.c                     |   58 ++++++++++++++++--
 gi/boxed.h                     |    5 ++
 gi/function.c                  |   94 ++++++++++++++++++------------
 gi/gerror.c                    |    8 +++
 gi/gerror.h                    |    3 +
 gi/object.c                    |  102 ++++++++++++++++++++++++++++-----
 gi/object.h                    |    4 +
 gi/param.c                     |   42 ++++++++++++--
 gi/param.h                     |    4 +
 gi/union.c                     |   69 ++++++++++++++++++----
 gi/union.h                     |    5 ++
 gi/value.c                     |   35 +++++++++++-
 gjs/jsapi-util-error.c         |   27 ++++++++-
 gjs/jsapi-util.c               |   99 +++++++++++++++-----------------
 gjs/jsapi-util.h               |  123 +++++++++++++++++++++++-----------------
 modules/system.c               |    5 +-
 test/js/testEverythingBasic.js |   47 +++++++++++++++
 18 files changed, 593 insertions(+), 224 deletions(-)
---
diff --git a/gi/arg.c b/gi/arg.c
index 2d57627..39b2c2c 100644
--- a/gi/arg.c
+++ b/gi/arg.c
@@ -1282,11 +1282,16 @@ gjs_value_to_g_argument(JSContext      *context,
         if (JSVAL_IS_NULL(value)) {
             arg->v_pointer = NULL;
         } else if (JSVAL_IS_OBJECT(value)) {
-            arg->v_pointer = gjs_gerror_from_error(context,
-                                                   JSVAL_TO_OBJECT(value));
+            if (gjs_typecheck_gerror(context, JSVAL_TO_OBJECT(value),
+                                      JS_TRUE)) {
+                arg->v_pointer = gjs_gerror_from_error(context,
+                                                       JSVAL_TO_OBJECT(value));
 
-            if (transfer != GI_TRANSFER_NOTHING)
-                arg->v_pointer = g_error_copy (arg->v_pointer);
+                if (transfer != GI_TRANSFER_NOTHING)
+                    arg->v_pointer = g_error_copy (arg->v_pointer);
+            } else {
+                wrong = TRUE;
+            }
         } else {
             wrong = TRUE;
             report_type_mismatch = TRUE;
@@ -1363,14 +1368,26 @@ gjs_value_to_g_argument(JSContext      *context,
 
                     /* special case GError too */
                     if (g_type_is_a(gtype, G_TYPE_ERROR)) {
-                        arg->v_pointer = gjs_gerror_from_error(context,
-                                                               JSVAL_TO_OBJECT(value));
+                        if (!gjs_typecheck_gerror(context, JSVAL_TO_OBJECT(value), JS_TRUE)) {
+                            arg->v_pointer = NULL;
+                            wrong = TRUE;
+                        } else {
+                            arg->v_pointer = gjs_gerror_from_error(context,
+                                                                   JSVAL_TO_OBJECT(value));
+                        }
                     } else {
-                        arg->v_pointer = gjs_c_struct_from_boxed(context,
-                                                                 JSVAL_TO_OBJECT(value));
+                        if (!gjs_typecheck_boxed(context, JSVAL_TO_OBJECT(value),
+                                                 interface_info, gtype,
+                                                 JS_TRUE)) {
+                            arg->v_pointer = NULL;
+                            wrong = TRUE;
+                        } else {
+                            arg->v_pointer = gjs_c_struct_from_boxed(context,
+                                                                     JSVAL_TO_OBJECT(value));
+                        }
                     }
 
-                    if (transfer != GI_TRANSFER_NOTHING) {
+                    if (!wrong && transfer != GI_TRANSFER_NOTHING) {
                         if (g_type_is_a(gtype, G_TYPE_BOXED))
                             arg->v_pointer = g_boxed_copy (gtype, arg->v_pointer);
                         else if (g_type_is_a(gtype, G_TYPE_VARIANT))
@@ -1384,37 +1401,39 @@ gjs_value_to_g_argument(JSContext      *context,
                     }
 
                 } else if (interface_type == GI_INFO_TYPE_UNION) {
-                    arg->v_pointer = gjs_c_union_from_union(context,
-                                                            JSVAL_TO_OBJECT(value));
-
-                    if (transfer != GI_TRANSFER_NOTHING) {
-                        if (g_type_is_a(gtype, G_TYPE_BOXED))
-                            arg->v_pointer = g_boxed_copy (gtype, arg->v_pointer);
-                        else {
-                            gjs_throw(context,
-                                      "Can't transfer ownership of a union type not registered as boxed");
+                    if (gjs_typecheck_union(context, JSVAL_TO_OBJECT(value),
+                                            interface_info, gtype, JS_TRUE)) {
+                        arg->v_pointer = gjs_c_union_from_union(context,
+                                                                JSVAL_TO_OBJECT(value));
+
+                        if (transfer != GI_TRANSFER_NOTHING) {
+                            if (g_type_is_a(gtype, G_TYPE_BOXED))
+                                arg->v_pointer = g_boxed_copy (gtype, arg->v_pointer);
+                            else {
+                                gjs_throw(context,
+                                          "Can't transfer ownership of a union type not registered as boxed");
 
-                            arg->v_pointer = NULL;
-                            wrong = TRUE;
+                                arg->v_pointer = NULL;
+                                wrong = TRUE;
+                            }
                         }
+                    } else {
+                        arg->v_pointer = NULL;
+                        wrong = TRUE;
                     }
 
                 } else if (gtype != G_TYPE_NONE) {
-
                     if (g_type_is_a(gtype, G_TYPE_OBJECT) || g_type_is_a(gtype, G_TYPE_INTERFACE)) {
-                        arg->v_pointer = gjs_g_object_from_object(context,
-                                                                  JSVAL_TO_OBJECT(value));
-                        if (arg->v_pointer != NULL) {
-                            if (!g_type_is_a(G_TYPE_FROM_INSTANCE(arg->v_pointer),
-                                             gtype)) {
-                                gjs_throw(context,
-                                          "Expected type '%s' but got '%s'",
-                                          g_type_name(gtype),
-                                          g_type_name(G_TYPE_FROM_INSTANCE(arg->v_pointer)));
-                                arg->v_pointer = NULL;
-                                wrong = TRUE;
-                            } else if (transfer != GI_TRANSFER_NOTHING)
+                        if (gjs_typecheck_object(context, JSVAL_TO_OBJECT(value),
+                                                 gtype, JS_TRUE)) {
+                            arg->v_pointer = gjs_g_object_from_object(context,
+                                                                      JSVAL_TO_OBJECT(value));
+
+                            if (transfer != GI_TRANSFER_NOTHING)
                                 g_object_ref(G_OBJECT(arg->v_pointer));
+                        } else {
+                            arg->v_pointer = NULL;
+                            wrong = TRUE;
                         }
                     } else if (g_type_is_a(gtype, G_TYPE_BOXED)) {
                         if (g_type_is_a(gtype, G_TYPE_CLOSURE)) {
diff --git a/gi/boxed.c b/gi/boxed.c
index 62c0be0..2b4fb18 100644
--- a/gi/boxed.c
+++ b/gi/boxed.c
@@ -1261,17 +1261,61 @@ gjs_c_struct_from_boxed(JSContext    *context,
         return NULL;
 
     priv = priv_from_js(context, obj);
-
     if (priv == NULL)
         return NULL;
 
+    return priv->gboxed;
+}
+
+JSBool
+gjs_typecheck_boxed(JSContext     *context,
+                    JSObject      *object,
+                    GIStructInfo  *expected_info,
+                    GType          expected_type,
+                    JSBool         throw)
+{
+    Boxed *priv;
+    JSBool result;
+
+    if (!do_base_typecheck(context, object, throw))
+        return JS_FALSE;
+
+    priv = priv_from_js(context, object);
+
     if (priv->gboxed == NULL) {
-        gjs_throw(context,
-                  "Object is %s.%s.prototype, not an object instance - cannot convert to a boxed instance",
-                  g_base_info_get_namespace( (GIBaseInfo*) priv->info),
-                  g_base_info_get_name( (GIBaseInfo*) priv->info));
-        return NULL;
+        if (throw) {
+            gjs_throw_custom(context, "TypeError",
+                             "Object is %s.%s.prototype, not an object instance - cannot convert to a boxed instance",
+                             g_base_info_get_namespace( (GIBaseInfo*) priv->info),
+                             g_base_info_get_name( (GIBaseInfo*) priv->info));
+        }
+
+        return JS_FALSE;
     }
 
-    return priv->gboxed;
+    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 = JS_TRUE;
+
+    if (!result && throw) {
+        if (expected_info != NULL) {
+            gjs_throw_custom(context, "TypeError",
+                             "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, "TypeError",
+                             "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;
 }
diff --git a/gi/boxed.h b/gi/boxed.h
index 3ac1ba0..6f32456 100644
--- a/gi/boxed.h
+++ b/gi/boxed.h
@@ -57,6 +57,11 @@ JSObject* gjs_boxed_from_c_struct      (JSContext             *context,
                                         GIStructInfo          *info,
                                         void                  *gboxed,
                                         GjsBoxedCreationFlags  flags);
+JSBool    gjs_typecheck_boxed          (JSContext             *context,
+                                        JSObject              *obj,
+                                        GIStructInfo          *expected_info,
+                                        GType                  expected_type,
+                                        JSBool                 throw);
 
 G_END_DECLS
 
diff --git a/gi/function.c b/gi/function.c
index 03bd5e8..a1fe2b2 100644
--- a/gi/function.c
+++ b/gi/function.c
@@ -508,6 +508,59 @@ get_length_from_arg (GArgument *arg, GITypeTag tag)
 }
 
 static JSBool
+gjs_fill_method_instance (JSContext  *context,
+                          JSObject   *obj,
+                          Function   *function,
+                          GIArgument *out_arg)
+{
+    GIBaseInfo *container = g_base_info_get_container((GIBaseInfo *) function->info);
+    GIInfoType type = g_base_info_get_type(container);
+    GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *)container);
+
+    switch (type) {
+    case GI_INFO_TYPE_STRUCT:
+    case GI_INFO_TYPE_BOXED:
+        /* GError must be special cased */
+        if (g_type_is_a(gtype, G_TYPE_ERROR)) {
+            if (!gjs_typecheck_gerror(context, obj, JS_TRUE))
+                return JS_FALSE;
+
+            out_arg->v_pointer = gjs_gerror_from_error(context, obj);
+        } else {
+            if (!gjs_typecheck_boxed(context, obj,
+                                     container, gtype,
+                                     JS_TRUE))
+                return JS_FALSE;
+
+            out_arg->v_pointer = gjs_c_struct_from_boxed(context, obj);
+        }
+        break;
+
+    case GI_INFO_TYPE_UNION:
+        if (!gjs_typecheck_union(context, obj,
+                                 container, gtype, JS_TRUE))
+            return JS_FALSE;
+
+        out_arg->v_pointer = gjs_c_union_from_union(context, obj);
+        break;
+
+    case GI_INFO_TYPE_OBJECT:
+    case GI_INFO_TYPE_INTERFACE:
+        if (!gjs_typecheck_object(context, obj,
+                                  gtype, JS_TRUE))
+            return JS_FALSE;
+
+        out_arg->v_pointer = gjs_g_object_from_object(context, obj);
+        break;
+
+    default:
+        g_assert_not_reached();
+    }
+
+    return JS_TRUE;
+}
+
+static JSBool
 gjs_invoke_c_function(JSContext      *context,
                       Function       *function,
                       JSObject       *obj, /* "this" object */
@@ -630,44 +683,9 @@ gjs_invoke_c_function(JSContext      *context,
     js_arg_pos = 0; /* index into argv */
 
     if (is_method) {
-        GIBaseInfo *container = g_base_info_get_container((GIBaseInfo *) function->info);
-        GIInfoType type = g_base_info_get_type(container);
-
-        g_assert_cmpuint(0, <, c_argc);
-
-        if (type == GI_INFO_TYPE_STRUCT || type == GI_INFO_TYPE_BOXED) {
-            GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *)container);
-
-            /* GError must be special cased */
-            if (g_type_is_a(gtype, G_TYPE_ERROR))
-                in_arg_cvalues[0].v_pointer = gjs_gerror_from_error(context, obj);
-            else
-                in_arg_cvalues[0].v_pointer = gjs_c_struct_from_boxed(context, obj);
-        } else if (type == GI_INFO_TYPE_UNION) {
-            in_arg_cvalues[0].v_pointer = gjs_c_union_from_union(context, obj);
-        } else { /* by fallback is always object */
-            GType gtype;
-
-            in_arg_cvalues[0].v_pointer = gjs_g_object_from_object(context, obj);
-            if (in_arg_cvalues[0].v_pointer == NULL) {
-                /* priv == NULL (user probably forgot to chain _init).
-                 * Anyway, in this case we've thrown an exception, so just
-                 * make sure we fail. */
-                failed = TRUE;
-                goto release;
-            }
-
-            gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *)container);
-            if (!g_type_is_a (G_TYPE_FROM_INSTANCE (in_arg_cvalues[0].v_pointer),
-                              gtype)) {
-                gjs_throw(context,
-                          "Expected type '%s' but got '%s'",
-                          g_type_name(gtype),
-                          g_type_name(G_TYPE_FROM_INSTANCE(in_arg_cvalues[0].v_pointer)));
-                failed = TRUE;
-                goto release;
-            }
-        }
+        if (!gjs_fill_method_instance(context, obj,
+                                      function, &in_arg_cvalues[0]))
+            return JS_FALSE;
         ffi_arg_pointers[0] = &in_arg_cvalues[0];
         ++c_arg_pos;
     }
diff --git a/gi/gerror.c b/gi/gerror.c
index b5ec1ab..5c035a1 100644
--- a/gi/gerror.c
+++ b/gi/gerror.c
@@ -650,3 +650,11 @@ gjs_gerror_from_error(JSContext    *context,
 
     return priv->gerror;
 }
+
+JSBool
+gjs_typecheck_gerror (JSContext *context,
+                      JSObject  *obj,
+                      JSBool     throw)
+{
+    return do_base_typecheck(context, obj, throw);
+}
diff --git a/gi/gerror.h b/gi/gerror.h
index 3940d1a..eddee61 100644
--- a/gi/gerror.h
+++ b/gi/gerror.h
@@ -48,6 +48,9 @@ GError*   gjs_gerror_from_error        (JSContext             *context,
 JSObject* gjs_error_from_gerror        (JSContext             *context,
                                         GError                *gerror,
                                         gboolean               add_stack);
+JSBool    gjs_typecheck_gerror         (JSContext             *context,
+                                        JSObject              *obj,
+                                        JSBool                 throw);
 
 G_END_DECLS
 
diff --git a/gi/object.c b/gi/object.c
index 7586b13..e710100 100644
--- a/gi/object.c
+++ b/gi/object.c
@@ -1049,11 +1049,7 @@ object_instance_trace(JSTracer *tracer,
     ObjectInstance *priv;
     GList *iter;
 
-    /* DO NOT use priv_from_js here: that uses JS_BeginRequest,
-       but this is called from the GC thread, and deadlocks
-       We know we're of the right JSClass anyway.
-    */
-    priv = JS_GetPrivate(tracer->context, obj);
+    priv = priv_from_js(tracer->context, obj);
 
     for (iter = priv->signals; iter; iter = iter->next) {
         ConnectData *cd = iter->data;
@@ -1172,6 +1168,9 @@ real_connect_func(JSContext *context,
     ConnectData *connect_data;
     JSBool ret = JS_FALSE;
 
+    if (!do_base_typecheck(context, obj, JS_TRUE))
+        return JS_FALSE;
+
     priv = priv_from_js(context, obj);
     gjs_debug_gsignal("connect obj %p priv %p argc %d", obj, priv, argc);
     if (priv == NULL) {
@@ -1271,6 +1270,9 @@ disconnect_func(JSContext *context,
     ObjectInstance *priv;
     gulong id;
 
+    if (!do_base_typecheck(context, obj, JS_TRUE))
+        return JS_FALSE;
+
     priv = priv_from_js(context, obj);
     gjs_debug_gsignal("disconnect obj %p priv %p argc %d", obj, priv, argc);
 
@@ -1321,6 +1323,9 @@ emit_func(JSContext *context,
     jsval retval;
     JSBool ret = JS_FALSE;
 
+    if (!do_base_typecheck(context, obj, JS_TRUE))
+        return JS_FALSE;
+
     priv = priv_from_js(context, obj);
     gjs_debug_gsignal("emit obj %p priv %p argc %d", obj, priv, argc);
 
@@ -1441,6 +1446,9 @@ to_string_func(JSContext *context,
     const char *name;
     jsval retval;
 
+    if (!do_base_typecheck(context, obj, JS_TRUE))
+        return JS_FALSE;
+
     priv = priv_from_js(context, obj);
 
     if (priv == NULL) {
@@ -1896,25 +1904,70 @@ gjs_g_object_from_object(JSContext    *context,
         return NULL;
 
     priv = priv_from_js(context, obj);
+    return priv->gobj;
+}
+
+JSBool
+gjs_typecheck_object(JSContext     *context,
+                     JSObject      *object,
+                     GType          expected_type,
+                     JSBool         throw)
+{
+    ObjectInstance *priv;
+    JSBool result;
+
+    if (!do_base_typecheck(context, object, throw))
+        return JS_FALSE;
+
+    priv = priv_from_js(context, object);
 
     if (priv == NULL) {
-        gjs_throw(context,
-                  "Object instance or prototype has not been properly initialized yet. "
-                  "Did you forget to chain-up from _init()?");
-        return NULL;
+        if (throw) {
+            gjs_throw(context,
+                      "Object instance or prototype has not been properly initialized yet. "
+                      "Did you forget to chain-up from _init()?");
+        }
+
+        return JS_FALSE;
     }
 
     if (priv->gobj == NULL) {
-        gjs_throw(context,
-                  "Object is %s.%s.prototype, not an object instance - cannot convert to GObject*",
-                  priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
-                  priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype));
-        return NULL;
+        if (throw) {
+            gjs_throw(context,
+                      "Object is %s.%s.prototype, not an object instance - cannot convert to GObject*",
+                      priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
+                      priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype));
+        }
+
+        return JS_FALSE;
     }
 
-    return priv->gobj;
+    g_assert(priv->gtype == G_OBJECT_TYPE(priv->gobj));
+
+    if (expected_type != G_TYPE_NONE)
+        result = g_type_is_a (priv->gtype, expected_type);
+    else
+        result = JS_TRUE;
+
+    if (!result && throw) {
+        if (priv->info) {
+            gjs_throw_custom(context, "TypeError",
+                             "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));
+        } else {
+            gjs_throw_custom(context, "TypeError",
+                             "Object is of type %s - cannot convert to %s",
+                             g_type_name(priv->gtype),
+                             g_type_name(expected_type));
+        }
+    }
+
+    return result;
 }
 
+
 static void
 find_vfunc_info (JSContext *context,
                  GType implementor_gtype,
@@ -2013,6 +2066,9 @@ gjs_hook_up_vfunc(JSContext *cx,
                         "function", &function))
         return JS_FALSE;
 
+    if (!do_base_typecheck(cx, object, JS_TRUE))
+        return JS_FALSE;
+
     priv = priv_from_js(cx, object);
     gtype = priv->gtype;
     info = priv->info;
@@ -2233,6 +2289,9 @@ gjs_register_type(JSContext *cx,
     if (!parent)
         return JS_FALSE;
 
+    if (!do_base_typecheck(cx, parent, JS_TRUE))
+        return JS_FALSE;
+
     parent_priv = priv_from_js(cx, parent);
 
     if (!parent_priv)
@@ -2283,6 +2342,9 @@ gjs_add_interface(JSContext *cx,
                         "gtype", &iface_jsobj))
         return JS_FALSE;
 
+    if (!do_base_typecheck(cx, object, JS_TRUE))
+        return JS_FALSE;
+
     priv = priv_from_js(cx, object);
 
     g_type_add_interface_static(priv->gtype,
@@ -2313,6 +2375,11 @@ gjs_register_property(JSContext *cx,
     obj = JSVAL_TO_OBJECT(argv[0]);
     pspec_js = JSVAL_TO_OBJECT(argv[1]);
 
+    if (!do_base_typecheck(cx, obj, JS_TRUE))
+        return JS_FALSE;
+    if (!gjs_typecheck_param(cx, pspec_js, G_TYPE_NONE, JS_TRUE))
+        return JS_FALSE;
+
     priv = priv_from_js(cx, obj);
     pspec = gjs_g_param_from_param(cx, pspec_js);
 
@@ -2350,6 +2417,11 @@ gjs_signal_new(JSContext *cx,
     }
 
     obj = JSVAL_TO_OBJECT(argv[0]);
+    if (!do_base_typecheck(cx, obj, JS_TRUE)) {
+        ret = JS_FALSE;
+        goto out;
+    }
+
     priv = priv_from_js(cx, obj);
 
     /* we only support standard accumulators for now */
diff --git a/gi/object.h b/gi/object.h
index 8a39b5d..1957abd 100644
--- a/gi/object.h
+++ b/gi/object.h
@@ -43,6 +43,10 @@ JSObject* gjs_object_from_g_object      (JSContext     *context,
                                          GObject       *gobj);
 GObject*  gjs_g_object_from_object      (JSContext     *context,
                                          JSObject      *obj);
+JSBool    gjs_typecheck_object          (JSContext     *context,
+                                         JSObject      *obj,
+                                         GType          expected_type,
+                                         JSBool         throw);
 
 G_END_DECLS
 
diff --git a/gi/param.c b/gi/param.c
index ae7bed5..509bdf7 100644
--- a/gi/param.c
+++ b/gi/param.c
@@ -618,14 +618,44 @@ gjs_g_param_from_param(JSContext    *context,
 
     priv = priv_from_js(context, obj);
 
-    if (priv == NULL)
-        return NULL;
+    return priv->gparam;
+}
+
+JSBool
+gjs_typecheck_param(JSContext     *context,
+                    JSObject      *object,
+                    GType          expected_type,
+                    JSBool         throw)
+{
+    Param *priv;
+    JSBool result;
+
+    if (!do_base_typecheck(context, object, throw))
+        return JS_FALSE;
+
+    priv = priv_from_js(context, object);
 
     if (priv->gparam == NULL) {
-        gjs_throw(context,
-                  "Object is a prototype, not an object instance - cannot convert to a paramspec instance");
-        return NULL;
+        if (throw) {
+            gjs_throw_custom(context, "TypeError",
+                             "Object is GObject.ParamSpec.prototype, not an object instance - "
+                             "cannot convert to a GObject.ParamSpec instance");
+        }
+
+        return JS_FALSE;
     }
 
-    return priv->gparam;
+    if (expected_type != G_TYPE_NONE)
+        result = g_type_is_a (G_TYPE_FROM_INSTANCE (priv->gparam), expected_type);
+    else
+        result = JS_TRUE;
+
+    if (!result && throw) {
+        gjs_throw_custom(context, "TypeError",
+                         "Object is of type %s - cannot convert to %s",
+                         g_type_name(G_TYPE_FROM_INSTANCE (priv->gparam)),
+                         g_type_name(expected_type));
+    }
+
+    return result;
 }
diff --git a/gi/param.h b/gi/param.h
index 6a50b43..56d2fed 100644
--- a/gi/param.h
+++ b/gi/param.h
@@ -39,6 +39,10 @@ GParamSpec* gjs_g_param_from_param     (JSContext  *context,
                                         JSObject   *obj);
 JSObject*   gjs_param_from_g_param     (JSContext  *context,
                                         GParamSpec *param);
+JSBool      gjs_typecheck_param        (JSContext  *context,
+                                        JSObject   *obj,
+                                        GType       expected_type,
+                                        JSBool      throw);
 JSObject*   gjs_lookup_param_prototype (JSContext  *context);
 
 
diff --git a/gi/union.c b/gi/union.c
index 36e9f4b..53ff468 100644
--- a/gi/union.c
+++ b/gi/union.c
@@ -44,6 +44,7 @@
 typedef struct {
     GIUnionInfo *info;
     void *gboxed; /* NULL if we are the prototype and not an instance */
+    GType gtype;
 } Union;
 
 static struct JSClass gjs_union_class;
@@ -194,7 +195,6 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(union)
     Union *priv;
     Union *proto_priv;
     JSObject *proto;
-    GType gtype;
     void *gboxed;
 
     GJS_NATIVE_CONSTRUCTOR_PRELUDE(union);
@@ -226,8 +226,7 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(union)
 
     priv->info = proto_priv->info;
     g_base_info_ref( (GIBaseInfo*) priv->info);
-
-    gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) priv->info);
+    priv->gtype = proto_priv->gtype;
 
     /* union_new happens to be implemented by calling
      * gjs_invoke_c_function(), which returns a jsval.
@@ -244,11 +243,11 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(union)
      * be garbage collected, we make a copy here to be
      * owned by us.
      */
-    priv->gboxed = g_boxed_copy(gtype, gboxed);
+    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(gtype));
+                        priv->gboxed, g_type_name(priv->gtype));
 
     GJS_NATIVE_CONSTRUCTOR_FINISH(union);
 
@@ -454,6 +453,7 @@ gjs_define_union_class(JSContext    *context,
     priv = g_slice_new0(Union);
     priv->info = info;
     g_base_info_ref( (GIBaseInfo*) priv->info);
+    priv->gtype = gtype;
     JS_SetPrivate(context, prototype, priv);
 
     gjs_debug(GJS_DEBUG_GBOXED, "Defined class %s prototype is %p class %p in object %p",
@@ -520,6 +520,7 @@ gjs_union_from_c_union(JSContext    *context,
     JS_SetPrivate(context, obj, priv);
     priv->info = info;
     g_base_info_ref( (GIBaseInfo *) priv->info);
+    priv->gtype = gtype;
     priv->gboxed = g_boxed_copy(gtype, gboxed);
 
     return obj;
@@ -536,16 +537,58 @@ gjs_c_union_from_union(JSContext    *context,
 
     priv = priv_from_js(context, obj);
 
-    if (priv == NULL)
-        return NULL;
+    return priv->gboxed;
+}
+
+JSBool
+gjs_typecheck_union(JSContext     *context,
+                    JSObject      *object,
+                    GIStructInfo  *expected_info,
+                    GType          expected_type,
+                    JSBool         throw)
+{
+    Union *priv;
+    JSBool result;
+
+    if (!do_base_typecheck(context, object, throw))
+        return JS_FALSE;
+
+    priv = priv_from_js(context, object);
 
     if (priv->gboxed == NULL) {
-        gjs_throw(context,
-                  "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 NULL;
+        if (throw) {
+            gjs_throw_custom(context, "TypeError",
+                             "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 JS_FALSE;
     }
 
-    return priv->gboxed;
+    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 = JS_TRUE;
+
+    if (!result && throw) {
+        if (expected_info != NULL) {
+            gjs_throw_custom(context, "TypeError",
+                             "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, "TypeError",
+                             "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;
 }
diff --git a/gi/union.h b/gi/union.h
index 87c4f96..08aff51 100644
--- a/gi/union.h
+++ b/gi/union.h
@@ -48,6 +48,11 @@ void*     gjs_c_union_from_union       (JSContext    *context,
 JSObject* gjs_union_from_c_union       (JSContext    *context,
                                         GIUnionInfo  *info,
                                         void         *gboxed);
+JSBool    gjs_typecheck_union          (JSContext             *context,
+                                        JSObject              *obj,
+                                        GIStructInfo          *expected_info,
+                                        GType                  expected_type,
+                                        JSBool                 throw);
 
 G_END_DECLS
 
diff --git a/gi/value.c b/gi/value.c
index 5d8fe16..4d8de2f 100644
--- a/gi/value.c
+++ b/gi/value.c
@@ -334,6 +334,11 @@ gjs_value_to_g_value_internal(JSContext    *context,
         } else if (JSVAL_IS_OBJECT(value)) {
             JSObject *obj;
             obj = JSVAL_TO_OBJECT(value);
+
+            if (!gjs_typecheck_object(context, obj,
+                                      gtype, JS_TRUE))
+                return JS_FALSE;
+
             gobj = gjs_g_object_from_object(context, obj);
         } else {
             gjs_throw(context,
@@ -392,9 +397,28 @@ gjs_value_to_g_value_internal(JSContext    *context,
 
             if (g_type_is_a(gtype, G_TYPE_ERROR)) {
                 /* special case GError */
+                if (!gjs_typecheck_gerror(context, obj, JS_TRUE))
+                    return JS_FALSE;
+
                 gboxed = gjs_gerror_from_error(context, obj);
             } else {
-                gboxed = gjs_c_struct_from_boxed(context, obj);
+                /* First try a union, if that fails,
+                   assume a boxed struct. Distinguishing
+                   which one is expected would require checking
+                   the associated GIBaseInfo, which is not necessary
+                   possible, if e.g. we see the GType without
+                   loading the typelib.
+                */
+                if (gjs_typecheck_union(context, obj,
+                                        NULL, gtype, JS_FALSE)) {
+                    gboxed = gjs_c_union_from_union(context, obj);
+                } else {
+                    if (!gjs_typecheck_boxed(context, obj,
+                                             NULL, gtype, JS_TRUE))
+                        return JS_FALSE;
+
+                    gboxed = gjs_c_struct_from_boxed(context, obj);
+                }
             }
         } else {
             gjs_throw(context,
@@ -415,6 +439,11 @@ gjs_value_to_g_value_internal(JSContext    *context,
             /* nothing to do */
         } else if (JSVAL_IS_OBJECT(value)) {
             JSObject *obj = JSVAL_TO_OBJECT(value);
+
+            if (!gjs_typecheck_boxed(context, obj,
+                                     NULL, G_TYPE_VARIANT, JS_TRUE))
+                return JS_FALSE;
+
             variant = gjs_c_struct_from_boxed(context, obj);
         } else {
             gjs_throw(context,
@@ -474,6 +503,10 @@ gjs_value_to_g_value_internal(JSContext    *context,
         } else if (JSVAL_IS_OBJECT(value)) {
             JSObject *obj;
             obj = JSVAL_TO_OBJECT(value);
+
+            if (!gjs_typecheck_param(context, obj, gtype, JS_TRUE))
+                return JS_FALSE;
+
             gparam = gjs_g_param_from_param(context, obj);
         } else {
             gjs_throw(context,
diff --git a/gjs/jsapi-util-error.c b/gjs/jsapi-util-error.c
index 8227fe1..c008b8c 100644
--- a/gjs/jsapi-util-error.c
+++ b/gjs/jsapi-util-error.c
@@ -43,8 +43,9 @@
  */
 static void
 gjs_throw_valist(JSContext       *context,
-                    const char      *format,
-                    va_list          args)
+                 const char      *error_class,
+                 const char      *format,
+                 va_list          args)
 {
     char *s;
     JSBool result;
@@ -82,7 +83,7 @@ gjs_throw_valist(JSContext       *context,
     }
 
     if (!gjs_object_get_property(context, JS_GetGlobalObject(context),
-                                 "Error", &v_constructor)) {
+                                 error_class, &v_constructor)) {
         JS_ReportError(context, "??? Missing Error constructor in global object?");
         goto out;
     }
@@ -125,7 +126,25 @@ gjs_throw(JSContext       *context,
     va_list args;
 
     va_start(args, format);
-    gjs_throw_valist(context, format, args);
+    gjs_throw_valist(context, "Error", format, args);
+    va_end(args);
+}
+
+/*
+ * Like gjs_throw, but allows to customize the error
+ * class. Mainly used for throwing TypeError instead of
+ * error.
+ */
+void
+gjs_throw_custom(JSContext       *context,
+                 const char      *error_class,
+                 const char      *format,
+                 ...)
+{
+    va_list args;
+
+    va_start(args, format);
+    gjs_throw_valist(context, error_class, format, args);
     va_end(args);
 }
 
diff --git a/gjs/jsapi-util.c b/gjs/jsapi-util.c
index ca2965e..59df435 100644
--- a/gjs/jsapi-util.c
+++ b/gjs/jsapi-util.c
@@ -567,64 +567,48 @@ gjs_throw_constructor_error(JSContext *context)
               "Constructor called as normal method. Use 'new SomeObject()' not 'SomeObject()'");
 }
 
-void*
-gjs_get_instance_private_dynamic(JSContext      *context,
-                                 JSObject       *obj,
-                                 JSClass        *static_clasp,
-                                 jsval          *argv)
+static const char*
+format_dynamic_class_name (const char *name)
 {
-    RuntimeData *rd;
-    JSClass *obj_class;
-    void *instance;
-
-    if (static_clasp->name != NULL) {
-        g_warning("Dynamic class should not have a name in the JSClass struct");
-        return NULL;
-    }
-
-    JS_BeginRequest(context);
-
-    obj_class = JS_GET_CLASS(context, obj);
-    g_assert(obj_class != NULL);
+    if (g_str_has_prefix(name, "_private_"))
+        return name + strlen("_private_");
+    else
+        return name;
+}
 
-    rd = get_data_from_context(context);
-    g_assert(rd != NULL);
+JSBool
+gjs_typecheck_static_instance(JSContext *context,
+                              JSObject  *obj,
+                              JSClass   *static_clasp,
+                              JSBool     throw)
+{
+    if (!JS_InstanceOf(context, obj, static_clasp, NULL)) {
+        if (throw) {
+            JSClass *obj_class = JS_GET_CLASS(context, obj);
 
-    /* Check that it's safe to cast to DynamicJSClass */
-    if (g_hash_table_lookup(rd->dynamic_classes, obj_class) == NULL) {
-        gjs_throw(context,
-                  "Object %p proto %p doesn't have a dynamically-registered class, it has %s",
-                  obj, JS_GetPrototype(context, obj), obj_class->name);
-        JS_EndRequest(context);
-        return NULL;
-    }
+            gjs_throw_custom(context, "TypeError",
+                             "Object %p is not a subclass of %s, it's a %s",
+                             obj, static_clasp->name, format_dynamic_class_name (obj_class->name));
+        }
 
-    if (static_clasp != ((DynamicJSClass*) obj_class)->static_class) {
-        gjs_throw(context, "Object is not a dynamically-registered class based on expected static class pointer");
-        JS_EndRequest(context);
-        return NULL;
+        return JS_FALSE;
     }
 
-    instance = JS_GetInstancePrivate(context, obj, obj_class, argv);
-    JS_EndRequest(context);
-
-    return instance;
+    return JS_TRUE;
 }
 
-void*
-gjs_get_instance_private_dynamic_with_typecheck(JSContext      *context,
-                                                JSObject       *obj,
-                                                JSClass        *static_clasp,
-                                                jsval          *argv)
+JSBool
+gjs_typecheck_dynamic_instance(JSContext *context,
+                               JSObject  *obj,
+                               JSClass   *static_clasp,
+                               JSBool     throw)
 {
     RuntimeData *rd;
     JSClass *obj_class;
-    void *instance;
+    gboolean wrong = FALSE;
 
-    if (static_clasp->name != NULL) {
-        g_warning("Dynamic class should not have a name in the JSClass struct");
-        return NULL;
-    }
+    obj_class = JS_GET_CLASS(context, obj);
+    g_assert(obj_class != NULL);
 
     JS_BeginRequest(context);
 
@@ -636,18 +620,29 @@ gjs_get_instance_private_dynamic_with_typecheck(JSContext      *context,
 
     /* Check that it's safe to cast to DynamicJSClass */
     if (g_hash_table_lookup(rd->dynamic_classes, obj_class) == NULL) {
-        JS_EndRequest(context);
-        return NULL;
+        wrong = TRUE;
+        goto out;
     }
 
     if (static_clasp != ((DynamicJSClass*) obj_class)->static_class) {
-        JS_EndRequest(context);
-        return NULL;
+        wrong = TRUE;
+        goto out;
     }
 
-    instance = JS_GetInstancePrivate(context, obj, obj_class, argv);
+ out:
     JS_EndRequest(context);
-    return instance;
+
+    if (wrong) {
+        if (throw) {
+            gjs_throw_custom(context, "TypeError",
+                             "Object %p is not a subclass of %s, it's a %s",
+                             obj, static_clasp->name, format_dynamic_class_name (obj_class->name));
+        }
+
+        return JS_FALSE;
+    }
+
+    return JS_TRUE;
 }
 
 JSObject*
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 0e186f9..191dfd8 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -55,54 +55,67 @@ typedef struct GjsRootedArray GjsRootedArray;
  */
 #define GJS_MODULE_PROP_FLAGS (JSPROP_PERMANENT | JSPROP_ENUMERATE)
 
-/* priv_from_js_with_typecheck checks that the object is in fact an
- * instance of the specified class before accessing its private data.
- * Keep in mind that the function can return JS_TRUE and still fill the
- * out parameter with NULL if the object is the prototype for a class
- * without the JSCLASS_CONSTRUCT_PROTOTYPE flag or if it the class simply
- * does not have any private data.
+/*
+ * Helper methods to access private data:
+ *
+ * do_base_typecheck: checks that object has the right JSClass, and possibly
+ *                    throw a TypeError exception if the check fails
+ * priv_from_js: accesses the object private field; as a debug measure,
+ *               it also checks that the object is of a compatible
+ *               JSClass, but it doesn't raise an exception (it
+ *               wouldn't be of much use, if subsequent code crashes on
+ *               NULL)
+ * priv_from_js_with_typecheck: a convenience function to call
+ *                              do_base_typecheck and priv_from_js
  */
-#define GJS_DEFINE_PRIV_FROM_JS(type, class) \
-    __attribute__((unused)) static JSBool           \
-    priv_from_js_with_typecheck(JSContext *context,     \
-                                JSObject  *object,  \
-                                type      **out)    \
-    {\
-        if (!out) \
-            return JS_FALSE; \
-        if (!JS_InstanceOf(context, object, &class, NULL)) \
-            return JS_FALSE; \
-        *out = JS_GetInstancePrivate(context, object, &class, NULL); \
-        return JS_TRUE; \
-    }\
-    static type*\
-    priv_from_js(JSContext *context, \
-                 JSObject  *object)  \
-    {\
-        return JS_GetInstancePrivate(context, object, &class, NULL); \
+#define GJS_DEFINE_PRIV_FROM_JS(type, class)                            \
+    __attribute__((unused)) static inline JSBool                        \
+    do_base_typecheck(JSContext *context,                               \
+                      JSObject  *object,                                \
+                      JSBool     throw)                                 \
+    {                                                                   \
+        return gjs_typecheck_static_instance(context, object, &class, throw); \
+    }                                                                   \
+    static inline type*                                                 \
+    priv_from_js(JSContext *context,                                    \
+                 JSObject  *object)                                     \
+    {                                                                   \
+        return JS_GetInstancePrivate(context, object, &class, NULL);    \
+    }                                                                   \
+    __attribute__((unused)) static JSBool                               \
+    priv_from_js_with_typecheck(JSContext *context,                     \
+                                JSObject  *object,                      \
+                                type      **out)                        \
+    {                                                                   \
+        if (!do_base_typecheck(context, object, JS_FALSE))              \
+            return JS_FALSE;                                            \
+        *out = priv_from_js(context, object);                           \
+        return JS_TRUE;                                                 \
     }
 
-
-#define GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(type, class) \
-    __attribute__((unused)) static JSBool\
-    priv_from_js_with_typecheck(JSContext *context, \
-                                JSObject  *object,  \
-                                type      **out)    \
-    {\
-        type *result; \
-        if (!out) \
-            return JS_FALSE; \
-        result = gjs_get_instance_private_dynamic_with_typecheck(context, object, &class, NULL); \
-        if (result == NULL) \
-            return JS_FALSE; \
-        *out = result; \
-        return JS_TRUE; \
-    }\
-    static type*\
-    priv_from_js(JSContext *context, \
-                 JSObject  *object)  \
-    {\
-        return gjs_get_instance_private_dynamic(context, object, &class, NULL); \
+#define GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(type, class)                    \
+    __attribute__((unused)) static inline JSBool                        \
+    do_base_typecheck(JSContext *context,                               \
+                      JSObject  *object,                                \
+                      JSBool     throw)                                 \
+    {                                                                   \
+        return gjs_typecheck_dynamic_instance(context, object, &class, throw); \
+    }                                                                   \
+    static inline type*                                                 \
+    priv_from_js(JSContext *context,                                    \
+                 JSObject  *object)                                     \
+    {                                                                   \
+        return JS_GetPrivate(context, object);                          \
+    }                                                                   \
+    __attribute__((unused)) static JSBool                               \
+    priv_from_js_with_typecheck(JSContext *context,                     \
+                                JSObject  *object,                      \
+                                type      **out)                        \
+    {                                                                   \
+        if (!do_base_typecheck(context, object, JS_FALSE))              \
+            return JS_FALSE;                                            \
+        *out = priv_from_js(context, object);                           \
+        return JS_TRUE;                                                 \
     }
 
 /**
@@ -227,14 +240,14 @@ JSObject *  gjs_init_class_dynamic           (JSContext       *context,
                                               JSFunctionSpec  *static_fs);
 void gjs_throw_constructor_error             (JSContext       *context);
 
-void* gjs_get_instance_private_dynamic                (JSContext  *context,
-                                                       JSObject   *obj,
-                                                       JSClass    *static_clasp,
-                                                       jsval      *argv);
-void* gjs_get_instance_private_dynamic_with_typecheck (JSContext  *context,
-                                                       JSObject   *obj,
-                                                       JSClass    *static_clasp,
-                                                       jsval      *argv);
+JSBool gjs_typecheck_static_instance          (JSContext  *context,
+                                               JSObject   *obj,
+                                               JSClass    *static_clasp,
+                                               JSBool      _throw);
+JSBool gjs_typecheck_dynamic_instance         (JSContext  *context,
+                                               JSObject   *obj,
+                                               JSClass    *static_clasp,
+                                               JSBool      _throw);
 
 JSObject*   gjs_construct_object_dynamic     (JSContext       *context,
                                               JSObject        *proto,
@@ -249,6 +262,10 @@ JSObject*   gjs_define_string_array          (JSContext       *context,
 void        gjs_throw                        (JSContext       *context,
                                               const char      *format,
                                               ...)  G_GNUC_PRINTF (2, 3);
+void        gjs_throw_custom                 (JSContext       *context,
+                                              const char      *error_class,
+                                              const char      *format,
+                                              ...)  G_GNUC_PRINTF (3, 4);
 void        gjs_throw_literal                (JSContext       *context,
                                               const char      *string);
 void        gjs_throw_g_error                (JSContext       *context,
diff --git a/modules/system.c b/modules/system.c
index 60b9cce..7089d8b 100644
--- a/modules/system.c
+++ b/modules/system.c
@@ -69,8 +69,11 @@ gjs_refcount(JSContext *context,
     if (!gjs_parse_args(context, "refcount", "o", argc, argv, "object", &target_obj))
         return JS_FALSE;
 
-    obj = gjs_g_object_from_object(context, target_obj);
+    if (!gjs_typecheck_object(context, target_obj,
+                              G_TYPE_OBJECT, JS_TRUE))
+        return JS_FALSE;
 
+    obj = gjs_g_object_from_object(context, target_obj);
     if (obj == NULL)
         return JS_FALSE;
 
diff --git a/test/js/testEverythingBasic.js b/test/js/testEverythingBasic.js
index fdcd3ef..99c35b9 100644
--- a/test/js/testEverythingBasic.js
+++ b/test/js/testEverythingBasic.js
@@ -550,4 +550,51 @@ function testGError() {
     });
 }
 
+function testWrongClassGObject() {
+    /* Function calls */
+    // Everything.func_obj_null_in expects a Everything.TestObj
+    assertRaises(function() {
+	Everything.func_obj_null_in(new Gio.SimpleAction);
+    });
+    assertRaises(function() {
+	Everything.func_obj_null_in(new GLib.KeyFile);
+    });
+    assertRaises(function() {
+	Everything.func_obj_null_in(Gio.File.new_for_path('/'));
+    });
+    Everything.func_obj_null_in(new Everything.TestSubObj);
+
+    /* Method calls */
+    assertRaises(function() {
+	Everything.TestObj.prototype.instance_method.call(new Gio.SimpleAction);
+    });
+    assertRaises(function() {
+	Everything.TestObj.prototype.instance_method.call(new GLib.KeyFile);
+    });
+    Everything.TestObj.prototype.instance_method.call(new Everything.TestSubObj);
+}
+
+function testWrongClassGBoxed() {
+    let simpleBoxed = new Everything.TestSimpleBoxedA;
+    // simpleBoxed.equals expects a Everything.TestSimpleBoxedA
+    assertRaises(function() {
+	simpleBoxed.equals(new Gio.SimpleAction);
+    })
+    assertRaises(function() {
+	simpleBoxed.equals(new Everything.TestObj);
+    })
+    assertRaises(function() {
+	simpleBoxed.equals(new GLib.KeyFile);
+    })
+    assertTrue(simpleBoxed.equals(simpleBoxed));
+
+    assertRaises(function() {
+	Everything.TestSimpleBoxedA.prototype.copy.call(new Gio.SimpleAction);
+    });
+    assertRaises(function() {
+	Everything.TestSimpleBoxedA.prototype.copy.call(new GLib.KeyFile);
+    })
+    Everything.TestSimpleBoxedA.prototype.copy.call(simpleBoxed);
+}
+
 gjstestRun();



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