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