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, ¶meters, &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