src: refactor options parsing

This is a major refactor of our Node’s parser. See `node_options.cc`
for how it is used, and  `node_options-inl.h` for the bulk
of its implementation.

Unfortunately, the implementation has come to have some
complexity, in order to meet the following goals:

- Make it easy to *use* for defining or changing options.
- Keep it (mostly) backwards-compatible.
  - No tests were harmed as part of this commit.
- Be as consistent as possible.
  - In particular, options can now generally accept arguments
    through both `--foo=bar` notation and `--foo bar` notation.
    We were previously very inconsistent on this point.
- Separate into different levels of scope, namely
  per-process (global), per-Isolate and per-Environment
  (+ debug options).
- Allow programmatic accessibility in the future.
  - This includes a possible expansion for `--help` output.

This commit also leaves a number of `TODO` comments, mostly for
improving consistency even more (possibly with having to modify
tests), improving embedder support, as well as removing pieces of
exposed configuration variables that should never have become
part of the public API but unfortunately are at this point.

PR-URL: https://github.com/nodejs/node/pull/22392
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
This commit is contained in:
Anna Henningsen
2018-08-10 02:45:28 +02:00
parent 92880f31da
commit 29a71bae40
24 changed files with 1353 additions and 888 deletions

View File

@@ -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',

View File

@@ -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,

View File

@@ -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);

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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();

View File

@@ -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.

View File

@@ -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()) {

File diff suppressed because it is too large Load Diff

View File

@@ -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,

View File

@@ -27,6 +27,7 @@
namespace node {
// TODO(addaleax): Deprecate and remove this ASAP.
extern bool zero_fill_all_buffers;
namespace Buffer {

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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_

View File

@@ -31,8 +31,6 @@
namespace node {
extern std::string icu_data_dir; // NOLINT(runtime/string)
namespace i18n {
bool InitializeICUDirectory(const std::string& path);

View File

@@ -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

422
src/node_options-inl.h Normal file
View File

@@ -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_

219
src/node_options.cc Normal file
View File

@@ -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

356
src/node_options.h Normal file
View File

@@ -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_

View File

@@ -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.