1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <errno.h>
6#include <stdlib.h>
7#include <string.h>
8#include <sys/stat.h>
9
10#include <algorithm>
11#include <fstream>
12#include <iomanip>
13#include <unordered_map>
14#include <utility>
15#include <vector>
16
17#ifdef ENABLE_VTUNE_JIT_INTERFACE
18#include "src/third_party/vtune/v8-vtune.h"
19#endif
20
21#include "include/libplatform/libplatform.h"
22#include "include/libplatform/v8-tracing.h"
23#include "include/v8-inspector.h"
24#include "src/api-inl.h"
25#include "src/base/cpu.h"
26#include "src/base/logging.h"
27#include "src/base/platform/platform.h"
28#include "src/base/platform/time.h"
29#include "src/base/sys-info.h"
30#include "src/basic-block-profiler.h"
31#include "src/d8-console.h"
32#include "src/d8-platforms.h"
33#include "src/d8.h"
34#include "src/debug/debug-interface.h"
35#include "src/interpreter/interpreter.h"
36#include "src/msan.h"
37#include "src/objects-inl.h"
38#include "src/objects.h"
39#include "src/ostreams.h"
40#include "src/parsing/parse-info.h"
41#include "src/parsing/parsing.h"
42#include "src/parsing/scanner-character-streams.h"
43#include "src/snapshot/natives.h"
44#include "src/trap-handler/trap-handler.h"
45#include "src/utils.h"
46#include "src/v8.h"
47#include "src/vm-state-inl.h"
48#include "src/wasm/wasm-engine.h"
49
50#if !defined(_WIN32) && !defined(_WIN64)
51#include <unistd.h> // NOLINT
52#else
53#include <windows.h> // NOLINT
54#endif // !defined(_WIN32) && !defined(_WIN64)
55
56#ifndef DCHECK
57#define DCHECK(condition) assert(condition)
58#endif
59
60#ifndef CHECK
61#define CHECK(condition) assert(condition)
62#endif
63
64namespace v8 {
65
66namespace {
67
68const int kMB = 1024 * 1024;
69
70const int kMaxWorkers = 100;
71const int kMaxSerializerMemoryUsage =
72 1 * kMB; // Arbitrary maximum for testing.
73
74// Base class for shell ArrayBuffer allocators. It forwards all opertions to
75// the default v8 allocator.
76class ArrayBufferAllocatorBase : public v8::ArrayBuffer::Allocator {
77 public:
78 void* Allocate(size_t length) override {
79 return allocator_->Allocate(length);
80 }
81
82 void* AllocateUninitialized(size_t length) override {
83 return allocator_->AllocateUninitialized(length);
84 }
85
86 void Free(void* data, size_t length) override {
87 allocator_->Free(data, length);
88 }
89
90 private:
91 std::unique_ptr<Allocator> allocator_ =
92 std::unique_ptr<Allocator>(NewDefaultAllocator());
93};
94
95// ArrayBuffer allocator that can use virtual memory to improve performance.
96class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase {
97 public:
98 void* Allocate(size_t length) override {
99 if (length >= kVMThreshold) return AllocateVM(length);
100 return ArrayBufferAllocatorBase::Allocate(length);
101 }
102
103 void* AllocateUninitialized(size_t length) override {
104 if (length >= kVMThreshold) return AllocateVM(length);
105 return ArrayBufferAllocatorBase::AllocateUninitialized(length);
106 }
107
108 void Free(void* data, size_t length) override {
109 if (length >= kVMThreshold) {
110 FreeVM(data, length);
111 } else {
112 ArrayBufferAllocatorBase::Free(data, length);
113 }
114 }
115
116 private:
117 static constexpr size_t kVMThreshold = 65536;
118 static constexpr size_t kTwoGB = 2u * 1024u * 1024u * 1024u;
119
120 void* AllocateVM(size_t length) {
121 DCHECK_LE(kVMThreshold, length);
122 // TODO(titzer): allocations should fail if >= 2gb because array buffers
123 // store their lengths as a SMI internally.
124 if (length >= kTwoGB) return nullptr;
125
126 v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
127 size_t page_size = page_allocator->AllocatePageSize();
128 size_t allocated = RoundUp(length, page_size);
129 // Rounding up could go over the limit.
130 if (allocated >= kTwoGB) return nullptr;
131 return i::AllocatePages(page_allocator, nullptr, allocated, page_size,
132 PageAllocator::kReadWrite);
133 }
134
135 void FreeVM(void* data, size_t length) {
136 v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
137 size_t page_size = page_allocator->AllocatePageSize();
138 size_t allocated = RoundUp(length, page_size);
139 CHECK(i::FreePages(page_allocator, data, allocated));
140 }
141};
142
143// ArrayBuffer allocator that never allocates over 10MB.
144class MockArrayBufferAllocator : public ArrayBufferAllocatorBase {
145 protected:
146 void* Allocate(size_t length) override {
147 return ArrayBufferAllocatorBase::Allocate(Adjust(length));
148 }
149
150 void* AllocateUninitialized(size_t length) override {
151 return ArrayBufferAllocatorBase::AllocateUninitialized(Adjust(length));
152 }
153
154 void Free(void* data, size_t length) override {
155 return ArrayBufferAllocatorBase::Free(data, Adjust(length));
156 }
157
158 private:
159 size_t Adjust(size_t length) {
160 const size_t kAllocationLimit = 10 * kMB;
161 return length > kAllocationLimit ? i::AllocatePageSize() : length;
162 }
163};
164
165// ArrayBuffer allocator that can be equipped with a limit to simulate system
166// OOM.
167class MockArrayBufferAllocatiorWithLimit : public MockArrayBufferAllocator {
168 public:
169 explicit MockArrayBufferAllocatiorWithLimit(size_t allocation_limit)
170 : space_left_(allocation_limit) {}
171
172 protected:
173 void* Allocate(size_t length) override {
174 if (length > space_left_) {
175 return nullptr;
176 }
177 space_left_ -= length;
178 return MockArrayBufferAllocator::Allocate(length);
179 }
180
181 void* AllocateUninitialized(size_t length) override {
182 if (length > space_left_) {
183 return nullptr;
184 }
185 space_left_ -= length;
186 return MockArrayBufferAllocator::AllocateUninitialized(length);
187 }
188
189 void Free(void* data, size_t length) override {
190 space_left_ += length;
191 return MockArrayBufferAllocator::Free(data, length);
192 }
193
194 private:
195 std::atomic<size_t> space_left_;
196};
197
198v8::Platform* g_default_platform;
199std::unique_ptr<v8::Platform> g_platform;
200
201static Local<Value> Throw(Isolate* isolate, const char* message) {
202 return isolate->ThrowException(
203 String::NewFromUtf8(isolate, message, NewStringType::kNormal)
204 .ToLocalChecked());
205}
206
207static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
208 Local<v8::Object> object, const char* property) {
209 Local<String> v8_str =
210 String::NewFromUtf8(isolate, property, NewStringType::kNormal)
211 .ToLocalChecked();
212 return object->Get(context, v8_str).ToLocalChecked();
213}
214
215Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) {
216 if (object->InternalFieldCount() != 1) {
217 Throw(isolate, "this is not a Worker");
218 return nullptr;
219 }
220
221 Worker* worker =
222 static_cast<Worker*>(object->GetAlignedPointerFromInternalField(0));
223 if (worker == nullptr) {
224 Throw(isolate, "Worker is defunct because main thread is terminating");
225 return nullptr;
226 }
227
228 return worker;
229}
230
231base::Thread::Options GetThreadOptions(const char* name) {
232 // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
233 // which is not enough to parse the big literal expressions used in tests.
234 // The stack size should be at least StackGuard::kLimitSize + some
235 // OS-specific padding for thread startup code. 2Mbytes seems to be enough.
236 return base::Thread::Options(name, 2 * kMB);
237}
238
239} // namespace
240
241namespace tracing {
242
243namespace {
244
245// String options that can be used to initialize TraceOptions.
246const char kRecordUntilFull[] = "record-until-full";
247const char kRecordContinuously[] = "record-continuously";
248const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
249
250const char kRecordModeParam[] = "record_mode";
251const char kEnableSystraceParam[] = "enable_systrace";
252const char kEnableArgumentFilterParam[] = "enable_argument_filter";
253const char kIncludedCategoriesParam[] = "included_categories";
254
255class TraceConfigParser {
256 public:
257 static void FillTraceConfig(v8::Isolate* isolate,
258 platform::tracing::TraceConfig* trace_config,
259 const char* json_str) {
260 HandleScope outer_scope(isolate);
261 Local<Context> context = Context::New(isolate);
262 Context::Scope context_scope(context);
263 HandleScope inner_scope(isolate);
264
265 Local<String> source =
266 String::NewFromUtf8(isolate, json_str, NewStringType::kNormal)
267 .ToLocalChecked();
268 Local<Value> result = JSON::Parse(context, source).ToLocalChecked();
269 Local<v8::Object> trace_config_object = Local<v8::Object>::Cast(result);
270
271 trace_config->SetTraceRecordMode(
272 GetTraceRecordMode(isolate, context, trace_config_object));
273 if (GetBoolean(isolate, context, trace_config_object,
274 kEnableSystraceParam)) {
275 trace_config->EnableSystrace();
276 }
277 if (GetBoolean(isolate, context, trace_config_object,
278 kEnableArgumentFilterParam)) {
279 trace_config->EnableArgumentFilter();
280 }
281 UpdateIncludedCategoriesList(isolate, context, trace_config_object,
282 trace_config);
283 }
284
285 private:
286 static bool GetBoolean(v8::Isolate* isolate, Local<Context> context,
287 Local<v8::Object> object, const char* property) {
288 Local<Value> value = GetValue(isolate, context, object, property);
289 if (value->IsNumber()) {
290 return value->BooleanValue(isolate);
291 }
292 return false;
293 }
294
295 static int UpdateIncludedCategoriesList(
296 v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object,
297 platform::tracing::TraceConfig* trace_config) {
298 Local<Value> value =
299 GetValue(isolate, context, object, kIncludedCategoriesParam);
300 if (value->IsArray()) {
301 Local<Array> v8_array = Local<Array>::Cast(value);
302 for (int i = 0, length = v8_array->Length(); i < length; ++i) {
303 Local<Value> v = v8_array->Get(context, i)
304 .ToLocalChecked()
305 ->ToString(context)
306 .ToLocalChecked();
307 String::Utf8Value str(isolate, v->ToString(context).ToLocalChecked());
308 trace_config->AddIncludedCategory(*str);
309 }
310 return v8_array->Length();
311 }
312 return 0;
313 }
314
315 static platform::tracing::TraceRecordMode GetTraceRecordMode(
316 v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object) {
317 Local<Value> value = GetValue(isolate, context, object, kRecordModeParam);
318 if (value->IsString()) {
319 Local<String> v8_string = value->ToString(context).ToLocalChecked();
320 String::Utf8Value str(isolate, v8_string);
321 if (strcmp(kRecordUntilFull, *str) == 0) {
322 return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
323 } else if (strcmp(kRecordContinuously, *str) == 0) {
324 return platform::tracing::TraceRecordMode::RECORD_CONTINUOUSLY;
325 } else if (strcmp(kRecordAsMuchAsPossible, *str) == 0) {
326 return platform::tracing::TraceRecordMode::RECORD_AS_MUCH_AS_POSSIBLE;
327 }
328 }
329 return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
330 }
331};
332
333} // namespace
334
335static platform::tracing::TraceConfig* CreateTraceConfigFromJSON(
336 v8::Isolate* isolate, const char* json_str) {
337 platform::tracing::TraceConfig* trace_config =
338 new platform::tracing::TraceConfig();
339 TraceConfigParser::FillTraceConfig(isolate, trace_config, json_str);
340 return trace_config;
341}
342
343} // namespace tracing
344
345
346class ExternalOwningOneByteStringResource
347 : public String::ExternalOneByteStringResource {
348 public:
349 ExternalOwningOneByteStringResource() {}
350 ExternalOwningOneByteStringResource(
351 std::unique_ptr<base::OS::MemoryMappedFile> file)
352 : file_(std::move(file)) {}
353 const char* data() const override {
354 return static_cast<char*>(file_->memory());
355 }
356 size_t length() const override { return file_->size(); }
357
358 private:
359 std::unique_ptr<base::OS::MemoryMappedFile> file_;
360};
361
362CounterMap* Shell::counter_map_;
363base::OS::MemoryMappedFile* Shell::counters_file_ = nullptr;
364CounterCollection Shell::local_counters_;
365CounterCollection* Shell::counters_ = &local_counters_;
366base::LazyMutex Shell::context_mutex_;
367const base::TimeTicks Shell::kInitialTicks =
368 base::TimeTicks::HighResolutionNow();
369Global<Function> Shell::stringify_function_;
370base::LazyMutex Shell::workers_mutex_;
371bool Shell::allow_new_workers_ = true;
372std::vector<Worker*> Shell::workers_;
373std::vector<ExternalizedContents> Shell::externalized_contents_;
374std::atomic<bool> Shell::script_executed_{false};
375base::LazyMutex Shell::isolate_status_lock_;
376std::map<v8::Isolate*, bool> Shell::isolate_status_;
377base::LazyMutex Shell::cached_code_mutex_;
378std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
379 Shell::cached_code_map_;
380
381Global<Context> Shell::evaluation_context_;
382ArrayBuffer::Allocator* Shell::array_buffer_allocator;
383ShellOptions Shell::options;
384base::OnceType Shell::quit_once_ = V8_ONCE_INIT;
385
386// Dummy external source stream which returns the whole source in one go.
387class DummySourceStream : public v8::ScriptCompiler::ExternalSourceStream {
388 public:
389 DummySourceStream(Local<String> source, Isolate* isolate) : done_(false) {
390 source_length_ = source->Utf8Length(isolate);
391 source_buffer_.reset(new uint8_t[source_length_]);
392 source->WriteUtf8(isolate, reinterpret_cast<char*>(source_buffer_.get()),
393 source_length_);
394 }
395
396 size_t GetMoreData(const uint8_t** src) override {
397 if (done_) {
398 return 0;
399 }
400 *src = source_buffer_.release();
401 done_ = true;
402
403 return source_length_;
404 }
405
406 private:
407 int source_length_;
408 std::unique_ptr<uint8_t[]> source_buffer_;
409 bool done_;
410};
411
412class BackgroundCompileThread : public base::Thread {
413 public:
414 BackgroundCompileThread(Isolate* isolate, Local<String> source)
415 : base::Thread(GetThreadOptions("BackgroundCompileThread")),
416 source_(source),
417 streamed_source_(base::make_unique<DummySourceStream>(source, isolate),
418 v8::ScriptCompiler::StreamedSource::UTF8),
419 task_(v8::ScriptCompiler::StartStreamingScript(isolate,
420 &streamed_source_)) {}
421
422 void Run() override { task_->Run(); }
423
424 v8::ScriptCompiler::StreamedSource* streamed_source() {
425 return &streamed_source_;
426 }
427
428 private:
429 Local<String> source_;
430 v8::ScriptCompiler::StreamedSource streamed_source_;
431 std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task_;
432};
433
434ScriptCompiler::CachedData* Shell::LookupCodeCache(Isolate* isolate,
435 Local<Value> source) {
436 base::MutexGuard lock_guard(cached_code_mutex_.Pointer());
437 CHECK(source->IsString());
438 v8::String::Utf8Value key(isolate, source);
439 DCHECK(*key);
440 auto entry = cached_code_map_.find(*key);
441 if (entry != cached_code_map_.end() && entry->second) {
442 int length = entry->second->length;
443 uint8_t* cache = new uint8_t[length];
444 memcpy(cache, entry->second->data, length);
445 ScriptCompiler::CachedData* cached_data = new ScriptCompiler::CachedData(
446 cache, length, ScriptCompiler::CachedData::BufferOwned);
447 return cached_data;
448 }
449 return nullptr;
450}
451
452void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> source,
453 const ScriptCompiler::CachedData* cache_data) {
454 base::MutexGuard lock_guard(cached_code_mutex_.Pointer());
455 CHECK(source->IsString());
456 if (cache_data == nullptr) return;
457 v8::String::Utf8Value key(isolate, source);
458 DCHECK(*key);
459 int length = cache_data->length;
460 uint8_t* cache = new uint8_t[length];
461 memcpy(cache, cache_data->data, length);
462 cached_code_map_[*key] = std::unique_ptr<ScriptCompiler::CachedData>(
463 new ScriptCompiler::CachedData(cache, length,
464 ScriptCompiler::CachedData::BufferOwned));
465}
466
467// Executes a string within the current v8 context.
468bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
469 Local<Value> name, PrintResult print_result,
470 ReportExceptions report_exceptions,
471 ProcessMessageQueue process_message_queue) {
472 if (i::FLAG_parse_only) {
473 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
474 i::VMState<PARSER> state(i_isolate);
475 i::Handle<i::String> str = Utils::OpenHandle(*(source));
476
477 // Set up ParseInfo.
478 i::ParseInfo parse_info(i_isolate);
479 parse_info.set_toplevel();
480 parse_info.set_allow_lazy_parsing();
481 parse_info.set_language_mode(
482 i::construct_language_mode(i::FLAG_use_strict));
483 parse_info.set_script(
484 parse_info.CreateScript(i_isolate, str, options.compile_options));
485
486 if (!i::parsing::ParseProgram(&parse_info, i_isolate)) {
487 fprintf(stderr, "Failed parsing\n");
488 return false;
489 }
490 return true;
491 }
492
493 HandleScope handle_scope(isolate);
494 TryCatch try_catch(isolate);
495 try_catch.SetVerbose(true);
496
497 MaybeLocal<Value> maybe_result;
498 bool success = true;
499 {
500 PerIsolateData* data = PerIsolateData::Get(isolate);
501 Local<Context> realm =
502 Local<Context>::New(isolate, data->realms_[data->realm_current_]);
503 Context::Scope context_scope(realm);
504 MaybeLocal<Script> maybe_script;
505 Local<Context> context(isolate->GetCurrentContext());
506 ScriptOrigin origin(name);
507
508 if (options.compile_options == ScriptCompiler::kConsumeCodeCache) {
509 ScriptCompiler::CachedData* cached_code =
510 LookupCodeCache(isolate, source);
511 if (cached_code != nullptr) {
512 ScriptCompiler::Source script_source(source, origin, cached_code);
513 maybe_script = ScriptCompiler::Compile(context, &script_source,
514 options.compile_options);
515 CHECK(!cached_code->rejected);
516 } else {
517 ScriptCompiler::Source script_source(source, origin);
518 maybe_script = ScriptCompiler::Compile(
519 context, &script_source, ScriptCompiler::kNoCompileOptions);
520 }
521 } else if (options.stress_background_compile) {
522 // Start a background thread compiling the script.
523 BackgroundCompileThread background_compile_thread(isolate, source);
524 background_compile_thread.Start();
525
526 // In parallel, compile on the main thread to flush out any data races.
527 {
528 TryCatch ignore_try_catch(isolate);
529 ScriptCompiler::Source script_source(source, origin);
530 USE(ScriptCompiler::Compile(context, &script_source,
531 ScriptCompiler::kNoCompileOptions));
532 }
533
534 // Join with background thread and finalize compilation.
535 background_compile_thread.Join();
536 maybe_script = v8::ScriptCompiler::Compile(
537 context, background_compile_thread.streamed_source(), source, origin);
538 } else {
539 ScriptCompiler::Source script_source(source, origin);
540 maybe_script = ScriptCompiler::Compile(context, &script_source,
541 options.compile_options);
542 }
543
544 Local<Script> script;
545 if (!maybe_script.ToLocal(&script)) {
546 // Print errors that happened during compilation.
547 if (report_exceptions) ReportException(isolate, &try_catch);
548 return false;
549 }
550
551 if (options.code_cache_options ==
552 ShellOptions::CodeCacheOptions::kProduceCache) {
553 // Serialize and store it in memory for the next execution.
554 ScriptCompiler::CachedData* cached_data =
555 ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
556 StoreInCodeCache(isolate, source, cached_data);
557 delete cached_data;
558 }
559 maybe_result = script->Run(realm);
560 if (options.code_cache_options ==
561 ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute) {
562 // Serialize and store it in memory for the next execution.
563 ScriptCompiler::CachedData* cached_data =
564 ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
565 StoreInCodeCache(isolate, source, cached_data);
566 delete cached_data;
567 }
568 if (process_message_queue && !EmptyMessageQueues(isolate)) success = false;
569 data->realm_current_ = data->realm_switch_;
570 }
571 Local<Value> result;
572 if (!maybe_result.ToLocal(&result)) {
573 DCHECK(try_catch.HasCaught());
574 // Print errors that happened during execution.
575 if (report_exceptions) ReportException(isolate, &try_catch);
576 return false;
577 }
578 DCHECK(!try_catch.HasCaught());
579 if (print_result) {
580 if (options.test_shell) {
581 if (!result->IsUndefined()) {
582 // If all went well and the result wasn't undefined then print
583 // the returned value.
584 v8::String::Utf8Value str(isolate, result);
585 fwrite(*str, sizeof(**str), str.length(), stdout);
586 printf("\n");
587 }
588 } else {
589 v8::String::Utf8Value str(isolate, Stringify(isolate, result));
590 fwrite(*str, sizeof(**str), str.length(), stdout);
591 printf("\n");
592 }
593 }
594 return success;
595}
596
597namespace {
598
599std::string ToSTLString(Isolate* isolate, Local<String> v8_str) {
600 String::Utf8Value utf8(isolate, v8_str);
601 // Should not be able to fail since the input is a String.
602 CHECK(*utf8);
603 return *utf8;
604}
605
606bool IsAbsolutePath(const std::string& path) {
607#if defined(_WIN32) || defined(_WIN64)
608 // TODO(adamk): This is an incorrect approximation, but should
609 // work for all our test-running cases.
610 return path.find(':') != std::string::npos;
611#else
612 return path[0] == '/';
613#endif
614}
615
616std::string GetWorkingDirectory() {
617#if defined(_WIN32) || defined(_WIN64)
618 char system_buffer[MAX_PATH];
619 // TODO(adamk): Support Unicode paths.
620 DWORD len = GetCurrentDirectoryA(MAX_PATH, system_buffer);
621 CHECK_GT(len, 0);
622 return system_buffer;
623#else
624 char curdir[PATH_MAX];
625 CHECK_NOT_NULL(getcwd(curdir, PATH_MAX));
626 return curdir;
627#endif
628}
629
630// Returns the directory part of path, without the trailing '/'.
631std::string DirName(const std::string& path) {
632 DCHECK(IsAbsolutePath(path));
633 size_t last_slash = path.find_last_of('/');
634 DCHECK(last_slash != std::string::npos);
635 return path.substr(0, last_slash);
636}
637
638// Resolves path to an absolute path if necessary, and does some
639// normalization (eliding references to the current directory
640// and replacing backslashes with slashes).
641std::string NormalizePath(const std::string& path,
642 const std::string& dir_name) {
643 std::string result;
644 if (IsAbsolutePath(path)) {
645 result = path;
646 } else {
647 result = dir_name + '/' + path;
648 }
649 std::replace(result.begin(), result.end(), '\\', '/');
650 size_t i;
651 while ((i = result.find("/./")) != std::string::npos) {
652 result.erase(i, 2);
653 }
654 return result;
655}
656
657// Per-context Module data, allowing sharing of module maps
658// across top-level module loads.
659class ModuleEmbedderData {
660 private:
661 class ModuleGlobalHash {
662 public:
663 explicit ModuleGlobalHash(Isolate* isolate) : isolate_(isolate) {}
664 size_t operator()(const Global<Module>& module) const {
665 return module.Get(isolate_)->GetIdentityHash();
666 }
667
668 private:
669 Isolate* isolate_;
670 };
671
672 public:
673 explicit ModuleEmbedderData(Isolate* isolate)
674 : module_to_specifier_map(10, ModuleGlobalHash(isolate)) {}
675
676 // Map from normalized module specifier to Module.
677 std::unordered_map<std::string, Global<Module>> specifier_to_module_map;
678 // Map from Module to its URL as defined in the ScriptOrigin
679 std::unordered_map<Global<Module>, std::string, ModuleGlobalHash>
680 module_to_specifier_map;
681};
682
683enum {
684 kModuleEmbedderDataIndex,
685 kInspectorClientIndex
686};
687
688void InitializeModuleEmbedderData(Local<Context> context) {
689 context->SetAlignedPointerInEmbedderData(
690 kModuleEmbedderDataIndex, new ModuleEmbedderData(context->GetIsolate()));
691}
692
693ModuleEmbedderData* GetModuleDataFromContext(Local<Context> context) {
694 return static_cast<ModuleEmbedderData*>(
695 context->GetAlignedPointerFromEmbedderData(kModuleEmbedderDataIndex));
696}
697
698void DisposeModuleEmbedderData(Local<Context> context) {
699 delete GetModuleDataFromContext(context);
700 context->SetAlignedPointerInEmbedderData(kModuleEmbedderDataIndex, nullptr);
701}
702
703MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
704 Local<String> specifier,
705 Local<Module> referrer) {
706 Isolate* isolate = context->GetIsolate();
707 ModuleEmbedderData* d = GetModuleDataFromContext(context);
708 auto specifier_it =
709 d->module_to_specifier_map.find(Global<Module>(isolate, referrer));
710 CHECK(specifier_it != d->module_to_specifier_map.end());
711 std::string absolute_path = NormalizePath(ToSTLString(isolate, specifier),
712 DirName(specifier_it->second));
713 auto module_it = d->specifier_to_module_map.find(absolute_path);
714 CHECK(module_it != d->specifier_to_module_map.end());
715 return module_it->second.Get(isolate);
716}
717
718} // anonymous namespace
719
720MaybeLocal<Module> Shell::FetchModuleTree(Local<Context> context,
721 const std::string& file_name) {
722 DCHECK(IsAbsolutePath(file_name));
723 Isolate* isolate = context->GetIsolate();
724 Local<String> source_text = ReadFile(isolate, file_name.c_str());
725 if (source_text.IsEmpty()) {
726 std::string msg = "Error reading: " + file_name;
727 Throw(isolate, msg.c_str());
728 return MaybeLocal<Module>();
729 }
730 ScriptOrigin origin(
731 String::NewFromUtf8(isolate, file_name.c_str(), NewStringType::kNormal)
732 .ToLocalChecked(),
733 Local<Integer>(), Local<Integer>(), Local<Boolean>(), Local<Integer>(),
734 Local<Value>(), Local<Boolean>(), Local<Boolean>(), True(isolate));
735 ScriptCompiler::Source source(source_text, origin);
736 Local<Module> module;
737 if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
738 return MaybeLocal<Module>();
739 }
740
741 ModuleEmbedderData* d = GetModuleDataFromContext(context);
742 CHECK(d->specifier_to_module_map
743 .insert(std::make_pair(file_name, Global<Module>(isolate, module)))
744 .second);
745 CHECK(d->module_to_specifier_map
746 .insert(std::make_pair(Global<Module>(isolate, module), file_name))
747 .second);
748
749 std::string dir_name = DirName(file_name);
750
751 for (int i = 0, length = module->GetModuleRequestsLength(); i < length; ++i) {
752 Local<String> name = module->GetModuleRequest(i);
753 std::string absolute_path =
754 NormalizePath(ToSTLString(isolate, name), dir_name);
755 if (!d->specifier_to_module_map.count(absolute_path)) {
756 if (FetchModuleTree(context, absolute_path).IsEmpty()) {
757 return MaybeLocal<Module>();
758 }
759 }
760 }
761
762 return module;
763}
764
765namespace {
766
767struct DynamicImportData {
768 DynamicImportData(Isolate* isolate_, Local<String> referrer_,
769 Local<String> specifier_,
770 Local<Promise::Resolver> resolver_)
771 : isolate(isolate_) {
772 referrer.Reset(isolate, referrer_);
773 specifier.Reset(isolate, specifier_);
774 resolver.Reset(isolate, resolver_);
775 }
776
777 Isolate* isolate;
778 Global<String> referrer;
779 Global<String> specifier;
780 Global<Promise::Resolver> resolver;
781};
782
783} // namespace
784
785MaybeLocal<Promise> Shell::HostImportModuleDynamically(
786 Local<Context> context, Local<ScriptOrModule> referrer,
787 Local<String> specifier) {
788 Isolate* isolate = context->GetIsolate();
789
790 MaybeLocal<Promise::Resolver> maybe_resolver =
791 Promise::Resolver::New(context);
792 Local<Promise::Resolver> resolver;
793 if (maybe_resolver.ToLocal(&resolver)) {
794 DynamicImportData* data = new DynamicImportData(
795 isolate, Local<String>::Cast(referrer->GetResourceName()), specifier,
796 resolver);
797 isolate->EnqueueMicrotask(Shell::DoHostImportModuleDynamically, data);
798 return resolver->GetPromise();
799 }
800
801 return MaybeLocal<Promise>();
802}
803
804void Shell::HostInitializeImportMetaObject(Local<Context> context,
805 Local<Module> module,
806 Local<Object> meta) {
807 Isolate* isolate = context->GetIsolate();
808 HandleScope handle_scope(isolate);
809
810 ModuleEmbedderData* d = GetModuleDataFromContext(context);
811 auto specifier_it =
812 d->module_to_specifier_map.find(Global<Module>(isolate, module));
813 CHECK(specifier_it != d->module_to_specifier_map.end());
814
815 Local<String> url_key =
816 String::NewFromUtf8(isolate, "url", NewStringType::kNormal)
817 .ToLocalChecked();
818 Local<String> url = String::NewFromUtf8(isolate, specifier_it->second.c_str(),
819 NewStringType::kNormal)
820 .ToLocalChecked();
821 meta->CreateDataProperty(context, url_key, url).ToChecked();
822}
823
824void Shell::DoHostImportModuleDynamically(void* import_data) {
825 std::unique_ptr<DynamicImportData> import_data_(
826 static_cast<DynamicImportData*>(import_data));
827 Isolate* isolate(import_data_->isolate);
828 HandleScope handle_scope(isolate);
829
830 Local<String> referrer(import_data_->referrer.Get(isolate));
831 Local<String> specifier(import_data_->specifier.Get(isolate));
832 Local<Promise::Resolver> resolver(import_data_->resolver.Get(isolate));
833
834 PerIsolateData* data = PerIsolateData::Get(isolate);
835 Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
836 Context::Scope context_scope(realm);
837
838 std::string source_url = ToSTLString(isolate, referrer);
839 std::string dir_name =
840 DirName(NormalizePath(source_url, GetWorkingDirectory()));
841 std::string file_name = ToSTLString(isolate, specifier);
842 std::string absolute_path = NormalizePath(file_name, dir_name);
843
844 TryCatch try_catch(isolate);
845 try_catch.SetVerbose(true);
846
847 ModuleEmbedderData* d = GetModuleDataFromContext(realm);
848 Local<Module> root_module;
849 auto module_it = d->specifier_to_module_map.find(absolute_path);
850 if (module_it != d->specifier_to_module_map.end()) {
851 root_module = module_it->second.Get(isolate);
852 } else if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
853 CHECK(try_catch.HasCaught());
854 resolver->Reject(realm, try_catch.Exception()).ToChecked();
855 return;
856 }
857
858 MaybeLocal<Value> maybe_result;
859 if (root_module->InstantiateModule(realm, ResolveModuleCallback)
860 .FromMaybe(false)) {
861 maybe_result = root_module->Evaluate(realm);
862 EmptyMessageQueues(isolate);
863 }
864
865 Local<Value> module;
866 if (!maybe_result.ToLocal(&module)) {
867 DCHECK(try_catch.HasCaught());
868 resolver->Reject(realm, try_catch.Exception()).ToChecked();
869 return;
870 }
871
872 DCHECK(!try_catch.HasCaught());
873 Local<Value> module_namespace = root_module->GetModuleNamespace();
874 resolver->Resolve(realm, module_namespace).ToChecked();
875}
876
877bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
878 HandleScope handle_scope(isolate);
879
880 PerIsolateData* data = PerIsolateData::Get(isolate);
881 Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
882 Context::Scope context_scope(realm);
883
884 std::string absolute_path = NormalizePath(file_name, GetWorkingDirectory());
885
886 TryCatch try_catch(isolate);
887 try_catch.SetVerbose(true);
888
889 Local<Module> root_module;
890 MaybeLocal<Value> maybe_exception;
891
892 if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
893 CHECK(try_catch.HasCaught());
894 ReportException(isolate, &try_catch);
895 return false;
896 }
897
898 MaybeLocal<Value> maybe_result;
899 if (root_module->InstantiateModule(realm, ResolveModuleCallback)
900 .FromMaybe(false)) {
901 maybe_result = root_module->Evaluate(realm);
902 EmptyMessageQueues(isolate);
903 }
904 Local<Value> result;
905 if (!maybe_result.ToLocal(&result)) {
906 DCHECK(try_catch.HasCaught());
907 // Print errors that happened during execution.
908 ReportException(isolate, &try_catch);
909 return false;
910 }
911 DCHECK(!try_catch.HasCaught());
912 return true;
913}
914
915PerIsolateData::PerIsolateData(Isolate* isolate)
916 : isolate_(isolate), realms_(nullptr) {
917 isolate->SetData(0, this);
918 if (i::FLAG_expose_async_hooks) {
919 async_hooks_wrapper_ = new AsyncHooks(isolate);
920 }
921}
922
923PerIsolateData::~PerIsolateData() {
924 isolate_->SetData(0, nullptr); // Not really needed, just to be sure...
925 if (i::FLAG_expose_async_hooks) {
926 delete async_hooks_wrapper_; // This uses the isolate
927 }
928}
929
930void PerIsolateData::SetTimeout(Local<Function> callback,
931 Local<Context> context) {
932 set_timeout_callbacks_.emplace(isolate_, callback);
933 set_timeout_contexts_.emplace(isolate_, context);
934}
935
936MaybeLocal<Function> PerIsolateData::GetTimeoutCallback() {
937 if (set_timeout_callbacks_.empty()) return MaybeLocal<Function>();
938 Local<Function> result = set_timeout_callbacks_.front().Get(isolate_);
939 set_timeout_callbacks_.pop();
940 return result;
941}
942
943MaybeLocal<Context> PerIsolateData::GetTimeoutContext() {
944 if (set_timeout_contexts_.empty()) return MaybeLocal<Context>();
945 Local<Context> result = set_timeout_contexts_.front().Get(isolate_);
946 set_timeout_contexts_.pop();
947 return result;
948}
949
950PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
951 data_->realm_count_ = 1;
952 data_->realm_current_ = 0;
953 data_->realm_switch_ = 0;
954 data_->realms_ = new Global<Context>[1];
955 data_->realms_[0].Reset(data_->isolate_,
956 data_->isolate_->GetEnteredOrMicrotaskContext());
957}
958
959
960PerIsolateData::RealmScope::~RealmScope() {
961 // Drop realms to avoid keeping them alive. We don't dispose the
962 // module embedder data for the first realm here, but instead do
963 // it in RunShell or in RunMain, if not running in interactive mode
964 for (int i = 1; i < data_->realm_count_; ++i) {
965 Global<Context>& realm = data_->realms_[i];
966 if (realm.IsEmpty()) continue;
967 DisposeModuleEmbedderData(realm.Get(data_->isolate_));
968 }
969 data_->realm_count_ = 0;
970 delete[] data_->realms_;
971}
972
973
974int PerIsolateData::RealmFind(Local<Context> context) {
975 for (int i = 0; i < realm_count_; ++i) {
976 if (realms_[i] == context) return i;
977 }
978 return -1;
979}
980
981
982int PerIsolateData::RealmIndexOrThrow(
983 const v8::FunctionCallbackInfo<v8::Value>& args,
984 int arg_offset) {
985 if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) {
986 Throw(args.GetIsolate(), "Invalid argument");
987 return -1;
988 }
989 int index = args[arg_offset]
990 ->Int32Value(args.GetIsolate()->GetCurrentContext())
991 .FromMaybe(-1);
992 if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) {
993 Throw(args.GetIsolate(), "Invalid realm index");
994 return -1;
995 }
996 return index;
997}
998
999
1000// performance.now() returns a time stamp as double, measured in milliseconds.
1001// When FLAG_verify_predictable mode is enabled it returns result of
1002// v8::Platform::MonotonicallyIncreasingTime().
1003void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
1004 if (i::FLAG_verify_predictable) {
1005 args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime());
1006 } else {
1007 base::TimeDelta delta =
1008 base::TimeTicks::HighResolutionNow() - kInitialTicks;
1009 args.GetReturnValue().Set(delta.InMillisecondsF());
1010 }
1011}
1012
1013
1014// Realm.current() returns the index of the currently active realm.
1015void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) {
1016 Isolate* isolate = args.GetIsolate();
1017 PerIsolateData* data = PerIsolateData::Get(isolate);
1018 int index = data->RealmFind(isolate->GetEnteredOrMicrotaskContext());
1019 if (index == -1) return;
1020 args.GetReturnValue().Set(index);
1021}
1022
1023
1024// Realm.owner(o) returns the index of the realm that created o.
1025void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) {
1026 Isolate* isolate = args.GetIsolate();
1027 PerIsolateData* data = PerIsolateData::Get(isolate);
1028 if (args.Length() < 1 || !args[0]->IsObject()) {
1029 Throw(args.GetIsolate(), "Invalid argument");
1030 return;
1031 }
1032 int index = data->RealmFind(args[0]
1033 ->ToObject(isolate->GetCurrentContext())
1034 .ToLocalChecked()
1035 ->CreationContext());
1036 if (index == -1) return;
1037 args.GetReturnValue().Set(index);
1038}
1039
1040
1041// Realm.global(i) returns the global object of realm i.
1042// (Note that properties of global objects cannot be read/written cross-realm.)
1043void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1044 PerIsolateData* data = PerIsolateData::Get(args.GetIsolate());
1045 int index = data->RealmIndexOrThrow(args, 0);
1046 if (index == -1) return;
1047 args.GetReturnValue().Set(
1048 Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global());
1049}
1050
1051MaybeLocal<Context> Shell::CreateRealm(
1052 const v8::FunctionCallbackInfo<v8::Value>& args, int index,
1053 v8::MaybeLocal<Value> global_object) {
1054 Isolate* isolate = args.GetIsolate();
1055 TryCatch try_catch(isolate);
1056 PerIsolateData* data = PerIsolateData::Get(isolate);
1057 if (index < 0) {
1058 Global<Context>* old_realms = data->realms_;
1059 index = data->realm_count_;
1060 data->realms_ = new Global<Context>[++data->realm_count_];
1061 for (int i = 0; i < index; ++i) {
1062 data->realms_[i].Reset(isolate, old_realms[i]);
1063 old_realms[i].Reset();
1064 }
1065 delete[] old_realms;
1066 }
1067 Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1068 Local<Context> context =
1069 Context::New(isolate, nullptr, global_template, global_object);
1070 DCHECK(!try_catch.HasCaught());
1071 if (context.IsEmpty()) return MaybeLocal<Context>();
1072 InitializeModuleEmbedderData(context);
1073 data->realms_[index].Reset(isolate, context);
1074 args.GetReturnValue().Set(index);
1075 return context;
1076}
1077
1078void Shell::DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args,
1079 int index) {
1080 Isolate* isolate = args.GetIsolate();
1081 PerIsolateData* data = PerIsolateData::Get(isolate);
1082 DisposeModuleEmbedderData(data->realms_[index].Get(isolate));
1083 data->realms_[index].Reset();
1084 isolate->ContextDisposedNotification();
1085 isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime());
1086}
1087
1088// Realm.create() creates a new realm with a distinct security token
1089// and returns its index.
1090void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1091 CreateRealm(args, -1, v8::MaybeLocal<Value>());
1092}
1093
1094// Realm.createAllowCrossRealmAccess() creates a new realm with the same
1095// security token as the current realm.
1096void Shell::RealmCreateAllowCrossRealmAccess(
1097 const v8::FunctionCallbackInfo<v8::Value>& args) {
1098 Local<Context> context;
1099 if (CreateRealm(args, -1, v8::MaybeLocal<Value>()).ToLocal(&context)) {
1100 context->SetSecurityToken(
1101 args.GetIsolate()->GetEnteredOrMicrotaskContext()->GetSecurityToken());
1102 }
1103}
1104
1105// Realm.navigate(i) creates a new realm with a distinct security token
1106// in place of realm i.
1107void Shell::RealmNavigate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1108 Isolate* isolate = args.GetIsolate();
1109 PerIsolateData* data = PerIsolateData::Get(isolate);
1110 int index = data->RealmIndexOrThrow(args, 0);
1111 if (index == -1) return;
1112 if (index == 0 || index == data->realm_current_ ||
1113 index == data->realm_switch_) {
1114 Throw(args.GetIsolate(), "Invalid realm index");
1115 return;
1116 }
1117
1118 Local<Context> context = Local<Context>::New(isolate, data->realms_[index]);
1119 v8::MaybeLocal<Value> global_object = context->Global();
1120 DisposeRealm(args, index);
1121 CreateRealm(args, index, global_object);
1122}
1123
1124// Realm.detachGlobal(i) detaches the global objects of realm i from realm i.
1125void Shell::RealmDetachGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1126 Isolate* isolate = args.GetIsolate();
1127 PerIsolateData* data = PerIsolateData::Get(isolate);
1128 int index = data->RealmIndexOrThrow(args, 0);
1129 if (index == -1) return;
1130 if (index == 0 || index == data->realm_current_ ||
1131 index == data->realm_switch_) {
1132 Throw(args.GetIsolate(), "Invalid realm index");
1133 return;
1134 }
1135
1136 HandleScope scope(isolate);
1137 Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
1138 realm->DetachGlobal();
1139}
1140
1141// Realm.dispose(i) disposes the reference to the realm i.
1142void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
1143 Isolate* isolate = args.GetIsolate();
1144 PerIsolateData* data = PerIsolateData::Get(isolate);
1145 int index = data->RealmIndexOrThrow(args, 0);
1146 if (index == -1) return;
1147 if (index == 0 ||
1148 index == data->realm_current_ || index == data->realm_switch_) {
1149 Throw(args.GetIsolate(), "Invalid realm index");
1150 return;
1151 }
1152 DisposeRealm(args, index);
1153}
1154
1155
1156// Realm.switch(i) switches to the realm i for consecutive interactive inputs.
1157void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) {
1158 Isolate* isolate = args.GetIsolate();
1159 PerIsolateData* data = PerIsolateData::Get(isolate);
1160 int index = data->RealmIndexOrThrow(args, 0);
1161 if (index == -1) return;
1162 data->realm_switch_ = index;
1163}
1164
1165
1166// Realm.eval(i, s) evaluates s in realm i and returns the result.
1167void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
1168 Isolate* isolate = args.GetIsolate();
1169 PerIsolateData* data = PerIsolateData::Get(isolate);
1170 int index = data->RealmIndexOrThrow(args, 0);
1171 if (index == -1) return;
1172 if (args.Length() < 2 || !args[1]->IsString()) {
1173 Throw(args.GetIsolate(), "Invalid argument");
1174 return;
1175 }
1176 ScriptCompiler::Source script_source(
1177 args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked());
1178 Local<UnboundScript> script;
1179 if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
1180 .ToLocal(&script)) {
1181 return;
1182 }
1183 Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
1184 realm->Enter();
1185 int previous_index = data->realm_current_;
1186 data->realm_current_ = data->realm_switch_ = index;
1187 Local<Value> result;
1188 if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) {
1189 realm->Exit();
1190 data->realm_current_ = data->realm_switch_ = previous_index;
1191 return;
1192 }
1193 realm->Exit();
1194 data->realm_current_ = data->realm_switch_ = previous_index;
1195 args.GetReturnValue().Set(result);
1196}
1197
1198
1199// Realm.shared is an accessor for a single shared value across realms.
1200void Shell::RealmSharedGet(Local<String> property,
1201 const PropertyCallbackInfo<Value>& info) {
1202 Isolate* isolate = info.GetIsolate();
1203 PerIsolateData* data = PerIsolateData::Get(isolate);
1204 if (data->realm_shared_.IsEmpty()) return;
1205 info.GetReturnValue().Set(data->realm_shared_);
1206}
1207
1208void Shell::RealmSharedSet(Local<String> property,
1209 Local<Value> value,
1210 const PropertyCallbackInfo<void>& info) {
1211 Isolate* isolate = info.GetIsolate();
1212 PerIsolateData* data = PerIsolateData::Get(isolate);
1213 data->realm_shared_.Reset(isolate, value);
1214}
1215
1216// async_hooks.createHook() registers functions to be called for different
1217// lifetime events of each async operation.
1218void Shell::AsyncHooksCreateHook(
1219 const v8::FunctionCallbackInfo<v8::Value>& args) {
1220 Local<Object> wrap =
1221 PerIsolateData::Get(args.GetIsolate())->GetAsyncHooks()->CreateHook(args);
1222 args.GetReturnValue().Set(wrap);
1223}
1224
1225// async_hooks.executionAsyncId() returns the asyncId of the current execution
1226// context.
1227void Shell::AsyncHooksExecutionAsyncId(
1228 const v8::FunctionCallbackInfo<v8::Value>& args) {
1229 Isolate* isolate = args.GetIsolate();
1230 HandleScope handle_scope(isolate);
1231 args.GetReturnValue().Set(v8::Number::New(
1232 isolate,
1233 PerIsolateData::Get(isolate)->GetAsyncHooks()->GetExecutionAsyncId()));
1234}
1235
1236void Shell::AsyncHooksTriggerAsyncId(
1237 const v8::FunctionCallbackInfo<v8::Value>& args) {
1238 Isolate* isolate = args.GetIsolate();
1239 HandleScope handle_scope(isolate);
1240 args.GetReturnValue().Set(v8::Number::New(
1241 isolate,
1242 PerIsolateData::Get(isolate)->GetAsyncHooks()->GetTriggerAsyncId()));
1243}
1244
1245void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) {
1246 for (int i = 0; i < args.Length(); i++) {
1247 HandleScope handle_scope(args.GetIsolate());
1248 if (i != 0) {
1249 fprintf(file, " ");
1250 }
1251
1252 // Explicitly catch potential exceptions in toString().
1253 v8::TryCatch try_catch(args.GetIsolate());
1254 Local<Value> arg = args[i];
1255 Local<String> str_obj;
1256
1257 if (arg->IsSymbol()) {
1258 arg = Local<Symbol>::Cast(arg)->Name();
1259 }
1260 if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
1261 .ToLocal(&str_obj)) {
1262 try_catch.ReThrow();
1263 return;
1264 }
1265
1266 v8::String::Utf8Value str(args.GetIsolate(), str_obj);
1267 int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), file));
1268 if (n != str.length()) {
1269 printf("Error in fwrite\n");
1270 base::OS::ExitProcess(1);
1271 }
1272 }
1273}
1274
1275void WriteAndFlush(FILE* file,
1276 const v8::FunctionCallbackInfo<v8::Value>& args) {
1277 WriteToFile(file, args);
1278 fprintf(file, "\n");
1279 fflush(file);
1280}
1281
1282void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
1283 WriteAndFlush(stdout, args);
1284}
1285
1286void Shell::PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args) {
1287 WriteAndFlush(stderr, args);
1288}
1289
1290void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) {
1291 WriteToFile(stdout, args);
1292}
1293
1294void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
1295 String::Utf8Value file(args.GetIsolate(), args[0]);
1296 if (*file == nullptr) {
1297 Throw(args.GetIsolate(), "Error loading file");
1298 return;
1299 }
1300 if (args.Length() == 2) {
1301 String::Utf8Value format(args.GetIsolate(), args[1]);
1302 if (*format && std::strcmp(*format, "binary") == 0) {
1303 ReadBuffer(args);
1304 return;
1305 }
1306 }
1307 Local<String> source = ReadFile(args.GetIsolate(), *file);
1308 if (source.IsEmpty()) {
1309 Throw(args.GetIsolate(), "Error loading file");
1310 return;
1311 }
1312 args.GetReturnValue().Set(source);
1313}
1314
1315
1316Local<String> Shell::ReadFromStdin(Isolate* isolate) {
1317 static const int kBufferSize = 256;
1318 char buffer[kBufferSize];
1319 Local<String> accumulator =
1320 String::NewFromUtf8(isolate, "", NewStringType::kNormal).ToLocalChecked();
1321 int length;
1322 while (true) {
1323 // Continue reading if the line ends with an escape '\\' or the line has
1324 // not been fully read into the buffer yet (does not end with '\n').
1325 // If fgets gets an error, just give up.
1326 char* input = nullptr;
1327 input = fgets(buffer, kBufferSize, stdin);
1328 if (input == nullptr) return Local<String>();
1329 length = static_cast<int>(strlen(buffer));
1330 if (length == 0) {
1331 return accumulator;
1332 } else if (buffer[length-1] != '\n') {
1333 accumulator = String::Concat(
1334 isolate, accumulator,
1335 String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
1336 .ToLocalChecked());
1337 } else if (length > 1 && buffer[length-2] == '\\') {
1338 buffer[length-2] = '\n';
1339 accumulator =
1340 String::Concat(isolate, accumulator,
1341 String::NewFromUtf8(isolate, buffer,
1342 NewStringType::kNormal, length - 1)
1343 .ToLocalChecked());
1344 } else {
1345 return String::Concat(
1346 isolate, accumulator,
1347 String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
1348 length - 1)
1349 .ToLocalChecked());
1350 }
1351 }
1352}
1353
1354
1355void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
1356 for (int i = 0; i < args.Length(); i++) {
1357 HandleScope handle_scope(args.GetIsolate());
1358 String::Utf8Value file(args.GetIsolate(), args[i]);
1359 if (*file == nullptr) {
1360 Throw(args.GetIsolate(), "Error loading file");
1361 return;
1362 }
1363 Local<String> source = ReadFile(args.GetIsolate(), *file);
1364 if (source.IsEmpty()) {
1365 Throw(args.GetIsolate(), "Error loading file");
1366 return;
1367 }
1368 if (!ExecuteString(
1369 args.GetIsolate(), source,
1370 String::NewFromUtf8(args.GetIsolate(), *file,
1371 NewStringType::kNormal)
1372 .ToLocalChecked(),
1373 kNoPrintResult,
1374 options.quiet_load ? kNoReportExceptions : kReportExceptions,
1375 kNoProcessMessageQueue)) {
1376 Throw(args.GetIsolate(), "Error executing file");
1377 return;
1378 }
1379 }
1380}
1381
1382void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
1383 Isolate* isolate = args.GetIsolate();
1384 args.GetReturnValue().Set(v8::Number::New(isolate, 0));
1385 if (args.Length() == 0 || !args[0]->IsFunction()) return;
1386 Local<Function> callback = Local<Function>::Cast(args[0]);
1387 Local<Context> context = isolate->GetCurrentContext();
1388 PerIsolateData::Get(isolate)->SetTimeout(callback, context);
1389}
1390
1391void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
1392 Isolate* isolate = args.GetIsolate();
1393 HandleScope handle_scope(isolate);
1394 if (args.Length() < 1 || !args[0]->IsString()) {
1395 Throw(args.GetIsolate(), "1st argument must be string");
1396 return;
1397 }
1398
1399 // d8 honors `options={type: string}`, which means the first argument is
1400 // not a filename but string of script to be run.
1401 bool load_from_file = true;
1402 if (args.Length() > 1 && args[1]->IsObject()) {
1403 Local<Object> object = args[1].As<Object>();
1404 Local<Context> context = isolate->GetCurrentContext();
1405 Local<Value> value = GetValue(args.GetIsolate(), context, object, "type");
1406 if (value->IsString()) {
1407 Local<String> worker_type = value->ToString(context).ToLocalChecked();
1408 String::Utf8Value str(isolate, worker_type);
1409 if (strcmp("string", *str) == 0) {
1410 load_from_file = false;
1411 } else if (strcmp("classic", *str) == 0) {
1412 load_from_file = true;
1413 } else {
1414 Throw(args.GetIsolate(), "Unsupported worker type");
1415 return;
1416 }
1417 }
1418 }
1419
1420 Local<Value> source;
1421 if (load_from_file) {
1422 String::Utf8Value filename(args.GetIsolate(), args[0]);
1423 source = ReadFile(args.GetIsolate(), *filename);
1424 if (source.IsEmpty()) {
1425 Throw(args.GetIsolate(), "Error loading worker script");
1426 return;
1427 }
1428 } else {
1429 source = args[0];
1430 }
1431
1432 if (!args.IsConstructCall()) {
1433 Throw(args.GetIsolate(), "Worker must be constructed with new");
1434 return;
1435 }
1436
1437 {
1438 base::MutexGuard lock_guard(workers_mutex_.Pointer());
1439 if (workers_.size() >= kMaxWorkers) {
1440 Throw(args.GetIsolate(), "Too many workers, I won't let you create more");
1441 return;
1442 }
1443
1444 // Initialize the embedder field to nullptr; if we return early without
1445 // creating a new Worker (because the main thread is terminating) we can
1446 // early-out from the instance calls.
1447 args.Holder()->SetAlignedPointerInInternalField(0, nullptr);
1448
1449 if (!allow_new_workers_) return;
1450
1451 Worker* worker = new Worker;
1452 args.Holder()->SetAlignedPointerInInternalField(0, worker);
1453 workers_.push_back(worker);
1454
1455 String::Utf8Value script(args.GetIsolate(), source);
1456 if (!*script) {
1457 Throw(args.GetIsolate(), "Can't get worker script");
1458 return;
1459 }
1460 worker->StartExecuteInThread(*script);
1461 }
1462}
1463
1464
1465void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1466 Isolate* isolate = args.GetIsolate();
1467 HandleScope handle_scope(isolate);
1468
1469 if (args.Length() < 1) {
1470 Throw(isolate, "Invalid argument");
1471 return;
1472 }
1473
1474 Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1475 if (!worker) {
1476 return;
1477 }
1478
1479 Local<Value> message = args[0];
1480 Local<Value> transfer =
1481 args.Length() >= 2 ? args[1] : Local<Value>::Cast(Undefined(isolate));
1482 std::unique_ptr<SerializationData> data =
1483 Shell::SerializeValue(isolate, message, transfer);
1484 if (data) {
1485 worker->PostMessage(std::move(data));
1486 }
1487}
1488
1489
1490void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1491 Isolate* isolate = args.GetIsolate();
1492 HandleScope handle_scope(isolate);
1493 Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1494 if (!worker) {
1495 return;
1496 }
1497
1498 std::unique_ptr<SerializationData> data = worker->GetMessage();
1499 if (data) {
1500 Local<Value> value;
1501 if (Shell::DeserializeValue(isolate, std::move(data)).ToLocal(&value)) {
1502 args.GetReturnValue().Set(value);
1503 }
1504 }
1505}
1506
1507
1508void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1509 Isolate* isolate = args.GetIsolate();
1510 HandleScope handle_scope(isolate);
1511 Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1512 if (!worker) {
1513 return;
1514 }
1515
1516 worker->Terminate();
1517}
1518
1519
1520void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) {
1521 int exit_code = (*args)[0]
1522 ->Int32Value(args->GetIsolate()->GetCurrentContext())
1523 .FromMaybe(0);
1524 CleanupWorkers();
1525 args->GetIsolate()->Exit();
1526 OnExit(args->GetIsolate());
1527 base::OS::ExitProcess(exit_code);
1528}
1529
1530
1531void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
1532 base::CallOnce(&quit_once_, &QuitOnce,
1533 const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args));
1534}
1535
1536void Shell::WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1537 SetWaitUntilDone(args.GetIsolate(), true);
1538}
1539
1540void Shell::NotifyDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1541 SetWaitUntilDone(args.GetIsolate(), false);
1542}
1543
1544void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
1545 args.GetReturnValue().Set(
1546 String::NewFromUtf8(args.GetIsolate(), V8::GetVersion(),
1547 NewStringType::kNormal).ToLocalChecked());
1548}
1549
1550
1551void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) {
1552 HandleScope handle_scope(isolate);
1553 Local<Context> context = isolate->GetCurrentContext();
1554 bool enter_context = context.IsEmpty();
1555 if (enter_context) {
1556 context = Local<Context>::New(isolate, evaluation_context_);
1557 context->Enter();
1558 }
1559 // Converts a V8 value to a C string.
1560 auto ToCString = [](const v8::String::Utf8Value& value) {
1561 return *value ? *value : "<string conversion failed>";
1562 };
1563
1564 v8::String::Utf8Value exception(isolate, try_catch->Exception());
1565 const char* exception_string = ToCString(exception);
1566 Local<Message> message = try_catch->Message();
1567 if (message.IsEmpty()) {
1568 // V8 didn't provide any extra information about this error; just
1569 // print the exception.
1570 printf("%s\n", exception_string);
1571 } else if (message->GetScriptOrigin().Options().IsWasm()) {
1572 // Print wasm-function[(function index)]:(offset): (message).
1573 int function_index = message->GetLineNumber(context).FromJust() - 1;
1574 int offset = message->GetStartColumn(context).FromJust();
1575 printf("wasm-function[%d]:%d: %s\n", function_index, offset,
1576 exception_string);
1577 } else {
1578 // Print (filename):(line number): (message).
1579 v8::String::Utf8Value filename(isolate,
1580 message->GetScriptOrigin().ResourceName());
1581 const char* filename_string = ToCString(filename);
1582 int linenum = message->GetLineNumber(context).FromMaybe(-1);
1583 printf("%s:%i: %s\n", filename_string, linenum, exception_string);
1584 Local<String> sourceline;
1585 if (message->GetSourceLine(context).ToLocal(&sourceline)) {
1586 // Print line of source code.
1587 v8::String::Utf8Value sourcelinevalue(isolate, sourceline);
1588 const char* sourceline_string = ToCString(sourcelinevalue);
1589 printf("%s\n", sourceline_string);
1590 // Print wavy underline (GetUnderline is deprecated).
1591 int start = message->GetStartColumn(context).FromJust();
1592 for (int i = 0; i < start; i++) {
1593 printf(" ");
1594 }
1595 int end = message->GetEndColumn(context).FromJust();
1596 for (int i = start; i < end; i++) {
1597 printf("^");
1598 }
1599 printf("\n");
1600 }
1601 }
1602 Local<Value> stack_trace_string;
1603 if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) &&
1604 stack_trace_string->IsString()) {
1605 v8::String::Utf8Value stack_trace(isolate,
1606 Local<String>::Cast(stack_trace_string));
1607 printf("%s\n", ToCString(stack_trace));
1608 }
1609 printf("\n");
1610 if (enter_context) context->Exit();
1611}
1612
1613
1614int32_t* Counter::Bind(const char* name, bool is_histogram) {
1615 int i;
1616 for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
1617 name_[i] = static_cast<char>(name[i]);
1618 name_[i] = '\0';
1619 is_histogram_ = is_histogram;
1620 return ptr();
1621}
1622
1623
1624void Counter::AddSample(int32_t sample) {
1625 count_++;
1626 sample_total_ += sample;
1627}
1628
1629
1630CounterCollection::CounterCollection() {
1631 magic_number_ = 0xDEADFACE;
1632 max_counters_ = kMaxCounters;
1633 max_name_size_ = Counter::kMaxNameSize;
1634 counters_in_use_ = 0;
1635}
1636
1637
1638Counter* CounterCollection::GetNextCounter() {
1639 if (counters_in_use_ == kMaxCounters) return nullptr;
1640 return &counters_[counters_in_use_++];
1641}
1642
1643
1644void Shell::MapCounters(v8::Isolate* isolate, const char* name) {
1645 counters_file_ = base::OS::MemoryMappedFile::create(
1646 name, sizeof(CounterCollection), &local_counters_);
1647 void* memory =
1648 (counters_file_ == nullptr) ? nullptr : counters_file_->memory();
1649 if (memory == nullptr) {
1650 printf("Could not map counters file %s\n", name);
1651 base::OS::ExitProcess(1);
1652 }
1653 counters_ = static_cast<CounterCollection*>(memory);
1654 isolate->SetCounterFunction(LookupCounter);
1655 isolate->SetCreateHistogramFunction(CreateHistogram);
1656 isolate->SetAddHistogramSampleFunction(AddHistogramSample);
1657}
1658
1659Counter* Shell::GetCounter(const char* name, bool is_histogram) {
1660 auto map_entry = counter_map_->find(name);
1661 Counter* counter =
1662 map_entry != counter_map_->end() ? map_entry->second : nullptr;
1663
1664 if (counter == nullptr) {
1665 counter = counters_->GetNextCounter();
1666 if (counter != nullptr) {
1667 (*counter_map_)[name] = counter;
1668 counter->Bind(name, is_histogram);
1669 }
1670 } else {
1671 DCHECK(counter->is_histogram() == is_histogram);
1672 }
1673 return counter;
1674}
1675
1676
1677int* Shell::LookupCounter(const char* name) {
1678 Counter* counter = GetCounter(name, false);
1679
1680 if (counter != nullptr) {
1681 return counter->ptr();
1682 } else {
1683 return nullptr;
1684 }
1685}
1686
1687
1688void* Shell::CreateHistogram(const char* name,
1689 int min,
1690 int max,
1691 size_t buckets) {
1692 return GetCounter(name, true);
1693}
1694
1695
1696void Shell::AddHistogramSample(void* histogram, int sample) {
1697 Counter* counter = reinterpret_cast<Counter*>(histogram);
1698 counter->AddSample(sample);
1699}
1700
1701// Turn a value into a human-readable string.
1702Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) {
1703 v8::Local<v8::Context> context =
1704 v8::Local<v8::Context>::New(isolate, evaluation_context_);
1705 if (stringify_function_.IsEmpty()) {
1706 Local<String> source =
1707 String::NewFromUtf8(isolate, stringify_source_, NewStringType::kNormal)
1708 .ToLocalChecked();
1709 Local<String> name =
1710 String::NewFromUtf8(isolate, "d8-stringify", NewStringType::kNormal)
1711 .ToLocalChecked();
1712 ScriptOrigin origin(name);
1713 Local<Script> script =
1714 Script::Compile(context, source, &origin).ToLocalChecked();
1715 stringify_function_.Reset(
1716 isolate, script->Run(context).ToLocalChecked().As<Function>());
1717 }
1718 Local<Function> fun = Local<Function>::New(isolate, stringify_function_);
1719 Local<Value> argv[1] = {value};
1720 v8::TryCatch try_catch(isolate);
1721 MaybeLocal<Value> result = fun->Call(context, Undefined(isolate), 1, argv);
1722 if (result.IsEmpty()) return String::Empty(isolate);
1723 return result.ToLocalChecked().As<String>();
1724}
1725
1726
1727Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
1728 Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
1729 global_template->Set(
1730 String::NewFromUtf8(isolate, "print", NewStringType::kNormal)
1731 .ToLocalChecked(),
1732 FunctionTemplate::New(isolate, Print));
1733 global_template->Set(
1734 String::NewFromUtf8(isolate, "printErr", NewStringType::kNormal)
1735 .ToLocalChecked(),
1736 FunctionTemplate::New(isolate, PrintErr));
1737 global_template->Set(
1738 String::NewFromUtf8(isolate, "write", NewStringType::kNormal)
1739 .ToLocalChecked(),
1740 FunctionTemplate::New(isolate, Write));
1741 global_template->Set(
1742 String::NewFromUtf8(isolate, "read", NewStringType::kNormal)
1743 .ToLocalChecked(),
1744 FunctionTemplate::New(isolate, Read));
1745 global_template->Set(
1746 String::NewFromUtf8(isolate, "readbuffer", NewStringType::kNormal)
1747 .ToLocalChecked(),
1748 FunctionTemplate::New(isolate, ReadBuffer));
1749 global_template->Set(
1750 String::NewFromUtf8(isolate, "readline", NewStringType::kNormal)
1751 .ToLocalChecked(),
1752 FunctionTemplate::New(isolate, ReadLine));
1753 global_template->Set(
1754 String::NewFromUtf8(isolate, "load", NewStringType::kNormal)
1755 .ToLocalChecked(),
1756 FunctionTemplate::New(isolate, Load));
1757 global_template->Set(
1758 String::NewFromUtf8(isolate, "setTimeout", NewStringType::kNormal)
1759 .ToLocalChecked(),
1760 FunctionTemplate::New(isolate, SetTimeout));
1761 // Some Emscripten-generated code tries to call 'quit', which in turn would
1762 // call C's exit(). This would lead to memory leaks, because there is no way
1763 // we can terminate cleanly then, so we need a way to hide 'quit'.
1764 if (!options.omit_quit) {
1765 global_template->Set(
1766 String::NewFromUtf8(isolate, "quit", NewStringType::kNormal)
1767 .ToLocalChecked(),
1768 FunctionTemplate::New(isolate, Quit));
1769 }
1770 Local<ObjectTemplate> test_template = ObjectTemplate::New(isolate);
1771 global_template->Set(
1772 String::NewFromUtf8(isolate, "testRunner", NewStringType::kNormal)
1773 .ToLocalChecked(),
1774 test_template);
1775 test_template->Set(
1776 String::NewFromUtf8(isolate, "notifyDone", NewStringType::kNormal)
1777 .ToLocalChecked(),
1778 FunctionTemplate::New(isolate, NotifyDone));
1779 test_template->Set(
1780 String::NewFromUtf8(isolate, "waitUntilDone", NewStringType::kNormal)
1781 .ToLocalChecked(),
1782 FunctionTemplate::New(isolate, WaitUntilDone));
1783 // Reliable access to quit functionality. The "quit" method function
1784 // installed on the global object can be hidden with the --omit-quit flag
1785 // (e.g. on asan bots).
1786 test_template->Set(
1787 String::NewFromUtf8(isolate, "quit", NewStringType::kNormal)
1788 .ToLocalChecked(),
1789 FunctionTemplate::New(isolate, Quit));
1790
1791 global_template->Set(
1792 String::NewFromUtf8(isolate, "version", NewStringType::kNormal)
1793 .ToLocalChecked(),
1794 FunctionTemplate::New(isolate, Version));
1795 global_template->Set(
1796 Symbol::GetToStringTag(isolate),
1797 String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1798 .ToLocalChecked());
1799
1800 // Bind the Realm object.
1801 Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
1802 realm_template->Set(
1803 String::NewFromUtf8(isolate, "current", NewStringType::kNormal)
1804 .ToLocalChecked(),
1805 FunctionTemplate::New(isolate, RealmCurrent));
1806 realm_template->Set(
1807 String::NewFromUtf8(isolate, "owner", NewStringType::kNormal)
1808 .ToLocalChecked(),
1809 FunctionTemplate::New(isolate, RealmOwner));
1810 realm_template->Set(
1811 String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1812 .ToLocalChecked(),
1813 FunctionTemplate::New(isolate, RealmGlobal));
1814 realm_template->Set(
1815 String::NewFromUtf8(isolate, "create", NewStringType::kNormal)
1816 .ToLocalChecked(),
1817 FunctionTemplate::New(isolate, RealmCreate));
1818 realm_template->Set(
1819 String::NewFromUtf8(isolate, "createAllowCrossRealmAccess",
1820 NewStringType::kNormal)
1821 .ToLocalChecked(),
1822 FunctionTemplate::New(isolate, RealmCreateAllowCrossRealmAccess));
1823 realm_template->Set(
1824 String::NewFromUtf8(isolate, "navigate", NewStringType::kNormal)
1825 .ToLocalChecked(),
1826 FunctionTemplate::New(isolate, RealmNavigate));
1827 realm_template->Set(
1828 String::NewFromUtf8(isolate, "detachGlobal", NewStringType::kNormal)
1829 .ToLocalChecked(),
1830 FunctionTemplate::New(isolate, RealmDetachGlobal));
1831 realm_template->Set(
1832 String::NewFromUtf8(isolate, "dispose", NewStringType::kNormal)
1833 .ToLocalChecked(),
1834 FunctionTemplate::New(isolate, RealmDispose));
1835 realm_template->Set(
1836 String::NewFromUtf8(isolate, "switch", NewStringType::kNormal)
1837 .ToLocalChecked(),
1838 FunctionTemplate::New(isolate, RealmSwitch));
1839 realm_template->Set(
1840 String::NewFromUtf8(isolate, "eval", NewStringType::kNormal)
1841 .ToLocalChecked(),
1842 FunctionTemplate::New(isolate, RealmEval));
1843 realm_template->SetAccessor(
1844 String::NewFromUtf8(isolate, "shared", NewStringType::kNormal)
1845 .ToLocalChecked(),
1846 RealmSharedGet, RealmSharedSet);
1847 global_template->Set(
1848 String::NewFromUtf8(isolate, "Realm", NewStringType::kNormal)
1849 .ToLocalChecked(),
1850 realm_template);
1851
1852 Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
1853 performance_template->Set(
1854 String::NewFromUtf8(isolate, "now", NewStringType::kNormal)
1855 .ToLocalChecked(),
1856 FunctionTemplate::New(isolate, PerformanceNow));
1857 global_template->Set(
1858 String::NewFromUtf8(isolate, "performance", NewStringType::kNormal)
1859 .ToLocalChecked(),
1860 performance_template);
1861
1862 Local<FunctionTemplate> worker_fun_template =
1863 FunctionTemplate::New(isolate, WorkerNew);
1864 Local<Signature> worker_signature =
1865 Signature::New(isolate, worker_fun_template);
1866 worker_fun_template->SetClassName(
1867 String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1868 .ToLocalChecked());
1869 worker_fun_template->ReadOnlyPrototype();
1870 worker_fun_template->PrototypeTemplate()->Set(
1871 String::NewFromUtf8(isolate, "terminate", NewStringType::kNormal)
1872 .ToLocalChecked(),
1873 FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(),
1874 worker_signature));
1875 worker_fun_template->PrototypeTemplate()->Set(
1876 String::NewFromUtf8(isolate, "postMessage", NewStringType::kNormal)
1877 .ToLocalChecked(),
1878 FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(),
1879 worker_signature));
1880 worker_fun_template->PrototypeTemplate()->Set(
1881 String::NewFromUtf8(isolate, "getMessage", NewStringType::kNormal)
1882 .ToLocalChecked(),
1883 FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(),
1884 worker_signature));
1885 worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1);
1886 global_template->Set(
1887 String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1888 .ToLocalChecked(),
1889 worker_fun_template);
1890
1891 Local<ObjectTemplate> os_templ = ObjectTemplate::New(isolate);
1892 AddOSMethods(isolate, os_templ);
1893 global_template->Set(
1894 String::NewFromUtf8(isolate, "os", NewStringType::kNormal)
1895 .ToLocalChecked(),
1896 os_templ);
1897
1898 if (i::FLAG_expose_async_hooks) {
1899 Local<ObjectTemplate> async_hooks_templ = ObjectTemplate::New(isolate);
1900 async_hooks_templ->Set(
1901 String::NewFromUtf8(isolate, "createHook", NewStringType::kNormal)
1902 .ToLocalChecked(),
1903 FunctionTemplate::New(isolate, AsyncHooksCreateHook));
1904 async_hooks_templ->Set(
1905 String::NewFromUtf8(isolate, "executionAsyncId", NewStringType::kNormal)
1906 .ToLocalChecked(),
1907 FunctionTemplate::New(isolate, AsyncHooksExecutionAsyncId));
1908 async_hooks_templ->Set(
1909 String::NewFromUtf8(isolate, "triggerAsyncId", NewStringType::kNormal)
1910 .ToLocalChecked(),
1911 FunctionTemplate::New(isolate, AsyncHooksTriggerAsyncId));
1912 global_template->Set(
1913 String::NewFromUtf8(isolate, "async_hooks", NewStringType::kNormal)
1914 .ToLocalChecked(),
1915 async_hooks_templ);
1916 }
1917
1918 return global_template;
1919}
1920
1921static void PrintNonErrorsMessageCallback(Local<Message> message,
1922 Local<Value> error) {
1923 // Nothing to do here for errors, exceptions thrown up to the shell will be
1924 // reported
1925 // separately by {Shell::ReportException} after they are caught.
1926 // Do print other kinds of messages.
1927 switch (message->ErrorLevel()) {
1928 case v8::Isolate::kMessageWarning:
1929 case v8::Isolate::kMessageLog:
1930 case v8::Isolate::kMessageInfo:
1931 case v8::Isolate::kMessageDebug: {
1932 break;
1933 }
1934
1935 case v8::Isolate::kMessageError: {
1936 // Ignore errors, printed elsewhere.
1937 return;
1938 }
1939
1940 default: {
1941 UNREACHABLE();
1942 break;
1943 }
1944 }
1945 // Converts a V8 value to a C string.
1946 auto ToCString = [](const v8::String::Utf8Value& value) {
1947 return *value ? *value : "<string conversion failed>";
1948 };
1949 Isolate* isolate = Isolate::GetCurrent();
1950 v8::String::Utf8Value msg(isolate, message->Get());
1951 const char* msg_string = ToCString(msg);
1952 // Print (filename):(line number): (message).
1953 v8::String::Utf8Value filename(isolate,
1954 message->GetScriptOrigin().ResourceName());
1955 const char* filename_string = ToCString(filename);
1956 Maybe<int> maybeline = message->GetLineNumber(isolate->GetCurrentContext());
1957 int linenum = maybeline.IsJust() ? maybeline.FromJust() : -1;
1958 printf("%s:%i: %s\n", filename_string, linenum, msg_string);
1959}
1960
1961void Shell::Initialize(Isolate* isolate) {
1962 // Set up counters
1963 if (i::StrLength(i::FLAG_map_counters) != 0)
1964 MapCounters(isolate, i::FLAG_map_counters);
1965 // Disable default message reporting.
1966 isolate->AddMessageListenerWithErrorLevel(
1967 PrintNonErrorsMessageCallback,
1968 v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
1969 v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
1970 v8::Isolate::kMessageLog);
1971}
1972
1973
1974Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
1975 // This needs to be a critical section since this is not thread-safe
1976 base::MutexGuard lock_guard(context_mutex_.Pointer());
1977 // Initialize the global objects
1978 Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1979 EscapableHandleScope handle_scope(isolate);
1980 Local<Context> context = Context::New(isolate, nullptr, global_template);
1981 DCHECK(!context.IsEmpty());
1982 InitializeModuleEmbedderData(context);
1983 if (options.include_arguments) {
1984 Context::Scope scope(context);
1985 const std::vector<const char*>& args = options.arguments;
1986 int size = static_cast<int>(args.size());
1987 Local<Array> array = Array::New(isolate, size);
1988 for (int i = 0; i < size; i++) {
1989 Local<String> arg =
1990 v8::String::NewFromUtf8(isolate, args[i], v8::NewStringType::kNormal)
1991 .ToLocalChecked();
1992 Local<Number> index = v8::Number::New(isolate, i);
1993 array->Set(context, index, arg).FromJust();
1994 }
1995 Local<String> name =
1996 String::NewFromUtf8(isolate, "arguments", NewStringType::kInternalized)
1997 .ToLocalChecked();
1998 context->Global()->Set(context, name, array).FromJust();
1999 }
2000 return handle_scope.Escape(context);
2001}
2002
2003void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
2004 HandleScope handle_scope(isolate);
2005 Local<Context> context = Context::New(isolate);
2006 Context::Scope context_scope(context);
2007
2008 Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate)
2009 ->interpreter()
2010 ->GetDispatchCountersObject();
2011 std::ofstream dispatch_counters_stream(
2012 i::FLAG_trace_ignition_dispatches_output_file);
2013 dispatch_counters_stream << *String::Utf8Value(
2014 isolate, JSON::Stringify(context, dispatch_counters).ToLocalChecked());
2015}
2016
2017namespace {
2018int LineFromOffset(Local<debug::Script> script, int offset) {
2019 debug::Location location = script->GetSourceLocation(offset);
2020 return location.GetLineNumber();
2021}
2022
2023void WriteLcovDataForRange(std::vector<uint32_t>& lines, int start_line,
2024 int end_line, uint32_t count) {
2025 // Ensure space in the array.
2026 lines.resize(std::max(static_cast<size_t>(end_line + 1), lines.size()), 0);
2027 // Boundary lines could be shared between two functions with different
2028 // invocation counts. Take the maximum.
2029 lines[start_line] = std::max(lines[start_line], count);
2030 lines[end_line] = std::max(lines[end_line], count);
2031 // Invocation counts for non-boundary lines are overwritten.
2032 for (int k = start_line + 1; k < end_line; k++) lines[k] = count;
2033}
2034
2035void WriteLcovDataForNamedRange(std::ostream& sink,
2036 std::vector<uint32_t>& lines,
2037 const std::string& name, int start_line,
2038 int end_line, uint32_t count) {
2039 WriteLcovDataForRange(lines, start_line, end_line, count);
2040 sink << "FN:" << start_line + 1 << "," << name << std::endl;
2041 sink << "FNDA:" << count << "," << name << std::endl;
2042}
2043} // namespace
2044
2045// Write coverage data in LCOV format. See man page for geninfo(1).
2046void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
2047 if (!file) return;
2048 HandleScope handle_scope(isolate);
2049 debug::Coverage coverage = debug::Coverage::CollectPrecise(isolate);
2050 std::ofstream sink(file, std::ofstream::app);
2051 for (size_t i = 0; i < coverage.ScriptCount(); i++) {
2052 debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
2053 Local<debug::Script> script = script_data.GetScript();
2054 // Skip unnamed scripts.
2055 Local<String> name;
2056 if (!script->Name().ToLocal(&name)) continue;
2057 std::string file_name = ToSTLString(isolate, name);
2058 // Skip scripts not backed by a file.
2059 if (!std::ifstream(file_name).good()) continue;
2060 sink << "SF:";
2061 sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl;
2062 std::vector<uint32_t> lines;
2063 for (size_t j = 0; j < script_data.FunctionCount(); j++) {
2064 debug::Coverage::FunctionData function_data =
2065 script_data.GetFunctionData(j);
2066
2067 // Write function stats.
2068 {
2069 debug::Location start =
2070 script->GetSourceLocation(function_data.StartOffset());
2071 debug::Location end =
2072 script->GetSourceLocation(function_data.EndOffset());
2073 int start_line = start.GetLineNumber();
2074 int end_line = end.GetLineNumber();
2075 uint32_t count = function_data.Count();
2076
2077 Local<String> name;
2078 std::stringstream name_stream;
2079 if (function_data.Name().ToLocal(&name)) {
2080 name_stream << ToSTLString(isolate, name);
2081 } else {
2082 name_stream << "<" << start_line + 1 << "-";
2083 name_stream << start.GetColumnNumber() << ">";
2084 }
2085
2086 WriteLcovDataForNamedRange(sink, lines, name_stream.str(), start_line,
2087 end_line, count);
2088 }
2089
2090 // Process inner blocks.
2091 for (size_t k = 0; k < function_data.BlockCount(); k++) {
2092 debug::Coverage::BlockData block_data = function_data.GetBlockData(k);
2093 int start_line = LineFromOffset(script, block_data.StartOffset());
2094 int end_line = LineFromOffset(script, block_data.EndOffset() - 1);
2095 uint32_t count = block_data.Count();
2096 WriteLcovDataForRange(lines, start_line, end_line, count);
2097 }
2098 }
2099 // Write per-line coverage. LCOV uses 1-based line numbers.
2100 for (size_t i = 0; i < lines.size(); i++) {
2101 sink << "DA:" << (i + 1) << "," << lines[i] << std::endl;
2102 }
2103 sink << "end_of_record" << std::endl;
2104 }
2105}
2106
2107void Shell::OnExit(v8::Isolate* isolate) {
2108 // Dump basic block profiling data.
2109 if (i::FLAG_turbo_profiling) {
2110 i::BasicBlockProfiler* profiler = i::BasicBlockProfiler::Get();
2111 i::StdoutStream{} << *profiler;
2112 }
2113 isolate->Dispose();
2114
2115 if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
2116 std::vector<std::pair<std::string, Counter*>> counters(
2117 counter_map_->begin(), counter_map_->end());
2118 std::sort(counters.begin(), counters.end());
2119
2120 if (i::FLAG_dump_counters_nvp) {
2121 // Dump counters as name-value pairs.
2122 for (auto pair : counters) {
2123 std::string key = pair.first;
2124 Counter* counter = pair.second;
2125 if (counter->is_histogram()) {
2126 std::cout << "\"c:" << key << "\"=" << counter->count() << "\n";
2127 std::cout << "\"t:" << key << "\"=" << counter->sample_total()
2128 << "\n";
2129 } else {
2130 std::cout << "\"" << key << "\"=" << counter->count() << "\n";
2131 }
2132 }
2133 } else {
2134 // Dump counters in formatted boxes.
2135 constexpr int kNameBoxSize = 64;
2136 constexpr int kValueBoxSize = 13;
2137 std::cout << "+" << std::string(kNameBoxSize, '-') << "+"
2138 << std::string(kValueBoxSize, '-') << "+\n";
2139 std::cout << "| Name" << std::string(kNameBoxSize - 5, ' ') << "| Value"
2140 << std::string(kValueBoxSize - 6, ' ') << "|\n";
2141 std::cout << "+" << std::string(kNameBoxSize, '-') << "+"
2142 << std::string(kValueBoxSize, '-') << "+\n";
2143 for (auto pair : counters) {
2144 std::string key = pair.first;
2145 Counter* counter = pair.second;
2146 if (counter->is_histogram()) {
2147 std::cout << "| c:" << std::setw(kNameBoxSize - 4) << std::left << key
2148 << " | " << std::setw(kValueBoxSize - 2) << std::right
2149 << counter->count() << " |\n";
2150 std::cout << "| t:" << std::setw(kNameBoxSize - 4) << std::left << key
2151 << " | " << std::setw(kValueBoxSize - 2) << std::right
2152 << counter->sample_total() << " |\n";
2153 } else {
2154 std::cout << "| " << std::setw(kNameBoxSize - 2) << std::left << key
2155 << " | " << std::setw(kValueBoxSize - 2) << std::right
2156 << counter->count() << " |\n";
2157 }
2158 }
2159 std::cout << "+" << std::string(kNameBoxSize, '-') << "+"
2160 << std::string(kValueBoxSize, '-') << "+\n";
2161 }
2162 }
2163
2164 delete counters_file_;
2165 delete counter_map_;
2166}
2167
2168
2169static FILE* FOpen(const char* path, const char* mode) {
2170#if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
2171 FILE* result;
2172 if (fopen_s(&result, path, mode) == 0) {
2173 return result;
2174 } else {
2175 return nullptr;
2176 }
2177#else
2178 FILE* file = fopen(path, mode);
2179 if (file == nullptr) return nullptr;
2180 struct stat file_stat;
2181 if (fstat(fileno(file), &file_stat) != 0) return nullptr;
2182 bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
2183 if (is_regular_file) return file;
2184 fclose(file);
2185 return nullptr;
2186#endif
2187}
2188
2189static char* ReadChars(const char* name, int* size_out) {
2190 if (Shell::options.read_from_tcp_port >= 0) {
2191 return Shell::ReadCharsFromTcpPort(name, size_out);
2192 }
2193
2194 FILE* file = FOpen(name, "rb");
2195 if (file == nullptr) return nullptr;
2196
2197 fseek(file, 0, SEEK_END);
2198 size_t size = ftell(file);
2199 rewind(file);
2200
2201 char* chars = new char[size + 1];
2202 chars[size] = '\0';
2203 for (size_t i = 0; i < size;) {
2204 i += fread(&chars[i], 1, size - i, file);
2205 if (ferror(file)) {
2206 fclose(file);
2207 delete[] chars;
2208 return nullptr;
2209 }
2210 }
2211 fclose(file);
2212 *size_out = static_cast<int>(size);
2213 return chars;
2214}
2215
2216
2217struct DataAndPersistent {
2218 uint8_t* data;
2219 int byte_length;
2220 Global<ArrayBuffer> handle;
2221};
2222
2223
2224static void ReadBufferWeakCallback(
2225 const v8::WeakCallbackInfo<DataAndPersistent>& data) {
2226 int byte_length = data.GetParameter()->byte_length;
2227 data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
2228 -static_cast<intptr_t>(byte_length));
2229
2230 delete[] data.GetParameter()->data;
2231 data.GetParameter()->handle.Reset();
2232 delete data.GetParameter();
2233}
2234
2235
2236void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) {
2237 static_assert(sizeof(char) == sizeof(uint8_t),
2238 "char and uint8_t should both have 1 byte");
2239 Isolate* isolate = args.GetIsolate();
2240 String::Utf8Value filename(isolate, args[0]);
2241 int length;
2242 if (*filename == nullptr) {
2243 Throw(isolate, "Error loading file");
2244 return;
2245 }
2246
2247 DataAndPersistent* data = new DataAndPersistent;
2248 data->data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
2249 if (data->data == nullptr) {
2250 delete data;
2251 Throw(isolate, "Error reading file");
2252 return;
2253 }
2254 data->byte_length = length;
2255 Local<v8::ArrayBuffer> buffer = ArrayBuffer::New(isolate, data->data, length);
2256 data->handle.Reset(isolate, buffer);
2257 data->handle.SetWeak(data, ReadBufferWeakCallback,
2258 v8::WeakCallbackType::kParameter);
2259 isolate->AdjustAmountOfExternalAllocatedMemory(length);
2260
2261 args.GetReturnValue().Set(buffer);
2262}
2263
2264// Reads a file into a v8 string.
2265Local<String> Shell::ReadFile(Isolate* isolate, const char* name) {
2266 std::unique_ptr<base::OS::MemoryMappedFile> file(
2267 base::OS::MemoryMappedFile::open(
2268 name, base::OS::MemoryMappedFile::FileMode::kReadOnly));
2269 if (!file) return Local<String>();
2270
2271 int size = static_cast<int>(file->size());
2272 char* chars = static_cast<char*>(file->memory());
2273 Local<String> result;
2274 if (i::FLAG_use_external_strings && i::String::IsAscii(chars, size)) {
2275 String::ExternalOneByteStringResource* resource =
2276 new ExternalOwningOneByteStringResource(std::move(file));
2277 result = String::NewExternalOneByte(isolate, resource).ToLocalChecked();
2278 } else {
2279 result = String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
2280 .ToLocalChecked();
2281 }
2282 return result;
2283}
2284
2285
2286void Shell::RunShell(Isolate* isolate) {
2287 HandleScope outer_scope(isolate);
2288 v8::Local<v8::Context> context =
2289 v8::Local<v8::Context>::New(isolate, evaluation_context_);
2290 v8::Context::Scope context_scope(context);
2291 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2292 Local<String> name =
2293 String::NewFromUtf8(isolate, "(d8)", NewStringType::kNormal)
2294 .ToLocalChecked();
2295 printf("V8 version %s\n", V8::GetVersion());
2296 while (true) {
2297 HandleScope inner_scope(isolate);
2298 printf("d8> ");
2299 Local<String> input = Shell::ReadFromStdin(isolate);
2300 if (input.IsEmpty()) break;
2301 ExecuteString(isolate, input, name, kPrintResult, kReportExceptions,
2302 kProcessMessageQueue);
2303 }
2304 printf("\n");
2305 // We need to explicitly clean up the module embedder data for
2306 // the interative shell context.
2307 DisposeModuleEmbedderData(context);
2308}
2309
2310class InspectorFrontend final : public v8_inspector::V8Inspector::Channel {
2311 public:
2312 explicit InspectorFrontend(Local<Context> context) {
2313 isolate_ = context->GetIsolate();
2314 context_.Reset(isolate_, context);
2315 }
2316 ~InspectorFrontend() override = default;
2317
2318 private:
2319 void sendResponse(
2320 int callId,
2321 std::unique_ptr<v8_inspector::StringBuffer> message) override {
2322 Send(message->string());
2323 }
2324 void sendNotification(
2325 std::unique_ptr<v8_inspector::StringBuffer> message) override {
2326 Send(message->string());
2327 }
2328 void flushProtocolNotifications() override {}
2329
2330 void Send(const v8_inspector::StringView& string) {
2331 v8::Isolate::AllowJavascriptExecutionScope allow_script(isolate_);
2332 v8::HandleScope handle_scope(isolate_);
2333 int length = static_cast<int>(string.length());
2334 DCHECK_LT(length, v8::String::kMaxLength);
2335 Local<String> message =
2336 (string.is8Bit()
2337 ? v8::String::NewFromOneByte(
2338 isolate_,
2339 reinterpret_cast<const uint8_t*>(string.characters8()),
2340 v8::