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