summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--node.gyp5
-rw-r--r--src/env-inl.h8
-rw-r--r--src/env.cc26
-rw-r--r--src/env.h13
-rw-r--r--src/inspector_agent.cc13
-rw-r--r--src/inspector_agent.h8
-rw-r--r--src/inspector_io.cc7
-rw-r--r--src/inspector_io.h10
-rw-r--r--src/inspector_js_api.cc4
-rw-r--r--src/node.cc818
-rw-r--r--src/node.h9
-rw-r--r--src/node_buffer.h1
-rw-r--r--src/node_config.cc48
-rw-r--r--src/node_constants.cc6
-rw-r--r--src/node_constants.h4
-rw-r--r--src/node_crypto.cc10
-rw-r--r--src/node_debug_options.cc142
-rw-r--r--src/node_debug_options.h42
-rw-r--r--src/node_i18n.h2
-rw-r--r--src/node_internals.h66
-rw-r--r--src/node_options-inl.h422
-rw-r--r--src/node_options.cc219
-rw-r--r--src/node_options.h356
-rw-r--r--src/node_worker.cc4
24 files changed, 1354 insertions, 889 deletions
diff --git a/node.gyp b/node.gyp
index a33dde4272..5b8ca26bb2 100644
--- a/node.gyp
+++ b/node.gyp
@@ -343,7 +343,6 @@
'src/node_config.cc',
'src/node_constants.cc',
'src/node_contextify.cc',
- 'src/node_debug_options.cc',
'src/node_domain.cc',
'src/node_encoding.cc',
'src/node_errors.h',
@@ -351,6 +350,7 @@
'src/node_http2.cc',
'src/node_http_parser.cc',
'src/node_messaging.cc',
+ 'src/node_options.cc',
'src/node_os.cc',
'src/node_platform.cc',
'src/node_perf.cc',
@@ -407,7 +407,6 @@
'src/node_code_cache.h',
'src/node_constants.h',
'src/node_contextify.h',
- 'src/node_debug_options.h',
'src/node_file.h',
'src/node_http2.h',
'src/node_http2_state.h',
@@ -415,6 +414,8 @@
'src/node_javascript.h',
'src/node_messaging.h',
'src/node_mutex.h',
+ 'src/node_options.h',
+ 'src/node_options-inl.h',
'src/node_perf.h',
'src/node_perf_common.h',
'src/node_persistent.h',
diff --git a/src/env-inl.h b/src/env-inl.h
index 3bca20c81c..28c30e0354 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -564,6 +564,14 @@ Environment::file_handle_read_wrap_freelist() {
return file_handle_read_wrap_freelist_;
}
+inline std::shared_ptr<EnvironmentOptions> Environment::options() {
+ return options_;
+}
+
+inline std::shared_ptr<PerIsolateOptions> IsolateData::options() {
+ return options_;
+}
+
void Environment::CreateImmediate(native_immediate_callback cb,
void* data,
v8::Local<v8::Object> obj,
diff --git a/src/env.cc b/src/env.cc
index 97405fe4b1..9c854f6f3f 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -48,6 +48,8 @@ IsolateData::IsolateData(Isolate* isolate,
if (platform_ != nullptr)
platform_->RegisterIsolate(this, event_loop);
+ options_.reset(new PerIsolateOptions(*per_process_opts->per_isolate));
+
// Create string and private symbol properties as internalized one byte
// strings after the platform is properly initialized.
//
@@ -136,9 +138,6 @@ Environment::Environment(IsolateData* isolate_data,
makecallback_cntr_(0),
should_abort_on_uncaught_toggle_(isolate_, 1),
trace_category_state_(isolate_, kTraceCategoryCount),
-#if HAVE_INSPECTOR
- inspector_agent_(new inspector::Agent(this)),
-#endif
http_parser_buffer_(nullptr),
fs_stats_field_array_(isolate_, kFsStatsFieldsLength * 2),
fs_stats_field_bigint_array_(isolate_, kFsStatsFieldsLength * 2),
@@ -148,6 +147,19 @@ Environment::Environment(IsolateData* isolate_data,
v8::Context::Scope context_scope(context);
set_as_external(v8::External::New(isolate(), this));
+ // We create new copies of the per-Environment option sets, so that it is
+ // easier to modify them after Environment creation. The defaults are
+ // part of the per-Isolate option set, for which in turn the defaults are
+ // part of the per-process option set.
+ options_.reset(new EnvironmentOptions(*isolate_data->options()->per_env));
+ options_->debug_options.reset(new DebugOptions(*options_->debug_options));
+
+#if HAVE_INSPECTOR
+ // We can only create the inspector agent after having cloned the options.
+ inspector_agent_ =
+ std::unique_ptr<inspector::Agent>(new inspector::Agent(this));
+#endif
+
AssignToContext(context, ContextInfo(""));
if (tracing_agent_writer_ != nullptr) {
@@ -211,10 +223,8 @@ Environment::~Environment() {
delete[] http_parser_buffer_;
}
-void Environment::Start(int argc,
- const char* const* argv,
- int exec_argc,
- const char* const* exec_argv,
+void Environment::Start(const std::vector<std::string>& args,
+ const std::vector<std::string>& exec_args,
bool start_profiler_idle_notifier) {
HandleScope handle_scope(isolate());
Context::Scope context_scope(context());
@@ -260,7 +270,7 @@ void Environment::Start(int argc,
process_template->GetFunction()->NewInstance(context()).ToLocalChecked();
set_process_object(process_object);
- SetupProcessObject(this, argc, argv, exec_argc, exec_argv);
+ SetupProcessObject(this, args, exec_args);
static uv_once_t init_once = UV_ONCE_INIT;
uv_once(&init_once, InitThreadLocalOnce);
diff --git a/src/env.h b/src/env.h
index b393670b21..556a09754f 100644
--- a/src/env.h
+++ b/src/env.h
@@ -34,6 +34,7 @@
#include "uv.h"
#include "v8.h"
#include "node.h"
+#include "node_options.h"
#include "node_http2_state.h"
#include <list>
@@ -365,6 +366,7 @@ class IsolateData {
inline uv_loop_t* event_loop() const;
inline uint32_t* zero_fill_field() const;
inline MultiIsolatePlatform* platform() const;
+ inline std::shared_ptr<PerIsolateOptions> options();
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)
@@ -398,6 +400,7 @@ class IsolateData {
uv_loop_t* const event_loop_;
uint32_t* const zero_fill_field_;
MultiIsolatePlatform* platform_;
+ std::shared_ptr<PerIsolateOptions> options_;
DISALLOW_COPY_AND_ASSIGN(IsolateData);
};
@@ -582,10 +585,8 @@ class Environment {
tracing::AgentWriterHandle* tracing_agent_writer);
~Environment();
- void Start(int argc,
- const char* const* argv,
- int exec_argc,
- const char* const* exec_argv,
+ void Start(const std::vector<std::string>& args,
+ const std::vector<std::string>& exec_args,
bool start_profiler_idle_notifier);
typedef void (*HandleCleanupCb)(Environment* env,
@@ -882,6 +883,8 @@ class Environment {
v8::EmbedderGraph* graph,
void* data);
+ inline std::shared_ptr<EnvironmentOptions> options();
+
private:
inline void CreateImmediate(native_immediate_callback cb,
void* data,
@@ -912,6 +915,8 @@ class Environment {
size_t makecallback_cntr_;
std::vector<double> destroy_async_id_list_;
+ std::shared_ptr<EnvironmentOptions> options_;
+
AliasedBuffer<uint32_t, v8::Uint32Array> should_abort_on_uncaught_toggle_;
int should_not_abort_scope_counter_ = 0;
diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc
index 0b99217d0a..adc32f1bcd 100644
--- a/src/inspector_agent.cc
+++ b/src/inspector_agent.cc
@@ -608,11 +608,14 @@ class NodeInspectorClient : public V8InspectorClient {
std::unique_ptr<MainThreadInterface> interface_;
};
-Agent::Agent(Environment* env) : parent_env_(env) {}
+Agent::Agent(Environment* env)
+ : parent_env_(env),
+ debug_options_(env->options()->debug_options) {}
Agent::~Agent() = default;
-bool Agent::Start(const std::string& path, const DebugOptions& options) {
+bool Agent::Start(const std::string& path,
+ std::shared_ptr<DebugOptions> options) {
path_ = path;
debug_options_ = options;
client_ = std::make_shared<NodeInspectorClient>(parent_env_);
@@ -626,8 +629,8 @@ bool Agent::Start(const std::string& path, const DebugOptions& options) {
StartDebugSignalHandler();
}
- bool wait_for_connect = options.wait_for_connect();
- if (!options.inspector_enabled() || !StartIoThread()) {
+ bool wait_for_connect = options->wait_for_connect();
+ if (!options->inspector_enabled || !StartIoThread()) {
return false;
}
if (wait_for_connect) {
@@ -789,7 +792,7 @@ void Agent::ContextCreated(Local<Context> context, const ContextInfo& info) {
}
bool Agent::WillWaitForConnect() {
- return debug_options_.wait_for_connect();
+ return debug_options_->wait_for_connect();
}
bool Agent::IsActive() {
diff --git a/src/inspector_agent.h b/src/inspector_agent.h
index dcd6e13aba..4e32d3ef1a 100644
--- a/src/inspector_agent.h
+++ b/src/inspector_agent.h
@@ -9,7 +9,7 @@
#error("This header can only be used when inspector is enabled")
#endif
-#include "node_debug_options.h"
+#include "node_options.h"
#include "node_persistent.h"
#include "v8.h"
@@ -45,7 +45,7 @@ class Agent {
~Agent();
// Create client_, may create io_ if option enabled
- bool Start(const std::string& path, const DebugOptions& options);
+ bool Start(const std::string& path, std::shared_ptr<DebugOptions> options);
// Stop and destroy io_
void Stop();
@@ -96,7 +96,7 @@ class Agent {
// Calls StartIoThread() from off the main thread.
void RequestIoThreadStart();
- DebugOptions& options() { return debug_options_; }
+ std::shared_ptr<DebugOptions> options() { return debug_options_; }
void ContextCreated(v8::Local<v8::Context> context, const ContextInfo& info);
private:
@@ -109,7 +109,7 @@ class Agent {
// Interface for transports, e.g. WebSocket server
std::unique_ptr<InspectorIo> io_;
std::string path_;
- DebugOptions debug_options_;
+ std::shared_ptr<DebugOptions> debug_options_;
bool pending_enable_async_hook_ = false;
bool pending_disable_async_hook_ = false;
diff --git a/src/inspector_io.cc b/src/inspector_io.cc
index 41fea546a8..da44d55d06 100644
--- a/src/inspector_io.cc
+++ b/src/inspector_io.cc
@@ -242,7 +242,7 @@ class InspectorIoDelegate: public node::inspector::SocketServerDelegate {
std::unique_ptr<InspectorIo> InspectorIo::Start(
std::shared_ptr<MainThreadHandle> main_thread,
const std::string& path,
- const DebugOptions& options) {
+ std::shared_ptr<DebugOptions> options) {
auto io = std::unique_ptr<InspectorIo>(
new InspectorIo(main_thread, path, options));
if (io->request_queue_->Expired()) { // Thread is not running
@@ -253,7 +253,7 @@ std::unique_ptr<InspectorIo> InspectorIo::Start(
InspectorIo::InspectorIo(std::shared_ptr<MainThreadHandle> main_thread,
const std::string& path,
- const DebugOptions& options)
+ std::shared_ptr<DebugOptions> options)
: main_thread_(main_thread), options_(options),
thread_(), script_name_(path), id_(GenerateID()) {
Mutex::ScopedLock scoped_lock(thread_start_lock_);
@@ -288,7 +288,8 @@ void InspectorIo::ThreadMain() {
new InspectorIoDelegate(queue, main_thread_, id_,
script_path, script_name_));
InspectorSocketServer server(std::move(delegate), &loop,
- options_.host_name(), options_.port());
+ options_->host().c_str(),
+ options_->port());
request_queue_ = queue->handle();
// Its lifetime is now that of the server delegate
queue.reset();
diff --git a/src/inspector_io.h b/src/inspector_io.h
index 7c43d212f0..2b9f0acd48 100644
--- a/src/inspector_io.h
+++ b/src/inspector_io.h
@@ -2,7 +2,6 @@
#define SRC_INSPECTOR_IO_H_
#include "inspector_socket_server.h"
-#include "node_debug_options.h"
#include "node_mutex.h"
#include "uv.h"
@@ -46,19 +45,20 @@ class InspectorIo {
// Returns empty pointer if thread was not started
static std::unique_ptr<InspectorIo> Start(
std::shared_ptr<MainThreadHandle> main_thread, const std::string& path,
- const DebugOptions& options);
+ std::shared_ptr<DebugOptions> options);
// Will block till the transport thread shuts down
~InspectorIo();
void StopAcceptingNewConnections();
- std::string host() const { return options_.host_name(); }
+ const std::string& host() const { return options_->host(); }
int port() const { return port_; }
std::vector<std::string> GetTargetIds() const;
private:
InspectorIo(std::shared_ptr<MainThreadHandle> handle,
- const std::string& path, const DebugOptions& options);
+ const std::string& path,
+ std::shared_ptr<DebugOptions> options);
// Wrapper for agent->ThreadMain()
static void ThreadMain(void* agent);
@@ -72,7 +72,7 @@ class InspectorIo {
// Used to post on a frontend interface thread, lives while the server is
// running
std::shared_ptr<RequestQueue> request_queue_;
- const DebugOptions options_;
+ std::shared_ptr<DebugOptions> options_;
// The IO thread runs its own uv_loop to implement the TCP server off
// the main thread.
diff --git a/src/inspector_js_api.cc b/src/inspector_js_api.cc
index 4e95598d3a..b8c78ba5eb 100644
--- a/src/inspector_js_api.cc
+++ b/src/inspector_js_api.cc
@@ -242,12 +242,12 @@ void Open(const FunctionCallbackInfo<Value>& args) {
if (args.Length() > 0 && args[0]->IsUint32()) {
uint32_t port = args[0]->Uint32Value();
- agent->options().set_port(static_cast<int>(port));
+ agent->options()->host_port.port = port;
}
if (args.Length() > 1 && args[1]->IsString()) {
Utf8Value host(env->isolate(), args[1].As<String>());
- agent->options().set_host_name(*host);
+ agent->options()->host_port.host_name = *host;
}
if (args.Length() > 2 && args[2]->IsBoolean()) {
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;
}
diff --git a/src/node.h b/src/node.h
index a94612eb0b..ab82be7ac4 100644
--- a/src/node.h
+++ b/src/node.h
@@ -199,6 +199,8 @@ typedef intptr_t ssize_t;
namespace node {
+// TODO(addaleax): Deprecate and remove all of these ASAP. They have been
+// made effectively non-functional anyway.
NODE_EXTERN extern bool no_deprecation;
#if HAVE_OPENSSL
NODE_EXTERN extern bool ssl_openssl_cert_store;
@@ -208,7 +210,12 @@ NODE_EXTERN extern bool force_fips_crypto;
# endif
#endif
+// TODO(addaleax): Officially deprecate this and replace it with something
+// better suited for a public embedder API.
NODE_EXTERN int Start(int argc, char* argv[]);
+
+// TODO(addaleax): Officially deprecate this and replace it with something
+// better suited for a public embedder API.
NODE_EXTERN void Init(int* argc,
const char** argv,
int* exec_argc,
@@ -265,6 +272,8 @@ NODE_EXTERN IsolateData* CreateIsolateData(
ArrayBufferAllocator* allocator);
NODE_EXTERN void FreeIsolateData(IsolateData* isolate_data);
+// TODO(addaleax): Add an official variant using STL containers, and move
+// per-Environment options parsing here.
NODE_EXTERN Environment* CreateEnvironment(IsolateData* isolate_data,
v8::Local<v8::Context> context,
int argc,
diff --git a/src/node_buffer.h b/src/node_buffer.h
index b4aa12cbcf..e8d306e7dd 100644
--- a/src/node_buffer.h
+++ b/src/node_buffer.h
@@ -27,6 +27,7 @@
namespace node {
+// TODO(addaleax): Deprecate and remove this ASAP.
extern bool zero_fill_all_buffers;
namespace Buffer {
diff --git a/src/node_config.cc b/src/node_config.cc
index 62fd4ef81e..d34269912e 100644
--- a/src/node_config.cc
+++ b/src/node_config.cc
@@ -2,7 +2,6 @@
#include "node_i18n.h"
#include "env-inl.h"
#include "util-inl.h"
-#include "node_debug_options.h"
namespace node {
@@ -56,6 +55,7 @@ static void Initialize(Local<Object> target,
#ifdef NODE_FIPS_MODE
READONLY_BOOLEAN_PROPERTY("fipsMode");
+ // TODO(addaleax): Use options parser variable instead.
if (force_fips_crypto)
READONLY_BOOLEAN_PROPERTY("fipsForced");
#endif
@@ -72,35 +72,38 @@ static void Initialize(Local<Object> target,
READONLY_BOOLEAN_PROPERTY("hasTracing");
#endif
- READONLY_STRING_PROPERTY(target, "icuDataDir", icu_data_dir);
+ // TODO(addaleax): This seems to be an unused, private API. Remove it?
+ READONLY_STRING_PROPERTY(target, "icuDataDir",
+ per_process_opts->icu_data_dir);
#endif // NODE_HAVE_I18N_SUPPORT
- if (config_preserve_symlinks)
+ if (env->options()->preserve_symlinks)
READONLY_BOOLEAN_PROPERTY("preserveSymlinks");
- if (config_preserve_symlinks_main)
+ if (env->options()->preserve_symlinks_main)
READONLY_BOOLEAN_PROPERTY("preserveSymlinksMain");
- if (config_experimental_modules) {
+ if (env->options()->experimental_modules) {
READONLY_BOOLEAN_PROPERTY("experimentalModules");
- if (!config_userland_loader.empty()) {
- READONLY_STRING_PROPERTY(target, "userLoader", config_userland_loader);
+ const std::string& userland_loader = env->options()->userland_loader;
+ if (!userland_loader.empty()) {
+ READONLY_STRING_PROPERTY(target, "userLoader", userland_loader);
}
}
- if (config_experimental_vm_modules)
+ if (env->options()->experimental_vm_modules)
READONLY_BOOLEAN_PROPERTY("experimentalVMModules");
- if (config_experimental_worker)
+ if (env->options()->experimental_worker)
READONLY_BOOLEAN_PROPERTY("experimentalWorker");
- if (config_experimental_repl_await)
+ if (env->options()->experimental_repl_await)
READONLY_BOOLEAN_PROPERTY("experimentalREPLAwait");
- if (config_pending_deprecation)
+ if (env->options()->pending_deprecation)
READONLY_BOOLEAN_PROPERTY("pendingDeprecation");
- if (config_expose_internals)
+ if (env->options()->expose_internals)
READONLY_BOOLEAN_PROPERTY("exposeInternals");
if (env->abort_on_uncaught_exception())
@@ -110,22 +113,25 @@ static void Initialize(Local<Object> target,
"bits",
Number::New(env->isolate(), 8 * sizeof(intptr_t)));
- if (!config_warning_file.empty()) {
- READONLY_STRING_PROPERTY(target, "warningFile", config_warning_file);
+ const std::string& warning_file = env->options()->redirect_warnings;
+ if (!warning_file.empty()) {
+ READONLY_STRING_PROPERTY(target, "warningFile", warning_file);
}
- Local<Object> debugOptions = Object::New(isolate);
- READONLY_PROPERTY(target, "debugOptions", debugOptions);
+ std::shared_ptr<DebugOptions> debug_options = env->options()->debug_options;
+ Local<Object> debug_options_obj = Object::New(isolate);
+ READONLY_PROPERTY(target, "debugOptions", debug_options_obj);
- READONLY_STRING_PROPERTY(debugOptions, "host", debug_options.host_name());
+ READONLY_STRING_PROPERTY(debug_options_obj, "host",
+ debug_options->host());
- READONLY_PROPERTY(debugOptions,
+ READONLY_PROPERTY(debug_options_obj,
"port",
- Integer::New(isolate, debug_options.port()));
+ Integer::New(isolate, debug_options->port()));
- READONLY_PROPERTY(debugOptions,
+ READONLY_PROPERTY(debug_options_obj,
"inspectorEnabled",
- Boolean::New(isolate, debug_options.inspector_enabled()));
+ Boolean::New(isolate, debug_options->inspector_enabled));
} // InitConfig
} // namespace node
diff --git a/src/node_constants.cc b/src/node_constants.cc
index f1468ff7ca..b6c7bf37a3 100644
--- a/src/node_constants.cc
+++ b/src/node_constants.cc
@@ -51,10 +51,6 @@ namespace node {
using v8::Local;
using v8::Object;
-#if HAVE_OPENSSL
-const char* default_cipher_list = DEFAULT_CIPHER_LIST_CORE;
-#endif
-
namespace {
void DefineErrnoConstants(Local<Object> target) {
@@ -1240,7 +1236,7 @@ void DefineCryptoConstants(Local<Object> target) {
DEFAULT_CIPHER_LIST_CORE);
NODE_DEFINE_STRING_CONSTANT(target,
"defaultCipherList",
- default_cipher_list);
+ per_process_opts->tls_cipher_list.c_str());
#endif
NODE_DEFINE_CONSTANT(target, INT_MAX);
}
diff --git a/src/node_constants.h b/src/node_constants.h
index 1de420e2de..6f73fb4d7d 100644
--- a/src/node_constants.h
+++ b/src/node_constants.h
@@ -66,10 +66,6 @@
namespace node {
-#if HAVE_OPENSSL
-extern const char* default_cipher_list;
-#endif
-
void DefineConstants(v8::Isolate* isolate, v8::Local<v8::Object> target);
} // namespace node
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 7f5544b8f2..11ed4a3f19 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -756,6 +756,8 @@ static X509_STORE* NewRootCertStore() {
if (*system_cert_path != '\0') {
X509_STORE_load_locations(store, system_cert_path, nullptr);
}
+ // TODO(addaleax): Replace `ssl_openssl_cert_store` with
+ // `per_process_opts->ssl_openssl_cert_store`.
if (ssl_openssl_cert_store) {
X509_STORE_set_default_paths(store);
} else {
@@ -5094,14 +5096,14 @@ void InitCryptoOnce() {
OPENSSL_no_config();
// --openssl-config=...
- if (!openssl_config.empty()) {
+ if (!per_process_opts->openssl_config.empty()) {
OPENSSL_load_builtin_modules();
#ifndef OPENSSL_NO_ENGINE
ENGINE_load_builtin_engines();
#endif
ERR_clear_error();
CONF_modules_load_file(
- openssl_config.c_str(),
+ per_process_opts->openssl_config.c_str(),
nullptr,
CONF_MFLAGS_DEFAULT_SECTION);
int err = ERR_get_error();
@@ -5119,6 +5121,9 @@ void InitCryptoOnce() {
#ifdef NODE_FIPS_MODE
/* Override FIPS settings in cnf file, if needed. */
unsigned long err = 0; // NOLINT(runtime/int)
+ // TODO(addaleax): Use commented part instead.
+ /*if (per_process_opts->enable_fips_crypto ||
+ per_process_opts->force_fips_crypto) {*/
if (enable_fips_crypto || force_fips_crypto) {
if (0 == FIPS_mode() && !FIPS_mode_set(1)) {
err = ERR_get_error();
@@ -5181,6 +5186,7 @@ void GetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
}
void SetFipsCrypto(const FunctionCallbackInfo<Value>& args) {
+ // TODO(addaleax): Use options parser variables instead.
CHECK(!force_fips_crypto);
Environment* env = Environment::GetCurrent(args);
const bool enabled = FIPS_mode();
diff --git a/src/node_debug_options.cc b/src/node_debug_options.cc
deleted file mode 100644
index 5fc29059dd..0000000000
--- a/src/node_debug_options.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "node_debug_options.h"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include "util.h"
-
-namespace node {
-
-namespace {
-const int default_inspector_port = 9229;
-
-inline std::string remove_brackets(const std::string& host) {
- if (!host.empty() && host.front() == '[' && host.back() == ']')
- return host.substr(1, host.size() - 2);
- else
- return host;
-}
-
-int parse_and_validate_port(const std::string& port) {
- char* endptr;
- errno = 0;
- const long result = strtol(port.c_str(), &endptr, 10); // NOLINT(runtime/int)
- if (errno != 0 || *endptr != '\0'||
- (result != 0 && result < 1024) || result > 65535) {
- fprintf(stderr, "Debug port must be 0 or in range 1024 to 65535.\n");
- exit(12);
- }
- return static_cast<int>(result);
-}
-
-std::pair<std::string, int> split_host_port(const std::string& arg) {
- // remove_brackets only works if no port is specified
- // so if it has an effect only an IPv6 address was specified
- std::string host = remove_brackets(arg);
- if (host.length() < arg.length())
- return {host, -1};
-
- size_t colon = arg.rfind(':');
- if (colon == std::string::npos) {
- // Either a port number or a host name. Assume that
- // if it's not all decimal digits, it's a host name.
- for (char c : arg) {
- if (c < '0' || c > '9') {
- return {arg, -1};
- }
- }
- return {"", parse_and_validate_port(arg)};
- }
- // host and port found
- return std::make_pair(remove_brackets(arg.substr(0, colon)),
- parse_and_validate_port(arg.substr(colon + 1)));
-}
-
-} // namespace
-
-DebugOptions::DebugOptions() :
- inspector_enabled_(false),
- deprecated_debug_(false),
- break_first_line_(false),
- break_node_first_line_(false),
- host_name_("127.0.0.1"), port_(-1) { }
-
-bool DebugOptions::ParseOption(const char* argv0, const std::string& option) {
- bool has_argument = false;
- std::string option_name;
- std::string argument;
-
- auto pos = option.find("=");
- if (pos == std::string::npos) {
- option_name = option;
- } else {
- option_name = option.substr(0, pos);
- argument = option.substr(pos + 1);
-
- if (argument.length() > 0)
- has_argument = true;
- else
- argument.clear();
- }
-
- // Note that --debug-port and --debug-brk in conjunction with --inspect
- // work but are undocumented.
- // --debug is no longer valid.
- // Ref: https://github.com/nodejs/node/issues/12630
- // Ref: https://github.com/nodejs/node/pull/12949
- if (option_name == "--inspect") {
- inspector_enabled_ = true;
- } else if (option_name == "--debug") {
- deprecated_debug_ = true;
- } else if (option_name == "--inspect-brk") {
- inspector_enabled_ = true;
- break_first_line_ = true;
- } else if (option_name == "--inspect-brk-node") {
- inspector_enabled_ = true;
- break_node_first_line_ = true;
- } else if (option_name == "--debug-brk") {
- break_first_line_ = true;
- deprecated_debug_ = true;
- } else if (option_name == "--debug-port" ||
- option_name == "--inspect-port") {
- if (!has_argument) {
- fprintf(stderr, "%s: %s requires an argument\n",
- argv0, option.c_str());
- exit(9);
- }
- } else {
- return false;
- }
-
-#if !HAVE_INSPECTOR
- if (inspector_enabled_) {
- fprintf(stderr,
- "Inspector support is not available with this Node.js build\n");
- }
- inspector_enabled_ = false;
- return false;
-#endif
-
- // argument can be specified for *any* option to specify host:port
- if (has_argument) {
- std::pair<std::string, int> host_port = split_host_port(argument);
- if (!host_port.first.empty()) {
- host_name_ = host_port.first;
- }
- if (host_port.second >= 0) {
- port_ = host_port.second;
- }
- }
-
- return true;
-}
-
-int DebugOptions::port() const {
- int port = port_;
- if (port < 0) {
- port = default_inspector_port;
- }
- return port;
-}
-
-} // namespace node
diff --git a/src/node_debug_options.h b/src/node_debug_options.h
deleted file mode 100644
index 98922ab099..0000000000
--- a/src/node_debug_options.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef SRC_NODE_DEBUG_OPTIONS_H_
-#define SRC_NODE_DEBUG_OPTIONS_H_
-
-#include <string>
-
-// Forward declaration to break recursive dependency chain with src/env.h.
-namespace node {
-
-class DebugOptions {
- public:
- DebugOptions();
- bool ParseOption(const char* argv0, const std::string& option);
- bool inspector_enabled() const { return inspector_enabled_; }
- bool deprecated_invocation() const {
- return deprecated_debug_ &&
- inspector_enabled_ &&
- break_first_line_;
- }
- bool invalid_invocation() const {
- return deprecated_debug_ && !inspector_enabled_;
- }
- bool wait_for_connect() const {
- return break_first_line_ || break_node_first_line_;
- }
- std::string host_name() const { return host_name_; }
- void set_host_name(std::string host_name) { host_name_ = host_name; }
- int port() const;
- void set_port(int port) { port_ = port; }
- bool break_node_first_line() const { return break_node_first_line_; }
-
- private:
- bool inspector_enabled_;
- bool deprecated_debug_;
- bool break_first_line_;
- bool break_node_first_line_;
- std::string host_name_;
- int port_;
-};
-
-} // namespace node
-
-#endif // SRC_NODE_DEBUG_OPTIONS_H_
diff --git a/src/node_i18n.h b/src/node_i18n.h
index 70a0c79f76..7faa5e57ef 100644
--- a/src/node_i18n.h
+++ b/src/node_i18n.h
@@ -31,8 +31,6 @@
namespace node {
-extern std::string icu_data_dir; // NOLINT(runtime/string)
-
namespace i18n {
bool InitializeICUDirectory(const std::string& path);
diff --git a/src/node_internals.h b/src/node_internals.h
index 968d229f10..60630953c7 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -33,7 +33,6 @@
#include "v8.h"
#include "tracing/trace_event.h"
#include "node_perf_common.h"
-#include "node_debug_options.h"
#include "node_api.h"
#include <stdint.h>
@@ -171,67 +170,10 @@ struct sockaddr;
namespace node {
-// Set in node.cc by ParseArgs with the value of --openssl-config.
-// Used in node_crypto.cc when initializing OpenSSL.
-extern std::string openssl_config;
-
-// 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
-extern bool config_preserve_symlinks;
-
-// 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
-extern bool config_preserve_symlinks_main;
-
-// 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
-extern bool config_experimental_modules;
-
-// 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
-extern bool config_experimental_vm_modules;
-
-// 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 the module loader.
-extern bool config_experimental_worker;
-
-// 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.
-extern bool config_experimental_repl_await;
-
-// 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
-extern std::string config_userland_loader;
-
-// 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
-extern bool config_expose_internals;
-
-// Set in node.cc by ParseArgs when --redirect-warnings= is used.
-// Used to redirect warning output to a file rather than sending
-// it to stderr.
-extern std::string config_warning_file; // NOLINT(runtime/string)
-
-// Set in node.cc by ParseArgs when --pending-deprecation or
-// NODE_PENDING_DEPRECATION is used
-extern bool config_pending_deprecation;
-
// Tells whether it is safe to call v8::Isolate::GetCurrent().
extern bool v8_initialized;
-// Contains initial debug options.
-// Set in node.cc.
-// Used in node_config.cc.
-extern node::DebugOptions debug_options;
+extern std::shared_ptr<PerProcessOptions> per_process_opts;
// Forward declaration
class Environment;
@@ -415,10 +357,8 @@ inline v8::Local<v8::Value> FillGlobalStatsArray(Environment* env,
void SetupBootstrapObject(Environment* env,
v8::Local<v8::Object> bootstrapper);
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);
// Call _register<module_name> functions for all of
// the built-in modules. Because built-in modules don't
diff --git a/src/node_options-inl.h b/src/node_options-inl.h
new file mode 100644
index 0000000000..e610cd50d1
--- /dev/null
+++ b/src/node_options-inl.h
@@ -0,0 +1,422 @@
+#ifndef SRC_NODE_OPTIONS_INL_H_
+#define SRC_NODE_OPTIONS_INL_H_
+
+#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#include "node_options.h"
+#include "util.h"
+#include <cstdlib>
+
+namespace node {
+
+PerIsolateOptions* PerProcessOptions::get_per_isolate_options() {
+ return per_isolate.get();
+}
+
+DebugOptions* EnvironmentOptions::get_debug_options() {
+ return debug_options.get();
+}
+
+EnvironmentOptions* PerIsolateOptions::get_per_env_options() {
+ return per_env.get();
+}
+
+template <typename Options>
+void OptionsParser<Options>::AddOption(const std::string& name,
+ bool Options::* field,
+ OptionEnvvarSettings env_setting) {
+ options_.emplace(name, OptionInfo {
+ kBoolean,
+ std::make_shared<SimpleOptionField<bool>>(field),
+ env_setting
+ });
+}
+
+template <typename Options>
+void OptionsParser<Options>::AddOption(const std::string& name,
+ int64_t Options::* field,
+ OptionEnvvarSettings env_setting) {
+ options_.emplace(name, OptionInfo {
+ kInteger,
+ std::make_shared<SimpleOptionField<int64_t>>(field),
+ env_setting
+ });
+}
+
+template <typename Options>
+void OptionsParser<Options>::AddOption(const std::string& name,
+ std::string Options::* field,
+ OptionEnvvarSettings env_setting) {
+ options_.emplace(name, OptionInfo {
+ kString,
+ std::make_shared<SimpleOptionField<std::string>>(field),
+ env_setting
+ });
+}
+
+template <typename Options>
+void OptionsParser<Options>::AddOption(
+ const std::string& name,
+ std::vector<std::string> Options::* field,
+ OptionEnvvarSettings env_setting) {
+ options_.emplace(name, OptionInfo {
+ kStringList,
+ std::make_shared<SimpleOptionField<std::vector<std::string>>>(field),
+ env_setting
+ });
+}
+
+template <typename Options>
+void OptionsParser<Options>::AddOption(const std::string& name,
+ HostPort Options::* field,
+ OptionEnvvarSettings env_setting) {
+ options_.emplace(name, OptionInfo {
+ kHostPort,
+ std::make_shared<SimpleOptionField<HostPort>>(field),
+ env_setting
+ });
+}
+
+template <typename Options>
+void OptionsParser<Options>::AddOption(const std::string& name, NoOp no_op_tag,
+ OptionEnvvarSettings env_setting) {
+ options_.emplace(name, OptionInfo { kNoOp, nullptr, env_setting });
+}
+
+template <typename Options>
+void OptionsParser<Options>::AddOption(const std::string& name,
+ V8Option v8_option_tag,
+ OptionEnvvarSettings env_setting) {
+ options_.emplace(name, OptionInfo { kV8Option, nullptr, env_setting });
+}
+
+template <typename Options>
+void OptionsParser<Options>::AddAlias(const std::string& from,
+ const std::string& to) {
+ aliases_[from] = { to };
+}
+
+template <typename Options>
+void OptionsParser<Options>::AddAlias(const std::string& from,
+ const std::vector<std::string>& to) {
+ aliases_[from] = to;
+}
+
+template <typename Options>
+void OptionsParser<Options>::AddAlias(
+ const std::string& from,
+ const std::initializer_list<std::string>& to) {
+ AddAlias(from, std::vector<std::string>(to));
+}
+
+template <typename Options>
+void OptionsParser<Options>::Implies(const std::string& from,
+ const std::string& to) {
+ auto it = options_.find(to);
+ CHECK_NE(it, options_.end());
+ CHECK_EQ(it->second.type, kBoolean);
+ implications_.emplace(from, Implication {
+ std::static_pointer_cast<OptionField<bool>>(it->second.field), true
+ });
+}
+
+template <typename Options>
+void OptionsParser<Options>::ImpliesNot(const std::string& from,
+ const std::string& to) {
+ auto it = options_.find(to);
+ CHECK_NE(it, options_.end());
+ CHECK_EQ(it->second.type, kBoolean);
+ implications_.emplace(from, Implication {
+ std::static_pointer_cast<OptionField<bool>>(it->second.field), false
+ });
+}
+
+template <typename Options>
+template <typename OriginalField, typename ChildOptions>
+auto OptionsParser<Options>::Convert(
+ std::shared_ptr<OriginalField> original,
+ ChildOptions* (Options::* get_child)()) {
+ // If we have a field on ChildOptions, and we want to access it from an
+ // Options instance, we call get_child() on the original Options and then
+ // access it, i.e. this class implements a kind of function chaining.
+ struct AdaptedField : BaseOptionField {
+ void* LookupImpl(Options* options) const override {
+ return original->LookupImpl((options->*get_child)());
+ }
+
+ AdaptedField(
+ std::shared_ptr<OriginalField> original,
+ ChildOptions* (Options::* get_child)())
+ : original(original), get_child(get_child) {}
+
+ std::shared_ptr<OriginalField> original;
+ ChildOptions* (Options::* get_child)();
+ };
+
+ return std::shared_ptr<BaseOptionField>(
+ new AdaptedField(original, get_child));
+}
+template <typename Options>
+template <typename ChildOptions>
+auto OptionsParser<Options>::Convert(
+ typename OptionsParser<ChildOptions>::OptionInfo original,
+ ChildOptions* (Options::* get_child)()) {
+ return OptionInfo {
+ original.type,
+ Convert(original.field, get_child),
+ original.env_setting
+ };
+}
+
+template <typename Options>
+template <typename ChildOptions>
+auto OptionsParser<Options>::Convert(
+ typename OptionsParser<ChildOptions>::Implication original,
+ ChildOptions* (Options::* get_child)()) {
+ return Implication {
+ std::static_pointer_cast<OptionField<bool>>(
+ Convert(original.target_field, get_child)),
+ original.target_value
+ };
+}
+
+template <typename Options>
+template <typename ChildOptions>
+void OptionsParser<Options>::Insert(
+ OptionsParser<ChildOptions>* child_options_parser,
+ ChildOptions* (Options::* get_child)()) {
+ aliases_.insert(child_options_parser->aliases_.begin(),
+ child_options_parser->aliases_.end());
+
+ for (const auto& pair : child_options_parser->options_)
+ options_.emplace(pair.first, Convert(pair.second, get_child));
+
+ for (const auto& pair : child_options_parser->implications_)
+ implications_.emplace(pair.first, Convert(pair.second, get_child));
+}
+
+inline std::string NotAllowedInEnvErr(const std::string& arg) {
+ return arg + " is not allowed in NODE_OPTIONS";
+}
+
+inline std::string RequiresArgumentErr(const std::string& arg) {
+ return arg + " requires an argument";
+}
+
+// We store some of the basic information around a single Parse call inside
+// this struct, to separate storage of command line arguments and their
+// handling. In particular, this makes it easier to introduce 'synthetic'
+// arguments that get inserted by expanding option aliases.
+struct ArgsInfo {
+ // Generally, the idea here is that the first entry in `*underlying` stores
+ // the "0th" argument (the program name), then `synthetic_args` are inserted,
+ // followed by the remainder of `*underlying`.
+ std::vector<std::string>* underlying;
+ std::vector<std::string> synthetic_args;
+
+ std::vector<std::string>* exec_args;
+
+ ArgsInfo(std::vector<std::string>* args,
+ std::vector<std::string>* exec_args)
+ : underlying(args), exec_args(exec_args) {}
+
+ size_t remaining() const {
+ // -1 to account for the program name.
+ return underlying->size() - 1 + synthetic_args.size();
+ }
+
+ bool empty() const { return remaining() == 0; }
+ const std::string& program_name() const { return underlying->at(0); }
+
+ std::string& first() {
+ return synthetic_args.empty() ? underlying->at(1) : synthetic_args.front();
+ }
+
+ std::string pop_first() {
+ std::string ret = std::move(first());
+ if (synthetic_args.empty()) {
+ // Only push arguments to `exec_args` that were also originally passed
+ // on the command line (i.e. not generated through alias expansion).
+ // '--' is a special case here since its purpose is to end `exec_argv`,
+ // which is why we do not include it.
+ if (exec_args != nullptr && first() != "--")
+ exec_args->push_back(ret);
+ underlying->erase(underlying->begin() + 1);
+ } else {
+ synthetic_args.erase(synthetic_args.begin());
+ }
+ return ret;
+ }
+};
+
+template <typename Options>
+void OptionsParser<Options>::Parse(
+ std::vector<std::string>* const orig_args,
+ std::vector<std::string>* const exec_args,
+ std::vector<std::string>* const v8_args,
+ Options* const options,
+ OptionEnvvarSettings required_env_settings,
+ std::string* const error) {
+ ArgsInfo args(orig_args, exec_args);
+
+ // The first entry is the process name. Make sure it ends up in the V8 argv,
+ // since V8::SetFlagsFromCommandLine() expects that to hold true for that
+ // array as well.
+ if (v8_args->empty())
+ v8_args->push_back(args.program_name());
+
+ while (!args.empty() && error->empty()) {
+ if (args.first().size() <= 1 || args.first()[0] != '-') break;
+
+ // We know that we're either going to consume this
+ // argument or fail completely.
+ const std::string arg = args.pop_first();
+
+ if (arg == "--") {
+ if (required_env_settings == kAllowedInEnvironment)
+ *error = NotAllowedInEnvErr("--");
+ break;
+ }
+
+ // Only allow --foo=bar notation for options starting with double dashes.
+ // (E.g. -e=a is not allowed as shorthand for --eval=a, which would
+ // otherwise be the result of alias expansion.)
+ const std::string::size_type equals_index =
+ arg[0] == '-' && arg[1] == '-' ? arg.find('=') : std::string::npos;
+ std::string name =
+ equals_index == std::string::npos ? arg : arg.substr(0, equals_index);
+
+ // Store the 'original name' of the argument. This name differs from
+ // 'name' in that it contains a possible '=' sign and is not affected
+ // by alias expansion.
+ std::string original_name = name;
+ if (equals_index != std::string::npos)
+ original_name += '=';
+
+ {
+ auto it = aliases_.end();
+ // Expand aliases:
+ // - If `name` can be found in `aliases_`.
+ // - If `name` + '=' can be found in `aliases_`.
+ // - If `name` + " <arg>" can be found in `aliases_`, and we have
+ // a subsequent argument that does not start with '-' itself.
+ while ((it = aliases_.find(name)) != aliases_.end() ||
+ (equals_index != std::string::npos &&
+ (it = aliases_.find(name + '=')) != aliases_.end()) ||
+ (!args.empty() &&
+ !args.first().empty() &&
+ args.first()[0] != '-' &&
+ (it = aliases_.find(name + " <arg>")) != aliases_.end())) {
+ const std::string prev_name = std::move(name);
+ const std::vector<std::string>& expansion = it->second;
+
+ // Use the first entry in the expansion as the new 'name'.
+ name = expansion.front();
+
+ if (expansion.size() > 1) {
+ // The other arguments, if any, are going to be handled later.
+ args.synthetic_args.insert(
+ args.synthetic_args.begin(),
+ expansion.begin() + 1,
+ expansion.end());
+ }
+
+ if (name == prev_name) break;
+ }
+ }
+
+ auto it = options_.find(name);
+
+ if (it == options_.end()) {
+ // We would assume that this is a V8 option if neither we nor any child
+ // parser knows about it, so we convert - to _ for
+ // canonicalization (since V8 accepts both) and look up again in order
+ // to find a match.
+ // TODO(addaleax): Make the canonicalization unconditional, i.e. allow
+ // both - and _ in Node's own options as well.
+ std::string::size_type index = 2; // Start after initial '--'.
+ while ((index = name.find('-', index + 1)) != std::string::npos)
+ name[index] = '_';
+ it = options_.find(name);
+ }
+
+ if ((it == options_.end() ||
+ it->second.env_setting == kDisallowedInEnvironment) &&
+ required_env_settings == kAllowedInEnvironment) {
+ *error = NotAllowedInEnvErr(original_name);
+ break;
+ }
+
+ if (it == options_.end()) {
+ v8_args->push_back(arg);
+ continue;
+ }
+
+ {
+ auto implications = implications_.equal_range(name);
+ for (auto it = implications.first; it != implications.second; ++it)
+ *it->second.target_field->Lookup(options) = it->second.target_value;
+ }
+
+ const OptionInfo& info = it->second;
+ std::string value;
+ if (info.type != kBoolean && info.type != kNoOp && info.type != kV8Option) {
+ if (equals_index != std::string::npos) {
+ value = arg.substr(equals_index + 1);
+ if (value.empty()) {
+ missing_argument:
+ *error = RequiresArgumentErr(original_name);
+ break;
+ }
+ } else {
+ if (args.empty())
+ goto missing_argument;
+
+ value = args.pop_first();
+
+ if (!value.empty() && value[0] == '-') {
+ goto missing_argument;
+ } else {
+ if (!value.empty() && value[0] == '\\' && value[1] == '-')
+ value = value.substr(1); // Treat \- as escaping an -.
+ }
+ }
+ }
+
+ switch (info.type) {
+ case kBoolean:
+ *std::static_pointer_cast<OptionField<bool>>(info.field)
+ ->Lookup(options) = true;
+ break;
+ case kInteger:
+ *std::static_pointer_cast<OptionField<int64_t>>(info.field)
+ ->Lookup(options) = std::atoll(value.c_str());
+ break;
+ case kString:
+ *std::static_pointer_cast<OptionField<std::string>>(info.field)
+ ->Lookup(options) = value;
+ break;
+ case kStringList:
+ std::static_pointer_cast<OptionField<std::vector<std::string>>>(
+ info.field)->Lookup(options)->emplace_back(std::move(value));
+ break;
+ case kHostPort:
+ std::static_pointer_cast<OptionField<HostPort>>(info.field)
+ ->Lookup(options)->Update(SplitHostPort(value, error));
+ break;
+ case kNoOp:
+ break;
+ case kV8Option:
+ v8_args->push_back(arg);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+}
+
+} // namespace node
+
+#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#endif // SRC_NODE_OPTIONS_INL_H_
diff --git a/src/node_options.cc b/src/node_options.cc
new file mode 100644
index 0000000000..b10de9ef28
--- /dev/null
+++ b/src/node_options.cc
@@ -0,0 +1,219 @@
+#include "node_options-inl.h"
+#include <errno.h>
+
+namespace node {
+
+DebugOptionsParser::DebugOptionsParser() {
+ AddOption("--inspect-port", &DebugOptions::host_port,
+ kAllowedInEnvironment);
+ AddAlias("--debug-port", "--inspect-port");
+
+ AddOption("--inspect", &DebugOptions::inspector_enabled,
+ kAllowedInEnvironment);
+ AddAlias("--inspect=", { "--inspect-port", "--inspect" });
+
+ AddOption("--debug", &DebugOptions::deprecated_debug);
+ AddAlias("--debug=", { "--inspect-port", "--debug" });
+
+ AddOption("--inspect-brk", &DebugOptions::break_first_line,
+ kAllowedInEnvironment);
+ Implies("--inspect-brk", "--inspect");
+ AddAlias("--inspect-brk=", { "--inspect-port", "--inspect-brk" });
+
+ AddOption("--inspect-brk-node", &DebugOptions::break_node_first_line);
+ Implies("--inspect-brk-node", "--inspect");
+ AddAlias("--inspect-brk-node=", { "--inspect-port", "--inspect-brk-node" });
+
+ AddOption("--debug-brk", &DebugOptions::break_first_line);
+ Implies("--debug-brk", "--debug");
+ AddAlias("--debug-brk=", { "--inspect-port", "--debug-brk" });
+}
+
+DebugOptionsParser DebugOptionsParser::instance;
+
+EnvironmentOptionsParser::EnvironmentOptionsParser() {
+ AddOption("--experimental-modules", &EnvironmentOptions::experimental_modules,
+ kAllowedInEnvironment);
+ AddOption("--experimental-repl-await",
+ &EnvironmentOptions::experimental_repl_await,
+ kAllowedInEnvironment);
+ AddOption("--experimental-vm-modules",
+ &EnvironmentOptions::experimental_vm_modules,
+ kAllowedInEnvironment);
+ AddOption("--experimental-worker", &EnvironmentOptions::experimental_worker,
+ kAllowedInEnvironment);
+ AddOption("--expose-internals", &EnvironmentOptions::expose_internals);
+ // TODO(addaleax): Remove this when adding -/_ canonicalization to the parser.
+ AddAlias("--expose_internals", "--expose-internals");
+ AddOption("--loader", &EnvironmentOptions::userland_loader,
+ kAllowedInEnvironment);
+ AddOption("--no-deprecation", &EnvironmentOptions::no_deprecation,
+ kAllowedInEnvironment);
+ AddOption("--no-force-async-hooks-checks",
+ &EnvironmentOptions::no_force_async_hooks_checks,
+ kAllowedInEnvironment);
+ AddOption("--no-warnings", &EnvironmentOptions::no_warnings,
+ kAllowedInEnvironment);
+ AddOption("--pending-deprecation", &EnvironmentOptions::pending_deprecation,
+ kAllowedInEnvironment);
+ AddOption("--preserve-symlinks", &EnvironmentOptions::preserve_symlinks);
+ AddOption("--preserve-symlinks-main",
+ &EnvironmentOptions::preserve_symlinks_main);
+ AddOption("--prof-process", &EnvironmentOptions::prof_process);
+ AddOption("--redirect-warnings", &EnvironmentOptions::redirect_warnings,
+ kAllowedInEnvironment);
+ AddOption("--throw-deprecation", &EnvironmentOptions::throw_deprecation,
+ kAllowedInEnvironment);
+ AddOption("--trace-deprecation", &EnvironmentOptions::trace_deprecation,
+ kAllowedInEnvironment);
+ AddOption("--trace-sync-io", &EnvironmentOptions::trace_sync_io,
+ kAllowedInEnvironment);
+ AddOption("--trace-warnings", &EnvironmentOptions::trace_warnings,
+ kAllowedInEnvironment);
+
+ AddOption("--check", &EnvironmentOptions::syntax_check_only);
+ AddAlias("-c", "--check");
+ // This option is only so that we can tell --eval with an empty string from
+ // no eval at all. Having it not start with a dash makes it inaccessible
+ // from the parser itself, but available for using Implies().
+ // TODO(addaleax): When moving --help over to something generated from the
+ // programmatic descriptions, this will need some special care.
+ // (See also [ssl_openssl_cert_store] below.)
+ AddOption("[has_eval_string]", &EnvironmentOptions::has_eval_string);
+ AddOption("--eval", &EnvironmentOptions::eval_string);
+ Implies("--eval", "[has_eval_string]");
+ AddOption("--print", &EnvironmentOptions::print_eval);
+ AddAlias("-e", "--eval");
+ AddAlias("--print <arg>", "-pe");
+ AddAlias("-pe", { "--print", "--eval" });
+ AddAlias("-p", "--print");
+ AddOption("--require", &EnvironmentOptions::preload_modules,
+ kAllowedInEnvironment);
+ AddAlias("-r", "--require");
+ AddOption("--interactive", &EnvironmentOptions::force_repl);
+ AddAlias("-i", "--interactive");
+
+ AddOption("--napi-modules", NoOp {}, kAllowedInEnvironment);
+
+ Insert(&DebugOptionsParser::instance,
+ &EnvironmentOptions::get_debug_options);
+}
+
+EnvironmentOptionsParser EnvironmentOptionsParser::instance;
+
+PerIsolateOptionsParser::PerIsolateOptionsParser() {
+ AddOption("--track-heap-objects", &PerIsolateOptions::track_heap_objects,
+ kAllowedInEnvironment);
+
+ // Explicitly add some V8 flags to mark them as allowed in NODE_OPTIONS.
+ AddOption("--abort_on_uncaught_exception", V8Option {},
+ kAllowedInEnvironment);
+ AddOption("--max_old_space_size", V8Option {}, kAllowedInEnvironment);
+ AddOption("--perf_basic_prof", V8Option {}, kAllowedInEnvironment);
+ AddOption("--perf_prof", V8Option {}, kAllowedInEnvironment);
+ AddOption("--stack_trace_limit", V8Option {}, kAllowedInEnvironment);
+
+ Insert(&EnvironmentOptionsParser::instance,
+ &PerIsolateOptions::get_per_env_options);
+}
+
+PerIsolateOptionsParser PerIsolateOptionsParser::instance;
+
+PerProcessOptionsParser::PerProcessOptionsParser() {
+ AddOption("--title", &PerProcessOptions::title, kAllowedInEnvironment);
+ AddOption("--trace-event-categories",
+ &PerProcessOptions::trace_event_categories,
+ kAllowedInEnvironment);
+ AddOption("--trace-event-file-pattern",
+ &PerProcessOptions::trace_event_file_pattern,
+ kAllowedInEnvironment);
+ AddAlias("--trace-events-enabled", {
+ "--trace-event-categories", "v8,node,node.async_hooks" });
+ AddOption("--v8-pool-size", &PerProcessOptions::v8_thread_pool_size,
+ kAllowedInEnvironment);
+ AddOption("--zero-fill-buffers", &PerProcessOptions::zero_fill_all_buffers,
+ kAllowedInEnvironment);
+
+ AddOption("--security-reverts", &PerProcessOptions::security_reverts);
+ AddOption("--help", &PerProcessOptions::print_help);
+ AddAlias("-h", "--help");
+ AddOption("--version", &PerProcessOptions::print_version);
+ AddAlias("-v", "--version");
+ AddOption("--v8-options", &PerProcessOptions::print_v8_help);
+
+#ifdef NODE_HAVE_I18N_SUPPORT
+ AddOption("--icu-data-dir", &PerProcessOptions::icu_data_dir,
+ kAllowedInEnvironment);
+#endif
+
+#if HAVE_OPENSSL
+ AddOption("--openssl-config", &PerProcessOptions::openssl_config,
+ kAllowedInEnvironment);
+ AddOption("--tls-cipher-list", &PerProcessOptions::tls_cipher_list,
+ kAllowedInEnvironment);
+ AddOption("--use-openssl-ca", &PerProcessOptions::use_openssl_ca,
+ kAllowedInEnvironment);
+ AddOption("--use-bundled-ca", &PerProcessOptions::use_bundled_ca,
+ kAllowedInEnvironment);
+ // Similar to [has_eval_string] above, except that the separation between
+ // this and use_openssl_ca only exists for option validation after parsing.
+ // This is not ideal.
+ AddOption("[ssl_openssl_cert_store]",
+ &PerProcessOptions::ssl_openssl_cert_store);
+ Implies("--use-openssl-ca", "[ssl_openssl_cert_store]");
+ ImpliesNot("--use-bundled-ca", "[ssl_openssl_cert_store]");
+#if NODE_FIPS_MODE
+ AddOption("--enable-fips", &PerProcessOptions::enable_fips_crypto,
+ kAllowedInEnvironment);
+ AddOption("--force-fips", &PerProcessOptions::force_fips_crypto,
+ kAllowedInEnvironment);
+#endif
+#endif
+
+ Insert(&PerIsolateOptionsParser::instance,
+ &PerProcessOptions::get_per_isolate_options);
+}
+
+PerProcessOptionsParser PerProcessOptionsParser::instance;
+
+inline std::string RemoveBrackets(const std::string& host) {
+ if (!host.empty() && host.front() == '[' && host.back() == ']')
+ return host.substr(1, host.size() - 2);
+ else
+ return host;
+}
+
+inline int ParseAndValidatePort(const std::string& port, std::string* error) {
+ char* endptr;
+ errno = 0;
+ const long result = strtol(port.c_str(), &endptr, 10); // NOLINT(runtime/int)
+ if (errno != 0 || *endptr != '\0'||
+ (result != 0 && result < 1024) || result > 65535) {
+ *error = "Port must be 0 or in range 1024 to 65535.";
+ }
+ return static_cast<int>(result);
+}
+
+HostPort SplitHostPort(const std::string& arg, std::string* error) {
+ // remove_brackets only works if no port is specified
+ // so if it has an effect only an IPv6 address was specified.
+ std::string host = RemoveBrackets(arg);
+ if (host.length() < arg.length())
+ return HostPort { host, -1 };
+
+ size_t colon = arg.rfind(':');
+ if (colon == std::string::npos) {
+ // Either a port number or a host name. Assume that
+ // if it's not all decimal digits, it's a host name.
+ for (char c : arg) {
+ if (c < '0' || c > '9') {
+ return HostPort { arg, -1 };
+ }
+ }
+ return HostPort { "", ParseAndValidatePort(arg, error) };
+ }
+ // Host and port found:
+ return HostPort { RemoveBrackets(arg.substr(0, colon)),
+ ParseAndValidatePort(arg.substr(colon + 1), error) };
+}
+} // namespace node
diff --git a/src/node_options.h b/src/node_options.h
new file mode 100644
index 0000000000..957e2b729d
--- /dev/null
+++ b/src/node_options.h
@@ -0,0 +1,356 @@
+#ifndef SRC_NODE_OPTIONS_H_
+#define SRC_NODE_OPTIONS_H_
+
+#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <memory>
+#include "node_constants.h"
+
+namespace node {
+
+struct HostPort {
+ std::string host_name;
+ int port;
+
+ void Update(const HostPort& other) {
+ if (!other.host_name.empty()) host_name = other.host_name;
+ if (other.port >= 0) port = other.port;
+ }
+};
+
+// These options are currently essentially per-Environment, but it can be nice
+// to keep them separate since they are a group of options applying to a very
+// specific part of Node. It might also make more sense for them to be
+// per-Isolate, rather than per-Environment.
+class DebugOptions {
+ public:
+ bool inspector_enabled = false;
+ bool deprecated_debug = false;
+ bool break_first_line = false;
+ bool break_node_first_line = false;
+ HostPort host_port = {"127.0.0.1", -1};
+ enum { kDefaultInspectorPort = 9229 };
+
+ bool deprecated_invocation() const {
+ return deprecated_debug &&
+ inspector_enabled &&
+ break_first_line;
+ }
+
+ bool invalid_invocation() const {
+ return deprecated_debug && !inspector_enabled;
+ }
+
+ bool wait_for_connect() const {
+ return break_first_line || break_node_first_line;
+ }
+
+ const std::string& host() {
+ return host_port.host_name;
+ }
+
+ int port() {
+ return host_port.port < 0 ? kDefaultInspectorPort : host_port.port;
+ }
+};
+
+class EnvironmentOptions {
+ public:
+ std::shared_ptr<DebugOptions> debug_options { new DebugOptions() };
+ bool experimental_modules = false;
+ bool experimental_repl_await = false;
+ bool experimental_vm_modules = false;
+ bool experimental_worker = false;
+ bool expose_internals = false;
+ bool no_deprecation = false;
+ bool no_force_async_hooks_checks = false;
+ bool no_warnings = false;
+ bool pending_deprecation = false;
+ bool preserve_symlinks = false;
+ bool preserve_symlinks_main = false;
+ bool prof_process = false;
+ std::string redirect_warnings;
+ bool throw_deprecation = false;
+ bool trace_deprecation = false;
+ bool trace_sync_io = false;
+ bool trace_warnings = false;
+ std::string userland_loader;
+
+ bool syntax_check_only = false;
+ bool has_eval_string = false;
+ std::string eval_string;
+ bool print_eval = false;
+ bool force_repl = false;
+
+ std::vector<std::string> preload_modules;
+
+ std::vector<std::string> user_argv;
+
+ inline DebugOptions* get_debug_options();
+};
+
+class PerIsolateOptions {
+ public:
+ std::shared_ptr<EnvironmentOptions> per_env { new EnvironmentOptions() };
+ bool track_heap_objects = false;
+
+ inline EnvironmentOptions* get_per_env_options();
+};
+
+class PerProcessOptions {
+ public:
+ std::shared_ptr<PerIsolateOptions> per_isolate { new PerIsolateOptions() };
+
+ std::string title;
+ std::string trace_event_categories;
+ std::string trace_event_file_pattern = "node_trace.${rotation}.log";
+ int64_t v8_thread_pool_size = 4;
+ bool zero_fill_all_buffers = false;
+
+ std::vector<std::string> security_reverts;
+ bool print_help = false;
+ bool print_v8_help = false;
+ bool print_version = false;
+
+#ifdef NODE_HAVE_I18N_SUPPORT
+ std::string icu_data_dir;
+#endif
+
+ // TODO(addaleax): Some of these could probably be per-Environment.
+#if HAVE_OPENSSL
+ std::string openssl_config;
+ std::string tls_cipher_list = DEFAULT_CIPHER_LIST_CORE;
+#ifdef NODE_OPENSSL_CERT_STORE
+ bool ssl_openssl_cert_store = true;
+#else
+ bool ssl_openssl_cert_store = false;
+#endif
+ bool use_openssl_ca = false;
+ bool use_bundled_ca = false;
+#if NODE_FIPS_MODE
+ bool enable_fips_crypto = false;
+ bool force_fips_crypto = false;
+#endif
+#endif
+
+ inline PerIsolateOptions* get_per_isolate_options();
+};
+
+// The actual options parser, as opposed to the structs containing them:
+
+HostPort SplitHostPort(const std::string& arg, std::string* error);
+
+enum OptionEnvvarSettings {
+ kAllowedInEnvironment,
+ kDisallowedInEnvironment
+};
+
+enum OptionType {
+ kNoOp,
+ kV8Option,
+ kBoolean,
+ kInteger,
+ kString,
+ kHostPort,
+ kStringList,
+};
+
+template <typename Options>
+class OptionsParser {
+ public:
+ virtual ~OptionsParser() {}
+
+ typedef Options TargetType;
+
+ struct NoOp {};
+ struct V8Option {};
+
+ // TODO(addaleax): A lot of the `std::string` usage here could be reduced
+ // to simple `const char*`s if it's reasonable to expect the values to be
+ // known at compile-time.
+
+ // These methods add a single option to the parser. Optionally, it can be
+ // specified whether the option should be allowed from environment variable
+ // sources (i.e. NODE_OPTIONS).
+ void AddOption(const std::string& name,
+ bool Options::* field,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ int64_t Options::* field,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ std::string Options::* field,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ std::vector<std::string> Options::* field,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ HostPort Options::* field,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ NoOp no_op_tag,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+ void AddOption(const std::string& name,
+ V8Option v8_option_tag,
+ OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
+
+ // Adds aliases. An alias can be of the form "--option-a" -> "--option-b",
+ // or have a more complex group expansion, like
+ // "--option-a" -> { "--option-b", "--harmony-foobar", "--eval", "42" }
+ // If `from` has the form "--option-a=", the alias will only be expanded if
+ // the option is presented in that form (i.e. with a '=').
+ // If `from` has the form "--option-a <arg>", the alias will only be expanded
+ // if the option has a non-option argument (not starting with -) following it.
+ void AddAlias(const std::string& from, const std::string& to);
+ void AddAlias(const std::string& from, const std::vector<std::string>& to);
+ void AddAlias(const std::string& from,
+ const std::initializer_list<std::string>& to);
+
+ // Add implications from some arbitary option to a boolean one, either
+ // in a way that makes `from` set `to` to true or to false.
+ void Implies(const std::string& from, const std::string& to);
+ void ImpliesNot(const std::string& from, const std::string& to);
+
+ // Insert options from another options parser into this one, along with
+ // a method that yields the target options type from this parser's options
+ // type.
+ template <typename ChildOptions>
+ void Insert(OptionsParser<ChildOptions>* child_options_parser,
+ ChildOptions* (Options::* get_child)());
+
+ // Parse a sequence of options into an options struct, a list of
+ // arguments that were parsed as options, a list of unknown/JS engine options,
+ // and leave the remainder in the input `args` vector.
+ //
+ // For example, an `args` input of
+ //
+ // node --foo --harmony-bar --fizzle=42 -- /path/to/cow moo
+ //
+ // expands as
+ //
+ // - `args` -> { "node", "/path/to/cow", "moo" }
+ // - `exec_args` -> { "--foo", "--harmony-bar", "--fizzle=42" }
+ // - `v8_args` -> `{ "node", "--harmony-bar" }
+ // - `options->foo == true`, `options->fizzle == 42`.
+ //
+ // If `*error` is set, the result of the parsing should be discarded and the
+ // contents of any of the argument vectors should be considered undefined.
+ virtual void Parse(std::vector<std::string>* const args,
+ std::vector<std::string>* const exec_args,
+ std::vector<std::string>* const v8_args,
+ Options* const options,
+ OptionEnvvarSettings required_env_settings,
+ std::string* const error);
+
+ private:
+ // We support the wide variety of different option types by remembering
+ // how to access them, given a certain `Options` struct.
+
+ // Represents a field within `Options`.
+ class BaseOptionField {
+ public:
+ virtual ~BaseOptionField() {}
+ virtual void* LookupImpl(Options* options) const = 0;
+ };
+
+ // Represents a field of type T within `Options`.
+ template <typename T>
+ class OptionField : public BaseOptionField {
+ public:
+ typedef T Type;
+
+ T* Lookup(Options* options) const {
+ return static_cast<T*>(this->LookupImpl(options));
+ }
+ };
+
+ // Represents a field of type T withing `Options` that can be looked up
+ // as a C++ member field.
+ template <typename T>
+ class SimpleOptionField : public OptionField<T> {
+ public:
+ explicit SimpleOptionField(T Options::* field) : field_(field) {}
+ void* LookupImpl(Options* options) const override {
+ return static_cast<void*>(&(options->*field_));
+ }
+
+ private:
+ T Options::* field_;
+ };
+
+ // An option consists of:
+ // - A type.
+ // - A way to store/access the property value.
+ // - The information of whether it may occur in an env var or not.
+ struct OptionInfo {
+ OptionType type;
+ std::shared_ptr<BaseOptionField> field;
+ OptionEnvvarSettings env_setting;
+ };
+
+ // An implied option is composed of the information on where to store a
+ // specific boolean value (if another specific option is encountered).
+ struct Implication {
+ std::shared_ptr<OptionField<bool>> target_field;
+ bool target_value;
+ };
+
+ // These are helpers that make `Insert()` support properties of other
+ // options structs, if we know how to access them.
+ template <typename OriginalField, typename ChildOptions>
+ static auto Convert(
+ std::shared_ptr<OriginalField> original,
+ ChildOptions* (Options::* get_child)());
+ template <typename ChildOptions>
+ static auto Convert(
+ typename OptionsParser<ChildOptions>::OptionInfo original,
+ ChildOptions* (Options::* get_child)());
+ template <typename ChildOptions>
+ static auto Convert(
+ typename OptionsParser<ChildOptions>::Implication original,
+ ChildOptions* (Options::* get_child)());
+
+ std::unordered_map<std::string, OptionInfo> options_;
+ std::unordered_map<std::string, std::vector<std::string>> aliases_;
+ std::unordered_multimap<std::string, Implication> implications_;
+
+ template <typename OtherOptions>
+ friend class OptionsParser;
+};
+
+class DebugOptionsParser : public OptionsParser<DebugOptions> {
+ public:
+ DebugOptionsParser();
+
+ static DebugOptionsParser instance;
+};
+
+class EnvironmentOptionsParser : public OptionsParser<EnvironmentOptions> {
+ public:
+ EnvironmentOptionsParser();
+
+ static EnvironmentOptionsParser instance;
+};
+
+class PerIsolateOptionsParser : public OptionsParser<PerIsolateOptions> {
+ public:
+ PerIsolateOptionsParser();
+
+ static PerIsolateOptionsParser instance;
+};
+
+class PerProcessOptionsParser : public OptionsParser<PerProcessOptions> {
+ public:
+ PerProcessOptionsParser();
+
+ static PerProcessOptionsParser instance;
+};
+
+} // namespace node
+
+#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#endif // SRC_NODE_OPTIONS_H_
diff --git a/src/node_worker.cc b/src/node_worker.cc
index 06edb96804..80deddedde 100644
--- a/src/node_worker.cc
+++ b/src/node_worker.cc
@@ -104,7 +104,9 @@ Worker::Worker(Environment* env, Local<Object> wrap)
env_->set_worker_context(this);
env_->set_thread_id(thread_id_);
- env_->Start(0, nullptr, 0, nullptr, env->profiler_idle_notifier_started());
+ env_->Start(std::vector<std::string>{},
+ std::vector<std::string>{},
+ env->profiler_idle_notifier_started());
}
// The new isolate won't be bothered on this thread again.