summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2019-04-10 05:08:48 +0800
committerJoyee Cheung <joyeec9h3@gmail.com>2019-04-13 17:24:51 +0800
commitdfd7e994258a36f3941c74295a8c037cb4850418 (patch)
tree03eaa022a99159912c97773a1b41952f3ee404b1 /src
parent9b6b567bc4dd8f40bad12528eebf12dac8a8027f (diff)
downloadandroid-node-v8-dfd7e994258a36f3941c74295a8c037cb4850418.tar.gz
android-node-v8-dfd7e994258a36f3941c74295a8c037cb4850418.tar.bz2
android-node-v8-dfd7e994258a36f3941c74295a8c037cb4850418.zip
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')
-rw-r--r--src/api/environment.cc4
-rw-r--r--src/node.cc11
-rw-r--r--src/node_binding.cc8
-rw-r--r--src/node_code_cache_stub.cc6
-rw-r--r--src/node_native_module.cc319
-rw-r--r--src/node_native_module.h97
-rw-r--r--src/node_native_module_env.cc229
-rw-r--r--src/node_native_module_env.h64
-rw-r--r--src/node_union_bytes.h1
9 files changed, 417 insertions, 322 deletions
diff --git a/src/api/environment.cc b/src/api/environment.cc
index 62eca337e1..fb7d7003bb 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<Context> NewContext(Isolate* isolate,
};
Local<Value> arguments[] = {context->Global(), exports};
MaybeLocal<Function> maybe_fn =
- per_process::native_module_loader.LookupAndCompile(
+ native_module::NativeModuleEnv::LookupAndCompile(
context, *module, &parameters, nullptr);
if (maybe_fn.IsEmpty()) {
return Local<Context>();
diff --git a/src/node.cc b/src/node.cc
index a5a0cf33e1..09817b8a4c 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<Value> ExecuteBootstrapper(Environment* env,
std::vector<Local<Value>>* arguments) {
EscapableHandleScope scope(env->isolate());
MaybeLocal<Function> 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<Value>();
@@ -401,7 +402,7 @@ MaybeLocal<Value> 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<std::string>* 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 ffa9ffdfdb..9a9bfa70a8 100644
--- a/src/node_binding.cc
+++ b/src/node_binding.cc
@@ -1,8 +1,8 @@
#include "node_binding.h"
+#include <atomic>
#include "env-inl.h"
-#include "node_native_module.h"
+#include "node_native_module_env.h"
#include "util.h"
-#include <atomic>
#if HAVE_OPENSSL
#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());
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 4fffa8e0c2..021910e829 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 <std::string, UnionString> 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 6462f39ee1..814adb620d 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, &parameters, 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, &parameters, 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)
diff --git a/src/node_native_module.h b/src/node_native_module.h
index 587c59022a..bbd4d89c40 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 <map>
+#include <memory>
#include <set>
#include <string>
-#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<v8::Object> target,
- v8::Local<v8::Value> unused,
- v8::Local<v8::Context> context,
- 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);
-
- // 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<v8::Function> LookupAndCompile(
- v8::Local<v8::Context> context,
- const char* id,
- std::vector<v8::Local<v8::String>>* parameters,
- Environment* optional_env);
-
- private:
- 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);
- // 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);
+ 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<v8::Function> CompileAsModule(Environment* env,
- 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();
- void InitializeModuleCategories();
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();
- 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<v8::Function> LookupAndCompile(
+ v8::Local<v8::Context> context,
+ const char* id,
+ std::vector<v8::Local<v8::String>>* parameters,
+ Result* result);
+ v8::MaybeLocal<v8::Function> CompileAsModule(v8::Local<v8::Context> 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 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<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)
diff --git a/src/node_native_module_env.h b/src/node_native_module_env.h
new file mode 100644
index 0000000000..2b1fabf89b
--- /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<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_
diff --git a/src/node_union_bytes.h b/src/node_union_bytes.h
index 0034f184cc..996d9880a4 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 {