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 6 // Defined when linking against shared lib on Windows. 7 #if defined(USING_V8_SHARED) && !defined(V8_SHARED) 8 #define V8_SHARED 9 #endif 10 11 #include <errno.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sys/stat.h> 15 16 #ifdef V8_SHARED 17 #include <assert.h> 18 #endif // V8_SHARED 19 20 #ifndef V8_SHARED 21 #include <algorithm> 22 #include <fstream> 23 #include <vector> 24 #endif // !V8_SHARED 25 26 #ifdef V8_SHARED 27 #include "include/v8-testing.h" 28 #endif // V8_SHARED 29 30 #ifdef ENABLE_VTUNE_JIT_INTERFACE 31 #include "src/third_party/vtune/v8-vtune.h" 32 #endif 33 34 #include "src/d8.h" 35 #include "src/ostreams.h" 36 37 #include "include/libplatform/libplatform.h" 38 #ifndef V8_SHARED 39 #include "src/api.h" 40 #include "src/base/cpu.h" 41 #include "src/base/logging.h" 42 #include "src/base/platform/platform.h" 43 #include "src/base/sys-info.h" 44 #include "src/basic-block-profiler.h" 45 #include "src/interpreter/interpreter.h" 46 #include "src/snapshot/natives.h" 47 #include "src/utils.h" 48 #include "src/v8.h" 49 #endif // !V8_SHARED 50 51 #if !defined(_WIN32) && !defined(_WIN64) 52 #include <unistd.h> // NOLINT 53 #else 54 #include <windows.h> // NOLINT 55 #if defined(_MSC_VER) 56 #include <crtdbg.h> // NOLINT 57 #endif // defined(_MSC_VER) 58 #endif // !defined(_WIN32) && !defined(_WIN64) 59 60 #ifndef DCHECK 61 #define DCHECK(condition) assert(condition) 62 #endif 63 64 #ifndef CHECK 65 #define CHECK(condition) assert(condition) 66 #endif 67 68 namespace v8 { 69 70 namespace { 71 72 const int MB = 1024 * 1024; 73 #ifndef V8_SHARED 74 const int kMaxWorkers = 50; 75 #endif 76 77 78 class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator { 79 public: 80 virtual void* Allocate(size_t length) { 81 void* data = AllocateUninitialized(length); 82 return data == NULL ? data : memset(data, 0, length); 83 } 84 virtual void* AllocateUninitialized(size_t length) { return malloc(length); } 85 virtual void Free(void* data, size_t) { free(data); } 86 }; 87 88 89 class MockArrayBufferAllocator : public v8::ArrayBuffer::Allocator { 90 public: 91 void* Allocate(size_t length) override { 92 size_t actual_length = length > 10 * MB ? 1 : length; 93 void* data = AllocateUninitialized(actual_length); 94 return data == NULL ? data : memset(data, 0, actual_length); 95 } 96 void* AllocateUninitialized(size_t length) override { 97 return length > 10 * MB ? malloc(1) : malloc(length); 98 } 99 void Free(void* p, size_t) override { free(p); } 100 }; 101 102 103 #ifndef V8_SHARED 104 // Predictable v8::Platform implementation. All background and foreground 105 // tasks are run immediately, delayed tasks are not executed at all. 106 class PredictablePlatform : public Platform { 107 public: 108 PredictablePlatform() {} 109 110 void CallOnBackgroundThread(Task* task, 111 ExpectedRuntime expected_runtime) override { 112 task->Run(); 113 delete task; 114 } 115 116 void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { 117 task->Run(); 118 delete task; 119 } 120 121 void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, 122 double delay_in_seconds) override { 123 delete task; 124 } 125 126 void CallIdleOnForegroundThread(v8::Isolate* isolate, 127 IdleTask* task) override { 128 UNREACHABLE(); 129 } 130 131 bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; } 132 133 double MonotonicallyIncreasingTime() override { 134 return synthetic_time_in_sec_ += 0.00001; 135 } 136 137 uint64_t AddTraceEvent(char phase, const uint8_t* categoryEnabledFlag, 138 const char* name, const char* scope, uint64_t id, 139 uint64_t bind_id, int numArgs, const char** argNames, 140 const uint8_t* argTypes, const uint64_t* argValues, 141 unsigned int flags) override { 142 return 0; 143 } 144 145 void UpdateTraceEventDuration(const uint8_t* categoryEnabledFlag, 146 const char* name, uint64_t handle) override {} 147 148 const uint8_t* GetCategoryGroupEnabled(const char* name) override { 149 static uint8_t no = 0; 150 return &no; 151 } 152 153 const char* GetCategoryGroupName( 154 const uint8_t* categoryEnabledFlag) override { 155 static const char* dummy = "dummy"; 156 return dummy; 157 } 158 159 private: 160 double synthetic_time_in_sec_ = 0.0; 161 162 DISALLOW_COPY_AND_ASSIGN(PredictablePlatform); 163 }; 164 #endif // !V8_SHARED 165 166 167 v8::Platform* g_platform = NULL; 168 169 170 static Local<Value> Throw(Isolate* isolate, const char* message) { 171 return isolate->ThrowException( 172 String::NewFromUtf8(isolate, message, NewStringType::kNormal) 173 .ToLocalChecked()); 174 } 175 176 177 #ifndef V8_SHARED 178 bool FindInObjectList(Local<Object> object, const Shell::ObjectList& list) { 179 for (int i = 0; i < list.length(); ++i) { 180 if (list[i]->StrictEquals(object)) { 181 return true; 182 } 183 } 184 return false; 185 } 186 187 188 Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) { 189 if (object->InternalFieldCount() != 1) { 190 Throw(isolate, "this is not a Worker"); 191 return NULL; 192 } 193 194 Worker* worker = 195 static_cast<Worker*>(object->GetAlignedPointerFromInternalField(0)); 196 if (worker == NULL) { 197 Throw(isolate, "Worker is defunct because main thread is terminating"); 198 return NULL; 199 } 200 201 return worker; 202 } 203 #endif // !V8_SHARED 204 205 206 } // namespace 207 208 209 class PerIsolateData { 210 public: 211 explicit PerIsolateData(Isolate* isolate) : isolate_(isolate), realms_(NULL) { 212 HandleScope scope(isolate); 213 isolate->SetData(0, this); 214 } 215 216 ~PerIsolateData() { 217 isolate_->SetData(0, NULL); // Not really needed, just to be sure... 218 } 219 220 inline static PerIsolateData* Get(Isolate* isolate) { 221 return reinterpret_cast<PerIsolateData*>(isolate->GetData(0)); 222 } 223 224 class RealmScope { 225 public: 226 explicit RealmScope(PerIsolateData* data); 227 ~RealmScope(); 228 private: 229 PerIsolateData* data_; 230 }; 231 232 private: 233 friend class Shell; 234 friend class RealmScope; 235 Isolate* isolate_; 236 int realm_count_; 237 int realm_current_; 238 int realm_switch_; 239 Global<Context>* realms_; 240 Global<Value> realm_shared_; 241 242 int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args, 243 int arg_offset); 244 int RealmFind(Local<Context> context); 245 }; 246 247 248 #ifndef V8_SHARED 249 CounterMap* Shell::counter_map_; 250 base::OS::MemoryMappedFile* Shell::counters_file_ = NULL; 251 CounterCollection Shell::local_counters_; 252 CounterCollection* Shell::counters_ = &local_counters_; 253 base::LazyMutex Shell::context_mutex_; 254 const base::TimeTicks Shell::kInitialTicks = 255 base::TimeTicks::HighResolutionNow(); 256 Global<Function> Shell::stringify_function_; 257 base::LazyMutex Shell::workers_mutex_; 258 bool Shell::allow_new_workers_ = true; 259 i::List<Worker*> Shell::workers_; 260 i::List<SharedArrayBuffer::Contents> Shell::externalized_shared_contents_; 261 #endif // !V8_SHARED 262 263 Global<Context> Shell::evaluation_context_; 264 ArrayBuffer::Allocator* Shell::array_buffer_allocator; 265 ShellOptions Shell::options; 266 base::OnceType Shell::quit_once_ = V8_ONCE_INIT; 267 268 #ifndef V8_SHARED 269 bool CounterMap::Match(void* key1, void* key2) { 270 const char* name1 = reinterpret_cast<const char*>(key1); 271 const char* name2 = reinterpret_cast<const char*>(key2); 272 return strcmp(name1, name2) == 0; 273 } 274 #endif // !V8_SHARED 275 276 277 // Converts a V8 value to a C string. 278 const char* Shell::ToCString(const v8::String::Utf8Value& value) { 279 return *value ? *value : "<string conversion failed>"; 280 } 281 282 283 ScriptCompiler::CachedData* CompileForCachedData( 284 Local<String> source, Local<Value> name, 285 ScriptCompiler::CompileOptions compile_options) { 286 int source_length = source->Length(); 287 uint16_t* source_buffer = new uint16_t[source_length]; 288 source->Write(source_buffer, 0, source_length); 289 int name_length = 0; 290 uint16_t* name_buffer = NULL; 291 if (name->IsString()) { 292 Local<String> name_string = Local<String>::Cast(name); 293 name_length = name_string->Length(); 294 name_buffer = new uint16_t[name_length]; 295 name_string->Write(name_buffer, 0, name_length); 296 } 297 Isolate::CreateParams create_params; 298 create_params.array_buffer_allocator = Shell::array_buffer_allocator; 299 Isolate* temp_isolate = Isolate::New(create_params); 300 ScriptCompiler::CachedData* result = NULL; 301 { 302 Isolate::Scope isolate_scope(temp_isolate); 303 HandleScope handle_scope(temp_isolate); 304 Context::Scope context_scope(Context::New(temp_isolate)); 305 Local<String> source_copy = 306 v8::String::NewFromTwoByte(temp_isolate, source_buffer, 307 v8::NewStringType::kNormal, 308 source_length).ToLocalChecked(); 309 Local<Value> name_copy; 310 if (name_buffer) { 311 name_copy = v8::String::NewFromTwoByte(temp_isolate, name_buffer, 312 v8::NewStringType::kNormal, 313 name_length).ToLocalChecked(); 314 } else { 315 name_copy = v8::Undefined(temp_isolate); 316 } 317 ScriptCompiler::Source script_source(source_copy, ScriptOrigin(name_copy)); 318 if (!ScriptCompiler::CompileUnboundScript(temp_isolate, &script_source, 319 compile_options).IsEmpty() && 320 script_source.GetCachedData()) { 321 int length = script_source.GetCachedData()->length; 322 uint8_t* cache = new uint8_t[length]; 323 memcpy(cache, script_source.GetCachedData()->data, length); 324 result = new ScriptCompiler::CachedData( 325 cache, length, ScriptCompiler::CachedData::BufferOwned); 326 } 327 } 328 temp_isolate->Dispose(); 329 delete[] source_buffer; 330 delete[] name_buffer; 331 return result; 332 } 333 334 335 // Compile a string within the current v8 context. 336 MaybeLocal<Script> Shell::CompileString( 337 Isolate* isolate, Local<String> source, Local<Value> name, 338 ScriptCompiler::CompileOptions compile_options, SourceType source_type) { 339 Local<Context> context(isolate->GetCurrentContext()); 340 ScriptOrigin origin(name); 341 // TODO(adamk): Make use of compile options for Modules. 342 if (compile_options == ScriptCompiler::kNoCompileOptions || 343 source_type == MODULE) { 344 ScriptCompiler::Source script_source(source, origin); 345 return source_type == SCRIPT 346 ? ScriptCompiler::Compile(context, &script_source, 347 compile_options) 348 : ScriptCompiler::CompileModule(context, &script_source, 349 compile_options); 350 } 351 352 ScriptCompiler::CachedData* data = 353 CompileForCachedData(source, name, compile_options); 354 ScriptCompiler::Source cached_source(source, origin, data); 355 if (compile_options == ScriptCompiler::kProduceCodeCache) { 356 compile_options = ScriptCompiler::kConsumeCodeCache; 357 } else if (compile_options == ScriptCompiler::kProduceParserCache) { 358 compile_options = ScriptCompiler::kConsumeParserCache; 359 } else { 360 DCHECK(false); // A new compile option? 361 } 362 if (data == NULL) compile_options = ScriptCompiler::kNoCompileOptions; 363 DCHECK_EQ(SCRIPT, source_type); 364 MaybeLocal<Script> result = 365 ScriptCompiler::Compile(context, &cached_source, compile_options); 366 CHECK(data == NULL || !data->rejected); 367 return result; 368 } 369 370 371 // Executes a string within the current v8 context. 372 bool Shell::ExecuteString(Isolate* isolate, Local<String> source, 373 Local<Value> name, bool print_result, 374 bool report_exceptions, SourceType source_type) { 375 HandleScope handle_scope(isolate); 376 TryCatch try_catch(isolate); 377 try_catch.SetVerbose(true); 378 379 MaybeLocal<Value> maybe_result; 380 { 381 PerIsolateData* data = PerIsolateData::Get(isolate); 382 Local<Context> realm = 383 Local<Context>::New(isolate, data->realms_[data->realm_current_]); 384 Context::Scope context_scope(realm); 385 Local<Script> script; 386 if (!Shell::CompileString(isolate, source, name, options.compile_options, 387 source_type).ToLocal(&script)) { 388 // Print errors that happened during compilation. 389 if (report_exceptions) ReportException(isolate, &try_catch); 390 return false; 391 } 392 maybe_result = script->Run(realm); 393 EmptyMessageQueues(isolate); 394 data->realm_current_ = data->realm_switch_; 395 } 396 Local<Value> result; 397 if (!maybe_result.ToLocal(&result)) { 398 DCHECK(try_catch.HasCaught()); 399 // Print errors that happened during execution. 400 if (report_exceptions) ReportException(isolate, &try_catch); 401 return false; 402 } 403 DCHECK(!try_catch.HasCaught()); 404 if (print_result) { 405 #if !defined(V8_SHARED) 406 if (options.test_shell) { 407 #endif 408 if (!result->IsUndefined()) { 409 // If all went well and the result wasn't undefined then print 410 // the returned value. 411 v8::String::Utf8Value str(result); 412 fwrite(*str, sizeof(**str), str.length(), stdout); 413 printf("\n"); 414 } 415 #if !defined(V8_SHARED) 416 } else { 417 v8::String::Utf8Value str(Stringify(isolate, result)); 418 fwrite(*str, sizeof(**str), str.length(), stdout); 419 printf("\n"); 420 } 421 #endif 422 } 423 return true; 424 } 425 426 427 PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) { 428 data_->realm_count_ = 1; 429 data_->realm_current_ = 0; 430 data_->realm_switch_ = 0; 431 data_->realms_ = new Global<Context>[1]; 432 data_->realms_[0].Reset(data_->isolate_, 433 data_->isolate_->GetEnteredContext()); 434 } 435 436 437 PerIsolateData::RealmScope::~RealmScope() { 438 // Drop realms to avoid keeping them alive. 439 for (int i = 0; i < data_->realm_count_; ++i) 440 data_->realms_[i].Reset(); 441 delete[] data_->realms_; 442 if (!data_->realm_shared_.IsEmpty()) 443 data_->realm_shared_.Reset(); 444 } 445 446 447 int PerIsolateData::RealmFind(Local<Context> context) { 448 for (int i = 0; i < realm_count_; ++i) { 449 if (realms_[i] == context) return i; 450 } 451 return -1; 452 } 453 454 455 int PerIsolateData::RealmIndexOrThrow( 456 const v8::FunctionCallbackInfo<v8::Value>& args, 457 int arg_offset) { 458 if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) { 459 Throw(args.GetIsolate(), "Invalid argument"); 460 return -1; 461 } 462 int index = args[arg_offset] 463 ->Int32Value(args.GetIsolate()->GetCurrentContext()) 464 .FromMaybe(-1); 465 if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) { 466 Throw(args.GetIsolate(), "Invalid realm index"); 467 return -1; 468 } 469 return index; 470 } 471 472 473 #ifndef V8_SHARED 474 // performance.now() returns a time stamp as double, measured in milliseconds. 475 // When FLAG_verify_predictable mode is enabled it returns result of 476 // v8::Platform::MonotonicallyIncreasingTime(). 477 void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) { 478 if (i::FLAG_verify_predictable) { 479 args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime()); 480 } else { 481 base::TimeDelta delta = 482 base::TimeTicks::HighResolutionNow() - kInitialTicks; 483 args.GetReturnValue().Set(delta.InMillisecondsF()); 484 } 485 } 486 #endif // !V8_SHARED 487 488 489 // Realm.current() returns the index of the currently active realm. 490 void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) { 491 Isolate* isolate = args.GetIsolate(); 492 PerIsolateData* data = PerIsolateData::Get(isolate); 493 int index = data->RealmFind(isolate->GetEnteredContext()); 494 if (index == -1) return; 495 args.GetReturnValue().Set(index); 496 } 497 498 499 // Realm.owner(o) returns the index of the realm that created o. 500 void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) { 501 Isolate* isolate = args.GetIsolate(); 502 PerIsolateData* data = PerIsolateData::Get(isolate); 503 if (args.Length() < 1 || !args[0]->IsObject()) { 504 Throw(args.GetIsolate(), "Invalid argument"); 505 return; 506 } 507 int index = data->RealmFind(args[0] 508 ->ToObject(isolate->GetCurrentContext()) 509 .ToLocalChecked() 510 ->CreationContext()); 511 if (index == -1) return; 512 args.GetReturnValue().Set(index); 513 } 514 515 516 // Realm.global(i) returns the global object of realm i. 517 // (Note that properties of global objects cannot be read/written cross-realm.) 518 void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { 519 PerIsolateData* data = PerIsolateData::Get(args.GetIsolate()); 520 int index = data->RealmIndexOrThrow(args, 0); 521 if (index == -1) return; 522 args.GetReturnValue().Set( 523 Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global()); 524 } 525 526 MaybeLocal<Context> Shell::CreateRealm( 527 const v8::FunctionCallbackInfo<v8::Value>& args) { 528 Isolate* isolate = args.GetIsolate(); 529 TryCatch try_catch(isolate); 530 PerIsolateData* data = PerIsolateData::Get(isolate); 531 Global<Context>* old_realms = data->realms_; 532 int index = data->realm_count_; 533 data->realms_ = new Global<Context>[++data->realm_count_]; 534 for (int i = 0; i < index; ++i) { 535 data->realms_[i].Reset(isolate, old_realms[i]); 536 old_realms[i].Reset(); 537 } 538 delete[] old_realms; 539 Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate); 540 Local<Context> context = Context::New(isolate, NULL, global_template); 541 if (context.IsEmpty()) { 542 DCHECK(try_catch.HasCaught()); 543 try_catch.ReThrow(); 544 return MaybeLocal<Context>(); 545 } 546 data->realms_[index].Reset(isolate, context); 547 args.GetReturnValue().Set(index); 548 return context; 549 } 550 551 // Realm.create() creates a new realm with a distinct security token 552 // and returns its index. 553 void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) { 554 CreateRealm(args); 555 } 556 557 // Realm.createAllowCrossRealmAccess() creates a new realm with the same 558 // security token as the current realm. 559 void Shell::RealmCreateAllowCrossRealmAccess( 560 const v8::FunctionCallbackInfo<v8::Value>& args) { 561 Local<Context> context; 562 if (CreateRealm(args).ToLocal(&context)) { 563 context->SetSecurityToken( 564 args.GetIsolate()->GetEnteredContext()->GetSecurityToken()); 565 } 566 } 567 568 // Realm.dispose(i) disposes the reference to the realm i. 569 void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) { 570 Isolate* isolate = args.GetIsolate(); 571 PerIsolateData* data = PerIsolateData::Get(isolate); 572 int index = data->RealmIndexOrThrow(args, 0); 573 if (index == -1) return; 574 if (index == 0 || 575 index == data->realm_current_ || index == data->realm_switch_) { 576 Throw(args.GetIsolate(), "Invalid realm index"); 577 return; 578 } 579 data->realms_[index].Reset(); 580 isolate->ContextDisposedNotification(); 581 isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime()); 582 } 583 584 585 // Realm.switch(i) switches to the realm i for consecutive interactive inputs. 586 void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) { 587 Isolate* isolate = args.GetIsolate(); 588 PerIsolateData* data = PerIsolateData::Get(isolate); 589 int index = data->RealmIndexOrThrow(args, 0); 590 if (index == -1) return; 591 data->realm_switch_ = index; 592 } 593 594 595 // Realm.eval(i, s) evaluates s in realm i and returns the result. 596 void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) { 597 Isolate* isolate = args.GetIsolate(); 598 PerIsolateData* data = PerIsolateData::Get(isolate); 599 int index = data->RealmIndexOrThrow(args, 0); 600 if (index == -1) return; 601 if (args.Length() < 2 || !args[1]->IsString()) { 602 Throw(args.GetIsolate(), "Invalid argument"); 603 return; 604 } 605 ScriptCompiler::Source script_source( 606 args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked()); 607 Local<UnboundScript> script; 608 if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source) 609 .ToLocal(&script)) { 610 return; 611 } 612 Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]); 613 realm->Enter(); 614 Local<Value> result; 615 if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) { 616 realm->Exit(); 617 return; 618 } 619 realm->Exit(); 620 args.GetReturnValue().Set(result); 621 } 622 623 624 // Realm.shared is an accessor for a single shared value across realms. 625 void Shell::RealmSharedGet(Local<String> property, 626 const PropertyCallbackInfo<Value>& info) { 627 Isolate* isolate = info.GetIsolate(); 628 PerIsolateData* data = PerIsolateData::Get(isolate); 629 if (data->realm_shared_.IsEmpty()) return; 630 info.GetReturnValue().Set(data->realm_shared_); 631 } 632 633 void Shell::RealmSharedSet(Local<String> property, 634 Local<Value> value, 635 const PropertyCallbackInfo<void>& info) { 636 Isolate* isolate = info.GetIsolate(); 637 PerIsolateData* data = PerIsolateData::Get(isolate); 638 data->realm_shared_.Reset(isolate, value); 639 } 640 641 642 void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) { 643 Write(args); 644 printf("\n"); 645 fflush(stdout); 646 } 647 648 649 void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) { 650 for (int i = 0; i < args.Length(); i++) { 651 HandleScope handle_scope(args.GetIsolate()); 652 if (i != 0) { 653 printf(" "); 654 } 655 656 // Explicitly catch potential exceptions in toString(). 657 v8::TryCatch try_catch(args.GetIsolate()); 658 Local<Value> arg = args[i]; 659 Local<String> str_obj; 660 661 if (arg->IsSymbol()) { 662 arg = Local<Symbol>::Cast(arg)->Name(); 663 } 664 if (!arg->ToString(args.GetIsolate()->GetCurrentContext()) 665 .ToLocal(&str_obj)) { 666 try_catch.ReThrow(); 667 return; 668 } 669 670 v8::String::Utf8Value str(str_obj); 671 int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), stdout)); 672 if (n != str.length()) { 673 printf("Error in fwrite\n"); 674 Exit(1); 675 } 676 } 677 } 678 679 680 void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) { 681 String::Utf8Value file(args[0]); 682 if (*file == NULL) { 683 Throw(args.GetIsolate(), "Error loading file"); 684 return; 685 } 686 Local<String> source = ReadFile(args.GetIsolate(), *file); 687 if (source.IsEmpty()) { 688 Throw(args.GetIsolate(), "Error loading file"); 689 return; 690 } 691 args.GetReturnValue().Set(source); 692 } 693 694 695 Local<String> Shell::ReadFromStdin(Isolate* isolate) { 696 static const int kBufferSize = 256; 697 char buffer[kBufferSize]; 698 Local<String> accumulator = 699 String::NewFromUtf8(isolate, "", NewStringType::kNormal).ToLocalChecked(); 700 int length; 701 while (true) { 702 // Continue reading if the line ends with an escape '\\' or the line has 703 // not been fully read into the buffer yet (does not end with '\n'). 704 // If fgets gets an error, just give up. 705 char* input = NULL; 706 input = fgets(buffer, kBufferSize, stdin); 707 if (input == NULL) return Local<String>(); 708 length = static_cast<int>(strlen(buffer)); 709 if (length == 0) { 710 return accumulator; 711 } else if (buffer[length-1] != '\n') { 712 accumulator = String::Concat( 713 accumulator, 714 String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length) 715 .ToLocalChecked()); 716 } else if (length > 1 && buffer[length-2] == '\\') { 717 buffer[length-2] = '\n'; 718 accumulator = String::Concat( 719 accumulator, 720 String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, 721 length - 1).ToLocalChecked()); 722 } else { 723 return String::Concat( 724 accumulator, 725 String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, 726 length - 1).ToLocalChecked()); 727 } 728 } 729 } 730 731 732 void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) { 733 for (int i = 0; i < args.Length(); i++) { 734 HandleScope handle_scope(args.GetIsolate()); 735 String::Utf8Value file(args[i]); 736 if (*file == NULL) { 737 Throw(args.GetIsolate(), "Error loading file"); 738 return; 739 } 740 Local<String> source = ReadFile(args.GetIsolate(), *file); 741 if (source.IsEmpty()) { 742 Throw(args.GetIsolate(), "Error loading file"); 743 return; 744 } 745 if (!ExecuteString( 746 args.GetIsolate(), source, 747 String::NewFromUtf8(args.GetIsolate(), *file, 748 NewStringType::kNormal).ToLocalChecked(), 749 false, true)) { 750 Throw(args.GetIsolate(), "Error executing file"); 751 return; 752 } 753 } 754 } 755 756 757 #ifndef V8_SHARED 758 void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) { 759 Isolate* isolate = args.GetIsolate(); 760 HandleScope handle_scope(isolate); 761 if (args.Length() < 1 || !args[0]->IsString()) { 762 Throw(args.GetIsolate(), "1st argument must be string"); 763 return; 764 } 765 766 if (!args.IsConstructCall()) { 767 Throw(args.GetIsolate(), "Worker must be constructed with new"); 768 return; 769 } 770 771 { 772 base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer()); 773 if (workers_.length() >= kMaxWorkers) { 774 Throw(args.GetIsolate(), "Too many workers, I won't let you create more"); 775 return; 776 } 777 778 // Initialize the internal field to NULL; if we return early without 779 // creating a new Worker (because the main thread is terminating) we can 780 // early-out from the instance calls. 781 args.Holder()->SetAlignedPointerInInternalField(0, NULL); 782 783 if (!allow_new_workers_) return; 784 785 Worker* worker = new Worker; 786 args.Holder()->SetAlignedPointerInInternalField(0, worker); 787 workers_.Add(worker); 788 789 String::Utf8Value script(args[0]); 790 if (!*script) { 791 Throw(args.GetIsolate(), "Can't get worker script"); 792 return; 793 } 794 worker->StartExecuteInThread(*script); 795 } 796 } 797 798 799 void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { 800 Isolate* isolate = args.GetIsolate(); 801 HandleScope handle_scope(isolate); 802 Local<Context> context = isolate->GetCurrentContext(); 803 804 if (args.Length() < 1) { 805 Throw(isolate, "Invalid argument"); 806 return; 807 } 808 809 Worker* worker = GetWorkerFromInternalField(isolate, args.Holder()); 810 if (!worker) { 811 return; 812 } 813 814 Local<Value> message = args[0]; 815 ObjectList to_transfer; 816 if (args.Length() >= 2) { 817 if (!args[1]->IsArray()) { 818 Throw(isolate, "Transfer list must be an Array"); 819 return; 820 } 821 822 Local<Array> transfer = Local<Array>::Cast(args[1]); 823 uint32_t length = transfer->Length(); 824 for (uint32_t i = 0; i < length; ++i) { 825 Local<Value> element; 826 if (transfer->Get(context, i).ToLocal(&element)) { 827 if (!element->IsArrayBuffer() && !element->IsSharedArrayBuffer()) { 828 Throw(isolate, 829 "Transfer array elements must be an ArrayBuffer or " 830 "SharedArrayBuffer."); 831 break; 832 } 833 834 to_transfer.Add(Local<Object>::Cast(element)); 835 } 836 } 837 } 838 839 ObjectList seen_objects; 840 SerializationData* data = new SerializationData; 841 if (SerializeValue(isolate, message, to_transfer, &seen_objects, data)) { 842 worker->PostMessage(data); 843 } else { 844 delete data; 845 } 846 } 847 848 849 void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { 850 Isolate* isolate = args.GetIsolate(); 851 HandleScope handle_scope(isolate); 852 Worker* worker = GetWorkerFromInternalField(isolate, args.Holder()); 853 if (!worker) { 854 return; 855 } 856 857 SerializationData* data = worker->GetMessage(); 858 if (data) { 859 int offset = 0; 860 Local<Value> data_value; 861 if (Shell::DeserializeValue(isolate, *data, &offset).ToLocal(&data_value)) { 862 args.GetReturnValue().Set(data_value); 863 } 864 delete data; 865 } 866 } 867 868 869 void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) { 870 Isolate* isolate = args.GetIsolate(); 871 HandleScope handle_scope(isolate); 872 Worker* worker = GetWorkerFromInternalField(isolate, args.Holder()); 873 if (!worker) { 874 return; 875 } 876 877 worker->Terminate(); 878 } 879 #endif // !V8_SHARED 880 881 882 void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) { 883 int exit_code = (*args)[0] 884 ->Int32Value(args->GetIsolate()->GetCurrentContext()) 885 .FromMaybe(0); 886 #ifndef V8_SHARED 887 CleanupWorkers(); 888 #endif // !V8_SHARED 889 OnExit(args->GetIsolate()); 890 Exit(exit_code); 891 } 892 893 894 void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) { 895 base::CallOnce(&quit_once_, &QuitOnce, 896 const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args)); 897 } 898 899 900 void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) { 901 args.GetReturnValue().Set( 902 String::NewFromUtf8(args.GetIsolate(), V8::GetVersion(), 903 NewStringType::kNormal).ToLocalChecked()); 904 } 905 906 907 void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) { 908 HandleScope handle_scope(isolate); 909 #ifndef V8_SHARED 910 Local<Context> context; 911 bool enter_context = !isolate->InContext(); 912 if (enter_context) { 913 context = Local<Context>::New(isolate, evaluation_context_); 914 context->Enter(); 915 } 916 #endif // !V8_SHARED 917 v8::String::Utf8Value exception(try_catch->Exception()); 918 const char* exception_string = ToCString(exception); 919 Local<Message> message = try_catch->Message(); 920 if (message.IsEmpty()) { 921 // V8 didn't provide any extra information about this error; just 922 // print the exception. 923 printf("%s\n", exception_string); 924 } else { 925 // Print (filename):(line number): (message). 926 v8::String::Utf8Value filename(message->GetScriptOrigin().ResourceName()); 927 const char* filename_string = ToCString(filename); 928 Maybe<int> maybeline = message->GetLineNumber(isolate->GetCurrentContext()); 929 int linenum = maybeline.IsJust() ? maybeline.FromJust() : -1; 930 printf("%s:%i: %s\n", filename_string, linenum, exception_string); 931 Local<String> sourceline; 932 if (message->GetSourceLine(isolate->GetCurrentContext()) 933 .ToLocal(&sourceline)) { 934 // Print line of source code. 935 v8::String::Utf8Value sourcelinevalue(sourceline); 936 const char* sourceline_string = ToCString(sourcelinevalue); 937 printf("%s\n", sourceline_string); 938 // Print wavy underline (GetUnderline is deprecated). 939 int start = 940 message->GetStartColumn(isolate->GetCurrentContext()).FromJust(); 941 for (int i = 0; i < start; i++) { 942 printf(" "); 943 } 944 int end = message->GetEndColumn(isolate->GetCurrentContext()).FromJust(); 945 for (int i = start; i < end; i++) { 946 printf("^"); 947 } 948 printf("\n"); 949 } 950 Local<Value> stack_trace_string; 951 if (try_catch->StackTrace(isolate->GetCurrentContext()) 952 .ToLocal(&stack_trace_string) && 953 stack_trace_string->IsString()) { 954 v8::String::Utf8Value stack_trace( 955 Local<String>::Cast(stack_trace_string)); 956 printf("%s\n", ToCString(stack_trace)); 957 } 958 } 959 printf("\n"); 960 #ifndef V8_SHARED 961 if (enter_context) context->Exit(); 962 #endif // !V8_SHARED 963 } 964 965 966 #ifndef V8_SHARED 967 int32_t* Counter::Bind(const char* name, bool is_histogram) { 968 int i; 969 for (i = 0; i < kMaxNameSize - 1 && name[i]; i++) 970 name_[i] = static_cast<char>(name[i]); 971 name_[i] = '\0'; 972 is_histogram_ = is_histogram; 973 return ptr(); 974 } 975 976 977 void Counter::AddSample(int32_t sample) { 978 count_++; 979 sample_total_ += sample; 980 } 981 982 983 CounterCollection::CounterCollection() { 984 magic_number_ = 0xDEADFACE; 985 max_counters_ = kMaxCounters; 986 max_name_size_ = Counter::kMaxNameSize; 987 counters_in_use_ = 0; 988 } 989 990 991 Counter* CounterCollection::GetNextCounter() { 992 if (counters_in_use_ == kMaxCounters) return NULL; 993 return &counters_[counters_in_use_++]; 994 } 995 996 997 void Shell::MapCounters(v8::Isolate* isolate, const char* name) { 998 counters_file_ = base::OS::MemoryMappedFile::create( 999 name, sizeof(CounterCollection), &local_counters_); 1000 void* memory = (counters_file_ == NULL) ? 1001 NULL : counters_file_->memory(); 1002 if (memory == NULL) { 1003 printf("Could not map counters file %s\n", name); 1004 Exit(1); 1005 } 1006 counters_ = static_cast<CounterCollection*>(memory); 1007 isolate->SetCounterFunction(LookupCounter); 1008 isolate->SetCreateHistogramFunction(CreateHistogram); 1009 isolate->SetAddHistogramSampleFunction(AddHistogramSample); 1010 } 1011 1012 1013 int CounterMap::Hash(const char* name) { 1014 int h = 0; 1015 int c; 1016 while ((c = *name++) != 0) { 1017 h += h << 5; 1018 h += c; 1019 } 1020 return h; 1021 } 1022 1023 1024 Counter* Shell::GetCounter(const char* name, bool is_histogram) { 1025 Counter* counter = counter_map_->Lookup(name); 1026 1027 if (counter == NULL) { 1028 counter = counters_->GetNextCounter(); 1029 if (counter != NULL) { 1030 counter_map_->Set(name, counter); 1031 counter->Bind(name, is_histogram); 1032 } 1033 } else { 1034 DCHECK(counter->is_histogram() == is_histogram); 1035 } 1036 return counter; 1037 } 1038 1039 1040 int* Shell::LookupCounter(const char* name) { 1041 Counter* counter = GetCounter(name, false); 1042 1043 if (counter != NULL) { 1044 return counter->ptr(); 1045 } else { 1046 return NULL; 1047 } 1048 } 1049 1050 1051 void* Shell::CreateHistogram(const char* name, 1052 int min, 1053 int max, 1054 size_t buckets) { 1055 return GetCounter(name, true); 1056 } 1057 1058 1059 void Shell::AddHistogramSample(void* histogram, int sample) { 1060 Counter* counter = reinterpret_cast<Counter*>(histogram); 1061 counter->AddSample(sample); 1062 } 1063 1064 // Turn a value into a human-readable string. 1065 Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) { 1066 v8::Local<v8::Context> context = 1067 v8::Local<v8::Context>::New(isolate, evaluation_context_); 1068 if (stringify_function_.IsEmpty()) { 1069 int source_index = i::NativesCollection<i::D8>::GetIndex("d8"); 1070 i::Vector<const char> source_string = 1071 i::NativesCollection<i::D8>::GetScriptSource(source_index); 1072 i::Vector<const char> source_name = 1073 i::NativesCollection<i::D8>::GetScriptName(source_index); 1074 Local<String> source = 1075 String::NewFromUtf8(isolate, source_string.start(), 1076 NewStringType::kNormal, source_string.length()) 1077 .ToLocalChecked(); 1078 Local<String> name = 1079 String::NewFromUtf8(isolate, source_name.start(), 1080 NewStringType::kNormal, source_name.length()) 1081 .ToLocalChecked(); 1082 ScriptOrigin origin(name); 1083 Local<Script> script = 1084 Script::Compile(context, source, &origin).ToLocalChecked(); 1085 stringify_function_.Reset( 1086 isolate, script->Run(context).ToLocalChecked().As<Function>()); 1087 } 1088 Local<Function> fun = Local<Function>::New(isolate, stringify_function_); 1089 Local<Value> argv[1] = {value}; 1090 v8::TryCatch try_catch(isolate); 1091 MaybeLocal<Value> result = 1092 fun->Call(context, Undefined(isolate), 1, argv).ToLocalChecked(); 1093 if (result.IsEmpty()) return String::Empty(isolate); 1094 return result.ToLocalChecked().As<String>(); 1095 } 1096 #endif // !V8_SHARED 1097 1098 1099 Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) { 1100 Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate); 1101 global_template->Set( 1102 String::NewFromUtf8(isolate, "print", NewStringType::kNormal) 1103 .ToLocalChecked(), 1104 FunctionTemplate::New(isolate, Print)); 1105 global_template->Set( 1106 String::NewFromUtf8(isolate, "write", NewStringType::kNormal) 1107 .ToLocalChecked(), 1108 FunctionTemplate::New(isolate, Write)); 1109 global_template->Set( 1110 String::NewFromUtf8(isolate, "read", NewStringType::kNormal) 1111 .ToLocalChecked(), 1112 FunctionTemplate::New(isolate, Read)); 1113 global_template->Set( 1114 String::NewFromUtf8(isolate, "readbuffer", NewStringType::kNormal) 1115 .ToLocalChecked(), 1116 FunctionTemplate::New(isolate, ReadBuffer)); 1117 global_template->Set( 1118 String::NewFromUtf8(isolate, "readline", NewStringType::kNormal) 1119 .ToLocalChecked(), 1120 FunctionTemplate::New(isolate, ReadLine)); 1121 global_template->Set( 1122 String::NewFromUtf8(isolate, "load", NewStringType::kNormal) 1123 .ToLocalChecked(), 1124 FunctionTemplate::New(isolate, Load)); 1125 // Some Emscripten-generated code tries to call 'quit', which in turn would 1126 // call C's exit(). This would lead to memory leaks, because there is no way 1127 // we can terminate cleanly then, so we need a way to hide 'quit'. 1128 if (!options.omit_quit) { 1129 global_template->Set( 1130 String::NewFromUtf8(isolate, "quit", NewStringType::kNormal) 1131 .ToLocalChecked(), 1132 FunctionTemplate::New(isolate, Quit)); 1133 } 1134 global_template->Set( 1135 String::NewFromUtf8(isolate, "version", NewStringType::kNormal) 1136 .ToLocalChecked(), 1137 FunctionTemplate::New(isolate, Version)); 1138 global_template->Set( 1139 Symbol::GetToStringTag(isolate), 1140 String::NewFromUtf8(isolate, "global", NewStringType::kNormal) 1141 .ToLocalChecked()); 1142 1143 // Bind the Realm object. 1144 Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate); 1145 realm_template->Set( 1146 String::NewFromUtf8(isolate, "current", NewStringType::kNormal) 1147 .ToLocalChecked(), 1148 FunctionTemplate::New(isolate, RealmCurrent)); 1149 realm_template->Set( 1150 String::NewFromUtf8(isolate, "owner", NewStringType::kNormal) 1151 .ToLocalChecked(), 1152 FunctionTemplate::New(isolate, RealmOwner)); 1153 realm_template->Set( 1154 String::NewFromUtf8(isolate, "global", NewStringType::kNormal) 1155 .ToLocalChecked(), 1156 FunctionTemplate::New(isolate, RealmGlobal)); 1157 realm_template->Set( 1158 String::NewFromUtf8(isolate, "create", NewStringType::kNormal) 1159 .ToLocalChecked(), 1160 FunctionTemplate::New(isolate, RealmCreate)); 1161 realm_template->Set( 1162 String::NewFromUtf8(isolate, "createAllowCrossRealmAccess", 1163 NewStringType::kNormal) 1164 .ToLocalChecked(), 1165 FunctionTemplate::New(isolate, RealmCreateAllowCrossRealmAccess)); 1166 realm_template->Set( 1167 String::NewFromUtf8(isolate, "dispose", NewStringType::kNormal) 1168 .ToLocalChecked(), 1169 FunctionTemplate::New(isolate, RealmDispose)); 1170 realm_template->Set( 1171 String::NewFromUtf8(isolate, "switch", NewStringType::kNormal) 1172 .ToLocalChecked(), 1173 FunctionTemplate::New(isolate, RealmSwitch)); 1174 realm_template->Set( 1175 String::NewFromUtf8(isolate, "eval", NewStringType::kNormal) 1176 .ToLocalChecked(), 1177 FunctionTemplate::New(isolate, RealmEval)); 1178 realm_template->SetAccessor( 1179 String::NewFromUtf8(isolate, "shared", NewStringType::kNormal) 1180 .ToLocalChecked(), 1181 RealmSharedGet, RealmSharedSet); 1182 global_template->Set( 1183 String::NewFromUtf8(isolate, "Realm", NewStringType::kNormal) 1184 .ToLocalChecked(), 1185 realm_template); 1186 1187 #ifndef V8_SHARED 1188 Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate); 1189 performance_template->Set( 1190 String::NewFromUtf8(isolate, "now", NewStringType::kNormal) 1191 .ToLocalChecked(), 1192 FunctionTemplate::New(isolate, PerformanceNow)); 1193 global_template->Set( 1194 String::NewFromUtf8(isolate, "performance", NewStringType::kNormal) 1195 .ToLocalChecked(), 1196 performance_template); 1197 1198 Local<FunctionTemplate> worker_fun_template = 1199 FunctionTemplate::New(isolate, WorkerNew); 1200 Local<Signature> worker_signature = 1201 Signature::New(isolate, worker_fun_template); 1202 worker_fun_template->SetClassName( 1203 String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal) 1204 .ToLocalChecked()); 1205 worker_fun_template->ReadOnlyPrototype(); 1206 worker_fun_template->PrototypeTemplate()->Set( 1207 String::NewFromUtf8(isolate, "terminate", NewStringType::kNormal) 1208 .ToLocalChecked(), 1209 FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(), 1210 worker_signature)); 1211 worker_fun_template->PrototypeTemplate()->Set( 1212 String::NewFromUtf8(isolate, "postMessage", NewStringType::kNormal) 1213 .ToLocalChecked(), 1214 FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(), 1215 worker_signature)); 1216 worker_fun_template->PrototypeTemplate()->Set( 1217 String::NewFromUtf8(isolate, "getMessage", NewStringType::kNormal) 1218 .ToLocalChecked(), 1219 FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(), 1220 worker_signature)); 1221 worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1); 1222 global_template->Set( 1223 String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal) 1224 .ToLocalChecked(), 1225 worker_fun_template); 1226 #endif // !V8_SHARED 1227 1228 Local<ObjectTemplate> os_templ = ObjectTemplate::New(isolate); 1229 AddOSMethods(isolate, os_templ); 1230 global_template->Set( 1231 String::NewFromUtf8(isolate, "os", NewStringType::kNormal) 1232 .ToLocalChecked(), 1233 os_templ); 1234 1235 return global_template; 1236 } 1237 1238 static void EmptyMessageCallback(Local<Message> message, Local<Value> error) { 1239 // Nothing to be done here, exceptions thrown up to the shell will be reported 1240 // separately by {Shell::ReportException} after they are caught. 1241 } 1242 1243 void Shell::Initialize(Isolate* isolate) { 1244 #ifndef V8_SHARED 1245 // Set up counters 1246 if (i::StrLength(i::FLAG_map_counters) != 0) 1247 MapCounters(isolate, i::FLAG_map_counters); 1248 #endif // !V8_SHARED 1249 // Disable default message reporting. 1250 isolate->AddMessageListener(EmptyMessageCallback); 1251 } 1252 1253 1254 Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) { 1255 #ifndef V8_SHARED 1256 // This needs to be a critical section since this is not thread-safe 1257 base::LockGuard<base::Mutex> lock_guard(context_mutex_.Pointer()); 1258 #endif // !V8_SHARED 1259 // Initialize the global objects 1260 Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate); 1261 EscapableHandleScope handle_scope(isolate); 1262 Local<Context> context = Context::New(isolate, NULL, global_template); 1263 DCHECK(!context.IsEmpty()); 1264 Context::Scope scope(context); 1265 1266 #ifndef V8_SHARED 1267 i::Factory* factory = reinterpret_cast<i::Isolate*>(isolate)->factory(); 1268 i::JSArguments js_args = i::FLAG_js_arguments; 1269 i::Handle<i::FixedArray> arguments_array = 1270 factory->NewFixedArray(js_args.argc); 1271 for (int j = 0; j < js_args.argc; j++) { 1272 i::Handle<i::String> arg = 1273 factory->NewStringFromUtf8(i::CStrVector(js_args[j])).ToHandleChecked(); 1274 arguments_array->set(j, *arg); 1275 } 1276 i::Handle<i::JSArray> arguments_jsarray = 1277 factory->NewJSArrayWithElements(arguments_array); 1278 context->Global() 1279 ->Set(context, 1280 String::NewFromUtf8(isolate, "arguments", NewStringType::kNormal) 1281 .ToLocalChecked(), 1282 Utils::ToLocal(arguments_jsarray)) 1283 .FromJust(); 1284 #endif // !V8_SHARED 1285 return handle_scope.Escape(context); 1286 } 1287 1288 1289 void Shell::Exit(int exit_code) { 1290 // Use _exit instead of exit to avoid races between isolate 1291 // threads and static destructors. 1292 fflush(stdout); 1293 fflush(stderr); 1294 _exit(exit_code); 1295 } 1296 1297 1298 #ifndef V8_SHARED 1299 struct CounterAndKey { 1300 Counter* counter; 1301 const char* key; 1302 }; 1303 1304 1305 inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) { 1306 return strcmp(lhs.key, rhs.key) < 0; 1307 } 1308 1309 void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) { 1310 HandleScope handle_scope(isolate); 1311 Local<Context> context = Context::New(isolate); 1312 Context::Scope context_scope(context); 1313 1314 Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate) 1315 ->interpreter() 1316 ->GetDispatchCountersObject(); 1317 std::ofstream dispatch_counters_stream( 1318 i::FLAG_trace_ignition_dispatches_output_file); 1319 dispatch_counters_stream << *String::Utf8Value( 1320 JSON::Stringify(context, dispatch_counters).ToLocalChecked()); 1321 } 1322 1323 #endif // !V8_SHARED 1324 1325 1326 void Shell::OnExit(v8::Isolate* isolate) { 1327 #ifndef V8_SHARED 1328 if (i::FLAG_dump_counters) { 1329 int number_of_counters = 0; 1330 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) { 1331 number_of_counters++; 1332 } 1333 CounterAndKey* counters = new CounterAndKey[number_of_counters]; 1334 int j = 0; 1335 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next(), j++) { 1336 counters[j].counter = i.CurrentValue(); 1337 counters[j].key = i.CurrentKey(); 1338 } 1339 std::sort(counters, counters + number_of_counters); 1340 printf("+----------------------------------------------------------------+" 1341 "-------------+\n"); 1342 printf("| Name |" 1343 " Value |\n"); 1344 printf("+----------------------------------------------------------------+" 1345 "-------------+\n"); 1346 for (j = 0; j < number_of_counters; j++) { 1347 Counter* counter = counters[j].counter; 1348 const char* key = counters[j].key; 1349 if (counter->is_histogram()) { 1350 printf("| c:%-60s | %11i |\n", key, counter->count()); 1351 printf("| t:%-60s | %11i |\n", key, counter->sample_total()); 1352 } else { 1353 printf("| %-62s | %11i |\n", key, counter->count()); 1354 } 1355 } 1356 printf("+----------------------------------------------------------------+" 1357 "-------------+\n"); 1358 delete [] counters; 1359 } 1360 1361 delete counters_file_; 1362 delete counter_map_; 1363 #endif // !V8_SHARED 1364 } 1365 1366 1367 1368 static FILE* FOpen(const char* path, const char* mode) { 1369 #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64)) 1370 FILE* result; 1371 if (fopen_s(&result, path, mode) == 0) { 1372 return result; 1373 } else { 1374 return NULL; 1375 } 1376 #else 1377 FILE* file = fopen(path, mode); 1378 if (file == NULL) return NULL; 1379 struct stat file_stat; 1380 if (fstat(fileno(file), &file_stat) != 0) return NULL; 1381 bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0); 1382 if (is_regular_file) return file; 1383 fclose(file); 1384 return NULL; 1385 #endif 1386 } 1387 1388 1389 static char* ReadChars(Isolate* isolate, const char* name, int* size_out) { 1390 FILE* file = FOpen(name, "rb"); 1391 if (file == NULL) return NULL; 1392 1393 fseek(file, 0, SEEK_END); 1394 size_t size = ftell(file); 1395 rewind(file); 1396 1397 char* chars = new char[size + 1]; 1398 chars[size] = '\0'; 1399 for (size_t i = 0; i < size;) { 1400 i += fread(&chars[i], 1, size - i, file); 1401 if (ferror(file)) { 1402 fclose(file); 1403 delete[] chars; 1404 return nullptr; 1405 } 1406 } 1407 fclose(file); 1408 *size_out = static_cast<int>(size); 1409 return chars; 1410 } 1411 1412 1413 struct DataAndPersistent { 1414 uint8_t* data; 1415 int byte_length; 1416 Global<ArrayBuffer> handle; 1417 }; 1418 1419 1420 static void ReadBufferWeakCallback( 1421 const v8::WeakCallbackInfo<DataAndPersistent>& data) { 1422 int byte_length = data.GetParameter()->byte_length; 1423 data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory( 1424 -static_cast<intptr_t>(byte_length)); 1425 1426 delete[] data.GetParameter()->data; 1427 data.GetParameter()->handle.Reset(); 1428 delete data.GetParameter(); 1429 } 1430 1431 1432 void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) { 1433 DCHECK(sizeof(char) == sizeof(uint8_t)); // NOLINT 1434 String::Utf8Value filename(args[0]); 1435 int length; 1436 if (*filename == NULL) { 1437 Throw(args.GetIsolate(), "Error loading file"); 1438 return; 1439 } 1440 1441 Isolate* isolate = args.GetIsolate(); 1442 DataAndPersistent* data = new DataAndPersistent; 1443 data->data = reinterpret_cast<uint8_t*>( 1444 ReadChars(args.GetIsolate(), *filename, &length)); 1445 if (data->data == NULL) { 1446 delete data; 1447 Throw(args.GetIsolate(), "Error reading file"); 1448 return; 1449 } 1450 data->byte_length = length; 1451 Local<v8::ArrayBuffer> buffer = ArrayBuffer::New(isolate, data->data, length); 1452 data->handle.Reset(isolate, buffer); 1453 data->handle.SetWeak(data, ReadBufferWeakCallback, 1454 v8::WeakCallbackType::kParameter); 1455 data->handle.MarkIndependent(); 1456 isolate->AdjustAmountOfExternalAllocatedMemory(length); 1457 1458 args.GetReturnValue().Set(buffer); 1459 } 1460 1461 1462 // Reads a file into a v8 string. 1463 Local<String> Shell::ReadFile(Isolate* isolate, const char* name) { 1464 int size = 0; 1465 char* chars = ReadChars(isolate, name, &size); 1466 if (chars == NULL) return Local<String>(); 1467 Local<String> result = 1468 String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size) 1469 .ToLocalChecked(); 1470 delete[] chars; 1471 return result; 1472 } 1473 1474 1475 void Shell::RunShell(Isolate* isolate) { 1476 HandleScope outer_scope(isolate); 1477 v8::Local<v8::Context> context = 1478 v8::Local<v8::Context>::New(isolate, evaluation_context_); 1479 v8::Context::Scope context_scope(context); 1480 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); 1481 Local<String> name = 1482 String::NewFromUtf8(isolate, "(d8)", NewStringType::kNormal) 1483 .ToLocalChecked(); 1484 printf("V8 version %s\n", V8::GetVersion()); 1485 while (true) { 1486 HandleScope inner_scope(isolate); 1487 printf("d8> "); 1488 #if defined(__native_client__) 1489 // Native Client libc is used to being embedded in Chrome and 1490 // has trouble recognizing when to flush. 1491 fflush(stdout); 1492 #endif 1493 Local<String> input = Shell::ReadFromStdin(isolate); 1494 if (input.IsEmpty()) break; 1495 ExecuteString(isolate, input, name, true, true); 1496 } 1497 printf("\n"); 1498 } 1499 1500 1501 SourceGroup::~SourceGroup() { 1502 #ifndef V8_SHARED 1503 delete thread_; 1504 thread_ = NULL; 1505 #endif // !V8_SHARED 1506 } 1507 1508 1509 void SourceGroup::Execute(Isolate* isolate) { 1510 bool exception_was_thrown = false; 1511 for (int i = begin_offset_; i < end_offset_; ++i) { 1512 const char* arg = argv_[i]; 1513 Shell::SourceType source_type = Shell::SCRIPT; 1514 if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) { 1515 // Execute argument given to -e option directly. 1516 HandleScope handle_scope(isolate); 1517 Local<String> file_name = 1518 String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal) 1519 .ToLocalChecked(); 1520 Local<String> source = 1521 String::NewFromUtf8(isolate, argv_[i + 1], NewStringType::kNormal) 1522 .ToLocalChecked(); 1523 Shell::options.script_executed = true; 1524 if (!Shell::ExecuteString(isolate, source, file_name, false, true)) { 1525 exception_was_thrown = true; 1526 break; 1527 } 1528 ++i; 1529 continue; 1530 } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) { 1531 // Treat the next file as a module. 1532 source_type = Shell::MODULE; 1533 arg = argv_[++i]; 1534 } else if (arg[0] == '-') { 1535 // Ignore other options. They have been parsed already. 1536 continue; 1537 } 1538 1539 // Use all other arguments as names of files to load and run. 1540 HandleScope handle_scope(isolate); 1541 Local<String> file_name = 1542 String::NewFromUtf8(isolate, arg, NewStringType::kNormal) 1543 .ToLocalChecked(); 1544 Local<String> source = ReadFile(isolate, arg); 1545 if (source.IsEmpty()) { 1546 printf("Error reading '%s'\n", arg); 1547 Shell::Exit(1); 1548 } 1549 Shell::options.script_executed = true; 1550 if (!Shell::ExecuteString(isolate, source, file_name, false, true, 1551 source_type)) { 1552 exception_was_thrown = true; 1553 break; 1554 } 1555 } 1556 if (exception_was_thrown != Shell::options.expected_to_throw) { 1557 Shell::Exit(1); 1558 } 1559 } 1560 1561 1562 Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) { 1563 int size; 1564 char* chars = ReadChars(isolate, name, &size); 1565 if (chars == NULL) return Local<String>(); 1566 Local<String> result = 1567 String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size) 1568 .ToLocalChecked(); 1569 delete[] chars; 1570 return result; 1571 } 1572 1573 1574 #ifndef V8_SHARED 1575 base::Thread::Options SourceGroup::GetThreadOptions() { 1576 // On some systems (OSX 10.6) the stack size default is 0.5Mb or less 1577 // which is not enough to parse the big literal expressions used in tests. 1578 // The stack size should be at least StackGuard::kLimitSize + some 1579 // OS-specific padding for thread startup code. 2Mbytes seems to be enough. 1580 return base::Thread::Options("IsolateThread", 2 * MB); 1581 } 1582 1583 1584 void SourceGroup::ExecuteInThread() { 1585 Isolate::CreateParams create_params; 1586 create_params.array_buffer_allocator = Shell::array_buffer_allocator; 1587 Isolate* isolate = Isolate::New(create_params); 1588 for (int i = 0; i < Shell::options.stress_runs; ++i) { 1589 next_semaphore_.Wait(); 1590 { 1591 Isolate::Scope iscope(isolate); 1592 { 1593 HandleScope scope(isolate); 1594 PerIsolateData data(isolate); 1595 Local<Context> context = Shell::CreateEvaluationContext(isolate); 1596 { 1597 Context::Scope cscope(context); 1598 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); 1599 Execute(isolate); 1600 } 1601 } 1602 Shell::CollectGarbage(isolate); 1603 } 1604 done_semaphore_.Signal(); 1605 } 1606 1607 isolate->Dispose(); 1608 } 1609 1610 1611 void SourceGroup::StartExecuteInThread() { 1612 if (thread_ == NULL) { 1613 thread_ = new IsolateThread(this); 1614 thread_->Start(); 1615 } 1616 next_semaphore_.Signal(); 1617 } 1618 1619 1620 void SourceGroup::WaitForThread() { 1621 if (thread_ == NULL) return; 1622 done_semaphore_.Wait(); 1623 } 1624 1625 1626 void SourceGroup::JoinThread() { 1627 if (thread_ == NULL) return; 1628 thread_->Join(); 1629 } 1630 1631 1632 SerializationData::~SerializationData() { 1633 // Any ArrayBuffer::Contents are owned by this SerializationData object if 1634 // ownership hasn't been transferred out via ReadArrayBufferContents. 1635 // SharedArrayBuffer::Contents may be used by multiple threads, so must be 1636 // cleaned up by the main thread in Shell::CleanupWorkers(). 1637 for (int i = 0; i < array_buffer_contents_.length(); ++i) { 1638 ArrayBuffer::Contents& contents = array_buffer_contents_[i]; 1639 if (contents.Data()) { 1640 Shell::array_buffer_allocator->Free(contents.Data(), 1641 contents.ByteLength()); 1642 } 1643 } 1644 } 1645 1646 1647 void SerializationData::WriteTag(SerializationTag tag) { data_.Add(tag); } 1648 1649 1650 void SerializationData::WriteMemory(const void* p, int length) { 1651 if (length > 0) { 1652 i::Vector<uint8_t> block = data_.AddBlock(0, length); 1653 memcpy(&block[0], p, length); 1654 } 1655 } 1656 1657 1658 void SerializationData::WriteArrayBufferContents( 1659 const ArrayBuffer::Contents& contents) { 1660 array_buffer_contents_.Add(contents); 1661 WriteTag(kSerializationTagTransferredArrayBuffer); 1662 int index = array_buffer_contents_.length() - 1; 1663 Write(index); 1664 } 1665 1666 1667 void SerializationData::WriteSharedArrayBufferContents( 1668 const SharedArrayBuffer::Contents& contents) { 1669 shared_array_buffer_contents_.Add(contents); 1670 WriteTag(kSerializationTagTransferredSharedArrayBuffer); 1671 int index = shared_array_buffer_contents_.length() - 1; 1672 Write(index); 1673 } 1674 1675 1676 SerializationTag SerializationData::ReadTag(int* offset) const { 1677 return static_cast<SerializationTag>(Read<uint8_t>(offset)); 1678 } 1679 1680 1681 void SerializationData::ReadMemory(void* p, int length, int* offset) const { 1682 if (length > 0) { 1683 memcpy(p, &data_[*offset], length); 1684 (*offset) += length; 1685 } 1686 } 1687 1688 1689 void SerializationData::ReadArrayBufferContents(ArrayBuffer::Contents* contents, 1690 int* offset) const { 1691 int index = Read<int>(offset); 1692 DCHECK(index < array_buffer_contents_.length()); 1693 *contents = array_buffer_contents_[index]; 1694 // Ownership of this ArrayBuffer::Contents is passed to the caller. Neuter 1695 // our copy so it won't be double-free'd when this SerializationData is 1696 // destroyed. 1697 array_buffer_contents_[index] = ArrayBuffer::Contents(); 1698 } 1699 1700 1701 void SerializationData::ReadSharedArrayBufferContents( 1702 SharedArrayBuffer::Contents* contents, int* offset) const { 1703 int index = Read<int>(offset); 1704 DCHECK(index < shared_array_buffer_contents_.length()); 1705 *contents = shared_array_buffer_contents_[index]; 1706 } 1707 1708 1709 void SerializationDataQueue::Enqueue(SerializationData* data) { 1710 base::LockGuard<base::Mutex> lock_guard(&mutex_); 1711 data_.Add(data); 1712 } 1713 1714 1715 bool SerializationDataQueue::Dequeue(SerializationData** data) { 1716 base::LockGuard<base::Mutex> lock_guard(&mutex_); 1717 *data = NULL; 1718 if (data_.is_empty()) return false; 1719 *data = data_.Remove(0); 1720 return true; 1721 } 1722 1723 1724 bool SerializationDataQueue::IsEmpty() { 1725 base::LockGuard<base::Mutex> lock_guard(&mutex_); 1726 return data_.is_empty(); 1727 } 1728 1729 1730 void SerializationDataQueue::Clear() { 1731 base::LockGuard<base::Mutex> lock_guard(&mutex_); 1732 for (int i = 0; i < data_.length(); ++i) { 1733 delete data_[i]; 1734 } 1735 data_.Clear(); 1736 } 1737 1738 1739 Worker::Worker() 1740 : in_semaphore_(0), 1741 out_semaphore_(0), 1742 thread_(NULL), 1743 script_(NULL), 1744 running_(false) {} 1745 1746 1747 Worker::~Worker() { 1748 delete thread_; 1749 thread_ = NULL; 1750 delete[] script_; 1751 script_ = NULL; 1752 in_queue_.Clear(); 1753 out_queue_.Clear(); 1754 } 1755 1756 1757 void Worker::StartExecuteInThread(const char* script) { 1758 running_ = true; 1759 script_ = i::StrDup(script); 1760 thread_ = new WorkerThread(this); 1761 thread_->Start(); 1762 } 1763 1764 1765 void Worker::PostMessage(SerializationData* data) { 1766 in_queue_.Enqueue(data); 1767 in_semaphore_.Signal(); 1768 } 1769 1770 1771 SerializationData* Worker::GetMessage() { 1772 SerializationData* data = NULL; 1773 while (!out_queue_.Dequeue(&data)) { 1774 // If the worker is no longer running, and there are no messages in the 1775 // queue, don't expect any more messages from it. 1776 if (!base::NoBarrier_Load(&running_)) break; 1777 out_semaphore_.Wait(); 1778 } 1779 return data; 1780 } 1781 1782 1783 void Worker::Terminate() { 1784 base::NoBarrier_Store(&running_, false); 1785 // Post NULL to wake the Worker thread message loop, and tell it to stop 1786 // running. 1787 PostMessage(NULL); 1788 } 1789 1790 1791 void Worker::WaitForThread() { 1792 Terminate(); 1793 thread_->Join(); 1794 } 1795 1796 1797 void Worker::ExecuteInThread() { 1798 Isolate::CreateParams create_params; 1799 create_params.array_buffer_allocator = Shell::array_buffer_allocator; 1800 Isolate* isolate = Isolate::New(create_params); 1801 { 1802 Isolate::Scope iscope(isolate); 1803 { 1804 HandleScope scope(isolate); 1805 PerIsolateData data(isolate); 1806 Local<Context> context = Shell::CreateEvaluationContext(isolate); 1807 { 1808 Context::Scope cscope(context); 1809 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); 1810 1811 Local<Object> global = context->Global(); 1812 Local<Value> this_value = External::New(isolate, this); 1813 Local<FunctionTemplate> postmessage_fun_template = 1814 FunctionTemplate::New(isolate, PostMessageOut, this_value); 1815 1816 Local<Function> postmessage_fun; 1817 if (postmessage_fun_template->GetFunction(context) 1818 .ToLocal(&postmessage_fun)) { 1819 global->Set(context, String::NewFromUtf8(isolate, "postMessage", 1820 NewStringType::kNormal) 1821 .ToLocalChecked(), 1822 postmessage_fun).FromJust(); 1823 } 1824 1825 // First run the script 1826 Local<String> file_name = 1827 String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal) 1828 .ToLocalChecked(); 1829 Local<String> source = 1830 String::NewFromUtf8(isolate, script_, NewStringType::kNormal) 1831 .ToLocalChecked(); 1832 if (Shell::ExecuteString(isolate, source, file_name, false, true)) { 1833 // Get the message handler 1834 Local<Value> onmessage = 1835 global->Get(context, String::NewFromUtf8(isolate, "onmessage", 1836 NewStringType::kNormal) 1837 .ToLocalChecked()).ToLocalChecked(); 1838 if (onmessage->IsFunction()) { 1839 Local<Function> onmessage_fun = Local<Function>::Cast(onmessage); 1840 // Now wait for messages 1841 while (true) { 1842 in_semaphore_.Wait(); 1843 SerializationData* data; 1844 if (!in_queue_.Dequeue(&data)) continue; 1845 if (data == NULL) { 1846 break; 1847 } 1848 int offset = 0; 1849 Local<Value> data_value; 1850 if (Shell::DeserializeValue(isolate, *data, &offset) 1851 .ToLocal(&data_value)) { 1852 Local<Value> argv[] = {data_value}; 1853 (void)onmessage_fun->Call(context, global, 1, argv); 1854 } 1855 delete data; 1856 } 1857 } 1858 } 1859 } 1860 } 1861 Shell::CollectGarbage(isolate); 1862 } 1863 isolate->Dispose(); 1864 1865 // Post NULL to wake the thread waiting on GetMessage() if there is one. 1866 out_queue_.Enqueue(NULL); 1867 out_semaphore_.Signal(); 1868 } 1869 1870 1871 void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) { 1872 Isolate* isolate = args.GetIsolate(); 1873 HandleScope handle_scope(isolate); 1874 1875 if (args.Length() < 1) { 1876 Throw(isolate, "Invalid argument"); 1877 return; 1878 } 1879 1880 Local<Value> message = args[0]; 1881 1882 // TODO(binji): Allow transferring from worker to main thread? 1883 Shell::ObjectList to_transfer; 1884 1885 Shell::ObjectList seen_objects; 1886 SerializationData* data = new SerializationData; 1887 if (Shell::SerializeValue(isolate, message, to_transfer, &seen_objects, 1888 data)) { 1889 DCHECK(args.Data()->IsExternal()); 1890 Local<External> this_value = Local<External>::Cast(args.Data()); 1891 Worker* worker = static_cast<Worker*>(this_value->Value()); 1892 worker->out_queue_.Enqueue(data); 1893 worker->out_semaphore_.Signal(); 1894 } else { 1895 delete data; 1896 } 1897 } 1898 #endif // !V8_SHARED 1899 1900 1901 void SetFlagsFromString(const char* flags) { 1902 v8::V8::SetFlagsFromString(flags, static_cast<int>(strlen(flags))); 1903 } 1904 1905 1906 bool Shell::SetOptions(int argc, char* argv[]) { 1907 bool logfile_per_isolate = false; 1908 for (int i = 0; i < argc; i++) { 1909 if (strcmp(argv[i], "--stress-opt") == 0) { 1910 options.stress_opt = true; 1911 argv[i] = NULL; 1912 } else if (strcmp(argv[i], "--nostress-opt") == 0) { 1913 options.stress_opt = false; 1914 argv[i] = NULL; 1915 } else if (strcmp(argv[i], "--stress-deopt") == 0) { 1916 options.stress_deopt = true; 1917 argv[i] = NULL; 1918 } else if (strcmp(argv[i], "--mock-arraybuffer-allocator") == 0) { 1919 options.mock_arraybuffer_allocator = true; 1920 argv[i] = NULL; 1921 } else if (strcmp(argv[i], "--noalways-opt") == 0) { 1922 // No support for stressing if we can't use --always-opt. 1923 options.stress_opt = false; 1924 options.stress_deopt = false; 1925 } else if (strcmp(argv[i], "--logfile-per-isolate") == 0) { 1926 logfile_per_isolate = true; 1927 argv[i] = NULL; 1928 } else if (strcmp(argv[i], "--shell") == 0) { 1929 options.interactive_shell = true; 1930 argv[i] = NULL; 1931 } else if (strcmp(argv[i], "--test") == 0) { 1932 options.test_shell = true; 1933 argv[i] = NULL; 1934 } else if (strcmp(argv[i], "--notest") == 0 || 1935 strcmp(argv[i], "--no-test") == 0) { 1936 options.test_shell = false; 1937 argv[i] = NULL; 1938 } else if (strcmp(argv[i], "--send-idle-notification") == 0) { 1939 options.send_idle_notification = true; 1940 argv[i] = NULL; 1941 } else if (strcmp(argv[i], "--invoke-weak-callbacks") == 0) { 1942 options.invoke_weak_callbacks = true; 1943 // TODO(jochen) See issue 3351 1944 options.send_idle_notification = true; 1945 argv[i] = NULL; 1946 } else if (strcmp(argv[i], "--omit-quit") == 0) { 1947 options.omit_quit = true; 1948 argv[i] = NULL; 1949 } else if (strcmp(argv[i], "-f") == 0) { 1950 // Ignore any -f flags for compatibility with other stand-alone 1951 // JavaScript engines. 1952 continue; 1953 } else if (strcmp(argv[i], "--isolate") == 0) { 1954 #ifdef V8_SHARED 1955 printf("D8 with shared library does not support multi-threading\n"); 1956 return false; 1957 #endif // V8_SHARED 1958 options.num_isolates++; 1959 } else if (strcmp(argv[i], "--dump-heap-constants") == 0) { 1960 #ifdef V8_SHARED 1961 printf("D8 with shared library does not support constant dumping\n"); 1962 return false; 1963 #else 1964 options.dump_heap_constants = true; 1965 argv[i] = NULL; 1966 #endif // V8_SHARED 1967 } else if (strcmp(argv[i], "--throws") == 0) { 1968 options.expected_to_throw = true; 1969 argv[i] = NULL; 1970 } else if (strncmp(argv[i], "--icu-data-file=", 16) == 0) { 1971 options.icu_data_file = argv[i] + 16; 1972 argv[i] = NULL; 1973 #ifdef V8_SHARED 1974 } else if (strcmp(argv[i], "--dump-counters") == 0) { 1975 printf("D8 with shared library does not include counters\n"); 1976 return false; 1977 #endif // V8_SHARED 1978 #ifdef V8_USE_EXTERNAL_STARTUP_DATA 1979 } else if (strncmp(argv[i], "--natives_blob=", 15) == 0) { 1980 options.natives_blob = argv[i] + 15; 1981 argv[i] = NULL; 1982 } else if (strncmp(argv[i], "--snapshot_blob=", 16) == 0) { 1983 options.snapshot_blob = argv[i] + 16; 1984 argv[i] = NULL; 1985 #endif // V8_USE_EXTERNAL_STARTUP_DATA 1986 } else if (strcmp(argv[i], "--cache") == 0 || 1987 strncmp(argv[i], "--cache=", 8) == 0) { 1988 const char* value = argv[i] + 7; 1989 if (!*value || strncmp(value, "=code", 6) == 0) { 1990 options.compile_options = v8::ScriptCompiler::kProduceCodeCache; 1991 } else if (strncmp(value, "=parse", 7) == 0) { 1992 options.compile_options = v8::ScriptCompiler::kProduceParserCache; 1993 } else if (strncmp(value, "=none", 6) == 0) { 1994 options.compile_options = v8::ScriptCompiler::kNoCompileOptions; 1995 } else { 1996 printf("Unknown option to --cache.\n"); 1997 return false; 1998 } 1999 argv[i] = NULL; 2000 } 2001 } 2002 2003 v8::V8::SetFlagsFromCommandLine(&argc, argv, true); 2004 2005 // Set up isolated source groups. 2006 options.isolate_sources = new SourceGroup[options.num_isolates]; 2007 SourceGroup* current = options.isolate_sources; 2008 current->Begin(argv, 1); 2009 for (int i = 1; i < argc; i++) { 2010 const char* str = argv[i]; 2011 if (strcmp(str, "--isolate") == 0) { 2012 current->End(i); 2013 current++; 2014 current->Begin(argv, i + 1); 2015 } else if (strcmp(str, "--module") == 0) { 2016 // Pass on to SourceGroup, which understands this option. 2017 } else if (strncmp(argv[i], "--", 2) == 0) { 2018 printf("Warning: unknown flag %s.\nTry --help for options\n", argv[i]); 2019 } else if (strcmp(str, "-e") == 0 && i + 1 < argc) { 2020 options.script_executed = true; 2021 } else if (strncmp(str, "-", 1) != 0) { 2022 // Not a flag, so it must be a script to execute. 2023 options.script_executed = true; 2024 } 2025 } 2026 current->End(argc); 2027 2028 if (!logfile_per_isolate && options.num_isolates) { 2029 SetFlagsFromString("--nologfile_per_isolate"); 2030 } 2031 2032 return true; 2033 } 2034 2035 2036 int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) { 2037 #ifndef V8_SHARED 2038 for (int i = 1; i < options.num_isolates; ++i) { 2039 options.isolate_sources[i].StartExecuteInThread(); 2040 } 2041 #endif // !V8_SHARED 2042 { 2043 HandleScope scope(isolate); 2044 Local<Context> context = CreateEvaluationContext(isolate); 2045 if (last_run && options.use_interactive_shell()) { 2046 // Keep using the same context in the interactive shell. 2047 evaluation_context_.Reset(isolate, context); 2048 } 2049 { 2050 Context::Scope cscope(context); 2051 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); 2052 options.isolate_sources[0].Execute(isolate); 2053 } 2054 } 2055 CollectGarbage(isolate); 2056 #ifndef V8_SHARED 2057 for (int i = 1; i < options.num_isolates; ++i) { 2058 if (last_run) { 2059 options.isolate_sources[i].JoinThread(); 2060 } else { 2061 options.isolate_sources[i].WaitForThread(); 2062 } 2063 } 2064 CleanupWorkers(); 2065 #endif // !V8_SHARED 2066 return 0; 2067 } 2068 2069 2070 void Shell::CollectGarbage(Isolate* isolate) { 2071 if (options.send_idle_notification) { 2072 const double kLongIdlePauseInSeconds = 1.0; 2073 isolate->ContextDisposedNotification(); 2074 isolate->IdleNotificationDeadline( 2075 g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds); 2076 } 2077 if (options.invoke_weak_callbacks) { 2078 // By sending a low memory notifications, we will try hard to collect all 2079 // garbage and will therefore also invoke all weak callbacks of actually 2080 // unreachable persistent handles. 2081 isolate->LowMemoryNotification(); 2082 } 2083 } 2084 2085 2086 void Shell::EmptyMessageQueues(Isolate* isolate) { 2087 #ifndef V8_SHARED 2088 if (!i::FLAG_verify_predictable) { 2089 #endif 2090 while (v8::platform::PumpMessageLoop(g_platform, isolate)) continue; 2091 #ifndef V8_SHARED 2092 } 2093 #endif 2094 } 2095 2096 2097 #ifndef V8_SHARED 2098 bool Shell::SerializeValue(Isolate* isolate, Local<Value> value, 2099 const ObjectList& to_transfer, 2100 ObjectList* seen_objects, 2101 SerializationData* out_data) { 2102 DCHECK(out_data); 2103 Local<Context> context = isolate->GetCurrentContext(); 2104 2105 if (value->IsUndefined()) { 2106 out_data->WriteTag(kSerializationTagUndefined); 2107 } else if (value->IsNull()) { 2108 out_data->WriteTag(kSerializationTagNull); 2109 } else if (value->IsTrue()) { 2110 out_data->WriteTag(kSerializationTagTrue); 2111 } else if (value->IsFalse()) { 2112 out_data->WriteTag(kSerializationTagFalse); 2113 } else if (value->IsNumber()) { 2114 Local<Number> num = Local<Number>::Cast(value); 2115 double value = num->Value(); 2116 out_data->WriteTag(kSerializationTagNumber); 2117 out_data->Write(value); 2118 } else if (value->IsString()) { 2119 v8::String::Utf8Value str(value); 2120 out_data->WriteTag(kSerializationTagString); 2121 out_data->Write(str.length()); 2122 out_data->WriteMemory(*str, str.length()); 2123 } else if (value->IsArray()) { 2124 Local<Array> array = Local<Array>::Cast(value); 2125 if (FindInObjectList(array, *seen_objects)) { 2126 Throw(isolate, "Duplicated arrays not supported"); 2127 return false; 2128 } 2129 seen_objects->Add(array); 2130 out_data->WriteTag(kSerializationTagArray); 2131 uint32_t length = array->Length(); 2132 out_data->Write(length); 2133 for (uint32_t i = 0; i < length; ++i) { 2134 Local<Value> element_value; 2135 if (array->Get(context, i).ToLocal(&element_value)) { 2136 if (!SerializeValue(isolate, element_value, to_transfer, seen_objects, 2137 out_data)) 2138 return false; 2139 } else { 2140 Throw(isolate, "Failed to serialize array element."); 2141 return false; 2142 } 2143 } 2144 } else if (value->IsArrayBuffer()) { 2145 Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(value); 2146 if (FindInObjectList(array_buffer, *seen_objects)) { 2147 Throw(isolate, "Duplicated array buffers not supported"); 2148 return false; 2149 } 2150 seen_objects->Add(array_buffer); 2151 if (FindInObjectList(array_buffer, to_transfer)) { 2152 // Transfer ArrayBuffer 2153 if (!array_buffer->IsNeuterable()) { 2154 Throw(isolate, "Attempting to transfer an un-neuterable ArrayBuffer"); 2155 return false; 2156 } 2157 2158 ArrayBuffer::Contents contents = array_buffer->IsExternal() 2159 ? array_buffer->GetContents() 2160 : array_buffer->Externalize(); 2161 array_buffer->Neuter(); 2162 out_data->WriteArrayBufferContents(contents); 2163 } else { 2164 ArrayBuffer::Contents contents = array_buffer->GetContents(); 2165 // Clone ArrayBuffer 2166 if (contents.ByteLength() > i::kMaxInt) { 2167 Throw(isolate, "ArrayBuffer is too big to clone"); 2168 return false; 2169 } 2170 2171 int32_t byte_length = static_cast<int32_t>(contents.ByteLength()); 2172 out_data->WriteTag(kSerializationTagArrayBuffer); 2173 out_data->Write(byte_length); 2174 out_data->WriteMemory(contents.Data(), byte_length); 2175 } 2176 } else if (value->IsSharedArrayBuffer()) { 2177 Local<SharedArrayBuffer> sab = Local<SharedArrayBuffer>::Cast(value); 2178 if (FindInObjectList(sab, *seen_objects)) { 2179 Throw(isolate, "Duplicated shared array buffers not supported"); 2180 return false; 2181 } 2182 seen_objects->Add(sab); 2183 if (!FindInObjectList(sab, to_transfer)) { 2184 Throw(isolate, "SharedArrayBuffer must be transferred"); 2185 return false; 2186 } 2187 2188 SharedArrayBuffer::Contents contents; 2189 if (sab->IsExternal()) { 2190 contents = sab->GetContents(); 2191 } else { 2192 contents = sab->Externalize(); 2193 base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer()); 2194 externalized_shared_contents_.Add(contents); 2195 } 2196 out_data->WriteSharedArrayBufferContents(contents); 2197 } else if (value->IsObject()) { 2198 Local<Object> object = Local<Object>::Cast(value); 2199 if (FindInObjectList(object, *seen_objects)) { 2200 Throw(isolate, "Duplicated objects not supported"); 2201 return false; 2202 } 2203 seen_objects->Add(object); 2204 Local<Array> property_names; 2205 if (!object->GetOwnPropertyNames(context).ToLocal(&property_names)) { 2206 Throw(isolate, "Unable to get property names"); 2207 return false; 2208 } 2209 2210 uint32_t length = property_names->Length(); 2211 out_data->WriteTag(kSerializationTagObject); 2212 out_data->Write(length); 2213 for (uint32_t i = 0; i < length; ++i) { 2214 Local<Value> name; 2215 Local<Value> property_value; 2216 if (property_names->Get(context, i).ToLocal(&name) && 2217 object->Get(context, name).ToLocal(&property_value)) { 2218 if (!SerializeValue(isolate, name, to_transfer, seen_objects, out_data)) 2219 return false; 2220 if (!SerializeValue(isolate, property_value, to_transfer, seen_objects, 2221 out_data)) 2222 return false; 2223 } else { 2224 Throw(isolate, "Failed to serialize property."); 2225 return false; 2226 } 2227 } 2228 } else { 2229 Throw(isolate, "Don't know how to serialize object"); 2230 return false; 2231 } 2232 2233 return true; 2234 } 2235 2236 2237 MaybeLocal<Value> Shell::DeserializeValue(Isolate* isolate, 2238 const SerializationData& data, 2239 int* offset) { 2240 DCHECK(offset); 2241 EscapableHandleScope scope(isolate); 2242 Local<Value> result; 2243 SerializationTag tag = data.ReadTag(offset); 2244 2245 switch (tag) { 2246 case kSerializationTagUndefined: 2247 result = Undefined(isolate); 2248 break; 2249 case kSerializationTagNull: 2250 result = Null(isolate); 2251 break; 2252 case kSerializationTagTrue: 2253 result = True(isolate); 2254 break; 2255 case kSerializationTagFalse: 2256 result = False(isolate); 2257 break; 2258 case kSerializationTagNumber: 2259 result = Number::New(isolate, data.Read<double>(offset)); 2260 break; 2261 case kSerializationTagString: { 2262 int length = data.Read<int>(offset); 2263 CHECK(length >= 0); 2264 std::vector<char> buffer(length + 1); // + 1 so it is never empty. 2265 data.ReadMemory(&buffer[0], length, offset); 2266 MaybeLocal<String> str = 2267 String::NewFromUtf8(isolate, &buffer[0], NewStringType::kNormal, 2268 length).ToLocalChecked(); 2269 if (!str.IsEmpty()) result = str.ToLocalChecked(); 2270 break; 2271 } 2272 case kSerializationTagArray: { 2273 uint32_t length = data.Read<uint32_t>(offset); 2274 Local<Array> array = Array::New(isolate, length); 2275 for (uint32_t i = 0; i < length; ++i) { 2276 Local<Value> element_value; 2277 CHECK(DeserializeValue(isolate, data, offset).ToLocal(&element_value)); 2278 array->Set(isolate->GetCurrentContext(), i, element_value).FromJust(); 2279 } 2280 result = array; 2281 break; 2282 } 2283 case kSerializationTagObject: { 2284 int length = data.Read<int>(offset); 2285 Local<Object> object = Object::New(isolate); 2286 for (int i = 0; i < length; ++i) { 2287 Local<Value> property_name; 2288 CHECK(DeserializeValue(isolate, data, offset).ToLocal(&property_name)); 2289 Local<Value> property_value; 2290 CHECK(DeserializeValue(isolate, data, offset).ToLocal(&property_value)); 2291 object->Set(isolate->GetCurrentContext(), property_name, property_value) 2292 .FromJust(); 2293 } 2294 result = object; 2295 break; 2296 } 2297 case kSerializationTagArrayBuffer: { 2298 int32_t byte_length = data.Read<int32_t>(offset); 2299 Local<ArrayBuffer> array_buffer = ArrayBuffer::New(isolate, byte_length); 2300 ArrayBuffer::Contents contents = array_buffer->GetContents(); 2301 DCHECK(static_cast<size_t>(byte_length) == contents.ByteLength()); 2302 data.ReadMemory(contents.Data(), byte_length, offset); 2303 result = array_buffer; 2304 break; 2305 } 2306 case kSerializationTagTransferredArrayBuffer: { 2307 ArrayBuffer::Contents contents; 2308 data.ReadArrayBufferContents(&contents, offset); 2309 result = ArrayBuffer::New(isolate, contents.Data(), contents.ByteLength(), 2310 ArrayBufferCreationMode::kInternalized); 2311 break; 2312 } 2313 case kSerializationTagTransferredSharedArrayBuffer: { 2314 SharedArrayBuffer::Contents contents; 2315 data.ReadSharedArrayBufferContents(&contents, offset); 2316 result = SharedArrayBuffer::New(isolate, contents.Data(), 2317 contents.ByteLength()); 2318 break; 2319 } 2320 default: 2321 UNREACHABLE(); 2322 } 2323 2324 return scope.Escape(result); 2325 } 2326 2327 2328 void Shell::CleanupWorkers() { 2329 // Make a copy of workers_, because we don't want to call Worker::Terminate 2330 // while holding the workers_mutex_ lock. Otherwise, if a worker is about to 2331 // create a new Worker, it would deadlock. 2332 i::List<Worker*> workers_copy; 2333 { 2334 base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer()); 2335 allow_new_workers_ = false; 2336 workers_copy.AddAll(workers_); 2337 workers_.Clear(); 2338 } 2339 2340 for (int i = 0; i < workers_copy.length(); ++i) { 2341 Worker* worker = workers_copy[i]; 2342 worker->WaitForThread(); 2343 delete worker; 2344 } 2345 2346 // Now that all workers are terminated, we can re-enable Worker creation. 2347 base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer()); 2348 allow_new_workers_ = true; 2349 2350 for (int i = 0; i < externalized_shared_contents_.length(); ++i) { 2351 const SharedArrayBuffer::Contents& contents = 2352 externalized_shared_contents_[i]; 2353 Shell::array_buffer_allocator->Free(contents.Data(), contents.ByteLength()); 2354 } 2355 externalized_shared_contents_.Clear(); 2356 } 2357 2358 2359 static void DumpHeapConstants(i::Isolate* isolate) { 2360 i::Heap* heap = isolate->heap(); 2361 2362 // Dump the INSTANCE_TYPES table to the console. 2363 printf("# List of known V8 instance types.\n"); 2364 #define DUMP_TYPE(T) printf(" %d: \"%s\",\n", i::T, #T); 2365 printf("INSTANCE_TYPES = {\n"); 2366 INSTANCE_TYPE_LIST(DUMP_TYPE) 2367 printf("}\n"); 2368 #undef DUMP_TYPE 2369 2370 // Dump the KNOWN_MAP table to the console. 2371 printf("\n# List of known V8 maps.\n"); 2372 #define ROOT_LIST_CASE(type, name, camel_name) \ 2373 if (n == NULL && o == heap->name()) n = #camel_name; 2374 #define STRUCT_LIST_CASE(upper_name, camel_name, name) \ 2375 if (n == NULL && o == heap->name##_map()) n = #camel_name "Map"; 2376 i::HeapObjectIterator it(heap->map_space()); 2377 printf("KNOWN_MAPS = {\n"); 2378 for (i::Object* o = it.Next(); o != NULL; o = it.Next()) { 2379 i::Map* m = i::Map::cast(o); 2380 const char* n = NULL; 2381 intptr_t p = reinterpret_cast<intptr_t>(m) & 0xfffff; 2382 int t = m->instance_type(); 2383 ROOT_LIST(ROOT_LIST_CASE) 2384 STRUCT_LIST(STRUCT_LIST_CASE) 2385 if (n == NULL) continue; 2386 printf(" 0x%05" V8PRIxPTR ": (%d, \"%s\"),\n", p, t, n); 2387 } 2388 printf("}\n"); 2389 #undef STRUCT_LIST_CASE 2390 #undef ROOT_LIST_CASE 2391 2392 // Dump the KNOWN_OBJECTS table to the console. 2393 printf("\n# List of known V8 objects.\n"); 2394 #define ROOT_LIST_CASE(type, name, camel_name) \ 2395 if (n == NULL && o == heap->name()) n = #camel_name; 2396 i::OldSpaces spit(heap); 2397 printf("KNOWN_OBJECTS = {\n"); 2398 for (i::PagedSpace* s = spit.next(); s != NULL; s = spit.next()) { 2399 i::HeapObjectIterator it(s); 2400 const char* sname = AllocationSpaceName(s->identity()); 2401 for (i::Object* o = it.Next(); o != NULL; o = it.Next()) { 2402 const char* n = NULL; 2403 intptr_t p = reinterpret_cast<intptr_t>(o) & 0xfffff; 2404 ROOT_LIST(ROOT_LIST_CASE) 2405 if (n == NULL) continue; 2406 printf(" (\"%s\", 0x%05" V8PRIxPTR "): \"%s\",\n", sname, p, n); 2407 } 2408 } 2409 printf("}\n"); 2410 #undef ROOT_LIST_CASE 2411 } 2412 #endif // !V8_SHARED 2413 2414 2415 int Shell::Main(int argc, char* argv[]) { 2416 #if (defined(_WIN32) || defined(_WIN64)) 2417 UINT new_flags = 2418 SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX; 2419 UINT existing_flags = SetErrorMode(new_flags); 2420 SetErrorMode(existing_flags | new_flags); 2421 #if defined(_MSC_VER) 2422 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); 2423 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); 2424 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); 2425 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); 2426 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); 2427 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); 2428 _set_error_mode(_OUT_TO_STDERR); 2429 #endif // defined(_MSC_VER) 2430 #endif // defined(_WIN32) || defined(_WIN64) 2431 if (!SetOptions(argc, argv)) return 1; 2432 v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file); 2433 #ifndef V8_SHARED 2434 g_platform = i::FLAG_verify_predictable 2435 ? new PredictablePlatform() 2436 : v8::platform::CreateDefaultPlatform(); 2437 #else 2438 g_platform = v8::platform::CreateDefaultPlatform(); 2439 #endif // !V8_SHARED 2440 2441 v8::V8::InitializePlatform(g_platform); 2442 v8::V8::Initialize(); 2443 if (options.natives_blob || options.snapshot_blob) { 2444 v8::V8::InitializeExternalStartupData(options.natives_blob, 2445 options.snapshot_blob); 2446 } else { 2447 v8::V8::InitializeExternalStartupData(argv[0]); 2448 } 2449 SetFlagsFromString("--trace-hydrogen-file=hydrogen.cfg"); 2450 SetFlagsFromString("--trace-turbo-cfg-file=turbo.cfg"); 2451 SetFlagsFromString("--redirect-code-traces-to=code.asm"); 2452 int result = 0; 2453 Isolate::CreateParams create_params; 2454 ShellArrayBufferAllocator shell_array_buffer_allocator; 2455 MockArrayBufferAllocator mock_arraybuffer_allocator; 2456 if (options.mock_arraybuffer_allocator) { 2457 Shell::array_buffer_allocator = &mock_arraybuffer_allocator; 2458 } else { 2459 Shell::array_buffer_allocator = &shell_array_buffer_allocator; 2460 } 2461 create_params.array_buffer_allocator = Shell::array_buffer_allocator; 2462 #ifdef ENABLE_VTUNE_JIT_INTERFACE 2463 create_params.code_event_handler = vTune::GetVtuneCodeEventHandler(); 2464 #endif 2465 #ifndef V8_SHARED 2466 create_params.constraints.ConfigureDefaults( 2467 base::SysInfo::AmountOfPhysicalMemory(), 2468 base::SysInfo::AmountOfVirtualMemory()); 2469 2470 Shell::counter_map_ = new CounterMap(); 2471 if (i::FLAG_dump_counters || i::FLAG_track_gc_object_stats) { 2472 create_params.counter_lookup_callback = LookupCounter; 2473 create_params.create_histogram_callback = CreateHistogram; 2474 create_params.add_histogram_sample_callback = AddHistogramSample; 2475 } 2476 #endif 2477 Isolate* isolate = Isolate::New(create_params); 2478 { 2479 Isolate::Scope scope(isolate); 2480 Initialize(isolate); 2481 PerIsolateData data(isolate); 2482 2483 #ifndef V8_SHARED 2484 if (options.dump_heap_constants) { 2485 DumpHeapConstants(reinterpret_cast<i::Isolate*>(isolate)); 2486 return 0; 2487 } 2488 #endif 2489 2490 if (options.stress_opt || options.stress_deopt) { 2491 Testing::SetStressRunType(options.stress_opt 2492 ? Testing::kStressTypeOpt 2493 : Testing::kStressTypeDeopt); 2494 options.stress_runs = Testing::GetStressRuns(); 2495 for (int i = 0; i < options.stress_runs && result == 0; i++) { 2496 printf("============ Stress %d/%d ============\n", i + 1, 2497 options.stress_runs); 2498 Testing::PrepareStressRun(i); 2499 bool last_run = i == options.stress_runs - 1; 2500 result = RunMain(isolate, argc, argv, last_run); 2501 } 2502 printf("======== Full Deoptimization =======\n"); 2503 Testing::DeoptimizeAll(isolate); 2504 #if !defined(V8_SHARED) 2505 } else if (i::FLAG_stress_runs > 0) { 2506 options.stress_runs = i::FLAG_stress_runs; 2507 for (int i = 0; i < options.stress_runs && result == 0; i++) { 2508 printf("============ Run %d/%d ============\n", i + 1, 2509 options.stress_runs); 2510 bool last_run = i == options.stress_runs - 1; 2511 result = RunMain(isolate, argc, argv, last_run); 2512 } 2513 #endif 2514 } else { 2515 bool last_run = true; 2516 result = RunMain(isolate, argc, argv, last_run); 2517 } 2518 2519 // Run interactive shell if explicitly requested or if no script has been 2520 // executed, but never on --test 2521 if (options.use_interactive_shell()) { 2522 RunShell(isolate); 2523 } 2524 2525 #ifndef V8_SHARED 2526 if (i::FLAG_ignition && i::FLAG_trace_ignition_dispatches && 2527 i::FLAG_trace_ignition_dispatches_output_file != nullptr) { 2528 WriteIgnitionDispatchCountersFile(isolate); 2529 } 2530 #endif 2531 2532 // Shut down contexts and collect garbage. 2533 evaluation_context_.Reset(); 2534 #ifndef V8_SHARED 2535 stringify_function_.Reset(); 2536 #endif // !V8_SHARED 2537 CollectGarbage(isolate); 2538 } 2539 OnExit(isolate); 2540 #ifndef V8_SHARED 2541 // Dump basic block profiling data. 2542 if (i::BasicBlockProfiler* profiler = 2543 reinterpret_cast<i::Isolate*>(isolate)->basic_block_profiler()) { 2544 i::OFStream os(stdout); 2545 os << *profiler; 2546 } 2547 #endif // !V8_SHARED 2548 isolate->Dispose(); 2549 V8::Dispose(); 2550 V8::ShutdownPlatform(); 2551 delete g_platform; 2552 2553 return result; 2554 } 2555 2556 } // namespace v8 2557 2558 2559 #ifndef GOOGLE3 2560 int main(int argc, char* argv[]) { 2561 return v8::Shell::Main(argc, argv); 2562 } 2563 #endif 2564