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 #ifdef COMPRESS_STARTUP_DATA_BZ2 12 #include <bzlib.h> 13 #endif 14 15 #include <errno.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <sys/stat.h> 19 20 #ifdef V8_SHARED 21 #include <assert.h> 22 #endif // V8_SHARED 23 24 #ifndef V8_SHARED 25 #include <algorithm> 26 #endif // !V8_SHARED 27 28 #ifdef V8_SHARED 29 #include "include/v8-testing.h" 30 #endif // V8_SHARED 31 32 #if !defined(V8_SHARED) && defined(ENABLE_GDB_JIT_INTERFACE) 33 #include "src/gdb-jit.h" 34 #endif 35 36 #ifdef ENABLE_VTUNE_JIT_INTERFACE 37 #include "src/third_party/vtune/v8-vtune.h" 38 #endif 39 40 #include "src/d8.h" 41 42 #include "include/libplatform/libplatform.h" 43 #ifndef V8_SHARED 44 #include "src/api.h" 45 #include "src/base/cpu.h" 46 #include "src/base/logging.h" 47 #include "src/base/platform/platform.h" 48 #include "src/base/sys-info.h" 49 #include "src/d8-debug.h" 50 #include "src/debug.h" 51 #include "src/natives.h" 52 #include "src/v8.h" 53 #endif // !V8_SHARED 54 55 #if !defined(_WIN32) && !defined(_WIN64) 56 #include <unistd.h> // NOLINT 57 #else 58 #include <windows.h> // NOLINT 59 #if defined(_MSC_VER) 60 #include <crtdbg.h> // NOLINT 61 #endif // defined(_MSC_VER) 62 #endif // !defined(_WIN32) && !defined(_WIN64) 63 64 #ifndef DCHECK 65 #define DCHECK(condition) assert(condition) 66 #endif 67 68 namespace v8 { 69 70 71 static Handle<Value> Throw(Isolate* isolate, const char* message) { 72 return isolate->ThrowException(String::NewFromUtf8(isolate, message)); 73 } 74 75 76 77 class PerIsolateData { 78 public: 79 explicit PerIsolateData(Isolate* isolate) : isolate_(isolate), realms_(NULL) { 80 HandleScope scope(isolate); 81 isolate->SetData(0, this); 82 } 83 84 ~PerIsolateData() { 85 isolate_->SetData(0, NULL); // Not really needed, just to be sure... 86 } 87 88 inline static PerIsolateData* Get(Isolate* isolate) { 89 return reinterpret_cast<PerIsolateData*>(isolate->GetData(0)); 90 } 91 92 class RealmScope { 93 public: 94 explicit RealmScope(PerIsolateData* data); 95 ~RealmScope(); 96 private: 97 PerIsolateData* data_; 98 }; 99 100 private: 101 friend class Shell; 102 friend class RealmScope; 103 Isolate* isolate_; 104 int realm_count_; 105 int realm_current_; 106 int realm_switch_; 107 Persistent<Context>* realms_; 108 Persistent<Value> realm_shared_; 109 110 int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args, 111 int arg_offset); 112 int RealmFind(Handle<Context> context); 113 }; 114 115 116 LineEditor *LineEditor::current_ = NULL; 117 118 119 LineEditor::LineEditor(Type type, const char* name) 120 : type_(type), name_(name) { 121 if (current_ == NULL || current_->type_ < type) current_ = this; 122 } 123 124 125 class DumbLineEditor: public LineEditor { 126 public: 127 explicit DumbLineEditor(Isolate* isolate) 128 : LineEditor(LineEditor::DUMB, "dumb"), isolate_(isolate) { } 129 virtual Handle<String> Prompt(const char* prompt); 130 private: 131 Isolate* isolate_; 132 }; 133 134 135 Handle<String> DumbLineEditor::Prompt(const char* prompt) { 136 printf("%s", prompt); 137 #if defined(__native_client__) 138 // Native Client libc is used to being embedded in Chrome and 139 // has trouble recognizing when to flush. 140 fflush(stdout); 141 #endif 142 return Shell::ReadFromStdin(isolate_); 143 } 144 145 146 #ifndef V8_SHARED 147 CounterMap* Shell::counter_map_; 148 base::OS::MemoryMappedFile* Shell::counters_file_ = NULL; 149 CounterCollection Shell::local_counters_; 150 CounterCollection* Shell::counters_ = &local_counters_; 151 base::Mutex Shell::context_mutex_; 152 const base::TimeTicks Shell::kInitialTicks = 153 base::TimeTicks::HighResolutionNow(); 154 Persistent<Context> Shell::utility_context_; 155 #endif // !V8_SHARED 156 157 Persistent<Context> Shell::evaluation_context_; 158 ShellOptions Shell::options; 159 const char* Shell::kPrompt = "d8> "; 160 161 162 #ifndef V8_SHARED 163 const int MB = 1024 * 1024; 164 165 bool CounterMap::Match(void* key1, void* key2) { 166 const char* name1 = reinterpret_cast<const char*>(key1); 167 const char* name2 = reinterpret_cast<const char*>(key2); 168 return strcmp(name1, name2) == 0; 169 } 170 #endif // !V8_SHARED 171 172 173 // Converts a V8 value to a C string. 174 const char* Shell::ToCString(const v8::String::Utf8Value& value) { 175 return *value ? *value : "<string conversion failed>"; 176 } 177 178 179 // Compile a string within the current v8 context. 180 Local<UnboundScript> Shell::CompileString( 181 Isolate* isolate, Local<String> source, Local<Value> name, 182 v8::ScriptCompiler::CompileOptions compile_options) { 183 ScriptOrigin origin(name); 184 ScriptCompiler::Source script_source(source, origin); 185 Local<UnboundScript> script = 186 ScriptCompiler::CompileUnbound(isolate, &script_source, compile_options); 187 188 // Was caching requested & successful? Then compile again, now with cache. 189 if (script_source.GetCachedData()) { 190 if (compile_options == ScriptCompiler::kProduceCodeCache) { 191 compile_options = ScriptCompiler::kConsumeCodeCache; 192 } else if (compile_options == ScriptCompiler::kProduceParserCache) { 193 compile_options = ScriptCompiler::kConsumeParserCache; 194 } else { 195 DCHECK(false); // A new compile option? 196 } 197 ScriptCompiler::Source cached_source( 198 source, origin, new v8::ScriptCompiler::CachedData( 199 script_source.GetCachedData()->data, 200 script_source.GetCachedData()->length, 201 v8::ScriptCompiler::CachedData::BufferNotOwned)); 202 script = ScriptCompiler::CompileUnbound(isolate, &cached_source, 203 compile_options); 204 } 205 return script; 206 } 207 208 209 // Executes a string within the current v8 context. 210 bool Shell::ExecuteString(Isolate* isolate, 211 Handle<String> source, 212 Handle<Value> name, 213 bool print_result, 214 bool report_exceptions) { 215 #ifndef V8_SHARED 216 bool FLAG_debugger = i::FLAG_debugger; 217 #else 218 bool FLAG_debugger = false; 219 #endif // !V8_SHARED 220 HandleScope handle_scope(isolate); 221 TryCatch try_catch; 222 options.script_executed = true; 223 if (FLAG_debugger) { 224 // When debugging make exceptions appear to be uncaught. 225 try_catch.SetVerbose(true); 226 } 227 228 Handle<UnboundScript> script = 229 Shell::CompileString(isolate, source, name, options.compile_options); 230 if (script.IsEmpty()) { 231 // Print errors that happened during compilation. 232 if (report_exceptions && !FLAG_debugger) 233 ReportException(isolate, &try_catch); 234 return false; 235 } else { 236 PerIsolateData* data = PerIsolateData::Get(isolate); 237 Local<Context> realm = 238 Local<Context>::New(isolate, data->realms_[data->realm_current_]); 239 realm->Enter(); 240 Handle<Value> result = script->BindToCurrentContext()->Run(); 241 realm->Exit(); 242 data->realm_current_ = data->realm_switch_; 243 if (result.IsEmpty()) { 244 DCHECK(try_catch.HasCaught()); 245 // Print errors that happened during execution. 246 if (report_exceptions && !FLAG_debugger) 247 ReportException(isolate, &try_catch); 248 return false; 249 } else { 250 DCHECK(!try_catch.HasCaught()); 251 if (print_result) { 252 #if !defined(V8_SHARED) 253 if (options.test_shell) { 254 #endif 255 if (!result->IsUndefined()) { 256 // If all went well and the result wasn't undefined then print 257 // the returned value. 258 v8::String::Utf8Value str(result); 259 fwrite(*str, sizeof(**str), str.length(), stdout); 260 printf("\n"); 261 } 262 #if !defined(V8_SHARED) 263 } else { 264 v8::TryCatch try_catch; 265 v8::Local<v8::Context> context = 266 v8::Local<v8::Context>::New(isolate, utility_context_); 267 v8::Context::Scope context_scope(context); 268 Handle<Object> global = context->Global(); 269 Handle<Value> fun = 270 global->Get(String::NewFromUtf8(isolate, "Stringify")); 271 Handle<Value> argv[1] = { result }; 272 Handle<Value> s = Handle<Function>::Cast(fun)->Call(global, 1, argv); 273 if (try_catch.HasCaught()) return true; 274 v8::String::Utf8Value str(s); 275 fwrite(*str, sizeof(**str), str.length(), stdout); 276 printf("\n"); 277 } 278 #endif 279 } 280 return true; 281 } 282 } 283 } 284 285 286 PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) { 287 data_->realm_count_ = 1; 288 data_->realm_current_ = 0; 289 data_->realm_switch_ = 0; 290 data_->realms_ = new Persistent<Context>[1]; 291 data_->realms_[0].Reset(data_->isolate_, 292 data_->isolate_->GetEnteredContext()); 293 } 294 295 296 PerIsolateData::RealmScope::~RealmScope() { 297 // Drop realms to avoid keeping them alive. 298 for (int i = 0; i < data_->realm_count_; ++i) 299 data_->realms_[i].Reset(); 300 delete[] data_->realms_; 301 if (!data_->realm_shared_.IsEmpty()) 302 data_->realm_shared_.Reset(); 303 } 304 305 306 int PerIsolateData::RealmFind(Handle<Context> context) { 307 for (int i = 0; i < realm_count_; ++i) { 308 if (realms_[i] == context) return i; 309 } 310 return -1; 311 } 312 313 314 int PerIsolateData::RealmIndexOrThrow( 315 const v8::FunctionCallbackInfo<v8::Value>& args, 316 int arg_offset) { 317 if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) { 318 Throw(args.GetIsolate(), "Invalid argument"); 319 return -1; 320 } 321 int index = args[arg_offset]->Int32Value(); 322 if (index < 0 || 323 index >= realm_count_ || 324 realms_[index].IsEmpty()) { 325 Throw(args.GetIsolate(), "Invalid realm index"); 326 return -1; 327 } 328 return index; 329 } 330 331 332 #ifndef V8_SHARED 333 // performance.now() returns a time stamp as double, measured in milliseconds. 334 // When FLAG_verify_predictable mode is enabled it returns current value 335 // of Heap::allocations_count(). 336 void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) { 337 if (i::FLAG_verify_predictable) { 338 Isolate* v8_isolate = args.GetIsolate(); 339 i::Heap* heap = reinterpret_cast<i::Isolate*>(v8_isolate)->heap(); 340 args.GetReturnValue().Set(heap->synthetic_time()); 341 } else { 342 base::TimeDelta delta = 343 base::TimeTicks::HighResolutionNow() - kInitialTicks; 344 args.GetReturnValue().Set(delta.InMillisecondsF()); 345 } 346 } 347 #endif // !V8_SHARED 348 349 350 // Realm.current() returns the index of the currently active realm. 351 void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) { 352 Isolate* isolate = args.GetIsolate(); 353 PerIsolateData* data = PerIsolateData::Get(isolate); 354 int index = data->RealmFind(isolate->GetEnteredContext()); 355 if (index == -1) return; 356 args.GetReturnValue().Set(index); 357 } 358 359 360 // Realm.owner(o) returns the index of the realm that created o. 361 void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) { 362 Isolate* isolate = args.GetIsolate(); 363 PerIsolateData* data = PerIsolateData::Get(isolate); 364 if (args.Length() < 1 || !args[0]->IsObject()) { 365 Throw(args.GetIsolate(), "Invalid argument"); 366 return; 367 } 368 int index = data->RealmFind(args[0]->ToObject()->CreationContext()); 369 if (index == -1) return; 370 args.GetReturnValue().Set(index); 371 } 372 373 374 // Realm.global(i) returns the global object of realm i. 375 // (Note that properties of global objects cannot be read/written cross-realm.) 376 void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { 377 PerIsolateData* data = PerIsolateData::Get(args.GetIsolate()); 378 int index = data->RealmIndexOrThrow(args, 0); 379 if (index == -1) return; 380 args.GetReturnValue().Set( 381 Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global()); 382 } 383 384 385 // Realm.create() creates a new realm and returns its index. 386 void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) { 387 Isolate* isolate = args.GetIsolate(); 388 PerIsolateData* data = PerIsolateData::Get(isolate); 389 Persistent<Context>* old_realms = data->realms_; 390 int index = data->realm_count_; 391 data->realms_ = new Persistent<Context>[++data->realm_count_]; 392 for (int i = 0; i < index; ++i) { 393 data->realms_[i].Reset(isolate, old_realms[i]); 394 } 395 delete[] old_realms; 396 Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate); 397 data->realms_[index].Reset( 398 isolate, Context::New(isolate, NULL, global_template)); 399 args.GetReturnValue().Set(index); 400 } 401 402 403 // Realm.dispose(i) disposes the reference to the realm i. 404 void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) { 405 Isolate* isolate = args.GetIsolate(); 406 PerIsolateData* data = PerIsolateData::Get(isolate); 407 int index = data->RealmIndexOrThrow(args, 0); 408 if (index == -1) return; 409 if (index == 0 || 410 index == data->realm_current_ || index == data->realm_switch_) { 411 Throw(args.GetIsolate(), "Invalid realm index"); 412 return; 413 } 414 data->realms_[index].Reset(); 415 } 416 417 418 // Realm.switch(i) switches to the realm i for consecutive interactive inputs. 419 void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) { 420 Isolate* isolate = args.GetIsolate(); 421 PerIsolateData* data = PerIsolateData::Get(isolate); 422 int index = data->RealmIndexOrThrow(args, 0); 423 if (index == -1) return; 424 data->realm_switch_ = index; 425 } 426 427 428 // Realm.eval(i, s) evaluates s in realm i and returns the result. 429 void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) { 430 Isolate* isolate = args.GetIsolate(); 431 PerIsolateData* data = PerIsolateData::Get(isolate); 432 int index = data->RealmIndexOrThrow(args, 0); 433 if (index == -1) return; 434 if (args.Length() < 2 || !args[1]->IsString()) { 435 Throw(args.GetIsolate(), "Invalid argument"); 436 return; 437 } 438 ScriptCompiler::Source script_source(args[1]->ToString()); 439 Handle<UnboundScript> script = ScriptCompiler::CompileUnbound( 440 isolate, &script_source); 441 if (script.IsEmpty()) return; 442 Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]); 443 realm->Enter(); 444 Handle<Value> result = script->BindToCurrentContext()->Run(); 445 realm->Exit(); 446 args.GetReturnValue().Set(result); 447 } 448 449 450 // Realm.shared is an accessor for a single shared value across realms. 451 void Shell::RealmSharedGet(Local<String> property, 452 const PropertyCallbackInfo<Value>& info) { 453 Isolate* isolate = info.GetIsolate(); 454 PerIsolateData* data = PerIsolateData::Get(isolate); 455 if (data->realm_shared_.IsEmpty()) return; 456 info.GetReturnValue().Set(data->realm_shared_); 457 } 458 459 void Shell::RealmSharedSet(Local<String> property, 460 Local<Value> value, 461 const PropertyCallbackInfo<void>& info) { 462 Isolate* isolate = info.GetIsolate(); 463 PerIsolateData* data = PerIsolateData::Get(isolate); 464 data->realm_shared_.Reset(isolate, value); 465 } 466 467 468 void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) { 469 Write(args); 470 printf("\n"); 471 fflush(stdout); 472 } 473 474 475 void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) { 476 for (int i = 0; i < args.Length(); i++) { 477 HandleScope handle_scope(args.GetIsolate()); 478 if (i != 0) { 479 printf(" "); 480 } 481 482 // Explicitly catch potential exceptions in toString(). 483 v8::TryCatch try_catch; 484 Handle<String> str_obj = args[i]->ToString(); 485 if (try_catch.HasCaught()) { 486 try_catch.ReThrow(); 487 return; 488 } 489 490 v8::String::Utf8Value str(str_obj); 491 int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), stdout)); 492 if (n != str.length()) { 493 printf("Error in fwrite\n"); 494 Exit(1); 495 } 496 } 497 } 498 499 500 void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) { 501 String::Utf8Value file(args[0]); 502 if (*file == NULL) { 503 Throw(args.GetIsolate(), "Error loading file"); 504 return; 505 } 506 Handle<String> source = ReadFile(args.GetIsolate(), *file); 507 if (source.IsEmpty()) { 508 Throw(args.GetIsolate(), "Error loading file"); 509 return; 510 } 511 args.GetReturnValue().Set(source); 512 } 513 514 515 Handle<String> Shell::ReadFromStdin(Isolate* isolate) { 516 static const int kBufferSize = 256; 517 char buffer[kBufferSize]; 518 Handle<String> accumulator = String::NewFromUtf8(isolate, ""); 519 int length; 520 while (true) { 521 // Continue reading if the line ends with an escape '\\' or the line has 522 // not been fully read into the buffer yet (does not end with '\n'). 523 // If fgets gets an error, just give up. 524 char* input = NULL; 525 input = fgets(buffer, kBufferSize, stdin); 526 if (input == NULL) return Handle<String>(); 527 length = static_cast<int>(strlen(buffer)); 528 if (length == 0) { 529 return accumulator; 530 } else if (buffer[length-1] != '\n') { 531 accumulator = String::Concat( 532 accumulator, 533 String::NewFromUtf8(isolate, buffer, String::kNormalString, length)); 534 } else if (length > 1 && buffer[length-2] == '\\') { 535 buffer[length-2] = '\n'; 536 accumulator = String::Concat( 537 accumulator, String::NewFromUtf8(isolate, buffer, 538 String::kNormalString, length - 1)); 539 } else { 540 return String::Concat( 541 accumulator, String::NewFromUtf8(isolate, buffer, 542 String::kNormalString, length - 1)); 543 } 544 } 545 } 546 547 548 void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) { 549 for (int i = 0; i < args.Length(); i++) { 550 HandleScope handle_scope(args.GetIsolate()); 551 String::Utf8Value file(args[i]); 552 if (*file == NULL) { 553 Throw(args.GetIsolate(), "Error loading file"); 554 return; 555 } 556 Handle<String> source = ReadFile(args.GetIsolate(), *file); 557 if (source.IsEmpty()) { 558 Throw(args.GetIsolate(), "Error loading file"); 559 return; 560 } 561 if (!ExecuteString(args.GetIsolate(), 562 source, 563 String::NewFromUtf8(args.GetIsolate(), *file), 564 false, 565 true)) { 566 Throw(args.GetIsolate(), "Error executing file"); 567 return; 568 } 569 } 570 } 571 572 573 void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) { 574 int exit_code = args[0]->Int32Value(); 575 OnExit(); 576 exit(exit_code); 577 } 578 579 580 void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) { 581 args.GetReturnValue().Set( 582 String::NewFromUtf8(args.GetIsolate(), V8::GetVersion())); 583 } 584 585 586 void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) { 587 HandleScope handle_scope(isolate); 588 #ifndef V8_SHARED 589 Handle<Context> utility_context; 590 bool enter_context = !isolate->InContext(); 591 if (enter_context) { 592 utility_context = Local<Context>::New(isolate, utility_context_); 593 utility_context->Enter(); 594 } 595 #endif // !V8_SHARED 596 v8::String::Utf8Value exception(try_catch->Exception()); 597 const char* exception_string = ToCString(exception); 598 Handle<Message> message = try_catch->Message(); 599 if (message.IsEmpty()) { 600 // V8 didn't provide any extra information about this error; just 601 // print the exception. 602 printf("%s\n", exception_string); 603 } else { 604 // Print (filename):(line number): (message). 605 v8::String::Utf8Value filename(message->GetScriptOrigin().ResourceName()); 606 const char* filename_string = ToCString(filename); 607 int linenum = message->GetLineNumber(); 608 printf("%s:%i: %s\n", filename_string, linenum, exception_string); 609 // Print line of source code. 610 v8::String::Utf8Value sourceline(message->GetSourceLine()); 611 const char* sourceline_string = ToCString(sourceline); 612 printf("%s\n", sourceline_string); 613 // Print wavy underline (GetUnderline is deprecated). 614 int start = message->GetStartColumn(); 615 for (int i = 0; i < start; i++) { 616 printf(" "); 617 } 618 int end = message->GetEndColumn(); 619 for (int i = start; i < end; i++) { 620 printf("^"); 621 } 622 printf("\n"); 623 v8::String::Utf8Value stack_trace(try_catch->StackTrace()); 624 if (stack_trace.length() > 0) { 625 const char* stack_trace_string = ToCString(stack_trace); 626 printf("%s\n", stack_trace_string); 627 } 628 } 629 printf("\n"); 630 #ifndef V8_SHARED 631 if (enter_context) utility_context->Exit(); 632 #endif // !V8_SHARED 633 } 634 635 636 #ifndef V8_SHARED 637 Handle<Array> Shell::GetCompletions(Isolate* isolate, 638 Handle<String> text, 639 Handle<String> full) { 640 EscapableHandleScope handle_scope(isolate); 641 v8::Local<v8::Context> utility_context = 642 v8::Local<v8::Context>::New(isolate, utility_context_); 643 v8::Context::Scope context_scope(utility_context); 644 Handle<Object> global = utility_context->Global(); 645 Local<Value> fun = 646 global->Get(String::NewFromUtf8(isolate, "GetCompletions")); 647 static const int kArgc = 3; 648 v8::Local<v8::Context> evaluation_context = 649 v8::Local<v8::Context>::New(isolate, evaluation_context_); 650 Handle<Value> argv[kArgc] = { evaluation_context->Global(), text, full }; 651 Local<Value> val = Local<Function>::Cast(fun)->Call(global, kArgc, argv); 652 return handle_scope.Escape(Local<Array>::Cast(val)); 653 } 654 655 656 Local<Object> Shell::DebugMessageDetails(Isolate* isolate, 657 Handle<String> message) { 658 EscapableHandleScope handle_scope(isolate); 659 v8::Local<v8::Context> context = 660 v8::Local<v8::Context>::New(isolate, utility_context_); 661 v8::Context::Scope context_scope(context); 662 Handle<Object> global = context->Global(); 663 Handle<Value> fun = 664 global->Get(String::NewFromUtf8(isolate, "DebugMessageDetails")); 665 static const int kArgc = 1; 666 Handle<Value> argv[kArgc] = { message }; 667 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv); 668 return handle_scope.Escape(Local<Object>(Handle<Object>::Cast(val))); 669 } 670 671 672 Local<Value> Shell::DebugCommandToJSONRequest(Isolate* isolate, 673 Handle<String> command) { 674 EscapableHandleScope handle_scope(isolate); 675 v8::Local<v8::Context> context = 676 v8::Local<v8::Context>::New(isolate, utility_context_); 677 v8::Context::Scope context_scope(context); 678 Handle<Object> global = context->Global(); 679 Handle<Value> fun = 680 global->Get(String::NewFromUtf8(isolate, "DebugCommandToJSONRequest")); 681 static const int kArgc = 1; 682 Handle<Value> argv[kArgc] = { command }; 683 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv); 684 return handle_scope.Escape(Local<Value>(val)); 685 } 686 687 688 int32_t* Counter::Bind(const char* name, bool is_histogram) { 689 int i; 690 for (i = 0; i < kMaxNameSize - 1 && name[i]; i++) 691 name_[i] = static_cast<char>(name[i]); 692 name_[i] = '\0'; 693 is_histogram_ = is_histogram; 694 return ptr(); 695 } 696 697 698 void Counter::AddSample(int32_t sample) { 699 count_++; 700 sample_total_ += sample; 701 } 702 703 704 CounterCollection::CounterCollection() { 705 magic_number_ = 0xDEADFACE; 706 max_counters_ = kMaxCounters; 707 max_name_size_ = Counter::kMaxNameSize; 708 counters_in_use_ = 0; 709 } 710 711 712 Counter* CounterCollection::GetNextCounter() { 713 if (counters_in_use_ == kMaxCounters) return NULL; 714 return &counters_[counters_in_use_++]; 715 } 716 717 718 void Shell::MapCounters(v8::Isolate* isolate, const char* name) { 719 counters_file_ = base::OS::MemoryMappedFile::create( 720 name, sizeof(CounterCollection), &local_counters_); 721 void* memory = (counters_file_ == NULL) ? 722 NULL : counters_file_->memory(); 723 if (memory == NULL) { 724 printf("Could not map counters file %s\n", name); 725 Exit(1); 726 } 727 counters_ = static_cast<CounterCollection*>(memory); 728 isolate->SetCounterFunction(LookupCounter); 729 isolate->SetCreateHistogramFunction(CreateHistogram); 730 isolate->SetAddHistogramSampleFunction(AddHistogramSample); 731 } 732 733 734 int CounterMap::Hash(const char* name) { 735 int h = 0; 736 int c; 737 while ((c = *name++) != 0) { 738 h += h << 5; 739 h += c; 740 } 741 return h; 742 } 743 744 745 Counter* Shell::GetCounter(const char* name, bool is_histogram) { 746 Counter* counter = counter_map_->Lookup(name); 747 748 if (counter == NULL) { 749 counter = counters_->GetNextCounter(); 750 if (counter != NULL) { 751 counter_map_->Set(name, counter); 752 counter->Bind(name, is_histogram); 753 } 754 } else { 755 DCHECK(counter->is_histogram() == is_histogram); 756 } 757 return counter; 758 } 759 760 761 int* Shell::LookupCounter(const char* name) { 762 Counter* counter = GetCounter(name, false); 763 764 if (counter != NULL) { 765 return counter->ptr(); 766 } else { 767 return NULL; 768 } 769 } 770 771 772 void* Shell::CreateHistogram(const char* name, 773 int min, 774 int max, 775 size_t buckets) { 776 return GetCounter(name, true); 777 } 778 779 780 void Shell::AddHistogramSample(void* histogram, int sample) { 781 Counter* counter = reinterpret_cast<Counter*>(histogram); 782 counter->AddSample(sample); 783 } 784 785 786 void Shell::InstallUtilityScript(Isolate* isolate) { 787 HandleScope scope(isolate); 788 // If we use the utility context, we have to set the security tokens so that 789 // utility, evaluation and debug context can all access each other. 790 v8::Local<v8::Context> utility_context = 791 v8::Local<v8::Context>::New(isolate, utility_context_); 792 v8::Local<v8::Context> evaluation_context = 793 v8::Local<v8::Context>::New(isolate, evaluation_context_); 794 utility_context->SetSecurityToken(Undefined(isolate)); 795 evaluation_context->SetSecurityToken(Undefined(isolate)); 796 v8::Context::Scope context_scope(utility_context); 797 798 if (i::FLAG_debugger) printf("JavaScript debugger enabled\n"); 799 // Install the debugger object in the utility scope 800 i::Debug* debug = reinterpret_cast<i::Isolate*>(isolate)->debug(); 801 debug->Load(); 802 i::Handle<i::Context> debug_context = debug->debug_context(); 803 i::Handle<i::JSObject> js_debug 804 = i::Handle<i::JSObject>(debug_context->global_object()); 805 utility_context->Global()->Set(String::NewFromUtf8(isolate, "$debug"), 806 Utils::ToLocal(js_debug)); 807 debug_context->set_security_token( 808 reinterpret_cast<i::Isolate*>(isolate)->heap()->undefined_value()); 809 810 // Run the d8 shell utility script in the utility context 811 int source_index = i::NativesCollection<i::D8>::GetIndex("d8"); 812 i::Vector<const char> shell_source = 813 i::NativesCollection<i::D8>::GetRawScriptSource(source_index); 814 i::Vector<const char> shell_source_name = 815 i::NativesCollection<i::D8>::GetScriptName(source_index); 816 Handle<String> source = 817 String::NewFromUtf8(isolate, shell_source.start(), String::kNormalString, 818 shell_source.length()); 819 Handle<String> name = 820 String::NewFromUtf8(isolate, shell_source_name.start(), 821 String::kNormalString, shell_source_name.length()); 822 ScriptOrigin origin(name); 823 Handle<Script> script = Script::Compile(source, &origin); 824 script->Run(); 825 // Mark the d8 shell script as native to avoid it showing up as normal source 826 // in the debugger. 827 i::Handle<i::Object> compiled_script = Utils::OpenHandle(*script); 828 i::Handle<i::Script> script_object = compiled_script->IsJSFunction() 829 ? i::Handle<i::Script>(i::Script::cast( 830 i::JSFunction::cast(*compiled_script)->shared()->script())) 831 : i::Handle<i::Script>(i::Script::cast( 832 i::SharedFunctionInfo::cast(*compiled_script)->script())); 833 script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE)); 834 835 // Start the in-process debugger if requested. 836 if (i::FLAG_debugger) v8::Debug::SetDebugEventListener(HandleDebugEvent); 837 } 838 #endif // !V8_SHARED 839 840 841 #ifdef COMPRESS_STARTUP_DATA_BZ2 842 class BZip2Decompressor : public v8::StartupDataDecompressor { 843 public: 844 virtual ~BZip2Decompressor() { } 845 846 protected: 847 virtual int DecompressData(char* raw_data, 848 int* raw_data_size, 849 const char* compressed_data, 850 int compressed_data_size) { 851 DCHECK_EQ(v8::StartupData::kBZip2, 852 v8::V8::GetCompressedStartupDataAlgorithm()); 853 unsigned int decompressed_size = *raw_data_size; 854 int result = 855 BZ2_bzBuffToBuffDecompress(raw_data, 856 &decompressed_size, 857 const_cast<char*>(compressed_data), 858 compressed_data_size, 859 0, 1); 860 if (result == BZ_OK) { 861 *raw_data_size = decompressed_size; 862 } 863 return result; 864 } 865 }; 866 #endif 867 868 869 Handle<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) { 870 Handle<ObjectTemplate> global_template = ObjectTemplate::New(isolate); 871 global_template->Set(String::NewFromUtf8(isolate, "print"), 872 FunctionTemplate::New(isolate, Print)); 873 global_template->Set(String::NewFromUtf8(isolate, "write"), 874 FunctionTemplate::New(isolate, Write)); 875 global_template->Set(String::NewFromUtf8(isolate, "read"), 876 FunctionTemplate::New(isolate, Read)); 877 global_template->Set(String::NewFromUtf8(isolate, "readbuffer"), 878 FunctionTemplate::New(isolate, ReadBuffer)); 879 global_template->Set(String::NewFromUtf8(isolate, "readline"), 880 FunctionTemplate::New(isolate, ReadLine)); 881 global_template->Set(String::NewFromUtf8(isolate, "load"), 882 FunctionTemplate::New(isolate, Load)); 883 global_template->Set(String::NewFromUtf8(isolate, "quit"), 884 FunctionTemplate::New(isolate, Quit)); 885 global_template->Set(String::NewFromUtf8(isolate, "version"), 886 FunctionTemplate::New(isolate, Version)); 887 888 // Bind the Realm object. 889 Handle<ObjectTemplate> realm_template = ObjectTemplate::New(isolate); 890 realm_template->Set(String::NewFromUtf8(isolate, "current"), 891 FunctionTemplate::New(isolate, RealmCurrent)); 892 realm_template->Set(String::NewFromUtf8(isolate, "owner"), 893 FunctionTemplate::New(isolate, RealmOwner)); 894 realm_template->Set(String::NewFromUtf8(isolate, "global"), 895 FunctionTemplate::New(isolate, RealmGlobal)); 896 realm_template->Set(String::NewFromUtf8(isolate, "create"), 897 FunctionTemplate::New(isolate, RealmCreate)); 898 realm_template->Set(String::NewFromUtf8(isolate, "dispose"), 899 FunctionTemplate::New(isolate, RealmDispose)); 900 realm_template->Set(String::NewFromUtf8(isolate, "switch"), 901 FunctionTemplate::New(isolate, RealmSwitch)); 902 realm_template->Set(String::NewFromUtf8(isolate, "eval"), 903 FunctionTemplate::New(isolate, RealmEval)); 904 realm_template->SetAccessor(String::NewFromUtf8(isolate, "shared"), 905 RealmSharedGet, RealmSharedSet); 906 global_template->Set(String::NewFromUtf8(isolate, "Realm"), realm_template); 907 908 #ifndef V8_SHARED 909 Handle<ObjectTemplate> performance_template = ObjectTemplate::New(isolate); 910 performance_template->Set(String::NewFromUtf8(isolate, "now"), 911 FunctionTemplate::New(isolate, PerformanceNow)); 912 global_template->Set(String::NewFromUtf8(isolate, "performance"), 913 performance_template); 914 #endif // !V8_SHARED 915 916 Handle<ObjectTemplate> os_templ = ObjectTemplate::New(isolate); 917 AddOSMethods(isolate, os_templ); 918 global_template->Set(String::NewFromUtf8(isolate, "os"), os_templ); 919 920 return global_template; 921 } 922 923 924 void Shell::Initialize(Isolate* isolate) { 925 #ifdef COMPRESS_STARTUP_DATA_BZ2 926 BZip2Decompressor startup_data_decompressor; 927 int bz2_result = startup_data_decompressor.Decompress(); 928 if (bz2_result != BZ_OK) { 929 fprintf(stderr, "bzip error code: %d\n", bz2_result); 930 Exit(1); 931 } 932 #endif 933 934 #ifndef V8_SHARED 935 Shell::counter_map_ = new CounterMap(); 936 // Set up counters 937 if (i::StrLength(i::FLAG_map_counters) != 0) 938 MapCounters(isolate, i::FLAG_map_counters); 939 if (i::FLAG_dump_counters || i::FLAG_track_gc_object_stats) { 940 isolate->SetCounterFunction(LookupCounter); 941 isolate->SetCreateHistogramFunction(CreateHistogram); 942 isolate->SetAddHistogramSampleFunction(AddHistogramSample); 943 } 944 #endif // !V8_SHARED 945 } 946 947 948 void Shell::InitializeDebugger(Isolate* isolate) { 949 if (options.test_shell) return; 950 #ifndef V8_SHARED 951 HandleScope scope(isolate); 952 Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate); 953 utility_context_.Reset(isolate, 954 Context::New(isolate, NULL, global_template)); 955 #endif // !V8_SHARED 956 } 957 958 959 Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) { 960 #ifndef V8_SHARED 961 // This needs to be a critical section since this is not thread-safe 962 base::LockGuard<base::Mutex> lock_guard(&context_mutex_); 963 #endif // !V8_SHARED 964 // Initialize the global objects 965 Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate); 966 EscapableHandleScope handle_scope(isolate); 967 Local<Context> context = Context::New(isolate, NULL, global_template); 968 DCHECK(!context.IsEmpty()); 969 Context::Scope scope(context); 970 971 #ifndef V8_SHARED 972 i::Factory* factory = reinterpret_cast<i::Isolate*>(isolate)->factory(); 973 i::JSArguments js_args = i::FLAG_js_arguments; 974 i::Handle<i::FixedArray> arguments_array = 975 factory->NewFixedArray(js_args.argc); 976 for (int j = 0; j < js_args.argc; j++) { 977 i::Handle<i::String> arg = 978 factory->NewStringFromUtf8(i::CStrVector(js_args[j])).ToHandleChecked(); 979 arguments_array->set(j, *arg); 980 } 981 i::Handle<i::JSArray> arguments_jsarray = 982 factory->NewJSArrayWithElements(arguments_array); 983 context->Global()->Set(String::NewFromUtf8(isolate, "arguments"), 984 Utils::ToLocal(arguments_jsarray)); 985 #endif // !V8_SHARED 986 return handle_scope.Escape(context); 987 } 988 989 990 void Shell::Exit(int exit_code) { 991 // Use _exit instead of exit to avoid races between isolate 992 // threads and static destructors. 993 fflush(stdout); 994 fflush(stderr); 995 _exit(exit_code); 996 } 997 998 999 #ifndef V8_SHARED 1000 struct CounterAndKey { 1001 Counter* counter; 1002 const char* key; 1003 }; 1004 1005 1006 inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) { 1007 return strcmp(lhs.key, rhs.key) < 0; 1008 } 1009 #endif // !V8_SHARED 1010 1011 1012 void Shell::OnExit() { 1013 LineEditor* line_editor = LineEditor::Get(); 1014 if (line_editor) line_editor->Close(); 1015 #ifndef V8_SHARED 1016 if (i::FLAG_dump_counters) { 1017 int number_of_counters = 0; 1018 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) { 1019 number_of_counters++; 1020 } 1021 CounterAndKey* counters = new CounterAndKey[number_of_counters]; 1022 int j = 0; 1023 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next(), j++) { 1024 counters[j].counter = i.CurrentValue(); 1025 counters[j].key = i.CurrentKey(); 1026 } 1027 std::sort(counters, counters + number_of_counters); 1028 printf("+----------------------------------------------------------------+" 1029 "-------------+\n"); 1030 printf("| Name |" 1031 " Value |\n"); 1032 printf("+----------------------------------------------------------------+" 1033 "-------------+\n"); 1034 for (j = 0; j < number_of_counters; j++) { 1035 Counter* counter = counters[j].counter; 1036 const char* key = counters[j].key; 1037 if (counter->is_histogram()) { 1038 printf("| c:%-60s | %11i |\n", key, counter->count()); 1039 printf("| t:%-60s | %11i |\n", key, counter->sample_total()); 1040 } else { 1041 printf("| %-62s | %11i |\n", key, counter->count()); 1042 } 1043 } 1044 printf("+----------------------------------------------------------------+" 1045 "-------------+\n"); 1046 delete [] counters; 1047 } 1048 delete counters_file_; 1049 delete counter_map_; 1050 #endif // !V8_SHARED 1051 } 1052 1053 1054 1055 static FILE* FOpen(const char* path, const char* mode) { 1056 #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64)) 1057 FILE* result; 1058 if (fopen_s(&result, path, mode) == 0) { 1059 return result; 1060 } else { 1061 return NULL; 1062 } 1063 #else 1064 FILE* file = fopen(path, mode); 1065 if (file == NULL) return NULL; 1066 struct stat file_stat; 1067 if (fstat(fileno(file), &file_stat) != 0) return NULL; 1068 bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0); 1069 if (is_regular_file) return file; 1070 fclose(file); 1071 return NULL; 1072 #endif 1073 } 1074 1075 1076 static char* ReadChars(Isolate* isolate, const char* name, int* size_out) { 1077 FILE* file = FOpen(name, "rb"); 1078 if (file == NULL) return NULL; 1079 1080 fseek(file, 0, SEEK_END); 1081 int size = ftell(file); 1082 rewind(file); 1083 1084 char* chars = new char[size + 1]; 1085 chars[size] = '\0'; 1086 for (int i = 0; i < size;) { 1087 int read = static_cast<int>(fread(&chars[i], 1, size - i, file)); 1088 i += read; 1089 } 1090 fclose(file); 1091 *size_out = size; 1092 return chars; 1093 } 1094 1095 1096 struct DataAndPersistent { 1097 uint8_t* data; 1098 Persistent<ArrayBuffer> handle; 1099 }; 1100 1101 1102 static void ReadBufferWeakCallback( 1103 const v8::WeakCallbackData<ArrayBuffer, DataAndPersistent>& data) { 1104 size_t byte_length = data.GetValue()->ByteLength(); 1105 data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory( 1106 -static_cast<intptr_t>(byte_length)); 1107 1108 delete[] data.GetParameter()->data; 1109 data.GetParameter()->handle.Reset(); 1110 delete data.GetParameter(); 1111 } 1112 1113 1114 void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) { 1115 DCHECK(sizeof(char) == sizeof(uint8_t)); // NOLINT 1116 String::Utf8Value filename(args[0]); 1117 int length; 1118 if (*filename == NULL) { 1119 Throw(args.GetIsolate(), "Error loading file"); 1120 return; 1121 } 1122 1123 Isolate* isolate = args.GetIsolate(); 1124 DataAndPersistent* data = new DataAndPersistent; 1125 data->data = reinterpret_cast<uint8_t*>( 1126 ReadChars(args.GetIsolate(), *filename, &length)); 1127 if (data->data == NULL) { 1128 delete data; 1129 Throw(args.GetIsolate(), "Error reading file"); 1130 return; 1131 } 1132 Handle<v8::ArrayBuffer> buffer = 1133 ArrayBuffer::New(isolate, data->data, length); 1134 data->handle.Reset(isolate, buffer); 1135 data->handle.SetWeak(data, ReadBufferWeakCallback); 1136 data->handle.MarkIndependent(); 1137 isolate->AdjustAmountOfExternalAllocatedMemory(length); 1138 1139 args.GetReturnValue().Set(buffer); 1140 } 1141 1142 1143 // Reads a file into a v8 string. 1144 Handle<String> Shell::ReadFile(Isolate* isolate, const char* name) { 1145 int size = 0; 1146 char* chars = ReadChars(isolate, name, &size); 1147 if (chars == NULL) return Handle<String>(); 1148 Handle<String> result = 1149 String::NewFromUtf8(isolate, chars, String::kNormalString, size); 1150 delete[] chars; 1151 return result; 1152 } 1153 1154 1155 void Shell::RunShell(Isolate* isolate) { 1156 HandleScope outer_scope(isolate); 1157 v8::Local<v8::Context> context = 1158 v8::Local<v8::Context>::New(isolate, evaluation_context_); 1159 v8::Context::Scope context_scope(context); 1160 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); 1161 Handle<String> name = String::NewFromUtf8(isolate, "(d8)"); 1162 LineEditor* console = LineEditor::Get(); 1163 printf("V8 version %s [console: %s]\n", V8::GetVersion(), console->name()); 1164 console->Open(isolate); 1165 while (true) { 1166 HandleScope inner_scope(isolate); 1167 Handle<String> input = console->Prompt(Shell::kPrompt); 1168 if (input.IsEmpty()) break; 1169 ExecuteString(isolate, input, name, true, true); 1170 } 1171 printf("\n"); 1172 } 1173 1174 1175 SourceGroup::~SourceGroup() { 1176 #ifndef V8_SHARED 1177 delete thread_; 1178 thread_ = NULL; 1179 #endif // !V8_SHARED 1180 } 1181 1182 1183 void SourceGroup::Execute(Isolate* isolate) { 1184 bool exception_was_thrown = false; 1185 for (int i = begin_offset_; i < end_offset_; ++i) { 1186 const char* arg = argv_[i]; 1187 if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) { 1188 // Execute argument given to -e option directly. 1189 HandleScope handle_scope(isolate); 1190 Handle<String> file_name = String::NewFromUtf8(isolate, "unnamed"); 1191 Handle<String> source = String::NewFromUtf8(isolate, argv_[i + 1]); 1192 if (!Shell::ExecuteString(isolate, source, file_name, false, true)) { 1193 exception_was_thrown = true; 1194 break; 1195 } 1196 ++i; 1197 } else if (arg[0] == '-') { 1198 // Ignore other options. They have been parsed already. 1199 } else { 1200 // Use all other arguments as names of files to load and run. 1201 HandleScope handle_scope(isolate); 1202 Handle<String> file_name = String::NewFromUtf8(isolate, arg); 1203 Handle<String> source = ReadFile(isolate, arg); 1204 if (source.IsEmpty()) { 1205 printf("Error reading '%s'\n", arg); 1206 Shell::Exit(1); 1207 } 1208 if (!Shell::ExecuteString(isolate, source, file_name, false, true)) { 1209 exception_was_thrown = true; 1210 break; 1211 } 1212 } 1213 } 1214 if (exception_was_thrown != Shell::options.expected_to_throw) { 1215 Shell::Exit(1); 1216 } 1217 } 1218 1219 1220 Handle<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) { 1221 int size; 1222 char* chars = ReadChars(isolate, name, &size); 1223 if (chars == NULL) return Handle<String>(); 1224 Handle<String> result = 1225 String::NewFromUtf8(isolate, chars, String::kNormalString, size); 1226 delete[] chars; 1227 return result; 1228 } 1229 1230 1231 #ifndef V8_SHARED 1232 base::Thread::Options SourceGroup::GetThreadOptions() { 1233 // On some systems (OSX 10.6) the stack size default is 0.5Mb or less 1234 // which is not enough to parse the big literal expressions used in tests. 1235 // The stack size should be at least StackGuard::kLimitSize + some 1236 // OS-specific padding for thread startup code. 2Mbytes seems to be enough. 1237 return base::Thread::Options("IsolateThread", 2 * MB); 1238 } 1239 1240 1241 void SourceGroup::ExecuteInThread() { 1242 Isolate* isolate = Isolate::New(); 1243 do { 1244 next_semaphore_.Wait(); 1245 { 1246 Isolate::Scope iscope(isolate); 1247 { 1248 HandleScope scope(isolate); 1249 PerIsolateData data(isolate); 1250 Local<Context> context = Shell::CreateEvaluationContext(isolate); 1251 { 1252 Context::Scope cscope(context); 1253 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); 1254 Execute(isolate); 1255 } 1256 } 1257 if (Shell::options.send_idle_notification) { 1258 const int kLongIdlePauseInMs = 1000; 1259 isolate->ContextDisposedNotification(); 1260 isolate->IdleNotification(kLongIdlePauseInMs); 1261 } 1262 if (Shell::options.invoke_weak_callbacks) { 1263 // By sending a low memory notifications, we will try hard to collect 1264 // all garbage and will therefore also invoke all weak callbacks of 1265 // actually unreachable persistent handles. 1266 isolate->LowMemoryNotification(); 1267 } 1268 } 1269 done_semaphore_.Signal(); 1270 } while (!Shell::options.last_run); 1271 1272 isolate->Dispose(); 1273 } 1274 1275 1276 void SourceGroup::StartExecuteInThread() { 1277 if (thread_ == NULL) { 1278 thread_ = new IsolateThread(this); 1279 thread_->Start(); 1280 } 1281 next_semaphore_.Signal(); 1282 } 1283 1284 1285 void SourceGroup::WaitForThread() { 1286 if (thread_ == NULL) return; 1287 if (Shell::options.last_run) { 1288 thread_->Join(); 1289 } else { 1290 done_semaphore_.Wait(); 1291 } 1292 } 1293 #endif // !V8_SHARED 1294 1295 1296 void SetFlagsFromString(const char* flags) { 1297 v8::V8::SetFlagsFromString(flags, static_cast<int>(strlen(flags))); 1298 } 1299 1300 1301 bool Shell::SetOptions(int argc, char* argv[]) { 1302 bool logfile_per_isolate = false; 1303 for (int i = 0; i < argc; i++) { 1304 if (strcmp(argv[i], "--stress-opt") == 0) { 1305 options.stress_opt = true; 1306 argv[i] = NULL; 1307 } else if (strcmp(argv[i], "--nostress-opt") == 0) { 1308 options.stress_opt = false; 1309 argv[i] = NULL; 1310 } else if (strcmp(argv[i], "--stress-deopt") == 0) { 1311 options.stress_deopt = true; 1312 argv[i] = NULL; 1313 } else if (strcmp(argv[i], "--mock-arraybuffer-allocator") == 0) { 1314 options.mock_arraybuffer_allocator = true; 1315 argv[i] = NULL; 1316 } else if (strcmp(argv[i], "--noalways-opt") == 0) { 1317 // No support for stressing if we can't use --always-opt. 1318 options.stress_opt = false; 1319 options.stress_deopt = false; 1320 } else if (strcmp(argv[i], "--logfile-per-isolate") == 0) { 1321 logfile_per_isolate = true; 1322 argv[i] = NULL; 1323 } else if (strcmp(argv[i], "--shell") == 0) { 1324 options.interactive_shell = true; 1325 argv[i] = NULL; 1326 } else if (strcmp(argv[i], "--test") == 0) { 1327 options.test_shell = true; 1328 argv[i] = NULL; 1329 } else if (strcmp(argv[i], "--send-idle-notification") == 0) { 1330 options.send_idle_notification = true; 1331 argv[i] = NULL; 1332 } else if (strcmp(argv[i], "--invoke-weak-callbacks") == 0) { 1333 options.invoke_weak_callbacks = true; 1334 // TODO(jochen) See issue 3351 1335 options.send_idle_notification = true; 1336 argv[i] = NULL; 1337 } else if (strcmp(argv[i], "-f") == 0) { 1338 // Ignore any -f flags for compatibility with other stand-alone 1339 // JavaScript engines. 1340 continue; 1341 } else if (strcmp(argv[i], "--isolate") == 0) { 1342 #ifdef V8_SHARED 1343 printf("D8 with shared library does not support multi-threading\n"); 1344 return false; 1345 #endif // V8_SHARED 1346 options.num_isolates++; 1347 } else if (strcmp(argv[i], "--dump-heap-constants") == 0) { 1348 #ifdef V8_SHARED 1349 printf("D8 with shared library does not support constant dumping\n"); 1350 return false; 1351 #else 1352 options.dump_heap_constants = true; 1353 argv[i] = NULL; 1354 #endif // V8_SHARED 1355 } else if (strcmp(argv[i], "--throws") == 0) { 1356 options.expected_to_throw = true; 1357 argv[i] = NULL; 1358 } else if (strncmp(argv[i], "--icu-data-file=", 16) == 0) { 1359 options.icu_data_file = argv[i] + 16; 1360 argv[i] = NULL; 1361 #ifdef V8_SHARED 1362 } else if (strcmp(argv[i], "--dump-counters") == 0) { 1363 printf("D8 with shared library does not include counters\n"); 1364 return false; 1365 } else if (strcmp(argv[i], "--debugger") == 0) { 1366 printf("Javascript debugger not included\n"); 1367 return false; 1368 #endif // V8_SHARED 1369 #ifdef V8_USE_EXTERNAL_STARTUP_DATA 1370 } else if (strncmp(argv[i], "--natives_blob=", 15) == 0) { 1371 options.natives_blob = argv[i] + 15; 1372 argv[i] = NULL; 1373 } else if (strncmp(argv[i], "--snapshot_blob=", 16) == 0) { 1374 options.snapshot_blob = argv[i] + 16; 1375 argv[i] = NULL; 1376 #endif // V8_USE_EXTERNAL_STARTUP_DATA 1377 } else if (strcmp(argv[i], "--cache") == 0 || 1378 strncmp(argv[i], "--cache=", 8) == 0) { 1379 const char* value = argv[i] + 7; 1380 if (!*value || strncmp(value, "=code", 6) == 0) { 1381 options.compile_options = v8::ScriptCompiler::kProduceCodeCache; 1382 } else if (strncmp(value, "=parse", 7) == 0) { 1383 options.compile_options = v8::ScriptCompiler::kProduceParserCache; 1384 } else if (strncmp(value, "=none", 6) == 0) { 1385 options.compile_options = v8::ScriptCompiler::kNoCompileOptions; 1386 } else { 1387 printf("Unknown option to --cache.\n"); 1388 return false; 1389 } 1390 argv[i] = NULL; 1391 } 1392 } 1393 1394 v8::V8::SetFlagsFromCommandLine(&argc, argv, true); 1395 1396 // Set up isolated source groups. 1397 options.isolate_sources = new SourceGroup[options.num_isolates]; 1398 SourceGroup* current = options.isolate_sources; 1399 current->Begin(argv, 1); 1400 for (int i = 1; i < argc; i++) { 1401 const char* str = argv[i]; 1402 if (strcmp(str, "--isolate") == 0) { 1403 current->End(i); 1404 current++; 1405 current->Begin(argv, i + 1); 1406 } else if (strncmp(argv[i], "--", 2) == 0) { 1407 printf("Warning: unknown flag %s.\nTry --help for options\n", argv[i]); 1408 } 1409 } 1410 current->End(argc); 1411 1412 if (!logfile_per_isolate && options.num_isolates) { 1413 SetFlagsFromString("--nologfile_per_isolate"); 1414 } 1415 1416 return true; 1417 } 1418 1419 1420 int Shell::RunMain(Isolate* isolate, int argc, char* argv[]) { 1421 #ifndef V8_SHARED 1422 for (int i = 1; i < options.num_isolates; ++i) { 1423 options.isolate_sources[i].StartExecuteInThread(); 1424 } 1425 #endif // !V8_SHARED 1426 { 1427 HandleScope scope(isolate); 1428 Local<Context> context = CreateEvaluationContext(isolate); 1429 if (options.last_run && options.use_interactive_shell()) { 1430 // Keep using the same context in the interactive shell. 1431 evaluation_context_.Reset(isolate, context); 1432 #ifndef V8_SHARED 1433 // If the interactive debugger is enabled make sure to activate 1434 // it before running the files passed on the command line. 1435 if (i::FLAG_debugger) { 1436 InstallUtilityScript(isolate); 1437 } 1438 #endif // !V8_SHARED 1439 } 1440 { 1441 Context::Scope cscope(context); 1442 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); 1443 options.isolate_sources[0].Execute(isolate); 1444 } 1445 } 1446 if (options.send_idle_notification) { 1447 const int kLongIdlePauseInMs = 1000; 1448 isolate->ContextDisposedNotification(); 1449 isolate->IdleNotification(kLongIdlePauseInMs); 1450 } 1451 if (options.invoke_weak_callbacks) { 1452 // By sending a low memory notifications, we will try hard to collect all 1453 // garbage and will therefore also invoke all weak callbacks of actually 1454 // unreachable persistent handles. 1455 isolate->LowMemoryNotification(); 1456 } 1457 1458 #ifndef V8_SHARED 1459 for (int i = 1; i < options.num_isolates; ++i) { 1460 options.isolate_sources[i].WaitForThread(); 1461 } 1462 #endif // !V8_SHARED 1463 return 0; 1464 } 1465 1466 1467 #ifndef V8_SHARED 1468 static void DumpHeapConstants(i::Isolate* isolate) { 1469 i::Heap* heap = isolate->heap(); 1470 1471 // Dump the INSTANCE_TYPES table to the console. 1472 printf("# List of known V8 instance types.\n"); 1473 #define DUMP_TYPE(T) printf(" %d: \"%s\",\n", i::T, #T); 1474 printf("INSTANCE_TYPES = {\n"); 1475 INSTANCE_TYPE_LIST(DUMP_TYPE) 1476 printf("}\n"); 1477 #undef DUMP_TYPE 1478 1479 // Dump the KNOWN_MAP table to the console. 1480 printf("\n# List of known V8 maps.\n"); 1481 #define ROOT_LIST_CASE(type, name, camel_name) \ 1482 if (n == NULL && o == heap->name()) n = #camel_name; 1483 #define STRUCT_LIST_CASE(upper_name, camel_name, name) \ 1484 if (n == NULL && o == heap->name##_map()) n = #camel_name "Map"; 1485 i::HeapObjectIterator it(heap->map_space()); 1486 printf("KNOWN_MAPS = {\n"); 1487 for (i::Object* o = it.Next(); o != NULL; o = it.Next()) { 1488 i::Map* m = i::Map::cast(o); 1489 const char* n = NULL; 1490 intptr_t p = reinterpret_cast<intptr_t>(m) & 0xfffff; 1491 int t = m->instance_type(); 1492 ROOT_LIST(ROOT_LIST_CASE) 1493 STRUCT_LIST(STRUCT_LIST_CASE) 1494 if (n == NULL) continue; 1495 printf(" 0x%05" V8PRIxPTR ": (%d, \"%s\"),\n", p, t, n); 1496 } 1497 printf("}\n"); 1498 #undef STRUCT_LIST_CASE 1499 #undef ROOT_LIST_CASE 1500 1501 // Dump the KNOWN_OBJECTS table to the console. 1502 printf("\n# List of known V8 objects.\n"); 1503 #define ROOT_LIST_CASE(type, name, camel_name) \ 1504 if (n == NULL && o == heap->name()) n = #camel_name; 1505 i::OldSpaces spit(heap); 1506 printf("KNOWN_OBJECTS = {\n"); 1507 for (i::PagedSpace* s = spit.next(); s != NULL; s = spit.next()) { 1508 i::HeapObjectIterator it(s); 1509 const char* sname = AllocationSpaceName(s->identity()); 1510 for (i::Object* o = it.Next(); o != NULL; o = it.Next()) { 1511 const char* n = NULL; 1512 intptr_t p = reinterpret_cast<intptr_t>(o) & 0xfffff; 1513 ROOT_LIST(ROOT_LIST_CASE) 1514 if (n == NULL) continue; 1515 printf(" (\"%s\", 0x%05" V8PRIxPTR "): \"%s\",\n", sname, p, n); 1516 } 1517 } 1518 printf("}\n"); 1519 #undef ROOT_LIST_CASE 1520 } 1521 #endif // !V8_SHARED 1522 1523 1524 class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator { 1525 public: 1526 virtual void* Allocate(size_t length) { 1527 void* data = AllocateUninitialized(length); 1528 return data == NULL ? data : memset(data, 0, length); 1529 } 1530 virtual void* AllocateUninitialized(size_t length) { return malloc(length); } 1531 virtual void Free(void* data, size_t) { free(data); } 1532 }; 1533 1534 1535 class MockArrayBufferAllocator : public v8::ArrayBuffer::Allocator { 1536 public: 1537 virtual void* Allocate(size_t) OVERRIDE { 1538 return malloc(0); 1539 } 1540 virtual void* AllocateUninitialized(size_t length) OVERRIDE { 1541 return malloc(0); 1542 } 1543 virtual void Free(void* p, size_t) OVERRIDE { 1544 free(p); 1545 } 1546 }; 1547 1548 1549 #ifdef V8_USE_EXTERNAL_STARTUP_DATA 1550 class StartupDataHandler { 1551 public: 1552 StartupDataHandler(const char* natives_blob, 1553 const char* snapshot_blob) { 1554 Load(natives_blob, &natives_, v8::V8::SetNativesDataBlob); 1555 Load(snapshot_blob, &snapshot_, v8::V8::SetSnapshotDataBlob); 1556 } 1557 1558 ~StartupDataHandler() { 1559 delete[] natives_.data; 1560 delete[] snapshot_.data; 1561 } 1562 1563 private: 1564 void Load(const char* blob_file, 1565 v8::StartupData* startup_data, 1566 void (*setter_fn)(v8::StartupData*)) { 1567 startup_data->data = NULL; 1568 startup_data->compressed_size = 0; 1569 startup_data->raw_size = 0; 1570 1571 if (!blob_file) 1572 return; 1573 1574 FILE* file = fopen(blob_file, "rb"); 1575 if (!file) 1576 return; 1577 1578 fseek(file, 0, SEEK_END); 1579 startup_data->raw_size = ftell(file); 1580 rewind(file); 1581 1582 startup_data->data = new char[startup_data->raw_size]; 1583 startup_data->compressed_size = fread( 1584 const_cast<char*>(startup_data->data), 1, startup_data->raw_size, 1585 file); 1586 fclose(file); 1587 1588 if (startup_data->raw_size == startup_data->compressed_size) 1589 (*setter_fn)(startup_data); 1590 } 1591 1592 v8::StartupData natives_; 1593 v8::StartupData snapshot_; 1594 1595 // Disallow copy & assign. 1596 StartupDataHandler(const StartupDataHandler& other); 1597 void operator=(const StartupDataHandler& other); 1598 }; 1599 #endif // V8_USE_EXTERNAL_STARTUP_DATA 1600 1601 1602 int Shell::Main(int argc, char* argv[]) { 1603 #if (defined(_WIN32) || defined(_WIN64)) 1604 UINT new_flags = 1605 SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX; 1606 UINT existing_flags = SetErrorMode(new_flags); 1607 SetErrorMode(existing_flags | new_flags); 1608 #if defined(_MSC_VER) 1609 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); 1610 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); 1611 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); 1612 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); 1613 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); 1614 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); 1615 _set_error_mode(_OUT_TO_STDERR); 1616 #endif // defined(_MSC_VER) 1617 #endif // defined(_WIN32) || defined(_WIN64) 1618 if (!SetOptions(argc, argv)) return 1; 1619 v8::V8::InitializeICU(options.icu_data_file); 1620 v8::Platform* platform = v8::platform::CreateDefaultPlatform(); 1621 v8::V8::InitializePlatform(platform); 1622 v8::V8::Initialize(); 1623 #ifdef V8_USE_EXTERNAL_STARTUP_DATA 1624 StartupDataHandler startup_data(options.natives_blob, options.snapshot_blob); 1625 #endif 1626 SetFlagsFromString("--trace-hydrogen-file=hydrogen.cfg"); 1627 SetFlagsFromString("--redirect-code-traces-to=code.asm"); 1628 ShellArrayBufferAllocator array_buffer_allocator; 1629 MockArrayBufferAllocator mock_arraybuffer_allocator; 1630 if (options.mock_arraybuffer_allocator) { 1631 v8::V8::SetArrayBufferAllocator(&mock_arraybuffer_allocator); 1632 } else { 1633 v8::V8::SetArrayBufferAllocator(&array_buffer_allocator); 1634 } 1635 int result = 0; 1636 Isolate::CreateParams create_params; 1637 #if !defined(V8_SHARED) && defined(ENABLE_GDB_JIT_INTERFACE) 1638 if (i::FLAG_gdbjit) { 1639 create_params.code_event_handler = i::GDBJITInterface::EventHandler; 1640 } 1641 #endif 1642 #ifdef ENABLE_VTUNE_JIT_INTERFACE 1643 vTune::InitializeVtuneForV8(create_params); 1644 #endif 1645 #ifndef V8_SHARED 1646 create_params.constraints.ConfigureDefaults( 1647 base::SysInfo::AmountOfPhysicalMemory(), 1648 base::SysInfo::AmountOfVirtualMemory(), 1649 base::SysInfo::NumberOfProcessors()); 1650 #endif 1651 Isolate* isolate = Isolate::New(create_params); 1652 DumbLineEditor dumb_line_editor(isolate); 1653 { 1654 Isolate::Scope scope(isolate); 1655 Initialize(isolate); 1656 PerIsolateData data(isolate); 1657 InitializeDebugger(isolate); 1658 1659 #ifndef V8_SHARED 1660 if (options.dump_heap_constants) { 1661 DumpHeapConstants(reinterpret_cast<i::Isolate*>(isolate)); 1662 return 0; 1663 } 1664 #endif 1665 1666 if (options.stress_opt || options.stress_deopt) { 1667 Testing::SetStressRunType(options.stress_opt 1668 ? Testing::kStressTypeOpt 1669 : Testing::kStressTypeDeopt); 1670 int stress_runs = Testing::GetStressRuns(); 1671 for (int i = 0; i < stress_runs && result == 0; i++) { 1672 printf("============ Stress %d/%d ============\n", i + 1, stress_runs); 1673 Testing::PrepareStressRun(i); 1674 options.last_run = (i == stress_runs - 1); 1675 result = RunMain(isolate, argc, argv); 1676 } 1677 printf("======== Full Deoptimization =======\n"); 1678 Testing::DeoptimizeAll(); 1679 #if !defined(V8_SHARED) 1680 } else if (i::FLAG_stress_runs > 0) { 1681 int stress_runs = i::FLAG_stress_runs; 1682 for (int i = 0; i < stress_runs && result == 0; i++) { 1683 printf("============ Run %d/%d ============\n", i + 1, stress_runs); 1684 options.last_run = (i == stress_runs - 1); 1685 result = RunMain(isolate, argc, argv); 1686 } 1687 #endif 1688 } else { 1689 result = RunMain(isolate, argc, argv); 1690 } 1691 1692 // Run interactive shell if explicitly requested or if no script has been 1693 // executed, but never on --test 1694 if (options.use_interactive_shell()) { 1695 #ifndef V8_SHARED 1696 if (!i::FLAG_debugger) { 1697 InstallUtilityScript(isolate); 1698 } 1699 #endif // !V8_SHARED 1700 RunShell(isolate); 1701 } 1702 } 1703 isolate->Dispose(); 1704 V8::Dispose(); 1705 V8::ShutdownPlatform(); 1706 delete platform; 1707 1708 OnExit(); 1709 1710 return result; 1711 } 1712 1713 } // namespace v8 1714 1715 1716 #ifndef GOOGLE3 1717 int main(int argc, char* argv[]) { 1718 return v8::Shell::Main(argc, argv); 1719 } 1720 #endif 1721