diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 3e3eaa5b60e17a..359e2b46bdb054 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -105,6 +105,11 @@ NativeModule.require('internal/inspector_async_hook').setup(); } + if (internalBinding('options').getOptions('--help')) { + NativeModule.require('internal/print_help').print(process.stdout); + return; + } + if (isMainThread) { mainThreadSetup.setupChildProcessIpcChannel(); } diff --git a/lib/internal/print_help.js b/lib/internal/print_help.js new file mode 100644 index 00000000000000..9d14671973402e --- /dev/null +++ b/lib/internal/print_help.js @@ -0,0 +1,151 @@ +'use strict'; +const { internalBinding } = require('internal/bootstrap/loaders'); +const { getOptions, types } = internalBinding('options'); + +const typeLookup = []; +for (const key of Object.keys(types)) + typeLookup[types[key]] = key; + +// Environment variables are parsed ad-hoc throughout the code base, +// so we gather the documentation here. +const { hasIntl, hasSmallICU, hasNodeOptions } = process.binding('config'); +const envVars = new Map([ + ['NODE_DEBUG', { helpText: "','-separated list of core modules that " + + 'should print debug information' }], + ['NODE_DEBUG_NATIVE', { helpText: "','-separated list of C++ core debug " + + 'categories that should print debug output' }], + ['NODE_DISABLE_COLORS', { helpText: 'set to 1 to disable colors in ' + + 'the REPL' }], + ['NODE_EXTRA_CA_CERTS', { helpText: 'path to additional CA certificates ' + + 'file' }], + ['NODE_NO_WARNINGS', { helpText: 'set to 1 to silence process warnings' }], + ['NODE_PATH', { helpText: `'${require('path').delimiter}'-separated list ` + + 'of directories prefixed to the module search path' }], + ['NODE_PENDING_DEPRECATION', { helpText: 'set to 1 to emit pending ' + + 'deprecation warnings' }], + ['NODE_PRESERVE_SYMLINKS', { helpText: 'set to 1 to preserve symbolic ' + + 'links when resolving and caching modules' }], + ['NODE_REDIRECT_WARNINGS', { helpText: 'write warnings to path instead ' + + 'of stderr' }], + ['NODE_REPL_HISTORY', { helpText: 'path to the persistent REPL ' + + 'history file' }], + ['OPENSSL_CONF', { helpText: 'load OpenSSL configuration from file' }] +].concat(hasIntl ? [ + ['NODE_ICU_DATA', { helpText: 'data path for ICU (Intl object) data' + + hasSmallICU ? '' : ' (will extend linked-in data)' }] +] : []).concat(hasNodeOptions ? [ + ['NODE_OPTIONS', { helpText: 'set CLI options in the environment via a ' + + 'space-separated list' }] +] : [])); + + +function indent(text, depth) { + return text.replace(/^/gm, ' '.repeat(depth)); +} + +function fold(text, width) { + return text.replace(new RegExp(`([^\n]{0,${width}})( |$)`, 'g'), + (_, newLine, end) => newLine + (end === ' ' ? '\n' : '')); +} + +function getArgDescription(type) { + switch (typeLookup[type]) { + case 'kNoOp': + case 'kV8Option': + case 'kBoolean': + break; + case 'kHostPort': + return '[host:]port'; + case 'kInteger': + case 'kString': + case 'kStringList': + return '...'; + case undefined: + break; + default: + require('assert').fail(`unknown option type ${type}`); + } +} + +function format({ options, aliases = new Map(), firstColumn, secondColumn }) { + let text = ''; + + for (const [ + name, { helpText, type, value } + ] of [...options.entries()].sort()) { + if (!helpText) continue; + + let displayName = name; + const argDescription = getArgDescription(type); + if (argDescription) + displayName += `=${argDescription}`; + + for (const [ from, to ] of aliases) { + // For cases like e.g. `-e, --eval`. + if (to[0] === name && to.length === 1) { + displayName = `${from}, ${displayName}`; + } + + // For cases like `--inspect-brk[=[host:]port]`. + const targetInfo = options.get(to[0]); + const targetArgDescription = + targetInfo ? getArgDescription(targetInfo.type) : '...'; + if (from === `${name}=`) { + displayName += `[=${targetArgDescription}]`; + } else if (from === `${name} <arg>`) { + displayName += ` [${targetArgDescription}]`; + } + } + + let displayHelpText = helpText; + if (value === true) { + // Mark boolean options we currently have enabled. + // In particular, it indicates whether --use-openssl-ca + // or --use-bundled-ca is the (current) default. + displayHelpText += ' (currently set)'; + } + + text += displayName; + if (displayName.length >= firstColumn) + text += '\n' + ' '.repeat(firstColumn); + else + text += ' '.repeat(firstColumn - displayName.length); + + text += indent(fold(displayHelpText, secondColumn), + firstColumn).trimLeft() + '\n'; + } + + return text; +} + +function print(stream) { + const { options, aliases } = getOptions(); + + // TODO(addaleax): Allow a bit of expansion depending on `stream.columns` + // if it is set. + const firstColumn = 28; + const secondColumn = 40; + + options.set('-', { helpText: 'script read from stdin (default; ' + + 'interactive mode if a tty)' }); + options.set('--', { helpText: 'indicate the end of node options' }); + stream.write( + 'Usage: node [options] [ -e script | script.js | - ] [arguments]\n' + + ' node inspect script.js [arguments]\n\n' + + 'Options:\n'); + stream.write(indent(format({ + options, aliases, firstColumn, secondColumn + }), 2)); + + stream.write('\nEnvironment variables:\n'); + + stream.write(format({ + options: envVars, firstColumn, secondColumn + })); + + stream.write('\nDocumentation can be found at https://nodejs.org/\n'); +} + +module.exports = { + print +}; diff --git a/node.gyp b/node.gyp index 5b8ca26bb2010d..d94e6ff62aa0db 100644 --- a/node.gyp +++ b/node.gyp @@ -132,6 +132,7 @@ 'lib/internal/modules/esm/translators.js', 'lib/internal/safe_globals.js', 'lib/internal/net.js', + 'lib/internal/print_help.js', 'lib/internal/priority_queue.js', 'lib/internal/process/esm_loader.js', 'lib/internal/process/main_thread_only.js', diff --git a/src/env.h b/src/env.h index 556a09754fc465..fa802e4016dfdb 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 565c965cf9fae6..c64dee8983b9d7 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() }; @@ -2345,157 +2348,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" - "Options:\n" - " - script read from stdin (default; \n" - " interactive mode if a tty)\n" - " -- indicate the end of node options\n" - " --abort-on-uncaught-exception\n" - " aborting instead of exiting causes a\n" - " core file to be generated for analysis\n" -#if HAVE_OPENSSL && NODE_FIPS_MODE - " --enable-fips enable FIPS crypto at startup\n" -#endif // NODE_FIPS_MODE && NODE_FIPS_MODE - " --experimental-modules experimental ES Module support\n" - " and caching modules\n" - " --experimental-repl-await experimental await keyword support\n" - " in REPL\n" - " --experimental-vm-modules experimental ES Module support\n" - " in vm module\n" - " --experimental-worker experimental threaded Worker support\n" -#if HAVE_OPENSSL && NODE_FIPS_MODE - " --force-fips force FIPS crypto (cannot be disabled)\n" -#endif // HAVE_OPENSSL && NODE_FIPS_MODE -#if defined(NODE_HAVE_I18N_SUPPORT) - " --icu-data-dir=dir set ICU data load path to dir\n" - " (overrides NODE_ICU_DATA)\n" -#if !defined(NODE_HAVE_SMALL_ICU) - " note: linked-in ICU data is present\n" -#endif -#endif // defined(NODE_HAVE_I18N_SUPPORT) -#if HAVE_INSPECTOR - " --inspect-brk[=[host:]port]\n" - " activate inspector on host:port\n" - " and break at start of user script\n" - " --inspect-port=[host:]port\n" - " set host:port for inspector\n" - " --inspect[=[host:]port] activate inspector on host:port\n" - " (default: 127.0.0.1:9229)\n" -#endif // HAVE_INSPECTOR - " --loader=file (with --experimental-modules) use the \n" - " specified file as a custom loader\n" - " for ECMAScript Modules \n" - " --napi-modules load N-API modules (no-op - option\n" - " kept for compatibility)\n" - " --no-deprecation silence deprecation warnings\n" - " --no-force-async-hooks-checks\n" - " disable checks for async_hooks\n" - " --no-warnings silence all process warnings\n" -#if HAVE_OPENSSL - " --openssl-config=file load OpenSSL configuration from the\n" - " specified file (overrides\n" - " OPENSSL_CONF)\n" -#endif // HAVE_OPENSSL - " --pending-deprecation emit pending deprecation warnings\n" - " --preserve-symlinks preserve symbolic links when resolving\n" - " --preserve-symlinks-main preserve symbolic links when resolving\n" - " the main module\n" - " --prof generate V8 profiler output\n" - " --prof-process process V8 profiler output generated\n" - " using --prof\n" - " --redirect-warnings=file\n" - " write warnings to file instead of\n" - " stderr\n" - " --throw-deprecation throw an exception on deprecations\n" - " --title=title the process title to use on start up\n" -#if HAVE_OPENSSL - " --tls-cipher-list=val use an alternative default TLS cipher " - "list\n" -#endif // HAVE_OPENSSL - " --trace-deprecation show stack traces on deprecations\n" - " --trace-event-categories comma separated list of trace event\n" - " categories to record\n" - " --trace-event-file-pattern Template string specifying the\n" - " filepath for the trace-events data, it\n" - " supports ${rotation} and ${pid}\n" - " log-rotation id. %%2$u is the pid.\n" - " --trace-events-enabled track trace events\n" - " --trace-sync-io show stack trace when use of sync IO\n" - " is detected after the first tick\n" - " --trace-warnings show stack traces on process warnings\n" - " --track-heap-objects track heap object allocations for heap " - "snapshots\n" -#if HAVE_OPENSSL - " --use-bundled-ca use bundled CA store" -#if !defined(NODE_OPENSSL_CERT_STORE) - " (default)" -#endif - "\n" - " --use-openssl-ca use OpenSSL's default CA store" -#if defined(NODE_OPENSSL_CERT_STORE) - " (default)" -#endif -#endif // HAVE_OPENSSL - "\n" - " --v8-options print v8 command line options\n" - " --v8-pool-size=num set v8's thread pool size\n" - " --zero-fill-buffers automatically zero-fill all newly " - "allocated\n" - " Buffer and SlowBuffer instances\n" - " -c, --check syntax check script without executing\n" - " -e, --eval script evaluate script\n" - " -h, --help print node command line options\n" - " -i, --interactive always enter the REPL even if stdin\n" - " does not appear to be a terminal\n" - " -p, --print evaluate script and print result\n" - " -r, --require module to preload (option can be " - "repeated)\n" - " -v, --version print Node.js version\n" - "\n" - "Environment variables:\n" - "NODE_DEBUG ','-separated list of core modules\n" - " that should print debug information\n" - "NODE_DEBUG_NATIVE ','-separated list of C++ core debug\n" - " categories that should print debug\n" - " output\n" - "NODE_DISABLE_COLORS set to 1 to disable colors in the REPL\n" - "NODE_EXTRA_CA_CERTS path to additional CA certificates\n" - " file\n" -#if defined(NODE_HAVE_I18N_SUPPORT) - "NODE_ICU_DATA data path for ICU (Intl object) data\n" -#if !defined(NODE_HAVE_SMALL_ICU) - " (will extend linked-in data)\n" -#endif -#endif // defined(NODE_HAVE_I18N_SUPPORT) - "NODE_NO_WARNINGS set to 1 to silence process warnings\n" -#if !defined(NODE_WITHOUT_NODE_OPTIONS) - "NODE_OPTIONS set CLI options in the environment\n" - " via a space-separated list\n" -#endif // !defined(NODE_WITHOUT_NODE_OPTIONS) -#ifdef _WIN32 - "NODE_PATH ';'-separated list of directories\n" -#else - "NODE_PATH ':'-separated list of directories\n" -#endif - " prefixed to the module search path\n" - "NODE_PENDING_DEPRECATION set to 1 to emit pending deprecation\n" - " warnings\n" - "NODE_PRESERVE_SYMLINKS set to 1 to preserve symbolic links\n" - " when resolving and caching modules\n" - "NODE_REDIRECT_WARNINGS write warnings to path instead of\n" - " stderr\n" - "NODE_REPL_HISTORY path to the persistent REPL history\n" - " file\n" - "OPENSSL_CONF load OpenSSL configuration from file\n" - "\n" - "Documentation can be found at https://nodejs.org/\n"); -} - static void StartInspector(Environment* env, const char* path, std::shared_ptr<DebugOptions> debug_options) { @@ -2747,13 +2599,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); @@ -2764,11 +2623,6 @@ void ProcessArgv(std::vector<std::string>* args, exit(0); } - if (per_process_opts->print_help) { - PrintHelp(); - exit(0); - } - if (per_process_opts->print_v8_help) { V8::SetFlagsFromString("--help", 6); exit(0); diff --git a/src/node_config.cc b/src/node_config.cc index c6e6211da288e4..080d8550665ad3 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -73,6 +73,10 @@ static void Initialize(Local<Object> target, READONLY_BOOLEAN_PROPERTY("hasTracing"); #endif +#if !defined(NODE_WITHOUT_NODE_OPTIONS) + READONLY_BOOLEAN_PROPERTY("hasNodeOptions"); +#endif + // TODO(addaleax): This seems to be an unused, private API. Remove it? READONLY_STRING_PROPERTY(target, "icuDataDir", per_process_opts->icu_data_dir); diff --git a/src/node_internals.h b/src/node_internals.h index eb9e79d9e8b522..2fa95024669eb1 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 e610cd50d11436..ba18e7c19b03c7 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 b10de9ef28ab9c..27e518d0f1f2d8 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,97 @@ 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" +#if defined(NODE_OPENSSL_CERT_STORE) + " (default)" +#endif + , + &PerProcessOptions::use_openssl_ca, kAllowedInEnvironment); - AddOption("--use-bundled-ca", &PerProcessOptions::use_bundled_ca, + AddOption("--use-bundled-ca", + "use bundled CA store" +#if !defined(NODE_OPENSSL_CERT_STORE) + " (default)" +#endif + , + &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 +332,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 957e2b729d9ff4..7891d550ae3dad 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 c6cd263aa2714a..bbee32615ff729 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 66be18eae2d150..66e2c9199bb23a 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 { @@ -132,4 +133,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 a9fce79ebeaec1..057346a2d04fca 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 diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 4a46ac905b8161..81563355d246ff 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -11,4 +11,4 @@ const list = process.moduleLoadList.slice(); const assert = require('assert'); -assert(list.length <= 73, list); +assert(list.length <= 74, list); diff --git a/test/parallel/test-cli-node-print-help.js b/test/parallel/test-cli-node-print-help.js index ad6da699e41c61..e715fdbcb8f576 100644 --- a/test/parallel/test-cli-node-print-help.js +++ b/test/parallel/test-cli-node-print-help.js @@ -27,12 +27,12 @@ function validateNodePrintHelp() { const cliHelpOptions = [ { compileConstant: HAVE_OPENSSL, - flags: [ '--openssl-config=file', '--tls-cipher-list=val', + flags: [ '--openssl-config=...', '--tls-cipher-list=...', '--use-bundled-ca', '--use-openssl-ca' ] }, { compileConstant: NODE_FIPS_MODE, flags: [ '--enable-fips', '--force-fips' ] }, { compileConstant: NODE_HAVE_I18N_SUPPORT, - flags: [ '--icu-data-dir=dir', 'NODE_ICU_DATA' ] }, + flags: [ '--icu-data-dir=...', 'NODE_ICU_DATA' ] }, { compileConstant: HAVE_INSPECTOR, flags: [ '--inspect-brk[=[host:]port]', '--inspect-port=[host:]port', '--inspect[=[host:]port]' ] },