d8.cc 119 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 <unordered_map>
13
#include <utility>
binji's avatar
binji committed
14
#include <vector>
15

16
#ifdef ENABLE_VTUNE_JIT_INTERFACE
17
#include "src/third_party/vtune/v8-vtune.h"
18 19
#endif

20
#include "src/d8-console.h"
21
#include "src/d8.h"
22
#include "src/ostreams.h"
23

24
#include "include/libplatform/libplatform.h"
25
#include "include/libplatform/v8-tracing.h"
26
#include "include/v8-inspector.h"
27
#include "src/api.h"
28 29 30
#include "src/base/cpu.h"
#include "src/base/logging.h"
#include "src/base/platform/platform.h"
31
#include "src/base/platform/time.h"
32
#include "src/base/sys-info.h"
33
#include "src/basic-block-profiler.h"
34
#include "src/debug/debug-interface.h"
35
#include "src/interpreter/interpreter.h"
36
#include "src/msan.h"
37
#include "src/objects-inl.h"
38
#include "src/objects.h"
39
#include "src/snapshot/natives.h"
eholk's avatar
eholk committed
40
#include "src/trap-handler/trap-handler.h"
41
#include "src/utils.h"
42
#include "src/v8.h"
43
#include "src/wasm/wasm-engine.h"
44

45
#if !defined(_WIN32) && !defined(_WIN64)
46
#include <unistd.h>  // NOLINT
47 48 49 50 51 52
#else
#include <windows.h>  // NOLINT
#if defined(_MSC_VER)
#include <crtdbg.h>  // NOLINT
#endif               // defined(_MSC_VER)
#endif               // !defined(_WIN32) && !defined(_WIN64)
53

54 55
#ifndef DCHECK
#define DCHECK(condition) assert(condition)
Yang Guo's avatar
Yang Guo committed
56 57 58 59
#endif

#ifndef CHECK
#define CHECK(condition) assert(condition)
60
#endif
61

62
namespace v8 {
63

64
namespace {
65

66
const int kMB = 1024 * 1024;
67

68 69 70
const int kMaxWorkers = 50;
const int kMaxSerializerMemoryUsage =
    1 * kMB;  // Arbitrary maximum for testing.
71

72 73
// Base class for shell ArrayBuffer allocators. It forwards all opertions to
// the default v8 allocator.
74 75 76
class ArrayBufferAllocatorBase : public v8::ArrayBuffer::Allocator {
 public:
  void* Allocate(size_t length) override {
77
    return allocator_->Allocate(length);
78
  }
79

80
  void* AllocateUninitialized(size_t length) override {
81
    return allocator_->AllocateUninitialized(length);
82
  }
83

84
  void Free(void* data, size_t length) override {
85
    allocator_->Free(data, length);
86
  }
87 88 89 90 91 92

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

93
// ArrayBuffer allocator that can use virtual memory to improve performance.
94 95
class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase {
 public:
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
  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);
    }
  }

114
 private:
115 116 117 118 119 120 121 122
  static constexpr size_t kVMThreshold = 65536;
  static constexpr size_t kTwoGB = 2u * 1024u * 1024u * 1024u;

  void* AllocateVM(size_t length) {
    DCHECK_LE(kVMThreshold, length);
    // TODO(titzer): allocations should fail if >= 2gb because array buffers
    // store their lengths as a SMI internally.
    if (length >= kTwoGB) return nullptr;
123 124

    size_t page_size = i::AllocatePageSize();
125
    size_t allocated = RoundUp(length, page_size);
126 127
    // Rounding up could go over the limit.
    if (allocated >= kTwoGB) return nullptr;
128
    return i::AllocatePages(nullptr, allocated, page_size,
129
                            PageAllocator::kReadWrite);
130 131 132
  }

  void FreeVM(void* data, size_t length) {
133
    size_t page_size = i::AllocatePageSize();
134
    size_t allocated = RoundUp(length, page_size);
135
    CHECK(i::FreePages(data, allocated));
136
  }
137 138
};

139
// ArrayBuffer allocator that never allocates over 10MB.
140
class MockArrayBufferAllocator : public ArrayBufferAllocatorBase {
141 142 143 144 145 146 147 148 149 150 151 152
  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));
  }

153
 private:
154
  size_t Adjust(size_t length) {
155
    const size_t kAllocationLimit = 10 * kMB;
156
    return length > kAllocationLimit ? i::AllocatePageSize() : length;
157
  }
158 159
};

160 161 162
// Predictable v8::Platform implementation. Background tasks and idle tasks are
// disallowed, and the time reported by {MonotonicallyIncreasingTime} is
// deterministic.
163 164
class PredictablePlatform : public Platform {
 public:
165 166 167 168
  explicit PredictablePlatform(std::unique_ptr<Platform> platform)
      : platform_(std::move(platform)) {
    DCHECK_NOT_NULL(platform_);
  }
169

170 171 172 173 174 175 176 177 178 179 180 181
  PageAllocator* GetPageAllocator() override {
    return platform_->GetPageAllocator();
  }

  void OnCriticalMemoryPressure() override {
    platform_->OnCriticalMemoryPressure();
  }

  bool OnCriticalMemoryPressure(size_t length) override {
    return platform_->OnCriticalMemoryPressure(length);
  }

182 183 184 185 186
  std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
      v8::Isolate* isolate) override {
    return platform_->GetForegroundTaskRunner(isolate);
  }

187
  std::shared_ptr<TaskRunner> GetWorkerThreadsTaskRunner(
188 189 190 191 192 193
      v8::Isolate* isolate) override {
    // Return the foreground task runner here, so that all tasks get executed
    // sequentially in a predictable order.
    return platform_->GetForegroundTaskRunner(isolate);
  }

194
  void CallOnWorkerThread(Task* task) override {
195 196
    // It's not defined when background tasks are being executed, so we can just
    // execute them right away.
197 198 199 200 201
    task->Run();
    delete task;
  }

  void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
202
    platform_->CallOnForegroundThread(isolate, task);
203 204 205 206
  }

  void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
                                     double delay_in_seconds) override {
207
    platform_->CallDelayedOnForegroundThread(isolate, task, delay_in_seconds);
208 209
  }

210
  void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {
211 212 213
    UNREACHABLE();
  }

214
  bool IdleTasksEnabled(Isolate* isolate) override { return false; }
215 216 217 218 219

  double MonotonicallyIncreasingTime() override {
    return synthetic_time_in_sec_ += 0.00001;
  }

220 221 222 223
  double CurrentClockTimeMillis() override {
    return MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond;
  }

224 225 226 227
  v8::TracingController* GetTracingController() override {
    return platform_->GetTracingController();
  }

228
  Platform* platform() const { return platform_.get(); }
229

230 231
 private:
  double synthetic_time_in_sec_ = 0.0;
232
  std::unique_ptr<Platform> platform_;
233 234 235 236

  DISALLOW_COPY_AND_ASSIGN(PredictablePlatform);
};

237 238 239 240 241 242 243 244
std::unique_ptr<v8::Platform> g_platform;

v8::Platform* GetDefaultPlatform() {
  return i::FLAG_verify_predictable
             ? static_cast<PredictablePlatform*>(g_platform.get())->platform()
             : g_platform.get();
}

245 246 247 248 249 250 251 252 253
static Local<Value> Throw(Isolate* isolate, const char* message) {
  return isolate->ThrowException(
      String::NewFromUtf8(isolate, message, NewStringType::kNormal)
          .ToLocalChecked());
}

Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) {
  if (object->InternalFieldCount() != 1) {
    Throw(isolate, "this is not a Worker");
254
    return nullptr;
255
  }
256

257 258
  Worker* worker =
      static_cast<Worker*>(object->GetAlignedPointerFromInternalField(0));
259
  if (worker == nullptr) {
260
    Throw(isolate, "Worker is defunct because main thread is terminating");
261
    return nullptr;
262
  }
263

264
  return worker;
265 266
}

267 268 269 270 271
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.
272
  return base::Thread::Options(name, 2 * kMB);
273
}
274

275 276
}  // namespace

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
namespace tracing {

namespace {

// String options that can be used to initialize TraceOptions.
const char kRecordUntilFull[] = "record-until-full";
const char kRecordContinuously[] = "record-continuously";
const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";

const char kRecordModeParam[] = "record_mode";
const char kEnableSystraceParam[] = "enable_systrace";
const char kEnableArgumentFilterParam[] = "enable_argument_filter";
const char kIncludedCategoriesParam[] = "included_categories";

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 =
        String::NewFromUtf8(isolate, json_str, NewStringType::kNormal)
            .ToLocalChecked();
    Local<Value> result = JSON::Parse(context, source).ToLocalChecked();
    Local<v8::Object> trace_config_object = Local<v8::Object>::Cast(result);

    trace_config->SetTraceRecordMode(
        GetTraceRecordMode(isolate, context, trace_config_object));
    if (GetBoolean(isolate, context, trace_config_object,
                   kEnableSystraceParam)) {
      trace_config->EnableSystrace();
    }
    if (GetBoolean(isolate, context, trace_config_object,
                   kEnableArgumentFilterParam)) {
      trace_config->EnableArgumentFilter();
    }
317 318
    UpdateIncludedCategoriesList(isolate, context, trace_config_object,
                                 trace_config);
319 320 321 322 323 324 325 326 327 328 329 330 331
  }

 private:
  static bool GetBoolean(v8::Isolate* isolate, Local<Context> context,
                         Local<v8::Object> object, const char* property) {
    Local<Value> value = GetValue(isolate, context, object, property);
    if (value->IsNumber()) {
      Local<Boolean> v8_boolean = value->ToBoolean(context).ToLocalChecked();
      return v8_boolean->Value();
    }
    return false;
  }

332
  static int UpdateIncludedCategoriesList(
333
      v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object,
334 335 336
      platform::tracing::TraceConfig* trace_config) {
    Local<Value> value =
        GetValue(isolate, context, object, kIncludedCategoriesParam);
337 338 339 340 341 342 343
    if (value->IsArray()) {
      Local<Array> v8_array = Local<Array>::Cast(value);
      for (int i = 0, length = v8_array->Length(); i < length; ++i) {
        Local<Value> v = v8_array->Get(context, i)
                             .ToLocalChecked()
                             ->ToString(context)
                             .ToLocalChecked();
344
        String::Utf8Value str(isolate, v->ToString(context).ToLocalChecked());
345
        trace_config->AddIncludedCategory(*str);
346 347 348 349 350 351 352 353 354 355 356
      }
      return v8_array->Length();
    }
    return 0;
  }

  static platform::tracing::TraceRecordMode GetTraceRecordMode(
      v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object) {
    Local<Value> value = GetValue(isolate, context, object, kRecordModeParam);
    if (value->IsString()) {
      Local<String> v8_string = value->ToString(context).ToLocalChecked();
357
      String::Utf8Value str(isolate, v8_string);
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
      if (strcmp(kRecordUntilFull, *str) == 0) {
        return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
      } else if (strcmp(kRecordContinuously, *str) == 0) {
        return platform::tracing::TraceRecordMode::RECORD_CONTINUOUSLY;
      } else if (strcmp(kRecordAsMuchAsPossible, *str) == 0) {
        return platform::tracing::TraceRecordMode::RECORD_AS_MUCH_AS_POSSIBLE;
      }
    }
    return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
  }

  static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
                               Local<v8::Object> object, const char* property) {
    Local<String> v8_str =
        String::NewFromUtf8(isolate, property, NewStringType::kNormal)
            .ToLocalChecked();
    return object->Get(context, v8_str).ToLocalChecked();
  }
};

}  // 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
389

390
class PerIsolateData {
391
 public:
392 393
  explicit PerIsolateData(Isolate* isolate)
      : isolate_(isolate), realms_(nullptr) {
394
    isolate->SetData(0, this);
395 396
  }

397
  ~PerIsolateData() {
398
    isolate_->SetData(0, nullptr);  // Not really needed, just to be sure...
399 400
  }

401
  inline static PerIsolateData* Get(Isolate* isolate) {
402
    return reinterpret_cast<PerIsolateData*>(isolate->GetData(0));
403
  }
404 405 406 407 408 409 410 411

  class RealmScope {
   public:
    explicit RealmScope(PerIsolateData* data);
    ~RealmScope();
   private:
    PerIsolateData* data_;
  };
412

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
  inline void SetTimeout(Local<Function> callback, Local<Context> context) {
    set_timeout_callbacks_.emplace(isolate_, callback);
    set_timeout_contexts_.emplace(isolate_, context);
  }

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

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

432
 private:
433 434
  friend class Shell;
  friend class RealmScope;
435
  Isolate* isolate_;
436 437 438
  int realm_count_;
  int realm_current_;
  int realm_switch_;
439 440
  Global<Context>* realms_;
  Global<Value> realm_shared_;
441 442
  std::queue<Global<Function>> set_timeout_callbacks_;
  std::queue<Global<Context>> set_timeout_contexts_;
443

444 445
  int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args,
                        int arg_offset);
446
  int RealmFind(Local<Context> context);
447 448
};

449 450 451 452 453 454 455 456 457 458 459 460 461 462
class ExternalOwningOneByteStringResource
    : public String::ExternalOneByteStringResource {
 public:
  ExternalOwningOneByteStringResource() : length_(0) {}
  ExternalOwningOneByteStringResource(std::unique_ptr<const char[]> data,
                                      size_t length)
      : data_(std::move(data)), length_(length) {}
  const char* data() const override { return data_.get(); }
  size_t length() const override { return length_; }

 private:
  std::unique_ptr<const char[]> data_;
  size_t length_;
};
463

464
CounterMap* Shell::counter_map_;
465
base::OS::MemoryMappedFile* Shell::counters_file_ = nullptr;
466 467
CounterCollection Shell::local_counters_;
CounterCollection* Shell::counters_ = &local_counters_;
468
base::LazyMutex Shell::context_mutex_;
469 470
const base::TimeTicks Shell::kInitialTicks =
    base::TimeTicks::HighResolutionNow();
yangguo's avatar
yangguo committed
471
Global<Function> Shell::stringify_function_;
472
base::LazyMutex Shell::workers_mutex_;
473
bool Shell::allow_new_workers_ = true;
474
std::vector<Worker*> Shell::workers_;
475
std::vector<ExternalizedContents> Shell::externalized_contents_;
476 477
base::LazyMutex Shell::isolate_status_lock_;
std::map<v8::Isolate*, bool> Shell::isolate_status_;
478
base::LazyMutex Shell::cached_code_mutex_;
479 480
std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
    Shell::cached_code_map_;
481

482
Global<Context> Shell::evaluation_context_;
483
ArrayBuffer::Allocator* Shell::array_buffer_allocator;
484
ShellOptions Shell::options;
485
base::OnceType Shell::quit_once_ = V8_ONCE_INIT;
486

487 488 489
bool CounterMap::Match(void* key1, void* key2) {
  const char* name1 = reinterpret_cast<const char*>(key1);
  const char* name2 = reinterpret_cast<const char*>(key2);
490
  return strcmp(name1, name2) == 0;
491 492
}

493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 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
// Dummy external source stream which returns the whole source in one go.
class DummySourceStream : public v8::ScriptCompiler::ExternalSourceStream {
 public:
  explicit DummySourceStream(Local<String> source) : done_(false) {
    source_length_ = source->Utf8Length();
    source_buffer_.reset(new uint8_t[source_length_]);
    source->WriteUtf8(reinterpret_cast<char*>(source_buffer_.get()),
                      source_length_);
  }

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

    return source_length_;
  }

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

class BackgroundCompileThread : public base::Thread {
 public:
  BackgroundCompileThread(Isolate* isolate, Local<String> source)
      : base::Thread(GetThreadOptions("BackgroundCompileThread")),
        source_(source),
        streamed_source_(new DummySourceStream(source),
                         v8::ScriptCompiler::StreamedSource::UTF8),
        task_(v8::ScriptCompiler::StartStreamingScript(isolate,
                                                       &streamed_source_)) {}

  void Run() override { task_->Run(); }

  v8::ScriptCompiler::StreamedSource* streamed_source() {
    return &streamed_source_;
  }

 private:
  Local<String> source_;
  v8::ScriptCompiler::StreamedSource streamed_source_;
  std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task_;
};
540

541 542
ScriptCompiler::CachedData* Shell::LookupCodeCache(Isolate* isolate,
                                                   Local<Value> source) {
543
  base::LockGuard<base::Mutex> lock_guard(cached_code_mutex_.Pointer());
544 545 546 547 548 549 550 551 552 553 554
  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];
    memcpy(cache, entry->second->data, length);
    ScriptCompiler::CachedData* cached_data = new ScriptCompiler::CachedData(
        cache, length, ScriptCompiler::CachedData::BufferOwned);
    return cached_data;
555
  }
556
  return nullptr;
557 558
}

559 560
void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> source,
                             const ScriptCompiler::CachedData* cache_data) {
561
  base::LockGuard<base::Mutex> lock_guard(cached_code_mutex_.Pointer());
562 563 564 565 566 567 568 569 570 571
  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];
  memcpy(cache, cache_data->data, length);
  cached_code_map_[*key] = std::unique_ptr<ScriptCompiler::CachedData>(
      new ScriptCompiler::CachedData(cache, length,
                                     ScriptCompiler::CachedData::BufferOwned));
572 573
}

574
// Executes a string within the current v8 context.
575
bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
576 577 578
                          Local<Value> name, PrintResult print_result,
                          ReportExceptions report_exceptions,
                          ProcessMessageQueue process_message_queue) {
579
  HandleScope handle_scope(isolate);
580
  TryCatch try_catch(isolate);
581
  try_catch.SetVerbose(true);
582

583
  MaybeLocal<Value> maybe_result;
584
  bool success = true;
585
  {
586 587
    PerIsolateData* data = PerIsolateData::Get(isolate);
    Local<Context> realm =
588
        Local<Context>::New(isolate, data->realms_[data->realm_current_]);
589
    Context::Scope context_scope(realm);
590
    MaybeLocal<Script> maybe_script;
591 592 593
    Local<Context> context(isolate->GetCurrentContext());
    ScriptOrigin origin(name);

594 595 596
    DCHECK(options.compile_options != ScriptCompiler::kProduceParserCache);
    DCHECK(options.compile_options != ScriptCompiler::kConsumeParserCache);
    if (options.compile_options == ScriptCompiler::kConsumeCodeCache) {
597 598 599 600 601 602 603 604 605 606 607 608 609
      ScriptCompiler::CachedData* cached_code =
          LookupCodeCache(isolate, source);
      if (cached_code != nullptr) {
        ScriptCompiler::Source script_source(source, origin, cached_code);
        maybe_script = ScriptCompiler::Compile(context, &script_source,
                                               options.compile_options);
        CHECK(!cached_code->rejected);
      } else {
        ScriptCompiler::Source script_source(source, origin);
        maybe_script = ScriptCompiler::Compile(
            context, &script_source, ScriptCompiler::kNoCompileOptions);
      }
    } else if (options.stress_background_compile) {
610 611 612 613 614 615 616
      // Start a background thread compiling the script.
      BackgroundCompileThread background_compile_thread(isolate, source);
      background_compile_thread.Start();

      // In parallel, compile on the main thread to flush out any data races.
      {
        TryCatch ignore_try_catch(isolate);
617 618 619
        ScriptCompiler::Source script_source(source, origin);
        USE(ScriptCompiler::Compile(context, &script_source,
                                    ScriptCompiler::kNoCompileOptions));
620 621 622 623 624
      }

      // Join with background thread and finalize compilation.
      background_compile_thread.Join();
      maybe_script = v8::ScriptCompiler::Compile(
625
          context, background_compile_thread.streamed_source(), source, origin);
626
    } else {
627
      ScriptCompiler::Source script_source(source, origin);
628 629
      maybe_script = ScriptCompiler::Compile(context, &script_source,
                                             options.compile_options);
630 631
    }

632
    Local<Script> script;
633
    if (!maybe_script.ToLocal(&script)) {
634 635 636
      // Print errors that happened during compilation.
      if (report_exceptions) ReportException(isolate, &try_catch);
      return false;
637
    }
638

639 640 641 642 643 644 645 646
    if (options.code_cache_options ==
        ShellOptions::CodeCacheOptions::kProduceCache) {
      // Serialize and store it in memory for the next execution.
      ScriptCompiler::CachedData* cached_data =
          ScriptCompiler::CreateCodeCache(script->GetUnboundScript(), source);
      StoreInCodeCache(isolate, source, cached_data);
      delete cached_data;
    }
647
    maybe_result = script->Run(realm);
648 649
    if (options.code_cache_options ==
        ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute) {
650 651 652 653 654 655
      // Serialize and store it in memory for the next execution.
      ScriptCompiler::CachedData* cached_data =
          ScriptCompiler::CreateCodeCache(script->GetUnboundScript(), source);
      StoreInCodeCache(isolate, source, cached_data);
      delete cached_data;
    }
656
    if (process_message_queue && !EmptyMessageQueues(isolate)) success = false;
657 658
    data->realm_current_ = data->realm_switch_;
  }
659 660
  Local<Value> result;
  if (!maybe_result.ToLocal(&result)) {
661 662
    DCHECK(try_catch.HasCaught());
    // Print errors that happened during execution.
663
    if (report_exceptions) ReportException(isolate, &try_catch);
664 665 666 667 668 669 670 671
    return false;
  }
  DCHECK(!try_catch.HasCaught());
  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.
672
        v8::String::Utf8Value str(isolate, result);
673 674
        fwrite(*str, sizeof(**str), str.length(), stdout);
        printf("\n");
675
      }
676
    } else {
677
      v8::String::Utf8Value str(isolate, Stringify(isolate, result));
678 679
      fwrite(*str, sizeof(**str), str.length(), stdout);
      printf("\n");
680 681
    }
  }
682
  return success;
683 684
}

685 686
namespace {

687 688
std::string ToSTLString(Isolate* isolate, Local<String> v8_str) {
  String::Utf8Value utf8(isolate, v8_str);
689 690 691 692 693 694
  // Should not be able to fail since the input is a String.
  CHECK(*utf8);
  return *utf8;
}

bool IsAbsolutePath(const std::string& path) {
695 696 697
#if defined(_WIN32) || defined(_WIN64)
  // TODO(adamk): This is an incorrect approximation, but should
  // work for all our test-running cases.
698
  return path.find(':') != std::string::npos;
699 700 701 702 703 704 705 706 707 708
#else
  return path[0] == '/';
#endif
}

std::string GetWorkingDirectory() {
#if defined(_WIN32) || defined(_WIN64)
  char system_buffer[MAX_PATH];
  // TODO(adamk): Support Unicode paths.
  DWORD len = GetCurrentDirectoryA(MAX_PATH, system_buffer);
709
  CHECK_GT(len, 0);
710 711 712 713 714 715 716 717
  return system_buffer;
#else
  char curdir[PATH_MAX];
  CHECK_NOT_NULL(getcwd(curdir, PATH_MAX));
  return curdir;
#endif
}

718
// Returns the directory part of path, without the trailing '/'.
719
std::string DirName(const std::string& path) {
720 721
  DCHECK(IsAbsolutePath(path));
  size_t last_slash = path.find_last_of('/');
722
  DCHECK(last_slash != std::string::npos);
723 724 725
  return path.substr(0, last_slash);
}

726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
// 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) {
  std::string result;
  if (IsAbsolutePath(path)) {
    result = path;
  } else {
    result = dir_name + '/' + path;
  }
  std::replace(result.begin(), result.end(), '\\', '/');
  size_t i;
  while ((i = result.find("/./")) != std::string::npos) {
    result.erase(i, 2);
  }
  return result;
743 744
}

745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
// 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)
762
      : module_to_specifier_map(10, ModuleGlobalHash(isolate)) {}
763 764 765

  // Map from normalized module specifier to Module.
  std::unordered_map<std::string, Global<Module>> specifier_to_module_map;
766
  // Map from Module to its URL as defined in the ScriptOrigin
767
  std::unordered_map<Global<Module>, std::string, ModuleGlobalHash>
768
      module_to_specifier_map;
769 770 771
};

enum {
772 773
  kModuleEmbedderDataIndex,
  kInspectorClientIndex
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
};

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);
}

791 792
MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
                                         Local<String> specifier,
793
                                         Local<Module> referrer) {
794
  Isolate* isolate = context->GetIsolate();
795
  ModuleEmbedderData* d = GetModuleDataFromContext(context);
796 797 798 799 800
  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));
801 802 803
  auto module_it = d->specifier_to_module_map.find(absolute_path);
  CHECK(module_it != d->specifier_to_module_map.end());
  return module_it->second.Get(isolate);
804 805 806 807
}

}  // anonymous namespace

808 809
MaybeLocal<Module> Shell::FetchModuleTree(Local<Context> context,
                                          const std::string& file_name) {
810
  DCHECK(IsAbsolutePath(file_name));
811
  Isolate* isolate = context->GetIsolate();
812 813
  Local<String> source_text = ReadFile(isolate, file_name.c_str());
  if (source_text.IsEmpty()) {
814 815 816
    std::string msg = "Error reading: " + file_name;
    Throw(isolate, msg.c_str());
    return MaybeLocal<Module>();
817 818 819
  }
  ScriptOrigin origin(
      String::NewFromUtf8(isolate, file_name.c_str(), NewStringType::kNormal)
820 821 822
          .ToLocalChecked(),
      Local<Integer>(), Local<Integer>(), Local<Boolean>(), Local<Integer>(),
      Local<Value>(), Local<Boolean>(), Local<Boolean>(), True(isolate));
823 824 825 826 827
  ScriptCompiler::Source source(source_text, origin);
  Local<Module> module;
  if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
    return MaybeLocal<Module>();
  }
828 829 830 831 832

  ModuleEmbedderData* d = GetModuleDataFromContext(context);
  CHECK(d->specifier_to_module_map
            .insert(std::make_pair(file_name, Global<Module>(isolate, module)))
            .second);
833 834 835
  CHECK(d->module_to_specifier_map
            .insert(std::make_pair(Global<Module>(isolate, module), file_name))
            .second);
836 837 838

  std::string dir_name = DirName(file_name);

839 840
  for (int i = 0, length = module->GetModuleRequestsLength(); i < length; ++i) {
    Local<String> name = module->GetModuleRequest(i);
841 842
    std::string absolute_path =
        NormalizePath(ToSTLString(isolate, name), dir_name);
843 844
    if (!d->specifier_to_module_map.count(absolute_path)) {
      if (FetchModuleTree(context, absolute_path).IsEmpty()) {
845 846 847 848 849 850 851 852
        return MaybeLocal<Module>();
      }
    }
  }

  return module;
}

853 854 855 856 857
namespace {

struct DynamicImportData {
  DynamicImportData(Isolate* isolate_, Local<String> referrer_,
                    Local<String> specifier_,
858
                    Local<Promise::Resolver> resolver_)
859 860 861
      : isolate(isolate_) {
    referrer.Reset(isolate, referrer_);
    specifier.Reset(isolate, specifier_);
862
    resolver.Reset(isolate, resolver_);
863 864 865 866 867
  }

  Isolate* isolate;
  Global<String> referrer;
  Global<String> specifier;
868
  Global<Promise::Resolver> resolver;
869 870 871
};

}  // namespace
872

873
MaybeLocal<Promise> Shell::HostImportModuleDynamically(
874 875
    Local<Context> context, Local<ScriptOrModule> referrer,
    Local<String> specifier) {
876
  Isolate* isolate = context->GetIsolate();
877 878 879 880 881

  MaybeLocal<Promise::Resolver> maybe_resolver =
      Promise::Resolver::New(context);
  Local<Promise::Resolver> resolver;
  if (maybe_resolver.ToLocal(&resolver)) {
882 883 884
    DynamicImportData* data = new DynamicImportData(
        isolate, Local<String>::Cast(referrer->GetResourceName()), specifier,
        resolver);
885 886 887 888 889
    isolate->EnqueueMicrotask(Shell::DoHostImportModuleDynamically, data);
    return resolver->GetPromise();
  }

  return MaybeLocal<Promise>();
890 891
}

892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
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 =
      String::NewFromUtf8(isolate, "url", NewStringType::kNormal)
          .ToLocalChecked();
  Local<String> url = String::NewFromUtf8(isolate, specifier_it->second.c_str(),
                                          NewStringType::kNormal)
                          .ToLocalChecked();
  meta->CreateDataProperty(context, url_key, url).ToChecked();
}

912 913 914 915 916 917 918 919
void Shell::DoHostImportModuleDynamically(void* import_data) {
  std::unique_ptr<DynamicImportData> import_data_(
      static_cast<DynamicImportData*>(import_data));
  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));
920
  Local<Promise::Resolver> resolver(import_data_->resolver.Get(isolate));
921 922 923 924 925

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

926
  std::string source_url = ToSTLString(isolate, referrer);
927
  std::string dir_name =
928
      DirName(NormalizePath(source_url, GetWorkingDirectory()));
929
  std::string file_name = ToSTLString(isolate, specifier);
930
  std::string absolute_path = NormalizePath(file_name, dir_name);
931 932 933 934 935 936 937 938 939 940 941

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

  ModuleEmbedderData* d = GetModuleDataFromContext(realm);
  Local<Module> root_module;
  auto module_it = d->specifier_to_module_map.find(absolute_path);
  if (module_it != d->specifier_to_module_map.end()) {
    root_module = module_it->second.Get(isolate);
  } else if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
    CHECK(try_catch.HasCaught());
942
    resolver->Reject(realm, try_catch.Exception()).ToChecked();
943 944 945 946
    return;
  }

  MaybeLocal<Value> maybe_result;
947 948
  if (root_module->InstantiateModule(realm, ResolveModuleCallback)
          .FromMaybe(false)) {
949 950 951 952 953 954 955
    maybe_result = root_module->Evaluate(realm);
    EmptyMessageQueues(isolate);
  }

  Local<Value> module;
  if (!maybe_result.ToLocal(&module)) {
    DCHECK(try_catch.HasCaught());
956
    resolver->Reject(realm, try_catch.Exception()).ToChecked();
957 958 959 960
    return;
  }

  DCHECK(!try_catch.HasCaught());
961 962
  Local<Value> module_namespace = root_module->GetModuleNamespace();
  resolver->Resolve(realm, module_namespace).ToChecked();
963 964
}

965 966 967
bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
  HandleScope handle_scope(isolate);

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

972
  std::string absolute_path = NormalizePath(file_name, GetWorkingDirectory());
973

974 975 976
  TryCatch try_catch(isolate);
  try_catch.SetVerbose(true);

977
  Local<Module> root_module;
978 979
  MaybeLocal<Value> maybe_exception;

980
  if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
981 982
    CHECK(try_catch.HasCaught());
    ReportException(isolate, &try_catch);
983 984 985 986
    return false;
  }

  MaybeLocal<Value> maybe_result;
987 988
  if (root_module->InstantiateModule(realm, ResolveModuleCallback)
          .FromMaybe(false)) {
989 990
    maybe_result = root_module->Evaluate(realm);
    EmptyMessageQueues(isolate);
991 992 993 994 995 996 997 998 999 1000 1001
  }
  Local<Value> result;
  if (!maybe_result.ToLocal(&result)) {
    DCHECK(try_catch.HasCaught());
    // Print errors that happened during execution.
    ReportException(isolate, &try_catch);
    return false;
  }
  DCHECK(!try_catch.HasCaught());
  return true;
}
1002

1003 1004 1005 1006
PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
  data_->realm_count_ = 1;
  data_->realm_current_ = 0;
  data_->realm_switch_ = 0;
1007
  data_->realms_ = new Global<Context>[1];
1008 1009
  data_->realms_[0].Reset(data_->isolate_,
                          data_->isolate_->GetEnteredContext());
1010 1011 1012 1013
}


PerIsolateData::RealmScope::~RealmScope() {
1014 1015 1016 1017
  // 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) {
1018 1019 1020 1021 1022 1023
    Global<Context>& realm = data_->realms_[i];
    if (realm.IsEmpty()) continue;
    DisposeModuleEmbedderData(realm.Get(data_->isolate_));
    // TODO(adamk): No need to reset manually, Globals reset when destructed.
    realm.Reset();
  }
1024
  data_->realm_count_ = 0;
1025
  delete[] data_->realms_;
1026
  // TODO(adamk): No need to reset manually, Globals reset when destructed.
1027
  if (!data_->realm_shared_.IsEmpty())
1028
    data_->realm_shared_.Reset();
1029 1030 1031
}


1032
int PerIsolateData::RealmFind(Local<Context> context) {
1033 1034 1035 1036 1037 1038 1039
  for (int i = 0; i < realm_count_; ++i) {
    if (realms_[i] == context) return i;
  }
  return -1;
}


1040 1041 1042 1043 1044 1045 1046
int PerIsolateData::RealmIndexOrThrow(
    const v8::FunctionCallbackInfo<v8::Value>& args,
    int arg_offset) {
  if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) {
    Throw(args.GetIsolate(), "Invalid argument");
    return -1;
  }
1047 1048 1049 1050
  int index = args[arg_offset]
                  ->Int32Value(args.GetIsolate()->GetCurrentContext())
                  .FromMaybe(-1);
  if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) {
1051 1052 1053 1054 1055 1056 1057
    Throw(args.GetIsolate(), "Invalid realm index");
    return -1;
  }
  return index;
}


1058
// performance.now() returns a time stamp as double, measured in milliseconds.
1059 1060
// When FLAG_verify_predictable mode is enabled it returns result of
// v8::Platform::MonotonicallyIncreasingTime().
1061
void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
1062
  if (i::FLAG_verify_predictable) {
1063
    args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime());
1064
  } else {
1065 1066
    base::TimeDelta delta =
        base::TimeTicks::HighResolutionNow() - kInitialTicks;
1067 1068
    args.GetReturnValue().Set(delta.InMillisecondsF());
  }
1069 1070 1071
}


1072
// Realm.current() returns the index of the currently active realm.
1073
void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) {
1074 1075
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1076
  int index = data->RealmFind(isolate->GetEnteredContext());
1077 1078
  if (index == -1) return;
  args.GetReturnValue().Set(index);
1079 1080 1081 1082
}


// Realm.owner(o) returns the index of the realm that created o.
1083
void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) {
1084 1085 1086
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
  if (args.Length() < 1 || !args[0]->IsObject()) {
1087
    Throw(args.GetIsolate(), "Invalid argument");
1088
    return;
1089
  }
1090 1091 1092 1093
  int index = data->RealmFind(args[0]
                                  ->ToObject(isolate->GetCurrentContext())
                                  .ToLocalChecked()
                                  ->CreationContext());
1094 1095
  if (index == -1) return;
  args.GetReturnValue().Set(index);
1096 1097 1098 1099 1100
}


// Realm.global(i) returns the global object of realm i.
// (Note that properties of global objects cannot be read/written cross-realm.)
1101
void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1102
  PerIsolateData* data = PerIsolateData::Get(args.GetIsolate());
1103 1104
  int index = data->RealmIndexOrThrow(args, 0);
  if (index == -1) return;
1105 1106
  args.GetReturnValue().Set(
      Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global());
1107 1108
}

1109
MaybeLocal<Context> Shell::CreateRealm(
1110 1111
    const v8::FunctionCallbackInfo<v8::Value>& args, int index,
    v8::MaybeLocal<Value> global_object) {
1112
  Isolate* isolate = args.GetIsolate();
1113
  TryCatch try_catch(isolate);
1114
  PerIsolateData* data = PerIsolateData::Get(isolate);
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
  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;
  }
1125
  Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1126
  Local<Context> context =
1127
      Context::New(isolate, nullptr, global_template, global_object);
1128 1129
  DCHECK(!try_catch.HasCaught());
  if (context.IsEmpty()) return MaybeLocal<Context>();
1130
  InitializeModuleEmbedderData(context);
1131
  data->realms_[index].Reset(isolate, context);
1132
  args.GetReturnValue().Set(index);
1133
  return context;
1134 1135
}

1136 1137 1138 1139 1140 1141 1142
void Shell::DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args,
                         int index) {
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
  DisposeModuleEmbedderData(data->realms_[index].Get(isolate));
  data->realms_[index].Reset();
  isolate->ContextDisposedNotification();
1143
  isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime());
1144 1145
}

1146 1147 1148
// Realm.create() creates a new realm with a distinct security token
// and returns its index.
void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1149
  CreateRealm(args, -1, v8::MaybeLocal<Value>());
1150 1151 1152 1153 1154 1155 1156
}

// 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;
1157
  if (CreateRealm(args, -1, v8::MaybeLocal<Value>()).ToLocal(&context)) {
1158 1159 1160 1161
    context->SetSecurityToken(
        args.GetIsolate()->GetEnteredContext()->GetSecurityToken());
  }
}
1162

1163 1164 1165 1166 1167 1168 1169
// 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;
1170 1171 1172 1173 1174
  if (index == 0 || index == data->realm_current_ ||
      index == data->realm_switch_) {
    Throw(args.GetIsolate(), "Invalid realm index");
    return;
  }
1175 1176 1177 1178 1179 1180 1181

  Local<Context> context = Local<Context>::New(isolate, data->realms_[index]);
  v8::MaybeLocal<Value> global_object = context->Global();
  DisposeRealm(args, index);
  CreateRealm(args, index, global_object);
}

1182
// Realm.dispose(i) disposes the reference to the realm i.
1183
void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
1184 1185
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1186 1187 1188
  int index = data->RealmIndexOrThrow(args, 0);
  if (index == -1) return;
  if (index == 0 ||
1189
      index == data->realm_current_ || index == data->realm_switch_) {
1190
    Throw(args.GetIsolate(), "Invalid realm index");
1191
    return;
1192
  }
1193
  DisposeRealm(args, index);
1194 1195 1196 1197
}


// Realm.switch(i) switches to the realm i for consecutive interactive inputs.
1198
void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) {
1199 1200
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1201 1202
  int index = data->RealmIndexOrThrow(args, 0);
  if (index == -1) return;
1203 1204 1205 1206 1207
  data->realm_switch_ = index;
}


// Realm.eval(i, s) evaluates s in realm i and returns the result.
1208
void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
1209 1210
  Isolate* isolate = args.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1211 1212 1213
  int index = data->RealmIndexOrThrow(args, 0);
  if (index == -1) return;
  if (args.Length() < 2 || !args[1]->IsString()) {
1214
    Throw(args.GetIsolate(), "Invalid argument");
1215
    return;
1216
  }
1217 1218 1219 1220 1221 1222 1223
  ScriptCompiler::Source script_source(
      args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked());
  Local<UnboundScript> script;
  if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
           .ToLocal(&script)) {
    return;
  }
1224
  Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
1225
  realm->Enter();
1226 1227
  int previous_index = data->realm_current_;
  data->realm_current_ = data->realm_switch_ = index;
1228 1229 1230
  Local<Value> result;
  if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) {
    realm->Exit();
1231
    data->realm_current_ = data->realm_switch_ = previous_index;
1232 1233
    return;
  }
1234
  realm->Exit();
1235
  data->realm_current_ = data->realm_switch_ = previous_index;
1236
  args.GetReturnValue().Set(result);
1237 1238 1239 1240
}


// Realm.shared is an accessor for a single shared value across realms.
1241 1242
void Shell::RealmSharedGet(Local<String> property,
                           const PropertyCallbackInfo<Value>& info) {
1243 1244
  Isolate* isolate = info.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1245 1246
  if (data->realm_shared_.IsEmpty()) return;
  info.GetReturnValue().Set(data->realm_shared_);
1247 1248 1249 1250
}

void Shell::RealmSharedSet(Local<String> property,
                           Local<Value> value,
1251
                           const PropertyCallbackInfo<void>& info) {
1252 1253
  Isolate* isolate = info.GetIsolate();
  PerIsolateData* data = PerIsolateData::Get(isolate);
1254
  data->realm_shared_.Reset(isolate, value);
1255 1256
}

1257
void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) {
1258
  for (int i = 0; i < args.Length(); i++) {
1259
    HandleScope handle_scope(args.GetIsolate());
1260
    if (i != 0) {
1261
      fprintf(file, " ");
1262
    }
1263 1264

    // Explicitly catch potential exceptions in toString().
1265
    v8::TryCatch try_catch(args.GetIsolate());
1266
    Local<Value> arg = args[i];
1267
    Local<String> str_obj;
1268 1269 1270 1271 1272

    if (arg->IsSymbol()) {
      arg = Local<Symbol>::Cast(arg)->Name();
    }
    if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
1273
             .ToLocal(&str_obj)) {
1274 1275 1276
      try_catch.ReThrow();
      return;
    }
1277

1278
    v8::String::Utf8Value str(args.GetIsolate(), str_obj);
1279
    int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), file));
1280 1281
    if (n != str.length()) {
      printf("Error in fwrite\n");
1282
      base::OS::ExitProcess(1);
1283
    }
1284 1285 1286
  }
}

1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
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);
}
1305

1306
void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
1307
  String::Utf8Value file(args.GetIsolate(), args[0]);
1308
  if (*file == nullptr) {
1309
    Throw(args.GetIsolate(), "Error loading file");
1310
    return;
1311
  }
1312
  if (args.Length() == 2) {
1313
    String::Utf8Value format(args.GetIsolate(), args[1]);
1314 1315 1316 1317 1318
    if (*format && std::strcmp(*format, "binary") == 0) {
      ReadBuffer(args);
      return;
    }
  }
1319
  Local<String> source = ReadFile(args.GetIsolate(), *file);
1320
  if (source.IsEmpty()) {
1321
    Throw(args.GetIsolate(), "Error loading file");
1322
    return;
1323
  }
1324
  args.GetReturnValue().Set(source);
1325 1326 1327
}


1328
Local<String> Shell::ReadFromStdin(Isolate* isolate) {
1329 1330
  static const int kBufferSize = 256;
  char buffer[kBufferSize];
1331 1332
  Local<String> accumulator =
      String::NewFromUtf8(isolate, "", NewStringType::kNormal).ToLocalChecked();
1333
  int length;
1334 1335 1336 1337
  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.
1338
    char* input = nullptr;
1339
    input = fgets(buffer, kBufferSize, stdin);
1340
    if (input == nullptr) return Local<String>();
1341
    length = static_cast<int>(strlen(buffer));
1342 1343 1344
    if (length == 0) {
      return accumulator;
    } else if (buffer[length-1] != '\n') {
1345 1346
      accumulator = String::Concat(
          accumulator,
1347 1348
          String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
              .ToLocalChecked());
1349 1350
    } else if (length > 1 && buffer[length-2] == '\\') {
      buffer[length-2] = '\n';
1351
      accumulator = String::Concat(
1352 1353 1354
          accumulator,
          String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
                              length - 1).ToLocalChecked());
1355
    } else {
1356
      return String::Concat(
1357 1358 1359
          accumulator,
          String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
                              length - 1).ToLocalChecked());
1360 1361
    }
  }
1362 1363 1364
}


1365
void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
1366
  for (int i = 0; i < args.Length(); i++) {
1367
    HandleScope handle_scope(args.GetIsolate());
1368
    String::Utf8Value file(args.GetIsolate(), args[i]);
1369
    if (*file == nullptr) {
1370
      Throw(args.GetIsolate(), "Error loading file");
1371
      return;
1372
    }
1373
    Local<String> source = ReadFile(args.GetIsolate(), *file);
1374
    if (source.IsEmpty()) {
1375
      Throw(args.GetIsolate(), "Error loading file");
1376
      return;
1377
    }
1378 1379 1380 1381 1382 1383 1384 1385
    if (!ExecuteString(
            args.GetIsolate(), source,
            String::NewFromUtf8(args.GetIsolate(), *file,
                                NewStringType::kNormal)
                .ToLocalChecked(),
            kNoPrintResult,
            options.quiet_load ? kNoReportExceptions : kReportExceptions,
            kNoProcessMessageQueue)) {
1386
      Throw(args.GetIsolate(), "Error executing file");
1387
      return;
1388 1389 1390 1391
    }
  }
}

1392 1393 1394 1395 1396 1397 1398 1399
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;
  Local<Function> callback = Local<Function>::Cast(args[0]);
  Local<Context> context = isolate->GetCurrentContext();
  PerIsolateData::Get(isolate)->SetTimeout(callback, context);
}
1400

1401 1402 1403
void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);
1404 1405
  if (args.Length() < 1 || !args[0]->IsString()) {
    Throw(args.GetIsolate(), "1st argument must be string");
1406 1407 1408
    return;
  }

1409 1410 1411 1412 1413
  if (!args.IsConstructCall()) {
    Throw(args.GetIsolate(), "Worker must be constructed with new");
    return;
  }

1414
  {
1415
    base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
1416
    if (workers_.size() >= kMaxWorkers) {
1417 1418 1419 1420
      Throw(args.GetIsolate(), "Too many workers, I won't let you create more");
      return;
    }

1421
    // Initialize the embedder field to nullptr; if we return early without
1422 1423
    // creating a new Worker (because the main thread is terminating) we can
    // early-out from the instance calls.
1424
    args.Holder()->SetAlignedPointerInInternalField(0, nullptr);
1425

1426 1427 1428
    if (!allow_new_workers_) return;

    Worker* worker = new Worker;
1429
    args.Holder()->SetAlignedPointerInInternalField(0, worker);
1430
    workers_.push_back(worker);
1431

1432
    String::Utf8Value script(args.GetIsolate(), args[0]);
1433 1434
    if (!*script) {
      Throw(args.GetIsolate(), "Can't get worker script");
1435 1436
      return;
    }
1437
    worker->StartExecuteInThread(*script);
1438
  }
1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450
}


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;
  }

1451 1452
  Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
  if (!worker) {
1453 1454 1455
    return;
  }

1456
  Local<Value> message = args[0];
1457 1458 1459 1460 1461 1462
  Local<Value> transfer =
      args.Length() >= 2 ? args[1] : Local<Value>::Cast(Undefined(isolate));
  std::unique_ptr<SerializationData> data =
      Shell::SerializeValue(isolate, message, transfer);
  if (data) {
    worker->PostMessage(std::move(data));
1463 1464 1465 1466 1467 1468 1469
  }
}


void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);
1470 1471
  Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
  if (!worker) {
1472 1473 1474
    return;
  }

1475
  std::unique_ptr<SerializationData> data = worker->GetMessage();
1476
  if (data) {
1477 1478 1479
    Local<Value> value;
    if (Shell::DeserializeValue(isolate, std::move(data)).ToLocal(&value)) {
      args.GetReturnValue().Set(value);
1480 1481 1482 1483 1484 1485 1486 1487
    }
  }
}


void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope handle_scope(isolate);
1488 1489
  Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
  if (!worker) {
1490 1491 1492 1493 1494 1495 1496
    return;
  }

  worker->Terminate();
}


1497
void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) {
1498 1499 1500
  int exit_code = (*args)[0]
                      ->Int32Value(args->GetIsolate()->GetCurrentContext())
                      .FromMaybe(0);
1501
  CleanupWorkers();
1502
  args->GetIsolate()->Exit();
1503
  OnExit(args->GetIsolate());
1504
  base::OS::ExitProcess(exit_code);
1505 1506 1507
}


1508 1509 1510 1511 1512
void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
  base::CallOnce(&quit_once_, &QuitOnce,
                 const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args));
}

1513 1514 1515 1516 1517 1518 1519
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);
}
1520

1521
void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
1522
  args.GetReturnValue().Set(
1523 1524
      String::NewFromUtf8(args.GetIsolate(), V8::GetVersion(),
                          NewStringType::kNormal).ToLocalChecked());
1525 1526 1527
}


1528 1529
void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) {
  HandleScope handle_scope(isolate);
1530 1531
  Local<Context> context = isolate->GetCurrentContext();
  bool enter_context = context.IsEmpty();
1532
  if (enter_context) {
yangguo's avatar
yangguo committed
1533 1534
    context = Local<Context>::New(isolate, evaluation_context_);
    context->Enter();
1535
  }
1536 1537 1538 1539 1540
  // Converts a V8 value to a C string.
  auto ToCString = [](const v8::String::Utf8Value& value) {
    return *value ? *value : "<string conversion failed>";
  };

1541
  v8::String::Utf8Value exception(isolate, try_catch->Exception());
1542
  const char* exception_string = ToCString(exception);
1543
  Local<Message> message = try_catch->Message();
1544 1545 1546
  if (message.IsEmpty()) {
    // V8 didn't provide any extra information about this error; just
    // print the exception.
1547
    printf("%s\n", exception_string);
1548
  } else if (message->GetScriptOrigin().Options().IsWasm()) {
1549
    // Print wasm-function[(function index)]:(offset): (message).
1550 1551
    int function_index = message->GetLineNumber(context).FromJust() - 1;
    int offset = message->GetStartColumn(context).FromJust();
1552 1553
    printf("wasm-function[%d]:%d: %s\n", function_index, offset,
           exception_string);
1554 1555
  } else {
    // Print (filename):(line number): (message).
1556 1557
    v8::String::Utf8Value filename(isolate,
                                   message->GetScriptOrigin().ResourceName());
1558
    const char* filename_string = ToCString(filename);
1559
    int linenum = message->GetLineNumber(context).FromMaybe(-1);
1560
    printf("%s:%i: %s\n", filename_string, linenum, exception_string);
1561
    Local<String> sourceline;
1562
    if (message->GetSourceLine(context).ToLocal(&sourceline)) {
1563
      // Print line of source code.
1564
      v8::String::Utf8Value sourcelinevalue(isolate, sourceline);
1565
      const char* sourceline_string = ToCString(sourcelinevalue);
1566 1567
      printf("%s\n", sourceline_string);
      // Print wavy underline (GetUnderline is deprecated).
1568
      int start = message->GetStartColumn(context).FromJust();
1569 1570 1571
      for (int i = 0; i < start; i++) {
        printf(" ");
      }
1572
      int end = message->GetEndColumn(context).FromJust();
1573 1574 1575 1576
      for (int i = start; i < end; i++) {
        printf("^");
      }
      printf("\n");
1577
    }
1578 1579 1580 1581
  }
  Local<Value> stack_trace_string;
  if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) &&
      stack_trace_string->IsString()) {
1582 1583
    v8::String::Utf8Value stack_trace(isolate,
                                      Local<String>::Cast(stack_trace_string));
1584
    printf("%s\n", ToCString(stack_trace));
1585
  }
1586
  printf("\n");
yangguo's avatar
yangguo committed
1587
  if (enter_context) context->Exit();
1588 1589 1590
}


1591
int32_t* Counter::Bind(const char* name, bool is_histogram) {
1592 1593 1594 1595
  int i;
  for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
    name_[i] = static_cast<char>(name[i]);
  name_[i] = '\0';
1596 1597 1598 1599 1600 1601 1602 1603
  is_histogram_ = is_histogram;
  return ptr();
}


void Counter::AddSample(int32_t sample) {
  count_++;
  sample_total_ += sample;
1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615
}


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


Counter* CounterCollection::GetNextCounter() {
1616
  if (counters_in_use_ == kMaxCounters) return nullptr;
1617 1618 1619 1620
  return &counters_[counters_in_use_++];
}


1621
void Shell::MapCounters(v8::Isolate* isolate, const char* name) {
1622
  counters_file_ = base::OS::MemoryMappedFile::create(
1623
      name, sizeof(CounterCollection), &local_counters_);
1624 1625 1626
  void* memory =
      (counters_file_ == nullptr) ? nullptr : counters_file_->memory();
  if (memory == nullptr) {
1627
    printf("Could not map counters file %s\n", name);
1628
    base::OS::ExitProcess(1);
1629 1630
  }
  counters_ = static_cast<CounterCollection*>(memory);
1631 1632 1633
  isolate->SetCounterFunction(LookupCounter);
  isolate->SetCreateHistogramFunction(CreateHistogram);
  isolate->SetAddHistogramSampleFunction(AddHistogramSample);
1634 1635 1636
}


1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647
int CounterMap::Hash(const char* name) {
  int h = 0;
  int c;
  while ((c = *name++) != 0) {
    h += h << 5;
    h += c;
  }
  return h;
}


1648
Counter* Shell::GetCounter(const char* name, bool is_histogram) {
1649
  Counter* counter = counter_map_->Lookup(name);
1650

1651
  if (counter == nullptr) {
1652
    counter = counters_->GetNextCounter();
1653
    if (counter != nullptr) {
1654 1655 1656 1657
      counter_map_->Set(name, counter);
      counter->Bind(name, is_histogram);
    }
  } else {
1658
    DCHECK(counter->is_histogram() == is_histogram);
1659 1660 1661 1662 1663 1664 1665 1666
  }
  return counter;
}


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

1667
  if (counter != nullptr) {
1668
    return counter->ptr();
1669
  } else {
1670
    return nullptr;
1671
  }
1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685
}


void* Shell::CreateHistogram(const char* name,
                             int min,
                             int max,
                             size_t buckets) {
  return GetCounter(name, true);
}


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

yangguo's avatar
yangguo committed
1688 1689 1690
// Turn a value into a human-readable string.
Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) {
  v8::Local<v8::Context> context =
1691
      v8::Local<v8::Context>::New(isolate, evaluation_context_);
yangguo's avatar
yangguo committed
1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714
  if (stringify_function_.IsEmpty()) {
    int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
    i::Vector<const char> source_string =
        i::NativesCollection<i::D8>::GetScriptSource(source_index);
    i::Vector<const char> source_name =
        i::NativesCollection<i::D8>::GetScriptName(source_index);
    Local<String> source =
        String::NewFromUtf8(isolate, source_string.start(),
                            NewStringType::kNormal, source_string.length())
            .ToLocalChecked();
    Local<String> name =
        String::NewFromUtf8(isolate, source_name.start(),
                            NewStringType::kNormal, source_name.length())
            .ToLocalChecked();
    ScriptOrigin origin(name);
    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);
1715
  MaybeLocal<Value> result = fun->Call(context, Undefined(isolate), 1, argv);
yangguo's avatar
yangguo committed
1716 1717
  if (result.IsEmpty()) return String::Empty(isolate);
  return result.ToLocalChecked().As<String>();
1718
}
1719

1720

1721 1722 1723 1724 1725 1726
Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
  Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
  global_template->Set(
      String::NewFromUtf8(isolate, "print", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, Print));
1727 1728 1729 1730
  global_template->Set(
      String::NewFromUtf8(isolate, "printErr", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, PrintErr));
1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750
  global_template->Set(
      String::NewFromUtf8(isolate, "write", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, Write));
  global_template->Set(
      String::NewFromUtf8(isolate, "read", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, Read));
  global_template->Set(
      String::NewFromUtf8(isolate, "readbuffer", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, ReadBuffer));
  global_template->Set(
      String::NewFromUtf8(isolate, "readline", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, ReadLine));
  global_template->Set(
      String::NewFromUtf8(isolate, "load", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, Load));
1751 1752 1753 1754
  global_template->Set(
      String::NewFromUtf8(isolate, "setTimeout", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, SetTimeout));
1755 1756 1757 1758
  // 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) {
1759 1760 1761 1762
    global_template->Set(
        String::NewFromUtf8(isolate, "quit", NewStringType::kNormal)
            .ToLocalChecked(),
        FunctionTemplate::New(isolate, Quit));
1763
  }
1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776
  Local<ObjectTemplate> test_template = ObjectTemplate::New(isolate);
  global_template->Set(
      String::NewFromUtf8(isolate, "testRunner", NewStringType::kNormal)
          .ToLocalChecked(),
      test_template);
  test_template->Set(
      String::NewFromUtf8(isolate, "notifyDone", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, NotifyDone));
  test_template->Set(
      String::NewFromUtf8(isolate, "waitUntilDone", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, WaitUntilDone));
1777 1778 1779 1780
  global_template->Set(
      String::NewFromUtf8(isolate, "version", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, Version));
1781 1782 1783 1784
  global_template->Set(
      Symbol::GetToStringTag(isolate),
      String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
          .ToLocalChecked());
1785

1786
  // Bind the Realm object.
1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
  Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
  realm_template->Set(
      String::NewFromUtf8(isolate, "current", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, RealmCurrent));
  realm_template->Set(
      String::NewFromUtf8(isolate, "owner", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, RealmOwner));
  realm_template->Set(
      String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, RealmGlobal));
  realm_template->Set(
      String::NewFromUtf8(isolate, "create", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, RealmCreate));
1804 1805 1806 1807 1808
  realm_template->Set(
      String::NewFromUtf8(isolate, "createAllowCrossRealmAccess",
                          NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, RealmCreateAllowCrossRealmAccess));
1809 1810 1811 1812
  realm_template->Set(
      String::NewFromUtf8(isolate, "navigate", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, RealmNavigate));
1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832
  realm_template->Set(
      String::NewFromUtf8(isolate, "dispose", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, RealmDispose));
  realm_template->Set(
      String::NewFromUtf8(isolate, "switch", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, RealmSwitch));
  realm_template->Set(
      String::NewFromUtf8(isolate, "eval", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, RealmEval));
  realm_template->SetAccessor(
      String::NewFromUtf8(isolate, "shared", NewStringType::kNormal)
          .ToLocalChecked(),
      RealmSharedGet, RealmSharedSet);
  global_template->Set(
      String::NewFromUtf8(isolate, "Realm", NewStringType::kNormal)
          .ToLocalChecked(),
      realm_template);
1833

1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844
  Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
  performance_template->Set(
      String::NewFromUtf8(isolate, "now", NewStringType::kNormal)
          .ToLocalChecked(),
      FunctionTemplate::New(isolate, PerformanceNow));
  global_template->Set(
      String::NewFromUtf8(isolate, "performance", NewStringType::kNormal)
          .ToLocalChecked(),
      performance_template);

  Local<FunctionTemplate> worker_fun_template =
1845
      FunctionTemplate::New(isolate, WorkerNew);
1846 1847 1848 1849 1850
  Local<Signature> worker_signature =
      Signature::New(isolate, worker_fun_template);
  worker_fun_template->SetClassName(
      String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
          .ToLocalChecked());
1851
  worker_fun_template->ReadOnlyPrototype();
1852
  worker_fun_template->PrototypeTemplate()->Set(
1853 1854
      String::NewFromUtf8(isolate, "terminate", NewStringType::kNormal)
          .ToLocalChecked(),
1855 1856
      FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(),
                            worker_signature));
1857
  worker_fun_template->PrototypeTemplate()->Set(
1858 1859
      String::NewFromUtf8(isolate, "postMessage", NewStringType::kNormal)
          .ToLocalChecked(),
1860 1861
      FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(),
                            worker_signature));
1862
  worker_fun_template->PrototypeTemplate()->Set(
1863 1864
      String::NewFromUtf8(isolate, "getMessage", NewStringType::kNormal)
          .ToLocalChecked(),
1865 1866
      FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(),
                            worker_signature));
1867
  worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1);
1868 1869 1870 1871
  global_template->Set(
      String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
          .ToLocalChecked(),
      worker_fun_template);
1872

1873
  Local<ObjectTemplate> os_templ = ObjectTemplate::New(isolate);
1874
  AddOSMethods(isolate, os_templ);
1875 1876 1877 1878
  global_template->Set(
      String::NewFromUtf8(isolate, "os", NewStringType::kNormal)
          .ToLocalChecked(),
      os_templ);
1879

1880 1881 1882
  return global_template;
}

1883 1884 1885 1886
static void PrintNonErrorsMessageCallback(Local<Message> message,
                                          Local<Value> error) {
  // Nothing to do here for errors, exceptions thrown up to the shell will be
  // reported
1887
  // separately by {Shell::ReportException} after they are caught.
1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911
  // Do print other kinds of messages.
  switch (message->ErrorLevel()) {
    case v8::Isolate::kMessageWarning:
    case v8::Isolate::kMessageLog:
    case v8::Isolate::kMessageInfo:
    case v8::Isolate::kMessageDebug: {
      break;
    }

    case v8::Isolate::kMessageError: {
      // Ignore errors, printed elsewhere.
      return;
    }

    default: {
      UNREACHABLE();
      break;
    }
  }
  // Converts a V8 value to a C string.
  auto ToCString = [](const v8::String::Utf8Value& value) {
    return *value ? *value : "<string conversion failed>";
  };
  Isolate* isolate = Isolate::GetCurrent();
1912
  v8::String::Utf8Value msg(isolate, message->Get());
1913 1914
  const char* msg_string = ToCString(msg);
  // Print (filename):(line number): (message).
1915 1916
  v8::String::Utf8Value filename(isolate,
                                 message->GetScriptOrigin().ResourceName());
1917 1918 1919 1920
  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);
1921
}
1922

1923
void Shell::Initialize(Isolate* isolate) {
1924 1925
  // Set up counters
  if (i::StrLength(i::FLAG_map_counters) != 0)
1926
    MapCounters(isolate, i::FLAG_map_counters);
1927
  // Disable default message reporting.
1928 1929 1930 1931 1932
  isolate->AddMessageListenerWithErrorLevel(
      PrintNonErrorsMessageCallback,
      v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
          v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
          v8::Isolate::kMessageLog);
1933 1934
}

1935

1936
Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
1937
  // This needs to be a critical section since this is not thread-safe
1938
  base::LockGuard<base::Mutex> lock_guard(context_mutex_.Pointer());
1939
  // Initialize the global objects
1940
  Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1941
  EscapableHandleScope handle_scope(isolate);
1942
  Local<Context> context = Context::New(isolate, nullptr, global_template);
1943
  DCHECK(!context.IsEmpty());
1944
  InitializeModuleEmbedderData(context);
1945
  Context::Scope scope(context);
1946

1947
  i::Factory* factory = reinterpret_cast<i::Isolate*>(isolate)->factory();
1948 1949
  i::JSArguments js_args = i::FLAG_js_arguments;
  i::Handle<i::FixedArray> arguments_array =
1950 1951
      factory->NewFixedArray(js_args.argc);
  for (int j = 0; j < js_args.argc; j++) {
1952
    i::Handle<i::String> arg =
1953
        factory->NewStringFromUtf8(i::CStrVector(js_args[j])).ToHandleChecked();
1954 1955 1956
    arguments_array->set(j, *arg);
  }
  i::Handle<i::JSArray> arguments_jsarray =
1957
      factory->NewJSArrayWithElements(arguments_array);
1958 1959 1960 1961 1962 1963
  context->Global()
      ->Set(context,
            String::NewFromUtf8(isolate, "arguments", NewStringType::kNormal)
                .ToLocalChecked(),
            Utils::ToLocal(arguments_jsarray))
      .FromJust();
1964
  return handle_scope.Escape(context);
1965 1966
}

1967 1968 1969 1970 1971 1972
struct CounterAndKey {
  Counter* counter;
  const char* key;
};


1973 1974
inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) {
  return strcmp(lhs.key, rhs.key) < 0;
1975
}
1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987

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(
1988
      isolate, JSON::Stringify(context, dispatch_counters).ToLocalChecked());
1989 1990
}

1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017
namespace {
int LineFromOffset(Local<debug::Script> script, int offset) {
  debug::Location location = script->GetSourceLocation(offset);
  return location.GetLineNumber();
}

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

void WriteLcovDataForNamedRange(std::ostream& sink,
                                std::vector<uint32_t>& lines, std::string name,
                                int start_line, int end_line, uint32_t count) {
  WriteLcovDataForRange(lines, start_line, end_line, count);
  sink << "FN:" << start_line + 1 << "," << name << std::endl;
  sink << "FNDA:" << count << "," << name << std::endl;
}
}  // namespace

2018 2019 2020 2021
// 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);
2022
  debug::Coverage coverage = debug::Coverage::CollectPrecise(isolate);
2023
  std::ofstream sink(file, std::ofstream::app);
2024
  for (size_t i = 0; i < coverage.ScriptCount(); i++) {
2025 2026
    debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
    Local<debug::Script> script = script_data.GetScript();
2027
    // Skip unnamed scripts.
2028 2029
    Local<String> name;
    if (!script->Name().ToLocal(&name)) continue;
2030
    std::string file_name = ToSTLString(isolate, name);
2031 2032 2033 2034 2035
    // 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;
2036 2037 2038
    for (size_t j = 0; j < script_data.FunctionCount(); j++) {
      debug::Coverage::FunctionData function_data =
          script_data.GetFunctionData(j);
2039

2040
      // Write function stats.
2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052
      {
        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)) {
2053
          name_stream << ToSTLString(isolate, name);
2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066
        } else {
          name_stream << "<" << start_line + 1 << "-";
          name_stream << start.GetColumnNumber() << ">";
        }

        WriteLcovDataForNamedRange(sink, lines, name_stream.str(), start_line,
                                   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());
2067
        int end_line = LineFromOffset(script, block_data.EndOffset() - 1);
2068 2069
        uint32_t count = block_data.Count();
        WriteLcovDataForRange(lines, start_line, end_line, count);
2070 2071
      }
    }
2072 2073 2074 2075 2076 2077 2078
    // 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;
  }
}
2079

2080
void Shell::OnExit(v8::Isolate* isolate) {
2081 2082 2083 2084 2085 2086 2087 2088
  // Dump basic block profiling data.
  if (i::BasicBlockProfiler* profiler =
          reinterpret_cast<i::Isolate*>(isolate)->basic_block_profiler()) {
    i::OFStream os(stdout);
    os << *profiler;
  }
  isolate->Dispose();

2089
  if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
2090
    int number_of_counters = 0;
2091
    for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) {
2092 2093 2094 2095 2096 2097 2098 2099
      number_of_counters++;
    }
    CounterAndKey* counters = new CounterAndKey[number_of_counters];
    int j = 0;
    for (CounterMap::Iterator i(counter_map_); i.More(); i.Next(), j++) {
      counters[j].counter = i.CurrentValue();
      counters[j].key = i.CurrentKey();
    }
2100
    std::sort(counters, counters + number_of_counters);
2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133

    if (i::FLAG_dump_counters_nvp) {
      // Dump counters as name-value pairs.
      for (j = 0; j < number_of_counters; j++) {
        Counter* counter = counters[j].counter;
        const char* key = counters[j].key;
        if (counter->is_histogram()) {
          printf("\"c:%s\"=%i\n", key, counter->count());
          printf("\"t:%s\"=%i\n", key, counter->sample_total());
        } else {
          printf("\"%s\"=%i\n", key, counter->count());
        }
      }
    } else {
      // Dump counters in formatted boxes.
      printf(
          "+----------------------------------------------------------------+"
          "-------------+\n");
      printf(
          "| Name                                                           |"
          " Value       |\n");
      printf(
          "+----------------------------------------------------------------+"
          "-------------+\n");
      for (j = 0; j < number_of_counters; j++) {
        Counter* counter = counters[j].counter;
        const char* key = counters[j].key;
        if (counter->is_histogram()) {
          printf("| c:%-60s | %11i |\n", key, counter->count());
          printf("| t:%-60s | %11i |\n", key, counter->sample_total());
        } else {
          printf("| %-62s | %11i |\n", key, counter->count());
        }
2134
      }
2135 2136 2137
      printf(
          "+----------------------------------------------------------------+"
          "-------------+\n");
2138
    }
2139
    delete [] counters;
2140
  }
2141

2142 2143
  delete counters_file_;
  delete counter_map_;
2144 2145
}

2146 2147

static FILE* FOpen(const char* path, const char* mode) {
2148
#if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
2149 2150
  FILE* result;
  if (fopen_s(&result, path, mode) == 0) {
2151
    return result;
2152
  } else {
2153
    return nullptr;
2154 2155 2156
  }
#else
  FILE* file = fopen(path, mode);
2157
  if (file == nullptr) return nullptr;
2158
  struct stat file_stat;
2159
  if (fstat(fileno(file), &file_stat) != 0) return nullptr;
2160 2161 2162
  bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
  if (is_regular_file) return file;
  fclose(file);
2163
  return nullptr;
2164 2165
#endif
}
2166

2167
static char* ReadChars(const char* name, int* size_out) {
2168 2169 2170 2171
  if (Shell::options.read_from_tcp_port >= 0) {
    return Shell::ReadCharsFromTcpPort(name, size_out);
  }

2172
  FILE* file = FOpen(name, "rb");
2173
  if (file == nullptr) return nullptr;
2174 2175

  fseek(file, 0, SEEK_END);
2176
  size_t size = ftell(file);
2177 2178 2179 2180
  rewind(file);

  char* chars = new char[size + 1];
  chars[size] = '\0';
2181 2182 2183 2184 2185 2186 2187
  for (size_t i = 0; i < size;) {
    i += fread(&chars[i], 1, size - i, file);
    if (ferror(file)) {
      fclose(file);
      delete[] chars;
      return nullptr;
    }
2188 2189
  }
  fclose(file);
2190
  *size_out = static_cast<int>(size);
2191 2192 2193
  return chars;
}

2194 2195 2196

struct DataAndPersistent {
  uint8_t* data;
2197 2198
  int byte_length;
  Global<ArrayBuffer> handle;
2199 2200 2201 2202
};


static void ReadBufferWeakCallback(
2203 2204
    const v8::WeakCallbackInfo<DataAndPersistent>& data) {
  int byte_length = data.GetParameter()->byte_length;
2205
  data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
2206 2207
      -static_cast<intptr_t>(byte_length));

2208 2209 2210
  delete[] data.GetParameter()->data;
  data.GetParameter()->handle.Reset();
  delete data.GetParameter();
2211
}
2212

2213

2214
void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) {
2215 2216
  static_assert(sizeof(char) == sizeof(uint8_t),
                "char and uint8_t should both have 1 byte");
2217
  Isolate* isolate = args.GetIsolate();
2218 2219
  String::Utf8Value filename(isolate, args[0]);
  int length;
2220
  if (*filename == nullptr) {
2221
    Throw(isolate, "Error loading file");
2222
    return;
2223
  }
2224

2225
  DataAndPersistent* data = new DataAndPersistent;
2226
  data->data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
2227
  if (data->data == nullptr) {
2228
    delete data;
2229
    Throw(isolate, "Error reading file");
2230
    return;
2231
  }
2232
  data->byte_length = length;
2233
  Local<v8::ArrayBuffer> buffer = ArrayBuffer::New(isolate, data->data, length);
2234
  data->handle.Reset(isolate, buffer);
2235 2236
  data->handle.SetWeak(data, ReadBufferWeakCallback,
                       v8::WeakCallbackType::kParameter);
2237 2238
  isolate->AdjustAmountOfExternalAllocatedMemory(length);

2239
  args.GetReturnValue().Set(buffer);
2240 2241
}

2242
// Reads a file into a v8 string.
2243
Local<String> Shell::ReadFile(Isolate* isolate, const char* name) {
2244
  int size = 0;
2245
  char* chars = ReadChars(name, &size);
2246
  if (chars == nullptr) return Local<String>();
2247
  Local<String> result;
2248
  if (i::FLAG_use_external_strings && i::String::IsAscii(chars, size)) {
2249 2250 2251 2252 2253 2254 2255 2256 2257
    String::ExternalOneByteStringResource* resource =
        new ExternalOwningOneByteStringResource(
            std::unique_ptr<const char[]>(chars), size);
    result = String::NewExternalOneByte(isolate, resource).ToLocalChecked();
  } else {
    result = String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
                 .ToLocalChecked();
    delete[] chars;
  }
2258 2259 2260 2261
  return result;
}


2262
void Shell::RunShell(Isolate* isolate) {
2263
  HandleScope outer_scope(isolate);
2264 2265 2266
  v8::Local<v8::Context> context =
      v8::Local<v8::Context>::New(isolate, evaluation_context_);
  v8::Context::Scope context_scope(context);
2267
  PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2268 2269 2270
  Local<String> name =
      String::NewFromUtf8(isolate, "(d8)", NewStringType::kNormal)
          .ToLocalChecked();
2271
  printf("V8 version %s\n", V8::GetVersion());
2272
  while (true) {
2273
    HandleScope inner_scope(isolate);
2274
    printf("d8> ");
2275
    Local<String> input = Shell::ReadFromStdin(isolate);
2276
    if (input.IsEmpty()) break;
2277 2278
    ExecuteString(isolate, input, name, kPrintResult, kReportExceptions,
                  kProcessMessageQueue);
2279 2280
  }
  printf("\n");
2281 2282 2283
  // We need to explicitly clean up the module embedder data for
  // the interative shell context.
  DisposeModuleEmbedderData(context);
2284 2285
}

2286 2287 2288 2289 2290 2291 2292 2293 2294
class InspectorFrontend final : public v8_inspector::V8Inspector::Channel {
 public:
  explicit InspectorFrontend(Local<Context> context) {
    isolate_ = context->GetIsolate();
    context_.Reset(isolate_, context);
  }
  virtual ~InspectorFrontend() = default;

 private:
2295 2296 2297 2298 2299 2300 2301 2302
  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());
2303 2304 2305 2306
  }
  void flushProtocolNotifications() override {}

  void Send(const v8_inspector::StringView& string) {
2307
    v8::Isolate::AllowJavascriptExecutionScope allow_script(isolate_);
2308
    int length = static_cast<int>(string.length());
2309
    DCHECK_LT(length, v8::String::kMaxLength);
2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329
    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();
    Local<String> callback_name =
        v8::String::NewFromUtf8(isolate_, "receive", v8::NewStringType::kNormal)
            .ToLocalChecked();
    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};
2330 2331
      USE(Local<Function>::Cast(callback)->Call(context, Undefined(isolate_), 1,
                                                args));
2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342
#ifdef DEBUG
      if (try_catch.HasCaught()) {
        Local<Object> exception = Local<Object>::Cast(try_catch.Exception());
        Local<String> key = v8::String::NewFromUtf8(isolate_, "message",
                                                    v8::NewStringType::kNormal)
                                .ToLocalChecked();
        Local<String> expected =
            v8::String::NewFromUtf8(isolate_,
                                    "Maximum call stack size exceeded",
                                    v8::NewStringType::kNormal)
                .ToLocalChecked();
2343
        Local<Value> value = exception->Get(context, key).ToLocalChecked();
2344
        DCHECK(value->StrictEquals(expected));
2345 2346
      }
#endif
2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375
    }
  }

  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();
    Local<String> function_name =
        String::NewFromUtf8(isolate_, "send", NewStringType::kNormal)
            .ToLocalChecked();
    CHECK(context->Global()->Set(context, function_name, function).FromJust());

2376 2377
    v8::debug::SetLiveEditEnabled(isolate_, true);

2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403
    context_.Reset(isolate_, context);
  }

 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();
2404
    std::unique_ptr<uint16_t[]> buffer(new uint16_t[length]);
2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418
    message->Write(buffer.get(), 0, length);
    v8_inspector::StringView message_view(buffer.get(), length);
    session->dispatchProtocolMessage(message_view);
    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_;
  Global<Context> context_;
  Isolate* isolate_;
};
2419

2420 2421
SourceGroup::~SourceGroup() {
  delete thread_;
2422
  thread_ = nullptr;
2423 2424 2425
}


2426
void SourceGroup::Execute(Isolate* isolate) {
2427
  bool exception_was_thrown = false;
2428 2429 2430 2431
  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.
2432
      HandleScope handle_scope(isolate);
2433 2434 2435 2436 2437 2438
      Local<String> file_name =
          String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
              .ToLocalChecked();
      Local<String> source =
          String::NewFromUtf8(isolate, argv_[i + 1], NewStringType::kNormal)
              .ToLocalChecked();
2439
      Shell::options.script_executed = true;
2440 2441 2442
      if (!Shell::ExecuteString(isolate, source, file_name,
                                Shell::kNoPrintResult, Shell::kReportExceptions,
                                Shell::kNoProcessMessageQueue)) {
2443 2444
        exception_was_thrown = true;
        break;
2445 2446
      }
      ++i;
2447 2448 2449 2450
      continue;
    } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) {
      // Treat the next file as a module.
      arg = argv_[++i];
2451 2452 2453 2454 2455 2456
      Shell::options.script_executed = true;
      if (!Shell::ExecuteModule(isolate, arg)) {
        exception_was_thrown = true;
        break;
      }
      continue;
2457 2458
    } else if (arg[0] == '-') {
      // Ignore other options. They have been parsed already.
2459 2460 2461 2462 2463
      continue;
    }

    // Use all other arguments as names of files to load and run.
    HandleScope handle_scope(isolate);
2464 2465 2466 2467
    Local<String> file_name =
        String::NewFromUtf8(isolate, arg, NewStringType::kNormal)
            .ToLocalChecked();
    Local<String> source = ReadFile(isolate, arg);
2468 2469
    if (source.IsEmpty()) {
      printf("Error reading '%s'\n", arg);
2470
      base::OS::ExitProcess(1);
2471
    }
2472
    Shell::options.script_executed = true;
2473 2474 2475
    if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
                              Shell::kReportExceptions,
                              Shell::kProcessMessageQueue)) {
2476 2477
      exception_was_thrown = true;
      break;
2478
    }
2479
  }
2480
  if (exception_was_thrown != Shell::options.expected_to_throw) {
2481
    base::OS::ExitProcess(1);
2482
  }
2483
}
2484

2485
Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
2486
  return Shell::ReadFile(isolate, name);
2487 2488
}

2489 2490
SourceGroup::IsolateThread::IsolateThread(SourceGroup* group)
    : base::Thread(GetThreadOptions("IsolateThread")), group_(group) {}
2491 2492

void SourceGroup::ExecuteInThread() {
2493
  Isolate::CreateParams create_params;
2494
  create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2495
  Isolate* isolate = Isolate::New(create_params);
2496 2497
  isolate->SetHostImportModuleDynamicallyCallback(
      Shell::HostImportModuleDynamically);
2498 2499
  isolate->SetHostInitializeImportMetaObjectCallback(
      Shell::HostInitializeImportMetaObject);
2500
  Shell::SetWaitUntilDone(isolate, false);
2501 2502
  D8Console console(isolate);
  debug::SetConsoleDelegate(isolate, &console);
binji's avatar
binji committed
2503
  for (int i = 0; i < Shell::options.stress_runs; ++i) {
2504
    next_semaphore_.Wait();
2505 2506
    {
      Isolate::Scope iscope(isolate);
2507
      PerIsolateData data(isolate);
2508
      {
2509 2510 2511 2512
        HandleScope scope(isolate);
        Local<Context> context = Shell::CreateEvaluationContext(isolate);
        {
          Context::Scope cscope(context);
2513 2514
          InspectorClient inspector_client(context,
                                           Shell::options.enable_inspector);
2515 2516
          PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
          Execute(isolate);
2517
          Shell::CompleteMessageLoop(isolate);
2518
        }
2519
        DisposeModuleEmbedderData(context);
2520
      }
2521
      Shell::CollectGarbage(isolate);
2522
    }
2523
    done_semaphore_.Signal();
binji's avatar
binji committed
2524
  }
2525

2526 2527 2528 2529 2530
  isolate->Dispose();
}


void SourceGroup::StartExecuteInThread() {
2531
  if (thread_ == nullptr) {
2532
    thread_ = new IsolateThread(this);
2533
    thread_->Start();
2534
  }
2535
  next_semaphore_.Signal();
2536 2537
}

2538

2539
void SourceGroup::WaitForThread() {
2540
  if (thread_ == nullptr) return;
binji's avatar
binji committed
2541 2542 2543 2544 2545
  done_semaphore_.Wait();
}


void SourceGroup::JoinThread() {
2546
  if (thread_ == nullptr) return;
binji's avatar
binji committed
2547
  thread_->Join();
2548
}
2549

2550
ExternalizedContents::~ExternalizedContents() {
2551
  if (base_ != nullptr) {
2552 2553 2554 2555 2556
    if (mode_ == ArrayBuffer::Allocator::AllocationMode::kReservation) {
      CHECK(i::FreePages(base_, length_));
    } else {
      Shell::array_buffer_allocator->Free(base_, length_);
    }
2557
  }
2558 2559
}

2560
void SerializationDataQueue::Enqueue(std::unique_ptr<SerializationData> data) {
2561
  base::LockGuard<base::Mutex> lock_guard(&mutex_);
2562
  data_.push_back(std::move(data));
2563 2564
}

2565 2566 2567
bool SerializationDataQueue::Dequeue(
    std::unique_ptr<SerializationData>* out_data) {
  out_data->reset();
2568
  base::LockGuard<base::Mutex> lock_guard(&mutex_);
2569 2570 2571
  if (data_.empty()) return false;
  *out_data = std::move(data_[0]);
  data_.erase(data_.begin());
2572 2573 2574 2575 2576 2577
  return true;
}


bool SerializationDataQueue::IsEmpty() {
  base::LockGuard<base::Mutex> lock_guard(&mutex_);
2578
  return data_.empty();
2579 2580 2581 2582 2583
}


void SerializationDataQueue::Clear() {
  base::LockGuard<base::Mutex> lock_guard(&mutex_);
2584
  data_.clear();
2585 2586 2587
}

Worker::Worker()
2588 2589
    : in_semaphore_(0),
      out_semaphore_(0),
2590 2591
      thread_(nullptr),
      script_(nullptr),
2592
      running_(false) {}
2593

2594 2595
Worker::~Worker() {
  delete thread_;
2596
  thread_ = nullptr;
2597
  delete[] script_;
2598
  script_ = nullptr;
2599 2600 2601
  in_queue_.Clear();
  out_queue_.Clear();
}
2602 2603


2604 2605 2606 2607 2608
void Worker::StartExecuteInThread(const char* script) {
  running_ = true;
  script_ = i::StrDup(script);
  thread_ = new WorkerThread(this);
  thread_->Start();
2609 2610
}

2611 2612
void Worker::PostMessage(std::unique_ptr<SerializationData> data) {
  in_queue_.Enqueue(std::move(data));
2613 2614 2615
  in_semaphore_.Signal();
}

2616 2617 2618
std::unique_ptr<SerializationData> Worker::GetMessage() {
  std::unique_ptr<SerializationData> result;
  while (!out_queue_.Dequeue(&result)) {
binji's avatar
binji committed
2619 2620
    // If the worker is no longer running, and there are no messages in the
    // queue, don't expect any more messages from it.
2621
    if (!base::Relaxed_Load(&running_)) break;
2622 2623
    out_semaphore_.Wait();
  }
2624
  return result;
2625 2626 2627 2628
}


void Worker::Terminate() {
2629
  base::Relaxed_Store(&running_, false);
2630
  // Post nullptr to wake the Worker thread message loop, and tell it to stop
2631
  // running.
2632
  PostMessage(nullptr);
binji's avatar
binji committed
2633 2634 2635 2636 2637
}


void Worker::WaitForThread() {
  Terminate();
2638
  thread_->Join();
2639 2640 2641 2642 2643 2644 2645
}


void Worker::ExecuteInThread() {
  Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = Shell::array_buffer_allocator;
  Isolate* isolate = Isolate::New(create_params);
2646 2647
  isolate->SetHostImportModuleDynamicallyCallback(
      Shell::HostImportModuleDynamically);
2648 2649
  isolate->SetHostInitializeImportMetaObjectCallback(
      Shell::HostInitializeImportMetaObject);
2650 2651
  D8Console console(isolate);
  debug::SetConsoleDelegate(isolate, &console);
2652 2653 2654 2655 2656 2657 2658 2659 2660 2661
  {
    Isolate::Scope iscope(isolate);
    {
      HandleScope scope(isolate);
      PerIsolateData data(isolate);
      Local<Context> context = Shell::CreateEvaluationContext(isolate);
      {
        Context::Scope cscope(context);
        PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));

2662 2663 2664
        Local<Object> global = context->Global();
        Local<Value> this_value = External::New(isolate, this);
        Local<FunctionTemplate> postmessage_fun_template =
2665 2666
            FunctionTemplate::New(isolate, PostMessageOut, this_value);

2667
        Local<Function> postmessage_fun;
2668 2669
        if (postmessage_fun_template->GetFunction(context)
                .ToLocal(&postmessage_fun)) {
2670 2671 2672 2673
          global->Set(context, String::NewFromUtf8(isolate, "postMessage",
                                                   NewStringType::kNormal)
                                   .ToLocalChecked(),
                      postmessage_fun).FromJust();
2674 2675 2676
        }

        // First run the script
2677 2678 2679 2680 2681 2682
        Local<String> file_name =
            String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
                .ToLocalChecked();
        Local<String> source =
            String::NewFromUtf8(isolate, script_, NewStringType::kNormal)
                .ToLocalChecked();
2683 2684 2685
        if (Shell::ExecuteString(
                isolate, source, file_name, Shell::kNoPrintResult,
                Shell::kReportExceptions, Shell::kProcessMessageQueue)) {
2686
          // Get the message handler
2687 2688 2689 2690
          Local<Value> onmessage =
              global->Get(context, String::NewFromUtf8(isolate, "onmessage",
                                                       NewStringType::kNormal)
                                       .ToLocalChecked()).ToLocalChecked();
2691
          if (onmessage->IsFunction()) {
2692
            Local<Function> onmessage_fun = Local<Function>::Cast(onmessage);
2693
            // Now wait for messages
binji's avatar
binji committed
2694
            while (true) {
2695
              in_semaphore_.Wait();
2696
              std::unique_ptr<SerializationData> data;
2697
              if (!in_queue_.Dequeue(&data)) continue;
2698
              if (!data) {
2699 2700
                break;
              }
2701 2702 2703 2704 2705
              v8::TryCatch try_catch(isolate);
              Local<Value> value;
              if (Shell::DeserializeValue(isolate, std::move(data))
                      .ToLocal(&value)) {
                Local<Value> argv[] = {value};
2706 2707
                (void)onmessage_fun->Call(context, global, 1, argv);
              }
2708 2709 2710
              if (try_catch.HasCaught()) {
                Shell::ReportException(isolate, &try_catch);
              }
2711 2712 2713 2714
            }
          }
        }
      }
2715
      DisposeModuleEmbedderData(context);
2716
    }
2717
    Shell::CollectGarbage(isolate);
2718 2719
  }
  isolate->Dispose();
2720

2721 2722
  // Post nullptr to wake the thread waiting on GetMessage() if there is one.
  out_queue_.Enqueue(nullptr);
binji's avatar
binji committed
2723
  out_semaphore_.Signal();
2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735
}


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;
  }

2736
  Local<Value> message = args[0];
2737 2738 2739 2740
  Local<Value> transfer = Undefined(isolate);
  std::unique_ptr<SerializationData> data =
      Shell::SerializeValue(isolate, message, transfer);
  if (data) {
2741
    DCHECK(args.Data()->IsExternal());
2742
    Local<External> this_value = Local<External>::Cast(args.Data());
2743
    Worker* worker = static_cast<Worker*>(this_value->Value());
2744
    worker->out_queue_.Enqueue(std::move(data));
2745 2746 2747
    worker->out_semaphore_.Signal();
  }
}
2748 2749


2750
void SetFlagsFromString(const char* flags) {
2751
  v8::V8::SetFlagsFromString(flags, static_cast<int>(strlen(flags)));
2752 2753 2754
}


2755
bool Shell::SetOptions(int argc, char* argv[]) {
2756
  bool logfile_per_isolate = false;
2757 2758
  for (int i = 0; i < argc; i++) {
    if (strcmp(argv[i], "--stress-opt") == 0) {
2759
      options.stress_opt = true;
2760
      argv[i] = nullptr;
2761 2762
    } else if (strcmp(argv[i], "--nostress-opt") == 0 ||
               strcmp(argv[i], "--no-stress-opt") == 0) {
2763
      options.stress_opt = false;
2764
      argv[i] = nullptr;
2765
    } else if (strcmp(argv[i], "--stress-deopt") == 0) {
2766
      options.stress_deopt = true;
2767
      argv[i] = nullptr;
2768 2769 2770 2771 2772 2773 2774
    } else if (strcmp(argv[i], "--stress-background-compile") == 0) {
      options.stress_background_compile = true;
      argv[i] = nullptr;
    } else if (strcmp(argv[i], "--nostress-background-compile") == 0 ||
               strcmp(argv[i], "--no-stress-background-compile") == 0) {
      options.stress_background_compile = false;
      argv[i] = nullptr;
2775 2776
    } else if (strcmp(argv[i], "--mock-arraybuffer-allocator") == 0) {
      options.mock_arraybuffer_allocator = true;
2777
      argv[i] = nullptr;
2778 2779
    } else if (strcmp(argv[i], "--noalways-opt") == 0 ||
               strcmp(argv[i], "--no-always-opt") == 0) {
2780
      // No support for stressing if we can't use --always-opt.
2781 2782
      options.stress_opt = false;
      options.stress_deopt = false;
2783 2784
    } else if (strcmp(argv[i], "--logfile-per-isolate") == 0) {
      logfile_per_isolate = true;
2785
      argv[i] = nullptr;
2786
    } else if (strcmp(argv[i], "--shell") == 0) {
2787
      options.interactive_shell = true;
2788
      argv[i] = nullptr;
2789
    } else if (strcmp(argv[i], "--test") == 0) {
2790
      options.test_shell = true;
2791
      argv[i] = nullptr;
2792 2793 2794
    } else if (strcmp(argv[i], "--notest") == 0 ||
               strcmp(argv[i], "--no-test") == 0) {
      options.test_shell = false;
2795
      argv[i] = nullptr;
2796 2797
    } else if (strcmp(argv[i], "--send-idle-notification") == 0) {
      options.send_idle_notification = true;
2798
      argv[i] = nullptr;
2799 2800 2801 2802
    } else if (strcmp(argv[i], "--invoke-weak-callbacks") == 0) {
      options.invoke_weak_callbacks = true;
      // TODO(jochen) See issue 3351
      options.send_idle_notification = true;
2803
      argv[i] = nullptr;
2804 2805
    } else if (strcmp(argv[i], "--omit-quit") == 0) {
      options.omit_quit = true;
2806
      argv[i] = nullptr;
2807 2808 2809 2810 2811
    } else if (strcmp(argv[i], "--no-wait-for-wasm") == 0) {
      // TODO(herhut) Remove this flag once wasm compilation is fully
      // isolate-independent.
      options.wait_for_wasm = false;
      argv[i] = nullptr;
2812 2813 2814 2815 2816 2817
    } else if (strcmp(argv[i], "-f") == 0) {
      // Ignore any -f flags for compatibility with other stand-alone
      // JavaScript engines.
      continue;
    } else if (strcmp(argv[i], "--isolate") == 0) {
      options.num_isolates++;
2818 2819
    } else if (strcmp(argv[i], "--throws") == 0) {
      options.expected_to_throw = true;
2820
      argv[i] = nullptr;
2821 2822
    } else if (strncmp(argv[i], "--icu-data-file=", 16) == 0) {
      options.icu_data_file = argv[i] + 16;
2823
      argv[i] = nullptr;
2824 2825 2826
#ifdef V8_USE_EXTERNAL_STARTUP_DATA
    } else if (strncmp(argv[i], "--natives_blob=", 15) == 0) {
      options.natives_blob = argv[i] + 15;
2827
      argv[i] = nullptr;
2828 2829
    } else if (strncmp(argv[i], "--snapshot_blob=", 16) == 0) {
      options.snapshot_blob = argv[i] + 16;
2830
      argv[i] = nullptr;
2831
#endif  // V8_USE_EXTERNAL_STARTUP_DATA
2832 2833 2834 2835
    } 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) {
2836 2837 2838
        options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
        options.code_cache_options =
            ShellOptions::CodeCacheOptions::kProduceCache;
2839 2840
      } else if (strncmp(value, "=none", 6) == 0) {
        options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2841 2842 2843 2844 2845 2846 2847 2848 2849 2850
        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;
2851 2852 2853 2854
      } else {
        printf("Unknown option to --cache.\n");
        return false;
      }
2855
      argv[i] = nullptr;
2856 2857
    } else if (strcmp(argv[i], "--enable-tracing") == 0) {
      options.trace_enabled = true;
2858
      argv[i] = nullptr;
2859 2860 2861
    } else if (strncmp(argv[i], "--trace-path=", 13) == 0) {
      options.trace_path = argv[i] + 13;
      argv[i] = nullptr;
2862 2863
    } else if (strncmp(argv[i], "--trace-config=", 15) == 0) {
      options.trace_config = argv[i] + 15;
2864
      argv[i] = nullptr;
2865 2866
    } else if (strcmp(argv[i], "--enable-inspector") == 0) {
      options.enable_inspector = true;
2867
      argv[i] = nullptr;
2868 2869
    } else if (strncmp(argv[i], "--lcov=", 7) == 0) {
      options.lcov_file = argv[i] + 7;
2870
      argv[i] = nullptr;
2871 2872
    } else if (strcmp(argv[i], "--disable-in-process-stack-traces") == 0) {
      options.disable_in_process_stack_traces = true;
2873
      argv[i] = nullptr;
2874 2875 2876
#ifdef V8_OS_POSIX
    } else if (strncmp(argv[i], "--read-from-tcp-port=", 21) == 0) {
      options.read_from_tcp_port = atoi(argv[i] + 21);
2877
      argv[i] = nullptr;
2878
#endif  // V8_OS_POSIX
2879 2880
    } else if (strcmp(argv[i], "--enable-os-system") == 0) {
      options.enable_os_system = true;
2881
      argv[i] = nullptr;
2882 2883 2884
    } else if (strcmp(argv[i], "--quiet-load") == 0) {
      options.quiet_load = true;
      argv[i] = nullptr;
2885 2886 2887
    } else if (strncmp(argv[i], "--thread-pool-size=", 19) == 0) {
      options.thread_pool_size = atoi(argv[i] + 19);
      argv[i] = nullptr;
2888
    }
2889 2890
  }

2891 2892
  v8::V8::SetFlagsFromCommandLine(&argc, argv, true);

2893
  // Set up isolated source groups.
2894 2895 2896 2897 2898 2899 2900 2901 2902
  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);
2903 2904
    } else if (strcmp(str, "--module") == 0) {
      // Pass on to SourceGroup, which understands this option.
2905 2906
    } else if (strncmp(argv[i], "--", 2) == 0) {
      printf("Warning: unknown flag %s.\nTry --help for options\n", argv[i]);
binji's avatar
binji committed
2907 2908 2909 2910 2911
    } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
      options.script_executed = true;
    } else if (strncmp(str, "-", 1) != 0) {
      // Not a flag, so it must be a script to execute.
      options.script_executed = true;
2912 2913 2914 2915
    }
  }
  current->End(argc);

2916 2917 2918 2919
  if (!logfile_per_isolate && options.num_isolates) {
    SetFlagsFromString("--nologfile_per_isolate");
  }

2920 2921 2922 2923
  return true;
}


binji's avatar
binji committed
2924
int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
2925 2926 2927
  for (int i = 1; i < options.num_isolates; ++i) {
    options.isolate_sources[i].StartExecuteInThread();
  }
2928
  {
2929
    SetWaitUntilDone(isolate, false);
2930
    if (options.lcov_file) {
2931
      debug::Coverage::SelectMode(isolate, debug::Coverage::kBlockCount);
2932
    }
2933 2934
    HandleScope scope(isolate);
    Local<Context> context = CreateEvaluationContext(isolate);
2935 2936
    bool use_existing_context = last_run && options.use_interactive_shell();
    if (use_existing_context) {
2937 2938
      // Keep using the same context in the interactive shell.
      evaluation_context_.Reset(isolate, context);
2939
    }
2940 2941
    {
      Context::Scope cscope(context);
2942
      InspectorClient inspector_client(context, options.enable_inspector);
2943 2944
      PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
      options.isolate_sources[0].Execute(isolate);
2945
      CompleteMessageLoop(isolate);
2946
    }
2947 2948 2949
    if (!use_existing_context) {
      DisposeModuleEmbedderData(context);
    }
2950
    WriteLcovData(isolate, options.lcov_file);
2951
  }
2952 2953
  CollectGarbage(isolate);
  for (int i = 1; i < options.num_isolates; ++i) {
binji's avatar
binji committed
2954 2955 2956 2957 2958
    if (last_run) {
      options.isolate_sources[i].JoinThread();
    } else {
      options.isolate_sources[i].WaitForThread();
    }
2959
  }
2960
  CleanupWorkers();
2961 2962 2963 2964 2965
  return 0;
}


void Shell::CollectGarbage(Isolate* isolate) {
2966
  if (options.send_idle_notification) {
2967
    const double kLongIdlePauseInSeconds = 1.0;
2968
    isolate->ContextDisposedNotification();
2969 2970
    isolate->IdleNotificationDeadline(
        g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
2971
  }
2972 2973 2974 2975
  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.
2976
    isolate->LowMemoryNotification();
2977
  }
2978 2979
}

2980 2981 2982 2983 2984 2985 2986 2987 2988
void Shell::SetWaitUntilDone(Isolate* isolate, bool value) {
  base::LockGuard<base::Mutex> guard(isolate_status_lock_.Pointer());
  if (isolate_status_.count(isolate) == 0) {
    isolate_status_.insert(std::make_pair(isolate, value));
  } else {
    isolate_status_[isolate] = value;
  }
}

2989
namespace {
2990
bool ProcessMessages(Isolate* isolate,
2991
                     std::function<platform::MessageLoopBehavior()> behavior) {
2992
  Platform* platform = GetDefaultPlatform();
2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011
  while (true) {
    while (v8::platform::PumpMessageLoop(platform, isolate, behavior())) {
      isolate->RunMicrotasks();
    }
    if (platform->IdleTasksEnabled(isolate)) {
      v8::platform::RunIdleTasks(platform, isolate,
                                 50.0 / base::Time::kMillisecondsPerSecond);
    }
    HandleScope handle_scope(isolate);
    PerIsolateData* data = PerIsolateData::Get(isolate);
    Local<Function> callback;
    if (!data->GetTimeoutCallback().ToLocal(&callback)) break;
    Local<Context> context;
    if (!data->GetTimeoutContext().ToLocal(&context)) break;
    TryCatch try_catch(isolate);
    try_catch.SetVerbose(true);
    Context::Scope context_scope(context);
    if (callback->Call(context, Undefined(isolate), 0, nullptr).IsEmpty()) {
      Shell::ReportException(isolate, &try_catch);
3012
      return false;
3013 3014
    }
  }
3015
  return true;
3016
}
3017
}  // anonymous namespace
3018 3019

void Shell::CompleteMessageLoop(Isolate* isolate) {
3020
  auto get_waiting_behaviour = [isolate]() {
3021 3022
    base::LockGuard<base::Mutex> guard(isolate_status_lock_.Pointer());
    DCHECK_GT(isolate_status_.count(isolate), 0);
3023 3024 3025
    i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
    i::wasm::CompilationManager* wasm_compilation_manager =
        i_isolate->wasm_engine()->compilation_manager();
3026 3027
    bool should_wait = (options.wait_for_wasm &&
                        wasm_compilation_manager->HasRunningCompileJob()) ||
3028 3029 3030 3031 3032
                       isolate_status_[isolate];
    return should_wait ? platform::MessageLoopBehavior::kWaitForWork
                       : platform::MessageLoopBehavior::kDoNotWait;
  };
  ProcessMessages(isolate, get_waiting_behaviour);
3033 3034
}

3035
bool Shell::EmptyMessageQueues(Isolate* isolate) {
3036 3037
  return ProcessMessages(
      isolate, []() { return platform::MessageLoopBehavior::kDoNotWait; });
3038 3039
}

3040 3041 3042
class Serializer : public ValueSerializer::Delegate {
 public:
  explicit Serializer(Isolate* isolate)
3043 3044 3045
      : isolate_(isolate),
        serializer_(isolate, this),
        current_memory_usage_(0) {}
3046

3047 3048 3049 3050 3051 3052 3053
  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>();
3054
    }
3055 3056 3057 3058 3059
    serializer_.WriteHeader();

    if (!serializer_.WriteValue(context, value).To(&ok)) {
      data_.reset();
      return Nothing<bool>();
3060
    }
3061 3062 3063

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

3066 3067 3068 3069 3070
    std::pair<uint8_t*, size_t> pair = serializer_.Release();
    data_->data_.reset(pair.first);
    data_->size_ = pair.second;
    return Just(true);
  }
3071

3072 3073
  std::unique_ptr<SerializationData> Release() { return std::move(data_); }

3074 3075 3076 3077 3078 3079 3080
  void AppendExternalizedContentsTo(std::vector<ExternalizedContents>* to) {
    to->insert(to->end(),
               std::make_move_iterator(externalized_contents_.begin()),
               std::make_move_iterator(externalized_contents_.end()));
    externalized_contents_.clear();
  }

3081 3082 3083 3084 3085 3086 3087 3088
 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 {
3089
    DCHECK_NOT_NULL(data_);
3090 3091 3092 3093
    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));
      }
3094 3095
    }

3096 3097
    size_t index = shared_array_buffers_.size();
    shared_array_buffers_.emplace_back(isolate_, shared_array_buffer);
3098 3099
    data_->shared_array_buffer_contents_.push_back(
        MaybeExternalize(shared_array_buffer));
3100 3101 3102 3103 3104
    return Just<uint32_t>(static_cast<uint32_t>(index));
  }

  void* ReallocateBufferMemory(void* old_buffer, size_t size,
                               size_t* actual_size) override {
3105 3106 3107 3108 3109
    // 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;

3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126
    void* result = realloc(old_buffer, size);
    *actual_size = result ? size : 0;
    return result;
  }

  void FreeBufferMemory(void* buffer) override { free(buffer); }

 private:
  Maybe<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
    if (transfer->IsArray()) {
      Local<Array> transfer_array = Local<Array>::Cast(transfer);
      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");
3127
            return Nothing<bool>();
3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140
          }

          Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(element);
          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);
3141
    } else {
3142 3143
      Throw(isolate_, "Transfer list must be an Array or undefined");
      return Nothing<bool>();
3144
    }
3145 3146
  }

3147 3148 3149 3150 3151 3152
  template <typename T>
  typename T::Contents MaybeExternalize(Local<T> array_buffer) {
    if (array_buffer->IsExternal()) {
      return array_buffer->GetContents();
    } else {
      typename T::Contents contents = array_buffer->Externalize();
3153
      externalized_contents_.emplace_back(contents);
3154 3155 3156 3157
      return contents;
    }
  }

3158 3159 3160 3161 3162 3163 3164 3165 3166
  Maybe<bool> FinalizeTransfer() {
    for (const auto& global_array_buffer : array_buffers_) {
      Local<ArrayBuffer> array_buffer =
          Local<ArrayBuffer>::New(isolate_, global_array_buffer);
      if (!array_buffer->IsNeuterable()) {
        Throw(isolate_, "ArrayBuffer could not be transferred");
        return Nothing<bool>();
      }

3167
      ArrayBuffer::Contents contents = MaybeExternalize(array_buffer);
3168 3169
      array_buffer->Neuter();
      data_->array_buffer_contents_.push_back(contents);
3170 3171
    }

3172
    return Just(true);
3173 3174
  }

3175 3176 3177 3178 3179
  Isolate* isolate_;
  ValueSerializer serializer_;
  std::unique_ptr<SerializationData> data_;
  std::vector<Global<ArrayBuffer>> array_buffers_;
  std::vector<Global<SharedArrayBuffer>> shared_array_buffers_;
3180
  std::vector<ExternalizedContents> externalized_contents_;
3181
  size_t current_memory_usage_;
3182

3183 3184
  DISALLOW_COPY_AND_ASSIGN(Serializer);
};
3185

3186 3187 3188 3189 3190 3191 3192 3193
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);
  }
3194

3195 3196 3197 3198
  MaybeLocal<Value> ReadValue(Local<Context> context) {
    bool read_header;
    if (!deserializer_.ReadHeader(context).To(&read_header)) {
      return MaybeLocal<Value>();
3199
    }
3200 3201 3202 3203 3204 3205

    uint32_t index = 0;
    for (const auto& contents : data_->array_buffer_contents()) {
      Local<ArrayBuffer> array_buffer =
          ArrayBuffer::New(isolate_, contents.Data(), contents.ByteLength());
      deserializer_.TransferArrayBuffer(index++, array_buffer);
3206
    }
3207

3208
    return deserializer_.ReadValue(context);
3209 3210
  }

3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222
  MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
      Isolate* isolate, uint32_t clone_id) override {
    DCHECK_NOT_NULL(data_);
    if (clone_id < data_->shared_array_buffer_contents().size()) {
      SharedArrayBuffer::Contents contents =
          data_->shared_array_buffer_contents().at(clone_id);
      return SharedArrayBuffer::New(isolate_, contents.Data(),
                                    contents.ByteLength());
    }
    return MaybeLocal<SharedArrayBuffer>();
  }

3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238
 private:
  Isolate* isolate_;
  ValueDeserializer deserializer_;
  std::unique_ptr<SerializationData> data_;

  DISALLOW_COPY_AND_ASSIGN(Deserializer);
};

std::unique_ptr<SerializationData> Shell::SerializeValue(
    Isolate* isolate, Local<Value> value, Local<Value> transfer) {
  bool ok;
  Local<Context> context = isolate->GetCurrentContext();
  Serializer serializer(isolate);
  if (serializer.WriteValue(context, value, transfer).To(&ok)) {
    std::unique_ptr<SerializationData> data = serializer.Release();
    base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
3239
    serializer.AppendExternalizedContentsTo(&externalized_contents_);
3240
    return data;
3241
  }
3242 3243
  // Append externalized contents even when WriteValue fails.
  serializer.AppendExternalizedContentsTo(&externalized_contents_);
3244 3245
  return nullptr;
}
3246

3247 3248 3249 3250 3251 3252
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);
3253 3254 3255 3256
}


void Shell::CleanupWorkers() {
3257 3258 3259
  // Make a copy of 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.
3260
  std::vector<Worker*> workers_copy;
3261
  {
3262
    base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
3263
    allow_new_workers_ = false;
3264
    workers_copy.swap(workers_);
3265 3266
  }

3267
  for (Worker* worker : workers_copy) {
binji's avatar
binji committed
3268
    worker->WaitForThread();
3269 3270
    delete worker;
  }
3271 3272

  // Now that all workers are terminated, we can re-enable Worker creation.
binji's avatar
binji committed
3273 3274
  base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
  allow_new_workers_ = true;
3275
  externalized_contents_.clear();
3276 3277
}

3278
int Shell::Main(int argc, char* argv[]) {
3279
  std::ofstream trace_file;
3280
  v8::base::EnsureConsoleOutput();
3281
  if (!SetOptions(argc, argv)) return 1;
3282
  v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);
3283 3284 3285 3286 3287 3288

  v8::platform::InProcessStackDumping in_process_stack_dumping =
      options.disable_in_process_stack_traces
          ? v8::platform::InProcessStackDumping::kDisabled
          : v8::platform::InProcessStackDumping::kEnabled;

3289
  std::unique_ptr<platform::tracing::TracingController> tracing;
3290
  if (options.trace_enabled && !i::FLAG_verify_predictable) {
3291
    tracing = base::make_unique<platform::tracing::TracingController>();
3292 3293

    trace_file.open(options.trace_path ? options.trace_path : "v8_trace.json");
3294 3295 3296
    platform::tracing::TraceBuffer* trace_buffer =
        platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
            platform::tracing::TraceBuffer::kRingBufferChunks,
3297
            platform::tracing::TraceWriter::CreateJSONTraceWriter(trace_file));
3298
    tracing->Initialize(trace_buffer);
3299 3300
  }

3301
  platform::tracing::TracingController* tracing_controller = tracing.get();
3302
  g_platform = v8::platform::NewDefaultPlatform(
3303 3304
      options.thread_pool_size, v8::platform::IdleTaskSupport::kEnabled,
      in_process_stack_dumping, std::move(tracing));
3305
  if (i::FLAG_verify_predictable) {
3306
    g_platform.reset(new PredictablePlatform(std::move(g_platform)));
3307 3308
  }

3309
  v8::V8::InitializePlatform(g_platform.get());
3310
  v8::V8::Initialize();
vogelheim's avatar
vogelheim committed
3311 3312 3313 3314 3315 3316
  if (options.natives_blob || options.snapshot_blob) {
    v8::V8::InitializeExternalStartupData(options.natives_blob,
                                          options.snapshot_blob);
  } else {
    v8::V8::InitializeExternalStartupData(argv[0]);
  }
3317
  SetFlagsFromString("--trace-turbo-cfg-file=turbo.cfg");
3318
  SetFlagsFromString("--redirect-code-traces-to=code.asm");
3319 3320
  int result = 0;
  Isolate::CreateParams create_params;
3321
  ShellArrayBufferAllocator shell_array_buffer_allocator;
3322 3323
  MockArrayBufferAllocator mock_arraybuffer_allocator;
  if (options.mock_arraybuffer_allocator) {
3324
    Shell::array_buffer_allocator = &mock_arraybuffer_allocator;
3325
  } else {
3326
    Shell::array_buffer_allocator = &shell_array_buffer_allocator;
3327
  }
3328
  create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3329
#ifdef ENABLE_VTUNE_JIT_INTERFACE
3330
  create_params.code_event_handler = vTune::GetVtuneCodeEventHandler();
3331
#endif
3332 3333
  create_params.constraints.ConfigureDefaults(
      base::SysInfo::AmountOfPhysicalMemory(),
3334
      base::SysInfo::AmountOfVirtualMemory());
3335 3336

  Shell::counter_map_ = new CounterMap();
3337
  if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp || i::FLAG_gc_stats) {
3338 3339 3340 3341
    create_params.counter_lookup_callback = LookupCounter;
    create_params.create_histogram_callback = CreateHistogram;
    create_params.add_histogram_sample_callback = AddHistogramSample;
  }
3342

3343 3344 3345
  if (V8_TRAP_HANDLER_SUPPORTED && i::FLAG_wasm_trap_handler) {
    constexpr bool use_default_signal_handler = true;
    if (!v8::V8::EnableWebAssemblyTrapHandler(use_default_signal_handler)) {
eholk's avatar
eholk committed
3346 3347 3348 3349 3350
      fprintf(stderr, "Could not register signal handler");
      exit(1);
    }
  }

3351
  Isolate* isolate = Isolate::New(create_params);
3352 3353
  isolate->SetHostImportModuleDynamicallyCallback(
      Shell::HostImportModuleDynamically);
3354 3355
  isolate->SetHostInitializeImportMetaObjectCallback(
      Shell::HostInitializeImportMetaObject);
3356

3357
  D8Console console(isolate);
3358
  {
3359
    Isolate::Scope scope(isolate);
3360
    Initialize(isolate);
3361
    PerIsolateData data(isolate);
3362
    debug::SetConsoleDelegate(isolate, &console);
3363

3364 3365
    if (options.trace_enabled) {
      platform::tracing::TraceConfig* trace_config;
3366 3367
      if (options.trace_config) {
        int size = 0;
3368
        char* trace_config_json_str = ReadChars(options.trace_config, &size);
3369 3370 3371 3372 3373 3374 3375
        trace_config =
            tracing::CreateTraceConfigFromJSON(isolate, trace_config_json_str);
        delete[] trace_config_json_str;
      } else {
        trace_config =
            platform::tracing::TraceConfig::CreateDefaultTraceConfig();
      }
3376
      tracing_controller->StartTracing(trace_config);
3377 3378
    }

3379 3380 3381 3382
    if (options.stress_opt || options.stress_deopt) {
      Testing::SetStressRunType(options.stress_opt
                                ? Testing::kStressTypeOpt
                                : Testing::kStressTypeDeopt);
binji's avatar
binji committed
3383 3384 3385 3386
      options.stress_runs = Testing::GetStressRuns();
      for (int i = 0; i < options.stress_runs && result == 0; i++) {
        printf("============ Stress %d/%d ============\n", i + 1,
               options.stress_runs);
3387
        Testing::PrepareStressRun(i);
binji's avatar
binji committed
3388 3389
        bool last_run = i == options.stress_runs - 1;
        result = RunMain(isolate, argc, argv, last_run);
3390 3391
      }
      printf("======== Full Deoptimization =======\n");
3392
      Testing::DeoptimizeAll(isolate);
3393
    } else if (i::FLAG_stress_runs > 0) {
binji's avatar
binji committed
3394 3395 3396 3397 3398 3399
      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,
               options.stress_runs);
        bool last_run = i == options.stress_runs - 1;
        result = RunMain(isolate, argc, argv, last_run);
3400
      }
3401 3402
    } else if (options.code_cache_options !=
               ShellOptions::CodeCacheOptions::kNoProduceCache) {
3403 3404 3405 3406 3407
      printf("============ Run: Produce code cache ============\n");
      // First run to produce the cache
      result = RunMain(isolate, argc, argv, false);

      // Change the options to consume cache
3408 3409 3410
      DCHECK(options.compile_options == v8::ScriptCompiler::kEagerCompile ||
             options.compile_options == v8::ScriptCompiler::kNoCompileOptions);
      options.compile_options = v8::ScriptCompiler::kConsumeCodeCache;
3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431

      printf("============ Run: Consume code cache ============\n");
      // Second run to consume the cache in new isolate
      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.
      isolate2->SetHostImportModuleDynamicallyCallback(
          Shell::HostImportModuleDynamically);
      isolate2->SetHostInitializeImportMetaObjectCallback(
          Shell::HostInitializeImportMetaObject);
      {
        D8Console console(isolate2);
        debug::SetConsoleDelegate(isolate2, &console);
        PerIsolateData data(isolate2);
        Isolate::Scope isolate_scope(isolate2);

        result = RunMain(isolate2, argc, argv, true);
      }
      isolate2->Dispose();
3432
    } else {
binji's avatar
binji committed
3433 3434
      bool last_run = true;
      result = RunMain(isolate, argc, argv, last_run);
3435
    }
3436

3437 3438
    // Run interactive shell if explicitly requested or if no script has been
    // executed, but never on --test
3439
    if (options.use_interactive_shell()) {
3440 3441
      RunShell(isolate);
    }
3442

3443
    if (i::FLAG_trace_ignition_dispatches &&
3444
        i::FLAG_trace_ignition_dispatches_output_file != nullptr) {
3445 3446 3447
      WriteIgnitionDispatchCountersFile(isolate);
    }

3448
    // Shut down contexts and collect garbage.
3449
    cached_code_map_.clear();
3450
    evaluation_context_.Reset();
3451
    stringify_function_.Reset();
3452
    CollectGarbage(isolate);
3453
  }
3454
  OnExit(isolate);
3455
  V8::Dispose();
3456
  V8::ShutdownPlatform();
3457

3458 3459 3460
  // Delete the platform explicitly here to write the tracing output to the
  // tracing file.
  g_platform.reset();
3461 3462 3463
  return result;
}

3464
}  // namespace v8
3465 3466


3467
#ifndef GOOGLE3
3468 3469 3470
int main(int argc, char* argv[]) {
  return v8::Shell::Main(argc, argv);
}
3471
#endif
3472 3473 3474

#undef CHECK
#undef DCHECK