summaryrefslogtreecommitdiff
path: root/src/node.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/node.cc')
-rw-r--r--src/node.cc818
1 files changed, 244 insertions, 574 deletions
diff --git a/src/node.cc b/src/node.cc
index 33a68b37a4..b600241710 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -27,7 +27,6 @@
#include "node_version.h"
#include "node_internals.h"
#include "node_revert.h"
-#include "node_debug_options.h"
#include "node_perf.h"
#include "node_context_data.h"
#include "tracing/traced_value.h"
@@ -172,19 +171,6 @@ using v8::Value;
static Mutex process_mutex;
static Mutex environ_mutex;
-static bool print_eval = false;
-static bool force_repl = false;
-static bool syntax_check_only = false;
-static bool trace_deprecation = false;
-static bool throw_deprecation = false;
-static bool trace_sync_io = false;
-static bool no_force_async_hooks_checks = false;
-static bool track_heap_objects = false;
-static const char* eval_string = nullptr;
-static std::vector<std::string> preload_modules;
-static const int v8_default_thread_pool_size = 4;
-static int v8_thread_pool_size = v8_default_thread_pool_size;
-static bool prof_process = false;
static bool v8_is_profiling = false;
static bool node_is_initialized = false;
static uv_once_t init_modpending_once = UV_ONCE_INIT;
@@ -193,93 +179,13 @@ static node_module* modlist_builtin;
static node_module* modlist_internal;
static node_module* modlist_linked;
static node_module* modlist_addon;
-static std::string trace_enabled_categories; // NOLINT(runtime/string)
-static std::string trace_file_pattern = // NOLINT(runtime/string)
- "node_trace.${rotation}.log";
+
+// TODO(addaleax): This should not be global.
static bool abort_on_uncaught_exception = false;
// Bit flag used to track security reverts (see node_revert.h)
unsigned int reverted = 0;
-#if defined(NODE_HAVE_I18N_SUPPORT)
-// Path to ICU data (for i18n / Intl)
-std::string icu_data_dir; // NOLINT(runtime/string)
-#endif
-
-// used by C++ modules as well
-bool no_deprecation = false;
-
-#if HAVE_OPENSSL
-// use OpenSSL's cert store instead of bundled certs
-bool ssl_openssl_cert_store =
-#if defined(NODE_OPENSSL_CERT_STORE)
- true;
-#else
- false;
-#endif
-
-# if NODE_FIPS_MODE
-// used by crypto module
-bool enable_fips_crypto = false;
-bool force_fips_crypto = false;
-# endif // NODE_FIPS_MODE
-std::string openssl_config; // NOLINT(runtime/string)
-#endif // HAVE_OPENSSL
-
-// true if process warnings should be suppressed
-bool no_process_warnings = false;
-bool trace_warnings = false;
-
-// Set in node.cc by ParseArgs when --preserve-symlinks is used.
-// Used in node_config.cc to set a constant on process.binding('config')
-// that is used by lib/module.js
-bool config_preserve_symlinks = false;
-
-// Set in node.cc by ParseArgs when --preserve-symlinks-main is used.
-// Used in node_config.cc to set a constant on process.binding('config')
-// that is used by lib/module.js
-bool config_preserve_symlinks_main = false;
-
-// Set in node.cc by ParseArgs when --experimental-modules is used.
-// Used in node_config.cc to set a constant on process.binding('config')
-// that is used by lib/module.js
-bool config_experimental_modules = false;
-
-// Set in node.cc by ParseArgs when --experimental-vm-modules is used.
-// Used in node_config.cc to set a constant on process.binding('config')
-// that is used by lib/vm.js
-bool config_experimental_vm_modules = false;
-
-// Set in node.cc by ParseArgs when --experimental-worker is used.
-// Used in node_config.cc to set a constant on process.binding('config')
-// that is used by lib/worker.js
-bool config_experimental_worker = false;
-
-// Set in node.cc by ParseArgs when --experimental-repl-await is used.
-// Used in node_config.cc to set a constant on process.binding('config')
-// that is used by lib/repl.js.
-bool config_experimental_repl_await = false;
-
-// Set in node.cc by ParseArgs when --loader is used.
-// Used in node_config.cc to set a constant on process.binding('config')
-// that is used by lib/internal/bootstrap/node.js
-std::string config_userland_loader; // NOLINT(runtime/string)
-
-// Set by ParseArgs when --pending-deprecation or NODE_PENDING_DEPRECATION
-// is used.
-bool config_pending_deprecation = false;
-
-// Set in node.cc by ParseArgs when --redirect-warnings= is used.
-std::string config_warning_file; // NOLINT(runtime/string)
-
-// Set in node.cc by ParseArgs when --expose-internals or --expose_internals is
-// used.
-// Used in node_config.cc to set a constant on process.binding('config')
-// that is used by lib/internal/bootstrap/node.js
-bool config_expose_internals = false;
-
-std::string config_process_title; // NOLINT(runtime/string)
-
bool v8_initialized = false;
bool linux_at_secure = false;
@@ -287,11 +193,12 @@ bool linux_at_secure = false;
// process-relative uptime base, initialized at start-up
double prog_start_time;
+std::shared_ptr<PerProcessOptions> per_process_opts {
+ new PerProcessOptions() };
+
static Mutex node_isolate_mutex;
static v8::Isolate* node_isolate;
-DebugOptions debug_options;
-
// Ensures that __metadata trace events are only emitted
// when tracing is enabled.
class NodeTraceStateObserver :
@@ -415,7 +322,7 @@ static struct {
#if HAVE_INSPECTOR
bool StartInspector(Environment* env, const char* script_path,
- const DebugOptions& options) {
+ std::shared_ptr<DebugOptions> options) {
// Inspector agent can't fail to start, but if it was configured to listen
// right away on the websocket port and fails to bind/etc, this will return
// false.
@@ -429,13 +336,14 @@ static struct {
#endif // HAVE_INSPECTOR
void StartTracingAgent() {
- if (trace_enabled_categories.empty()) {
+ if (per_process_opts->trace_event_categories.empty()) {
tracing_file_writer_ = tracing_agent_->DefaultHandle();
} else {
tracing_file_writer_ = tracing_agent_->AddClient(
- ParseCommaSeparatedSet(trace_enabled_categories),
+ ParseCommaSeparatedSet(per_process_opts->trace_event_categories),
std::unique_ptr<tracing::AsyncTraceWriter>(
- new tracing::NodeTraceWriter(trace_file_pattern)),
+ new tracing::NodeTraceWriter(
+ per_process_opts->trace_event_file_pattern)),
tracing::Agent::kUseDefaultCategories);
}
}
@@ -1844,7 +1752,7 @@ static void EnvSetter(Local<Name> property,
Local<Value> value,
const PropertyCallbackInfo<Value>& info) {
Environment* env = Environment::GetCurrent(info);
- if (config_pending_deprecation && env->EmitProcessEnvWarning() &&
+ if (env->options()->pending_deprecation && env->EmitProcessEnvWarning() &&
!value->IsString() && !value->IsNumber() && !value->IsBoolean()) {
if (ProcessEmitDeprecationWarning(
env,
@@ -2034,11 +1942,11 @@ static Local<Object> GetFeatures(Environment* env) {
static void DebugPortGetter(Local<Name> property,
const PropertyCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
Mutex::ScopedLock lock(process_mutex);
- int port = debug_options.port();
+ int port = env->options()->debug_options->port();
#if HAVE_INSPECTOR
if (port == 0) {
- Environment* env = Environment::GetCurrent(info);
if (auto io = env->inspector_agent()->io())
port = io->port();
}
@@ -2050,8 +1958,10 @@ static void DebugPortGetter(Local<Name> property,
static void DebugPortSetter(Local<Name> property,
Local<Value> value,
const PropertyCallbackInfo<void>& info) {
+ Environment* env = Environment::GetCurrent(info);
Mutex::ScopedLock lock(process_mutex);
- debug_options.set_port(value->Int32Value());
+ env->options()->debug_options->host_port.port =
+ value->Int32Value(env->context()).FromMaybe(0);
}
@@ -2081,10 +1991,8 @@ namespace {
} // anonymous namespace
void SetupProcessObject(Environment* env,
- int argc,
- const char* const* argv,
- int exec_argc,
- const char* const* exec_argv) {
+ const std::vector<std::string>& args,
+ const std::vector<std::string>& exec_args) {
HandleScope scope(env->isolate());
Local<Object> process = env->process_object();
@@ -2222,18 +2130,22 @@ void SetupProcessObject(Environment* env,
#endif
// process.argv
- Local<Array> arguments = Array::New(env->isolate(), argc);
- for (int i = 0; i < argc; ++i) {
- arguments->Set(i, String::NewFromUtf8(env->isolate(), argv[i],
- v8::NewStringType::kNormal).ToLocalChecked());
+ Local<Array> arguments = Array::New(env->isolate(), args.size());
+ for (size_t i = 0; i < args.size(); ++i) {
+ arguments->Set(env->context(), i,
+ String::NewFromUtf8(env->isolate(), args[i].c_str(),
+ v8::NewStringType::kNormal).ToLocalChecked())
+ .FromJust();
}
process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "argv"), arguments);
// process.execArgv
- Local<Array> exec_arguments = Array::New(env->isolate(), exec_argc);
- for (int i = 0; i < exec_argc; ++i) {
- exec_arguments->Set(i, String::NewFromUtf8(env->isolate(), exec_argv[i],
- v8::NewStringType::kNormal).ToLocalChecked());
+ Local<Array> exec_arguments = Array::New(env->isolate(), exec_args.size());
+ for (size_t i = 0; i < exec_args.size(); ++i) {
+ exec_arguments->Set(env->context(), i,
+ String::NewFromUtf8(env->isolate(), exec_args[i].c_str(),
+ v8::NewStringType::kNormal).ToLocalChecked())
+ .FromJust();
}
process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "execArgv"),
exec_arguments);
@@ -2262,29 +2174,33 @@ void SetupProcessObject(Environment* env,
GetParentProcessId).FromJust());
// -e, --eval
- if (eval_string) {
+ if (env->options()->has_eval_string) {
READONLY_PROPERTY(process,
"_eval",
- String::NewFromUtf8(env->isolate(), eval_string,
+ String::NewFromUtf8(
+ env->isolate(),
+ env->options()->eval_string.c_str(),
v8::NewStringType::kNormal).ToLocalChecked());
}
// -p, --print
- if (print_eval) {
+ if (env->options()->print_eval) {
READONLY_PROPERTY(process, "_print_eval", True(env->isolate()));
}
// -c, --check
- if (syntax_check_only) {
+ if (env->options()->syntax_check_only) {
READONLY_PROPERTY(process, "_syntax_check_only", True(env->isolate()));
}
// -i, --interactive
- if (force_repl) {
+ if (env->options()->force_repl) {
READONLY_PROPERTY(process, "_forceRepl", True(env->isolate()));
}
// -r, --require
+ std::vector<std::string> preload_modules =
+ std::move(env->options()->preload_modules);
if (!preload_modules.empty()) {
Local<Array> array = Array::New(env->isolate());
for (unsigned int i = 0; i < preload_modules.size(); ++i) {
@@ -2302,22 +2218,23 @@ void SetupProcessObject(Environment* env,
}
// --no-deprecation
- if (no_deprecation) {
+ // TODO(addaleax): Uncomment the commented part.
+ if (/*env->options()->*/no_deprecation) {
READONLY_PROPERTY(process, "noDeprecation", True(env->isolate()));
}
// --no-warnings
- if (no_process_warnings) {
+ if (env->options()->no_warnings) {
READONLY_PROPERTY(process, "noProcessWarnings", True(env->isolate()));
}
// --trace-warnings
- if (trace_warnings) {
+ if (env->options()->trace_warnings) {
READONLY_PROPERTY(process, "traceProcessWarnings", True(env->isolate()));
}
// --throw-deprecation
- if (throw_deprecation) {
+ if (env->options()->throw_deprecation) {
READONLY_PROPERTY(process, "throwDeprecation", True(env->isolate()));
}
@@ -2327,35 +2244,35 @@ void SetupProcessObject(Environment* env,
#endif // NODE_NO_BROWSER_GLOBALS
// --prof-process
- if (prof_process) {
+ if (env->options()->prof_process) {
READONLY_PROPERTY(process, "profProcess", True(env->isolate()));
}
// --trace-deprecation
- if (trace_deprecation) {
+ if (env->options()->trace_deprecation) {
READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate()));
}
- // TODO(refack): move the following 3 to `node_config`
+ // TODO(refack): move the following 4 to `node_config`
// --inspect-brk
- if (debug_options.wait_for_connect()) {
+ if (env->options()->debug_options->wait_for_connect()) {
READONLY_DONT_ENUM_PROPERTY(process,
"_breakFirstLine", True(env->isolate()));
}
- if (debug_options.break_node_first_line()) {
+ if (env->options()->debug_options->break_node_first_line) {
READONLY_DONT_ENUM_PROPERTY(process,
"_breakNodeFirstLine", True(env->isolate()));
}
// --inspect --debug-brk
- if (debug_options.deprecated_invocation()) {
+ if (env->options()->debug_options->deprecated_invocation()) {
READONLY_DONT_ENUM_PROPERTY(process,
"_deprecatedDebugBrk", True(env->isolate()));
}
// --debug or, --debug-brk without --inspect
- if (debug_options.invalid_invocation()) {
+ if (env->options()->debug_options->invalid_invocation()) {
READONLY_DONT_ENUM_PROPERTY(process,
"_invalidDebug", True(env->isolate()));
}
@@ -2379,7 +2296,7 @@ void SetupProcessObject(Environment* env,
v8::NewStringType::kInternalized,
exec_path_len).ToLocalChecked();
} else {
- exec_path_value = String::NewFromUtf8(env->isolate(), argv[0],
+ exec_path_value = String::NewFromUtf8(env->isolate(), args[0].c_str(),
v8::NewStringType::kInternalized).ToLocalChecked();
}
process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "execPath"),
@@ -2561,7 +2478,8 @@ void LoadEnvironment(Environment* env) {
get_binding_fn,
get_linked_binding_fn,
get_internal_binding_fn,
- Boolean::New(env->isolate(), debug_options.break_node_first_line())
+ Boolean::New(env->isolate(),
+ env->options()->debug_options->break_node_first_line)
};
// Bootstrap internal loaders
@@ -2742,359 +2660,8 @@ static void PrintHelp() {
}
-static bool ArgIsAllowed(const char* arg, const char* allowed) {
- for (; *arg && *allowed; arg++, allowed++) {
- // Like normal strcmp(), except that a '_' in `allowed` matches either a '-'
- // or '_' in `arg`.
- if (*allowed == '_') {
- if (!(*arg == '_' || *arg == '-'))
- return false;
- } else {
- if (*arg != *allowed)
- return false;
- }
- }
-
- // "--some-arg=val" is allowed for "--some-arg"
- if (*arg == '=')
- return true;
-
- // Both must be null, or one string is just a prefix of the other, not a
- // match.
- return !*arg && !*allowed;
-}
-
-
-static void CheckIfAllowedInEnv(const char* exe, bool is_env,
- const char* arg) {
- if (!is_env)
- return;
-
- static const char* whitelist[] = {
- // Node options, sorted in `node --help` order for ease of comparison.
- // Please, update NODE_OPTIONS section in cli.md if changed.
- "--enable-fips",
- "--experimental-modules",
- "--experimental-repl-await",
- "--experimental-vm-modules",
- "--experimental-worker",
- "--force-fips",
- "--icu-data-dir",
- "--inspect",
- "--inspect-brk",
- "--inspect-port",
- "--loader",
- "--napi-modules",
- "--no-deprecation",
- "--no-force-async-hooks-checks",
- "--no-warnings",
- "--openssl-config",
- "--pending-deprecation",
- "--redirect-warnings",
- "--require",
- "--throw-deprecation",
- "--title",
- "--tls-cipher-list",
- "--trace-deprecation",
- "--trace-event-categories",
- "--trace-event-file-pattern",
- "--trace-events-enabled",
- "--trace-sync-io",
- "--trace-warnings",
- "--track-heap-objects",
- "--use-bundled-ca",
- "--use-openssl-ca",
- "--v8-pool-size",
- "--zero-fill-buffers",
- "-r",
-
- // V8 options (define with '_', which allows '-' or '_')
- "--abort_on_uncaught_exception",
- "--max_old_space_size",
- "--perf_basic_prof",
- "--perf_prof",
- "--stack_trace_limit",
- };
-
- for (unsigned i = 0; i < arraysize(whitelist); i++) {
- const char* allowed = whitelist[i];
- if (ArgIsAllowed(arg, allowed))
- return;
- }
-
- fprintf(stderr, "%s: %s is not allowed in NODE_OPTIONS\n", exe, arg);
- exit(9);
-}
-
-
-// Parse command line arguments.
-//
-// argv is modified in place. exec_argv and v8_argv are out arguments that
-// ParseArgs() allocates memory for and stores a pointer to the output
-// vector in. The caller should free them with delete[].
-//
-// On exit:
-//
-// * argv contains the arguments with node and V8 options filtered out.
-// * exec_argv contains both node and V8 options and nothing else.
-// * v8_argv contains argv[0] plus any V8 options
-static void ParseArgs(int* argc,
- const char** argv,
- int* exec_argc,
- const char*** exec_argv,
- int* v8_argc,
- const char*** v8_argv,
- bool is_env) {
- const unsigned int nargs = static_cast<unsigned int>(*argc);
- const char** new_exec_argv = new const char*[nargs];
- const char** new_v8_argv = new const char*[nargs];
- const char** new_argv = new const char*[nargs];
-#if HAVE_OPENSSL
- bool use_bundled_ca = false;
- bool use_openssl_ca = false;
-#endif // HAVE_OPENSSL
-
- for (unsigned int i = 0; i < nargs; ++i) {
- new_exec_argv[i] = nullptr;
- new_v8_argv[i] = nullptr;
- new_argv[i] = nullptr;
- }
-
- // exec_argv starts with the first option, the other two start with argv[0].
- unsigned int new_exec_argc = 0;
- unsigned int new_v8_argc = 1;
- unsigned int new_argc = 1;
- new_v8_argv[0] = argv[0];
- new_argv[0] = argv[0];
-
- unsigned int index = 1;
- bool short_circuit = false;
- while (index < nargs && argv[index][0] == '-' && !short_circuit) {
- const char* const arg = argv[index];
- unsigned int args_consumed = 1;
-
- CheckIfAllowedInEnv(argv[0], is_env, arg);
-
- if (debug_options.ParseOption(argv[0], arg)) {
- // Done, consumed by DebugOptions::ParseOption().
- } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
- printf("%s\n", NODE_VERSION);
- exit(0);
- } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
- PrintHelp();
- exit(0);
- } else if (strcmp(arg, "--eval") == 0 ||
- strcmp(arg, "-e") == 0 ||
- strcmp(arg, "--print") == 0 ||
- strcmp(arg, "-pe") == 0 ||
- strcmp(arg, "-p") == 0) {
- bool is_eval = strchr(arg, 'e') != nullptr;
- bool is_print = strchr(arg, 'p') != nullptr;
- print_eval = print_eval || is_print;
- // --eval, -e and -pe always require an argument.
- if (is_eval == true) {
- args_consumed += 1;
- eval_string = argv[index + 1];
- if (eval_string == nullptr) {
- fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
- exit(9);
- }
- } else if ((index + 1 < nargs) &&
- argv[index + 1] != nullptr &&
- argv[index + 1][0] != '-') {
- args_consumed += 1;
- eval_string = argv[index + 1];
- if (strncmp(eval_string, "\\-", 2) == 0) {
- // Starts with "\\-": escaped expression, drop the backslash.
- eval_string += 1;
- }
- }
- } else if (strcmp(arg, "--require") == 0 ||
- strcmp(arg, "-r") == 0) {
- const char* module = argv[index + 1];
- if (module == nullptr) {
- fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
- exit(9);
- }
- args_consumed += 1;
- preload_modules.push_back(module);
- } else if (strcmp(arg, "--check") == 0 || strcmp(arg, "-c") == 0) {
- syntax_check_only = true;
- } else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) {
- force_repl = true;
- } else if (strcmp(arg, "--no-deprecation") == 0) {
- no_deprecation = true;
- } else if (strcmp(arg, "--napi-modules") == 0) {
- // no-op
- } else if (strcmp(arg, "--no-warnings") == 0) {
- no_process_warnings = true;
- } else if (strcmp(arg, "--trace-warnings") == 0) {
- trace_warnings = true;
- } else if (strncmp(arg, "--redirect-warnings=", 20) == 0) {
- config_warning_file = arg + 20;
- } else if (strcmp(arg, "--trace-deprecation") == 0) {
- trace_deprecation = true;
- } else if (strcmp(arg, "--trace-sync-io") == 0) {
- trace_sync_io = true;
- } else if (strcmp(arg, "--no-force-async-hooks-checks") == 0) {
- no_force_async_hooks_checks = true;
- } else if (strcmp(arg, "--trace-events-enabled") == 0) {
- if (trace_enabled_categories.empty())
- trace_enabled_categories = "v8,node,node.async_hooks";
- } else if (strcmp(arg, "--trace-event-categories") == 0) {
- const char* categories = argv[index + 1];
- if (categories == nullptr) {
- fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
- exit(9);
- }
- args_consumed += 1;
- trace_enabled_categories = categories;
- } else if (strcmp(arg, "--trace-event-file-pattern") == 0) {
- const char* file_pattern = argv[index + 1];
- if (file_pattern == nullptr) {
- fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
- exit(9);
- }
- args_consumed += 1;
- trace_file_pattern = file_pattern;
- } else if (strcmp(arg, "--track-heap-objects") == 0) {
- track_heap_objects = true;
- } else if (strcmp(arg, "--throw-deprecation") == 0) {
- throw_deprecation = true;
- } else if (strncmp(arg, "--security-revert=", 18) == 0) {
- const char* cve = arg + 18;
- Revert(cve);
- } else if (strncmp(arg, "--title=", 8) == 0) {
- config_process_title = arg + 8;
- } else if (strcmp(arg, "--preserve-symlinks") == 0) {
- config_preserve_symlinks = true;
- } else if (strcmp(arg, "--preserve-symlinks-main") == 0) {
- config_preserve_symlinks_main = true;
- } else if (strcmp(arg, "--experimental-modules") == 0) {
- config_experimental_modules = true;
- } else if (strcmp(arg, "--experimental-vm-modules") == 0) {
- config_experimental_vm_modules = true;
- } else if (strcmp(arg, "--experimental-worker") == 0) {
- config_experimental_worker = true;
- } else if (strcmp(arg, "--experimental-repl-await") == 0) {
- config_experimental_repl_await = true;
- } else if (strcmp(arg, "--loader") == 0) {
- const char* module = argv[index + 1];
- if (!config_experimental_modules) {
- fprintf(stderr, "%s: %s requires --experimental-modules be enabled\n",
- argv[0], arg);
- exit(9);
- }
- if (module == nullptr) {
- fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
- exit(9);
- }
- args_consumed += 1;
- config_userland_loader = module;
- } else if (strcmp(arg, "--prof-process") == 0) {
- prof_process = true;
- short_circuit = true;
- } else if (strcmp(arg, "--zero-fill-buffers") == 0) {
- zero_fill_all_buffers = true;
- } else if (strcmp(arg, "--pending-deprecation") == 0) {
- config_pending_deprecation = true;
- } else if (strcmp(arg, "--v8-options") == 0) {
- new_v8_argv[new_v8_argc] = "--help";
- new_v8_argc += 1;
- } else if (strncmp(arg, "--v8-pool-size=", 15) == 0) {
- v8_thread_pool_size = atoi(arg + 15);
-#if HAVE_OPENSSL
- } else if (strncmp(arg, "--tls-cipher-list=", 18) == 0) {
- default_cipher_list = arg + 18;
- } else if (strncmp(arg, "--use-openssl-ca", 16) == 0) {
- ssl_openssl_cert_store = true;
- use_openssl_ca = true;
- } else if (strncmp(arg, "--use-bundled-ca", 16) == 0) {
- use_bundled_ca = true;
- ssl_openssl_cert_store = false;
-#if NODE_FIPS_MODE
- } else if (strcmp(arg, "--enable-fips") == 0) {
- enable_fips_crypto = true;
- } else if (strcmp(arg, "--force-fips") == 0) {
- force_fips_crypto = true;
-#endif /* NODE_FIPS_MODE */
- } else if (strncmp(arg, "--openssl-config=", 17) == 0) {
- openssl_config.assign(arg + 17);
-#endif /* HAVE_OPENSSL */
-#if defined(NODE_HAVE_I18N_SUPPORT)
- } else if (strncmp(arg, "--icu-data-dir=", 15) == 0) {
- icu_data_dir.assign(arg + 15);
-#endif
- } else if (strcmp(arg, "--expose-internals") == 0 ||
- strcmp(arg, "--expose_internals") == 0) {
- config_expose_internals = true;
- } else if (strcmp(arg, "-") == 0) {
- break;
- } else if (strcmp(arg, "--") == 0) {
- index += 1;
- break;
- } else if (strcmp(arg, "--abort-on-uncaught-exception") == 0 ||
- strcmp(arg, "--abort_on_uncaught_exception") == 0) {
- abort_on_uncaught_exception = true;
- // Also a V8 option. Pass through as-is.
- new_v8_argv[new_v8_argc] = arg;
- new_v8_argc += 1;
- } else {
- // V8 option. Pass through as-is.
- new_v8_argv[new_v8_argc] = arg;
- new_v8_argc += 1;
- }
-
- memcpy(new_exec_argv + new_exec_argc,
- argv + index,
- args_consumed * sizeof(*argv));
-
- new_exec_argc += args_consumed;
- index += args_consumed;
- }
-
-#if HAVE_OPENSSL
- if (use_openssl_ca && use_bundled_ca) {
- fprintf(stderr,
- "%s: either --use-openssl-ca or --use-bundled-ca can be used, "
- "not both\n",
- argv[0]);
- exit(9);
- }
-#endif
-
- if (eval_string != nullptr && syntax_check_only) {
- fprintf(stderr,
- "%s: either --check or --eval can be used, not both\n", argv[0]);
- exit(9);
- }
-
- // Copy remaining arguments.
- const unsigned int args_left = nargs - index;
-
- if (is_env && args_left) {
- fprintf(stderr, "%s: %s is not supported in NODE_OPTIONS\n",
- argv[0], argv[index]);
- exit(9);
- }
-
- memcpy(new_argv + new_argc, argv + index, args_left * sizeof(*argv));
- new_argc += args_left;
-
- *exec_argc = new_exec_argc;
- *exec_argv = new_exec_argv;
- *v8_argc = new_v8_argc;
- *v8_argv = new_v8_argv;
-
- // Copy new_argv over argv and update argc.
- memcpy(argv, new_argv, new_argc * sizeof(*argv));
- delete[] new_argv;
- *argc = static_cast<int>(new_argc);
-}
-
-
static void StartInspector(Environment* env, const char* path,
- DebugOptions debug_options) {
+ std::shared_ptr<DebugOptions> debug_options) {
#if HAVE_INSPECTOR
CHECK(!env->inspector_agent()->IsListening());
v8_platform.StartInspector(env, path, debug_options);
@@ -3327,26 +2894,87 @@ inline void PlatformInit() {
#endif // _WIN32
}
+// TODO(addaleax): Remove, both from the public API and in implementation.
+bool no_deprecation = false;
+#if HAVE_OPENSSL
+bool ssl_openssl_cert_store = false;
+#if NODE_FIPS_MODE
+bool enable_fips_crypto = false;
+bool force_fips_crypto = false;
+#endif
+#endif
-void ProcessArgv(int* argc,
- const char** argv,
- int* exec_argc,
- const char*** exec_argv,
- bool is_env = false) {
+void ProcessArgv(std::vector<std::string>* args,
+ std::vector<std::string>* exec_args,
+ bool is_env) {
// Parse a few arguments which are specific to Node.
- int v8_argc;
- const char** v8_argv;
- ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv, is_env);
+ std::vector<std::string> v8_args;
+ std::string error;
+ PerProcessOptionsParser::instance.Parse(
+ args,
+ exec_args,
+ &v8_args,
+ per_process_opts.get(),
+ is_env ? kAllowedInEnvironment : kDisallowedInEnvironment,
+ &error);
+ if (!error.empty()) {
+ fprintf(stderr, "%s: %s\n", args->at(0).c_str(), error.c_str());
+ exit(9);
+ }
+
+ if (per_process_opts->print_version) {
+ printf("%s\n", NODE_VERSION);
+ exit(0);
+ }
+
+ if (per_process_opts->print_help) {
+ PrintHelp();
+ exit(0);
+ }
+
+ if (per_process_opts->print_v8_help) {
+ V8::SetFlagsFromString("--help", 6);
+ exit(0);
+ }
+
+ for (const std::string& cve : per_process_opts->security_reverts)
+ Revert(cve.c_str());
+
+ // TODO(addaleax): Move this validation to the option parsers.
+ auto env_opts = per_process_opts->per_isolate->per_env;
+ if (!env_opts->userland_loader.empty() &&
+ !env_opts->experimental_modules) {
+ fprintf(stderr, "%s: --loader requires --experimental-modules be enabled\n",
+ args->at(0).c_str());
+ exit(9);
+ }
+
+ if (env_opts->syntax_check_only && env_opts->has_eval_string) {
+ fprintf(stderr, "%s: either --check or --eval can be used, not both\n",
+ args->at(0).c_str());
+ exit(9);
+ }
+
+ if (per_process_opts->use_openssl_ca && per_process_opts->use_bundled_ca) {
+ fprintf(stderr, "%s: either --use-openssl-ca or --use-bundled-ca can be "
+ "used, not both\n",
+ args->at(0).c_str());
+ exit(9);
+ }
+
+ if (std::find(v8_args.begin(), v8_args.end(),
+ "--abort-on-uncaught-exception") != v8_args.end() ||
+ std::find(v8_args.begin(), v8_args.end(),
+ "--abort_on_uncaught_exception") != v8_args.end()) {
+ abort_on_uncaught_exception = true;
+ }
// TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler
// manually? That would give us a little more control over its runtime
// behavior but it could also interfere with the user's intentions in ways
// we fail to anticipate. Dillema.
- for (int i = 1; i < v8_argc; ++i) {
- if (strncmp(v8_argv[i], "--prof", sizeof("--prof") - 1) == 0) {
- v8_is_profiling = true;
- break;
- }
+ if (std::find(v8_args.begin(), v8_args.end(), "--prof") != v8_args.end()) {
+ v8_is_profiling = true;
}
#ifdef __POSIX__
@@ -3358,28 +2986,40 @@ void ProcessArgv(int* argc,
}
#endif
- // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify
- // the argv array or the elements it points to.
- if (v8_argc > 1)
- V8::SetFlagsFromCommandLine(&v8_argc, const_cast<char**>(v8_argv), true);
+ std::vector<char*> v8_args_as_char_ptr(v8_args.size());
+ if (v8_args.size() > 0) {
+ for (size_t i = 0; i < v8_args.size(); ++i)
+ v8_args_as_char_ptr[i] = &v8_args[i][0];
+ int argc = v8_args.size();
+ V8::SetFlagsFromCommandLine(&argc, &v8_args_as_char_ptr[0], true);
+ v8_args_as_char_ptr.resize(argc);
+ }
// Anything that's still in v8_argv is not a V8 or a node option.
- for (int i = 1; i < v8_argc; i++) {
- fprintf(stderr, "%s: bad option: %s\n", argv[0], v8_argv[i]);
+ for (size_t i = 1; i < v8_args_as_char_ptr.size(); i++) {
+ fprintf(stderr, "%s: bad option: %s\n",
+ args->at(0).c_str(), v8_args_as_char_ptr[i]);
}
- delete[] v8_argv;
- v8_argv = nullptr;
- if (v8_argc > 1) {
+ if (v8_args_as_char_ptr.size() > 1) {
exit(9);
}
+
+ // TODO(addaleax): Remove.
+ zero_fill_all_buffers = per_process_opts->zero_fill_all_buffers;
+ no_deprecation = per_process_opts->per_isolate->per_env->no_deprecation;
+#if HAVE_OPENSSL
+ ssl_openssl_cert_store = per_process_opts->ssl_openssl_cert_store;
+#if NODE_FIPS_MODE
+ enable_fips_crypto = per_process_opts->enable_fips_crypto;
+ force_fips_crypto = per_process_opts->force_fips_crypto;
+#endif
+#endif
}
-void Init(int* argc,
- const char** argv,
- int* exec_argc,
- const char*** exec_argv) {
+void Init(std::vector<std::string>* argv,
+ std::vector<std::string>* exec_argv) {
// Initialize prog_start_time to get relative uptime.
prog_start_time = static_cast<double>(uv_now(uv_default_loop()));
@@ -3396,78 +3036,80 @@ void Init(int* argc,
V8::SetFlagsFromString(NODE_V8_OPTIONS, sizeof(NODE_V8_OPTIONS) - 1);
#endif
+ std::shared_ptr<EnvironmentOptions> default_env_options =
+ per_process_opts->per_isolate->per_env;
{
std::string text;
- config_pending_deprecation =
+ default_env_options->pending_deprecation =
SafeGetenv("NODE_PENDING_DEPRECATION", &text) && text[0] == '1';
}
// Allow for environment set preserving symlinks.
{
std::string text;
- config_preserve_symlinks =
+ default_env_options->preserve_symlinks =
SafeGetenv("NODE_PRESERVE_SYMLINKS", &text) && text[0] == '1';
}
{
std::string text;
- config_preserve_symlinks_main =
+ default_env_options->preserve_symlinks_main =
SafeGetenv("NODE_PRESERVE_SYMLINKS_MAIN", &text) && text[0] == '1';
}
- if (config_warning_file.empty())
- SafeGetenv("NODE_REDIRECT_WARNINGS", &config_warning_file);
+ if (default_env_options->redirect_warnings.empty()) {
+ SafeGetenv("NODE_REDIRECT_WARNINGS",
+ &default_env_options->redirect_warnings);
+ }
#if HAVE_OPENSSL
- if (openssl_config.empty())
- SafeGetenv("OPENSSL_CONF", &openssl_config);
+ std::string* openssl_config = &per_process_opts->openssl_config;
+ if (openssl_config->empty()) {
+ SafeGetenv("OPENSSL_CONF", openssl_config);
+ }
#endif
#if !defined(NODE_WITHOUT_NODE_OPTIONS)
std::string node_options;
if (SafeGetenv("NODE_OPTIONS", &node_options)) {
- // Smallest tokens are 2-chars (a not space and a space), plus 2 extra
- // pointers, for the prepended executable name, and appended NULL pointer.
- size_t max_len = 2 + (node_options.length() + 1) / 2;
- const char** argv_from_env = new const char*[max_len];
- int argc_from_env = 0;
+ std::vector<std::string> env_argv;
// [0] is expected to be the program name, fill it in from the real argv.
- argv_from_env[argc_from_env++] = argv[0];
-
- char* cstr = strdup(node_options.c_str());
- char* initptr = cstr;
- char* token;
- while ((token = strtok(initptr, " "))) { // NOLINT(runtime/threadsafe_fn)
- initptr = nullptr;
- argv_from_env[argc_from_env++] = token;
- }
- argv_from_env[argc_from_env] = nullptr;
- int exec_argc_;
- const char** exec_argv_ = nullptr;
- ProcessArgv(&argc_from_env, argv_from_env, &exec_argc_, &exec_argv_, true);
- delete[] exec_argv_;
- delete[] argv_from_env;
- free(cstr);
+ env_argv.push_back(argv->at(0));
+
+ // Split NODE_OPTIONS at each ' ' character.
+ std::string::size_type index = std::string::npos;
+ do {
+ std::string::size_type prev_index = index;
+ index = node_options.find(' ', index + 1);
+ if (index - prev_index == 1) continue;
+
+ const std::string option = node_options.substr(prev_index + 1, index);
+ if (!option.empty())
+ env_argv.emplace_back(std::move(option));
+ } while (index != std::string::npos);
+
+
+ ProcessArgv(&env_argv, nullptr, true);
}
#endif
- ProcessArgv(argc, argv, exec_argc, exec_argv);
+ ProcessArgv(argv, exec_argv, false);
// Set the process.title immediately after processing argv if --title is set.
- if (!config_process_title.empty())
- uv_set_process_title(config_process_title.c_str());
+ if (!per_process_opts->title.empty())
+ uv_set_process_title(per_process_opts->title.c_str());
#if defined(NODE_HAVE_I18N_SUPPORT)
// If the parameter isn't given, use the env variable.
- if (icu_data_dir.empty())
- SafeGetenv("NODE_ICU_DATA", &icu_data_dir);
+ if (per_process_opts->icu_data_dir.empty())
+ SafeGetenv("NODE_ICU_DATA", &per_process_opts->icu_data_dir);
// Initialize ICU.
// If icu_data_dir is empty here, it will load the 'minimal' data.
- if (!i18n::InitializeICUDirectory(icu_data_dir)) {
+ if (!i18n::InitializeICUDirectory(per_process_opts->icu_data_dir)) {
fprintf(stderr,
"%s: could not initialize ICU "
"(check NODE_ICU_DATA or --icu-data-dir parameters)\n",
- argv[0]);
+ argv->at(0).c_str());
exit(9);
}
#endif
@@ -3478,6 +3120,27 @@ void Init(int* argc,
node_is_initialized = true;
}
+// TODO(addaleax): Deprecate and eventually remove this.
+void Init(int* argc,
+ const char** argv,
+ int* exec_argc,
+ const char*** exec_argv) {
+ std::vector<std::string> argv_(argv, argv + *argc); // NOLINT
+ std::vector<std::string> exec_argv_;
+
+ Init(&argv_, &exec_argv_);
+
+ *argc = argv_.size();
+ *exec_argc = exec_argv_.size();
+ // These leak memory, because, in the original code of this function, no
+ // extra allocations were visible. This should be okay because this function
+ // is only supposed to be called once per process, though.
+ *exec_argv = Malloc<const char*>(*exec_argc);
+ for (int i = 0; i < *exec_argc; ++i)
+ (*exec_argv)[i] = strdup(exec_argv_[i].c_str());
+ for (int i = 0; i < *argc; ++i)
+ argv[i] = strdup(argv_[i].c_str());
+}
void RunAtExit(Environment* env) {
env->RunAtExitCallbacks();
@@ -3602,9 +3265,13 @@ Environment* CreateEnvironment(IsolateData* isolate_data,
Isolate* isolate = context->GetIsolate();
HandleScope handle_scope(isolate);
Context::Scope context_scope(context);
- auto env = new Environment(isolate_data, context,
- v8_platform.GetTracingAgentWriter());
- env->Start(argc, argv, exec_argc, exec_argv, v8_is_profiling);
+ // TODO(addaleax): This is a much better place for parsing per-Environment
+ // options than the global parse call.
+ std::vector<std::string> args(argv, argv + argc);
+ std::vector<std::string> exec_args(exec_argv, exec_argv + exec_argc);
+ Environment* env = new Environment(isolate_data, context,
+ v8_platform.GetTracingAgentWriter());
+ env->Start(args, exec_args, v8_is_profiling);
return env;
}
@@ -3657,23 +3324,27 @@ Local<Context> NewContext(Isolate* isolate,
inline int Start(Isolate* isolate, IsolateData* isolate_data,
- int argc, const char* const* argv,
- int exec_argc, const char* const* exec_argv) {
+ const std::vector<std::string>& args,
+ const std::vector<std::string>& exec_args) {
HandleScope handle_scope(isolate);
Local<Context> context = NewContext(isolate);
Context::Scope context_scope(context);
Environment env(isolate_data, context, v8_platform.GetTracingAgentWriter());
- env.Start(argc, argv, exec_argc, exec_argv, v8_is_profiling);
+ env.Start(args, exec_args, v8_is_profiling);
- const char* path = argc > 1 ? argv[1] : nullptr;
- StartInspector(&env, path, debug_options);
+ const char* path = args.size() > 1 ? args[1].c_str() : nullptr;
+ StartInspector(&env, path, env.options()->debug_options);
- if (debug_options.inspector_enabled() && !v8_platform.InspectorStarted(&env))
+ if (env.options()->debug_options->inspector_enabled &&
+ !v8_platform.InspectorStarted(&env)) {
return 12; // Signal internal error.
+ }
env.set_abort_on_uncaught_exception(abort_on_uncaught_exception);
- if (no_force_async_hooks_checks) {
+ // TODO(addaleax): Maybe access this option directly instead of setting
+ // a boolean member of Environment. Ditto below for trace_sync_io.
+ if (env.options()->no_force_async_hooks_checks) {
env.async_hooks()->no_force_checks();
}
@@ -3684,7 +3355,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
env.async_hooks()->pop_async_id(1);
}
- env.set_trace_sync_io(trace_sync_io);
+ env.set_trace_sync_io(env.options()->trace_sync_io);
{
SealHandleScope seal(isolate);
@@ -3759,8 +3430,8 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator) {
}
inline int Start(uv_loop_t* event_loop,
- int argc, const char* const* argv,
- int exec_argc, const char* const* exec_argv) {
+ const std::vector<std::string>& args,
+ const std::vector<std::string>& exec_args) {
std::unique_ptr<ArrayBufferAllocator, decltype(&FreeArrayBufferAllocator)>
allocator(CreateArrayBufferAllocator(), &FreeArrayBufferAllocator);
Isolate* const isolate = NewIsolate(allocator.get());
@@ -3785,11 +3456,13 @@ inline int Start(uv_loop_t* event_loop,
v8_platform.Platform(),
allocator.get()),
&FreeIsolateData);
- if (track_heap_objects) {
+ // TODO(addaleax): This should load a real per-Isolate option, currently
+ // this is still effectively per-process.
+ if (isolate_data->options()->track_heap_objects) {
isolate->GetHeapProfiler()->StartTrackingHeapObjects(true);
}
exit_code =
- Start(isolate, isolate_data.get(), argc, argv, exec_argc, exec_argv);
+ Start(isolate, isolate_data.get(), args, exec_args);
}
{
@@ -3813,11 +3486,10 @@ int Start(int argc, char** argv) {
// Hack around with the argv pointer. Used for process.title = "blah".
argv = uv_setup_args(argc, argv);
- // This needs to run *before* V8::Initialize(). The const_cast is not
- // optional, in case you're wondering.
- int exec_argc;
- const char** exec_argv;
- Init(&argc, const_cast<const char**>(argv), &exec_argc, &exec_argv);
+ std::vector<std::string> args(argv, argv + argc);
+ std::vector<std::string> exec_args;
+ // This needs to run *before* V8::Initialize().
+ Init(&args, &exec_args);
#if HAVE_OPENSSL
{
@@ -3835,12 +3507,13 @@ int Start(int argc, char** argv) {
V8::SetEntropySource(crypto::EntropySource);
#endif // HAVE_OPENSSL
- v8_platform.Initialize(v8_thread_pool_size);
+ v8_platform.Initialize(
+ per_process_opts->v8_thread_pool_size);
V8::Initialize();
performance::performance_v8_start = PERFORMANCE_NOW();
v8_initialized = true;
const int exit_code =
- Start(uv_default_loop(), argc, argv, exec_argc, exec_argv);
+ Start(uv_default_loop(), args, exec_args);
v8_platform.StopTracingAgent();
v8_initialized = false;
V8::Dispose();
@@ -3853,9 +3526,6 @@ int Start(int argc, char** argv) {
// will never be fully cleaned up.
v8_platform.Dispose();
- delete[] exec_argv;
- exec_argv = nullptr;
-
return exit_code;
}