• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /// Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <errno.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 
10 #include <algorithm>
11 #include <fstream>
12 #include <unordered_map>
13 #include <utility>
14 #include <vector>
15 
16 #ifdef ENABLE_VTUNE_JIT_INTERFACE
17 #include "src/third_party/vtune/v8-vtune.h"
18 #endif
19 
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-inl.h"
28 #include "src/base/cpu.h"
29 #include "src/base/logging.h"
30 #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"
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 #else
48 #include <windows.h>  // NOLINT
49 #endif               // !defined(_WIN32) && !defined(_WIN64)
50 
51 #ifndef DCHECK
52 #define DCHECK(condition) assert(condition)
53 #endif
54 
55 #ifndef CHECK
56 #define CHECK(condition) assert(condition)
57 #endif
58 
59 namespace v8 {
60 
61 namespace {
62 
63 const int kMB = 1024 * 1024;
64 
65 const int kMaxWorkers = 50;
66 const int kMaxSerializerMemoryUsage =
67     1 * kMB;  // Arbitrary maximum for testing.
68 
69 // Base class for shell ArrayBuffer allocators. It forwards all opertions to
70 // the default v8 allocator.
71 class ArrayBufferAllocatorBase : public v8::ArrayBuffer::Allocator {
72  public:
Allocate(size_t length)73   void* Allocate(size_t length) override {
74     return allocator_->Allocate(length);
75   }
76 
AllocateUninitialized(size_t length)77   void* AllocateUninitialized(size_t length) override {
78     return allocator_->AllocateUninitialized(length);
79   }
80 
Free(void * data,size_t length)81   void Free(void* data, size_t length) override {
82     allocator_->Free(data, length);
83   }
84 
85  private:
86   std::unique_ptr<Allocator> allocator_ =
87       std::unique_ptr<Allocator>(NewDefaultAllocator());
88 };
89 
90 // ArrayBuffer allocator that can use virtual memory to improve performance.
91 class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase {
92  public:
Allocate(size_t length)93   void* Allocate(size_t length) override {
94     if (length >= kVMThreshold) return AllocateVM(length);
95     return ArrayBufferAllocatorBase::Allocate(length);
96   }
97 
AllocateUninitialized(size_t length)98   void* AllocateUninitialized(size_t length) override {
99     if (length >= kVMThreshold) return AllocateVM(length);
100     return ArrayBufferAllocatorBase::AllocateUninitialized(length);
101   }
102 
Free(void * data,size_t length)103   void Free(void* data, size_t length) override {
104     if (length >= kVMThreshold) {
105       FreeVM(data, length);
106     } else {
107       ArrayBufferAllocatorBase::Free(data, length);
108     }
109   }
110 
111  private:
112   static constexpr size_t kVMThreshold = 65536;
113   static constexpr size_t kTwoGB = 2u * 1024u * 1024u * 1024u;
114 
AllocateVM(size_t length)115   void* AllocateVM(size_t length) {
116     DCHECK_LE(kVMThreshold, length);
117     // TODO(titzer): allocations should fail if >= 2gb because array buffers
118     // store their lengths as a SMI internally.
119     if (length >= kTwoGB) return nullptr;
120 
121     size_t page_size = i::AllocatePageSize();
122     size_t allocated = RoundUp(length, page_size);
123     // Rounding up could go over the limit.
124     if (allocated >= kTwoGB) return nullptr;
125     return i::AllocatePages(nullptr, allocated, page_size,
126                             PageAllocator::kReadWrite);
127   }
128 
FreeVM(void * data,size_t length)129   void FreeVM(void* data, size_t length) {
130     size_t page_size = i::AllocatePageSize();
131     size_t allocated = RoundUp(length, page_size);
132     CHECK(i::FreePages(data, allocated));
133   }
134 };
135 
136 // ArrayBuffer allocator that never allocates over 10MB.
137 class MockArrayBufferAllocator : public ArrayBufferAllocatorBase {
Allocate(size_t length)138   void* Allocate(size_t length) override {
139     return ArrayBufferAllocatorBase::Allocate(Adjust(length));
140   }
141 
AllocateUninitialized(size_t length)142   void* AllocateUninitialized(size_t length) override {
143     return ArrayBufferAllocatorBase::AllocateUninitialized(Adjust(length));
144   }
145 
Free(void * data,size_t length)146   void Free(void* data, size_t length) override {
147     return ArrayBufferAllocatorBase::Free(data, Adjust(length));
148   }
149 
150  private:
Adjust(size_t length)151   size_t Adjust(size_t length) {
152     const size_t kAllocationLimit = 10 * kMB;
153     return length > kAllocationLimit ? i::AllocatePageSize() : length;
154   }
155 };
156 
157 // Predictable v8::Platform implementation. Worker threads are disabled, idle
158 // tasks are disallowed, and the time reported by {MonotonicallyIncreasingTime}
159 // is deterministic.
160 class PredictablePlatform : public Platform {
161  public:
PredictablePlatform(std::unique_ptr<Platform> platform)162   explicit PredictablePlatform(std::unique_ptr<Platform> platform)
163       : platform_(std::move(platform)) {
164     DCHECK_NOT_NULL(platform_);
165   }
166 
GetPageAllocator()167   PageAllocator* GetPageAllocator() override {
168     return platform_->GetPageAllocator();
169   }
170 
OnCriticalMemoryPressure()171   void OnCriticalMemoryPressure() override {
172     platform_->OnCriticalMemoryPressure();
173   }
174 
OnCriticalMemoryPressure(size_t length)175   bool OnCriticalMemoryPressure(size_t length) override {
176     return platform_->OnCriticalMemoryPressure(length);
177   }
178 
GetForegroundTaskRunner(v8::Isolate * isolate)179   std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
180       v8::Isolate* isolate) override {
181     return platform_->GetForegroundTaskRunner(isolate);
182   }
183 
NumberOfWorkerThreads()184   int NumberOfWorkerThreads() override { return 0; }
185 
CallOnWorkerThread(std::unique_ptr<Task> task)186   void CallOnWorkerThread(std::unique_ptr<Task> task) override {
187     // It's not defined when background tasks are being executed, so we can just
188     // execute them right away.
189     task->Run();
190   }
191 
CallDelayedOnWorkerThread(std::unique_ptr<Task> task,double delay_in_seconds)192   void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
193                                  double delay_in_seconds) override {
194     // Never run delayed tasks.
195   }
196 
CallOnForegroundThread(v8::Isolate * isolate,Task * task)197   void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
198     platform_->CallOnForegroundThread(isolate, task);
199   }
200 
CallDelayedOnForegroundThread(v8::Isolate * isolate,Task * task,double delay_in_seconds)201   void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
202                                      double delay_in_seconds) override {
203     platform_->CallDelayedOnForegroundThread(isolate, task, delay_in_seconds);
204   }
205 
CallIdleOnForegroundThread(Isolate * isolate,IdleTask * task)206   void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {
207     UNREACHABLE();
208   }
209 
IdleTasksEnabled(Isolate * isolate)210   bool IdleTasksEnabled(Isolate* isolate) override { return false; }
211 
MonotonicallyIncreasingTime()212   double MonotonicallyIncreasingTime() override {
213     return synthetic_time_in_sec_ += 0.00001;
214   }
215 
CurrentClockTimeMillis()216   double CurrentClockTimeMillis() override {
217     return MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond;
218   }
219 
GetTracingController()220   v8::TracingController* GetTracingController() override {
221     return platform_->GetTracingController();
222   }
223 
platform() const224   Platform* platform() const { return platform_.get(); }
225 
226  private:
227   double synthetic_time_in_sec_ = 0.0;
228   std::unique_ptr<Platform> platform_;
229 
230   DISALLOW_COPY_AND_ASSIGN(PredictablePlatform);
231 };
232 
233 std::unique_ptr<v8::Platform> g_platform;
234 
GetDefaultPlatform()235 v8::Platform* GetDefaultPlatform() {
236   return i::FLAG_verify_predictable
237              ? static_cast<PredictablePlatform*>(g_platform.get())->platform()
238              : g_platform.get();
239 }
240 
Throw(Isolate * isolate,const char * message)241 static Local<Value> Throw(Isolate* isolate, const char* message) {
242   return isolate->ThrowException(
243       String::NewFromUtf8(isolate, message, NewStringType::kNormal)
244           .ToLocalChecked());
245 }
246 
GetWorkerFromInternalField(Isolate * isolate,Local<Object> object)247 Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) {
248   if (object->InternalFieldCount() != 1) {
249     Throw(isolate, "this is not a Worker");
250     return nullptr;
251   }
252 
253   Worker* worker =
254       static_cast<Worker*>(object->GetAlignedPointerFromInternalField(0));
255   if (worker == nullptr) {
256     Throw(isolate, "Worker is defunct because main thread is terminating");
257     return nullptr;
258   }
259 
260   return worker;
261 }
262 
GetThreadOptions(const char * name)263 base::Thread::Options GetThreadOptions(const char* name) {
264   // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
265   // which is not enough to parse the big literal expressions used in tests.
266   // The stack size should be at least StackGuard::kLimitSize + some
267   // OS-specific padding for thread startup code.  2Mbytes seems to be enough.
268   return base::Thread::Options(name, 2 * kMB);
269 }
270 
271 }  // namespace
272 
273 namespace tracing {
274 
275 namespace {
276 
277 // String options that can be used to initialize TraceOptions.
278 const char kRecordUntilFull[] = "record-until-full";
279 const char kRecordContinuously[] = "record-continuously";
280 const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
281 
282 const char kRecordModeParam[] = "record_mode";
283 const char kEnableSystraceParam[] = "enable_systrace";
284 const char kEnableArgumentFilterParam[] = "enable_argument_filter";
285 const char kIncludedCategoriesParam[] = "included_categories";
286 
287 class TraceConfigParser {
288  public:
FillTraceConfig(v8::Isolate * isolate,platform::tracing::TraceConfig * trace_config,const char * json_str)289   static void FillTraceConfig(v8::Isolate* isolate,
290                               platform::tracing::TraceConfig* trace_config,
291                               const char* json_str) {
292     HandleScope outer_scope(isolate);
293     Local<Context> context = Context::New(isolate);
294     Context::Scope context_scope(context);
295     HandleScope inner_scope(isolate);
296 
297     Local<String> source =
298         String::NewFromUtf8(isolate, json_str, NewStringType::kNormal)
299             .ToLocalChecked();
300     Local<Value> result = JSON::Parse(context, source).ToLocalChecked();
301     Local<v8::Object> trace_config_object = Local<v8::Object>::Cast(result);
302 
303     trace_config->SetTraceRecordMode(
304         GetTraceRecordMode(isolate, context, trace_config_object));
305     if (GetBoolean(isolate, context, trace_config_object,
306                    kEnableSystraceParam)) {
307       trace_config->EnableSystrace();
308     }
309     if (GetBoolean(isolate, context, trace_config_object,
310                    kEnableArgumentFilterParam)) {
311       trace_config->EnableArgumentFilter();
312     }
313     UpdateIncludedCategoriesList(isolate, context, trace_config_object,
314                                  trace_config);
315   }
316 
317  private:
GetBoolean(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object,const char * property)318   static bool GetBoolean(v8::Isolate* isolate, Local<Context> context,
319                          Local<v8::Object> object, const char* property) {
320     Local<Value> value = GetValue(isolate, context, object, property);
321     if (value->IsNumber()) {
322       Local<Boolean> v8_boolean = value->ToBoolean(context).ToLocalChecked();
323       return v8_boolean->Value();
324     }
325     return false;
326   }
327 
UpdateIncludedCategoriesList(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object,platform::tracing::TraceConfig * trace_config)328   static int UpdateIncludedCategoriesList(
329       v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object,
330       platform::tracing::TraceConfig* trace_config) {
331     Local<Value> value =
332         GetValue(isolate, context, object, kIncludedCategoriesParam);
333     if (value->IsArray()) {
334       Local<Array> v8_array = Local<Array>::Cast(value);
335       for (int i = 0, length = v8_array->Length(); i < length; ++i) {
336         Local<Value> v = v8_array->Get(context, i)
337                              .ToLocalChecked()
338                              ->ToString(context)
339                              .ToLocalChecked();
340         String::Utf8Value str(isolate, v->ToString(context).ToLocalChecked());
341         trace_config->AddIncludedCategory(*str);
342       }
343       return v8_array->Length();
344     }
345     return 0;
346   }
347 
GetTraceRecordMode(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object)348   static platform::tracing::TraceRecordMode GetTraceRecordMode(
349       v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object) {
350     Local<Value> value = GetValue(isolate, context, object, kRecordModeParam);
351     if (value->IsString()) {
352       Local<String> v8_string = value->ToString(context).ToLocalChecked();
353       String::Utf8Value str(isolate, v8_string);
354       if (strcmp(kRecordUntilFull, *str) == 0) {
355         return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
356       } else if (strcmp(kRecordContinuously, *str) == 0) {
357         return platform::tracing::TraceRecordMode::RECORD_CONTINUOUSLY;
358       } else if (strcmp(kRecordAsMuchAsPossible, *str) == 0) {
359         return platform::tracing::TraceRecordMode::RECORD_AS_MUCH_AS_POSSIBLE;
360       }
361     }
362     return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
363   }
364 
GetValue(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object,const char * property)365   static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
366                                Local<v8::Object> object, const char* property) {
367     Local<String> v8_str =
368         String::NewFromUtf8(isolate, property, NewStringType::kNormal)
369             .ToLocalChecked();
370     return object->Get(context, v8_str).ToLocalChecked();
371   }
372 };
373 
374 }  // namespace
375 
CreateTraceConfigFromJSON(v8::Isolate * isolate,const char * json_str)376 static platform::tracing::TraceConfig* CreateTraceConfigFromJSON(
377     v8::Isolate* isolate, const char* json_str) {
378   platform::tracing::TraceConfig* trace_config =
379       new platform::tracing::TraceConfig();
380   TraceConfigParser::FillTraceConfig(isolate, trace_config, json_str);
381   return trace_config;
382 }
383 
384 }  // namespace tracing
385 
386 
387 class ExternalOwningOneByteStringResource
388     : public String::ExternalOneByteStringResource {
389  public:
ExternalOwningOneByteStringResource()390   ExternalOwningOneByteStringResource() : length_(0) {}
ExternalOwningOneByteStringResource(std::unique_ptr<const char[]> data,size_t length)391   ExternalOwningOneByteStringResource(std::unique_ptr<const char[]> data,
392                                       size_t length)
393       : data_(std::move(data)), length_(length) {}
data() const394   const char* data() const override { return data_.get(); }
length() const395   size_t length() const override { return length_; }
396 
397  private:
398   std::unique_ptr<const char[]> data_;
399   size_t length_;
400 };
401 
402 CounterMap* Shell::counter_map_;
403 base::OS::MemoryMappedFile* Shell::counters_file_ = nullptr;
404 CounterCollection Shell::local_counters_;
405 CounterCollection* Shell::counters_ = &local_counters_;
406 base::LazyMutex Shell::context_mutex_;
407 const base::TimeTicks Shell::kInitialTicks =
408     base::TimeTicks::HighResolutionNow();
409 Global<Function> Shell::stringify_function_;
410 base::LazyMutex Shell::workers_mutex_;
411 bool Shell::allow_new_workers_ = true;
412 std::vector<Worker*> Shell::workers_;
413 std::vector<ExternalizedContents> Shell::externalized_contents_;
414 std::atomic<bool> Shell::script_executed_{false};
415 base::LazyMutex Shell::isolate_status_lock_;
416 std::map<v8::Isolate*, bool> Shell::isolate_status_;
417 base::LazyMutex Shell::cached_code_mutex_;
418 std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
419     Shell::cached_code_map_;
420 
421 Global<Context> Shell::evaluation_context_;
422 ArrayBuffer::Allocator* Shell::array_buffer_allocator;
423 ShellOptions Shell::options;
424 base::OnceType Shell::quit_once_ = V8_ONCE_INIT;
425 
426 // Dummy external source stream which returns the whole source in one go.
427 class DummySourceStream : public v8::ScriptCompiler::ExternalSourceStream {
428  public:
DummySourceStream(Local<String> source,Isolate * isolate)429   DummySourceStream(Local<String> source, Isolate* isolate) : done_(false) {
430     source_length_ = source->Utf8Length(isolate);
431     source_buffer_.reset(new uint8_t[source_length_]);
432     source->WriteUtf8(isolate, reinterpret_cast<char*>(source_buffer_.get()),
433                       source_length_);
434   }
435 
GetMoreData(const uint8_t ** src)436   virtual size_t GetMoreData(const uint8_t** src) {
437     if (done_) {
438       return 0;
439     }
440     *src = source_buffer_.release();
441     done_ = true;
442 
443     return source_length_;
444   }
445 
446  private:
447   int source_length_;
448   std::unique_ptr<uint8_t[]> source_buffer_;
449   bool done_;
450 };
451 
452 class BackgroundCompileThread : public base::Thread {
453  public:
BackgroundCompileThread(Isolate * isolate,Local<String> source)454   BackgroundCompileThread(Isolate* isolate, Local<String> source)
455       : base::Thread(GetThreadOptions("BackgroundCompileThread")),
456         source_(source),
457         streamed_source_(new DummySourceStream(source, isolate),
458                          v8::ScriptCompiler::StreamedSource::UTF8),
459         task_(v8::ScriptCompiler::StartStreamingScript(isolate,
460                                                        &streamed_source_)) {}
461 
Run()462   void Run() override { task_->Run(); }
463 
streamed_source()464   v8::ScriptCompiler::StreamedSource* streamed_source() {
465     return &streamed_source_;
466   }
467 
468  private:
469   Local<String> source_;
470   v8::ScriptCompiler::StreamedSource streamed_source_;
471   std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task_;
472 };
473 
LookupCodeCache(Isolate * isolate,Local<Value> source)474 ScriptCompiler::CachedData* Shell::LookupCodeCache(Isolate* isolate,
475                                                    Local<Value> source) {
476   base::LockGuard<base::Mutex> lock_guard(cached_code_mutex_.Pointer());
477   CHECK(source->IsString());
478   v8::String::Utf8Value key(isolate, source);
479   DCHECK(*key);
480   auto entry = cached_code_map_.find(*key);
481   if (entry != cached_code_map_.end() && entry->second) {
482     int length = entry->second->length;
483     uint8_t* cache = new uint8_t[length];
484     memcpy(cache, entry->second->data, length);
485     ScriptCompiler::CachedData* cached_data = new ScriptCompiler::CachedData(
486         cache, length, ScriptCompiler::CachedData::BufferOwned);
487     return cached_data;
488   }
489   return nullptr;
490 }
491 
StoreInCodeCache(Isolate * isolate,Local<Value> source,const ScriptCompiler::CachedData * cache_data)492 void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> source,
493                              const ScriptCompiler::CachedData* cache_data) {
494   base::LockGuard<base::Mutex> lock_guard(cached_code_mutex_.Pointer());
495   CHECK(source->IsString());
496   if (cache_data == nullptr) return;
497   v8::String::Utf8Value key(isolate, source);
498   DCHECK(*key);
499   int length = cache_data->length;
500   uint8_t* cache = new uint8_t[length];
501   memcpy(cache, cache_data->data, length);
502   cached_code_map_[*key] = std::unique_ptr<ScriptCompiler::CachedData>(
503       new ScriptCompiler::CachedData(cache, length,
504                                      ScriptCompiler::CachedData::BufferOwned));
505 }
506 
507 // Executes a string within the current v8 context.
ExecuteString(Isolate * isolate,Local<String> source,Local<Value> name,PrintResult print_result,ReportExceptions report_exceptions,ProcessMessageQueue process_message_queue)508 bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
509                           Local<Value> name, PrintResult print_result,
510                           ReportExceptions report_exceptions,
511                           ProcessMessageQueue process_message_queue) {
512   HandleScope handle_scope(isolate);
513   TryCatch try_catch(isolate);
514   try_catch.SetVerbose(true);
515 
516   MaybeLocal<Value> maybe_result;
517   bool success = true;
518   {
519     PerIsolateData* data = PerIsolateData::Get(isolate);
520     Local<Context> realm =
521         Local<Context>::New(isolate, data->realms_[data->realm_current_]);
522     Context::Scope context_scope(realm);
523     MaybeLocal<Script> maybe_script;
524     Local<Context> context(isolate->GetCurrentContext());
525     ScriptOrigin origin(name);
526 
527     DCHECK(options.compile_options != ScriptCompiler::kProduceParserCache);
528     DCHECK(options.compile_options != ScriptCompiler::kConsumeParserCache);
529     if (options.compile_options == ScriptCompiler::kConsumeCodeCache) {
530       ScriptCompiler::CachedData* cached_code =
531           LookupCodeCache(isolate, source);
532       if (cached_code != nullptr) {
533         ScriptCompiler::Source script_source(source, origin, cached_code);
534         maybe_script = ScriptCompiler::Compile(context, &script_source,
535                                                options.compile_options);
536         CHECK(!cached_code->rejected);
537       } else {
538         ScriptCompiler::Source script_source(source, origin);
539         maybe_script = ScriptCompiler::Compile(
540             context, &script_source, ScriptCompiler::kNoCompileOptions);
541       }
542     } else if (options.stress_background_compile) {
543       // Start a background thread compiling the script.
544       BackgroundCompileThread background_compile_thread(isolate, source);
545       background_compile_thread.Start();
546 
547       // In parallel, compile on the main thread to flush out any data races.
548       {
549         TryCatch ignore_try_catch(isolate);
550         ScriptCompiler::Source script_source(source, origin);
551         USE(ScriptCompiler::Compile(context, &script_source,
552                                     ScriptCompiler::kNoCompileOptions));
553       }
554 
555       // Join with background thread and finalize compilation.
556       background_compile_thread.Join();
557       maybe_script = v8::ScriptCompiler::Compile(
558           context, background_compile_thread.streamed_source(), source, origin);
559     } else {
560       ScriptCompiler::Source script_source(source, origin);
561       maybe_script = ScriptCompiler::Compile(context, &script_source,
562                                              options.compile_options);
563     }
564 
565     Local<Script> script;
566     if (!maybe_script.ToLocal(&script)) {
567       // Print errors that happened during compilation.
568       if (report_exceptions) ReportException(isolate, &try_catch);
569       return false;
570     }
571 
572     if (options.code_cache_options ==
573         ShellOptions::CodeCacheOptions::kProduceCache) {
574       // Serialize and store it in memory for the next execution.
575       ScriptCompiler::CachedData* cached_data =
576           ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
577       StoreInCodeCache(isolate, source, cached_data);
578       delete cached_data;
579     }
580     maybe_result = script->Run(realm);
581     if (options.code_cache_options ==
582         ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute) {
583       // Serialize and store it in memory for the next execution.
584       ScriptCompiler::CachedData* cached_data =
585           ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
586       StoreInCodeCache(isolate, source, cached_data);
587       delete cached_data;
588     }
589     if (process_message_queue && !EmptyMessageQueues(isolate)) success = false;
590     data->realm_current_ = data->realm_switch_;
591   }
592   Local<Value> result;
593   if (!maybe_result.ToLocal(&result)) {
594     DCHECK(try_catch.HasCaught());
595     // Print errors that happened during execution.
596     if (report_exceptions) ReportException(isolate, &try_catch);
597     return false;
598   }
599   DCHECK(!try_catch.HasCaught());
600   if (print_result) {
601     if (options.test_shell) {
602       if (!result->IsUndefined()) {
603         // If all went well and the result wasn't undefined then print
604         // the returned value.
605         v8::String::Utf8Value str(isolate, result);
606         fwrite(*str, sizeof(**str), str.length(), stdout);
607         printf("\n");
608       }
609     } else {
610       v8::String::Utf8Value str(isolate, Stringify(isolate, result));
611       fwrite(*str, sizeof(**str), str.length(), stdout);
612       printf("\n");
613     }
614   }
615   return success;
616 }
617 
618 namespace {
619 
ToSTLString(Isolate * isolate,Local<String> v8_str)620 std::string ToSTLString(Isolate* isolate, Local<String> v8_str) {
621   String::Utf8Value utf8(isolate, v8_str);
622   // Should not be able to fail since the input is a String.
623   CHECK(*utf8);
624   return *utf8;
625 }
626 
IsAbsolutePath(const std::string & path)627 bool IsAbsolutePath(const std::string& path) {
628 #if defined(_WIN32) || defined(_WIN64)
629   // TODO(adamk): This is an incorrect approximation, but should
630   // work for all our test-running cases.
631   return path.find(':') != std::string::npos;
632 #else
633   return path[0] == '/';
634 #endif
635 }
636 
GetWorkingDirectory()637 std::string GetWorkingDirectory() {
638 #if defined(_WIN32) || defined(_WIN64)
639   char system_buffer[MAX_PATH];
640   // TODO(adamk): Support Unicode paths.
641   DWORD len = GetCurrentDirectoryA(MAX_PATH, system_buffer);
642   CHECK_GT(len, 0);
643   return system_buffer;
644 #else
645   char curdir[PATH_MAX];
646   CHECK_NOT_NULL(getcwd(curdir, PATH_MAX));
647   return curdir;
648 #endif
649 }
650 
651 // Returns the directory part of path, without the trailing '/'.
DirName(const std::string & path)652 std::string DirName(const std::string& path) {
653   DCHECK(IsAbsolutePath(path));
654   size_t last_slash = path.find_last_of('/');
655   DCHECK(last_slash != std::string::npos);
656   return path.substr(0, last_slash);
657 }
658 
659 // Resolves path to an absolute path if necessary, and does some
660 // normalization (eliding references to the current directory
661 // and replacing backslashes with slashes).
NormalizePath(const std::string & path,const std::string & dir_name)662 std::string NormalizePath(const std::string& path,
663                           const std::string& dir_name) {
664   std::string result;
665   if (IsAbsolutePath(path)) {
666     result = path;
667   } else {
668     result = dir_name + '/' + path;
669   }
670   std::replace(result.begin(), result.end(), '\\', '/');
671   size_t i;
672   while ((i = result.find("/./")) != std::string::npos) {
673     result.erase(i, 2);
674   }
675   return result;
676 }
677 
678 // Per-context Module data, allowing sharing of module maps
679 // across top-level module loads.
680 class ModuleEmbedderData {
681  private:
682   class ModuleGlobalHash {
683    public:
ModuleGlobalHash(Isolate * isolate)684     explicit ModuleGlobalHash(Isolate* isolate) : isolate_(isolate) {}
operator ()(const Global<Module> & module) const685     size_t operator()(const Global<Module>& module) const {
686       return module.Get(isolate_)->GetIdentityHash();
687     }
688 
689    private:
690     Isolate* isolate_;
691   };
692 
693  public:
ModuleEmbedderData(Isolate * isolate)694   explicit ModuleEmbedderData(Isolate* isolate)
695       : module_to_specifier_map(10, ModuleGlobalHash(isolate)) {}
696 
697   // Map from normalized module specifier to Module.
698   std::unordered_map<std::string, Global<Module>> specifier_to_module_map;
699   // Map from Module to its URL as defined in the ScriptOrigin
700   std::unordered_map<Global<Module>, std::string, ModuleGlobalHash>
701       module_to_specifier_map;
702 };
703 
704 enum {
705   kModuleEmbedderDataIndex,
706   kInspectorClientIndex
707 };
708 
InitializeModuleEmbedderData(Local<Context> context)709 void InitializeModuleEmbedderData(Local<Context> context) {
710   context->SetAlignedPointerInEmbedderData(
711       kModuleEmbedderDataIndex, new ModuleEmbedderData(context->GetIsolate()));
712 }
713 
GetModuleDataFromContext(Local<Context> context)714 ModuleEmbedderData* GetModuleDataFromContext(Local<Context> context) {
715   return static_cast<ModuleEmbedderData*>(
716       context->GetAlignedPointerFromEmbedderData(kModuleEmbedderDataIndex));
717 }
718 
DisposeModuleEmbedderData(Local<Context> context)719 void DisposeModuleEmbedderData(Local<Context> context) {
720   delete GetModuleDataFromContext(context);
721   context->SetAlignedPointerInEmbedderData(kModuleEmbedderDataIndex, nullptr);
722 }
723 
ResolveModuleCallback(Local<Context> context,Local<String> specifier,Local<Module> referrer)724 MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
725                                          Local<String> specifier,
726                                          Local<Module> referrer) {
727   Isolate* isolate = context->GetIsolate();
728   ModuleEmbedderData* d = GetModuleDataFromContext(context);
729   auto specifier_it =
730       d->module_to_specifier_map.find(Global<Module>(isolate, referrer));
731   CHECK(specifier_it != d->module_to_specifier_map.end());
732   std::string absolute_path = NormalizePath(ToSTLString(isolate, specifier),
733                                             DirName(specifier_it->second));
734   auto module_it = d->specifier_to_module_map.find(absolute_path);
735   CHECK(module_it != d->specifier_to_module_map.end());
736   return module_it->second.Get(isolate);
737 }
738 
739 }  // anonymous namespace
740 
FetchModuleTree(Local<Context> context,const std::string & file_name)741 MaybeLocal<Module> Shell::FetchModuleTree(Local<Context> context,
742                                           const std::string& file_name) {
743   DCHECK(IsAbsolutePath(file_name));
744   Isolate* isolate = context->GetIsolate();
745   Local<String> source_text = ReadFile(isolate, file_name.c_str());
746   if (source_text.IsEmpty()) {
747     std::string msg = "Error reading: " + file_name;
748     Throw(isolate, msg.c_str());
749     return MaybeLocal<Module>();
750   }
751   ScriptOrigin origin(
752       String::NewFromUtf8(isolate, file_name.c_str(), NewStringType::kNormal)
753           .ToLocalChecked(),
754       Local<Integer>(), Local<Integer>(), Local<Boolean>(), Local<Integer>(),
755       Local<Value>(), Local<Boolean>(), Local<Boolean>(), True(isolate));
756   ScriptCompiler::Source source(source_text, origin);
757   Local<Module> module;
758   if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
759     return MaybeLocal<Module>();
760   }
761 
762   ModuleEmbedderData* d = GetModuleDataFromContext(context);
763   CHECK(d->specifier_to_module_map
764             .insert(std::make_pair(file_name, Global<Module>(isolate, module)))
765             .second);
766   CHECK(d->module_to_specifier_map
767             .insert(std::make_pair(Global<Module>(isolate, module), file_name))
768             .second);
769 
770   std::string dir_name = DirName(file_name);
771 
772   for (int i = 0, length = module->GetModuleRequestsLength(); i < length; ++i) {
773     Local<String> name = module->GetModuleRequest(i);
774     std::string absolute_path =
775         NormalizePath(ToSTLString(isolate, name), dir_name);
776     if (!d->specifier_to_module_map.count(absolute_path)) {
777       if (FetchModuleTree(context, absolute_path).IsEmpty()) {
778         return MaybeLocal<Module>();
779       }
780     }
781   }
782 
783   return module;
784 }
785 
786 namespace {
787 
788 struct DynamicImportData {
DynamicImportDatav8::__anoncd2d3b7e0511::DynamicImportData789   DynamicImportData(Isolate* isolate_, Local<String> referrer_,
790                     Local<String> specifier_,
791                     Local<Promise::Resolver> resolver_)
792       : isolate(isolate_) {
793     referrer.Reset(isolate, referrer_);
794     specifier.Reset(isolate, specifier_);
795     resolver.Reset(isolate, resolver_);
796   }
797 
798   Isolate* isolate;
799   Global<String> referrer;
800   Global<String> specifier;
801   Global<Promise::Resolver> resolver;
802 };
803 
804 }  // namespace
805 
HostImportModuleDynamically(Local<Context> context,Local<ScriptOrModule> referrer,Local<String> specifier)806 MaybeLocal<Promise> Shell::HostImportModuleDynamically(
807     Local<Context> context, Local<ScriptOrModule> referrer,
808     Local<String> specifier) {
809   Isolate* isolate = context->GetIsolate();
810 
811   MaybeLocal<Promise::Resolver> maybe_resolver =
812       Promise::Resolver::New(context);
813   Local<Promise::Resolver> resolver;
814   if (maybe_resolver.ToLocal(&resolver)) {
815     DynamicImportData* data = new DynamicImportData(
816         isolate, Local<String>::Cast(referrer->GetResourceName()), specifier,
817         resolver);
818     isolate->EnqueueMicrotask(Shell::DoHostImportModuleDynamically, data);
819     return resolver->GetPromise();
820   }
821 
822   return MaybeLocal<Promise>();
823 }
824 
HostInitializeImportMetaObject(Local<Context> context,Local<Module> module,Local<Object> meta)825 void Shell::HostInitializeImportMetaObject(Local<Context> context,
826                                            Local<Module> module,
827                                            Local<Object> meta) {
828   Isolate* isolate = context->GetIsolate();
829   HandleScope handle_scope(isolate);
830 
831   ModuleEmbedderData* d = GetModuleDataFromContext(context);
832   auto specifier_it =
833       d->module_to_specifier_map.find(Global<Module>(isolate, module));
834   CHECK(specifier_it != d->module_to_specifier_map.end());
835 
836   Local<String> url_key =
837       String::NewFromUtf8(isolate, "url", NewStringType::kNormal)
838           .ToLocalChecked();
839   Local<String> url = String::NewFromUtf8(isolate, specifier_it->second.c_str(),
840                                           NewStringType::kNormal)
841                           .ToLocalChecked();
842   meta->CreateDataProperty(context, url_key, url).ToChecked();
843 }
844 
DoHostImportModuleDynamically(void * import_data)845 void Shell::DoHostImportModuleDynamically(void* import_data) {
846   std::unique_ptr<DynamicImportData> import_data_(
847       static_cast<DynamicImportData*>(import_data));
848   Isolate* isolate(import_data_->isolate);
849   HandleScope handle_scope(isolate);
850 
851   Local<String> referrer(import_data_->referrer.Get(isolate));
852   Local<String> specifier(import_data_->specifier.Get(isolate));
853   Local<Promise::Resolver> resolver(import_data_->resolver.Get(isolate));
854 
855   PerIsolateData* data = PerIsolateData::Get(isolate);
856   Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
857   Context::Scope context_scope(realm);
858 
859   std::string source_url = ToSTLString(isolate, referrer);
860   std::string dir_name =
861       DirName(NormalizePath(source_url, GetWorkingDirectory()));
862   std::string file_name = ToSTLString(isolate, specifier);
863   std::string absolute_path = NormalizePath(file_name, dir_name);
864 
865   TryCatch try_catch(isolate);
866   try_catch.SetVerbose(true);
867 
868   ModuleEmbedderData* d = GetModuleDataFromContext(realm);
869   Local<Module> root_module;
870   auto module_it = d->specifier_to_module_map.find(absolute_path);
871   if (module_it != d->specifier_to_module_map.end()) {
872     root_module = module_it->second.Get(isolate);
873   } else if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
874     CHECK(try_catch.HasCaught());
875     resolver->Reject(realm, try_catch.Exception()).ToChecked();
876     return;
877   }
878 
879   MaybeLocal<Value> maybe_result;
880   if (root_module->InstantiateModule(realm, ResolveModuleCallback)
881           .FromMaybe(false)) {
882     maybe_result = root_module->Evaluate(realm);
883     EmptyMessageQueues(isolate);
884   }
885 
886   Local<Value> module;
887   if (!maybe_result.ToLocal(&module)) {
888     DCHECK(try_catch.HasCaught());
889     resolver->Reject(realm, try_catch.Exception()).ToChecked();
890     return;
891   }
892 
893   DCHECK(!try_catch.HasCaught());
894   Local<Value> module_namespace = root_module->GetModuleNamespace();
895   resolver->Resolve(realm, module_namespace).ToChecked();
896 }
897 
ExecuteModule(Isolate * isolate,const char * file_name)898 bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
899   HandleScope handle_scope(isolate);
900 
901   PerIsolateData* data = PerIsolateData::Get(isolate);
902   Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
903   Context::Scope context_scope(realm);
904 
905   std::string absolute_path = NormalizePath(file_name, GetWorkingDirectory());
906 
907   TryCatch try_catch(isolate);
908   try_catch.SetVerbose(true);
909 
910   Local<Module> root_module;
911   MaybeLocal<Value> maybe_exception;
912 
913   if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
914     CHECK(try_catch.HasCaught());
915     ReportException(isolate, &try_catch);
916     return false;
917   }
918 
919   MaybeLocal<Value> maybe_result;
920   if (root_module->InstantiateModule(realm, ResolveModuleCallback)
921           .FromMaybe(false)) {
922     maybe_result = root_module->Evaluate(realm);
923     EmptyMessageQueues(isolate);
924   }
925   Local<Value> result;
926   if (!maybe_result.ToLocal(&result)) {
927     DCHECK(try_catch.HasCaught());
928     // Print errors that happened during execution.
929     ReportException(isolate, &try_catch);
930     return false;
931   }
932   DCHECK(!try_catch.HasCaught());
933   return true;
934 }
935 
PerIsolateData(Isolate * isolate)936 PerIsolateData::PerIsolateData(Isolate* isolate)
937     : isolate_(isolate), realms_(nullptr) {
938   isolate->SetData(0, this);
939   if (i::FLAG_expose_async_hooks) {
940     async_hooks_wrapper_ = new AsyncHooks(isolate);
941   }
942 }
943 
~PerIsolateData()944 PerIsolateData::~PerIsolateData() {
945   isolate_->SetData(0, nullptr);  // Not really needed, just to be sure...
946   if (i::FLAG_expose_async_hooks) {
947     delete async_hooks_wrapper_;  // This uses the isolate
948   }
949 }
950 
SetTimeout(Local<Function> callback,Local<Context> context)951 void PerIsolateData::SetTimeout(Local<Function> callback,
952                                 Local<Context> context) {
953   set_timeout_callbacks_.emplace(isolate_, callback);
954   set_timeout_contexts_.emplace(isolate_, context);
955 }
956 
GetTimeoutCallback()957 MaybeLocal<Function> PerIsolateData::GetTimeoutCallback() {
958   if (set_timeout_callbacks_.empty()) return MaybeLocal<Function>();
959   Local<Function> result = set_timeout_callbacks_.front().Get(isolate_);
960   set_timeout_callbacks_.pop();
961   return result;
962 }
963 
GetTimeoutContext()964 MaybeLocal<Context> PerIsolateData::GetTimeoutContext() {
965   if (set_timeout_contexts_.empty()) return MaybeLocal<Context>();
966   Local<Context> result = set_timeout_contexts_.front().Get(isolate_);
967   set_timeout_contexts_.pop();
968   return result;
969 }
970 
RealmScope(PerIsolateData * data)971 PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
972   data_->realm_count_ = 1;
973   data_->realm_current_ = 0;
974   data_->realm_switch_ = 0;
975   data_->realms_ = new Global<Context>[1];
976   data_->realms_[0].Reset(data_->isolate_,
977                           data_->isolate_->GetEnteredContext());
978 }
979 
980 
~RealmScope()981 PerIsolateData::RealmScope::~RealmScope() {
982   // Drop realms to avoid keeping them alive. We don't dispose the
983   // module embedder data for the first realm here, but instead do
984   // it in RunShell or in RunMain, if not running in interactive mode
985   for (int i = 1; i < data_->realm_count_; ++i) {
986     Global<Context>& realm = data_->realms_[i];
987     if (realm.IsEmpty()) continue;
988     DisposeModuleEmbedderData(realm.Get(data_->isolate_));
989     // TODO(adamk): No need to reset manually, Globals reset when destructed.
990     realm.Reset();
991   }
992   data_->realm_count_ = 0;
993   delete[] data_->realms_;
994   // TODO(adamk): No need to reset manually, Globals reset when destructed.
995   if (!data_->realm_shared_.IsEmpty())
996     data_->realm_shared_.Reset();
997 }
998 
999 
RealmFind(Local<Context> context)1000 int PerIsolateData::RealmFind(Local<Context> context) {
1001   for (int i = 0; i < realm_count_; ++i) {
1002     if (realms_[i] == context) return i;
1003   }
1004   return -1;
1005 }
1006 
1007 
RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value> & args,int arg_offset)1008 int PerIsolateData::RealmIndexOrThrow(
1009     const v8::FunctionCallbackInfo<v8::Value>& args,
1010     int arg_offset) {
1011   if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) {
1012     Throw(args.GetIsolate(), "Invalid argument");
1013     return -1;
1014   }
1015   int index = args[arg_offset]
1016                   ->Int32Value(args.GetIsolate()->GetCurrentContext())
1017                   .FromMaybe(-1);
1018   if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) {
1019     Throw(args.GetIsolate(), "Invalid realm index");
1020     return -1;
1021   }
1022   return index;
1023 }
1024 
1025 
1026 // performance.now() returns a time stamp as double, measured in milliseconds.
1027 // When FLAG_verify_predictable mode is enabled it returns result of
1028 // v8::Platform::MonotonicallyIncreasingTime().
PerformanceNow(const v8::FunctionCallbackInfo<v8::Value> & args)1029 void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
1030   if (i::FLAG_verify_predictable) {
1031     args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime());
1032   } else {
1033     base::TimeDelta delta =
1034         base::TimeTicks::HighResolutionNow() - kInitialTicks;
1035     args.GetReturnValue().Set(delta.InMillisecondsF());
1036   }
1037 }
1038 
1039 
1040 // Realm.current() returns the index of the currently active realm.
RealmCurrent(const v8::FunctionCallbackInfo<v8::Value> & args)1041 void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) {
1042   Isolate* isolate = args.GetIsolate();
1043   PerIsolateData* data = PerIsolateData::Get(isolate);
1044   int index = data->RealmFind(isolate->GetEnteredContext());
1045   if (index == -1) return;
1046   args.GetReturnValue().Set(index);
1047 }
1048 
1049 
1050 // Realm.owner(o) returns the index of the realm that created o.
RealmOwner(const v8::FunctionCallbackInfo<v8::Value> & args)1051 void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) {
1052   Isolate* isolate = args.GetIsolate();
1053   PerIsolateData* data = PerIsolateData::Get(isolate);
1054   if (args.Length() < 1 || !args[0]->IsObject()) {
1055     Throw(args.GetIsolate(), "Invalid argument");
1056     return;
1057   }
1058   int index = data->RealmFind(args[0]
1059                                   ->ToObject(isolate->GetCurrentContext())
1060                                   .ToLocalChecked()
1061                                   ->CreationContext());
1062   if (index == -1) return;
1063   args.GetReturnValue().Set(index);
1064 }
1065 
1066 
1067 // Realm.global(i) returns the global object of realm i.
1068 // (Note that properties of global objects cannot be read/written cross-realm.)
RealmGlobal(const v8::FunctionCallbackInfo<v8::Value> & args)1069 void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1070   PerIsolateData* data = PerIsolateData::Get(args.GetIsolate());
1071   int index = data->RealmIndexOrThrow(args, 0);
1072   if (index == -1) return;
1073   args.GetReturnValue().Set(
1074       Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global());
1075 }
1076 
CreateRealm(const v8::FunctionCallbackInfo<v8::Value> & args,int index,v8::MaybeLocal<Value> global_object)1077 MaybeLocal<Context> Shell::CreateRealm(
1078     const v8::FunctionCallbackInfo<v8::Value>& args, int index,
1079     v8::MaybeLocal<Value> global_object) {
1080   Isolate* isolate = args.GetIsolate();
1081   TryCatch try_catch(isolate);
1082   PerIsolateData* data = PerIsolateData::Get(isolate);
1083   if (index < 0) {
1084     Global<Context>* old_realms = data->realms_;
1085     index = data->realm_count_;
1086     data->realms_ = new Global<Context>[++data->realm_count_];
1087     for (int i = 0; i < index; ++i) {
1088       data->realms_[i].Reset(isolate, old_realms[i]);
1089       old_realms[i].Reset();
1090     }
1091     delete[] old_realms;
1092   }
1093   Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1094   Local<Context> context =
1095       Context::New(isolate, nullptr, global_template, global_object);
1096   DCHECK(!try_catch.HasCaught());
1097   if (context.IsEmpty()) return MaybeLocal<Context>();
1098   InitializeModuleEmbedderData(context);
1099   data->realms_[index].Reset(isolate, context);
1100   args.GetReturnValue().Set(index);
1101   return context;
1102 }
1103 
DisposeRealm(const v8::FunctionCallbackInfo<v8::Value> & args,int index)1104 void Shell::DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args,
1105                          int index) {
1106   Isolate* isolate = args.GetIsolate();
1107   PerIsolateData* data = PerIsolateData::Get(isolate);
1108   DisposeModuleEmbedderData(data->realms_[index].Get(isolate));
1109   data->realms_[index].Reset();
1110   isolate->ContextDisposedNotification();
1111   isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime());
1112 }
1113 
1114 // Realm.create() creates a new realm with a distinct security token
1115 // and returns its index.
RealmCreate(const v8::FunctionCallbackInfo<v8::Value> & args)1116 void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1117   CreateRealm(args, -1, v8::MaybeLocal<Value>());
1118 }
1119 
1120 // Realm.createAllowCrossRealmAccess() creates a new realm with the same
1121 // security token as the current realm.
RealmCreateAllowCrossRealmAccess(const v8::FunctionCallbackInfo<v8::Value> & args)1122 void Shell::RealmCreateAllowCrossRealmAccess(
1123     const v8::FunctionCallbackInfo<v8::Value>& args) {
1124   Local<Context> context;
1125   if (CreateRealm(args, -1, v8::MaybeLocal<Value>()).ToLocal(&context)) {
1126     context->SetSecurityToken(
1127         args.GetIsolate()->GetEnteredContext()->GetSecurityToken());
1128   }
1129 }
1130 
1131 // Realm.navigate(i) creates a new realm with a distinct security token
1132 // in place of realm i.
RealmNavigate(const v8::FunctionCallbackInfo<v8::Value> & args)1133 void Shell::RealmNavigate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1134   Isolate* isolate = args.GetIsolate();
1135   PerIsolateData* data = PerIsolateData::Get(isolate);
1136   int index = data->RealmIndexOrThrow(args, 0);
1137   if (index == -1) return;
1138   if (index == 0 || index == data->realm_current_ ||
1139       index == data->realm_switch_) {
1140     Throw(args.GetIsolate(), "Invalid realm index");
1141     return;
1142   }
1143 
1144   Local<Context> context = Local<Context>::New(isolate, data->realms_[index]);
1145   v8::MaybeLocal<Value> global_object = context->Global();
1146   DisposeRealm(args, index);
1147   CreateRealm(args, index, global_object);
1148 }
1149 
1150 // Realm.dispose(i) disposes the reference to the realm i.
RealmDispose(const v8::FunctionCallbackInfo<v8::Value> & args)1151 void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
1152   Isolate* isolate = args.GetIsolate();
1153   PerIsolateData* data = PerIsolateData::Get(isolate);
1154   int index = data->RealmIndexOrThrow(args, 0);
1155   if (index == -1) return;
1156   if (index == 0 ||
1157       index == data->realm_current_ || index == data->realm_switch_) {
1158     Throw(args.GetIsolate(), "Invalid realm index");
1159     return;
1160   }
1161   DisposeRealm(args, index);
1162 }
1163 
1164 
1165 // Realm.switch(i) switches to the realm i for consecutive interactive inputs.
RealmSwitch(const v8::FunctionCallbackInfo<v8::Value> & args)1166 void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) {
1167   Isolate* isolate = args.GetIsolate();
1168   PerIsolateData* data = PerIsolateData::Get(isolate);
1169   int index = data->RealmIndexOrThrow(args, 0);
1170   if (index == -1) return;
1171   data->realm_switch_ = index;
1172 }
1173 
1174 
1175 // Realm.eval(i, s) evaluates s in realm i and returns the result.
RealmEval(const v8::FunctionCallbackInfo<v8::Value> & args)1176 void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
1177   Isolate* isolate = args.GetIsolate();
1178   PerIsolateData* data = PerIsolateData::Get(isolate);
1179   int index = data->RealmIndexOrThrow(args, 0);
1180   if (index == -1) return;
1181   if (args.Length() < 2 || !args[1]->IsString()) {
1182     Throw(args.GetIsolate(), "Invalid argument");
1183     return;
1184   }
1185   ScriptCompiler::Source script_source(
1186       args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked());
1187   Local<UnboundScript> script;
1188   if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
1189            .ToLocal(&script)) {
1190     return;
1191   }
1192   Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
1193   realm->Enter();
1194   int previous_index = data->realm_current_;
1195   data->realm_current_ = data->realm_switch_ = index;
1196   Local<Value> result;
1197   if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) {
1198     realm->Exit();
1199     data->realm_current_ = data->realm_switch_ = previous_index;
1200     return;
1201   }
1202   realm->Exit();
1203   data->realm_current_ = data->realm_switch_ = previous_index;
1204   args.GetReturnValue().Set(result);
1205 }
1206 
1207 
1208 // Realm.shared is an accessor for a single shared value across realms.
RealmSharedGet(Local<String> property,const PropertyCallbackInfo<Value> & info)1209 void Shell::RealmSharedGet(Local<String> property,
1210                            const PropertyCallbackInfo<Value>& info) {
1211   Isolate* isolate = info.GetIsolate();
1212   PerIsolateData* data = PerIsolateData::Get(isolate);
1213   if (data->realm_shared_.IsEmpty()) return;
1214   info.GetReturnValue().Set(data->realm_shared_);
1215 }
1216 
RealmSharedSet(Local<String> property,Local<Value> value,const PropertyCallbackInfo<void> & info)1217 void Shell::RealmSharedSet(Local<String> property,
1218                            Local<Value> value,
1219                            const PropertyCallbackInfo<void>& info) {
1220   Isolate* isolate = info.GetIsolate();
1221   PerIsolateData* data = PerIsolateData::Get(isolate);
1222   data->realm_shared_.Reset(isolate, value);
1223 }
1224 
1225 // async_hooks.createHook() registers functions to be called for different
1226 // lifetime events of each async operation.
AsyncHooksCreateHook(const v8::FunctionCallbackInfo<v8::Value> & args)1227 void Shell::AsyncHooksCreateHook(
1228     const v8::FunctionCallbackInfo<v8::Value>& args) {
1229   Local<Object> wrap =
1230       PerIsolateData::Get(args.GetIsolate())->GetAsyncHooks()->CreateHook(args);
1231   args.GetReturnValue().Set(wrap);
1232 }
1233 
1234 // async_hooks.executionAsyncId() returns the asyncId of the current execution
1235 // context.
AsyncHooksExecutionAsyncId(const v8::FunctionCallbackInfo<v8::Value> & args)1236 void Shell::AsyncHooksExecutionAsyncId(
1237     const v8::FunctionCallbackInfo<v8::Value>& args) {
1238   Isolate* isolate = args.GetIsolate();
1239   HandleScope handle_scope(isolate);
1240   args.GetReturnValue().Set(v8::Number::New(
1241       isolate,
1242       PerIsolateData::Get(isolate)->GetAsyncHooks()->GetExecutionAsyncId()));
1243 }
1244 
AsyncHooksTriggerAsyncId(const v8::FunctionCallbackInfo<v8::Value> & args)1245 void Shell::AsyncHooksTriggerAsyncId(
1246     const v8::FunctionCallbackInfo<v8::Value>& args) {
1247   Isolate* isolate = args.GetIsolate();
1248   HandleScope handle_scope(isolate);
1249   args.GetReturnValue().Set(v8::Number::New(
1250       isolate,
1251       PerIsolateData::Get(isolate)->GetAsyncHooks()->GetTriggerAsyncId()));
1252 }
1253 
WriteToFile(FILE * file,const v8::FunctionCallbackInfo<v8::Value> & args)1254 void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) {
1255   for (int i = 0; i < args.Length(); i++) {
1256     HandleScope handle_scope(args.GetIsolate());
1257     if (i != 0) {
1258       fprintf(file, " ");
1259     }
1260 
1261     // Explicitly catch potential exceptions in toString().
1262     v8::TryCatch try_catch(args.GetIsolate());
1263     Local<Value> arg = args[i];
1264     Local<String> str_obj;
1265 
1266     if (arg->IsSymbol()) {
1267       arg = Local<Symbol>::Cast(arg)->Name();
1268     }
1269     if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
1270              .ToLocal(&str_obj)) {
1271       try_catch.ReThrow();
1272       return;
1273     }
1274 
1275     v8::String::Utf8Value str(args.GetIsolate(), str_obj);
1276     int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), file));
1277     if (n != str.length()) {
1278       printf("Error in fwrite\n");
1279       base::OS::ExitProcess(1);
1280     }
1281   }
1282 }
1283 
WriteAndFlush(FILE * file,const v8::FunctionCallbackInfo<v8::Value> & args)1284 void WriteAndFlush(FILE* file,
1285                    const v8::FunctionCallbackInfo<v8::Value>& args) {
1286   WriteToFile(file, args);
1287   fprintf(file, "\n");
1288   fflush(file);
1289 }
1290 
Print(const v8::FunctionCallbackInfo<v8::Value> & args)1291 void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
1292   WriteAndFlush(stdout, args);
1293 }
1294 
PrintErr(const v8::FunctionCallbackInfo<v8::Value> & args)1295 void Shell::PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args) {
1296   WriteAndFlush(stderr, args);
1297 }
1298 
Write(const v8::FunctionCallbackInfo<v8::Value> & args)1299 void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) {
1300   WriteToFile(stdout, args);
1301 }
1302 
Read(const v8::FunctionCallbackInfo<v8::Value> & args)1303 void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
1304   String::Utf8Value file(args.GetIsolate(), args[0]);
1305   if (*file == nullptr) {
1306     Throw(args.GetIsolate(), "Error loading file");
1307     return;
1308   }
1309   if (args.Length() == 2) {
1310     String::Utf8Value format(args.GetIsolate(), args[1]);
1311     if (*format && std::strcmp(*format, "binary") == 0) {
1312       ReadBuffer(args);
1313       return;
1314     }
1315   }
1316   Local<String> source = ReadFile(args.GetIsolate(), *file);
1317   if (source.IsEmpty()) {
1318     Throw(args.GetIsolate(), "Error loading file");
1319     return;
1320   }
1321   args.GetReturnValue().Set(source);
1322 }
1323 
1324 
ReadFromStdin(Isolate * isolate)1325 Local<String> Shell::ReadFromStdin(Isolate* isolate) {
1326   static const int kBufferSize = 256;
1327   char buffer[kBufferSize];
1328   Local<String> accumulator =
1329       String::NewFromUtf8(isolate, "", NewStringType::kNormal).ToLocalChecked();
1330   int length;
1331   while (true) {
1332     // Continue reading if the line ends with an escape '\\' or the line has
1333     // not been fully read into the buffer yet (does not end with '\n').
1334     // If fgets gets an error, just give up.
1335     char* input = nullptr;
1336     input = fgets(buffer, kBufferSize, stdin);
1337     if (input == nullptr) return Local<String>();
1338     length = static_cast<int>(strlen(buffer));
1339     if (length == 0) {
1340       return accumulator;
1341     } else if (buffer[length-1] != '\n') {
1342       accumulator = String::Concat(
1343           isolate, accumulator,
1344           String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
1345               .ToLocalChecked());
1346     } else if (length > 1 && buffer[length-2] == '\\') {
1347       buffer[length-2] = '\n';
1348       accumulator =
1349           String::Concat(isolate, accumulator,
1350                          String::NewFromUtf8(isolate, buffer,
1351                                              NewStringType::kNormal, length - 1)
1352                              .ToLocalChecked());
1353     } else {
1354       return String::Concat(
1355           isolate, accumulator,
1356           String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
1357                               length - 1)
1358               .ToLocalChecked());
1359     }
1360   }
1361 }
1362 
1363 
Load(const v8::FunctionCallbackInfo<v8::Value> & args)1364 void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
1365   for (int i = 0; i < args.Length(); i++) {
1366     HandleScope handle_scope(args.GetIsolate());
1367     String::Utf8Value file(args.GetIsolate(), args[i]);
1368     if (*file == nullptr) {
1369       Throw(args.GetIsolate(), "Error loading file");
1370       return;
1371     }
1372     Local<String> source = ReadFile(args.GetIsolate(), *file);
1373     if (source.IsEmpty()) {
1374       Throw(args.GetIsolate(), "Error loading file");
1375       return;
1376     }
1377     if (!ExecuteString(
1378             args.GetIsolate(), source,
1379             String::NewFromUtf8(args.GetIsolate(), *file,
1380                                 NewStringType::kNormal)
1381                 .ToLocalChecked(),
1382             kNoPrintResult,
1383             options.quiet_load ? kNoReportExceptions : kReportExceptions,
1384             kNoProcessMessageQueue)) {
1385       Throw(args.GetIsolate(), "Error executing file");
1386       return;
1387     }
1388   }
1389 }
1390 
SetTimeout(const v8::FunctionCallbackInfo<v8::Value> & args)1391 void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
1392   Isolate* isolate = args.GetIsolate();
1393   args.GetReturnValue().Set(v8::Number::New(isolate, 0));
1394   if (args.Length() == 0 || !args[0]->IsFunction()) return;
1395   Local<Function> callback = Local<Function>::Cast(args[0]);
1396   Local<Context> context = isolate->GetCurrentContext();
1397   PerIsolateData::Get(isolate)->SetTimeout(callback, context);
1398 }
1399 
WorkerNew(const v8::FunctionCallbackInfo<v8::Value> & args)1400 void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
1401   Isolate* isolate = args.GetIsolate();
1402   HandleScope handle_scope(isolate);
1403   if (args.Length() < 1 || !args[0]->IsString()) {
1404     Throw(args.GetIsolate(), "1st argument must be string");
1405     return;
1406   }
1407 
1408   if (!args.IsConstructCall()) {
1409     Throw(args.GetIsolate(), "Worker must be constructed with new");
1410     return;
1411   }
1412 
1413   {
1414     base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
1415     if (workers_.size() >= kMaxWorkers) {
1416       Throw(args.GetIsolate(), "Too many workers, I won't let you create more");
1417       return;
1418     }
1419 
1420     // Initialize the embedder field to nullptr; if we return early without
1421     // creating a new Worker (because the main thread is terminating) we can
1422     // early-out from the instance calls.
1423     args.Holder()->SetAlignedPointerInInternalField(0, nullptr);
1424 
1425     if (!allow_new_workers_) return;
1426 
1427     Worker* worker = new Worker;
1428     args.Holder()->SetAlignedPointerInInternalField(0, worker);
1429     workers_.push_back(worker);
1430 
1431     String::Utf8Value script(args.GetIsolate(), args[0]);
1432     if (!*script) {
1433       Throw(args.GetIsolate(), "Can't get worker script");
1434       return;
1435     }
1436     worker->StartExecuteInThread(*script);
1437   }
1438 }
1439 
1440 
WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value> & args)1441 void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1442   Isolate* isolate = args.GetIsolate();
1443   HandleScope handle_scope(isolate);
1444 
1445   if (args.Length() < 1) {
1446     Throw(isolate, "Invalid argument");
1447     return;
1448   }
1449 
1450   Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1451   if (!worker) {
1452     return;
1453   }
1454 
1455   Local<Value> message = args[0];
1456   Local<Value> transfer =
1457       args.Length() >= 2 ? args[1] : Local<Value>::Cast(Undefined(isolate));
1458   std::unique_ptr<SerializationData> data =
1459       Shell::SerializeValue(isolate, message, transfer);
1460   if (data) {
1461     worker->PostMessage(std::move(data));
1462   }
1463 }
1464 
1465 
WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value> & args)1466 void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1467   Isolate* isolate = args.GetIsolate();
1468   HandleScope handle_scope(isolate);
1469   Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1470   if (!worker) {
1471     return;
1472   }
1473 
1474   std::unique_ptr<SerializationData> data = worker->GetMessage();
1475   if (data) {
1476     Local<Value> value;
1477     if (Shell::DeserializeValue(isolate, std::move(data)).ToLocal(&value)) {
1478       args.GetReturnValue().Set(value);
1479     }
1480   }
1481 }
1482 
1483 
WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value> & args)1484 void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1485   Isolate* isolate = args.GetIsolate();
1486   HandleScope handle_scope(isolate);
1487   Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1488   if (!worker) {
1489     return;
1490   }
1491 
1492   worker->Terminate();
1493 }
1494 
1495 
QuitOnce(v8::FunctionCallbackInfo<v8::Value> * args)1496 void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) {
1497   int exit_code = (*args)[0]
1498                       ->Int32Value(args->GetIsolate()->GetCurrentContext())
1499                       .FromMaybe(0);
1500   CleanupWorkers();
1501   args->GetIsolate()->Exit();
1502   OnExit(args->GetIsolate());
1503   base::OS::ExitProcess(exit_code);
1504 }
1505 
1506 
Quit(const v8::FunctionCallbackInfo<v8::Value> & args)1507 void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
1508   base::CallOnce(&quit_once_, &QuitOnce,
1509                  const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args));
1510 }
1511 
WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value> & args)1512 void Shell::WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1513   SetWaitUntilDone(args.GetIsolate(), true);
1514 }
1515 
NotifyDone(const v8::FunctionCallbackInfo<v8::Value> & args)1516 void Shell::NotifyDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1517   SetWaitUntilDone(args.GetIsolate(), false);
1518 }
1519 
Version(const v8::FunctionCallbackInfo<v8::Value> & args)1520 void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
1521   args.GetReturnValue().Set(
1522       String::NewFromUtf8(args.GetIsolate(), V8::GetVersion(),
1523                           NewStringType::kNormal).ToLocalChecked());
1524 }
1525 
1526 
ReportException(Isolate * isolate,v8::TryCatch * try_catch)1527 void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) {
1528   HandleScope handle_scope(isolate);
1529   Local<Context> context = isolate->GetCurrentContext();
1530   bool enter_context = context.IsEmpty();
1531   if (enter_context) {
1532     context = Local<Context>::New(isolate, evaluation_context_);
1533     context->Enter();
1534   }
1535   // Converts a V8 value to a C string.
1536   auto ToCString = [](const v8::String::Utf8Value& value) {
1537     return *value ? *value : "<string conversion failed>";
1538   };
1539 
1540   v8::String::Utf8Value exception(isolate, try_catch->Exception());
1541   const char* exception_string = ToCString(exception);
1542   Local<Message> message = try_catch->Message();
1543   if (message.IsEmpty()) {
1544     // V8 didn't provide any extra information about this error; just
1545     // print the exception.
1546     printf("%s\n", exception_string);
1547   } else if (message->GetScriptOrigin().Options().IsWasm()) {
1548     // Print wasm-function[(function index)]:(offset): (message).
1549     int function_index = message->GetLineNumber(context).FromJust() - 1;
1550     int offset = message->GetStartColumn(context).FromJust();
1551     printf("wasm-function[%d]:%d: %s\n", function_index, offset,
1552            exception_string);
1553   } else {
1554     // Print (filename):(line number): (message).
1555     v8::String::Utf8Value filename(isolate,
1556                                    message->GetScriptOrigin().ResourceName());
1557     const char* filename_string = ToCString(filename);
1558     int linenum = message->GetLineNumber(context).FromMaybe(-1);
1559     printf("%s:%i: %s\n", filename_string, linenum, exception_string);
1560     Local<String> sourceline;
1561     if (message->GetSourceLine(context).ToLocal(&sourceline)) {
1562       // Print line of source code.
1563       v8::String::Utf8Value sourcelinevalue(isolate, sourceline);
1564       const char* sourceline_string = ToCString(sourcelinevalue);
1565       printf("%s\n", sourceline_string);
1566       // Print wavy underline (GetUnderline is deprecated).
1567       int start = message->GetStartColumn(context).FromJust();
1568       for (int i = 0; i < start; i++) {
1569         printf(" ");
1570       }
1571       int end = message->GetEndColumn(context).FromJust();
1572       for (int i = start; i < end; i++) {
1573         printf("^");
1574       }
1575       printf("\n");
1576     }
1577   }
1578   Local<Value> stack_trace_string;
1579   if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) &&
1580       stack_trace_string->IsString()) {
1581     v8::String::Utf8Value stack_trace(isolate,
1582                                       Local<String>::Cast(stack_trace_string));
1583     printf("%s\n", ToCString(stack_trace));
1584   }
1585   printf("\n");
1586   if (enter_context) context->Exit();
1587 }
1588 
1589 
Bind(const char * name,bool is_histogram)1590 int32_t* Counter::Bind(const char* name, bool is_histogram) {
1591   int i;
1592   for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
1593     name_[i] = static_cast<char>(name[i]);
1594   name_[i] = '\0';
1595   is_histogram_ = is_histogram;
1596   return ptr();
1597 }
1598 
1599 
AddSample(int32_t sample)1600 void Counter::AddSample(int32_t sample) {
1601   count_++;
1602   sample_total_ += sample;
1603 }
1604 
1605 
CounterCollection()1606 CounterCollection::CounterCollection() {
1607   magic_number_ = 0xDEADFACE;
1608   max_counters_ = kMaxCounters;
1609   max_name_size_ = Counter::kMaxNameSize;
1610   counters_in_use_ = 0;
1611 }
1612 
1613 
GetNextCounter()1614 Counter* CounterCollection::GetNextCounter() {
1615   if (counters_in_use_ == kMaxCounters) return nullptr;
1616   return &counters_[counters_in_use_++];
1617 }
1618 
1619 
MapCounters(v8::Isolate * isolate,const char * name)1620 void Shell::MapCounters(v8::Isolate* isolate, const char* name) {
1621   counters_file_ = base::OS::MemoryMappedFile::create(
1622       name, sizeof(CounterCollection), &local_counters_);
1623   void* memory =
1624       (counters_file_ == nullptr) ? nullptr : counters_file_->memory();
1625   if (memory == nullptr) {
1626     printf("Could not map counters file %s\n", name);
1627     base::OS::ExitProcess(1);
1628   }
1629   counters_ = static_cast<CounterCollection*>(memory);
1630   isolate->SetCounterFunction(LookupCounter);
1631   isolate->SetCreateHistogramFunction(CreateHistogram);
1632   isolate->SetAddHistogramSampleFunction(AddHistogramSample);
1633 }
1634 
GetCounter(const char * name,bool is_histogram)1635 Counter* Shell::GetCounter(const char* name, bool is_histogram) {
1636   auto map_entry = counter_map_->find(name);
1637   Counter* counter =
1638       map_entry != counter_map_->end() ? map_entry->second : nullptr;
1639 
1640   if (counter == nullptr) {
1641     counter = counters_->GetNextCounter();
1642     if (counter != nullptr) {
1643       (*counter_map_)[name] = counter;
1644       counter->Bind(name, is_histogram);
1645     }
1646   } else {
1647     DCHECK(counter->is_histogram() == is_histogram);
1648   }
1649   return counter;
1650 }
1651 
1652 
LookupCounter(const char * name)1653 int* Shell::LookupCounter(const char* name) {
1654   Counter* counter = GetCounter(name, false);
1655 
1656   if (counter != nullptr) {
1657     return counter->ptr();
1658   } else {
1659     return nullptr;
1660   }
1661 }
1662 
1663 
CreateHistogram(const char * name,int min,int max,size_t buckets)1664 void* Shell::CreateHistogram(const char* name,
1665                              int min,
1666                              int max,
1667                              size_t buckets) {
1668   return GetCounter(name, true);
1669 }
1670 
1671 
AddHistogramSample(void * histogram,int sample)1672 void Shell::AddHistogramSample(void* histogram, int sample) {
1673   Counter* counter = reinterpret_cast<Counter*>(histogram);
1674   counter->AddSample(sample);
1675 }
1676 
1677 // Turn a value into a human-readable string.
Stringify(Isolate * isolate,Local<Value> value)1678 Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) {
1679   v8::Local<v8::Context> context =
1680       v8::Local<v8::Context>::New(isolate, evaluation_context_);
1681   if (stringify_function_.IsEmpty()) {
1682     int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
1683     i::Vector<const char> source_string =
1684         i::NativesCollection<i::D8>::GetScriptSource(source_index);
1685     i::Vector<const char> source_name =
1686         i::NativesCollection<i::D8>::GetScriptName(source_index);
1687     Local<String> source =
1688         String::NewFromUtf8(isolate, source_string.start(),
1689                             NewStringType::kNormal, source_string.length())
1690             .ToLocalChecked();
1691     Local<String> name =
1692         String::NewFromUtf8(isolate, source_name.start(),
1693                             NewStringType::kNormal, source_name.length())
1694             .ToLocalChecked();
1695     ScriptOrigin origin(name);
1696     Local<Script> script =
1697         Script::Compile(context, source, &origin).ToLocalChecked();
1698     stringify_function_.Reset(
1699         isolate, script->Run(context).ToLocalChecked().As<Function>());
1700   }
1701   Local<Function> fun = Local<Function>::New(isolate, stringify_function_);
1702   Local<Value> argv[1] = {value};
1703   v8::TryCatch try_catch(isolate);
1704   MaybeLocal<Value> result = fun->Call(context, Undefined(isolate), 1, argv);
1705   if (result.IsEmpty()) return String::Empty(isolate);
1706   return result.ToLocalChecked().As<String>();
1707 }
1708 
1709 
CreateGlobalTemplate(Isolate * isolate)1710 Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
1711   Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
1712   global_template->Set(
1713       String::NewFromUtf8(isolate, "print", NewStringType::kNormal)
1714           .ToLocalChecked(),
1715       FunctionTemplate::New(isolate, Print));
1716   global_template->Set(
1717       String::NewFromUtf8(isolate, "printErr", NewStringType::kNormal)
1718           .ToLocalChecked(),
1719       FunctionTemplate::New(isolate, PrintErr));
1720   global_template->Set(
1721       String::NewFromUtf8(isolate, "write", NewStringType::kNormal)
1722           .ToLocalChecked(),
1723       FunctionTemplate::New(isolate, Write));
1724   global_template->Set(
1725       String::NewFromUtf8(isolate, "read", NewStringType::kNormal)
1726           .ToLocalChecked(),
1727       FunctionTemplate::New(isolate, Read));
1728   global_template->Set(
1729       String::NewFromUtf8(isolate, "readbuffer", NewStringType::kNormal)
1730           .ToLocalChecked(),
1731       FunctionTemplate::New(isolate, ReadBuffer));
1732   global_template->Set(
1733       String::NewFromUtf8(isolate, "readline", NewStringType::kNormal)
1734           .ToLocalChecked(),
1735       FunctionTemplate::New(isolate, ReadLine));
1736   global_template->Set(
1737       String::NewFromUtf8(isolate, "load", NewStringType::kNormal)
1738           .ToLocalChecked(),
1739       FunctionTemplate::New(isolate, Load));
1740   global_template->Set(
1741       String::NewFromUtf8(isolate, "setTimeout", NewStringType::kNormal)
1742           .ToLocalChecked(),
1743       FunctionTemplate::New(isolate, SetTimeout));
1744   // Some Emscripten-generated code tries to call 'quit', which in turn would
1745   // call C's exit(). This would lead to memory leaks, because there is no way
1746   // we can terminate cleanly then, so we need a way to hide 'quit'.
1747   if (!options.omit_quit) {
1748     global_template->Set(
1749         String::NewFromUtf8(isolate, "quit", NewStringType::kNormal)
1750             .ToLocalChecked(),
1751         FunctionTemplate::New(isolate, Quit));
1752   }
1753   Local<ObjectTemplate> test_template = ObjectTemplate::New(isolate);
1754   global_template->Set(
1755       String::NewFromUtf8(isolate, "testRunner", NewStringType::kNormal)
1756           .ToLocalChecked(),
1757       test_template);
1758   test_template->Set(
1759       String::NewFromUtf8(isolate, "notifyDone", NewStringType::kNormal)
1760           .ToLocalChecked(),
1761       FunctionTemplate::New(isolate, NotifyDone));
1762   test_template->Set(
1763       String::NewFromUtf8(isolate, "waitUntilDone", NewStringType::kNormal)
1764           .ToLocalChecked(),
1765       FunctionTemplate::New(isolate, WaitUntilDone));
1766   global_template->Set(
1767       String::NewFromUtf8(isolate, "version", NewStringType::kNormal)
1768           .ToLocalChecked(),
1769       FunctionTemplate::New(isolate, Version));
1770   global_template->Set(
1771       Symbol::GetToStringTag(isolate),
1772       String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1773           .ToLocalChecked());
1774 
1775   // Bind the Realm object.
1776   Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
1777   realm_template->Set(
1778       String::NewFromUtf8(isolate, "current", NewStringType::kNormal)
1779           .ToLocalChecked(),
1780       FunctionTemplate::New(isolate, RealmCurrent));
1781   realm_template->Set(
1782       String::NewFromUtf8(isolate, "owner", NewStringType::kNormal)
1783           .ToLocalChecked(),
1784       FunctionTemplate::New(isolate, RealmOwner));
1785   realm_template->Set(
1786       String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1787           .ToLocalChecked(),
1788       FunctionTemplate::New(isolate, RealmGlobal));
1789   realm_template->Set(
1790       String::NewFromUtf8(isolate, "create", NewStringType::kNormal)
1791           .ToLocalChecked(),
1792       FunctionTemplate::New(isolate, RealmCreate));
1793   realm_template->Set(
1794       String::NewFromUtf8(isolate, "createAllowCrossRealmAccess",
1795                           NewStringType::kNormal)
1796           .ToLocalChecked(),
1797       FunctionTemplate::New(isolate, RealmCreateAllowCrossRealmAccess));
1798   realm_template->Set(
1799       String::NewFromUtf8(isolate, "navigate", NewStringType::kNormal)
1800           .ToLocalChecked(),
1801       FunctionTemplate::New(isolate, RealmNavigate));
1802   realm_template->Set(
1803       String::NewFromUtf8(isolate, "dispose", NewStringType::kNormal)
1804           .ToLocalChecked(),
1805       FunctionTemplate::New(isolate, RealmDispose));
1806   realm_template->Set(
1807       String::NewFromUtf8(isolate, "switch", NewStringType::kNormal)
1808           .ToLocalChecked(),
1809       FunctionTemplate::New(isolate, RealmSwitch));
1810   realm_template->Set(
1811       String::NewFromUtf8(isolate, "eval", NewStringType::kNormal)
1812           .ToLocalChecked(),
1813       FunctionTemplate::New(isolate, RealmEval));
1814   realm_template->SetAccessor(
1815       String::NewFromUtf8(isolate, "shared", NewStringType::kNormal)
1816           .ToLocalChecked(),
1817       RealmSharedGet, RealmSharedSet);
1818   global_template->Set(
1819       String::NewFromUtf8(isolate, "Realm", NewStringType::kNormal)
1820           .ToLocalChecked(),
1821       realm_template);
1822 
1823   Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
1824   performance_template->Set(
1825       String::NewFromUtf8(isolate, "now", NewStringType::kNormal)
1826           .ToLocalChecked(),
1827       FunctionTemplate::New(isolate, PerformanceNow));
1828   global_template->Set(
1829       String::NewFromUtf8(isolate, "performance", NewStringType::kNormal)
1830           .ToLocalChecked(),
1831       performance_template);
1832 
1833   Local<FunctionTemplate> worker_fun_template =
1834       FunctionTemplate::New(isolate, WorkerNew);
1835   Local<Signature> worker_signature =
1836       Signature::New(isolate, worker_fun_template);
1837   worker_fun_template->SetClassName(
1838       String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1839           .ToLocalChecked());
1840   worker_fun_template->ReadOnlyPrototype();
1841   worker_fun_template->PrototypeTemplate()->Set(
1842       String::NewFromUtf8(isolate, "terminate", NewStringType::kNormal)
1843           .ToLocalChecked(),
1844       FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(),
1845                             worker_signature));
1846   worker_fun_template->PrototypeTemplate()->Set(
1847       String::NewFromUtf8(isolate, "postMessage", NewStringType::kNormal)
1848           .ToLocalChecked(),
1849       FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(),
1850                             worker_signature));
1851   worker_fun_template->PrototypeTemplate()->Set(
1852       String::NewFromUtf8(isolate, "getMessage", NewStringType::kNormal)
1853           .ToLocalChecked(),
1854       FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(),
1855                             worker_signature));
1856   worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1);
1857   global_template->Set(
1858       String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1859           .ToLocalChecked(),
1860       worker_fun_template);
1861 
1862   Local<ObjectTemplate> os_templ = ObjectTemplate::New(isolate);
1863   AddOSMethods(isolate, os_templ);
1864   global_template->Set(
1865       String::NewFromUtf8(isolate, "os", NewStringType::kNormal)
1866           .ToLocalChecked(),
1867       os_templ);
1868 
1869   if (i::FLAG_expose_async_hooks) {
1870     Local<ObjectTemplate> async_hooks_templ = ObjectTemplate::New(isolate);
1871     async_hooks_templ->Set(
1872         String::NewFromUtf8(isolate, "createHook", NewStringType::kNormal)
1873             .ToLocalChecked(),
1874         FunctionTemplate::New(isolate, AsyncHooksCreateHook));
1875     async_hooks_templ->Set(
1876         String::NewFromUtf8(isolate, "executionAsyncId", NewStringType::kNormal)
1877             .ToLocalChecked(),
1878         FunctionTemplate::New(isolate, AsyncHooksExecutionAsyncId));
1879     async_hooks_templ->Set(
1880         String::NewFromUtf8(isolate, "triggerAsyncId", NewStringType::kNormal)
1881             .ToLocalChecked(),
1882         FunctionTemplate::New(isolate, AsyncHooksTriggerAsyncId));
1883     global_template->Set(
1884         String::NewFromUtf8(isolate, "async_hooks", NewStringType::kNormal)
1885             .ToLocalChecked(),
1886         async_hooks_templ);
1887   }
1888 
1889   return global_template;
1890 }
1891 
PrintNonErrorsMessageCallback(Local<Message> message,Local<Value> error)1892 static void PrintNonErrorsMessageCallback(Local<Message> message,
1893                                           Local<Value> error) {
1894   // Nothing to do here for errors, exceptions thrown up to the shell will be
1895   // reported
1896   // separately by {Shell::ReportException} after they are caught.
1897   // Do print other kinds of messages.
1898   switch (message->ErrorLevel()) {
1899     case v8::Isolate::kMessageWarning:
1900     case v8::Isolate::kMessageLog:
1901     case v8::Isolate::kMessageInfo:
1902     case v8::Isolate::kMessageDebug: {
1903       break;
1904     }
1905 
1906     case v8::Isolate::kMessageError: {
1907       // Ignore errors, printed elsewhere.
1908       return;
1909     }
1910 
1911     default: {
1912       UNREACHABLE();
1913       break;
1914     }
1915   }
1916   // Converts a V8 value to a C string.
1917   auto ToCString = [](const v8::String::Utf8Value& value) {
1918     return *value ? *value : "<string conversion failed>";
1919   };
1920   Isolate* isolate = Isolate::GetCurrent();
1921   v8::String::Utf8Value msg(isolate, message->Get());
1922   const char* msg_string = ToCString(msg);
1923   // Print (filename):(line number): (message).
1924   v8::String::Utf8Value filename(isolate,
1925                                  message->GetScriptOrigin().ResourceName());
1926   const char* filename_string = ToCString(filename);
1927   Maybe<int> maybeline = message->GetLineNumber(isolate->GetCurrentContext());
1928   int linenum = maybeline.IsJust() ? maybeline.FromJust() : -1;
1929   printf("%s:%i: %s\n", filename_string, linenum, msg_string);
1930 }
1931 
Initialize(Isolate * isolate)1932 void Shell::Initialize(Isolate* isolate) {
1933   // Set up counters
1934   if (i::StrLength(i::FLAG_map_counters) != 0)
1935     MapCounters(isolate, i::FLAG_map_counters);
1936   // Disable default message reporting.
1937   isolate->AddMessageListenerWithErrorLevel(
1938       PrintNonErrorsMessageCallback,
1939       v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
1940           v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
1941           v8::Isolate::kMessageLog);
1942 }
1943 
1944 
CreateEvaluationContext(Isolate * isolate)1945 Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
1946   // This needs to be a critical section since this is not thread-safe
1947   base::LockGuard<base::Mutex> lock_guard(context_mutex_.Pointer());
1948   // Initialize the global objects
1949   Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1950   EscapableHandleScope handle_scope(isolate);
1951   Local<Context> context = Context::New(isolate, nullptr, global_template);
1952   DCHECK(!context.IsEmpty());
1953   InitializeModuleEmbedderData(context);
1954   Context::Scope scope(context);
1955 
1956   i::Factory* factory = reinterpret_cast<i::Isolate*>(isolate)->factory();
1957   i::JSArguments js_args = i::FLAG_js_arguments;
1958   i::Handle<i::FixedArray> arguments_array =
1959       factory->NewFixedArray(js_args.argc);
1960   for (int j = 0; j < js_args.argc; j++) {
1961     i::Handle<i::String> arg =
1962         factory->NewStringFromUtf8(i::CStrVector(js_args[j])).ToHandleChecked();
1963     arguments_array->set(j, *arg);
1964   }
1965   i::Handle<i::JSArray> arguments_jsarray =
1966       factory->NewJSArrayWithElements(arguments_array);
1967   context->Global()
1968       ->Set(context,
1969             String::NewFromUtf8(isolate, "arguments", NewStringType::kNormal)
1970                 .ToLocalChecked(),
1971             Utils::ToLocal(arguments_jsarray))
1972       .FromJust();
1973   return handle_scope.Escape(context);
1974 }
1975 
1976 struct CounterAndKey {
1977   Counter* counter;
1978   const char* key;
1979 };
1980 
1981 
operator <(const CounterAndKey & lhs,const CounterAndKey & rhs)1982 inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) {
1983   return strcmp(lhs.key, rhs.key) < 0;
1984 }
1985 
WriteIgnitionDispatchCountersFile(v8::Isolate * isolate)1986 void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
1987   HandleScope handle_scope(isolate);
1988   Local<Context> context = Context::New(isolate);
1989   Context::Scope context_scope(context);
1990 
1991   Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate)
1992                                         ->interpreter()
1993                                         ->GetDispatchCountersObject();
1994   std::ofstream dispatch_counters_stream(
1995       i::FLAG_trace_ignition_dispatches_output_file);
1996   dispatch_counters_stream << *String::Utf8Value(
1997       isolate, JSON::Stringify(context, dispatch_counters).ToLocalChecked());
1998 }
1999 
2000 namespace {
LineFromOffset(Local<debug::Script> script,int offset)2001 int LineFromOffset(Local<debug::Script> script, int offset) {
2002   debug::Location location = script->GetSourceLocation(offset);
2003   return location.GetLineNumber();
2004 }
2005 
WriteLcovDataForRange(std::vector<uint32_t> & lines,int start_line,int end_line,uint32_t count)2006 void WriteLcovDataForRange(std::vector<uint32_t>& lines, int start_line,
2007                            int end_line, uint32_t count) {
2008   // Ensure space in the array.
2009   lines.resize(std::max(static_cast<size_t>(end_line + 1), lines.size()), 0);
2010   // Boundary lines could be shared between two functions with different
2011   // invocation counts. Take the maximum.
2012   lines[start_line] = std::max(lines[start_line], count);
2013   lines[end_line] = std::max(lines[end_line], count);
2014   // Invocation counts for non-boundary lines are overwritten.
2015   for (int k = start_line + 1; k < end_line; k++) lines[k] = count;
2016 }
2017 
WriteLcovDataForNamedRange(std::ostream & sink,std::vector<uint32_t> & lines,std::string name,int start_line,int end_line,uint32_t count)2018 void WriteLcovDataForNamedRange(std::ostream& sink,
2019                                 std::vector<uint32_t>& lines, std::string name,
2020                                 int start_line, int end_line, uint32_t count) {
2021   WriteLcovDataForRange(lines, start_line, end_line, count);
2022   sink << "FN:" << start_line + 1 << "," << name << std::endl;
2023   sink << "FNDA:" << count << "," << name << std::endl;
2024 }
2025 }  // namespace
2026 
2027 // Write coverage data in LCOV format. See man page for geninfo(1).
WriteLcovData(v8::Isolate * isolate,const char * file)2028 void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
2029   if (!file) return;
2030   HandleScope handle_scope(isolate);
2031   debug::Coverage coverage = debug::Coverage::CollectPrecise(isolate);
2032   std::ofstream sink(file, std::ofstream::app);
2033   for (size_t i = 0; i < coverage.ScriptCount(); i++) {
2034     debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
2035     Local<debug::Script> script = script_data.GetScript();
2036     // Skip unnamed scripts.
2037     Local<String> name;
2038     if (!script->Name().ToLocal(&name)) continue;
2039     std::string file_name = ToSTLString(isolate, name);
2040     // Skip scripts not backed by a file.
2041     if (!std::ifstream(file_name).good()) continue;
2042     sink << "SF:";
2043     sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl;
2044     std::vector<uint32_t> lines;
2045     for (size_t j = 0; j < script_data.FunctionCount(); j++) {
2046       debug::Coverage::FunctionData function_data =
2047           script_data.GetFunctionData(j);
2048 
2049       // Write function stats.
2050       {
2051         debug::Location start =
2052             script->GetSourceLocation(function_data.StartOffset());
2053         debug::Location end =
2054             script->GetSourceLocation(function_data.EndOffset());
2055         int start_line = start.GetLineNumber();
2056         int end_line = end.GetLineNumber();
2057         uint32_t count = function_data.Count();
2058 
2059         Local<String> name;
2060         std::stringstream name_stream;
2061         if (function_data.Name().ToLocal(&name)) {
2062           name_stream << ToSTLString(isolate, name);
2063         } else {
2064           name_stream << "<" << start_line + 1 << "-";
2065           name_stream << start.GetColumnNumber() << ">";
2066         }
2067 
2068         WriteLcovDataForNamedRange(sink, lines, name_stream.str(), start_line,
2069                                    end_line, count);
2070       }
2071 
2072       // Process inner blocks.
2073       for (size_t k = 0; k < function_data.BlockCount(); k++) {
2074         debug::Coverage::BlockData block_data = function_data.GetBlockData(k);
2075         int start_line = LineFromOffset(script, block_data.StartOffset());
2076         int end_line = LineFromOffset(script, block_data.EndOffset() - 1);
2077         uint32_t count = block_data.Count();
2078         WriteLcovDataForRange(lines, start_line, end_line, count);
2079       }
2080     }
2081     // Write per-line coverage. LCOV uses 1-based line numbers.
2082     for (size_t i = 0; i < lines.size(); i++) {
2083       sink << "DA:" << (i + 1) << "," << lines[i] << std::endl;
2084     }
2085     sink << "end_of_record" << std::endl;
2086   }
2087 }
2088 
OnExit(v8::Isolate * isolate)2089 void Shell::OnExit(v8::Isolate* isolate) {
2090   // Dump basic block profiling data.
2091   if (i::FLAG_turbo_profiling) {
2092     i::BasicBlockProfiler* profiler = i::BasicBlockProfiler::Get();
2093     i::StdoutStream{} << *profiler;
2094   }
2095   isolate->Dispose();
2096 
2097   if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
2098     const int number_of_counters = static_cast<int>(counter_map_->size());
2099     CounterAndKey* counters = new CounterAndKey[number_of_counters];
2100     int j = 0;
2101     for (auto map_entry : *counter_map_) {
2102       counters[j].counter = map_entry.second;
2103       counters[j].key = map_entry.first;
2104       j++;
2105     }
2106     std::sort(counters, counters + number_of_counters);
2107 
2108     if (i::FLAG_dump_counters_nvp) {
2109       // Dump counters as name-value pairs.
2110       for (j = 0; j < number_of_counters; j++) {
2111         Counter* counter = counters[j].counter;
2112         const char* key = counters[j].key;
2113         if (counter->is_histogram()) {
2114           printf("\"c:%s\"=%i\n", key, counter->count());
2115           printf("\"t:%s\"=%i\n", key, counter->sample_total());
2116         } else {
2117           printf("\"%s\"=%i\n", key, counter->count());
2118         }
2119       }
2120     } else {
2121       // Dump counters in formatted boxes.
2122       printf(
2123           "+----------------------------------------------------------------+"
2124           "-------------+\n");
2125       printf(
2126           "| Name                                                           |"
2127           " Value       |\n");
2128       printf(
2129           "+----------------------------------------------------------------+"
2130           "-------------+\n");
2131       for (j = 0; j < number_of_counters; j++) {
2132         Counter* counter = counters[j].counter;
2133         const char* key = counters[j].key;
2134         if (counter->is_histogram()) {
2135           printf("| c:%-60s | %11i |\n", key, counter->count());
2136           printf("| t:%-60s | %11i |\n", key, counter->sample_total());
2137         } else {
2138           printf("| %-62s | %11i |\n", key, counter->count());
2139         }
2140       }
2141       printf(
2142           "+----------------------------------------------------------------+"
2143           "-------------+\n");
2144     }
2145     delete [] counters;
2146   }
2147 
2148   delete counters_file_;
2149   delete counter_map_;
2150 }
2151 
2152 
FOpen(const char * path,const char * mode)2153 static FILE* FOpen(const char* path, const char* mode) {
2154 #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
2155   FILE* result;
2156   if (fopen_s(&result, path, mode) == 0) {
2157     return result;
2158   } else {
2159     return nullptr;
2160   }
2161 #else
2162   FILE* file = fopen(path, mode);
2163   if (file == nullptr) return nullptr;
2164   struct stat file_stat;
2165   if (fstat(fileno(file), &file_stat) != 0) return nullptr;
2166   bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
2167   if (is_regular_file) return file;
2168   fclose(file);
2169   return nullptr;
2170 #endif
2171 }
2172 
ReadChars(const char * name,int * size_out)2173 static char* ReadChars(const char* name, int* size_out) {
2174   if (Shell::options.read_from_tcp_port >= 0) {
2175     return Shell::ReadCharsFromTcpPort(name, size_out);
2176   }
2177 
2178   FILE* file = FOpen(name, "rb");
2179   if (file == nullptr) return nullptr;
2180 
2181   fseek(file, 0, SEEK_END);
2182   size_t size = ftell(file);
2183   rewind(file);
2184 
2185   char* chars = new char[size + 1];
2186   chars[size] = '\0';
2187   for (size_t i = 0; i < size;) {
2188     i += fread(&chars[i], 1, size - i, file);
2189     if (ferror(file)) {
2190       fclose(file);
2191       delete[] chars;
2192       return nullptr;
2193     }
2194   }
2195   fclose(file);
2196   *size_out = static_cast<int>(size);
2197   return chars;
2198 }
2199 
2200 
2201 struct DataAndPersistent {
2202   uint8_t* data;
2203   int byte_length;
2204   Global<ArrayBuffer> handle;
2205 };
2206 
2207 
ReadBufferWeakCallback(const v8::WeakCallbackInfo<DataAndPersistent> & data)2208 static void ReadBufferWeakCallback(
2209     const v8::WeakCallbackInfo<DataAndPersistent>& data) {
2210   int byte_length = data.GetParameter()->byte_length;
2211   data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
2212       -static_cast<intptr_t>(byte_length));
2213 
2214   delete[] data.GetParameter()->data;
2215   data.GetParameter()->handle.Reset();
2216   delete data.GetParameter();
2217 }
2218 
2219 
ReadBuffer(const v8::FunctionCallbackInfo<v8::Value> & args)2220 void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) {
2221   static_assert(sizeof(char) == sizeof(uint8_t),
2222                 "char and uint8_t should both have 1 byte");
2223   Isolate* isolate = args.GetIsolate();
2224   String::Utf8Value filename(isolate, args[0]);
2225   int length;
2226   if (*filename == nullptr) {
2227     Throw(isolate, "Error loading file");
2228     return;
2229   }
2230 
2231   DataAndPersistent* data = new DataAndPersistent;
2232   data->data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
2233   if (data->data == nullptr) {
2234     delete data;
2235     Throw(isolate, "Error reading file");
2236     return;
2237   }
2238   data->byte_length = length;
2239   Local<v8::ArrayBuffer> buffer = ArrayBuffer::New(isolate, data->data, length);
2240   data->handle.Reset(isolate, buffer);
2241   data->handle.SetWeak(data, ReadBufferWeakCallback,
2242                        v8::WeakCallbackType::kParameter);
2243   isolate->AdjustAmountOfExternalAllocatedMemory(length);
2244 
2245   args.GetReturnValue().Set(buffer);
2246 }
2247 
2248 // Reads a file into a v8 string.
ReadFile(Isolate * isolate,const char * name)2249 Local<String> Shell::ReadFile(Isolate* isolate, const char* name) {
2250   int size = 0;
2251   char* chars = ReadChars(name, &size);
2252   if (chars == nullptr) return Local<String>();
2253   Local<String> result;
2254   if (i::FLAG_use_external_strings && i::String::IsAscii(chars, size)) {
2255     String::ExternalOneByteStringResource* resource =
2256         new ExternalOwningOneByteStringResource(
2257             std::unique_ptr<const char[]>(chars), size);
2258     result = String::NewExternalOneByte(isolate, resource).ToLocalChecked();
2259   } else {
2260     result = String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
2261                  .ToLocalChecked();
2262     delete[] chars;
2263   }
2264   return result;
2265 }
2266 
2267 
RunShell(Isolate * isolate)2268 void Shell::RunShell(Isolate* isolate) {
2269   HandleScope outer_scope(isolate);
2270   v8::Local<v8::Context> context =
2271       v8::Local<v8::Context>::New(isolate, evaluation_context_);
2272   v8::Context::Scope context_scope(context);
2273   PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2274   Local<String> name =
2275       String::NewFromUtf8(isolate, "(d8)", NewStringType::kNormal)
2276           .ToLocalChecked();
2277   printf("V8 version %s\n", V8::GetVersion());
2278   while (true) {
2279     HandleScope inner_scope(isolate);
2280     printf("d8> ");
2281     Local<String> input = Shell::ReadFromStdin(isolate);
2282     if (input.IsEmpty()) break;
2283     ExecuteString(isolate, input, name, kPrintResult, kReportExceptions,
2284                   kProcessMessageQueue);
2285   }
2286   printf("\n");
2287   // We need to explicitly clean up the module embedder data for
2288   // the interative shell context.
2289   DisposeModuleEmbedderData(context);
2290 }
2291 
2292 class InspectorFrontend final : public v8_inspector::V8Inspector::Channel {
2293  public:
InspectorFrontend(Local<Context> context)2294   explicit InspectorFrontend(Local<Context> context) {
2295     isolate_ = context->GetIsolate();
2296     context_.Reset(isolate_, context);
2297   }
2298   virtual ~InspectorFrontend() = default;
2299 
2300  private:
sendResponse(int callId,std::unique_ptr<v8_inspector::StringBuffer> message)2301   void sendResponse(
2302       int callId,
2303       std::unique_ptr<v8_inspector::StringBuffer> message) override {
2304     Send(message->string());
2305   }
sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message)2306   void sendNotification(
2307       std::unique_ptr<v8_inspector::StringBuffer> message) override {
2308     Send(message->string());
2309   }
flushProtocolNotifications()2310   void flushProtocolNotifications() override {}
2311 
Send(const v8_inspector::StringView & string)2312   void Send(const v8_inspector::StringView& string) {
2313     v8::Isolate::AllowJavascriptExecutionScope allow_script(isolate_);
2314     int length = static_cast<int>(string.length());
2315     DCHECK_LT(length, v8::String::kMaxLength);
2316     Local<String> message =
2317         (string.is8Bit()
2318              ? v8::String::NewFromOneByte(
2319                    isolate_,
2320                    reinterpret_cast<const uint8_t*>(string.characters8()),
2321                    v8::NewStringType::kNormal, length)
2322              : v8::String::NewFromTwoByte(
2323                    isolate_,
2324                    reinterpret_cast<const uint16_t*>(string.characters16()),
2325                    v8::NewStringType::kNormal, length))
2326             .ToLocalChecked();
2327     Local<String> callback_name =
2328         v8::String::NewFromUtf8(isolate_, "receive", v8::NewStringType::kNormal)
2329             .ToLocalChecked();
2330     Local<Context> context = context_.Get(isolate_);
2331     Local<Value> callback =
2332         context->Global()->Get(context, callback_name).ToLocalChecked();
2333     if (callback->IsFunction()) {
2334       v8::TryCatch try_catch(isolate_);
2335       Local<Value> args[] = {message};
2336       USE(Local<Function>::Cast(callback)->Call(context, Undefined(isolate_), 1,
2337                                                 args));
2338 #ifdef DEBUG
2339       if (try_catch.HasCaught()) {
2340         Local<Object> exception = Local<Object>::Cast(try_catch.Exception());
2341         Local<String> key = v8::String::NewFromUtf8(isolate_, "message",
2342                                                     v8::NewStringType::kNormal)
2343                                 .ToLocalChecked();
2344         Local<String> expected =
2345             v8::String::NewFromUtf8(isolate_,
2346                                     "Maximum call stack size exceeded",
2347                                     v8::NewStringType::kNormal)
2348                 .ToLocalChecked();
2349         Local<Value> value = exception->Get(context, key).ToLocalChecked();
2350         DCHECK(value->StrictEquals(expected));
2351       }
2352 #endif
2353     }
2354   }
2355 
2356   Isolate* isolate_;
2357   Global<Context> context_;
2358 };
2359 
2360 class InspectorClient : public v8_inspector::V8InspectorClient {
2361  public:
InspectorClient(Local<Context> context,bool connect)2362   InspectorClient(Local<Context> context, bool connect) {
2363     if (!connect) return;
2364     isolate_ = context->GetIsolate();
2365     channel_.reset(new InspectorFrontend(context));
2366     inspector_ = v8_inspector::V8Inspector::create(isolate_, this);
2367     session_ =
2368         inspector_->connect(1, channel_.get(), v8_inspector::StringView());
2369     context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
2370     inspector_->contextCreated(v8_inspector::V8ContextInfo(
2371         context, kContextGroupId, v8_inspector::StringView()));
2372 
2373     Local<Value> function =
2374         FunctionTemplate::New(isolate_, SendInspectorMessage)
2375             ->GetFunction(context)
2376             .ToLocalChecked();
2377     Local<String> function_name =
2378         String::NewFromUtf8(isolate_, "send", NewStringType::kNormal)
2379             .ToLocalChecked();
2380     CHECK(context->Global()->Set(context, function_name, function).FromJust());
2381 
2382     context_.Reset(isolate_, context);
2383   }
2384 
2385  private:
GetSession(Local<Context> context)2386   static v8_inspector::V8InspectorSession* GetSession(Local<Context> context) {
2387     InspectorClient* inspector_client = static_cast<InspectorClient*>(
2388         context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex));
2389     return inspector_client->session_.get();
2390   }
2391 
ensureDefaultContextInGroup(int group_id)2392   Local<Context> ensureDefaultContextInGroup(int group_id) override {
2393     DCHECK(isolate_);
2394     DCHECK_EQ(kContextGroupId, group_id);
2395     return context_.Get(isolate_);
2396   }
2397 
SendInspectorMessage(const v8::FunctionCallbackInfo<v8::Value> & args)2398   static void SendInspectorMessage(
2399       const v8::FunctionCallbackInfo<v8::Value>& args) {
2400     Isolate* isolate = args.GetIsolate();
2401     v8::HandleScope handle_scope(isolate);
2402     Local<Context> context = isolate->GetCurrentContext();
2403     args.GetReturnValue().Set(Undefined(isolate));
2404     Local<String> message = args[0]->ToString(context).ToLocalChecked();
2405     v8_inspector::V8InspectorSession* session =
2406         InspectorClient::GetSession(context);
2407     int length = message->Length();
2408     std::unique_ptr<uint16_t[]> buffer(new uint16_t[length]);
2409     message->Write(isolate, buffer.get(), 0, length);
2410     v8_inspector::StringView message_view(buffer.get(), length);
2411     session->dispatchProtocolMessage(message_view);
2412     args.GetReturnValue().Set(True(isolate));
2413   }
2414 
2415   static const int kContextGroupId = 1;
2416 
2417   std::unique_ptr<v8_inspector::V8Inspector> inspector_;
2418   std::unique_ptr<v8_inspector::V8InspectorSession> session_;
2419   std::unique_ptr<v8_inspector::V8Inspector::Channel> channel_;
2420   Global<Context> context_;
2421   Isolate* isolate_;
2422 };
2423 
~SourceGroup()2424 SourceGroup::~SourceGroup() {
2425   delete thread_;
2426   thread_ = nullptr;
2427 }
2428 
ends_with(const char * input,const char * suffix)2429 bool ends_with(const char* input, const char* suffix) {
2430   size_t input_length = strlen(input);
2431   size_t suffix_length = strlen(suffix);
2432   if (suffix_length <= input_length) {
2433     return strcmp(input + input_length - suffix_length, suffix) == 0;
2434   }
2435   return false;
2436 }
2437 
Execute(Isolate * isolate)2438 void SourceGroup::Execute(Isolate* isolate) {
2439   bool exception_was_thrown = false;
2440   for (int i = begin_offset_; i < end_offset_; ++i) {
2441     const char* arg = argv_[i];
2442     if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
2443       // Execute argument given to -e option directly.
2444       HandleScope handle_scope(isolate);
2445       Local<String> file_name =
2446           String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
2447               .ToLocalChecked();
2448       Local<String> source =
2449           String::NewFromUtf8(isolate, argv_[i + 1], NewStringType::kNormal)
2450               .ToLocalChecked();
2451       Shell::set_script_executed();
2452       if (!Shell::ExecuteString(isolate, source, file_name,
2453                                 Shell::kNoPrintResult, Shell::kReportExceptions,
2454                                 Shell::kNoProcessMessageQueue)) {
2455         exception_was_thrown = true;
2456         break;
2457       }
2458       ++i;
2459       continue;
2460     } else if (ends_with(arg, ".mjs")) {
2461       Shell::set_script_executed();
2462       if (!Shell::ExecuteModule(isolate, arg)) {
2463         exception_was_thrown = true;
2464         break;
2465       }
2466       continue;
2467     } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) {
2468       // Treat the next file as a module.
2469       arg = argv_[++i];
2470       Shell::set_script_executed();
2471       if (!Shell::ExecuteModule(isolate, arg)) {
2472         exception_was_thrown = true;
2473         break;
2474       }
2475       continue;
2476     } else if (arg[0] == '-') {
2477       // Ignore other options. They have been parsed already.
2478       continue;
2479     }
2480 
2481     // Use all other arguments as names of files to load and run.
2482     HandleScope handle_scope(isolate);
2483     Local<String> file_name =
2484         String::NewFromUtf8(isolate, arg, NewStringType::kNormal)
2485             .ToLocalChecked();
2486     Local<String> source = ReadFile(isolate, arg);
2487     if (source.IsEmpty()) {
2488       printf("Error reading '%s'\n", arg);
2489       base::OS::ExitProcess(1);
2490     }
2491     Shell::set_script_executed();
2492     if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
2493                               Shell::kReportExceptions,
2494                               Shell::kProcessMessageQueue)) {
2495       exception_was_thrown = true;
2496       break;
2497     }
2498   }
2499   if (exception_was_thrown != Shell::options.expected_to_throw) {
2500     base::OS::ExitProcess(1);
2501   }
2502 }
2503 
ReadFile(Isolate * isolate,const char * name)2504 Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
2505   return Shell::ReadFile(isolate, name);
2506 }
2507 
IsolateThread(SourceGroup * group)2508 SourceGroup::IsolateThread::IsolateThread(SourceGroup* group)
2509     : base::Thread(GetThreadOptions("IsolateThread")), group_(group) {}
2510 
ExecuteInThread()2511 void SourceGroup::ExecuteInThread() {
2512   Isolate::CreateParams create_params;
2513   create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2514   Isolate* isolate = Isolate::New(create_params);
2515   isolate->SetHostImportModuleDynamicallyCallback(
2516       Shell::HostImportModuleDynamically);
2517   isolate->SetHostInitializeImportMetaObjectCallback(
2518       Shell::HostInitializeImportMetaObject);
2519   Shell::SetWaitUntilDone(isolate, false);
2520   D8Console console(isolate);
2521   debug::SetConsoleDelegate(isolate, &console);
2522   for (int i = 0; i < Shell::options.stress_runs; ++i) {
2523     next_semaphore_.Wait();
2524     {
2525       Isolate::Scope iscope(isolate);
2526       PerIsolateData data(isolate);
2527       {
2528         HandleScope scope(isolate);
2529         Local<Context> context = Shell::CreateEvaluationContext(isolate);
2530         {
2531           Context::Scope cscope(context);
2532           InspectorClient inspector_client(context,
2533                                            Shell::options.enable_inspector);
2534           PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2535           Execute(isolate);
2536           Shell::CompleteMessageLoop(isolate);
2537         }
2538         DisposeModuleEmbedderData(context);
2539       }
2540       Shell::CollectGarbage(isolate);
2541     }
2542     done_semaphore_.Signal();
2543   }
2544 
2545   isolate->Dispose();
2546 }
2547 
2548 
StartExecuteInThread()2549 void SourceGroup::StartExecuteInThread() {
2550   if (thread_ == nullptr) {
2551     thread_ = new IsolateThread(this);
2552     thread_->Start();
2553   }
2554   next_semaphore_.Signal();
2555 }
2556 
2557 
WaitForThread()2558 void SourceGroup::WaitForThread() {
2559   if (thread_ == nullptr) return;
2560   done_semaphore_.Wait();
2561 }
2562 
2563 
JoinThread()2564 void SourceGroup::JoinThread() {
2565   if (thread_ == nullptr) return;
2566   thread_->Join();
2567 }
2568 
~ExternalizedContents()2569 ExternalizedContents::~ExternalizedContents() {
2570   if (data_ != nullptr) {
2571     deleter_(data_, length_, deleter_data_);
2572   }
2573 }
2574 
Enqueue(std::unique_ptr<SerializationData> data)2575 void SerializationDataQueue::Enqueue(std::unique_ptr<SerializationData> data) {
2576   base::LockGuard<base::Mutex> lock_guard(&mutex_);
2577   data_.push_back(std::move(data));
2578 }
2579 
Dequeue(std::unique_ptr<SerializationData> * out_data)2580 bool SerializationDataQueue::Dequeue(
2581     std::unique_ptr<SerializationData>* out_data) {
2582   out_data->reset();
2583   base::LockGuard<base::Mutex> lock_guard(&mutex_);
2584   if (data_.empty()) return false;
2585   *out_data = std::move(data_[0]);
2586   data_.erase(data_.begin());
2587   return true;
2588 }
2589 
2590 
IsEmpty()2591 bool SerializationDataQueue::IsEmpty() {
2592   base::LockGuard<base::Mutex> lock_guard(&mutex_);
2593   return data_.empty();
2594 }
2595 
2596 
Clear()2597 void SerializationDataQueue::Clear() {
2598   base::LockGuard<base::Mutex> lock_guard(&mutex_);
2599   data_.clear();
2600 }
2601 
Worker()2602 Worker::Worker()
2603     : in_semaphore_(0),
2604       out_semaphore_(0),
2605       thread_(nullptr),
2606       script_(nullptr),
2607       running_(false) {}
2608 
~Worker()2609 Worker::~Worker() {
2610   delete thread_;
2611   thread_ = nullptr;
2612   delete[] script_;
2613   script_ = nullptr;
2614   in_queue_.Clear();
2615   out_queue_.Clear();
2616 }
2617 
2618 
StartExecuteInThread(const char * script)2619 void Worker::StartExecuteInThread(const char* script) {
2620   running_ = true;
2621   script_ = i::StrDup(script);
2622   thread_ = new WorkerThread(this);
2623   thread_->Start();
2624 }
2625 
PostMessage(std::unique_ptr<SerializationData> data)2626 void Worker::PostMessage(std::unique_ptr<SerializationData> data) {
2627   in_queue_.Enqueue(std::move(data));
2628   in_semaphore_.Signal();
2629 }
2630 
GetMessage()2631 std::unique_ptr<SerializationData> Worker::GetMessage() {
2632   std::unique_ptr<SerializationData> result;
2633   while (!out_queue_.Dequeue(&result)) {
2634     // If the worker is no longer running, and there are no messages in the
2635     // queue, don't expect any more messages from it.
2636     if (!base::Relaxed_Load(&running_)) break;
2637     out_semaphore_.Wait();
2638   }
2639   return result;
2640 }
2641 
2642 
Terminate()2643 void Worker::Terminate() {
2644   base::Relaxed_Store(&running_, false);
2645   // Post nullptr to wake the Worker thread message loop, and tell it to stop
2646   // running.
2647   PostMessage(nullptr);
2648 }
2649 
2650 
WaitForThread()2651 void Worker::WaitForThread() {
2652   Terminate();
2653   thread_->Join();
2654 }
2655 
2656 
ExecuteInThread()2657 void Worker::ExecuteInThread() {
2658   Isolate::CreateParams create_params;
2659   create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2660   Isolate* isolate = Isolate::New(create_params);
2661   isolate->SetHostImportModuleDynamicallyCallback(
2662       Shell::HostImportModuleDynamically);
2663   isolate->SetHostInitializeImportMetaObjectCallback(
2664       Shell::HostInitializeImportMetaObject);
2665   D8Console console(isolate);
2666   debug::SetConsoleDelegate(isolate, &console);
2667   {
2668     Isolate::Scope iscope(isolate);
2669     {
2670       HandleScope scope(isolate);
2671       PerIsolateData data(isolate);
2672       Local<Context> context = Shell::CreateEvaluationContext(isolate);
2673       {
2674         Context::Scope cscope(context);
2675         PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2676 
2677         Local<Object> global = context->Global();
2678         Local<Value> this_value = External::New(isolate, this);
2679         Local<FunctionTemplate> postmessage_fun_template =
2680             FunctionTemplate::New(isolate, PostMessageOut, this_value);
2681 
2682         Local<Function> postmessage_fun;
2683         if (postmessage_fun_template->GetFunction(context)
2684                 .ToLocal(&postmessage_fun)) {
2685           global->Set(context, String::NewFromUtf8(isolate, "postMessage",
2686                                                    NewStringType::kNormal)
2687                                    .ToLocalChecked(),
2688                       postmessage_fun).FromJust();
2689         }
2690 
2691         // First run the script
2692         Local<String> file_name =
2693             String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
2694                 .ToLocalChecked();
2695         Local<String> source =
2696             String::NewFromUtf8(isolate, script_, NewStringType::kNormal)
2697                 .ToLocalChecked();
2698         if (Shell::ExecuteString(
2699                 isolate, source, file_name, Shell::kNoPrintResult,
2700                 Shell::kReportExceptions, Shell::kProcessMessageQueue)) {
2701           // Get the message handler
2702           Local<Value> onmessage =
2703               global->Get(context, String::NewFromUtf8(isolate, "onmessage",
2704                                                        NewStringType::kNormal)
2705                                        .ToLocalChecked()).ToLocalChecked();
2706           if (onmessage->IsFunction()) {
2707             Local<Function> onmessage_fun = Local<Function>::Cast(onmessage);
2708             // Now wait for messages
2709             while (true) {
2710               in_semaphore_.Wait();
2711               std::unique_ptr<SerializationData> data;
2712               if (!in_queue_.Dequeue(&data)) continue;
2713               if (!data) {
2714                 break;
2715               }
2716               v8::TryCatch try_catch(isolate);
2717               Local<Value> value;
2718               if (Shell::DeserializeValue(isolate, std::move(data))
2719                       .ToLocal(&value)) {
2720                 Local<Value> argv[] = {value};
2721                 MaybeLocal<Value> result =
2722                     onmessage_fun->Call(context, global, 1, argv);
2723                 USE(result);
2724               }
2725               if (try_catch.HasCaught()) {
2726                 Shell::ReportException(isolate, &try_catch);
2727               }
2728             }
2729           }
2730         }
2731       }
2732       DisposeModuleEmbedderData(context);
2733     }
2734     Shell::CollectGarbage(isolate);
2735   }
2736   isolate->Dispose();
2737 
2738   // Post nullptr to wake the thread waiting on GetMessage() if there is one.
2739   out_queue_.Enqueue(nullptr);
2740   out_semaphore_.Signal();
2741 }
2742 
2743 
PostMessageOut(const v8::FunctionCallbackInfo<v8::Value> & args)2744 void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) {
2745   Isolate* isolate = args.GetIsolate();
2746   HandleScope handle_scope(isolate);
2747 
2748   if (args.Length() < 1) {
2749     Throw(isolate, "Invalid argument");
2750     return;
2751   }
2752 
2753   Local<Value> message = args[0];
2754   Local<Value> transfer = Undefined(isolate);
2755   std::unique_ptr<SerializationData> data =
2756       Shell::SerializeValue(isolate, message, transfer);
2757   if (data) {
2758     DCHECK(args.Data()->IsExternal());
2759     Local<External> this_value = Local<External>::Cast(args.Data());
2760     Worker* worker = static_cast<Worker*>(this_value->Value());
2761     worker->out_queue_.Enqueue(std::move(data));
2762     worker->out_semaphore_.Signal();
2763   }
2764 }
2765 
2766 
SetFlagsFromString(const char * flags)2767 void SetFlagsFromString(const char* flags) {
2768   v8::V8::SetFlagsFromString(flags, static_cast<int>(strlen(flags)));
2769 }
2770 
2771 
SetOptions(int argc,char * argv[])2772 bool Shell::SetOptions(int argc, char* argv[]) {
2773   bool logfile_per_isolate = false;
2774   for (int i = 0; i < argc; i++) {
2775     if (strcmp(argv[i], "--stress-opt") == 0) {
2776       options.stress_opt = true;
2777       argv[i] = nullptr;
2778     } else if (strcmp(argv[i], "--nostress-opt") == 0 ||
2779                strcmp(argv[i], "--no-stress-opt") == 0) {
2780       options.stress_opt = false;
2781       argv[i] = nullptr;
2782     } else if (strcmp(argv[i], "--stress-deopt") == 0) {
2783       options.stress_deopt = true;
2784       argv[i] = nullptr;
2785     } else if (strcmp(argv[i], "--stress-background-compile") == 0) {
2786       options.stress_background_compile = true;
2787       argv[i] = nullptr;
2788     } else if (strcmp(argv[i], "--nostress-background-compile") == 0 ||
2789                strcmp(argv[i], "--no-stress-background-compile") == 0) {
2790       options.stress_background_compile = false;
2791       argv[i] = nullptr;
2792     } else if (strcmp(argv[i], "--noalways-opt") == 0 ||
2793                strcmp(argv[i], "--no-always-opt") == 0) {
2794       // No support for stressing if we can't use --always-opt.
2795       options.stress_opt = false;
2796       options.stress_deopt = false;
2797     } else if (strcmp(argv[i], "--logfile-per-isolate") == 0) {
2798       logfile_per_isolate = true;
2799       argv[i] = nullptr;
2800     } else if (strcmp(argv[i], "--shell") == 0) {
2801       options.interactive_shell = true;
2802       argv[i] = nullptr;
2803     } else if (strcmp(argv[i], "--test") == 0) {
2804       options.test_shell = true;
2805       argv[i] = nullptr;
2806     } else if (strcmp(argv[i], "--notest") == 0 ||
2807                strcmp(argv[i], "--no-test") == 0) {
2808       options.test_shell = false;
2809       argv[i] = nullptr;
2810     } else if (strcmp(argv[i], "--send-idle-notification") == 0) {
2811       options.send_idle_notification = true;
2812       argv[i] = nullptr;
2813     } else if (strcmp(argv[i], "--invoke-weak-callbacks") == 0) {
2814       options.invoke_weak_callbacks = true;
2815       // TODO(jochen) See issue 3351
2816       options.send_idle_notification = true;
2817       argv[i] = nullptr;
2818     } else if (strcmp(argv[i], "--omit-quit") == 0) {
2819       options.omit_quit = true;
2820       argv[i] = nullptr;
2821     } else if (strcmp(argv[i], "--no-wait-for-wasm") == 0) {
2822       // TODO(herhut) Remove this flag once wasm compilation is fully
2823       // isolate-independent.
2824       options.wait_for_wasm = false;
2825       argv[i] = nullptr;
2826     } else if (strcmp(argv[i], "-f") == 0) {
2827       // Ignore any -f flags for compatibility with other stand-alone
2828       // JavaScript engines.
2829       continue;
2830     } else if (strcmp(argv[i], "--isolate") == 0) {
2831       options.num_isolates++;
2832     } else if (strcmp(argv[i], "--throws") == 0) {
2833       options.expected_to_throw = true;
2834       argv[i] = nullptr;
2835     } else if (strncmp(argv[i], "--icu-data-file=", 16) == 0) {
2836       options.icu_data_file = argv[i] + 16;
2837       argv[i] = nullptr;
2838 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
2839     } else if (strncmp(argv[i], "--natives_blob=", 15) == 0) {
2840       options.natives_blob = argv[i] + 15;
2841       argv[i] = nullptr;
2842     } else if (strncmp(argv[i], "--snapshot_blob=", 16) == 0) {
2843       options.snapshot_blob = argv[i] + 16;
2844       argv[i] = nullptr;
2845 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
2846     } else if (strcmp(argv[i], "--cache") == 0 ||
2847                strncmp(argv[i], "--cache=", 8) == 0) {
2848       const char* value = argv[i] + 7;
2849       if (!*value || strncmp(value, "=code", 6) == 0) {
2850         options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2851         options.code_cache_options =
2852             ShellOptions::CodeCacheOptions::kProduceCache;
2853       } else if (strncmp(value, "=none", 6) == 0) {
2854         options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2855         options.code_cache_options =
2856             ShellOptions::CodeCacheOptions::kNoProduceCache;
2857       } else if (strncmp(value, "=after-execute", 15) == 0) {
2858         options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2859         options.code_cache_options =
2860             ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute;
2861       } else if (strncmp(value, "=full-code-cache", 17) == 0) {
2862         options.compile_options = v8::ScriptCompiler::kEagerCompile;
2863         options.code_cache_options =
2864             ShellOptions::CodeCacheOptions::kProduceCache;
2865       } else {
2866         printf("Unknown option to --cache.\n");
2867         return false;
2868       }
2869       argv[i] = nullptr;
2870     } else if (strcmp(argv[i], "--enable-tracing") == 0) {
2871       options.trace_enabled = true;
2872       argv[i] = nullptr;
2873     } else if (strncmp(argv[i], "--trace-path=", 13) == 0) {
2874       options.trace_path = argv[i] + 13;
2875       argv[i] = nullptr;
2876     } else if (strncmp(argv[i], "--trace-config=", 15) == 0) {
2877       options.trace_config = argv[i] + 15;
2878       argv[i] = nullptr;
2879     } else if (strcmp(argv[i], "--enable-inspector") == 0) {
2880       options.enable_inspector = true;
2881       argv[i] = nullptr;
2882     } else if (strncmp(argv[i], "--lcov=", 7) == 0) {
2883       options.lcov_file = argv[i] + 7;
2884       argv[i] = nullptr;
2885     } else if (strcmp(argv[i], "--disable-in-process-stack-traces") == 0) {
2886       options.disable_in_process_stack_traces = true;
2887       argv[i] = nullptr;
2888 #ifdef V8_OS_POSIX
2889     } else if (strncmp(argv[i], "--read-from-tcp-port=", 21) == 0) {
2890       options.read_from_tcp_port = atoi(argv[i] + 21);
2891       argv[i] = nullptr;
2892 #endif  // V8_OS_POSIX
2893     } else if (strcmp(argv[i], "--enable-os-system") == 0) {
2894       options.enable_os_system = true;
2895       argv[i] = nullptr;
2896     } else if (strcmp(argv[i], "--quiet-load") == 0) {
2897       options.quiet_load = true;
2898       argv[i] = nullptr;
2899     } else if (strncmp(argv[i], "--thread-pool-size=", 19) == 0) {
2900       options.thread_pool_size = atoi(argv[i] + 19);
2901       argv[i] = nullptr;
2902     }
2903   }
2904 
2905   v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
2906   options.mock_arraybuffer_allocator = i::FLAG_mock_arraybuffer_allocator;
2907 
2908   // Set up isolated source groups.
2909   options.isolate_sources = new SourceGroup[options.num_isolates];
2910   SourceGroup* current = options.isolate_sources;
2911   current->Begin(argv, 1);
2912   for (int i = 1; i < argc; i++) {
2913     const char* str = argv[i];
2914     if (strcmp(str, "--isolate") == 0) {
2915       current->End(i);
2916       current++;
2917       current->Begin(argv, i + 1);
2918     } else if (strcmp(str, "--module") == 0) {
2919       // Pass on to SourceGroup, which understands this option.
2920     } else if (strncmp(str, "--", 2) == 0) {
2921       printf("Warning: unknown flag %s.\nTry --help for options\n", str);
2922     } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
2923       set_script_executed();
2924     } else if (strncmp(str, "-", 1) != 0) {
2925       // Not a flag, so it must be a script to execute.
2926       set_script_executed();
2927     }
2928   }
2929   current->End(argc);
2930 
2931   if (!logfile_per_isolate && options.num_isolates) {
2932     SetFlagsFromString("--nologfile_per_isolate");
2933   }
2934 
2935   return true;
2936 }
2937 
RunMain(Isolate * isolate,int argc,char * argv[],bool last_run)2938 int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
2939   for (int i = 1; i < options.num_isolates; ++i) {
2940     options.isolate_sources[i].StartExecuteInThread();
2941   }
2942   {
2943     SetWaitUntilDone(isolate, false);
2944     if (options.lcov_file) {
2945       debug::Coverage::SelectMode(isolate, debug::Coverage::kBlockCount);
2946     }
2947     HandleScope scope(isolate);
2948     Local<Context> context = CreateEvaluationContext(isolate);
2949     bool use_existing_context = last_run && use_interactive_shell();
2950     if (use_existing_context) {
2951       // Keep using the same context in the interactive shell.
2952       evaluation_context_.Reset(isolate, context);
2953     }
2954     {
2955       Context::Scope cscope(context);
2956       InspectorClient inspector_client(context, options.enable_inspector);
2957       PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2958       options.isolate_sources[0].Execute(isolate);
2959       CompleteMessageLoop(isolate);
2960     }
2961     if (!use_existing_context) {
2962       DisposeModuleEmbedderData(context);
2963     }
2964     WriteLcovData(isolate, options.lcov_file);
2965   }
2966   CollectGarbage(isolate);
2967   for (int i = 1; i < options.num_isolates; ++i) {
2968     if (last_run) {
2969       options.isolate_sources[i].JoinThread();
2970     } else {
2971       options.isolate_sources[i].WaitForThread();
2972     }
2973   }
2974   CleanupWorkers();
2975   return 0;
2976 }
2977 
2978 
CollectGarbage(Isolate * isolate)2979 void Shell::CollectGarbage(Isolate* isolate) {
2980   if (options.send_idle_notification) {
2981     const double kLongIdlePauseInSeconds = 1.0;
2982     isolate->ContextDisposedNotification();
2983     isolate->IdleNotificationDeadline(
2984         g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
2985   }
2986   if (options.invoke_weak_callbacks) {
2987     // By sending a low memory notifications, we will try hard to collect all
2988     // garbage and will therefore also invoke all weak callbacks of actually
2989     // unreachable persistent handles.
2990     isolate->LowMemoryNotification();
2991   }
2992 }
2993 
SetWaitUntilDone(Isolate * isolate,bool value)2994 void Shell::SetWaitUntilDone(Isolate* isolate, bool value) {
2995   base::LockGuard<base::Mutex> guard(isolate_status_lock_.Pointer());
2996   if (isolate_status_.count(isolate) == 0) {
2997     isolate_status_.insert(std::make_pair(isolate, value));
2998   } else {
2999     isolate_status_[isolate] = value;
3000   }
3001 }
3002 
3003 namespace {
ProcessMessages(Isolate * isolate,std::function<platform::MessageLoopBehavior ()> behavior)3004 bool ProcessMessages(Isolate* isolate,
3005                      std::function<platform::MessageLoopBehavior()> behavior) {
3006   Platform* platform = GetDefaultPlatform();
3007   while (true) {
3008     while (v8::platform::PumpMessageLoop(platform, isolate, behavior())) {
3009       isolate->RunMicrotasks();
3010     }
3011     if (platform->IdleTasksEnabled(isolate)) {
3012       v8::platform::RunIdleTasks(platform, isolate,
3013                                  50.0 / base::Time::kMillisecondsPerSecond);
3014     }
3015     HandleScope handle_scope(isolate);
3016     PerIsolateData* data = PerIsolateData::Get(isolate);
3017     Local<Function> callback;
3018     if (!data->GetTimeoutCallback().ToLocal(&callback)) break;
3019     Local<Context> context;
3020     if (!data->GetTimeoutContext().ToLocal(&context)) break;
3021     TryCatch try_catch(isolate);
3022     try_catch.SetVerbose(true);
3023     Context::Scope context_scope(context);
3024     if (callback->Call(context, Undefined(isolate), 0, nullptr).IsEmpty()) {
3025       Shell::ReportException(isolate, &try_catch);
3026       return false;
3027     }
3028   }
3029   return true;
3030 }
3031 }  // anonymous namespace
3032 
CompleteMessageLoop(Isolate * isolate)3033 void Shell::CompleteMessageLoop(Isolate* isolate) {
3034   auto get_waiting_behaviour = [isolate]() {
3035     base::LockGuard<base::Mutex> guard(isolate_status_lock_.Pointer());
3036     DCHECK_GT(isolate_status_.count(isolate), 0);
3037     i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
3038     i::wasm::WasmEngine* wasm_engine = i_isolate->wasm_engine();
3039     bool should_wait = (options.wait_for_wasm &&
3040                         wasm_engine->HasRunningCompileJob(i_isolate)) ||
3041                        isolate_status_[isolate];
3042     return should_wait ? platform::MessageLoopBehavior::kWaitForWork
3043                        : platform::MessageLoopBehavior::kDoNotWait;
3044   };
3045   ProcessMessages(isolate, get_waiting_behaviour);
3046 }
3047 
EmptyMessageQueues(Isolate * isolate)3048 bool Shell::EmptyMessageQueues(Isolate* isolate) {
3049   return ProcessMessages(
3050       isolate, []() { return platform::MessageLoopBehavior::kDoNotWait; });
3051 }
3052 
3053 class Serializer : public ValueSerializer::Delegate {
3054  public:
Serializer(Isolate * isolate)3055   explicit Serializer(Isolate* isolate)
3056       : isolate_(isolate),
3057         serializer_(isolate, this),
3058         current_memory_usage_(0) {}
3059 
WriteValue(Local<Context> context,Local<Value> value,Local<Value> transfer)3060   Maybe<bool> WriteValue(Local<Context> context, Local<Value> value,
3061                          Local<Value> transfer) {
3062     bool ok;
3063     DCHECK(!data_);
3064     data_.reset(new SerializationData);
3065     if (!PrepareTransfer(context, transfer).To(&ok)) {
3066       return Nothing<bool>();
3067     }
3068     serializer_.WriteHeader();
3069 
3070     if (!serializer_.WriteValue(context, value).To(&ok)) {
3071       data_.reset();
3072       return Nothing<bool>();
3073     }
3074 
3075     if (!FinalizeTransfer().To(&ok)) {
3076       return Nothing<bool>();
3077     }
3078 
3079     std::pair<uint8_t*, size_t> pair = serializer_.Release();
3080     data_->data_.reset(pair.first);
3081     data_->size_ = pair.second;
3082     return Just(true);
3083   }
3084 
Release()3085   std::unique_ptr<SerializationData> Release() { return std::move(data_); }
3086 
AppendExternalizedContentsTo(std::vector<ExternalizedContents> * to)3087   void AppendExternalizedContentsTo(std::vector<ExternalizedContents>* to) {
3088     to->insert(to->end(),
3089                std::make_move_iterator(externalized_contents_.begin()),
3090                std::make_move_iterator(externalized_contents_.end()));
3091     externalized_contents_.clear();
3092   }
3093 
3094  protected:
3095   // Implements ValueSerializer::Delegate.
ThrowDataCloneError(Local<String> message)3096   void ThrowDataCloneError(Local<String> message) override {
3097     isolate_->ThrowException(Exception::Error(message));
3098   }
3099 
GetSharedArrayBufferId(Isolate * isolate,Local<SharedArrayBuffer> shared_array_buffer)3100   Maybe<uint32_t> GetSharedArrayBufferId(
3101       Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) override {
3102     DCHECK_NOT_NULL(data_);
3103     for (size_t index = 0; index < shared_array_buffers_.size(); ++index) {
3104       if (shared_array_buffers_[index] == shared_array_buffer) {
3105         return Just<uint32_t>(static_cast<uint32_t>(index));
3106       }
3107     }
3108 
3109     size_t index = shared_array_buffers_.size();
3110     shared_array_buffers_.emplace_back(isolate_, shared_array_buffer);
3111     data_->shared_array_buffer_contents_.push_back(
3112         MaybeExternalize(shared_array_buffer));
3113     return Just<uint32_t>(static_cast<uint32_t>(index));
3114   }
3115 
GetWasmModuleTransferId(Isolate * isolate,Local<WasmCompiledModule> module)3116   Maybe<uint32_t> GetWasmModuleTransferId(
3117       Isolate* isolate, Local<WasmCompiledModule> module) override {
3118     DCHECK_NOT_NULL(data_);
3119     for (size_t index = 0; index < wasm_modules_.size(); ++index) {
3120       if (wasm_modules_[index] == module) {
3121         return Just<uint32_t>(static_cast<uint32_t>(index));
3122       }
3123     }
3124 
3125     size_t index = wasm_modules_.size();
3126     wasm_modules_.emplace_back(isolate_, module);
3127     data_->transferrable_modules_.push_back(module->GetTransferrableModule());
3128     return Just<uint32_t>(static_cast<uint32_t>(index));
3129   }
3130 
ReallocateBufferMemory(void * old_buffer,size_t size,size_t * actual_size)3131   void* ReallocateBufferMemory(void* old_buffer, size_t size,
3132                                size_t* actual_size) override {
3133     // Not accurate, because we don't take into account reallocated buffers,
3134     // but this is fine for testing.
3135     current_memory_usage_ += size;
3136     if (current_memory_usage_ > kMaxSerializerMemoryUsage) return nullptr;
3137 
3138     void* result = realloc(old_buffer, size);
3139     *actual_size = result ? size : 0;
3140     return result;
3141   }
3142 
FreeBufferMemory(void * buffer)3143   void FreeBufferMemory(void* buffer) override { free(buffer); }
3144 
3145  private:
PrepareTransfer(Local<Context> context,Local<Value> transfer)3146   Maybe<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
3147     if (transfer->IsArray()) {
3148       Local<Array> transfer_array = Local<Array>::Cast(transfer);
3149       uint32_t length = transfer_array->Length();
3150       for (uint32_t i = 0; i < length; ++i) {
3151         Local<Value> element;
3152         if (transfer_array->Get(context, i).ToLocal(&element)) {
3153           if (!element->IsArrayBuffer()) {
3154             Throw(isolate_, "Transfer array elements must be an ArrayBuffer");
3155             return Nothing<bool>();
3156           }
3157 
3158           Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(element);
3159           serializer_.TransferArrayBuffer(
3160               static_cast<uint32_t>(array_buffers_.size()), array_buffer);
3161           array_buffers_.emplace_back(isolate_, array_buffer);
3162         } else {
3163           return Nothing<bool>();
3164         }
3165       }
3166       return Just(true);
3167     } else if (transfer->IsUndefined()) {
3168       return Just(true);
3169     } else {
3170       Throw(isolate_, "Transfer list must be an Array or undefined");
3171       return Nothing<bool>();
3172     }
3173   }
3174 
3175   template <typename T>
MaybeExternalize(Local<T> array_buffer)3176   typename T::Contents MaybeExternalize(Local<T> array_buffer) {
3177     if (array_buffer->IsExternal()) {
3178       return array_buffer->GetContents();
3179     } else {
3180       typename T::Contents contents = array_buffer->Externalize();
3181       externalized_contents_.emplace_back(contents);
3182       return contents;
3183     }
3184   }
3185 
FinalizeTransfer()3186   Maybe<bool> FinalizeTransfer() {
3187     for (const auto& global_array_buffer : array_buffers_) {
3188       Local<ArrayBuffer> array_buffer =
3189           Local<ArrayBuffer>::New(isolate_, global_array_buffer);
3190       if (!array_buffer->IsNeuterable()) {
3191         Throw(isolate_, "ArrayBuffer could not be transferred");
3192         return Nothing<bool>();
3193       }
3194 
3195       ArrayBuffer::Contents contents = MaybeExternalize(array_buffer);
3196       array_buffer->Neuter();
3197       data_->array_buffer_contents_.push_back(contents);
3198     }
3199 
3200     return Just(true);
3201   }
3202 
3203   Isolate* isolate_;
3204   ValueSerializer serializer_;
3205   std::unique_ptr<SerializationData> data_;
3206   std::vector<Global<ArrayBuffer>> array_buffers_;
3207   std::vector<Global<SharedArrayBuffer>> shared_array_buffers_;
3208   std::vector<Global<WasmCompiledModule>> wasm_modules_;
3209   std::vector<ExternalizedContents> externalized_contents_;
3210   size_t current_memory_usage_;
3211 
3212   DISALLOW_COPY_AND_ASSIGN(Serializer);
3213 };
3214 
3215 class Deserializer : public ValueDeserializer::Delegate {
3216  public:
Deserializer(Isolate * isolate,std::unique_ptr<SerializationData> data)3217   Deserializer(Isolate* isolate, std::unique_ptr<SerializationData> data)
3218       : isolate_(isolate),
3219         deserializer_(isolate, data->data(), data->size(), this),
3220         data_(std::move(data)) {
3221     deserializer_.SetSupportsLegacyWireFormat(true);
3222   }
3223 
ReadValue(Local<Context> context)3224   MaybeLocal<Value> ReadValue(Local<Context> context) {
3225     bool read_header;
3226     if (!deserializer_.ReadHeader(context).To(&read_header)) {
3227       return MaybeLocal<Value>();
3228     }
3229 
3230     uint32_t index = 0;
3231     for (const auto& contents : data_->array_buffer_contents()) {
3232       Local<ArrayBuffer> array_buffer =
3233           ArrayBuffer::New(isolate_, contents.Data(), contents.ByteLength());
3234       deserializer_.TransferArrayBuffer(index++, array_buffer);
3235     }
3236 
3237     return deserializer_.ReadValue(context);
3238   }
3239 
GetSharedArrayBufferFromId(Isolate * isolate,uint32_t clone_id)3240   MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
3241       Isolate* isolate, uint32_t clone_id) override {
3242     DCHECK_NOT_NULL(data_);
3243     if (clone_id < data_->shared_array_buffer_contents().size()) {
3244       SharedArrayBuffer::Contents contents =
3245           data_->shared_array_buffer_contents().at(clone_id);
3246       return SharedArrayBuffer::New(isolate_, contents.Data(),
3247                                     contents.ByteLength());
3248     }
3249     return MaybeLocal<SharedArrayBuffer>();
3250   }
3251 
GetWasmModuleFromId(Isolate * isolate,uint32_t transfer_id)3252   MaybeLocal<WasmCompiledModule> GetWasmModuleFromId(
3253       Isolate* isolate, uint32_t transfer_id) override {
3254     DCHECK_NOT_NULL(data_);
3255     if (transfer_id < data_->transferrable_modules().size()) {
3256       return WasmCompiledModule::FromTransferrableModule(
3257           isolate_, data_->transferrable_modules().at(transfer_id));
3258     }
3259     return MaybeLocal<WasmCompiledModule>();
3260   }
3261 
3262  private:
3263   Isolate* isolate_;
3264   ValueDeserializer deserializer_;
3265   std::unique_ptr<SerializationData> data_;
3266 
3267   DISALLOW_COPY_AND_ASSIGN(Deserializer);
3268 };
3269 
SerializeValue(Isolate * isolate,Local<Value> value,Local<Value> transfer)3270 std::unique_ptr<SerializationData> Shell::SerializeValue(
3271     Isolate* isolate, Local<Value> value, Local<Value> transfer) {
3272   bool ok;
3273   Local<Context> context = isolate->GetCurrentContext();
3274   Serializer serializer(isolate);
3275   std::unique_ptr<SerializationData> data;
3276   if (serializer.WriteValue(context, value, transfer).To(&ok)) {
3277     data = serializer.Release();
3278   }
3279   // Append externalized contents even when WriteValue fails.
3280   base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
3281   serializer.AppendExternalizedContentsTo(&externalized_contents_);
3282   return data;
3283 }
3284 
DeserializeValue(Isolate * isolate,std::unique_ptr<SerializationData> data)3285 MaybeLocal<Value> Shell::DeserializeValue(
3286     Isolate* isolate, std::unique_ptr<SerializationData> data) {
3287   Local<Value> value;
3288   Local<Context> context = isolate->GetCurrentContext();
3289   Deserializer deserializer(isolate, std::move(data));
3290   return deserializer.ReadValue(context);
3291 }
3292 
3293 
CleanupWorkers()3294 void Shell::CleanupWorkers() {
3295   // Make a copy of workers_, because we don't want to call Worker::Terminate
3296   // while holding the workers_mutex_ lock. Otherwise, if a worker is about to
3297   // create a new Worker, it would deadlock.
3298   std::vector<Worker*> workers_copy;
3299   {
3300     base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
3301     allow_new_workers_ = false;
3302     workers_copy.swap(workers_);
3303   }
3304 
3305   for (Worker* worker : workers_copy) {
3306     worker->WaitForThread();
3307     delete worker;
3308   }
3309 
3310   // Now that all workers are terminated, we can re-enable Worker creation.
3311   base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
3312   allow_new_workers_ = true;
3313   externalized_contents_.clear();
3314 }
3315 
Main(int argc,char * argv[])3316 int Shell::Main(int argc, char* argv[]) {
3317   std::ofstream trace_file;
3318   v8::base::EnsureConsoleOutput();
3319   if (!SetOptions(argc, argv)) return 1;
3320   v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);
3321 
3322   v8::platform::InProcessStackDumping in_process_stack_dumping =
3323       options.disable_in_process_stack_traces
3324           ? v8::platform::InProcessStackDumping::kDisabled
3325           : v8::platform::InProcessStackDumping::kEnabled;
3326 
3327   std::unique_ptr<platform::tracing::TracingController> tracing;
3328   if (options.trace_enabled && !i::FLAG_verify_predictable) {
3329     tracing = base::make_unique<platform::tracing::TracingController>();
3330 
3331     trace_file.open(options.trace_path ? options.trace_path : "v8_trace.json");
3332     platform::tracing::TraceBuffer* trace_buffer =
3333         platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
3334             platform::tracing::TraceBuffer::kRingBufferChunks,
3335             platform::tracing::TraceWriter::CreateJSONTraceWriter(trace_file));
3336     tracing->Initialize(trace_buffer);
3337   }
3338 
3339   platform::tracing::TracingController* tracing_controller = tracing.get();
3340   g_platform = v8::platform::NewDefaultPlatform(
3341       options.thread_pool_size, v8::platform::IdleTaskSupport::kEnabled,
3342       in_process_stack_dumping, std::move(tracing));
3343   if (i::FLAG_verify_predictable) {
3344     g_platform.reset(new PredictablePlatform(std::move(g_platform)));
3345   }
3346 
3347   v8::V8::InitializePlatform(g_platform.get());
3348   v8::V8::Initialize();
3349   if (options.natives_blob || options.snapshot_blob) {
3350     v8::V8::InitializeExternalStartupData(options.natives_blob,
3351                                           options.snapshot_blob);
3352   } else {
3353     v8::V8::InitializeExternalStartupData(argv[0]);
3354   }
3355   if (i::FLAG_trace_turbo_cfg_file == nullptr) {
3356     SetFlagsFromString("--trace-turbo-cfg-file=turbo.cfg");
3357   }
3358   if (i::FLAG_redirect_code_traces_to == nullptr) {
3359     SetFlagsFromString("--redirect-code-traces-to=code.asm");
3360   }
3361   int result = 0;
3362   Isolate::CreateParams create_params;
3363   ShellArrayBufferAllocator shell_array_buffer_allocator;
3364   MockArrayBufferAllocator mock_arraybuffer_allocator;
3365   if (options.mock_arraybuffer_allocator) {
3366     Shell::array_buffer_allocator = &mock_arraybuffer_allocator;
3367   } else {
3368     Shell::array_buffer_allocator = &shell_array_buffer_allocator;
3369   }
3370   create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3371 #ifdef ENABLE_VTUNE_JIT_INTERFACE
3372   create_params.code_event_handler = vTune::GetVtuneCodeEventHandler();
3373 #endif
3374   create_params.constraints.ConfigureDefaults(
3375       base::SysInfo::AmountOfPhysicalMemory(),
3376       base::SysInfo::AmountOfVirtualMemory());
3377 
3378   Shell::counter_map_ = new CounterMap();
3379   if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp || i::FLAG_gc_stats) {
3380     create_params.counter_lookup_callback = LookupCounter;
3381     create_params.create_histogram_callback = CreateHistogram;
3382     create_params.add_histogram_sample_callback = AddHistogramSample;
3383   }
3384 
3385   if (V8_TRAP_HANDLER_SUPPORTED && i::FLAG_wasm_trap_handler) {
3386     constexpr bool use_default_trap_handler = true;
3387     if (!v8::V8::EnableWebAssemblyTrapHandler(use_default_trap_handler)) {
3388       FATAL("Could not register trap handler");
3389     }
3390   }
3391 
3392   Isolate* isolate = Isolate::New(create_params);
3393   isolate->SetHostImportModuleDynamicallyCallback(
3394       Shell::HostImportModuleDynamically);
3395   isolate->SetHostInitializeImportMetaObjectCallback(
3396       Shell::HostInitializeImportMetaObject);
3397 
3398   D8Console console(isolate);
3399   {
3400     Isolate::Scope scope(isolate);
3401     Initialize(isolate);
3402     PerIsolateData data(isolate);
3403     debug::SetConsoleDelegate(isolate, &console);
3404 
3405     if (options.trace_enabled) {
3406       platform::tracing::TraceConfig* trace_config;
3407       if (options.trace_config) {
3408         int size = 0;
3409         char* trace_config_json_str = ReadChars(options.trace_config, &size);
3410         trace_config =
3411             tracing::CreateTraceConfigFromJSON(isolate, trace_config_json_str);
3412         delete[] trace_config_json_str;
3413       } else {
3414         trace_config =
3415             platform::tracing::TraceConfig::CreateDefaultTraceConfig();
3416       }
3417       tracing_controller->StartTracing(trace_config);
3418     }
3419 
3420     if (options.stress_opt || options.stress_deopt) {
3421       Testing::SetStressRunType(options.stress_opt
3422                                 ? Testing::kStressTypeOpt
3423                                 : Testing::kStressTypeDeopt);
3424       options.stress_runs = Testing::GetStressRuns();
3425       for (int i = 0; i < options.stress_runs && result == 0; i++) {
3426         printf("============ Stress %d/%d ============\n", i + 1,
3427                options.stress_runs);
3428         Testing::PrepareStressRun(i);
3429         bool last_run = i == options.stress_runs - 1;
3430         result = RunMain(isolate, argc, argv, last_run);
3431       }
3432       printf("======== Full Deoptimization =======\n");
3433       Testing::DeoptimizeAll(isolate);
3434     } else if (i::FLAG_stress_runs > 0) {
3435       options.stress_runs = i::FLAG_stress_runs;
3436       for (int i = 0; i < options.stress_runs && result == 0; i++) {
3437         printf("============ Run %d/%d ============\n", i + 1,
3438                options.stress_runs);
3439         bool last_run = i == options.stress_runs - 1;
3440         result = RunMain(isolate, argc, argv, last_run);
3441       }
3442     } else if (options.code_cache_options !=
3443                ShellOptions::CodeCacheOptions::kNoProduceCache) {
3444       printf("============ Run: Produce code cache ============\n");
3445       // First run to produce the cache
3446       Isolate::CreateParams create_params;
3447       create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3448       i::FLAG_hash_seed ^= 1337;  // Use a different hash seed.
3449       Isolate* isolate2 = Isolate::New(create_params);
3450       i::FLAG_hash_seed ^= 1337;  // Restore old hash seed.
3451       isolate2->SetHostImportModuleDynamicallyCallback(
3452           Shell::HostImportModuleDynamically);
3453       isolate2->SetHostInitializeImportMetaObjectCallback(
3454           Shell::HostInitializeImportMetaObject);
3455       {
3456         D8Console console(isolate2);
3457         debug::SetConsoleDelegate(isolate2, &console);
3458         PerIsolateData data(isolate2);
3459         Isolate::Scope isolate_scope(isolate2);
3460 
3461         result = RunMain(isolate2, argc, argv, false);
3462       }
3463       isolate2->Dispose();
3464 
3465       // Change the options to consume cache
3466       DCHECK(options.compile_options == v8::ScriptCompiler::kEagerCompile ||
3467              options.compile_options == v8::ScriptCompiler::kNoCompileOptions);
3468       options.compile_options = v8::ScriptCompiler::kConsumeCodeCache;
3469 
3470       printf("============ Run: Consume code cache ============\n");
3471       // Second run to consume the cache in current isolate
3472       result = RunMain(isolate, argc, argv, true);
3473       options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
3474     } else {
3475       bool last_run = true;
3476       result = RunMain(isolate, argc, argv, last_run);
3477     }
3478 
3479     // Run interactive shell if explicitly requested or if no script has been
3480     // executed, but never on --test
3481     if (use_interactive_shell()) {
3482       RunShell(isolate);
3483     }
3484 
3485     if (i::FLAG_trace_ignition_dispatches &&
3486         i::FLAG_trace_ignition_dispatches_output_file != nullptr) {
3487       WriteIgnitionDispatchCountersFile(isolate);
3488     }
3489 
3490     // Shut down contexts and collect garbage.
3491     cached_code_map_.clear();
3492     evaluation_context_.Reset();
3493     stringify_function_.Reset();
3494     CollectGarbage(isolate);
3495   }
3496   OnExit(isolate);
3497   V8::Dispose();
3498   V8::ShutdownPlatform();
3499 
3500   // Delete the platform explicitly here to write the tracing output to the
3501   // tracing file.
3502   g_platform.reset();
3503   return result;
3504 }
3505 
3506 }  // namespace v8
3507 
3508 
3509 #ifndef GOOGLE3
main(int argc,char * argv[])3510 int main(int argc, char* argv[]) {
3511   return v8::Shell::Main(argc, argv);
3512 }
3513 #endif
3514 
3515 #undef CHECK
3516 #undef DCHECK
3517