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>
This commit is contained in:
parent
9b6b567bc4
commit
dfd7e99425
2
node.gyp
2
node.gyp
@ -463,6 +463,7 @@
|
|||||||
'src/node_messaging.cc',
|
'src/node_messaging.cc',
|
||||||
'src/node_metadata.cc',
|
'src/node_metadata.cc',
|
||||||
'src/node_native_module.cc',
|
'src/node_native_module.cc',
|
||||||
|
'src/node_native_module_env.cc',
|
||||||
'src/node_options.cc',
|
'src/node_options.cc',
|
||||||
'src/node_os.cc',
|
'src/node_os.cc',
|
||||||
'src/node_perf.cc',
|
'src/node_perf.cc',
|
||||||
@ -543,6 +544,7 @@
|
|||||||
'src/node_metadata.h',
|
'src/node_metadata.h',
|
||||||
'src/node_mutex.h',
|
'src/node_mutex.h',
|
||||||
'src/node_native_module.h',
|
'src/node_native_module.h',
|
||||||
|
'src/node_native_module_env.h',
|
||||||
'src/node_object_wrap.h',
|
'src/node_object_wrap.h',
|
||||||
'src/node_options.h',
|
'src/node_options.h',
|
||||||
'src/node_options-inl.h',
|
'src/node_options-inl.h',
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "node_context_data.h"
|
#include "node_context_data.h"
|
||||||
#include "node_errors.h"
|
#include "node_errors.h"
|
||||||
#include "node_internals.h"
|
#include "node_internals.h"
|
||||||
#include "node_native_module.h"
|
#include "node_native_module_env.h"
|
||||||
#include "node_platform.h"
|
#include "node_platform.h"
|
||||||
#include "node_process.h"
|
#include "node_process.h"
|
||||||
#include "node_v8_platform-inl.h"
|
#include "node_v8_platform-inl.h"
|
||||||
@ -351,7 +351,7 @@ Local<Context> NewContext(Isolate* isolate,
|
|||||||
};
|
};
|
||||||
Local<Value> arguments[] = {context->Global(), exports};
|
Local<Value> arguments[] = {context->Global(), exports};
|
||||||
MaybeLocal<Function> maybe_fn =
|
MaybeLocal<Function> maybe_fn =
|
||||||
per_process::native_module_loader.LookupAndCompile(
|
native_module::NativeModuleEnv::LookupAndCompile(
|
||||||
context, *module, ¶meters, nullptr);
|
context, *module, ¶meters, nullptr);
|
||||||
if (maybe_fn.IsEmpty()) {
|
if (maybe_fn.IsEmpty()) {
|
||||||
return Local<Context>();
|
return Local<Context>();
|
||||||
|
11
src/node.cc
11
src/node.cc
@ -31,7 +31,7 @@
|
|||||||
#include "node_errors.h"
|
#include "node_errors.h"
|
||||||
#include "node_internals.h"
|
#include "node_internals.h"
|
||||||
#include "node_metadata.h"
|
#include "node_metadata.h"
|
||||||
#include "node_native_module.h"
|
#include "node_native_module_env.h"
|
||||||
#include "node_options-inl.h"
|
#include "node_options-inl.h"
|
||||||
#include "node_perf.h"
|
#include "node_perf.h"
|
||||||
#include "node_platform.h"
|
#include "node_platform.h"
|
||||||
@ -118,8 +118,10 @@
|
|||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
|
using native_module::NativeModuleEnv;
|
||||||
using options_parser::kAllowedInEnvironment;
|
using options_parser::kAllowedInEnvironment;
|
||||||
using options_parser::kDisallowedInEnvironment;
|
using options_parser::kDisallowedInEnvironment;
|
||||||
|
|
||||||
using v8::Array;
|
using v8::Array;
|
||||||
using v8::Boolean;
|
using v8::Boolean;
|
||||||
using v8::Context;
|
using v8::Context;
|
||||||
@ -207,8 +209,7 @@ MaybeLocal<Value> ExecuteBootstrapper(Environment* env,
|
|||||||
std::vector<Local<Value>>* arguments) {
|
std::vector<Local<Value>>* arguments) {
|
||||||
EscapableHandleScope scope(env->isolate());
|
EscapableHandleScope scope(env->isolate());
|
||||||
MaybeLocal<Function> maybe_fn =
|
MaybeLocal<Function> maybe_fn =
|
||||||
per_process::native_module_loader.LookupAndCompile(
|
NativeModuleEnv::LookupAndCompile(env->context(), id, parameters, env);
|
||||||
env->context(), id, parameters, env);
|
|
||||||
|
|
||||||
if (maybe_fn.IsEmpty()) {
|
if (maybe_fn.IsEmpty()) {
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
@ -401,7 +402,7 @@ MaybeLocal<Value> StartMainThreadExecution(Environment* env) {
|
|||||||
// To allow people to extend Node in different ways, this hook allows
|
// 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
|
// one to drop a file lib/_third_party_main.js into the build
|
||||||
// directory which will be executed instead of Node's normal loading.
|
// 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");
|
return StartExecution(env, "internal/main/run_third_party_main");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -724,6 +725,8 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
|
|||||||
per_process::metadata.versions.InitializeIntlVersions();
|
per_process::metadata.versions.InitializeIntlVersions();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
NativeModuleEnv::InitializeCodeCache();
|
||||||
|
|
||||||
// We should set node_is_initialized here instead of in node::Start,
|
// We should set node_is_initialized here instead of in node::Start,
|
||||||
// otherwise embedders using node::Init to initialize everything will not be
|
// otherwise embedders using node::Init to initialize everything will not be
|
||||||
// able to set it and native modules will not load for them.
|
// able to set it and native modules will not load for them.
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "node_binding.h"
|
#include "node_binding.h"
|
||||||
#include "env-inl.h"
|
|
||||||
#include "node_native_module.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include "env-inl.h"
|
||||||
|
#include "node_native_module_env.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
#if HAVE_OPENSSL
|
#if HAVE_OPENSSL
|
||||||
#define NODE_BUILTIN_OPENSSL_MODULES(V) V(crypto) V(tls_wrap)
|
#define NODE_BUILTIN_OPENSSL_MODULES(V) V(crypto) V(tls_wrap)
|
||||||
@ -593,13 +593,13 @@ void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
|
|||||||
exports->SetPrototype(env->context(), Null(env->isolate())).FromJust());
|
exports->SetPrototype(env->context(), Null(env->isolate())).FromJust());
|
||||||
DefineConstants(env->isolate(), exports);
|
DefineConstants(env->isolate(), exports);
|
||||||
} else if (!strcmp(*module_v, "natives")) {
|
} 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
|
// Legacy feature: process.binding('natives').config contains stringified
|
||||||
// config.gypi
|
// config.gypi
|
||||||
CHECK(exports
|
CHECK(exports
|
||||||
->Set(env->context(),
|
->Set(env->context(),
|
||||||
env->config_string(),
|
env->config_string(),
|
||||||
per_process::native_module_loader.GetConfigString(
|
native_module::NativeModuleEnv::GetConfigString(
|
||||||
env->isolate()))
|
env->isolate()))
|
||||||
.FromJust());
|
.FromJust());
|
||||||
} else {
|
} else {
|
||||||
|
@ -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
|
// 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`
|
// The stub here is used when configure is run without `--code-cache-path`
|
||||||
@ -8,8 +8,8 @@ namespace node {
|
|||||||
namespace native_module {
|
namespace native_module {
|
||||||
|
|
||||||
// The generated source code would insert <std::string, UnionString> pairs
|
// The generated source code would insert <std::string, UnionString> pairs
|
||||||
// into native_module_loader.code_cache_.
|
// into NativeModuleLoader::instance.code_cache_.
|
||||||
void NativeModuleLoader::LoadCodeCache() {}
|
void NativeModuleEnv::InitializeCodeCache() {}
|
||||||
|
|
||||||
} // namespace native_module
|
} // namespace native_module
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
@ -1,40 +1,60 @@
|
|||||||
#include "node_native_module.h"
|
#include "node_native_module.h"
|
||||||
#include "node_errors.h"
|
#include "util-inl.h"
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
namespace per_process {
|
|
||||||
native_module::NativeModuleLoader native_module_loader;
|
|
||||||
} // namespace per_process
|
|
||||||
|
|
||||||
namespace native_module {
|
namespace native_module {
|
||||||
|
|
||||||
using v8::Array;
|
|
||||||
using v8::ArrayBuffer;
|
|
||||||
using v8::Context;
|
using v8::Context;
|
||||||
using v8::DEFAULT;
|
|
||||||
using v8::EscapableHandleScope;
|
using v8::EscapableHandleScope;
|
||||||
using v8::Function;
|
using v8::Function;
|
||||||
using v8::FunctionCallbackInfo;
|
|
||||||
using v8::HandleScope;
|
using v8::HandleScope;
|
||||||
using v8::Integer;
|
using v8::Integer;
|
||||||
using v8::IntegrityLevel;
|
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::Maybe;
|
using v8::Maybe;
|
||||||
using v8::MaybeLocal;
|
using v8::MaybeLocal;
|
||||||
using v8::Name;
|
|
||||||
using v8::None;
|
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
using v8::PropertyCallbackInfo;
|
|
||||||
using v8::Script;
|
using v8::Script;
|
||||||
using v8::ScriptCompiler;
|
using v8::ScriptCompiler;
|
||||||
using v8::ScriptOrigin;
|
using v8::ScriptOrigin;
|
||||||
using v8::Set;
|
|
||||||
using v8::SideEffectType;
|
|
||||||
using v8::String;
|
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() {
|
void NativeModuleLoader::InitializeModuleCategories() {
|
||||||
if (module_categories_.is_initialized) {
|
if (module_categories_.is_initialized) {
|
||||||
@ -105,182 +125,52 @@ void NativeModuleLoader::InitializeModuleCategories() {
|
|||||||
module_categories_.is_initialized = true;
|
module_categories_.is_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(joyeecheung): make these more general and put them into util.h
|
const std::set<std::string>& NativeModuleLoader::GetCannotBeRequired() {
|
||||||
Local<Object> MapToObject(Local<Context> context,
|
InitializeModuleCategories();
|
||||||
const NativeModuleRecordMap& in) {
|
return module_categories_.cannot_be_required;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Local<Set> ToJsSet(Local<Context> context,
|
const std::set<std::string>& NativeModuleLoader::GetCanBeRequired() {
|
||||||
const std::set<std::string>& in) {
|
InitializeModuleCategories();
|
||||||
Isolate* isolate = context->GetIsolate();
|
return module_categories_.can_be_required;
|
||||||
Local<Set> out = Set::New(isolate);
|
|
||||||
for (auto const& x : in) {
|
|
||||||
out->Add(context, OneByteString(isolate, x.c_str(), x.size()))
|
|
||||||
.ToLocalChecked();
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeModuleLoader::Exists(const char* id) {
|
bool NativeModuleLoader::CanBeRequired(const char* id) {
|
||||||
return source_.find(id) != source_.end();
|
return GetCanBeRequired().count(id) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeModuleLoader::GetModuleCategories(
|
bool NativeModuleLoader::CannotBeRequired(const char* id) {
|
||||||
Local<Name> property, const PropertyCallbackInfo<Value>& info) {
|
return GetCannotBeRequired().count(id) == 1;
|
||||||
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(
|
NativeModuleCacheMap* NativeModuleLoader::code_cache() {
|
||||||
const FunctionCallbackInfo<Value>& args) {
|
return &code_cache_;
|
||||||
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(
|
ScriptCompiler::CachedData* NativeModuleLoader::GetCodeCache(
|
||||||
Local<Name> property, const PropertyCallbackInfo<Value>& info) {
|
const char* id) const {
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
Mutex::ScopedLock lock(code_cache_mutex_);
|
Mutex::ScopedLock lock(code_cache_mutex_);
|
||||||
|
|
||||||
ScriptCompiler::CachedData* cached_data = nullptr;
|
|
||||||
const auto it = code_cache_.find(id);
|
const auto it = code_cache_.find(id);
|
||||||
if (it == code_cache_.end()) {
|
if (it == code_cache_.end()) {
|
||||||
// The module has not been compiled before.
|
// The module has not been compiled before.
|
||||||
return MaybeLocal<Uint8Array>();
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
return it->second.get();
|
||||||
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(
|
MaybeLocal<Function> NativeModuleLoader::CompileAsModule(
|
||||||
const FunctionCallbackInfo<Value>& args) {
|
Local<Context> context,
|
||||||
Environment* env = Environment::GetCurrent(args);
|
const char* id,
|
||||||
CHECK(args[0]->IsString());
|
NativeModuleLoader::Result* result) {
|
||||||
node::Utf8Value id(env->isolate(), args[0].As<String>());
|
Isolate* isolate = context->GetIsolate();
|
||||||
|
std::vector<Local<String>> parameters = {
|
||||||
MaybeLocal<Function> result = CompileAsModule(env, *id);
|
FIXED_ONE_BYTE_STRING(isolate, "exports"),
|
||||||
if (!result.IsEmpty()) {
|
FIXED_ONE_BYTE_STRING(isolate, "require"),
|
||||||
args.GetReturnValue().Set(result.ToLocalChecked());
|
FIXED_ONE_BYTE_STRING(isolate, "module"),
|
||||||
}
|
FIXED_ONE_BYTE_STRING(isolate, "process"),
|
||||||
}
|
FIXED_ONE_BYTE_STRING(isolate, "internalBinding"),
|
||||||
|
FIXED_ONE_BYTE_STRING(isolate, "primordials")};
|
||||||
MaybeLocal<Function> NativeModuleLoader::CompileAsModule(Environment* env,
|
return LookupAndCompile(context, id, ¶meters, result);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns Local<Function> of the compiled module if return_code_cache
|
// Returns Local<Function> of the compiled module if return_code_cache
|
||||||
@ -290,7 +180,7 @@ MaybeLocal<Function> NativeModuleLoader::LookupAndCompile(
|
|||||||
Local<Context> context,
|
Local<Context> context,
|
||||||
const char* id,
|
const char* id,
|
||||||
std::vector<Local<String>>* parameters,
|
std::vector<Local<String>>* parameters,
|
||||||
Environment* optional_env) {
|
NativeModuleLoader::Result* result) {
|
||||||
Isolate* isolate = context->GetIsolate();
|
Isolate* isolate = context->GetIsolate();
|
||||||
EscapableHandleScope scope(isolate);
|
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 =
|
ScriptCompiler::CompileOptions options =
|
||||||
use_cache ? ScriptCompiler::kConsumeCodeCache
|
has_cache ? ScriptCompiler::kConsumeCodeCache
|
||||||
: ScriptCompiler::kEagerCompile;
|
: ScriptCompiler::kEagerCompile;
|
||||||
ScriptCompiler::Source script_source(source, origin, cached_data);
|
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
|
// 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
|
// will never be in any of these two sets, but the two sets are only for
|
||||||
// testing anyway.
|
// 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
|
// Generate new cache for next compilation
|
||||||
std::unique_ptr<ScriptCompiler::CachedData> new_cached_data(
|
std::unique_ptr<ScriptCompiler::CachedData> new_cached_data(
|
||||||
ScriptCompiler::CreateCodeCacheForFunction(fun));
|
ScriptCompiler::CreateCodeCacheForFunction(fun));
|
||||||
@ -373,56 +251,5 @@ MaybeLocal<Function> NativeModuleLoader::LookupAndCompile(
|
|||||||
return scope.Escape(fun);
|
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 native_module
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
NODE_MODULE_CONTEXT_AWARE_INTERNAL(
|
|
||||||
native_module, node::native_module::NativeModuleLoader::Initialize)
|
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "env.h"
|
|
||||||
#include "node_mutex.h"
|
#include "node_mutex.h"
|
||||||
#include "node_union_bytes.h"
|
#include "node_union_bytes.h"
|
||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
@ -23,79 +23,57 @@ using NativeModuleCacheMap =
|
|||||||
// handles compilation and caching of builtin modules (NativeModule)
|
// handles compilation and caching of builtin modules (NativeModule)
|
||||||
// and bootstrappers, whose source are bundled into the binary
|
// and bootstrappers, whose source are bundled into the binary
|
||||||
// as static data.
|
// as static data.
|
||||||
// This class should not depend on a particular isolate, context, or
|
// This class should not depend on any Environment, or depend on access to
|
||||||
// environment. Rather it should take them as arguments when necessary.
|
// the its own singleton - that should be encapsulated in NativeModuleEnv
|
||||||
// The instances of this class are per-process.
|
// instead.
|
||||||
class NativeModuleLoader {
|
class NativeModuleLoader {
|
||||||
public:
|
private:
|
||||||
|
// Only allow access from friends.
|
||||||
|
friend class NativeModuleEnv;
|
||||||
|
friend class CodeCacheBuilder;
|
||||||
|
|
||||||
NativeModuleLoader();
|
NativeModuleLoader();
|
||||||
// TODO(joyeecheung): maybe we should make this a singleton, instead of
|
|
||||||
// putting it in per_process.
|
|
||||||
NativeModuleLoader(const NativeModuleLoader&) = delete;
|
NativeModuleLoader(const NativeModuleLoader&) = delete;
|
||||||
NativeModuleLoader& operator=(const NativeModuleLoader&) = delete;
|
NativeModuleLoader& operator=(const NativeModuleLoader&) = delete;
|
||||||
|
static NativeModuleLoader* GetInstance();
|
||||||
|
|
||||||
static void Initialize(v8::Local<v8::Object> target,
|
// Generated by tools/js2c.py as node_javascript.cc
|
||||||
v8::Local<v8::Value> unused,
|
void LoadJavaScriptSource(); // Loads data into source_
|
||||||
v8::Local<v8::Context> context,
|
UnionBytes GetConfig(); // Return data for config.gypi
|
||||||
void* priv);
|
|
||||||
v8::Local<v8::Object> GetSourceObject(v8::Local<v8::Context> context) const;
|
|
||||||
// Returns config.gypi as a JSON string
|
|
||||||
v8::Local<v8::String> GetConfigString(v8::Isolate* isolate) const;
|
|
||||||
|
|
||||||
bool Exists(const char* id);
|
bool Exists(const char* id);
|
||||||
|
v8::Local<v8::Object> GetSourceObject(v8::Local<v8::Context> context);
|
||||||
|
v8::Local<v8::String> GetConfigString(v8::Isolate* isolate);
|
||||||
|
std::vector<std::string> GetModuleIds();
|
||||||
|
|
||||||
// For bootstrappers optional_env may be a nullptr.
|
struct ModuleCategories {
|
||||||
|
bool is_initialized = false;
|
||||||
|
std::set<std::string> can_be_required;
|
||||||
|
std::set<std::string> cannot_be_required;
|
||||||
|
};
|
||||||
|
void InitializeModuleCategories();
|
||||||
|
const std::set<std::string>& GetCannotBeRequired();
|
||||||
|
const std::set<std::string>& GetCanBeRequired();
|
||||||
|
|
||||||
|
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
|
// If an exception is encountered (e.g. source code contains
|
||||||
// syntax error), the returned value is empty.
|
// syntax error), the returned value is empty.
|
||||||
v8::MaybeLocal<v8::Function> LookupAndCompile(
|
v8::MaybeLocal<v8::Function> LookupAndCompile(
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
const char* id,
|
const char* id,
|
||||||
std::vector<v8::Local<v8::String>>* parameters,
|
std::vector<v8::Local<v8::String>>* parameters,
|
||||||
Environment* optional_env);
|
Result* result);
|
||||||
|
v8::MaybeLocal<v8::Function> CompileAsModule(v8::Local<v8::Context> context,
|
||||||
private:
|
const char* id,
|
||||||
static void GetModuleCategories(
|
Result* result);
|
||||||
v8::Local<v8::Name> property,
|
|
||||||
const v8::PropertyCallbackInfo<v8::Value>& info);
|
|
||||||
static void GetCacheUsage(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
// Passing ids of builtin module source code into JS land as
|
|
||||||
// internalBinding('native_module').moduleIds
|
|
||||||
static void ModuleIdsGetter(v8::Local<v8::Name> property,
|
|
||||||
const v8::PropertyCallbackInfo<v8::Value>& info);
|
|
||||||
// Passing config.gypi into JS land as internalBinding('native_module').config
|
|
||||||
static void ConfigStringGetter(
|
|
||||||
v8::Local<v8::Name> property,
|
|
||||||
const v8::PropertyCallbackInfo<v8::Value>& info);
|
|
||||||
// Get code cache for a specific native module
|
|
||||||
static void GetCodeCache(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
v8::MaybeLocal<v8::Uint8Array> GetCodeCache(v8::Isolate* isolate,
|
|
||||||
const char* id) const;
|
|
||||||
// Compile a specific native module as a function
|
|
||||||
static void CompileFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
|
|
||||||
// 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<v8::Function> CompileAsModule(Environment* env,
|
|
||||||
const char* id);
|
|
||||||
|
|
||||||
void InitializeModuleCategories();
|
|
||||||
struct ModuleCategories {
|
|
||||||
bool is_initialized = false;
|
|
||||||
std::set<std::string> can_be_required;
|
|
||||||
std::set<std::string> cannot_be_required;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
static NativeModuleLoader instance_;
|
||||||
ModuleCategories module_categories_;
|
ModuleCategories module_categories_;
|
||||||
|
|
||||||
NativeModuleRecordMap source_;
|
NativeModuleRecordMap source_;
|
||||||
NativeModuleCacheMap code_cache_;
|
NativeModuleCacheMap code_cache_;
|
||||||
UnionBytes config_;
|
UnionBytes config_;
|
||||||
@ -103,13 +81,8 @@ class NativeModuleLoader {
|
|||||||
// Used to synchronize access to the code cache map
|
// Used to synchronize access to the code cache map
|
||||||
Mutex code_cache_mutex_;
|
Mutex code_cache_mutex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace native_module
|
} // namespace native_module
|
||||||
|
|
||||||
namespace per_process {
|
|
||||||
extern native_module::NativeModuleLoader native_module_loader;
|
|
||||||
} // namespace per_process
|
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||||
|
229
src/node_native_module_env.cc
Normal file
229
src/node_native_module_env.cc
Normal file
@ -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<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NativeModuleEnv::Exists(const char* id) {
|
||||||
|
return NativeModuleLoader::GetInstance()->Exists(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Object> NativeModuleEnv::GetSourceObject(Local<Context> context) {
|
||||||
|
return NativeModuleLoader::GetInstance()->GetSourceObject(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<String> NativeModuleEnv::GetConfigString(Isolate* isolate) {
|
||||||
|
return NativeModuleLoader::GetInstance()->GetConfigString(isolate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeModuleEnv::GetModuleCategories(
|
||||||
|
Local<Name> property, const PropertyCallbackInfo<Value>& info) {
|
||||||
|
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 =
|
||||||
|
NativeModuleLoader::GetInstance()->GetCannotBeRequired();
|
||||||
|
std::set<std::string> 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<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))
|
||||||
|
.FromJust();
|
||||||
|
result
|
||||||
|
->Set(env->context(),
|
||||||
|
OneByteString(isolate, "compiledWithoutCache"),
|
||||||
|
ToJsSet(context, env->native_modules_without_cache))
|
||||||
|
.FromJust();
|
||||||
|
args.GetReturnValue().Set(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeModuleEnv::ModuleIdsGetter(Local<Name> property,
|
||||||
|
const PropertyCallbackInfo<Value>& info) {
|
||||||
|
Isolate* isolate = info.GetIsolate();
|
||||||
|
|
||||||
|
std::vector<std::string> ids =
|
||||||
|
NativeModuleLoader::GetInstance()->GetModuleIds();
|
||||||
|
info.GetReturnValue().Set(
|
||||||
|
ToV8Value(isolate->GetCurrentContext(), ids).ToLocalChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeModuleEnv::ConfigStringGetter(
|
||||||
|
Local<Name> property, const PropertyCallbackInfo<Value>& 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<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
CHECK(args[0]->IsString());
|
||||||
|
node::Utf8Value id_v(env->isolate(), args[0].As<String>());
|
||||||
|
const char* id = *id_v;
|
||||||
|
NativeModuleLoader::Result result;
|
||||||
|
MaybeLocal<Function> maybe =
|
||||||
|
NativeModuleLoader::GetInstance()->CompileAsModule(
|
||||||
|
env->context(), id, &result);
|
||||||
|
RecordResult(id, result, env);
|
||||||
|
if (!maybe.IsEmpty()) {
|
||||||
|
args.GetReturnValue().Set(maybe.ToLocalChecked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns Local<Function> of the compiled module if return_code_cache
|
||||||
|
// is false (we are only compiling the function).
|
||||||
|
// Otherwise return a Local<Object> containing the cache.
|
||||||
|
MaybeLocal<Function> NativeModuleEnv::LookupAndCompile(
|
||||||
|
Local<Context> context,
|
||||||
|
const char* id,
|
||||||
|
std::vector<Local<String>>* parameters,
|
||||||
|
Environment* optional_env) {
|
||||||
|
NativeModuleLoader::Result result;
|
||||||
|
MaybeLocal<Function> 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<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;
|
||||||
|
|
||||||
|
ScriptCompiler::CachedData* cached_data =
|
||||||
|
NativeModuleLoader::GetInstance()->GetCodeCache(id);
|
||||||
|
if (cached_data != nullptr) {
|
||||||
|
Local<ArrayBuffer> 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<Object> target,
|
||||||
|
Local<Value> unused,
|
||||||
|
Local<Context> context,
|
||||||
|
void* priv) {
|
||||||
|
Environment* env = Environment::GetCurrent(context);
|
||||||
|
|
||||||
|
target
|
||||||
|
->SetAccessor(env->context(),
|
||||||
|
env->config_string(),
|
||||||
|
ConfigStringGetter,
|
||||||
|
nullptr,
|
||||||
|
MaybeLocal<Value>(),
|
||||||
|
DEFAULT,
|
||||||
|
None,
|
||||||
|
SideEffectType::kHasNoSideEffect)
|
||||||
|
.Check();
|
||||||
|
target
|
||||||
|
->SetAccessor(env->context(),
|
||||||
|
FIXED_ONE_BYTE_STRING(env->isolate(), "moduleIds"),
|
||||||
|
ModuleIdsGetter,
|
||||||
|
nullptr,
|
||||||
|
MaybeLocal<Value>(),
|
||||||
|
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)
|
64
src/node_native_module_env.h
Normal file
64
src/node_native_module_env.h
Normal file
@ -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<v8::Object> target,
|
||||||
|
v8::Local<v8::Value> unused,
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
void* priv);
|
||||||
|
|
||||||
|
static v8::MaybeLocal<v8::Function> LookupAndCompile(
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
const char* id,
|
||||||
|
std::vector<v8::Local<v8::String>>* parameters,
|
||||||
|
Environment* optional_env);
|
||||||
|
|
||||||
|
static v8::Local<v8::Object> GetSourceObject(v8::Local<v8::Context> context);
|
||||||
|
// Returns config.gypi as a JSON string
|
||||||
|
static v8::Local<v8::String> 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<v8::Name> property,
|
||||||
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||||
|
static void GetCacheUsage(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
// Passing ids of builtin module source code into JS land as
|
||||||
|
// internalBinding('native_module').moduleIds
|
||||||
|
static void ModuleIdsGetter(v8::Local<v8::Name> property,
|
||||||
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||||
|
// Passing config.gypi into JS land as internalBinding('native_module').config
|
||||||
|
static void ConfigStringGetter(
|
||||||
|
v8::Local<v8::Name> property,
|
||||||
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||||
|
// Compile a specific native module as a function
|
||||||
|
static void CompileFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void GetCodeCache(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace native_module
|
||||||
|
|
||||||
|
} // namespace node
|
||||||
|
|
||||||
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||||
|
|
||||||
|
#endif // SRC_NODE_NATIVE_MODULE_ENV_H_
|
@ -7,7 +7,6 @@
|
|||||||
// A union of const uint8_t* or const uint16_t* data that can be
|
// A union of const uint8_t* or const uint16_t* data that can be
|
||||||
// turned into external v8::String when given an isolate.
|
// turned into external v8::String when given an isolate.
|
||||||
|
|
||||||
#include "env.h"
|
|
||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
@ -30,9 +30,9 @@ if (child.status !== 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verifies that:
|
// Verifies that:
|
||||||
// - node::LoadCodeCache()
|
// - NativeModuleEnv::InitializeCodeCache()
|
||||||
// are defined in the generated code.
|
// 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({
|
const rl = readline.createInterface({
|
||||||
input: fs.createReadStream(dest),
|
input: fs.createReadStream(dest),
|
||||||
@ -42,7 +42,7 @@ const rl = readline.createInterface({
|
|||||||
let hasCacheDef = false;
|
let hasCacheDef = false;
|
||||||
|
|
||||||
rl.on('line', common.mustCallAtLeast((line) => {
|
rl.on('line', common.mustCallAtLeast((line) => {
|
||||||
if (line.includes('LoadCodeCache(')) {
|
if (line.includes('InitializeCodeCache(')) {
|
||||||
hasCacheDef = true;
|
hasCacheDef = true;
|
||||||
}
|
}
|
||||||
}, 2));
|
}, 2));
|
||||||
|
@ -63,7 +63,7 @@ function getInitalizer(key, cache) {
|
|||||||
`${defName}, static_cast<int>(arraysize(${defName})), ` +
|
`${defName}, static_cast<int>(arraysize(${defName})), ` +
|
||||||
'policy)';
|
'policy)';
|
||||||
const initializer =
|
const initializer =
|
||||||
'code_cache_.emplace(\n' +
|
'code_cache->emplace(\n' +
|
||||||
` "${key}",\n` +
|
` "${key}",\n` +
|
||||||
` ${dataDef}\n` +
|
` ${dataDef}\n` +
|
||||||
');';
|
');';
|
||||||
@ -107,8 +107,7 @@ for (const key of [...canBeRequired].sort(lexical)) {
|
|||||||
`, total = ${formatSize(totalCacheSize)}`);
|
`, total = ${formatSize(totalCacheSize)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = `#include "node_native_module.h"
|
const result = `#include "node_native_module_env.h"
|
||||||
#include "node_internals.h"
|
|
||||||
|
|
||||||
// This file is generated by tools/generate_code_cache.js
|
// This file is generated by tools/generate_code_cache.js
|
||||||
// and is used when configure is run with \`--code-cache-path\`
|
// and is used when configure is run with \`--code-cache-path\`
|
||||||
@ -117,7 +116,13 @@ namespace node {
|
|||||||
namespace native_module {
|
namespace native_module {
|
||||||
${cacheDefinitions.join('\n\n')}
|
${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;
|
auto policy = v8::ScriptCompiler::CachedData::BufferPolicy::BufferNotOwned;
|
||||||
${cacheInitializers.join('\n ')}
|
${cacheInitializers.join('\n ')}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user