[gjs/wip/ptomato/mozjs52: 6/38] js: Refactor global object creation
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/ptomato/mozjs52: 6/38] js: Refactor global object creation
- Date: Wed, 28 Jun 2017 00:02:39 +0000 (UTC)
commit 3e675f97fe54bf4c967ecfdb02da99250bab34df
Author: Philip Chimento <philip chimento gmail com>
Date: Sun Jun 25 15:31:30 2017 -0700
js: Refactor global object creation
In order to more easily create global objects, we refactor code that
deals with them into gjs/global.cpp and gjs/global.h. Previously global
objects were set up partly in gjs_context_constructed() and partly in
gjs_init_context_standard(); we disentangle that code and move everything
dealing with setting up the GjsContext into gjs_context_constructed(),
while global object setup moves to gjs_create_global_object().
Since global objects all share the same root importer, we must split the
setup into two. Creating the root importer is the responsibility of
gjs_context_constructed(), and it requires that the first global has been
created. After creating the root importer, the global object is finished
with gjs_define_global_properties().
In the case of global objects beyond the first, such as the global object
for the coverage compartment, gjs_define_global_properties() will also
wrap the root importer in a cross-compartment wrapper so that the new
global can access it.
https://bugzilla.gnome.org/show_bug.cgi?id=781429
gjs-srcs.mk | 2 +
gjs/context.cpp | 270 +++++-------------------------
gjs/coverage.cpp | 75 +--------
gjs/global.cpp | 352 +++++++++++++++++++++++++++++++++++++++
gjs/global.h | 72 ++++++++
gjs/importer.cpp | 79 ++-------
gjs/importer.h | 18 +--
gjs/jsapi-class.h | 1 +
gjs/jsapi-constructor-proxy.cpp | 5 +-
gjs/jsapi-constructor-proxy.h | 3 +-
gjs/jsapi-util.cpp | 95 -----------
gjs/jsapi-util.h | 34 ----
12 files changed, 493 insertions(+), 513 deletions(-)
---
diff --git a/gjs-srcs.mk b/gjs-srcs.mk
index 503c5c9..b7c1efd 100644
--- a/gjs-srcs.mk
+++ b/gjs-srcs.mk
@@ -54,6 +54,8 @@ gjs_srcs = \
gjs/context-private.h \
gjs/coverage-internal.h \
gjs/coverage.cpp \
+ gjs/global.cpp \
+ gjs/global.h \
gjs/importer.cpp \
gjs/importer.h \
gjs/jsapi-class.h \
diff --git a/gjs/context.cpp b/gjs/context.cpp
index ca8d43d..d45c3f4 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -28,8 +28,8 @@
#include <gio/gio.h>
#include "context-private.h"
+#include "global.h"
#include "importer.h"
-#include "jsapi-constructor-proxy.h"
#include "jsapi-private.h"
#include "jsapi-util.h"
#include "jsapi-wrapper.h"
@@ -113,157 +113,6 @@ enum {
static GMutex contexts_lock;
static GList *all_contexts = NULL;
-static bool
-gjs_log(JSContext *context,
- unsigned argc,
- JS::Value *vp)
-{
- JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
-
- if (argc != 1) {
- gjs_throw(context, "Must pass a single argument to log()");
- return false;
- }
-
- JS_BeginRequest(context);
-
- /* JS::ToString might throw, in which case we will only log that the value
- * could not be converted to string */
- JS::AutoSaveExceptionState exc_state(context);
- JS::RootedString jstr(context, JS::ToString(context, argv[0]));
- exc_state.restore();
-
- if (jstr == NULL) {
- g_message("JS LOG: <cannot convert value to string>");
- JS_EndRequest(context);
- return true;
- }
-
- GjsAutoJSChar s(context);
- if (!gjs_string_to_utf8(context, JS::StringValue(jstr), &s)) {
- JS_EndRequest(context);
- return false;
- }
- g_message("JS LOG: %s", s.get());
-
- JS_EndRequest(context);
- argv.rval().setUndefined();
- return true;
-}
-
-static bool
-gjs_log_error(JSContext *context,
- unsigned argc,
- JS::Value *vp)
-{
- JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
-
- if ((argc != 1 && argc != 2) || !argv[0].isObject()) {
- gjs_throw(context, "Must pass an exception and optionally a message to logError()");
- return false;
- }
-
- JS_BeginRequest(context);
-
- JS::RootedString jstr(context);
-
- if (argc == 2) {
- /* JS::ToString might throw, in which case we will only log that the
- * value could be converted to string */
- JS::AutoSaveExceptionState exc_state(context);
- jstr = JS::ToString(context, argv[1]);
- exc_state.restore();
- }
-
- gjs_log_exception_full(context, argv[0], jstr);
-
- JS_EndRequest(context);
- argv.rval().setUndefined();
- return true;
-}
-
-static bool
-gjs_print_parse_args(JSContext *context,
- JS::CallArgs &argv,
- char **buffer)
-{
- GString *str;
- guint n;
-
- JS_BeginRequest(context);
-
- str = g_string_new("");
- for (n = 0; n < argv.length(); ++n) {
- /* JS::ToString might throw, in which case we will only log that the
- * value could not be converted to string */
- JS::AutoSaveExceptionState exc_state(context);
- JS::RootedString jstr(context, JS::ToString(context, argv[n]));
- exc_state.restore();
-
- if (jstr != NULL) {
- GjsAutoJSChar s(context);
- if (!gjs_string_to_utf8(context, JS::StringValue(jstr), &s)) {
- JS_EndRequest(context);
- g_string_free(str, true);
- return false;
- }
-
- g_string_append(str, s);
- if (n < (argv.length()-1))
- g_string_append_c(str, ' ');
- } else {
- JS_EndRequest(context);
- *buffer = g_string_free(str, true);
- if (!*buffer)
- *buffer = g_strdup("<invalid string>");
- return true;
- }
-
- }
- *buffer = g_string_free(str, false);
-
- JS_EndRequest(context);
- return true;
-}
-
-static bool
-gjs_print(JSContext *context,
- unsigned argc,
- JS::Value *vp)
-{
- JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
- char *buffer;
-
- if (!gjs_print_parse_args(context, argv, &buffer)) {
- return false;
- }
-
- g_print("%s\n", buffer);
- g_free(buffer);
-
- argv.rval().setUndefined();
- return true;
-}
-
-static bool
-gjs_printerr(JSContext *context,
- unsigned argc,
- JS::Value *vp)
-{
- JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
- char *buffer;
-
- if (!gjs_print_parse_args(context, argv, &buffer)) {
- return false;
- }
-
- g_printerr("%s\n", buffer);
- g_free(buffer);
-
- argv.rval().setUndefined();
- return true;
-}
-
static void
on_garbage_collect(JSRuntime *rt,
JSGCStatus status,
@@ -277,45 +126,6 @@ on_garbage_collect(JSRuntime *rt,
gjs_object_clear_toggles();
}
-/* Requires request, does not throw error */
-static bool
-gjs_define_promise_object(JSContext *cx,
- JS::HandleObject global)
-{
- /* This is not a regular import, we just load the module's code from the
- * GResource and evaluate it */
-
- GError *error = NULL;
- GBytes *lie_bytes = g_resources_lookup_data("/org/gnome/gjs/modules/_lie.js",
- G_RESOURCE_LOOKUP_FLAGS_NONE,
- &error);
- if (lie_bytes == NULL) {
- g_critical("Failed to load Promise resource: %s", error->message);
- g_clear_error(&error);
- return false;
- }
-
- /* It should be OK to cast these bytes to const char *, since the module is
- * a text file and we setUTF8(true) below */
- size_t lie_length;
- const char *lie_code = static_cast<const char *>(g_bytes_get_data(lie_bytes,
- &lie_length));
- JS::CompileOptions options(cx);
- options.setUTF8(true)
- .setSourceIsLazy(true)
- .setFile("<Promise>");
-
- JS::RootedValue promise(cx);
- if (!JS::Evaluate(cx, global, options, lie_code, lie_length, &promise)) {
- g_bytes_unref(lie_bytes);
- return false;
- }
- g_bytes_unref(lie_bytes);
-
- return JS_DefineProperty(cx, global, "Promise", promise,
- JSPROP_READONLY | JSPROP_PERMANENT);
-}
-
static void
gjs_context_init(GjsContext *js_context)
{
@@ -465,14 +275,6 @@ gjs_context_finalize(GObject *object)
G_OBJECT_CLASS(gjs_context_parent_class)->finalize(object);
}
-static JSFunctionSpec global_funcs[] = {
- JS_FS("log", gjs_log, 1, GJS_MODULE_PROP_FLAGS),
- JS_FS("logError", gjs_log_error, 2, GJS_MODULE_PROP_FLAGS),
- JS_FS("print", gjs_print, 0, GJS_MODULE_PROP_FLAGS),
- JS_FS("printerr", gjs_printerr, 0, GJS_MODULE_PROP_FLAGS),
- JS_FS_END
-};
-
static void
gjs_context_constructed(GObject *object)
{
@@ -502,47 +304,55 @@ gjs_context_constructed(GObject *object)
/* set ourselves as the private data */
JS_SetContextPrivate(js_context->context, js_context);
- JS::RootedObject global(js_context->context);
- if (!gjs_init_context_standard(js_context->context, &global))
- g_error("Failed to initialize context");
+ /* setExtraWarnings: Be extra strict about code that might hide a bug */
+ if (!g_getenv("GJS_DISABLE_EXTRA_WARNINGS")) {
+ gjs_debug(GJS_DEBUG_CONTEXT, "Enabling extra warnings");
+ JS::RuntimeOptionsRef(js_context->context).setExtraWarnings(true);
+ }
- JSAutoCompartment ac(js_context->context, global);
+ if (!g_getenv("GJS_DISABLE_JIT")) {
+ gjs_debug(GJS_DEBUG_CONTEXT, "Enabling JIT");
+ JS::RuntimeOptionsRef(js_context->context)
+ .setIon(true)
+ .setBaseline(true)
+ .setAsmJS(true);
+ }
- if (!JS_DefineProperty(js_context->context, global, "window", global,
- JSPROP_READONLY | JSPROP_PERMANENT))
- g_error("No memory to export global object as 'window'");
+ /* setDontReportUncaught: Don't send exceptions to our error report handler;
+ * instead leave them set. This allows us to get at the exception object. */
+ JS::ContextOptionsRef(js_context->context).setDontReportUncaught(true);
- if (!JS_DefineFunctions(js_context->context, global, &global_funcs[0]))
- g_error("Failed to define properties on the global object");
+ JS::RootedObject global(js_context->context,
+ gjs_create_global_object(js_context->context));
+ if (!global) {
+ gjs_log_exception(js_context->context);
+ g_error("Failed to initialize global object");
+ }
+
+ JSAutoCompartment ac(js_context->context, global);
new (&js_context->global) JS::Heap<JSObject *>(global);
JS_AddExtraGCRootsTracer(js_context->runtime, gjs_context_tracer, js_context);
- gjs_define_constructor_proxy_factory(js_context->context);
-
- /* We create the global-to-runtime root importer with the
- * passed-in search path. If someone else already created
- * the root importer, this is a no-op.
- */
- if (!gjs_create_root_importer(js_context->context,
- js_context->search_path ?
- (const char**) js_context->search_path :
- NULL,
- true))
+ JS::RootedObject importer(js_context->context,
+ gjs_create_root_importer(js_context->context, js_context->search_path ?
+ js_context->search_path : nullptr));
+ if (!importer)
g_error("Failed to create root importer");
- /* Now copy the global root importer (which we just created,
- * if it didn't exist) to our global object
- */
- if (!gjs_define_root_importer(js_context->context, global))
- g_error("Failed to point 'imports' property at root importer");
-
- /* FIXME: We should define the Promise object before any imports, in case
- * the imports want to use it. Currently that's not possible as it needs to
- * import GLib */
- if(!gjs_define_promise_object(js_context->context, global))
- g_error("Failed to define global Promise object");
+ JS::Value v_importer = gjs_get_global_slot(js_context->context,
+ GJS_GLOBAL_SLOT_IMPORTS);
+ g_assert(((void) "Someone else already created root importer",
+ v_importer.isUndefined()));
+
+ gjs_set_global_slot(js_context->context, GJS_GLOBAL_SLOT_IMPORTS,
+ JS::ObjectValue(*importer));
+
+ if (!gjs_define_global_properties(js_context->context, global)) {
+ gjs_log_exception(js_context->context);
+ g_error("Failed to define properties on global object");
+ }
JS_EndRequest(js_context->context);
diff --git a/gjs/coverage.cpp b/gjs/coverage.cpp
index 52a39df..722e7e5 100644
--- a/gjs/coverage.cpp
+++ b/gjs/coverage.cpp
@@ -25,6 +25,7 @@
#include "coverage.h"
#include "coverage-internal.h"
+#include "global.h"
#include "importer.h"
#include "jsapi-util-args.h"
#include "util/error.h"
@@ -1258,24 +1259,6 @@ gjs_coverage_init(GjsCoverage *self)
{
}
-static JSClass coverage_global_class = {
- "GjsCoverageGlobal",
- JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(GJS_GLOBAL_SLOT_LAST) |
- JSCLASS_IMPLEMENTS_BARRIERS,
- NULL, /* addProperty */
- NULL, /* deleteProperty */
- NULL, /* getProperty */
- NULL, /* setProperty */
- NULL, /* enumerate */
- NULL, /* resolve */
- NULL, /* convert */
- NULL, /* finalize */
- NULL, /* call */
- NULL, /* hasInstance */
- NULL, /* construct */
- JS_GlobalObjectTraceHook
-};
-
static bool
gjs_context_eval_file_in_compartment(GjsContext *context,
const char *filename,
@@ -1557,27 +1540,6 @@ gjs_inject_value_into_coverage_compartment(GjsCoverage *coverage,
return true;
}
-/* Gets the root import and wraps it into a cross-compartment
- * object so that it can be used in the debugger compartment */
-static JSObject *
-gjs_wrap_root_importer_in_compartment(JSContext *context,
- JS::HandleObject compartment)
-{
- JSAutoRequest ar(context);
- JSAutoCompartment ac(context, compartment);
- JS::RootedValue importer(context,
- gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS));
-
- g_assert(importer.isObject());
-
- JS::RootedObject wrapped_importer(context, &importer.toObject());
- if (!JS_WrapObject(context, &wrapped_importer)) {
- return NULL;
- }
-
- return wrapped_importer;
-}
-
static bool
bootstrap_coverage(GjsCoverage *coverage)
{
@@ -1593,8 +1555,7 @@ bootstrap_coverage(GjsCoverage *coverage)
JS::CompartmentOptions options;
options.setVersion(JSVERSION_LATEST);
JS::RootedObject debugger_compartment(context,
- JS_NewGlobalObject(context, &coverage_global_class, NULL,
- JS::FireOnNewGlobalHook, options));
+ gjs_create_global_object(context));
{
JSAutoCompartment compartment(context, debugger_compartment);
JS::RootedObject debuggeeWrapper(context, debuggee);
@@ -1610,35 +1571,9 @@ bootstrap_coverage(GjsCoverage *coverage)
return false;
}
- if (!JS_InitStandardClasses(context, debugger_compartment)) {
- gjs_throw(context, "Failed to init standard classes");
- return false;
- }
-
- if (!JS_InitReflect(context, debugger_compartment)) {
- gjs_throw(context, "Failed to init Reflect");
- return false;
- }
-
- if (!JS_DefineDebuggerObject(context, debugger_compartment)) {
- gjs_throw(context, "Failed to init Debugger");
- return false;
- }
-
- JS::RootedObject wrapped_importer(context,
- gjs_wrap_root_importer_in_compartment(context,
- debugger_compartment));;
-
- if (!wrapped_importer) {
- gjs_throw(context, "Failed to wrap root importer in debugger compartment");
- return false;
- }
-
- /* Now copy the global root importer (which we just created,
- * if it didn't exist) to our global object
- */
- if (!gjs_define_root_importer_object(context, debugger_compartment, wrapped_importer)) {
- gjs_throw(context, "Failed to set 'imports' on debugger compartment");
+ if (!gjs_define_global_properties(context, debugger_compartment)) {
+ gjs_throw(context, "Failed to define global properties on debugger "
+ "compartment");
return false;
}
diff --git a/gjs/global.cpp b/gjs/global.cpp
new file mode 100644
index 0000000..2256d71
--- /dev/null
+++ b/gjs/global.cpp
@@ -0,0 +1,352 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2008 litl, LLC
+ * Copyright (c) 2009 Red Hat, Inc.
+ * Copyright (c) 2017 Philip Chimento <philip chimento 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 <gio/gio.h>
+
+#include "global.h"
+#include "importer.h"
+#include "jsapi-constructor-proxy.h"
+#include "jsapi-util.h"
+#include "jsapi-wrapper.h"
+
+static bool
+gjs_log(JSContext *cx,
+ unsigned argc,
+ JS::Value *vp)
+{
+ JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
+
+ if (argc != 1) {
+ gjs_throw(cx, "Must pass a single argument to log()");
+ return false;
+ }
+
+ JSAutoRequest ar(cx);
+
+ /* JS::ToString might throw, in which case we will only log that the value
+ * could not be converted to string */
+ JS::AutoSaveExceptionState exc_state(cx);
+ JS::RootedString jstr(cx, JS::ToString(cx, argv[0]));
+ exc_state.restore();
+
+ if (jstr == NULL) {
+ g_message("JS LOG: <cannot convert value to string>");
+ return true;
+ }
+
+ GjsAutoJSChar s(cx);
+ if (!gjs_string_to_utf8(cx, JS::StringValue(jstr), &s))
+ return false;
+
+ g_message("JS LOG: %s", s.get());
+
+ argv.rval().setUndefined();
+ return true;
+}
+
+static bool
+gjs_log_error(JSContext *cx,
+ unsigned argc,
+ JS::Value *vp)
+{
+ JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
+
+ if ((argc != 1 && argc != 2) || !argv[0].isObject()) {
+ gjs_throw(cx, "Must pass an exception and optionally a message to logError()");
+ return false;
+ }
+
+ JSAutoRequest ar(cx);
+
+ JS::RootedString jstr(cx);
+
+ if (argc == 2) {
+ /* JS::ToString might throw, in which case we will only log that the
+ * value could not be converted to string */
+ JS::AutoSaveExceptionState exc_state(cx);
+ jstr = JS::ToString(cx, argv[1]);
+ exc_state.restore();
+ }
+
+ gjs_log_exception_full(cx, argv[0], jstr);
+
+ argv.rval().setUndefined();
+ return true;
+}
+
+static bool
+gjs_print_parse_args(JSContext *cx,
+ JS::CallArgs& argv,
+ GjsAutoChar *buffer)
+{
+ GString *str;
+ guint n;
+
+ JSAutoRequest ar(cx);
+
+ str = g_string_new("");
+ for (n = 0; n < argv.length(); ++n) {
+ /* JS::ToString might throw, in which case we will only log that the
+ * value could not be converted to string */
+ JS::AutoSaveExceptionState exc_state(cx);
+ JS::RootedString jstr(cx, JS::ToString(cx, argv[n]));
+ exc_state.restore();
+
+ if (jstr != NULL) {
+ GjsAutoJSChar s(cx);
+ if (!gjs_string_to_utf8(cx, JS::StringValue(jstr), &s)) {
+ g_string_free(str, true);
+ return false;
+ }
+
+ g_string_append(str, s);
+ if (n < (argv.length()-1))
+ g_string_append_c(str, ' ');
+ } else {
+ *buffer = g_string_free(str, true);
+ if (!*buffer)
+ *buffer = g_strdup("<invalid string>");
+ return true;
+ }
+
+ }
+ *buffer = g_string_free(str, false);
+
+ return true;
+}
+
+static bool
+gjs_print(JSContext *context,
+ unsigned argc,
+ JS::Value *vp)
+{
+ JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
+
+ GjsAutoChar buffer;
+ if (!gjs_print_parse_args(context, argv, &buffer))
+ return false;
+
+ g_print("%s\n", buffer.get());
+
+ argv.rval().setUndefined();
+ return true;
+}
+
+static bool
+gjs_printerr(JSContext *context,
+ unsigned argc,
+ JS::Value *vp)
+{
+ JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
+
+ GjsAutoChar buffer;
+ if (!gjs_print_parse_args(context, argv, &buffer))
+ return false;
+
+ g_printerr("%s\n", buffer.get());
+
+ argv.rval().setUndefined();
+ return true;
+}
+
+class GjsGlobal {
+ static constexpr JSClass klass = {
+ "GjsGlobal",
+ JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(GJS_GLOBAL_SLOT_LAST) |
+ JSCLASS_IMPLEMENTS_BARRIERS,
+ nullptr, /* addProperty */
+ nullptr, /* deleteProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ nullptr, /* enumerate */
+ nullptr, /* resolve */
+ nullptr, /* convert */
+ nullptr, /* finalize */
+ nullptr, /* call */
+ nullptr, /* hasInstance */
+ nullptr, /* construct */
+ JS_GlobalObjectTraceHook
+ };
+
+ static constexpr JSFunctionSpec static_funcs[] = {
+ JS_FS("log", gjs_log, 1, GJS_MODULE_PROP_FLAGS),
+ JS_FS("logError", gjs_log_error, 2, GJS_MODULE_PROP_FLAGS),
+ JS_FS("print", gjs_print, 0, GJS_MODULE_PROP_FLAGS),
+ JS_FS("printerr", gjs_printerr, 0, GJS_MODULE_PROP_FLAGS),
+ JS_FS_END
+ };
+
+ static bool
+ define_promise_object(JSContext *cx,
+ JS::HandleObject global)
+ {
+ /* This is not a regular import, we just load the module's code from the
+ * GResource and evaluate it */
+
+ GError *error = NULL;
+ GBytes *lie_bytes = g_resources_lookup_data("/org/gnome/gjs/modules/_lie.js",
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ &error);
+ if (lie_bytes == NULL) {
+ g_critical("Failed to load Promise resource: %s", error->message);
+ g_clear_error(&error);
+ return false;
+ }
+
+ /* It should be OK to cast these bytes to const char *, since the module is
+ * a text file and we setUTF8(true) below */
+ size_t lie_length;
+ const char *lie_code = static_cast<const char *>(g_bytes_get_data(lie_bytes,
+ &lie_length));
+ JS::CompileOptions options(cx);
+ options.setUTF8(true)
+ .setSourceIsLazy(true)
+ .setFile("<Promise>");
+
+ JS::RootedValue promise(cx);
+ if (!JS::Evaluate(cx, global, options, lie_code, lie_length, &promise)) {
+ g_bytes_unref(lie_bytes);
+ return false;
+ }
+ g_bytes_unref(lie_bytes);
+
+ return JS_DefineProperty(cx, global, "Promise", promise,
+ JSPROP_READONLY | JSPROP_PERMANENT);
+ }
+
+public:
+
+ static JSObject *
+ create(JSContext *cx)
+ {
+ JS::CompartmentOptions compartment_options;
+ compartment_options.setVersion(JSVERSION_LATEST);
+ JS::RootedObject global(cx,
+ JS_NewGlobalObject(cx, &GjsGlobal::klass, nullptr,
+ JS::FireOnNewGlobalHook, compartment_options));
+ if (!global)
+ return nullptr;
+
+ JSAutoCompartment ac(cx, global);
+
+ if (!JS_InitStandardClasses(cx, global) ||
+ !JS_InitReflect(cx, global) ||
+ !JS_DefineDebuggerObject(cx, global))
+ return nullptr;
+
+ return global;
+ }
+
+ static bool
+ define_properties(JSContext *cx,
+ JS::HandleObject global)
+ {
+ if (!JS_DefineProperty(cx, global, "window", global,
+ JSPROP_READONLY | JSPROP_PERMANENT) ||
+ !JS_DefineFunctions(cx, global, GjsGlobal::static_funcs) ||
+ !gjs_define_constructor_proxy_factory(cx, global))
+ return false;
+
+ JS::Value v_importer = gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS);
+ g_assert(((void) "importer should be defined before passing null "
+ "importer to GjsGlobal::define_properties",
+ v_importer.isObject()));
+ JS::RootedObject root_importer(cx, &v_importer.toObject());
+
+ /* Wrapping is a no-op if the importer is already in the same
+ * compartment. */
+ if (!JS_WrapObject(cx, &root_importer) ||
+ !gjs_object_define_property(cx, global, GJS_STRING_IMPORTS,
+ root_importer, GJS_MODULE_PROP_FLAGS))
+ return false;
+
+ /* FIXME: We should define the Promise object before any imports, in
+ * case the imports want to use it. Currently that's not possible as it
+ * needs to import GLib */
+ return define_promise_object(cx, global);
+ }
+};
+
+/**
+ * gjs_create_global_object:
+ * @cx: a #JSContext
+ *
+ * Creates a global object, and initializes it with the default API.
+ *
+ * Returns: the created global object on success, nullptr otherwise, in which
+ * case an exception is pending on @cx
+ */
+JSObject *
+gjs_create_global_object(JSContext *cx)
+{
+ return GjsGlobal::create(cx);
+}
+
+/**
+ * gjs_define_global_properties:
+ * @cx: a #JSContext
+ * @global: a JS global object that has not yet been passed to this function
+ *
+ * Defines properties on the global object such as 'window' and 'imports'.
+ * This function completes the initialization of a new global object, but it
+ * is separate from gjs_create_global_object() because all globals share the
+ * same root importer.
+ * The code creating the main global for the JS context needs to create the
+ * root importer in between calling gjs_create_global_object() and
+ * gjs_define_global_properties().
+ *
+ * The caller of this function should be in the compartment for @global.
+ * If the root importer object belongs to a different compartment, this
+ * function will create a cross-compartment wrapper for it.
+ *
+ * Returns: true on success, false otherwise, in which case an exception is
+ * pending on @cx
+ */
+bool
+gjs_define_global_properties(JSContext *cx,
+ JS::HandleObject global)
+{
+ return GjsGlobal::define_properties(cx, global);
+}
+
+void
+gjs_set_global_slot(JSContext *cx,
+ GjsGlobalSlot slot,
+ JS::Value value)
+{
+ JSObject *global = gjs_get_import_global(cx);
+ JS_SetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot, value);
+}
+
+JS::Value
+gjs_get_global_slot(JSContext *cx,
+ GjsGlobalSlot slot)
+{
+ JSObject *global = gjs_get_import_global(cx);
+ return JS_GetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot);
+}
+
+decltype(GjsGlobal::klass) constexpr GjsGlobal::klass;
+decltype(GjsGlobal::static_funcs) constexpr GjsGlobal::static_funcs;
diff --git a/gjs/global.h b/gjs/global.h
new file mode 100644
index 0000000..31056d6
--- /dev/null
+++ b/gjs/global.h
@@ -0,0 +1,72 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2017 Philip Chimento <philip chimento 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.
+ */
+
+#ifndef GJS_GLOBAL_H
+#define GJS_GLOBAL_H
+
+#include <glib.h>
+
+#include "jsapi-wrapper.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GJS_GLOBAL_SLOT_IMPORTS,
+ GJS_GLOBAL_SLOT_PROTOTYPE_gtype,
+ GJS_GLOBAL_SLOT_PROTOTYPE_function,
+ GJS_GLOBAL_SLOT_PROTOTYPE_ns,
+ GJS_GLOBAL_SLOT_PROTOTYPE_repo,
+ GJS_GLOBAL_SLOT_PROTOTYPE_byte_array,
+ GJS_GLOBAL_SLOT_PROTOTYPE_importer,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_context,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_gradient,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_image_surface,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_linear_gradient,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_path,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_pattern,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_pdf_surface,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_ps_surface,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_radial_gradient,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_region,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_solid_pattern,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_surface,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_surface_pattern,
+ GJS_GLOBAL_SLOT_PROTOTYPE_cairo_svg_surface,
+ GJS_GLOBAL_SLOT_LAST,
+} GjsGlobalSlot;
+
+JSObject *gjs_create_global_object(JSContext *cx);
+
+bool gjs_define_global_properties(JSContext *cx,
+ JS::HandleObject global);
+
+JS::Value gjs_get_global_slot(JSContext *cx,
+ GjsGlobalSlot slot);
+
+void gjs_set_global_slot(JSContext *context,
+ GjsGlobalSlot slot,
+ JS::Value value);
+
+G_END_DECLS
+
+#endif /* GJS_GLOBAL_H */
diff --git a/gjs/importer.cpp b/gjs/importer.cpp
index 6bc2492..d88d418 100644
--- a/gjs/importer.cpp
+++ b/gjs/importer.cpp
@@ -63,6 +63,9 @@ static const JSClass gjs_importer_class = *js::Jsvalify(&gjs_importer_real_class
GJS_DEFINE_PRIV_FROM_JS(Importer, gjs_importer_class)
+static JSObject *gjs_define_importer(JSContext *, JS::HandleObject,
+ const char *, const char **, bool);
+
static bool
importer_to_string(JSContext *cx,
unsigned argc,
@@ -966,12 +969,12 @@ gjs_get_search_path(void)
}
static JSObject*
-gjs_create_importer(JSContext *context,
- const char *importer_name,
- const char **initial_search_path,
- bool add_standard_search_path,
- bool is_root,
- JS::HandleObject in_object)
+gjs_create_importer(JSContext *context,
+ const char *importer_name,
+ const char * const *initial_search_path,
+ bool add_standard_search_path,
+ bool is_root,
+ JS::HandleObject in_object)
{
char **paths[2] = {0};
char **search_path;
@@ -1001,7 +1004,7 @@ gjs_create_importer(JSContext *context,
return importer;
}
-JSObject*
+static JSObject *
gjs_define_importer(JSContext *context,
JS::HandleObject in_object,
const char *importer_name,
@@ -1024,62 +1027,10 @@ gjs_define_importer(JSContext *context,
return importer;
}
-/* If this were called twice for the same runtime with different args it
- * would basically be a bug, but checking for that is a lot of code so
- * we just ignore all calls after the first and hope the args are the same.
- */
-bool
-gjs_create_root_importer(JSContext *context,
- const char **initial_search_path,
- bool add_standard_search_path)
-{
- JS::Value importer;
-
- JS_BeginRequest(context);
-
- importer = gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS);
-
- if (G_UNLIKELY (!importer.isUndefined())) {
- gjs_debug(GJS_DEBUG_IMPORTER,
- "Someone else already created root importer, ignoring second request");
-
- JS_EndRequest(context);
- return true;
- }
-
- importer = JS::ObjectValue(*gjs_create_importer(context, "imports",
- initial_search_path,
- add_standard_search_path,
- true, JS::NullPtr()));
- gjs_set_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS, importer);
-
- JS_EndRequest(context);
- return true;
-}
-
-bool
-gjs_define_root_importer_object(JSContext *context,
- JS::HandleObject in_object,
- JS::HandleObject root_importer)
-{
- JSAutoRequest ar(context);
-
- if (!gjs_object_define_property(context, in_object,
- GJS_STRING_IMPORTS, root_importer,
- GJS_MODULE_PROP_FLAGS)) {
- gjs_debug(GJS_DEBUG_IMPORTER, "DefineProperty imports on %p failed",
- in_object.get());
- return false;
- }
-
- return true;
-}
-
-bool
-gjs_define_root_importer(JSContext *cx,
- JS::HandleObject in_object)
+JSObject *
+gjs_create_root_importer(JSContext *cx,
+ const char * const *search_path)
{
- JS::Value importer = gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS);
- JS::RootedObject rooted_importer(cx, &importer.toObject());
- return gjs_define_root_importer_object(cx, in_object, rooted_importer);
+ return gjs_create_importer(cx, "imports", search_path, true, true,
+ JS::NullPtr());
}
diff --git a/gjs/importer.h b/gjs/importer.h
index 0d0ee88..70d1718 100644
--- a/gjs/importer.h
+++ b/gjs/importer.h
@@ -30,22 +30,8 @@
G_BEGIN_DECLS
-bool gjs_create_root_importer (JSContext *context,
- const char **initial_search_path,
- bool add_standard_search_path);
-
-bool gjs_define_root_importer(JSContext *cx,
- JS::HandleObject in_object);
-
-bool gjs_define_root_importer_object(JSContext *context,
- JS::HandleObject in_object,
- JS::HandleObject root_importer);
-
-JSObject *gjs_define_importer(JSContext *context,
- JS::HandleObject in_object,
- const char *importer_name,
- const char **initial_search_path,
- bool add_standard_search_path);
+JSObject *gjs_create_root_importer(JSContext *cx,
+ const char * const *search_path);
G_END_DECLS
diff --git a/gjs/jsapi-class.h b/gjs/jsapi-class.h
index 7797901..0d487e7 100644
--- a/gjs/jsapi-class.h
+++ b/gjs/jsapi-class.h
@@ -24,6 +24,7 @@
#ifndef GJS_JSAPI_CLASS_H
#define GJS_JSAPI_CLASS_H
+#include "global.h"
#include "jsapi-util.h"
#include "jsapi-wrapper.h"
#include "util/log.h"
diff --git a/gjs/jsapi-constructor-proxy.cpp b/gjs/jsapi-constructor-proxy.cpp
index 6bb59b2..ea59bd3 100644
--- a/gjs/jsapi-constructor-proxy.cpp
+++ b/gjs/jsapi-constructor-proxy.cpp
@@ -173,11 +173,10 @@ create_gjs_constructor_proxy(JSContext *cx,
}
bool
-gjs_define_constructor_proxy_factory(JSContext *cx)
+gjs_define_constructor_proxy_factory(JSContext *cx,
+ JS::HandleObject global)
{
bool found;
- JS::RootedObject global(cx, gjs_get_import_global(cx));
-
if (!JS_HasProperty(cx, global, constructor_proxy_create_name, &found))
return false;
if (found)
diff --git a/gjs/jsapi-constructor-proxy.h b/gjs/jsapi-constructor-proxy.h
index ef54145..bc45b07 100644
--- a/gjs/jsapi-constructor-proxy.h
+++ b/gjs/jsapi-constructor-proxy.h
@@ -30,7 +30,8 @@
G_BEGIN_DECLS
-bool gjs_define_constructor_proxy_factory(JSContext *cx);
+bool gjs_define_constructor_proxy_factory(JSContext *cx,
+ JS::HandleObject global);
G_END_DECLS
diff --git a/gjs/jsapi-util.cpp b/gjs/jsapi-util.cpp
index 0caa514..183e40e 100644
--- a/gjs/jsapi-util.cpp
+++ b/gjs/jsapi-util.cpp
@@ -45,101 +45,6 @@ gjs_util_error_quark (void)
return g_quark_from_static_string ("gjs-util-error-quark");
}
-static JSClass global_class = {
- "GjsGlobal",
- JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(GJS_GLOBAL_SLOT_LAST) |
- JSCLASS_IMPLEMENTS_BARRIERS,
- NULL, /* addProperty */
- NULL, /* deleteProperty */
- NULL, /* getProperty */
- NULL, /* setProperty */
- NULL, /* enumerate */
- NULL, /* resolve */
- NULL, /* convert */
- NULL, /* finalize */
- NULL, /* call */
- NULL, /* hasInstance */
- NULL, /* construct */
- JS_GlobalObjectTraceHook
-};
-
-/**
- * gjs_init_context_standard:
- * @context: a #JSContext
- * @global_out: (out): The created global object.
-
- * This function creates a global object given the context,
- * and initializes it with the default API.
- *
- * Returns: true on success, false otherwise
- */
-bool
-gjs_init_context_standard (JSContext *context,
- JS::MutableHandleObject global)
-{
- JS::CompartmentOptions compartment_options;
-
- bool extra_warnings = false;
- if (!g_getenv("GJS_DISABLE_EXTRA_WARNINGS")) {
- gjs_debug(GJS_DEBUG_CONTEXT, "Enabling extra warnings");
- extra_warnings = true;
- }
-
- /* setDontReportUncaught: Don't send exceptions to our error report handler;
- * instead leave them set. This allows us to get at the exception object.
- *
- * setExtraWarnings: Report warnings to error reporter function.
- */
- JS::ContextOptionsRef(context).setDontReportUncaught(true);
- JS::RuntimeOptionsRef(context).setExtraWarnings(extra_warnings);
-
- if (!g_getenv("GJS_DISABLE_JIT")) {
- gjs_debug(GJS_DEBUG_CONTEXT, "Enabling JIT");
- JS::RuntimeOptionsRef(context)
- .setIon(true)
- .setBaseline(true)
- .setAsmJS(true);
- }
-
- compartment_options.setVersion(JSVERSION_LATEST);
- global.set(JS_NewGlobalObject(context, &global_class, NULL,
- JS::FireOnNewGlobalHook, compartment_options));
- if (global == NULL)
- return false;
-
- JSAutoCompartment ac(context, global);
-
- if (!JS_InitStandardClasses(context, global))
- return false;
-
- if (!JS_InitReflect(context, global))
- return false;
-
- if (!JS_DefineDebuggerObject(context, global))
- return false;
-
- return true;
-}
-
-void
-gjs_set_global_slot (JSContext *context,
- GjsGlobalSlot slot,
- JS::Value value)
-{
- JSObject *global;
- global = gjs_get_import_global(context);
- JS_SetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot, value);
-}
-
-JS::Value
-gjs_get_global_slot (JSContext *context,
- GjsGlobalSlot slot)
-{
- JSObject *global;
- global = gjs_get_import_global(context);
- return JS_GetReservedSlot(global, JSCLASS_GLOBAL_SLOT_COUNT + slot);
-}
-
bool
gjs_object_get_property(JSContext *cx,
JS::HandleObject obj,
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index b3b6efb..f1c53f4 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -127,31 +127,6 @@ enum {
GJS_UTIL_ERROR_ARGUMENT_TYPE_MISMATCH
};
-typedef enum {
- GJS_GLOBAL_SLOT_IMPORTS,
- GJS_GLOBAL_SLOT_PROTOTYPE_gtype,
- GJS_GLOBAL_SLOT_PROTOTYPE_function,
- GJS_GLOBAL_SLOT_PROTOTYPE_ns,
- GJS_GLOBAL_SLOT_PROTOTYPE_repo,
- GJS_GLOBAL_SLOT_PROTOTYPE_byte_array,
- GJS_GLOBAL_SLOT_PROTOTYPE_importer,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_context,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_gradient,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_image_surface,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_linear_gradient,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_path,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_pattern,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_pdf_surface,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_ps_surface,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_radial_gradient,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_region,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_solid_pattern,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_surface,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_surface_pattern,
- GJS_GLOBAL_SLOT_PROTOTYPE_cairo_svg_surface,
- GJS_GLOBAL_SLOT_LAST,
-} GjsGlobalSlot;
-
typedef struct GjsRootedArray GjsRootedArray;
/* Flags that should be set on properties exported from native code modules.
@@ -178,17 +153,8 @@ typedef struct GjsRootedArray GjsRootedArray;
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
JS::RootedObject to(cx, &args.computeThis(cx).toObject())
-bool gjs_init_context_standard(JSContext *context,
- JS::MutableHandleObject global);
-
JSObject* gjs_get_import_global (JSContext *context);
-JS::Value gjs_get_global_slot (JSContext *context,
- GjsGlobalSlot slot);
-void gjs_set_global_slot (JSContext *context,
- GjsGlobalSlot slot,
- JS::Value value);
-
void gjs_throw_constructor_error (JSContext *context);
void gjs_throw_abstract_constructor_error(JSContext *context,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]