1 |
|
|
#include "node_native_module.h" |
2 |
|
|
#include "debug_utils-inl.h" |
3 |
|
|
#include "node_internals.h" |
4 |
|
|
#include "util-inl.h" |
5 |
|
|
|
6 |
|
|
namespace node { |
7 |
|
|
namespace native_module { |
8 |
|
|
|
9 |
|
|
using v8::Context; |
10 |
|
|
using v8::EscapableHandleScope; |
11 |
|
|
using v8::Function; |
12 |
|
|
using v8::Isolate; |
13 |
|
|
using v8::Local; |
14 |
|
|
using v8::MaybeLocal; |
15 |
|
|
using v8::Object; |
16 |
|
|
using v8::ScriptCompiler; |
17 |
|
|
using v8::ScriptOrigin; |
18 |
|
|
using v8::String; |
19 |
|
|
|
20 |
|
|
NativeModuleLoader NativeModuleLoader::instance_; |
21 |
|
|
|
22 |
|
5255 |
NativeModuleLoader::NativeModuleLoader() : config_(GetConfig()) { |
23 |
|
5255 |
LoadJavaScriptSource(); |
24 |
|
5255 |
} |
25 |
|
|
|
26 |
|
415454 |
NativeModuleLoader* NativeModuleLoader::GetInstance() { |
27 |
|
415454 |
return &instance_; |
28 |
|
|
} |
29 |
|
|
|
30 |
|
17 |
bool NativeModuleLoader::Exists(const char* id) { |
31 |
|
17 |
return source_.find(id) != source_.end(); |
32 |
|
|
} |
33 |
|
|
|
34 |
|
17 |
bool NativeModuleLoader::Add(const char* id, const UnionBytes& source) { |
35 |
✗✓ |
17 |
if (Exists(id)) { |
36 |
|
|
return false; |
37 |
|
|
} |
38 |
|
17 |
source_.emplace(id, source); |
39 |
|
17 |
return true; |
40 |
|
|
} |
41 |
|
|
|
42 |
|
41 |
Local<Object> NativeModuleLoader::GetSourceObject(Local<Context> context) { |
43 |
|
41 |
Isolate* isolate = context->GetIsolate(); |
44 |
|
41 |
Local<Object> out = Object::New(isolate); |
45 |
✓✓ |
12259 |
for (auto const& x : source_) { |
46 |
|
12218 |
Local<String> key = OneByteString(isolate, x.first.c_str(), x.first.size()); |
47 |
|
36654 |
out->Set(context, key, x.second.ToStringChecked(isolate)).FromJust(); |
48 |
|
|
} |
49 |
|
41 |
return out; |
50 |
|
|
} |
51 |
|
|
|
52 |
|
888 |
Local<String> NativeModuleLoader::GetConfigString(Isolate* isolate) { |
53 |
|
888 |
return config_.ToStringChecked(isolate); |
54 |
|
|
} |
55 |
|
|
|
56 |
|
891 |
std::vector<std::string> NativeModuleLoader::GetModuleIds() { |
57 |
|
891 |
std::vector<std::string> ids; |
58 |
|
891 |
ids.reserve(source_.size()); |
59 |
✓✓ |
266564 |
for (auto const& x : source_) { |
60 |
|
265673 |
ids.emplace_back(x.first); |
61 |
|
|
} |
62 |
|
891 |
return ids; |
63 |
|
|
} |
64 |
|
|
|
65 |
|
1790 |
void NativeModuleLoader::InitializeModuleCategories() { |
66 |
✓✓ |
1790 |
if (module_categories_.is_initialized) { |
67 |
|
|
DCHECK(!module_categories_.can_be_required.empty()); |
68 |
|
1783 |
return; |
69 |
|
|
} |
70 |
|
|
|
71 |
|
|
std::vector<std::string> prefixes = { |
72 |
|
|
#if !HAVE_OPENSSL |
73 |
|
|
"internal/crypto/", |
74 |
|
|
"internal/debugger/", |
75 |
|
|
#endif // !HAVE_OPENSSL |
76 |
|
|
|
77 |
|
|
"internal/bootstrap/", |
78 |
|
|
"internal/per_context/", |
79 |
|
|
"internal/deps/", |
80 |
|
|
"internal/main/" |
81 |
|
49 |
}; |
82 |
|
|
|
83 |
|
|
module_categories_.can_be_required.emplace( |
84 |
|
7 |
"internal/deps/cjs-module-lexer/lexer"); |
85 |
|
|
|
86 |
✓✓ |
49 |
module_categories_.cannot_be_required = std::set<std::string> { |
87 |
|
|
#if !HAVE_INSPECTOR |
88 |
|
|
"inspector", |
89 |
|
|
"internal/util/inspector", |
90 |
|
|
#endif // !HAVE_INSPECTOR |
91 |
|
|
|
92 |
|
|
#if !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT) |
93 |
|
|
"trace_events", |
94 |
|
|
#endif // !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT) |
95 |
|
|
|
96 |
|
|
#if !HAVE_OPENSSL |
97 |
|
|
"crypto", |
98 |
|
|
"crypto/promises", |
99 |
|
|
"https", |
100 |
|
|
"http2", |
101 |
|
|
"tls", |
102 |
|
|
"_tls_common", |
103 |
|
|
"_tls_wrap", |
104 |
|
|
"internal/tls/secure-pair", |
105 |
|
|
"internal/tls/parse-cert-string", |
106 |
|
|
"internal/tls/secure-context", |
107 |
|
|
"internal/http2/core", |
108 |
|
|
"internal/http2/compat", |
109 |
|
|
"internal/policy/manifest", |
110 |
|
|
"internal/process/policy", |
111 |
|
|
"internal/streams/lazy_transform", |
112 |
|
|
#endif // !HAVE_OPENSSL |
113 |
|
|
"sys", // Deprecated. |
114 |
|
|
"wasi", // Experimental. |
115 |
|
|
"internal/test/binding", |
116 |
|
|
"internal/v8_prof_polyfill", |
117 |
|
|
"internal/v8_prof_processor", |
118 |
|
42 |
}; |
119 |
|
|
|
120 |
✓✓ |
2093 |
for (auto const& x : source_) { |
121 |
|
2086 |
const std::string& id = x.first; |
122 |
✓✓ |
10430 |
for (auto const& prefix : prefixes) { |
123 |
✓✓ |
8344 |
if (prefix.length() > id.length()) { |
124 |
|
2443 |
continue; |
125 |
|
|
} |
126 |
✓✓✓✓ ✓✓ |
6174 |
if (id.find(prefix) == 0 && |
127 |
|
273 |
module_categories_.can_be_required.count(id) == 0) { |
128 |
|
266 |
module_categories_.cannot_be_required.emplace(id); |
129 |
|
|
} |
130 |
|
|
} |
131 |
|
|
} |
132 |
|
|
|
133 |
✓✓ |
2093 |
for (auto const& x : source_) { |
134 |
|
2086 |
const std::string& id = x.first; |
135 |
✓✓ |
2086 |
if (0 == module_categories_.cannot_be_required.count(id)) { |
136 |
|
1785 |
module_categories_.can_be_required.emplace(id); |
137 |
|
|
} |
138 |
|
|
} |
139 |
|
|
|
140 |
|
7 |
module_categories_.is_initialized = true; |
141 |
|
|
} |
142 |
|
|
|
143 |
|
1 |
const std::set<std::string>& NativeModuleLoader::GetCannotBeRequired() { |
144 |
|
1 |
InitializeModuleCategories(); |
145 |
|
1 |
return module_categories_.cannot_be_required; |
146 |
|
|
} |
147 |
|
|
|
148 |
|
1789 |
const std::set<std::string>& NativeModuleLoader::GetCanBeRequired() { |
149 |
|
1789 |
InitializeModuleCategories(); |
150 |
|
1789 |
return module_categories_.can_be_required; |
151 |
|
|
} |
152 |
|
|
|
153 |
|
1788 |
bool NativeModuleLoader::CanBeRequired(const char* id) { |
154 |
|
1788 |
return GetCanBeRequired().count(id) == 1; |
155 |
|
|
} |
156 |
|
|
|
157 |
|
|
bool NativeModuleLoader::CannotBeRequired(const char* id) { |
158 |
|
|
return GetCannotBeRequired().count(id) == 1; |
159 |
|
|
} |
160 |
|
|
|
161 |
|
5187 |
NativeModuleCacheMap* NativeModuleLoader::code_cache() { |
162 |
|
5187 |
return &code_cache_; |
163 |
|
|
} |
164 |
|
|
|
165 |
|
|
ScriptCompiler::CachedData* NativeModuleLoader::GetCodeCache( |
166 |
|
|
const char* id) const { |
167 |
|
|
Mutex::ScopedLock lock(code_cache_mutex_); |
168 |
|
|
const auto it = code_cache_.find(id); |
169 |
|
|
if (it == code_cache_.end()) { |
170 |
|
|
// The module has not been compiled before. |
171 |
|
|
return nullptr; |
172 |
|
|
} |
173 |
|
|
return it->second.get(); |
174 |
|
|
} |
175 |
|
|
|
176 |
|
399318 |
MaybeLocal<Function> NativeModuleLoader::CompileAsModule( |
177 |
|
|
Local<Context> context, |
178 |
|
|
const char* id, |
179 |
|
|
NativeModuleLoader::Result* result) { |
180 |
|
399318 |
Isolate* isolate = context->GetIsolate(); |
181 |
|
|
std::vector<Local<String>> parameters = { |
182 |
|
399318 |
FIXED_ONE_BYTE_STRING(isolate, "exports"), |
183 |
|
399318 |
FIXED_ONE_BYTE_STRING(isolate, "require"), |
184 |
|
399318 |
FIXED_ONE_BYTE_STRING(isolate, "module"), |
185 |
|
399318 |
FIXED_ONE_BYTE_STRING(isolate, "process"), |
186 |
|
399318 |
FIXED_ONE_BYTE_STRING(isolate, "internalBinding"), |
187 |
|
798636 |
FIXED_ONE_BYTE_STRING(isolate, "primordials")}; |
188 |
|
399318 |
return LookupAndCompile(context, id, ¶meters, result); |
189 |
|
|
} |
190 |
|
|
|
191 |
|
|
#ifdef NODE_BUILTIN_MODULES_PATH |
192 |
|
|
static std::string OnDiskFileName(const char* id) { |
193 |
|
|
std::string filename = NODE_BUILTIN_MODULES_PATH; |
194 |
|
|
filename += "/"; |
195 |
|
|
|
196 |
|
|
if (strncmp(id, "internal/deps", strlen("internal/deps")) == 0) { |
197 |
|
|
id += strlen("internal/"); |
198 |
|
|
} else { |
199 |
|
|
filename += "lib/"; |
200 |
|
|
} |
201 |
|
|
filename += id; |
202 |
|
|
filename += ".js"; |
203 |
|
|
|
204 |
|
|
return filename; |
205 |
|
|
} |
206 |
|
|
#endif // NODE_BUILTIN_MODULES_PATH |
207 |
|
|
|
208 |
|
409958 |
MaybeLocal<String> NativeModuleLoader::LoadBuiltinModuleSource(Isolate* isolate, |
209 |
|
|
const char* id) { |
210 |
|
|
#ifdef NODE_BUILTIN_MODULES_PATH |
211 |
|
|
if (strncmp(id, "embedder_main_", strlen("embedder_main_")) == 0) { |
212 |
|
|
#endif // NODE_BUILTIN_MODULES_PATH |
213 |
|
409958 |
const auto source_it = source_.find(id); |
214 |
✗✓ |
409958 |
if (UNLIKELY(source_it == source_.end())) { |
215 |
|
|
fprintf(stderr, "Cannot find native builtin: \"%s\".\n", id); |
216 |
|
|
ABORT(); |
217 |
|
|
} |
218 |
|
819916 |
return source_it->second.ToStringChecked(isolate); |
219 |
|
|
#ifdef NODE_BUILTIN_MODULES_PATH |
220 |
|
|
} |
221 |
|
|
std::string filename = OnDiskFileName(id); |
222 |
|
|
|
223 |
|
|
std::string contents; |
224 |
|
|
int r = ReadFileSync(&contents, filename.c_str()); |
225 |
|
|
if (r != 0) { |
226 |
|
|
const std::string buf = SPrintF("Cannot read local builtin. %s: %s \"%s\"", |
227 |
|
|
uv_err_name(r), |
228 |
|
|
uv_strerror(r), |
229 |
|
|
filename); |
230 |
|
|
Local<String> message = OneByteString(isolate, buf.c_str()); |
231 |
|
|
isolate->ThrowException(v8::Exception::Error(message)); |
232 |
|
|
return MaybeLocal<String>(); |
233 |
|
|
} |
234 |
|
|
return String::NewFromUtf8( |
235 |
|
|
isolate, contents.c_str(), v8::NewStringType::kNormal, contents.length()); |
236 |
|
|
#endif // NODE_BUILTIN_MODULES_PATH |
237 |
|
|
} |
238 |
|
|
|
239 |
|
|
// Returns Local<Function> of the compiled module if return_code_cache |
240 |
|
|
// is false (we are only compiling the function). |
241 |
|
|
// Otherwise return a Local<Object> containing the cache. |
242 |
|
409958 |
MaybeLocal<Function> NativeModuleLoader::LookupAndCompile( |
243 |
|
|
Local<Context> context, |
244 |
|
|
const char* id, |
245 |
|
|
std::vector<Local<String>>* parameters, |
246 |
|
|
NativeModuleLoader::Result* result) { |
247 |
|
409958 |
Isolate* isolate = context->GetIsolate(); |
248 |
|
409958 |
EscapableHandleScope scope(isolate); |
249 |
|
|
|
250 |
|
|
Local<String> source; |
251 |
✗✓ |
819916 |
if (!LoadBuiltinModuleSource(isolate, id).ToLocal(&source)) { |
252 |
|
|
return {}; |
253 |
|
|
} |
254 |
|
|
|
255 |
|
1229874 |
std::string filename_s = std::string("node:") + id; |
256 |
|
|
Local<String> filename = |
257 |
|
409958 |
OneByteString(isolate, filename_s.c_str(), filename_s.size()); |
258 |
✓✗ |
409958 |
ScriptOrigin origin(isolate, filename, 0, 0, true); |
259 |
|
|
|
260 |
|
409958 |
ScriptCompiler::CachedData* cached_data = nullptr; |
261 |
|
|
{ |
262 |
|
|
// Note: The lock here should not extend into the |
263 |
|
|
// `CompileFunctionInContext()` call below, because this function may |
264 |
|
|
// recurse if there is a syntax error during bootstrap (because the fatal |
265 |
|
|
// exception handler is invoked, which may load built-in modules). |
266 |
|
819916 |
Mutex::ScopedLock lock(code_cache_mutex_); |
267 |
|
409958 |
auto cache_it = code_cache_.find(id); |
268 |
✓✓ |
409958 |
if (cache_it != code_cache_.end()) { |
269 |
|
|
// Transfer ownership to ScriptCompiler::Source later. |
270 |
|
393792 |
cached_data = cache_it->second.release(); |
271 |
|
393792 |
code_cache_.erase(cache_it); |
272 |
|
|
} |
273 |
|
|
} |
274 |
|
|
|
275 |
|
409958 |
const bool has_cache = cached_data != nullptr; |
276 |
|
409958 |
ScriptCompiler::CompileOptions options = |
277 |
✓✓ |
409958 |
has_cache ? ScriptCompiler::kConsumeCodeCache |
278 |
|
|
: ScriptCompiler::kEagerCompile; |
279 |
|
409958 |
ScriptCompiler::Source script_source(source, origin, cached_data); |
280 |
|
|
|
281 |
|
|
per_process::Debug(DebugCategory::CODE_CACHE, |
282 |
|
|
"Compiling %s %s code cache\n", |
283 |
|
|
id, |
284 |
✓✓ |
409958 |
has_cache ? "with" : "without"); |
285 |
|
|
|
286 |
|
|
MaybeLocal<Function> maybe_fun = |
287 |
|
|
ScriptCompiler::CompileFunctionInContext(context, |
288 |
|
|
&script_source, |
289 |
|
|
parameters->size(), |
290 |
|
|
parameters->data(), |
291 |
|
|
0, |
292 |
|
|
nullptr, |
293 |
|
409958 |
options); |
294 |
|
|
|
295 |
|
|
// This could fail when there are early errors in the native modules, |
296 |
|
|
// e.g. the syntax errors |
297 |
|
|
Local<Function> fun; |
298 |
✗✓ |
409958 |
if (!maybe_fun.ToLocal(&fun)) { |
299 |
|
|
// In the case of early errors, v8 is already capable of |
300 |
|
|
// decorating the stack for us - note that we use CompileFunctionInContext |
301 |
|
|
// so there is no need to worry about wrappers. |
302 |
|
|
return MaybeLocal<Function>(); |
303 |
|
|
} |
304 |
|
|
|
305 |
|
|
// XXX(joyeecheung): this bookkeeping is not exactly accurate because |
306 |
|
|
// it only starts after the Environment is created, so the per_context.js |
307 |
|
|
// will never be in any of these two sets, but the two sets are only for |
308 |
|
|
// testing anyway. |
309 |
|
|
|
310 |
✓✓ |
393792 |
*result = (has_cache && !script_source.GetCachedData()->rejected) |
311 |
✓✓ |
803750 |
? Result::kWithCache |
312 |
|
|
: Result::kWithoutCache; |
313 |
|
|
|
314 |
✓✓ |
409958 |
if (has_cache) { |
315 |
|
393792 |
per_process::Debug(DebugCategory::CODE_CACHE, |
316 |
|
|
"Code cache of %s (%s) %s\n", |
317 |
|
|
id, |
318 |
|
393792 |
script_source.GetCachedData()->buffer_policy == |
319 |
|
|
ScriptCompiler::CachedData::BufferNotOwned |
320 |
✗✓ |
787584 |
? "BufferNotOwned" |
321 |
|
|
: "BufferOwned", |
322 |
✓✓ |
393792 |
script_source.GetCachedData()->rejected ? "is rejected" |
323 |
|
|
: "is accepted"); |
324 |
|
|
} |
325 |
|
|
|
326 |
|
|
// Generate new cache for next compilation |
327 |
|
|
std::unique_ptr<ScriptCompiler::CachedData> new_cached_data( |
328 |
|
409958 |
ScriptCompiler::CreateCodeCacheForFunction(fun)); |
329 |
✗✓ |
409958 |
CHECK_NOT_NULL(new_cached_data); |
330 |
|
|
|
331 |
|
|
{ |
332 |
|
819916 |
Mutex::ScopedLock lock(code_cache_mutex_); |
333 |
|
409958 |
const auto it = code_cache_.find(id); |
334 |
|
|
// TODO(joyeecheung): it's safer for each thread to have its own |
335 |
|
|
// copy of the code cache map. |
336 |
✓✓ |
409958 |
if (it == code_cache_.end()) { |
337 |
|
408211 |
code_cache_.emplace(id, std::move(new_cached_data)); |
338 |
|
|
} else { |
339 |
|
1747 |
it->second.reset(new_cached_data.release()); |
340 |
|
|
} |
341 |
|
|
} |
342 |
|
|
|
343 |
|
409958 |
return scope.Escape(fun); |
344 |
|
|
} |
345 |
|
|
|
346 |
|
|
} // namespace native_module |
347 |
|
|
} // namespace node |