summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/main.rs4
-rw-r--r--cli/tests/integration/run_tests.rs18
-rw-r--r--cli/tests/testdata/empty.out0
-rw-r--r--cli/tests/testdata/set_exit_code_0.ts2
-rw-r--r--cli/tests/testdata/set_exit_code_1.ts2
-rw-r--r--cli/tests/testdata/set_exit_code_2.ts2
-rw-r--r--runtime/js/30_os.js11
-rw-r--r--runtime/lib.rs11
-rw-r--r--runtime/ops/os.rs10
9 files changed, 57 insertions, 3 deletions
diff --git a/cli/main.rs b/cli/main.rs
index e2ef26cd3..3d8a80cee 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -93,6 +93,7 @@ use std::iter::once;
use std::path::PathBuf;
use std::pin::Pin;
use std::rc::Rc;
+use std::sync::atomic::Ordering::Relaxed;
use std::sync::Arc;
fn create_web_worker_callback(ps: ProcState) -> Arc<CreateWebWorkerCb> {
@@ -1475,4 +1476,7 @@ pub fn main() {
logger::init(flags.log_level);
unwrap_or_exit(run_basic(get_subcommand(flags)));
+
+ let code = deno_runtime::EXIT_CODE.load(Relaxed);
+ std::process::exit(code);
}
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index d6a5a4c25..47041e499 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -859,6 +859,24 @@ itest!(exit_error42 {
output: "exit_error42.ts.out",
});
+itest!(set_exit_code_0 {
+ args: "run --no-check --unstable set_exit_code_0.ts",
+ output: "empty.out",
+ exit_code: 0,
+});
+
+itest!(set_exit_code_1 {
+ args: "run --no-check --unstable set_exit_code_1.ts",
+ output: "empty.out",
+ exit_code: 42,
+});
+
+itest!(set_exit_code_2 {
+ args: "run --no-check --unstable set_exit_code_2.ts",
+ output: "empty.out",
+ exit_code: 42,
+});
+
itest!(heapstats {
args: "run --quiet --unstable --v8-flags=--expose-gc heapstats.js",
output: "heapstats.js.out",
diff --git a/cli/tests/testdata/empty.out b/cli/tests/testdata/empty.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/cli/tests/testdata/empty.out
diff --git a/cli/tests/testdata/set_exit_code_0.ts b/cli/tests/testdata/set_exit_code_0.ts
new file mode 100644
index 000000000..7eb14fa3c
--- /dev/null
+++ b/cli/tests/testdata/set_exit_code_0.ts
@@ -0,0 +1,2 @@
+Deno.core.opSync("op_set_exit_code", 42);
+Deno.exit(0); // Takes precedence.
diff --git a/cli/tests/testdata/set_exit_code_1.ts b/cli/tests/testdata/set_exit_code_1.ts
new file mode 100644
index 000000000..96ec9889c
--- /dev/null
+++ b/cli/tests/testdata/set_exit_code_1.ts
@@ -0,0 +1,2 @@
+Deno.core.opSync("op_set_exit_code", 42);
+Deno.exit();
diff --git a/cli/tests/testdata/set_exit_code_2.ts b/cli/tests/testdata/set_exit_code_2.ts
new file mode 100644
index 000000000..a1f2b5d3c
--- /dev/null
+++ b/cli/tests/testdata/set_exit_code_2.ts
@@ -0,0 +1,2 @@
+Deno.core.opSync("op_set_exit_code", 42);
+// Exits naturally.
diff --git a/runtime/js/30_os.js b/runtime/js/30_os.js
index 15df6f554..f6bada6a5 100644
--- a/runtime/js/30_os.js
+++ b/runtime/js/30_os.js
@@ -31,7 +31,14 @@
exitHandler = fn;
}
- function exit(code = 0) {
+ function exit(code) {
+ // Set exit code first so unload event listeners can override it.
+ if (typeof code === "number") {
+ core.opSync("op_set_exit_code", code);
+ } else {
+ code = 0;
+ }
+
// Dispatches `unload` only when it's not dispatched yet.
if (!window[SymbolFor("isUnloadDispatched")]) {
// Invokes the `unload` hooks before exiting
@@ -44,7 +51,7 @@
return;
}
- core.opSync("op_exit", code);
+ core.opSync("op_exit");
throw new Error("Code not reachable");
}
diff --git a/runtime/lib.rs b/runtime/lib.rs
index fb6159dd9..ca250ce20 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -1,5 +1,7 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+use std::sync::atomic::AtomicI32;
+
pub use deno_broadcast_channel;
pub use deno_console;
pub use deno_crypto;
@@ -29,3 +31,12 @@ pub mod worker;
mod worker_bootstrap;
pub use worker_bootstrap::BootstrapOptions;
+
+// The global may not be very elegant but:
+//
+// 1. op_exit() calls std::process::exit() so there is not much point storing
+// the exit code in runtime state
+//
+// 2. storing it in runtime state makes retrieving it again in cli/main.rs
+// unduly complicated
+pub static EXIT_CODE: AtomicI32 = AtomicI32::new(0);
diff --git a/runtime/ops/os.rs b/runtime/ops/os.rs
index 0a6269ac5..8e96de75d 100644
--- a/runtime/ops/os.rs
+++ b/runtime/ops/os.rs
@@ -10,6 +10,7 @@ use deno_core::OpState;
use serde::Serialize;
use std::collections::HashMap;
use std::env;
+use std::sync::atomic::Ordering::Relaxed;
pub fn init() -> Extension {
Extension::builder()
@@ -23,6 +24,7 @@ pub fn init() -> Extension {
("op_hostname", op_sync(op_hostname)),
("op_loadavg", op_sync(op_loadavg)),
("op_os_release", op_sync(op_os_release)),
+ ("op_set_exit_code", op_sync(op_set_exit_code)),
("op_system_memory_info", op_sync(op_system_memory_info)),
])
.build()
@@ -95,7 +97,13 @@ fn op_delete_env(
Ok(())
}
-fn op_exit(_state: &mut OpState, code: i32, _: ()) -> Result<(), AnyError> {
+fn op_set_exit_code(_: &mut OpState, code: i32, _: ()) -> Result<(), AnyError> {
+ crate::EXIT_CODE.store(code, Relaxed);
+ Ok(())
+}
+
+fn op_exit(_: &mut OpState, _: (), _: ()) -> Result<(), AnyError> {
+ let code = crate::EXIT_CODE.load(Relaxed);
std::process::exit(code)
}