From dfd7e994258a36f3941c74295a8c037cb4850418 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 10 Apr 2019 05:08:48 +0800 Subject: 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 Reviewed-By: Richard Lau --- src/node_native_module_env.cc | 229 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 src/node_native_module_env.cc (limited to 'src/node_native_module_env.cc') diff --git a/src/node_native_module_env.cc b/src/node_native_module_env.cc new file mode 100644 index 0000000000..fc48436dc1 --- /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) -- cgit v1.2.3