• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <iterator>
14 #include <string>
15 #include <tuple>
16 #include <unordered_map>
17 #include <utility>
18 #include <vector>
19 
20 #ifdef ENABLE_VTUNE_JIT_INTERFACE
21 #include "src/third_party/vtune/v8-vtune.h"
22 #endif
23 
24 #include "include/libplatform/libplatform.h"
25 #include "include/libplatform/v8-tracing.h"
26 #include "include/v8-inspector.h"
27 #include "include/v8-profiler.h"
28 #include "src/api/api-inl.h"
29 #include "src/base/cpu.h"
30 #include "src/base/logging.h"
31 #include "src/base/platform/platform.h"
32 #include "src/base/platform/time.h"
33 #include "src/base/sys-info.h"
34 #include "src/d8/d8-console.h"
35 #include "src/d8/d8-platforms.h"
36 #include "src/d8/d8.h"
37 #include "src/debug/debug-interface.h"
38 #include "src/deoptimizer/deoptimizer.h"
39 #include "src/diagnostics/basic-block-profiler.h"
40 #include "src/execution/vm-state-inl.h"
41 #include "src/flags/flags.h"
42 #include "src/handles/maybe-handles.h"
43 #include "src/init/v8.h"
44 #include "src/interpreter/interpreter.h"
45 #include "src/logging/counters.h"
46 #include "src/logging/log-utils.h"
47 #include "src/objects/managed.h"
48 #include "src/objects/objects-inl.h"
49 #include "src/objects/objects.h"
50 #include "src/parsing/parse-info.h"
51 #include "src/parsing/parsing.h"
52 #include "src/parsing/scanner-character-streams.h"
53 #include "src/profiler/profile-generator.h"
54 #include "src/sanitizer/msan.h"
55 #include "src/snapshot/snapshot.h"
56 #include "src/tasks/cancelable-task.h"
57 #include "src/trap-handler/trap-handler.h"
58 #include "src/utils/ostreams.h"
59 #include "src/utils/utils.h"
60 #include "src/wasm/wasm-engine.h"
61 
62 #ifdef V8_FUZZILLI
63 #include "src/d8/cov.h"
64 #endif  // V8_FUZZILLI
65 
66 #ifdef V8_USE_PERFETTO
67 #include "perfetto/tracing.h"
68 #endif  // V8_USE_PERFETTO
69 
70 #ifdef V8_INTL_SUPPORT
71 #include "unicode/locid.h"
72 #endif  // V8_INTL_SUPPORT
73 
74 #ifdef V8_OS_LINUX
75 #include <sys/mman.h>  // For MultiMappedAllocator.
76 #endif
77 
78 #if !defined(_WIN32) && !defined(_WIN64)
79 #include <unistd.h>  // NOLINT
80 #else
81 #include <windows.h>  // NOLINT
82 #endif                // !defined(_WIN32) && !defined(_WIN64)
83 
84 #ifndef DCHECK
85 #define DCHECK(condition) assert(condition)
86 #endif
87 
88 #ifndef CHECK
89 #define CHECK(condition) assert(condition)
90 #endif
91 
92 #define TRACE_BS(...)                                     \
93   do {                                                    \
94     if (i::FLAG_trace_backing_store) PrintF(__VA_ARGS__); \
95   } while (false)
96 
97 namespace v8 {
98 
99 namespace {
100 
101 const int kMB = 1024 * 1024;
102 
103 #ifdef V8_FUZZILLI
104 // REPRL = read-eval-print-reset-loop
105 // These file descriptors are being opened when Fuzzilli uses fork & execve to
106 // run V8.
107 #define REPRL_CRFD 100  // Control read file decriptor
108 #define REPRL_CWFD 101  // Control write file decriptor
109 #define REPRL_DRFD 102  // Data read file decriptor
110 #define REPRL_DWFD 103  // Data write file decriptor
111 bool fuzzilli_reprl = true;
112 #else
113 bool fuzzilli_reprl = false;
114 #endif  // V8_FUZZILLI
115 
116 const int kMaxSerializerMemoryUsage =
117     1 * kMB;  // Arbitrary maximum for testing.
118 
119 // Base class for shell ArrayBuffer allocators. It forwards all opertions to
120 // the default v8 allocator.
121 class ArrayBufferAllocatorBase : public v8::ArrayBuffer::Allocator {
122  public:
Allocate(size_t length)123   void* Allocate(size_t length) override {
124     return allocator_->Allocate(length);
125   }
126 
AllocateUninitialized(size_t length)127   void* AllocateUninitialized(size_t length) override {
128     return allocator_->AllocateUninitialized(length);
129   }
130 
Free(void * data,size_t length)131   void Free(void* data, size_t length) override {
132     allocator_->Free(data, length);
133   }
134 
135  private:
136   std::unique_ptr<Allocator> allocator_ =
137       std::unique_ptr<Allocator>(NewDefaultAllocator());
138 };
139 
140 // ArrayBuffer allocator that can use virtual memory to improve performance.
141 class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase {
142  public:
Allocate(size_t length)143   void* Allocate(size_t length) override {
144     if (length >= kVMThreshold) return AllocateVM(length);
145     return ArrayBufferAllocatorBase::Allocate(length);
146   }
147 
AllocateUninitialized(size_t length)148   void* AllocateUninitialized(size_t length) override {
149     if (length >= kVMThreshold) return AllocateVM(length);
150     return ArrayBufferAllocatorBase::AllocateUninitialized(length);
151   }
152 
Free(void * data,size_t length)153   void Free(void* data, size_t length) override {
154     if (length >= kVMThreshold) {
155       FreeVM(data, length);
156     } else {
157       ArrayBufferAllocatorBase::Free(data, length);
158     }
159   }
160 
161  private:
162   static constexpr size_t kVMThreshold = 65536;
163 
AllocateVM(size_t length)164   void* AllocateVM(size_t length) {
165     DCHECK_LE(kVMThreshold, length);
166     v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
167     size_t page_size = page_allocator->AllocatePageSize();
168     size_t allocated = RoundUp(length, page_size);
169     return i::AllocatePages(page_allocator, nullptr, allocated, page_size,
170                             PageAllocator::kReadWrite);
171   }
172 
FreeVM(void * data,size_t length)173   void FreeVM(void* data, size_t length) {
174     v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
175     size_t page_size = page_allocator->AllocatePageSize();
176     size_t allocated = RoundUp(length, page_size);
177     CHECK(i::FreePages(page_allocator, data, allocated));
178   }
179 };
180 
181 // ArrayBuffer allocator that never allocates over 10MB.
182 class MockArrayBufferAllocator : public ArrayBufferAllocatorBase {
183  protected:
Allocate(size_t length)184   void* Allocate(size_t length) override {
185     return ArrayBufferAllocatorBase::Allocate(Adjust(length));
186   }
187 
AllocateUninitialized(size_t length)188   void* AllocateUninitialized(size_t length) override {
189     return ArrayBufferAllocatorBase::AllocateUninitialized(Adjust(length));
190   }
191 
Free(void * data,size_t length)192   void Free(void* data, size_t length) override {
193     return ArrayBufferAllocatorBase::Free(data, Adjust(length));
194   }
195 
196  private:
Adjust(size_t length)197   size_t Adjust(size_t length) {
198     const size_t kAllocationLimit = 10 * kMB;
199     return length > kAllocationLimit ? i::AllocatePageSize() : length;
200   }
201 };
202 
203 // ArrayBuffer allocator that can be equipped with a limit to simulate system
204 // OOM.
205 class MockArrayBufferAllocatiorWithLimit : public MockArrayBufferAllocator {
206  public:
MockArrayBufferAllocatiorWithLimit(size_t allocation_limit)207   explicit MockArrayBufferAllocatiorWithLimit(size_t allocation_limit)
208       : space_left_(allocation_limit) {}
209 
210  protected:
Allocate(size_t length)211   void* Allocate(size_t length) override {
212     if (length > space_left_) {
213       return nullptr;
214     }
215     space_left_ -= length;
216     return MockArrayBufferAllocator::Allocate(length);
217   }
218 
AllocateUninitialized(size_t length)219   void* AllocateUninitialized(size_t length) override {
220     if (length > space_left_) {
221       return nullptr;
222     }
223     space_left_ -= length;
224     return MockArrayBufferAllocator::AllocateUninitialized(length);
225   }
226 
Free(void * data,size_t length)227   void Free(void* data, size_t length) override {
228     space_left_ += length;
229     return MockArrayBufferAllocator::Free(data, length);
230   }
231 
232  private:
233   std::atomic<size_t> space_left_;
234 };
235 
236 #ifdef V8_OS_LINUX
237 
238 // This is a mock allocator variant that provides a huge virtual allocation
239 // backed by a small real allocation that is repeatedly mapped. If you create an
240 // array on memory allocated by this allocator, you will observe that elements
241 // will alias each other as if their indices were modulo-divided by the real
242 // allocation length.
243 // The purpose is to allow stability-testing of huge (typed) arrays without
244 // actually consuming huge amounts of physical memory.
245 // This is currently only available on Linux because it relies on {mremap}.
246 class MultiMappedAllocator : public ArrayBufferAllocatorBase {
247  protected:
Allocate(size_t length)248   void* Allocate(size_t length) override {
249     if (length < kChunkSize) {
250       return ArrayBufferAllocatorBase::Allocate(length);
251     }
252     // We use mmap, which initializes pages to zero anyway.
253     return AllocateUninitialized(length);
254   }
255 
AllocateUninitialized(size_t length)256   void* AllocateUninitialized(size_t length) override {
257     if (length < kChunkSize) {
258       return ArrayBufferAllocatorBase::AllocateUninitialized(length);
259     }
260     size_t rounded_length = RoundUp(length, kChunkSize);
261     int prot = PROT_READ | PROT_WRITE;
262     // We have to specify MAP_SHARED to make {mremap} below do what we want.
263     int flags = MAP_SHARED | MAP_ANONYMOUS;
264     void* real_alloc = mmap(nullptr, kChunkSize, prot, flags, -1, 0);
265     if (reinterpret_cast<intptr_t>(real_alloc) == -1) {
266       // If we ran into some limit (physical or virtual memory, or number
267       // of mappings, etc), return {nullptr}, which callers can handle.
268       if (errno == ENOMEM) {
269         return nullptr;
270       }
271       // Other errors may be bugs which we want to learn about.
272       FATAL("mmap (real) failed with error %d: %s", errno, strerror(errno));
273     }
274     void* virtual_alloc =
275         mmap(nullptr, rounded_length, prot, flags | MAP_NORESERVE, -1, 0);
276     if (reinterpret_cast<intptr_t>(virtual_alloc) == -1) {
277       if (errno == ENOMEM) {
278         // Undo earlier, successful mappings.
279         munmap(real_alloc, kChunkSize);
280         return nullptr;
281       }
282       FATAL("mmap (virtual) failed with error %d: %s", errno, strerror(errno));
283     }
284     i::Address virtual_base = reinterpret_cast<i::Address>(virtual_alloc);
285     i::Address virtual_end = virtual_base + rounded_length;
286     for (i::Address to_map = virtual_base; to_map < virtual_end;
287          to_map += kChunkSize) {
288       // Specifying 0 as the "old size" causes the existing map entry to not
289       // get deleted, which is important so that we can remap it again in the
290       // next iteration of this loop.
291       void* result =
292           mremap(real_alloc, 0, kChunkSize, MREMAP_MAYMOVE | MREMAP_FIXED,
293                  reinterpret_cast<void*>(to_map));
294       if (reinterpret_cast<intptr_t>(result) == -1) {
295         if (errno == ENOMEM) {
296           // Undo earlier, successful mappings.
297           munmap(real_alloc, kChunkSize);
298           munmap(virtual_alloc, (to_map - virtual_base));
299           return nullptr;
300         }
301         FATAL("mremap failed with error %d: %s", errno, strerror(errno));
302       }
303     }
304     base::MutexGuard lock_guard(&regions_mutex_);
305     regions_[virtual_alloc] = real_alloc;
306     return virtual_alloc;
307   }
308 
Free(void * data,size_t length)309   void Free(void* data, size_t length) override {
310     if (length < kChunkSize) {
311       return ArrayBufferAllocatorBase::Free(data, length);
312     }
313     base::MutexGuard lock_guard(&regions_mutex_);
314     void* real_alloc = regions_[data];
315     munmap(real_alloc, kChunkSize);
316     size_t rounded_length = RoundUp(length, kChunkSize);
317     munmap(data, rounded_length);
318     regions_.erase(data);
319   }
320 
321  private:
322   // Aiming for a "Huge Page" (2M on Linux x64) to go easy on the TLB.
323   static constexpr size_t kChunkSize = 2 * 1024 * 1024;
324 
325   std::unordered_map<void*, void*> regions_;
326   base::Mutex regions_mutex_;
327 };
328 
329 #endif  // V8_OS_LINUX
330 
331 v8::Platform* g_default_platform;
332 std::unique_ptr<v8::Platform> g_platform;
333 
Throw(Isolate * isolate,const char * message)334 static Local<Value> Throw(Isolate* isolate, const char* message) {
335   return isolate->ThrowException(
336       String::NewFromUtf8(isolate, message).ToLocalChecked());
337 }
338 
TryGetValue(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object,const char * property)339 static MaybeLocal<Value> TryGetValue(v8::Isolate* isolate,
340                                      Local<Context> context,
341                                      Local<v8::Object> object,
342                                      const char* property) {
343   MaybeLocal<String> v8_str = String::NewFromUtf8(isolate, property);
344   if (v8_str.IsEmpty()) return {};
345   return object->Get(context, v8_str.ToLocalChecked());
346 }
347 
GetValue(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object,const char * property)348 static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
349                              Local<v8::Object> object, const char* property) {
350   return TryGetValue(isolate, context, object, property).ToLocalChecked();
351 }
352 
GetWorkerFromInternalField(Isolate * isolate,Local<Object> object)353 std::shared_ptr<Worker> GetWorkerFromInternalField(Isolate* isolate,
354                                                    Local<Object> object) {
355   if (object->InternalFieldCount() != 1) {
356     Throw(isolate, "this is not a Worker");
357     return nullptr;
358   }
359 
360   i::Handle<i::Object> handle = Utils::OpenHandle(*object->GetInternalField(0));
361   if (handle->IsSmi()) {
362     Throw(isolate, "Worker is defunct because main thread is terminating");
363     return nullptr;
364   }
365   auto managed = i::Handle<i::Managed<Worker>>::cast(handle);
366   return managed->get();
367 }
368 
GetThreadOptions(const char * name)369 base::Thread::Options GetThreadOptions(const char* name) {
370   // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
371   // which is not enough to parse the big literal expressions used in tests.
372   // The stack size should be at least StackGuard::kLimitSize + some
373   // OS-specific padding for thread startup code.  2Mbytes seems to be enough.
374   return base::Thread::Options(name, 2 * kMB);
375 }
376 
377 }  // namespace
378 
379 namespace tracing {
380 
381 namespace {
382 
383 static constexpr char kIncludedCategoriesParam[] = "included_categories";
384 
385 class TraceConfigParser {
386  public:
FillTraceConfig(v8::Isolate * isolate,platform::tracing::TraceConfig * trace_config,const char * json_str)387   static void FillTraceConfig(v8::Isolate* isolate,
388                               platform::tracing::TraceConfig* trace_config,
389                               const char* json_str) {
390     HandleScope outer_scope(isolate);
391     Local<Context> context = Context::New(isolate);
392     Context::Scope context_scope(context);
393     HandleScope inner_scope(isolate);
394 
395     Local<String> source =
396         String::NewFromUtf8(isolate, json_str).ToLocalChecked();
397     Local<Value> result = JSON::Parse(context, source).ToLocalChecked();
398     Local<v8::Object> trace_config_object = Local<v8::Object>::Cast(result);
399 
400     UpdateIncludedCategoriesList(isolate, context, trace_config_object,
401                                  trace_config);
402   }
403 
404  private:
UpdateIncludedCategoriesList(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object,platform::tracing::TraceConfig * trace_config)405   static int UpdateIncludedCategoriesList(
406       v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object,
407       platform::tracing::TraceConfig* trace_config) {
408     Local<Value> value =
409         GetValue(isolate, context, object, kIncludedCategoriesParam);
410     if (value->IsArray()) {
411       Local<Array> v8_array = Local<Array>::Cast(value);
412       for (int i = 0, length = v8_array->Length(); i < length; ++i) {
413         Local<Value> v = v8_array->Get(context, i)
414                              .ToLocalChecked()
415                              ->ToString(context)
416                              .ToLocalChecked();
417         String::Utf8Value str(isolate, v->ToString(context).ToLocalChecked());
418         trace_config->AddIncludedCategory(*str);
419       }
420       return v8_array->Length();
421     }
422     return 0;
423   }
424 };
425 
426 }  // namespace
427 
CreateTraceConfigFromJSON(v8::Isolate * isolate,const char * json_str)428 static platform::tracing::TraceConfig* CreateTraceConfigFromJSON(
429     v8::Isolate* isolate, const char* json_str) {
430   platform::tracing::TraceConfig* trace_config =
431       new platform::tracing::TraceConfig();
432   TraceConfigParser::FillTraceConfig(isolate, trace_config, json_str);
433   return trace_config;
434 }
435 
436 }  // namespace tracing
437 
438 class ExternalOwningOneByteStringResource
439     : public String::ExternalOneByteStringResource {
440  public:
441   ExternalOwningOneByteStringResource() = default;
ExternalOwningOneByteStringResource(std::unique_ptr<base::OS::MemoryMappedFile> file)442   ExternalOwningOneByteStringResource(
443       std::unique_ptr<base::OS::MemoryMappedFile> file)
444       : file_(std::move(file)) {}
data() const445   const char* data() const override {
446     return static_cast<char*>(file_->memory());
447   }
length() const448   size_t length() const override { return file_->size(); }
449 
450  private:
451   std::unique_ptr<base::OS::MemoryMappedFile> file_;
452 };
453 
454 CounterMap* Shell::counter_map_;
455 base::OS::MemoryMappedFile* Shell::counters_file_ = nullptr;
456 CounterCollection Shell::local_counters_;
457 CounterCollection* Shell::counters_ = &local_counters_;
458 base::LazyMutex Shell::context_mutex_;
459 const base::TimeTicks Shell::kInitialTicks =
460     base::TimeTicks::HighResolutionNow();
461 Global<Function> Shell::stringify_function_;
462 base::LazyMutex Shell::workers_mutex_;
463 bool Shell::allow_new_workers_ = true;
464 std::unordered_set<std::shared_ptr<Worker>> Shell::running_workers_;
465 std::atomic<bool> Shell::script_executed_{false};
466 base::LazyMutex Shell::isolate_status_lock_;
467 std::map<v8::Isolate*, bool> Shell::isolate_status_;
468 std::map<v8::Isolate*, int> Shell::isolate_running_streaming_tasks_;
469 base::LazyMutex Shell::cached_code_mutex_;
470 std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
471     Shell::cached_code_map_;
472 std::atomic<int> Shell::unhandled_promise_rejections_{0};
473 
474 Global<Context> Shell::evaluation_context_;
475 ArrayBuffer::Allocator* Shell::array_buffer_allocator;
476 bool check_d8_flag_contradictions = true;
477 ShellOptions Shell::options;
478 base::OnceType Shell::quit_once_ = V8_ONCE_INIT;
479 
LookupCodeCache(Isolate * isolate,Local<Value> source)480 ScriptCompiler::CachedData* Shell::LookupCodeCache(Isolate* isolate,
481                                                    Local<Value> source) {
482   base::MutexGuard lock_guard(cached_code_mutex_.Pointer());
483   CHECK(source->IsString());
484   v8::String::Utf8Value key(isolate, source);
485   DCHECK(*key);
486   auto entry = cached_code_map_.find(*key);
487   if (entry != cached_code_map_.end() && entry->second) {
488     int length = entry->second->length;
489     uint8_t* cache = new uint8_t[length];
490     memcpy(cache, entry->second->data, length);
491     ScriptCompiler::CachedData* cached_data = new ScriptCompiler::CachedData(
492         cache, length, ScriptCompiler::CachedData::BufferOwned);
493     return cached_data;
494   }
495   return nullptr;
496 }
497 
StoreInCodeCache(Isolate * isolate,Local<Value> source,const ScriptCompiler::CachedData * cache_data)498 void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> source,
499                              const ScriptCompiler::CachedData* cache_data) {
500   base::MutexGuard lock_guard(cached_code_mutex_.Pointer());
501   CHECK(source->IsString());
502   if (cache_data == nullptr) return;
503   v8::String::Utf8Value key(isolate, source);
504   DCHECK(*key);
505   int length = cache_data->length;
506   uint8_t* cache = new uint8_t[length];
507   memcpy(cache, cache_data->data, length);
508   cached_code_map_[*key] = std::unique_ptr<ScriptCompiler::CachedData>(
509       new ScriptCompiler::CachedData(cache, length,
510                                      ScriptCompiler::CachedData::BufferOwned));
511 }
512 
513 // Dummy external source stream which returns the whole source in one go.
514 // TODO(leszeks): Also test chunking the data.
515 class DummySourceStream : public v8::ScriptCompiler::ExternalSourceStream {
516  public:
DummySourceStream(Local<String> source)517   explicit DummySourceStream(Local<String> source) : done_(false) {
518     source_buffer_ = Utils::OpenHandle(*source)->ToCString(
519         i::ALLOW_NULLS, i::FAST_STRING_TRAVERSAL, &source_length_);
520   }
521 
GetMoreData(const uint8_t ** src)522   size_t GetMoreData(const uint8_t** src) override {
523     if (done_) {
524       return 0;
525     }
526     *src = reinterpret_cast<uint8_t*>(source_buffer_.release());
527     done_ = true;
528 
529     return source_length_;
530   }
531 
532  private:
533   int source_length_;
534   std::unique_ptr<char[]> source_buffer_;
535   bool done_;
536 };
537 
538 class StreamingCompileTask final : public v8::Task {
539  public:
StreamingCompileTask(Isolate * isolate,v8::ScriptCompiler::StreamedSource * streamed_source)540   StreamingCompileTask(Isolate* isolate,
541                        v8::ScriptCompiler::StreamedSource* streamed_source)
542       : isolate_(isolate),
543         script_streaming_task_(
544             v8::ScriptCompiler::StartStreaming(isolate, streamed_source)) {
545     Shell::NotifyStartStreamingTask(isolate_);
546   }
547 
Run()548   void Run() override {
549     script_streaming_task_->Run();
550     // Signal that the task has finished using the task runner to wake the
551     // message loop.
552     Shell::PostForegroundTask(isolate_, std::make_unique<FinishTask>(isolate_));
553   }
554 
555  private:
556   class FinishTask final : public v8::Task {
557    public:
FinishTask(Isolate * isolate)558     explicit FinishTask(Isolate* isolate) : isolate_(isolate) {}
Run()559     void Run() final { Shell::NotifyFinishStreamingTask(isolate_); }
560     Isolate* isolate_;
561   };
562 
563   Isolate* isolate_;
564   std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask>
565       script_streaming_task_;
566 };
567 
568 // Executes a string within the current v8 context.
ExecuteString(Isolate * isolate,Local<String> source,Local<Value> name,PrintResult print_result,ReportExceptions report_exceptions,ProcessMessageQueue process_message_queue)569 bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
570                           Local<Value> name, PrintResult print_result,
571                           ReportExceptions report_exceptions,
572                           ProcessMessageQueue process_message_queue) {
573   if (i::FLAG_parse_only) {
574     i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
575     i::VMState<PARSER> state(i_isolate);
576     i::Handle<i::String> str = Utils::OpenHandle(*(source));
577 
578     // Set up ParseInfo.
579     i::UnoptimizedCompileState compile_state(i_isolate);
580 
581     i::UnoptimizedCompileFlags flags =
582         i::UnoptimizedCompileFlags::ForToplevelCompile(
583             i_isolate, true, i::construct_language_mode(i::FLAG_use_strict),
584             i::REPLMode::kNo);
585 
586     if (options.compile_options == v8::ScriptCompiler::kEagerCompile) {
587       flags.set_is_eager(true);
588     }
589 
590     i::ParseInfo parse_info(i_isolate, flags, &compile_state);
591 
592     i::Handle<i::Script> script = parse_info.CreateScript(
593         i_isolate, str, i::kNullMaybeHandle, ScriptOriginOptions());
594     if (!i::parsing::ParseProgram(&parse_info, script, i_isolate,
595                                   i::parsing::ReportStatisticsMode::kYes)) {
596       parse_info.pending_error_handler()->PrepareErrors(
597           i_isolate, parse_info.ast_value_factory());
598       parse_info.pending_error_handler()->ReportErrors(i_isolate, script);
599 
600       fprintf(stderr, "Failed parsing\n");
601       return false;
602     }
603     return true;
604   }
605 
606   HandleScope handle_scope(isolate);
607   TryCatch try_catch(isolate);
608   try_catch.SetVerbose(report_exceptions == kReportExceptions);
609 
610   MaybeLocal<Value> maybe_result;
611   bool success = true;
612   {
613     PerIsolateData* data = PerIsolateData::Get(isolate);
614     Local<Context> realm =
615         Local<Context>::New(isolate, data->realms_[data->realm_current_]);
616     Context::Scope context_scope(realm);
617     MaybeLocal<Script> maybe_script;
618     Local<Context> context(isolate->GetCurrentContext());
619     ScriptOrigin origin(name);
620 
621     if (options.compile_options == ScriptCompiler::kConsumeCodeCache) {
622       ScriptCompiler::CachedData* cached_code =
623           LookupCodeCache(isolate, source);
624       if (cached_code != nullptr) {
625         ScriptCompiler::Source script_source(source, origin, cached_code);
626         maybe_script = ScriptCompiler::Compile(context, &script_source,
627                                                options.compile_options);
628         CHECK(!cached_code->rejected);
629       } else {
630         ScriptCompiler::Source script_source(source, origin);
631         maybe_script = ScriptCompiler::Compile(
632             context, &script_source, ScriptCompiler::kNoCompileOptions);
633       }
634     } else if (options.streaming_compile) {
635       v8::ScriptCompiler::StreamedSource streamed_source(
636           std::make_unique<DummySourceStream>(source),
637           v8::ScriptCompiler::StreamedSource::UTF8);
638 
639       PostBlockingBackgroundTask(
640           std::make_unique<StreamingCompileTask>(isolate, &streamed_source));
641 
642       // Pump the loop until the streaming task completes.
643       Shell::CompleteMessageLoop(isolate);
644 
645       maybe_script =
646           ScriptCompiler::Compile(context, &streamed_source, source, origin);
647     } else {
648       ScriptCompiler::Source script_source(source, origin);
649       maybe_script = ScriptCompiler::Compile(context, &script_source,
650                                              options.compile_options);
651     }
652 
653     Local<Script> script;
654     if (!maybe_script.ToLocal(&script)) {
655       return false;
656     }
657 
658     if (options.code_cache_options ==
659         ShellOptions::CodeCacheOptions::kProduceCache) {
660       // Serialize and store it in memory for the next execution.
661       ScriptCompiler::CachedData* cached_data =
662           ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
663       StoreInCodeCache(isolate, source, cached_data);
664       delete cached_data;
665     }
666     maybe_result = script->Run(realm);
667     if (options.code_cache_options ==
668         ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute) {
669       // Serialize and store it in memory for the next execution.
670       ScriptCompiler::CachedData* cached_data =
671           ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
672       StoreInCodeCache(isolate, source, cached_data);
673       delete cached_data;
674     }
675     if (process_message_queue) {
676       if (!EmptyMessageQueues(isolate)) success = false;
677       if (!HandleUnhandledPromiseRejections(isolate)) success = false;
678     }
679     data->realm_current_ = data->realm_switch_;
680   }
681   Local<Value> result;
682   if (!maybe_result.ToLocal(&result)) {
683     DCHECK(try_catch.HasCaught());
684     return false;
685   }
686   // It's possible that a FinalizationRegistry cleanup task threw an error.
687   if (try_catch.HasCaught()) success = false;
688   if (print_result) {
689     if (options.test_shell) {
690       if (!result->IsUndefined()) {
691         // If all went well and the result wasn't undefined then print
692         // the returned value.
693         v8::String::Utf8Value str(isolate, result);
694         fwrite(*str, sizeof(**str), str.length(), stdout);
695         printf("\n");
696       }
697     } else {
698       v8::String::Utf8Value str(isolate, Stringify(isolate, result));
699       fwrite(*str, sizeof(**str), str.length(), stdout);
700       printf("\n");
701     }
702   }
703   return success;
704 }
705 
706 namespace {
707 
ToSTLString(Isolate * isolate,Local<String> v8_str)708 std::string ToSTLString(Isolate* isolate, Local<String> v8_str) {
709   String::Utf8Value utf8(isolate, v8_str);
710   // Should not be able to fail since the input is a String.
711   CHECK(*utf8);
712   return *utf8;
713 }
714 
IsAbsolutePath(const std::string & path)715 bool IsAbsolutePath(const std::string& path) {
716 #if defined(_WIN32) || defined(_WIN64)
717   // This is an incorrect approximation, but should
718   // work for all our test-running cases.
719   return path.find(':') != std::string::npos;
720 #else
721   return path[0] == '/';
722 #endif
723 }
724 
GetWorkingDirectory()725 std::string GetWorkingDirectory() {
726 #if defined(_WIN32) || defined(_WIN64)
727   char system_buffer[MAX_PATH];
728   // Unicode paths are unsupported, which is fine as long as
729   // the test directory doesn't include any such paths.
730   DWORD len = GetCurrentDirectoryA(MAX_PATH, system_buffer);
731   CHECK_GT(len, 0);
732   return system_buffer;
733 #else
734   char curdir[PATH_MAX];
735   CHECK_NOT_NULL(getcwd(curdir, PATH_MAX));
736   return curdir;
737 #endif
738 }
739 
740 // Returns the directory part of path, without the trailing '/'.
DirName(const std::string & path)741 std::string DirName(const std::string& path) {
742   DCHECK(IsAbsolutePath(path));
743   size_t last_slash = path.find_last_of('/');
744   DCHECK(last_slash != std::string::npos);
745   return path.substr(0, last_slash);
746 }
747 
748 // Resolves path to an absolute path if necessary, and does some
749 // normalization (eliding references to the current directory
750 // and replacing backslashes with slashes).
NormalizePath(const std::string & path,const std::string & dir_name)751 std::string NormalizePath(const std::string& path,
752                           const std::string& dir_name) {
753   std::string absolute_path;
754   if (IsAbsolutePath(path)) {
755     absolute_path = path;
756   } else {
757     absolute_path = dir_name + '/' + path;
758   }
759   std::replace(absolute_path.begin(), absolute_path.end(), '\\', '/');
760   std::vector<std::string> segments;
761   std::istringstream segment_stream(absolute_path);
762   std::string segment;
763   while (std::getline(segment_stream, segment, '/')) {
764     if (segment == "..") {
765       segments.pop_back();
766     } else if (segment != ".") {
767       segments.push_back(segment);
768     }
769   }
770   // Join path segments.
771   std::ostringstream os;
772   std::copy(segments.begin(), segments.end() - 1,
773             std::ostream_iterator<std::string>(os, "/"));
774   os << *segments.rbegin();
775   return os.str();
776 }
777 
778 // Per-context Module data, allowing sharing of module maps
779 // across top-level module loads.
780 class ModuleEmbedderData {
781  private:
782   class ModuleGlobalHash {
783    public:
ModuleGlobalHash(Isolate * isolate)784     explicit ModuleGlobalHash(Isolate* isolate) : isolate_(isolate) {}
operator ()(const Global<Module> & module) const785     size_t operator()(const Global<Module>& module) const {
786       return module.Get(isolate_)->GetIdentityHash();
787     }
788 
789    private:
790     Isolate* isolate_;
791   };
792 
793  public:
ModuleEmbedderData(Isolate * isolate)794   explicit ModuleEmbedderData(Isolate* isolate)
795       : module_to_specifier_map(10, ModuleGlobalHash(isolate)) {}
796 
797   // Map from normalized module specifier to Module.
798   std::unordered_map<std::string, Global<Module>> specifier_to_module_map;
799   // Map from Module to its URL as defined in the ScriptOrigin
800   std::unordered_map<Global<Module>, std::string, ModuleGlobalHash>
801       module_to_specifier_map;
802 };
803 
804 enum { kModuleEmbedderDataIndex, kInspectorClientIndex };
805 
InitializeModuleEmbedderData(Local<Context> context)806 void InitializeModuleEmbedderData(Local<Context> context) {
807   context->SetAlignedPointerInEmbedderData(
808       kModuleEmbedderDataIndex, new ModuleEmbedderData(context->GetIsolate()));
809 }
810 
GetModuleDataFromContext(Local<Context> context)811 ModuleEmbedderData* GetModuleDataFromContext(Local<Context> context) {
812   return static_cast<ModuleEmbedderData*>(
813       context->GetAlignedPointerFromEmbedderData(kModuleEmbedderDataIndex));
814 }
815 
DisposeModuleEmbedderData(Local<Context> context)816 void DisposeModuleEmbedderData(Local<Context> context) {
817   delete GetModuleDataFromContext(context);
818   context->SetAlignedPointerInEmbedderData(kModuleEmbedderDataIndex, nullptr);
819 }
820 
ResolveModuleCallback(Local<Context> context,Local<String> specifier,Local<Module> referrer)821 MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
822                                          Local<String> specifier,
823                                          Local<Module> referrer) {
824   Isolate* isolate = context->GetIsolate();
825   ModuleEmbedderData* d = GetModuleDataFromContext(context);
826   auto specifier_it =
827       d->module_to_specifier_map.find(Global<Module>(isolate, referrer));
828   CHECK(specifier_it != d->module_to_specifier_map.end());
829   std::string absolute_path = NormalizePath(ToSTLString(isolate, specifier),
830                                             DirName(specifier_it->second));
831   auto module_it = d->specifier_to_module_map.find(absolute_path);
832   CHECK(module_it != d->specifier_to_module_map.end());
833   return module_it->second.Get(isolate);
834 }
835 
836 }  // anonymous namespace
837 
FetchModuleTree(Local<Module> referrer,Local<Context> context,const std::string & file_name)838 MaybeLocal<Module> Shell::FetchModuleTree(Local<Module> referrer,
839                                           Local<Context> context,
840                                           const std::string& file_name) {
841   DCHECK(IsAbsolutePath(file_name));
842   Isolate* isolate = context->GetIsolate();
843   Local<String> source_text = ReadFile(isolate, file_name.c_str());
844   if (source_text.IsEmpty() && options.fuzzy_module_file_extensions) {
845     std::string fallback_file_name = file_name + ".js";
846     source_text = ReadFile(isolate, fallback_file_name.c_str());
847     if (source_text.IsEmpty()) {
848       fallback_file_name = file_name + ".mjs";
849       source_text = ReadFile(isolate, fallback_file_name.c_str());
850     }
851   }
852 
853   ModuleEmbedderData* d = GetModuleDataFromContext(context);
854   if (source_text.IsEmpty()) {
855     std::string msg = "d8: Error reading  module from " + file_name;
856     if (!referrer.IsEmpty()) {
857       auto specifier_it =
858           d->module_to_specifier_map.find(Global<Module>(isolate, referrer));
859       CHECK(specifier_it != d->module_to_specifier_map.end());
860       msg += "\n    imported by " + specifier_it->second;
861     }
862     Throw(isolate, msg.c_str());
863     return MaybeLocal<Module>();
864   }
865   ScriptOrigin origin(
866       String::NewFromUtf8(isolate, file_name.c_str()).ToLocalChecked(),
867       Local<Integer>(), Local<Integer>(), Local<Boolean>(), Local<Integer>(),
868       Local<Value>(), Local<Boolean>(), Local<Boolean>(), True(isolate));
869   ScriptCompiler::Source source(source_text, origin);
870   Local<Module> module;
871   if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
872     return MaybeLocal<Module>();
873   }
874 
875   CHECK(d->specifier_to_module_map
876             .insert(std::make_pair(file_name, Global<Module>(isolate, module)))
877             .second);
878   CHECK(d->module_to_specifier_map
879             .insert(std::make_pair(Global<Module>(isolate, module), file_name))
880             .second);
881 
882   std::string dir_name = DirName(file_name);
883 
884   for (int i = 0, length = module->GetModuleRequestsLength(); i < length; ++i) {
885     Local<String> name = module->GetModuleRequest(i);
886     std::string absolute_path =
887         NormalizePath(ToSTLString(isolate, name), dir_name);
888     if (d->specifier_to_module_map.count(absolute_path)) continue;
889     if (FetchModuleTree(module, context, absolute_path).IsEmpty()) {
890       return MaybeLocal<Module>();
891     }
892   }
893 
894   return module;
895 }
896 
897 namespace {
898 
899 struct DynamicImportData {
DynamicImportDatav8::__anone44fe7ab0511::DynamicImportData900   DynamicImportData(Isolate* isolate_, Local<String> referrer_,
901                     Local<String> specifier_,
902                     Local<Promise::Resolver> resolver_)
903       : isolate(isolate_) {
904     referrer.Reset(isolate, referrer_);
905     specifier.Reset(isolate, specifier_);
906     resolver.Reset(isolate, resolver_);
907   }
908 
909   Isolate* isolate;
910   Global<String> referrer;
911   Global<String> specifier;
912   Global<Promise::Resolver> resolver;
913 };
914 
915 struct ModuleResolutionData {
ModuleResolutionDatav8::__anone44fe7ab0511::ModuleResolutionData916   ModuleResolutionData(Isolate* isolate_, Local<Value> module_namespace_,
917                        Local<Promise::Resolver> resolver_)
918       : isolate(isolate_) {
919     module_namespace.Reset(isolate, module_namespace_);
920     resolver.Reset(isolate, resolver_);
921   }
922 
923   Isolate* isolate;
924   Global<Value> module_namespace;
925   Global<Promise::Resolver> resolver;
926 };
927 
928 }  // namespace
929 
ModuleResolutionSuccessCallback(const FunctionCallbackInfo<Value> & info)930 void Shell::ModuleResolutionSuccessCallback(
931     const FunctionCallbackInfo<Value>& info) {
932   std::unique_ptr<ModuleResolutionData> module_resolution_data(
933       static_cast<ModuleResolutionData*>(
934           info.Data().As<v8::External>()->Value()));
935   Isolate* isolate(module_resolution_data->isolate);
936   HandleScope handle_scope(isolate);
937 
938   Local<Promise::Resolver> resolver(
939       module_resolution_data->resolver.Get(isolate));
940   Local<Value> module_namespace(
941       module_resolution_data->module_namespace.Get(isolate));
942 
943   PerIsolateData* data = PerIsolateData::Get(isolate);
944   Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
945   Context::Scope context_scope(realm);
946 
947   resolver->Resolve(realm, module_namespace).ToChecked();
948 }
949 
ModuleResolutionFailureCallback(const FunctionCallbackInfo<Value> & info)950 void Shell::ModuleResolutionFailureCallback(
951     const FunctionCallbackInfo<Value>& info) {
952   std::unique_ptr<ModuleResolutionData> module_resolution_data(
953       static_cast<ModuleResolutionData*>(
954           info.Data().As<v8::External>()->Value()));
955   Isolate* isolate(module_resolution_data->isolate);
956   HandleScope handle_scope(isolate);
957 
958   Local<Promise::Resolver> resolver(
959       module_resolution_data->resolver.Get(isolate));
960 
961   PerIsolateData* data = PerIsolateData::Get(isolate);
962   Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
963   Context::Scope context_scope(realm);
964 
965   DCHECK_EQ(info.Length(), 1);
966   resolver->Reject(realm, info[0]).ToChecked();
967 }
968 
HostImportModuleDynamically(Local<Context> context,Local<ScriptOrModule> referrer,Local<String> specifier)969 MaybeLocal<Promise> Shell::HostImportModuleDynamically(
970     Local<Context> context, Local<ScriptOrModule> referrer,
971     Local<String> specifier) {
972   Isolate* isolate = context->GetIsolate();
973 
974   MaybeLocal<Promise::Resolver> maybe_resolver =
975       Promise::Resolver::New(context);
976   Local<Promise::Resolver> resolver;
977   if (maybe_resolver.ToLocal(&resolver)) {
978     DynamicImportData* data = new DynamicImportData(
979         isolate, Local<String>::Cast(referrer->GetResourceName()), specifier,
980         resolver);
981     isolate->EnqueueMicrotask(Shell::DoHostImportModuleDynamically, data);
982     return resolver->GetPromise();
983   }
984 
985   return MaybeLocal<Promise>();
986 }
987 
HostInitializeImportMetaObject(Local<Context> context,Local<Module> module,Local<Object> meta)988 void Shell::HostInitializeImportMetaObject(Local<Context> context,
989                                            Local<Module> module,
990                                            Local<Object> meta) {
991   Isolate* isolate = context->GetIsolate();
992   HandleScope handle_scope(isolate);
993 
994   ModuleEmbedderData* d = GetModuleDataFromContext(context);
995   auto specifier_it =
996       d->module_to_specifier_map.find(Global<Module>(isolate, module));
997   CHECK(specifier_it != d->module_to_specifier_map.end());
998 
999   Local<String> url_key =
1000       String::NewFromUtf8Literal(isolate, "url", NewStringType::kInternalized);
1001   Local<String> url = String::NewFromUtf8(isolate, specifier_it->second.c_str())
1002                           .ToLocalChecked();
1003   meta->CreateDataProperty(context, url_key, url).ToChecked();
1004 }
1005 
DoHostImportModuleDynamically(void * import_data)1006 void Shell::DoHostImportModuleDynamically(void* import_data) {
1007   std::unique_ptr<DynamicImportData> import_data_(
1008       static_cast<DynamicImportData*>(import_data));
1009   Isolate* isolate(import_data_->isolate);
1010   HandleScope handle_scope(isolate);
1011 
1012   Local<String> referrer(import_data_->referrer.Get(isolate));
1013   Local<String> specifier(import_data_->specifier.Get(isolate));
1014   Local<Promise::Resolver> resolver(import_data_->resolver.Get(isolate));
1015 
1016   PerIsolateData* data = PerIsolateData::Get(isolate);
1017   Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
1018   Context::Scope context_scope(realm);
1019 
1020   std::string source_url = ToSTLString(isolate, referrer);
1021   std::string dir_name =
1022       DirName(NormalizePath(source_url, GetWorkingDirectory()));
1023   std::string file_name = ToSTLString(isolate, specifier);
1024   std::string absolute_path = NormalizePath(file_name, dir_name);
1025 
1026   TryCatch try_catch(isolate);
1027   try_catch.SetVerbose(true);
1028 
1029   ModuleEmbedderData* d = GetModuleDataFromContext(realm);
1030   Local<Module> root_module;
1031   auto module_it = d->specifier_to_module_map.find(absolute_path);
1032   if (module_it != d->specifier_to_module_map.end()) {
1033     root_module = module_it->second.Get(isolate);
1034   } else if (!FetchModuleTree(Local<Module>(), realm, absolute_path)
1035                   .ToLocal(&root_module)) {
1036     CHECK(try_catch.HasCaught());
1037     resolver->Reject(realm, try_catch.Exception()).ToChecked();
1038     return;
1039   }
1040 
1041   MaybeLocal<Value> maybe_result;
1042   if (root_module->InstantiateModule(realm, ResolveModuleCallback)
1043           .FromMaybe(false)) {
1044     maybe_result = root_module->Evaluate(realm);
1045     CHECK_IMPLIES(i::FLAG_harmony_top_level_await, !maybe_result.IsEmpty());
1046     EmptyMessageQueues(isolate);
1047   }
1048 
1049   Local<Value> result;
1050   if (!maybe_result.ToLocal(&result)) {
1051     DCHECK(try_catch.HasCaught());
1052     resolver->Reject(realm, try_catch.Exception()).ToChecked();
1053     return;
1054   }
1055 
1056   Local<Value> module_namespace = root_module->GetModuleNamespace();
1057   if (i::FLAG_harmony_top_level_await) {
1058     Local<Promise> result_promise(Local<Promise>::Cast(result));
1059     if (result_promise->State() == Promise::kRejected) {
1060       resolver->Reject(realm, result_promise->Result()).ToChecked();
1061       return;
1062     }
1063 
1064     // Setup callbacks, and then chain them to the result promise.
1065     // ModuleResolutionData will be deleted by the callbacks.
1066     auto module_resolution_data =
1067         new ModuleResolutionData(isolate, module_namespace, resolver);
1068     Local<v8::External> edata = External::New(isolate, module_resolution_data);
1069     Local<Function> callback_success;
1070     CHECK(Function::New(realm, ModuleResolutionSuccessCallback, edata)
1071               .ToLocal(&callback_success));
1072     Local<Function> callback_failure;
1073     CHECK(Function::New(realm, ModuleResolutionFailureCallback, edata)
1074               .ToLocal(&callback_failure));
1075     result_promise->Then(realm, callback_success, callback_failure)
1076         .ToLocalChecked();
1077   } else {
1078     // TODO(cbruni): Clean up exception handling after introducing new
1079     // API for evaluating async modules.
1080     DCHECK(!try_catch.HasCaught());
1081     resolver->Resolve(realm, module_namespace).ToChecked();
1082   }
1083 }
1084 
ExecuteModule(Isolate * isolate,const char * file_name)1085 bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
1086   HandleScope handle_scope(isolate);
1087 
1088   PerIsolateData* data = PerIsolateData::Get(isolate);
1089   Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
1090   Context::Scope context_scope(realm);
1091 
1092   std::string absolute_path = NormalizePath(file_name, GetWorkingDirectory());
1093 
1094   // Use a non-verbose TryCatch and report exceptions manually using
1095   // Shell::ReportException, because some errors (such as file errors) are
1096   // thrown without entering JS and thus do not trigger
1097   // isolate->ReportPendingMessages().
1098   TryCatch try_catch(isolate);
1099 
1100   Local<Module> root_module;
1101 
1102   if (!FetchModuleTree(Local<Module>(), realm, absolute_path)
1103            .ToLocal(&root_module)) {
1104     CHECK(try_catch.HasCaught());
1105     ReportException(isolate, &try_catch);
1106     return false;
1107   }
1108 
1109   MaybeLocal<Value> maybe_result;
1110   if (root_module->InstantiateModule(realm, ResolveModuleCallback)
1111           .FromMaybe(false)) {
1112     maybe_result = root_module->Evaluate(realm);
1113     CHECK_IMPLIES(i::FLAG_harmony_top_level_await, !maybe_result.IsEmpty());
1114     EmptyMessageQueues(isolate);
1115   }
1116   Local<Value> result;
1117   if (!maybe_result.ToLocal(&result)) {
1118     DCHECK(try_catch.HasCaught());
1119     ReportException(isolate, &try_catch);
1120     return false;
1121   }
1122   if (i::FLAG_harmony_top_level_await) {
1123     // Loop until module execution finishes
1124     // TODO(cbruni): This is a bit wonky. "Real" engines would not be
1125     // able to just busy loop waiting for execution to finish.
1126     Local<Promise> result_promise(Local<Promise>::Cast(result));
1127     while (result_promise->State() == Promise::kPending) {
1128       isolate->PerformMicrotaskCheckpoint();
1129     }
1130 
1131     if (result_promise->State() == Promise::kRejected) {
1132       // If the exception has been caught by the promise pipeline, we rethrow
1133       // here in order to ReportException.
1134       // TODO(cbruni): Clean this up after we create a new API for the case
1135       // where TLA is enabled.
1136       if (!try_catch.HasCaught()) {
1137         isolate->ThrowException(result_promise->Result());
1138       } else {
1139         DCHECK_EQ(try_catch.Exception(), result_promise->Result());
1140       }
1141       ReportException(isolate, &try_catch);
1142       return false;
1143     }
1144   }
1145 
1146   DCHECK(!try_catch.HasCaught());
1147   return true;
1148 }
1149 
PerIsolateData(Isolate * isolate)1150 PerIsolateData::PerIsolateData(Isolate* isolate)
1151     : isolate_(isolate), realms_(nullptr) {
1152   isolate->SetData(0, this);
1153   if (i::FLAG_expose_async_hooks) {
1154     async_hooks_wrapper_ = new AsyncHooks(isolate);
1155   }
1156   ignore_unhandled_promises_ = false;
1157 }
1158 
~PerIsolateData()1159 PerIsolateData::~PerIsolateData() {
1160   isolate_->SetData(0, nullptr);  // Not really needed, just to be sure...
1161   if (i::FLAG_expose_async_hooks) {
1162     delete async_hooks_wrapper_;  // This uses the isolate
1163   }
1164 }
1165 
SetTimeout(Local<Function> callback,Local<Context> context)1166 void PerIsolateData::SetTimeout(Local<Function> callback,
1167                                 Local<Context> context) {
1168   set_timeout_callbacks_.emplace(isolate_, callback);
1169   set_timeout_contexts_.emplace(isolate_, context);
1170 }
1171 
GetTimeoutCallback()1172 MaybeLocal<Function> PerIsolateData::GetTimeoutCallback() {
1173   if (set_timeout_callbacks_.empty()) return MaybeLocal<Function>();
1174   Local<Function> result = set_timeout_callbacks_.front().Get(isolate_);
1175   set_timeout_callbacks_.pop();
1176   return result;
1177 }
1178 
GetTimeoutContext()1179 MaybeLocal<Context> PerIsolateData::GetTimeoutContext() {
1180   if (set_timeout_contexts_.empty()) return MaybeLocal<Context>();
1181   Local<Context> result = set_timeout_contexts_.front().Get(isolate_);
1182   set_timeout_contexts_.pop();
1183   return result;
1184 }
1185 
RemoveUnhandledPromise(Local<Promise> promise)1186 void PerIsolateData::RemoveUnhandledPromise(Local<Promise> promise) {
1187   if (ignore_unhandled_promises_) return;
1188   // Remove handled promises from the list
1189   DCHECK_EQ(promise->GetIsolate(), isolate_);
1190   for (auto it = unhandled_promises_.begin(); it != unhandled_promises_.end();
1191        ++it) {
1192     v8::Local<v8::Promise> unhandled_promise = std::get<0>(*it).Get(isolate_);
1193     if (unhandled_promise == promise) {
1194       unhandled_promises_.erase(it--);
1195     }
1196   }
1197 }
1198 
AddUnhandledPromise(Local<Promise> promise,Local<Message> message,Local<Value> exception)1199 void PerIsolateData::AddUnhandledPromise(Local<Promise> promise,
1200                                          Local<Message> message,
1201                                          Local<Value> exception) {
1202   if (ignore_unhandled_promises_) return;
1203   DCHECK_EQ(promise->GetIsolate(), isolate_);
1204   unhandled_promises_.emplace_back(v8::Global<v8::Promise>(isolate_, promise),
1205                                    v8::Global<v8::Message>(isolate_, message),
1206                                    v8::Global<v8::Value>(isolate_, exception));
1207 }
1208 
HandleUnhandledPromiseRejections()1209 int PerIsolateData::HandleUnhandledPromiseRejections() {
1210   // Avoid recursive calls to HandleUnhandledPromiseRejections.
1211   if (ignore_unhandled_promises_) return 0;
1212   ignore_unhandled_promises_ = true;
1213   v8::HandleScope scope(isolate_);
1214   // Ignore promises that get added during error reporting.
1215   size_t i = 0;
1216   for (; i < unhandled_promises_.size(); i++) {
1217     const auto& tuple = unhandled_promises_[i];
1218     Local<v8::Message> message = std::get<1>(tuple).Get(isolate_);
1219     Local<v8::Value> value = std::get<2>(tuple).Get(isolate_);
1220     Shell::ReportException(isolate_, message, value);
1221   }
1222   unhandled_promises_.clear();
1223   ignore_unhandled_promises_ = false;
1224   return static_cast<int>(i);
1225 }
1226 
RealmScope(PerIsolateData * data)1227 PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
1228   data_->realm_count_ = 1;
1229   data_->realm_current_ = 0;
1230   data_->realm_switch_ = 0;
1231   data_->realms_ = new Global<Context>[1];
1232   data_->realms_[0].Reset(data_->isolate_,
1233                           data_->isolate_->GetEnteredOrMicrotaskContext());
1234 }
1235 
~RealmScope()1236 PerIsolateData::RealmScope::~RealmScope() {
1237   // Drop realms to avoid keeping them alive. We don't dispose the
1238   // module embedder data for the first realm here, but instead do
1239   // it in RunShell or in RunMain, if not running in interactive mode
1240   for (int i = 1; i < data_->realm_count_; ++i) {
1241     Global<Context>& realm = data_->realms_[i];
1242     if (realm.IsEmpty()) continue;
1243     DisposeModuleEmbedderData(realm.Get(data_->isolate_));
1244   }
1245   data_->realm_count_ = 0;
1246   delete[] data_->realms_;
1247 }
1248 
RealmFind(Local<Context> context)1249 int PerIsolateData::RealmFind(Local<Context> context) {
1250   for (int i = 0; i < realm_count_; ++i) {
1251     if (realms_[i] == context) return i;
1252   }
1253   return -1;
1254 }
1255 
RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value> & args,int arg_offset)1256 int PerIsolateData::RealmIndexOrThrow(
1257     const v8::FunctionCallbackInfo<v8::Value>& args, int arg_offset) {
1258   if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) {
1259     Throw(args.GetIsolate(), "Invalid argument");
1260     return -1;
1261   }
1262   int index = args[arg_offset]
1263                   ->Int32Value(args.GetIsolate()->GetCurrentContext())
1264                   .FromMaybe(-1);
1265   if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) {
1266     Throw(args.GetIsolate(), "Invalid realm index");
1267     return -1;
1268   }
1269   return index;
1270 }
1271 
1272 // performance.now() returns a time stamp as double, measured in milliseconds.
1273 // When FLAG_verify_predictable mode is enabled it returns result of
1274 // v8::Platform::MonotonicallyIncreasingTime().
PerformanceNow(const v8::FunctionCallbackInfo<v8::Value> & args)1275 void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
1276   if (i::FLAG_verify_predictable) {
1277     args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime());
1278   } else {
1279     base::TimeDelta delta =
1280         base::TimeTicks::HighResolutionNow() - kInitialTicks;
1281     args.GetReturnValue().Set(delta.InMillisecondsF());
1282   }
1283 }
1284 
1285 // performance.measureMemory() implements JavaScript Memory API proposal.
1286 // See https://github.com/ulan/javascript-agent-memory/blob/master/explainer.md.
PerformanceMeasureMemory(const v8::FunctionCallbackInfo<v8::Value> & args)1287 void Shell::PerformanceMeasureMemory(
1288     const v8::FunctionCallbackInfo<v8::Value>& args) {
1289   v8::MeasureMemoryMode mode = v8::MeasureMemoryMode::kSummary;
1290   v8::Isolate* isolate = args.GetIsolate();
1291   Local<Context> context = isolate->GetCurrentContext();
1292   if (args.Length() >= 1 && args[0]->IsObject()) {
1293     Local<Object> object = args[0].As<Object>();
1294     Local<Value> value = TryGetValue(isolate, context, object, "detailed")
1295                              .FromMaybe(Local<Value>());
1296     if (!value.IsEmpty() && value->IsBoolean() &&
1297         value->BooleanValue(isolate)) {
1298       mode = v8::MeasureMemoryMode::kDetailed;
1299     }
1300   }
1301   Local<v8::Promise::Resolver> promise_resolver =
1302       v8::Promise::Resolver::New(context).ToLocalChecked();
1303   args.GetIsolate()->MeasureMemory(
1304       v8::MeasureMemoryDelegate::Default(isolate, context, promise_resolver,
1305                                          mode),
1306       v8::MeasureMemoryExecution::kEager);
1307   args.GetReturnValue().Set(promise_resolver->GetPromise());
1308 }
1309 
1310 // Realm.current() returns the index of the currently active realm.
RealmCurrent(const v8::FunctionCallbackInfo<v8::Value> & args)1311 void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) {
1312   Isolate* isolate = args.GetIsolate();
1313   PerIsolateData* data = PerIsolateData::Get(isolate);
1314   int index = data->RealmFind(isolate->GetEnteredOrMicrotaskContext());
1315   if (index == -1) return;
1316   args.GetReturnValue().Set(index);
1317 }
1318 
1319 // Realm.owner(o) returns the index of the realm that created o.
RealmOwner(const v8::FunctionCallbackInfo<v8::Value> & args)1320 void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) {
1321   Isolate* isolate = args.GetIsolate();
1322   PerIsolateData* data = PerIsolateData::Get(isolate);
1323   if (args.Length() < 1 || !args[0]->IsObject()) {
1324     Throw(args.GetIsolate(), "Invalid argument");
1325     return;
1326   }
1327   Local<Object> object =
1328       args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked();
1329   i::Handle<i::JSReceiver> i_object = Utils::OpenHandle(*object);
1330   if (i_object->IsJSGlobalProxy() &&
1331       i::Handle<i::JSGlobalProxy>::cast(i_object)->IsDetached()) {
1332     return;
1333   }
1334   int index = data->RealmFind(object->CreationContext());
1335   if (index == -1) return;
1336   args.GetReturnValue().Set(index);
1337 }
1338 
1339 // Realm.global(i) returns the global object of realm i.
1340 // (Note that properties of global objects cannot be read/written cross-realm.)
RealmGlobal(const v8::FunctionCallbackInfo<v8::Value> & args)1341 void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1342   PerIsolateData* data = PerIsolateData::Get(args.GetIsolate());
1343   int index = data->RealmIndexOrThrow(args, 0);
1344   if (index == -1) return;
1345   args.GetReturnValue().Set(
1346       Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global());
1347 }
1348 
CreateRealm(const v8::FunctionCallbackInfo<v8::Value> & args,int index,v8::MaybeLocal<Value> global_object)1349 MaybeLocal<Context> Shell::CreateRealm(
1350     const v8::FunctionCallbackInfo<v8::Value>& args, int index,
1351     v8::MaybeLocal<Value> global_object) {
1352   Isolate* isolate = args.GetIsolate();
1353   TryCatch try_catch(isolate);
1354   PerIsolateData* data = PerIsolateData::Get(isolate);
1355   if (index < 0) {
1356     Global<Context>* old_realms = data->realms_;
1357     index = data->realm_count_;
1358     data->realms_ = new Global<Context>[++data->realm_count_];
1359     for (int i = 0; i < index; ++i) {
1360       data->realms_[i].Reset(isolate, old_realms[i]);
1361       old_realms[i].Reset();
1362     }
1363     delete[] old_realms;
1364   }
1365   Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1366   Local<Context> context =
1367       Context::New(isolate, nullptr, global_template, global_object);
1368   DCHECK(!try_catch.HasCaught());
1369   if (context.IsEmpty()) return MaybeLocal<Context>();
1370   InitializeModuleEmbedderData(context);
1371   data->realms_[index].Reset(isolate, context);
1372   args.GetReturnValue().Set(index);
1373   return context;
1374 }
1375 
DisposeRealm(const v8::FunctionCallbackInfo<v8::Value> & args,int index)1376 void Shell::DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args,
1377                          int index) {
1378   Isolate* isolate = args.GetIsolate();
1379   PerIsolateData* data = PerIsolateData::Get(isolate);
1380   Local<Context> context = data->realms_[index].Get(isolate);
1381   DisposeModuleEmbedderData(context);
1382   data->realms_[index].Reset();
1383   // ContextDisposedNotification expects the disposed context to be entered.
1384   v8::Context::Scope scope(context);
1385   isolate->ContextDisposedNotification();
1386   isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime());
1387 }
1388 
1389 // Realm.create() creates a new realm with a distinct security token
1390 // and returns its index.
RealmCreate(const v8::FunctionCallbackInfo<v8::Value> & args)1391 void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1392   CreateRealm(args, -1, v8::MaybeLocal<Value>());
1393 }
1394 
1395 // Realm.createAllowCrossRealmAccess() creates a new realm with the same
1396 // security token as the current realm.
RealmCreateAllowCrossRealmAccess(const v8::FunctionCallbackInfo<v8::Value> & args)1397 void Shell::RealmCreateAllowCrossRealmAccess(
1398     const v8::FunctionCallbackInfo<v8::Value>& args) {
1399   Local<Context> context;
1400   if (CreateRealm(args, -1, v8::MaybeLocal<Value>()).ToLocal(&context)) {
1401     context->SetSecurityToken(
1402         args.GetIsolate()->GetEnteredOrMicrotaskContext()->GetSecurityToken());
1403   }
1404 }
1405 
1406 // Realm.navigate(i) creates a new realm with a distinct security token
1407 // in place of realm i.
RealmNavigate(const v8::FunctionCallbackInfo<v8::Value> & args)1408 void Shell::RealmNavigate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1409   Isolate* isolate = args.GetIsolate();
1410   PerIsolateData* data = PerIsolateData::Get(isolate);
1411   int index = data->RealmIndexOrThrow(args, 0);
1412   if (index == -1) return;
1413   if (index == 0 || index == data->realm_current_ ||
1414       index == data->realm_switch_) {
1415     Throw(args.GetIsolate(), "Invalid realm index");
1416     return;
1417   }
1418 
1419   Local<Context> context = Local<Context>::New(isolate, data->realms_[index]);
1420   v8::MaybeLocal<Value> global_object = context->Global();
1421 
1422   // Context::Global doesn't return JSGlobalProxy if DetachGlobal is called in
1423   // advance.
1424   if (!global_object.IsEmpty()) {
1425     HandleScope scope(isolate);
1426     if (!Utils::OpenHandle(*global_object.ToLocalChecked())
1427              ->IsJSGlobalProxy()) {
1428       global_object = v8::MaybeLocal<Value>();
1429     }
1430   }
1431 
1432   DisposeRealm(args, index);
1433   CreateRealm(args, index, global_object);
1434 }
1435 
1436 // Realm.detachGlobal(i) detaches the global objects of realm i from realm i.
RealmDetachGlobal(const v8::FunctionCallbackInfo<v8::Value> & args)1437 void Shell::RealmDetachGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1438   Isolate* isolate = args.GetIsolate();
1439   PerIsolateData* data = PerIsolateData::Get(isolate);
1440   int index = data->RealmIndexOrThrow(args, 0);
1441   if (index == -1) return;
1442   if (index == 0 || index == data->realm_current_ ||
1443       index == data->realm_switch_) {
1444     Throw(args.GetIsolate(), "Invalid realm index");
1445     return;
1446   }
1447 
1448   HandleScope scope(isolate);
1449   Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
1450   realm->DetachGlobal();
1451 }
1452 
1453 // Realm.dispose(i) disposes the reference to the realm i.
RealmDispose(const v8::FunctionCallbackInfo<v8::Value> & args)1454 void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
1455   Isolate* isolate = args.GetIsolate();
1456   PerIsolateData* data = PerIsolateData::Get(isolate);
1457   int index = data->RealmIndexOrThrow(args, 0);
1458   if (index == -1) return;
1459   if (index == 0 || index == data->realm_current_ ||
1460       index == data->realm_switch_) {
1461     Throw(args.GetIsolate(), "Invalid realm index");
1462     return;
1463   }
1464   DisposeRealm(args, index);
1465 }
1466 
1467 // Realm.switch(i) switches to the realm i for consecutive interactive inputs.
RealmSwitch(const v8::FunctionCallbackInfo<v8::Value> & args)1468 void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) {
1469   Isolate* isolate = args.GetIsolate();
1470   PerIsolateData* data = PerIsolateData::Get(isolate);
1471   int index = data->RealmIndexOrThrow(args, 0);
1472   if (index == -1) return;
1473   data->realm_switch_ = index;
1474 }
1475 
1476 // Realm.eval(i, s) evaluates s in realm i and returns the result.
RealmEval(const v8::FunctionCallbackInfo<v8::Value> & args)1477 void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
1478   Isolate* isolate = args.GetIsolate();
1479   PerIsolateData* data = PerIsolateData::Get(isolate);
1480   int index = data->RealmIndexOrThrow(args, 0);
1481   if (index == -1) return;
1482   if (args.Length() < 2 || !args[1]->IsString()) {
1483     Throw(args.GetIsolate(), "Invalid argument");
1484     return;
1485   }
1486   ScriptOrigin origin(String::NewFromUtf8Literal(isolate, "(d8)",
1487                                                  NewStringType::kInternalized));
1488   ScriptCompiler::Source script_source(
1489       args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked(), origin);
1490   Local<UnboundScript> script;
1491   if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
1492            .ToLocal(&script)) {
1493     return;
1494   }
1495   Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
1496   realm->Enter();
1497   int previous_index = data->realm_current_;
1498   data->realm_current_ = data->realm_switch_ = index;
1499   Local<Value> result;
1500   if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) {
1501     realm->Exit();
1502     data->realm_current_ = data->realm_switch_ = previous_index;
1503     return;
1504   }
1505   realm->Exit();
1506   data->realm_current_ = data->realm_switch_ = previous_index;
1507   args.GetReturnValue().Set(result);
1508 }
1509 
1510 // Realm.shared is an accessor for a single shared value across realms.
RealmSharedGet(Local<String> property,const PropertyCallbackInfo<Value> & info)1511 void Shell::RealmSharedGet(Local<String> property,
1512                            const PropertyCallbackInfo<Value>& info) {
1513   Isolate* isolate = info.GetIsolate();
1514   PerIsolateData* data = PerIsolateData::Get(isolate);
1515   if (data->realm_shared_.IsEmpty()) return;
1516   info.GetReturnValue().Set(data->realm_shared_);
1517 }
1518 
RealmSharedSet(Local<String> property,Local<Value> value,const PropertyCallbackInfo<void> & info)1519 void Shell::RealmSharedSet(Local<String> property, Local<Value> value,
1520                            const PropertyCallbackInfo<void>& info) {
1521   Isolate* isolate = info.GetIsolate();
1522   PerIsolateData* data = PerIsolateData::Get(isolate);
1523   data->realm_shared_.Reset(isolate, value);
1524 }
1525 
LogGetAndStop(const v8::FunctionCallbackInfo<v8::Value> & args)1526 void Shell::LogGetAndStop(const v8::FunctionCallbackInfo<v8::Value>& args) {
1527   Isolate* isolate = args.GetIsolate();
1528   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1529   HandleScope handle_scope(isolate);
1530 
1531   std::string file_name = i_isolate->logger()->file_name();
1532   if (!i::Log::IsLoggingToTemporaryFile(file_name)) {
1533     Throw(isolate, "Only capturing from temporary files is supported.");
1534     return;
1535   }
1536   if (!i_isolate->logger()->is_logging()) {
1537     Throw(isolate, "Logging not enabled.");
1538     return;
1539   }
1540 
1541   std::string raw_log;
1542   FILE* log_file = i_isolate->logger()->TearDownAndGetLogFile();
1543   CHECK_NOT_NULL(log_file);
1544 
1545   bool exists = false;
1546   raw_log = i::ReadFile(log_file, &exists, true);
1547   fclose(log_file);
1548 
1549   if (!exists) {
1550     Throw(isolate, "Unable to read log file.");
1551     return;
1552   }
1553   Local<String> result =
1554       String::NewFromUtf8(isolate, raw_log.c_str(), NewStringType::kNormal,
1555                           static_cast<int>(raw_log.size()))
1556           .ToLocalChecked();
1557 
1558   args.GetReturnValue().Set(result);
1559 }
1560 
1561 // async_hooks.createHook() registers functions to be called for different
1562 // lifetime events of each async operation.
AsyncHooksCreateHook(const v8::FunctionCallbackInfo<v8::Value> & args)1563 void Shell::AsyncHooksCreateHook(
1564     const v8::FunctionCallbackInfo<v8::Value>& args) {
1565   Local<Object> wrap =
1566       PerIsolateData::Get(args.GetIsolate())->GetAsyncHooks()->CreateHook(args);
1567   args.GetReturnValue().Set(wrap);
1568 }
1569 
1570 // async_hooks.executionAsyncId() returns the asyncId of the current execution
1571 // context.
AsyncHooksExecutionAsyncId(const v8::FunctionCallbackInfo<v8::Value> & args)1572 void Shell::AsyncHooksExecutionAsyncId(
1573     const v8::FunctionCallbackInfo<v8::Value>& args) {
1574   Isolate* isolate = args.GetIsolate();
1575   HandleScope handle_scope(isolate);
1576   args.GetReturnValue().Set(v8::Number::New(
1577       isolate,
1578       PerIsolateData::Get(isolate)->GetAsyncHooks()->GetExecutionAsyncId()));
1579 }
1580 
AsyncHooksTriggerAsyncId(const v8::FunctionCallbackInfo<v8::Value> & args)1581 void Shell::AsyncHooksTriggerAsyncId(
1582     const v8::FunctionCallbackInfo<v8::Value>& args) {
1583   Isolate* isolate = args.GetIsolate();
1584   HandleScope handle_scope(isolate);
1585   args.GetReturnValue().Set(v8::Number::New(
1586       isolate,
1587       PerIsolateData::Get(isolate)->GetAsyncHooks()->GetTriggerAsyncId()));
1588 }
1589 
WriteToFile(FILE * file,const v8::FunctionCallbackInfo<v8::Value> & args)1590 void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) {
1591   for (int i = 0; i < args.Length(); i++) {
1592     HandleScope handle_scope(args.GetIsolate());
1593     if (i != 0) {
1594       fprintf(file, " ");
1595     }
1596 
1597     // Explicitly catch potential exceptions in toString().
1598     v8::TryCatch try_catch(args.GetIsolate());
1599     Local<Value> arg = args[i];
1600     Local<String> str_obj;
1601 
1602     if (arg->IsSymbol()) {
1603       arg = Local<Symbol>::Cast(arg)->Description();
1604     }
1605     if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
1606              .ToLocal(&str_obj)) {
1607       try_catch.ReThrow();
1608       return;
1609     }
1610 
1611     v8::String::Utf8Value str(args.GetIsolate(), str_obj);
1612     int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), file));
1613     if (n != str.length()) {
1614       printf("Error in fwrite\n");
1615       base::OS::ExitProcess(1);
1616     }
1617   }
1618 }
1619 
WriteAndFlush(FILE * file,const v8::FunctionCallbackInfo<v8::Value> & args)1620 void WriteAndFlush(FILE* file,
1621                    const v8::FunctionCallbackInfo<v8::Value>& args) {
1622   WriteToFile(file, args);
1623   fprintf(file, "\n");
1624   fflush(file);
1625 }
1626 
Print(const v8::FunctionCallbackInfo<v8::Value> & args)1627 void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
1628   WriteAndFlush(stdout, args);
1629 }
1630 
PrintErr(const v8::FunctionCallbackInfo<v8::Value> & args)1631 void Shell::PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args) {
1632   WriteAndFlush(stderr, args);
1633 }
1634 
Write(const v8::FunctionCallbackInfo<v8::Value> & args)1635 void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) {
1636   WriteToFile(stdout, args);
1637 }
1638 
Read(const v8::FunctionCallbackInfo<v8::Value> & args)1639 void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
1640   String::Utf8Value file(args.GetIsolate(), args[0]);
1641   if (*file == nullptr) {
1642     Throw(args.GetIsolate(), "Error loading file");
1643     return;
1644   }
1645   if (args.Length() == 2) {
1646     String::Utf8Value format(args.GetIsolate(), args[1]);
1647     if (*format && std::strcmp(*format, "binary") == 0) {
1648       ReadBuffer(args);
1649       return;
1650     }
1651   }
1652   Local<String> source = ReadFile(args.GetIsolate(), *file);
1653   if (source.IsEmpty()) {
1654     Throw(args.GetIsolate(), "Error loading file");
1655     return;
1656   }
1657   args.GetReturnValue().Set(source);
1658 }
1659 
ReadFromStdin(Isolate * isolate)1660 Local<String> Shell::ReadFromStdin(Isolate* isolate) {
1661   static const int kBufferSize = 256;
1662   char buffer[kBufferSize];
1663   Local<String> accumulator = String::NewFromUtf8Literal(isolate, "");
1664   int length;
1665   while (true) {
1666     // Continue reading if the line ends with an escape '\\' or the line has
1667     // not been fully read into the buffer yet (does not end with '\n').
1668     // If fgets gets an error, just give up.
1669     char* input = nullptr;
1670     input = fgets(buffer, kBufferSize, stdin);
1671     if (input == nullptr) return Local<String>();
1672     length = static_cast<int>(strlen(buffer));
1673     if (length == 0) {
1674       return accumulator;
1675     } else if (buffer[length - 1] != '\n') {
1676       accumulator = String::Concat(
1677           isolate, accumulator,
1678           String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
1679               .ToLocalChecked());
1680     } else if (length > 1 && buffer[length - 2] == '\\') {
1681       buffer[length - 2] = '\n';
1682       accumulator =
1683           String::Concat(isolate, accumulator,
1684                          String::NewFromUtf8(isolate, buffer,
1685                                              NewStringType::kNormal, length - 1)
1686                              .ToLocalChecked());
1687     } else {
1688       return String::Concat(
1689           isolate, accumulator,
1690           String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
1691                               length - 1)
1692               .ToLocalChecked());
1693     }
1694   }
1695 }
1696 
Load(const v8::FunctionCallbackInfo<v8::Value> & args)1697 void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
1698   for (int i = 0; i < args.Length(); i++) {
1699     HandleScope handle_scope(args.GetIsolate());
1700     String::Utf8Value file(args.GetIsolate(), args[i]);
1701     if (*file == nullptr) {
1702       Throw(args.GetIsolate(), "Error loading file");
1703       return;
1704     }
1705     Local<String> source = ReadFile(args.GetIsolate(), *file);
1706     if (source.IsEmpty()) {
1707       Throw(args.GetIsolate(), "Error loading file");
1708       return;
1709     }
1710     if (!ExecuteString(
1711             args.GetIsolate(), source,
1712             String::NewFromUtf8(args.GetIsolate(), *file).ToLocalChecked(),
1713             kNoPrintResult,
1714             options.quiet_load ? kNoReportExceptions : kReportExceptions,
1715             kNoProcessMessageQueue)) {
1716       Throw(args.GetIsolate(), "Error executing file");
1717       return;
1718     }
1719   }
1720 }
1721 
SetTimeout(const v8::FunctionCallbackInfo<v8::Value> & args)1722 void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
1723   Isolate* isolate = args.GetIsolate();
1724   args.GetReturnValue().Set(v8::Number::New(isolate, 0));
1725   if (args.Length() == 0 || !args[0]->IsFunction()) return;
1726   Local<Function> callback = Local<Function>::Cast(args[0]);
1727   Local<Context> context = isolate->GetCurrentContext();
1728   PerIsolateData::Get(isolate)->SetTimeout(callback, context);
1729 }
1730 
WorkerNew(const v8::FunctionCallbackInfo<v8::Value> & args)1731 void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
1732   Isolate* isolate = args.GetIsolate();
1733   HandleScope handle_scope(isolate);
1734   if (args.Length() < 1 || !args[0]->IsString()) {
1735     Throw(args.GetIsolate(), "1st argument must be string");
1736     return;
1737   }
1738 
1739   // d8 honors `options={type: string}`, which means the first argument is
1740   // not a filename but string of script to be run.
1741   bool load_from_file = true;
1742   if (args.Length() > 1 && args[1]->IsObject()) {
1743     Local<Object> object = args[1].As<Object>();
1744     Local<Context> context = isolate->GetCurrentContext();
1745     Local<Value> value;
1746     if (TryGetValue(args.GetIsolate(), context, object, "type")
1747             .ToLocal(&value) &&
1748         value->IsString()) {
1749       Local<String> worker_type = value->ToString(context).ToLocalChecked();
1750       String::Utf8Value str(isolate, worker_type);
1751       if (strcmp("string", *str) == 0) {
1752         load_from_file = false;
1753       } else if (strcmp("classic", *str) == 0) {
1754         load_from_file = true;
1755       } else {
1756         Throw(args.GetIsolate(), "Unsupported worker type");
1757         return;
1758       }
1759     }
1760   }
1761 
1762   Local<Value> source;
1763   if (load_from_file) {
1764     String::Utf8Value filename(args.GetIsolate(), args[0]);
1765     source = ReadFile(args.GetIsolate(), *filename);
1766     if (source.IsEmpty()) {
1767       Throw(args.GetIsolate(), "Error loading worker script");
1768       return;
1769     }
1770   } else {
1771     source = args[0];
1772   }
1773 
1774   if (!args.IsConstructCall()) {
1775     Throw(args.GetIsolate(), "Worker must be constructed with new");
1776     return;
1777   }
1778 
1779   // Initialize the embedder field to 0; if we return early without
1780   // creating a new Worker (because the main thread is terminating) we can
1781   // early-out from the instance calls.
1782   args.Holder()->SetInternalField(0, v8::Integer::New(isolate, 0));
1783 
1784   {
1785     // Don't allow workers to create more workers if the main thread
1786     // is waiting for existing running workers to terminate.
1787     base::MutexGuard lock_guard(workers_mutex_.Pointer());
1788     if (!allow_new_workers_) return;
1789 
1790     String::Utf8Value script(args.GetIsolate(), source);
1791     if (!*script) {
1792       Throw(args.GetIsolate(), "Can't get worker script");
1793       return;
1794     }
1795 
1796     // The C++ worker object's lifetime is shared between the Managed<Worker>
1797     // object on the heap, which the JavaScript object points to, and an
1798     // internal std::shared_ptr in the worker thread itself.
1799     auto worker = std::make_shared<Worker>(*script);
1800     i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1801     const size_t kWorkerSizeEstimate = 4 * 1024 * 1024;  // stack + heap.
1802     i::Handle<i::Object> managed = i::Managed<Worker>::FromSharedPtr(
1803         i_isolate, kWorkerSizeEstimate, worker);
1804     args.Holder()->SetInternalField(0, Utils::ToLocal(managed));
1805     if (!Worker::StartWorkerThread(std::move(worker))) {
1806       Throw(args.GetIsolate(), "Can't start thread");
1807       return;
1808     }
1809   }
1810 }
1811 
WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value> & args)1812 void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1813   Isolate* isolate = args.GetIsolate();
1814   HandleScope handle_scope(isolate);
1815 
1816   if (args.Length() < 1) {
1817     Throw(isolate, "Invalid argument");
1818     return;
1819   }
1820 
1821   std::shared_ptr<Worker> worker =
1822       GetWorkerFromInternalField(isolate, args.Holder());
1823   if (!worker.get()) {
1824     return;
1825   }
1826 
1827   Local<Value> message = args[0];
1828   Local<Value> transfer =
1829       args.Length() >= 2 ? args[1] : Local<Value>::Cast(Undefined(isolate));
1830   std::unique_ptr<SerializationData> data =
1831       Shell::SerializeValue(isolate, message, transfer);
1832   if (data) {
1833     worker->PostMessage(std::move(data));
1834   }
1835 }
1836 
WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value> & args)1837 void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1838   Isolate* isolate = args.GetIsolate();
1839   HandleScope handle_scope(isolate);
1840   std::shared_ptr<Worker> worker =
1841       GetWorkerFromInternalField(isolate, args.Holder());
1842   if (!worker.get()) {
1843     return;
1844   }
1845 
1846   std::unique_ptr<SerializationData> data = worker->GetMessage();
1847   if (data) {
1848     Local<Value> value;
1849     if (Shell::DeserializeValue(isolate, std::move(data)).ToLocal(&value)) {
1850       args.GetReturnValue().Set(value);
1851     }
1852   }
1853 }
1854 
WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value> & args)1855 void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1856   Isolate* isolate = args.GetIsolate();
1857   HandleScope handle_scope(isolate);
1858   std::shared_ptr<Worker> worker =
1859       GetWorkerFromInternalField(isolate, args.Holder());
1860   if (!worker.get()) {
1861     return;
1862   }
1863 
1864   worker->Terminate();
1865 }
1866 
WorkerTerminateAndWait(const v8::FunctionCallbackInfo<v8::Value> & args)1867 void Shell::WorkerTerminateAndWait(
1868     const v8::FunctionCallbackInfo<v8::Value>& args) {
1869   Isolate* isolate = args.GetIsolate();
1870   HandleScope handle_scope(isolate);
1871   std::shared_ptr<Worker> worker =
1872       GetWorkerFromInternalField(isolate, args.Holder());
1873   if (!worker.get()) {
1874     return;
1875   }
1876 
1877   worker->TerminateAndWaitForThread();
1878 }
1879 
QuitOnce(v8::FunctionCallbackInfo<v8::Value> * args)1880 void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) {
1881   int exit_code = (*args)[0]
1882                       ->Int32Value(args->GetIsolate()->GetCurrentContext())
1883                       .FromMaybe(0);
1884   WaitForRunningWorkers();
1885   args->GetIsolate()->Exit();
1886   OnExit(args->GetIsolate());
1887   base::OS::ExitProcess(exit_code);
1888 }
1889 
Quit(const v8::FunctionCallbackInfo<v8::Value> & args)1890 void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
1891   base::CallOnce(&quit_once_, &QuitOnce,
1892                  const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args));
1893 }
1894 
WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value> & args)1895 void Shell::WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1896   SetWaitUntilDone(args.GetIsolate(), true);
1897 }
1898 
NotifyDone(const v8::FunctionCallbackInfo<v8::Value> & args)1899 void Shell::NotifyDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1900   SetWaitUntilDone(args.GetIsolate(), false);
1901 }
1902 
Version(const v8::FunctionCallbackInfo<v8::Value> & args)1903 void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
1904   args.GetReturnValue().Set(
1905       String::NewFromUtf8(args.GetIsolate(), V8::GetVersion())
1906           .ToLocalChecked());
1907 }
1908 
1909 #ifdef V8_FUZZILLI
1910 
1911 // We have to assume that the fuzzer will be able to call this function e.g. by
1912 // enumerating the properties of the global object and eval'ing them. As such
1913 // this function is implemented in a way that requires passing some magic value
1914 // as first argument (with the idea being that the fuzzer won't be able to
1915 // generate this value) which then also acts as a selector for the operation
1916 // to perform.
Fuzzilli(const v8::FunctionCallbackInfo<v8::Value> & args)1917 void Shell::Fuzzilli(const v8::FunctionCallbackInfo<v8::Value>& args) {
1918   HandleScope handle_scope(args.GetIsolate());
1919 
1920   String::Utf8Value operation(args.GetIsolate(), args[0]);
1921   if (*operation == nullptr) {
1922     return;
1923   }
1924 
1925   if (strcmp(*operation, "FUZZILLI_CRASH") == 0) {
1926     auto arg = args[1]
1927                    ->Int32Value(args.GetIsolate()->GetCurrentContext())
1928                    .FromMaybe(0);
1929     switch (arg) {
1930       case 0:
1931         V8_IMMEDIATE_CRASH();
1932         break;
1933       case 1:
1934         CHECK(0);
1935         break;
1936       default:
1937         DCHECK(false);
1938         break;
1939     }
1940   } else if (strcmp(*operation, "FUZZILLI_PRINT") == 0) {
1941     static FILE* fzliout = fdopen(REPRL_DWFD, "w");
1942     if (!fzliout) {
1943       fprintf(
1944           stderr,
1945           "Fuzzer output channel not available, printing to stdout instead\n");
1946       fzliout = stdout;
1947     }
1948 
1949     String::Utf8Value string(args.GetIsolate(), args[1]);
1950     if (*string == nullptr) {
1951       return;
1952     }
1953     fprintf(fzliout, "%s\n", *string);
1954     fflush(fzliout);
1955   }
1956 }
1957 
1958 #endif  // V8_FUZZILLI
1959 
ReportException(Isolate * isolate,Local<v8::Message> message,Local<v8::Value> exception_obj)1960 void Shell::ReportException(Isolate* isolate, Local<v8::Message> message,
1961                             Local<v8::Value> exception_obj) {
1962   HandleScope handle_scope(isolate);
1963   Local<Context> context = isolate->GetCurrentContext();
1964   bool enter_context = context.IsEmpty();
1965   if (enter_context) {
1966     context = Local<Context>::New(isolate, evaluation_context_);
1967     context->Enter();
1968   }
1969   // Converts a V8 value to a C string.
1970   auto ToCString = [](const v8::String::Utf8Value& value) {
1971     return *value ? *value : "<string conversion failed>";
1972   };
1973 
1974   v8::String::Utf8Value exception(isolate, exception_obj);
1975   const char* exception_string = ToCString(exception);
1976   if (message.IsEmpty()) {
1977     // V8 didn't provide any extra information about this error; just
1978     // print the exception.
1979     printf("%s\n", exception_string);
1980   } else if (message->GetScriptOrigin().Options().IsWasm()) {
1981     // Print wasm-function[(function index)]:(offset): (message).
1982     int function_index = message->GetWasmFunctionIndex();
1983     int offset = message->GetStartColumn(context).FromJust();
1984     printf("wasm-function[%d]:0x%x: %s\n", function_index, offset,
1985            exception_string);
1986   } else {
1987     // Print (filename):(line number): (message).
1988     v8::String::Utf8Value filename(isolate,
1989                                    message->GetScriptOrigin().ResourceName());
1990     const char* filename_string = ToCString(filename);
1991     int linenum = message->GetLineNumber(context).FromMaybe(-1);
1992     printf("%s:%i: %s\n", filename_string, linenum, exception_string);
1993     Local<String> sourceline;
1994     if (message->GetSourceLine(context).ToLocal(&sourceline)) {
1995       // Print line of source code.
1996       v8::String::Utf8Value sourcelinevalue(isolate, sourceline);
1997       const char* sourceline_string = ToCString(sourcelinevalue);
1998       printf("%s\n", sourceline_string);
1999       // Print wavy underline (GetUnderline is deprecated).
2000       int start = message->GetStartColumn(context).FromJust();
2001       for (int i = 0; i < start; i++) {
2002         printf(" ");
2003       }
2004       int end = message->GetEndColumn(context).FromJust();
2005       for (int i = start; i < end; i++) {
2006         printf("^");
2007       }
2008       printf("\n");
2009     }
2010   }
2011   Local<Value> stack_trace_string;
2012   if (v8::TryCatch::StackTrace(context, exception_obj)
2013           .ToLocal(&stack_trace_string) &&
2014       stack_trace_string->IsString()) {
2015     v8::String::Utf8Value stack_trace(isolate,
2016                                       Local<String>::Cast(stack_trace_string));
2017     printf("%s\n", ToCString(stack_trace));
2018   }
2019   printf("\n");
2020   if (enter_context) context->Exit();
2021 }
2022 
ReportException(v8::Isolate * isolate,v8::TryCatch * try_catch)2023 void Shell::ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
2024   ReportException(isolate, try_catch->Message(), try_catch->Exception());
2025 }
2026 
Bind(const char * name,bool is_histogram)2027 int32_t* Counter::Bind(const char* name, bool is_histogram) {
2028   int i;
2029   for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
2030     name_[i] = static_cast<char>(name[i]);
2031   name_[i] = '\0';
2032   is_histogram_ = is_histogram;
2033   return ptr();
2034 }
2035 
AddSample(int32_t sample)2036 void Counter::AddSample(int32_t sample) {
2037   count_++;
2038   sample_total_ += sample;
2039 }
2040 
CounterCollection()2041 CounterCollection::CounterCollection() {
2042   magic_number_ = 0xDEADFACE;
2043   max_counters_ = kMaxCounters;
2044   max_name_size_ = Counter::kMaxNameSize;
2045   counters_in_use_ = 0;
2046 }
2047 
GetNextCounter()2048 Counter* CounterCollection::GetNextCounter() {
2049   if (counters_in_use_ == kMaxCounters) return nullptr;
2050   return &counters_[counters_in_use_++];
2051 }
2052 
MapCounters(v8::Isolate * isolate,const char * name)2053 void Shell::MapCounters(v8::Isolate* isolate, const char* name) {
2054   counters_file_ = base::OS::MemoryMappedFile::create(
2055       name, sizeof(CounterCollection), &local_counters_);
2056   void* memory =
2057       (counters_file_ == nullptr) ? nullptr : counters_file_->memory();
2058   if (memory == nullptr) {
2059     printf("Could not map counters file %s\n", name);
2060     base::OS::ExitProcess(1);
2061   }
2062   counters_ = static_cast<CounterCollection*>(memory);
2063   isolate->SetCounterFunction(LookupCounter);
2064   isolate->SetCreateHistogramFunction(CreateHistogram);
2065   isolate->SetAddHistogramSampleFunction(AddHistogramSample);
2066 }
2067 
GetCounter(const char * name,bool is_histogram)2068 Counter* Shell::GetCounter(const char* name, bool is_histogram) {
2069   auto map_entry = counter_map_->find(name);
2070   Counter* counter =
2071       map_entry != counter_map_->end() ? map_entry->second : nullptr;
2072 
2073   if (counter == nullptr) {
2074     counter = counters_->GetNextCounter();
2075     if (counter != nullptr) {
2076       (*counter_map_)[name] = counter;
2077       counter->Bind(name, is_histogram);
2078     }
2079   } else {
2080     DCHECK(counter->is_histogram() == is_histogram);
2081   }
2082   return counter;
2083 }
2084 
LookupCounter(const char * name)2085 int* Shell::LookupCounter(const char* name) {
2086   Counter* counter = GetCounter(name, false);
2087 
2088   if (counter != nullptr) {
2089     return counter->ptr();
2090   } else {
2091     return nullptr;
2092   }
2093 }
2094 
CreateHistogram(const char * name,int min,int max,size_t buckets)2095 void* Shell::CreateHistogram(const char* name, int min, int max,
2096                              size_t buckets) {
2097   return GetCounter(name, true);
2098 }
2099 
AddHistogramSample(void * histogram,int sample)2100 void Shell::AddHistogramSample(void* histogram, int sample) {
2101   Counter* counter = reinterpret_cast<Counter*>(histogram);
2102   counter->AddSample(sample);
2103 }
2104 
2105 // Turn a value into a human-readable string.
Stringify(Isolate * isolate,Local<Value> value)2106 Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) {
2107   v8::Local<v8::Context> context =
2108       v8::Local<v8::Context>::New(isolate, evaluation_context_);
2109   if (stringify_function_.IsEmpty()) {
2110     Local<String> source =
2111         String::NewFromUtf8(isolate, stringify_source_).ToLocalChecked();
2112     Local<String> name = String::NewFromUtf8Literal(isolate, "d8-stringify");
2113     ScriptOrigin origin(name);
2114     Local<Script> script =
2115         Script::Compile(context, source, &origin).ToLocalChecked();
2116     stringify_function_.Reset(
2117         isolate, script->Run(context).ToLocalChecked().As<Function>());
2118   }
2119   Local<Function> fun = Local<Function>::New(isolate, stringify_function_);
2120   Local<Value> argv[1] = {value};
2121   v8::TryCatch try_catch(isolate);
2122   MaybeLocal<Value> result = fun->Call(context, Undefined(isolate), 1, argv);
2123   if (result.IsEmpty()) return String::Empty(isolate);
2124   return result.ToLocalChecked().As<String>();
2125 }
2126 
CreateGlobalTemplate(Isolate * isolate)2127 Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
2128   Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
2129   global_template->Set(Symbol::GetToStringTag(isolate),
2130                        String::NewFromUtf8Literal(isolate, "global"));
2131   global_template->Set(isolate, "version",
2132                        FunctionTemplate::New(isolate, Version));
2133 
2134   global_template->Set(isolate, "print", FunctionTemplate::New(isolate, Print));
2135   global_template->Set(isolate, "printErr",
2136                        FunctionTemplate::New(isolate, PrintErr));
2137   global_template->Set(isolate, "write", FunctionTemplate::New(isolate, Write));
2138   global_template->Set(isolate, "read", FunctionTemplate::New(isolate, Read));
2139   global_template->Set(isolate, "readbuffer",
2140                        FunctionTemplate::New(isolate, ReadBuffer));
2141   global_template->Set(isolate, "readline",
2142                        FunctionTemplate::New(isolate, ReadLine));
2143   global_template->Set(isolate, "load", FunctionTemplate::New(isolate, Load));
2144   global_template->Set(isolate, "setTimeout",
2145                        FunctionTemplate::New(isolate, SetTimeout));
2146   // Some Emscripten-generated code tries to call 'quit', which in turn would
2147   // call C's exit(). This would lead to memory leaks, because there is no way
2148   // we can terminate cleanly then, so we need a way to hide 'quit'.
2149   if (!options.omit_quit) {
2150     global_template->Set(isolate, "quit", FunctionTemplate::New(isolate, Quit));
2151   }
2152   global_template->Set(isolate, "testRunner",
2153                        Shell::CreateTestRunnerTemplate(isolate));
2154   global_template->Set(isolate, "Realm", Shell::CreateRealmTemplate(isolate));
2155   global_template->Set(isolate, "performance",
2156                        Shell::CreatePerformanceTemplate(isolate));
2157   global_template->Set(isolate, "Worker", Shell::CreateWorkerTemplate(isolate));
2158   // Prevent fuzzers from creating side effects.
2159   if (!i::FLAG_fuzzing) {
2160     global_template->Set(isolate, "os", Shell::CreateOSTemplate(isolate));
2161   }
2162   global_template->Set(isolate, "d8", Shell::CreateD8Template(isolate));
2163 
2164 #ifdef V8_FUZZILLI
2165   global_template->Set(
2166       String::NewFromUtf8(isolate, "fuzzilli", NewStringType::kNormal)
2167           .ToLocalChecked(),
2168       FunctionTemplate::New(isolate, Fuzzilli), PropertyAttribute::DontEnum);
2169 #endif  // V8_FUZZILLI
2170 
2171   if (i::FLAG_expose_async_hooks) {
2172     global_template->Set(isolate, "async_hooks",
2173                          Shell::CreateAsyncHookTemplate(isolate));
2174   }
2175 
2176   return global_template;
2177 }
2178 
CreateOSTemplate(Isolate * isolate)2179 Local<ObjectTemplate> Shell::CreateOSTemplate(Isolate* isolate) {
2180   Local<ObjectTemplate> os_template = ObjectTemplate::New(isolate);
2181   AddOSMethods(isolate, os_template);
2182   return os_template;
2183 }
2184 
CreateWorkerTemplate(Isolate * isolate)2185 Local<FunctionTemplate> Shell::CreateWorkerTemplate(Isolate* isolate) {
2186   Local<FunctionTemplate> worker_fun_template =
2187       FunctionTemplate::New(isolate, WorkerNew);
2188   Local<Signature> worker_signature =
2189       Signature::New(isolate, worker_fun_template);
2190   worker_fun_template->SetClassName(
2191       String::NewFromUtf8Literal(isolate, "Worker"));
2192   worker_fun_template->ReadOnlyPrototype();
2193   worker_fun_template->PrototypeTemplate()->Set(
2194       isolate, "terminate",
2195       FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(),
2196                             worker_signature));
2197   worker_fun_template->PrototypeTemplate()->Set(
2198       isolate, "terminateAndWait",
2199       FunctionTemplate::New(isolate, WorkerTerminateAndWait, Local<Value>(),
2200                             worker_signature));
2201   worker_fun_template->PrototypeTemplate()->Set(
2202       isolate, "postMessage",
2203       FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(),
2204                             worker_signature));
2205   worker_fun_template->PrototypeTemplate()->Set(
2206       isolate, "getMessage",
2207       FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(),
2208                             worker_signature));
2209   worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1);
2210   return worker_fun_template;
2211 }
2212 
CreateAsyncHookTemplate(Isolate * isolate)2213 Local<ObjectTemplate> Shell::CreateAsyncHookTemplate(Isolate* isolate) {
2214   Local<ObjectTemplate> async_hooks_templ = ObjectTemplate::New(isolate);
2215   async_hooks_templ->Set(isolate, "createHook",
2216                          FunctionTemplate::New(isolate, AsyncHooksCreateHook));
2217   async_hooks_templ->Set(
2218       isolate, "executionAsyncId",
2219       FunctionTemplate::New(isolate, AsyncHooksExecutionAsyncId));
2220   async_hooks_templ->Set(
2221       isolate, "triggerAsyncId",
2222       FunctionTemplate::New(isolate, AsyncHooksTriggerAsyncId));
2223   return async_hooks_templ;
2224 }
2225 
CreateTestRunnerTemplate(Isolate * isolate)2226 Local<ObjectTemplate> Shell::CreateTestRunnerTemplate(Isolate* isolate) {
2227   Local<ObjectTemplate> test_template = ObjectTemplate::New(isolate);
2228   test_template->Set(isolate, "notifyDone",
2229                      FunctionTemplate::New(isolate, NotifyDone));
2230   test_template->Set(isolate, "waitUntilDone",
2231                      FunctionTemplate::New(isolate, WaitUntilDone));
2232   // Reliable access to quit functionality. The "quit" method function
2233   // installed on the global object can be hidden with the --omit-quit flag
2234   // (e.g. on asan bots).
2235   test_template->Set(isolate, "quit", FunctionTemplate::New(isolate, Quit));
2236   return test_template;
2237 }
2238 
CreatePerformanceTemplate(Isolate * isolate)2239 Local<ObjectTemplate> Shell::CreatePerformanceTemplate(Isolate* isolate) {
2240   Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
2241   performance_template->Set(isolate, "now",
2242                             FunctionTemplate::New(isolate, PerformanceNow));
2243   performance_template->Set(
2244       isolate, "measureMemory",
2245       FunctionTemplate::New(isolate, PerformanceMeasureMemory));
2246   return performance_template;
2247 }
2248 
CreateRealmTemplate(Isolate * isolate)2249 Local<ObjectTemplate> Shell::CreateRealmTemplate(Isolate* isolate) {
2250   Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
2251   realm_template->Set(isolate, "current",
2252                       FunctionTemplate::New(isolate, RealmCurrent));
2253   realm_template->Set(isolate, "owner",
2254                       FunctionTemplate::New(isolate, RealmOwner));
2255   realm_template->Set(isolate, "global",
2256                       FunctionTemplate::New(isolate, RealmGlobal));
2257   realm_template->Set(isolate, "create",
2258                       FunctionTemplate::New(isolate, RealmCreate));
2259   realm_template->Set(
2260       isolate, "createAllowCrossRealmAccess",
2261       FunctionTemplate::New(isolate, RealmCreateAllowCrossRealmAccess));
2262   realm_template->Set(isolate, "navigate",
2263                       FunctionTemplate::New(isolate, RealmNavigate));
2264   realm_template->Set(isolate, "detachGlobal",
2265                       FunctionTemplate::New(isolate, RealmDetachGlobal));
2266   realm_template->Set(isolate, "dispose",
2267                       FunctionTemplate::New(isolate, RealmDispose));
2268   realm_template->Set(isolate, "switch",
2269                       FunctionTemplate::New(isolate, RealmSwitch));
2270   realm_template->Set(isolate, "eval",
2271                       FunctionTemplate::New(isolate, RealmEval));
2272   realm_template->SetAccessor(String::NewFromUtf8Literal(isolate, "shared"),
2273                               RealmSharedGet, RealmSharedSet);
2274   return realm_template;
2275 }
2276 
CreateD8Template(Isolate * isolate)2277 Local<ObjectTemplate> Shell::CreateD8Template(Isolate* isolate) {
2278   Local<ObjectTemplate> d8_template = ObjectTemplate::New(isolate);
2279   {
2280     Local<ObjectTemplate> log_template = ObjectTemplate::New(isolate);
2281     log_template->Set(isolate, "getAndStop",
2282                       FunctionTemplate::New(isolate, LogGetAndStop));
2283 
2284     d8_template->Set(isolate, "log", log_template);
2285   }
2286   return d8_template;
2287 }
2288 
PrintMessageCallback(Local<Message> message,Local<Value> error)2289 static void PrintMessageCallback(Local<Message> message, Local<Value> error) {
2290   switch (message->ErrorLevel()) {
2291     case v8::Isolate::kMessageWarning:
2292     case v8::Isolate::kMessageLog:
2293     case v8::Isolate::kMessageInfo:
2294     case v8::Isolate::kMessageDebug: {
2295       break;
2296     }
2297 
2298     case v8::Isolate::kMessageError: {
2299       Shell::ReportException(message->GetIsolate(), message, error);
2300       return;
2301     }
2302 
2303     default: {
2304       UNREACHABLE();
2305     }
2306   }
2307   // Converts a V8 value to a C string.
2308   auto ToCString = [](const v8::String::Utf8Value& value) {
2309     return *value ? *value : "<string conversion failed>";
2310   };
2311   Isolate* isolate = message->GetIsolate();
2312   v8::String::Utf8Value msg(isolate, message->Get());
2313   const char* msg_string = ToCString(msg);
2314   // Print (filename):(line number): (message).
2315   v8::String::Utf8Value filename(isolate,
2316                                  message->GetScriptOrigin().ResourceName());
2317   const char* filename_string = ToCString(filename);
2318   Maybe<int> maybeline = message->GetLineNumber(isolate->GetCurrentContext());
2319   int linenum = maybeline.IsJust() ? maybeline.FromJust() : -1;
2320   printf("%s:%i: %s\n", filename_string, linenum, msg_string);
2321 }
2322 
PromiseRejectCallback(v8::PromiseRejectMessage data)2323 void Shell::PromiseRejectCallback(v8::PromiseRejectMessage data) {
2324   if (options.ignore_unhandled_promises) return;
2325   if (data.GetEvent() == v8::kPromiseRejectAfterResolved ||
2326       data.GetEvent() == v8::kPromiseResolveAfterResolved) {
2327     // Ignore reject/resolve after resolved.
2328     return;
2329   }
2330   v8::Local<v8::Promise> promise = data.GetPromise();
2331   v8::Isolate* isolate = promise->GetIsolate();
2332   PerIsolateData* isolate_data = PerIsolateData::Get(isolate);
2333 
2334   if (data.GetEvent() == v8::kPromiseHandlerAddedAfterReject) {
2335     isolate_data->RemoveUnhandledPromise(promise);
2336     return;
2337   }
2338 
2339   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2340   bool capture_exceptions =
2341       i_isolate->get_capture_stack_trace_for_uncaught_exceptions();
2342   isolate->SetCaptureStackTraceForUncaughtExceptions(true);
2343   v8::Local<Value> exception = data.GetValue();
2344   v8::Local<Message> message;
2345   // Assume that all objects are stack-traces.
2346   if (exception->IsObject()) {
2347     message = v8::Exception::CreateMessage(isolate, exception);
2348   }
2349   if (!exception->IsNativeError() &&
2350       (message.IsEmpty() || message->GetStackTrace().IsEmpty())) {
2351     // If there is no real Error object, manually throw and catch a stack trace.
2352     v8::TryCatch try_catch(isolate);
2353     try_catch.SetVerbose(true);
2354     isolate->ThrowException(v8::Exception::Error(
2355         v8::String::NewFromUtf8Literal(isolate, "Unhandled Promise.")));
2356     message = try_catch.Message();
2357     exception = try_catch.Exception();
2358   }
2359   isolate->SetCaptureStackTraceForUncaughtExceptions(capture_exceptions);
2360 
2361   isolate_data->AddUnhandledPromise(promise, message, exception);
2362 }
2363 
Initialize(Isolate * isolate,D8Console * console,bool isOnMainThread)2364 void Shell::Initialize(Isolate* isolate, D8Console* console,
2365                        bool isOnMainThread) {
2366   isolate->SetPromiseRejectCallback(PromiseRejectCallback);
2367   if (isOnMainThread) {
2368     // Set up counters
2369     if (i::FLAG_map_counters[0] != '\0') {
2370       MapCounters(isolate, i::FLAG_map_counters);
2371     }
2372     // Disable default message reporting.
2373     isolate->AddMessageListenerWithErrorLevel(
2374         PrintMessageCallback,
2375         v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
2376             v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
2377             v8::Isolate::kMessageLog);
2378   }
2379 
2380   isolate->SetHostImportModuleDynamicallyCallback(
2381       Shell::HostImportModuleDynamically);
2382   isolate->SetHostInitializeImportMetaObjectCallback(
2383       Shell::HostInitializeImportMetaObject);
2384 
2385 #ifdef V8_FUZZILLI
2386   // Let the parent process (Fuzzilli) know we are ready.
2387   if (options.fuzzilli_enable_builtins_coverage) {
2388     cov_init_builtins_edges(static_cast<uint32_t>(
2389         i::BasicBlockProfiler::Get()
2390             ->GetCoverageBitmap(reinterpret_cast<i::Isolate*>(isolate))
2391             .size()));
2392   }
2393   char helo[] = "HELO";
2394   if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) {
2395     fuzzilli_reprl = false;
2396   }
2397 
2398   if (memcmp(helo, "HELO", 4) != 0) {
2399     fprintf(stderr, "Invalid response from parent\n");
2400     _exit(-1);
2401   }
2402 #endif  // V8_FUZZILLI
2403 
2404   debug::SetConsoleDelegate(isolate, console);
2405 }
2406 
CreateEvaluationContext(Isolate * isolate)2407 Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
2408   // This needs to be a critical section since this is not thread-safe
2409   base::MutexGuard lock_guard(context_mutex_.Pointer());
2410   // Initialize the global objects
2411   Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
2412   EscapableHandleScope handle_scope(isolate);
2413   Local<Context> context = Context::New(isolate, nullptr, global_template);
2414   DCHECK(!context.IsEmpty());
2415   if (i::FLAG_perf_prof_annotate_wasm || i::FLAG_vtune_prof_annotate_wasm) {
2416     isolate->SetWasmLoadSourceMapCallback(ReadFile);
2417   }
2418   InitializeModuleEmbedderData(context);
2419   if (options.include_arguments) {
2420     Context::Scope scope(context);
2421     const std::vector<const char*>& args = options.arguments;
2422     int size = static_cast<int>(args.size());
2423     Local<Array> array = Array::New(isolate, size);
2424     for (int i = 0; i < size; i++) {
2425       Local<String> arg =
2426           v8::String::NewFromUtf8(isolate, args[i]).ToLocalChecked();
2427       Local<Number> index = v8::Number::New(isolate, i);
2428       array->Set(context, index, arg).FromJust();
2429     }
2430     Local<String> name = String::NewFromUtf8Literal(
2431         isolate, "arguments", NewStringType::kInternalized);
2432     context->Global()->Set(context, name, array).FromJust();
2433   }
2434   return handle_scope.Escape(context);
2435 }
2436 
WriteIgnitionDispatchCountersFile(v8::Isolate * isolate)2437 void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
2438   HandleScope handle_scope(isolate);
2439   Local<Context> context = Context::New(isolate);
2440   Context::Scope context_scope(context);
2441 
2442   Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate)
2443                                         ->interpreter()
2444                                         ->GetDispatchCountersObject();
2445   std::ofstream dispatch_counters_stream(
2446       i::FLAG_trace_ignition_dispatches_output_file);
2447   dispatch_counters_stream << *String::Utf8Value(
2448       isolate, JSON::Stringify(context, dispatch_counters).ToLocalChecked());
2449 }
2450 
2451 namespace {
LineFromOffset(Local<debug::Script> script,int offset)2452 int LineFromOffset(Local<debug::Script> script, int offset) {
2453   debug::Location location = script->GetSourceLocation(offset);
2454   return location.GetLineNumber();
2455 }
2456 
WriteLcovDataForRange(std::vector<uint32_t> * lines,int start_line,int end_line,uint32_t count)2457 void WriteLcovDataForRange(std::vector<uint32_t>* lines, int start_line,
2458                            int end_line, uint32_t count) {
2459   // Ensure space in the array.
2460   lines->resize(std::max(static_cast<size_t>(end_line + 1), lines->size()), 0);
2461   // Boundary lines could be shared between two functions with different
2462   // invocation counts. Take the maximum.
2463   (*lines)[start_line] = std::max((*lines)[start_line], count);
2464   (*lines)[end_line] = std::max((*lines)[end_line], count);
2465   // Invocation counts for non-boundary lines are overwritten.
2466   for (int k = start_line + 1; k < end_line; k++) (*lines)[k] = count;
2467 }
2468 
WriteLcovDataForNamedRange(std::ostream & sink,std::vector<uint32_t> * lines,const std::string & name,int start_line,int end_line,uint32_t count)2469 void WriteLcovDataForNamedRange(std::ostream& sink,
2470                                 std::vector<uint32_t>* lines,
2471                                 const std::string& name, int start_line,
2472                                 int end_line, uint32_t count) {
2473   WriteLcovDataForRange(lines, start_line, end_line, count);
2474   sink << "FN:" << start_line + 1 << "," << name << std::endl;
2475   sink << "FNDA:" << count << "," << name << std::endl;
2476 }
2477 }  // namespace
2478 
2479 // Write coverage data in LCOV format. See man page for geninfo(1).
WriteLcovData(v8::Isolate * isolate,const char * file)2480 void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
2481   if (!file) return;
2482   HandleScope handle_scope(isolate);
2483   debug::Coverage coverage = debug::Coverage::CollectPrecise(isolate);
2484   std::ofstream sink(file, std::ofstream::app);
2485   for (size_t i = 0; i < coverage.ScriptCount(); i++) {
2486     debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
2487     Local<debug::Script> script = script_data.GetScript();
2488     // Skip unnamed scripts.
2489     Local<String> name;
2490     if (!script->Name().ToLocal(&name)) continue;
2491     std::string file_name = ToSTLString(isolate, name);
2492     // Skip scripts not backed by a file.
2493     if (!std::ifstream(file_name).good()) continue;
2494     sink << "SF:";
2495     sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl;
2496     std::vector<uint32_t> lines;
2497     for (size_t j = 0; j < script_data.FunctionCount(); j++) {
2498       debug::Coverage::FunctionData function_data =
2499           script_data.GetFunctionData(j);
2500 
2501       // Write function stats.
2502       {
2503         debug::Location start =
2504             script->GetSourceLocation(function_data.StartOffset());
2505         debug::Location end =
2506             script->GetSourceLocation(function_data.EndOffset());
2507         int start_line = start.GetLineNumber();
2508         int end_line = end.GetLineNumber();
2509         uint32_t count = function_data.Count();
2510 
2511         Local<String> name;
2512         std::stringstream name_stream;
2513         if (function_data.Name().ToLocal(&name)) {
2514           name_stream << ToSTLString(isolate, name);
2515         } else {
2516           name_stream << "<" << start_line + 1 << "-";
2517           name_stream << start.GetColumnNumber() << ">";
2518         }
2519 
2520         WriteLcovDataForNamedRange(sink, &lines, name_stream.str(), start_line,
2521                                    end_line, count);
2522       }
2523 
2524       // Process inner blocks.
2525       for (size_t k = 0; k < function_data.BlockCount(); k++) {
2526         debug::Coverage::BlockData block_data = function_data.GetBlockData(k);
2527         int start_line = LineFromOffset(script, block_data.StartOffset());
2528         int end_line = LineFromOffset(script, block_data.EndOffset() - 1);
2529         uint32_t count = block_data.Count();
2530         WriteLcovDataForRange(&lines, start_line, end_line, count);
2531       }
2532     }
2533     // Write per-line coverage. LCOV uses 1-based line numbers.
2534     for (size_t i = 0; i < lines.size(); i++) {
2535       sink << "DA:" << (i + 1) << "," << lines[i] << std::endl;
2536     }
2537     sink << "end_of_record" << std::endl;
2538   }
2539 }
2540 
OnExit(v8::Isolate * isolate)2541 void Shell::OnExit(v8::Isolate* isolate) {
2542   isolate->Dispose();
2543 
2544   if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
2545     std::vector<std::pair<std::string, Counter*>> counters(
2546         counter_map_->begin(), counter_map_->end());
2547     std::sort(counters.begin(), counters.end());
2548 
2549     if (i::FLAG_dump_counters_nvp) {
2550       // Dump counters as name-value pairs.
2551       for (const auto& pair : counters) {
2552         std::string key = pair.first;
2553         Counter* counter = pair.second;
2554         if (counter->is_histogram()) {
2555           std::cout << "\"c:" << key << "\"=" << counter->count() << "\n";
2556           std::cout << "\"t:" << key << "\"=" << counter->sample_total()
2557                     << "\n";
2558         } else {
2559           std::cout << "\"" << key << "\"=" << counter->count() << "\n";
2560         }
2561       }
2562     } else {
2563       // Dump counters in formatted boxes.
2564       constexpr int kNameBoxSize = 64;
2565       constexpr int kValueBoxSize = 13;
2566       std::cout << "+" << std::string(kNameBoxSize, '-') << "+"
2567                 << std::string(kValueBoxSize, '-') << "+\n";
2568       std::cout << "| Name" << std::string(kNameBoxSize - 5, ' ') << "| Value"
2569                 << std::string(kValueBoxSize - 6, ' ') << "|\n";
2570       std::cout << "+" << std::string(kNameBoxSize, '-') << "+"
2571                 << std::string(kValueBoxSize, '-') << "+\n";
2572       for (const auto& pair : counters) {
2573         std::string key = pair.first;
2574         Counter* counter = pair.second;
2575         if (counter->is_histogram()) {
2576           std::cout << "| c:" << std::setw(kNameBoxSize - 4) << std::left << key
2577                     << " | " << std::setw(kValueBoxSize - 2) << std::right
2578                     << counter->count() << " |\n";
2579           std::cout << "| t:" << std::setw(kNameBoxSize - 4) << std::left << key
2580                     << " | " << std::setw(kValueBoxSize - 2) << std::right
2581                     << counter->sample_total() << " |\n";
2582         } else {
2583           std::cout << "| " << std::setw(kNameBoxSize - 2) << std::left << key
2584                     << " | " << std::setw(kValueBoxSize - 2) << std::right
2585                     << counter->count() << " |\n";
2586         }
2587       }
2588       std::cout << "+" << std::string(kNameBoxSize, '-') << "+"
2589                 << std::string(kValueBoxSize, '-') << "+\n";
2590     }
2591   }
2592 
2593   delete counters_file_;
2594   delete counter_map_;
2595 }
2596 
FOpen(const char * path,const char * mode)2597 static FILE* FOpen(const char* path, const char* mode) {
2598 #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
2599   FILE* result;
2600   if (fopen_s(&result, path, mode) == 0) {
2601     return result;
2602   } else {
2603     return nullptr;
2604   }
2605 #else
2606   FILE* file = fopen(path, mode);
2607   if (file == nullptr) return nullptr;
2608   struct stat file_stat;
2609   if (fstat(fileno(file), &file_stat) != 0) return nullptr;
2610   bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
2611   if (is_regular_file) return file;
2612   fclose(file);
2613   return nullptr;
2614 #endif
2615 }
2616 
ReadChars(const char * name,int * size_out)2617 static char* ReadChars(const char* name, int* size_out) {
2618   if (Shell::options.read_from_tcp_port >= 0) {
2619     return Shell::ReadCharsFromTcpPort(name, size_out);
2620   }
2621 
2622   FILE* file = FOpen(name, "rb");
2623   if (file == nullptr) return nullptr;
2624 
2625   fseek(file, 0, SEEK_END);
2626   size_t size = ftell(file);
2627   rewind(file);
2628 
2629   char* chars = new char[size + 1];
2630   chars[size] = '\0';
2631   for (size_t i = 0; i < size;) {
2632     i += fread(&chars[i], 1, size - i, file);
2633     if (ferror(file)) {
2634       fclose(file);
2635       delete[] chars;
2636       return nullptr;
2637     }
2638   }
2639   fclose(file);
2640   *size_out = static_cast<int>(size);
2641   return chars;
2642 }
2643 
ReadBuffer(const v8::FunctionCallbackInfo<v8::Value> & args)2644 void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) {
2645   static_assert(sizeof(char) == sizeof(uint8_t),
2646                 "char and uint8_t should both have 1 byte");
2647   Isolate* isolate = args.GetIsolate();
2648   String::Utf8Value filename(isolate, args[0]);
2649   int length;
2650   if (*filename == nullptr) {
2651     Throw(isolate, "Error loading file");
2652     return;
2653   }
2654 
2655   uint8_t* data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
2656   if (data == nullptr) {
2657     Throw(isolate, "Error reading file");
2658     return;
2659   }
2660   std::unique_ptr<v8::BackingStore> backing_store =
2661       ArrayBuffer::NewBackingStore(
2662           data, length,
2663           [](void* data, size_t length, void*) {
2664             delete[] reinterpret_cast<uint8_t*>(data);
2665           },
2666           nullptr);
2667   Local<v8::ArrayBuffer> buffer =
2668       ArrayBuffer::New(isolate, std::move(backing_store));
2669 
2670   args.GetReturnValue().Set(buffer);
2671 }
2672 
2673 // Reads a file into a v8 string.
ReadFile(Isolate * isolate,const char * name)2674 Local<String> Shell::ReadFile(Isolate* isolate, const char* name) {
2675   std::unique_ptr<base::OS::MemoryMappedFile> file(
2676       base::OS::MemoryMappedFile::open(
2677           name, base::OS::MemoryMappedFile::FileMode::kReadOnly));
2678   if (!file) return Local<String>();
2679 
2680   int size = static_cast<int>(file->size());
2681   char* chars = static_cast<char*>(file->memory());
2682   Local<String> result;
2683   if (i::FLAG_use_external_strings && i::String::IsAscii(chars, size)) {
2684     String::ExternalOneByteStringResource* resource =
2685         new ExternalOwningOneByteStringResource(std::move(file));
2686     result = String::NewExternalOneByte(isolate, resource).ToLocalChecked();
2687   } else {
2688     result = String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
2689                  .ToLocalChecked();
2690   }
2691   return result;
2692 }
2693 
RunShell(Isolate * isolate)2694 void Shell::RunShell(Isolate* isolate) {
2695   HandleScope outer_scope(isolate);
2696   v8::Local<v8::Context> context =
2697       v8::Local<v8::Context>::New(isolate, evaluation_context_);
2698   v8::Context::Scope context_scope(context);
2699   PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2700   Local<String> name = String::NewFromUtf8Literal(isolate, "(d8)");
2701   printf("V8 version %s\n", V8::GetVersion());
2702   while (true) {
2703     HandleScope inner_scope(isolate);
2704     printf("d8> ");
2705     Local<String> input = Shell::ReadFromStdin(isolate);
2706     if (input.IsEmpty()) break;
2707     ExecuteString(isolate, input, name, kPrintResult, kReportExceptions,
2708                   kProcessMessageQueue);
2709   }
2710   printf("\n");
2711   // We need to explicitly clean up the module embedder data for
2712   // the interative shell context.
2713   DisposeModuleEmbedderData(context);
2714 }
2715 
2716 class InspectorFrontend final : public v8_inspector::V8Inspector::Channel {
2717  public:
InspectorFrontend(Local<Context> context)2718   explicit InspectorFrontend(Local<Context> context) {
2719     isolate_ = context->GetIsolate();
2720     context_.Reset(isolate_, context);
2721   }
2722   ~InspectorFrontend() override = default;
2723 
2724  private:
sendResponse(int callId,std::unique_ptr<v8_inspector::StringBuffer> message)2725   void sendResponse(
2726       int callId,
2727       std::unique_ptr<v8_inspector::StringBuffer> message) override {
2728     Send(message->string());
2729   }
sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message)2730   void sendNotification(
2731       std::unique_ptr<v8_inspector::StringBuffer> message) override {
2732     Send(message->string());
2733   }
flushProtocolNotifications()2734   void flushProtocolNotifications() override {}
2735 
Send(const v8_inspector::StringView & string)2736   void Send(const v8_inspector::StringView& string) {
2737     v8::Isolate::AllowJavascriptExecutionScope allow_script(isolate_);
2738     v8::HandleScope handle_scope(isolate_);
2739     int length = static_cast<int>(string.length());
2740     DCHECK_LT(length, v8::String::kMaxLength);
2741     Local<String> message =
2742         (string.is8Bit()
2743              ? v8::String::NewFromOneByte(
2744                    isolate_,
2745                    reinterpret_cast<const uint8_t*>(string.characters8()),
2746                    v8::NewStringType::kNormal, length)
2747              : v8::String::NewFromTwoByte(
2748                    isolate_,
2749                    reinterpret_cast<const uint16_t*>(string.characters16()),
2750                    v8::NewStringType::kNormal, length))
2751             .ToLocalChecked();
2752     Local<String> callback_name = v8::String::NewFromUtf8Literal(
2753         isolate_, "receive", NewStringType::kInternalized);
2754     Local<Context> context = context_.Get(isolate_);
2755     Local<Value> callback =
2756         context->Global()->Get(context, callback_name).ToLocalChecked();
2757     if (callback->IsFunction()) {
2758       v8::TryCatch try_catch(isolate_);
2759       Local<Value> args[] = {message};
2760       USE(Local<Function>::Cast(callback)->Call(context, Undefined(isolate_), 1,
2761                                                 args));
2762 #ifdef DEBUG
2763       if (try_catch.HasCaught()) {
2764         Local<Object> exception = Local<Object>::Cast(try_catch.Exception());
2765         Local<String> key = v8::String::NewFromUtf8Literal(
2766             isolate_, "message", NewStringType::kInternalized);
2767         Local<String> expected = v8::String::NewFromUtf8Literal(
2768             isolate_, "Maximum call stack size exceeded");
2769         Local<Value> value = exception->Get(context, key).ToLocalChecked();
2770         DCHECK(value->StrictEquals(expected));
2771       }
2772 #endif
2773     }
2774   }
2775 
2776   Isolate* isolate_;
2777   Global<Context> context_;
2778 };
2779 
2780 class InspectorClient : public v8_inspector::V8InspectorClient {
2781  public:
InspectorClient(Local<Context> context,bool connect)2782   InspectorClient(Local<Context> context, bool connect) {
2783     if (!connect) return;
2784     isolate_ = context->GetIsolate();
2785     channel_.reset(new InspectorFrontend(context));
2786     inspector_ = v8_inspector::V8Inspector::create(isolate_, this);
2787     session_ =
2788         inspector_->connect(1, channel_.get(), v8_inspector::StringView());
2789     context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
2790     inspector_->contextCreated(v8_inspector::V8ContextInfo(
2791         context, kContextGroupId, v8_inspector::StringView()));
2792 
2793     Local<Value> function =
2794         FunctionTemplate::New(isolate_, SendInspectorMessage)
2795             ->GetFunction(context)
2796             .ToLocalChecked();
2797     Local<String> function_name = String::NewFromUtf8Literal(
2798         isolate_, "send", NewStringType::kInternalized);
2799     CHECK(context->Global()->Set(context, function_name, function).FromJust());
2800 
2801     context_.Reset(isolate_, context);
2802   }
2803 
runMessageLoopOnPause(int contextGroupId)2804   void runMessageLoopOnPause(int contextGroupId) override {
2805     v8::Isolate::AllowJavascriptExecutionScope allow_script(isolate_);
2806     v8::HandleScope handle_scope(isolate_);
2807     Local<String> callback_name = v8::String::NewFromUtf8Literal(
2808         isolate_, "handleInspectorMessage", NewStringType::kInternalized);
2809     Local<Context> context = context_.Get(isolate_);
2810     Local<Value> callback =
2811         context->Global()->Get(context, callback_name).ToLocalChecked();
2812     if (!callback->IsFunction()) return;
2813 
2814     v8::TryCatch try_catch(isolate_);
2815     try_catch.SetVerbose(true);
2816     is_paused = true;
2817 
2818     while (is_paused) {
2819       USE(Local<Function>::Cast(callback)->Call(context, Undefined(isolate_), 0,
2820                                                 {}));
2821       if (try_catch.HasCaught()) {
2822         is_paused = false;
2823       }
2824     }
2825   }
2826 
quitMessageLoopOnPause()2827   void quitMessageLoopOnPause() override { is_paused = false; }
2828 
2829  private:
GetSession(Local<Context> context)2830   static v8_inspector::V8InspectorSession* GetSession(Local<Context> context) {
2831     InspectorClient* inspector_client = static_cast<InspectorClient*>(
2832         context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex));
2833     return inspector_client->session_.get();
2834   }
2835 
ensureDefaultContextInGroup(int group_id)2836   Local<Context> ensureDefaultContextInGroup(int group_id) override {
2837     DCHECK(isolate_);
2838     DCHECK_EQ(kContextGroupId, group_id);
2839     return context_.Get(isolate_);
2840   }
2841 
SendInspectorMessage(const v8::FunctionCallbackInfo<v8::Value> & args)2842   static void SendInspectorMessage(
2843       const v8::FunctionCallbackInfo<v8::Value>& args) {
2844     Isolate* isolate = args.GetIsolate();
2845     v8::HandleScope handle_scope(isolate);
2846     Local<Context> context = isolate->GetCurrentContext();
2847     args.GetReturnValue().Set(Undefined(isolate));
2848     Local<String> message = args[0]->ToString(context).ToLocalChecked();
2849     v8_inspector::V8InspectorSession* session =
2850         InspectorClient::GetSession(context);
2851     int length = message->Length();
2852     std::unique_ptr<uint16_t[]> buffer(new uint16_t[length]);
2853     message->Write(isolate, buffer.get(), 0, length);
2854     v8_inspector::StringView message_view(buffer.get(), length);
2855     {
2856       v8::SealHandleScope seal_handle_scope(isolate);
2857       session->dispatchProtocolMessage(message_view);
2858     }
2859     args.GetReturnValue().Set(True(isolate));
2860   }
2861 
2862   static const int kContextGroupId = 1;
2863 
2864   std::unique_ptr<v8_inspector::V8Inspector> inspector_;
2865   std::unique_ptr<v8_inspector::V8InspectorSession> session_;
2866   std::unique_ptr<v8_inspector::V8Inspector::Channel> channel_;
2867   bool is_paused = false;
2868   Global<Context> context_;
2869   Isolate* isolate_;
2870 };
2871 
~SourceGroup()2872 SourceGroup::~SourceGroup() {
2873   delete thread_;
2874   thread_ = nullptr;
2875 }
2876 
ends_with(const char * input,const char * suffix)2877 bool ends_with(const char* input, const char* suffix) {
2878   size_t input_length = strlen(input);
2879   size_t suffix_length = strlen(suffix);
2880   if (suffix_length <= input_length) {
2881     return strcmp(input + input_length - suffix_length, suffix) == 0;
2882   }
2883   return false;
2884 }
2885 
Execute(Isolate * isolate)2886 bool SourceGroup::Execute(Isolate* isolate) {
2887   bool success = true;
2888 #ifdef V8_FUZZILLI
2889   if (fuzzilli_reprl) {
2890     HandleScope handle_scope(isolate);
2891     Local<String> file_name =
2892         String::NewFromUtf8(isolate, "fuzzcode.js", NewStringType::kNormal)
2893             .ToLocalChecked();
2894 
2895     size_t script_size;
2896     CHECK_EQ(read(REPRL_CRFD, &script_size, 8), 8);
2897     char* buffer = new char[script_size + 1];
2898     char* ptr = buffer;
2899     size_t remaining = script_size;
2900     while (remaining > 0) {
2901       ssize_t rv = read(REPRL_DRFD, ptr, remaining);
2902       CHECK_GE(rv, 0);
2903       remaining -= rv;
2904       ptr += rv;
2905     }
2906     buffer[script_size] = 0;
2907 
2908     Local<String> source =
2909         String::NewFromUtf8(isolate, buffer, NewStringType::kNormal)
2910             .ToLocalChecked();
2911     delete[] buffer;
2912     Shell::set_script_executed();
2913     if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
2914                               Shell::kReportExceptions,
2915                               Shell::kNoProcessMessageQueue)) {
2916       return false;
2917     }
2918   }
2919 #endif  // V8_FUZZILLI
2920   for (int i = begin_offset_; i < end_offset_; ++i) {
2921     const char* arg = argv_[i];
2922     if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
2923       // Execute argument given to -e option directly.
2924       HandleScope handle_scope(isolate);
2925       Local<String> file_name = String::NewFromUtf8Literal(isolate, "unnamed");
2926       Local<String> source =
2927           String::NewFromUtf8(isolate, argv_[i + 1]).ToLocalChecked();
2928       Shell::set_script_executed();
2929       if (!Shell::ExecuteString(isolate, source, file_name,
2930                                 Shell::kNoPrintResult, Shell::kReportExceptions,
2931                                 Shell::kNoProcessMessageQueue)) {
2932         success = false;
2933         break;
2934       }
2935       ++i;
2936       continue;
2937     } else if (ends_with(arg, ".mjs")) {
2938       Shell::set_script_executed();
2939       if (!Shell::ExecuteModule(isolate, arg)) {
2940         success = false;
2941         break;
2942       }
2943       continue;
2944     } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) {
2945       // Treat the next file as a module.
2946       arg = argv_[++i];
2947       Shell::set_script_executed();
2948       if (!Shell::ExecuteModule(isolate, arg)) {
2949         success = false;
2950         break;
2951       }
2952       continue;
2953     } else if (arg[0] == '-') {
2954       // Ignore other options. They have been parsed already.
2955       continue;
2956     }
2957 
2958     // Use all other arguments as names of files to load and run.
2959     HandleScope handle_scope(isolate);
2960     Local<String> file_name =
2961         String::NewFromUtf8(isolate, arg).ToLocalChecked();
2962     Local<String> source = ReadFile(isolate, arg);
2963     if (source.IsEmpty()) {
2964       printf("Error reading '%s'\n", arg);
2965       base::OS::ExitProcess(1);
2966     }
2967     Shell::set_script_executed();
2968     if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
2969                               Shell::kReportExceptions,
2970                               Shell::kProcessMessageQueue)) {
2971       success = false;
2972       break;
2973     }
2974   }
2975   return success;
2976 }
2977 
ReadFile(Isolate * isolate,const char * name)2978 Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
2979   return Shell::ReadFile(isolate, name);
2980 }
2981 
IsolateThread(SourceGroup * group)2982 SourceGroup::IsolateThread::IsolateThread(SourceGroup* group)
2983     : base::Thread(GetThreadOptions("IsolateThread")), group_(group) {}
2984 
ExecuteInThread()2985 void SourceGroup::ExecuteInThread() {
2986   Isolate::CreateParams create_params;
2987   create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2988   Isolate* isolate = Isolate::New(create_params);
2989   Shell::SetWaitUntilDone(isolate, false);
2990   D8Console console(isolate);
2991   Shell::Initialize(isolate, &console, false);
2992 
2993   for (int i = 0; i < Shell::options.stress_runs; ++i) {
2994     next_semaphore_.Wait();
2995     {
2996       Isolate::Scope iscope(isolate);
2997       PerIsolateData data(isolate);
2998       {
2999         HandleScope scope(isolate);
3000         Local<Context> context = Shell::CreateEvaluationContext(isolate);
3001         {
3002           Context::Scope cscope(context);
3003           InspectorClient inspector_client(context,
3004                                            Shell::options.enable_inspector);
3005           PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
3006           Execute(isolate);
3007           Shell::CompleteMessageLoop(isolate);
3008         }
3009         DisposeModuleEmbedderData(context);
3010       }
3011       Shell::CollectGarbage(isolate);
3012     }
3013     done_semaphore_.Signal();
3014   }
3015 
3016   isolate->Dispose();
3017 }
3018 
StartExecuteInThread()3019 void SourceGroup::StartExecuteInThread() {
3020   if (thread_ == nullptr) {
3021     thread_ = new IsolateThread(this);
3022     CHECK(thread_->Start());
3023   }
3024   next_semaphore_.Signal();
3025 }
3026 
WaitForThread()3027 void SourceGroup::WaitForThread() {
3028   if (thread_ == nullptr) return;
3029   done_semaphore_.Wait();
3030 }
3031 
JoinThread()3032 void SourceGroup::JoinThread() {
3033   if (thread_ == nullptr) return;
3034   thread_->Join();
3035 }
3036 
Enqueue(std::unique_ptr<SerializationData> data)3037 void SerializationDataQueue::Enqueue(std::unique_ptr<SerializationData> data) {
3038   base::MutexGuard lock_guard(&mutex_);
3039   data_.push_back(std::move(data));
3040 }
3041 
Dequeue(std::unique_ptr<SerializationData> * out_data)3042 bool SerializationDataQueue::Dequeue(
3043     std::unique_ptr<SerializationData>* out_data) {
3044   out_data->reset();
3045   base::MutexGuard lock_guard(&mutex_);
3046   if (data_.empty()) return false;
3047   *out_data = std::move(data_[0]);
3048   data_.erase(data_.begin());
3049   return true;
3050 }
3051 
IsEmpty()3052 bool SerializationDataQueue::IsEmpty() {
3053   base::MutexGuard lock_guard(&mutex_);
3054   return data_.empty();
3055 }
3056 
Clear()3057 void SerializationDataQueue::Clear() {
3058   base::MutexGuard lock_guard(&mutex_);
3059   data_.clear();
3060 }
3061 
Worker(const char * script)3062 Worker::Worker(const char* script) : script_(i::StrDup(script)) {
3063   running_.store(false);
3064 }
3065 
~Worker()3066 Worker::~Worker() {
3067   DCHECK_NULL(isolate_);
3068 
3069   delete thread_;
3070   thread_ = nullptr;
3071   delete[] script_;
3072   script_ = nullptr;
3073 }
3074 
StartWorkerThread(std::shared_ptr<Worker> worker)3075 bool Worker::StartWorkerThread(std::shared_ptr<Worker> worker) {
3076   worker->running_.store(true);
3077   auto thread = new WorkerThread(worker);
3078   worker->thread_ = thread;
3079   if (thread->Start()) {
3080     // Wait until the worker is ready to receive messages.
3081     worker->started_semaphore_.Wait();
3082     Shell::AddRunningWorker(std::move(worker));
3083     return true;
3084   }
3085   return false;
3086 }
3087 
Run()3088 void Worker::WorkerThread::Run() {
3089   // Prevent a lifetime cycle from Worker -> WorkerThread -> Worker.
3090   // We must clear the worker_ field of the thread, but we keep the
3091   // worker alive via a stack root until the thread finishes execution
3092   // and removes itself from the running set. Thereafter the only
3093   // remaining reference can be from a JavaScript object via a Managed.
3094   auto worker = std::move(worker_);
3095   worker_ = nullptr;
3096   worker->ExecuteInThread();
3097   Shell::RemoveRunningWorker(worker);
3098 }
3099 
3100 class ProcessMessageTask : public i::CancelableTask {
3101  public:
ProcessMessageTask(i::CancelableTaskManager * task_manager,std::shared_ptr<Worker> worker,std::unique_ptr<SerializationData> data)3102   ProcessMessageTask(i::CancelableTaskManager* task_manager,
3103                      std::shared_ptr<Worker> worker,
3104                      std::unique_ptr<SerializationData> data)
3105       : i::CancelableTask(task_manager),
3106         worker_(worker),
3107         data_(std::move(data)) {}
3108 
RunInternal()3109   void RunInternal() override { worker_->ProcessMessage(std::move(data_)); }
3110 
3111  private:
3112   std::shared_ptr<Worker> worker_;
3113   std::unique_ptr<SerializationData> data_;
3114 };
3115 
PostMessage(std::unique_ptr<SerializationData> data)3116 void Worker::PostMessage(std::unique_ptr<SerializationData> data) {
3117   // Hold the worker_mutex_ so that the worker thread can't delete task_runner_
3118   // after we've checked running_.
3119   base::MutexGuard lock_guard(&worker_mutex_);
3120   if (!running_.load()) {
3121     return;
3122   }
3123   std::unique_ptr<v8::Task> task(new ProcessMessageTask(
3124       task_manager_, shared_from_this(), std::move(data)));
3125   task_runner_->PostNonNestableTask(std::move(task));
3126 }
3127 
3128 class TerminateTask : public i::CancelableTask {
3129  public:
TerminateTask(i::CancelableTaskManager * task_manager,std::shared_ptr<Worker> worker)3130   TerminateTask(i::CancelableTaskManager* task_manager,
3131                 std::shared_ptr<Worker> worker)
3132       : i::CancelableTask(task_manager), worker_(worker) {}
3133 
RunInternal()3134   void RunInternal() override {
3135     // Make sure the worker doesn't enter the task loop after processing this
3136     // task.
3137     worker_->running_.store(false);
3138   }
3139 
3140  private:
3141   std::shared_ptr<Worker> worker_;
3142 };
3143 
GetMessage()3144 std::unique_ptr<SerializationData> Worker::GetMessage() {
3145   std::unique_ptr<SerializationData> result;
3146   while (!out_queue_.Dequeue(&result)) {
3147     // If the worker is no longer running, and there are no messages in the
3148     // queue, don't expect any more messages from it.
3149     if (!running_.load()) {
3150       break;
3151     }
3152     out_semaphore_.Wait();
3153   }
3154   return result;
3155 }
3156 
Terminate()3157 void Worker::Terminate() {
3158   // Hold the worker_mutex_ so that the worker thread can't delete task_runner_
3159   // after we've checked running_.
3160   base::MutexGuard lock_guard(&worker_mutex_);
3161   if (!running_.load()) {
3162     return;
3163   }
3164   // Post a task to wake up the worker thread.
3165   std::unique_ptr<v8::Task> task(
3166       new TerminateTask(task_manager_, shared_from_this()));
3167   task_runner_->PostTask(std::move(task));
3168 }
3169 
TerminateAndWaitForThread()3170 void Worker::TerminateAndWaitForThread() {
3171   Terminate();
3172   thread_->Join();
3173 }
3174 
ProcessMessage(std::unique_ptr<SerializationData> data)3175 void Worker::ProcessMessage(std::unique_ptr<SerializationData> data) {
3176   if (!running_.load()) {
3177     return;
3178   }
3179 
3180   DCHECK_NOT_NULL(isolate_);
3181   HandleScope scope(isolate_);
3182   Local<Context> context = context_.Get(isolate_);
3183   Context::Scope cscope(context);
3184   Local<Object> global = context->Global();
3185 
3186   // Get the message handler.
3187   Local<Value> onmessage = global
3188                                ->Get(context, String::NewFromUtf8Literal(
3189                                                   isolate_, "onmessage",
3190                                                   NewStringType::kInternalized))
3191                                .ToLocalChecked();
3192   if (!onmessage->IsFunction()) {
3193     return;
3194   }
3195   Local<Function> onmessage_fun = Local<Function>::Cast(onmessage);
3196 
3197   v8::TryCatch try_catch(isolate_);
3198   try_catch.SetVerbose(true);
3199   Local<Value> value;
3200   if (Shell::DeserializeValue(isolate_, std::move(data)).ToLocal(&value)) {
3201     Local<Value> argv[] = {value};
3202     MaybeLocal<Value> result = onmessage_fun->Call(context, global, 1, argv);
3203     USE(result);
3204   }
3205 }
3206 
ProcessMessages()3207 void Worker::ProcessMessages() {
3208   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate_);
3209   i::SaveAndSwitchContext saved_context(i_isolate, i::Context());
3210   SealHandleScope shs(isolate_);
3211   while (running_.load() && v8::platform::PumpMessageLoop(
3212                                 g_default_platform, isolate_,
3213                                 platform::MessageLoopBehavior::kWaitForWork)) {
3214     if (running_.load()) {
3215       MicrotasksScope::PerformCheckpoint(isolate_);
3216     }
3217   }
3218 }
3219 
ExecuteInThread()3220 void Worker::ExecuteInThread() {
3221   Isolate::CreateParams create_params;
3222   create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3223   isolate_ = Isolate::New(create_params);
3224   {
3225     base::MutexGuard lock_guard(&worker_mutex_);
3226     task_runner_ = g_default_platform->GetForegroundTaskRunner(isolate_);
3227     task_manager_ =
3228         reinterpret_cast<i::Isolate*>(isolate_)->cancelable_task_manager();
3229   }
3230   // The Worker is now ready to receive messages.
3231   started_semaphore_.Signal();
3232 
3233   D8Console console(isolate_);
3234   Shell::Initialize(isolate_, &console, false);
3235   {
3236     Isolate::Scope iscope(isolate_);
3237     {
3238       HandleScope scope(isolate_);
3239       PerIsolateData data(isolate_);
3240       Local<Context> context = Shell::CreateEvaluationContext(isolate_);
3241       context_.Reset(isolate_, context);
3242       {
3243         Context::Scope cscope(context);
3244         PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate_));
3245 
3246         Local<Object> global = context->Global();
3247         Local<Value> this_value = External::New(isolate_, this);
3248         Local<FunctionTemplate> postmessage_fun_template =
3249             FunctionTemplate::New(isolate_, PostMessageOut, this_value);
3250 
3251         Local<Function> postmessage_fun;
3252         if (postmessage_fun_template->GetFunction(context).ToLocal(
3253                 &postmessage_fun)) {
3254           global
3255               ->Set(context,
3256                     v8::String::NewFromUtf8Literal(
3257                         isolate_, "postMessage", NewStringType::kInternalized),
3258                     postmessage_fun)
3259               .FromJust();
3260         }
3261 
3262         // First run the script
3263         Local<String> file_name =
3264             String::NewFromUtf8Literal(isolate_, "unnamed");
3265         Local<String> source =
3266             String::NewFromUtf8(isolate_, script_).ToLocalChecked();
3267         if (Shell::ExecuteString(
3268                 isolate_, source, file_name, Shell::kNoPrintResult,
3269                 Shell::kReportExceptions, Shell::kProcessMessageQueue)) {
3270           // Check that there's a message handler
3271           Local<Value> onmessage =
3272               global
3273                   ->Get(context, String::NewFromUtf8Literal(
3274                                      isolate_, "onmessage",
3275                                      NewStringType::kInternalized))
3276                   .ToLocalChecked();
3277           if (onmessage->IsFunction()) {
3278             // Now wait for messages
3279             ProcessMessages();
3280           }
3281         }
3282       }
3283       DisposeModuleEmbedderData(context);
3284     }
3285     Shell::CollectGarbage(isolate_);
3286   }
3287   // TODO(cbruni): Check for unhandled promises here.
3288   {
3289     // Hold the mutex to ensure running_ and task_runner_ change state
3290     // atomically (see Worker::PostMessage which reads them).
3291     base::MutexGuard lock_guard(&worker_mutex_);
3292     running_.store(false);
3293     task_runner_.reset();
3294     task_manager_ = nullptr;
3295   }
3296   context_.Reset();
3297   platform::NotifyIsolateShutdown(g_default_platform, isolate_);
3298   isolate_->Dispose();
3299   isolate_ = nullptr;
3300 
3301   // Post nullptr to wake the thread waiting on GetMessage() if there is one.
3302   out_queue_.Enqueue(nullptr);
3303   out_semaphore_.Signal();
3304 }
3305 
PostMessageOut(const v8::FunctionCallbackInfo<v8::Value> & args)3306 void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) {
3307   Isolate* isolate = args.GetIsolate();
3308   HandleScope handle_scope(isolate);
3309 
3310   if (args.Length() < 1) {
3311     Throw(isolate, "Invalid argument");
3312     return;
3313   }
3314 
3315   Local<Value> message = args[0];
3316   Local<Value> transfer = Undefined(isolate);
3317   std::unique_ptr<SerializationData> data =
3318       Shell::SerializeValue(isolate, message, transfer);
3319   if (data) {
3320     DCHECK(args.Data()->IsExternal());
3321     Local<External> this_value = Local<External>::Cast(args.Data());
3322     Worker* worker = static_cast<Worker*>(this_value->Value());
3323     worker->out_queue_.Enqueue(std::move(data));
3324     worker->out_semaphore_.Signal();
3325   }
3326 }
3327 
SetOptions(int argc,char * argv[])3328 bool Shell::SetOptions(int argc, char* argv[]) {
3329   bool logfile_per_isolate = false;
3330   bool no_always_opt = false;
3331   for (int i = 0; i < argc; i++) {
3332     if (strcmp(argv[i], "--") == 0) {
3333       argv[i] = nullptr;
3334       for (int j = i + 1; j < argc; j++) {
3335         options.arguments.push_back(argv[j]);
3336         argv[j] = nullptr;
3337       }
3338       break;
3339     } else if (strcmp(argv[i], "--no-arguments") == 0) {
3340       options.include_arguments = false;
3341       argv[i] = nullptr;
3342     } else if (strcmp(argv[i], "--stress-opt") == 0) {
3343       options.stress_opt = true;
3344       argv[i] = nullptr;
3345     } else if (strcmp(argv[i], "--nostress-opt") == 0 ||
3346                strcmp(argv[i], "--no-stress-opt") == 0) {
3347       options.stress_opt = false;
3348       argv[i] = nullptr;
3349     } else if (strcmp(argv[i], "--stress-snapshot") == 0) {
3350       options.stress_snapshot = true;
3351       // Incremental marking is incompatible with the stress_snapshot mode;
3352       // specifically, serialization may clear bytecode arrays from shared
3353       // function infos which the MarkCompactCollector (running concurrently)
3354       // may still need. See also https://crbug.com/v8/10882.
3355       //
3356       // We thus force the implication
3357       //
3358       //   --stress-snapshot ~~> --no-incremental-marking
3359       //
3360       // Note: This is not an issue in production because we don't clear SFI's
3361       // there (that only happens in mksnapshot and in --stress-snapshot mode).
3362       i::FLAG_incremental_marking = false;
3363       argv[i] = nullptr;
3364     } else if (strcmp(argv[i], "--nostress-snapshot") == 0 ||
3365                strcmp(argv[i], "--no-stress-snapshot") == 0) {
3366       options.stress_snapshot = false;
3367       argv[i] = nullptr;
3368     } else if (strcmp(argv[i], "--noalways-opt") == 0 ||
3369                strcmp(argv[i], "--no-always-opt") == 0) {
3370       no_always_opt = true;
3371     } else if (strcmp(argv[i], "--fuzzing") == 0 ||
3372                strcmp(argv[i], "--no-abort-on-contradictory-flags") == 0 ||
3373                strcmp(argv[i], "--noabort-on-contradictory-flags") == 0) {
3374       check_d8_flag_contradictions = false;
3375     } else if (strcmp(argv[i], "--abort-on-contradictory-flags") == 0) {
3376       check_d8_flag_contradictions = true;
3377     } else if (strcmp(argv[i], "--logfile-per-isolate") == 0) {
3378       logfile_per_isolate = true;
3379       argv[i] = nullptr;
3380     } else if (strcmp(argv[i], "--shell") == 0) {
3381       options.interactive_shell = true;
3382       argv[i] = nullptr;
3383     } else if (strcmp(argv[i], "--test") == 0) {
3384       options.test_shell = true;
3385       argv[i] = nullptr;
3386     } else if (strcmp(argv[i], "--notest") == 0 ||
3387                strcmp(argv[i], "--no-test") == 0) {
3388       options.test_shell = false;
3389       argv[i] = nullptr;
3390     } else if (strcmp(argv[i], "--send-idle-notification") == 0) {
3391       options.send_idle_notification = true;
3392       argv[i] = nullptr;
3393     } else if (strcmp(argv[i], "--invoke-weak-callbacks") == 0) {
3394       options.invoke_weak_callbacks = true;
3395       // TODO(jochen) See issue 3351
3396       options.send_idle_notification = true;
3397       argv[i] = nullptr;
3398     } else if (strcmp(argv[i], "--omit-quit") == 0) {
3399       options.omit_quit = true;
3400       argv[i] = nullptr;
3401     } else if (strcmp(argv[i], "--no-wait-for-background-tasks") == 0) {
3402       // TODO(herhut) Remove this flag once wasm compilation is fully
3403       // isolate-independent.
3404       options.wait_for_background_tasks = false;
3405       argv[i] = nullptr;
3406     } else if (strcmp(argv[i], "-f") == 0) {
3407       // Ignore any -f flags for compatibility with other stand-alone
3408       // JavaScript engines.
3409       continue;
3410     } else if (strcmp(argv[i], "--ignore-unhandled-promises") == 0) {
3411       options.ignore_unhandled_promises = true;
3412       argv[i] = nullptr;
3413     } else if (strcmp(argv[i], "--isolate") == 0) {
3414       options.num_isolates++;
3415     } else if (strcmp(argv[i], "--throws") == 0) {
3416       options.expected_to_throw = true;
3417       argv[i] = nullptr;
3418     } else if (strncmp(argv[i], "--icu-data-file=", 16) == 0) {
3419       options.icu_data_file = argv[i] + 16;
3420       argv[i] = nullptr;
3421     } else if (strncmp(argv[i], "--icu-locale=", 13) == 0) {
3422       options.icu_locale = argv[i] + 13;
3423       argv[i] = nullptr;
3424 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
3425     } else if (strncmp(argv[i], "--snapshot_blob=", 16) == 0) {
3426       options.snapshot_blob = argv[i] + 16;
3427       argv[i] = nullptr;
3428 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
3429     } else if (strcmp(argv[i], "--cache") == 0 ||
3430                strncmp(argv[i], "--cache=", 8) == 0) {
3431       const char* value = argv[i] + 7;
3432       if (!*value || strncmp(value, "=code", 6) == 0) {
3433         options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
3434         options.code_cache_options =
3435             ShellOptions::CodeCacheOptions::kProduceCache;
3436       } else if (strncmp(value, "=none", 6) == 0) {
3437         options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
3438         options.code_cache_options =
3439             ShellOptions::CodeCacheOptions::kNoProduceCache;
3440       } else if (strncmp(value, "=after-execute", 15) == 0) {
3441         options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
3442         options.code_cache_options =
3443             ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute;
3444       } else if (strncmp(value, "=full-code-cache", 17) == 0) {
3445         options.compile_options = v8::ScriptCompiler::kEagerCompile;
3446         options.code_cache_options =
3447             ShellOptions::CodeCacheOptions::kProduceCache;
3448       } else {
3449         printf("Unknown option to --cache.\n");
3450         return false;
3451       }
3452       argv[i] = nullptr;
3453     } else if (strcmp(argv[i], "--streaming-compile") == 0) {
3454       options.streaming_compile = true;
3455       argv[i] = nullptr;
3456     } else if ((strcmp(argv[i], "--no-streaming-compile") == 0) ||
3457                (strcmp(argv[i], "--nostreaming-compile") == 0)) {
3458       options.streaming_compile = false;
3459       argv[i] = nullptr;
3460     } else if (strcmp(argv[i], "--enable-tracing") == 0) {
3461       options.trace_enabled = true;
3462       argv[i] = nullptr;
3463     } else if (strncmp(argv[i], "--trace-path=", 13) == 0) {
3464       options.trace_path = argv[i] + 13;
3465       argv[i] = nullptr;
3466     } else if (strncmp(argv[i], "--trace-config=", 15) == 0) {
3467       options.trace_config = argv[i] + 15;
3468       argv[i] = nullptr;
3469     } else if (strcmp(argv[i], "--enable-inspector") == 0) {
3470       options.enable_inspector = true;
3471       argv[i] = nullptr;
3472     } else if (strncmp(argv[i], "--lcov=", 7) == 0) {
3473       options.lcov_file = argv[i] + 7;
3474       argv[i] = nullptr;
3475     } else if (strcmp(argv[i], "--disable-in-process-stack-traces") == 0) {
3476       options.disable_in_process_stack_traces = true;
3477       argv[i] = nullptr;
3478 #ifdef V8_OS_POSIX
3479     } else if (strncmp(argv[i], "--read-from-tcp-port=", 21) == 0) {
3480       options.read_from_tcp_port = atoi(argv[i] + 21);
3481       argv[i] = nullptr;
3482 #endif  // V8_OS_POSIX
3483     } else if (strcmp(argv[i], "--enable-os-system") == 0) {
3484       options.enable_os_system = true;
3485       argv[i] = nullptr;
3486     } else if (strcmp(argv[i], "--quiet-load") == 0) {
3487       options.quiet_load = true;
3488       argv[i] = nullptr;
3489     } else if (strncmp(argv[i], "--thread-pool-size=", 19) == 0) {
3490       options.thread_pool_size = atoi(argv[i] + 19);
3491       argv[i] = nullptr;
3492     } else if (strcmp(argv[i], "--stress-delay-tasks") == 0) {
3493       // Delay execution of tasks by 0-100ms randomly (based on --random-seed).
3494       options.stress_delay_tasks = true;
3495       argv[i] = nullptr;
3496     } else if (strcmp(argv[i], "--cpu-profiler") == 0) {
3497       options.cpu_profiler = true;
3498       argv[i] = nullptr;
3499     } else if (strcmp(argv[i], "--cpu-profiler-print") == 0) {
3500       options.cpu_profiler = true;
3501       options.cpu_profiler_print = true;
3502       argv[i] = nullptr;
3503 #ifdef V8_FUZZILLI
3504     } else if (strcmp(argv[i], "--no-fuzzilli-enable-builtins-coverage") == 0) {
3505       options.fuzzilli_enable_builtins_coverage = false;
3506       argv[i] = nullptr;
3507     } else if (strcmp(argv[i], "--fuzzilli-coverage-statistics") == 0) {
3508       options.fuzzilli_coverage_statistics = true;
3509       argv[i] = nullptr;
3510 #endif
3511     } else if (strcmp(argv[i], "--fuzzy-module-file-extensions") == 0) {
3512       options.fuzzy_module_file_extensions = true;
3513       argv[i] = nullptr;
3514     }
3515   }
3516 
3517   if (options.stress_opt && no_always_opt && check_d8_flag_contradictions) {
3518     FATAL("Flag --no-always-opt is incompatible with --stress-opt.");
3519   }
3520 
3521   const char* usage =
3522       "Synopsis:\n"
3523       "  shell [options] [--shell] [<file>...]\n"
3524       "  d8 [options] [-e <string>] [--shell] [[--module] <file>...]\n\n"
3525       "  -e        execute a string in V8\n"
3526       "  --shell   run an interactive JavaScript shell\n"
3527       "  --module  execute a file as a JavaScript module\n\n";
3528   using HelpOptions = i::FlagList::HelpOptions;
3529   i::FLAG_abort_on_contradictory_flags = true;
3530   i::FlagList::SetFlagsFromCommandLine(&argc, argv, true,
3531                                        HelpOptions(HelpOptions::kExit, usage));
3532   options.mock_arraybuffer_allocator = i::FLAG_mock_arraybuffer_allocator;
3533   options.mock_arraybuffer_allocator_limit =
3534       i::FLAG_mock_arraybuffer_allocator_limit;
3535 #if V8_OS_LINUX
3536   options.multi_mapped_mock_allocator = i::FLAG_multi_mapped_mock_allocator;
3537 #endif
3538 
3539   // Set up isolated source groups.
3540   options.isolate_sources = new SourceGroup[options.num_isolates];
3541   SourceGroup* current = options.isolate_sources;
3542   current->Begin(argv, 1);
3543   for (int i = 1; i < argc; i++) {
3544     const char* str = argv[i];
3545     if (strcmp(str, "--isolate") == 0) {
3546       current->End(i);
3547       current++;
3548       current->Begin(argv, i + 1);
3549     } else if (strcmp(str, "--module") == 0) {
3550       // Pass on to SourceGroup, which understands this option.
3551     } else if (strncmp(str, "--", 2) == 0) {
3552       printf("Warning: unknown flag %s.\nTry --help for options\n", str);
3553     } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
3554       set_script_executed();
3555     } else if (strncmp(str, "-", 1) != 0) {
3556       // Not a flag, so it must be a script to execute.
3557       set_script_executed();
3558     }
3559   }
3560   current->End(argc);
3561 
3562   if (!logfile_per_isolate && options.num_isolates) {
3563     V8::SetFlagsFromString("--no-logfile-per-isolate");
3564   }
3565 
3566   return true;
3567 }
3568 
RunMain(Isolate * isolate,bool last_run)3569 int Shell::RunMain(Isolate* isolate, bool last_run) {
3570   for (int i = 1; i < options.num_isolates; ++i) {
3571     options.isolate_sources[i].StartExecuteInThread();
3572   }
3573   bool success = true;
3574   {
3575     SetWaitUntilDone(isolate, false);
3576     if (options.lcov_file) {
3577       debug::Coverage::SelectMode(isolate, debug::CoverageMode::kBlockCount);
3578     }
3579     HandleScope scope(isolate);
3580     Local<Context> context = CreateEvaluationContext(isolate);
3581     bool use_existing_context = last_run && use_interactive_shell();
3582     if (use_existing_context) {
3583       // Keep using the same context in the interactive shell.
3584       evaluation_context_.Reset(isolate, context);
3585     }
3586     {
3587       Context::Scope cscope(context);
3588       InspectorClient inspector_client(context, options.enable_inspector);
3589       PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
3590       if (!options.isolate_sources[0].Execute(isolate)) success = false;
3591       if (!CompleteMessageLoop(isolate)) success = false;
3592     }
3593     if (!use_existing_context) {
3594       DisposeModuleEmbedderData(context);
3595     }
3596     WriteLcovData(isolate, options.lcov_file);
3597     if (last_run && options.stress_snapshot) {
3598       static constexpr bool kClearRecompilableData = true;
3599       i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
3600       i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
3601       // TODO(jgruber,v8:10500): Don't deoptimize once we support serialization
3602       // of optimized code.
3603       i::Deoptimizer::DeoptimizeAll(i_isolate);
3604       i::Snapshot::ClearReconstructableDataForSerialization(
3605           i_isolate, kClearRecompilableData);
3606       i::Snapshot::SerializeDeserializeAndVerifyForTesting(i_isolate,
3607                                                            i_context);
3608     }
3609   }
3610   CollectGarbage(isolate);
3611   for (int i = 1; i < options.num_isolates; ++i) {
3612     if (last_run) {
3613       options.isolate_sources[i].JoinThread();
3614     } else {
3615       options.isolate_sources[i].WaitForThread();
3616     }
3617   }
3618   WaitForRunningWorkers();
3619   if (Shell::unhandled_promise_rejections_.load() > 0) {
3620     printf("%i pending unhandled Promise rejection(s) detected.\n",
3621            Shell::unhandled_promise_rejections_.load());
3622     success = false;
3623     // RunMain may be executed multiple times, e.g. in REPRL mode, so we have to
3624     // reset this counter.
3625     Shell::unhandled_promise_rejections_.store(0);
3626   }
3627   // In order to finish successfully, success must be != expected_to_throw.
3628   return success == Shell::options.expected_to_throw ? 1 : 0;
3629 }
3630 
CollectGarbage(Isolate * isolate)3631 void Shell::CollectGarbage(Isolate* isolate) {
3632   if (options.send_idle_notification) {
3633     const double kLongIdlePauseInSeconds = 1.0;
3634     isolate->ContextDisposedNotification();
3635     isolate->IdleNotificationDeadline(
3636         g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
3637   }
3638   if (options.invoke_weak_callbacks) {
3639     // By sending a low memory notifications, we will try hard to collect all
3640     // garbage and will therefore also invoke all weak callbacks of actually
3641     // unreachable persistent handles.
3642     isolate->LowMemoryNotification();
3643   }
3644 }
3645 
SetWaitUntilDone(Isolate * isolate,bool value)3646 void Shell::SetWaitUntilDone(Isolate* isolate, bool value) {
3647   base::MutexGuard guard(isolate_status_lock_.Pointer());
3648   isolate_status_[isolate] = value;
3649 }
3650 
NotifyStartStreamingTask(Isolate * isolate)3651 void Shell::NotifyStartStreamingTask(Isolate* isolate) {
3652   DCHECK(options.streaming_compile);
3653   base::MutexGuard guard(isolate_status_lock_.Pointer());
3654   ++isolate_running_streaming_tasks_[isolate];
3655 }
3656 
NotifyFinishStreamingTask(Isolate * isolate)3657 void Shell::NotifyFinishStreamingTask(Isolate* isolate) {
3658   DCHECK(options.streaming_compile);
3659   base::MutexGuard guard(isolate_status_lock_.Pointer());
3660   --isolate_running_streaming_tasks_[isolate];
3661   DCHECK_GE(isolate_running_streaming_tasks_[isolate], 0);
3662 }
3663 
3664 namespace {
RunSetTimeoutCallback(Isolate * isolate,bool * did_run)3665 bool RunSetTimeoutCallback(Isolate* isolate, bool* did_run) {
3666   PerIsolateData* data = PerIsolateData::Get(isolate);
3667   HandleScope handle_scope(isolate);
3668   Local<Function> callback;
3669   if (!data->GetTimeoutCallback().ToLocal(&callback)) return true;
3670   Local<Context> context;
3671   if (!data->GetTimeoutContext().ToLocal(&context)) return true;
3672   TryCatch try_catch(isolate);
3673   try_catch.SetVerbose(true);
3674   Context::Scope context_scope(context);
3675   if (callback->Call(context, Undefined(isolate), 0, nullptr).IsEmpty()) {
3676     return false;
3677   }
3678   *did_run = true;
3679   return true;
3680 }
3681 
ProcessMessages(Isolate * isolate,const std::function<platform::MessageLoopBehavior ()> & behavior)3682 bool ProcessMessages(
3683     Isolate* isolate,
3684     const std::function<platform::MessageLoopBehavior()>& behavior) {
3685   while (true) {
3686     i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
3687     i::SaveAndSwitchContext saved_context(i_isolate, i::Context());
3688     SealHandleScope shs(isolate);
3689     while (v8::platform::PumpMessageLoop(g_default_platform, isolate,
3690                                          behavior())) {
3691       MicrotasksScope::PerformCheckpoint(isolate);
3692 
3693       if (i::FLAG_verify_predictable) {
3694         // In predictable mode we push all background tasks into the foreground
3695         // task queue of the {kProcessGlobalPredictablePlatformWorkerTaskQueue}
3696         // isolate. We execute the tasks after one foreground task has been
3697         // executed.
3698         while (v8::platform::PumpMessageLoop(
3699             g_default_platform,
3700             kProcessGlobalPredictablePlatformWorkerTaskQueue, behavior())) {
3701         }
3702       }
3703     }
3704     if (g_default_platform->IdleTasksEnabled(isolate)) {
3705       v8::platform::RunIdleTasks(g_default_platform, isolate,
3706                                  50.0 / base::Time::kMillisecondsPerSecond);
3707     }
3708     bool ran_set_timeout = false;
3709     if (!RunSetTimeoutCallback(isolate, &ran_set_timeout)) {
3710       return false;
3711     }
3712 
3713     if (!ran_set_timeout) return true;
3714   }
3715   return true;
3716 }
3717 }  // anonymous namespace
3718 
CompleteMessageLoop(Isolate * isolate)3719 bool Shell::CompleteMessageLoop(Isolate* isolate) {
3720   auto get_waiting_behaviour = [isolate]() {
3721     base::MutexGuard guard(isolate_status_lock_.Pointer());
3722     DCHECK_GT(isolate_status_.count(isolate), 0);
3723     bool should_wait = (options.wait_for_background_tasks &&
3724                         isolate->HasPendingBackgroundTasks()) ||
3725                        isolate_status_[isolate] ||
3726                        isolate_running_streaming_tasks_[isolate] > 0;
3727     return should_wait ? platform::MessageLoopBehavior::kWaitForWork
3728                        : platform::MessageLoopBehavior::kDoNotWait;
3729   };
3730   return ProcessMessages(isolate, get_waiting_behaviour);
3731 }
3732 
EmptyMessageQueues(Isolate * isolate)3733 bool Shell::EmptyMessageQueues(Isolate* isolate) {
3734   return ProcessMessages(
3735       isolate, []() { return platform::MessageLoopBehavior::kDoNotWait; });
3736 }
3737 
PostForegroundTask(Isolate * isolate,std::unique_ptr<Task> task)3738 void Shell::PostForegroundTask(Isolate* isolate, std::unique_ptr<Task> task) {
3739   g_default_platform->GetForegroundTaskRunner(isolate)->PostTask(
3740       std::move(task));
3741 }
3742 
PostBlockingBackgroundTask(std::unique_ptr<Task> task)3743 void Shell::PostBlockingBackgroundTask(std::unique_ptr<Task> task) {
3744   g_default_platform->CallBlockingTaskOnWorkerThread(std::move(task));
3745 }
3746 
HandleUnhandledPromiseRejections(Isolate * isolate)3747 bool Shell::HandleUnhandledPromiseRejections(Isolate* isolate) {
3748   if (options.ignore_unhandled_promises) return true;
3749   PerIsolateData* data = PerIsolateData::Get(isolate);
3750   int count = data->HandleUnhandledPromiseRejections();
3751   Shell::unhandled_promise_rejections_.store(
3752       Shell::unhandled_promise_rejections_.load() + count);
3753   return count == 0;
3754 }
3755 
3756 class Serializer : public ValueSerializer::Delegate {
3757  public:
Serializer(Isolate * isolate)3758   explicit Serializer(Isolate* isolate)
3759       : isolate_(isolate),
3760         serializer_(isolate, this),
3761         current_memory_usage_(0) {}
3762 
WriteValue(Local<Context> context,Local<Value> value,Local<Value> transfer)3763   Maybe<bool> WriteValue(Local<Context> context, Local<Value> value,
3764                          Local<Value> transfer) {
3765     bool ok;
3766     DCHECK(!data_);
3767     data_.reset(new SerializationData);
3768     if (!PrepareTransfer(context, transfer).To(&ok)) {
3769       return Nothing<bool>();
3770     }
3771     serializer_.WriteHeader();
3772 
3773     if (!serializer_.WriteValue(context, value).To(&ok)) {
3774       data_.reset();
3775       return Nothing<bool>();
3776     }
3777 
3778     if (!FinalizeTransfer().To(&ok)) {
3779       return Nothing<bool>();
3780     }
3781 
3782     std::pair<uint8_t*, size_t> pair = serializer_.Release();
3783     data_->data_.reset(pair.first);
3784     data_->size_ = pair.second;
3785     return Just(true);
3786   }
3787 
Release()3788   std::unique_ptr<SerializationData> Release() { return std::move(data_); }
3789 
AppendBackingStoresTo(std::vector<std::shared_ptr<BackingStore>> * to)3790   void AppendBackingStoresTo(std::vector<std::shared_ptr<BackingStore>>* to) {
3791     to->insert(to->end(), std::make_move_iterator(backing_stores_.begin()),
3792                std::make_move_iterator(backing_stores_.end()));
3793     backing_stores_.clear();
3794   }
3795 
3796  protected:
3797   // Implements ValueSerializer::Delegate.
ThrowDataCloneError(Local<String> message)3798   void ThrowDataCloneError(Local<String> message) override {
3799     isolate_->ThrowException(Exception::Error(message));
3800   }
3801 
GetSharedArrayBufferId(Isolate * isolate,Local<SharedArrayBuffer> shared_array_buffer)3802   Maybe<uint32_t> GetSharedArrayBufferId(
3803       Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) override {
3804     DCHECK_NOT_NULL(data_);
3805     for (size_t index = 0; index < shared_array_buffers_.size(); ++index) {
3806       if (shared_array_buffers_[index] == shared_array_buffer) {
3807         return Just<uint32_t>(static_cast<uint32_t>(index));
3808       }
3809     }
3810 
3811     size_t index = shared_array_buffers_.size();
3812     shared_array_buffers_.emplace_back(isolate_, shared_array_buffer);
3813     data_->sab_backing_stores_.push_back(
3814         shared_array_buffer->GetBackingStore());
3815     return Just<uint32_t>(static_cast<uint32_t>(index));
3816   }
3817 
GetWasmModuleTransferId(Isolate * isolate,Local<WasmModuleObject> module)3818   Maybe<uint32_t> GetWasmModuleTransferId(
3819       Isolate* isolate, Local<WasmModuleObject> module) override {
3820     DCHECK_NOT_NULL(data_);
3821     for (size_t index = 0; index < wasm_modules_.size(); ++index) {
3822       if (wasm_modules_[index] == module) {
3823         return Just<uint32_t>(static_cast<uint32_t>(index));
3824       }
3825     }
3826 
3827     size_t index = wasm_modules_.size();
3828     wasm_modules_.emplace_back(isolate_, module);
3829     data_->compiled_wasm_modules_.push_back(module->GetCompiledModule());
3830     return Just<uint32_t>(static_cast<uint32_t>(index));
3831   }
3832 
ReallocateBufferMemory(void * old_buffer,size_t size,size_t * actual_size)3833   void* ReallocateBufferMemory(void* old_buffer, size_t size,
3834                                size_t* actual_size) override {
3835     // Not accurate, because we don't take into account reallocated buffers,
3836     // but this is fine for testing.
3837     current_memory_usage_ += size;
3838     if (current_memory_usage_ > kMaxSerializerMemoryUsage) return nullptr;
3839 
3840     void* result = realloc(old_buffer, size);
3841     *actual_size = result ? size : 0;
3842     return result;
3843   }
3844 
FreeBufferMemory(void * buffer)3845   void FreeBufferMemory(void* buffer) override { free(buffer); }
3846 
3847  private:
PrepareTransfer(Local<Context> context,Local<Value> transfer)3848   Maybe<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
3849     if (transfer->IsArray()) {
3850       Local<Array> transfer_array = Local<Array>::Cast(transfer);
3851       uint32_t length = transfer_array->Length();
3852       for (uint32_t i = 0; i < length; ++i) {
3853         Local<Value> element;
3854         if (transfer_array->Get(context, i).ToLocal(&element)) {
3855           if (!element->IsArrayBuffer()) {
3856             Throw(isolate_, "Transfer array elements must be an ArrayBuffer");
3857             return Nothing<bool>();
3858           }
3859 
3860           Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(element);
3861 
3862           if (std::find(array_buffers_.begin(), array_buffers_.end(),
3863                         array_buffer) != array_buffers_.end()) {
3864             Throw(isolate_,
3865                   "ArrayBuffer occurs in the transfer array more than once");
3866             return Nothing<bool>();
3867           }
3868 
3869           serializer_.TransferArrayBuffer(
3870               static_cast<uint32_t>(array_buffers_.size()), array_buffer);
3871           array_buffers_.emplace_back(isolate_, array_buffer);
3872         } else {
3873           return Nothing<bool>();
3874         }
3875       }
3876       return Just(true);
3877     } else if (transfer->IsUndefined()) {
3878       return Just(true);
3879     } else {
3880       Throw(isolate_, "Transfer list must be an Array or undefined");
3881       return Nothing<bool>();
3882     }
3883   }
3884 
FinalizeTransfer()3885   Maybe<bool> FinalizeTransfer() {
3886     for (const auto& global_array_buffer : array_buffers_) {
3887       Local<ArrayBuffer> array_buffer =
3888           Local<ArrayBuffer>::New(isolate_, global_array_buffer);
3889       if (!array_buffer->IsDetachable()) {
3890         Throw(isolate_, "ArrayBuffer could not be transferred");
3891         return Nothing<bool>();
3892       }
3893 
3894       auto backing_store = array_buffer->GetBackingStore();
3895       data_->backing_stores_.push_back(std::move(backing_store));
3896       array_buffer->Detach();
3897     }
3898 
3899     return Just(true);
3900   }
3901 
3902   Isolate* isolate_;
3903   ValueSerializer serializer_;
3904   std::unique_ptr<SerializationData> data_;
3905   std::vector<Global<ArrayBuffer>> array_buffers_;
3906   std::vector<Global<SharedArrayBuffer>> shared_array_buffers_;
3907   std::vector<Global<WasmModuleObject>> wasm_modules_;
3908   std::vector<std::shared_ptr<v8::BackingStore>> backing_stores_;
3909   size_t current_memory_usage_;
3910 
3911   DISALLOW_COPY_AND_ASSIGN(Serializer);
3912 };
3913 
3914 class Deserializer : public ValueDeserializer::Delegate {
3915  public:
Deserializer(Isolate * isolate,std::unique_ptr<SerializationData> data)3916   Deserializer(Isolate* isolate, std::unique_ptr<SerializationData> data)
3917       : isolate_(isolate),
3918         deserializer_(isolate, data->data(), data->size(), this),
3919         data_(std::move(data)) {
3920     deserializer_.SetSupportsLegacyWireFormat(true);
3921   }
3922 
ReadValue(Local<Context> context)3923   MaybeLocal<Value> ReadValue(Local<Context> context) {
3924     bool read_header;
3925     if (!deserializer_.ReadHeader(context).To(&read_header)) {
3926       return MaybeLocal<Value>();
3927     }
3928 
3929     uint32_t index = 0;
3930     for (const auto& backing_store : data_->backing_stores()) {
3931       Local<ArrayBuffer> array_buffer =
3932           ArrayBuffer::New(isolate_, std::move(backing_store));
3933       deserializer_.TransferArrayBuffer(index++, array_buffer);
3934     }
3935 
3936     return deserializer_.ReadValue(context);
3937   }
3938 
GetSharedArrayBufferFromId(Isolate * isolate,uint32_t clone_id)3939   MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
3940       Isolate* isolate, uint32_t clone_id) override {
3941     DCHECK_NOT_NULL(data_);
3942     if (clone_id < data_->sab_backing_stores().size()) {
3943       return SharedArrayBuffer::New(
3944           isolate_, std::move(data_->sab_backing_stores().at(clone_id)));
3945     }
3946     return MaybeLocal<SharedArrayBuffer>();
3947   }
3948 
GetWasmModuleFromId(Isolate * isolate,uint32_t transfer_id)3949   MaybeLocal<WasmModuleObject> GetWasmModuleFromId(
3950       Isolate* isolate, uint32_t transfer_id) override {
3951     DCHECK_NOT_NULL(data_);
3952     if (transfer_id >= data_->compiled_wasm_modules().size()) return {};
3953     return WasmModuleObject::FromCompiledModule(
3954         isolate_, data_->compiled_wasm_modules().at(transfer_id));
3955   }
3956 
3957  private:
3958   Isolate* isolate_;
3959   ValueDeserializer deserializer_;
3960   std::unique_ptr<SerializationData> data_;
3961 
3962   DISALLOW_COPY_AND_ASSIGN(Deserializer);
3963 };
3964 
3965 class D8Testing {
3966  public:
3967   /**
3968    * Get the number of runs of a given test that is required to get the full
3969    * stress coverage.
3970    */
GetStressRuns()3971   static int GetStressRuns() {
3972     if (internal::FLAG_stress_runs != 0) return internal::FLAG_stress_runs;
3973 #ifdef DEBUG
3974     // In debug mode the code runs much slower so stressing will only make two
3975     // runs.
3976     return 2;
3977 #else
3978     return 5;
3979 #endif
3980   }
3981 
3982   /**
3983    * Indicate the number of the run which is about to start. The value of run
3984    * should be between 0 and one less than the result from GetStressRuns()
3985    */
PrepareStressRun(int run)3986   static void PrepareStressRun(int run) {
3987     static const char* kLazyOptimizations =
3988         "--prepare-always-opt "
3989         "--max-inlined-bytecode-size=999999 "
3990         "--max-inlined-bytecode-size-cumulative=999999 "
3991         "--noalways-opt";
3992 
3993     if (run == 0) {
3994       V8::SetFlagsFromString(kLazyOptimizations);
3995     } else if (run == GetStressRuns() - 1) {
3996       i::FLAG_always_opt = true;
3997     }
3998   }
3999 
4000   /**
4001    * Force deoptimization of all functions.
4002    */
DeoptimizeAll(Isolate * isolate)4003   static void DeoptimizeAll(Isolate* isolate) {
4004     i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
4005     i::HandleScope scope(i_isolate);
4006     i::Deoptimizer::DeoptimizeAll(i_isolate);
4007   }
4008 };
4009 
SerializeValue(Isolate * isolate,Local<Value> value,Local<Value> transfer)4010 std::unique_ptr<SerializationData> Shell::SerializeValue(
4011     Isolate* isolate, Local<Value> value, Local<Value> transfer) {
4012   bool ok;
4013   Local<Context> context = isolate->GetCurrentContext();
4014   Serializer serializer(isolate);
4015   std::unique_ptr<SerializationData> data;
4016   if (serializer.WriteValue(context, value, transfer).To(&ok)) {
4017     data = serializer.Release();
4018   }
4019   return data;
4020 }
4021 
DeserializeValue(Isolate * isolate,std::unique_ptr<SerializationData> data)4022 MaybeLocal<Value> Shell::DeserializeValue(
4023     Isolate* isolate, std::unique_ptr<SerializationData> data) {
4024   Local<Value> value;
4025   Local<Context> context = isolate->GetCurrentContext();
4026   Deserializer deserializer(isolate, std::move(data));
4027   return deserializer.ReadValue(context);
4028 }
4029 
AddRunningWorker(std::shared_ptr<Worker> worker)4030 void Shell::AddRunningWorker(std::shared_ptr<Worker> worker) {
4031   workers_mutex_.Pointer()->AssertHeld();  // caller should hold the mutex.
4032   running_workers_.insert(worker);
4033 }
4034 
RemoveRunningWorker(const std::shared_ptr<Worker> & worker)4035 void Shell::RemoveRunningWorker(const std::shared_ptr<Worker>& worker) {
4036   base::MutexGuard lock_guard(workers_mutex_.Pointer());
4037   auto it = running_workers_.find(worker);
4038   if (it != running_workers_.end()) running_workers_.erase(it);
4039 }
4040 
WaitForRunningWorkers()4041 void Shell::WaitForRunningWorkers() {
4042   // Make a copy of running_workers_, because we don't want to call
4043   // Worker::Terminate while holding the workers_mutex_ lock. Otherwise, if a
4044   // worker is about to create a new Worker, it would deadlock.
4045   std::unordered_set<std::shared_ptr<Worker>> workers_copy;
4046   {
4047     base::MutexGuard lock_guard(workers_mutex_.Pointer());
4048     allow_new_workers_ = false;
4049     workers_copy.swap(running_workers_);
4050   }
4051 
4052   for (auto& worker : workers_copy) {
4053     worker->TerminateAndWaitForThread();
4054   }
4055 
4056   // Now that all workers are terminated, we can re-enable Worker creation.
4057   base::MutexGuard lock_guard(workers_mutex_.Pointer());
4058   DCHECK(running_workers_.empty());
4059   allow_new_workers_ = true;
4060 }
4061 
Main(int argc,char * argv[])4062 int Shell::Main(int argc, char* argv[]) {
4063   v8::base::EnsureConsoleOutput();
4064   if (!SetOptions(argc, argv)) return 1;
4065 
4066   v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);
4067 
4068 #ifdef V8_INTL_SUPPORT
4069   if (options.icu_locale != nullptr) {
4070     icu::Locale locale(options.icu_locale);
4071     UErrorCode error_code = U_ZERO_ERROR;
4072     icu::Locale::setDefault(locale, error_code);
4073   }
4074 #endif  // V8_INTL_SUPPORT
4075 
4076   v8::platform::InProcessStackDumping in_process_stack_dumping =
4077       options.disable_in_process_stack_traces
4078           ? v8::platform::InProcessStackDumping::kDisabled
4079           : v8::platform::InProcessStackDumping::kEnabled;
4080 
4081   std::unique_ptr<platform::tracing::TracingController> tracing;
4082   std::ofstream trace_file;
4083   if (options.trace_enabled && !i::FLAG_verify_predictable) {
4084     tracing = std::make_unique<platform::tracing::TracingController>();
4085     const char* trace_path =
4086         options.trace_path ? options.trace_path : "v8_trace.json";
4087     trace_file.open(trace_path);
4088     if (!trace_file.good()) {
4089       printf("Cannot open trace file '%s' for writing: %s.\n", trace_path,
4090              strerror(errno));
4091       return 1;
4092     }
4093 
4094 #ifdef V8_USE_PERFETTO
4095     // Set up the in-process backend that the tracing controller will connect
4096     // to.
4097     perfetto::TracingInitArgs init_args;
4098     init_args.backends = perfetto::BackendType::kInProcessBackend;
4099     perfetto::Tracing::Initialize(init_args);
4100 
4101     tracing->InitializeForPerfetto(&trace_file);
4102 #else
4103     platform::tracing::TraceBuffer* trace_buffer =
4104         platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
4105             platform::tracing::TraceBuffer::kRingBufferChunks,
4106             platform::tracing::TraceWriter::CreateJSONTraceWriter(trace_file));
4107     tracing->Initialize(trace_buffer);
4108 #endif  // V8_USE_PERFETTO
4109   }
4110 
4111   platform::tracing::TracingController* tracing_controller = tracing.get();
4112   g_platform = v8::platform::NewDefaultPlatform(
4113       options.thread_pool_size, v8::platform::IdleTaskSupport::kEnabled,
4114       in_process_stack_dumping, std::move(tracing));
4115   g_default_platform = g_platform.get();
4116   if (i::FLAG_verify_predictable) {
4117     g_platform = MakePredictablePlatform(std::move(g_platform));
4118   }
4119   if (options.stress_delay_tasks) {
4120     int64_t random_seed = i::FLAG_fuzzer_random_seed;
4121     if (!random_seed) random_seed = i::FLAG_random_seed;
4122     // If random_seed is still 0 here, the {DelayedTasksPlatform} will choose a
4123     // random seed.
4124     g_platform = MakeDelayedTasksPlatform(std::move(g_platform), random_seed);
4125   }
4126 
4127   if (i::FLAG_trace_turbo_cfg_file == nullptr) {
4128     V8::SetFlagsFromString("--trace-turbo-cfg-file=turbo.cfg");
4129   }
4130   if (i::FLAG_redirect_code_traces_to == nullptr) {
4131     V8::SetFlagsFromString("--redirect-code-traces-to=code.asm");
4132   }
4133   v8::V8::InitializePlatform(g_platform.get());
4134   v8::V8::Initialize();
4135   if (options.snapshot_blob) {
4136     v8::V8::InitializeExternalStartupDataFromFile(options.snapshot_blob);
4137   } else {
4138     v8::V8::InitializeExternalStartupData(argv[0]);
4139   }
4140   int result = 0;
4141   Isolate::CreateParams create_params;
4142   ShellArrayBufferAllocator shell_array_buffer_allocator;
4143   MockArrayBufferAllocator mock_arraybuffer_allocator;
4144   const size_t memory_limit =
4145       options.mock_arraybuffer_allocator_limit * options.num_isolates;
4146   MockArrayBufferAllocatiorWithLimit mock_arraybuffer_allocator_with_limit(
4147       memory_limit >= options.mock_arraybuffer_allocator_limit
4148           ? memory_limit
4149           : std::numeric_limits<size_t>::max());
4150 #if V8_OS_LINUX
4151   MultiMappedAllocator multi_mapped_mock_allocator;
4152 #endif  // V8_OS_LINUX
4153   if (options.mock_arraybuffer_allocator) {
4154     if (memory_limit) {
4155       Shell::array_buffer_allocator = &mock_arraybuffer_allocator_with_limit;
4156     } else {
4157       Shell::array_buffer_allocator = &mock_arraybuffer_allocator;
4158     }
4159 #if V8_OS_LINUX
4160   } else if (options.multi_mapped_mock_allocator) {
4161     Shell::array_buffer_allocator = &multi_mapped_mock_allocator;
4162 #endif  // V8_OS_LINUX
4163   } else {
4164     Shell::array_buffer_allocator = &shell_array_buffer_allocator;
4165   }
4166   create_params.array_buffer_allocator = Shell::array_buffer_allocator;
4167 #ifdef ENABLE_VTUNE_JIT_INTERFACE
4168   create_params.code_event_handler = vTune::GetVtuneCodeEventHandler();
4169 #endif
4170   create_params.constraints.ConfigureDefaults(
4171       base::SysInfo::AmountOfPhysicalMemory(),
4172       base::SysInfo::AmountOfVirtualMemory());
4173 
4174   Shell::counter_map_ = new CounterMap();
4175   if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp ||
4176       i::TracingFlags::is_gc_stats_enabled()) {
4177     create_params.counter_lookup_callback = LookupCounter;
4178     create_params.create_histogram_callback = CreateHistogram;
4179     create_params.add_histogram_sample_callback = AddHistogramSample;
4180   }
4181 
4182   if (V8_TRAP_HANDLER_SUPPORTED && i::FLAG_wasm_trap_handler) {
4183     constexpr bool use_default_trap_handler = true;
4184     if (!v8::V8::EnableWebAssemblyTrapHandler(use_default_trap_handler)) {
4185       FATAL("Could not register trap handler");
4186     }
4187   }
4188 
4189   Isolate* isolate = Isolate::New(create_params);
4190 
4191   {
4192     D8Console console(isolate);
4193     Isolate::Scope scope(isolate);
4194     Initialize(isolate, &console);
4195     PerIsolateData data(isolate);
4196 
4197     // Fuzzilli REPRL = read-eval-print-loop
4198     do {
4199 #ifdef V8_FUZZILLI
4200       if (fuzzilli_reprl) {
4201         unsigned action = 0;
4202         ssize_t nread = read(REPRL_CRFD, &action, 4);
4203         if (nread != 4 || action != 'cexe') {
4204           fprintf(stderr, "Unknown action: %u\n", action);
4205           _exit(-1);
4206         }
4207       }
4208 #endif  // V8_FUZZILLI
4209 
4210       result = 0;
4211 
4212       if (options.trace_enabled) {
4213         platform::tracing::TraceConfig* trace_config;
4214         if (options.trace_config) {
4215           int size = 0;
4216           char* trace_config_json_str = ReadChars(options.trace_config, &size);
4217           trace_config = tracing::CreateTraceConfigFromJSON(
4218               isolate, trace_config_json_str);
4219           delete[] trace_config_json_str;
4220         } else {
4221           trace_config =
4222               platform::tracing::TraceConfig::CreateDefaultTraceConfig();
4223         }
4224         tracing_controller->StartTracing(trace_config);
4225       }
4226 
4227       CpuProfiler* cpu_profiler;
4228       if (options.cpu_profiler) {
4229         cpu_profiler = CpuProfiler::New(isolate);
4230         CpuProfilingOptions profile_options;
4231         cpu_profiler->StartProfiling(String::Empty(isolate), profile_options);
4232       }
4233 
4234       if (options.stress_opt) {
4235         options.stress_runs = D8Testing::GetStressRuns();
4236         for (int i = 0; i < options.stress_runs && result == 0; i++) {
4237           printf("============ Stress %d/%d ============\n", i + 1,
4238                  options.stress_runs.get());
4239           D8Testing::PrepareStressRun(i);
4240           bool last_run = i == options.stress_runs - 1;
4241           result = RunMain(isolate, last_run);
4242         }
4243         printf("======== Full Deoptimization =======\n");
4244         D8Testing::DeoptimizeAll(isolate);
4245       } else if (i::FLAG_stress_runs > 0) {
4246         options.stress_runs = i::FLAG_stress_runs;
4247         for (int i = 0; i < options.stress_runs && result == 0; i++) {
4248           printf("============ Run %d/%d ============\n", i + 1,
4249                  options.stress_runs.get());
4250           bool last_run = i == options.stress_runs - 1;
4251           result = RunMain(isolate, last_run);
4252         }
4253       } else if (options.code_cache_options !=
4254                  ShellOptions::CodeCacheOptions::kNoProduceCache) {
4255         printf("============ Run: Produce code cache ============\n");
4256         // First run to produce the cache
4257         Isolate::CreateParams create_params;
4258         create_params.array_buffer_allocator = Shell::array_buffer_allocator;
4259         i::FLAG_hash_seed ^= 1337;  // Use a different hash seed.
4260         Isolate* isolate2 = Isolate::New(create_params);
4261         i::FLAG_hash_seed ^= 1337;  // Restore old hash seed.
4262         {
4263           D8Console console(isolate2);
4264           Initialize(isolate2, &console);
4265           PerIsolateData data(isolate2);
4266           Isolate::Scope isolate_scope(isolate2);
4267 
4268           result = RunMain(isolate2, false);
4269         }
4270         isolate2->Dispose();
4271 
4272         // Change the options to consume cache
4273         DCHECK(options.compile_options == v8::ScriptCompiler::kEagerCompile ||
4274                options.compile_options ==
4275                    v8::ScriptCompiler::kNoCompileOptions);
4276         options.compile_options.Overwrite(
4277             v8::ScriptCompiler::kConsumeCodeCache);
4278         options.code_cache_options.Overwrite(
4279             ShellOptions::CodeCacheOptions::kNoProduceCache);
4280 
4281         printf("============ Run: Consume code cache ============\n");
4282         // Second run to consume the cache in current isolate
4283         result = RunMain(isolate, true);
4284         options.compile_options.Overwrite(
4285             v8::ScriptCompiler::kNoCompileOptions);
4286       } else {
4287         bool last_run = true;
4288         result = RunMain(isolate, last_run);
4289       }
4290 
4291       // Run interactive shell if explicitly requested or if no script has been
4292       // executed, but never on --test
4293       if (use_interactive_shell()) {
4294         RunShell(isolate);
4295       }
4296 
4297       if (i::FLAG_trace_ignition_dispatches &&
4298           i::FLAG_trace_ignition_dispatches_output_file != nullptr) {
4299         WriteIgnitionDispatchCountersFile(isolate);
4300       }
4301 
4302       if (options.cpu_profiler) {
4303         CpuProfile* profile =
4304             cpu_profiler->StopProfiling(String::Empty(isolate));
4305         if (options.cpu_profiler_print) {
4306           const internal::ProfileNode* root =
4307               reinterpret_cast<const internal::ProfileNode*>(
4308                   profile->GetTopDownRoot());
4309           root->Print(0);
4310         }
4311         profile->Delete();
4312         cpu_profiler->Dispose();
4313       }
4314 
4315       // Shut down contexts and collect garbage.
4316       cached_code_map_.clear();
4317       evaluation_context_.Reset();
4318       stringify_function_.Reset();
4319       CollectGarbage(isolate);
4320 #ifdef V8_FUZZILLI
4321       // Send result to parent (fuzzilli) and reset edge guards.
4322       if (fuzzilli_reprl) {
4323         int status = result << 8;
4324         std::vector<bool> bitmap;
4325         if (options.fuzzilli_enable_builtins_coverage) {
4326           bitmap = i::BasicBlockProfiler::Get()->GetCoverageBitmap(
4327               reinterpret_cast<i::Isolate*>(isolate));
4328           cov_update_builtins_basic_block_coverage(bitmap);
4329         }
4330         if (options.fuzzilli_coverage_statistics) {
4331           int tot = 0;
4332           for (bool b : bitmap) {
4333             if (b) tot++;
4334           }
4335           static int iteration_counter = 0;
4336           std::ofstream covlog("covlog.txt", std::ios::app);
4337           covlog << iteration_counter << "\t" << tot << "\t"
4338                  << sanitizer_cov_count_discovered_edges() << "\t"
4339                  << bitmap.size() << std::endl;
4340           iteration_counter++;
4341         }
4342         // In REPRL mode, stdout and stderr can be regular files, so they need
4343         // to be flushed after every execution
4344         fflush(stdout);
4345         fflush(stderr);
4346         CHECK_EQ(write(REPRL_CWFD, &status, 4), 4);
4347         sanitizer_cov_reset_edgeguards();
4348         if (options.fuzzilli_enable_builtins_coverage) {
4349           i::BasicBlockProfiler::Get()->ResetCounts(
4350               reinterpret_cast<i::Isolate*>(isolate));
4351         }
4352       }
4353 #endif  // V8_FUZZILLI
4354     } while (fuzzilli_reprl);
4355   }
4356   OnExit(isolate);
4357 
4358   V8::Dispose();
4359   V8::ShutdownPlatform();
4360 
4361   // Delete the platform explicitly here to write the tracing output to the
4362   // tracing file.
4363   if (options.trace_enabled) {
4364     tracing_controller->StopTracing();
4365   }
4366   g_platform.reset();
4367   return result;
4368 }
4369 
4370 }  // namespace v8
4371 
4372 #ifndef GOOGLE3
main(int argc,char * argv[])4373 int main(int argc, char* argv[]) { return v8::Shell::Main(argc, argv); }
4374 #endif
4375 
4376 #undef CHECK
4377 #undef DCHECK
4378 #undef TRACE_BS
4379