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