diff --git a/benchmark/fixtures/require-cachable.js b/benchmark/fixtures/require-cachable.js index f651728dc78..4afda3cda47 100644 --- a/benchmark/fixtures/require-cachable.js +++ b/benchmark/fixtures/require-cachable.js @@ -1,13 +1,10 @@ 'use strict'; -const list = require('internal/bootstrap/cache'); +const { internalBinding } = require('internal/test/binding'); const { - isMainThread -} = require('worker_threads'); + moduleCategories: { canBeRequired } +} = internalBinding('native_module'); -for (const key of list.cachableBuiltins) { - if (!isMainThread && key === 'trace_events') { - continue; - } +for (const key of canBeRequired) { require(key); } diff --git a/lib/internal/bootstrap/cache.js b/lib/internal/bootstrap/cache.js deleted file mode 100644 index 0f58929c9ce..00000000000 --- a/lib/internal/bootstrap/cache.js +++ /dev/null @@ -1,85 +0,0 @@ -'use strict'; - -// This is only exposed for internal build steps and testing purposes. -// We create new copies of the source and the code cache -// so the resources eventually used to compile builtin modules -// cannot be tampered with even with --expose-internals. - -const { NativeModule } = require('internal/bootstrap/loaders'); -const { - getCodeCache, compileFunction -} = internalBinding('native_module'); -const { hasTracing, hasInspector } = internalBinding('config'); - -// Modules with source code compiled in js2c that -// cannot be compiled with the code cache. -const cannotBeRequired = [ - 'sys', // Deprecated. - 'internal/v8_prof_polyfill', - 'internal/v8_prof_processor', - - 'internal/test/binding', - - 'internal/bootstrap/environment', - 'internal/bootstrap/primordials', - 'internal/bootstrap/loaders', - 'internal/bootstrap/node', - - 'internal/per_context/setup', - 'internal/per_context/domexception', -]; - -// Skip modules that cannot be required when they are not -// built into the binary. -if (!hasInspector) { - cannotBeRequired.push( - 'inspector', - 'internal/util/inspector', - ); -} -if (!hasTracing) { - cannotBeRequired.push('trace_events'); -} -if (!process.versions.openssl) { - cannotBeRequired.push( - 'crypto', - 'https', - 'http2', - 'tls', - '_tls_common', - '_tls_wrap', - 'internal/crypto/certificate', - 'internal/crypto/cipher', - 'internal/crypto/diffiehellman', - 'internal/crypto/hash', - 'internal/crypto/keygen', - 'internal/crypto/keys', - 'internal/crypto/pbkdf2', - 'internal/crypto/random', - 'internal/crypto/scrypt', - 'internal/crypto/sig', - 'internal/crypto/util', - 'internal/http2/core', - 'internal/http2/compat', - 'internal/policy/manifest', - 'internal/process/policy', - 'internal/streams/lazy_transform', - ); -} - -const cachableBuiltins = []; -for (const id of NativeModule.map.keys()) { - if (id.startsWith('internal/deps') || id.startsWith('internal/main')) { - cannotBeRequired.push(id); - } - if (!cannotBeRequired.includes(id)) { - cachableBuiltins.push(id); - } -} - -module.exports = { - cachableBuiltins, - getCodeCache, - compileFunction, - cannotBeRequired -}; diff --git a/node.gyp b/node.gyp index e3575a854f3..554a8349755 100644 --- a/node.gyp +++ b/node.gyp @@ -27,7 +27,6 @@ 'node_intermediate_lib_type%': 'static_library', 'library_files': [ 'lib/internal/bootstrap/primordials.js', - 'lib/internal/bootstrap/cache.js', 'lib/internal/bootstrap/environment.js', 'lib/internal/bootstrap/loaders.js', 'lib/internal/bootstrap/node.js', diff --git a/src/node_native_module.cc b/src/node_native_module.cc index 41f1588c4da..08c0ab16e3d 100644 --- a/src/node_native_module.cc +++ b/src/node_native_module.cc @@ -36,6 +36,75 @@ using v8::String; using v8::Uint8Array; using v8::Value; +void NativeModuleLoader::InitializeModuleCategories() { + if (module_categories_.is_initialized) { + DCHECK(!module_categories_.can_be_required.empty()); + return; + } + + std::vector prefixes = { +#if !HAVE_OPENSSL + "internal/crypto/", +#endif // !HAVE_OPENSSL + + "internal/bootstrap/", + "internal/per_context/", + "internal/deps/", + "internal/main/" + }; + + module_categories_.cannot_be_required = std::set { +#if !HAVE_INSPECTOR + "inspector", + "internal/util/inspector", +#endif // !HAVE_INSPECTOR + +#if !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT) + "trace_events", +#endif // !NODE_USE_V8_PLATFORM + +#if !HAVE_OPENSSL + "crypto", + "https", + "http2", + "tls", + "_tls_common", + "_tls_wrap", + "internal/http2/core", + "internal/http2/compat", + "internal/policy/manifest", + "internal/process/policy", + "internal/streams/lazy_transform", +#endif // !HAVE_OPENSSL + + "sys", // Deprecated. + "internal/test/binding", + "internal/v8_prof_polyfill", + "internal/v8_prof_processor", + }; + + for (auto const& x : source_) { + const std::string& id = x.first; + for (auto const& prefix : prefixes) { + if (prefix.length() > id.length()) { + continue; + } + if (id.find(prefix) == 0) { + module_categories_.cannot_be_required.emplace(id); + } + } + } + + for (auto const& x : source_) { + const std::string& id = x.first; + if (0 == module_categories_.cannot_be_required.count(id)) { + module_categories_.can_be_required.emplace(id); + } + } + + module_categories_.is_initialized = true; +} + // TODO(joyeecheung): make these more general and put them into util.h Local MapToObject(Local context, const NativeModuleRecordMap& in) { @@ -63,6 +132,39 @@ bool NativeModuleLoader::Exists(const char* id) { return source_.find(id) != source_.end(); } +void NativeModuleLoader::GetModuleCategories( + Local property, const PropertyCallbackInfo& info) { + per_process::native_module_loader.InitializeModuleCategories(); + + Environment* env = Environment::GetCurrent(info); + Isolate* isolate = env->isolate(); + Local context = env->context(); + Local result = Object::New(isolate); + + // Copy from the per-process categories + std::set cannot_be_required = + per_process::native_module_loader.module_categories_.cannot_be_required; + std::set can_be_required = + per_process::native_module_loader.module_categories_.can_be_required; + + if (!env->owns_process_state()) { + can_be_required.erase("trace_events"); + cannot_be_required.insert("trace_events"); + } + + result + ->Set(context, + OneByteString(isolate, "cannotBeRequired"), + ToJsSet(context, cannot_be_required)) + .FromJust(); + result + ->Set(context, + OneByteString(isolate, "canBeRequired"), + ToJsSet(context, can_be_required)) + .FromJust(); + info.GetReturnValue().Set(result); +} + void NativeModuleLoader::GetCacheUsage( const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -298,6 +400,18 @@ void NativeModuleLoader::Initialize(Local target, SideEffectType::kHasNoSideEffect) .FromJust()); + CHECK(target + ->SetAccessor( + env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "moduleCategories"), + GetModuleCategories, + nullptr, + env->as_callback_data(), + DEFAULT, + None, + SideEffectType::kHasNoSideEffect) + .FromJust()); + env->SetMethod( target, "getCacheUsage", NativeModuleLoader::GetCacheUsage); env->SetMethod( diff --git a/src/node_native_module.h b/src/node_native_module.h index be1fc92a767..587c59022af 100644 --- a/src/node_native_module.h +++ b/src/node_native_module.h @@ -54,6 +54,9 @@ class NativeModuleLoader { Environment* optional_env); private: + static void GetModuleCategories( + v8::Local property, + const v8::PropertyCallbackInfo& info); static void GetCacheUsage(const v8::FunctionCallbackInfo& args); // Passing ids of builtin module source code into JS land as // internalBinding('native_module').moduleIds @@ -84,6 +87,15 @@ class NativeModuleLoader { static v8::MaybeLocal CompileAsModule(Environment* env, const char* id); + void InitializeModuleCategories(); + struct ModuleCategories { + bool is_initialized = false; + std::set can_be_required; + std::set cannot_be_required; + }; + + ModuleCategories module_categories_; + NativeModuleRecordMap source_; NativeModuleCacheMap code_cache_; UnionBytes config_; diff --git a/test/code-cache/test-code-cache.js b/test/code-cache/test-code-cache.js index 6232f1ae61b..e179be7aee3 100644 --- a/test/code-cache/test-code-cache.js +++ b/test/code-cache/test-code-cache.js @@ -7,22 +7,15 @@ const { isMainThread } = require('../common'); const assert = require('assert'); -const { - cachableBuiltins, - cannotBeRequired -} = require('internal/bootstrap/cache'); - const { internalBinding } = require('internal/test/binding'); const { - getCacheUsage + getCacheUsage, + moduleCategories: { canBeRequired, cannotBeRequired } } = internalBinding('native_module'); -for (const key of cachableBuiltins) { - if (!isMainThread && key === 'trace_events') { - continue; // Cannot load trace_events in workers - } +for (const key of canBeRequired) { require(key); } @@ -60,7 +53,7 @@ if (process.config.variables.node_code_cache_path === undefined) { ); for (const key of loadedModules) { - if (cannotBeRequired.includes(key)) { + if (cannotBeRequired.has(key)) { assert(compiledWithoutCache.has(key), `"${key}" should've been compiled without code cache`); } else { diff --git a/tools/generate_code_cache.js b/tools/generate_code_cache.js index a434f640c29..f324c3d779c 100644 --- a/tools/generate_code_cache.js +++ b/tools/generate_code_cache.js @@ -7,11 +7,12 @@ // compiled into the binary using the `--code-cache-path` option // of `configure`. +const { internalBinding } = require('internal/test/binding'); const { + moduleCategories: { canBeRequired }, getCodeCache, compileFunction, - cachableBuiltins -} = require('internal/bootstrap/cache'); +} = internalBinding('native_module'); const { types: { @@ -85,7 +86,9 @@ function lexical(a, b) { return 0; } -for (const key of cachableBuiltins.sort(lexical)) { +// TODO(joyeecheung): support non-modules that require different +// parameters in the wrapper. +for (const key of [...canBeRequired].sort(lexical)) { compileFunction(key); // compile it const cachedData = getCodeCache(key); if (!isUint8Array(cachedData)) {