summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/env.cc9
-rw-r--r--src/env.h15
-rw-r--r--src/node.cc36
-rw-r--r--src/node_code_cache.h19
-rw-r--r--src/node_code_cache_stub.cc22
-rw-r--r--src/node_internals.h5
-rw-r--r--src/node_javascript.h41
-rw-r--r--src/node_native_module.cc324
-rw-r--r--src/node_native_module.h64
-rw-r--r--src/node_union_bytes.h93
10 files changed, 348 insertions, 280 deletions
diff --git a/src/env.cc b/src/env.cc
index a39c51c26d..5731bad993 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -1,13 +1,14 @@
-#include "node_internals.h"
#include "async_wrap.h"
-#include "v8-profiler.h"
#include "node_buffer.h"
-#include "node_platform.h"
-#include "node_file.h"
#include "node_context_data.h"
+#include "node_file.h"
+#include "node_internals.h"
+#include "node_native_module.h"
+#include "node_platform.h"
#include "node_worker.h"
#include "tracing/agent.h"
#include "tracing/traced_value.h"
+#include "v8-profiler.h"
#include <stdio.h>
#include <algorithm>
diff --git a/src/env.h b/src/env.h
index 7f35f60494..6bed104dbb 100644
--- a/src/env.h
+++ b/src/env.h
@@ -29,13 +29,13 @@
#include "inspector_agent.h"
#endif
#include "handle_wrap.h"
+#include "node.h"
+#include "node_http2_state.h"
+#include "node_options.h"
#include "req_wrap.h"
#include "util.h"
#include "uv.h"
#include "v8.h"
-#include "node.h"
-#include "node_options.h"
-#include "node_http2_state.h"
#include <list>
#include <stdint.h>
@@ -347,12 +347,6 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
V(libuv_stream_wrap_ctor_template, v8::FunctionTemplate) \
V(message_port, v8::Object) \
V(message_port_constructor_template, v8::FunctionTemplate) \
- V(native_modules_code_cache, v8::Object) \
- V(native_modules_code_cache_hash, v8::Object) \
- V(native_modules_source, v8::Object) \
- V(native_modules_source_hash, v8::Object) \
- V(native_modules_with_cache, v8::Set) \
- V(native_modules_without_cache, v8::Set) \
V(performance_entry_callback, v8::Function) \
V(performance_entry_template, v8::Function) \
V(pipe_constructor_template, v8::FunctionTemplate) \
@@ -684,6 +678,9 @@ class Environment {
// List of id's that have been destroyed and need the destroy() cb called.
inline std::vector<double>* destroy_async_id_list();
+ std::set<std::string> native_modules_with_cache;
+ std::set<std::string> native_modules_without_cache;
+
std::unordered_multimap<int, loader::ModuleWrap*> hash_to_module_map;
std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map;
std::unordered_map<uint32_t, contextify::ContextifyScript*>
diff --git a/src/node.cc b/src/node.cc
index faef976e97..20aafe6a05 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -24,7 +24,6 @@
#include "node_context_data.h"
#include "node_errors.h"
#include "node_internals.h"
-#include "node_javascript.h"
#include "node_native_module.h"
#include "node_perf.h"
#include "node_platform.h"
@@ -130,7 +129,7 @@ typedef int mode_t;
namespace node {
-using native_module::NativeModule;
+using native_module::NativeModuleLoader;
using options_parser::kAllowedInEnvironment;
using options_parser::kDisallowedInEnvironment;
using v8::Array;
@@ -212,7 +211,7 @@ double prog_start_time;
Mutex per_process_opts_mutex;
std::shared_ptr<PerProcessOptions> per_process_opts {
new PerProcessOptions() };
-
+NativeModuleLoader per_process_loader;
static Mutex node_isolate_mutex;
static Isolate* node_isolate;
@@ -1243,8 +1242,7 @@ static void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
Null(env->isolate())).FromJust());
DefineConstants(env->isolate(), exports);
} else if (!strcmp(*module_v, "natives")) {
- exports = Object::New(env->isolate());
- NativeModule::GetNatives(env, exports);
+ exports = per_process_loader.GetSourceObject(env->context());
} else {
return ThrowIfNoSuchModule(env, *module_v);
}
@@ -1780,18 +1778,24 @@ void LoadEnvironment(Environment* env) {
// The bootstrapper scripts are lib/internal/bootstrap/loaders.js and
// lib/internal/bootstrap/node.js, each included as a static C string
- // defined in node_javascript.h, generated in node_javascript.cc by
- // node_js2c.
+ // generated in node_javascript.cc by node_js2c.
- // TODO(joyeecheung): use NativeModule::Compile
+ // TODO(joyeecheung): use NativeModuleLoader::Compile
+ // We duplicate the string literals here since once we refactor the bootstrap
+ // compilation out to NativeModuleLoader none of this is going to matter
+ Isolate* isolate = env->isolate();
Local<String> loaders_name =
- FIXED_ONE_BYTE_STRING(env->isolate(), "internal/bootstrap/loaders.js");
+ FIXED_ONE_BYTE_STRING(isolate, "internal/bootstrap/loaders.js");
+ Local<String> loaders_source =
+ per_process_loader.GetSource(isolate, "internal/bootstrap/loaders");
MaybeLocal<Function> loaders_bootstrapper =
- GetBootstrapper(env, LoadersBootstrapperSource(env), loaders_name);
+ GetBootstrapper(env, loaders_source, loaders_name);
Local<String> node_name =
- FIXED_ONE_BYTE_STRING(env->isolate(), "internal/bootstrap/node.js");
+ FIXED_ONE_BYTE_STRING(isolate, "internal/bootstrap/node.js");
+ Local<String> node_source =
+ per_process_loader.GetSource(isolate, "internal/bootstrap/node");
MaybeLocal<Function> node_bootstrapper =
- GetBootstrapper(env, NodeBootstrapperSource(env), node_name);
+ GetBootstrapper(env, node_source, node_name);
if (loaders_bootstrapper.IsEmpty() || node_bootstrapper.IsEmpty()) {
// Execution was interrupted.
@@ -1843,8 +1847,6 @@ void LoadEnvironment(Environment* env) {
env->options()->debug_options->break_node_first_line)
};
- NativeModule::LoadBindings(env);
-
// Bootstrap internal loaders
Local<Value> bootstrapped_loaders;
if (!ExecuteBootstrapper(env, loaders_bootstrapper.ToLocalChecked(),
@@ -2485,7 +2487,6 @@ void FreePlatform(MultiIsolatePlatform* platform) {
delete platform;
}
-
Local<Context> NewContext(Isolate* isolate,
Local<ObjectTemplate> object_template) {
auto context = Context::New(isolate, nullptr, object_template);
@@ -2499,8 +2500,9 @@ Local<Context> NewContext(Isolate* isolate,
// Run lib/internal/per_context.js
Context::Scope context_scope(context);
- // TODO(joyeecheung): use NativeModule::Compile
- Local<String> per_context = NodePerContextSource(isolate);
+ // TODO(joyeecheung): use NativeModuleLoader::Compile
+ Local<String> per_context =
+ per_process_loader.GetSource(isolate, "internal/per_context");
ScriptCompiler::Source per_context_src(per_context, nullptr);
Local<Script> s = ScriptCompiler::Compile(
context,
diff --git a/src/node_code_cache.h b/src/node_code_cache.h
deleted file mode 100644
index 8054279d55..0000000000
--- a/src/node_code_cache.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef SRC_NODE_CODE_CACHE_H_
-#define SRC_NODE_CODE_CACHE_H_
-
-#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
-
-#include "node_internals.h"
-
-namespace node {
-
-extern const bool native_module_has_code_cache;
-
-void DefineCodeCache(Environment* env, v8::Local<v8::Object> target);
-void DefineCodeCacheHash(Environment* env, v8::Local<v8::Object> target);
-
-} // namespace node
-
-#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
-
-#endif // SRC_NODE_CODE_CACHE_H_
diff --git a/src/node_code_cache_stub.cc b/src/node_code_cache_stub.cc
index 2fb86c5bf7..95997bb120 100644
--- a/src/node_code_cache_stub.cc
+++ b/src/node_code_cache_stub.cc
@@ -1,23 +1,19 @@
-#include "node_code_cache.h"
+#include "node_native_module.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`
namespace node {
+namespace native_module {
-const bool native_module_has_code_cache = false;
+// The generated source code would insert <std::string, UnionString> pairs
+// into native_module_loader.code_cache_.
+void NativeModuleLoader::LoadCodeCache() {}
-void DefineCodeCache(Environment* env, v8::Local<v8::Object> target) {
- // When we do not produce code cache for builtin modules,
- // `internalBinding('code_cache')` returns an empty object
- // (here as `target`) so this is a noop.
-}
-
-void DefineCodeCacheHash(Environment* env, v8::Local<v8::Object> target) {
- // When we do not produce code cache for builtin modules,
- // `internalBinding('code_cache_hash')` returns an empty object
- // (here as `target`) so this is a noop.
-}
+// The generated source code would instert <std::string, std::string> pairs
+// into native_module_loader.code_cache_hash_.
+void NativeModuleLoader::LoadCodeCacheHash() {}
+} // namespace native_module
} // namespace node
diff --git a/src/node_internals.h b/src/node_internals.h
index b6cd628086..68c0b1f753 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -171,6 +171,10 @@ struct sockaddr;
namespace node {
+namespace native_module {
+class NativeModuleLoader;
+}
+
extern Mutex process_mutex;
extern Mutex environ_mutex;
@@ -179,6 +183,7 @@ extern bool v8_initialized;
extern Mutex per_process_opts_mutex;
extern std::shared_ptr<PerProcessOptions> per_process_opts;
+extern native_module::NativeModuleLoader per_process_loader;
// Forward declaration
class Environment;
diff --git a/src/node_javascript.h b/src/node_javascript.h
deleted file mode 100644
index 80ef40b4ec..0000000000
--- a/src/node_javascript.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-#ifndef SRC_NODE_JAVASCRIPT_H_
-#define SRC_NODE_JAVASCRIPT_H_
-
-#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
-
-#include "node_internals.h"
-
-namespace node {
-
-void DefineJavaScript(Environment* env, v8::Local<v8::Object> target);
-void DefineJavaScriptHash(Environment* env, v8::Local<v8::Object> target);
-v8::Local<v8::String> NodePerContextSource(v8::Isolate* isolate);
-v8::Local<v8::String> LoadersBootstrapperSource(Environment* env);
-v8::Local<v8::String> NodeBootstrapperSource(Environment* env);
-
-} // namespace node
-
-#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
-
-#endif // SRC_NODE_JAVASCRIPT_H_
diff --git a/src/node_native_module.cc b/src/node_native_module.cc
index ea7fe9189a..11136a244c 100644
--- a/src/node_native_module.cc
+++ b/src/node_native_module.cc
@@ -1,13 +1,13 @@
#include "node_native_module.h"
-#include "node_code_cache.h"
#include "node_errors.h"
-#include "node_javascript.h"
+#include "node_internals.h"
namespace node {
namespace native_module {
using v8::Array;
using v8::ArrayBuffer;
+using v8::ArrayBufferCreationMode;
using v8::Context;
using v8::EscapableHandleScope;
using v8::Function;
@@ -25,94 +25,107 @@ using v8::ScriptCompiler;
using v8::ScriptOrigin;
using v8::Set;
using v8::String;
-using v8::TryCatch;
using v8::Uint8Array;
using v8::Value;
-void NativeModule::GetNatives(Environment* env, Local<Object> exports) {
- DefineJavaScript(env, exports);
+// 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)).FromJust();
+ }
+ return out;
+}
+
+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;
}
-void NativeModule::LoadBindings(Environment* env) {
- // TODO(joyeecheung): put the static values into a
- // std::map<std::string, const uint8_t*> instead of a v8::Object,
- // because here they are only looked up from the C++ side
- // (except in process.binding('natives') which we don't use)
- // so there is little value to put them in a v8::Object upfront.
- // Moreover, a std::map lookup should be faster than a lookup on
- // an V8 Object in dictionary mode.
+void NativeModuleLoader::GetCacheUsage(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
Local<Context> context = env->context();
- Local<Value> null = Null(isolate);
-
- Local<Object> native_modules_source = Object::New(isolate);
- CHECK(native_modules_source->SetPrototype(context, null).FromJust());
- DefineJavaScript(env, native_modules_source);
- native_modules_source->SetIntegrityLevel(context, IntegrityLevel::kFrozen)
+ Local<Object> result = Object::New(isolate);
+ result
+ ->Set(env->context(),
+ OneByteString(isolate, "compiledWithCache"),
+ ToJsSet(context, env->native_modules_with_cache))
.FromJust();
- env->set_native_modules_source(native_modules_source);
-
- Local<Object> native_modules_source_hash = Object::New(isolate);
- CHECK(native_modules_source_hash->SetPrototype(context, null).FromJust());
- DefineJavaScriptHash(env, native_modules_source_hash);
- native_modules_source_hash
- ->SetIntegrityLevel(context, IntegrityLevel::kFrozen)
+ result
+ ->Set(env->context(),
+ OneByteString(isolate, "compiledWithoutCache"),
+ ToJsSet(context, env->native_modules_without_cache))
.FromJust();
- env->set_native_modules_source_hash(native_modules_source_hash);
+ args.GetReturnValue().Set(result);
+}
- Local<Object> native_modules_code_cache = Object::New(isolate);
- CHECK(native_modules_code_cache->SetPrototype(context, null).FromJust());
- DefineCodeCache(env, native_modules_code_cache);
- native_modules_code_cache->SetIntegrityLevel(context, IntegrityLevel::kFrozen)
- .FromJust();
- env->set_native_modules_code_cache(native_modules_code_cache);
+void NativeModuleLoader::GetSourceObject(
+ const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ args.GetReturnValue().Set(per_process_loader.GetSourceObject(env->context()));
+}
- Local<Object> native_modules_code_cache_hash = Object::New(isolate);
- CHECK(native_modules_code_cache_hash->SetPrototype(context, null).FromJust());
- DefineCodeCacheHash(env, native_modules_code_cache_hash);
- native_modules_code_cache_hash
- ->SetIntegrityLevel(context, IntegrityLevel::kFrozen)
- .FromJust();
- env->set_native_modules_code_cache_hash(native_modules_code_cache_hash);
+Local<Object> NativeModuleLoader::GetSourceObject(
+ Local<Context> context) const {
+ return MapToObject(context, source_);
+}
+
+Local<String> NativeModuleLoader::GetSource(Isolate* isolate,
+ const char* id) const {
+ const auto it = source_.find(id);
+ CHECK_NE(it, source_.end());
+ return it->second.ToStringChecked(isolate);
+}
- env->set_native_modules_with_cache(Set::New(isolate));
- env->set_native_modules_without_cache(Set::New(isolate));
+NativeModuleLoader::NativeModuleLoader() {
+ LoadJavaScriptSource();
+ LoadJavaScriptHash();
+ LoadCodeCache();
+ LoadCodeCacheHash();
}
-void NativeModule::CompileCodeCache(const FunctionCallbackInfo<Value>& args) {
+void NativeModuleLoader::CompileCodeCache(
+ const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK(args[0]->IsString());
- Local<String> id = args[0].As<String>();
+ node::Utf8Value id(env->isolate(), args[0].As<String>());
- Local<Value> result = CompileAsModule(env, id, true);
+ // TODO(joyeecheung): allow compiling cache for bootstrapper by
+ // switching on id
+ Local<Value> result = CompileAsModule(env, *id, true);
if (!result.IsEmpty()) {
args.GetReturnValue().Set(result);
}
}
-void NativeModule::CompileFunction(const FunctionCallbackInfo<Value>& args) {
+void NativeModuleLoader::CompileFunction(
+ const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK(args[0]->IsString());
- Local<String> id = args[0].As<String>();
-
- Local<Value> result = CompileAsModule(env, id, false);
+ node::Utf8Value id(env->isolate(), args[0].As<String>());
+ Local<Value> result = CompileAsModule(env, *id, false);
if (!result.IsEmpty()) {
args.GetReturnValue().Set(result);
}
}
-Local<Value> NativeModule::CompileAsModule(Environment* env,
- Local<String> id,
- bool produce_code_cache) {
- Local<String> parameters[] = {env->exports_string(),
- env->require_string(),
- env->module_string(),
- env->process_string(),
- env->internal_binding_string()};
-
- return Compile(
- env, id, parameters, arraysize(parameters), produce_code_cache);
+Local<Value> NativeModuleLoader::CompileAsModule(Environment* env,
+ const char* id,
+ bool produce_code_cache) {
+ return per_process_loader.LookupAndCompile(
+ env->context(), id, produce_code_cache, env);
}
// Currently V8 only checks that the length of the source code is the
@@ -139,30 +152,25 @@ Local<Value> NativeModule::CompileAsModule(Environment* env,
// early in the bootstrap process so it should be easy to detect and fix.
// Returns nullptr if there is no code cache corresponding to the id
-ScriptCompiler::CachedData* GetCachedData(Environment* env, Local<String> id) {
- HandleScope scope(env->isolate());
- Local<Context> context = env->context();
-
- Local<Value> result =
- env->native_modules_code_cache()->Get(context, id).ToLocalChecked();
+ScriptCompiler::CachedData* NativeModuleLoader::GetCachedData(
+ const char* id) const {
+ const auto it = per_process_loader.code_cache_.find(id);
// This could be false if the module cannot be cached somehow.
// See lib/internal/bootstrap/cache.js on the modules that cannot be cached
- if (result->IsUndefined()) {
+ if (it == per_process_loader.code_cache_.end()) {
return nullptr;
}
- CHECK(result->IsUint8Array());
- Local<Uint8Array> code_cache = result.As<Uint8Array>();
+ const uint8_t* code_cache_value = it->second.one_bytes_data();
+ size_t code_cache_length = it->second.length();
- result =
- env->native_modules_code_cache_hash()->Get(context, id).ToLocalChecked();
- CHECK(result->IsString());
- Local<String> code_cache_hash = result.As<String>();
+ const auto it2 = code_cache_hash_.find(id);
+ CHECK_NE(it2, code_cache_hash_.end());
+ const std::string& code_cache_hash_value = it2->second;
- result =
- env->native_modules_source_hash()->Get(context, id).ToLocalChecked();
- CHECK(result->IsString());
- Local<String> source_hash = result.As<String>();
+ const auto it3 = source_hash_.find(id);
+ CHECK_NE(it3, source_hash_.end());
+ const std::string& source_hash_value = it3->second;
// It may fail when any of the inputs of the `node_js2c` target in
// node.gyp is modified but the tools/generate_code_cache.js
@@ -170,33 +178,26 @@ ScriptCompiler::CachedData* GetCachedData(Environment* env, Local<String> id) {
// FIXME(joyeecheung): Figure out how to resolve the dependency issue.
// When the code cache was introduced we were at a point where refactoring
// node.gyp may not be worth the effort.
- CHECK(code_cache_hash->StrictEquals(source_hash));
+ CHECK_EQ(code_cache_hash_value, source_hash_value);
- ArrayBuffer::Contents contents = code_cache->Buffer()->GetContents();
- uint8_t* data = static_cast<uint8_t*>(contents.Data());
- return new ScriptCompiler::CachedData(data + code_cache->ByteOffset(),
- code_cache->ByteLength());
+ return new ScriptCompiler::CachedData(code_cache_value, code_cache_length);
}
// Returns Local<Function> of the compiled module if produce_code_cache
// is false (we are only compiling the function).
// Otherwise return a Local<Object> containing the cache.
-Local<Value> NativeModule::Compile(Environment* env,
- Local<String> id,
- Local<String> parameters[],
- size_t parameters_count,
- bool produce_code_cache) {
- EscapableHandleScope scope(env->isolate());
- Local<Context> context = env->context();
- Isolate* isolate = env->isolate();
+Local<Value> NativeModuleLoader::LookupAndCompile(Local<Context> context,
+ const char* id,
+ bool produce_code_cache,
+ Environment* optional_env) {
+ Isolate* isolate = context->GetIsolate();
+ EscapableHandleScope scope(isolate);
- Local<Value> result =
- env->native_modules_source()->Get(context, id).ToLocalChecked();
- CHECK(result->IsString());
- Local<String> source = result.As<String>();
+ Local<String> source = GetSource(isolate, id);
+ std::string filename_s = id + std::string(".js");
Local<String> filename =
- String::Concat(isolate, id, FIXED_ONE_BYTE_STRING(isolate, ".js"));
+ OneByteString(isolate, filename_s.c_str(), filename_s.size());
Local<Integer> line_offset = Integer::New(isolate, 0);
Local<Integer> column_offset = Integer::New(isolate, 0);
ScriptOrigin origin(filename, line_offset, column_offset);
@@ -208,8 +209,8 @@ Local<Value> NativeModule::Compile(Environment* env,
// built with them.
// 2. If we are generating code cache for tools/general_code_cache.js, we
// are not going to use any cache ourselves.
- if (native_module_has_code_cache && !produce_code_cache) {
- cached_data = GetCachedData(env, id);
+ if (has_code_cache_ && !produce_code_cache) {
+ cached_data = GetCachedData(id);
if (cached_data != nullptr) {
use_cache = true;
}
@@ -226,35 +227,55 @@ Local<Value> NativeModule::Compile(Environment* env,
options = ScriptCompiler::kNoCompileOptions;
}
- MaybeLocal<Function> maybe_fun =
- ScriptCompiler::CompileFunctionInContext(context,
- &script_source,
- parameters_count,
- parameters,
- 0,
- nullptr,
- options);
+ MaybeLocal<Function> maybe_fun;
+ // Currently we assume if Environment is ready, then we must be compiling
+ // native modules instead of bootstrappers.
+ if (optional_env != nullptr) {
+ Local<String> parameters[] = {optional_env->exports_string(),
+ optional_env->require_string(),
+ optional_env->module_string(),
+ optional_env->process_string(),
+ optional_env->internal_binding_string()};
+ maybe_fun = ScriptCompiler::CompileFunctionInContext(context,
+ &script_source,
+ arraysize(parameters),
+ parameters,
+ 0,
+ nullptr,
+ options);
+ } else {
+ // Until we migrate bootstrappers compilations here this is unreachable
+ // TODO(joyeecheung): it should be possible to generate the argument names
+ // from some special comments for the bootstrapper case.
+ // Note that for bootstrappers we may not be able to get the argument
+ // names as env->some_string() because we might be compiling before
+ // those strings are initialized.
+ UNREACHABLE();
+ }
- TryCatch try_catch(isolate);
Local<Function> fun;
// This could fail when there are early errors in the native modules,
// e.g. the syntax errors
if (maybe_fun.IsEmpty() || !maybe_fun.ToLocal(&fun)) {
- DecorateErrorStack(env, try_catch);
- try_catch.ReThrow();
+ // In the case of early errors, v8 is already capable of
+ // decorating the stack for us - note that we use CompileFunctionInContext
+ // so there is no need to worry about wrappers.
return scope.Escape(Local<Value>());
}
if (use_cache) {
- // If the cache is rejected, something must be wrong with the build
- // and we should just crash.
- CHECK(!script_source.GetCachedData()->rejected);
- if (env->native_modules_with_cache()->Add(context, id).IsEmpty()) {
- return scope.Escape(Local<Value>());
+ 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 (env->native_modules_without_cache()->Add(context, id).IsEmpty()) {
- return scope.Escape(Local<Value>());
+ if (optional_env != nullptr) {
+ optional_env->native_modules_without_cache.insert(id);
}
}
@@ -262,59 +283,38 @@ Local<Value> NativeModule::Compile(Environment* env,
std::unique_ptr<ScriptCompiler::CachedData> cached_data(
ScriptCompiler::CreateCodeCacheForFunction(fun));
CHECK_NE(cached_data, nullptr);
- char* data =
- reinterpret_cast<char*>(const_cast<uint8_t*>(cached_data->data));
-
- // Since we have no API to create a buffer from a new'ed pointer,
- // we will need to copy it - but this code path is only run by the
- // tooling that generates the code cache to be bundled in the binary
+ size_t cached_data_length = cached_data->length;
+ // Since we have no special allocator to create an ArrayBuffer
+ // from a new'ed pointer, we will need to copy it - but this
+ // code path is only run by the tooling that generates the code
+ // cache to be bundled in the binary
// so it should be fine.
- Local<Object> buf =
- Buffer::Copy(env, data, cached_data->length).ToLocalChecked();
- return scope.Escape(buf);
+ MallocedBuffer<uint8_t> copied(cached_data->length);
+ memcpy(copied.data, cached_data->data, cached_data_length);
+ Local<ArrayBuffer> buf =
+ ArrayBuffer::New(isolate,
+ copied.release(),
+ cached_data_length,
+ ArrayBufferCreationMode::kInternalized);
+ return scope.Escape(Uint8Array::New(buf, 0, cached_data_length));
} else {
return scope.Escape(fun);
}
}
-void Initialize(Local<Object> target,
- Local<Value> unused,
- Local<Context> context) {
+void NativeModuleLoader::Initialize(Local<Object> target,
+ Local<Value> unused,
+ Local<Context> context) {
Environment* env = Environment::GetCurrent(context);
- target
- ->Set(context,
- FIXED_ONE_BYTE_STRING(env->isolate(), "source"),
- env->native_modules_source())
- .FromJust();
- target
- ->Set(context,
- FIXED_ONE_BYTE_STRING(env->isolate(), "sourceHash"),
- env->native_modules_source_hash())
- .FromJust();
- target
- ->Set(context,
- FIXED_ONE_BYTE_STRING(env->isolate(), "codeCache"),
- env->native_modules_code_cache())
- .FromJust();
- target
- ->Set(context,
- FIXED_ONE_BYTE_STRING(env->isolate(), "codeCacheHash"),
- env->native_modules_code_cache_hash())
- .FromJust();
- target
- ->Set(context,
- FIXED_ONE_BYTE_STRING(env->isolate(), "compiledWithCache"),
- env->native_modules_with_cache())
- .FromJust();
- target
- ->Set(context,
- FIXED_ONE_BYTE_STRING(env->isolate(), "compiledWithoutCache"),
- env->native_modules_without_cache())
- .FromJust();
-
- env->SetMethod(target, "compileFunction", NativeModule::CompileFunction);
- env->SetMethod(target, "compileCodeCache", NativeModule::CompileCodeCache);
+ env->SetMethod(
+ target, "getSource", NativeModuleLoader::GetSourceObject);
+ env->SetMethod(
+ target, "getCacheUsage", NativeModuleLoader::GetCacheUsage);
+ env->SetMethod(
+ target, "compileFunction", NativeModuleLoader::CompileFunction);
+ env->SetMethod(
+ target, "compileCodeCache", NativeModuleLoader::CompileCodeCache);
// internalBinding('native_module') should be frozen
target->SetIntegrityLevel(context, IntegrityLevel::kFrozen).FromJust();
}
@@ -322,5 +322,5 @@ void Initialize(Local<Object> target,
} // namespace native_module
} // namespace node
-NODE_MODULE_CONTEXT_AWARE_INTERNAL(native_module,
- node::native_module::Initialize)
+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 c4ffbfb0cd..ad9343f199 100644
--- a/src/node_native_module.h
+++ b/src/node_native_module.h
@@ -3,34 +3,68 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
-#include "node_internals.h"
+#include <map>
+#include <set>
+#include <string>
+#include "env.h"
+#include "node_union_bytes.h"
+#include "v8.h"
namespace node {
namespace native_module {
-// The native (C++) side of the native module compilation.
+using NativeModuleRecordMap = std::map<std::string, UnionBytes>;
+using NativeModuleHashMap = std::map<std::string, std::string>;
-class NativeModule {
+// The native (C++) side of the native module compilation.
+// This class should not depend on Environment
+class NativeModuleLoader {
public:
- // For legacy process.binding('natives') which is mutable
- static void GetNatives(Environment* env, v8::Local<v8::Object> exports);
- // Loads the static JavaScript source code and the cache into Environment
- static void LoadBindings(Environment* env);
+ NativeModuleLoader();
+ static void Initialize(v8::Local<v8::Object> target,
+ v8::Local<v8::Value> unused,
+ v8::Local<v8::Context> context);
+ v8::Local<v8::Object> GetSourceObject(v8::Local<v8::Context> context) const;
+ v8::Local<v8::String> GetSource(v8::Isolate* isolate, const char* id) const;
+
+ private:
+ static void GetCacheUsage(const v8::FunctionCallbackInfo<v8::Value>& args);
+ // For legacy process.binding('natives') which is mutable, and for
+ // internalBinding('native_module').source for internal use
+ static void GetSourceObject(const v8::FunctionCallbackInfo<v8::Value>& args);
// Compile code cache for a specific native module
static void CompileCodeCache(const v8::FunctionCallbackInfo<v8::Value>& args);
// Compile a specific native module as a function
static void CompileFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
- private:
+ // Generated by tools/js2c.py as node_javascript.cc
+ void LoadJavaScriptSource(); // Loads data into source_
+ void LoadJavaScriptHash(); // Loads data into source_hash_
+
+ // 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_
+ void LoadCodeCacheHash(); // Loads data into code_cache_hash_
+
+ v8::ScriptCompiler::CachedData* GetCachedData(const char* id) const;
static v8::Local<v8::Value> CompileAsModule(Environment* env,
- v8::Local<v8::String> id,
+ const char* id,
bool produce_code_cache);
- // TODO(joyeecheung): make this public and reuse it to compile bootstrappers
- static v8::Local<v8::Value> Compile(Environment* env,
- v8::Local<v8::String> id,
- v8::Local<v8::String> parameters[],
- size_t parameters_count,
- bool produce_code_cache);
+ // TODO(joyeecheung): make this public and reuse it to compile bootstrappers.
+ // For bootstrappers optional_env may be a nullptr.
+ // This method magically knows what parameter it should pass to
+ // the function to be compiled.
+ v8::Local<v8::Value> LookupAndCompile(v8::Local<v8::Context> context,
+ const char* id,
+ bool produce_code_cache,
+ Environment* optional_env);
+
+ bool has_code_cache_ = false;
+ NativeModuleRecordMap source_;
+ NativeModuleRecordMap code_cache_;
+ NativeModuleHashMap source_hash_;
+ NativeModuleHashMap code_cache_hash_;
};
} // namespace native_module
diff --git a/src/node_union_bytes.h b/src/node_union_bytes.h
new file mode 100644
index 0000000000..128bb11ed0
--- /dev/null
+++ b/src/node_union_bytes.h
@@ -0,0 +1,93 @@
+
+#ifndef SRC_NODE_UNION_BYTES_H_
+#define SRC_NODE_UNION_BYTES_H_
+
+#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+// 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 {
+
+class NonOwningExternalOneByteResource
+ : public v8::String::ExternalOneByteStringResource {
+ public:
+ explicit NonOwningExternalOneByteResource(const uint8_t* data, size_t length)
+ : data_(data), length_(length) {}
+ ~NonOwningExternalOneByteResource() override = default;
+
+ const char* data() const override {
+ return reinterpret_cast<const char*>(data_);
+ }
+ size_t length() const override { return length_; }
+
+ private:
+ const uint8_t* data_;
+ size_t length_;
+ DISALLOW_COPY_AND_ASSIGN(NonOwningExternalOneByteResource);
+};
+
+class NonOwningExternalTwoByteResource
+ : public v8::String::ExternalStringResource {
+ public:
+ explicit NonOwningExternalTwoByteResource(const uint16_t* data, size_t length)
+ : data_(data), length_(length) {}
+ ~NonOwningExternalTwoByteResource() override = default;
+
+ const uint16_t* data() const override { return data_; }
+ size_t length() const override { return length_; }
+
+ private:
+ const uint16_t* data_;
+ size_t length_;
+ DISALLOW_COPY_AND_ASSIGN(NonOwningExternalTwoByteResource);
+};
+
+// Similar to a v8::String, but it's independent from Isolates
+// and can be materialized in Isolates as external Strings
+// via ToStringChecked. The data pointers are owned by the caller.
+class UnionBytes {
+ public:
+ UnionBytes(const uint16_t* data, size_t length)
+ : is_one_byte_(false), two_bytes_(data), length_(length) {}
+ UnionBytes(const uint8_t* data, size_t length)
+ : is_one_byte_(true), one_bytes_(data), length_(length) {}
+ bool is_one_byte() const { return is_one_byte_; }
+ const uint16_t* two_bytes_data() const {
+ CHECK(!is_one_byte_);
+ return two_bytes_;
+ }
+ const uint8_t* one_bytes_data() const {
+ CHECK(is_one_byte_);
+ return one_bytes_;
+ }
+ v8::Local<v8::String> ToStringChecked(v8::Isolate* isolate) const {
+ if (is_one_byte_) {
+ NonOwningExternalOneByteResource* source =
+ new NonOwningExternalOneByteResource(one_bytes_, length_);
+ return v8::String::NewExternalOneByte(isolate, source).ToLocalChecked();
+ } else {
+ NonOwningExternalTwoByteResource* source =
+ new NonOwningExternalTwoByteResource(two_bytes_, length_);
+ return v8::String::NewExternalTwoByte(isolate, source).ToLocalChecked();
+ }
+ }
+ size_t length() { return length_; }
+
+ private:
+ bool is_one_byte_;
+ union {
+ const uint8_t* one_bytes_;
+ const uint16_t* two_bytes_;
+ };
+ size_t length_;
+};
+
+} // namespace node
+
+#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#endif // SRC_NODE_UNION_BYTES_H_