summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2018-11-14 22:38:12 +0800
committerJoyee Cheung <joyeec9h3@gmail.com>2018-11-20 01:17:15 +0800
commit7778c035a0a3f9bbb988eb0298a026d80f423e03 (patch)
treed3c80bffa25e84ae9993aaf0314192c2ac4f969f /src
parent092ab7a1d39d97a3e93f8ed46d80d8ad783b2f9c (diff)
downloadandroid-node-v8-7778c035a0a3f9bbb988eb0298a026d80f423e03.tar.gz
android-node-v8-7778c035a0a3f9bbb988eb0298a026d80f423e03.tar.bz2
android-node-v8-7778c035a0a3f9bbb988eb0298a026d80f423e03.zip
src: use STL containers instead of v8 values for static module data
Instead of putting the source code and the cache in v8::Objects, put them in per-process std::maps. This has the following benefits: - It's slightly lighter in weight compared to storing things on the v8 heap. Also it may be slightly faster since the preivous v8::Object is already in dictionary mode - though the difference is very small given the number of native modules is limited. - The source and code cache generation templates are now much simpler since they just initialize static arrays and manipulate STL constructs. - The static native module data can be accessed independently of any Environment or Isolate, and it's easy to look them up from the C++'s side. - It's now impossible to mutate the source code used to compile native modules from the JS land since it's completely separate from the v8 heap. We can still get the constant strings from process.binding('natives') but that's all. A few drive-by fixes: - Remove DecorateErrorStack in LookupAndCompile - We don't need to capture the exception to decorate when we encounter errors during native module compilation, as those errors should be syntax errors and v8 is able to decorate them well. We use CompileFunctionInContext so there is no need to worry about wrappers either. - The code cache could be rejected when node is started with v8 flags. Instead of aborting in that case, simply keep a record in the native_module_without_cache set. - Refactor js2c.py a bit, reduce code duplication and inline Render() to make the one-byte/two-byte special treatment easier to read. PR-URL: https://github.com/nodejs/node/pull/24384 Fixes: https://github.com/Remove Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
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_