Skip to content

Commit

Permalink
feat(runtime): add op_set_exit_code (denoland#12911)
Browse files Browse the repository at this point in the history
Set the exit code to use if none is provided to Deno.exit(), or when
Deno exits naturally.

Needed for process.exitCode Node compat. Paves the way for denoland#12888.
  • Loading branch information
bnoordhuis authored Nov 27, 2021
1 parent 1d3f734 commit 993a1dd
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 3 deletions.
4 changes: 4 additions & 0 deletions cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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);
}
18 changes: 18 additions & 0 deletions cli/tests/integration/run_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Empty file added cli/tests/testdata/empty.out
Empty file.
2 changes: 2 additions & 0 deletions cli/tests/testdata/set_exit_code_0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Deno.core.opSync("op_set_exit_code", 42);
Deno.exit(0); // Takes precedence.
2 changes: 2 additions & 0 deletions cli/tests/testdata/set_exit_code_1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Deno.core.opSync("op_set_exit_code", 42);
Deno.exit();
2 changes: 2 additions & 0 deletions cli/tests/testdata/set_exit_code_2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Deno.core.opSync("op_set_exit_code", 42);
// Exits naturally.
11 changes: 9 additions & 2 deletions runtime/js/30_os.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -44,7 +51,7 @@
return;
}

core.opSync("op_exit", code);
core.opSync("op_exit");
throw new Error("Code not reachable");
}

Expand Down
11 changes: 11 additions & 0 deletions runtime/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
10 changes: 9 additions & 1 deletion runtime/ops/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand Down Expand Up @@ -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)
}

Expand Down

0 comments on commit 993a1dd

Please sign in to comment.