d8.cc 168 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// 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 14
#include <iterator>
#include <string>
15
#include <tuple>
16
#include <type_traits>
17
#include <unordered_map>
18
#include <utility>
binji's avatar
binji committed
19
#include <vector>
20

21
#ifdef ENABLE_VTUNE_JIT_INTERFACE
22
#include "src/third_party/vtune/v8-vtune.h"
23 24
#endif

25
#include "include/libplatform/libplatform.h"
26
#include "include/libplatform/v8-tracing.h"
27
#include "include/v8-inspector.h"
28
#include "include/v8-profiler.h"
29
#include "src/api/api-inl.h"
30 31 32
#include "src/base/cpu.h"
#include "src/base/logging.h"
#include "src/base/platform/platform.h"
33
#include "src/base/platform/time.h"
34
#include "src/base/platform/wrappers.h"
35
#include "src/base/sys-info.h"
36 37 38
#include "src/d8/d8-console.h"
#include "src/d8/d8-platforms.h"
#include "src/d8/d8.h"
39
#include "src/debug/debug-interface.h"
40
#include "src/deoptimizer/deoptimizer.h"
41
#include "src/diagnostics/basic-block-profiler.h"
42
#include "src/execution/vm-state-inl.h"
43
#include "src/flags/flags.h"
44
#include "src/handles/maybe-handles.h"
45
#include "src/init/v8.h"
46
#include "src/interpreter/interpreter.h"
47
#include "src/logging/counters.h"
48
#include "src/logging/log-utils.h"
49
#include "src/objects/managed.h"
50 51
#include "src/objects/objects-inl.h"
#include "src/objects/objects.h"
52 53 54
#include "src/parsing/parse-info.h"
#include "src/parsing/parsing.h"
#include "src/parsing/scanner-character-streams.h"
55
#include "src/profiler/profile-generator.h"
56
#include "src/sanitizer/msan.h"
57
#include "src/snapshot/snapshot.h"
58
#include "src/tasks/cancelable-task.h"
eholk's avatar
eholk committed
59
#include "src/trap-handler/trap-handler.h"
60 61
#include "src/utils/ostreams.h"
#include "src/utils/utils.h"
62
#include "src/wasm/wasm-engine.h"
63

64 65 66 67
#ifdef V8_FUZZILLI
#include "src/d8/cov.h"
#endif  // V8_FUZZILLI

68 69 70 71
#ifdef V8_USE_PERFETTO
#include "perfetto/tracing.h"
#endif  // V8_USE_PERFETTO

72 73 74 75
#ifdef V8_INTL_SUPPORT
#include "unicode/locid.h"
#endif  // V8_INTL_SUPPORT

76 77 78 79
#ifdef V8_OS_LINUX
#include <sys/mman.h>  // For MultiMappedAllocator.
#endif

80
#if !defined(_WIN32) && !defined(_WIN64)
81
#include <unistd.h>  // NOLINT
82 83
#else
#include <windows.h>  // NOLINT
84
#endif                // !defined(_WIN32) && !defined(_WIN64)
85

86 87
#ifndef DCHECK
#define DCHECK(condition) assert(condition)
Yang Guo's avatar
Yang Guo committed
88 89 90 91
#endif

#ifndef CHECK
#define CHECK(condition) assert(condition)
92
#endif
93

94 95 96 97 98
#define TRACE_BS(...)                                     \
  do {                                                    \
    if (i::FLAG_trace_backing_store) PrintF(__VA_ARGS__); \
  } while (false)

99
namespace v8 {
100

101
namespace {
102

103
const int kMB = 1024 * 1024;
104

105
#ifdef V8_FUZZILLI
106
// REPRL = read-eval-print-reset-loop
107 108 109 110 111 112 113 114 115 116 117
// These file descriptors are being opened when Fuzzilli uses fork & execve to
// run V8.
#define REPRL_CRFD 100  // Control read file decriptor
#define REPRL_CWFD 101  // Control write file decriptor
#define REPRL_DRFD 102  // Data read file decriptor
#define REPRL_DWFD 103  // Data write file decriptor
bool fuzzilli_reprl = true;
#else
bool fuzzilli_reprl = false;
#endif  // V8_FUZZILLI

118 119
const int kMaxSerializerMemoryUsage =
    1 * kMB;  // Arbitrary maximum for testing.
120

121 122
// Base class for shell ArrayBuffer allocators. It forwards all opertions to
// the default v8 allocator.
123 124 125
class ArrayBufferAllocatorBase : public v8::ArrayBuffer::Allocator {
 public:
  void* Allocate(size_t length) override {
126
    return allocator_->Allocate(length);
127
  }
128

129
  void* AllocateUninitialized(size_t length) override {
130
    return allocator_->AllocateUninitialized(length);
131
  }
132

133
  void Free(void* data, size_t length) override {
134
    allocator_->Free(data, length);
135
  }
136 137 138 139 140 141

 private:
  std::unique_ptr<Allocator> allocator_ =
      std::unique_ptr<Allocator>(NewDefaultAllocator());
};

142
// ArrayBuffer allocator that can use virtual memory to improve performance.
143 144
class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase {
 public:
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
  void* Allocate(size_t length) override {
    if (length >= kVMThreshold) return AllocateVM(length);
    return ArrayBufferAllocatorBase::Allocate(length);
  }

  void* AllocateUninitialized(size_t length) override {
    if (length >= kVMThreshold) return AllocateVM(length);
    return ArrayBufferAllocatorBase::AllocateUninitialized(length);
  }

  void Free(void* data, size_t length) override {
    if (length >= kVMThreshold) {
      FreeVM(data, length);
    } else {
      ArrayBufferAllocatorBase::Free(data, length);
    }
  }

163
 private:
164 165 166 167
  static constexpr size_t kVMThreshold = 65536;

  void* AllocateVM(size_t length) {
    DCHECK_LE(kVMThreshold, length);
168 169
    v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
    size_t page_size = page_allocator->AllocatePageSize();
170
    size_t allocated = RoundUp(length, page_size);
171
    return i::AllocatePages(page_allocator, nullptr, allocated, page_size,
172
                            PageAllocator::kReadWrite);
173 174 175
  }

  void FreeVM(void* data, size_t length) {
176 177
    v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
    size_t page_size = page_allocator->AllocatePageSize();
178
    size_t allocated = RoundUp(length, page_size);
179
    CHECK(i::FreePages(page_allocator, data, allocated));
180
  }
181 182
};

183
// ArrayBuffer allocator that never allocates over 10MB.
184
class MockArrayBufferAllocator : public ArrayBufferAllocatorBase {
185
 protected:
186 187 188 189 190 191 192 193 194 195 196 197
  void* Allocate(size_t length) override {
    return ArrayBufferAllocatorBase::Allocate(Adjust(length));
  }

  void* AllocateUninitialized(size_t length) override {
    return ArrayBufferAllocatorBase::AllocateUninitialized(Adjust(length));
  }

  void Free(void* data, size_t length) override {
    return ArrayBufferAllocatorBase::Free(data, Adjust(length));
  }

198
 private:
199
  size_t Adjust(size_t length) {
200
    const size_t kAllocationLimit = 10 * kMB;
201
    return length > kAllocationLimit ? i::AllocatePageSize() : length;
202
  }
203 204
};

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
// ArrayBuffer allocator that can be equipped with a limit to simulate system
// OOM.
class MockArrayBufferAllocatiorWithLimit : public MockArrayBufferAllocator {
 public:
  explicit MockArrayBufferAllocatiorWithLimit(size_t allocation_limit)
      : space_left_(allocation_limit) {}

 protected:
  void* Allocate(size_t length) override {
    if (length > space_left_) {
      return nullptr;
    }
    space_left_ -= length;
    return MockArrayBufferAllocator::Allocate(length);
  }

  void* AllocateUninitialized(size_t length) override {
    if (length > space_left_) {
      return nullptr;
    }
    space_left_ -= length;
    return MockArrayBufferAllocator::AllocateUninitialized(length);
  }

  void Free(void* data, size_t length) override {
    space_left_ += length;
    return MockArrayBufferAllocator::Free(data, length);
  }

 private:
  std::atomic<size_t> space_left_;
};

238 239 240 241 242 243 244 245 246 247 248 249 250
#ifdef V8_OS_LINUX

// This is a mock allocator variant that provides a huge virtual allocation
// backed by a small real allocation that is repeatedly mapped. If you create an
// array on memory allocated by this allocator, you will observe that elements
// will alias each other as if their indices were modulo-divided by the real
// allocation length.
// The purpose is to allow stability-testing of huge (typed) arrays without
// actually consuming huge amounts of physical memory.
// This is currently only available on Linux because it relies on {mremap}.
class MultiMappedAllocator : public ArrayBufferAllocatorBase {
 protected:
  void* Allocate(size_t length) override {
251 252 253
    if (length < kChunkSize) {
      return ArrayBufferAllocatorBase::Allocate(length);
    }
254 255 256 257 258
    // We use mmap, which initializes pages to zero anyway.
    return AllocateUninitialized(length);
  }

  void* AllocateUninitialized(size_t length) override {
259 260 261
    if (length < kChunkSize) {
      return ArrayBufferAllocatorBase::AllocateUninitialized(length);
    }
262 263 264
    size_t rounded_length = RoundUp(length, kChunkSize);
    int prot = PROT_READ | PROT_WRITE;
    // We have to specify MAP_SHARED to make {mremap} below do what we want.
265
    int flags = MAP_SHARED | MAP_ANONYMOUS;
266
    void* real_alloc = mmap(nullptr, kChunkSize, prot, flags, -1, 0);
267
    if (reinterpret_cast<intptr_t>(real_alloc) == -1) {
268 269 270 271 272 273
      // If we ran into some limit (physical or virtual memory, or number
      // of mappings, etc), return {nullptr}, which callers can handle.
      if (errno == ENOMEM) {
        return nullptr;
      }
      // Other errors may be bugs which we want to learn about.
274 275
      FATAL("mmap (real) failed with error %d: %s", errno, strerror(errno));
    }
276 277
    void* virtual_alloc =
        mmap(nullptr, rounded_length, prot, flags | MAP_NORESERVE, -1, 0);
278
    if (reinterpret_cast<intptr_t>(virtual_alloc) == -1) {
279 280 281 282 283 284
      if (errno == ENOMEM) {
        // Undo earlier, successful mappings.
        munmap(real_alloc, kChunkSize);
        return nullptr;
      }
      FATAL("mmap (virtual) failed with error %d: %s", errno, strerror(errno));
285
    }
286 287 288 289 290 291 292 293 294 295
    i::Address virtual_base = reinterpret_cast<i::Address>(virtual_alloc);
    i::Address virtual_end = virtual_base + rounded_length;
    for (i::Address to_map = virtual_base; to_map < virtual_end;
         to_map += kChunkSize) {
      // Specifying 0 as the "old size" causes the existing map entry to not
      // get deleted, which is important so that we can remap it again in the
      // next iteration of this loop.
      void* result =
          mremap(real_alloc, 0, kChunkSize, MREMAP_MAYMOVE | MREMAP_FIXED,
                 reinterpret_cast<void*>(to_map));
296
      if (reinterpret_cast<intptr_t>(result) == -1) {
297 298 299 300 301 302
        if (errno == ENOMEM) {
          // Undo earlier, successful mappings.
          munmap(real_alloc, kChunkSize);
          munmap(virtual_alloc, (to_map - virtual_base));
          return nullptr;
        }
303 304
        FATAL("mremap failed with error %d: %s", errno, strerror(errno));
      }
305
    }
306
    base::MutexGuard lock_guard(&regions_mutex_);
307 308 309 310 311
    regions_[virtual_alloc] = real_alloc;
    return virtual_alloc;
  }

  void Free(void* data, size_t length) override {
312 313 314
    if (length < kChunkSize) {
      return ArrayBufferAllocatorBase::Free(data, length);
    }
315
    base::MutexGuard lock_guard(&regions_mutex_);
316 317 318 319 320 321 322 323 324 325 326 327
    void* real_alloc = regions_[data];
    munmap(real_alloc, kChunkSize);
    size_t rounded_length = RoundUp(length, kChunkSize);
    munmap(data, rounded_length);
    regions_.erase(data);
  }

 private:
  // Aiming for a "Huge Page" (2M on Linux x64) to go easy on the TLB.
  static constexpr size_t kChunkSize = 2 * 1024 * 1024;

  std::unordered_map<void*, void*> regions_;
328
  base::Mutex regions_mutex_;
329 330 331 332
};

#endif  // V8_OS_LINUX

333
v8::Platform* g_default_platform;
334 335
std::unique_ptr<v8::Platform> g_platform;

336
static Local<Value> Throw(Isolate* isolate, const char* message) {
337 338
  return isolate->ThrowException(v8::Exception::Error(
      String::NewFromUtf8(isolate, message).ToLocalChecked()));
339 340
}

341 342 343 344
static MaybeLocal<Value> TryGetValue(v8::Isolate* isolate,
                                     Local<Context> context,
                                     Local<v8::Object> object,
                                     const char* property) {
345 346 347
  MaybeLocal<String> v8_str = String::NewFromUtf8(isolate, property);
  if (v8_str.IsEmpty()) return {};
  return object->Get(context, v8_str.ToLocalChecked());
348 349 350 351 352
}

static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
                             Local<v8::Object> object, const char* property) {
  return TryGetValue(isolate, context, object, property).ToLocalChecked();
353 354
}

355 356
std::shared_ptr<Worker> GetWorkerFromInternalField(Isolate* isolate,
                                                   Local<Object> object) {
357 358
  if (object->InternalFieldCount() != 1) {
    Throw(isolate, "this is not a Worker");
359
    return nullptr;
360
  }
361

362 363
  i::Handle<i::Object> handle = Utils::OpenHandle(*object->GetInternalField(0));
  if (handle->IsSmi()) {
364
    Throw(isolate, "Worker is defunct because main thread is terminating");
365
    return nullptr;
366
  }
367
  auto managed = i::Handle<i::Managed<Worker>>::cast(handle);
368
  return managed->get();
369 370
}

371 372 373 374 375
base::Thread::Options GetThreadOptions(const char* name) {
  // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
  // which is not enough to parse the big literal expressions used in tests.
  // The stack size should be at least StackGuard::kLimitSize + some
  // OS-specific padding for thread startup code.  2Mbytes seems to be enough.
376
  return base::Thread::Options(name, 2 * kMB);
377
}
378

379 380
}  // namespace

381 382 383 384
namespace tracing {

namespace {

385
static constexpr char kIncludedCategoriesParam[] = "included_categories";
386 387 388 389 390 391 392 393 394 395 396 397

class TraceConfigParser {
 public:
  static void FillTraceConfig(v8::Isolate* isolate,
                              platform::tracing::TraceConfig* trace_config,
                              const char* json_str) {
    HandleScope outer_scope(isolate);
    Local<Context> context = Context::New(isolate);
    Context::Scope context_scope(context);
    HandleScope inner_scope(isolate);

    Local<String> source =
398
        String::NewFromUtf8(isolate, json_str).ToLocalChecked();
399
    Local<Value> result = JSON::Parse(context, source).ToLocalChecked();
400
    Local<v8::Object> trace_config_object = result.As<v8::Object>();
401

402 403
    UpdateIncludedCategoriesList(isolate, context, trace_config_object,
                                 trace_config);
404 405 406
  }

 private:
407
  static int UpdateIncludedCategoriesList(
408
      v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object,
409 410 411
      platform::tracing::TraceConfig* trace_config) {
    Local<Value> value =
        GetValue(isolate, context, object, kIncludedCategoriesParam);
412
    if (value->IsArray()) {
413
      Local<Array> v8_array = value.As<Array>();
414 415 416 417 418
      for (int i = 0, length = v8_array->Length(); i < length; ++i) {
        Local<Value> v = v8_array->Get(context, i)
                             .ToLocalChecked()
                             ->ToString(context)
                             .ToLocalChecked();
419
        String::Utf8Value str(isolate, v->ToString(context).ToLocalChecked());
420
        trace_config->AddIncludedCategory(*str);
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
      }
      return v8_array->Length();
    }
    return 0;
  }
};

}  // namespace

static platform::tracing::TraceConfig* CreateTraceConfigFromJSON(
    v8::Isolate* isolate, const char* json_str) {
  platform::tracing::TraceConfig* trace_config =
      new platform::tracing::TraceConfig();
  TraceConfigParser::FillTraceConfig(isolate, trace_config, json_str);
  return trace_config;
}

}  // namespace tracing
439

440 441 442
class ExternalOwningOneByteStringResource
    : public String::ExternalOneByteStringResource {
 public:
443
  ExternalOwningOneByteStringResource() = default;
444 445 446 447 448 449 450
  ExternalOwningOneByteStringResource(
      std::unique_ptr<base::OS::MemoryMappedFile> file)
      : file_(std::move(file)) {}
  const char* data() const override {
    return static_cast<char*>(file_->memory());
  }
  size_t length() const override { return file_->size(); }
451 452

 private:
453
  std::unique_ptr<base::OS::MemoryMappedFile> file_;
454
};
455

456
CounterMap* Shell::counter_map_;
457
base::OS::MemoryMappedFile* Shell::counters_file_ = nullptr;
458 459
CounterCollection Shell::local_counters_;
CounterCollection* Shell::counters_ = &local_counters_;
460
base::LazyMutex Shell::context_mutex_;
461 462
const base::TimeTicks Shell::kInitialTicks =
    base::TimeTicks::HighResolutionNow();
yangguo's avatar
yangguo committed
463
Global<Function> Shell::stringify_function_;
464
base::LazyMutex Shell::workers_mutex_;
465
bool Shell::allow_new_workers_ = true;
466
std::unordered_set<std::shared_ptr<Worker>> Shell::running_workers_;
467
std::atomic<bool> Shell::script_executed_{false};
468
std::atomic<bool> Shell::valid_fuzz_script_{false};
469 470
base::LazyMutex Shell::isolate_status_lock_;
std::map<v8::Isolate*, bool> Shell::isolate_status_;
471
std::map<v8::Isolate*, int> Shell::isolate_running_streaming_tasks_;
472
base::LazyMutex Shell::cached_code_mutex_;
473 474
std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
    Shell::cached_code_map_;
475
std::atomic<int> Shell::unhandled_promise_rejections_{0};
476

477
Global<Context> Shell::evaluation_context_;
478
ArrayBuffer::Allocator* Shell::array_buffer_allocator;
479
bool check_d8_flag_contradictions = true;
480
ShellOptions Shell::options;
481
base::OnceType Shell::quit_once_ = V8_ONCE_INIT;
482

483 484
ScriptCompiler::CachedData* Shell::LookupCodeCache(Isolate* isolate,
                                                   Local<Value> source) {
485
  base::MutexGuard lock_guard(cached_code_mutex_.Pointer());
486 487 488 489 490 491 492
  CHECK(source->IsString());
  v8::String::Utf8Value key(isolate, source);
  DCHECK(*key);
  auto entry = cached_code_map_.find(*key);
  if (entry != cached_code_map_.end() && entry->second) {
    int length = entry->second->length;
    uint8_t* cache = new uint8_t[length];
493
    base::Memcpy(cache, entry->second->data, length);
494 495 496
    ScriptCompiler::CachedData* cached_data = new ScriptCompiler::CachedData(
        cache, length, ScriptCompiler::CachedData::BufferOwned);
    return cached_data;
497
  }
498
  return nullptr;
499 500
}

501 502
void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> source,
                             const ScriptCompiler::CachedData* cache_data) {
503
  base::MutexGuard lock_guard(cached_code_mutex_.Pointer());
504 505 506 507 508 509
  CHECK(source->IsString());
  if (cache_data == nullptr) return;
  v8::String::Utf8Value key(isolate, source);
  DCHECK(*key);
  int length = cache_data->length;
  uint8_t* cache = new uint8_t[length];
510
  base::Memcpy(cache, cache_data->data, length);
511 512 513
  cached_code_map_[*key] = std::unique_ptr<ScriptCompiler::CachedData>(
      new ScriptCompiler::CachedData(cache, length,
                                     ScriptCompiler::CachedData::BufferOwned));
514 515
}

516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
// Dummy external source stream which returns the whole source in one go.
// TODO(leszeks): Also test chunking the data.
class DummySourceStream : public v8::ScriptCompiler::ExternalSourceStream {
 public:
  explicit DummySourceStream(Local<String> source) : done_(false) {
    source_buffer_ = Utils::OpenHandle(*source)->ToCString(
        i::ALLOW_NULLS, i::FAST_STRING_TRAVERSAL, &source_length_);
  }

  size_t GetMoreData(const uint8_t** src) override {
    if (done_) {
      return 0;
    }
    *src = reinterpret_cast<uint8_t*>(source_buffer_.release());
    done_ = true;

    return source_length_;
  }

 private:
  int source_length_;
  std::unique_ptr<char[]> source_buffer_;
  bool done_;
};

class StreamingCompileTask final : public v8::Task {
 public:
  StreamingCompileTask(Isolate* isolate,
544
                       v8::ScriptCompiler::StreamedSource* streamed_source,
545
                       v8::ScriptType type)
546
      : isolate_(isolate),
547 548
        script_streaming_task_(v8::ScriptCompiler::StartStreaming(
            isolate, streamed_source, type)) {
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
    Shell::NotifyStartStreamingTask(isolate_);
  }

  void Run() override {
    script_streaming_task_->Run();
    // Signal that the task has finished using the task runner to wake the
    // message loop.
    Shell::PostForegroundTask(isolate_, std::make_unique<FinishTask>(isolate_));
  }

 private:
  class FinishTask final : public v8::Task {
   public:
    explicit FinishTask(Isolate* isolate) : isolate_(isolate) {}
    void Run() final { Shell::NotifyFinishStreamingTask(isolate_); }
    Isolate* isolate_;
  };

  Isolate* isolate_;
  std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask>
      script_streaming_task_;
};

572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
namespace {
template <class T>
MaybeLocal<T> CompileStreamed(Local<Context> context,
                              ScriptCompiler::StreamedSource* v8_source,
                              Local<String> full_source_string,
                              const ScriptOrigin& origin) {}

template <>
MaybeLocal<Script> CompileStreamed(Local<Context> context,
                                   ScriptCompiler::StreamedSource* v8_source,
                                   Local<String> full_source_string,
                                   const ScriptOrigin& origin) {
  return ScriptCompiler::Compile(context, v8_source, full_source_string,
                                 origin);
}

template <>
MaybeLocal<Module> CompileStreamed(Local<Context> context,
                                   ScriptCompiler::StreamedSource* v8_source,
                                   Local<String> full_source_string,
                                   const ScriptOrigin& origin) {
  return ScriptCompiler::CompileModule(context, v8_source, full_source_string,
                                       origin);
}

template <class T>
MaybeLocal<T> Compile(Local<Context> context, ScriptCompiler::Source* source,
                      ScriptCompiler::CompileOptions options) {}
template <>
MaybeLocal<Script> Compile(Local<Context> context,
                           ScriptCompiler::Source* source,
                           ScriptCompiler::CompileOptions options) {
  return ScriptCompiler::Compile(context, source, options);
}

template <>
MaybeLocal<Module> Compile(Local<Context> context,
                           ScriptCompiler::Source* source,
                           ScriptCompiler::CompileOptions options) {
  return ScriptCompiler::CompileModule(context->GetIsolate(), source, options);
}

}  // namespace

template <class T>
MaybeLocal<T> Shell::CompileString(Isolate* isolate, Local<Context> context,
                                   Local<String> source,
                                   const ScriptOrigin& origin) {
  if (options.streaming_compile) {
    v8::ScriptCompiler::StreamedSource streamed_source(
        std::make_unique<DummySourceStream>(source),
        v8::ScriptCompiler::StreamedSource::UTF8);
    PostBlockingBackgroundTask(std::make_unique<StreamingCompileTask>(
        isolate, &streamed_source,
626 627
        std::is_same<T, Module>::value ? v8::ScriptType::kModule
                                       : v8::ScriptType::kClassic));
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
    // Pump the loop until the streaming task completes.
    Shell::CompleteMessageLoop(isolate);
    return CompileStreamed<T>(context, &streamed_source, source, origin);
  }

  ScriptCompiler::CachedData* cached_code = nullptr;
  if (options.compile_options == ScriptCompiler::kConsumeCodeCache) {
    cached_code = LookupCodeCache(isolate, source);
  }
  ScriptCompiler::Source script_source(source, origin, cached_code);
  MaybeLocal<T> result =
      Compile<T>(context, &script_source,
                 cached_code ? ScriptCompiler::kConsumeCodeCache
                             : ScriptCompiler::kNoCompileOptions);
  if (cached_code) CHECK(!cached_code->rejected);
  return result;
}

646
// Executes a string within the current v8 context.
647
bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
648 649 650
                          Local<Value> name, PrintResult print_result,
                          ReportExceptions report_exceptions,
                          ProcessMessageQueue process_message_queue) {
651 652 653 654 655 656
  if (i::FLAG_parse_only) {
    i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
    i::VMState<PARSER> state(i_isolate);
    i::Handle<i::String> str = Utils::OpenHandle(*(source));

    // Set up ParseInfo.
657 658
    i::UnoptimizedCompileState compile_state(i_isolate);

659 660 661 662 663
    i::UnoptimizedCompileFlags flags =
        i::UnoptimizedCompileFlags::ForToplevelCompile(
            i_isolate, true, i::construct_language_mode(i::FLAG_use_strict),
            i::REPLMode::kNo);

664 665 666 667
    if (options.compile_options == v8::ScriptCompiler::kEagerCompile) {
      flags.set_is_eager(true);
    }

668
    i::ParseInfo parse_info(i_isolate, flags, &compile_state);
669 670

    i::Handle<i::Script> script = parse_info.CreateScript(
671
        i_isolate, str, i::kNullMaybeHandle, ScriptOriginOptions());
672 673 674 675 676 677
    if (!i::parsing::ParseProgram(&parse_info, script, i_isolate,
                                  i::parsing::ReportStatisticsMode::kYes)) {
      parse_info.pending_error_handler()->PrepareErrors(
          i_isolate, parse_info.ast_value_factory());
      parse_info.pending_error_handler()->ReportErrors(i_isolate, script);

678 679 680 681 682 683
      fprintf(stderr, "Failed parsing\n");
      return false;
    }
    return true;
  }

684
  HandleScope handle_scope(isolate);
685
  TryCatch try_catch(isolate);
686
  try_catch.SetVerbose(report_exceptions == kReportExceptions);
687

688
  MaybeLocal<Value> maybe_result;
689
  bool success = true;
690
  {
691 692
    PerIsolateData* data = PerIsolateData::Get(isolate);
    Local<Context> realm =
693
        Local<Context>::New(isolate, data->realms_[data->realm_current_]);
694
    Context::Scope context_scope(realm);
695
    MaybeLocal<Script> maybe_script;
696
    Local<Context> context(isolate->GetCurrentContext());
697
    ScriptOrigin origin(isolate, name);
698

699
    Local<Script> script;
700 701
    if (!CompileString<Script>(isolate, context, source, origin)
             .ToLocal(&script)) {
702
      return false;
703
    }
704

705 706 707 708
    if (options.code_cache_options ==
        ShellOptions::CodeCacheOptions::kProduceCache) {
      // Serialize and store it in memory for the next execution.
      ScriptCompiler::CachedData* cached_data =
709
          ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
710 711 712
      StoreInCodeCache(isolate, source, cached_data);
      delete cached_data;
    }
713
    maybe_result = script->Run(realm);
714 715
    if (options.code_cache_options ==
        ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute) {
716 717
      // Serialize and store it in memory for the next execution.
      ScriptCompiler::CachedData* cached_data =
718
          ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
719 720 721
      StoreInCodeCache(isolate, source, cached_data);
      delete cached_data;
    }
722 723 724 725
    if (process_message_queue) {
      if (!EmptyMessageQueues(isolate)) success = false;
      if (!HandleUnhandledPromiseRejections(isolate)) success = false;
    }
726 727
    data->realm_current_ = data->realm_switch_;
  }
728 729
  Local<Value> result;
  if (!maybe_result.ToLocal(&result)) {
730 731 732
    DCHECK(try_catch.HasCaught());
    return false;
  }
733
  // It's possible that a FinalizationRegistry cleanup task threw an error.
734
  if (try_catch.HasCaught()) success = false;
735 736 737 738 739
  if (print_result) {
    if (options.test_shell) {
      if (!result->IsUndefined()) {
        // If all went well and the result wasn't undefined then print
        // the returned value.
740
        v8::String::Utf8Value str(isolate, result);
741 742
        fwrite(*str, sizeof(**str), str.length(), stdout);
        printf("\n");
743
      }
744
    } else {
745
      v8::String::Utf8Value str(isolate, Stringify(isolate, result));
746 747
      fwrite(*str, sizeof(**str), str.length(), stdout);
      printf("\n");
748 749
    }
  }
750
  return success;
751 752
}

753 754
namespace {

755 756
std::string ToSTLString(Isolate* isolate, Local<String> v8_str) {
  String::Utf8Value utf8(isolate, v8_str);
757 758 759 760 761 762
  // Should not be able to fail since the input is a String.
  CHECK(*utf8);
  return *utf8;
}

bool IsAbsolutePath(const std::string& path) {
763
#if defined(_WIN32) || defined(_WIN64)
764
  // This is an incorrect approximation, but should
765
  // work for all our test-running cases.
766
  return path.find(':') != std::string::npos;
767 768 769 770 771 772 773 774
#else
  return path[0] == '/';
#endif
}

std::string GetWorkingDirectory() {
#if defined(_WIN32) || defined(_WIN64)
  char system_buffer[MAX_PATH];
775 776
  // Unicode paths are unsupported, which is fine as long as
  // the test directory doesn't include any such paths.
777
  DWORD len = GetCurrentDirectoryA(MAX_PATH, system_buffer);
778
  CHECK_GT(len, 0);
779 780 781 782 783 784 785 786
  return system_buffer;
#else
  char curdir[PATH_MAX];
  CHECK_NOT_NULL(getcwd(curdir, PATH_MAX));
  return curdir;
#endif
}

787
// Returns the directory part of path, without the trailing '/'.
788
std::string DirName(const std::string& path) {
789 790
  DCHECK(IsAbsolutePath(path));
  size_t last_slash = path.find_last_of('/');
791
  DCHECK(last_slash != std::string::npos);
792 793 794
  return path.substr(0, last_slash);
}

795 796 797 798 799
// Resolves path to an absolute path if necessary, and does some
// normalization (eliding references to the current directory
// and replacing backslashes with slashes).
std::string NormalizePath(const std::string& path,
                          const std::string& dir_name) {
800
  std::string absolute_path;
801
  if (IsAbsolutePath(path)) {
802
    absolute_path = path;
803
  } else {
804 805 806 807 808 809 810 811 812 813 814 815
    absolute_path = dir_name + '/' + path;
  }
  std::replace(absolute_path.begin(), absolute_path.end(), '\\', '/');
  std::vector<std::string> segments;
  std::istringstream segment_stream(absolute_path);
  std::string segment;
  while (std::getline(segment_stream, segment, '/')) {
    if (segment == "..") {
      segments.pop_back();
    } else if (segment != ".") {
      segments.push_back(segment);
    }
816
  }
817 818 819 820 821 822
  // Join path segments.
  std::ostringstream os;
  std::copy(segments.begin(), segments.end() - 1,
            std::ostream_iterator<std::string>(os, "/"));
  os << *segments.rbegin();
  return os.str();
823 824
}

825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841
// Per-context Module data, allowing sharing of module maps
// across top-level module loads.
class ModuleEmbedderData {
 private:
  class ModuleGlobalHash {
   public:
    explicit ModuleGlobalHash(Isolate* isolate) : isolate_(isolate) {}
    size_t operator()(const Global<Module>& module) const {
      return module.Get(isolate_)->GetIdentityHash();
    }

   private:
    Isolate* isolate_;
  };

 public:
  explicit ModuleEmbedderData(Isolate* isolate)
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
      : module_to_specifier_map(10, ModuleGlobalHash(isolate)),
        json_module_to_parsed_json_map(10, ModuleGlobalHash(isolate)) {}

  static ModuleType ModuleTypeFromImportAssertions(
      Local<Context> context, Local<FixedArray> import_assertions,
      bool hasPositions) {
    Isolate* isolate = context->GetIsolate();
    const int kV8AssertionEntrySize = hasPositions ? 3 : 2;
    for (int i = 0; i < import_assertions->Length();
         i += kV8AssertionEntrySize) {
      Local<String> v8_assertion_key =
          import_assertions->Get(context, i).As<v8::String>();
      std::string assertion_key = ToSTLString(isolate, v8_assertion_key);

      if (assertion_key == "type") {
        Local<String> v8_assertion_value =
            import_assertions->Get(context, i + 1).As<String>();
        std::string assertion_value = ToSTLString(isolate, v8_assertion_value);
        if (assertion_value == "json") {
          return ModuleType::kJSON;
        } else {
          // JSON is currently the only supported non-JS type
          return ModuleType::kInvalid;
        }
      }
    }
868

869 870 871 872 873 874
    // If no type is asserted, default to JS.
    return ModuleType::kJavaScript;
  }

  // Map from (normalized module specifier, module type) pair to Module.
  std::map<std::pair<std::string, ModuleType>, Global<Module>> module_map;
875
  // Map from Module to its URL as defined in the ScriptOrigin
876
  std::unordered_map<Global<Module>, std::string, ModuleGlobalHash>
877
      module_to_specifier_map;
878 879 880 881
  // Map from JSON Module to its parsed content, for use in module
  // JSONModuleEvaluationSteps
  std::unordered_map<Global<Module>, Global<Value>, ModuleGlobalHash>
      json_module_to_parsed_json_map;
882 883
};

884
enum { kModuleEmbedderDataIndex, kInspectorClientIndex };
885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900

void InitializeModuleEmbedderData(Local<Context> context) {
  context->SetAlignedPointerInEmbedderData(
      kModuleEmbedderDataIndex, new ModuleEmbedderData(context->GetIsolate()));
}

ModuleEmbedderData* GetModuleDataFromContext(Local<Context> context) {
  return static_cast<ModuleEmbedderData*>(
      context->GetAlignedPointerFromEmbedderData(kModuleEmbedderDataIndex));
}

void DisposeModuleEmbedderData(Local<Context> context) {
  delete GetModuleDataFromContext(context);
  context->SetAlignedPointerInEmbedderData(kModuleEmbedderDataIndex, nullptr);
}

901 902
MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
                                         Local<String> specifier,
903
                                         Local<FixedArray> import_assertions,
904
                                         Local<Module> referrer) {
905
  Isolate* isolate = context->GetIsolate();
906
  ModuleEmbedderData* d = GetModuleDataFromContext(context);
907 908 909 910 911
  auto specifier_it =
      d->module_to_specifier_map.find(Global<Module>(isolate, referrer));
  CHECK(specifier_it != d->module_to_specifier_map.end());
  std::string absolute_path = NormalizePath(ToSTLString(isolate, specifier),
                                            DirName(specifier_it->second));
912 913 914 915 916
  ModuleType module_type = ModuleEmbedderData::ModuleTypeFromImportAssertions(
      context, import_assertions, true);
  auto module_it =
      d->module_map.find(std::make_pair(absolute_path, module_type));
  CHECK(module_it != d->module_map.end());
917
  return module_it->second.Get(isolate);
918 919 920 921
}

}  // anonymous namespace

922 923
MaybeLocal<Module> Shell::FetchModuleTree(Local<Module> referrer,
                                          Local<Context> context,
924 925
                                          const std::string& file_name,
                                          ModuleType module_type) {
926
  DCHECK(IsAbsolutePath(file_name));
927
  Isolate* isolate = context->GetIsolate();
928
  Local<String> source_text = ReadFile(isolate, file_name.c_str());
929 930 931 932 933 934 935 936
  if (source_text.IsEmpty() && options.fuzzy_module_file_extensions) {
    std::string fallback_file_name = file_name + ".js";
    source_text = ReadFile(isolate, fallback_file_name.c_str());
    if (source_text.IsEmpty()) {
      fallback_file_name = file_name + ".mjs";
      source_text = ReadFile(isolate, fallback_file_name.c_str());
    }
  }
937 938

  ModuleEmbedderData* d = GetModuleDataFromContext(context);
939
  if (source_text.IsEmpty()) {
940 941 942 943 944 945 946
    std::string msg = "d8: Error reading  module from " + file_name;
    if (!referrer.IsEmpty()) {
      auto specifier_it =
          d->module_to_specifier_map.find(Global<Module>(isolate, referrer));
      CHECK(specifier_it != d->module_to_specifier_map.end());
      msg += "\n    imported by " + specifier_it->second;
    }
947 948
    Throw(isolate, msg.c_str());
    return MaybeLocal<Module>();
949 950
  }
  ScriptOrigin origin(
951 952
      isolate, String::NewFromUtf8(isolate, file_name.c_str()).ToLocalChecked(),
      0, 0, false, -1, Local<Value>(), false, false, true);
953

954
  Local<Module> module;
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
  if (module_type == ModuleType::kJavaScript) {
    ScriptCompiler::Source source(source_text, origin);
    if (!CompileString<Module>(isolate, context, source_text, origin)
             .ToLocal(&module)) {
      return MaybeLocal<Module>();
    }
  } else if (module_type == ModuleType::kJSON) {
    Local<Value> parsed_json;
    if (!v8::JSON::Parse(context, source_text).ToLocal(&parsed_json)) {
      return MaybeLocal<Module>();
    }

    std::vector<Local<String>> export_names{
        String::NewFromUtf8(isolate, "default").ToLocalChecked()};

    module = v8::Module::CreateSyntheticModule(
        isolate,
        String::NewFromUtf8(isolate, file_name.c_str()).ToLocalChecked(),
        export_names, Shell::JSONModuleEvaluationSteps);

    CHECK(d->json_module_to_parsed_json_map
              .insert(std::make_pair(Global<Module>(isolate, module),
                                     Global<Value>(isolate, parsed_json)))
              .second);
  } else {
    UNREACHABLE();
981
  }
982

983 984 985
  CHECK(d->module_map
            .insert(std::make_pair(std::make_pair(file_name, module_type),
                                   Global<Module>(isolate, module)))
986
            .second);
987 988 989
  CHECK(d->module_to_specifier_map
            .insert(std::make_pair(Global<Module>(isolate, module), file_name))
            .second);
990 991 992

  std::string dir_name = DirName(file_name);

993 994 995 996 997
  Local<FixedArray> module_requests = module->GetModuleRequests();
  for (int i = 0, length = module_requests->Length(); i < length; ++i) {
    Local<ModuleRequest> module_request =
        module_requests->Get(context, i).As<ModuleRequest>();
    Local<String> name = module_request->GetSpecifier();
998 999
    std::string absolute_path =
        NormalizePath(ToSTLString(isolate, name), dir_name);
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
    Local<FixedArray> import_assertions = module_request->GetImportAssertions();
    ModuleType request_module_type =
        ModuleEmbedderData::ModuleTypeFromImportAssertions(
            context, import_assertions, true);

    if (request_module_type == ModuleType::kInvalid) {
      Throw(isolate, "Invalid module type was asserted");
      return MaybeLocal<Module>();
    }

    if (d->module_map.count(
            std::make_pair(absolute_path, request_module_type))) {
      continue;
    }

    if (FetchModuleTree(module, context, absolute_path, request_module_type)
            .IsEmpty()) {
1017
      return MaybeLocal<Module>();
1018 1019 1020 1021 1022 1023
    }
  }

  return module;
}

1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
MaybeLocal<Value> Shell::JSONModuleEvaluationSteps(Local<Context> context,
                                                   Local<Module> module) {
  Isolate* isolate = context->GetIsolate();

  ModuleEmbedderData* d = GetModuleDataFromContext(context);
  auto json_value_it =
      d->json_module_to_parsed_json_map.find(Global<Module>(isolate, module));
  CHECK(json_value_it != d->json_module_to_parsed_json_map.end());
  Local<Value> json_value = json_value_it->second.Get(isolate);

  TryCatch try_catch(isolate);
  Maybe<bool> result = module->SetSyntheticModuleExport(
      isolate,
      String::NewFromUtf8Literal(isolate, "default",
                                 NewStringType::kInternalized),
      json_value);

  // Setting the default export should never fail.
  CHECK(!try_catch.HasCaught());
  CHECK(!result.IsNothing() && result.FromJust());

  if (i::FLAG_harmony_top_level_await) {
    Local<Promise::Resolver> resolver =
        Promise::Resolver::New(context).ToLocalChecked();
    resolver->Resolve(context, Undefined(isolate)).ToChecked();
    return resolver->GetPromise();
  }

  return Undefined(isolate);
}

1055 1056 1057
struct DynamicImportData {
  DynamicImportData(Isolate* isolate_, Local<String> referrer_,
                    Local<String> specifier_,
1058
                    Local<FixedArray> import_assertions_,
1059
                    Local<Promise::Resolver> resolver_)
1060 1061 1062
      : isolate(isolate_) {
    referrer.Reset(isolate, referrer_);
    specifier.Reset(isolate, specifier_);
1063
    import_assertions.Reset(isolate, import_assertions_);
1064
    resolver.Reset(isolate, resolver_);
1065 1066 1067 1068 1069
  }

  Isolate* isolate;
  Global<String> referrer;
  Global<String> specifier;
1070
  Global<FixedArray> import_assertions;
1071
  Global<Promise::Resolver> resolver;
1072 1073
};

1074
namespace {
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
struct ModuleResolutionData {
  ModuleResolutionData(Isolate* isolate_, Local<Value> module_namespace_,
                       Local<Promise::Resolver> resolver_)
      : isolate(isolate_) {
    module_namespace.Reset(isolate, module_namespace_);
    resolver.Reset(isolate, resolver_);
  }

  Isolate* isolate;
  Global<Value> module_namespace;
  Global<Promise::Resolver> resolver;
};

1088
}  // namespace
1089

1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
void Shell::ModuleResolutionSuccessCallback(
    const FunctionCallbackInfo<Value>& info) {
  std::unique_ptr<ModuleResolutionData> module_resolution_data(
      static_cast<ModuleResolutionData*>(
          info.Data().As<v8::External>()->Value()));
  Isolate* isolate(module_resolution_data->isolate);
  HandleScope handle_scope(isolate);

  Local<Promise::Resolver> resolver(
      module_resolution_data->resolver.Get(isolate));
  Local<Value> module_namespace(
      module_resolution_data->module_namespace.Get(isolate));

  PerIsolateData* data = PerIsolateData::Get(isolate);
  Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
  Context::Scope context_scope(realm);

  resolver->Resolve(realm, module_namespace).ToChecked();
}

void Shell::ModuleResolutionFailureCallback(
    const FunctionCallbackInfo<Value>& info) {
  std::unique_ptr<ModuleResolutionData> module_resolution_data(
      static_cast<ModuleResolutionData*>(
          info.Data().As<v8::External>()->Value()));
  Isolate* isolate(module_resolution_data->isolate);
  HandleScope handle_scope(isolate);

  Local<Promise::Resolver> resolver(
      module_resolution_data->resolver.Get(isolate));

  PerIsolateData* data = PerIsolateData::Get(isolate);
  Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
  Context::Scope context_scope(realm);

  DCHECK_EQ(info.Length(), 1);
  resolver->Reject(realm, info[0]).ToChecked();
}

1129
MaybeLocal<Promise> Shell::HostImportModuleDynamically(
1130
    Local<Context> context, Local<ScriptOrModule> referrer,
1131
    Local<String> specifier, Local<FixedArray> import_assertions) {
1132
  Isolate* isolate = context->GetIsolate();
1133 1134 1135 1136 1137

  MaybeLocal<Promise::Resolver> maybe_resolver =
      Promise::Resolver::New(context);
  Local<Promise::Resolver> resolver;
  if (maybe_resolver.ToLocal(&resolver)) {
1138 1139 1140
    DynamicImportData* data =
        new DynamicImportData(isolate, referrer->GetResourceName().As<String>(),
                              specifier, import_assertions, resolver);
1141
    PerIsolateData::Get(isolate)->AddDynamicImportData(data);
1142 1143 1144 1145 1146
    isolate->EnqueueMicrotask(Shell::DoHostImportModuleDynamically, data);
    return resolver->GetPromise();
  }

  return MaybeLocal<Promise>();
1147 1148
}

1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
void Shell::HostInitializeImportMetaObject(Local<Context> context,
                                           Local<Module> module,
                                           Local<Object> meta) {
  Isolate* isolate = context->GetIsolate();
  HandleScope handle_scope(isolate);

  ModuleEmbedderData* d = GetModuleDataFromContext(context);
  auto specifier_it =
      d->module_to_specifier_map.find(Global<Module>(isolate, module));
  CHECK(specifier_it != d->module_to_specifier_map.end());

  Local<String> url_key =
1161
      String::NewFromUtf8Literal(isolate, "url", NewStringType::kInternalized);
1162
  Local<String> url = String::NewFromUtf8(isolate, specifier_it->second.c_str())
1163 1164 1165 1166
                          .ToLocalChecked();
  meta->CreateDataProperty(context, url_key, url).ToChecked();
}

1167
void Shell::DoHostImportModuleDynamically(void* import_data) {
1168 1169 1170
  DynamicImportData* import_data_ =
      static_cast<DynamicImportData*>(import_data);

1171 1172 1173 1174 1175
  Isolate* isolate(import_data_->isolate);
  HandleScope handle_scope(isolate);

  Local<String> referrer(import_data_->referrer.Get(isolate));
  Local<String> specifier(import_data_->specifier.Get(isolate));
1176 1177
  Local<FixedArray> import_assertions(
      import_data_->import_assertions.Get(isolate));
1178
  Local<Promise::Resolver> resolver(import_data_->resolver.Get(isolate));
1179 1180

  PerIsolateData* data = PerIsolateData::Get(isolate);
1181 1182
  PerIsolateData::Get(isolate)->DeleteDynamicImportData(import_data_);

1183 1184 1185
  Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
  Context::Scope context_scope(realm);

1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198
  ModuleType module_type = ModuleEmbedderData::ModuleTypeFromImportAssertions(
      realm, import_assertions, false);

  TryCatch try_catch(isolate);
  try_catch.SetVerbose(true);

  if (module_type == ModuleType::kInvalid) {
    Throw(isolate, "Invalid module type was asserted");
    CHECK(try_catch.HasCaught());
    resolver->Reject(realm, try_catch.Exception()).ToChecked();
    return;
  }

1199
  std::string source_url = ToSTLString(isolate, referrer);
1200
  std::string dir_name =
1201
      DirName(NormalizePath(source_url, GetWorkingDirectory()));
1202
  std::string file_name = ToSTLString(isolate, specifier);
1203
  std::string absolute_path = NormalizePath(file_name, dir_name);
1204 1205 1206

  ModuleEmbedderData* d = GetModuleDataFromContext(realm);
  Local<Module> root_module;
1207 1208 1209
  auto module_it =
      d->module_map.find(std::make_pair(absolute_path, module_type));
  if (module_it != d->module_map.end()) {
1210
    root_module = module_it->second.Get(isolate);
1211 1212
  } else if (!FetchModuleTree(Local<Module>(), realm, absolute_path,
                              module_type)
1213
                  .ToLocal(&root_module)) {
1214
    CHECK(try_catch.HasCaught());
1215
    resolver->Reject(realm, try_catch.Exception()).ToChecked();
1216 1217 1218 1219
    return;
  }

  MaybeLocal<Value> maybe_result;
1220 1221
  if (root_module->InstantiateModule(realm, ResolveModuleCallback)
          .FromMaybe(false)) {
1222
    maybe_result = root_module->Evaluate(realm);
1223
    CHECK_IMPLIES(i::FLAG_harmony_top_level_await, !maybe_result.IsEmpty());
1224 1225 1226
    EmptyMessageQueues(isolate);
  }

1227 1228
  Local<Value> result;
  if (!maybe_result.ToLocal(&result)) {
1229
    DCHECK(try_catch.HasCaught());
1230
    resolver->Reject(realm, try_catch.Exception()).ToChecked();
1231 1232 1233
    return;
  }

1234
  Local<Value> module_namespace = root_module->GetModuleNamespace();
1235
  if (i::FLAG_harmony_top_level_await) {
1236
    Local<Promise> result_promise(result.As<Promise>());
1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255
    if (result_promise->State() == Promise::kRejected) {
      resolver->Reject(realm, result_promise->Result()).ToChecked();
      return;
    }

    // Setup callbacks, and then chain them to the result promise.
    // ModuleResolutionData will be deleted by the callbacks.
    auto module_resolution_data =
        new ModuleResolutionData(isolate, module_namespace, resolver);
    Local<v8::External> edata = External::New(isolate, module_resolution_data);
    Local<Function> callback_success;
    CHECK(Function::New(realm, ModuleResolutionSuccessCallback, edata)
              .ToLocal(&callback_success));
    Local<Function> callback_failure;
    CHECK(Function::New(realm, ModuleResolutionFailureCallback, edata)
              .ToLocal(&callback_failure));
    result_promise->Then(realm, callback_success, callback_failure)
        .ToLocalChecked();
  } else {
Camillo Bruni's avatar
Camillo Bruni committed
1256
    // TODO(cbruni): Clean up exception handling after introducing new
1257 1258 1259 1260
    // API for evaluating async modules.
    DCHECK(!try_catch.HasCaught());
    resolver->Resolve(realm, module_namespace).ToChecked();
  }
1261 1262
}

1263 1264 1265
bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
  HandleScope handle_scope(isolate);

1266 1267 1268 1269
  PerIsolateData* data = PerIsolateData::Get(isolate);
  Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
  Context::Scope context_scope(realm);

1270
  std::string absolute_path = NormalizePath(file_name, GetWorkingDirectory());
1271

1272 1273 1274 1275
  // Use a non-verbose TryCatch and report exceptions manually using
  // Shell::ReportException, because some errors (such as file errors) are
  // thrown without entering JS and thus do not trigger
  // isolate->ReportPendingMessages().
1276 1277
  TryCatch try_catch(isolate);

1278
  Local<Module> root_module;
1279

1280 1281
  if (!FetchModuleTree(Local<Module>(), realm, absolute_path,
                       ModuleType::kJavaScript)
1282
           .ToLocal(&root_module)) {
1283 1284
    CHECK(try_catch.HasCaught());
    ReportException(isolate, &try_catch);
1285 1286 1287 1288
    return false;
  }

  MaybeLocal<Value> maybe_result;
1289 1290
  if (root_module->InstantiateModule(realm, ResolveModuleCallback)
          .FromMaybe(false)) {
1291
    maybe_result = root_module->Evaluate(realm);
1292
    CHECK_IMPLIES(i::FLAG_harmony_top_level_await, !maybe_result.IsEmpty());
1293
    EmptyMessageQueues(isolate);
1294 1295 1296 1297 1298 1299 1300
  }
  Local<Value> result;
  if (!maybe_result.ToLocal(&result)) {
    DCHECK(try_catch.HasCaught());
    ReportException(isolate, &try_catch);
    return false;
  }
1301 1302
  if (i::FLAG_harmony_top_level_await) {
    // Loop until module execution finishes
Camillo Bruni's avatar
Camillo Bruni committed
1303
    // TODO(cbruni): This is a bit wonky. "Real" engines would not be
1304
    // able to just busy loop waiting for execution to finish.
1305
    Local<Promise> result_promise(result.As<Promise>());
1306
    while (result_promise->State() == Promise::kPending) {
1307
      isolate->PerformMicrotaskCheckpoint();
1308 1309 1310 1311 1312
    }

    if (result_promise->State() == Promise::kRejected) {
      // If the exception has been caught by the promise pipeline, we rethrow
      // here in order to ReportException.
Camillo Bruni's avatar
Camillo Bruni committed
1313
      // TODO(cbruni): Clean this up after we create a new API for the case
1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324
      // where TLA is enabled.
      if (!try_catch.HasCaught()) {
        isolate->ThrowException(result_promise->Result());
      } else {
        DCHECK_EQ(try_catch.Exception(), result_promise->Result());
      }
      ReportException(isolate, &try_catch);
      return false;
    }
  }

1325 1326 1327
  DCHECK(!try_catch.HasCaught());
  return true;
}
1328

1329 1330 1331 1332 1333 1334
PerIsolateData::PerIsolateData(Isolate* isolate)
    : isolate_(isolate), realms_(nullptr) {
  isolate->SetData(0, this);
  if (i::FLAG_expose_async_hooks) {
    async_hooks_wrapper_ = new AsyncHooks(isolate);
  }
1335
  ignore_unhandled_promises_ = false;
1336 1337 1338 1339 1340 1341 1342
}

PerIsolateData::~PerIsolateData() {
  isolate_->SetData(0, nullptr);  // Not really needed, just to be sure...
  if (i::FLAG_expose_async_hooks) {
    delete async_hooks_wrapper_;  // This uses the isolate
  }
1343 1344 1345 1346 1347
#if defined(LEAK_SANITIZER)
  for (DynamicImportData* data : import_data_) {
    delete data;
  }
#endif
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369
}

void PerIsolateData::SetTimeout(Local<Function> callback,
                                Local<Context> context) {
  set_timeout_callbacks_.emplace(isolate_, callback);
  set_timeout_contexts_.emplace(isolate_, context);
}

MaybeLocal<Function> PerIsolateData::GetTimeoutCallback() {
  if (set_timeout_callbacks_.empty()) return MaybeLocal<Function>();
  Local<Function> result = set_timeout_callbacks_.front().Get(isolate_);
  set_timeout_callbacks_.pop();
  return result;
}

MaybeLocal<Context> PerIsolateData::GetTimeoutContext() {
  if (set_timeout_contexts_.empty()) return MaybeLocal<Context>();
  Local<Context> result = set_timeout_contexts_.front().Get(isolate_);
  set_timeout_contexts_.pop();
  return result;
}

1370
void PerIsolateData::RemoveUnhandledPromise(Local<Promise> promise) {
1371
  if (ignore_unhandled_promises_) return;
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385
  // Remove handled promises from the list
  DCHECK_EQ(promise->GetIsolate(), isolate_);
  for (auto it = unhandled_promises_.begin(); it != unhandled_promises_.end();
       ++it) {
    v8::Local<v8::Promise> unhandled_promise = std::get<0>(*it).Get(isolate_);
    if (unhandled_promise == promise) {
      unhandled_promises_.erase(it--);
    }
  }
}

void PerIsolateData::AddUnhandledPromise(Local<Promise> promise,
                                         Local<Message> message,
                                         Local<Value> exception) {
1386
  if (ignore_unhandled_promises_) return;
1387
  DCHECK_EQ(promise->GetIsolate(), isolate_);
1388 1389 1390
  unhandled_promises_.emplace_back(v8::Global<v8::Promise>(isolate_, promise),
                                   v8::Global<v8::Message>(isolate_, message),
                                   v8::Global<v8::Value>(isolate_, exception));
1391 1392 1393
}

int PerIsolateData::HandleUnhandledPromiseRejections() {
1394 1395 1396
  // Avoid recursive calls to HandleUnhandledPromiseRejections.
  if (ignore_unhandled_promises_) return 0;
  ignore_unhandled_promises_ = true;
1397
  v8::HandleScope scope(isolate_);
1398
  // Ignore promises that get added during error reporting.
1399 1400
  size_t i = 0;
  for (; i < unhandled_promises_.size(); i++) {
1401
    const auto& tuple = unhandled_promises_[i];
1402 1403 1404 1405 1406
    Local<v8::Message> message = std::get<1>(tuple).Get(isolate_);
    Local<v8::Value> value = std::get<2>(tuple).Get(isolate_);
    Shell::ReportException(isolate_, message, value);
  }
  unhandled_promises_.clear();
1407
  ignore_unhandled_promises_ = false;
1408
  return static_cast<int>(i);
1409 1410
}

1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422
void PerIsolateData::AddDynamicImportData(DynamicImportData* data) {
#if defined(LEAK_SANITIZER)
  import_data_.insert(data);
#endif
}
void PerIsolateData::DeleteDynamicImportData(DynamicImportData* data) {
#if defined(LEAK_SANITIZER)
  import_data_.erase(data);
#endif
  delete data;
}

1423 1424 1425 1426
PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
  data_->realm_count_ = 1;
  data_->realm_current_ = 0;
  data_->realm_switch_ = 0;
1427
  data_->realms_ = new Global<Context>[1];
1428
  data_->realms_[0].Reset(data_->isolate_,
1429
                          data_->isolate_->GetEnteredOrMicrotaskContext());
1430 1431 1432
}

PerIsolateData::RealmScope::~RealmScope() {
1433 1434 1435 1436
  // Drop realms to avoid keeping them alive. We don't dispose the
  // module embedder data for the first realm here, but instead do
  // it in RunShell or in RunMain, if not running in interactive mode
  for (int i = 1; i < data_->realm_count_; ++i) {
1437 1438 1439 1440
    Global<Context>& realm = data_->realms_[i];
    if (realm.IsEmpty()) continue;
    DisposeModuleEmbedderData(realm.Get(data_->isolate_));
  }
1441
  data_->realm_count_ = 0;
1442 1443 1444
  delete[] data_->realms_;
}

1445
int PerIsolateData::RealmFind(Local<Context> context) {
1446 1447 1448 1449 1450 1451
  for (int i = 0; i < realm_count_; ++i) {
    if (realms_[i] == context) return i;
  }
  return -1;
}

1452
int PerIsolateData::RealmIndexOrThrow(
1453
    const v8::FunctionCallbackInfo<v8::Value>& args, int arg_offset) {
1454 1455 1456 1457
  if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) {
    Throw(args.GetIsolate(), "Invalid argument");
    return -1;
  }
1458 1459 1460 1461
  int index = args[arg_offset]
                  ->Int32Value(args.GetIsolate()->GetCurrentContext())
                  .FromMaybe(-1);
  if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) {
1462 1463 1464 1465 1466 1467
    Throw(args.GetIsolate(), "Invalid realm index");
    return -1;
  }
  return index;
}

1468
// performance.now() returns a time stamp as double, measured in milliseconds.
1469 1470
// When FLAG_verify_predictable mode is enabled it returns result of
// v8::Platform::MonotonicallyIncreasingTime().
1471
void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
1472
  if (i::FLAG_verify_predictable) {
1473
    args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime());
1474
  } else {
1475 1476
    base::TimeDelta delta =
        base::TimeTicks::HighResolutionNow() - kInitialTicks;
1477 1478
    args.GetReturnValue().Set(delta.InMillisecondsF());
  }
1479 1480
}

1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
// performance.measureMemory() implements JavaScript Memory API proposal.
// See https://github.com/ulan/javascript-agent-memory/blob/master/explainer.md.
void Shell::PerformanceMeasureMemory(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::MeasureMemoryMode mode = v8::MeasureMemoryMode::kSummary;
  v8::Isolate* isolate = args.GetIsolate();
  Local<Context> context = isolate->GetCurrentContext();
  if (args.Length() >= 1 && args[0]->IsObject()) {
    Local<Object> object = args[0].As<Object>();
    Local<Value> value = TryGetValue(isolate, context, object, "detailed")
                             .FromMaybe(Local<Value>());
    if (!value.IsEmpty() && value->IsBoolean() &&
        value->BooleanValue(isolate)) {
      mode = v8::MeasureMemoryMode::kDetailed;
    }
  }
1497 1498 1499 1500 1501 1502 1503
  Local<v8::Promise::Resolver> promise_resolver =
      v8::Promise::Resolver::New(context).ToLocalChecked();
  args.GetIsolate()->MeasureMemory(
      v8::MeasureMemoryDelegate::Default(isolate, context, promise_resolver,
                                         mode),
      v8::MeasureMemoryExecution::kEager);
  args.GetReturnValue().Set(promise_resolver->GetPromise());
1504 1505
}

1506
// Realm.current() returns the index of the currently active realm.
1507
void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) {
1508 1509
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1510
  int index = data->RealmFind(isolate->GetEnteredOrMicrotaskContext());
1511 1512
  if (index == -1) return;
  args.GetReturnValue().Set(index);
1513 1514 1515
}

// Realm.owner(o) returns the index of the realm that created o.
1516
void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) {
1517 1518 1519
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
  if (args.Length() < 1 || !args[0]->IsObject()) {
1520
    Throw(args.GetIsolate(), "Invalid argument");
1521
    return;
1522
  }
1523 1524 1525 1526 1527 1528 1529 1530
  Local<Object> object =
      args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked();
  i::Handle<i::JSReceiver> i_object = Utils::OpenHandle(*object);
  if (i_object->IsJSGlobalProxy() &&
      i::Handle<i::JSGlobalProxy>::cast(i_object)->IsDetached()) {
    return;
  }
  int index = data->RealmFind(object->CreationContext());
1531 1532
  if (index == -1) return;
  args.GetReturnValue().Set(index);
1533 1534 1535 1536
}

// Realm.global(i) returns the global object of realm i.
// (Note that properties of global objects cannot be read/written cross-realm.)
1537
void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1538
  PerIsolateData* data = PerIsolateData::Get(args.GetIsolate());
1539 1540
  int index = data->RealmIndexOrThrow(args, 0);
  if (index == -1) return;
1541 1542
  args.GetReturnValue().Set(
      Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global());
1543 1544
}

1545
MaybeLocal<Context> Shell::CreateRealm(
1546 1547
    const v8::FunctionCallbackInfo<v8::Value>& args, int index,
    v8::MaybeLocal<Value> global_object) {
1548
  Isolate* isolate = args.GetIsolate();
1549
  TryCatch try_catch(isolate);
1550
  PerIsolateData* data = PerIsolateData::Get(isolate);
1551 1552 1553 1554 1555 1556 1557 1558 1559 1560
  if (index < 0) {
    Global<Context>* old_realms = data->realms_;
    index = data->realm_count_;
    data->realms_ = new Global<Context>[++data->realm_count_];
    for (int i = 0; i < index; ++i) {
      data->realms_[i].Reset(isolate, old_realms[i]);
      old_realms[i].Reset();
    }
    delete[] old_realms;
  }
1561
  Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1562
  Local<Context> context =
1563
      Context::New(isolate, nullptr, global_template, global_object);
1564 1565
  DCHECK(!try_catch.HasCaught());
  if (context.IsEmpty()) return MaybeLocal<Context>();
1566
  InitializeModuleEmbedderData(context);
1567
  data->realms_[index].Reset(isolate, context);
1568
  args.GetReturnValue().Set(index);
1569
  return context;
1570 1571
}

1572 1573 1574 1575
void Shell::DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args,
                         int index) {
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1576 1577
  Local<Context> context = data->realms_[index].Get(isolate);
  DisposeModuleEmbedderData(context);
1578
  data->realms_[index].Reset();
1579 1580
  // ContextDisposedNotification expects the disposed context to be entered.
  v8::Context::Scope scope(context);
1581
  isolate->ContextDisposedNotification();
1582
  isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime());
1583 1584
}

1585 1586 1587
// Realm.create() creates a new realm with a distinct security token
// and returns its index.
void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1588
  CreateRealm(args, -1, v8::MaybeLocal<Value>());
1589 1590 1591 1592 1593 1594 1595
}

// Realm.createAllowCrossRealmAccess() creates a new realm with the same
// security token as the current realm.
void Shell::RealmCreateAllowCrossRealmAccess(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  Local<Context> context;
1596
  if (CreateRealm(args, -1, v8::MaybeLocal<Value>()).ToLocal(&context)) {
1597
    context->SetSecurityToken(
1598
        args.GetIsolate()->GetEnteredOrMicrotaskContext()->GetSecurityToken());
1599 1600
  }
}
1601

1602 1603 1604 1605 1606 1607 1608
// Realm.navigate(i) creates a new realm with a distinct security token
// in place of realm i.
void Shell::RealmNavigate(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
  int index = data->RealmIndexOrThrow(args, 0);
  if (index == -1) return;
1609 1610 1611 1612 1613
  if (index == 0 || index == data->realm_current_ ||
      index == data->realm_switch_) {
    Throw(args.GetIsolate(), "Invalid realm index");
    return;
  }
1614 1615 1616

  Local<Context> context = Local<Context>::New(isolate, data->realms_[index]);
  v8::MaybeLocal<Value> global_object = context->Global();
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627

  // Context::Global doesn't return JSGlobalProxy if DetachGlobal is called in
  // advance.
  if (!global_object.IsEmpty()) {
    HandleScope scope(isolate);
    if (!Utils::OpenHandle(*global_object.ToLocalChecked())
             ->IsJSGlobalProxy()) {
      global_object = v8::MaybeLocal<Value>();
    }
  }

1628 1629 1630 1631
  DisposeRealm(args, index);
  CreateRealm(args, index, global_object);
}

1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648
// Realm.detachGlobal(i) detaches the global objects of realm i from realm i.
void Shell::RealmDetachGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
  int index = data->RealmIndexOrThrow(args, 0);
  if (index == -1) return;
  if (index == 0 || index == data->realm_current_ ||
      index == data->realm_switch_) {
    Throw(args.GetIsolate(), "Invalid realm index");
    return;
  }

  HandleScope scope(isolate);
  Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
  realm->DetachGlobal();
}

1649
// Realm.dispose(i) disposes the reference to the realm i.
1650
void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
1651 1652
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1653 1654
  int index = data->RealmIndexOrThrow(args, 0);
  if (index == -1) return;
1655 1656
  if (index == 0 || index == data->realm_current_ ||
      index == data->realm_switch_) {
1657
    Throw(args.GetIsolate(), "Invalid realm index");
1658
    return;
1659
  }
1660
  DisposeRealm(args, index);
1661 1662 1663
}

// Realm.switch(i) switches to the realm i for consecutive interactive inputs.
1664
void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) {
1665 1666
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1667 1668
  int index = data->RealmIndexOrThrow(args, 0);
  if (index == -1) return;
1669 1670 1671 1672
  data->realm_switch_ = index;
}

// Realm.eval(i, s) evaluates s in realm i and returns the result.
1673
void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
1674 1675
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1676 1677 1678
  int index = data->RealmIndexOrThrow(args, 0);
  if (index == -1) return;
  if (args.Length() < 2 || !args[1]->IsString()) {
1679
    Throw(args.GetIsolate(), "Invalid argument");
1680
    return;
1681
  }
1682 1683
  ScriptOrigin origin(isolate,
                      String::NewFromUtf8Literal(isolate, "(d8)",
1684
                                                 NewStringType::kInternalized));
1685
  ScriptCompiler::Source script_source(
1686
      args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked(), origin);
1687 1688 1689 1690 1691
  Local<UnboundScript> script;
  if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
           .ToLocal(&script)) {
    return;
  }
1692
  Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
1693
  realm->Enter();
1694 1695
  int previous_index = data->realm_current_;
  data->realm_current_ = data->realm_switch_ = index;
1696 1697 1698
  Local<Value> result;
  if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) {
    realm->Exit();
1699
    data->realm_current_ = data->realm_switch_ = previous_index;
1700 1701
    return;
  }
1702
  realm->Exit();
1703
  data->realm_current_ = data->realm_switch_ = previous_index;
1704
  args.GetReturnValue().Set(result);
1705 1706 1707
}

// Realm.shared is an accessor for a single shared value across realms.
1708 1709
void Shell::RealmSharedGet(Local<String> property,
                           const PropertyCallbackInfo<Value>& info) {
1710 1711
  Isolate* isolate = info.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1712 1713
  if (data->realm_shared_.IsEmpty()) return;
  info.GetReturnValue().Set(data->realm_shared_);
1714 1715
}

1716
void Shell::RealmSharedSet(Local<String> property, Local<Value> value,
1717
                           const PropertyCallbackInfo<void>& info) {
1718 1719
  Isolate* isolate = info.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1720
  data->realm_shared_.Reset(isolate, value);
1721 1722
}

1723 1724 1725 1726 1727 1728
void Shell::LogGetAndStop(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope handle_scope(isolate);

  std::string file_name = i_isolate->logger()->file_name();
1729 1730
  if (!i::Log::IsLoggingToTemporaryFile(file_name)) {
    Throw(isolate, "Only capturing from temporary files is supported.");
1731 1732 1733 1734 1735 1736 1737 1738 1739
    return;
  }
  if (!i_isolate->logger()->is_logging()) {
    Throw(isolate, "Logging not enabled.");
    return;
  }

  std::string raw_log;
  FILE* log_file = i_isolate->logger()->TearDownAndGetLogFile();
1740 1741 1742 1743
  if (!log_file) {
    Throw(isolate, "Log file does not exist.");
    return;
  }
1744 1745 1746

  bool exists = false;
  raw_log = i::ReadFile(log_file, &exists, true);
1747
  base::Fclose(log_file);
1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760

  if (!exists) {
    Throw(isolate, "Unable to read log file.");
    return;
  }
  Local<String> result =
      String::NewFromUtf8(isolate, raw_log.c_str(), NewStringType::kNormal,
                          static_cast<int>(raw_log.size()))
          .ToLocalChecked();

  args.GetReturnValue().Set(result);
}

1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789
// async_hooks.createHook() registers functions to be called for different
// lifetime events of each async operation.
void Shell::AsyncHooksCreateHook(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  Local<Object> wrap =
      PerIsolateData::Get(args.GetIsolate())->GetAsyncHooks()->CreateHook(args);
  args.GetReturnValue().Set(wrap);
}

// async_hooks.executionAsyncId() returns the asyncId of the current execution
// context.
void Shell::AsyncHooksExecutionAsyncId(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);
  args.GetReturnValue().Set(v8::Number::New(
      isolate,
      PerIsolateData::Get(isolate)->GetAsyncHooks()->GetExecutionAsyncId()));
}

void Shell::AsyncHooksTriggerAsyncId(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);
  args.GetReturnValue().Set(v8::Number::New(
      isolate,
      PerIsolateData::Get(isolate)->GetAsyncHooks()->GetTriggerAsyncId()));
}

1790
void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) {
1791
  for (int i = 0; i < args.Length(); i++) {
1792
    HandleScope handle_scope(args.GetIsolate());
1793
    if (i != 0) {
1794
      fprintf(file, " ");
1795
    }
1796 1797

    // Explicitly catch potential exceptions in toString().
1798
    v8::TryCatch try_catch(args.GetIsolate());
1799
    Local<Value> arg = args[i];
1800
    Local<String> str_obj;
1801 1802

    if (arg->IsSymbol()) {
1803
      arg = arg.As<Symbol>()->Description();
1804 1805
    }
    if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
1806
             .ToLocal(&str_obj)) {
1807 1808 1809
      try_catch.ReThrow();
      return;
    }
1810

1811
    v8::String::Utf8Value str(args.GetIsolate(), str_obj);
1812
    int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), file));
1813 1814
    if (n != str.length()) {
      printf("Error in fwrite\n");
1815
      base::OS::ExitProcess(1);
1816
    }
1817 1818 1819
  }
}

1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837
void WriteAndFlush(FILE* file,
                   const v8::FunctionCallbackInfo<v8::Value>& args) {
  WriteToFile(file, args);
  fprintf(file, "\n");
  fflush(file);
}

void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
  WriteAndFlush(stdout, args);
}

void Shell::PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args) {
  WriteAndFlush(stderr, args);
}

void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) {
  WriteToFile(stdout, args);
}
1838

1839
void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
1840
  String::Utf8Value file(args.GetIsolate(), args[0]);
1841
  if (*file == nullptr) {
1842
    Throw(args.GetIsolate(), "Error loading file");
1843
    return;
1844
  }
1845
  if (args.Length() == 2) {
1846
    String::Utf8Value format(args.GetIsolate(), args[1]);
1847 1848 1849 1850 1851
    if (*format && std::strcmp(*format, "binary") == 0) {
      ReadBuffer(args);
      return;
    }
  }
1852
  Local<String> source = ReadFile(args.GetIsolate(), *file);
1853
  if (source.IsEmpty()) {
1854
    Throw(args.GetIsolate(), "Error loading file");
1855
    return;
1856
  }
1857
  args.GetReturnValue().Set(source);
1858 1859
}

1860
Local<String> Shell::ReadFromStdin(Isolate* isolate) {
1861 1862
  static const int kBufferSize = 256;
  char buffer[kBufferSize];
1863
  Local<String> accumulator = String::NewFromUtf8Literal(isolate, "");
1864
  int length;
1865 1866 1867 1868
  while (true) {
    // Continue reading if the line ends with an escape '\\' or the line has
    // not been fully read into the buffer yet (does not end with '\n').
    // If fgets gets an error, just give up.
1869
    char* input = nullptr;
1870
    input = fgets(buffer, kBufferSize, stdin);
1871
    if (input == nullptr) return Local<String>();
1872
    length = static_cast<int>(strlen(buffer));
1873 1874
    if (length == 0) {
      return accumulator;
1875
    } else if (buffer[length - 1] != '\n') {
1876
      accumulator = String::Concat(
1877
          isolate, accumulator,
1878 1879
          String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
              .ToLocalChecked());
1880 1881
    } else if (length > 1 && buffer[length - 2] == '\\') {
      buffer[length - 2] = '\n';
1882 1883 1884 1885 1886
      accumulator =
          String::Concat(isolate, accumulator,
                         String::NewFromUtf8(isolate, buffer,
                                             NewStringType::kNormal, length - 1)
                             .ToLocalChecked());
1887
    } else {
1888
      return String::Concat(
1889
          isolate, accumulator,
1890
          String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
1891 1892
                              length - 1)
              .ToLocalChecked());
1893 1894
    }
  }
1895 1896
}

1897
void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
1898
  for (int i = 0; i < args.Length(); i++) {
1899
    HandleScope handle_scope(args.GetIsolate());
1900
    String::Utf8Value file(args.GetIsolate(), args[i]);
1901
    if (*file == nullptr) {
1902
      Throw(args.GetIsolate(), "Error loading file");
1903
      return;
1904
    }
1905
    Local<String> source = ReadFile(args.GetIsolate(), *file);
1906
    if (source.IsEmpty()) {
1907
      Throw(args.GetIsolate(), "Error loading file");
1908
      return;
1909
    }
1910 1911
    if (!ExecuteString(
            args.GetIsolate(), source,
1912
            String::NewFromUtf8(args.GetIsolate(), *file).ToLocalChecked(),
1913 1914 1915
            kNoPrintResult,
            options.quiet_load ? kNoReportExceptions : kReportExceptions,
            kNoProcessMessageQueue)) {
1916
      Throw(args.GetIsolate(), "Error executing file");
1917
      return;
1918 1919 1920 1921
    }
  }
}

1922 1923 1924 1925
void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  args.GetReturnValue().Set(v8::Number::New(isolate, 0));
  if (args.Length() == 0 || !args[0]->IsFunction()) return;
1926
  Local<Function> callback = args[0].As<Function>();
1927 1928 1929
  Local<Context> context = isolate->GetCurrentContext();
  PerIsolateData::Get(isolate)->SetTimeout(callback, context);
}
1930

1931
enum WorkerType { kClassic, kString, kFunction, kInvalid, kNone };
1932

1933 1934 1935 1936
void ReadWorkerTypeAndArguments(const v8::FunctionCallbackInfo<v8::Value>& args,
                                WorkerType* worker_type,
                                Local<Value>* arguments = nullptr) {
  Isolate* isolate = args.GetIsolate();
1937 1938 1939
  if (args.Length() > 1 && args[1]->IsObject()) {
    Local<Object> object = args[1].As<Object>();
    Local<Context> context = isolate->GetCurrentContext();
1940
    Local<Value> value;
1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001
    if (!TryGetValue(isolate, context, object, "type").ToLocal(&value)) {
      *worker_type = WorkerType::kNone;
      return;
    }
    if (!value->IsString()) {
      *worker_type = WorkerType::kInvalid;
      return;
    }
    Local<String> worker_type_string =
        value->ToString(context).ToLocalChecked();
    String::Utf8Value str(isolate, worker_type_string);
    if (strcmp("string", *str) == 0) {
      *worker_type = WorkerType::kString;
    } else if (strcmp("classic", *str) == 0) {
      *worker_type = WorkerType::kClassic;
    } else if (strcmp("function", *str) == 0) {
      *worker_type = WorkerType::kFunction;
    } else {
      *worker_type = WorkerType::kInvalid;
    }
    if (arguments != nullptr) {
      bool got_arguments =
          TryGetValue(isolate, context, object, "arguments").ToLocal(arguments);
      USE(got_arguments);
    }
  } else {
    *worker_type = WorkerType::kNone;
  }
}

bool FunctionAndArgumentsToString(Local<Function> function,
                                  Local<Value> arguments, Local<String>* source,
                                  Isolate* isolate) {
  Local<Context> context = isolate->GetCurrentContext();
  MaybeLocal<String> maybe_function_string =
      function->FunctionProtoToString(context);
  Local<String> function_string;
  if (!maybe_function_string.ToLocal(&function_string)) {
    Throw(isolate, "Failed to convert function to string");
    return false;
  }
  *source = String::NewFromUtf8Literal(isolate, "(");
  *source = String::Concat(isolate, *source, function_string);
  Local<String> middle = String::NewFromUtf8Literal(isolate, ")(");
  *source = String::Concat(isolate, *source, middle);
  if (!arguments.IsEmpty() && !arguments->IsUndefined()) {
    if (!arguments->IsArray()) {
      Throw(isolate, "'arguments' must be an array");
      return false;
    }
    Local<String> comma = String::NewFromUtf8Literal(isolate, ",");
    Local<Array> array = arguments.As<Array>();
    for (uint32_t i = 0; i < array->Length(); ++i) {
      if (i > 0) {
        *source = String::Concat(isolate, *source, comma);
      }
      Local<Value> argument = array->Get(context, i).ToLocalChecked();
      Local<String> argument_string;
      if (!JSON::Stringify(context, argument).ToLocal(&argument_string)) {
        Throw(isolate, "Failed to convert argument to string");
        return false;
2002
      }
2003
      *source = String::Concat(isolate, *source, argument_string);
2004 2005
    }
  }
2006 2007 2008 2009
  Local<String> suffix = String::NewFromUtf8Literal(isolate, ")");
  *source = String::Concat(isolate, *source, suffix);
  return true;
}
2010

2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035
void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);
  if (args.Length() < 1 || (!args[0]->IsString() && !args[0]->IsFunction())) {
    Throw(isolate, "1st argument must be a string or a function");
    return;
  }

  Local<String> source;
  if (args[0]->IsFunction()) {
    // d8 supports `options={type: 'function', arguments:[...]}`, which means
    // the first argument is a function with the code to be ran. Restrictions
    // apply; in particular the function will be converted to a string and the
    // Worker constructed based on it.
    WorkerType worker_type;
    Local<Value> arguments;
    ReadWorkerTypeAndArguments(args, &worker_type, &arguments);
    if (worker_type != WorkerType::kFunction) {
      Throw(isolate, "Invalid or missing worker type");
      return;
    }

    // Source: ( function_to_string )( params )
    if (!FunctionAndArgumentsToString(args[0].As<Function>(), arguments,
                                      &source, isolate)) {
2036 2037 2038
      return;
    }
  } else {
2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061
    // d8 honors `options={type: 'string'}`, which means the first argument is
    // not a filename but string of script to be run.
    bool load_from_file = true;
    WorkerType worker_type;
    ReadWorkerTypeAndArguments(args, &worker_type);
    if (worker_type == WorkerType::kString) {
      load_from_file = false;
    } else if (worker_type != WorkerType::kNone &&
               worker_type != WorkerType::kClassic) {
      Throw(isolate, "Invalid worker type");
      return;
    }

    if (load_from_file) {
      String::Utf8Value filename(isolate, args[0]);
      source = ReadFile(isolate, *filename);
      if (source.IsEmpty()) {
        Throw(args.GetIsolate(), "Error loading worker script");
        return;
      }
    } else {
      source = args[0].As<String>();
    }
2062 2063
  }

2064
  if (!args.IsConstructCall()) {
2065
    Throw(isolate, "Worker must be constructed with new");
2066 2067 2068
    return;
  }

2069 2070 2071 2072 2073
  // Initialize the embedder field to 0; if we return early without
  // creating a new Worker (because the main thread is terminating) we can
  // early-out from the instance calls.
  args.Holder()->SetInternalField(0, v8::Integer::New(isolate, 0));

2074
  {
2075 2076
    // Don't allow workers to create more workers if the main thread
    // is waiting for existing running workers to terminate.
2077
    base::MutexGuard lock_guard(workers_mutex_.Pointer());
2078 2079
    if (!allow_new_workers_) return;

2080
    String::Utf8Value script(isolate, source);
2081
    if (!*script) {
2082
      Throw(isolate, "Can't get worker script");
2083 2084
      return;
    }
2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095

    // The C++ worker object's lifetime is shared between the Managed<Worker>
    // object on the heap, which the JavaScript object points to, and an
    // internal std::shared_ptr in the worker thread itself.
    auto worker = std::make_shared<Worker>(*script);
    i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
    const size_t kWorkerSizeEstimate = 4 * 1024 * 1024;  // stack + heap.
    i::Handle<i::Object> managed = i::Managed<Worker>::FromSharedPtr(
        i_isolate, kWorkerSizeEstimate, worker);
    args.Holder()->SetInternalField(0, Utils::ToLocal(managed));
    if (!Worker::StartWorkerThread(std::move(worker))) {
2096
      Throw(isolate, "Can't start thread");
2097 2098
      return;
    }
2099
  }
2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110
}

void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);

  if (args.Length() < 1) {
    Throw(isolate, "Invalid argument");
    return;
  }

2111 2112 2113
  std::shared_ptr<Worker> worker =
      GetWorkerFromInternalField(isolate, args.Holder());
  if (!worker.get()) {
2114 2115 2116
    return;
  }

2117
  Local<Value> message = args[0];
2118
  Local<Value> transfer =
2119
      args.Length() >= 2 ? args[1] : Undefined(isolate).As<Value>();
2120 2121 2122 2123
  std::unique_ptr<SerializationData> data =
      Shell::SerializeValue(isolate, message, transfer);
  if (data) {
    worker->PostMessage(std::move(data));
2124 2125 2126 2127 2128 2129
  }
}

void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);
2130 2131 2132
  std::shared_ptr<Worker> worker =
      GetWorkerFromInternalField(isolate, args.Holder());
  if (!worker.get()) {
2133 2134 2135
    return;
  }

2136
  std::unique_ptr<SerializationData> data = worker->GetMessage();
2137
  if (data) {
2138 2139 2140
    Local<Value> value;
    if (Shell::DeserializeValue(isolate, std::move(data)).ToLocal(&value)) {
      args.GetReturnValue().Set(value);
2141 2142 2143 2144 2145 2146 2147
    }
  }
}

void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);
2148 2149 2150
  std::shared_ptr<Worker> worker =
      GetWorkerFromInternalField(isolate, args.Holder());
  if (!worker.get()) {
2151 2152 2153 2154 2155 2156
    return;
  }

  worker->Terminate();
}

2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169
void Shell::WorkerTerminateAndWait(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);
  std::shared_ptr<Worker> worker =
      GetWorkerFromInternalField(isolate, args.Holder());
  if (!worker.get()) {
    return;
  }

  worker->TerminateAndWaitForThread();
}

2170
void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) {
2171 2172 2173
  int exit_code = (*args)[0]
                      ->Int32Value(args->GetIsolate()->GetCurrentContext())
                      .FromMaybe(0);
2174
  WaitForRunningWorkers();
2175
  args->GetIsolate()->Exit();
2176
  OnExit(args->GetIsolate());
2177
  base::OS::ExitProcess(exit_code);
2178 2179
}

2180 2181 2182 2183 2184
void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
  base::CallOnce(&quit_once_, &QuitOnce,
                 const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args));
}

2185 2186 2187 2188 2189 2190 2191
void Shell::WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
  SetWaitUntilDone(args.GetIsolate(), true);
}

void Shell::NotifyDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
  SetWaitUntilDone(args.GetIsolate(), false);
}
2192

2193
void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
2194 2195 2196
  args.GetReturnValue().Set(
      String::NewFromUtf8(args.GetIsolate(), V8::GetVersion())
          .ToLocalChecked());
2197 2198
}

2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249
#ifdef V8_FUZZILLI

// We have to assume that the fuzzer will be able to call this function e.g. by
// enumerating the properties of the global object and eval'ing them. As such
// this function is implemented in a way that requires passing some magic value
// as first argument (with the idea being that the fuzzer won't be able to
// generate this value) which then also acts as a selector for the operation
// to perform.
void Shell::Fuzzilli(const v8::FunctionCallbackInfo<v8::Value>& args) {
  HandleScope handle_scope(args.GetIsolate());

  String::Utf8Value operation(args.GetIsolate(), args[0]);
  if (*operation == nullptr) {
    return;
  }

  if (strcmp(*operation, "FUZZILLI_CRASH") == 0) {
    auto arg = args[1]
                   ->Int32Value(args.GetIsolate()->GetCurrentContext())
                   .FromMaybe(0);
    switch (arg) {
      case 0:
        V8_IMMEDIATE_CRASH();
        break;
      case 1:
        CHECK(0);
        break;
      default:
        DCHECK(false);
        break;
    }
  } else if (strcmp(*operation, "FUZZILLI_PRINT") == 0) {
    static FILE* fzliout = fdopen(REPRL_DWFD, "w");
    if (!fzliout) {
      fprintf(
          stderr,
          "Fuzzer output channel not available, printing to stdout instead\n");
      fzliout = stdout;
    }

    String::Utf8Value string(args.GetIsolate(), args[1]);
    if (*string == nullptr) {
      return;
    }
    fprintf(fzliout, "%s\n", *string);
    fflush(fzliout);
  }
}

#endif  // V8_FUZZILLI

2250 2251
void Shell::ReportException(Isolate* isolate, Local<v8::Message> message,
                            Local<v8::Value> exception_obj) {
2252
  HandleScope handle_scope(isolate);
2253 2254
  Local<Context> context = isolate->GetCurrentContext();
  bool enter_context = context.IsEmpty();
2255
  if (enter_context) {
yangguo's avatar
yangguo committed
2256 2257
    context = Local<Context>::New(isolate, evaluation_context_);
    context->Enter();
2258
  }
2259 2260 2261 2262 2263
  // Converts a V8 value to a C string.
  auto ToCString = [](const v8::String::Utf8Value& value) {
    return *value ? *value : "<string conversion failed>";
  };

2264
  v8::String::Utf8Value exception(isolate, exception_obj);
2265
  const char* exception_string = ToCString(exception);
2266 2267 2268
  if (message.IsEmpty()) {
    // V8 didn't provide any extra information about this error; just
    // print the exception.
2269
    printf("%s\n", exception_string);
2270
  } else if (message->GetScriptOrigin().Options().IsWasm()) {
2271
    // Print wasm-function[(function index)]:(offset): (message).
2272
    int function_index = message->GetWasmFunctionIndex();
2273
    int offset = message->GetStartColumn(context).FromJust();
2274
    printf("wasm-function[%d]:0x%x: %s\n", function_index, offset,
2275
           exception_string);
2276 2277
  } else {
    // Print (filename):(line number): (message).
2278 2279
    v8::String::Utf8Value filename(isolate,
                                   message->GetScriptOrigin().ResourceName());
2280
    const char* filename_string = ToCString(filename);
2281
    int linenum = message->GetLineNumber(context).FromMaybe(-1);
2282
    printf("%s:%i: %s\n", filename_string, linenum, exception_string);
2283
    Local<String> sourceline;
2284
    if (message->GetSourceLine(context).ToLocal(&sourceline)) {
2285
      // Print line of source code.
2286
      v8::String::Utf8Value sourcelinevalue(isolate, sourceline);
2287
      const char* sourceline_string = ToCString(sourcelinevalue);
2288 2289
      printf("%s\n", sourceline_string);
      // Print wavy underline (GetUnderline is deprecated).
2290
      int start = message->GetStartColumn(context).FromJust();
2291 2292 2293
      for (int i = 0; i < start; i++) {
        printf(" ");
      }
2294
      int end = message->GetEndColumn(context).FromJust();
2295 2296 2297 2298
      for (int i = start; i < end; i++) {
        printf("^");
      }
      printf("\n");
2299
    }
2300 2301
  }
  Local<Value> stack_trace_string;
2302 2303
  if (v8::TryCatch::StackTrace(context, exception_obj)
          .ToLocal(&stack_trace_string) &&
2304
      stack_trace_string->IsString()) {
2305
    v8::String::Utf8Value stack_trace(isolate, stack_trace_string.As<String>());
2306
    printf("%s\n", ToCString(stack_trace));
2307
  }
2308
  printf("\n");
yangguo's avatar
yangguo committed
2309
  if (enter_context) context->Exit();
2310 2311
}

2312 2313 2314 2315
void Shell::ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
  ReportException(isolate, try_catch->Message(), try_catch->Exception());
}

2316
int32_t* Counter::Bind(const char* name, bool is_histogram) {
2317 2318 2319 2320
  int i;
  for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
    name_[i] = static_cast<char>(name[i]);
  name_[i] = '\0';
2321 2322 2323 2324 2325 2326 2327
  is_histogram_ = is_histogram;
  return ptr();
}

void Counter::AddSample(int32_t sample) {
  count_++;
  sample_total_ += sample;
2328 2329 2330 2331 2332 2333 2334 2335 2336 2337
}

CounterCollection::CounterCollection() {
  magic_number_ = 0xDEADFACE;
  max_counters_ = kMaxCounters;
  max_name_size_ = Counter::kMaxNameSize;
  counters_in_use_ = 0;
}

Counter* CounterCollection::GetNextCounter() {
2338
  if (counters_in_use_ == kMaxCounters) return nullptr;
2339 2340 2341
  return &counters_[counters_in_use_++];
}

2342
void Shell::MapCounters(v8::Isolate* isolate, const char* name) {
2343
  counters_file_ = base::OS::MemoryMappedFile::create(
2344
      name, sizeof(CounterCollection), &local_counters_);
2345 2346 2347
  void* memory =
      (counters_file_ == nullptr) ? nullptr : counters_file_->memory();
  if (memory == nullptr) {
2348
    printf("Could not map counters file %s\n", name);
2349
    base::OS::ExitProcess(1);
2350 2351
  }
  counters_ = static_cast<CounterCollection*>(memory);
2352 2353 2354
  isolate->SetCounterFunction(LookupCounter);
  isolate->SetCreateHistogramFunction(CreateHistogram);
  isolate->SetAddHistogramSampleFunction(AddHistogramSample);
2355 2356
}

2357
Counter* Shell::GetCounter(const char* name, bool is_histogram) {
2358 2359 2360
  auto map_entry = counter_map_->find(name);
  Counter* counter =
      map_entry != counter_map_->end() ? map_entry->second : nullptr;
2361

2362
  if (counter == nullptr) {
2363
    counter = counters_->GetNextCounter();
2364
    if (counter != nullptr) {
2365
      (*counter_map_)[name] = counter;
2366 2367 2368
      counter->Bind(name, is_histogram);
    }
  } else {
2369
    DCHECK(counter->is_histogram() == is_histogram);
2370 2371 2372 2373 2374 2375 2376
  }
  return counter;
}

int* Shell::LookupCounter(const char* name) {
  Counter* counter = GetCounter(name, false);

2377
  if (counter != nullptr) {
2378
    return counter->ptr();
2379
  } else {
2380
    return nullptr;
2381
  }
2382 2383
}

2384
void* Shell::CreateHistogram(const char* name, int min, int max,
2385 2386 2387 2388 2389 2390 2391
                             size_t buckets) {
  return GetCounter(name, true);
}

void Shell::AddHistogramSample(void* histogram, int sample) {
  Counter* counter = reinterpret_cast<Counter*>(histogram);
  counter->AddSample(sample);
2392 2393
}

yangguo's avatar
yangguo committed
2394 2395 2396
// Turn a value into a human-readable string.
Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) {
  v8::Local<v8::Context> context =
2397
      v8::Local<v8::Context>::New(isolate, evaluation_context_);
yangguo's avatar
yangguo committed
2398 2399
  if (stringify_function_.IsEmpty()) {
    Local<String> source =
2400
        String::NewFromUtf8(isolate, stringify_source_).ToLocalChecked();
2401
    Local<String> name = String::NewFromUtf8Literal(isolate, "d8-stringify");
2402
    ScriptOrigin origin(isolate, name);
yangguo's avatar
yangguo committed
2403 2404 2405 2406 2407 2408 2409 2410
    Local<Script> script =
        Script::Compile(context, source, &origin).ToLocalChecked();
    stringify_function_.Reset(
        isolate, script->Run(context).ToLocalChecked().As<Function>());
  }
  Local<Function> fun = Local<Function>::New(isolate, stringify_function_);
  Local<Value> argv[1] = {value};
  v8::TryCatch try_catch(isolate);
2411
  MaybeLocal<Value> result = fun->Call(context, Undefined(isolate), 1, argv);
yangguo's avatar
yangguo committed
2412 2413
  if (result.IsEmpty()) return String::Empty(isolate);
  return result.ToLocalChecked().As<String>();
2414
}
2415

2416 2417
Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
  Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
2418 2419 2420 2421 2422
  global_template->Set(Symbol::GetToStringTag(isolate),
                       String::NewFromUtf8Literal(isolate, "global"));
  global_template->Set(isolate, "version",
                       FunctionTemplate::New(isolate, Version));

2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434
  global_template->Set(isolate, "print", FunctionTemplate::New(isolate, Print));
  global_template->Set(isolate, "printErr",
                       FunctionTemplate::New(isolate, PrintErr));
  global_template->Set(isolate, "write", FunctionTemplate::New(isolate, Write));
  global_template->Set(isolate, "read", FunctionTemplate::New(isolate, Read));
  global_template->Set(isolate, "readbuffer",
                       FunctionTemplate::New(isolate, ReadBuffer));
  global_template->Set(isolate, "readline",
                       FunctionTemplate::New(isolate, ReadLine));
  global_template->Set(isolate, "load", FunctionTemplate::New(isolate, Load));
  global_template->Set(isolate, "setTimeout",
                       FunctionTemplate::New(isolate, SetTimeout));
2435 2436 2437 2438
  // Some Emscripten-generated code tries to call 'quit', which in turn would
  // call C's exit(). This would lead to memory leaks, because there is no way
  // we can terminate cleanly then, so we need a way to hide 'quit'.
  if (!options.omit_quit) {
2439
    global_template->Set(isolate, "quit", FunctionTemplate::New(isolate, Quit));
2440
  }
2441 2442 2443 2444 2445 2446
  global_template->Set(isolate, "testRunner",
                       Shell::CreateTestRunnerTemplate(isolate));
  global_template->Set(isolate, "Realm", Shell::CreateRealmTemplate(isolate));
  global_template->Set(isolate, "performance",
                       Shell::CreatePerformanceTemplate(isolate));
  global_template->Set(isolate, "Worker", Shell::CreateWorkerTemplate(isolate));
2447 2448 2449 2450
  // Prevent fuzzers from creating side effects.
  if (!i::FLAG_fuzzing) {
    global_template->Set(isolate, "os", Shell::CreateOSTemplate(isolate));
  }
2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515
  global_template->Set(isolate, "d8", Shell::CreateD8Template(isolate));

#ifdef V8_FUZZILLI
  global_template->Set(
      String::NewFromUtf8(isolate, "fuzzilli", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, Fuzzilli), PropertyAttribute::DontEnum);
#endif  // V8_FUZZILLI

  if (i::FLAG_expose_async_hooks) {
    global_template->Set(isolate, "async_hooks",
                         Shell::CreateAsyncHookTemplate(isolate));
  }

  return global_template;
}

Local<ObjectTemplate> Shell::CreateOSTemplate(Isolate* isolate) {
  Local<ObjectTemplate> os_template = ObjectTemplate::New(isolate);
  AddOSMethods(isolate, os_template);
  return os_template;
}

Local<FunctionTemplate> Shell::CreateWorkerTemplate(Isolate* isolate) {
  Local<FunctionTemplate> worker_fun_template =
      FunctionTemplate::New(isolate, WorkerNew);
  Local<Signature> worker_signature =
      Signature::New(isolate, worker_fun_template);
  worker_fun_template->SetClassName(
      String::NewFromUtf8Literal(isolate, "Worker"));
  worker_fun_template->ReadOnlyPrototype();
  worker_fun_template->PrototypeTemplate()->Set(
      isolate, "terminate",
      FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(),
                            worker_signature));
  worker_fun_template->PrototypeTemplate()->Set(
      isolate, "terminateAndWait",
      FunctionTemplate::New(isolate, WorkerTerminateAndWait, Local<Value>(),
                            worker_signature));
  worker_fun_template->PrototypeTemplate()->Set(
      isolate, "postMessage",
      FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(),
                            worker_signature));
  worker_fun_template->PrototypeTemplate()->Set(
      isolate, "getMessage",
      FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(),
                            worker_signature));
  worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1);
  return worker_fun_template;
}

Local<ObjectTemplate> Shell::CreateAsyncHookTemplate(Isolate* isolate) {
  Local<ObjectTemplate> async_hooks_templ = ObjectTemplate::New(isolate);
  async_hooks_templ->Set(isolate, "createHook",
                         FunctionTemplate::New(isolate, AsyncHooksCreateHook));
  async_hooks_templ->Set(
      isolate, "executionAsyncId",
      FunctionTemplate::New(isolate, AsyncHooksExecutionAsyncId));
  async_hooks_templ->Set(
      isolate, "triggerAsyncId",
      FunctionTemplate::New(isolate, AsyncHooksTriggerAsyncId));
  return async_hooks_templ;
}

Local<ObjectTemplate> Shell::CreateTestRunnerTemplate(Isolate* isolate) {
2516
  Local<ObjectTemplate> test_template = ObjectTemplate::New(isolate);
2517 2518 2519 2520
  test_template->Set(isolate, "notifyDone",
                     FunctionTemplate::New(isolate, NotifyDone));
  test_template->Set(isolate, "waitUntilDone",
                     FunctionTemplate::New(isolate, WaitUntilDone));
2521 2522 2523
  // Reliable access to quit functionality. The "quit" method function
  // installed on the global object can be hidden with the --omit-quit flag
  // (e.g. on asan bots).
2524
  test_template->Set(isolate, "quit", FunctionTemplate::New(isolate, Quit));
2525 2526
  return test_template;
}
2527

2528 2529 2530 2531 2532 2533 2534 2535 2536
Local<ObjectTemplate> Shell::CreatePerformanceTemplate(Isolate* isolate) {
  Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
  performance_template->Set(isolate, "now",
                            FunctionTemplate::New(isolate, PerformanceNow));
  performance_template->Set(
      isolate, "measureMemory",
      FunctionTemplate::New(isolate, PerformanceMeasureMemory));
  return performance_template;
}
2537

2538
Local<ObjectTemplate> Shell::CreateRealmTemplate(Isolate* isolate) {
2539
  Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
2540 2541 2542 2543 2544 2545 2546 2547
  realm_template->Set(isolate, "current",
                      FunctionTemplate::New(isolate, RealmCurrent));
  realm_template->Set(isolate, "owner",
                      FunctionTemplate::New(isolate, RealmOwner));
  realm_template->Set(isolate, "global",
                      FunctionTemplate::New(isolate, RealmGlobal));
  realm_template->Set(isolate, "create",
                      FunctionTemplate::New(isolate, RealmCreate));
2548
  realm_template->Set(
2549
      isolate, "createAllowCrossRealmAccess",
2550
      FunctionTemplate::New(isolate, RealmCreateAllowCrossRealmAccess));
2551 2552 2553 2554 2555 2556 2557 2558 2559 2560
  realm_template->Set(isolate, "navigate",
                      FunctionTemplate::New(isolate, RealmNavigate));
  realm_template->Set(isolate, "detachGlobal",
                      FunctionTemplate::New(isolate, RealmDetachGlobal));
  realm_template->Set(isolate, "dispose",
                      FunctionTemplate::New(isolate, RealmDispose));
  realm_template->Set(isolate, "switch",
                      FunctionTemplate::New(isolate, RealmSwitch));
  realm_template->Set(isolate, "eval",
                      FunctionTemplate::New(isolate, RealmEval));
2561 2562
  realm_template->SetAccessor(String::NewFromUtf8Literal(isolate, "shared"),
                              RealmSharedGet, RealmSharedSet);
2563 2564
  return realm_template;
}
2565

2566 2567
Local<ObjectTemplate> Shell::CreateD8Template(Isolate* isolate) {
  Local<ObjectTemplate> d8_template = ObjectTemplate::New(isolate);
2568 2569 2570 2571 2572 2573 2574
  {
    Local<ObjectTemplate> log_template = ObjectTemplate::New(isolate);
    log_template->Set(isolate, "getAndStop",
                      FunctionTemplate::New(isolate, LogGetAndStop));

    d8_template->Set(isolate, "log", log_template);
  }
2575
  return d8_template;
2576 2577
}

2578
static void PrintMessageCallback(Local<Message> message, Local<Value> error) {
2579 2580 2581 2582 2583 2584 2585 2586 2587
  switch (message->ErrorLevel()) {
    case v8::Isolate::kMessageWarning:
    case v8::Isolate::kMessageLog:
    case v8::Isolate::kMessageInfo:
    case v8::Isolate::kMessageDebug: {
      break;
    }

    case v8::Isolate::kMessageError: {
2588
      Shell::ReportException(message->GetIsolate(), message, error);
2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599
      return;
    }

    default: {
      UNREACHABLE();
    }
  }
  // Converts a V8 value to a C string.
  auto ToCString = [](const v8::String::Utf8Value& value) {
    return *value ? *value : "<string conversion failed>";
  };
2600
  Isolate* isolate = message->GetIsolate();
2601
  v8::String::Utf8Value msg(isolate, message->Get());
2602 2603
  const char* msg_string = ToCString(msg);
  // Print (filename):(line number): (message).
2604 2605
  v8::String::Utf8Value filename(isolate,
                                 message->GetScriptOrigin().ResourceName());
2606 2607 2608 2609
  const char* filename_string = ToCString(filename);
  Maybe<int> maybeline = message->GetLineNumber(isolate->GetCurrentContext());
  int linenum = maybeline.IsJust() ? maybeline.FromJust() : -1;
  printf("%s:%i: %s\n", filename_string, linenum, msg_string);
2610
}
2611

2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639
void Shell::PromiseRejectCallback(v8::PromiseRejectMessage data) {
  if (options.ignore_unhandled_promises) return;
  if (data.GetEvent() == v8::kPromiseRejectAfterResolved ||
      data.GetEvent() == v8::kPromiseResolveAfterResolved) {
    // Ignore reject/resolve after resolved.
    return;
  }
  v8::Local<v8::Promise> promise = data.GetPromise();
  v8::Isolate* isolate = promise->GetIsolate();
  PerIsolateData* isolate_data = PerIsolateData::Get(isolate);

  if (data.GetEvent() == v8::kPromiseHandlerAddedAfterReject) {
    isolate_data->RemoveUnhandledPromise(promise);
    return;
  }

  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  bool capture_exceptions =
      i_isolate->get_capture_stack_trace_for_uncaught_exceptions();
  isolate->SetCaptureStackTraceForUncaughtExceptions(true);
  v8::Local<Value> exception = data.GetValue();
  v8::Local<Message> message;
  // Assume that all objects are stack-traces.
  if (exception->IsObject()) {
    message = v8::Exception::CreateMessage(isolate, exception);
  }
  if (!exception->IsNativeError() &&
      (message.IsEmpty() || message->GetStackTrace().IsEmpty())) {
2640 2641 2642 2643
    // If there is no real Error object, manually create a stack trace.
    exception = v8::Exception::Error(
        v8::String::NewFromUtf8Literal(isolate, "Unhandled Promise."));
    message = Exception::CreateMessage(isolate, exception);
2644 2645 2646 2647 2648 2649
  }
  isolate->SetCaptureStackTraceForUncaughtExceptions(capture_exceptions);

  isolate_data->AddUnhandledPromise(promise, message, exception);
}

2650 2651
void Shell::Initialize(Isolate* isolate, D8Console* console,
                       bool isOnMainThread) {
2652
  isolate->SetPromiseRejectCallback(PromiseRejectCallback);
2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663
  if (isOnMainThread) {
    // Set up counters
    if (i::FLAG_map_counters[0] != '\0') {
      MapCounters(isolate, i::FLAG_map_counters);
    }
    // Disable default message reporting.
    isolate->AddMessageListenerWithErrorLevel(
        PrintMessageCallback,
        v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
            v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
            v8::Isolate::kMessageLog);
2664
  }
2665 2666 2667 2668 2669 2670

  isolate->SetHostImportModuleDynamicallyCallback(
      Shell::HostImportModuleDynamically);
  isolate->SetHostInitializeImportMetaObjectCallback(
      Shell::HostInitializeImportMetaObject);

2671 2672
#ifdef V8_FUZZILLI
  // Let the parent process (Fuzzilli) know we are ready.
2673 2674 2675 2676 2677 2678
  if (options.fuzzilli_enable_builtins_coverage) {
    cov_init_builtins_edges(static_cast<uint32_t>(
        i::BasicBlockProfiler::Get()
            ->GetCoverageBitmap(reinterpret_cast<i::Isolate*>(isolate))
            .size()));
  }
2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689
  char helo[] = "HELO";
  if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) {
    fuzzilli_reprl = false;
  }

  if (memcmp(helo, "HELO", 4) != 0) {
    fprintf(stderr, "Invalid response from parent\n");
    _exit(-1);
  }
#endif  // V8_FUZZILLI

2690
  debug::SetConsoleDelegate(isolate, console);
2691 2692
}

2693
Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
2694
  // This needs to be a critical section since this is not thread-safe
2695
  base::MutexGuard lock_guard(context_mutex_.Pointer());
2696
  // Initialize the global objects
2697
  Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
2698
  EscapableHandleScope handle_scope(isolate);
2699
  Local<Context> context = Context::New(isolate, nullptr, global_template);
2700
  DCHECK(!context.IsEmpty());
2701
  if (i::FLAG_perf_prof_annotate_wasm || i::FLAG_vtune_prof_annotate_wasm) {
2702 2703
    isolate->SetWasmLoadSourceMapCallback(ReadFile);
  }
2704
  InitializeModuleEmbedderData(context);
2705 2706 2707 2708
  if (options.include_arguments) {
    Context::Scope scope(context);
    const std::vector<const char*>& args = options.arguments;
    int size = static_cast<int>(args.size());
2709 2710 2711
    Local<Array> array = Array::New(isolate, size);
    for (int i = 0; i < size; i++) {
      Local<String> arg =
2712
          v8::String::NewFromUtf8(isolate, args[i]).ToLocalChecked();
2713 2714 2715
      Local<Number> index = v8::Number::New(isolate, i);
      array->Set(context, index, arg).FromJust();
    }
2716 2717
    Local<String> name = String::NewFromUtf8Literal(
        isolate, "arguments", NewStringType::kInternalized);
2718 2719
    context->Global()->Set(context, name, array).FromJust();
  }
2720
  return handle_scope.Escape(context);
2721 2722
}

2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733
void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
  HandleScope handle_scope(isolate);
  Local<Context> context = Context::New(isolate);
  Context::Scope context_scope(context);

  Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate)
                                        ->interpreter()
                                        ->GetDispatchCountersObject();
  std::ofstream dispatch_counters_stream(
      i::FLAG_trace_ignition_dispatches_output_file);
  dispatch_counters_stream << *String::Utf8Value(
2734
      isolate, JSON::Stringify(context, dispatch_counters).ToLocalChecked());
2735 2736
}

2737 2738 2739 2740 2741 2742
namespace {
int LineFromOffset(Local<debug::Script> script, int offset) {
  debug::Location location = script->GetSourceLocation(offset);
  return location.GetLineNumber();
}

2743 2744
void WriteLcovDataForRange(std::vector<uint32_t>* lines, int start_line,
                           int end_line, uint32_t count) {
2745
  // Ensure space in the array.
2746
  lines->resize(std::max(static_cast<size_t>(end_line + 1), lines->size()), 0);
2747 2748
  // Boundary lines could be shared between two functions with different
  // invocation counts. Take the maximum.
2749 2750
  (*lines)[start_line] = std::max((*lines)[start_line], count);
  (*lines)[end_line] = std::max((*lines)[end_line], count);
2751
  // Invocation counts for non-boundary lines are overwritten.
2752
  for (int k = start_line + 1; k < end_line; k++) (*lines)[k] = count;
2753 2754
}

2755 2756 2757 2758
void WriteLcovDataForNamedRange(std::ostream& sink,
                                std::vector<uint32_t>* lines,
                                const std::string& name, int start_line,
                                int end_line, uint32_t count) {
2759 2760 2761 2762 2763 2764
  WriteLcovDataForRange(lines, start_line, end_line, count);
  sink << "FN:" << start_line + 1 << "," << name << std::endl;
  sink << "FNDA:" << count << "," << name << std::endl;
}
}  // namespace

2765 2766 2767 2768
// Write coverage data in LCOV format. See man page for geninfo(1).
void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
  if (!file) return;
  HandleScope handle_scope(isolate);
2769
  debug::Coverage coverage = debug::Coverage::CollectPrecise(isolate);
2770
  std::ofstream sink(file, std::ofstream::app);
2771
  for (size_t i = 0; i < coverage.ScriptCount(); i++) {
2772 2773
    debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
    Local<debug::Script> script = script_data.GetScript();
2774
    // Skip unnamed scripts.
2775 2776
    Local<String> name;
    if (!script->Name().ToLocal(&name)) continue;
2777
    std::string file_name = ToSTLString(isolate, name);
2778 2779 2780 2781 2782
    // Skip scripts not backed by a file.
    if (!std::ifstream(file_name).good()) continue;
    sink << "SF:";
    sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl;
    std::vector<uint32_t> lines;
2783 2784 2785
    for (size_t j = 0; j < script_data.FunctionCount(); j++) {
      debug::Coverage::FunctionData function_data =
          script_data.GetFunctionData(j);
2786

2787
      // Write function stats.
2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799
      {
        debug::Location start =
            script->GetSourceLocation(function_data.StartOffset());
        debug::Location end =
            script->GetSourceLocation(function_data.EndOffset());
        int start_line = start.GetLineNumber();
        int end_line = end.GetLineNumber();
        uint32_t count = function_data.Count();

        Local<String> name;
        std::stringstream name_stream;
        if (function_data.Name().ToLocal(&name)) {
2800
          name_stream << ToSTLString(isolate, name);
2801 2802 2803 2804 2805
        } else {
          name_stream << "<" << start_line + 1 << "-";
          name_stream << start.GetColumnNumber() << ">";
        }

2806
        WriteLcovDataForNamedRange(sink, &lines, name_stream.str(), start_line,
2807 2808 2809 2810 2811 2812 2813
                                   end_line, count);
      }

      // Process inner blocks.
      for (size_t k = 0; k < function_data.BlockCount(); k++) {
        debug::Coverage::BlockData block_data = function_data.GetBlockData(k);
        int start_line = LineFromOffset(script, block_data.StartOffset());
2814
        int end_line = LineFromOffset(script, block_data.EndOffset() - 1);
2815
        uint32_t count = block_data.Count();
2816
        WriteLcovDataForRange(&lines, start_line, end_line, count);
2817 2818
      }
    }
2819 2820 2821 2822 2823 2824 2825
    // Write per-line coverage. LCOV uses 1-based line numbers.
    for (size_t i = 0; i < lines.size(); i++) {
      sink << "DA:" << (i + 1) << "," << lines[i] << std::endl;
    }
    sink << "end_of_record" << std::endl;
  }
}
2826

2827
void Shell::OnExit(v8::Isolate* isolate) {
2828 2829
  isolate->Dispose();

2830
  if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
2831 2832 2833
    std::vector<std::pair<std::string, Counter*>> counters(
        counter_map_->begin(), counter_map_->end());
    std::sort(counters.begin(), counters.end());
2834 2835 2836

    if (i::FLAG_dump_counters_nvp) {
      // Dump counters as name-value pairs.
2837
      for (const auto& pair : counters) {
2838 2839
        std::string key = pair.first;
        Counter* counter = pair.second;
2840
        if (counter->is_histogram()) {
2841 2842 2843
          std::cout << "\"c:" << key << "\"=" << counter->count() << "\n";
          std::cout << "\"t:" << key << "\"=" << counter->sample_total()
                    << "\n";
2844
        } else {
2845
          std::cout << "\"" << key << "\"=" << counter->count() << "\n";
2846 2847 2848 2849
        }
      }
    } else {
      // Dump counters in formatted boxes.
2850 2851 2852 2853 2854 2855 2856 2857
      constexpr int kNameBoxSize = 64;
      constexpr int kValueBoxSize = 13;
      std::cout << "+" << std::string(kNameBoxSize, '-') << "+"
                << std::string(kValueBoxSize, '-') << "+\n";
      std::cout << "| Name" << std::string(kNameBoxSize - 5, ' ') << "| Value"
                << std::string(kValueBoxSize - 6, ' ') << "|\n";
      std::cout << "+" << std::string(kNameBoxSize, '-') << "+"
                << std::string(kValueBoxSize, '-') << "+\n";
2858
      for (const auto& pair : counters) {
2859 2860
        std::string key = pair.first;
        Counter* counter = pair.second;
2861
        if (counter->is_histogram()) {
2862 2863 2864 2865 2866 2867
          std::cout << "| c:" << std::setw(kNameBoxSize - 4) << std::left << key
                    << " | " << std::setw(kValueBoxSize - 2) << std::right
                    << counter->count() << " |\n";
          std::cout << "| t:" << std::setw(kNameBoxSize - 4) << std::left << key
                    << " | " << std::setw(kValueBoxSize - 2) << std::right
                    << counter->sample_total() << " |\n";
2868
        } else {
2869 2870 2871
          std::cout << "| " << std::setw(kNameBoxSize - 2) << std::left << key
                    << " | " << std::setw(kValueBoxSize - 2) << std::right
                    << counter->count() << " |\n";
2872
        }
2873
      }
2874 2875
      std::cout << "+" << std::string(kNameBoxSize, '-') << "+"
                << std::string(kValueBoxSize, '-') << "+\n";
2876 2877
    }
  }
2878

2879 2880
  delete counters_file_;
  delete counter_map_;
2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958

  if (options.simulate_errors && is_valid_fuzz_script()) {
    // Simulate several errors detectable by fuzzers behind a flag if the
    // minimum file size for fuzzing was executed.
    FuzzerMonitor::SimulateErrors();
  }
}

void Dummy(char* arg) {}

V8_NOINLINE void FuzzerMonitor::SimulateErrors() {
  // Initialize a fresh RNG to not interfere with JS execution.
  std::unique_ptr<base::RandomNumberGenerator> rng;
  int64_t seed = internal::FLAG_random_seed;
  if (seed != 0) {
    rng = std::make_unique<base::RandomNumberGenerator>(seed);
  } else {
    rng = std::make_unique<base::RandomNumberGenerator>();
  }

  double p = rng->NextDouble();
  if (p < 0.1) {
    ControlFlowViolation();
  } else if (p < 0.2) {
    DCheck();
  } else if (p < 0.3) {
    Fatal();
  } else if (p < 0.4) {
    ObservableDifference();
  } else if (p < 0.5) {
    UndefinedBehavior();
  } else if (p < 0.6) {
    UseAfterFree();
  } else if (p < 0.7) {
    UseOfUninitializedValue();
  }
}

V8_NOINLINE void FuzzerMonitor::ControlFlowViolation() {
  // Control flow violation caught by CFI.
  void (*func)() = (void (*)()) & Dummy;
  func();
}

V8_NOINLINE void FuzzerMonitor::DCheck() {
  // Caught in debug builds.
  DCHECK(false);
}

V8_NOINLINE void FuzzerMonitor::Fatal() {
  // Caught in all build types.
  FATAL("Fake error.");
}

V8_NOINLINE void FuzzerMonitor::ObservableDifference() {
  // Observable difference caught by differential fuzzing.
  printf("___fake_difference___\n");
}

V8_NOINLINE void FuzzerMonitor::UndefinedBehavior() {
  // Caught by UBSAN.
  int32_t val = -1;
  USE(val << 8);
}

V8_NOINLINE void FuzzerMonitor::UseAfterFree() {
  // Use-after-free caught by ASAN.
  std::vector<bool>* storage = new std::vector<bool>(3);
  delete storage;
  USE(storage->at(1));
}

V8_NOINLINE void FuzzerMonitor::UseOfUninitializedValue() {
// Use-of-uninitialized-value caught by MSAN.
#if defined(__clang__)
  int uninitialized[1];
  if (uninitialized[0]) USE(uninitialized);
#endif
2959 2960
}

2961
static FILE* FOpen(const char* path, const char* mode) {
2962
#if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
2963 2964
  FILE* result;
  if (fopen_s(&result, path, mode) == 0) {
2965
    return result;
2966
  } else {
2967
    return nullptr;
2968 2969
  }
#else
2970
  FILE* file = base::Fopen(path, mode);
2971
  if (file == nullptr) return nullptr;
2972
  struct stat file_stat;
2973
  if (fstat(fileno(file), &file_stat) != 0) return nullptr;
2974 2975
  bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
  if (is_regular_file) return file;
2976
  base::Fclose(file);
2977
  return nullptr;
2978 2979
#endif
}
2980

2981
static char* ReadChars(const char* name, int* size_out) {
2982 2983 2984 2985
  if (Shell::options.read_from_tcp_port >= 0) {
    return Shell::ReadCharsFromTcpPort(name, size_out);
  }

2986
  FILE* file = FOpen(name, "rb");
2987
  if (file == nullptr) return nullptr;
2988 2989

  fseek(file, 0, SEEK_END);
2990
  size_t size = ftell(file);
2991 2992 2993 2994
  rewind(file);

  char* chars = new char[size + 1];
  chars[size] = '\0';
2995 2996 2997
  for (size_t i = 0; i < size;) {
    i += fread(&chars[i], 1, size - i, file);
    if (ferror(file)) {
2998
      base::Fclose(file);
2999 3000 3001
      delete[] chars;
      return nullptr;
    }
3002
  }
3003
  base::Fclose(file);
3004
  *size_out = static_cast<int>(size);
3005 3006 3007
  return chars;
}

3008
void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) {
3009 3010
  static_assert(sizeof(char) == sizeof(uint8_t),
                "char and uint8_t should both have 1 byte");
3011
  Isolate* isolate = args.GetIsolate();
3012 3013
  String::Utf8Value filename(isolate, args[0]);
  int length;
3014
  if (*filename == nullptr) {
3015
    Throw(isolate, "Error loading file");
3016
    return;
3017
  }
3018

3019 3020
  uint8_t* data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
  if (data == nullptr) {
3021
    Throw(isolate, "Error reading file");
3022
    return;
3023
  }
3024 3025 3026 3027 3028 3029 3030 3031 3032
  std::unique_ptr<v8::BackingStore> backing_store =
      ArrayBuffer::NewBackingStore(
          data, length,
          [](void* data, size_t length, void*) {
            delete[] reinterpret_cast<uint8_t*>(data);
          },
          nullptr);
  Local<v8::ArrayBuffer> buffer =
      ArrayBuffer::New(isolate, std::move(backing_store));
3033

3034
  args.GetReturnValue().Set(buffer);
3035 3036
}

3037
// Reads a file into a v8 string.
3038
Local<String> Shell::ReadFile(Isolate* isolate, const char* name) {
3039
  std::unique_ptr<base::OS::MemoryMappedFile> file(
3040 3041
      base::OS::MemoryMappedFile::open(
          name, base::OS::MemoryMappedFile::FileMode::kReadOnly));
3042 3043 3044 3045
  if (!file) return Local<String>();

  int size = static_cast<int>(file->size());
  char* chars = static_cast<char*>(file->memory());
3046
  Local<String> result;
3047
  if (i::FLAG_use_external_strings && i::String::IsAscii(chars, size)) {
3048
    String::ExternalOneByteStringResource* resource =
3049
        new ExternalOwningOneByteStringResource(std::move(file));
3050 3051 3052 3053 3054
    result = String::NewExternalOneByte(isolate, resource).ToLocalChecked();
  } else {
    result = String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
                 .ToLocalChecked();
  }
3055 3056 3057
  return result;
}

3058
void Shell::RunShell(Isolate* isolate) {
3059
  HandleScope outer_scope(isolate);
3060 3061 3062
  v8::Local<v8::Context> context =
      v8::Local<v8::Context>::New(isolate, evaluation_context_);
  v8::Context::Scope context_scope(context);
3063
  PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
3064
  Local<String> name = String::NewFromUtf8Literal(isolate, "(d8)");
3065
  printf("V8 version %s\n", V8::GetVersion());
3066
  while (true) {
3067
    HandleScope inner_scope(isolate);
3068
    printf("d8> ");
3069
    Local<String> input = Shell::ReadFromStdin(isolate);
3070
    if (input.IsEmpty()) break;
3071 3072
    ExecuteString(isolate, input, name, kPrintResult, kReportExceptions,
                  kProcessMessageQueue);
3073 3074
  }
  printf("\n");
3075 3076 3077
  // We need to explicitly clean up the module embedder data for
  // the interative shell context.
  DisposeModuleEmbedderData(context);
3078 3079
}

3080 3081 3082 3083 3084 3085
class InspectorFrontend final : public v8_inspector::V8Inspector::Channel {
 public:
  explicit InspectorFrontend(Local<Context> context) {
    isolate_ = context->GetIsolate();
    context_.Reset(isolate_, context);
  }
3086
  ~InspectorFrontend() override = default;
3087 3088

 private:
3089 3090 3091 3092 3093 3094 3095 3096
  void sendResponse(
      int callId,
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
    Send(message->string());
  }
  void sendNotification(
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
    Send(message->string());
3097 3098 3099 3100
  }
  void flushProtocolNotifications() override {}

  void Send(const v8_inspector::StringView& string) {
3101
    v8::Isolate::AllowJavascriptExecutionScope allow_script(isolate_);
3102
    v8::HandleScope handle_scope(isolate_);
3103
    int length = static_cast<int>(string.length());
3104
    DCHECK_LT(length, v8::String::kMaxLength);
3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115
    Local<String> message =
        (string.is8Bit()
             ? v8::String::NewFromOneByte(
                   isolate_,
                   reinterpret_cast<const uint8_t*>(string.characters8()),
                   v8::NewStringType::kNormal, length)
             : v8::String::NewFromTwoByte(
                   isolate_,
                   reinterpret_cast<const uint16_t*>(string.characters16()),
                   v8::NewStringType::kNormal, length))
            .ToLocalChecked();
3116 3117
    Local<String> callback_name = v8::String::NewFromUtf8Literal(
        isolate_, "receive", NewStringType::kInternalized);
3118 3119 3120 3121 3122 3123
    Local<Context> context = context_.Get(isolate_);
    Local<Value> callback =
        context->Global()->Get(context, callback_name).ToLocalChecked();
    if (callback->IsFunction()) {
      v8::TryCatch try_catch(isolate_);
      Local<Value> args[] = {message};
3124
      USE(callback.As<Function>()->Call(context, Undefined(isolate_), 1, args));
3125 3126
#ifdef DEBUG
      if (try_catch.HasCaught()) {
3127
        Local<Object> exception = try_catch.Exception().As<Object>();
3128 3129 3130 3131
        Local<String> key = v8::String::NewFromUtf8Literal(
            isolate_, "message", NewStringType::kInternalized);
        Local<String> expected = v8::String::NewFromUtf8Literal(
            isolate_, "Maximum call stack size exceeded");
3132
        Local<Value> value = exception->Get(context, key).ToLocalChecked();
3133
        DCHECK(value->StrictEquals(expected));
3134 3135
      }
#endif
3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159
    }
  }

  Isolate* isolate_;
  Global<Context> context_;
};

class InspectorClient : public v8_inspector::V8InspectorClient {
 public:
  InspectorClient(Local<Context> context, bool connect) {
    if (!connect) return;
    isolate_ = context->GetIsolate();
    channel_.reset(new InspectorFrontend(context));
    inspector_ = v8_inspector::V8Inspector::create(isolate_, this);
    session_ =
        inspector_->connect(1, channel_.get(), v8_inspector::StringView());
    context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
    inspector_->contextCreated(v8_inspector::V8ContextInfo(
        context, kContextGroupId, v8_inspector::StringView()));

    Local<Value> function =
        FunctionTemplate::New(isolate_, SendInspectorMessage)
            ->GetFunction(context)
            .ToLocalChecked();
3160 3161
    Local<String> function_name = String::NewFromUtf8Literal(
        isolate_, "send", NewStringType::kInternalized);
3162 3163 3164 3165 3166
    CHECK(context->Global()->Set(context, function_name, function).FromJust());

    context_.Reset(isolate_, context);
  }

3167 3168 3169
  void runMessageLoopOnPause(int contextGroupId) override {
    v8::Isolate::AllowJavascriptExecutionScope allow_script(isolate_);
    v8::HandleScope handle_scope(isolate_);
3170 3171
    Local<String> callback_name = v8::String::NewFromUtf8Literal(
        isolate_, "handleInspectorMessage", NewStringType::kInternalized);
3172 3173 3174 3175 3176 3177
    Local<Context> context = context_.Get(isolate_);
    Local<Value> callback =
        context->Global()->Get(context, callback_name).ToLocalChecked();
    if (!callback->IsFunction()) return;

    v8::TryCatch try_catch(isolate_);
3178
    try_catch.SetVerbose(true);
3179 3180 3181
    is_paused = true;

    while (is_paused) {
3182
      USE(callback.As<Function>()->Call(context, Undefined(isolate_), 0, {}));
3183 3184 3185 3186 3187 3188 3189 3190
      if (try_catch.HasCaught()) {
        is_paused = false;
      }
    }
  }

  void quitMessageLoopOnPause() override { is_paused = false; }

3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213
 private:
  static v8_inspector::V8InspectorSession* GetSession(Local<Context> context) {
    InspectorClient* inspector_client = static_cast<InspectorClient*>(
        context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex));
    return inspector_client->session_.get();
  }

  Local<Context> ensureDefaultContextInGroup(int group_id) override {
    DCHECK(isolate_);
    DCHECK_EQ(kContextGroupId, group_id);
    return context_.Get(isolate_);
  }

  static void SendInspectorMessage(
      const v8::FunctionCallbackInfo<v8::Value>& args) {
    Isolate* isolate = args.GetIsolate();
    v8::HandleScope handle_scope(isolate);
    Local<Context> context = isolate->GetCurrentContext();
    args.GetReturnValue().Set(Undefined(isolate));
    Local<String> message = args[0]->ToString(context).ToLocalChecked();
    v8_inspector::V8InspectorSession* session =
        InspectorClient::GetSession(context);
    int length = message->Length();
3214
    std::unique_ptr<uint16_t[]> buffer(new uint16_t[length]);
3215
    message->Write(isolate, buffer.get(), 0, length);
3216
    v8_inspector::StringView message_view(buffer.get(), length);
3217 3218 3219 3220
    {
      v8::SealHandleScope seal_handle_scope(isolate);
      session->dispatchProtocolMessage(message_view);
    }
3221 3222 3223 3224 3225 3226 3227 3228
    args.GetReturnValue().Set(True(isolate));
  }

  static const int kContextGroupId = 1;

  std::unique_ptr<v8_inspector::V8Inspector> inspector_;
  std::unique_ptr<v8_inspector::V8InspectorSession> session_;
  std::unique_ptr<v8_inspector::V8Inspector::Channel> channel_;
3229
  bool is_paused = false;
3230 3231 3232
  Global<Context> context_;
  Isolate* isolate_;
};
3233

3234 3235
SourceGroup::~SourceGroup() {
  delete thread_;
3236
  thread_ = nullptr;
3237 3238
}

3239 3240 3241 3242 3243 3244 3245 3246
bool ends_with(const char* input, const char* suffix) {
  size_t input_length = strlen(input);
  size_t suffix_length = strlen(suffix);
  if (suffix_length <= input_length) {
    return strcmp(input + input_length - suffix_length, suffix) == 0;
  }
  return false;
}
3247

3248 3249
bool SourceGroup::Execute(Isolate* isolate) {
  bool success = true;
3250
#ifdef V8_FUZZILLI
3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279
  if (fuzzilli_reprl) {
    HandleScope handle_scope(isolate);
    Local<String> file_name =
        String::NewFromUtf8(isolate, "fuzzcode.js", NewStringType::kNormal)
            .ToLocalChecked();

    size_t script_size;
    CHECK_EQ(read(REPRL_CRFD, &script_size, 8), 8);
    char* buffer = new char[script_size + 1];
    char* ptr = buffer;
    size_t remaining = script_size;
    while (remaining > 0) {
      ssize_t rv = read(REPRL_DRFD, ptr, remaining);
      CHECK_GE(rv, 0);
      remaining -= rv;
      ptr += rv;
    }
    buffer[script_size] = 0;

    Local<String> source =
        String::NewFromUtf8(isolate, buffer, NewStringType::kNormal)
            .ToLocalChecked();
    delete[] buffer;
    Shell::set_script_executed();
    if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
                              Shell::kReportExceptions,
                              Shell::kNoProcessMessageQueue)) {
      return false;
    }
3280 3281
  }
#endif  // V8_FUZZILLI
3282 3283 3284 3285
  for (int i = begin_offset_; i < end_offset_; ++i) {
    const char* arg = argv_[i];
    if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
      // Execute argument given to -e option directly.
3286
      HandleScope handle_scope(isolate);
3287
      Local<String> file_name = String::NewFromUtf8Literal(isolate, "unnamed");
3288
      Local<String> source =
3289
          String::NewFromUtf8(isolate, argv_[i + 1]).ToLocalChecked();
3290
      Shell::set_script_executed();
3291 3292 3293
      if (!Shell::ExecuteString(isolate, source, file_name,
                                Shell::kNoPrintResult, Shell::kReportExceptions,
                                Shell::kNoProcessMessageQueue)) {
3294
        success = false;
3295
        break;
3296 3297
      }
      ++i;
3298
      continue;
3299
    } else if (ends_with(arg, ".mjs")) {
3300
      Shell::set_script_executed();
3301
      if (!Shell::ExecuteModule(isolate, arg)) {
3302
        success = false;
3303 3304 3305
        break;
      }
      continue;
3306 3307 3308
    } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) {
      // Treat the next file as a module.
      arg = argv_[++i];
3309
      Shell::set_script_executed();
3310
      if (!Shell::ExecuteModule(isolate, arg)) {
3311
        success = false;
3312 3313 3314
        break;
      }
      continue;
3315 3316
    } else if (arg[0] == '-') {
      // Ignore other options. They have been parsed already.
3317 3318 3319 3320 3321
      continue;
    }

    // Use all other arguments as names of files to load and run.
    HandleScope handle_scope(isolate);
3322
    Local<String> file_name =
3323
        String::NewFromUtf8(isolate, arg).ToLocalChecked();
3324
    Local<String> source = ReadFile(isolate, arg);
3325 3326
    if (source.IsEmpty()) {
      printf("Error reading '%s'\n", arg);
3327
      base::OS::ExitProcess(1);
3328
    }
3329
    Shell::set_script_executed();
3330
    Shell::update_script_size(source->Length());
3331 3332 3333
    if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
                              Shell::kReportExceptions,
                              Shell::kProcessMessageQueue)) {
3334
      success = false;
3335
      break;
3336
    }
3337
  }
3338
  return success;
3339
}
3340

3341 3342 3343 3344
Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
  return Shell::ReadFile(isolate, name);
}

3345 3346
SourceGroup::IsolateThread::IsolateThread(SourceGroup* group)
    : base::Thread(GetThreadOptions("IsolateThread")), group_(group) {}
3347 3348

void SourceGroup::ExecuteInThread() {
3349
  Isolate::CreateParams create_params;
3350
  create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3351
  Isolate* isolate = Isolate::New(create_params);
3352
  Shell::SetWaitUntilDone(isolate, false);
3353
  D8Console console(isolate);
3354 3355
  Shell::Initialize(isolate, &console, false);

binji's avatar
binji committed
3356
  for (int i = 0; i < Shell::options.stress_runs; ++i) {
3357
    next_semaphore_.Wait();
3358 3359
    {
      Isolate::Scope iscope(isolate);
3360
      PerIsolateData data(isolate);
3361
      {
3362 3363 3364 3365
        HandleScope scope(isolate);
        Local<Context> context = Shell::CreateEvaluationContext(isolate);
        {
          Context::Scope cscope(context);
3366 3367
          InspectorClient inspector_client(context,
                                           Shell::options.enable_inspector);
3368 3369
          PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
          Execute(isolate);
3370
          Shell::CompleteMessageLoop(isolate);
3371
        }
3372
        DisposeModuleEmbedderData(context);
3373
      }
3374
      Shell::CollectGarbage(isolate);
3375
    }
3376
    done_semaphore_.Signal();
binji's avatar
binji committed
3377
  }
3378

3379 3380 3381 3382
  isolate->Dispose();
}

void SourceGroup::StartExecuteInThread() {
3383
  if (thread_ == nullptr) {
3384
    thread_ = new IsolateThread(this);
3385
    CHECK(thread_->Start());
3386
  }
3387
  next_semaphore_.Signal();
3388 3389 3390
}

void SourceGroup::WaitForThread() {
3391
  if (thread_ == nullptr) return;
binji's avatar
binji committed
3392 3393 3394 3395
  done_semaphore_.Wait();
}

void SourceGroup::JoinThread() {
3396
  if (thread_ == nullptr) return;
binji's avatar
binji committed
3397
  thread_->Join();
3398
}
3399

3400
void SerializationDataQueue::Enqueue(std::unique_ptr<SerializationData> data) {
3401
  base::MutexGuard lock_guard(&mutex_);
3402
  data_.push_back(std::move(data));
3403 3404
}

3405 3406 3407
bool SerializationDataQueue::Dequeue(
    std::unique_ptr<SerializationData>* out_data) {
  out_data->reset();
3408
  base::MutexGuard lock_guard(&mutex_);
3409 3410 3411 3412
  if (data_.empty()) return false;
  *out_data = std::move(data_[0]);
  data_.erase(data_.begin());
  return true;
3413 3414
}

3415
bool SerializationDataQueue::IsEmpty() {
3416
  base::MutexGuard lock_guard(&mutex_);
3417
  return data_.empty();
3418 3419
}

3420 3421 3422
void SerializationDataQueue::Clear() {
  base::MutexGuard lock_guard(&mutex_);
  data_.clear();
3423
}
3424

3425 3426 3427
Worker::Worker(const char* script) : script_(i::StrDup(script)) {
  running_.store(false);
}
3428

3429
Worker::~Worker() {
3430 3431
  DCHECK_NULL(isolate_);

3432
  delete thread_;
3433
  thread_ = nullptr;
3434
  delete[] script_;
3435
  script_ = nullptr;
3436
}
3437

3438
bool Worker::StartWorkerThread(std::shared_ptr<Worker> worker) {
3439
  worker->running_.store(true);
3440 3441 3442
  auto thread = new WorkerThread(worker);
  worker->thread_ = thread;
  if (thread->Start()) {
3443 3444
    // Wait until the worker is ready to receive messages.
    worker->started_semaphore_.Wait();
3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460
    Shell::AddRunningWorker(std::move(worker));
    return true;
  }
  return false;
}

void Worker::WorkerThread::Run() {
  // Prevent a lifetime cycle from Worker -> WorkerThread -> Worker.
  // We must clear the worker_ field of the thread, but we keep the
  // worker alive via a stack root until the thread finishes execution
  // and removes itself from the running set. Thereafter the only
  // remaining reference can be from a JavaScript object via a Managed.
  auto worker = std::move(worker_);
  worker_ = nullptr;
  worker->ExecuteInThread();
  Shell::RemoveRunningWorker(worker);
3461 3462
}

3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478
class ProcessMessageTask : public i::CancelableTask {
 public:
  ProcessMessageTask(i::CancelableTaskManager* task_manager,
                     std::shared_ptr<Worker> worker,
                     std::unique_ptr<SerializationData> data)
      : i::CancelableTask(task_manager),
        worker_(worker),
        data_(std::move(data)) {}

  void RunInternal() override { worker_->ProcessMessage(std::move(data_)); }

 private:
  std::shared_ptr<Worker> worker_;
  std::unique_ptr<SerializationData> data_;
};

3479
void Worker::PostMessage(std::unique_ptr<SerializationData> data) {
3480 3481 3482 3483 3484 3485 3486 3487 3488
  // Hold the worker_mutex_ so that the worker thread can't delete task_runner_
  // after we've checked running_.
  base::MutexGuard lock_guard(&worker_mutex_);
  if (!running_.load()) {
    return;
  }
  std::unique_ptr<v8::Task> task(new ProcessMessageTask(
      task_manager_, shared_from_this(), std::move(data)));
  task_runner_->PostNonNestableTask(std::move(task));
3489 3490
}

3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506
class TerminateTask : public i::CancelableTask {
 public:
  TerminateTask(i::CancelableTaskManager* task_manager,
                std::shared_ptr<Worker> worker)
      : i::CancelableTask(task_manager), worker_(worker) {}

  void RunInternal() override {
    // Make sure the worker doesn't enter the task loop after processing this
    // task.
    worker_->running_.store(false);
  }

 private:
  std::shared_ptr<Worker> worker_;
};

3507
std::unique_ptr<SerializationData> Worker::GetMessage() {
3508 3509 3510 3511
  std::unique_ptr<SerializationData> result;
  while (!out_queue_.Dequeue(&result)) {
    // If the worker is no longer running, and there are no messages in the
    // queue, don't expect any more messages from it.
3512 3513 3514
    if (!running_.load()) {
      break;
    }
3515 3516 3517
    out_semaphore_.Wait();
  }
  return result;
3518 3519 3520
}

void Worker::Terminate() {
3521 3522 3523 3524 3525 3526 3527 3528 3529 3530
  // Hold the worker_mutex_ so that the worker thread can't delete task_runner_
  // after we've checked running_.
  base::MutexGuard lock_guard(&worker_mutex_);
  if (!running_.load()) {
    return;
  }
  // Post a task to wake up the worker thread.
  std::unique_ptr<v8::Task> task(
      new TerminateTask(task_manager_, shared_from_this()));
  task_runner_->PostTask(std::move(task));
binji's avatar
binji committed
3531 3532
}

3533
void Worker::TerminateAndWaitForThread() {
binji's avatar
binji committed
3534
  Terminate();
3535
  thread_->Join();
3536 3537
}

3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549
void Worker::ProcessMessage(std::unique_ptr<SerializationData> data) {
  if (!running_.load()) {
    return;
  }

  DCHECK_NOT_NULL(isolate_);
  HandleScope scope(isolate_);
  Local<Context> context = context_.Get(isolate_);
  Context::Scope cscope(context);
  Local<Object> global = context->Global();

  // Get the message handler.
3550 3551 3552 3553 3554
  MaybeLocal<Value> maybe_onmessage = global->Get(
      context, String::NewFromUtf8Literal(isolate_, "onmessage",
                                          NewStringType::kInternalized));
  Local<Value> onmessage;
  if (!maybe_onmessage.ToLocal(&onmessage) || !onmessage->IsFunction()) {
3555 3556
    return;
  }
3557
  Local<Function> onmessage_fun = onmessage.As<Function>();
3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581

  v8::TryCatch try_catch(isolate_);
  try_catch.SetVerbose(true);
  Local<Value> value;
  if (Shell::DeserializeValue(isolate_, std::move(data)).ToLocal(&value)) {
    Local<Value> argv[] = {value};
    MaybeLocal<Value> result = onmessage_fun->Call(context, global, 1, argv);
    USE(result);
  }
}

void Worker::ProcessMessages() {
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate_);
  i::SaveAndSwitchContext saved_context(i_isolate, i::Context());
  SealHandleScope shs(isolate_);
  while (running_.load() && v8::platform::PumpMessageLoop(
                                g_default_platform, isolate_,
                                platform::MessageLoopBehavior::kWaitForWork)) {
    if (running_.load()) {
      MicrotasksScope::PerformCheckpoint(isolate_);
    }
  }
}

3582 3583 3584
void Worker::ExecuteInThread() {
  Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3585
  isolate_ = Isolate::New(create_params);
3586
  {
3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598
    base::MutexGuard lock_guard(&worker_mutex_);
    task_runner_ = g_default_platform->GetForegroundTaskRunner(isolate_);
    task_manager_ =
        reinterpret_cast<i::Isolate*>(isolate_)->cancelable_task_manager();
  }
  // The Worker is now ready to receive messages.
  started_semaphore_.Signal();

  D8Console console(isolate_);
  Shell::Initialize(isolate_, &console, false);
  {
    Isolate::Scope iscope(isolate_);
3599
    {
3600 3601 3602 3603
      HandleScope scope(isolate_);
      PerIsolateData data(isolate_);
      Local<Context> context = Shell::CreateEvaluationContext(isolate_);
      context_.Reset(isolate_, context);
3604 3605
      {
        Context::Scope cscope(context);
3606
        PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate_));
3607

3608
        Local<Object> global = context->Global();
3609
        Local<Value> this_value = External::New(isolate_, this);
3610
        Local<FunctionTemplate> postmessage_fun_template =
3611
            FunctionTemplate::New(isolate_, PostMessageOut, this_value);
3612

3613
        Local<Function> postmessage_fun;
3614 3615 3616 3617
        if (postmessage_fun_template->GetFunction(context).ToLocal(
                &postmessage_fun)) {
          global
              ->Set(context,
3618
                    v8::String::NewFromUtf8Literal(
3619
                        isolate_, "postMessage", NewStringType::kInternalized),
3620 3621
                    postmessage_fun)
              .FromJust();
3622 3623 3624
        }

        // First run the script
3625
        Local<String> file_name =
3626
            String::NewFromUtf8Literal(isolate_, "unnamed");
3627
        Local<String> source =
3628
            String::NewFromUtf8(isolate_, script_).ToLocalChecked();
3629
        if (Shell::ExecuteString(
3630
                isolate_, source, file_name, Shell::kNoPrintResult,
3631
                Shell::kReportExceptions, Shell::kProcessMessageQueue)) {
3632
          // Check that there's a message handler
3633
          Local<Value> onmessage =
3634
              global
3635 3636 3637
                  ->Get(context, String::NewFromUtf8Literal(
                                     isolate_, "onmessage",
                                     NewStringType::kInternalized))
3638
                  .ToLocalChecked();
3639 3640
          if (onmessage->IsFunction()) {
            // Now wait for messages
3641
            ProcessMessages();
3642 3643 3644
          }
        }
      }
3645
      DisposeModuleEmbedderData(context);
3646
    }
3647
    Shell::CollectGarbage(isolate_);
3648
  }
3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661
  // TODO(cbruni): Check for unhandled promises here.
  {
    // Hold the mutex to ensure running_ and task_runner_ change state
    // atomically (see Worker::PostMessage which reads them).
    base::MutexGuard lock_guard(&worker_mutex_);
    running_.store(false);
    task_runner_.reset();
    task_manager_ = nullptr;
  }
  context_.Reset();
  platform::NotifyIsolateShutdown(g_default_platform, isolate_);
  isolate_->Dispose();
  isolate_ = nullptr;
3662 3663 3664 3665

  // Post nullptr to wake the thread waiting on GetMessage() if there is one.
  out_queue_.Enqueue(nullptr);
  out_semaphore_.Signal();
3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676
}

void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);

  if (args.Length() < 1) {
    Throw(isolate, "Invalid argument");
    return;
  }

3677
  Local<Value> message = args[0];
3678 3679 3680 3681
  Local<Value> transfer = Undefined(isolate);
  std::unique_ptr<SerializationData> data =
      Shell::SerializeValue(isolate, message, transfer);
  if (data) {
3682
    DCHECK(args.Data()->IsExternal());
3683
    Local<External> this_value = args.Data().As<External>();
3684
    Worker* worker = static_cast<Worker*>(this_value->Value());
3685
    worker->out_queue_.Enqueue(std::move(data));
3686
    worker->out_semaphore_.Signal();
3687 3688
  }
}
3689

3690
bool Shell::SetOptions(int argc, char* argv[]) {
3691
  bool logfile_per_isolate = false;
3692
  bool no_always_opt = false;
3693
  for (int i = 0; i < argc; i++) {
3694 3695 3696 3697 3698 3699 3700
    if (strcmp(argv[i], "--") == 0) {
      argv[i] = nullptr;
      for (int j = i + 1; j < argc; j++) {
        options.arguments.push_back(argv[j]);
        argv[j] = nullptr;
      }
      break;
3701 3702 3703
    } else if (strcmp(argv[i], "--no-arguments") == 0) {
      options.include_arguments = false;
      argv[i] = nullptr;
3704 3705 3706
    } else if (strcmp(argv[i], "--simulate-errors") == 0) {
      options.simulate_errors = true;
      argv[i] = nullptr;
3707
    } else if (strcmp(argv[i], "--stress-opt") == 0) {
3708
      options.stress_opt = true;
3709
      argv[i] = nullptr;
3710 3711
    } else if (strcmp(argv[i], "--nostress-opt") == 0 ||
               strcmp(argv[i], "--no-stress-opt") == 0) {
3712
      options.stress_opt = false;
3713
      argv[i] = nullptr;
3714 3715
    } else if (strcmp(argv[i], "--stress-snapshot") == 0) {
      options.stress_snapshot = true;
3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727
      // Incremental marking is incompatible with the stress_snapshot mode;
      // specifically, serialization may clear bytecode arrays from shared
      // function infos which the MarkCompactCollector (running concurrently)
      // may still need. See also https://crbug.com/v8/10882.
      //
      // We thus force the implication
      //
      //   --stress-snapshot ~~> --no-incremental-marking
      //
      // Note: This is not an issue in production because we don't clear SFI's
      // there (that only happens in mksnapshot and in --stress-snapshot mode).
      i::FLAG_incremental_marking = false;
3728 3729 3730 3731 3732
      argv[i] = nullptr;
    } else if (strcmp(argv[i], "--nostress-snapshot") == 0 ||
               strcmp(argv[i], "--no-stress-snapshot") == 0) {
      options.stress_snapshot = false;
      argv[i] = nullptr;
3733 3734
    } else if (strcmp(argv[i], "--noalways-opt") == 0 ||
               strcmp(argv[i], "--no-always-opt") == 0) {
3735 3736 3737 3738 3739 3740 3741
      no_always_opt = true;
    } else if (strcmp(argv[i], "--fuzzing") == 0 ||
               strcmp(argv[i], "--no-abort-on-contradictory-flags") == 0 ||
               strcmp(argv[i], "--noabort-on-contradictory-flags") == 0) {
      check_d8_flag_contradictions = false;
    } else if (strcmp(argv[i], "--abort-on-contradictory-flags") == 0) {
      check_d8_flag_contradictions = true;
3742 3743
    } else if (strcmp(argv[i], "--logfile-per-isolate") == 0) {
      logfile_per_isolate = true;
3744
      argv[i] = nullptr;
3745
    } else if (strcmp(argv[i], "--shell") == 0) {
3746
      options.interactive_shell = true;
3747
      argv[i] = nullptr;
3748
    } else if (strcmp(argv[i], "--test") == 0) {
3749
      options.test_shell = true;
3750
      argv[i] = nullptr;
3751 3752 3753
    } else if (strcmp(argv[i], "--notest") == 0 ||
               strcmp(argv[i], "--no-test") == 0) {
      options.test_shell = false;
3754
      argv[i] = nullptr;
3755 3756
    } else if (strcmp(argv[i], "--send-idle-notification") == 0) {
      options.send_idle_notification = true;
3757
      argv[i] = nullptr;
3758 3759 3760 3761
    } else if (strcmp(argv[i], "--invoke-weak-callbacks") == 0) {
      options.invoke_weak_callbacks = true;
      // TODO(jochen) See issue 3351
      options.send_idle_notification = true;
3762
      argv[i] = nullptr;
3763 3764
    } else if (strcmp(argv[i], "--omit-quit") == 0) {
      options.omit_quit = true;
3765
      argv[i] = nullptr;
3766
    } else if (strcmp(argv[i], "--no-wait-for-background-tasks") == 0) {
3767 3768
      // TODO(herhut) Remove this flag once wasm compilation is fully
      // isolate-independent.
3769
      options.wait_for_background_tasks = false;
3770
      argv[i] = nullptr;
3771 3772 3773 3774
    } else if (strcmp(argv[i], "-f") == 0) {
      // Ignore any -f flags for compatibility with other stand-alone
      // JavaScript engines.
      continue;
3775 3776 3777
    } else if (strcmp(argv[i], "--ignore-unhandled-promises") == 0) {
      options.ignore_unhandled_promises = true;
      argv[i] = nullptr;
3778 3779
    } else if (strcmp(argv[i], "--isolate") == 0) {
      options.num_isolates++;
3780 3781
    } else if (strcmp(argv[i], "--throws") == 0) {
      options.expected_to_throw = true;
3782
      argv[i] = nullptr;
3783 3784
    } else if (strncmp(argv[i], "--icu-data-file=", 16) == 0) {
      options.icu_data_file = argv[i] + 16;
3785
      argv[i] = nullptr;
3786 3787 3788
    } else if (strncmp(argv[i], "--icu-locale=", 13) == 0) {
      options.icu_locale = argv[i] + 13;
      argv[i] = nullptr;
3789 3790 3791
#ifdef V8_USE_EXTERNAL_STARTUP_DATA
    } else if (strncmp(argv[i], "--snapshot_blob=", 16) == 0) {
      options.snapshot_blob = argv[i] + 16;
3792
      argv[i] = nullptr;
3793
#endif  // V8_USE_EXTERNAL_STARTUP_DATA
3794 3795 3796 3797
    } else if (strcmp(argv[i], "--cache") == 0 ||
               strncmp(argv[i], "--cache=", 8) == 0) {
      const char* value = argv[i] + 7;
      if (!*value || strncmp(value, "=code", 6) == 0) {
3798 3799 3800
        options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
        options.code_cache_options =
            ShellOptions::CodeCacheOptions::kProduceCache;
3801 3802
      } else if (strncmp(value, "=none", 6) == 0) {
        options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
3803 3804 3805 3806 3807 3808 3809 3810 3811 3812
        options.code_cache_options =
            ShellOptions::CodeCacheOptions::kNoProduceCache;
      } else if (strncmp(value, "=after-execute", 15) == 0) {
        options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
        options.code_cache_options =
            ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute;
      } else if (strncmp(value, "=full-code-cache", 17) == 0) {
        options.compile_options = v8::ScriptCompiler::kEagerCompile;
        options.code_cache_options =
            ShellOptions::CodeCacheOptions::kProduceCache;
3813 3814 3815 3816
      } else {
        printf("Unknown option to --cache.\n");
        return false;
      }
3817
      argv[i] = nullptr;
3818 3819 3820 3821 3822 3823 3824
    } else if (strcmp(argv[i], "--streaming-compile") == 0) {
      options.streaming_compile = true;
      argv[i] = nullptr;
    } else if ((strcmp(argv[i], "--no-streaming-compile") == 0) ||
               (strcmp(argv[i], "--nostreaming-compile") == 0)) {
      options.streaming_compile = false;
      argv[i] = nullptr;
3825 3826
    } else if (strcmp(argv[i], "--enable-tracing") == 0) {
      options.trace_enabled = true;
3827
      argv[i] = nullptr;
3828 3829 3830
    } else if (strncmp(argv[i], "--trace-path=", 13) == 0) {
      options.trace_path = argv[i] + 13;
      argv[i] = nullptr;
3831 3832
    } else if (strncmp(argv[i], "--trace-config=", 15) == 0) {
      options.trace_config = argv[i] + 15;
3833
      argv[i] = nullptr;
3834 3835
    } else if (strcmp(argv[i], "--enable-inspector") == 0) {
      options.enable_inspector = true;
3836
      argv[i] = nullptr;
3837 3838
    } else if (strncmp(argv[i], "--lcov=", 7) == 0) {
      options.lcov_file = argv[i] + 7;
3839
      argv[i] = nullptr;
3840 3841
    } else if (strcmp(argv[i], "--disable-in-process-stack-traces") == 0) {
      options.disable_in_process_stack_traces = true;
3842
      argv[i] = nullptr;
3843 3844 3845
#ifdef V8_OS_POSIX
    } else if (strncmp(argv[i], "--read-from-tcp-port=", 21) == 0) {
      options.read_from_tcp_port = atoi(argv[i] + 21);
3846
      argv[i] = nullptr;
3847
#endif  // V8_OS_POSIX
3848 3849
    } else if (strcmp(argv[i], "--enable-os-system") == 0) {
      options.enable_os_system = true;
3850
      argv[i] = nullptr;
3851 3852 3853
    } else if (strcmp(argv[i], "--quiet-load") == 0) {
      options.quiet_load = true;
      argv[i] = nullptr;
3854 3855 3856
    } else if (strncmp(argv[i], "--thread-pool-size=", 19) == 0) {
      options.thread_pool_size = atoi(argv[i] + 19);
      argv[i] = nullptr;
3857 3858 3859 3860
    } else if (strcmp(argv[i], "--stress-delay-tasks") == 0) {
      // Delay execution of tasks by 0-100ms randomly (based on --random-seed).
      options.stress_delay_tasks = true;
      argv[i] = nullptr;
3861 3862 3863 3864 3865 3866 3867
    } else if (strcmp(argv[i], "--cpu-profiler") == 0) {
      options.cpu_profiler = true;
      argv[i] = nullptr;
    } else if (strcmp(argv[i], "--cpu-profiler-print") == 0) {
      options.cpu_profiler = true;
      options.cpu_profiler_print = true;
      argv[i] = nullptr;
3868 3869 3870 3871 3872 3873 3874 3875
#ifdef V8_FUZZILLI
    } else if (strcmp(argv[i], "--no-fuzzilli-enable-builtins-coverage") == 0) {
      options.fuzzilli_enable_builtins_coverage = false;
      argv[i] = nullptr;
    } else if (strcmp(argv[i], "--fuzzilli-coverage-statistics") == 0) {
      options.fuzzilli_coverage_statistics = true;
      argv[i] = nullptr;
#endif
3876 3877 3878
    } else if (strcmp(argv[i], "--fuzzy-module-file-extensions") == 0) {
      options.fuzzy_module_file_extensions = true;
      argv[i] = nullptr;
3879 3880 3881 3882 3883 3884
#ifdef V8_ENABLE_SYSTEM_INSTRUMENTATION
    } else if (strcmp(argv[i], "--enable-system-instrumentation") == 0) {
      options.enable_system_instrumentation = true;
      options.trace_enabled = true;
      argv[i] = nullptr;
#endif
3885
    }
3886 3887
  }

3888 3889 3890 3891
  if (options.stress_opt && no_always_opt && check_d8_flag_contradictions) {
    FATAL("Flag --no-always-opt is incompatible with --stress-opt.");
  }

3892 3893 3894 3895 3896 3897 3898 3899
  const char* usage =
      "Synopsis:\n"
      "  shell [options] [--shell] [<file>...]\n"
      "  d8 [options] [-e <string>] [--shell] [[--module] <file>...]\n\n"
      "  -e        execute a string in V8\n"
      "  --shell   run an interactive JavaScript shell\n"
      "  --module  execute a file as a JavaScript module\n\n";
  using HelpOptions = i::FlagList::HelpOptions;
3900
  i::FLAG_abort_on_contradictory_flags = true;
3901 3902
  i::FlagList::SetFlagsFromCommandLine(&argc, argv, true,
                                       HelpOptions(HelpOptions::kExit, usage));
3903
  options.mock_arraybuffer_allocator = i::FLAG_mock_arraybuffer_allocator;
3904 3905
  options.mock_arraybuffer_allocator_limit =
      i::FLAG_mock_arraybuffer_allocator_limit;
3906 3907 3908
#if V8_OS_LINUX
  options.multi_mapped_mock_allocator = i::FLAG_multi_mapped_mock_allocator;
#endif
3909

3910
  // Set up isolated source groups.
3911 3912 3913 3914 3915 3916 3917 3918 3919
  options.isolate_sources = new SourceGroup[options.num_isolates];
  SourceGroup* current = options.isolate_sources;
  current->Begin(argv, 1);
  for (int i = 1; i < argc; i++) {
    const char* str = argv[i];
    if (strcmp(str, "--isolate") == 0) {
      current->End(i);
      current++;
      current->Begin(argv, i + 1);
3920 3921
    } else if (strcmp(str, "--module") == 0) {
      // Pass on to SourceGroup, which understands this option.
3922
    } else if (strncmp(str, "--", 2) == 0) {
3923 3924 3925
      if (!i::FLAG_correctness_fuzzer_suppressions) {
        printf("Warning: unknown flag %s.\nTry --help for options\n", str);
      }
binji's avatar
binji committed
3926
    } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
3927
      set_script_executed();
binji's avatar
binji committed
3928 3929
    } else if (strncmp(str, "-", 1) != 0) {
      // Not a flag, so it must be a script to execute.
3930
      set_script_executed();
3931 3932 3933 3934
    }
  }
  current->End(argc);

3935
  if (!logfile_per_isolate && options.num_isolates) {
3936
    V8::SetFlagsFromString("--no-logfile-per-isolate");
3937 3938
  }

3939 3940 3941
  return true;
}

3942
int Shell::RunMain(Isolate* isolate, bool last_run) {
3943 3944 3945
  for (int i = 1; i < options.num_isolates; ++i) {
    options.isolate_sources[i].StartExecuteInThread();
  }
3946
  bool success = true;
3947
  {
3948
    SetWaitUntilDone(isolate, false);
3949
    if (options.lcov_file) {
3950
      debug::Coverage::SelectMode(isolate, debug::CoverageMode::kBlockCount);
3951
    }
3952 3953
    HandleScope scope(isolate);
    Local<Context> context = CreateEvaluationContext(isolate);
3954
    bool use_existing_context = last_run && use_interactive_shell();
3955
    if (use_existing_context) {
3956 3957
      // Keep using the same context in the interactive shell.
      evaluation_context_.Reset(isolate, context);
3958
    }
3959 3960
    {
      Context::Scope cscope(context);
3961
      InspectorClient inspector_client(context, options.enable_inspector);
3962
      PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
3963 3964
      if (!options.isolate_sources[0].Execute(isolate)) success = false;
      if (!CompleteMessageLoop(isolate)) success = false;
3965
    }
3966 3967 3968
    if (!use_existing_context) {
      DisposeModuleEmbedderData(context);
    }
3969
    WriteLcovData(isolate, options.lcov_file);
3970 3971
    if (last_run && options.stress_snapshot) {
      static constexpr bool kClearRecompilableData = true;
3972 3973
      i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
      i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
3974 3975 3976
      // TODO(jgruber,v8:10500): Don't deoptimize once we support serialization
      // of optimized code.
      i::Deoptimizer::DeoptimizeAll(i_isolate);
3977 3978
      i::Snapshot::ClearReconstructableDataForSerialization(
          i_isolate, kClearRecompilableData);
3979 3980 3981
      i::Snapshot::SerializeDeserializeAndVerifyForTesting(i_isolate,
                                                           i_context);
    }
3982
  }
3983 3984
  CollectGarbage(isolate);
  for (int i = 1; i < options.num_isolates; ++i) {
binji's avatar
binji committed
3985 3986 3987 3988 3989
    if (last_run) {
      options.isolate_sources[i].JoinThread();
    } else {
      options.isolate_sources[i].WaitForThread();
    }
3990
  }
3991
  WaitForRunningWorkers();
3992 3993 3994 3995
  if (Shell::unhandled_promise_rejections_.load() > 0) {
    printf("%i pending unhandled Promise rejection(s) detected.\n",
           Shell::unhandled_promise_rejections_.load());
    success = false;
3996 3997 3998
    // RunMain may be executed multiple times, e.g. in REPRL mode, so we have to
    // reset this counter.
    Shell::unhandled_promise_rejections_.store(0);
3999
  }
4000 4001
  // In order to finish successfully, success must be != expected_to_throw.
  return success == Shell::options.expected_to_throw ? 1 : 0;
4002 4003 4004
}

void Shell::CollectGarbage(Isolate* isolate) {
4005
  if (options.send_idle_notification) {
4006
    const double kLongIdlePauseInSeconds = 1.0;
4007
    isolate->ContextDisposedNotification();
4008 4009
    isolate->IdleNotificationDeadline(
        g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
4010
  }
4011 4012 4013 4014
  if (options.invoke_weak_callbacks) {
    // By sending a low memory notifications, we will try hard to collect all
    // garbage and will therefore also invoke all weak callbacks of actually
    // unreachable persistent handles.
4015
    isolate->LowMemoryNotification();
4016
  }
4017 4018
}

4019
void Shell::SetWaitUntilDone(Isolate* isolate, bool value) {
4020
  base::MutexGuard guard(isolate_status_lock_.Pointer());
4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034
  isolate_status_[isolate] = value;
}

void Shell::NotifyStartStreamingTask(Isolate* isolate) {
  DCHECK(options.streaming_compile);
  base::MutexGuard guard(isolate_status_lock_.Pointer());
  ++isolate_running_streaming_tasks_[isolate];
}

void Shell::NotifyFinishStreamingTask(Isolate* isolate) {
  DCHECK(options.streaming_compile);
  base::MutexGuard guard(isolate_status_lock_.Pointer());
  --isolate_running_streaming_tasks_[isolate];
  DCHECK_GE(isolate_running_streaming_tasks_[isolate], 0);
4035 4036
}

4037
namespace {
4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054
bool RunSetTimeoutCallback(Isolate* isolate, bool* did_run) {
  PerIsolateData* data = PerIsolateData::Get(isolate);
  HandleScope handle_scope(isolate);
  Local<Function> callback;
  if (!data->GetTimeoutCallback().ToLocal(&callback)) return true;
  Local<Context> context;
  if (!data->GetTimeoutContext().ToLocal(&context)) return true;
  TryCatch try_catch(isolate);
  try_catch.SetVerbose(true);
  Context::Scope context_scope(context);
  if (callback->Call(context, Undefined(isolate), 0, nullptr).IsEmpty()) {
    return false;
  }
  *did_run = true;
  return true;
}

4055 4056 4057
bool ProcessMessages(
    Isolate* isolate,
    const std::function<platform::MessageLoopBehavior()>& behavior) {
4058
  while (true) {
4059
    i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
4060
    i::SaveAndSwitchContext saved_context(i_isolate, i::Context());
4061
    SealHandleScope shs(isolate);
4062 4063
    while (v8::platform::PumpMessageLoop(g_default_platform, isolate,
                                         behavior())) {
4064
      MicrotasksScope::PerformCheckpoint(isolate);
4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075

      if (i::FLAG_verify_predictable) {
        // In predictable mode we push all background tasks into the foreground
        // task queue of the {kProcessGlobalPredictablePlatformWorkerTaskQueue}
        // isolate. We execute the tasks after one foreground task has been
        // executed.
        while (v8::platform::PumpMessageLoop(
            g_default_platform,
            kProcessGlobalPredictablePlatformWorkerTaskQueue, behavior())) {
        }
      }
4076
    }
4077 4078
    if (g_default_platform->IdleTasksEnabled(isolate)) {
      v8::platform::RunIdleTasks(g_default_platform, isolate,
4079 4080
                                 50.0 / base::Time::kMillisecondsPerSecond);
    }
4081 4082
    bool ran_set_timeout = false;
    if (!RunSetTimeoutCallback(isolate, &ran_set_timeout)) {
4083
      return false;
4084
    }
4085

4086
    if (!ran_set_timeout) return true;
4087
  }
4088
  return true;
4089
}
4090
}  // anonymous namespace
4091

4092
bool Shell::CompleteMessageLoop(Isolate* isolate) {
4093
  auto get_waiting_behaviour = [isolate]() {
4094
    base::MutexGuard guard(isolate_status_lock_.Pointer());
4095
    DCHECK_GT(isolate_status_.count(isolate), 0);
4096 4097
    bool should_wait = (options.wait_for_background_tasks &&
                        isolate->HasPendingBackgroundTasks()) ||
4098 4099
                       isolate_status_[isolate] ||
                       isolate_running_streaming_tasks_[isolate] > 0;
4100 4101 4102
    return should_wait ? platform::MessageLoopBehavior::kWaitForWork
                       : platform::MessageLoopBehavior::kDoNotWait;
  };
4103
  return ProcessMessages(isolate, get_waiting_behaviour);
4104 4105
}

4106
bool Shell::EmptyMessageQueues(Isolate* isolate) {
4107 4108
  return ProcessMessages(
      isolate, []() { return platform::MessageLoopBehavior::kDoNotWait; });
4109 4110
}

4111 4112 4113 4114 4115 4116 4117 4118 4119
void Shell::PostForegroundTask(Isolate* isolate, std::unique_ptr<Task> task) {
  g_default_platform->GetForegroundTaskRunner(isolate)->PostTask(
      std::move(task));
}

void Shell::PostBlockingBackgroundTask(std::unique_ptr<Task> task) {
  g_default_platform->CallBlockingTaskOnWorkerThread(std::move(task));
}

4120 4121 4122 4123 4124 4125 4126 4127 4128
bool Shell::HandleUnhandledPromiseRejections(Isolate* isolate) {
  if (options.ignore_unhandled_promises) return true;
  PerIsolateData* data = PerIsolateData::Get(isolate);
  int count = data->HandleUnhandledPromiseRejections();
  Shell::unhandled_promise_rejections_.store(
      Shell::unhandled_promise_rejections_.load() + count);
  return count == 0;
}

4129 4130 4131
class Serializer : public ValueSerializer::Delegate {
 public:
  explicit Serializer(Isolate* isolate)
4132 4133 4134
      : isolate_(isolate),
        serializer_(isolate, this),
        current_memory_usage_(0) {}
4135

4136 4137 4138
  Serializer(const Serializer&) = delete;
  Serializer& operator=(const Serializer&) = delete;

4139 4140 4141 4142 4143 4144 4145
  Maybe<bool> WriteValue(Local<Context> context, Local<Value> value,
                         Local<Value> transfer) {
    bool ok;
    DCHECK(!data_);
    data_.reset(new SerializationData);
    if (!PrepareTransfer(context, transfer).To(&ok)) {
      return Nothing<bool>();
4146
    }
4147 4148 4149 4150 4151
    serializer_.WriteHeader();

    if (!serializer_.WriteValue(context, value).To(&ok)) {
      data_.reset();
      return Nothing<bool>();
4152
    }
4153 4154 4155

    if (!FinalizeTransfer().To(&ok)) {
      return Nothing<bool>();
4156 4157
    }

4158 4159 4160 4161 4162
    std::pair<uint8_t*, size_t> pair = serializer_.Release();
    data_->data_.reset(pair.first);
    data_->size_ = pair.second;
    return Just(true);
  }
4163

4164 4165
  std::unique_ptr<SerializationData> Release() { return std::move(data_); }

4166 4167 4168 4169
  void AppendBackingStoresTo(std::vector<std::shared_ptr<BackingStore>>* to) {
    to->insert(to->end(), std::make_move_iterator(backing_stores_.begin()),
               std::make_move_iterator(backing_stores_.end()));
    backing_stores_.clear();
4170 4171
  }

4172 4173 4174 4175 4176 4177 4178 4179
 protected:
  // Implements ValueSerializer::Delegate.
  void ThrowDataCloneError(Local<String> message) override {
    isolate_->ThrowException(Exception::Error(message));
  }

  Maybe<uint32_t> GetSharedArrayBufferId(
      Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) override {
4180
    DCHECK_NOT_NULL(data_);
4181 4182 4183 4184
    for (size_t index = 0; index < shared_array_buffers_.size(); ++index) {
      if (shared_array_buffers_[index] == shared_array_buffer) {
        return Just<uint32_t>(static_cast<uint32_t>(index));
      }
4185 4186
    }

4187 4188
    size_t index = shared_array_buffers_.size();
    shared_array_buffers_.emplace_back(isolate_, shared_array_buffer);
4189 4190
    data_->sab_backing_stores_.push_back(
        shared_array_buffer->GetBackingStore());
4191 4192 4193
    return Just<uint32_t>(static_cast<uint32_t>(index));
  }

4194
  Maybe<uint32_t> GetWasmModuleTransferId(
4195
      Isolate* isolate, Local<WasmModuleObject> module) override {
4196 4197 4198 4199 4200 4201 4202 4203 4204
    DCHECK_NOT_NULL(data_);
    for (size_t index = 0; index < wasm_modules_.size(); ++index) {
      if (wasm_modules_[index] == module) {
        return Just<uint32_t>(static_cast<uint32_t>(index));
      }
    }

    size_t index = wasm_modules_.size();
    wasm_modules_.emplace_back(isolate_, module);
4205
    data_->compiled_wasm_modules_.push_back(module->GetCompiledModule());
4206 4207 4208
    return Just<uint32_t>(static_cast<uint32_t>(index));
  }

4209 4210
  void* ReallocateBufferMemory(void* old_buffer, size_t size,
                               size_t* actual_size) override {
4211 4212 4213 4214 4215
    // Not accurate, because we don't take into account reallocated buffers,
    // but this is fine for testing.
    current_memory_usage_ += size;
    if (current_memory_usage_ > kMaxSerializerMemoryUsage) return nullptr;

4216
    void* result = base::Realloc(old_buffer, size);
4217 4218 4219 4220
    *actual_size = result ? size : 0;
    return result;
  }

4221
  void FreeBufferMemory(void* buffer) override { base::Free(buffer); }
4222 4223 4224 4225

 private:
  Maybe<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
    if (transfer->IsArray()) {
4226
      Local<Array> transfer_array = transfer.As<Array>();
4227 4228 4229 4230 4231 4232
      uint32_t length = transfer_array->Length();
      for (uint32_t i = 0; i < length; ++i) {
        Local<Value> element;
        if (transfer_array->Get(context, i).ToLocal(&element)) {
          if (!element->IsArrayBuffer()) {
            Throw(isolate_, "Transfer array elements must be an ArrayBuffer");
4233
            return Nothing<bool>();
4234 4235
          }

4236
          Local<ArrayBuffer> array_buffer = element.As<ArrayBuffer>();
4237 4238 4239 4240 4241 4242 4243 4244

          if (std::find(array_buffers_.begin(), array_buffers_.end(),
                        array_buffer) != array_buffers_.end()) {
            Throw(isolate_,
                  "ArrayBuffer occurs in the transfer array more than once");
            return Nothing<bool>();
          }

4245 4246 4247 4248 4249 4250 4251 4252 4253 4254
          serializer_.TransferArrayBuffer(
              static_cast<uint32_t>(array_buffers_.size()), array_buffer);
          array_buffers_.emplace_back(isolate_, array_buffer);
        } else {
          return Nothing<bool>();
        }
      }
      return Just(true);
    } else if (transfer->IsUndefined()) {
      return Just(true);
4255
    } else {
4256 4257
      Throw(isolate_, "Transfer list must be an Array or undefined");
      return Nothing<bool>();
4258
    }
4259 4260 4261 4262 4263 4264
  }

  Maybe<bool> FinalizeTransfer() {
    for (const auto& global_array_buffer : array_buffers_) {
      Local<ArrayBuffer> array_buffer =
          Local<ArrayBuffer>::New(isolate_, global_array_buffer);
4265
      if (!array_buffer->IsDetachable()) {
4266 4267 4268 4269
        Throw(isolate_, "ArrayBuffer could not be transferred");
        return Nothing<bool>();
      }

4270 4271
      auto backing_store = array_buffer->GetBackingStore();
      data_->backing_stores_.push_back(std::move(backing_store));
4272
      array_buffer->Detach();
4273 4274
    }

4275
    return Just(true);
4276 4277
  }

4278 4279 4280 4281 4282
  Isolate* isolate_;
  ValueSerializer serializer_;
  std::unique_ptr<SerializationData> data_;
  std::vector<Global<ArrayBuffer>> array_buffers_;
  std::vector<Global<SharedArrayBuffer>> shared_array_buffers_;
4283
  std::vector<Global<WasmModuleObject>> wasm_modules_;
4284
  std::vector<std::shared_ptr<v8::BackingStore>> backing_stores_;
4285
  size_t current_memory_usage_;
4286
};
4287

4288 4289 4290 4291 4292 4293 4294 4295
class Deserializer : public ValueDeserializer::Delegate {
 public:
  Deserializer(Isolate* isolate, std::unique_ptr<SerializationData> data)
      : isolate_(isolate),
        deserializer_(isolate, data->data(), data->size(), this),
        data_(std::move(data)) {
    deserializer_.SetSupportsLegacyWireFormat(true);
  }
4296

4297 4298 4299
  Deserializer(const Deserializer&) = delete;
  Deserializer& operator=(const Deserializer&) = delete;

4300 4301 4302 4303
  MaybeLocal<Value> ReadValue(Local<Context> context) {
    bool read_header;
    if (!deserializer_.ReadHeader(context).To(&read_header)) {
      return MaybeLocal<Value>();
4304
    }
4305 4306

    uint32_t index = 0;
4307
    for (const auto& backing_store : data_->backing_stores()) {
4308 4309
      Local<ArrayBuffer> array_buffer =
          ArrayBuffer::New(isolate_, std::move(backing_store));
4310
      deserializer_.TransferArrayBuffer(index++, array_buffer);
4311
    }
4312

4313
    return deserializer_.ReadValue(context);
4314 4315
  }

4316 4317 4318
  MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
      Isolate* isolate, uint32_t clone_id) override {
    DCHECK_NOT_NULL(data_);
4319
    if (clone_id < data_->sab_backing_stores().size()) {
4320 4321
      return SharedArrayBuffer::New(
          isolate_, std::move(data_->sab_backing_stores().at(clone_id)));
4322 4323 4324 4325
    }
    return MaybeLocal<SharedArrayBuffer>();
  }

4326
  MaybeLocal<WasmModuleObject> GetWasmModuleFromId(
4327 4328
      Isolate* isolate, uint32_t transfer_id) override {
    DCHECK_NOT_NULL(data_);
4329 4330 4331
    if (transfer_id >= data_->compiled_wasm_modules().size()) return {};
    return WasmModuleObject::FromCompiledModule(
        isolate_, data_->compiled_wasm_modules().at(transfer_id));
4332 4333
  }

4334 4335 4336 4337 4338 4339
 private:
  Isolate* isolate_;
  ValueDeserializer deserializer_;
  std::unique_ptr<SerializationData> data_;
};

4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367
class D8Testing {
 public:
  /**
   * Get the number of runs of a given test that is required to get the full
   * stress coverage.
   */
  static int GetStressRuns() {
    if (internal::FLAG_stress_runs != 0) return internal::FLAG_stress_runs;
#ifdef DEBUG
    // In debug mode the code runs much slower so stressing will only make two
    // runs.
    return 2;
#else
    return 5;
#endif
  }

  /**
   * Indicate the number of the run which is about to start. The value of run
   * should be between 0 and one less than the result from GetStressRuns()
   */
  static void PrepareStressRun(int run) {
    static const char* kLazyOptimizations =
        "--prepare-always-opt "
        "--max-inlined-bytecode-size=999999 "
        "--max-inlined-bytecode-size-cumulative=999999 "
        "--noalways-opt";

4368
    if (run == 0) {
4369
      V8::SetFlagsFromString(kLazyOptimizations);
4370 4371
    } else if (run == GetStressRuns() - 1) {
      i::FLAG_always_opt = true;
4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384
    }
  }

  /**
   * Force deoptimization of all functions.
   */
  static void DeoptimizeAll(Isolate* isolate) {
    i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
    i::HandleScope scope(i_isolate);
    i::Deoptimizer::DeoptimizeAll(i_isolate);
  }
};

4385 4386 4387 4388 4389
std::unique_ptr<SerializationData> Shell::SerializeValue(
    Isolate* isolate, Local<Value> value, Local<Value> transfer) {
  bool ok;
  Local<Context> context = isolate->GetCurrentContext();
  Serializer serializer(isolate);
4390
  std::unique_ptr<SerializationData> data;
4391
  if (serializer.WriteValue(context, value, transfer).To(&ok)) {
4392
    data = serializer.Release();
4393
  }
4394
  return data;
4395
}
4396

4397 4398 4399 4400 4401 4402
MaybeLocal<Value> Shell::DeserializeValue(
    Isolate* isolate, std::unique_ptr<SerializationData> data) {
  Local<Value> value;
  Local<Context> context = isolate->GetCurrentContext();
  Deserializer deserializer(isolate, std::move(data));
  return deserializer.ReadValue(context);
4403 4404
}

4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420
void Shell::AddRunningWorker(std::shared_ptr<Worker> worker) {
  workers_mutex_.Pointer()->AssertHeld();  // caller should hold the mutex.
  running_workers_.insert(worker);
}

void Shell::RemoveRunningWorker(const std::shared_ptr<Worker>& worker) {
  base::MutexGuard lock_guard(workers_mutex_.Pointer());
  auto it = running_workers_.find(worker);
  if (it != running_workers_.end()) running_workers_.erase(it);
}

void Shell::WaitForRunningWorkers() {
  // Make a copy of running_workers_, because we don't want to call
  // Worker::Terminate while holding the workers_mutex_ lock. Otherwise, if a
  // worker is about to create a new Worker, it would deadlock.
  std::unordered_set<std::shared_ptr<Worker>> workers_copy;
4421
  {
4422
    base::MutexGuard lock_guard(workers_mutex_.Pointer());
4423
    allow_new_workers_ = false;
4424
    workers_copy.swap(running_workers_);
4425 4426
  }

4427
  for (auto& worker : workers_copy) {
4428
    worker->TerminateAndWaitForThread();
4429
  }
4430 4431

  // Now that all workers are terminated, we can re-enable Worker creation.
4432
  base::MutexGuard lock_guard(workers_mutex_.Pointer());
4433
  DCHECK(running_workers_.empty());
binji's avatar
binji committed
4434
  allow_new_workers_ = true;
4435 4436
}

4437
int Shell::Main(int argc, char* argv[]) {
4438
  v8::base::EnsureConsoleOutput();
4439
  if (!SetOptions(argc, argv)) return 1;
4440

4441
  v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);
4442

4443 4444 4445 4446 4447 4448 4449 4450
#ifdef V8_INTL_SUPPORT
  if (options.icu_locale != nullptr) {
    icu::Locale locale(options.icu_locale);
    UErrorCode error_code = U_ZERO_ERROR;
    icu::Locale::setDefault(locale, error_code);
  }
#endif  // V8_INTL_SUPPORT

4451 4452 4453 4454 4455
  v8::platform::InProcessStackDumping in_process_stack_dumping =
      options.disable_in_process_stack_traces
          ? v8::platform::InProcessStackDumping::kDisabled
          : v8::platform::InProcessStackDumping::kEnabled;

4456
  std::ofstream trace_file;
4457
  std::unique_ptr<platform::tracing::TracingController> tracing;
4458
  if (options.trace_enabled && !i::FLAG_verify_predictable) {
4459
    tracing = std::make_unique<platform::tracing::TracingController>();
4460 4461 4462 4463 4464 4465 4466 4467
    const char* trace_path =
        options.trace_path ? options.trace_path : "v8_trace.json";
    trace_file.open(trace_path);
    if (!trace_file.good()) {
      printf("Cannot open trace file '%s' for writing: %s.\n", trace_path,
             strerror(errno));
      return 1;
    }
4468 4469

#ifdef V8_USE_PERFETTO
4470 4471
    // Set up the in-process backend that the tracing controller will connect
    // to.
4472 4473 4474 4475
    perfetto::TracingInitArgs init_args;
    init_args.backends = perfetto::BackendType::kInProcessBackend;
    perfetto::Tracing::Initialize(init_args);

4476 4477
    tracing->InitializeForPerfetto(&trace_file);
#else
4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494
    platform::tracing::TraceBuffer* trace_buffer = nullptr;
#if defined(V8_ENABLE_SYSTEM_INSTRUMENTATION)
    if (options.enable_system_instrumentation) {
      trace_buffer =
          platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
              platform::tracing::TraceBuffer::kRingBufferChunks,
              platform::tracing::TraceWriter::
                  CreateSystemInstrumentationTraceWriter());
    }
#endif  // V8_ENABLE_SYSTEM_INSTRUMENTATION
    if (!trace_buffer) {
      trace_buffer =
          platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
              platform::tracing::TraceBuffer::kRingBufferChunks,
              platform::tracing::TraceWriter::CreateJSONTraceWriter(
                  trace_file));
    }
4495
    tracing->Initialize(trace_buffer);
4496
#endif  // V8_USE_PERFETTO
4497 4498
  }

4499
  platform::tracing::TracingController* tracing_controller = tracing.get();
4500
  g_platform = v8::platform::NewDefaultPlatform(
4501 4502
      options.thread_pool_size, v8::platform::IdleTaskSupport::kEnabled,
      in_process_stack_dumping, std::move(tracing));
4503
  g_default_platform = g_platform.get();
4504
  if (i::FLAG_verify_predictable) {
4505 4506
    g_platform = MakePredictablePlatform(std::move(g_platform));
  }
4507
  if (options.stress_delay_tasks) {
4508 4509 4510 4511 4512
    int64_t random_seed = i::FLAG_fuzzer_random_seed;
    if (!random_seed) random_seed = i::FLAG_random_seed;
    // If random_seed is still 0 here, the {DelayedTasksPlatform} will choose a
    // random seed.
    g_platform = MakeDelayedTasksPlatform(std::move(g_platform), random_seed);
4513 4514
  }

4515
  if (i::FLAG_trace_turbo_cfg_file == nullptr) {
4516
    V8::SetFlagsFromString("--trace-turbo-cfg-file=turbo.cfg");
4517 4518
  }
  if (i::FLAG_redirect_code_traces_to == nullptr) {
4519
    V8::SetFlagsFromString("--redirect-code-traces-to=code.asm");
4520
  }
4521
  v8::V8::InitializePlatform(g_platform.get());
4522
  v8::V8::Initialize();
4523 4524
  if (options.snapshot_blob) {
    v8::V8::InitializeExternalStartupDataFromFile(options.snapshot_blob);
vogelheim's avatar
vogelheim committed
4525 4526 4527
  } else {
    v8::V8::InitializeExternalStartupData(argv[0]);
  }
4528 4529
  int result = 0;
  Isolate::CreateParams create_params;
4530
  ShellArrayBufferAllocator shell_array_buffer_allocator;
4531
  MockArrayBufferAllocator mock_arraybuffer_allocator;
4532 4533 4534 4535 4536 4537
  const size_t memory_limit =
      options.mock_arraybuffer_allocator_limit * options.num_isolates;
  MockArrayBufferAllocatiorWithLimit mock_arraybuffer_allocator_with_limit(
      memory_limit >= options.mock_arraybuffer_allocator_limit
          ? memory_limit
          : std::numeric_limits<size_t>::max());
4538 4539 4540
#if V8_OS_LINUX
  MultiMappedAllocator multi_mapped_mock_allocator;
#endif  // V8_OS_LINUX
4541
  if (options.mock_arraybuffer_allocator) {
4542 4543 4544 4545 4546
    if (memory_limit) {
      Shell::array_buffer_allocator = &mock_arraybuffer_allocator_with_limit;
    } else {
      Shell::array_buffer_allocator = &mock_arraybuffer_allocator;
    }
4547 4548 4549 4550
#if V8_OS_LINUX
  } else if (options.multi_mapped_mock_allocator) {
    Shell::array_buffer_allocator = &multi_mapped_mock_allocator;
#endif  // V8_OS_LINUX
4551
  } else {
4552
    Shell::array_buffer_allocator = &shell_array_buffer_allocator;
4553
  }
4554
  create_params.array_buffer_allocator = Shell::array_buffer_allocator;
4555
#ifdef ENABLE_VTUNE_JIT_INTERFACE
4556
  create_params.code_event_handler = vTune::GetVtuneCodeEventHandler();
4557
#endif
4558 4559
  create_params.constraints.ConfigureDefaults(
      base::SysInfo::AmountOfPhysicalMemory(),
4560
      base::SysInfo::AmountOfVirtualMemory());
4561 4562

  Shell::counter_map_ = new CounterMap();
4563 4564
  if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp ||
      i::TracingFlags::is_gc_stats_enabled()) {
4565 4566 4567 4568
    create_params.counter_lookup_callback = LookupCounter;
    create_params.create_histogram_callback = CreateHistogram;
    create_params.add_histogram_sample_callback = AddHistogramSample;
  }
4569

4570
  if (V8_TRAP_HANDLER_SUPPORTED && i::FLAG_wasm_trap_handler) {
4571 4572
    constexpr bool use_default_trap_handler = true;
    if (!v8::V8::EnableWebAssemblyTrapHandler(use_default_trap_handler)) {
4573
      FATAL("Could not register trap handler");
eholk's avatar
eholk committed
4574 4575 4576
    }
  }

4577
  Isolate* isolate = Isolate::New(create_params);
4578

4579
  {
4580
    D8Console console(isolate);
4581
    Isolate::Scope scope(isolate);
4582
    Initialize(isolate, &console);
4583
    PerIsolateData data(isolate);
4584

4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610
    // Fuzzilli REPRL = read-eval-print-loop
    do {
#ifdef V8_FUZZILLI
      if (fuzzilli_reprl) {
        unsigned action = 0;
        ssize_t nread = read(REPRL_CRFD, &action, 4);
        if (nread != 4 || action != 'cexe') {
          fprintf(stderr, "Unknown action: %u\n", action);
          _exit(-1);
        }
      }
#endif  // V8_FUZZILLI

      result = 0;

      if (options.trace_enabled) {
        platform::tracing::TraceConfig* trace_config;
        if (options.trace_config) {
          int size = 0;
          char* trace_config_json_str = ReadChars(options.trace_config, &size);
          trace_config = tracing::CreateTraceConfigFromJSON(
              isolate, trace_config_json_str);
          delete[] trace_config_json_str;
        } else {
          trace_config =
              platform::tracing::TraceConfig::CreateDefaultTraceConfig();
4611 4612 4613
          if (options.enable_system_instrumentation) {
            trace_config->AddIncludedCategory("disabled-by-default-v8.compile");
          }
4614 4615
        }
        tracing_controller->StartTracing(trace_config);
4616
      }
4617

4618 4619 4620 4621 4622
      CpuProfiler* cpu_profiler;
      if (options.cpu_profiler) {
        cpu_profiler = CpuProfiler::New(isolate);
        CpuProfilingOptions profile_options;
        cpu_profiler->StartProfiling(String::Empty(isolate), profile_options);
4623
      }
4624 4625 4626 4627 4628

      if (options.stress_opt) {
        options.stress_runs = D8Testing::GetStressRuns();
        for (int i = 0; i < options.stress_runs && result == 0; i++) {
          printf("============ Stress %d/%d ============\n", i + 1,
4629
                 options.stress_runs.get());
4630 4631 4632 4633 4634 4635 4636 4637 4638 4639
          D8Testing::PrepareStressRun(i);
          bool last_run = i == options.stress_runs - 1;
          result = RunMain(isolate, last_run);
        }
        printf("======== Full Deoptimization =======\n");
        D8Testing::DeoptimizeAll(isolate);
      } else if (i::FLAG_stress_runs > 0) {
        options.stress_runs = i::FLAG_stress_runs;
        for (int i = 0; i < options.stress_runs && result == 0; i++) {
          printf("============ Run %d/%d ============\n", i + 1,
4640
                 options.stress_runs.get());
4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666
          bool last_run = i == options.stress_runs - 1;
          result = RunMain(isolate, last_run);
        }
      } else if (options.code_cache_options !=
                 ShellOptions::CodeCacheOptions::kNoProduceCache) {
        printf("============ Run: Produce code cache ============\n");
        // First run to produce the cache
        Isolate::CreateParams create_params;
        create_params.array_buffer_allocator = Shell::array_buffer_allocator;
        i::FLAG_hash_seed ^= 1337;  // Use a different hash seed.
        Isolate* isolate2 = Isolate::New(create_params);
        i::FLAG_hash_seed ^= 1337;  // Restore old hash seed.
        {
          D8Console console(isolate2);
          Initialize(isolate2, &console);
          PerIsolateData data(isolate2);
          Isolate::Scope isolate_scope(isolate2);

          result = RunMain(isolate2, false);
        }
        isolate2->Dispose();

        // Change the options to consume cache
        DCHECK(options.compile_options == v8::ScriptCompiler::kEagerCompile ||
               options.compile_options ==
                   v8::ScriptCompiler::kNoCompileOptions);
4667 4668 4669 4670
        options.compile_options.Overwrite(
            v8::ScriptCompiler::kConsumeCodeCache);
        options.code_cache_options.Overwrite(
            ShellOptions::CodeCacheOptions::kNoProduceCache);
4671 4672 4673 4674

        printf("============ Run: Consume code cache ============\n");
        // Second run to consume the cache in current isolate
        result = RunMain(isolate, true);
4675 4676
        options.compile_options.Overwrite(
            v8::ScriptCompiler::kNoCompileOptions);
4677 4678
      } else {
        bool last_run = true;
4679
        result = RunMain(isolate, last_run);
4680
      }
4681

4682 4683 4684 4685
      // Run interactive shell if explicitly requested or if no script has been
      // executed, but never on --test
      if (use_interactive_shell()) {
        RunShell(isolate);
4686
      }
4687

4688 4689 4690 4691
      if (i::FLAG_trace_ignition_dispatches &&
          i::FLAG_trace_ignition_dispatches_output_file != nullptr) {
        WriteIgnitionDispatchCountersFile(isolate);
      }
4692

4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703
      if (options.cpu_profiler) {
        CpuProfile* profile =
            cpu_profiler->StopProfiling(String::Empty(isolate));
        if (options.cpu_profiler_print) {
          const internal::ProfileNode* root =
              reinterpret_cast<const internal::ProfileNode*>(
                  profile->GetTopDownRoot());
          root->Print(0);
        }
        profile->Delete();
        cpu_profiler->Dispose();
4704 4705
      }

4706 4707 4708 4709 4710 4711 4712 4713 4714
      // Shut down contexts and collect garbage.
      cached_code_map_.clear();
      evaluation_context_.Reset();
      stringify_function_.Reset();
      CollectGarbage(isolate);
#ifdef V8_FUZZILLI
      // Send result to parent (fuzzilli) and reset edge guards.
      if (fuzzilli_reprl) {
        int status = result << 8;
4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732
        std::vector<bool> bitmap;
        if (options.fuzzilli_enable_builtins_coverage) {
          bitmap = i::BasicBlockProfiler::Get()->GetCoverageBitmap(
              reinterpret_cast<i::Isolate*>(isolate));
          cov_update_builtins_basic_block_coverage(bitmap);
        }
        if (options.fuzzilli_coverage_statistics) {
          int tot = 0;
          for (bool b : bitmap) {
            if (b) tot++;
          }
          static int iteration_counter = 0;
          std::ofstream covlog("covlog.txt", std::ios::app);
          covlog << iteration_counter << "\t" << tot << "\t"
                 << sanitizer_cov_count_discovered_edges() << "\t"
                 << bitmap.size() << std::endl;
          iteration_counter++;
        }
4733 4734 4735 4736
        // In REPRL mode, stdout and stderr can be regular files, so they need
        // to be flushed after every execution
        fflush(stdout);
        fflush(stderr);
4737
        CHECK_EQ(write(REPRL_CWFD, &status, 4), 4);
4738 4739 4740 4741 4742
        sanitizer_cov_reset_edgeguards();
        if (options.fuzzilli_enable_builtins_coverage) {
          i::BasicBlockProfiler::Get()->ResetCounts(
              reinterpret_cast<i::Isolate*>(isolate));
        }
4743 4744 4745
      }
#endif  // V8_FUZZILLI
    } while (fuzzilli_reprl);
4746
  }
4747
  OnExit(isolate);
4748

4749
  V8::Dispose();
4750
  V8::ShutdownPlatform();
4751

4752 4753
  // Delete the platform explicitly here to write the tracing output to the
  // tracing file.
4754 4755 4756
  if (options.trace_enabled) {
    tracing_controller->StopTracing();
  }
4757
  g_platform.reset();
4758 4759 4760
  return result;
}

4761
}  // namespace v8
4762

4763
#ifndef GOOGLE3
4764
int main(int argc, char* argv[]) { return v8::Shell::Main(argc, argv); }
4765
#endif
4766 4767 4768

#undef CHECK
#undef DCHECK
4769
#undef TRACE_BS