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