src: consolidate exit codes in the code base

Add an ExitCode enum class and use it throughout the code base
instead of hard-coding the exit codes everywhere. At the moment,
the exit codes used in many places do not actually conform to
what the documentation describes. With the new enums (which
are also available to the JS land as constants in an internal
binding) we could migrate to a more consistent usage of the
codes, and eventually expose the constants to the user land
when they are stable enough.

PR-URL: https://github.com/nodejs/node/pull/44746
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
This commit is contained in:
Joyee Cheung authored and GitHub committed 2022-10-06 12:41:16 +00:00
1 parent 66cedb4600
commit be525d7d04
36 files changed
+381 -177

No files matched your search

+3 -1
View File
@@ -8,6 +8,8 @@ const {
Symbol,
} = primordials;
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
const promiseHooks = require('internal/promise_hooks');
const async_wrap = internalBinding('async_wrap');
@@ -171,7 +173,7 @@ function fatalError(e) {
if (getOptionValue('--abort-on-uncaught-exception')) {
process.abort();
}
process.exit(1);
process.exit(kGenericUserError);
}
function lookupPublicResource(resource) {
+5 -3
View File
@@ -15,6 +15,8 @@ const EventEmitter = require('events');
const { owner_symbol } = require('internal/async_hooks').symbols;
const Worker = require('internal/cluster/worker');
const { internal, sendHelper } = require('internal/cluster/utils');
const { exitCodes: { kNoFailure } } = internalBinding('errors');
const cluster = new EventEmitter();
const handles = new SafeMap();
const indexes = new SafeMap();
@@ -43,7 +45,7 @@ cluster._setupWorker = function() {
if (!worker.exitedAfterDisconnect) {
// Unexpected disconnect, primary exited, or some such nastiness, so
// worker exits immediately.
process.exit(0);
process.exit(kNoFailure);
}
});
@@ -278,10 +280,10 @@ Worker.prototype.destroy = function() {
this.exitedAfterDisconnect = true;
if (!this.isConnected()) {
process.exit(0);
process.exit(kNoFailure);
} else {
this.state = 'destroying';
send({ act: 'exitedAfterDisconnect' }, () => process.disconnect());
process.once('disconnect', () => process.exit(0));
process.once('disconnect', () => process.exit(kNoFailure));
}
};
+12 -5
View File
@@ -44,6 +44,12 @@ const { 0: InspectClient, 1: createRepl } =
const debuglog = util.debuglog('inspect');
const { ERR_DEBUGGER_STARTUP_ERROR } = require('internal/errors').codes;
const {
exitCodes: {
kGenericUserError,
kNoFailure,
},
} = internalBinding('errors');
async function portIsFree(host, port, timeout = 3000) {
if (port === 0) return; // Binding to a random port.
@@ -167,7 +173,7 @@ class NodeInspector {
// Handle all possible exits
process.on('exit', () => this.killChild());
const exitCodeZero = () => process.exit(0);
const exitCodeZero = () => process.exit(kNoFailure);
process.once('SIGTERM', exitCodeZero);
process.once('SIGHUP', exitCodeZero);
@@ -234,7 +240,7 @@ class NodeInspector {
}
}
this.stdout.write(' failed to connect, please retry\n');
process.exit(1);
process.exit(kGenericUserError);
}
clearLine() {
@@ -314,7 +320,7 @@ function parseArgv(args) {
} catch (e) {
if (e.code === 'ESRCH') {
console.error(`Target process: ${pid} doesn't exist.`);
process.exit(1);
process.exit(kGenericUserError);
}
throw e;
}
@@ -337,7 +343,8 @@ function startInspect(argv = ArrayPrototypeSlice(process.argv, 2),
console.error(` ${invokedAs} <host>:<port>`);
console.error(` ${invokedAs} --port=<port>`);
console.error(` ${invokedAs} -p <pid>`);
process.exit(1);
// TODO(joyeecheung): should be kInvalidCommandLineArgument.
process.exit(kGenericUserError);
}
const options = parseArgv(argv);
@@ -355,7 +362,7 @@ function startInspect(argv = ArrayPrototypeSlice(process.argv, 2),
console.error(e.message);
}
if (inspector.child) inspector.child.kill();
process.exit(1);
process.exit(kGenericUserError);
}
process.on('uncaughtException', handleUnexpectedError);
+4 -1
View File
@@ -17,6 +17,8 @@ const console = require('internal/console/global');
const { getOptionValue } = require('internal/options');
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
prepareMainThreadExecution();
markBootstrapComplete();
@@ -31,7 +33,8 @@ if (process.env.NODE_REPL_EXTERNAL_MODULE) {
// If we can't write to stderr, we'd like to make this a noop,
// so use console.error.
console.error('Cannot specify --input-type for REPL');
process.exit(1);
// TODO(joyeecheung): should be kInvalidCommandLineArgument.
process.exit(kGenericUserError);
}
esmLoader.loadESM(() => {
+2 -1
View File
@@ -5,6 +5,7 @@ const {
} = require('internal/process/pre_execution');
const { isUsingInspector } = require('internal/util/inspector');
const { run } = require('internal/test_runner/runner');
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
prepareMainThreadExecution(false);
markBootstrapComplete();
@@ -22,5 +23,5 @@ if (isUsingInspector()) {
const tapStream = run({ concurrency, inspectPort });
tapStream.pipe(process.stdout);
tapStream.once('test:fail', () => {
process.exitCode = 1;
process.exitCode = kGenericUserError;
});
+5 -3
View File
@@ -12,7 +12,10 @@ const {
prepareMainThreadExecution,
markBootstrapComplete
} = require('internal/process/pre_execution');
const { triggerUncaughtException } = internalBinding('errors');
const {
triggerUncaughtException,
exitCodes: { kNoFailure },
} = internalBinding('errors');
const { getOptionValue } = require('internal/options');
const { emitExperimentalWarning } = require('internal/util');
const { FilesWatcher } = require('internal/watch_mode/files_watcher');
@@ -24,7 +27,6 @@ const { setTimeout, clearTimeout } = require('timers');
const { resolve } = require('path');
const { once, on } = require('events');
prepareMainThreadExecution(false, false);
markBootstrapComplete();
@@ -125,7 +127,7 @@ function signalHandler(signal) {
return async () => {
watcher.clear();
const exitCode = await killAndWait(signal, true);
process.exit(exitCode ?? 0);
process.exit(exitCode ?? kNoFailure);
};
}
process.on('SIGTERM', signalHandler('SIGTERM'));
+2 -1
View File
@@ -66,6 +66,7 @@ let debug = require('internal/util/debuglog').debuglog('worker', (fn) => {
});
const assert = require('internal/assert');
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
patchProcessObject();
setupInspectorHooks();
@@ -234,7 +235,7 @@ function workerOnGlobalUncaughtException(error, fromPromise) {
if (!process._exiting) {
try {
process._exiting = true;
process.exitCode = 1;
process.exitCode = kGenericUserError;
if (!handlerThrew) {
process.emit('exit', process.exitCode);
}
@@ -1,10 +1,12 @@
'use strict';
const { exitCodes: { kUnfinishedTopLevelAwait } } = internalBinding('errors');
// Handle a Promise from running code that potentially does Top-Level Await.
// In that case, it makes sense to set the exit code to a specific non-zero
// value if the main code never finishes running.
function handleProcessExit() {
process.exitCode ??= 13;
process.exitCode ??= kUnfinishedTopLevelAwait;
}
module.exports = {
+3 -1
View File
@@ -40,6 +40,8 @@ const { getOptionValue } = require('internal/options');
const shouldAbortOnUncaughtException = getOptionValue(
'--abort-on-uncaught-exception'
);
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
const { abort, exit, _rawDebug } = process;
// #endregion
@@ -72,7 +74,7 @@ function REACTION_EXIT(error) {
if (shouldAbortOnUncaughtException) {
abort();
}
exit(1);
exit(kGenericUserError);
}
function REACTION_LOG(error) {
+3 -2
View File
@@ -14,6 +14,7 @@ const {
ERR_EVAL_ESM_CANNOT_PRINT,
},
} = require('internal/errors');
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
const {
executionAsyncId,
@@ -161,8 +162,8 @@ function createOnGlobalUncaughtException() {
try {
if (!process._exiting) {
process._exiting = true;
process.exitCode = 1;
process.emit('exit', 1);
process.exitCode = kGenericUserError;
process.emit('exit', kGenericUserError);
}
} catch {
// Nothing to be done about it at this point.
+3 -2
View File
@@ -58,6 +58,7 @@ const kInternal = Symbol('internal properties');
function assert(x, msg) {
if (!x) throw new ERR_ASSERTION(msg || 'assertion error');
}
const { exitCodes: { kNoFailure } } = internalBinding('errors');
const binding = internalBinding('process_methods');
@@ -187,12 +188,12 @@ function wrapProcessMethods(binding) {
if (!process._exiting) {
process._exiting = true;
process.emit('exit', process.exitCode || 0);
process.emit('exit', process.exitCode || kNoFailure);
}
// FIXME(joyeecheung): This is an undocumented API that gets monkey-patched
// in the user land. Either document it, or deprecate it in favor of a
// better public alternative.
process.reallyExit(process.exitCode || 0);
process.reallyExit(process.exitCode || kNoFailure);
}
function kill(pid, sig) {
+3 -2
View File
@@ -24,7 +24,8 @@ const { deprecate } = require('internal/util');
const {
noSideEffectsToString,
triggerUncaughtException
triggerUncaughtException,
exitCodes: { kGenericUserError },
} = internalBinding('errors');
const {
@@ -294,7 +295,7 @@ function processPromiseRejections() {
const handled = emit(reason, promise, promiseInfo);
if (!handled) {
emitUnhandledRejectionWarning(uid, reason);
process.exitCode = 1;
process.exitCode = kGenericUserError;
}
break;
}
+3 -1
View File
@@ -13,6 +13,8 @@ const {
ERR_TEST_FAILURE,
},
} = require('internal/errors');
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
const { kEmptyObject } = require('internal/util');
const { getOptionValue } = require('internal/options');
const { kCancelledByParent, Test, ItTest, Suite } = require('internal/test_runner/test');
@@ -118,7 +120,7 @@ function getGlobalRoot() {
globalRoot = createTestTree();
globalRoot.reporter.pipe(process.stdout);
globalRoot.reporter.once('test:fail', () => {
process.exitCode = 1;
process.exitCode = kGenericUserError;
});
}
return globalRoot;
+2 -1
View File
@@ -34,6 +34,7 @@ const {
} = require('internal/test_runner/utils');
const { basename, join, resolve } = require('path');
const { once } = require('events');
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
const kFilterArgs = ['--test'];
@@ -90,7 +91,7 @@ function createTestFileList() {
} catch (err) {
if (err?.code === 'ENOENT') {
console.error(`Could not find '${err.path}'`);
process.exit(1);
process.exit(kGenericUserError);
}
throw err;
+1
View File
@@ -600,6 +600,7 @@
'src/node_contextify.h',
'src/node_dir.h',
'src/node_errors.h',
'src/node_exit_code.h',
'src/node_external_reference.h',
'src/node_file.h',
'src/node_file-inl.h',
+12 -4
View File
@@ -7,6 +7,7 @@ using v8::Function;
using v8::Global;
using v8::HandleScope;
using v8::Isolate;
using v8::Just;
using v8::Local;
using v8::Locker;
using v8::Maybe;
@@ -15,7 +16,7 @@ using v8::SealHandleScope;
namespace node {
Maybe<int> SpinEventLoop(Environment* env) {
Maybe<ExitCode> SpinEventLoopInternal(Environment* env) {
CHECK_NOT_NULL(env);
MultiIsolatePlatform* platform = GetMultiIsolatePlatform(env);
CHECK_NOT_NULL(platform);
@@ -25,7 +26,7 @@ Maybe<int> SpinEventLoop(Environment* env) {
Context::Scope context_scope(env->context());
SealHandleScope seal(isolate);
if (env->is_stopping()) return Nothing<int>();
if (env->is_stopping()) return Nothing<ExitCode>();
env->set_trace_sync_io(env->options()->trace_sync_io);
{
@@ -59,7 +60,7 @@ Maybe<int> SpinEventLoop(Environment* env) {
env->performance_state()->Mark(
node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
}
if (env->is_stopping()) return Nothing<int>();
if (env->is_stopping()) return Nothing<ExitCode>();
env->set_trace_sync_io(false);
// Clear the serialize callback even though the JS-land queue should
@@ -69,7 +70,7 @@ Maybe<int> SpinEventLoop(Environment* env) {
env->PrintInfoForSnapshotIfDebug();
env->ForEachRealm([](Realm* realm) { realm->VerifyNoStrongBaseObjects(); });
return EmitProcessExit(env);
return EmitProcessExitInternal(env);
}
struct CommonEnvironmentSetup::Impl {
@@ -155,6 +156,13 @@ CommonEnvironmentSetup::~CommonEnvironmentSetup() {
delete impl_;
}
Maybe<int> SpinEventLoop(Environment* env) {
Maybe<ExitCode> result = SpinEventLoopInternal(env);
if (result.IsNothing()) {
return Nothing<int>();
}
return Just(static_cast<int>(result.FromJust()));
}
uv_loop_t* CommonEnvironmentSetup::event_loop() const {
return &impl_->loop;
+22 -4
View File
@@ -1,7 +1,9 @@
#include <cstdlib>
#include "node.h"
#include "node_builtins.h"
#include "node_context_data.h"
#include "node_errors.h"
#include "node_exit_code.h"
#include "node_internals.h"
#include "node_options-inl.h"
#include "node_platform.h"
@@ -368,6 +370,7 @@ Environment* CreateEnvironment(
Environment* env = new Environment(
isolate_data, context, args, exec_args, nullptr, flags, thread_id);
#if HAVE_INSPECTOR
// TODO(joyeecheung): handle the exit code returned by InitializeInspector().
if (env->should_create_inspector()) {
if (inspector_parent_handle) {
env->InitializeInspector(
@@ -763,19 +766,34 @@ ThreadId AllocateEnvironmentThreadId() {
return ThreadId { next_thread_id++ };
}
void DefaultProcessExitHandler(Environment* env, int exit_code) {
[[noreturn]] void Exit(ExitCode exit_code) {
exit(static_cast<int>(exit_code));
}
void DefaultProcessExitHandlerInternal(Environment* env, ExitCode exit_code) {
env->set_can_call_into_js(false);
env->stop_sub_worker_contexts();
env->isolate()->DumpAndResetStats();
DisposePlatform();
uv_library_shutdown();
exit(exit_code);
Exit(exit_code);
}
void DefaultProcessExitHandler(Environment* env, int exit_code) {
DefaultProcessExitHandlerInternal(env, static_cast<ExitCode>(exit_code));
}
void SetProcessExitHandler(Environment* env,
std::function<void(Environment*, int)>&& handler) {
void SetProcessExitHandler(
Environment* env, std::function<void(Environment*, ExitCode)>&& handler) {
env->set_process_exit_handler(std::move(handler));
}
void SetProcessExitHandler(Environment* env,
std::function<void(Environment*, int)>&& handler) {
auto movedHandler = std::move(handler);
env->set_process_exit_handler([=](Environment* env, ExitCode exit_code) {
movedHandler(env, static_cast<int>(exit_code));
});
}
} // namespace node
+17 -5
View File
@@ -53,11 +53,15 @@ Maybe<bool> EmitProcessBeforeExit(Environment* env) {
Nothing<bool>() : Just(true);
}
int EmitExit(Environment* env) {
return EmitProcessExit(env).FromMaybe(1);
static ExitCode EmitExitInternal(Environment* env) {
return EmitProcessExitInternal(env).FromMaybe(ExitCode::kGenericUserError);
}
Maybe<int> EmitProcessExit(Environment* env) {
int EmitExit(Environment* env) {
return static_cast<int>(EmitExitInternal(env));
}
Maybe<ExitCode> EmitProcessExitInternal(Environment* env) {
// process.emit('exit')
Isolate* isolate = env->isolate();
HandleScope handle_scope(isolate);
@@ -80,10 +84,18 @@ Maybe<int> EmitProcessExit(Environment* env) {
// Reload exit code, it may be changed by `emit('exit')`
!process_object->Get(context, exit_code).ToLocal(&code_v) ||
!code_v->Int32Value(context).To(&code)) {
return Nothing<int>();
return Nothing<ExitCode>();
}
return Just(code);
return Just(static_cast<ExitCode>(code));
}
Maybe<int> EmitProcessExit(Environment* env) {
Maybe<ExitCode> result = EmitProcessExitInternal(env);
if (result.IsNothing()) {
return Nothing<int>();
}
return Just(static_cast<int>(result.FromJust()));
}
typedef void (*CleanupHook)(void* arg);
+1 -1
View File
@@ -801,7 +801,7 @@ void Environment::set_main_utf16(std::unique_ptr<v8::String::Value> str) {
}
void Environment::set_process_exit_handler(
std::function<void(Environment*, int)>&& handler) {
std::function<void(Environment*, ExitCode)>&& handler) {
process_exit_handler_ = std::move(handler);
}
+7 -6
View File
@@ -1500,14 +1500,14 @@ void AsyncHooks::FailWithCorruptedAsyncStack(double expected_async_id) {
expected_async_id);
DumpBacktrace(stderr);
fflush(stderr);
if (!env()->abort_on_uncaught_exception())
exit(1);
// TODO(joyeecheung): should this exit code be more specific?
if (!env()->abort_on_uncaught_exception()) Exit(ExitCode::kGenericUserError);
fprintf(stderr, "\n");
fflush(stderr);
ABORT_NO_BACKTRACE();
}
void Environment::Exit(int exit_code) {
void Environment::Exit(ExitCode exit_code) {
if (options()->trace_exit) {
HandleScope handle_scope(isolate());
Isolate::DisallowJavascriptExecutionScope disallow_js(
@@ -1520,8 +1520,9 @@ void Environment::Exit(int exit_code) {
uv_os_getpid(), thread_id());
}
fprintf(
stderr, "WARNING: Exited the environment with code %d\n", exit_code);
fprintf(stderr,
"WARNING: Exited the environment with code %d\n",
static_cast<int>(exit_code));
PrintStackTrace(isolate(),
StackTrace::CurrentStackTrace(
isolate(), stack_trace_limit(), StackTrace::kDetailed));
@@ -1535,7 +1536,7 @@ void Environment::stop_sub_worker_contexts() {
while (!sub_worker_contexts_.empty()) {
Worker* w = *sub_worker_contexts_.begin();
remove_sub_worker_context(w);
w->Exit(1);
w->Exit(ExitCode::kGenericUserError);
w->JoinThread();
}
}
+10 -5
View File
@@ -37,6 +37,7 @@
#include "node.h"
#include "node_binding.h"
#include "node_builtins.h"
#include "node_exit_code.h"
#include "node_main_instance.h"
#include "node_options.h"
#include "node_perf_common.h"
@@ -577,6 +578,10 @@ struct SnapshotData {
SnapshotData() = default;
};
void DefaultProcessExitHandlerInternal(Environment* env, ExitCode exit_code);
v8::Maybe<ExitCode> SpinEventLoopInternal(Environment* env);
v8::Maybe<ExitCode> EmitProcessExitInternal(Environment* env);
/**
* Environment is a per-isolate data structure that represents an execution
* environment. Each environment has a principal realm. An environment can
@@ -612,7 +617,7 @@ class Environment : public MemoryRetainer {
#if HAVE_INSPECTOR
// If the environment is created for a worker, pass parent_handle and
// the ownership if transferred into the Environment.
int InitializeInspector(
ExitCode InitializeInspector(
std::unique_ptr<inspector::ParentInspectorHandle> parent_handle);
#endif
@@ -685,7 +690,7 @@ class Environment : public MemoryRetainer {
void RegisterHandleCleanups();
void CleanupHandles();
void Exit(int code);
void Exit(ExitCode code);
void ExitEnv();
// Register clean-up cb to be called on environment destruction.
@@ -1010,7 +1015,7 @@ class Environment : public MemoryRetainer {
inline void set_main_utf16(std::unique_ptr<v8::String::Value>);
inline void set_process_exit_handler(
std::function<void(Environment*, int)>&& handler);
std::function<void(Environment*, ExitCode)>&& handler);
void RunAndClearNativeImmediates(bool only_refed = false);
void RunAndClearInterrupts();
@@ -1176,8 +1181,8 @@ class Environment : public MemoryRetainer {
std::unordered_set<int> unmanaged_fds_;
std::function<void(Environment*, int)> process_exit_handler_ {
DefaultProcessExitHandler };
std::function<void(Environment*, ExitCode)> process_exit_handler_{
DefaultProcessExitHandlerInternal};
std::unique_ptr<Realm> principal_realm_ = nullptr;
+90 -57
View File
@@ -166,7 +166,7 @@ void SignalExit(int signo, siginfo_t* info, void* ucontext) {
#endif // __POSIX__
#if HAVE_INSPECTOR
int Environment::InitializeInspector(
ExitCode Environment::InitializeInspector(
std::unique_ptr<inspector::ParentInspectorHandle> parent_handle) {
std::string inspector_path;
bool is_main = !parent_handle;
@@ -187,7 +187,7 @@ int Environment::InitializeInspector(
is_main);
if (options_->debug_options().inspector_enabled &&
!inspector_agent_->IsListening()) {
return 12; // Signal internal error
return ExitCode::kInvalidCommandLineArgument2; // Signal internal error
}
profiler::StartProfilers(this);
@@ -196,7 +196,7 @@ int Environment::InitializeInspector(
inspector_agent_->PauseOnNextJavascriptStatement("Break at bootstrap");
}
return 0;
return ExitCode::kNoFailure;
}
#endif // HAVE_INSPECTOR
@@ -626,11 +626,10 @@ void ResetStdio() {
#endif // __POSIX__
}
int ProcessGlobalArgs(std::vector<std::string>* args,
std::vector<std::string>* exec_args,
std::vector<std::string>* errors,
OptionEnvvarSettings settings) {
static ExitCode ProcessGlobalArgsInternal(std::vector<std::string>* args,
std::vector<std::string>* exec_args,
std::vector<std::string>* errors,
OptionEnvvarSettings settings) {
// Parse a few arguments which are specific to Node.
std::vector<std::string> v8_args;
@@ -643,14 +642,15 @@ int ProcessGlobalArgs(std::vector<std::string>* args,
settings,
errors);
if (!errors->empty()) return 9;
if (!errors->empty()) return ExitCode::kInvalidCommandLineArgument;
std::string revert_error;
for (const std::string& cve : per_process::cli_options->security_reverts) {
Revert(cve.c_str(), &revert_error);
if (!revert_error.empty()) {
errors->emplace_back(std::move(revert_error));
return 12;
// TODO(joyeecheung): merge into kInvalidCommandLineArgument.
return ExitCode::kInvalidCommandLineArgument2;
}
}
@@ -658,7 +658,8 @@ int ProcessGlobalArgs(std::vector<std::string>* args,
per_process::cli_options->disable_proto != "throw" &&
per_process::cli_options->disable_proto != "") {
errors->emplace_back("invalid mode passed to --disable-proto");
return 12;
// TODO(joyeecheung): merge into kInvalidCommandLineArgument.
return ExitCode::kInvalidCommandLineArgument2;
}
// TODO(aduh95): remove this when the harmony-import-assertions flag
@@ -698,19 +699,29 @@ int ProcessGlobalArgs(std::vector<std::string>* args,
for (size_t i = 1; i < v8_args_as_char_ptr.size(); i++)
errors->push_back("bad option: " + std::string(v8_args_as_char_ptr[i]));
if (v8_args_as_char_ptr.size() > 1) return 9;
if (v8_args_as_char_ptr.size() > 1)
return ExitCode::kInvalidCommandLineArgument;
return 0;
return ExitCode::kNoFailure;
}
int ProcessGlobalArgs(std::vector<std::string>* args,
std::vector<std::string>* exec_args,
std::vector<std::string>* errors,
OptionEnvvarSettings settings) {
return static_cast<int>(
ProcessGlobalArgsInternal(args, exec_args, errors, settings));
}
static std::atomic_bool init_called{false};
// TODO(addaleax): Turn this into a wrapper around InitializeOncePerProcess()
// (with the corresponding additional flags set), then eventually remove this.
int InitializeNodeWithArgs(std::vector<std::string>* argv,
std::vector<std::string>* exec_argv,
std::vector<std::string>* errors,
ProcessInitializationFlags::Flags flags) {
static ExitCode InitializeNodeWithArgsInternal(
std::vector<std::string>* argv,
std::vector<std::string>* exec_argv,
std::vector<std::string>* errors,
ProcessInitializationFlags::Flags flags) {
// Make sure InitializeNodeWithArgs() is called only once.
CHECK(!init_called.exchange(true));
@@ -747,26 +758,22 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
std::vector<std::string> env_argv =
ParseNodeOptionsEnvVar(node_options, errors);
if (!errors->empty()) return 9;
if (!errors->empty()) return ExitCode::kInvalidCommandLineArgument;
// [0] is expected to be the program name, fill it in from the real argv.
env_argv.insert(env_argv.begin(), argv->at(0));
const int exit_code = ProcessGlobalArgs(&env_argv,
nullptr,
errors,
kAllowedInEnvironment);
if (exit_code != 0) return exit_code;
const ExitCode exit_code = ProcessGlobalArgsInternal(
&env_argv, nullptr, errors, kAllowedInEnvironment);
if (exit_code != ExitCode::kNoFailure) return exit_code;
}
}
#endif
if (!(flags & ProcessInitializationFlags::kDisableCLIOptions)) {
const int exit_code = ProcessGlobalArgs(argv,
exec_argv,
errors,
kDisallowedInEnvironment);
if (exit_code != 0) return exit_code;
const ExitCode exit_code = ProcessGlobalArgsInternal(
argv, exec_argv, errors, kDisallowedInEnvironment);
if (exit_code != ExitCode::kNoFailure) return exit_code;
}
// Set the process.title immediately after processing argv if --title is set.
@@ -803,7 +810,7 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
if (!i18n::InitializeICUDirectory(per_process::cli_options->icu_data_dir)) {
errors->push_back("could not initialize ICU "
"(check NODE_ICU_DATA or --icu-data-dir parameters)\n");
return 9;
return ExitCode::kInvalidCommandLineArgument;
}
per_process::metadata.versions.InitializeIntlVersions();
}
@@ -821,12 +828,21 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
// otherwise embedders using node::Init to initialize everything will not be
// able to set it and native addons will not load for them.
node_is_initialized = true;
return 0;
return ExitCode::kNoFailure;
}
std::unique_ptr<InitializationResult> InitializeOncePerProcess(
const std::vector<std::string>& args,
ProcessInitializationFlags::Flags flags) {
int InitializeNodeWithArgs(std::vector<std::string>* argv,
std::vector<std::string>* exec_argv,
std::vector<std::string>* errors,
ProcessInitializationFlags::Flags flags) {
return static_cast<int>(
InitializeNodeWithArgsInternal(argv, exec_argv, errors, flags));
}
static std::unique_ptr<InitializationResultImpl>
InitializeOncePerProcessInternal(const std::vector<std::string>& args,
ProcessInitializationFlags::Flags flags =
ProcessInitializationFlags::kNoFlags) {
auto result = std::make_unique<InitializationResultImpl>();
result->args_ = args;
@@ -840,9 +856,9 @@ std::unique_ptr<InitializationResult> InitializeOncePerProcess(
// This needs to run *before* V8::Initialize().
{
result->exit_code_ = InitializeNodeWithArgs(
result->exit_code_ = InitializeNodeWithArgsInternal(
&result->args_, &result->exec_args_, &result->errors_, flags);
if (result->exit_code() != 0) {
if (result->exit_code_enum() != ExitCode::kNoFailure) {
result->early_return_ = true;
return result;
}
@@ -860,7 +876,7 @@ std::unique_ptr<InitializationResult> InitializeOncePerProcess(
if (!(flags & ProcessInitializationFlags::kNoPrintHelpOrVersionOutput)) {
if (per_process::cli_options->print_version) {
printf("%s\n", NODE_VERSION);
result->exit_code_ = 0;
result->exit_code_ = ExitCode::kNoFailure;
result->early_return_ = true;
return result;
}
@@ -868,14 +884,14 @@ std::unique_ptr<InitializationResult> InitializeOncePerProcess(
if (per_process::cli_options->print_bash_completion) {
std::string completion = options_parser::GetBashCompletion();
printf("%s\n", completion.c_str());
result->exit_code_ = 0;
result->exit_code_ = ExitCode::kNoFailure;
result->early_return_ = true;
return result;
}
if (per_process::cli_options->print_v8_help) {
V8::SetFlagsFromString("--help", static_cast<size_t>(6));
result->exit_code_ = 0;
result->exit_code_ = ExitCode::kNoFailure;
result->early_return_ = true;
return result;
}
@@ -952,7 +968,8 @@ std::unique_ptr<InitializationResult> InitializeOncePerProcess(
if (ERR_peek_error() != 0) {
// XXX: ERR_GET_REASON does not return something that is
// useful as an exit code at all.
result->exit_code_ = ERR_GET_REASON(ERR_peek_error());
result->exit_code_ =
static_cast<ExitCode>(ERR_GET_REASON(ERR_peek_error()));
result->early_return_ = true;
result->errors_.emplace_back("OpenSSL configuration error:\n" +
GetOpenSSLErrorString());
@@ -966,7 +983,8 @@ std::unique_ptr<InitializationResult> InitializeOncePerProcess(
if (!crypto::ProcessFipsOptions()) {
// XXX: ERR_GET_REASON does not return something that is
// useful as an exit code at all.
result->exit_code_ = ERR_GET_REASON(ERR_peek_error());
result->exit_code_ =
static_cast<ExitCode>(ERR_GET_REASON(ERR_peek_error()));
result->early_return_ = true;
result->errors_.emplace_back(
"OpenSSL error when trying to enable FIPS:\n" +
@@ -1004,6 +1022,12 @@ std::unique_ptr<InitializationResult> InitializeOncePerProcess(
return result;
}
std::unique_ptr<InitializationResult> InitializeOncePerProcess(
const std::vector<std::string>& args,
ProcessInitializationFlags::Flags flags) {
return InitializeOncePerProcessInternal(args, flags);
}
void TearDownOncePerProcess() {
const uint64_t flags = init_process_flags.load();
ResetStdio();
@@ -1037,9 +1061,9 @@ void TearDownOncePerProcess() {
InitializationResult::~InitializationResult() {}
InitializationResultImpl::~InitializationResultImpl() {}
int GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr,
const InitializationResult* result) {
int exit_code = result->exit_code();
ExitCode GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr,
const InitializationResultImpl* result) {
ExitCode exit_code = result->exit_code_enum();
// nullptr indicates there's no snapshot data.
DCHECK_NULL(*snapshot_data_ptr);
@@ -1053,7 +1077,8 @@ int GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr,
"node:embedded_snapshot_main was specified as snapshot "
"entry point but Node.js was built without embedded "
"snapshot.\n");
exit_code = 1;
// TODO(joyeecheung): should be kInvalidCommandLineArgument instead.
exit_code = ExitCode::kGenericUserError;
return exit_code;
}
} else {
@@ -1062,7 +1087,7 @@ int GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr,
std::make_unique<SnapshotData>();
exit_code = node::SnapshotBuilder::Generate(
generated_data.get(), result->args(), result->exec_args());
if (exit_code == 0) {
if (exit_code == ExitCode::kNoFailure) {
*snapshot_data_ptr = generated_data.release();
} else {
return exit_code;
@@ -1086,14 +1111,15 @@ int GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr,
fprintf(stderr,
"Cannot open %s for writing a snapshot.\n",
snapshot_blob_path.c_str());
exit_code = 1;
// TODO(joyeecheung): should be kStartupSnapshotFailure.
exit_code = ExitCode::kGenericUserError;
}
return exit_code;
}
int LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
const InitializationResult* result) {
int exit_code = result->exit_code();
ExitCode LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
const InitializationResultImpl* result) {
ExitCode exit_code = result->exit_code_enum();
// nullptr indicates there's no snapshot data.
DCHECK_NULL(*snapshot_data_ptr);
// --snapshot-blob indicates that we are reading a customized snapshot.
@@ -1102,13 +1128,15 @@ int LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
FILE* fp = fopen(filename.c_str(), "rb");
if (fp == nullptr) {
fprintf(stderr, "Cannot open %s", filename.c_str());
exit_code = 1;
// TODO(joyeecheung): should be kStartupSnapshotFailure.
exit_code = ExitCode::kGenericUserError;
return exit_code;
}
std::unique_ptr<SnapshotData> read_data = std::make_unique<SnapshotData>();
if (!SnapshotData::FromBlob(read_data.get(), fp)) {
// If we fail to read the customized snapshot, simply exit with 1.
exit_code = 1;
// TODO(joyeecheung): should be kStartupSnapshotFailure.
exit_code = ExitCode::kGenericUserError;
return exit_code;
}
*snapshot_data_ptr = read_data.release();
@@ -1137,22 +1165,23 @@ int LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
return exit_code;
}
int Start(int argc, char** argv) {
static ExitCode StartInternal(int argc, char** argv) {
CHECK_GT(argc, 0);
// Hack around with the argv pointer. Used for process.title = "blah".
argv = uv_setup_args(argc, argv);
std::unique_ptr<InitializationResult> result =
InitializeOncePerProcess(std::vector<std::string>(argv, argv + argc));
std::unique_ptr<InitializationResultImpl> result =
InitializeOncePerProcessInternal(
std::vector<std::string>(argv, argv + argc));
for (const std::string& error : result->errors()) {
FPrintF(stderr, "%s: %s\n", result->args().at(0), error);
}
if (result->early_return()) {
return result->exit_code();
return result->exit_code_enum();
}
DCHECK_EQ(result->exit_code(), 0);
DCHECK_EQ(result->exit_code_enum(), ExitCode::kNoFailure);
const SnapshotData* snapshot_data = nullptr;
auto cleanup_process = OnScopeLeave([&]() {
@@ -1172,7 +1201,7 @@ int Start(int argc, char** argv) {
fprintf(stderr,
"--build-snapshot must be used with an entry point script.\n"
"Usage: node --build-snapshot /path/to/entry.js\n");
return 9;
return ExitCode::kInvalidCommandLineArgument;
}
return GenerateAndWriteSnapshotData(&snapshot_data, result.get());
}
@@ -1181,6 +1210,10 @@ int Start(int argc, char** argv) {
return LoadSnapshotDataAndRun(&snapshot_data, result.get());
}
int Start(int argc, char** argv) {
return static_cast<int>(StartInternal(argc, argv));
}
int Stop(Environment* env) {
env->ExitEnv();
return 0;
+15 -4
View File
@@ -569,7 +569,7 @@ TryCatchScope::~TryCatchScope() {
if (message.IsEmpty())
message = Exception::CreateMessage(env_->isolate(), exception);
ReportFatalException(env_, exception, message, enhance);
env_->Exit(7);
env_->Exit(ExitCode::kExceptionInFatalExceptionHandler);
}
}
@@ -1019,6 +1019,17 @@ void Initialize(Local<Object> target,
context, target, "noSideEffectsToString", NoSideEffectsToString);
SetMethod(
context, target, "triggerUncaughtException", TriggerUncaughtException);
Isolate* isolate = context->GetIsolate();
Local<Object> exit_codes = Object::New(isolate);
READONLY_PROPERTY(target, "exitCodes", exit_codes);
#define V(Name, Code) \
constexpr int k##Name = static_cast<int>(ExitCode::k##Name); \
NODE_DEFINE_CONSTANT(exit_codes, k##Name);
EXIT_CODE_LIST(V)
#undef V
}
void DecorateErrorStack(Environment* env,
@@ -1094,7 +1105,7 @@ void TriggerUncaughtException(Isolate* isolate,
if (!fatal_exception_function->IsFunction()) {
ReportFatalException(
env, error, message, EnhanceFatalException::kDontEnhance);
env->Exit(6);
env->Exit(ExitCode::kInvalidFatalExceptionMonkeyPatching);
return;
}
@@ -1145,9 +1156,9 @@ void TriggerUncaughtException(Isolate* isolate,
Local<Value> code;
if (process_object->Get(env->context(), exit_code).ToLocal(&code) &&
code->IsInt32()) {
env->Exit(code.As<Int32>()->Value());
env->Exit(static_cast<ExitCode>(code.As<Int32>()->Value()));
} else {
env->Exit(1);
env->Exit(ExitCode::kGenericUserError);
}
}
-1
View File
@@ -276,7 +276,6 @@ v8::ModifyCodeGenerationFromStringsResult ModifyCodeGenerationFromStrings(
v8::Local<v8::Context> context,
v8::Local<v8::Value> source,
bool is_code_like);
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+54
View File
@@ -0,0 +1,54 @@
#ifndef SRC_NODE_EXIT_CODE_H_
#define SRC_NODE_EXIT_CODE_H_
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
namespace node {
#define EXIT_CODE_LIST(V) \
V(NoFailure, 0) \
/* 1 was intended for uncaught JS exceptions from the user land but we */ \
/* actually use this for all kinds of generic errors. */ \
V(GenericUserError, 1) \
/* 2 is unused */ \
/* 3 is actually unused because we pre-compile all builtins during */ \
/* snapshot building, when we exit with 1 if there's any error. */ \
V(InternalJSParseError, 3) \
/* 4 is actually unused. We exit with 1 in this case. */ \
V(InternalJSEvaluationFailure, 4) \
/* 5 is actually unused. We exit with 133 (128+SIGTRAP) or 134 */ \
/* (128+SIGABRT) in this case. */ \
V(V8FatalError, 5) \
V(InvalidFatalExceptionMonkeyPatching, 6) \
V(ExceptionInFatalExceptionHandler, 7) \
/* 8 is unused */ \
V(InvalidCommandLineArgument, 9) \
V(BootstrapFailure, 10) \
/* 11 is unused */ \
/* This was intended for invalid inspector arguments but is actually now */ \
/* just a duplicate of InvalidCommandLineArgument */ \
V(InvalidCommandLineArgument2, 12) \
V(UnfinishedTopLevelAwait, 13) \
V(StartupSnapshotFailure, 14) \
/* If the process exits from unhandled signals e.g. SIGABRT, SIGTRAP, */ \
/* typically the exit codes are 128 + signal number. We also exit with */ \
/* certain error codes directly for legacy reasons. Here we define those */ \
/* that are used to normalize the exit code on Windows. */ \
V(Abort, 134)
// TODO(joyeecheung): expose this to user land when the codes are stable.
// The underlying type should be an int, or we can get undefined behavior when
// casting error codes into exit codes (technically we shouldn't do that,
// but that's how things have been).
enum class ExitCode : int {
#define V(Name, Code) k##Name = Code,
EXIT_CODE_LIST(V)
#undef V
};
[[noreturn]] void Exit(ExitCode exit_code);
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#endif // SRC_NODE_EXIT_CODE_H_
+3 -2
View File
@@ -312,14 +312,15 @@ void MarkBootstrapComplete(const v8::FunctionCallbackInfo<v8::Value>& args);
class InitializationResultImpl final : public InitializationResult {
public:
~InitializationResultImpl();
int exit_code() const { return exit_code_; }
int exit_code() const { return static_cast<int>(exit_code_enum()); }
ExitCode exit_code_enum() const { return exit_code_; }
bool early_return() const { return early_return_; }
const std::vector<std::string>& args() const { return args_; }
const std::vector<std::string>& exec_args() const { return exec_args_; }
const std::vector<std::string>& errors() const { return errors_; }
MultiIsolatePlatform* platform() const { return platform_; }
int exit_code_ = 0;
ExitCode exit_code_ = ExitCode::kNoFailure;
std::vector<std::string> args_;
std::vector<std::string> exec_args_;
std::vector<std::string> errors_;
+4
View File
@@ -64,6 +64,8 @@ int wmain(int argc, wchar_t* wargv[]) {
if (size == 0) {
// This should never happen.
fprintf(stderr, "Could not convert arguments to utf8.");
// TODO(joyeecheung): should be ExitCode::kInvalidCommandLineArgument,
// but we are not ready to expose that to node.h yet.
exit(1);
}
// Do the actual conversion
@@ -79,6 +81,8 @@ int wmain(int argc, wchar_t* wargv[]) {
if (result == 0) {
// This should never happen.
fprintf(stderr, "Could not convert arguments to utf8.");
// TODO(joyeecheung): should be ExitCode::kInvalidCommandLineArgument,
// but we are not ready to expose that to node.h yet.
exit(1);
}
}
+12 -7
View File
@@ -118,12 +118,12 @@ NodeMainInstance::~NodeMainInstance() {
isolate_->Dispose();
}
int NodeMainInstance::Run() {
ExitCode NodeMainInstance::Run() {
Locker locker(isolate_);
Isolate::Scope isolate_scope(isolate_);
HandleScope handle_scope(isolate_);
int exit_code = 0;
ExitCode exit_code = ExitCode::kNoFailure;
DeleteFnPtr<Environment, FreeEnvironment> env =
CreateMainEnvironment(&exit_code);
CHECK_NOT_NULL(env);
@@ -133,11 +133,12 @@ int NodeMainInstance::Run() {
return exit_code;
}
void NodeMainInstance::Run(int* exit_code, Environment* env) {
if (*exit_code == 0) {
void NodeMainInstance::Run(ExitCode* exit_code, Environment* env) {
if (*exit_code == ExitCode::kNoFailure) {
LoadEnvironment(env, StartExecutionCallback{});
*exit_code = SpinEventLoop(env).FromMaybe(1);
*exit_code =
SpinEventLoopInternal(env).FromMaybe(ExitCode::kGenericUserError);
}
#if defined(LEAK_SANITIZER)
@@ -146,8 +147,8 @@ void NodeMainInstance::Run(int* exit_code, Environment* env) {
}
DeleteFnPtr<Environment, FreeEnvironment>
NodeMainInstance::CreateMainEnvironment(int* exit_code) {
*exit_code = 0; // Reset the exit code to 0
NodeMainInstance::CreateMainEnvironment(ExitCode* exit_code) {
*exit_code = ExitCode::kNoFailure; // Reset the exit code to 0
HandleScope handle_scope(isolate_);
@@ -180,6 +181,8 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) {
SetIsolateErrorHandlers(isolate_, {});
env->InitializeMainContext(context, &(snapshot_data_->env_info));
#if HAVE_INSPECTOR
// TODO(joyeecheung): handle the exit code returned by
// InitializeInspector().
env->InitializeInspector({});
#endif
@@ -198,6 +201,8 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) {
EnvironmentFlags::kDefaultFlags,
{}));
#if HAVE_INSPECTOR
// TODO(joyeecheung): handle the exit code returned by
// InitializeInspector().
env->InitializeInspector({});
#endif
if (env->principal_realm()->RunBootstrapping().IsEmpty()) {
+4 -3
View File
@@ -7,6 +7,7 @@
#include <memory>
#include "node.h"
#include "node_exit_code.h"
#include "util.h"
#include "uv.h"
#include "v8.h"
@@ -57,13 +58,13 @@ class NodeMainInstance {
~NodeMainInstance();
// Start running the Node.js instances, return the exit code when finished.
int Run();
void Run(int* exit_code, Environment* env);
ExitCode Run();
void Run(ExitCode* exit_code, Environment* env);
IsolateData* isolate_data() { return isolate_data_.get(); }
DeleteFnPtr<Environment, FreeEnvironment> CreateMainEnvironment(
int* exit_code);
ExitCode* exit_code);
NodeMainInstance(const NodeMainInstance&) = delete;
NodeMainInstance& operator=(const NodeMainInstance&) = delete;
+6 -1
View File
@@ -45,6 +45,7 @@ using v8::HeapStatistics;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Maybe;
using v8::NewStringType;
using v8::Number;
using v8::Object;
@@ -442,7 +443,11 @@ static void DebugEnd(const FunctionCallbackInfo<Value>& args) {
static void ReallyExit(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
RunAtExit(env);
int code = args[0]->Int32Value(env->context()).FromMaybe(0);
ExitCode code = ExitCode::kNoFailure;
Maybe<int32_t> code_int = args[0]->Int32Value(env->context());
if (!code_int.IsNothing()) {
code = static_cast<ExitCode>(code_int.FromJust());
}
env->Exit(code);
}
+7 -6
View File
@@ -5,6 +5,7 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include <cstdint>
#include "node_exit_code.h"
#include "node_mutex.h"
#include "v8.h"
@@ -15,14 +16,14 @@ struct SnapshotData;
class NODE_EXTERN_PRIVATE SnapshotBuilder {
public:
static int Generate(std::ostream& out,
const std::vector<std::string> args,
const std::vector<std::string> exec_args);
static ExitCode Generate(std::ostream& out,
const std::vector<std::string> args,
const std::vector<std::string> exec_args);
// Generate the snapshot into out.
static int Generate(SnapshotData* out,
const std::vector<std::string> args,
const std::vector<std::string> exec_args);
static ExitCode Generate(SnapshotData* out,
const std::vector<std::string> args,
const std::vector<std::string> exec_args);
// If nullptr is returned, the binary is not built with embedded
// snapshot.
+22 -25
View File
@@ -1067,14 +1067,9 @@ void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
const_cast<v8::StartupData*>(&(data->v8_snapshot_blob_data));
}
// TODO(joyeecheung): share these exit code constants across the code base.
constexpr int UNCAUGHT_EXCEPTION_ERROR = 1;
constexpr int BOOTSTRAP_ERROR = 10;
constexpr int SNAPSHOT_ERROR = 14;
int SnapshotBuilder::Generate(SnapshotData* out,
const std::vector<std::string> args,
const std::vector<std::string> exec_args) {
ExitCode SnapshotBuilder::Generate(SnapshotData* out,
const std::vector<std::string> args,
const std::vector<std::string> exec_args) {
const std::vector<intptr_t>& external_references =
CollectExternalReferences();
Isolate* isolate = Isolate::Allocate();
@@ -1137,7 +1132,7 @@ int SnapshotBuilder::Generate(SnapshotData* out,
if (!contextify::ContextifyContext::CreateV8Context(
isolate, global_template, nullptr, nullptr)
.ToLocal(&vm_context)) {
return SNAPSHOT_ERROR;
return ExitCode::kStartupSnapshotFailure;
}
}
@@ -1146,13 +1141,13 @@ int SnapshotBuilder::Generate(SnapshotData* out,
// without breaking compatibility.
Local<Context> base_context = NewContext(isolate);
if (base_context.IsEmpty()) {
return BOOTSTRAP_ERROR;
return ExitCode::kBootstrapFailure;
}
ResetContextSettingsBeforeSnapshot(base_context);
Local<Context> main_context = NewContext(isolate);
if (main_context.IsEmpty()) {
return BOOTSTRAP_ERROR;
return ExitCode::kBootstrapFailure;
}
// Initialize the main instance context.
{
@@ -1169,7 +1164,7 @@ int SnapshotBuilder::Generate(SnapshotData* out,
// Run scripts in lib/internal/bootstrap/
if (env->principal_realm()->RunBootstrapping().IsEmpty()) {
return BOOTSTRAP_ERROR;
return ExitCode::kBootstrapFailure;
}
// If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
// loaded via LoadEnvironment() to execute process.argv[1] as the entry
@@ -1178,17 +1173,19 @@ int SnapshotBuilder::Generate(SnapshotData* out,
// in the future).
if (snapshot_type == SnapshotMetadata::Type::kFullyCustomized) {
#if HAVE_INSPECTOR
// TODO(joyeecheung): move this before RunBootstrapping().
// TODO(joyeecheung): handle the exit code returned by
// InitializeInspector().
env->InitializeInspector({});
#endif
if (LoadEnvironment(env, StartExecutionCallback{}).IsEmpty()) {
return UNCAUGHT_EXCEPTION_ERROR;
return ExitCode::kGenericUserError;
}
// FIXME(joyeecheung): right now running the loop in the snapshot
// builder seems to introduces inconsistencies in JS land that need to
// be synchronized again after snapshot restoration.
int exit_code = SpinEventLoop(env).FromMaybe(UNCAUGHT_EXCEPTION_ERROR);
if (exit_code != 0) {
ExitCode exit_code =
SpinEventLoopInternal(env).FromMaybe(ExitCode::kGenericUserError);
if (exit_code != ExitCode::kNoFailure) {
return exit_code;
}
}
@@ -1206,7 +1203,7 @@ int SnapshotBuilder::Generate(SnapshotData* out,
#ifdef NODE_USE_NODE_CODE_CACHE
// Regenerate all the code cache.
if (!builtins::BuiltinLoader::CompileAllBuiltins(main_context)) {
return UNCAUGHT_EXCEPTION_ERROR;
return ExitCode::kGenericUserError;
}
builtins::BuiltinLoader::CopyCodeCache(&(out->code_cache));
for (const auto& item : out->code_cache) {
@@ -1241,7 +1238,7 @@ int SnapshotBuilder::Generate(SnapshotData* out,
// We must be able to rehash the blob when we restore it or otherwise
// the hash seed would be fixed by V8, introducing a vulnerability.
if (!out->v8_snapshot_blob_data.CanBeRehashed()) {
return SNAPSHOT_ERROR;
return ExitCode::kStartupSnapshotFailure;
}
out->metadata = SnapshotMetadata{snapshot_type,
@@ -1260,17 +1257,17 @@ int SnapshotBuilder::Generate(SnapshotData* out,
PrintLibuvHandleInformation(env->event_loop(), stderr);
}
if (!queues_are_empty) {
return SNAPSHOT_ERROR;
return ExitCode::kStartupSnapshotFailure;
}
return 0;
return ExitCode::kNoFailure;
}
int SnapshotBuilder::Generate(std::ostream& out,
const std::vector<std::string> args,
const std::vector<std::string> exec_args) {
ExitCode SnapshotBuilder::Generate(std::ostream& out,
const std::vector<std::string> args,
const std::vector<std::string> exec_args) {
SnapshotData data;
int exit_code = Generate(&data, args, exec_args);
if (exit_code != 0) {
ExitCode exit_code = Generate(&data, args, exec_args);
if (exit_code != ExitCode::kNoFailure) {
return exit_code;
}
FormatBlob(out, &data);
+32 -14
View File
@@ -137,7 +137,8 @@ class WorkerThreadData {
if (ret != 0) {
char err_buf[128];
uv_err_name_r(ret, err_buf, sizeof(err_buf));
w->Exit(1, "ERR_WORKER_INIT_FAILED", err_buf);
// TODO(joyeecheung): maybe this should be kBootstrapFailure instead?
w->Exit(ExitCode::kGenericUserError, "ERR_WORKER_INIT_FAILED", err_buf);
return;
}
loop_init_failed_ = false;
@@ -156,7 +157,10 @@ class WorkerThreadData {
Isolate* isolate = Isolate::Allocate();
if (isolate == nullptr) {
w->Exit(1, "ERR_WORKER_INIT_FAILED", "Failed to create new Isolate");
// TODO(joyeecheung): maybe this should be kBootstrapFailure instead?
w->Exit(ExitCode::kGenericUserError,
"ERR_WORKER_INIT_FAILED",
"Failed to create new Isolate");
return;
}
@@ -257,7 +261,10 @@ size_t Worker::NearHeapLimit(void* data, size_t current_heap_limit,
"new_limit=%" PRIu64 "\n",
static_cast<uint64_t>(new_limit));
}
worker->Exit(1, "ERR_WORKER_OUT_OF_MEMORY", "JS heap out of memory");
// TODO(joyeecheung): maybe this should be kV8FatalError instead?
worker->Exit(ExitCode::kGenericUserError,
"ERR_WORKER_OUT_OF_MEMORY",
"JS heap out of memory");
return new_limit;
}
@@ -322,7 +329,10 @@ void Worker::Run() {
context = NewContext(isolate_);
}
if (context.IsEmpty()) {
Exit(1, "ERR_WORKER_INIT_FAILED", "Failed to create new Context");
// TODO(joyeecheung): maybe this should be kBootstrapFailure instead?
Exit(ExitCode::kGenericUserError,
"ERR_WORKER_INIT_FAILED",
"Failed to create new Context");
return;
}
}
@@ -343,7 +353,7 @@ void Worker::Run() {
CHECK_NOT_NULL(env_);
env_->set_env_vars(std::move(env_vars_));
SetProcessExitHandler(env_.get(), [this](Environment*, int exit_code) {
Exit(exit_code);
Exit(static_cast<ExitCode>(exit_code));
});
}
{
@@ -367,14 +377,16 @@ void Worker::Run() {
}
{
Maybe<int> exit_code = SpinEventLoop(env_.get());
Maybe<ExitCode> exit_code = SpinEventLoopInternal(env_.get());
Mutex::ScopedLock lock(mutex_);
if (exit_code_ == 0 && exit_code.IsJust()) {
if (exit_code_ == ExitCode::kNoFailure && exit_code.IsJust()) {
exit_code_ = exit_code.FromJust();
}
Debug(this, "Exiting thread for worker %llu with exit code %d",
thread_id_.id, exit_code_);
Debug(this,
"Exiting thread for worker %llu with exit code %d",
thread_id_.id,
static_cast<int>(exit_code_));
}
}
@@ -419,7 +431,7 @@ void Worker::JoinThread() {
Undefined(env()->isolate())).Check();
Local<Value> args[] = {
Integer::New(env()->isolate(), exit_code_),
Integer::New(env()->isolate(), static_cast<int>(exit_code_)),
custom_error_ != nullptr
? OneByteString(env()->isolate(), custom_error_).As<Value>()
: Null(env()->isolate()).As<Value>(),
@@ -684,7 +696,7 @@ void Worker::StopThread(const FunctionCallbackInfo<Value>& args) {
ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
Debug(w, "Worker %llu is getting stopped by parent", w->thread_id_.id);
w->Exit(1);
w->Exit(ExitCode::kGenericUserError);
}
void Worker::Ref(const FunctionCallbackInfo<Value>& args) {
@@ -724,10 +736,16 @@ Local<Float64Array> Worker::GetResourceLimits(Isolate* isolate) const {
return Float64Array::New(ab, 0, kTotalResourceLimitCount);
}
void Worker::Exit(int code, const char* error_code, const char* error_message) {
void Worker::Exit(ExitCode code,
const char* error_code,
const char* error_message) {
Mutex::ScopedLock lock(mutex_);
Debug(this, "Worker %llu called Exit(%d, %s, %s)",
thread_id_.id, code, error_code, error_message);
Debug(this,
"Worker %llu called Exit(%d, %s, %s)",
thread_id_.id,
static_cast<int>(code),
error_code,
error_message);
if (error_code != nullptr) {
custom_error_ = error_code;
+3 -2
View File
@@ -5,6 +5,7 @@
#include <optional>
#include <unordered_map>
#include "node_exit_code.h"
#include "node_messaging.h"
#include "uv.h"
@@ -41,7 +42,7 @@ class Worker : public AsyncWrap {
// Forcibly exit the thread with a specified exit code. This may be called
// from any thread. `error_code` and `error_message` can be used to create
// a custom `'error'` event before emitting `'exit'`.
void Exit(int code,
void Exit(ExitCode code,
const char* error_code = nullptr,
const char* error_message = nullptr);
@@ -95,7 +96,7 @@ class Worker : public AsyncWrap {
const char* custom_error_ = nullptr;
std::string custom_error_str_;
int exit_code_ = 0;
ExitCode exit_code_ = ExitCode::kNoFailure;
ThreadId thread_id_;
uintptr_t stack_base_ = 0;
+2 -1
View File
@@ -27,6 +27,7 @@
#include "v8.h"
#include "node.h"
#include "node_exit_code.h"
#include <climits>
#include <cstddef>
@@ -118,7 +119,7 @@ void DumpBacktrace(FILE* fp);
// Windows 8+ does not like abort() in Release mode
#ifdef _WIN32
#define ABORT_NO_BACKTRACE() _exit(134)
#define ABORT_NO_BACKTRACE() _exit(static_cast<int>(node::ExitCode::kAbort))
#else
#define ABORT_NO_BACKTRACE() abort()
#endif
+4 -4
View File
@@ -84,18 +84,18 @@ int BuildSnapshot(int argc, char* argv[]) {
return 1;
}
int exit_code = 0;
node::ExitCode exit_code = node::ExitCode::kNoFailure;
{
exit_code = node::SnapshotBuilder::Generate(
out, result->args(), result->exec_args());
if (exit_code == 0) {
if (exit_code == node::ExitCode::kNoFailure) {
if (!out) {
std::cerr << "Failed to write " << out_path << "\n";
exit_code = 1;
exit_code = node::ExitCode::kGenericUserError;
}
}
}
node::TearDownOncePerProcess();
return exit_code;
return static_cast<int>(exit_code);
}