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