[gjs/mozjs31] js: Call JS_Init() and JS_ShutDown()
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/mozjs31] js: Call JS_Init() and JS_ShutDown()
- Date: Thu, 17 Nov 2016 02:08:06 +0000 (UTC)
commit 3244c9a6ce99abb487e6d0dabcd68d98772e2bb1
Author: Philip Chimento <philip endlessm com>
Date: Fri Nov 4 19:02:55 2016 -0700
js: Call JS_Init() and JS_ShutDown()
Starting with mozjs31, JS_Init() is required. Calling JS_ShutDown() on
exit is not required, but may become so in the future.
This does so in the constructor and destructor of a static object.
Normally this is discouraged because the order in which the constructors
and destructors are called is not guaranteed, but I don't think that is a
problem here since it's unlikely that anyone will try to use GJS API from
a static constructor.
However, API clients still must unref any GjsContext before the program
shuts down. Usually this is not a problem, unless a JS script calls
System.exit(), in which case the code would exit immediately without
unreffing the GjsContext before JS_ShutDown was called. Instead, we make
System.exit() throw an uncatchable exception, which is propagated all the
way up to JS::Evaluate() and turned into a GError with code
GJS_ERROR_SYSTEM_EXIT, thrown from gjs_context_eval().
For this, we need to expose GjsError and its error codes in the public
API. (They should have been exposed already, because gjs_context_eval()
could already throw GJS_ERROR_FAILED.)
Finally, the tests added as part of this patch made the testSystemExit
script obsolete.
https://bugzilla.gnome.org/show_bug.cgi?id=751252
Makefile-insttest.am | 6 ------
Makefile.am | 5 +++--
NEWS | 17 +++++++++++++++++
gjs/console.cpp | 4 ++--
gjs/context-private.h | 3 ---
gjs/context.cpp | 26 +++++++++++++++++++++-----
gjs/gjs.h | 1 +
gjs/runtime.cpp | 19 +++++++++++++++++++
installed-tests/scripts/testSystemExit.js | 3 ---
modules/console.cpp | 14 +++++++++++++-
test/gjs-tests.cpp | 27 +++++++++++++++++++++++++++
test/testCommandLine.sh | 7 +++++++
util/error.h | 3 ++-
13 files changed, 112 insertions(+), 23 deletions(-)
---
diff --git a/Makefile-insttest.am b/Makefile-insttest.am
index bb13b6b..4e2c78a 100644
--- a/Makefile-insttest.am
+++ b/Makefile-insttest.am
@@ -178,7 +178,6 @@ EXTRA_DIST += \
installed-tests/js/testCairo.js \
installed-tests/js/testGtk.js \
installed-tests/js/testGDBus.js \
- installed-tests/scripts/testSystemExit.js \
$(NULL)
if BUILDOPT_INSTALL_TESTS
@@ -205,9 +204,4 @@ endif
< $(srcdir)/installed-tests/script.test.in > $@.tmp && \
mv $@.tmp $@
-jsscripttestsdir = $(gjsinsttestdir)/scripts
-jsscripttests_DATA = installed-tests/scripts/testSystemExit.js
-installedtestmeta_DATA += testSystemExit.test
endif
-
-MAINTAINERCLEANFILES += testSystemExit.test
diff --git a/Makefile.am b/Makefile.am
index c0df6c2..ae1cf6e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -34,7 +34,9 @@ gjs_public_includedir = $(includedir)/gjs-1.0
########################################################################
nobase_gjs_public_include_HEADERS = \
gjs/context.h \
- gjs/gjs.h
+ gjs/gjs.h \
+ util/error.h \
+ $(NULL)
########################################################################
pkgconfig_DATA = gjs-1.0.pc
@@ -93,7 +95,6 @@ libgjs_la_SOURCES = \
modules/modules.cpp \
modules/modules.h \
util/error.cpp \
- util/error.h \
util/hash-x32.cpp \
util/hash-x32.h \
util/glib.cpp \
diff --git a/NEWS b/NEWS
index d7cf1f3..5497531 100644
--- a/NEWS
+++ b/NEWS
@@ -103,6 +103,23 @@ NEXT
arguments into account for the time being, while still passing them on to the
script. A warning will be logged if you are using the deprecated behaviour.
+- News for GJS embedders such as gnome-shell:
+
+ * New API: gjs_error_quark() is now exposed, and the error domain GJS_ERROR
+ and codes GJS_ERROR_FAILED and GJS_ERROR_SYSTEM_EXIT.
+
+ * Backwards-incompatible change: Calling System.exit() from JS code will now
+ not abort the program immediately, but instead will return immediately from
+ gjs_context_eval() so that you can unref your GjsContext before internal
+ resources are released on exit.
+
+ If gjs_context_eval() or gjs_context_eval_file() returns an error with code
+ GJS_ERROR_SYSTEM_EXIT, it means that the JS code called System.exit(). The
+ exit code will be found in the 'exit_status_p' out parameter to
+ gjs_context_eval() or gjs_context_eval_file(). If you receive this error,
+ you should do any cleanup needed and exit your program with the given exit
+ code.
+
Version 1.46.0
--------------
diff --git a/gjs/console.cpp b/gjs/console.cpp
index d60840b..c10f776 100644
--- a/gjs/console.cpp
+++ b/gjs/console.cpp
@@ -290,8 +290,8 @@ main(int argc, char **argv)
/* evaluate the script */
if (!gjs_context_eval(js_context, script, len,
filename, &code, &error)) {
- code = 1;
- g_printerr("%s\n", error->message);
+ if (!g_error_matches(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT))
+ g_printerr("%s\n", error->message);
g_clear_error(&error);
goto out;
}
diff --git a/gjs/context-private.h b/gjs/context-private.h
index 045909f..5b24b77 100644
--- a/gjs/context-private.h
+++ b/gjs/context-private.h
@@ -37,9 +37,6 @@ void _gjs_context_schedule_gc_if_needed (GjsContext *js_context);
void _gjs_context_exit(GjsContext *js_context,
uint8_t exit_code);
-bool _gjs_context_should_exit(GjsContext *js_context,
- uint8_t *exit_code_p);
-
G_END_DECLS
#endif /* __GJS_CONTEXT_PRIVATE_H__ */
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 5c8c267..c3edd89 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -568,15 +568,22 @@ _gjs_context_exit(GjsContext *js_context,
js_context->exit_code = exit_code;
}
-bool
-_gjs_context_should_exit(GjsContext *js_context,
- uint8_t *exit_code_p)
+static bool
+context_should_exit(GjsContext *js_context,
+ uint8_t *exit_code_p)
{
if (exit_code_p != NULL)
*exit_code_p = js_context->exit_code;
return js_context->should_exit;
}
+static void
+context_reset_exit(GjsContext *js_context)
+{
+ js_context->should_exit = false;
+ js_context->exit_code = 0;
+}
+
/**
* gjs_context_maybe_gc:
* @context: a #GjsContext
@@ -671,14 +678,22 @@ gjs_context_eval(GjsContext *js_context,
if (!gjs_eval_with_scope(js_context->context, JS::NullPtr(), script,
script_len, filename, &retval)) {
uint8_t code;
- if (_gjs_context_should_exit(js_context, &code))
- exit(code);
+ if (context_should_exit(js_context, &code)) {
+ /* exit_status_p is public API so can't be changed, but should be
+ * uint8_t, not int */
+ *exit_status_p = code;
+ g_set_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT,
+ "Exit with code %d", code);
+ goto out; /* Don't log anything */
+ }
gjs_log_exception(js_context->context);
g_set_error(error,
GJS_ERROR,
GJS_ERROR_FAILED,
"JS_EvaluateScript() failed");
+ /* No exit code from script, but we don't want to exit(0) */
+ *exit_status_p = 1;
goto out;
}
@@ -698,6 +713,7 @@ gjs_context_eval(GjsContext *js_context,
out:
g_object_unref(G_OBJECT(js_context));
+ context_reset_exit(js_context);
return ret;
}
diff --git a/gjs/gjs.h b/gjs/gjs.h
index b2a32f2..5cb1810 100644
--- a/gjs/gjs.h
+++ b/gjs/gjs.h
@@ -25,5 +25,6 @@
#define __GJS_GJS_H__
#include <gjs/context.h>
+#include <util/error.h>
#endif /* __GJS_GJS_H__ */
diff --git a/gjs/runtime.cpp b/gjs/runtime.cpp
index 6fd9467..5619e89 100644
--- a/gjs/runtime.cpp
+++ b/gjs/runtime.cpp
@@ -222,6 +222,24 @@ gjs_finalize_callback(JSFreeOp *fop,
data->in_gc_sweep = false;
}
+class GjsInit {
+public:
+ GjsInit() {
+ if (!JS_Init())
+ g_error("Could not initialize Javascript");
+ }
+
+ ~GjsInit() {
+ JS_ShutDown();
+ }
+
+ operator bool() {
+ return true;
+ }
+};
+
+static GjsInit gjs_is_inited;
+
JSRuntime *
gjs_runtime_for_current_thread(void)
{
@@ -229,6 +247,7 @@ gjs_runtime_for_current_thread(void)
RuntimeData *data;
if (!runtime) {
+ g_assert(gjs_is_inited);
runtime = JS_NewRuntime(32*1024*1024 /* max bytes */, JS_USE_HELPER_THREADS);
if (runtime == NULL)
g_error("Failed to create javascript runtime");
diff --git a/modules/console.cpp b/modules/console.cpp
index 817c722..abcb175 100644
--- a/modules/console.cpp
+++ b/modules/console.cpp
@@ -53,6 +53,7 @@
#include <glib/gprintf.h>
#include "console.h"
+#include "gjs/context.h"
#include "gjs/jsapi-private.h"
#include "gjs/jsapi-wrapper.h"
@@ -195,7 +196,18 @@ gjs_console_interact(JSContext *context,
JS::CompileOptions options(context);
options.setUTF8(true)
.setFileAndLine("typein", startline);
- JS::Evaluate(context, object, options, buffer->str, buffer->len, &result);
+ if (!JS::Evaluate(context, object, options, buffer->str, buffer->len,
+ &result)) {
+ /* If this was an uncatchable exception, throw another uncatchable
+ * exception on up to the surrounding JS::Evaluate() in main(). This
+ * happens when you run gjs-console and type imports.system.exit(0);
+ * at the prompt. If we don't throw another uncatchable exception
+ * here, then it's swallowed and main() won't exit. */
+ if (!JS_IsExceptionPending(context)) {
+ argv.rval().set(result);
+ return false;
+ }
+ }
gjs_schedule_gc_if_needed(context);
diff --git a/test/gjs-tests.cpp b/test/gjs-tests.cpp
index 64e9d96..3e55909 100644
--- a/test/gjs-tests.cpp
+++ b/test/gjs-tests.cpp
@@ -30,6 +30,7 @@
#include "gjs/jsapi-util.h"
#include "gjs/jsapi-wrapper.h"
#include "gjs-test-utils.h"
+#include "util/error.h"
static void
gjstest_test_func_gjs_context_construct_destroy(void)
@@ -59,6 +60,31 @@ gjstest_test_func_gjs_context_construct_eval(void)
g_object_unref (context);
}
+static void
+gjstest_test_func_gjs_context_exit(void)
+{
+ GjsContext *context = gjs_context_new();
+ GError *error = NULL;
+ int status;
+
+ bool ok = gjs_context_eval(context, "imports.system.exit(0);", -1,
+ "<input>", &status, &error);
+ g_assert_false(ok);
+ g_assert_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT);
+ g_assert_cmpuint(status, ==, 0);
+
+ g_clear_error(&error);
+
+ ok = gjs_context_eval(context, "imports.system.exit(42);", -1, "<input>",
+ &status, &error);
+ g_assert_false(ok);
+ g_assert_error(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT);
+ g_assert_cmpuint(status, ==, 42);
+
+ g_clear_error(&error);
+ g_object_unref(context);
+}
+
#define JS_CLASS "\
const Lang = imports.lang; \
const GObject = imports.gi.GObject; \
@@ -251,6 +277,7 @@ main(int argc,
g_test_add_func("/gjs/context/construct/destroy", gjstest_test_func_gjs_context_construct_destroy);
g_test_add_func("/gjs/context/construct/eval", gjstest_test_func_gjs_context_construct_eval);
+ g_test_add_func("/gjs/context/exit", gjstest_test_func_gjs_context_exit);
g_test_add_func("/gjs/gobject/js_defined_type", gjstest_test_func_gjs_gobject_js_defined_type);
g_test_add_func("/gjs/jsutil/strip_shebang/no_shebang",
gjstest_test_strip_shebang_no_advance_for_no_shebang);
g_test_add_func("/gjs/jsutil/strip_shebang/have_shebang",
gjstest_test_strip_shebang_advance_for_shebang);
diff --git a/test/testCommandLine.sh b/test/testCommandLine.sh
index 89ada6c..9ce23ff 100755
--- a/test/testCommandLine.sh
+++ b/test/testCommandLine.sh
@@ -18,6 +18,13 @@ fail () {
exit 1
}
+# Test that System.exit() works in gjs-console
+"$gjs" -c 'imports.system.exit(0)' || \
+ fail "System.exit(0) should exit successfully"
+if "gjs" -c 'imports.system.exit(42)' -ne 42; then
+ fail "System.exit(42) should exit with the correct exit code"
+fi
+
# gjs --help prints GJS help
"$gjs" --help >/dev/null || \
fail "--help should succeed"
diff --git a/util/error.h b/util/error.h
index 85b2cb6..01fa96b 100644
--- a/util/error.h
+++ b/util/error.h
@@ -32,7 +32,8 @@ GQuark gjs_error_quark(void);
#define GJS_ERROR gjs_error_quark()
typedef enum {
- GJS_ERROR_FAILED
+ GJS_ERROR_FAILED,
+ GJS_ERROR_SYSTEM_EXIT,
} GjsError;
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]