[gjs/wip/gobj-kitchen-sink: 16/20] object: Fix handling classes without repository information



commit c760b0d1e7a9401ef059c563ac59120bfab2ac03
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Fri Dec 16 18:31:59 2011 +0100

    object: Fix handling classes without repository information
    
    If a class doesn't have a GIObjectInfo, we should keep priv->info = NULL
    NULL instead of picking that of a class, because this way we can
    recognized it as resolve (this is the only case where we should look at
    GType for methods).
    
    Also, these classes should not be in an arbitrary namespace, instead
    create an ad-hoc object inside imports.gi and stuff all of them there.
    This is needed as soon custom objects will have dynamic JS classes.

 gi/object.c |  199 +++++++++++++++++++++++++++++++++++++++--------------------
 gi/object.h |    3 +-
 gi/repo.c   |   16 ++++-
 gi/repo.h   |    1 +
 4 files changed, 147 insertions(+), 72 deletions(-)
---
diff --git a/gi/object.c b/gi/object.c
index 4f66698..a9ed166 100644
--- a/gi/object.c
+++ b/gi/object.c
@@ -332,6 +332,58 @@ find_vfunc_on_parent(GIObjectInfo *info,
     return vfunc;
 }
 
+static JSBool
+object_instance_new_resolve_no_info(JSContext       *context,
+                                    JSObject        *obj,
+                                    JSObject       **objp,
+                                    ObjectInstance  *priv,
+                                    char            *name)
+{
+    GIFunctionInfo *method_info;
+    JSBool ret;
+    GType *interfaces;
+    guint n_interfaces;
+    guint i;
+
+    ret = JS_TRUE;
+    interfaces = g_type_interfaces(priv->gtype, &n_interfaces);
+    for (i = 0; i < n_interfaces; i++) {
+        GIBaseInfo *base_info;
+        GIInterfaceInfo *iface_info;
+
+        base_info = g_irepository_find_by_gtype(g_irepository_get_default(),
+                                                interfaces[i]);
+
+        if (base_info == NULL)
+            continue;
+
+        if (g_base_info_get_type(base_info) != GI_INFO_TYPE_INTERFACE) {
+            g_base_info_unref(base_info);
+            continue;
+        }
+
+        iface_info = (GIInterfaceInfo*) base_info;
+
+        method_info = g_interface_info_find_method(iface_info, name);
+
+        g_base_info_unref(base_info);
+
+
+        if (method_info != NULL) {
+            if (gjs_define_function(context, obj, priv->gtype,
+                                    (GICallableInfo *)method_info)) {
+                *objp = obj;
+            } else {
+                ret = JS_FALSE;
+            }
+
+            g_base_info_unref( (GIBaseInfo*) method_info);
+        }
+    }
+
+    g_free(interfaces);
+}
+
 /*
  * Like JSResolveOp, but flags provide contextual information as follows:
  *
@@ -369,14 +421,22 @@ object_instance_new_resolve(JSContext *context,
                      name,
                      obj,
                      priv,
-                     priv ? g_base_info_get_namespace (priv->info) : "",
-                     priv ? g_base_info_get_name (priv->info) : "",
+                     priv && priv->info ? g_base_info_get_namespace (priv->info) : "",
+                     priv && priv->info ? g_base_info_get_name (priv->info) : "",
                      priv ? priv->gobj : NULL,
                      (priv && priv->gobj) ? g_type_name_from_instance((GTypeInstance*) priv->gobj) : "(type unknown)");
 
     if (priv == NULL) {
-        throw_priv_is_null_error(context);
-        goto out; /* we are the wrong class */
+        /* We won't have a private until the initializer is called, so
+         * just defer to prototype chains in this case.
+         *
+         * This isn't too bad: either you get undefined if the field
+         * doesn't exist on any of the prototype chains, or whatever code
+         * will run afterwards will fail because of the "priv == NULL"
+         * check there.
+         */
+        ret = JS_TRUE;
+        goto out;
     }
 
     if (priv->gobj != NULL) {
@@ -384,6 +444,14 @@ object_instance_new_resolve(JSContext *context,
         goto out;
     }
 
+    /* If we have no GIRepository information (we're a JS GObject subclass),
+     * we need to look at exposing interfaces. Look up our interfaces through
+     * GType data, and then hope that *those* are introspectable. */
+    if (priv->info == NULL) {
+        ret = object_instance_new_resolve_no_info(context, obj, objp, priv, name);
+        goto out;
+    }
+
     if (g_str_has_prefix (name, "vfunc_")) {
         /* The only time we find a vfunc info is when we're the base
          * class that defined the vfunc. If we let regular prototype
@@ -761,7 +829,8 @@ init_object_private (JSContext *context,
 
     priv->gtype = proto_priv->gtype;
     priv->info = proto_priv->info;
-    g_base_info_ref( (GIBaseInfo*) priv->info);
+    if (priv->info)
+        g_base_info_ref( (GIBaseInfo*) priv->info);
 
  out:
     JS_EndRequest(context);
@@ -922,14 +991,15 @@ object_instance_finalize(JSContext *context,
     if (priv == NULL)
         return; /* we are the prototype, not a real instance, so constructor never called */
 
-    TRACE(GJS_OBJECT_PROXY_FINALIZE(priv, priv->gobj, g_base_info_get_namespace ( (GIBaseInfo*) priv->info),
-                                    g_base_info_get_name ( (GIBaseInfo*) priv->info) ));
+    TRACE(GJS_OBJECT_PROXY_FINALIZE(priv, priv->gobj,
+                                    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));
 
     if (priv->gobj) {
         if (G_UNLIKELY (priv->gobj->ref_count <= 0)) {
             g_error("Finalizing proxy for an already freed object of type: %s.%s\n",
-                    g_base_info_get_namespace((GIBaseInfo*) priv->info),
-                    g_base_info_get_name((GIBaseInfo*) priv->info));
+                    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));
         }
         set_js_obj(context, priv->gobj, NULL);
         g_object_remove_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify,
@@ -974,7 +1044,7 @@ gjs_lookup_object_prototype(JSContext    *context,
 {
     JSObject *proto;
 
-    if (!gjs_define_object_class(context, NULL, gtype, NULL, &proto, NULL))
+    if (!gjs_define_object_class(context, NULL, gtype, NULL, &proto))
         return NULL;
     return proto;
 }
@@ -1005,8 +1075,8 @@ real_connect_func(JSContext *context,
     if (priv->gobj == NULL) {
         /* prototype, not an instance. */
         gjs_throw(context, "Can't connect to signals on %s.%s.prototype; only on instances",
-                     g_base_info_get_namespace( (GIBaseInfo*) priv->info),
-                     g_base_info_get_name( (GIBaseInfo*) priv->info));
+                  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;
     }
 
@@ -1098,8 +1168,8 @@ disconnect_func(JSContext *context,
     if (priv->gobj == NULL) {
         /* prototype, not an instance. */
         gjs_throw(context, "Can't disconnect signal on %s.%s.prototype; only on instances",
-                     g_base_info_get_namespace( (GIBaseInfo*) priv->info),
-                     g_base_info_get_name( (GIBaseInfo*) priv->info));
+                  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;
     }
 
@@ -1148,8 +1218,8 @@ emit_func(JSContext *context,
     if (priv->gobj == NULL) {
         /* prototype, not an instance. */
         gjs_throw(context, "Can't emit signal on %s.%s.prototype; only on instances",
-                     g_base_info_get_namespace( (GIBaseInfo*) priv->info),
-                     g_base_info_get_name( (GIBaseInfo*) priv->info));
+                  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;
     }
 
@@ -1262,8 +1332,13 @@ to_string_func(JSContext *context,
         return JS_FALSE; /* wrong class passed in */
     }
 
-    namespace = g_base_info_get_namespace( (GIBaseInfo*) priv->info);
-    name = g_base_info_get_name( (GIBaseInfo*) priv->info);
+    if (priv->info) {
+        namespace = g_base_info_get_namespace( (GIBaseInfo*) priv->info);
+        name = g_base_info_get_name( (GIBaseInfo*) priv->info);
+    } else {
+        namespace = "";
+        name = g_type_name(priv->gtype);
+    }
 
     if (priv->gobj == NULL) {
         strval = g_strdup_printf ("[object prototype of GIName:%s.%s jsobj %p]", namespace, name, obj);
@@ -1356,36 +1431,12 @@ gjs_define_static_methods(JSContext    *context,
     return JS_TRUE;
 }
 
-static GIObjectInfo*
-get_base_info(JSContext *context,
-              GType      gtype)
-{
-    GIBaseInfo *info = NULL;
-
-    while (TRUE) {
-        info = g_irepository_find_by_gtype(g_irepository_get_default(),
-                                           gtype);
-        if (info != NULL)
-            break;
-         if (gtype == G_TYPE_OBJECT)
-            gjs_fatal("No introspection data on GObject - pretty much screwed");
-
-        gjs_debug(GJS_DEBUG_GOBJECT,
-                  "No introspection data on '%s' so trying parent type '%s'",
-                  g_type_name(gtype), g_type_name(g_type_parent(gtype)));
-
-        gtype = g_type_parent(gtype);
-    }
-    return (GIObjectInfo*)info;
-}
-
 JSBool
 gjs_define_object_class(JSContext     *context,
                         JSObject      *in_object,
                         GType          gtype,
                         JSObject     **constructor_p,
-                        JSObject     **prototype_p,
-                        GIObjectInfo **class_info_p)
+                        JSObject     **prototype_p)
 {
     const char *constructor_name;
     JSObject *prototype;
@@ -1394,21 +1445,21 @@ gjs_define_object_class(JSContext     *context,
     jsval value;
     ObjectInstance *priv;
     GIObjectInfo *info = NULL;
-    gboolean has_own_info = TRUE;
+    const char *ns;
 
     g_assert(gtype != G_TYPE_INVALID);
 
     info = (GIObjectInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), gtype);
-    if (!info) {
-        has_own_info = FALSE;
-        info = get_base_info(context, gtype);
-    }
 
     if (!in_object) {
-        in_object = gjs_lookup_namespace_object(context, (GIBaseInfo*) info);
+        if (info)
+            in_object = gjs_lookup_namespace_object(context, (GIBaseInfo*) info);
+        else
+            in_object = gjs_lookup_private_namespace(context);
 
         if (!in_object) {
-            g_base_info_unref((GIBaseInfo*)info);
+            if (info)
+                g_base_info_unref((GIBaseInfo*)info);
             return FALSE;
         }
     }
@@ -1470,7 +1521,7 @@ gjs_define_object_class(JSContext     *context,
      * 'Object' (or whatever the first known ancestor is)
      *
      */
-    if (!has_own_info) {
+    if (!info) {
         constructor_name = g_type_name(gtype);
     } else {
         constructor_name = g_base_info_get_name((GIBaseInfo*) info);
@@ -1498,9 +1549,7 @@ gjs_define_object_class(JSContext     *context,
             if (constructor_p)
                 *constructor_p = constructor;
 
-            if (class_info_p)
-                *class_info_p = info;
-            else
+            if (info)
                 g_base_info_unref((GIBaseInfo*)info);
             return TRUE;
         }
@@ -1514,13 +1563,20 @@ gjs_define_object_class(JSContext     *context,
        parent_proto = gjs_lookup_object_prototype(context, parent_gtype);
     }
 
+    /* This is only used to disambiguate classes in the import global.
+     * We can safely set "unknown" if there is no info, as in that case
+     * the name is globally unique (it's a GType name). */
+    if (info)
+        ns = g_base_info_get_namespace((GIBaseInfo*) info);
+    else
+        ns = "unknown";
     prototype = gjs_init_class_dynamic(context, in_object,
                                        /* parent prototype JSObject* for
                                         * prototype; NULL for
                                         * Object.prototype
                                         */
                                        parent_proto,
-                                       g_base_info_get_namespace( (GIBaseInfo*) info),
+                                       ns,
                                        constructor_name,
                                        &gjs_object_instance_class,
                                        /* constructor for instances (NULL for
@@ -1545,7 +1601,8 @@ gjs_define_object_class(JSContext     *context,
 
     priv = g_slice_new0(ObjectInstance);
     priv->info = info;
-    g_base_info_ref( (GIBaseInfo*) priv->info);
+    if (info)
+        g_base_info_ref( (GIBaseInfo*) priv->info);
     priv->gtype = gtype;
     JS_SetPrivate(context, prototype, priv);
 
@@ -1561,12 +1618,15 @@ gjs_define_object_class(JSContext     *context,
        if (!JSVAL_IS_OBJECT(value)) {
             gjs_throw(context, "Property '%s' does not look like a constructor",
                       constructor_name);
-            g_base_info_unref((GIBaseInfo*)info);
+            if (info)
+                g_base_info_unref((GIBaseInfo*)info);
             return FALSE;
        }
 
        constructor = JSVAL_TO_OBJECT(value);
-       gjs_define_static_methods(context, constructor, gtype, info);
+
+       if (info)
+           gjs_define_static_methods(context, constructor, gtype, info);
     }
 
     value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, gtype));
@@ -1579,9 +1639,7 @@ gjs_define_object_class(JSContext     *context,
     if (constructor_p)
         *constructor_p = constructor;
 
-    if (class_info_p)
-        *class_info_p = info;
-    else
+    if (info)
         g_base_info_unref((GIBaseInfo*)info);
     return TRUE;
 }
@@ -1663,14 +1721,13 @@ gjs_object_from_g_object(JSContext    *context,
     if (obj == NULL) {
         /* We have to create a wrapper */
         JSObject *proto;
-        GIObjectInfo *info;
 
         gjs_debug_marshal(GJS_DEBUG_GOBJECT,
                           "Wrapping %s with JSObject",
                           g_type_name_from_instance((GTypeInstance*) gobj));
 
 
-        if (!gjs_define_object_class(context, NULL, G_TYPE_FROM_INSTANCE(gobj), NULL, &proto, &info))
+        if (!gjs_define_object_class(context, NULL, G_TYPE_FROM_INSTANCE(gobj), NULL, &proto))
             return NULL;
 
         JS_BeginRequest(context);
@@ -1692,8 +1749,6 @@ gjs_object_from_g_object(JSContext    *context,
         /* see the comment in init_object_instance() for this */
         g_object_unref(gobj);
 
-        g_base_info_unref( (GIBaseInfo*) info);
-
         g_assert(peek_js_obj(context, gobj) == obj);
     }
 
@@ -1811,7 +1866,7 @@ gjs_hook_up_vfunc(JSContext *cx,
     JSObject *object;
     JSObject *function;
     ObjectInstance *priv;
-    GType gtype;
+    GType gtype, info_gtype;
     GIObjectInfo *info;
     GIVFuncInfo *vfunc;
     gpointer implementor_vtable;
@@ -1828,6 +1883,18 @@ gjs_hook_up_vfunc(JSContext *cx,
     gtype = priv->gtype;
     info = priv->info;
 
+    /* find the first class that actually has repository information */
+    info_gtype = gtype;
+    while (!info && info_gtype != G_TYPE_OBJECT) {
+        info_gtype = g_type_parent(info_gtype);
+
+        info = g_irepository_find_by_gtype(g_irepository_get_default(), info_gtype);
+    }
+
+    /* If we don't have 'info', we don't have the base class (GObject).
+     * This is awful, so abort now. */
+    g_assert(info != NULL);
+
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
 
     vfunc = find_vfunc_on_parent(info, name);
diff --git a/gi/object.h b/gi/object.h
index e0dc3a4..8a39b5d 100644
--- a/gi/object.h
+++ b/gi/object.h
@@ -36,8 +36,7 @@ JSBool    gjs_define_object_class       (JSContext     *context,
                                          JSObject      *in_object,
                                          GType          gtype,
                                          JSObject     **constructor_p,
-                                         JSObject     **prototype_p,
-                                         GIObjectInfo **class_info_p);
+                                         JSObject     **prototype_p);
 JSObject* gjs_lookup_object_prototype   (JSContext     *context,
                                          GType          gtype);
 JSObject* gjs_object_from_g_object      (JSContext     *context,
diff --git a/gi/repo.c b/gi/repo.c
index 5a34e20..0dc739a 100644
--- a/gi/repo.c
+++ b/gi/repo.c
@@ -43,6 +43,8 @@
 #include <girepository.h>
 #include <string.h>
 
+#define DUMPBIN "_gjs_private"
+
 typedef struct {
     void *dummy;
 
@@ -323,6 +325,8 @@ repo_new(JSContext *context)
 
     g_assert(gjs_object_has_property(context, repo, "versions"));
 
+    JS_DefineObject(context, repo, DUMPBIN, NULL, NULL, JSPROP_PERMANENT);
+
     /* FIXME - hack to make namespaces load, since
      * gobject-introspection does not yet search a path properly.
      */
@@ -469,17 +473,14 @@ gjs_define_info(JSContext  *context,
     case GI_INFO_TYPE_OBJECT:
         {
             GType gtype;
-            GIBaseInfo *info_for_gtype;
             gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)info);
 
             if (g_type_is_a (gtype, G_TYPE_PARAM)) {
                 if (!gjs_define_param_class(context, in_object, NULL))
                     return JS_FALSE;
             } else if (g_type_is_a (gtype, G_TYPE_OBJECT)) {
-                if (!gjs_define_object_class(context, in_object, gtype, NULL, NULL, &info_for_gtype))
+                if (!gjs_define_object_class(context, in_object, gtype, NULL, NULL))
                     return JS_FALSE;
-                g_assert(g_base_info_equal(info, info_for_gtype));
-                g_base_info_unref(info_for_gtype);
             } else {
                 gjs_throw (context,
                            "Unsupported type %s, deriving from fundamental %s",
@@ -521,6 +522,13 @@ gjs_define_info(JSContext  *context,
     return JS_TRUE;
 }
 
+/* Get the "unknown namespace", which should be used for unnamespaced types */
+JSObject*
+gjs_lookup_private_namespace(JSContext *context)
+{
+    return gjs_lookup_namespace_object_by_name(context, DUMPBIN);
+}
+
 /* Get the namespace object that the GIBaseInfo should be inside */
 JSObject*
 gjs_lookup_namespace_object(JSContext  *context,
diff --git a/gi/repo.h b/gi/repo.h
index 8ac71d7..0161e94 100644
--- a/gi/repo.h
+++ b/gi/repo.h
@@ -36,6 +36,7 @@ JSBool      gjs_define_repo                     (JSContext      *context,
                                                  JSObject       *module_obj,
                                                  const char     *name);
 const char* gjs_info_type_name                  (GIInfoType      type);
+JSObject*   gjs_lookup_private_namespace        (JSContext      *context);
 JSObject*   gjs_lookup_namespace_object         (JSContext      *context,
                                                  GIBaseInfo     *info);
 JSObject*   gjs_lookup_namespace_object_by_name (JSContext      *context,



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