diff options
author | Joyee Cheung <joyeec9h3@gmail.com> | 2019-04-10 00:08:48 +0300 |
---|---|---|
committer | Joyee Cheung <joyeec9h3@gmail.com> | 2019-04-13 12:24:51 +0300 |
commit | dfd7e994258a36f3941c74295a8c037cb4850418 (patch) | |
tree | 03eaa022a99159912c97773a1b41952f3ee404b1 /src/node_native_module.cc | |
parent | 9b6b567bc4dd8f40bad12528eebf12dac8a8027f (diff) |
src: make a Environment-independent proxy class for NativeModuleLoader
This patch splits `NativeModuleLoader` into two parts - a singleton
that only relies on v8 and `node::Mutex` and a proxy class for
the singleton (`NativeModuleEnv`) that provides limited access to
the singleton as well as C++ bindings for the Node.js binary.
`NativeModuleLoader` is then no longer aware of `Environment`.
PR-URL: https://github.com/nodejs/node/pull/27160
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
Diffstat (limited to 'src/node_native_module.cc')
-rw-r--r-- | src/node_native_module.cc | 319 |
1 files changed, 73 insertions, 246 deletions
diff --git a/src/node_native_module.cc b/src/node_native_module.cc index 6462f39ee14..814adb620dc 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<Object> NativeModuleLoader::GetSourceObject(Local<Context> context) { + Isolate* isolate = context->GetIsolate(); + Local<Object> out = Object::New(isolate); + for (auto const& x : source_) { + Local<String> key = OneByteString(isolate, x.first.c_str(), x.first.size()); + out->Set(context, key, x.second.ToStringChecked(isolate)).FromJust(); + } + return out; +} + +Local<String> NativeModuleLoader::GetConfigString(Isolate* isolate) { + return config_.ToStringChecked(isolate); +} + +std::vector<std::string> NativeModuleLoader::GetModuleIds() { + std::vector<std::string> 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<Object> MapToObject(Local<Context> context, - const NativeModuleRecordMap& in) { - Isolate* isolate = context->GetIsolate(); - Local<Object> out = Object::New(isolate); - for (auto const& x : in) { - Local<String> key = OneByteString(isolate, x.first.c_str(), x.first.size()); - out->Set(context, key, x.second.ToStringChecked(isolate)).Check(); - } - return out; +const std::set<std::string>& NativeModuleLoader::GetCannotBeRequired() { + InitializeModuleCategories(); + return module_categories_.cannot_be_required; } -Local<Set> ToJsSet(Local<Context> context, - const std::set<std::string>& in) { - Isolate* isolate = context->GetIsolate(); - Local<Set> 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<std::string>& 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<Name> property, const PropertyCallbackInfo<Value>& info) { - per_process::native_module_loader.InitializeModuleCategories(); - - Environment* env = Environment::GetCurrent(info); - Isolate* isolate = env->isolate(); - Local<Context> context = env->context(); - Local<Object> result = Object::New(isolate); - - // Copy from the per-process categories - std::set<std::string> cannot_be_required = - per_process::native_module_loader.module_categories_.cannot_be_required; - std::set<std::string> 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<Value>& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = env->isolate(); - Local<Context> context = env->context(); - Local<Object> 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<Name> property, const PropertyCallbackInfo<Value>& info) { - Isolate* isolate = info.GetIsolate(); - - const NativeModuleRecordMap& source_ = - per_process::native_module_loader.source_; - std::vector<Local<Value>> 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<Name> property, const PropertyCallbackInfo<Value>& info) { - info.GetReturnValue().Set( - per_process::native_module_loader.GetConfigString(info.GetIsolate())); -} - -Local<Object> NativeModuleLoader::GetSourceObject( - Local<Context> context) const { - return MapToObject(context, source_); -} - -Local<String> 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<Value>& 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<String>()); - const char* id = *id_v; - - const NativeModuleLoader& loader = per_process::native_module_loader; - MaybeLocal<Uint8Array> 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<Uint8Array> 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<Uint8Array>(); - } - - cached_data = it->second.get(); - - Local<ArrayBuffer> 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<Value>& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsString()); - node::Utf8Value id(env->isolate(), args[0].As<String>()); - - MaybeLocal<Function> result = CompileAsModule(env, *id); - if (!result.IsEmpty()) { - args.GetReturnValue().Set(result.ToLocalChecked()); + return nullptr; } + return it->second.get(); } -MaybeLocal<Function> NativeModuleLoader::CompileAsModule(Environment* env, - const char* id) { - std::vector<Local<String>> 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<Function> NativeModuleLoader::CompileAsModule( + Local<Context> context, + const char* id, + NativeModuleLoader::Result* result) { + Isolate* isolate = context->GetIsolate(); + std::vector<Local<String>> 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<Function> of the compiled module if return_code_cache @@ -290,7 +180,7 @@ MaybeLocal<Function> NativeModuleLoader::LookupAndCompile( Local<Context> context, const char* id, std::vector<Local<String>>* parameters, - Environment* optional_env) { + NativeModuleLoader::Result* result) { Isolate* isolate = context->GetIsolate(); EscapableHandleScope scope(isolate); @@ -317,9 +207,9 @@ MaybeLocal<Function> 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<Function> 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<ScriptCompiler::CachedData> new_cached_data( ScriptCompiler::CreateCodeCacheForFunction(fun)); @@ -373,56 +251,5 @@ MaybeLocal<Function> NativeModuleLoader::LookupAndCompile( return scope.Escape(fun); } -void NativeModuleLoader::Initialize(Local<Object> target, - Local<Value> unused, - Local<Context> context, - void* priv) { - Environment* env = Environment::GetCurrent(context); - - CHECK(target - ->SetAccessor(env->context(), - env->config_string(), - ConfigStringGetter, - nullptr, - MaybeLocal<Value>(), - DEFAULT, - None, - SideEffectType::kHasNoSideEffect) - .FromJust()); - CHECK(target - ->SetAccessor(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "moduleIds"), - ModuleIdsGetter, - nullptr, - MaybeLocal<Value>(), - 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) |