[gjs/wip/gdbus-2: 8/13] Rework dynamic class system



commit 3e76ce772f60d5a905fb2343cacefb7d62f2659c
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Wed Aug 8 01:02:42 2012 +0200

    Rework dynamic class system
    
    Stop creating multiple JS classes on demand, and instead use the
    same JS class and create multiple constructors with different
    prototypes, but hand-rolling our own version of JS_InitClass, that
    also avoids polluting the global object.
    This means that JS_ConstructObject is no longer usable with dynamic
    classes, use gjs_construct_object_dynamic or JS_New instead.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=679688

 Makefile.am               |    1 +
 gi/boxed.c                |   59 +++-------
 gi/gerror.c               |   51 +++------
 gi/interface.c            |   57 +++------
 gi/object.c               |   68 ++++--------
 gi/param.c                |   59 +++-------
 gi/union.c                |   59 +++-------
 gjs/compat.h              |   21 ++--
 gjs/jsapi-dynamic-class.c |  222 ++++++++++++++++++++++++++++++++++++
 gjs/jsapi-util.c          |  277 ---------------------------------------------
 gjs/jsapi-util.h          |   43 ++------
 11 files changed, 356 insertions(+), 561 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index d532973..d74811a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -108,6 +108,7 @@ libgjs_la_SOURCES =		\
 	gjs/gi.c		\
 	gjs/jsapi-private.cpp	\
 	gjs/jsapi-util.c	\
+	gjs/jsapi-dynamic-class.c \
 	gjs/jsapi-util-array.c	\
 	gjs/jsapi-util-error.c	\
 	gjs/jsapi-util-string.c	\
diff --git a/gi/boxed.c b/gi/boxed.c
index 6c68140..2ca9b78 100644
--- a/gi/boxed.c
+++ b/gi/boxed.c
@@ -59,7 +59,7 @@ static JSBool boxed_set_field_from_value(JSContext   *context,
 
 static struct JSClass gjs_boxed_class;
 
-GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(Boxed, gjs_boxed_class)
+GJS_DEFINE_PRIV_FROM_JS(Boxed, gjs_boxed_class)
 
 static JSBool
 gjs_define_static_methods(JSContext    *context,
@@ -886,7 +886,7 @@ define_boxed_class_fields (JSContext *context,
  * make sure it doesn't get freed.
  */
 static struct JSClass gjs_boxed_class = {
-    NULL, /* dynamic class, no name here */
+    "GObject_Boxed",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_NEW_RESOLVE |
     JSCLASS_NEW_RESOLVE_GETS_START |
@@ -1123,37 +1123,26 @@ gjs_define_boxed_class(JSContext    *context,
         }
     }
 
-    prototype = gjs_init_class_dynamic(context, in_object,
-                                          /* parent prototype JSObject* for
-                                           * prototype; NULL for
-                                           * Object.prototype
-                                           */
-                                          NULL,
-                                          g_base_info_get_namespace( (GIBaseInfo*) info),
-                                          constructor_name,
-                                          &gjs_boxed_class,
-                                          /* constructor for instances (NULL for
-                                           * none - just name the prototype like
-                                           * Math - rarely correct)
-                                           */
-                                          gjs_boxed_constructor,
-                                          /* number of constructor args (less can be passed) */
-                                          1,
-                                          /* props of prototype */
-                                          &gjs_boxed_proto_props[0],
-                                          /* funcs of prototype */
-                                          &gjs_boxed_proto_funcs[0],
-                                          /* props of constructor, MyConstructor.myprop */
-                                          NULL,
-                                          /* funcs of constructor, MyConstructor.myfunc() */
-                                          NULL);
-    if (prototype == NULL) {
+    if (!gjs_init_class_dynamic(context, in_object,
+                                NULL, /* parent prototype */
+                                g_base_info_get_namespace( (GIBaseInfo*) info),
+                                constructor_name,
+                                &gjs_boxed_class,
+                                gjs_boxed_constructor, 1,
+                                /* props of prototype */
+                                &gjs_boxed_proto_props[0],
+                                /* funcs of prototype */
+                                &gjs_boxed_proto_funcs[0],
+                                /* props of constructor, MyConstructor.myprop */
+                                NULL,
+                                /* funcs of constructor, MyConstructor.myfunc() */
+                                NULL,
+                                &prototype,
+                                &constructor)) {
         gjs_log_exception(context, NULL);
         gjs_fatal("Can't init class %s", constructor_name);
     }
 
-    g_assert(gjs_object_has_property(context, in_object, constructor_name));
-
     GJS_INC_COUNTER(boxed);
     priv = g_slice_new0(Boxed);
     priv->info = info;
@@ -1167,18 +1156,6 @@ gjs_define_boxed_class(JSContext    *context,
     priv->can_allocate_directly = struct_is_simple (priv->info);
 
     define_boxed_class_fields (context, priv, prototype);
-
-    constructor = NULL;
-    gjs_object_get_property(context, in_object, constructor_name, &value);
-    if (!JSVAL_IS_VOID(value)) {
-        if (!JSVAL_IS_OBJECT(value)) {
-            gjs_throw(context, "Property '%s' does not look like a constructor",
-                      constructor_name);
-            return JS_FALSE;
-        }
-    }
-
-    constructor = JSVAL_TO_OBJECT(value);
     gjs_define_static_methods (context, constructor, priv->gtype, priv->info);
 
     value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, priv->gtype));
diff --git a/gi/gerror.c b/gi/gerror.c
index 5c035a1..36d7ece 100644
--- a/gi/gerror.c
+++ b/gi/gerror.c
@@ -56,7 +56,7 @@ static struct JSClass gjs_error_class;
 
 static void define_error_properties(JSContext *, JSObject *);
 
-GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(Error, gjs_error_class)
+GJS_DEFINE_PRIV_FROM_JS(Error, gjs_error_class)
 
 GJS_NATIVE_CONSTRUCTOR_DECLARE(error)
 {
@@ -300,7 +300,7 @@ error_constructor_value_of(JSContext *context, uintN argc, jsval *vp)
  * class have.
  */
 static struct JSClass gjs_error_class = {
-    NULL, /* dynamic class, no name here */
+    "GLib_Error",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_NEW_RESOLVE |
     JSCLASS_NEW_RESOLVE_GETS_START,
@@ -438,29 +438,26 @@ gjs_define_error_class(JSContext    *context,
     parent_proto = gjs_lookup_boxed_prototype(context, glib_error_info);
     g_base_info_unref((GIBaseInfo*)glib_error_info);
 
-    prototype = gjs_init_class_dynamic(context, in_object,
-                                          parent_proto,
-                                          g_base_info_get_namespace( (GIBaseInfo*) info),
-                                          constructor_name,
-                                          &gjs_error_class,
-                                          gjs_error_constructor,
-                                          /* number of constructor args (less can be passed) */
-                                          1,
-                                          /* props of prototype */
-                                          &gjs_error_proto_props[0],
-                                          /* funcs of prototype */
-                                          &gjs_error_proto_funcs[0],
-                                          /* props of constructor, MyConstructor.myprop */
-                                          NULL,
-                                          /* funcs of constructor, MyConstructor.myfunc() */
-                                          &gjs_error_constructor_funcs[0]);
-    if (prototype == NULL) {
+    if (!gjs_init_class_dynamic(context, in_object,
+                                parent_proto,
+                                g_base_info_get_namespace( (GIBaseInfo*) info),
+                                constructor_name,
+                                &gjs_error_class,
+                                gjs_error_constructor, 1,
+                                /* props of prototype */
+                                &gjs_error_proto_props[0],
+                                /* funcs of prototype */
+                                &gjs_error_proto_funcs[0],
+                                /* props of constructor, MyConstructor.myprop */
+                                NULL,
+                                /* funcs of constructor, MyConstructor.myfunc() */
+                                &gjs_error_constructor_funcs[0],
+                                &prototype,
+                                &constructor)) {
         gjs_log_exception(context, NULL);
         gjs_fatal("Can't init class %s", constructor_name);
     }
 
-    g_assert(gjs_object_has_property(context, in_object, constructor_name));
-
     GJS_INC_COUNTER(gerror);
     priv = g_slice_new0(Error);
     priv->info = info;
@@ -472,18 +469,6 @@ gjs_define_error_class(JSContext    *context,
     gjs_debug(GJS_DEBUG_GBOXED, "Defined class %s prototype is %p class %p in object %p",
               constructor_name, prototype, JS_GET_CLASS(context, prototype), in_object);
 
-    constructor = NULL;
-    gjs_object_get_property(context, in_object, constructor_name, &value);
-    if (!JSVAL_IS_VOID(value)) {
-        if (!JSVAL_IS_OBJECT(value)) {
-            gjs_throw(context, "Property '%s' does not look like a constructor",
-                      constructor_name);
-            return JS_FALSE;
-        }
-    }
-
-    constructor = JSVAL_TO_OBJECT(value);
-
     gjs_define_enum_values(context, constructor, priv->info);
 
     if (constructor_p)
diff --git a/gi/interface.c b/gi/interface.c
index 7de8839..d451582 100644
--- a/gi/interface.c
+++ b/gi/interface.c
@@ -42,7 +42,7 @@ typedef struct {
 
 static struct JSClass gjs_interface_class;
 
-GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(Interface, gjs_interface_class)
+GJS_DEFINE_PRIV_FROM_JS(Interface, gjs_interface_class)
 
 GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(interface)
 
@@ -143,7 +143,7 @@ interface_new_resolve(JSContext *context,
 }
 
 static struct JSClass gjs_interface_class = {
-    NULL, /* dynamic */
+    "GObject_Interface",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_NEW_RESOLVE |
     JSCLASS_NEW_RESOLVE_GETS_START,
@@ -209,34 +209,24 @@ gjs_define_interface_class(JSContext       *context,
         return JS_TRUE;
     }
 
-    prototype = gjs_init_class_dynamic(context, in_object,
-                                       /* parent prototype JSObject* for
-                                        * prototype; NULL for
-                                        * Object.prototype
-                                        */
-                                       NULL,
-                                       g_base_info_get_namespace((GIBaseInfo*)info),
-                                       constructor_name,
-                                       &gjs_interface_class,
-                                       /* constructor for instances (NULL for
-                                        * none - just name the prototype like
-                                        * Math - rarely correct)
-                                        */
-                                       gjs_interface_constructor,
-                                       /* number of constructor args */
-                                       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() */
-                                       NULL);
-    if (prototype == NULL)
+    if (!gjs_init_class_dynamic(context, in_object,
+                                NULL,
+                                g_base_info_get_namespace((GIBaseInfo*)info),
+                                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() */
+                                NULL,
+                                &prototype,
+                                &constructor)) {
         gjs_fatal("Can't init class %s", constructor_name);
-
-    g_assert(gjs_object_has_property(context, in_object, constructor_name));
+    }
 
     GJS_INC_COUNTER(interface);
     priv = g_slice_new0(Interface);
@@ -245,15 +235,6 @@ gjs_define_interface_class(JSContext       *context,
     g_base_info_ref((GIBaseInfo*)priv->info);
     JS_SetPrivate(context, prototype, priv);
 
-    gjs_object_get_property(context, in_object, constructor_name, &value);
-
-    if (!JSVAL_IS_OBJECT(value)) {
-        gjs_throw(context, "Property '%s' does not look like a constructor",
-                  constructor_name);
-        return FALSE;
-    }
-
-    constructor = JSVAL_TO_OBJECT(value);
     gjs_define_static_methods(context, constructor, priv->gtype, priv->info);
 
     value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, priv->gtype));
diff --git a/gi/object.c b/gi/object.c
index 0b2f114..c43237c 100644
--- a/gi/object.c
+++ b/gi/object.c
@@ -86,7 +86,7 @@ enum {
 
 static struct JSClass gjs_object_instance_class;
 
-GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(ObjectInstance, gjs_object_instance_class)
+GJS_DEFINE_PRIV_FROM_JS(ObjectInstance, gjs_object_instance_class)
 
 static JSObject*       peek_js_obj  (JSContext *context,
                                      GObject   *gobj);
@@ -1581,7 +1581,7 @@ to_string_func(JSContext *context,
 }
 
 static struct JSClass gjs_object_instance_class = {
-    NULL, /* We copy this class struct with multiple names */
+    "GObject_Object",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_NEW_RESOLVE |
     JSCLASS_NEW_RESOLVE_GETS_START |
@@ -1809,34 +1809,23 @@ gjs_define_object_class(JSContext     *context,
         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,
-                                       ns,
-                                       constructor_name,
-                                       &gjs_object_instance_class,
-                                       /* constructor for instances (NULL for
-                                        * none - just name the prototype like
-                                        * Math - rarely correct)
-                                        */
-                                       gjs_object_instance_constructor,
-                                       /* number of constructor args */
-                                       0,
-                                       /* props of prototype */
-                                       parent_proto ? NULL : &gjs_object_instance_proto_props[0],
-                                       /* funcs of prototype */
-                                       parent_proto ? NULL : &gjs_object_instance_proto_funcs[0],
-                                       /* props of constructor, MyConstructor.myprop */
-                                       NULL,
-                                       /* funcs of constructor, MyConstructor.myfunc() */
-                                       NULL);
-    if (prototype == NULL)
+    if (!gjs_init_class_dynamic(context, in_object,
+                                parent_proto,
+                                ns, constructor_name,
+                                &gjs_object_instance_class,
+                                gjs_object_instance_constructor, 0,
+                                /* props of prototype */
+                                parent_proto ? NULL : &gjs_object_instance_proto_props[0],
+                                /* funcs of prototype */
+                                parent_proto ? NULL : &gjs_object_instance_proto_funcs[0],
+                                /* props of constructor, MyConstructor.myprop */
+                                NULL,
+                                /* funcs of constructor, MyConstructor.myfunc() */
+                                NULL,
+                                &prototype,
+                                &constructor)) {
         gjs_fatal("Can't init class %s", constructor_name);
-
-    g_assert(gjs_object_has_property(context, in_object, constructor_name));
+    }
 
     GJS_INC_COUNTER(object);
     priv = g_slice_new0(ObjectInstance);
@@ -1850,25 +1839,8 @@ gjs_define_object_class(JSContext     *context,
     gjs_debug(GJS_DEBUG_GOBJECT, "Defined class %s prototype %p class %p in object %p",
               constructor_name, prototype, JS_GET_CLASS(context, prototype), in_object);
 
-    /* Now get the constructor we defined in
-     * gjs_init_class_dynamic
-     */
-    gjs_object_get_property(context, in_object, constructor_name, &value);
-    constructor = NULL;
-    if (!JSVAL_IS_VOID(value)) {
-       if (!JSVAL_IS_OBJECT(value)) {
-            gjs_throw(context, "Property '%s' does not look like a constructor",
-                      constructor_name);
-            if (info)
-                g_base_info_unref((GIBaseInfo*)info);
-            return FALSE;
-       }
-
-       constructor = JSVAL_TO_OBJECT(value);
-
-       if (info)
-           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));
     JS_DefineProperty(context, constructor, "$gtype", value,
diff --git a/gi/param.c b/gi/param.c
index 0829aad..39cef90 100644
--- a/gi/param.c
+++ b/gi/param.c
@@ -43,7 +43,7 @@ typedef struct {
 
 static struct JSClass gjs_param_class;
 
-GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(Param, gjs_param_class)
+GJS_DEFINE_PRIV_FROM_JS(Param, gjs_param_class)
 
 static GIFieldInfo *
 find_field_info(GIObjectInfo *info,
@@ -430,7 +430,7 @@ param_new_internal(JSContext *cx,
  * class have.
  */
 static struct JSClass gjs_param_class = {
-    NULL, /* dynamic */
+    "GObject_ParamSpec",
     JSCLASS_HAS_PRIVATE,
     JS_PropertyStub,
     JS_PropertyStub,
@@ -512,48 +512,25 @@ gjs_define_param_class(JSContext    *context,
         return JS_TRUE;
     }
 
-    /* we could really just use JS_InitClass for this since we have one class instead of
-     * N classes on-demand. But, this deals with namespacing and such for us.
-     */
-    prototype = gjs_init_class_dynamic(context, in_object,
-                                          /* parent prototype JSObject* for
-                                           * prototype; NULL for
-                                           * Object.prototype
-                                           */
-                                          NULL,
-                                          "GObject",
-                                          constructor_name,
-                                          &gjs_param_class,
-                                          /* constructor for instances (NULL for
-                                           * none - just name the prototype like
-                                           * Math - rarely correct)
-                                           */
-                                          gjs_param_constructor,
-                                          /* number of constructor args */
-                                          0,
-                                          /* props of prototype */
-                                          &gjs_param_proto_props[0],
-                                          /* funcs of prototype */
-                                          &gjs_param_proto_funcs[0],
-                                          /* props of constructor, MyConstructor.myprop */
-                                          NULL,
-                                          /* funcs of constructor, MyConstructor.myfunc() */
-                                          gjs_param_constructor_funcs);
-    if (prototype == NULL)
+    if (!gjs_init_class_dynamic(context, in_object,
+                                NULL,
+                                "GObject",
+                                constructor_name,
+                                &gjs_param_class,
+                                gjs_param_constructor, 0,
+                                /* props of prototype */
+                                &gjs_param_proto_props[0],
+                                /* funcs of prototype */
+                                &gjs_param_proto_funcs[0],
+                                /* props of constructor, MyConstructor.myprop */
+                                NULL,
+                                /* funcs of constructor, MyConstructor.myfunc() */
+                                gjs_param_constructor_funcs,
+                                &prototype,
+                                &constructor)) {
         gjs_fatal("Can't init class %s", constructor_name);
-
-    constructor = NULL;
-    gjs_object_get_property(context, in_object, constructor_name, &value);
-    if (value != JSVAL_VOID) {
-        if (!JSVAL_IS_OBJECT(value)) {
-            gjs_throw(context, "Property '%s' does not look like a constructor",
-                      constructor_name);
-            return JS_FALSE;
-        }
     }
 
-    constructor = JSVAL_TO_OBJECT(value);
-
     value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, G_TYPE_PARAM));
     JS_DefineProperty(context, constructor, "$gtype", value,
                       NULL, NULL, JSPROP_PERMANENT);
diff --git a/gi/union.c b/gi/union.c
index 098070a..dcbe806 100644
--- a/gi/union.c
+++ b/gi/union.c
@@ -49,7 +49,7 @@ typedef struct {
 
 static struct JSClass gjs_union_class;
 
-GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(Union, gjs_union_class)
+GJS_DEFINE_PRIV_FROM_JS(Union, gjs_union_class)
 
 /*
  * Like JSResolveOp, but flags provide contextual information as follows:
@@ -286,7 +286,7 @@ union_finalize(JSContext *context,
  * class have.
  */
 static struct JSClass gjs_union_class = {
-    NULL, /* dynamic class, no name here */
+    "GObject_Union",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_NEW_RESOLVE |
     JSCLASS_NEW_RESOLVE_GETS_START,
@@ -415,34 +415,24 @@ gjs_define_union_class(JSContext    *context,
         }
     }
 
-    prototype = gjs_init_class_dynamic(context, in_object,
-                                       /* parent prototype JSObject* for
-                                        * prototype; NULL for
-                                        * Object.prototype
-                                        */
-                                       NULL,
-                                       g_base_info_get_namespace( (GIBaseInfo*) info),
-                                       constructor_name,
-                                       &gjs_union_class,
-                                       /* constructor for instances (NULL for
-                                        * none - just name the prototype like
-                                        * Math - rarely correct)
-                                        */
-                                       gjs_union_constructor,
-                                       /* number of constructor args */
-                                       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);
-    if (prototype == NULL)
+    if (!gjs_init_class_dynamic(context, in_object,
+                                NULL,
+                                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)) {
         gjs_fatal("Can't init class %s", constructor_name);
-
-    g_assert(gjs_object_has_property(context, in_object, constructor_name));
+    }
 
     GJS_INC_COUNTER(boxed);
     priv = g_slice_new0(Union);
@@ -454,17 +444,6 @@ gjs_define_union_class(JSContext    *context,
     gjs_debug(GJS_DEBUG_GBOXED, "Defined class %s prototype is %p class %p in object %p",
               constructor_name, prototype, JS_GET_CLASS(context, prototype), in_object);
 
-    gjs_object_get_property(context, in_object, constructor_name, &value);
-    if (!JSVAL_IS_VOID(value)) {
-        if (!JSVAL_IS_OBJECT(value)) {
-            gjs_throw(context, "Property '%s' does not look like a constructor",
-                      constructor_name);
-            return JS_FALSE;
-        }
-    }
-
-    constructor = JSVAL_TO_OBJECT(value);
-    
     value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, gtype));
     JS_DefineProperty(context, constructor, "$gtype", value,
                       NULL, NULL, JSPROP_PERMANENT);
diff --git a/gjs/compat.h b/gjs/compat.h
index 96f2761..d0667b9 100644
--- a/gjs/compat.h
+++ b/gjs/compat.h
@@ -32,6 +32,8 @@
 #include <jsapi.h>
 #include <glib.h>
 
+#include <gjs/jsapi-util.h>
+
 G_BEGIN_DECLS
 
 /* This file inspects jsapi.h and attempts to provide a compatibility shim.
@@ -61,18 +63,17 @@ gjs_##name##_constructor(JSContext  *context,           \
  * GJS_NATIVE_CONSTRUCTOR_PRELUDE:
  * Call after the initial variable declaration.
  */
-#define GJS_NATIVE_CONSTRUCTOR_PRELUDE(name)                                         \
-    {                                                                                \
-        if (!JS_IsConstructing(context, vp)) {  \
-            gjs_throw_constructor_error(context);                                    \
-            return JS_FALSE;                                                         \
-        }                                                                            \
-        object = JS_NewObjectForConstructor(context, vp);                            \
-        if (object == NULL)                                                          \
-            return JS_FALSE;                                                         \
+#define GJS_NATIVE_CONSTRUCTOR_PRELUDE(name)                            \
+    {                                                                   \
+        if (!JS_IsConstructing(context, vp)) {                          \
+            gjs_throw_constructor_error(context);                       \
+            return JS_FALSE;                                            \
+        }                                                               \
+        object = gjs_new_object_for_constructor(context, &gjs_##name##_class, vp); \
+        if (object == NULL)                                             \
+            return JS_FALSE;                                            \
     }
 
-
 /**
  * GJS_NATIVE_CONSTRUCTOR_FINISH:
  * Call this at the end of a constructor when it's completed
diff --git a/gjs/jsapi-dynamic-class.c b/gjs/jsapi-dynamic-class.c
new file mode 100644
index 0000000..c6b8d36
--- /dev/null
+++ b/gjs/jsapi-dynamic-class.c
@@ -0,0 +1,222 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2008  litl, LLC
+ *               2012 Giovanni Campagna <scampa giovanni gmail com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <util/log.h>
+#include <util/glib.h>
+#include <util/misc.h>
+
+#include "jsapi-util.h"
+#include "compat.h"
+#include "jsapi-private.h"
+
+#include <string.h>
+#include <math.h>
+
+/*
+ * JS 1.8.5 has JS_NewObjectForConstructor, but it attempts
+ * to retrieve the JSClass from private fields in the constructor function,
+ * which fails for our "dynamic classes".
+ * This is the version included in SpiderMonkey 1.9 and later, to be
+ * used until we rebase on a newer libmozjs.
+ */
+JSObject *
+gjs_new_object_for_constructor(JSContext *context,
+                               JSClass   *clasp,
+                               jsval     *vp)
+{
+    jsval     callee;
+    JSObject *parent;
+    jsval     prototype;
+
+    callee = JS_CALLEE(context, vp);
+    parent = JS_GetParent(context, JSVAL_TO_OBJECT (callee));
+
+    if (!gjs_object_get_property(context, JSVAL_TO_OBJECT (callee), "prototype",
+                                 &prototype))
+        return NULL;
+
+    return JS_NewObjectWithGivenProto(context, clasp,
+                                      JSVAL_TO_OBJECT(prototype), parent);
+}
+
+JSBool
+gjs_init_class_dynamic(JSContext       *context,
+                       JSObject        *in_object,
+                       JSObject        *parent_proto,
+                       const char      *ns_name,
+                       const char      *class_name,
+                       JSClass         *clasp,
+                       JSNative         constructor_native,
+                       uintN            nargs,
+                       JSPropertySpec  *proto_ps,
+                       JSFunctionSpec  *proto_fs,
+                       JSPropertySpec  *static_ps,
+                       JSFunctionSpec  *static_fs,
+                       JSObject       **prototype_p,
+                       JSObject       **constructor_p)
+{
+    JSObject *global;
+    /* Force these variables on the stack, so the conservative GC will
+       find them */
+    JSObject * volatile prototype;
+    JSObject * volatile constructor;
+    JSFunction * volatile constructor_fun;
+    char *full_function_name = NULL;
+    JSBool res = JS_FALSE;
+
+    /* Without a name, JS_NewObject fails */
+    g_assert (clasp->name != NULL);
+
+    /* gjs_init_class_dynamic only makes sense for instantiable classes,
+       use JS_InitClass for static classes like Math */
+    g_assert (constructor_native != NULL);
+
+    JS_BeginRequest(context);
+
+    global = gjs_get_import_global(context);
+
+    /* Class initalization consists of three parts:
+       - building a prototype
+       - defining prototype properties and functions
+       - building a constructor and definining it on the right object
+       - defining constructor properties and functions
+       - linking the constructor and the prototype, so that
+         JS_NewObjectForConstructor can find it
+    */
+
+    /*
+     * JS_NewObject will try to search for clasp prototype in the global
+     * object if parent_proto is NULL, which is wrong, but it's not
+     * a problem because it will fallback to Object.prototype if the clasp's
+     * constructor is not found (and it won't be found, because we never call
+     * JS_InitClass).
+     */
+    prototype = JS_NewObject(context, clasp, parent_proto, global);
+    if (!prototype)
+        goto out;
+
+    if (proto_ps && !JS_DefineProperties(context, prototype, proto_ps))
+        goto out;
+    if (proto_fs && !JS_DefineFunctions(context, prototype, proto_fs))
+        goto out;
+
+    full_function_name = g_strdup_printf("%s_%s", ns_name, class_name);
+    constructor_fun = JS_NewFunction(context, constructor_native, nargs, JSFUN_CONSTRUCTOR,
+                                     global, full_function_name);
+    if (!constructor_fun)
+        goto out;
+
+    constructor = JS_GetFunctionObject(constructor_fun);
+
+    if (static_ps && !JS_DefineProperties(context, constructor, static_ps))
+        goto out;
+    if (static_fs && !JS_DefineFunctions(context, constructor, static_fs))
+        goto out;
+
+    if (!JS_DefineProperty(context, constructor, "prototype", OBJECT_TO_JSVAL(prototype),
+                           JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY))
+        goto out;
+    if (!JS_DefineProperty(context, prototype, "constructor", OBJECT_TO_JSVAL(constructor),
+                           JS_PropertyStub, JS_StrictPropertyStub, 0))
+        goto out;
+
+    /* The constructor defined by JS_InitClass has no property attributes, but this
+       is a more useful default for gjs */
+    if (!JS_DefineProperty(context, in_object, class_name, OBJECT_TO_JSVAL(constructor),
+                           JS_PropertyStub, JS_StrictPropertyStub, GJS_MODULE_PROP_FLAGS))
+        goto out;
+
+    if (constructor_p)
+        *constructor_p = constructor;
+    if (prototype_p)
+        *prototype_p = prototype;
+
+    res = JS_TRUE;
+
+    prototype = NULL;
+    constructor_fun = NULL;
+    constructor = NULL;
+
+ out:
+    JS_EndRequest(context);
+    g_free(full_function_name);
+
+    return res;
+}
+
+static const char*
+format_dynamic_class_name (const char *name)
+{
+    if (g_str_has_prefix(name, "_private_"))
+        return name + strlen("_private_");
+    else
+        return name;
+}
+
+JSBool
+gjs_typecheck_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);
+
+            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*
+gjs_construct_object_dynamic(JSContext      *context,
+                             JSObject       *proto,
+                             uintN           argc,
+                             jsval          *argv)
+{
+    JSObject *constructor;
+    JSObject *result = NULL;
+    jsval value;
+
+    JS_BeginRequest(context);
+
+    if (!gjs_object_require_property(context, proto, "prototype",
+                                     "constructor", &value))
+        goto out;
+
+    constructor = JSVAL_TO_OBJECT(value);
+    result = JS_New(context, constructor, argc, argv);
+
+ out:
+    JS_EndRequest(context);
+    return result;
+}
diff --git a/gjs/jsapi-util.c b/gjs/jsapi-util.c
index 394ac3c..a84c4e3 100644
--- a/gjs/jsapi-util.c
+++ b/gjs/jsapi-util.c
@@ -47,8 +47,6 @@ typedef struct {
 } ContextFrame;
 
 typedef struct {
-    GHashTable *dynamic_classes;
-
     JSObject *import_global;
 
     JSContext *default_context;
@@ -58,13 +56,7 @@ typedef struct {
     GSList *context_stack;
 } RuntimeData;
 
-typedef struct {
-    JSClass base;
-    JSClass *static_class;
-} DynamicJSClass;
-
 static RuntimeData* get_data_from_runtime(JSRuntime *runtime);
-static RuntimeData* get_data_from_context(JSContext *context);
 
 /**
  * gjs_get_import_global:
@@ -288,7 +280,6 @@ gjs_runtime_init(JSRuntime *runtime)
         gjs_fatal("JSRuntime already initialized or private data in use by someone else");
 
     rd = g_slice_new0(RuntimeData);
-    rd->dynamic_classes = g_hash_table_new(g_direct_hash, g_direct_equal);
     JS_SetRuntimePrivate(runtime, rd);
 }
 
@@ -309,8 +300,6 @@ void
 gjs_runtime_destroy(JSRuntime *runtime)
 {
     RuntimeData *rd;
-    void *key;
-    void *value;
 
     rd = JS_GetRuntimePrivate(runtime);
     if (rd->context_stack != NULL || rd->current_frame.depth != 0)
@@ -324,18 +313,6 @@ gjs_runtime_destroy(JSRuntime *runtime)
     gjs_debug(GJS_DEBUG_CONTEXT,
               "Destroying any remaining dataset items on runtime");
 
-    while (gjs_g_hash_table_remove_one(rd->dynamic_classes, &key, &value)) {
-        JSClass *clasp = value;
-
-        gjs_debug(GJS_DEBUG_GREPO,
-                  "Finalizing dynamic class '%s'",
-                  clasp->name);
-
-        g_free( (char*) clasp->name); /* we know we malloc'd the char* even though it's const */
-        g_slice_free(DynamicJSClass, (DynamicJSClass*) clasp);
-    }
-
-    g_hash_table_destroy(rd->dynamic_classes);
     g_slice_free(RuntimeData, rd);
 }
 
@@ -351,12 +328,6 @@ get_data_from_runtime(JSRuntime *runtime)
     return rd;
 }
 
-static RuntimeData*
-get_data_from_context(JSContext *context)
-{
-    return get_data_from_runtime(JS_GetRuntime(context));
-}
-
 /* Checks whether an object has a property; unlike JS_GetProperty(),
  * never sets an exception. Treats a property with a value of JSVAL_VOID
  * the same as an absent property and returns false in both cases.
@@ -445,121 +416,6 @@ gjs_object_require_property(JSContext       *context,
     }
 }
 
-JSObject*
-gjs_init_class_dynamic(JSContext      *context,
-                       JSObject       *in_object,
-                       JSObject       *parent_proto,
-                       const char     *ns_name,
-                       const char     *class_name,
-                       JSClass        *clasp,
-                       JSNative        constructor,
-                       uintN           nargs,
-                       JSPropertySpec *ps,
-                       JSFunctionSpec *fs,
-                       JSPropertySpec *static_ps,
-                       JSFunctionSpec *static_fs)
-{
-    jsval value;
-    char *private_name;
-    JSObject *global;
-    JSObject *prototype;
-
-    if (clasp->name != NULL) {
-        g_warning("Dynamic class should not have a name in the JSClass struct");
-        return NULL;
-    }
-
-    JS_BeginRequest(context);
-
-    /* We use a special "fake" global object to store our constructors
-     * in for future use. Using the actual global object of the context would
-     * result in different contexts having different class definitions for
-     * the same GObject class; since the proxies are shared between all
-     * contexts, this would produce confusing results.
-     */
-    global = gjs_get_import_global(context);
-
-    /* JS_InitClass() wants to define the constructor in the global object, so
-     * we give it a private and namespaced name... passing in the namespace
-     * object instead of global object seems to break JS_ConstructObject()
-     * which then can't find the constructor for the class. I am probably
-     * missing something.
-     */
-    private_name = g_strdup_printf("_private_%s_%s", ns_name, class_name);
-
-    prototype = NULL;
-    if (gjs_object_get_property(context, global,
-                                private_name, &value) &&
-        JSVAL_IS_OBJECT(value)) {
-        jsval proto_val;
-
-        g_free(private_name); /* don't need it anymore */
-
-        if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(value), NULL,
-                                         "prototype", &proto_val) ||
-            !JSVAL_IS_OBJECT(proto_val)) {
-            gjs_throw(context, "prototype was not defined or not an object?");
-            goto error;
-        }
-        prototype = JSVAL_TO_OBJECT(proto_val);
-    } else {
-        DynamicJSClass *class_copy;
-        RuntimeData *rd;
-
-        rd = get_data_from_context(context);
-
-        class_copy = g_slice_new0(DynamicJSClass);
-        class_copy->base = *clasp;
-
-        class_copy->base.name = private_name; /* Pass ownership of memory */
-        class_copy->static_class = clasp;
-
-        /* record the allocated class to be destroyed with the runtime and so
-         * we can do an IS_DYNAMIC_CLASS check
-         */
-        g_hash_table_replace(rd->dynamic_classes,
-                             class_copy, class_copy);
-
-        gjs_debug(GJS_DEBUG_GREPO,
-                  "Initializing dynamic class %s %p",
-                  class_name, class_copy);
-
-        prototype = JS_InitClass(context, global,
-                                 parent_proto, &class_copy->base,
-                                 constructor, nargs,
-                                 ps, fs,
-                                 static_ps, static_fs);
-        if (prototype == NULL)
-            goto error;
-
-        /* Retrieve the property again so we can define it in
-         * in_object
-         */
-        if (!gjs_object_require_property(context, global, NULL,
-                                         class_copy->base.name, &value))
-            goto error;
-    }
-    g_assert(!JSVAL_IS_VOID(value));
-    g_assert(prototype != NULL);
-
-    /* Now manually define our constructor with a sane name, in the
-     * namespace object.
-     */
-    if (!JS_DefineProperty(context, in_object,
-                           class_name,
-                           value,
-                           NULL, NULL,
-                           GJS_MODULE_PROP_FLAGS))
-        goto error;
-
-    JS_EndRequest(context);
-    return prototype;
-
- error:
-    JS_EndRequest(context);
-    return NULL;
-}
-
 void
 gjs_throw_constructor_error(JSContext *context)
 {
@@ -589,139 +445,6 @@ gjs_throw_abstract_constructor_error(JSContext *context,
     gjs_throw(context, "You cannot construct new instances of '%s'", name);
 }
 
-static const char*
-format_dynamic_class_name (const char *name)
-{
-    if (g_str_has_prefix(name, "_private_"))
-        return name + strlen("_private_");
-    else
-        return name;
-}
-
-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);
-
-            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;
-}
-
-JSBool
-gjs_typecheck_dynamic_instance(JSContext *context,
-                               JSObject  *obj,
-                               JSClass   *static_clasp,
-                               JSBool     throw)
-{
-    RuntimeData *rd;
-    JSClass *obj_class;
-    gboolean wrong = FALSE;
-
-    obj_class = JS_GET_CLASS(context, obj);
-    g_assert(obj_class != NULL);
-
-    JS_BeginRequest(context);
-
-    obj_class = JS_GET_CLASS(context, obj);
-    g_assert(obj_class != NULL);
-
-    rd = get_data_from_context(context);
-    g_assert(rd != NULL);
-
-    /* Check that it's safe to cast to DynamicJSClass */
-    if (g_hash_table_lookup(rd->dynamic_classes, obj_class) == NULL) {
-        wrong = TRUE;
-        goto out;
-    }
-
-    if (static_clasp != ((DynamicJSClass*) obj_class)->static_class) {
-        wrong = TRUE;
-        goto out;
-    }
-
- out:
-    JS_EndRequest(context);
-
-    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*
-gjs_construct_object_dynamic(JSContext      *context,
-                             JSObject       *proto,
-                             uintN           argc,
-                             jsval          *argv)
-{
-    RuntimeData *rd;
-    JSClass *proto_class;
-    JSObject *global;
-    JSObject *result;
-
-    JS_BeginRequest(context);
-
-    /* We use the "import global" rather than the global object for the current
-     * object so that we fine the constructors we stored there in
-     * js_init_class_dynamic.
-     */
-    global = gjs_get_import_global(context);
-
-    proto_class = JS_GET_CLASS(context, proto);
-
-    rd = get_data_from_context(context);
-
-    /* Check that it's safe to cast to DynamicJSClass */
-    if (g_hash_table_lookup(rd->dynamic_classes, proto_class) == NULL) {
-        gjs_throw(context, "Prototype is not for a dynamically-registered class");
-        goto error;
-    }
-
-    gjs_debug_lifecycle(GJS_DEBUG_GREPO,
-                        "Constructing instance of dynamic class %s %p from proto %p",
-                        proto_class->name, proto_class, proto);
-
-    /* Passing in the import global as 'parent' results in it being the global object
-     * used for looking up the constructor for the object. It also results in
-     * it being stored as the parent object of the newly constructed object.
-     * (Not necessarily sensible, but for something like creating the proxy object
-     * for a GObject more sensible than using the global object of the current context.)
-     */
-    if (argc > 0)
-        result = JS_ConstructObjectWithArguments(context, proto_class, proto, global, argc, argv);
-    else
-        result = JS_ConstructObject(context, proto_class, proto, global);
-
-    if (!result)
-        goto error;
-
-    JS_EndRequest(context);
-    return result;
-
- error:
-    JS_EndRequest(context);
-    return NULL;
-}
-
 JSObject*
 gjs_define_string_array(JSContext   *context,
                         JSObject    *in_object,
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 410fa40..34be2e0 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -74,7 +74,7 @@ typedef struct GjsRootedArray GjsRootedArray;
                       JSObject  *object,                                \
                       JSBool     throw)                                 \
     {                                                                   \
-        return gjs_typecheck_static_instance(context, object, &class, throw); \
+        return gjs_typecheck_instance(context, object, &class, throw);  \
     }                                                                   \
     static inline type*                                                 \
     priv_from_js(JSContext *context,                                    \
@@ -93,31 +93,6 @@ typedef struct GjsRootedArray GjsRootedArray;
         return JS_TRUE;                                                 \
     }
 
-#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;                                                 \
-    }
-
 /**
  * GJS_DEFINE_PROTO:
  * @tn: The name of the prototype, as a string
@@ -226,7 +201,11 @@ gboolean    gjs_object_require_property      (JSContext       *context,
                                               const char      *obj_description,
                                               const char      *property_name,
                                               jsval           *value_p);
-JSObject *  gjs_init_class_dynamic           (JSContext       *context,
+
+JSObject   *gjs_new_object_for_constructor   (JSContext       *context,
+                                              JSClass         *clasp,
+                                              jsval           *vp);
+JSBool      gjs_init_class_dynamic           (JSContext       *context,
                                               JSObject        *in_object,
                                               JSObject        *parent_proto,
                                               const char      *ns_name,
@@ -237,16 +216,14 @@ JSObject *  gjs_init_class_dynamic           (JSContext       *context,
                                               JSPropertySpec  *ps,
                                               JSFunctionSpec  *fs,
                                               JSPropertySpec  *static_ps,
-                                              JSFunctionSpec  *static_fs);
+                                              JSFunctionSpec  *static_fs,
+                                              JSObject       **constructor_p,
+                                              JSObject       **prototype_p);
 void gjs_throw_constructor_error             (JSContext       *context);
 void gjs_throw_abstract_constructor_error    (JSContext       *context,
                                               jsval           *vp);
 
-JSBool gjs_typecheck_static_instance          (JSContext  *context,
-                                               JSObject   *obj,
-                                               JSClass    *static_clasp,
-                                               JSBool      _throw);
-JSBool gjs_typecheck_dynamic_instance         (JSContext  *context,
+JSBool gjs_typecheck_instance                 (JSContext  *context,
                                                JSObject   *obj,
                                                JSClass    *static_clasp,
                                                JSBool      _throw);



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