summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/env.h4
-rw-r--r--src/node.cc26
-rw-r--r--src/node_internals.h2
-rw-r--r--src/node_options-inl.h90
-rw-r--r--src/node_options.cc353
-rw-r--r--src/node_options.h20
-rw-r--r--src/util-inl.h59
-rw-r--r--src/util.cc11
-rw-r--r--src/util.h12
9 files changed, 476 insertions, 101 deletions
diff --git a/src/env.h b/src/env.h
index 556a09754f..fa802e4016 100644
--- a/src/env.h
+++ b/src/env.h
@@ -118,6 +118,7 @@ struct PackageConfig {
// for the sake of convenience. Strings should be ASCII-only.
#define PER_ISOLATE_STRING_PROPERTIES(V) \
V(address_string, "address") \
+ V(aliases_string, "aliases") \
V(args_string, "args") \
V(async, "async") \
V(async_ids_stack_string, "async_ids_stack") \
@@ -156,6 +157,7 @@ struct PackageConfig {
V(entries_string, "entries") \
V(entry_type_string, "entryType") \
V(env_pairs_string, "envPairs") \
+ V(env_var_settings_string, "envVarSettings") \
V(errno_string, "errno") \
V(error_string, "error") \
V(exit_code_string, "exitCode") \
@@ -176,6 +178,7 @@ struct PackageConfig {
V(get_shared_array_buffer_id_string, "_getSharedArrayBufferId") \
V(gid_string, "gid") \
V(handle_string, "handle") \
+ V(help_text_string, "helpText") \
V(homedir_string, "homedir") \
V(host_string, "host") \
V(hostmaster_string, "hostmaster") \
@@ -233,6 +236,7 @@ struct PackageConfig {
V(onunpipe_string, "onunpipe") \
V(onwrite_string, "onwrite") \
V(openssl_error_stack, "opensslErrorStack") \
+ V(options_string, "options") \
V(output_string, "output") \
V(order_string, "order") \
V(parse_error_string, "Parse Error") \
diff --git a/src/node.cc b/src/node.cc
index 25dc8d2bbe..1336cdecb6 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -126,6 +126,8 @@ typedef int mode_t;
namespace node {
+using options_parser::kAllowedInEnvironment;
+using options_parser::kDisallowedInEnvironment;
using v8::Array;
using v8::ArrayBuffer;
using v8::Boolean;
@@ -183,6 +185,7 @@ bool linux_at_secure = false;
// process-relative uptime base, initialized at start-up
double prog_start_time;
+Mutex per_process_opts_mutex;
std::shared_ptr<PerProcessOptions> per_process_opts {
new PerProcessOptions() };
@@ -2346,8 +2349,6 @@ void LoadEnvironment(Environment* env) {
}
static void PrintHelp() {
- // XXX: If you add an option here, please also add it to doc/node.1 and
- // doc/api/cli.md
printf("Usage: node [options] [ -e script | script.js | - ] [arguments]\n"
" node inspect script.js [arguments]\n"
"\n"
@@ -2747,13 +2748,20 @@ void ProcessArgv(std::vector<std::string>* args,
// Parse a few arguments which are specific to Node.
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);
+
+ {
+ // TODO(addaleax): The mutex here should ideally be held during the
+ // entire function, but that doesn't play well with the exit() calls below.
+ Mutex::ScopedLock lock(per_process_opts_mutex);
+ options_parser::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);
diff --git a/src/node_internals.h b/src/node_internals.h
index eb9e79d9e8..2fa9502466 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -119,6 +119,7 @@ struct sockaddr;
V(js_stream) \
V(messaging) \
V(module_wrap) \
+ V(options) \
V(os) \
V(performance) \
V(pipe_wrap) \
@@ -176,6 +177,7 @@ extern Mutex environ_mutex;
// Tells whether it is safe to call v8::Isolate::GetCurrent().
extern bool v8_initialized;
+extern Mutex per_process_opts_mutex;
extern std::shared_ptr<PerProcessOptions> per_process_opts;
extern const char* const environment_flags[];
diff --git a/src/node_options-inl.h b/src/node_options-inl.h
index e610cd50d1..ba18e7c19b 100644
--- a/src/node_options-inl.h
+++ b/src/node_options-inl.h
@@ -21,73 +21,88 @@ EnvironmentOptions* PerIsolateOptions::get_per_env_options() {
return per_env.get();
}
+namespace options_parser {
+
template <typename Options>
void OptionsParser<Options>::AddOption(const std::string& name,
+ const std::string& help_text,
bool Options::* field,
OptionEnvvarSettings env_setting) {
- options_.emplace(name, OptionInfo {
- kBoolean,
- std::make_shared<SimpleOptionField<bool>>(field),
- env_setting
- });
+ options_.emplace(name,
+ OptionInfo{kBoolean,
+ std::make_shared<SimpleOptionField<bool>>(field),
+ env_setting,
+ help_text});
}
template <typename Options>
void OptionsParser<Options>::AddOption(const std::string& name,
+ const std::string& help_text,
int64_t Options::* field,
OptionEnvvarSettings env_setting) {
- options_.emplace(name, OptionInfo {
- kInteger,
- std::make_shared<SimpleOptionField<int64_t>>(field),
- env_setting
- });
+ options_.emplace(
+ name,
+ OptionInfo{kInteger,
+ std::make_shared<SimpleOptionField<int64_t>>(field),
+ env_setting,
+ help_text});
}
template <typename Options>
void OptionsParser<Options>::AddOption(const std::string& name,
+ const std::string& help_text,
std::string Options::* field,
OptionEnvvarSettings env_setting) {
- options_.emplace(name, OptionInfo {
- kString,
- std::make_shared<SimpleOptionField<std::string>>(field),
- env_setting
- });
+ options_.emplace(
+ name,
+ OptionInfo{kString,
+ std::make_shared<SimpleOptionField<std::string>>(field),
+ env_setting,
+ help_text});
}
template <typename Options>
void OptionsParser<Options>::AddOption(
const std::string& name,
+ const std::string& help_text,
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
+ env_setting,
+ help_text
});
}
template <typename Options>
void OptionsParser<Options>::AddOption(const std::string& name,
+ const std::string& help_text,
HostPort Options::* field,
OptionEnvvarSettings env_setting) {
- options_.emplace(name, OptionInfo {
- kHostPort,
- std::make_shared<SimpleOptionField<HostPort>>(field),
- env_setting
- });
+ options_.emplace(
+ name,
+ OptionInfo{kHostPort,
+ std::make_shared<SimpleOptionField<HostPort>>(field),
+ env_setting,
+ help_text});
}
template <typename Options>
-void OptionsParser<Options>::AddOption(const std::string& name, NoOp no_op_tag,
+void OptionsParser<Options>::AddOption(const std::string& name,
+ const std::string& help_text,
+ NoOp no_op_tag,
OptionEnvvarSettings env_setting) {
- options_.emplace(name, OptionInfo { kNoOp, nullptr, env_setting });
+ options_.emplace(name, OptionInfo{kNoOp, nullptr, env_setting, help_text});
}
template <typename Options>
void OptionsParser<Options>::AddOption(const std::string& name,
+ const std::string& help_text,
V8Option v8_option_tag,
OptionEnvvarSettings env_setting) {
- options_.emplace(name, OptionInfo { kV8Option, nullptr, env_setting });
+ options_.emplace(name,
+ OptionInfo{kV8Option, nullptr, env_setting, help_text});
}
template <typename Options>
@@ -161,11 +176,10 @@ 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
- };
+ return OptionInfo{original.type,
+ Convert(original.field, get_child),
+ original.env_setting,
+ original.help_text};
}
template <typename Options>
@@ -385,24 +399,21 @@ void OptionsParser<Options>::Parse(
switch (info.type) {
case kBoolean:
- *std::static_pointer_cast<OptionField<bool>>(info.field)
- ->Lookup(options) = true;
+ *Lookup<bool>(info.field, options) = true;
break;
case kInteger:
- *std::static_pointer_cast<OptionField<int64_t>>(info.field)
- ->Lookup(options) = std::atoll(value.c_str());
+ *Lookup<int64_t>(info.field, options) = std::atoll(value.c_str());
break;
case kString:
- *std::static_pointer_cast<OptionField<std::string>>(info.field)
- ->Lookup(options) = value;
+ *Lookup<std::string>(info.field, options) = value;
break;
case kStringList:
- std::static_pointer_cast<OptionField<std::vector<std::string>>>(
- info.field)->Lookup(options)->emplace_back(std::move(value));
+ Lookup<std::vector<std::string>>(info.field, options)
+ ->emplace_back(std::move(value));
break;
case kHostPort:
- std::static_pointer_cast<OptionField<HostPort>>(info.field)
- ->Lookup(options)->Update(SplitHostPort(value, error));
+ Lookup<HostPort>(info.field, options)
+ ->Update(SplitHostPort(value, error));
break;
case kNoOp:
break;
@@ -415,6 +426,7 @@ void OptionsParser<Options>::Parse(
}
}
+} // namespace options_parser
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
diff --git a/src/node_options.cc b/src/node_options.cc
index b10de9ef28..88fac6a01f 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -1,30 +1,55 @@
-#include "node_options-inl.h"
#include <errno.h>
+#include "node_internals.h"
+#include "node_options-inl.h"
+
+using v8::Boolean;
+using v8::Context;
+using v8::FunctionCallbackInfo;
+using v8::Integer;
+using v8::Isolate;
+using v8::Local;
+using v8::Map;
+using v8::Number;
+using v8::Object;
+using v8::String;
+using v8::Undefined;
+using v8::Value;
namespace node {
+namespace options_parser {
+
+// XXX: If you add an option here, please also add it to doc/node.1 and
+// doc/api/cli.md
+// TODO(addaleax): Make that unnecessary.
DebugOptionsParser::DebugOptionsParser() {
- AddOption("--inspect-port", &DebugOptions::host_port,
+ AddOption("--inspect-port",
+ "set host:port for inspector",
+ &DebugOptions::host_port,
kAllowedInEnvironment);
AddAlias("--debug-port", "--inspect-port");
- AddOption("--inspect", &DebugOptions::inspector_enabled,
+ AddOption("--inspect",
+ "activate inspector on host:port (default: 127.0.0.1:9229)",
+ &DebugOptions::inspector_enabled,
kAllowedInEnvironment);
AddAlias("--inspect=", { "--inspect-port", "--inspect" });
- AddOption("--debug", &DebugOptions::deprecated_debug);
+ AddOption("--debug", "", &DebugOptions::deprecated_debug);
AddAlias("--debug=", { "--inspect-port", "--debug" });
- AddOption("--inspect-brk", &DebugOptions::break_first_line,
+ AddOption("--inspect-brk",
+ "activate inspector on host:port and break at start of user script",
+ &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);
+ 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);
+ AddOption("--debug-brk", "", &DebugOptions::break_first_line);
Implies("--debug-brk", "--debug");
AddAlias("--debug-brk=", { "--inspect-port", "--debug-brk" });
}
@@ -32,46 +57,80 @@ DebugOptionsParser::DebugOptionsParser() {
DebugOptionsParser DebugOptionsParser::instance;
EnvironmentOptionsParser::EnvironmentOptionsParser() {
- AddOption("--experimental-modules", &EnvironmentOptions::experimental_modules,
+ AddOption("--experimental-modules",
+ "experimental ES Module support and caching modules",
+ &EnvironmentOptions::experimental_modules,
kAllowedInEnvironment);
AddOption("--experimental-repl-await",
+ "experimental await keyword support in REPL",
&EnvironmentOptions::experimental_repl_await,
kAllowedInEnvironment);
AddOption("--experimental-vm-modules",
+ "experimental ES Module support in vm module",
&EnvironmentOptions::experimental_vm_modules,
kAllowedInEnvironment);
- AddOption("--experimental-worker", &EnvironmentOptions::experimental_worker,
+ AddOption("--experimental-worker",
+ "experimental threaded Worker support",
+ &EnvironmentOptions::experimental_worker,
kAllowedInEnvironment);
- AddOption("--expose-internals", &EnvironmentOptions::expose_internals);
+ 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,
+ AddOption("--loader",
+ "(with --experimental-modules) use the specified file as a "
+ "custom loader",
+ &EnvironmentOptions::userland_loader,
kAllowedInEnvironment);
- AddOption("--no-deprecation", &EnvironmentOptions::no_deprecation,
+ AddOption("--no-deprecation",
+ "silence deprecation warnings",
+ &EnvironmentOptions::no_deprecation,
kAllowedInEnvironment);
AddOption("--no-force-async-hooks-checks",
+ "disable checks for async_hooks",
&EnvironmentOptions::no_force_async_hooks_checks,
kAllowedInEnvironment);
- AddOption("--no-warnings", &EnvironmentOptions::no_warnings,
+ AddOption("--no-warnings",
+ "silence all process warnings",
+ &EnvironmentOptions::no_warnings,
kAllowedInEnvironment);
- AddOption("--pending-deprecation", &EnvironmentOptions::pending_deprecation,
+ AddOption("--pending-deprecation",
+ "emit pending deprecation warnings",
+ &EnvironmentOptions::pending_deprecation,
kAllowedInEnvironment);
- AddOption("--preserve-symlinks", &EnvironmentOptions::preserve_symlinks);
+ AddOption("--preserve-symlinks",
+ "preserve symbolic links when resolving",
+ &EnvironmentOptions::preserve_symlinks);
AddOption("--preserve-symlinks-main",
+ "preserve symbolic links when resolving the main module",
&EnvironmentOptions::preserve_symlinks_main);
- AddOption("--prof-process", &EnvironmentOptions::prof_process);
- AddOption("--redirect-warnings", &EnvironmentOptions::redirect_warnings,
+ AddOption("--prof-process",
+ "process V8 profiler output generated using --prof",
+ &EnvironmentOptions::prof_process);
+ AddOption("--redirect-warnings",
+ "write warnings to file instead of stderr",
+ &EnvironmentOptions::redirect_warnings,
kAllowedInEnvironment);
- AddOption("--throw-deprecation", &EnvironmentOptions::throw_deprecation,
+ AddOption("--throw-deprecation",
+ "throw an exception on deprecations",
+ &EnvironmentOptions::throw_deprecation,
kAllowedInEnvironment);
- AddOption("--trace-deprecation", &EnvironmentOptions::trace_deprecation,
+ AddOption("--trace-deprecation",
+ "show stack traces on deprecations",
+ &EnvironmentOptions::trace_deprecation,
kAllowedInEnvironment);
- AddOption("--trace-sync-io", &EnvironmentOptions::trace_sync_io,
+ AddOption("--trace-sync-io",
+ "show stack trace when use of sync IO is detected after the "
+ "first tick",
+ &EnvironmentOptions::trace_sync_io,
kAllowedInEnvironment);
- AddOption("--trace-warnings", &EnvironmentOptions::trace_warnings,
+ AddOption("--trace-warnings",
+ "show stack traces on process warnings",
+ &EnvironmentOptions::trace_warnings,
kAllowedInEnvironment);
- AddOption("--check", &EnvironmentOptions::syntax_check_only);
+ AddOption("--check",
+ "syntax check script without executing",
+ &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
@@ -79,21 +138,28 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
// 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);
+ AddOption("[has_eval_string]", "", &EnvironmentOptions::has_eval_string);
+ AddOption("--eval", "evaluate script", &EnvironmentOptions::eval_string);
Implies("--eval", "[has_eval_string]");
- AddOption("--print", &EnvironmentOptions::print_eval);
+ AddOption("--print",
+ "evaluate script and print result",
+ &EnvironmentOptions::print_eval);
AddAlias("-e", "--eval");
AddAlias("--print <arg>", "-pe");
AddAlias("-pe", { "--print", "--eval" });
AddAlias("-p", "--print");
- AddOption("--require", &EnvironmentOptions::preload_modules,
+ AddOption("--require",
+ "module to preload (option can be repeated)",
+ &EnvironmentOptions::preload_modules,
kAllowedInEnvironment);
AddAlias("-r", "--require");
- AddOption("--interactive", &EnvironmentOptions::force_repl);
+ AddOption("--interactive",
+ "always enter the REPL even if stdin does not appear "
+ "to be a terminal",
+ &EnvironmentOptions::force_repl);
AddAlias("-i", "--interactive");
- AddOption("--napi-modules", NoOp {}, kAllowedInEnvironment);
+ AddOption("--napi-modules", "", NoOp{}, kAllowedInEnvironment);
Insert(&DebugOptionsParser::instance,
&EnvironmentOptions::get_debug_options);
@@ -102,16 +168,21 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
EnvironmentOptionsParser EnvironmentOptionsParser::instance;
PerIsolateOptionsParser::PerIsolateOptionsParser() {
- AddOption("--track-heap-objects", &PerIsolateOptions::track_heap_objects,
+ AddOption("--track-heap-objects",
+ "track heap object allocations for heap snapshots",
+ &PerIsolateOptions::track_heap_objects,
kAllowedInEnvironment);
// Explicitly add some V8 flags to mark them as allowed in NODE_OPTIONS.
- AddOption("--abort_on_uncaught_exception", V8Option {},
+ AddOption("--abort_on_uncaught_exception",
+ "aborting instead of exiting causes a core file to be generated "
+ "for analysis",
+ 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);
+ 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);
@@ -120,52 +191,89 @@ PerIsolateOptionsParser::PerIsolateOptionsParser() {
PerIsolateOptionsParser PerIsolateOptionsParser::instance;
PerProcessOptionsParser::PerProcessOptionsParser() {
- AddOption("--title", &PerProcessOptions::title, kAllowedInEnvironment);
+ AddOption("--title",
+ "the process title to use on startup",
+ &PerProcessOptions::title,
+ kAllowedInEnvironment);
AddOption("--trace-event-categories",
+ "comma separated list of trace event categories to record",
&PerProcessOptions::trace_event_categories,
kAllowedInEnvironment);
AddOption("--trace-event-file-pattern",
+ "Template string specifying the filepath for the trace-events "
+ "data, it supports ${rotation} and ${pid} log-rotation id. %2$u "
+ "is the pid.",
&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,
+ AddOption("--v8-pool-size",
+ "set V8's thread pool size",
+ &PerProcessOptions::v8_thread_pool_size,
kAllowedInEnvironment);
- AddOption("--zero-fill-buffers", &PerProcessOptions::zero_fill_all_buffers,
+ AddOption("--zero-fill-buffers",
+ "automatically zero-fill all newly allocated Buffer and "
+ "SlowBuffer instances",
+ &PerProcessOptions::zero_fill_all_buffers,
kAllowedInEnvironment);
- AddOption("--security-reverts", &PerProcessOptions::security_reverts);
- AddOption("--help", &PerProcessOptions::print_help);
+ AddOption("--security-reverts", "", &PerProcessOptions::security_reverts);
+ AddOption("--help",
+ "print node command line options",
+ &PerProcessOptions::print_help);
AddAlias("-h", "--help");
- AddOption("--version", &PerProcessOptions::print_version);
+ AddOption(
+ "--version", "print Node.js version", &PerProcessOptions::print_version);
AddAlias("-v", "--version");
- AddOption("--v8-options", &PerProcessOptions::print_v8_help);
+ AddOption("--v8-options",
+ "print V8 command line options",
+ &PerProcessOptions::print_v8_help);
#ifdef NODE_HAVE_I18N_SUPPORT
- AddOption("--icu-data-dir", &PerProcessOptions::icu_data_dir,
+ AddOption("--icu-data-dir",
+ "set ICU data load path to dir (overrides NODE_ICU_DATA)"
+#ifndef NODE_HAVE_SMALL_ICU
+ " (note: linked-in ICU data is present)\n"
+#endif
+ ,
+ &PerProcessOptions::icu_data_dir,
kAllowedInEnvironment);
#endif
#if HAVE_OPENSSL
- AddOption("--openssl-config", &PerProcessOptions::openssl_config,
+ AddOption("--openssl-config",
+ "load OpenSSL configuration from the specified file "
+ "(overrides OPENSSL_CONF)",
+ &PerProcessOptions::openssl_config,
kAllowedInEnvironment);
- AddOption("--tls-cipher-list", &PerProcessOptions::tls_cipher_list,
+ AddOption("--tls-cipher-list",
+ "use an alternative default TLS cipher list",
+ &PerProcessOptions::tls_cipher_list,
kAllowedInEnvironment);
- AddOption("--use-openssl-ca", &PerProcessOptions::use_openssl_ca,
+ AddOption("--use-openssl-ca",
+ "use OpenSSL's default CA store",
+ &PerProcessOptions::use_openssl_ca,
kAllowedInEnvironment);
- AddOption("--use-bundled-ca", &PerProcessOptions::use_bundled_ca,
+ AddOption("--use-bundled-ca",
+ "use bundled CA store",
+ &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,
+ AddOption("--enable-fips",
+ "enable FIPS crypto at startup",
+ &PerProcessOptions::enable_fips_crypto,
kAllowedInEnvironment);
- AddOption("--force-fips", &PerProcessOptions::force_fips_crypto,
+ AddOption("--force-fips",
+ "force FIPS crypto (cannot be disabled)",
+ &PerProcessOptions::force_fips_crypto,
kAllowedInEnvironment);
#endif
#endif
@@ -216,4 +324,153 @@ HostPort SplitHostPort(const std::string& arg, std::string* error) {
return HostPort { RemoveBrackets(arg.substr(0, colon)),
ParseAndValidatePort(arg.substr(colon + 1), error) };
}
+
+// Usage: Either:
+// - getOptions() to get all options + metadata or
+// - getOptions(string) to get the value of a particular option
+void GetOptions(const FunctionCallbackInfo<Value>& args) {
+ Mutex::ScopedLock lock(per_process_opts_mutex);
+ Environment* env = Environment::GetCurrent(args);
+ Isolate* isolate = env->isolate();
+ Local<Context> context = env->context();
+
+ // Temporarily act as if the current Environment's/IsolateData's options were
+ // the default options, i.e. like they are the ones we'd access for global
+ // options parsing, so that all options are available from the main parser.
+ auto original_per_isolate = per_process_opts->per_isolate;
+ per_process_opts->per_isolate = env->isolate_data()->options();
+ auto original_per_env = per_process_opts->per_isolate->per_env;
+ per_process_opts->per_isolate->per_env = env->options();
+ OnScopeLeave on_scope_leave([&]() {
+ per_process_opts->per_isolate->per_env = original_per_env;
+ per_process_opts->per_isolate = original_per_isolate;
+ });
+
+ const auto& parser = PerProcessOptionsParser::instance;
+
+ std::string filter;
+ if (args[0]->IsString()) filter = *node::Utf8Value(isolate, args[0]);
+
+ Local<Map> options = Map::New(isolate);
+ for (const auto& item : parser.options_) {
+ if (!filter.empty() && item.first != filter) continue;
+
+ Local<Value> value;
+ const auto& option_info = item.second;
+ auto field = option_info.field;
+ PerProcessOptions* opts = per_process_opts.get();
+ switch (option_info.type) {
+ case kNoOp:
+ case kV8Option:
+ value = Undefined(isolate);
+ break;
+ case kBoolean:
+ value = Boolean::New(isolate, *parser.Lookup<bool>(field, opts));
+ break;
+ case kInteger:
+ value = Number::New(isolate, *parser.Lookup<int64_t>(field, opts));
+ break;
+ case kString:
+ if (!ToV8Value(context, *parser.Lookup<std::string>(field, opts))
+ .ToLocal(&value)) {
+ return;
+ }
+ break;
+ case kStringList:
+ if (!ToV8Value(context,
+ *parser.Lookup<std::vector<std::string>>(field, opts))
+ .ToLocal(&value)) {
+ return;
+ }
+ break;
+ case kHostPort: {
+ const HostPort& host_port = *parser.Lookup<HostPort>(field, opts);
+ Local<Object> obj = Object::New(isolate);
+ Local<Value> host;
+ if (!ToV8Value(context, host_port.host_name).ToLocal(&host) ||
+ obj->Set(context, env->host_string(), host).IsNothing() ||
+ obj->Set(context,
+ env->port_string(),
+ Integer::New(isolate, host_port.port))
+ .IsNothing()) {
+ return;
+ }
+ value = obj;
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ CHECK(!value.IsEmpty());
+
+ if (!filter.empty()) {
+ args.GetReturnValue().Set(value);
+ return;
+ }
+
+ Local<Value> name = ToV8Value(context, item.first).ToLocalChecked();
+ Local<Object> info = Object::New(isolate);
+ Local<Value> help_text;
+ if (!ToV8Value(context, option_info.help_text).ToLocal(&help_text) ||
+ !info->Set(context, env->help_text_string(), help_text)
+ .FromMaybe(false) ||
+ !info->Set(context,
+ env->env_var_settings_string(),
+ Integer::New(isolate,
+ static_cast<int>(option_info.env_setting)))
+ .FromMaybe(false) ||
+ !info->Set(context,
+ env->type_string(),
+ Integer::New(isolate, static_cast<int>(option_info.type)))
+ .FromMaybe(false) ||
+ info->Set(context, env->value_string(), value).IsNothing() ||
+ options->Set(context, name, info).IsEmpty()) {
+ return;
+ }
+ }
+
+ if (!filter.empty()) return;
+
+ Local<Value> aliases;
+ if (!ToV8Value(context, parser.aliases_).ToLocal(&aliases)) return;
+
+ Local<Object> ret = Object::New(isolate);
+ if (ret->Set(context, env->options_string(), options).IsNothing() ||
+ ret->Set(context, env->aliases_string(), aliases).IsNothing()) {
+ return;
+ }
+
+ args.GetReturnValue().Set(ret);
+}
+
+void Initialize(Local<Object> target,
+ Local<Value> unused,
+ Local<Context> context) {
+ Environment* env = Environment::GetCurrent(context);
+ Isolate* isolate = env->isolate();
+ env->SetMethodNoSideEffect(target, "getOptions", GetOptions);
+
+ Local<Object> env_settings = Object::New(isolate);
+ NODE_DEFINE_CONSTANT(env_settings, kAllowedInEnvironment);
+ NODE_DEFINE_CONSTANT(env_settings, kDisallowedInEnvironment);
+ target
+ ->Set(
+ context, FIXED_ONE_BYTE_STRING(isolate, "envSettings"), env_settings)
+ .FromJust();
+
+ Local<Object> types = Object::New(isolate);
+ NODE_DEFINE_CONSTANT(types, kNoOp);
+ NODE_DEFINE_CONSTANT(types, kV8Option);
+ NODE_DEFINE_CONSTANT(types, kBoolean);
+ NODE_DEFINE_CONSTANT(types, kInteger);
+ NODE_DEFINE_CONSTANT(types, kString);
+ NODE_DEFINE_CONSTANT(types, kHostPort);
+ NODE_DEFINE_CONSTANT(types, kStringList);
+ target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "types"), types)
+ .FromJust();
+}
+
+} // namespace options_parser
} // namespace node
+
+NODE_MODULE_CONTEXT_AWARE_INTERNAL(options, node::options_parser::Initialize)
diff --git a/src/node_options.h b/src/node_options.h
index 957e2b729d..7891d550ae 100644
--- a/src/node_options.h
+++ b/src/node_options.h
@@ -141,7 +141,10 @@ class PerProcessOptions {
// The actual options parser, as opposed to the structs containing them:
+namespace options_parser {
+
HostPort SplitHostPort(const std::string& arg, std::string* error);
+void GetOptions(const v8::FunctionCallbackInfo<v8::Value>& args);
enum OptionEnvvarSettings {
kAllowedInEnvironment,
@@ -176,24 +179,31 @@ class OptionsParser {
// specified whether the option should be allowed from environment variable
// sources (i.e. NODE_OPTIONS).
void AddOption(const std::string& name,
+ const std::string& help_text,
bool Options::* field,
OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
void AddOption(const std::string& name,
+ const std::string& help_text,
int64_t Options::* field,
OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
void AddOption(const std::string& name,
+ const std::string& help_text,
std::string Options::* field,
OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
void AddOption(const std::string& name,
+ const std::string& help_text,
std::vector<std::string> Options::* field,
OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
void AddOption(const std::string& name,
+ const std::string& help_text,
HostPort Options::* field,
OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
void AddOption(const std::string& name,
+ const std::string& help_text,
NoOp no_op_tag,
OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
void AddOption(const std::string& name,
+ const std::string& help_text,
V8Option v8_option_tag,
OptionEnvvarSettings env_setting = kDisallowedInEnvironment);
@@ -281,6 +291,12 @@ class OptionsParser {
T Options::* field_;
};
+ template <typename T>
+ inline T* Lookup(std::shared_ptr<BaseOptionField> field,
+ Options* options) const {
+ return std::static_pointer_cast<OptionField<T>>(field)->Lookup(options);
+ }
+
// An option consists of:
// - A type.
// - A way to store/access the property value.
@@ -289,6 +305,7 @@ class OptionsParser {
OptionType type;
std::shared_ptr<BaseOptionField> field;
OptionEnvvarSettings env_setting;
+ std::string help_text;
};
// An implied option is composed of the information on where to store a
@@ -319,6 +336,8 @@ class OptionsParser {
template <typename OtherOptions>
friend class OptionsParser;
+
+ friend void GetOptions(const v8::FunctionCallbackInfo<v8::Value>& args);
};
class DebugOptionsParser : public OptionsParser<DebugOptions> {
@@ -349,6 +368,7 @@ class PerProcessOptionsParser : public OptionsParser<PerProcessOptions> {
static PerProcessOptionsParser instance;
};
+} // namespace options_parser
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
diff --git a/src/util-inl.h b/src/util-inl.h
index c6cd263aa2..bbee32615f 100644
--- a/src/util-inl.h
+++ b/src/util-inl.h
@@ -24,8 +24,9 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
-#include "util.h"
+#include <limits.h> // INT_MAX
#include <cstring>
+#include "util.h"
#if defined(_MSC_VER)
#include <intrin.h>
@@ -397,6 +398,62 @@ inline char* Calloc(size_t n) { return Calloc<char>(n); }
inline char* UncheckedMalloc(size_t n) { return UncheckedMalloc<char>(n); }
inline char* UncheckedCalloc(size_t n) { return UncheckedCalloc<char>(n); }
+// This is a helper in the .cc file so including util-inl.h doesn't include more
+// headers than we really need to.
+void ThrowErrStringTooLong(v8::Isolate* isolate);
+
+v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
+ const std::string& str) {
+ v8::Isolate* isolate = context->GetIsolate();
+ if (UNLIKELY(str.size() >= static_cast<size_t>(v8::String::kMaxLength))) {
+ // V8 only has a TODO comment about adding an exception when the maximum
+ // string size is exceeded.
+ ThrowErrStringTooLong(isolate);
+ return v8::MaybeLocal<v8::Value>();
+ }
+
+ return v8::String::NewFromUtf8(
+ isolate, str.data(), v8::NewStringType::kNormal, str.size())
+ .FromMaybe(v8::Local<v8::String>());
+}
+
+template <typename T>
+v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
+ const std::vector<T>& vec) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::EscapableHandleScope handle_scope(isolate);
+
+ v8::Local<v8::Array> arr = v8::Array::New(isolate, vec.size());
+ for (size_t i = 0; i < vec.size(); ++i) {
+ v8::Local<v8::Value> val;
+ if (!ToV8Value(context, vec[i]).ToLocal(&val) ||
+ arr->Set(context, i, val).IsNothing()) {
+ return v8::MaybeLocal<v8::Value>();
+ }
+ }
+
+ return handle_scope.Escape(arr);
+}
+
+template <typename T, typename U>
+v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
+ const std::unordered_map<T, U>& map) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::EscapableHandleScope handle_scope(isolate);
+
+ v8::Local<v8::Map> ret = v8::Map::New(isolate);
+ for (const auto& item : map) {
+ v8::Local<v8::Value> first, second;
+ if (!ToV8Value(context, item.first).ToLocal(&first) ||
+ !ToV8Value(context, item.second).ToLocal(&second) ||
+ ret->Set(context, first, second).IsEmpty()) {
+ return v8::MaybeLocal<v8::Value>();
+ }
+ }
+
+ return handle_scope.Escape(ret);
+}
+
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
diff --git a/src/util.cc b/src/util.cc
index c36ea5169d..e1a846f74d 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -19,12 +19,13 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
-#include "string_bytes.h"
+#include <stdio.h>
+#include <sstream>
#include "node_buffer.h"
+#include "node_errors.h"
#include "node_internals.h"
+#include "string_bytes.h"
#include "uv.h"
-#include <stdio.h>
-#include <sstream>
namespace node {
@@ -133,4 +134,8 @@ std::set<std::string> ParseCommaSeparatedSet(const std::string& in) {
return out;
}
+void ThrowErrStringTooLong(v8::Isolate* isolate) {
+ isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
+}
+
} // namespace node
diff --git a/src/util.h b/src/util.h
index a9fce79ebe..057346a2d0 100644
--- a/src/util.h
+++ b/src/util.h
@@ -34,9 +34,10 @@
#include <stdlib.h>
#include <string.h>
-#include <string>
#include <functional> // std::function
#include <set>
+#include <string>
+#include <unordered_map>
namespace node {
@@ -479,6 +480,15 @@ using DeleteFnPtr = typename FunctionDeleter<T, function>::Pointer;
std::set<std::string> ParseCommaSeparatedSet(const std::string& in);
+inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
+ const std::string& str);
+template <typename T>
+inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
+ const std::vector<T>& vec);
+template <typename T, typename U>
+inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
+ const std::unordered_map<T, U>& map);
+
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS