src: cache the result of GetOptions() in JS land

Instead of calling into C++ each time we need to check the value
of a command line option, cache the option map in a new
`internal/options` module for faster access to the values in JS land.

PR-URL: https://github.com/nodejs/node/pull/24091
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Refael Ackermann <refack@gmail.com>
This commit is contained in:
Joyee Cheung 2018-11-05 04:52:50 +08:00 committed by Rich Trott
parent 350bef6a10
commit f895b5a58e
15 changed files with 49 additions and 44 deletions

View File

@ -35,8 +35,8 @@ const {
ERR_CRYPTO_FIPS_UNAVAILABLE ERR_CRYPTO_FIPS_UNAVAILABLE
} = require('internal/errors').codes; } = require('internal/errors').codes;
const constants = internalBinding('constants').crypto; const constants = internalBinding('constants').crypto;
const { getOptions } = internalBinding('options'); const { getOptionValue } = require('internal/options');
const pendingDeprecation = getOptions('--pending-deprecation'); const pendingDeprecation = getOptionValue('--pending-deprecation');
const { const {
fipsMode, fipsMode,
fipsForced fipsForced

View File

@ -1,8 +1,7 @@
'use strict'; 'use strict';
const { getOptions } = internalBinding('options'); const { options, aliases } = require('internal/options');
function print(stream) { function print(stream) {
const { options, aliases } = getOptions();
const all_opts = [...options.keys(), ...aliases.keys()]; const all_opts = [...options.keys(), ...aliases.keys()];
stream.write(`_node_complete() { stream.write(`_node_complete() {

View File

@ -251,8 +251,7 @@
NativeModule.isInternal = function(id) { NativeModule.isInternal = function(id) {
return id.startsWith('internal/') || return id.startsWith('internal/') ||
(id === 'worker_threads' && (id === 'worker_threads' && !config.experimentalWorker);
!internalBinding('options').getOptions('--experimental-worker'));
}; };
} }

View File

@ -103,12 +103,13 @@
NativeModule.require('internal/inspector_async_hook').setup(); NativeModule.require('internal/inspector_async_hook').setup();
} }
const { getOptions } = internalBinding('options'); const { getOptionValue } = NativeModule.require('internal/options');
const helpOption = getOptions('--help'); const helpOption = getOptionValue('--help');
const completionBashOption = getOptions('--completion-bash'); const completionBashOption = getOptionValue('--completion-bash');
const experimentalModulesOption = getOptions('--experimental-modules'); const experimentalModulesOption = getOptionValue('--experimental-modules');
const experimentalVMModulesOption = getOptions('--experimental-vm-modules'); const experimentalVMModulesOption =
const experimentalWorkerOption = getOptions('--experimental-worker'); getOptionValue('--experimental-vm-modules');
const experimentalWorkerOption = getOptionValue('--experimental-worker');
if (helpOption) { if (helpOption) {
NativeModule.require('internal/print_help').print(process.stdout); NativeModule.require('internal/print_help').print(process.stdout);
return; return;
@ -721,10 +722,9 @@
const get = () => { const get = () => {
const { const {
getOptions,
envSettings: { kAllowedInEnvironment } envSettings: { kAllowedInEnvironment }
} = internalBinding('options'); } = internalBinding('options');
const { options, aliases } = getOptions(); const { options, aliases } = NativeModule.require('internal/options');
const allowedNodeEnvironmentFlags = []; const allowedNodeEnvironmentFlags = [];
for (const [name, info] of options) { for (const [name, info] of options) {

View File

@ -9,7 +9,7 @@ const {
CHAR_HASH, CHAR_HASH,
} = require('internal/constants'); } = require('internal/constants');
const { getOptions } = internalBinding('options'); const { getOptionValue } = require('internal/options');
// Invoke with makeRequireFunction(module) where |module| is the Module object // Invoke with makeRequireFunction(module) where |module| is the Module object
// to use as the context for the require() function. // to use as the context for the require() function.
@ -107,7 +107,7 @@ const builtinLibs = [
'v8', 'vm', 'zlib' 'v8', 'vm', 'zlib'
]; ];
if (getOptions('--experimental-worker')) { if (getOptionValue('--experimental-worker')) {
builtinLibs.push('worker_threads'); builtinLibs.push('worker_threads');
builtinLibs.sort(); builtinLibs.sort();
} }

View File

@ -41,10 +41,10 @@ const {
stripBOM, stripBOM,
stripShebang stripShebang
} = require('internal/modules/cjs/helpers'); } = require('internal/modules/cjs/helpers');
const options = internalBinding('options'); const { getOptionValue } = require('internal/options');
const preserveSymlinks = options.getOptions('--preserve-symlinks'); const preserveSymlinks = getOptionValue('--preserve-symlinks');
const preserveSymlinksMain = options.getOptions('--preserve-symlinks-main'); const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const experimentalModules = options.getOptions('--experimental-modules'); const experimentalModules = getOptionValue('--experimental-modules');
const { const {
ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_TYPE,

View File

@ -6,9 +6,9 @@ const internalFS = require('internal/fs/utils');
const { NativeModule } = require('internal/bootstrap/loaders'); const { NativeModule } = require('internal/bootstrap/loaders');
const { extname } = require('path'); const { extname } = require('path');
const { realpathSync } = require('fs'); const { realpathSync } = require('fs');
const { getOptions } = internalBinding('options'); const { getOptionValue } = require('internal/options');
const preserveSymlinks = getOptions('--preserve-symlinks'); const preserveSymlinks = getOptionValue('--preserve-symlinks');
const preserveSymlinksMain = getOptions('--preserve-symlinks-main'); const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const { const {
ERR_MISSING_MODULE, ERR_MISSING_MODULE,
ERR_MODULE_RESOLUTION_LEGACY, ERR_MODULE_RESOLUTION_LEGACY,

18
lib/internal/options.js Normal file
View File

@ -0,0 +1,18 @@
'use strict';
const { getOptions } = internalBinding('options');
const { options, aliases } = getOptions();
function getOptionValue(option) {
const result = options.get(option);
if (!result) {
return undefined;
}
return result.value;
}
module.exports = {
options,
aliases,
getOptionValue
};

View File

@ -1,5 +1,6 @@
'use strict'; 'use strict';
const { getOptions, types } = internalBinding('options');
const { types } = internalBinding('options');
const typeLookup = []; const typeLookup = [];
for (const key of Object.keys(types)) for (const key of Object.keys(types))
@ -132,7 +133,7 @@ function format({ options, aliases = new Map(), firstColumn, secondColumn }) {
} }
function print(stream) { function print(stream) {
const { options, aliases } = getOptions(); const { options, aliases } = require('internal/options');
// Use 75 % of the available width, and at least 70 characters. // Use 75 % of the available width, and at least 70 characters.
const width = Math.max(70, (stream.columns || 0) * 0.75); const width = Math.max(70, (stream.columns || 0) * 0.75);

View File

@ -48,7 +48,7 @@ exports.ESMLoader = undefined;
exports.setup = function() { exports.setup = function() {
let ESMLoader = new Loader(); let ESMLoader = new Loader();
const loaderPromise = (async () => { const loaderPromise = (async () => {
const userLoader = internalBinding('options').getOptions('--loader'); const userLoader = require('internal/options').getOptionValue('--loader');
if (userLoader) { if (userLoader) {
const hooks = await ESMLoader.import( const hooks = await ESMLoader.import(
userLoader, pathToFileURL(`${process.cwd()}/`).href); userLoader, pathToFileURL(`${process.cwd()}/`).href);

View File

@ -70,7 +70,7 @@ const {
ERR_SCRIPT_EXECUTION_INTERRUPTED ERR_SCRIPT_EXECUTION_INTERRUPTED
} = require('internal/errors').codes; } = require('internal/errors').codes;
const { sendInspectorCommand } = require('internal/util/inspector'); const { sendInspectorCommand } = require('internal/util/inspector');
const experimentalREPLAwait = internalBinding('options').getOptions( const experimentalREPLAwait = require('internal/options').getOptionValue(
'--experimental-repl-await' '--experimental-repl-await'
); );
const { isRecoverableError } = require('internal/repl/recoverable'); const { isRecoverableError } = require('internal/repl/recoverable');

View File

@ -418,7 +418,7 @@ module.exports = {
compileFunction, compileFunction,
}; };
if (internalBinding('options').getOptions('--experimental-vm-modules')) { if (require('internal/options').getOptionValue('--experimental-vm-modules')) {
const { SourceTextModule } = require('internal/vm/source_text_module'); const { SourceTextModule } = require('internal/vm/source_text_module');
module.exports.SourceTextModule = SourceTextModule; module.exports.SourceTextModule = SourceTextModule;
} }

View File

@ -133,6 +133,7 @@
'lib/internal/modules/esm/translators.js', 'lib/internal/modules/esm/translators.js',
'lib/internal/safe_globals.js', 'lib/internal/safe_globals.js',
'lib/internal/net.js', 'lib/internal/net.js',
'lib/internal/options.js',
'lib/internal/print_help.js', 'lib/internal/print_help.js',
'lib/internal/priority_queue.js', 'lib/internal/priority_queue.js',
'lib/internal/process/esm_loader.js', 'lib/internal/process/esm_loader.js',

View File

@ -365,9 +365,8 @@ HostPort SplitHostPort(const std::string& arg,
ParseAndValidatePort(arg.substr(colon + 1), errors) }; ParseAndValidatePort(arg.substr(colon + 1), errors) };
} }
// Usage: Either: // Return a map containing all the options and their metadata as well
// - getOptions() to get all options + metadata or // as the aliases
// - getOptions(string) to get the value of a particular option
void GetOptions(const FunctionCallbackInfo<Value>& args) { void GetOptions(const FunctionCallbackInfo<Value>& args) {
Mutex::ScopedLock lock(per_process_opts_mutex); Mutex::ScopedLock lock(per_process_opts_mutex);
Environment* env = Environment::GetCurrent(args); Environment* env = Environment::GetCurrent(args);
@ -388,13 +387,8 @@ void GetOptions(const FunctionCallbackInfo<Value>& args) {
const auto& parser = PerProcessOptionsParser::instance; const auto& parser = PerProcessOptionsParser::instance;
std::string filter;
if (args[0]->IsString()) filter = *node::Utf8Value(isolate, args[0]);
Local<Map> options = Map::New(isolate); Local<Map> options = Map::New(isolate);
for (const auto& item : parser.options_) { for (const auto& item : parser.options_) {
if (!filter.empty() && item.first != filter) continue;
Local<Value> value; Local<Value> value;
const auto& option_info = item.second; const auto& option_info = item.second;
auto field = option_info.field; auto field = option_info.field;
@ -443,11 +437,6 @@ void GetOptions(const FunctionCallbackInfo<Value>& args) {
} }
CHECK(!value.IsEmpty()); CHECK(!value.IsEmpty());
if (!filter.empty()) {
args.GetReturnValue().Set(value);
return;
}
Local<Value> name = ToV8Value(context, item.first).ToLocalChecked(); Local<Value> name = ToV8Value(context, item.first).ToLocalChecked();
Local<Object> info = Object::New(isolate); Local<Object> info = Object::New(isolate);
Local<Value> help_text; Local<Value> help_text;
@ -469,8 +458,6 @@ void GetOptions(const FunctionCallbackInfo<Value>& args) {
} }
} }
if (!filter.empty()) return;
Local<Value> aliases; Local<Value> aliases;
if (!ToV8Value(context, parser.aliases_).ToLocal(&aliases)) return; if (!ToV8Value(context, parser.aliases_).ToLocal(&aliases)) return;

View File

@ -11,4 +11,4 @@ const list = process.moduleLoadList.slice();
const assert = require('assert'); const assert = require('assert');
assert(list.length <= 77, list); assert(list.length <= 78, list);