[gjs/wip/gdbus-2: 8/13] Rework dynamic class system
- From: Giovanni Campagna <gcampagna src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/gdbus-2: 8/13] Rework dynamic class system
- Date: Fri, 2 Nov 2012 13:10:40 +0000 (UTC)
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]