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