1 |
|
|
#include "node_builtins.h" |
2 |
|
|
#include "debug_utils-inl.h" |
3 |
|
|
#include "env-inl.h" |
4 |
|
|
#include "node_external_reference.h" |
5 |
|
|
#include "node_internals.h" |
6 |
|
|
#include "util-inl.h" |
7 |
|
|
|
8 |
|
|
namespace node { |
9 |
|
|
namespace builtins { |
10 |
|
|
|
11 |
|
|
using v8::Context; |
12 |
|
|
using v8::DEFAULT; |
13 |
|
|
using v8::EscapableHandleScope; |
14 |
|
|
using v8::Function; |
15 |
|
|
using v8::FunctionCallbackInfo; |
16 |
|
|
using v8::IntegrityLevel; |
17 |
|
|
using v8::Isolate; |
18 |
|
|
using v8::Local; |
19 |
|
|
using v8::MaybeLocal; |
20 |
|
|
using v8::Name; |
21 |
|
|
using v8::None; |
22 |
|
|
using v8::Object; |
23 |
|
|
using v8::PropertyCallbackInfo; |
24 |
|
|
using v8::ScriptCompiler; |
25 |
|
|
using v8::ScriptOrigin; |
26 |
|
|
using v8::Set; |
27 |
|
|
using v8::SideEffectType; |
28 |
|
|
using v8::String; |
29 |
|
|
using v8::Undefined; |
30 |
|
|
using v8::Value; |
31 |
|
|
|
32 |
|
|
BuiltinLoader BuiltinLoader::instance_; |
33 |
|
|
|
34 |
|
5710 |
BuiltinLoader::BuiltinLoader() : config_(GetConfig()), has_code_cache_(false) { |
35 |
|
5710 |
LoadJavaScriptSource(); |
36 |
|
|
#if defined(NODE_HAVE_I18N_SUPPORT) |
37 |
|
|
#ifdef NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_LEXER_PATH |
38 |
|
|
AddExternalizedBuiltin( |
39 |
|
|
"internal/deps/cjs-module-lexer/lexer", |
40 |
|
|
STRINGIFY(NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_LEXER_PATH)); |
41 |
|
|
#endif // NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_LEXER_PATH |
42 |
|
|
|
43 |
|
|
#ifdef NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_DIST_LEXER_PATH |
44 |
|
|
AddExternalizedBuiltin( |
45 |
|
|
"internal/deps/cjs-module-lexer/dist/lexer", |
46 |
|
|
STRINGIFY(NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_DIST_LEXER_PATH)); |
47 |
|
|
#endif // NODE_SHARED_BUILTIN_CJS_MODULE_LEXER_DIST_LEXER_PATH |
48 |
|
|
|
49 |
|
|
#ifdef NODE_SHARED_BUILTIN_UNDICI_UNDICI_PATH |
50 |
|
|
AddExternalizedBuiltin("internal/deps/undici/undici", |
51 |
|
|
STRINGIFY(NODE_SHARED_BUILTIN_UNDICI_UNDICI_PATH)); |
52 |
|
|
#endif // NODE_SHARED_BUILTIN_UNDICI_UNDICI_PATH |
53 |
|
|
#endif // NODE_HAVE_I18N_SUPPORT |
54 |
|
5710 |
} |
55 |
|
|
|
56 |
|
928524 |
BuiltinLoader* BuiltinLoader::GetInstance() { |
57 |
|
928524 |
return &instance_; |
58 |
|
|
} |
59 |
|
|
|
60 |
|
|
bool BuiltinLoader::Exists(const char* id) { |
61 |
|
|
auto& source = GetInstance()->source_; |
62 |
|
|
return source.find(id) != source.end(); |
63 |
|
|
} |
64 |
|
|
|
65 |
|
19 |
bool BuiltinLoader::Add(const char* id, const UnionBytes& source) { |
66 |
|
19 |
auto result = GetInstance()->source_.emplace(id, source); |
67 |
|
19 |
return result.second; |
68 |
|
|
} |
69 |
|
|
|
70 |
|
42 |
Local<Object> BuiltinLoader::GetSourceObject(Local<Context> context) { |
71 |
|
42 |
Isolate* isolate = context->GetIsolate(); |
72 |
|
42 |
Local<Object> out = Object::New(isolate); |
73 |
|
42 |
auto& source = GetInstance()->source_; |
74 |
✓✓ |
13356 |
for (auto const& x : source) { |
75 |
|
13314 |
Local<String> key = OneByteString(isolate, x.first.c_str(), x.first.size()); |
76 |
|
39942 |
out->Set(context, key, x.second.ToStringChecked(isolate)).FromJust(); |
77 |
|
|
} |
78 |
|
42 |
return out; |
79 |
|
|
} |
80 |
|
|
|
81 |
|
842 |
Local<String> BuiltinLoader::GetConfigString(Isolate* isolate) { |
82 |
|
842 |
return GetInstance()->config_.ToStringChecked(isolate); |
83 |
|
|
} |
84 |
|
|
|
85 |
|
846 |
std::vector<std::string> BuiltinLoader::GetBuiltinIds() { |
86 |
|
846 |
std::vector<std::string> ids; |
87 |
|
846 |
ids.reserve(source_.size()); |
88 |
✓✓ |
269211 |
for (auto const& x : source_) { |
89 |
|
268365 |
ids.emplace_back(x.first); |
90 |
|
|
} |
91 |
|
846 |
return ids; |
92 |
|
|
} |
93 |
|
|
|
94 |
|
2 |
void BuiltinLoader::InitializeBuiltinCategories() { |
95 |
✓✓ |
2 |
if (builtin_categories_.is_initialized) { |
96 |
|
|
DCHECK(!builtin_categories_.can_be_required.empty()); |
97 |
|
1 |
return; |
98 |
|
|
} |
99 |
|
|
|
100 |
|
|
std::vector<std::string> prefixes = { |
101 |
|
|
#if !HAVE_OPENSSL |
102 |
|
|
"internal/crypto/", |
103 |
|
|
"internal/debugger/", |
104 |
|
|
#endif // !HAVE_OPENSSL |
105 |
|
|
|
106 |
|
|
"internal/bootstrap/", |
107 |
|
|
"internal/per_context/", |
108 |
|
|
"internal/deps/", |
109 |
|
|
"internal/main/" |
110 |
|
7 |
}; |
111 |
|
|
|
112 |
|
|
builtin_categories_.can_be_required.emplace( |
113 |
|
1 |
"internal/deps/cjs-module-lexer/lexer"); |
114 |
|
|
|
115 |
✓✓ |
7 |
builtin_categories_.cannot_be_required = std::set<std::string> { |
116 |
|
|
#if !HAVE_INSPECTOR |
117 |
|
|
"inspector", "inspector/promises", "internal/util/inspector", |
118 |
|
|
#endif // !HAVE_INSPECTOR |
119 |
|
|
|
120 |
|
|
#if !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT) |
121 |
|
|
"trace_events", |
122 |
|
|
#endif // !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT) |
123 |
|
|
|
124 |
|
|
#if !HAVE_OPENSSL |
125 |
|
|
"crypto", "crypto/promises", "https", "http2", "tls", "_tls_common", |
126 |
|
|
"_tls_wrap", "internal/tls/secure-pair", |
127 |
|
|
"internal/tls/parse-cert-string", "internal/tls/secure-context", |
128 |
|
|
"internal/http2/core", "internal/http2/compat", |
129 |
|
|
"internal/policy/manifest", "internal/process/policy", |
130 |
|
|
"internal/streams/lazy_transform", |
131 |
|
|
#endif // !HAVE_OPENSSL |
132 |
|
|
"sys", // Deprecated. |
133 |
|
|
"wasi", // Experimental. |
134 |
|
|
"internal/test/binding", "internal/v8_prof_polyfill", |
135 |
|
|
"internal/v8_prof_processor", |
136 |
|
6 |
}; |
137 |
|
|
|
138 |
✓✓ |
318 |
for (auto const& x : source_) { |
139 |
|
317 |
const std::string& id = x.first; |
140 |
✓✓ |
1585 |
for (auto const& prefix : prefixes) { |
141 |
✓✓ |
1268 |
if (prefix.length() > id.length()) { |
142 |
|
364 |
continue; |
143 |
|
|
} |
144 |
✓✓✓✓ ✓✓ |
943 |
if (id.find(prefix) == 0 && |
145 |
|
39 |
builtin_categories_.can_be_required.count(id) == 0) { |
146 |
|
38 |
builtin_categories_.cannot_be_required.emplace(id); |
147 |
|
|
} |
148 |
|
|
} |
149 |
|
|
} |
150 |
|
|
|
151 |
✓✓ |
318 |
for (auto const& x : source_) { |
152 |
|
317 |
const std::string& id = x.first; |
153 |
✓✓ |
317 |
if (0 == builtin_categories_.cannot_be_required.count(id)) { |
154 |
|
274 |
builtin_categories_.can_be_required.emplace(id); |
155 |
|
|
} |
156 |
|
|
} |
157 |
|
|
|
158 |
|
1 |
builtin_categories_.is_initialized = true; |
159 |
|
|
} |
160 |
|
|
|
161 |
|
1 |
const std::set<std::string>& BuiltinLoader::GetCannotBeRequired() { |
162 |
|
1 |
InitializeBuiltinCategories(); |
163 |
|
1 |
return builtin_categories_.cannot_be_required; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
1 |
const std::set<std::string>& BuiltinLoader::GetCanBeRequired() { |
167 |
|
1 |
InitializeBuiltinCategories(); |
168 |
|
1 |
return builtin_categories_.can_be_required; |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
bool BuiltinLoader::CanBeRequired(const char* id) { |
172 |
|
|
return GetCanBeRequired().count(id) == 1; |
173 |
|
|
} |
174 |
|
|
|
175 |
|
|
bool BuiltinLoader::CannotBeRequired(const char* id) { |
176 |
|
|
return GetCannotBeRequired().count(id) == 1; |
177 |
|
|
} |
178 |
|
|
|
179 |
|
5638 |
BuiltinCodeCacheMap* BuiltinLoader::code_cache() { |
180 |
|
5638 |
return &code_cache_; |
181 |
|
|
} |
182 |
|
|
|
183 |
|
|
ScriptCompiler::CachedData* BuiltinLoader::GetCodeCache(const char* id) const { |
184 |
|
|
Mutex::ScopedLock lock(code_cache_mutex_); |
185 |
|
|
const auto it = code_cache_.find(id); |
186 |
|
|
if (it == code_cache_.end()) { |
187 |
|
|
// The module has not been compiled before. |
188 |
|
|
return nullptr; |
189 |
|
|
} |
190 |
|
|
return it->second.get(); |
191 |
|
|
} |
192 |
|
|
|
193 |
|
|
#ifdef NODE_BUILTIN_MODULES_PATH |
194 |
|
|
static std::string OnDiskFileName(const char* id) { |
195 |
|
|
std::string filename = NODE_BUILTIN_MODULES_PATH; |
196 |
|
|
filename += "/"; |
197 |
|
|
|
198 |
|
|
if (strncmp(id, "internal/deps", strlen("internal/deps")) == 0) { |
199 |
|
|
id += strlen("internal/"); |
200 |
|
|
} else { |
201 |
|
|
filename += "lib/"; |
202 |
|
|
} |
203 |
|
|
filename += id; |
204 |
|
|
filename += ".js"; |
205 |
|
|
|
206 |
|
|
return filename; |
207 |
|
|
} |
208 |
|
|
#endif // NODE_BUILTIN_MODULES_PATH |
209 |
|
|
|
210 |
|
467061 |
MaybeLocal<String> BuiltinLoader::LoadBuiltinSource(Isolate* isolate, |
211 |
|
|
const char* id) { |
212 |
|
|
#ifdef NODE_BUILTIN_MODULES_PATH |
213 |
|
|
if (strncmp(id, "embedder_main_", strlen("embedder_main_")) == 0) { |
214 |
|
|
#endif // NODE_BUILTIN_MODULES_PATH |
215 |
|
467061 |
const auto source_it = source_.find(id); |
216 |
✗✓ |
467061 |
if (UNLIKELY(source_it == source_.end())) { |
217 |
|
|
fprintf(stderr, "Cannot find native builtin: \"%s\".\n", id); |
218 |
|
|
ABORT(); |
219 |
|
|
} |
220 |
|
934122 |
return source_it->second.ToStringChecked(isolate); |
221 |
|
|
#ifdef NODE_BUILTIN_MODULES_PATH |
222 |
|
|
} |
223 |
|
|
std::string filename = OnDiskFileName(id); |
224 |
|
|
|
225 |
|
|
std::string contents; |
226 |
|
|
int r = ReadFileSync(&contents, filename.c_str()); |
227 |
|
|
if (r != 0) { |
228 |
|
|
const std::string buf = SPrintF("Cannot read local builtin. %s: %s \"%s\"", |
229 |
|
|
uv_err_name(r), |
230 |
|
|
uv_strerror(r), |
231 |
|
|
filename); |
232 |
|
|
Local<String> message = OneByteString(isolate, buf.c_str()); |
233 |
|
|
isolate->ThrowException(v8::Exception::Error(message)); |
234 |
|
|
return MaybeLocal<String>(); |
235 |
|
|
} |
236 |
|
|
return String::NewFromUtf8( |
237 |
|
|
isolate, contents.c_str(), v8::NewStringType::kNormal, contents.length()); |
238 |
|
|
#endif // NODE_BUILTIN_MODULES_PATH |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
#if defined(NODE_HAVE_I18N_SUPPORT) |
242 |
|
|
void BuiltinLoader::AddExternalizedBuiltin(const char* id, |
243 |
|
|
const char* filename) { |
244 |
|
|
std::string source; |
245 |
|
|
int r = ReadFileSync(&source, filename); |
246 |
|
|
if (r != 0) { |
247 |
|
|
fprintf( |
248 |
|
|
stderr, "Cannot load externalized builtin: \"%s:%s\".\n", id, filename); |
249 |
|
|
ABORT(); |
250 |
|
|
return; |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8( |
254 |
|
|
icu::StringPiece(source.data(), source.length())); |
255 |
|
|
auto source_utf16 = std::make_unique<icu::UnicodeString>(utf16); |
256 |
|
|
Add(id, |
257 |
|
|
UnionBytes(reinterpret_cast<const uint16_t*>((*source_utf16).getBuffer()), |
258 |
|
|
utf16.length())); |
259 |
|
|
// keep source bytes for builtin alive while BuiltinLoader exists |
260 |
|
|
GetInstance()->externalized_source_bytes_.push_back(std::move(source_utf16)); |
261 |
|
|
} |
262 |
|
|
#endif // NODE_HAVE_I18N_SUPPORT |
263 |
|
|
|
264 |
|
|
// Returns Local<Function> of the compiled module if return_code_cache |
265 |
|
|
// is false (we are only compiling the function). |
266 |
|
|
// Otherwise return a Local<Object> containing the cache. |
267 |
|
467061 |
MaybeLocal<Function> BuiltinLoader::LookupAndCompileInternal( |
268 |
|
|
Local<Context> context, |
269 |
|
|
const char* id, |
270 |
|
|
std::vector<Local<String>>* parameters, |
271 |
|
|
BuiltinLoader::Result* result) { |
272 |
|
467061 |
Isolate* isolate = context->GetIsolate(); |
273 |
|
467061 |
EscapableHandleScope scope(isolate); |
274 |
|
|
|
275 |
|
|
Local<String> source; |
276 |
✗✓ |
934122 |
if (!LoadBuiltinSource(isolate, id).ToLocal(&source)) { |
277 |
|
|
return {}; |
278 |
|
|
} |
279 |
|
|
|
280 |
|
1401183 |
std::string filename_s = std::string("node:") + id; |
281 |
|
|
Local<String> filename = |
282 |
|
467061 |
OneByteString(isolate, filename_s.c_str(), filename_s.size()); |
283 |
✓✗ |
467061 |
ScriptOrigin origin(isolate, filename, 0, 0, true); |
284 |
|
|
|
285 |
|
467061 |
ScriptCompiler::CachedData* cached_data = nullptr; |
286 |
|
|
{ |
287 |
|
|
// Note: The lock here should not extend into the |
288 |
|
|
// `CompileFunction()` call below, because this function may recurse if |
289 |
|
|
// there is a syntax error during bootstrap (because the fatal exception |
290 |
|
|
// handler is invoked, which may load built-in modules). |
291 |
|
934122 |
Mutex::ScopedLock lock(code_cache_mutex_); |
292 |
|
467061 |
auto cache_it = code_cache_.find(id); |
293 |
✓✓ |
467061 |
if (cache_it != code_cache_.end()) { |
294 |
|
|
// Transfer ownership to ScriptCompiler::Source later. |
295 |
|
462079 |
cached_data = cache_it->second.release(); |
296 |
|
462079 |
code_cache_.erase(cache_it); |
297 |
|
|
} |
298 |
|
|
} |
299 |
|
|
|
300 |
|
467061 |
const bool has_cache = cached_data != nullptr; |
301 |
|
467061 |
ScriptCompiler::CompileOptions options = |
302 |
✓✓ |
467061 |
has_cache ? ScriptCompiler::kConsumeCodeCache |
303 |
|
|
: ScriptCompiler::kEagerCompile; |
304 |
|
467061 |
ScriptCompiler::Source script_source(source, origin, cached_data); |
305 |
|
|
|
306 |
|
|
per_process::Debug(DebugCategory::CODE_CACHE, |
307 |
|
|
"Compiling %s %s code cache\n", |
308 |
|
|
id, |
309 |
✓✓ |
467061 |
has_cache ? "with" : "without"); |
310 |
|
|
|
311 |
|
|
MaybeLocal<Function> maybe_fun = |
312 |
|
|
ScriptCompiler::CompileFunction(context, |
313 |
|
|
&script_source, |
314 |
|
|
parameters->size(), |
315 |
|
|
parameters->data(), |
316 |
|
|
0, |
317 |
|
|
nullptr, |
318 |
|
467061 |
options); |
319 |
|
|
|
320 |
|
|
// This could fail when there are early errors in the built-in modules, |
321 |
|
|
// e.g. the syntax errors |
322 |
|
|
Local<Function> fun; |
323 |
✗✓ |
467061 |
if (!maybe_fun.ToLocal(&fun)) { |
324 |
|
|
// In the case of early errors, v8 is already capable of |
325 |
|
|
// decorating the stack for us - note that we use CompileFunction |
326 |
|
|
// so there is no need to worry about wrappers. |
327 |
|
|
return MaybeLocal<Function>(); |
328 |
|
|
} |
329 |
|
|
|
330 |
|
|
// XXX(joyeecheung): this bookkeeping is not exactly accurate because |
331 |
|
|
// it only starts after the Environment is created, so the per_context.js |
332 |
|
|
// will never be in any of these two sets, but the two sets are only for |
333 |
|
|
// testing anyway. |
334 |
|
|
|
335 |
✓✓ |
462079 |
*result = (has_cache && !script_source.GetCachedData()->rejected) |
336 |
✓✓ |
929140 |
? Result::kWithCache |
337 |
|
|
: Result::kWithoutCache; |
338 |
|
|
|
339 |
✓✓ |
467061 |
if (has_cache) { |
340 |
|
462079 |
per_process::Debug(DebugCategory::CODE_CACHE, |
341 |
|
|
"Code cache of %s (%s) %s\n", |
342 |
|
|
id, |
343 |
|
462079 |
script_source.GetCachedData()->buffer_policy == |
344 |
|
|
ScriptCompiler::CachedData::BufferNotOwned |
345 |
✗✓ |
924158 |
? "BufferNotOwned" |
346 |
|
|
: "BufferOwned", |
347 |
✓✓ |
462079 |
script_source.GetCachedData()->rejected ? "is rejected" |
348 |
|
|
: "is accepted"); |
349 |
|
|
} |
350 |
|
|
|
351 |
|
|
// Generate new cache for next compilation |
352 |
|
|
std::unique_ptr<ScriptCompiler::CachedData> new_cached_data( |
353 |
|
467061 |
ScriptCompiler::CreateCodeCacheForFunction(fun)); |
354 |
✗✓ |
467061 |
CHECK_NOT_NULL(new_cached_data); |
355 |
|
|
|
356 |
|
|
{ |
357 |
|
934122 |
Mutex::ScopedLock lock(code_cache_mutex_); |
358 |
|
467061 |
const auto it = code_cache_.find(id); |
359 |
|
|
// TODO(joyeecheung): it's safer for each thread to have its own |
360 |
|
|
// copy of the code cache map. |
361 |
✓✓ |
467061 |
if (it == code_cache_.end()) { |
362 |
|
466080 |
code_cache_.emplace(id, std::move(new_cached_data)); |
363 |
|
|
} else { |
364 |
|
981 |
it->second.reset(new_cached_data.release()); |
365 |
|
|
} |
366 |
|
|
} |
367 |
|
|
|
368 |
|
467061 |
return scope.Escape(fun); |
369 |
|
|
} |
370 |
|
|
|
371 |
|
|
// Returns Local<Function> of the compiled module if return_code_cache |
372 |
|
|
// is false (we are only compiling the function). |
373 |
|
|
// Otherwise return a Local<Object> containing the cache. |
374 |
|
467061 |
MaybeLocal<Function> BuiltinLoader::LookupAndCompile( |
375 |
|
|
Local<Context> context, const char* id, Environment* optional_env) { |
376 |
|
|
Result result; |
377 |
|
467061 |
std::vector<Local<String>> parameters; |
378 |
|
467061 |
Isolate* isolate = context->GetIsolate(); |
379 |
|
|
// Detects parameters of the scripts based on module ids. |
380 |
|
|
// internal/bootstrap/loaders: process, getLinkedBinding, |
381 |
|
|
// getInternalBinding, primordials |
382 |
✓✓ |
467061 |
if (strcmp(id, "internal/bootstrap/loaders") == 0) { |
383 |
|
807 |
parameters = { |
384 |
|
807 |
FIXED_ONE_BYTE_STRING(isolate, "process"), |
385 |
|
807 |
FIXED_ONE_BYTE_STRING(isolate, "getLinkedBinding"), |
386 |
|
807 |
FIXED_ONE_BYTE_STRING(isolate, "getInternalBinding"), |
387 |
|
807 |
FIXED_ONE_BYTE_STRING(isolate, "primordials"), |
388 |
|
807 |
}; |
389 |
✓✓ |
466254 |
} else if (strncmp(id, |
390 |
|
|
"internal/per_context/", |
391 |
|
|
strlen("internal/per_context/")) == 0) { |
392 |
|
|
// internal/per_context/*: global, exports, primordials |
393 |
|
456 |
parameters = { |
394 |
|
456 |
FIXED_ONE_BYTE_STRING(isolate, "exports"), |
395 |
|
456 |
FIXED_ONE_BYTE_STRING(isolate, "primordials"), |
396 |
|
456 |
}; |
397 |
✓✓ |
465798 |
} else if (strncmp(id, "internal/main/", strlen("internal/main/")) == 0 || |
398 |
✓✓ |
459312 |
strncmp(id, |
399 |
|
|
"internal/bootstrap/", |
400 |
|
|
strlen("internal/bootstrap/")) == 0) { |
401 |
|
|
// internal/main/*, internal/bootstrap/*: process, require, |
402 |
|
|
// internalBinding, primordials |
403 |
|
9728 |
parameters = { |
404 |
|
9728 |
FIXED_ONE_BYTE_STRING(isolate, "process"), |
405 |
|
9728 |
FIXED_ONE_BYTE_STRING(isolate, "require"), |
406 |
|
9728 |
FIXED_ONE_BYTE_STRING(isolate, "internalBinding"), |
407 |
|
9728 |
FIXED_ONE_BYTE_STRING(isolate, "primordials"), |
408 |
|
9728 |
}; |
409 |
✓✓ |
456070 |
} else if (strncmp(id, "embedder_main_", strlen("embedder_main_")) == 0) { |
410 |
|
|
// Synthetic embedder main scripts from LoadEnvironment(): process, require |
411 |
|
19 |
parameters = { |
412 |
|
19 |
FIXED_ONE_BYTE_STRING(isolate, "process"), |
413 |
|
19 |
FIXED_ONE_BYTE_STRING(isolate, "require"), |
414 |
|
19 |
}; |
415 |
|
|
} else { |
416 |
|
|
// others: exports, require, module, process, internalBinding, primordials |
417 |
|
456051 |
parameters = { |
418 |
|
456051 |
FIXED_ONE_BYTE_STRING(isolate, "exports"), |
419 |
|
456051 |
FIXED_ONE_BYTE_STRING(isolate, "require"), |
420 |
|
456051 |
FIXED_ONE_BYTE_STRING(isolate, "module"), |
421 |
|
456051 |
FIXED_ONE_BYTE_STRING(isolate, "process"), |
422 |
|
456051 |
FIXED_ONE_BYTE_STRING(isolate, "internalBinding"), |
423 |
|
456051 |
FIXED_ONE_BYTE_STRING(isolate, "primordials"), |
424 |
|
456051 |
}; |
425 |
|
|
} |
426 |
|
|
|
427 |
|
|
MaybeLocal<Function> maybe = GetInstance()->LookupAndCompileInternal( |
428 |
|
467061 |
context, id, ¶meters, &result); |
429 |
✓✓ |
467061 |
if (optional_env != nullptr) { |
430 |
|
464484 |
RecordResult(id, result, optional_env); |
431 |
|
|
} |
432 |
|
467061 |
return maybe; |
433 |
|
|
} |
434 |
|
|
|
435 |
|
10414 |
MaybeLocal<Value> BuiltinLoader::CompileAndCall(Local<Context> context, |
436 |
|
|
const char* id, |
437 |
|
|
Realm* realm) { |
438 |
|
10414 |
Isolate* isolate = context->GetIsolate(); |
439 |
|
|
// Arguments must match the parameters specified in |
440 |
|
|
// BuiltinLoader::LookupAndCompile(). |
441 |
|
20528 |
std::vector<Local<Value>> arguments; |
442 |
|
|
// Detects parameters of the scripts based on module ids. |
443 |
|
|
// internal/bootstrap/loaders: process, getLinkedBinding, |
444 |
|
|
// getInternalBinding, primordials |
445 |
✓✓ |
10414 |
if (strcmp(id, "internal/bootstrap/loaders") == 0) { |
446 |
|
|
Local<Value> get_linked_binding; |
447 |
|
|
Local<Value> get_internal_binding; |
448 |
|
800 |
if (!NewFunctionTemplate(isolate, binding::GetLinkedBinding) |
449 |
|
800 |
->GetFunction(context) |
450 |
✓✗ |
1600 |
.ToLocal(&get_linked_binding) || |
451 |
|
800 |
!NewFunctionTemplate(isolate, binding::GetInternalBinding) |
452 |
✗✓ |
1600 |
->GetFunction(context) |
453 |
✗✓ |
800 |
.ToLocal(&get_internal_binding)) { |
454 |
|
|
return MaybeLocal<Value>(); |
455 |
|
|
} |
456 |
|
2400 |
arguments = {realm->process_object(), |
457 |
|
|
get_linked_binding, |
458 |
|
|
get_internal_binding, |
459 |
|
800 |
realm->primordials()}; |
460 |
✓✓ |
9614 |
} else if (strncmp(id, "internal/main/", strlen("internal/main/")) == 0 || |
461 |
✓✓ |
3219 |
strncmp(id, |
462 |
|
|
"internal/bootstrap/", |
463 |
|
|
strlen("internal/bootstrap/")) == 0) { |
464 |
|
|
// internal/main/*, internal/bootstrap/*: process, require, |
465 |
|
|
// internalBinding, primordials |
466 |
|
47975 |
arguments = {realm->process_object(), |
467 |
|
|
realm->builtin_module_require(), |
468 |
|
|
realm->internal_binding_loader(), |
469 |
|
9595 |
realm->primordials()}; |
470 |
✓✗ |
19 |
} else if (strncmp(id, "embedder_main_", strlen("embedder_main_")) == 0) { |
471 |
|
|
// Synthetic embedder main scripts from LoadEnvironment(): process, require |
472 |
|
57 |
arguments = { |
473 |
|
|
realm->process_object(), |
474 |
|
|
realm->builtin_module_require(), |
475 |
|
19 |
}; |
476 |
|
|
} else { |
477 |
|
|
// This should be invoked with the other CompileAndCall() methods, as |
478 |
|
|
// we are unable to generate the arguments. |
479 |
|
|
// Currently there are two cases: |
480 |
|
|
// internal/per_context/*: the arguments are generated in |
481 |
|
|
// InitializePrimordials() |
482 |
|
|
// all the other cases: the arguments are generated in the JS-land loader. |
483 |
|
|
UNREACHABLE(); |
484 |
|
|
} |
485 |
|
|
return CompileAndCall( |
486 |
|
10414 |
context, id, arguments.size(), arguments.data(), realm->env()); |
487 |
|
|
} |
488 |
|
|
|
489 |
|
10849 |
MaybeLocal<Value> BuiltinLoader::CompileAndCall(Local<Context> context, |
490 |
|
|
const char* id, |
491 |
|
|
int argc, |
492 |
|
|
Local<Value> argv[], |
493 |
|
|
Environment* optional_env) { |
494 |
|
|
// Arguments must match the parameters specified in |
495 |
|
|
// BuiltinLoader::LookupAndCompile(). |
496 |
|
10849 |
MaybeLocal<Function> maybe_fn = LookupAndCompile(context, id, optional_env); |
497 |
|
|
Local<Function> fn; |
498 |
✗✓ |
10849 |
if (!maybe_fn.ToLocal(&fn)) { |
499 |
|
|
return MaybeLocal<Value>(); |
500 |
|
|
} |
501 |
|
21698 |
Local<Value> undefined = Undefined(context->GetIsolate()); |
502 |
|
10849 |
return fn->Call(context, undefined, argc, argv); |
503 |
|
|
} |
504 |
|
|
|
505 |
|
7 |
bool BuiltinLoader::CompileAllBuiltins(Local<Context> context) { |
506 |
|
7 |
BuiltinLoader* loader = GetInstance(); |
507 |
|
14 |
std::vector<std::string> ids = loader->GetBuiltinIds(); |
508 |
|
7 |
bool all_succeeded = true; |
509 |
|
7 |
std::string v8_tools_prefix = "internal/deps/v8/tools/"; |
510 |
✓✓ |
2226 |
for (const auto& id : ids) { |
511 |
✓✓ |
2219 |
if (id.compare(0, v8_tools_prefix.size(), v8_tools_prefix) == 0) { |
512 |
|
77 |
continue; |
513 |
|
|
} |
514 |
|
4284 |
v8::TryCatch bootstrapCatch(context->GetIsolate()); |
515 |
|
2142 |
USE(loader->LookupAndCompile(context, id.c_str(), nullptr)); |
516 |
✗✓ |
2142 |
if (bootstrapCatch.HasCaught()) { |
517 |
|
|
per_process::Debug(DebugCategory::CODE_CACHE, |
518 |
|
|
"Failed to compile code cache for %s\n", |
519 |
|
|
id.c_str()); |
520 |
|
|
all_succeeded = false; |
521 |
|
|
PrintCaughtException(context->GetIsolate(), context, bootstrapCatch); |
522 |
|
|
} |
523 |
|
|
} |
524 |
|
7 |
return all_succeeded; |
525 |
|
|
} |
526 |
|
|
|
527 |
|
7 |
void BuiltinLoader::CopyCodeCache(std::vector<CodeCacheInfo>* out) { |
528 |
|
7 |
BuiltinLoader* loader = GetInstance(); |
529 |
|
14 |
Mutex::ScopedLock lock(loader->code_cache_mutex()); |
530 |
|
7 |
auto in = loader->code_cache(); |
531 |
✓✓ |
2149 |
for (auto const& item : *in) { |
532 |
|
2142 |
out->push_back( |
533 |
|
2142 |
{item.first, |
534 |
|
2142 |
{item.second->data, item.second->data + item.second->length}}); |
535 |
|
|
} |
536 |
|
7 |
} |
537 |
|
|
|
538 |
|
5631 |
void BuiltinLoader::RefreshCodeCache(const std::vector<CodeCacheInfo>& in) { |
539 |
|
5631 |
BuiltinLoader* loader = GetInstance(); |
540 |
|
5631 |
Mutex::ScopedLock lock(loader->code_cache_mutex()); |
541 |
|
5631 |
auto out = loader->code_cache(); |
542 |
✓✓ |
1728717 |
for (auto const& item : in) { |
543 |
|
1723086 |
size_t length = item.data.size(); |
544 |
|
1723086 |
uint8_t* buffer = new uint8_t[length]; |
545 |
|
1723086 |
memcpy(buffer, item.data.data(), length); |
546 |
|
|
auto new_cache = std::make_unique<v8::ScriptCompiler::CachedData>( |
547 |
|
3446172 |
buffer, length, v8::ScriptCompiler::CachedData::BufferOwned); |
548 |
|
1723086 |
auto cache_it = out->find(item.id); |
549 |
✗✓ |
1723086 |
if (cache_it != out->end()) { |
550 |
|
|
// Release the old cache and replace it with the new copy. |
551 |
|
|
cache_it->second.reset(new_cache.release()); |
552 |
|
|
} else { |
553 |
|
1723086 |
out->emplace(item.id, new_cache.release()); |
554 |
|
|
} |
555 |
|
|
} |
556 |
|
5631 |
loader->has_code_cache_ = true; |
557 |
|
5631 |
} |
558 |
|
|
|
559 |
|
1 |
void BuiltinLoader::GetBuiltinCategories( |
560 |
|
|
Local<Name> property, const PropertyCallbackInfo<Value>& info) { |
561 |
|
1 |
Environment* env = Environment::GetCurrent(info); |
562 |
|
1 |
Isolate* isolate = env->isolate(); |
563 |
|
1 |
Local<Context> context = env->context(); |
564 |
|
1 |
Local<Object> result = Object::New(isolate); |
565 |
|
|
|
566 |
|
|
// Copy from the per-process categories |
567 |
|
|
std::set<std::string> cannot_be_required = |
568 |
|
1 |
GetInstance()->GetCannotBeRequired(); |
569 |
|
1 |
std::set<std::string> can_be_required = GetInstance()->GetCanBeRequired(); |
570 |
|
|
|
571 |
✗✓ |
1 |
if (!env->owns_process_state()) { |
572 |
|
|
can_be_required.erase("trace_events"); |
573 |
|
|
cannot_be_required.insert("trace_events"); |
574 |
|
|
} |
575 |
|
|
|
576 |
|
|
Local<Value> cannot_be_required_js; |
577 |
|
|
Local<Value> can_be_required_js; |
578 |
|
|
|
579 |
✗✓ |
2 |
if (!ToV8Value(context, cannot_be_required).ToLocal(&cannot_be_required_js)) |
580 |
|
|
return; |
581 |
|
1 |
if (result |
582 |
|
1 |
->Set(context, |
583 |
|
|
OneByteString(isolate, "cannotBeRequired"), |
584 |
|
2 |
cannot_be_required_js) |
585 |
✗✓ |
1 |
.IsNothing()) |
586 |
|
|
return; |
587 |
✗✓ |
2 |
if (!ToV8Value(context, can_be_required).ToLocal(&can_be_required_js)) return; |
588 |
|
1 |
if (result |
589 |
|
1 |
->Set(context, |
590 |
|
|
OneByteString(isolate, "canBeRequired"), |
591 |
|
2 |
can_be_required_js) |
592 |
✗✓ |
1 |
.IsNothing()) { |
593 |
|
|
return; |
594 |
|
|
} |
595 |
|
2 |
info.GetReturnValue().Set(result); |
596 |
|
|
} |
597 |
|
|
|
598 |
|
1 |
void BuiltinLoader::GetCacheUsage(const FunctionCallbackInfo<Value>& args) { |
599 |
|
1 |
Environment* env = Environment::GetCurrent(args); |
600 |
|
1 |
Isolate* isolate = env->isolate(); |
601 |
|
1 |
Local<Context> context = env->context(); |
602 |
|
1 |
Local<Object> result = Object::New(isolate); |
603 |
|
|
|
604 |
|
|
Local<Value> builtins_with_cache_js; |
605 |
|
|
Local<Value> builtins_without_cache_js; |
606 |
|
|
Local<Value> builtins_in_snapshot_js; |
607 |
|
1 |
if (!ToV8Value(context, env->builtins_with_cache) |
608 |
✗✓ |
1 |
.ToLocal(&builtins_with_cache_js)) { |
609 |
|
|
return; |
610 |
|
|
} |
611 |
|
1 |
if (result |
612 |
|
1 |
->Set(env->context(), |
613 |
|
|
OneByteString(isolate, "compiledWithCache"), |
614 |
|
2 |
builtins_with_cache_js) |
615 |
✗✓ |
1 |
.IsNothing()) { |
616 |
|
|
return; |
617 |
|
|
} |
618 |
|
|
|
619 |
|
1 |
if (!ToV8Value(context, env->builtins_without_cache) |
620 |
✗✓ |
1 |
.ToLocal(&builtins_without_cache_js)) { |
621 |
|
|
return; |
622 |
|
|
} |
623 |
|
1 |
if (result |
624 |
|
1 |
->Set(env->context(), |
625 |
|
|
OneByteString(isolate, "compiledWithoutCache"), |
626 |
|
2 |
builtins_without_cache_js) |
627 |
✗✓ |
1 |
.IsNothing()) { |
628 |
|
|
return; |
629 |
|
|
} |
630 |
|
|
|
631 |
|
1 |
if (!ToV8Value(context, env->builtins_in_snapshot) |
632 |
✗✓ |
1 |
.ToLocal(&builtins_without_cache_js)) { |
633 |
|
|
return; |
634 |
|
|
} |
635 |
|
1 |
if (result |
636 |
|
1 |
->Set(env->context(), |
637 |
|
|
OneByteString(isolate, "compiledInSnapshot"), |
638 |
|
2 |
builtins_without_cache_js) |
639 |
✗✓ |
1 |
.IsNothing()) { |
640 |
|
|
return; |
641 |
|
|
} |
642 |
|
|
|
643 |
|
2 |
args.GetReturnValue().Set(result); |
644 |
|
|
} |
645 |
|
|
|
646 |
|
839 |
void BuiltinLoader::BuiltinIdsGetter(Local<Name> property, |
647 |
|
|
const PropertyCallbackInfo<Value>& info) { |
648 |
|
839 |
Isolate* isolate = info.GetIsolate(); |
649 |
|
|
|
650 |
|
839 |
std::vector<std::string> ids = GetInstance()->GetBuiltinIds(); |
651 |
|
2517 |
info.GetReturnValue().Set( |
652 |
|
1678 |
ToV8Value(isolate->GetCurrentContext(), ids).ToLocalChecked()); |
653 |
|
839 |
} |
654 |
|
|
|
655 |
|
800 |
void BuiltinLoader::ConfigStringGetter( |
656 |
|
|
Local<Name> property, const PropertyCallbackInfo<Value>& info) { |
657 |
|
800 |
info.GetReturnValue().Set(GetConfigString(info.GetIsolate())); |
658 |
|
800 |
} |
659 |
|
|
|
660 |
|
464484 |
void BuiltinLoader::RecordResult(const char* id, |
661 |
|
|
BuiltinLoader::Result result, |
662 |
|
|
Environment* env) { |
663 |
✓✓ |
464484 |
if (result == BuiltinLoader::Result::kWithCache) { |
664 |
|
452144 |
env->builtins_with_cache.insert(id); |
665 |
|
|
} else { |
666 |
|
12340 |
env->builtins_without_cache.insert(id); |
667 |
|
|
} |
668 |
|
464484 |
} |
669 |
|
|
|
670 |
|
454070 |
void BuiltinLoader::CompileFunction(const FunctionCallbackInfo<Value>& args) { |
671 |
|
454070 |
Environment* env = Environment::GetCurrent(args); |
672 |
✗✓ |
908140 |
CHECK(args[0]->IsString()); |
673 |
|
1362210 |
node::Utf8Value id_v(env->isolate(), args[0].As<String>()); |
674 |
|
454070 |
const char* id = *id_v; |
675 |
|
|
MaybeLocal<Function> maybe = |
676 |
|
454070 |
GetInstance()->LookupAndCompile(env->context(), id, env); |
677 |
|
|
Local<Function> fn; |
678 |
✓✗ |
454070 |
if (maybe.ToLocal(&fn)) { |
679 |
|
908140 |
args.GetReturnValue().Set(fn); |
680 |
|
|
} |
681 |
|
454070 |
} |
682 |
|
|
|
683 |
|
4 |
void BuiltinLoader::HasCachedBuiltins(const FunctionCallbackInfo<Value>& args) { |
684 |
✓✗ |
4 |
args.GetReturnValue().Set( |
685 |
|
4 |
v8::Boolean::New(args.GetIsolate(), GetInstance()->has_code_cache_)); |
686 |
|
4 |
} |
687 |
|
|
|
688 |
|
|
// TODO(joyeecheung): It is somewhat confusing that Class::Initialize |
689 |
|
|
// is used to initialize to the binding, but it is the current convention. |
690 |
|
|
// Rename this across the code base to something that makes more sense. |
691 |
|
800 |
void BuiltinLoader::Initialize(Local<Object> target, |
692 |
|
|
Local<Value> unused, |
693 |
|
|
Local<Context> context, |
694 |
|
|
void* priv) { |
695 |
|
800 |
Environment* env = Environment::GetCurrent(context); |
696 |
|
800 |
Isolate* isolate = env->isolate(); |
697 |
|
|
|
698 |
|
|
target |
699 |
|
800 |
->SetAccessor(context, |
700 |
|
|
env->config_string(), |
701 |
|
|
ConfigStringGetter, |
702 |
|
|
nullptr, |
703 |
|
|
MaybeLocal<Value>(), |
704 |
|
|
DEFAULT, |
705 |
|
|
None, |
706 |
|
3200 |
SideEffectType::kHasNoSideEffect) |
707 |
|
|
.Check(); |
708 |
|
|
target |
709 |
|
800 |
->SetAccessor(context, |
710 |
|
|
FIXED_ONE_BYTE_STRING(isolate, "builtinIds"), |
711 |
|
|
BuiltinIdsGetter, |
712 |
|
|
nullptr, |
713 |
|
|
MaybeLocal<Value>(), |
714 |
|
|
DEFAULT, |
715 |
|
|
None, |
716 |
|
3200 |
SideEffectType::kHasNoSideEffect) |
717 |
|
|
.Check(); |
718 |
|
|
|
719 |
|
|
target |
720 |
|
800 |
->SetAccessor(context, |
721 |
|
|
FIXED_ONE_BYTE_STRING(isolate, "builtinCategories"), |
722 |
|
|
GetBuiltinCategories, |
723 |
|
|
nullptr, |
724 |
|
|
Local<Value>(), |
725 |
|
|
DEFAULT, |
726 |
|
|
None, |
727 |
|
1600 |
SideEffectType::kHasNoSideEffect) |
728 |
|
|
.Check(); |
729 |
|
|
|
730 |
|
800 |
SetMethod(context, target, "getCacheUsage", BuiltinLoader::GetCacheUsage); |
731 |
|
800 |
SetMethod(context, target, "compileFunction", BuiltinLoader::CompileFunction); |
732 |
|
800 |
SetMethod(context, target, "hasCachedBuiltins", HasCachedBuiltins); |
733 |
|
|
// internalBinding('builtins') should be frozen |
734 |
|
800 |
target->SetIntegrityLevel(context, IntegrityLevel::kFrozen).FromJust(); |
735 |
|
800 |
} |
736 |
|
|
|
737 |
|
5639 |
void BuiltinLoader::RegisterExternalReferences( |
738 |
|
|
ExternalReferenceRegistry* registry) { |
739 |
|
5639 |
registry->Register(ConfigStringGetter); |
740 |
|
5639 |
registry->Register(BuiltinIdsGetter); |
741 |
|
5639 |
registry->Register(GetBuiltinCategories); |
742 |
|
5639 |
registry->Register(GetCacheUsage); |
743 |
|
5639 |
registry->Register(CompileFunction); |
744 |
|
5639 |
registry->Register(HasCachedBuiltins); |
745 |
|
5639 |
} |
746 |
|
|
|
747 |
|
|
} // namespace builtins |
748 |
|
|
} // namespace node |
749 |
|
|
|
750 |
|
5710 |
NODE_BINDING_CONTEXT_AWARE_INTERNAL(builtins, |
751 |
|
|
node::builtins::BuiltinLoader::Initialize) |
752 |
|
5639 |
NODE_BINDING_EXTERNAL_REFERENCE( |
753 |
|
|
builtins, node::builtins::BuiltinLoader::RegisterExternalReferences) |