• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &parameters, &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