diff --git a/node.gyp b/node.gyp index 9d6d0dd7cbb695..9088c4ce9612f7 100644 --- a/node.gyp +++ b/node.gyp @@ -463,6 +463,7 @@ 'src/node_messaging.cc', 'src/node_metadata.cc', 'src/node_native_module.cc', + 'src/node_native_module_env.cc', 'src/node_options.cc', 'src/node_os.cc', 'src/node_perf.cc', @@ -543,6 +544,7 @@ 'src/node_metadata.h', 'src/node_mutex.h', 'src/node_native_module.h', + 'src/node_native_module_env.h', 'src/node_object_wrap.h', 'src/node_options.h', 'src/node_options-inl.h', diff --git a/src/api/environment.cc b/src/api/environment.cc index 62eca337e15627..fb7d7003bba0b3 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -3,7 +3,7 @@ #include "node_context_data.h" #include "node_errors.h" #include "node_internals.h" -#include "node_native_module.h" +#include "node_native_module_env.h" #include "node_platform.h" #include "node_process.h" #include "node_v8_platform-inl.h" @@ -351,7 +351,7 @@ Local NewContext(Isolate* isolate, }; Local arguments[] = {context->Global(), exports}; MaybeLocal maybe_fn = - per_process::native_module_loader.LookupAndCompile( + native_module::NativeModuleEnv::LookupAndCompile( context, *module, ¶meters, nullptr); if (maybe_fn.IsEmpty()) { return Local(); diff --git a/src/node.cc b/src/node.cc index a5a0cf33e1d548..09817b8a4c1d0d 100644 --- a/src/node.cc +++ b/src/node.cc @@ -31,7 +31,7 @@ #include "node_errors.h" #include "node_internals.h" #include "node_metadata.h" -#include "node_native_module.h" +#include "node_native_module_env.h" #include "node_options-inl.h" #include "node_perf.h" #include "node_platform.h" @@ -118,8 +118,10 @@ namespace node { +using native_module::NativeModuleEnv; using options_parser::kAllowedInEnvironment; using options_parser::kDisallowedInEnvironment; + using v8::Array; using v8::Boolean; using v8::Context; @@ -207,8 +209,7 @@ MaybeLocal ExecuteBootstrapper(Environment* env, std::vector>* arguments) { EscapableHandleScope scope(env->isolate()); MaybeLocal maybe_fn = - per_process::native_module_loader.LookupAndCompile( - env->context(), id, parameters, env); + NativeModuleEnv::LookupAndCompile(env->context(), id, parameters, env); if (maybe_fn.IsEmpty()) { return MaybeLocal(); @@ -401,7 +402,7 @@ MaybeLocal StartMainThreadExecution(Environment* env) { // To allow people to extend Node in different ways, this hook allows // one to drop a file lib/_third_party_main.js into the build // directory which will be executed instead of Node's normal loading. - if (per_process::native_module_loader.Exists("_third_party_main")) { + if (NativeModuleEnv::Exists("_third_party_main")) { return StartExecution(env, "internal/main/run_third_party_main"); } @@ -724,6 +725,8 @@ int InitializeNodeWithArgs(std::vector* argv, per_process::metadata.versions.InitializeIntlVersions(); #endif + NativeModuleEnv::InitializeCodeCache(); + // We should set node_is_initialized here instead of in node::Start, // otherwise embedders using node::Init to initialize everything will not be // able to set it and native modules will not load for them. diff --git a/src/node_binding.cc b/src/node_binding.cc index ffa9ffdfdbfb0c..9a9bfa70a8a378 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -1,8 +1,8 @@ #include "node_binding.h" +#include #include "env-inl.h" -#include "node_native_module.h" +#include "node_native_module_env.h" #include "util.h" -#include #if HAVE_OPENSSL #define NODE_BUILTIN_OPENSSL_MODULES(V) V(crypto) V(tls_wrap) @@ -593,13 +593,13 @@ void GetInternalBinding(const FunctionCallbackInfo& args) { exports->SetPrototype(env->context(), Null(env->isolate())).FromJust()); DefineConstants(env->isolate(), exports); } else if (!strcmp(*module_v, "natives")) { - exports = per_process::native_module_loader.GetSourceObject(env->context()); + exports = native_module::NativeModuleEnv::GetSourceObject(env->context()); // Legacy feature: process.binding('natives').config contains stringified // config.gypi CHECK(exports ->Set(env->context(), env->config_string(), - per_process::native_module_loader.GetConfigString( + native_module::NativeModuleEnv::GetConfigString( env->isolate())) .FromJust()); } else { diff --git a/src/node_code_cache_stub.cc b/src/node_code_cache_stub.cc index 4fffa8e0c29557..021910e8298b63 100644 --- a/src/node_code_cache_stub.cc +++ b/src/node_code_cache_stub.cc @@ -1,5 +1,5 @@ -#include "node_native_module.h" +#include "node_native_module_env.h" // This is supposed to be generated by tools/generate_code_cache.js // The stub here is used when configure is run without `--code-cache-path` @@ -8,8 +8,8 @@ namespace node { namespace native_module { // The generated source code would insert pairs -// into native_module_loader.code_cache_. -void NativeModuleLoader::LoadCodeCache() {} +// into NativeModuleLoader::instance.code_cache_. +void NativeModuleEnv::InitializeCodeCache() {} } // namespace native_module } // namespace node diff --git a/src/node_native_module.cc b/src/node_native_module.cc index 6462f39ee14b9a..814adb620dcf8a 100644 --- a/src/node_native_module.cc +++ b/src/node_native_module.cc @@ -1,40 +1,60 @@ #include "node_native_module.h" -#include "node_errors.h" +#include "util-inl.h" namespace node { - -namespace per_process { -native_module::NativeModuleLoader native_module_loader; -} // namespace per_process - namespace native_module { -using v8::Array; -using v8::ArrayBuffer; using v8::Context; -using v8::DEFAULT; using v8::EscapableHandleScope; using v8::Function; -using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Integer; -using v8::IntegrityLevel; using v8::Isolate; using v8::Local; using v8::Maybe; using v8::MaybeLocal; -using v8::Name; -using v8::None; using v8::Object; -using v8::PropertyCallbackInfo; using v8::Script; using v8::ScriptCompiler; using v8::ScriptOrigin; -using v8::Set; -using v8::SideEffectType; using v8::String; -using v8::Uint8Array; -using v8::Value; + +NativeModuleLoader NativeModuleLoader::instance_; + +NativeModuleLoader::NativeModuleLoader() : config_(GetConfig()) { + LoadJavaScriptSource(); +} + +NativeModuleLoader* NativeModuleLoader::GetInstance() { + return &instance_; +} + +bool NativeModuleLoader::Exists(const char* id) { + return source_.find(id) != source_.end(); +} + +Local NativeModuleLoader::GetSourceObject(Local context) { + Isolate* isolate = context->GetIsolate(); + Local out = Object::New(isolate); + for (auto const& x : source_) { + Local key = OneByteString(isolate, x.first.c_str(), x.first.size()); + out->Set(context, key, x.second.ToStringChecked(isolate)).FromJust(); + } + return out; +} + +Local NativeModuleLoader::GetConfigString(Isolate* isolate) { + return config_.ToStringChecked(isolate); +} + +std::vector NativeModuleLoader::GetModuleIds() { + std::vector ids; + ids.reserve(source_.size()); + for (auto const& x : source_) { + ids.emplace_back(x.first); + } + return ids; +} void NativeModuleLoader::InitializeModuleCategories() { if (module_categories_.is_initialized) { @@ -105,182 +125,52 @@ void NativeModuleLoader::InitializeModuleCategories() { module_categories_.is_initialized = true; } -// TODO(joyeecheung): make these more general and put them into util.h -Local MapToObject(Local context, - const NativeModuleRecordMap& in) { - Isolate* isolate = context->GetIsolate(); - Local out = Object::New(isolate); - for (auto const& x : in) { - Local key = OneByteString(isolate, x.first.c_str(), x.first.size()); - out->Set(context, key, x.second.ToStringChecked(isolate)).Check(); - } - return out; +const std::set& NativeModuleLoader::GetCannotBeRequired() { + InitializeModuleCategories(); + return module_categories_.cannot_be_required; } -Local ToJsSet(Local context, - const std::set& in) { - Isolate* isolate = context->GetIsolate(); - Local out = Set::New(isolate); - for (auto const& x : in) { - out->Add(context, OneByteString(isolate, x.c_str(), x.size())) - .ToLocalChecked(); - } - return out; +const std::set& NativeModuleLoader::GetCanBeRequired() { + InitializeModuleCategories(); + return module_categories_.can_be_required; } -bool NativeModuleLoader::Exists(const char* id) { - return source_.find(id) != source_.end(); +bool NativeModuleLoader::CanBeRequired(const char* id) { + return GetCanBeRequired().count(id) == 1; } -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)) - .Check(); - result - ->Set(context, - OneByteString(isolate, "canBeRequired"), - ToJsSet(context, can_be_required)) - .Check(); - info.GetReturnValue().Set(result); -} - -void NativeModuleLoader::GetCacheUsage( - const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = env->isolate(); - Local context = env->context(); - Local result = Object::New(isolate); - result - ->Set(env->context(), - OneByteString(isolate, "compiledWithCache"), - ToJsSet(context, env->native_modules_with_cache)) - .Check(); - result - ->Set(env->context(), - OneByteString(isolate, "compiledWithoutCache"), - ToJsSet(context, env->native_modules_without_cache)) - .Check(); - args.GetReturnValue().Set(result); -} - -void NativeModuleLoader::ModuleIdsGetter( - Local property, const PropertyCallbackInfo& info) { - Isolate* isolate = info.GetIsolate(); - - const NativeModuleRecordMap& source_ = - per_process::native_module_loader.source_; - std::vector> ids; - ids.reserve(source_.size()); - - for (auto const& x : source_) { - ids.emplace_back(OneByteString(isolate, x.first.c_str(), x.first.size())); - } - - info.GetReturnValue().Set(Array::New(isolate, ids.data(), ids.size())); -} - -void NativeModuleLoader::ConfigStringGetter( - Local property, const PropertyCallbackInfo& info) { - info.GetReturnValue().Set( - per_process::native_module_loader.GetConfigString(info.GetIsolate())); -} - -Local NativeModuleLoader::GetSourceObject( - Local context) const { - return MapToObject(context, source_); -} - -Local NativeModuleLoader::GetConfigString(Isolate* isolate) const { - return config_.ToStringChecked(isolate); -} - -NativeModuleLoader::NativeModuleLoader() : config_(GetConfig()) { - LoadJavaScriptSource(); - LoadCodeCache(); +bool NativeModuleLoader::CannotBeRequired(const char* id) { + return GetCannotBeRequired().count(id) == 1; } -// This is supposed to be run only by the main thread in -// tools/generate_code_cache.js -void NativeModuleLoader::GetCodeCache(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = env->isolate(); - CHECK(env->is_main_thread()); - - CHECK(args[0]->IsString()); - node::Utf8Value id_v(isolate, args[0].As()); - const char* id = *id_v; - - const NativeModuleLoader& loader = per_process::native_module_loader; - MaybeLocal ret = loader.GetCodeCache(isolate, id); - if (!ret.IsEmpty()) { - args.GetReturnValue().Set(ret.ToLocalChecked()); - } +NativeModuleCacheMap* NativeModuleLoader::code_cache() { + return &code_cache_; } -// This is supposed to be run only by the main thread in -// tools/generate_code_cache.js -MaybeLocal NativeModuleLoader::GetCodeCache(Isolate* isolate, - const char* id) const { - EscapableHandleScope scope(isolate); +ScriptCompiler::CachedData* NativeModuleLoader::GetCodeCache( + const char* id) const { Mutex::ScopedLock lock(code_cache_mutex_); - - ScriptCompiler::CachedData* cached_data = nullptr; const auto it = code_cache_.find(id); if (it == code_cache_.end()) { // The module has not been compiled before. - return MaybeLocal(); - } - - cached_data = it->second.get(); - - Local buf = ArrayBuffer::New(isolate, cached_data->length); - memcpy(buf->GetContents().Data(), cached_data->data, cached_data->length); - return scope.Escape(Uint8Array::New(buf, 0, cached_data->length)); -} - -void NativeModuleLoader::CompileFunction( - const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsString()); - node::Utf8Value id(env->isolate(), args[0].As()); - - MaybeLocal result = CompileAsModule(env, *id); - if (!result.IsEmpty()) { - args.GetReturnValue().Set(result.ToLocalChecked()); + return nullptr; } + return it->second.get(); } -MaybeLocal NativeModuleLoader::CompileAsModule(Environment* env, - const char* id) { - std::vector> parameters = {env->exports_string(), - env->require_string(), - env->module_string(), - env->process_string(), - env->internal_binding_string(), - env->primordials_string()}; - return per_process::native_module_loader.LookupAndCompile( - env->context(), id, ¶meters, env); +MaybeLocal NativeModuleLoader::CompileAsModule( + Local context, + const char* id, + NativeModuleLoader::Result* result) { + Isolate* isolate = context->GetIsolate(); + std::vector> parameters = { + FIXED_ONE_BYTE_STRING(isolate, "exports"), + FIXED_ONE_BYTE_STRING(isolate, "require"), + FIXED_ONE_BYTE_STRING(isolate, "module"), + FIXED_ONE_BYTE_STRING(isolate, "process"), + FIXED_ONE_BYTE_STRING(isolate, "internalBinding"), + FIXED_ONE_BYTE_STRING(isolate, "primordials")}; + return LookupAndCompile(context, id, ¶meters, result); } // Returns Local of the compiled module if return_code_cache @@ -290,7 +180,7 @@ MaybeLocal NativeModuleLoader::LookupAndCompile( Local context, const char* id, std::vector>* parameters, - Environment* optional_env) { + NativeModuleLoader::Result* result) { Isolate* isolate = context->GetIsolate(); EscapableHandleScope scope(isolate); @@ -317,9 +207,9 @@ MaybeLocal NativeModuleLoader::LookupAndCompile( } } - const bool use_cache = cached_data != nullptr; + const bool has_cache = cached_data != nullptr; ScriptCompiler::CompileOptions options = - use_cache ? ScriptCompiler::kConsumeCodeCache + has_cache ? ScriptCompiler::kConsumeCodeCache : ScriptCompiler::kEagerCompile; ScriptCompiler::Source script_source(source, origin, cached_data); @@ -346,22 +236,10 @@ MaybeLocal NativeModuleLoader::LookupAndCompile( // it only starts after the Environment is created, so the per_context.js // will never be in any of these two sets, but the two sets are only for // testing anyway. - if (use_cache) { - if (optional_env != nullptr) { - // This could happen when Node is run with any v8 flag, but - // the cache is not generated with one - if (script_source.GetCachedData()->rejected) { - optional_env->native_modules_without_cache.insert(id); - } else { - optional_env->native_modules_with_cache.insert(id); - } - } - } else { - if (optional_env != nullptr) { - optional_env->native_modules_without_cache.insert(id); - } - } + *result = (has_cache && !script_source.GetCachedData()->rejected) + ? Result::kWithCache + : Result::kWithoutCache; // Generate new cache for next compilation std::unique_ptr new_cached_data( ScriptCompiler::CreateCodeCacheForFunction(fun)); @@ -373,56 +251,5 @@ MaybeLocal NativeModuleLoader::LookupAndCompile( return scope.Escape(fun); } -void NativeModuleLoader::Initialize(Local target, - Local unused, - Local context, - void* priv) { - Environment* env = Environment::GetCurrent(context); - - CHECK(target - ->SetAccessor(env->context(), - env->config_string(), - ConfigStringGetter, - nullptr, - MaybeLocal(), - DEFAULT, - None, - SideEffectType::kHasNoSideEffect) - .FromJust()); - CHECK(target - ->SetAccessor(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "moduleIds"), - ModuleIdsGetter, - nullptr, - MaybeLocal(), - DEFAULT, - None, - 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( - target, "compileFunction", NativeModuleLoader::CompileFunction); - env->SetMethod(target, "getCodeCache", NativeModuleLoader::GetCodeCache); - // internalBinding('native_module') should be frozen - target->SetIntegrityLevel(context, IntegrityLevel::kFrozen).Check(); -} - } // namespace native_module } // namespace node - -NODE_MODULE_CONTEXT_AWARE_INTERNAL( - native_module, node::native_module::NativeModuleLoader::Initialize) diff --git a/src/node_native_module.h b/src/node_native_module.h index 587c59022afc56..bbd4d89c4001cd 100644 --- a/src/node_native_module.h +++ b/src/node_native_module.h @@ -4,9 +4,9 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include +#include #include #include -#include "env.h" #include "node_mutex.h" #include "node_union_bytes.h" #include "v8.h" @@ -23,79 +23,57 @@ using NativeModuleCacheMap = // handles compilation and caching of builtin modules (NativeModule) // and bootstrappers, whose source are bundled into the binary // as static data. -// This class should not depend on a particular isolate, context, or -// environment. Rather it should take them as arguments when necessary. -// The instances of this class are per-process. +// This class should not depend on any Environment, or depend on access to +// the its own singleton - that should be encapsulated in NativeModuleEnv +// instead. class NativeModuleLoader { - public: + private: + // Only allow access from friends. + friend class NativeModuleEnv; + friend class CodeCacheBuilder; + NativeModuleLoader(); - // TODO(joyeecheung): maybe we should make this a singleton, instead of - // putting it in per_process. NativeModuleLoader(const NativeModuleLoader&) = delete; NativeModuleLoader& operator=(const NativeModuleLoader&) = delete; - - static void Initialize(v8::Local target, - v8::Local unused, - v8::Local context, - void* priv); - v8::Local GetSourceObject(v8::Local context) const; - // Returns config.gypi as a JSON string - v8::Local GetConfigString(v8::Isolate* isolate) const; - - bool Exists(const char* id); - - // For bootstrappers optional_env may be a nullptr. - // If an exception is encountered (e.g. source code contains - // syntax error), the returned value is empty. - v8::MaybeLocal LookupAndCompile( - v8::Local context, - const char* id, - std::vector>* parameters, - 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 - static void ModuleIdsGetter(v8::Local property, - const v8::PropertyCallbackInfo& info); - // Passing config.gypi into JS land as internalBinding('native_module').config - static void ConfigStringGetter( - v8::Local property, - const v8::PropertyCallbackInfo& info); - // Get code cache for a specific native module - static void GetCodeCache(const v8::FunctionCallbackInfo& args); - v8::MaybeLocal GetCodeCache(v8::Isolate* isolate, - const char* id) const; - // Compile a specific native module as a function - static void CompileFunction(const v8::FunctionCallbackInfo& args); + static NativeModuleLoader* GetInstance(); // Generated by tools/js2c.py as node_javascript.cc void LoadJavaScriptSource(); // Loads data into source_ UnionBytes GetConfig(); // Return data for config.gypi - // Generated by tools/generate_code_cache.js as node_code_cache.cc when - // the build is configured with --code-cache-path=.... They are noops - // in node_code_cache_stub.cc - void LoadCodeCache(); // Loads data into code_cache_ - - // Compile a script as a NativeModule that can be loaded via - // NativeModule.p.require in JS land. - static v8::MaybeLocal CompileAsModule(Environment* env, - const char* id); + bool Exists(const char* id); + v8::Local GetSourceObject(v8::Local context); + v8::Local GetConfigString(v8::Isolate* isolate); + std::vector GetModuleIds(); - void InitializeModuleCategories(); struct ModuleCategories { bool is_initialized = false; std::set can_be_required; std::set cannot_be_required; }; + void InitializeModuleCategories(); + const std::set& GetCannotBeRequired(); + const std::set& GetCanBeRequired(); - ModuleCategories module_categories_; + bool CanBeRequired(const char* id); + bool CannotBeRequired(const char* id); + + NativeModuleCacheMap* code_cache(); + v8::ScriptCompiler::CachedData* GetCodeCache(const char* id) const; + enum class Result { kWithCache, kWithoutCache }; + // If an exception is encountered (e.g. source code contains + // syntax error), the returned value is empty. + v8::MaybeLocal LookupAndCompile( + v8::Local context, + const char* id, + std::vector>* parameters, + Result* result); + v8::MaybeLocal CompileAsModule(v8::Local context, + const char* id, + Result* result); + static NativeModuleLoader instance_; + ModuleCategories module_categories_; NativeModuleRecordMap source_; NativeModuleCacheMap code_cache_; UnionBytes config_; @@ -103,13 +81,8 @@ class NativeModuleLoader { // Used to synchronize access to the code cache map Mutex code_cache_mutex_; }; - } // namespace native_module -namespace per_process { -extern native_module::NativeModuleLoader native_module_loader; -} // namespace per_process - } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/node_native_module_env.cc b/src/node_native_module_env.cc new file mode 100644 index 00000000000000..fc48436dc14d44 --- /dev/null +++ b/src/node_native_module_env.cc @@ -0,0 +1,229 @@ +#include "node_native_module_env.h" +#include "env-inl.h" + +namespace node { +namespace native_module { + +using v8::ArrayBuffer; +using v8::Context; +using v8::DEFAULT; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::IntegrityLevel; +using v8::Isolate; +using v8::Local; +using v8::Maybe; +using v8::MaybeLocal; +using v8::Name; +using v8::None; +using v8::Object; +using v8::PropertyCallbackInfo; +using v8::ScriptCompiler; +using v8::Set; +using v8::SideEffectType; +using v8::String; +using v8::Uint8Array; +using v8::Value; + +// TODO(joyeecheung): make these more general and put them into util.h +Local ToJsSet(Local context, const std::set& in) { + Isolate* isolate = context->GetIsolate(); + Local out = Set::New(isolate); + for (auto const& x : in) { + out->Add(context, OneByteString(isolate, x.c_str(), x.size())) + .ToLocalChecked(); + } + return out; +} + +bool NativeModuleEnv::Exists(const char* id) { + return NativeModuleLoader::GetInstance()->Exists(id); +} + +Local NativeModuleEnv::GetSourceObject(Local context) { + return NativeModuleLoader::GetInstance()->GetSourceObject(context); +} + +Local NativeModuleEnv::GetConfigString(Isolate* isolate) { + return NativeModuleLoader::GetInstance()->GetConfigString(isolate); +} + +void NativeModuleEnv::GetModuleCategories( + Local property, const PropertyCallbackInfo& info) { + 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 = + NativeModuleLoader::GetInstance()->GetCannotBeRequired(); + std::set can_be_required = + NativeModuleLoader::GetInstance()->GetCanBeRequired(); + + 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 NativeModuleEnv::GetCacheUsage(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Isolate* isolate = env->isolate(); + Local context = env->context(); + Local result = Object::New(isolate); + result + ->Set(env->context(), + OneByteString(isolate, "compiledWithCache"), + ToJsSet(context, env->native_modules_with_cache)) + .FromJust(); + result + ->Set(env->context(), + OneByteString(isolate, "compiledWithoutCache"), + ToJsSet(context, env->native_modules_without_cache)) + .FromJust(); + args.GetReturnValue().Set(result); +} + +void NativeModuleEnv::ModuleIdsGetter(Local property, + const PropertyCallbackInfo& info) { + Isolate* isolate = info.GetIsolate(); + + std::vector ids = + NativeModuleLoader::GetInstance()->GetModuleIds(); + info.GetReturnValue().Set( + ToV8Value(isolate->GetCurrentContext(), ids).ToLocalChecked()); +} + +void NativeModuleEnv::ConfigStringGetter( + Local property, const PropertyCallbackInfo& info) { + info.GetReturnValue().Set(GetConfigString(info.GetIsolate())); +} + +void NativeModuleEnv::RecordResult(const char* id, + NativeModuleLoader::Result result, + Environment* env) { + if (result == NativeModuleLoader::Result::kWithCache) { + env->native_modules_with_cache.insert(id); + } else { + env->native_modules_without_cache.insert(id); + } +} +void NativeModuleEnv::CompileFunction(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsString()); + node::Utf8Value id_v(env->isolate(), args[0].As()); + const char* id = *id_v; + NativeModuleLoader::Result result; + MaybeLocal maybe = + NativeModuleLoader::GetInstance()->CompileAsModule( + env->context(), id, &result); + RecordResult(id, result, env); + if (!maybe.IsEmpty()) { + args.GetReturnValue().Set(maybe.ToLocalChecked()); + } +} + +// Returns Local of the compiled module if return_code_cache +// is false (we are only compiling the function). +// Otherwise return a Local containing the cache. +MaybeLocal NativeModuleEnv::LookupAndCompile( + Local context, + const char* id, + std::vector>* parameters, + Environment* optional_env) { + NativeModuleLoader::Result result; + MaybeLocal maybe = + NativeModuleLoader::GetInstance()->LookupAndCompile( + context, id, parameters, &result); + if (optional_env != nullptr) { + RecordResult(id, result, optional_env); + } + return maybe; +} + +// This is supposed to be run only by the main thread in +// tools/generate_code_cache.js +void NativeModuleEnv::GetCodeCache(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Isolate* isolate = env->isolate(); + CHECK(env->is_main_thread()); + + CHECK(args[0]->IsString()); + node::Utf8Value id_v(isolate, args[0].As()); + const char* id = *id_v; + + ScriptCompiler::CachedData* cached_data = + NativeModuleLoader::GetInstance()->GetCodeCache(id); + if (cached_data != nullptr) { + Local buf = ArrayBuffer::New(isolate, cached_data->length); + memcpy(buf->GetContents().Data(), cached_data->data, cached_data->length); + args.GetReturnValue().Set(Uint8Array::New(buf, 0, cached_data->length)); + } +} + +// TODO(joyeecheung): It is somewhat confusing that Class::Initialize +// is used to initilaize to the binding, but it is the current convention. +// Rename this across the code base to something that makes more sense. +void NativeModuleEnv::Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + + target + ->SetAccessor(env->context(), + env->config_string(), + ConfigStringGetter, + nullptr, + MaybeLocal(), + DEFAULT, + None, + SideEffectType::kHasNoSideEffect) + .Check(); + target + ->SetAccessor(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "moduleIds"), + ModuleIdsGetter, + nullptr, + MaybeLocal(), + DEFAULT, + None, + SideEffectType::kHasNoSideEffect) + .Check(); + + target + ->SetAccessor(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "moduleCategories"), + GetModuleCategories, + nullptr, + env->as_callback_data(), + DEFAULT, + None, + SideEffectType::kHasNoSideEffect) + .Check(); + + env->SetMethod(target, "getCacheUsage", NativeModuleEnv::GetCacheUsage); + env->SetMethod(target, "getCodeCache", NativeModuleEnv::GetCodeCache); + env->SetMethod(target, "compileFunction", NativeModuleEnv::CompileFunction); + // internalBinding('native_module') should be frozen + target->SetIntegrityLevel(context, IntegrityLevel::kFrozen).FromJust(); +} + +} // namespace native_module +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL( + native_module, node::native_module::NativeModuleEnv::Initialize) diff --git a/src/node_native_module_env.h b/src/node_native_module_env.h new file mode 100644 index 00000000000000..2b1fabf89b9776 --- /dev/null +++ b/src/node_native_module_env.h @@ -0,0 +1,64 @@ +#ifndef SRC_NODE_NATIVE_MODULE_ENV_H_ +#define SRC_NODE_NATIVE_MODULE_ENV_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_native_module.h" + +namespace node { +class Environment; + +namespace native_module { + +class NativeModuleEnv { + public: + static void Initialize(v8::Local target, + v8::Local unused, + v8::Local context, + void* priv); + + static v8::MaybeLocal LookupAndCompile( + v8::Local context, + const char* id, + std::vector>* parameters, + Environment* optional_env); + + static v8::Local GetSourceObject(v8::Local context); + // Returns config.gypi as a JSON string + static v8::Local GetConfigString(v8::Isolate* isolate); + static bool Exists(const char* id); + + // Loads data into NativeModuleLoader::.instance.code_cache_ + // Generated by mkcodecache as node_code_cache.cc when + // the build is configured with --code-cache-path=.... They are noops + // in node_code_cache_stub.cc + static void InitializeCodeCache(); + + private: + static void RecordResult(const char* id, + NativeModuleLoader::Result result, + Environment* env); + 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 + static void ModuleIdsGetter(v8::Local property, + const v8::PropertyCallbackInfo& info); + // Passing config.gypi into JS land as internalBinding('native_module').config + static void ConfigStringGetter( + v8::Local property, + const v8::PropertyCallbackInfo& info); + // Compile a specific native module as a function + static void CompileFunction(const v8::FunctionCallbackInfo& args); + static void GetCodeCache(const v8::FunctionCallbackInfo& args); +}; + +} // namespace native_module + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_NATIVE_MODULE_ENV_H_ diff --git a/src/node_union_bytes.h b/src/node_union_bytes.h index 0034f184cc38ca..996d9880a48224 100644 --- a/src/node_union_bytes.h +++ b/src/node_union_bytes.h @@ -7,7 +7,6 @@ // A union of const uint8_t* or const uint16_t* data that can be // turned into external v8::String when given an isolate. -#include "env.h" #include "v8.h" namespace node { diff --git a/test/code-cache/test-code-cache-generator.js b/test/code-cache/test-code-cache-generator.js index 78a2b1c9a639a0..a117a2e6a6ea94 100644 --- a/test/code-cache/test-code-cache-generator.js +++ b/test/code-cache/test-code-cache-generator.js @@ -30,9 +30,9 @@ if (child.status !== 0) { } // Verifies that: -// - node::LoadCodeCache() +// - NativeModuleEnv::InitializeCodeCache() // are defined in the generated code. -// See src/node_native_module.h for explanations. +// See src/node_native_module_env.h for explanations. const rl = readline.createInterface({ input: fs.createReadStream(dest), @@ -42,7 +42,7 @@ const rl = readline.createInterface({ let hasCacheDef = false; rl.on('line', common.mustCallAtLeast((line) => { - if (line.includes('LoadCodeCache(')) { + if (line.includes('InitializeCodeCache(')) { hasCacheDef = true; } }, 2)); diff --git a/tools/generate_code_cache.js b/tools/generate_code_cache.js index f324c3d779c191..8afbc14a9bce4d 100644 --- a/tools/generate_code_cache.js +++ b/tools/generate_code_cache.js @@ -63,7 +63,7 @@ function getInitalizer(key, cache) { `${defName}, static_cast(arraysize(${defName})), ` + 'policy)'; const initializer = - 'code_cache_.emplace(\n' + + 'code_cache->emplace(\n' + ` "${key}",\n` + ` ${dataDef}\n` + ');'; @@ -107,8 +107,7 @@ for (const key of [...canBeRequired].sort(lexical)) { `, total = ${formatSize(totalCacheSize)}`); } -const result = `#include "node_native_module.h" -#include "node_internals.h" +const result = `#include "node_native_module_env.h" // This file is generated by tools/generate_code_cache.js // and is used when configure is run with \`--code-cache-path\` @@ -117,7 +116,13 @@ namespace node { namespace native_module { ${cacheDefinitions.join('\n\n')} -void NativeModuleLoader::LoadCodeCache() { +void NativeModuleEnv::InitializeCodeCache() { + NativeModuleCacheMap* code_cache = + NativeModuleLoader::GetInstance()->code_cache(); + if (!code_cache->empty()) { + return; + } + auto policy = v8::ScriptCompiler::CachedData::BufferPolicy::BufferNotOwned; ${cacheInitializers.join('\n ')} }