[gjs/ewlsh/main-loop-hooks] Propagate the exit code from runAsync using Gio.ApplicationError
- From: Evan Welsh <ewlsh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/ewlsh/main-loop-hooks] Propagate the exit code from runAsync using Gio.ApplicationError
- Date: Sun, 6 Mar 2022 02:28:41 +0000 (UTC)
commit 057e060af70e28b812fdf413f240c3385d82e71f
Author: Evan Welsh <contact evanwelsh com>
Date: Sat Mar 5 18:03:30 2022 -0800
Propagate the exit code from runAsync using Gio.ApplicationError
gjs/console.cpp | 7 +++++--
gjs/context.cpp | 46 ++++++++++++++++++++++++++++++++++++++++---
gjs/error-types.h | 1 +
modules/core/overrides/Gio.js | 21 +++++++++++++++++---
modules/core/overrides/Gtk.js | 14 +------------
5 files changed, 68 insertions(+), 21 deletions(-)
---
diff --git a/gjs/console.cpp b/gjs/console.cpp
index 49c822992..856f27513 100644
--- a/gjs/console.cpp
+++ b/gjs/console.cpp
@@ -184,13 +184,16 @@ int define_argv_and_eval_script(GjsContext* js_context, int argc,
!gjs_context_eval_module(js_context, uri, &code_u8, &error)) {
code = code_u8;
- if (!g_error_matches(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT))
+ if (!g_error_matches(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT) &&
+ !g_error_matches(error, GJS_ERROR,
+ GJS_ERROR_APPLICATION_EXIT_CODE))
g_critical("%s", error->message);
g_clear_error(&error);
}
} else if (!gjs_context_eval(js_context, script, len, filename, &code,
&error)) {
- if (!g_error_matches(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT))
+ if (!g_error_matches(error, GJS_ERROR, GJS_ERROR_SYSTEM_EXIT) &&
+ !g_error_matches(error, GJS_ERROR, GJS_ERROR_APPLICATION_EXIT_CODE))
g_critical("%s", error->message);
g_clear_error(&error);
}
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 9917b2c27..6c22ddd10 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -621,7 +621,8 @@ GjsContextPrivate::GjsContextPrivate(JSContext* cx, GjsContext* public_context)
m_cx(cx),
m_owner_thread(std::this_thread::get_id()),
m_dispatcher(this),
- m_environment_preparer(cx) {
+ m_environment_preparer(cx),
+ m_exit_code(0) {
JS_SetGCCallback(
cx,
[](JSContext*, JSGCStatus status, JS::GCReason reason, void* data) {
@@ -1304,6 +1305,34 @@ void GjsContextPrivate::auto_profile_exit(bool auto_profile) {
gjs_profiler_stop(m_profiler);
}
+[[nodiscard]] static bool error_has_exit_code(JSContext* cx,
+ JS::HandleValue thrown_value,
+ uint8_t* exit_code) {
+ if (!thrown_value.isObject())
+ return false;
+
+ JS::AutoSaveExceptionState saved_exc(cx);
+ JS::RootedObject exc(cx, &thrown_value.toObject());
+ JS::RootedValue exc_error_code(cx);
+ bool retval = false;
+
+ if (!JS_GetProperty(cx, exc, "exitCode", &exc_error_code))
+ goto out;
+
+ if (!exc_error_code.isNumber())
+ goto out;
+
+ retval = true;
+
+ if (!exit_code)
+ goto out;
+
+ *exit_code = exc_error_code.toNumber();
+out:
+ saved_exc.restore();
+ return retval;
+}
+
bool GjsContextPrivate::handle_exit_code(bool no_sync_error_pending,
const char* source_type,
const char* identifier,
@@ -1323,6 +1352,17 @@ bool GjsContextPrivate::handle_exit_code(bool no_sync_error_pending,
// be pending even if the script returned
// true synchronously
if (JS_IsExceptionPending(m_cx)) {
+ JS::RootedValue exc(m_cx);
+ if (JS_GetPendingException(m_cx, &exc) &&
+ error_has_exit_code(m_cx, exc, exit_code)) {
+ // If the exception specifies exitCode don't log it as unhandled
+ gjs_log_exception(m_cx);
+
+ g_set_error(error, GJS_ERROR, GJS_ERROR_APPLICATION_EXIT_CODE,
+ "Exit with code %d", code);
+ return false;
+ }
+
g_set_error(error, GJS_ERROR, GJS_ERROR_FAILED,
"%s %s threw an exception", source_type, identifier);
gjs_log_exception_uncaught(m_cx);
@@ -1379,7 +1419,7 @@ bool GjsContextPrivate::eval(const char* script, size_t script_len,
/**
* If there are no errors and the mainloop hook
- * is set, cal it.
+ * is set, call it.
*/
if (ok && m_main_loop_hook)
ok = run_main_loop_hook();
@@ -1484,7 +1524,7 @@ bool GjsContextPrivate::eval_module(const char* identifier,
/**
* If there are no errors and the mainloop hook
- * is set, cal it.
+ * is set, call it.
*/
if (ok && m_main_loop_hook)
ok = run_main_loop_hook();
diff --git a/gjs/error-types.h b/gjs/error-types.h
index 4c470b5c7..0c69abcdc 100644
--- a/gjs/error-types.h
+++ b/gjs/error-types.h
@@ -25,6 +25,7 @@ GQuark gjs_error_quark(void);
typedef enum {
GJS_ERROR_FAILED,
GJS_ERROR_SYSTEM_EXIT,
+ GJS_ERROR_APPLICATION_EXIT_CODE,
} GjsError;
GJS_EXPORT
diff --git a/modules/core/overrides/Gio.js b/modules/core/overrides/Gio.js
index 6b382bfc8..fb72e4e9e 100644
--- a/modules/core/overrides/Gio.js
+++ b/modules/core/overrides/Gio.js
@@ -7,6 +7,15 @@ var Signals = imports.signals;
var {setMainLoopHook} = imports._promiseNative;
var Gio;
+class ApplicationError extends Error {
+ constructor(message, exitCode, options) {
+ super(message, options);
+
+ this.name = 'ApplicationError';
+ this.exitCode = exitCode;
+ }
+}
+
// Ensures that a Gio.UnixFDList being passed into or out of a DBus method with
// a parameter type that includes 'h' somewhere, actually has entries in it for
// each of the indices being passed as an 'h' parameter.
@@ -443,14 +452,20 @@ function _promisify(proto, asyncFunc,
function _init() {
Gio = this;
+ Gio.ApplicationError = ApplicationError;
+
Gio.Application.prototype.runAsync = function (...args) {
return new Promise((resolve, reject) => {
setMainLoopHook(() => {
try {
- this.run(...args);
- resolve();
+ const exitCode = this.run(...args);
+
+ if (exitCode)
+ reject(new ApplicationError('Application exited with unknown error', exitCode));
+ else
+ resolve();
} catch (error) {
- reject(error);
+ reject(new ApplicationError('Application exited with error', 1, {cause: error}));
}
});
});
diff --git a/modules/core/overrides/Gtk.js b/modules/core/overrides/Gtk.js
index 5dfc0e69a..a749669bb 100644
--- a/modules/core/overrides/Gtk.js
+++ b/modules/core/overrides/Gtk.js
@@ -5,7 +5,6 @@
const Legacy = imports._legacy;
const {Gio, GjsPrivate, GObject} = imports.gi;
const {_registerType} = imports._common;
-const {setMainLoopHook} = imports._promiseNative;
let Gtk;
let BuilderScope;
@@ -13,18 +12,7 @@ let BuilderScope;
function _init() {
Gtk = this;
- Gtk.Application.prototype.runAsync = function (...args) {
- return new Promise((resolve, reject) => {
- setMainLoopHook(() => {
- try {
- this.run(...args);
- resolve();
- } catch (error) {
- reject(error);
- }
- });
- });
- };
+ Gtk.ApplicationError = Gio.ApplicationError;
Gtk.children = GObject.__gtkChildren__;
Gtk.cssName = GObject.__gtkCssName__;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]