d8.cc revision 69a99ed0b2b2ef69d393c371b03db3a98aaf880e
1// Copyright 2011 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#ifdef USING_V8_SHARED // Defined when linking against shared lib on Windows. 30#define V8_SHARED 31#endif 32 33#ifdef COMPRESS_STARTUP_DATA_BZ2 34#include <bzlib.h> 35#endif 36 37#include <errno.h> 38#include <stdlib.h> 39#include <string.h> 40#include <sys/stat.h> 41 42#ifdef V8_SHARED 43#include <assert.h> 44#include "../include/v8-testing.h" 45#endif // V8_SHARED 46 47#include "d8.h" 48 49#ifndef V8_SHARED 50#include "api.h" 51#include "checks.h" 52#include "d8-debug.h" 53#include "debug.h" 54#include "natives.h" 55#include "platform.h" 56#include "v8.h" 57#endif // V8_SHARED 58 59#if !defined(_WIN32) && !defined(_WIN64) 60#include <unistd.h> // NOLINT 61#endif 62 63#ifndef ASSERT 64#define ASSERT(condition) assert(condition) 65#endif 66 67namespace v8 { 68 69 70#ifndef V8_SHARED 71LineEditor *LineEditor::first_ = NULL; 72const char* Shell::kHistoryFileName = ".d8_history"; 73 74 75LineEditor::LineEditor(Type type, const char* name) 76 : type_(type), 77 name_(name), 78 next_(first_) { 79 first_ = this; 80} 81 82 83LineEditor* LineEditor::Get() { 84 LineEditor* current = first_; 85 LineEditor* best = current; 86 while (current != NULL) { 87 if (current->type_ > best->type_) 88 best = current; 89 current = current->next_; 90 } 91 return best; 92} 93 94 95class DumbLineEditor: public LineEditor { 96 public: 97 DumbLineEditor() : LineEditor(LineEditor::DUMB, "dumb") { } 98 virtual i::SmartPointer<char> Prompt(const char* prompt); 99}; 100 101 102static DumbLineEditor dumb_line_editor; 103 104 105i::SmartPointer<char> DumbLineEditor::Prompt(const char* prompt) { 106 static const int kBufferSize = 256; 107 char buffer[kBufferSize]; 108 printf("%s", prompt); 109 char* str = fgets(buffer, kBufferSize, stdin); 110 return i::SmartPointer<char>(str ? i::StrDup(str) : str); 111} 112 113 114CounterMap* Shell::counter_map_; 115i::OS::MemoryMappedFile* Shell::counters_file_ = NULL; 116CounterCollection Shell::local_counters_; 117CounterCollection* Shell::counters_ = &local_counters_; 118i::Mutex* Shell::context_mutex_(i::OS::CreateMutex()); 119Persistent<Context> Shell::utility_context_; 120#endif // V8_SHARED 121 122Persistent<Context> Shell::evaluation_context_; 123ShellOptions Shell::options; 124const char* Shell::kPrompt = "d8> "; 125 126 127#ifndef V8_SHARED 128bool CounterMap::Match(void* key1, void* key2) { 129 const char* name1 = reinterpret_cast<const char*>(key1); 130 const char* name2 = reinterpret_cast<const char*>(key2); 131 return strcmp(name1, name2) == 0; 132} 133#endif // V8_SHARED 134 135 136// Converts a V8 value to a C string. 137const char* Shell::ToCString(const v8::String::Utf8Value& value) { 138 return *value ? *value : "<string conversion failed>"; 139} 140 141 142// Executes a string within the current v8 context. 143bool Shell::ExecuteString(Handle<String> source, 144 Handle<Value> name, 145 bool print_result, 146 bool report_exceptions) { 147#ifndef V8_SHARED 148 bool FLAG_debugger = i::FLAG_debugger; 149#else 150 bool FLAG_debugger = false; 151#endif // V8_SHARED 152 HandleScope handle_scope; 153 TryCatch try_catch; 154 options.script_executed = true; 155 if (FLAG_debugger) { 156 // When debugging make exceptions appear to be uncaught. 157 try_catch.SetVerbose(true); 158 } 159 Handle<Script> script = Script::Compile(source, name); 160 if (script.IsEmpty()) { 161 // Print errors that happened during compilation. 162 if (report_exceptions && !FLAG_debugger) 163 ReportException(&try_catch); 164 return false; 165 } else { 166 Handle<Value> result = script->Run(); 167 if (result.IsEmpty()) { 168 ASSERT(try_catch.HasCaught()); 169 // Print errors that happened during execution. 170 if (report_exceptions && !FLAG_debugger) 171 ReportException(&try_catch); 172 return false; 173 } else { 174 ASSERT(!try_catch.HasCaught()); 175 if (print_result && !result->IsUndefined()) { 176 // If all went well and the result wasn't undefined then print 177 // the returned value. 178 v8::String::Utf8Value str(result); 179 const char* cstr = ToCString(str); 180 printf("%s\n", cstr); 181 } 182 return true; 183 } 184 } 185} 186 187 188Handle<Value> Shell::Print(const Arguments& args) { 189 Handle<Value> val = Write(args); 190 printf("\n"); 191 fflush(stdout); 192 return val; 193} 194 195 196Handle<Value> Shell::Write(const Arguments& args) { 197 for (int i = 0; i < args.Length(); i++) { 198 HandleScope handle_scope; 199 if (i != 0) { 200 printf(" "); 201 } 202 v8::String::Utf8Value str(args[i]); 203 int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), stdout)); 204 if (n != str.length()) { 205 printf("Error in fwrite\n"); 206 exit(1); 207 } 208 } 209 return Undefined(); 210} 211 212 213Handle<Value> Shell::Read(const Arguments& args) { 214 String::Utf8Value file(args[0]); 215 if (*file == NULL) { 216 return ThrowException(String::New("Error loading file")); 217 } 218 Handle<String> source = ReadFile(*file); 219 if (source.IsEmpty()) { 220 return ThrowException(String::New("Error loading file")); 221 } 222 return source; 223} 224 225 226Handle<Value> Shell::ReadLine(const Arguments& args) { 227 static const int kBufferSize = 256; 228 char buffer[kBufferSize]; 229 Handle<String> accumulator = String::New(""); 230 int length; 231 while (true) { 232 // Continue reading if the line ends with an escape '\\' or the line has 233 // not been fully read into the buffer yet (does not end with '\n'). 234 // If fgets gets an error, just give up. 235 if (fgets(buffer, kBufferSize, stdin) == NULL) return Null(); 236 length = static_cast<int>(strlen(buffer)); 237 if (length == 0) { 238 return accumulator; 239 } else if (buffer[length-1] != '\n') { 240 accumulator = String::Concat(accumulator, String::New(buffer, length)); 241 } else if (length > 1 && buffer[length-2] == '\\') { 242 buffer[length-2] = '\n'; 243 accumulator = String::Concat(accumulator, String::New(buffer, length-1)); 244 } else { 245 return String::Concat(accumulator, String::New(buffer, length-1)); 246 } 247 } 248} 249 250 251Handle<Value> Shell::Load(const Arguments& args) { 252 for (int i = 0; i < args.Length(); i++) { 253 HandleScope handle_scope; 254 String::Utf8Value file(args[i]); 255 if (*file == NULL) { 256 return ThrowException(String::New("Error loading file")); 257 } 258 Handle<String> source = ReadFile(*file); 259 if (source.IsEmpty()) { 260 return ThrowException(String::New("Error loading file")); 261 } 262 if (!ExecuteString(source, String::New(*file), false, false)) { 263 return ThrowException(String::New("Error executing file")); 264 } 265 } 266 return Undefined(); 267} 268 269 270Handle<Value> Shell::CreateExternalArray(const Arguments& args, 271 ExternalArrayType type, 272 size_t element_size) { 273 ASSERT(element_size == 1 || element_size == 2 || element_size == 4 || 274 element_size == 8); 275 if (args.Length() != 1) { 276 return ThrowException( 277 String::New("Array constructor needs one parameter.")); 278 } 279 static const int kMaxLength = 0x3fffffff; 280#ifndef V8_SHARED 281 ASSERT(kMaxLength == i::ExternalArray::kMaxLength); 282#endif // V8_SHARED 283 size_t length = 0; 284 if (args[0]->IsUint32()) { 285 length = args[0]->Uint32Value(); 286 } else if (args[0]->IsNumber()) { 287 double raw_length = args[0]->NumberValue(); 288 if (raw_length < 0) { 289 return ThrowException(String::New("Array length must not be negative.")); 290 } 291 if (raw_length > kMaxLength) { 292 return ThrowException( 293 String::New("Array length exceeds maximum length.")); 294 } 295 length = static_cast<size_t>(raw_length); 296 } else { 297 return ThrowException(String::New("Array length must be a number.")); 298 } 299 if (length > static_cast<size_t>(kMaxLength)) { 300 return ThrowException(String::New("Array length exceeds maximum length.")); 301 } 302 void* data = calloc(length, element_size); 303 if (data == NULL) { 304 return ThrowException(String::New("Memory allocation failed.")); 305 } 306 Handle<Object> array = Object::New(); 307 Persistent<Object> persistent_array = Persistent<Object>::New(array); 308 persistent_array.MakeWeak(data, ExternalArrayWeakCallback); 309 persistent_array.MarkIndependent(); 310 array->SetIndexedPropertiesToExternalArrayData(data, type, 311 static_cast<int>(length)); 312 array->Set(String::New("length"), 313 Int32::New(static_cast<int32_t>(length)), ReadOnly); 314 array->Set(String::New("BYTES_PER_ELEMENT"), 315 Int32::New(static_cast<int32_t>(element_size))); 316 return array; 317} 318 319 320void Shell::ExternalArrayWeakCallback(Persistent<Value> object, void* data) { 321 free(data); 322 object.Dispose(); 323} 324 325 326Handle<Value> Shell::Int8Array(const Arguments& args) { 327 return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t)); 328} 329 330 331Handle<Value> Shell::Uint8Array(const Arguments& args) { 332 return CreateExternalArray(args, kExternalUnsignedByteArray, sizeof(uint8_t)); 333} 334 335 336Handle<Value> Shell::Int16Array(const Arguments& args) { 337 return CreateExternalArray(args, kExternalShortArray, sizeof(int16_t)); 338} 339 340 341Handle<Value> Shell::Uint16Array(const Arguments& args) { 342 return CreateExternalArray(args, kExternalUnsignedShortArray, 343 sizeof(uint16_t)); 344} 345 346 347Handle<Value> Shell::Int32Array(const Arguments& args) { 348 return CreateExternalArray(args, kExternalIntArray, sizeof(int32_t)); 349} 350 351 352Handle<Value> Shell::Uint32Array(const Arguments& args) { 353 return CreateExternalArray(args, kExternalUnsignedIntArray, sizeof(uint32_t)); 354} 355 356 357Handle<Value> Shell::Float32Array(const Arguments& args) { 358 return CreateExternalArray(args, kExternalFloatArray, 359 sizeof(float)); // NOLINT 360} 361 362 363Handle<Value> Shell::Float64Array(const Arguments& args) { 364 return CreateExternalArray(args, kExternalDoubleArray, 365 sizeof(double)); // NOLINT 366} 367 368 369Handle<Value> Shell::PixelArray(const Arguments& args) { 370 return CreateExternalArray(args, kExternalPixelArray, sizeof(uint8_t)); 371} 372 373 374Handle<Value> Shell::Yield(const Arguments& args) { 375 v8::Unlocker unlocker; 376 return Undefined(); 377} 378 379 380Handle<Value> Shell::Quit(const Arguments& args) { 381 int exit_code = args[0]->Int32Value(); 382#ifndef V8_SHARED 383 OnExit(); 384#endif // V8_SHARED 385 exit(exit_code); 386 return Undefined(); 387} 388 389 390Handle<Value> Shell::Version(const Arguments& args) { 391 return String::New(V8::GetVersion()); 392} 393 394 395void Shell::ReportException(v8::TryCatch* try_catch) { 396 HandleScope handle_scope; 397 v8::String::Utf8Value exception(try_catch->Exception()); 398 const char* exception_string = ToCString(exception); 399 Handle<Message> message = try_catch->Message(); 400 if (message.IsEmpty()) { 401 // V8 didn't provide any extra information about this error; just 402 // print the exception. 403 printf("%s\n", exception_string); 404 } else { 405 // Print (filename):(line number): (message). 406 v8::String::Utf8Value filename(message->GetScriptResourceName()); 407 const char* filename_string = ToCString(filename); 408 int linenum = message->GetLineNumber(); 409 printf("%s:%i: %s\n", filename_string, linenum, exception_string); 410 // Print line of source code. 411 v8::String::Utf8Value sourceline(message->GetSourceLine()); 412 const char* sourceline_string = ToCString(sourceline); 413 printf("%s\n", sourceline_string); 414 // Print wavy underline (GetUnderline is deprecated). 415 int start = message->GetStartColumn(); 416 for (int i = 0; i < start; i++) { 417 printf(" "); 418 } 419 int end = message->GetEndColumn(); 420 for (int i = start; i < end; i++) { 421 printf("^"); 422 } 423 printf("\n"); 424 v8::String::Utf8Value stack_trace(try_catch->StackTrace()); 425 if (stack_trace.length() > 0) { 426 const char* stack_trace_string = ToCString(stack_trace); 427 printf("%s\n", stack_trace_string); 428 } 429 } 430} 431 432 433#ifndef V8_SHARED 434Handle<Array> Shell::GetCompletions(Handle<String> text, Handle<String> full) { 435 HandleScope handle_scope; 436 Context::Scope context_scope(utility_context_); 437 Handle<Object> global = utility_context_->Global(); 438 Handle<Value> fun = global->Get(String::New("GetCompletions")); 439 static const int kArgc = 3; 440 Handle<Value> argv[kArgc] = { evaluation_context_->Global(), text, full }; 441 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv); 442 return handle_scope.Close(Handle<Array>::Cast(val)); 443} 444 445 446#ifdef ENABLE_DEBUGGER_SUPPORT 447Handle<Object> Shell::DebugMessageDetails(Handle<String> message) { 448 Context::Scope context_scope(utility_context_); 449 Handle<Object> global = utility_context_->Global(); 450 Handle<Value> fun = global->Get(String::New("DebugMessageDetails")); 451 static const int kArgc = 1; 452 Handle<Value> argv[kArgc] = { message }; 453 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv); 454 return Handle<Object>::Cast(val); 455} 456 457 458Handle<Value> Shell::DebugCommandToJSONRequest(Handle<String> command) { 459 Context::Scope context_scope(utility_context_); 460 Handle<Object> global = utility_context_->Global(); 461 Handle<Value> fun = global->Get(String::New("DebugCommandToJSONRequest")); 462 static const int kArgc = 1; 463 Handle<Value> argv[kArgc] = { command }; 464 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv); 465 return val; 466} 467#endif // ENABLE_DEBUGGER_SUPPORT 468#endif // V8_SHARED 469 470 471#ifndef V8_SHARED 472int32_t* Counter::Bind(const char* name, bool is_histogram) { 473 int i; 474 for (i = 0; i < kMaxNameSize - 1 && name[i]; i++) 475 name_[i] = static_cast<char>(name[i]); 476 name_[i] = '\0'; 477 is_histogram_ = is_histogram; 478 return ptr(); 479} 480 481 482void Counter::AddSample(int32_t sample) { 483 count_++; 484 sample_total_ += sample; 485} 486 487 488CounterCollection::CounterCollection() { 489 magic_number_ = 0xDEADFACE; 490 max_counters_ = kMaxCounters; 491 max_name_size_ = Counter::kMaxNameSize; 492 counters_in_use_ = 0; 493} 494 495 496Counter* CounterCollection::GetNextCounter() { 497 if (counters_in_use_ == kMaxCounters) return NULL; 498 return &counters_[counters_in_use_++]; 499} 500 501 502void Shell::MapCounters(const char* name) { 503 counters_file_ = i::OS::MemoryMappedFile::create( 504 name, sizeof(CounterCollection), &local_counters_); 505 void* memory = (counters_file_ == NULL) ? 506 NULL : counters_file_->memory(); 507 if (memory == NULL) { 508 printf("Could not map counters file %s\n", name); 509 exit(1); 510 } 511 counters_ = static_cast<CounterCollection*>(memory); 512 V8::SetCounterFunction(LookupCounter); 513 V8::SetCreateHistogramFunction(CreateHistogram); 514 V8::SetAddHistogramSampleFunction(AddHistogramSample); 515} 516 517 518int CounterMap::Hash(const char* name) { 519 int h = 0; 520 int c; 521 while ((c = *name++) != 0) { 522 h += h << 5; 523 h += c; 524 } 525 return h; 526} 527 528 529Counter* Shell::GetCounter(const char* name, bool is_histogram) { 530 Counter* counter = counter_map_->Lookup(name); 531 532 if (counter == NULL) { 533 counter = counters_->GetNextCounter(); 534 if (counter != NULL) { 535 counter_map_->Set(name, counter); 536 counter->Bind(name, is_histogram); 537 } 538 } else { 539 ASSERT(counter->is_histogram() == is_histogram); 540 } 541 return counter; 542} 543 544 545int* Shell::LookupCounter(const char* name) { 546 Counter* counter = GetCounter(name, false); 547 548 if (counter != NULL) { 549 return counter->ptr(); 550 } else { 551 return NULL; 552 } 553} 554 555 556void* Shell::CreateHistogram(const char* name, 557 int min, 558 int max, 559 size_t buckets) { 560 return GetCounter(name, true); 561} 562 563 564void Shell::AddHistogramSample(void* histogram, int sample) { 565 Counter* counter = reinterpret_cast<Counter*>(histogram); 566 counter->AddSample(sample); 567} 568 569 570void Shell::InstallUtilityScript() { 571 Locker lock; 572 HandleScope scope; 573 // If we use the utility context, we have to set the security tokens so that 574 // utility, evaluation and debug context can all access each other. 575 utility_context_->SetSecurityToken(Undefined()); 576 evaluation_context_->SetSecurityToken(Undefined()); 577 Context::Scope utility_scope(utility_context_); 578 579#ifdef ENABLE_DEBUGGER_SUPPORT 580 // Install the debugger object in the utility scope 581 i::Debug* debug = i::Isolate::Current()->debug(); 582 debug->Load(); 583 i::Handle<i::JSObject> js_debug 584 = i::Handle<i::JSObject>(debug->debug_context()->global()); 585 utility_context_->Global()->Set(String::New("$debug"), 586 Utils::ToLocal(js_debug)); 587 debug->debug_context()->set_security_token(HEAP->undefined_value()); 588#endif // ENABLE_DEBUGGER_SUPPORT 589 590 // Run the d8 shell utility script in the utility context 591 int source_index = i::NativesCollection<i::D8>::GetIndex("d8"); 592 i::Vector<const char> shell_source = 593 i::NativesCollection<i::D8>::GetRawScriptSource(source_index); 594 i::Vector<const char> shell_source_name = 595 i::NativesCollection<i::D8>::GetScriptName(source_index); 596 Handle<String> source = String::New(shell_source.start(), 597 shell_source.length()); 598 Handle<String> name = String::New(shell_source_name.start(), 599 shell_source_name.length()); 600 Handle<Script> script = Script::Compile(source, name); 601 script->Run(); 602 // Mark the d8 shell script as native to avoid it showing up as normal source 603 // in the debugger. 604 i::Handle<i::Object> compiled_script = Utils::OpenHandle(*script); 605 i::Handle<i::Script> script_object = compiled_script->IsJSFunction() 606 ? i::Handle<i::Script>(i::Script::cast( 607 i::JSFunction::cast(*compiled_script)->shared()->script())) 608 : i::Handle<i::Script>(i::Script::cast( 609 i::SharedFunctionInfo::cast(*compiled_script)->script())); 610 script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE)); 611 612#ifdef ENABLE_DEBUGGER_SUPPORT 613 // Start the in-process debugger if requested. 614 if (i::FLAG_debugger && !i::FLAG_debugger_agent) { 615 v8::Debug::SetDebugEventListener(HandleDebugEvent); 616 } 617#endif // ENABLE_DEBUGGER_SUPPORT 618} 619#endif // V8_SHARED 620 621 622#ifdef COMPRESS_STARTUP_DATA_BZ2 623class BZip2Decompressor : public v8::StartupDataDecompressor { 624 public: 625 virtual ~BZip2Decompressor() { } 626 627 protected: 628 virtual int DecompressData(char* raw_data, 629 int* raw_data_size, 630 const char* compressed_data, 631 int compressed_data_size) { 632 ASSERT_EQ(v8::StartupData::kBZip2, 633 v8::V8::GetCompressedStartupDataAlgorithm()); 634 unsigned int decompressed_size = *raw_data_size; 635 int result = 636 BZ2_bzBuffToBuffDecompress(raw_data, 637 &decompressed_size, 638 const_cast<char*>(compressed_data), 639 compressed_data_size, 640 0, 1); 641 if (result == BZ_OK) { 642 *raw_data_size = decompressed_size; 643 } 644 return result; 645 } 646}; 647#endif 648 649Handle<ObjectTemplate> Shell::CreateGlobalTemplate() { 650 Handle<ObjectTemplate> global_template = ObjectTemplate::New(); 651 global_template->Set(String::New("print"), FunctionTemplate::New(Print)); 652 global_template->Set(String::New("write"), FunctionTemplate::New(Write)); 653 global_template->Set(String::New("read"), FunctionTemplate::New(Read)); 654 global_template->Set(String::New("readline"), 655 FunctionTemplate::New(ReadLine)); 656 global_template->Set(String::New("load"), FunctionTemplate::New(Load)); 657 global_template->Set(String::New("quit"), FunctionTemplate::New(Quit)); 658 global_template->Set(String::New("version"), FunctionTemplate::New(Version)); 659 660 // Bind the handlers for external arrays. 661 global_template->Set(String::New("Int8Array"), 662 FunctionTemplate::New(Int8Array)); 663 global_template->Set(String::New("Uint8Array"), 664 FunctionTemplate::New(Uint8Array)); 665 global_template->Set(String::New("Int16Array"), 666 FunctionTemplate::New(Int16Array)); 667 global_template->Set(String::New("Uint16Array"), 668 FunctionTemplate::New(Uint16Array)); 669 global_template->Set(String::New("Int32Array"), 670 FunctionTemplate::New(Int32Array)); 671 global_template->Set(String::New("Uint32Array"), 672 FunctionTemplate::New(Uint32Array)); 673 global_template->Set(String::New("Float32Array"), 674 FunctionTemplate::New(Float32Array)); 675 global_template->Set(String::New("Float64Array"), 676 FunctionTemplate::New(Float64Array)); 677 global_template->Set(String::New("PixelArray"), 678 FunctionTemplate::New(PixelArray)); 679 680#ifdef LIVE_OBJECT_LIST 681 global_template->Set(String::New("lol_is_enabled"), True()); 682#else 683 global_template->Set(String::New("lol_is_enabled"), False()); 684#endif 685 686#ifndef V8_SHARED 687 Handle<ObjectTemplate> os_templ = ObjectTemplate::New(); 688 AddOSMethods(os_templ); 689 global_template->Set(String::New("os"), os_templ); 690#endif // V8_SHARED 691 692 return global_template; 693} 694 695 696void Shell::Initialize() { 697#ifdef COMPRESS_STARTUP_DATA_BZ2 698 BZip2Decompressor startup_data_decompressor; 699 int bz2_result = startup_data_decompressor.Decompress(); 700 if (bz2_result != BZ_OK) { 701 fprintf(stderr, "bzip error code: %d\n", bz2_result); 702 exit(1); 703 } 704#endif 705 706#ifndef V8_SHARED 707 Shell::counter_map_ = new CounterMap(); 708 // Set up counters 709 if (i::StrLength(i::FLAG_map_counters) != 0) 710 MapCounters(i::FLAG_map_counters); 711 if (i::FLAG_dump_counters) { 712 V8::SetCounterFunction(LookupCounter); 713 V8::SetCreateHistogramFunction(CreateHistogram); 714 V8::SetAddHistogramSampleFunction(AddHistogramSample); 715 } 716#endif // V8_SHARED 717 if (options.test_shell) return; 718 719#ifndef V8_SHARED 720 Locker lock; 721 HandleScope scope; 722 Handle<ObjectTemplate> global_template = CreateGlobalTemplate(); 723 utility_context_ = Context::New(NULL, global_template); 724 725#ifdef ENABLE_DEBUGGER_SUPPORT 726 // Start the debugger agent if requested. 727 if (i::FLAG_debugger_agent) { 728 v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port, true); 729 } 730#endif // ENABLE_DEBUGGER_SUPPORT 731#endif // V8_SHARED 732} 733 734 735Persistent<Context> Shell::CreateEvaluationContext() { 736#ifndef V8_SHARED 737 // This needs to be a critical section since this is not thread-safe 738 i::ScopedLock lock(context_mutex_); 739#endif // V8_SHARED 740 // Initialize the global objects 741 Handle<ObjectTemplate> global_template = CreateGlobalTemplate(); 742 Persistent<Context> context = Context::New(NULL, global_template); 743 ASSERT(!context.IsEmpty()); 744 Context::Scope scope(context); 745 746#ifndef V8_SHARED 747 i::JSArguments js_args = i::FLAG_js_arguments; 748 i::Handle<i::FixedArray> arguments_array = 749 FACTORY->NewFixedArray(js_args.argc()); 750 for (int j = 0; j < js_args.argc(); j++) { 751 i::Handle<i::String> arg = 752 FACTORY->NewStringFromUtf8(i::CStrVector(js_args[j])); 753 arguments_array->set(j, *arg); 754 } 755 i::Handle<i::JSArray> arguments_jsarray = 756 FACTORY->NewJSArrayWithElements(arguments_array); 757 context->Global()->Set(String::New("arguments"), 758 Utils::ToLocal(arguments_jsarray)); 759#endif // V8_SHARED 760 return context; 761} 762 763 764#ifndef V8_SHARED 765void Shell::OnExit() { 766 if (i::FLAG_dump_counters) { 767 printf("+----------------------------------------+-------------+\n"); 768 printf("| Name | Value |\n"); 769 printf("+----------------------------------------+-------------+\n"); 770 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) { 771 Counter* counter = i.CurrentValue(); 772 if (counter->is_histogram()) { 773 printf("| c:%-36s | %11i |\n", i.CurrentKey(), counter->count()); 774 printf("| t:%-36s | %11i |\n", i.CurrentKey(), counter->sample_total()); 775 } else { 776 printf("| %-38s | %11i |\n", i.CurrentKey(), counter->count()); 777 } 778 } 779 printf("+----------------------------------------+-------------+\n"); 780 } 781 if (counters_file_ != NULL) 782 delete counters_file_; 783} 784#endif // V8_SHARED 785 786 787static FILE* FOpen(const char* path, const char* mode) { 788#if (defined(_WIN32) || defined(_WIN64)) 789 FILE* result; 790 if (fopen_s(&result, path, mode) == 0) { 791 return result; 792 } else { 793 return NULL; 794 } 795#else 796 FILE* file = fopen(path, mode); 797 if (file == NULL) return NULL; 798 struct stat file_stat; 799 if (fstat(fileno(file), &file_stat) != 0) return NULL; 800 bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0); 801 if (is_regular_file) return file; 802 fclose(file); 803 return NULL; 804#endif 805} 806 807 808static char* ReadChars(const char* name, int* size_out) { 809 // Release the V8 lock while reading files. 810 v8::Unlocker unlocker(Isolate::GetCurrent()); 811 FILE* file = FOpen(name, "rb"); 812 if (file == NULL) return NULL; 813 814 fseek(file, 0, SEEK_END); 815 int size = ftell(file); 816 rewind(file); 817 818 char* chars = new char[size + 1]; 819 chars[size] = '\0'; 820 for (int i = 0; i < size;) { 821 int read = static_cast<int>(fread(&chars[i], 1, size - i, file)); 822 i += read; 823 } 824 fclose(file); 825 *size_out = size; 826 return chars; 827} 828 829 830#ifndef V8_SHARED 831static char* ReadToken(char* data, char token) { 832 char* next = i::OS::StrChr(data, token); 833 if (next != NULL) { 834 *next = '\0'; 835 return (next + 1); 836 } 837 838 return NULL; 839} 840 841 842static char* ReadLine(char* data) { 843 return ReadToken(data, '\n'); 844} 845 846 847static char* ReadWord(char* data) { 848 return ReadToken(data, ' '); 849} 850#endif // V8_SHARED 851 852 853// Reads a file into a v8 string. 854Handle<String> Shell::ReadFile(const char* name) { 855 int size = 0; 856 char* chars = ReadChars(name, &size); 857 if (chars == NULL) return Handle<String>(); 858 Handle<String> result = String::New(chars); 859 delete[] chars; 860 return result; 861} 862 863 864void Shell::RunShell() { 865 Locker locker; 866 Context::Scope context_scope(evaluation_context_); 867 HandleScope handle_scope; 868 Handle<String> name = String::New("(d8)"); 869#ifndef V8_SHARED 870 LineEditor* editor = LineEditor::Get(); 871 printf("V8 version %s [console: %s]\n", V8::GetVersion(), editor->name()); 872 if (i::FLAG_debugger) { 873 printf("JavaScript debugger enabled\n"); 874 } 875 editor->Open(); 876 while (true) { 877 i::SmartPointer<char> input = editor->Prompt(Shell::kPrompt); 878 if (input.is_empty()) break; 879 editor->AddHistory(*input); 880 ExecuteString(String::New(*input), name, true, true); 881 } 882 editor->Close(); 883#else 884 printf("V8 version %s [D8 light using shared library]\n", V8::GetVersion()); 885 static const int kBufferSize = 256; 886 while (true) { 887 char buffer[kBufferSize]; 888 printf("%s", Shell::kPrompt); 889 if (fgets(buffer, kBufferSize, stdin) == NULL) break; 890 ExecuteString(String::New(buffer), name, true, true); 891 } 892#endif // V8_SHARED 893 printf("\n"); 894} 895 896 897#ifndef V8_SHARED 898class ShellThread : public i::Thread { 899 public: 900 ShellThread(int no, i::Vector<const char> files) 901 : Thread("d8:ShellThread"), 902 no_(no), files_(files) { } 903 virtual void Run(); 904 private: 905 int no_; 906 i::Vector<const char> files_; 907}; 908 909 910void ShellThread::Run() { 911 char* ptr = const_cast<char*>(files_.start()); 912 while ((ptr != NULL) && (*ptr != '\0')) { 913 // For each newline-separated line. 914 char* next_line = ReadLine(ptr); 915 916 if (*ptr == '#') { 917 // Skip comment lines. 918 ptr = next_line; 919 continue; 920 } 921 922 // Prepare the context for this thread. 923 Locker locker; 924 HandleScope scope; 925 Persistent<Context> thread_context = Shell::CreateEvaluationContext(); 926 Context::Scope context_scope(thread_context); 927 928 while ((ptr != NULL) && (*ptr != '\0')) { 929 char* filename = ptr; 930 ptr = ReadWord(ptr); 931 932 // Skip empty strings. 933 if (strlen(filename) == 0) { 934 break; 935 } 936 937 Handle<String> str = Shell::ReadFile(filename); 938 if (str.IsEmpty()) { 939 printf("WARNING: %s not found\n", filename); 940 break; 941 } 942 943 Shell::ExecuteString(str, String::New(filename), false, false); 944 } 945 946 thread_context.Dispose(); 947 ptr = next_line; 948 } 949} 950#endif // V8_SHARED 951 952 953void SourceGroup::ExitShell(int exit_code) { 954 // Use _exit instead of exit to avoid races between isolate 955 // threads and static destructors. 956 fflush(stdout); 957 fflush(stderr); 958 _exit(exit_code); 959} 960 961 962void SourceGroup::Execute() { 963 for (int i = begin_offset_; i < end_offset_; ++i) { 964 const char* arg = argv_[i]; 965 if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) { 966 // Execute argument given to -e option directly. 967 HandleScope handle_scope; 968 Handle<String> file_name = String::New("unnamed"); 969 Handle<String> source = String::New(argv_[i + 1]); 970 if (!Shell::ExecuteString(source, file_name, false, true)) { 971 ExitShell(1); 972 return; 973 } 974 ++i; 975 } else if (arg[0] == '-') { 976 // Ignore other options. They have been parsed already. 977 } else { 978 // Use all other arguments as names of files to load and run. 979 HandleScope handle_scope; 980 Handle<String> file_name = String::New(arg); 981 Handle<String> source = ReadFile(arg); 982 if (source.IsEmpty()) { 983 printf("Error reading '%s'\n", arg); 984 ExitShell(1); 985 return; 986 } 987 if (!Shell::ExecuteString(source, file_name, false, true)) { 988 ExitShell(1); 989 return; 990 } 991 } 992 } 993} 994 995 996Handle<String> SourceGroup::ReadFile(const char* name) { 997 int size; 998 const char* chars = ReadChars(name, &size); 999 if (chars == NULL) return Handle<String>(); 1000 Handle<String> result = String::New(chars, size); 1001 delete[] chars; 1002 return result; 1003} 1004 1005 1006#ifndef V8_SHARED 1007i::Thread::Options SourceGroup::GetThreadOptions() { 1008 i::Thread::Options options; 1009 options.name = "IsolateThread"; 1010 // On some systems (OSX 10.6) the stack size default is 0.5Mb or less 1011 // which is not enough to parse the big literal expressions used in tests. 1012 // The stack size should be at least StackGuard::kLimitSize + some 1013 // OS-specific padding for thread startup code. 1014 options.stack_size = 2 << 20; // 2 Mb seems to be enough 1015 return options; 1016} 1017 1018 1019void SourceGroup::ExecuteInThread() { 1020 Isolate* isolate = Isolate::New(); 1021 do { 1022 if (next_semaphore_ != NULL) next_semaphore_->Wait(); 1023 { 1024 Isolate::Scope iscope(isolate); 1025 Locker lock(isolate); 1026 HandleScope scope; 1027 Persistent<Context> context = Shell::CreateEvaluationContext(); 1028 { 1029 Context::Scope cscope(context); 1030 Execute(); 1031 } 1032 context.Dispose(); 1033 } 1034 if (done_semaphore_ != NULL) done_semaphore_->Signal(); 1035 } while (!Shell::options.last_run); 1036 isolate->Dispose(); 1037} 1038 1039 1040void SourceGroup::StartExecuteInThread() { 1041 if (thread_ == NULL) { 1042 thread_ = new IsolateThread(this); 1043 thread_->Start(); 1044 } 1045 next_semaphore_->Signal(); 1046} 1047 1048 1049void SourceGroup::WaitForThread() { 1050 if (thread_ == NULL) return; 1051 if (Shell::options.last_run) { 1052 thread_->Join(); 1053 thread_ = NULL; 1054 } else { 1055 done_semaphore_->Wait(); 1056 } 1057} 1058#endif // V8_SHARED 1059 1060 1061bool Shell::SetOptions(int argc, char* argv[]) { 1062 for (int i = 0; i < argc; i++) { 1063 if (strcmp(argv[i], "--stress-opt") == 0) { 1064 options.stress_opt = true; 1065 argv[i] = NULL; 1066 } else if (strcmp(argv[i], "--stress-deopt") == 0) { 1067 options.stress_deopt = true; 1068 argv[i] = NULL; 1069 } else if (strcmp(argv[i], "--noalways-opt") == 0) { 1070 // No support for stressing if we can't use --always-opt. 1071 options.stress_opt = false; 1072 options.stress_deopt = false; 1073 } else if (strcmp(argv[i], "--shell") == 0) { 1074 options.interactive_shell = true; 1075 argv[i] = NULL; 1076 } else if (strcmp(argv[i], "--test") == 0) { 1077 options.test_shell = true; 1078 argv[i] = NULL; 1079 } else if (strcmp(argv[i], "--preemption") == 0) { 1080#ifdef V8_SHARED 1081 printf("D8 with shared library does not support multi-threading\n"); 1082 return false; 1083#else 1084 options.use_preemption = true; 1085 argv[i] = NULL; 1086#endif // V8_SHARED 1087 } else if (strcmp(argv[i], "--no-preemption") == 0) { 1088#ifdef V8_SHARED 1089 printf("D8 with shared library does not support multi-threading\n"); 1090 return false; 1091#else 1092 options.use_preemption = false; 1093 argv[i] = NULL; 1094#endif // V8_SHARED 1095 } else if (strcmp(argv[i], "--preemption-interval") == 0) { 1096#ifdef V8_SHARED 1097 printf("D8 with shared library does not support multi-threading\n"); 1098 return false; 1099#else 1100 if (++i < argc) { 1101 argv[i-1] = NULL; 1102 char* end = NULL; 1103 options.preemption_interval = strtol(argv[i], &end, 10); // NOLINT 1104 if (options.preemption_interval <= 0 1105 || *end != '\0' 1106 || errno == ERANGE) { 1107 printf("Invalid value for --preemption-interval '%s'\n", argv[i]); 1108 return false; 1109 } 1110 argv[i] = NULL; 1111 } else { 1112 printf("Missing value for --preemption-interval\n"); 1113 return false; 1114 } 1115#endif // V8_SHARED 1116 } else if (strcmp(argv[i], "-f") == 0) { 1117 // Ignore any -f flags for compatibility with other stand-alone 1118 // JavaScript engines. 1119 continue; 1120 } else if (strcmp(argv[i], "--isolate") == 0) { 1121#ifdef V8_SHARED 1122 printf("D8 with shared library does not support multi-threading\n"); 1123 return false; 1124#endif // V8_SHARED 1125 options.num_isolates++; 1126 } 1127#ifdef V8_SHARED 1128 else if (strcmp(argv[i], "--dump-counters") == 0) { 1129 printf("D8 with shared library does not include counters\n"); 1130 return false; 1131 } else if (strcmp(argv[i], "-p") == 0) { 1132 printf("D8 with shared library does not support multi-threading\n"); 1133 return false; 1134 } else if (strcmp(argv[i], "--debugger") == 0) { 1135 printf("Javascript debugger not included\n"); 1136 return false; 1137 } 1138#endif // V8_SHARED 1139 } 1140 1141#ifndef V8_SHARED 1142 // Run parallel threads if we are not using --isolate 1143 for (int i = 1; i < argc; i++) { 1144 if (argv[i] == NULL) continue; 1145 if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) { 1146 if (options.num_isolates > 1) { 1147 printf("-p is not compatible with --isolate\n"); 1148 return false; 1149 } 1150 argv[i] = NULL; 1151 if (options.parallel_files == NULL) { 1152 options.parallel_files = new i::List<i::Vector<const char> >(); 1153 } 1154 int size = 0; 1155 const char* files = ReadChars(argv[++i], &size); 1156 if (files == NULL) { 1157 printf("-p option incomplete\n"); 1158 return false; 1159 } 1160 argv[i] = NULL; 1161 options.parallel_files->Add(i::Vector<const char>(files, size)); 1162 delete[] files; 1163 } 1164 } 1165#endif // V8_SHARED 1166 1167 v8::V8::SetFlagsFromCommandLine(&argc, argv, true); 1168 1169 // set up isolated source groups 1170 options.isolate_sources = new SourceGroup[options.num_isolates]; 1171 SourceGroup* current = options.isolate_sources; 1172 current->Begin(argv, 1); 1173 for (int i = 1; i < argc; i++) { 1174 const char* str = argv[i]; 1175 if (strcmp(str, "--isolate") == 0) { 1176 current->End(i); 1177 current++; 1178 current->Begin(argv, i + 1); 1179 } else if (strncmp(argv[i], "--", 2) == 0) { 1180 printf("Warning: unknown flag %s.\nTry --help for options\n", argv[i]); 1181 } 1182 } 1183 current->End(argc); 1184 1185 return true; 1186} 1187 1188 1189int Shell::RunMain(int argc, char* argv[]) { 1190#ifndef V8_SHARED 1191 i::List<i::Thread*> threads(1); 1192 if (options.parallel_files != NULL) 1193 for (int i = 0; i < options.parallel_files->length(); i++) { 1194 i::Vector<const char> files = options.parallel_files->at(i); 1195 ShellThread* thread = new ShellThread(threads.length(), files); 1196 thread->Start(); 1197 threads.Add(thread); 1198 } 1199 1200 for (int i = 1; i < options.num_isolates; ++i) { 1201 options.isolate_sources[i].StartExecuteInThread(); 1202 } 1203#endif // V8_SHARED 1204 { // NOLINT 1205 Locker lock; 1206 HandleScope scope; 1207 Persistent<Context> context = CreateEvaluationContext(); 1208 if (options.last_run) { 1209 // Keep using the same context in the interactive shell. 1210 evaluation_context_ = context; 1211#ifndef V8_SHARED 1212 // If the interactive debugger is enabled make sure to activate 1213 // it before running the files passed on the command line. 1214 if (i::FLAG_debugger) { 1215 InstallUtilityScript(); 1216 } 1217#endif // V8_SHARED 1218 } 1219 { 1220 Context::Scope cscope(context); 1221 options.isolate_sources[0].Execute(); 1222 } 1223 if (!options.last_run) { 1224 context.Dispose(); 1225 } 1226 1227#ifndef V8_SHARED 1228 // Start preemption if threads have been created and preemption is enabled. 1229 if (options.parallel_files != NULL 1230 && threads.length() > 0 1231 && options.use_preemption) { 1232 Locker::StartPreemption(options.preemption_interval); 1233 } 1234#endif // V8_SHARED 1235 } 1236 1237#ifndef V8_SHARED 1238 for (int i = 1; i < options.num_isolates; ++i) { 1239 options.isolate_sources[i].WaitForThread(); 1240 } 1241 1242 if (options.parallel_files != NULL) 1243 for (int i = 0; i < threads.length(); i++) { 1244 i::Thread* thread = threads[i]; 1245 thread->Join(); 1246 delete thread; 1247 } 1248#endif // V8_SHARED 1249 return 0; 1250} 1251 1252 1253int Shell::Main(int argc, char* argv[]) { 1254 if (!SetOptions(argc, argv)) return 1; 1255 Initialize(); 1256 1257 int result = 0; 1258 if (options.stress_opt || options.stress_deopt) { 1259 Testing::SetStressRunType( 1260 options.stress_opt ? Testing::kStressTypeOpt 1261 : Testing::kStressTypeDeopt); 1262 int stress_runs = Testing::GetStressRuns(); 1263 for (int i = 0; i < stress_runs && result == 0; i++) { 1264 printf("============ Stress %d/%d ============\n", i + 1, stress_runs); 1265 Testing::PrepareStressRun(i); 1266 options.last_run = (i == stress_runs - 1); 1267 result = RunMain(argc, argv); 1268 } 1269 printf("======== Full Deoptimization =======\n"); 1270 Testing::DeoptimizeAll(); 1271 } else { 1272 result = RunMain(argc, argv); 1273 } 1274 1275 1276#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT) 1277 // Run remote debugger if requested, but never on --test 1278 if (i::FLAG_remote_debugger && !options.test_shell) { 1279 InstallUtilityScript(); 1280 RunRemoteDebugger(i::FLAG_debugger_port); 1281 return 0; 1282 } 1283#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT 1284 1285 // Run interactive shell if explicitly requested or if no script has been 1286 // executed, but never on --test 1287 1288 if (( options.interactive_shell 1289 || !options.script_executed ) 1290 && !options.test_shell ) { 1291#ifndef V8_SHARED 1292 if (!i::FLAG_debugger) { 1293 InstallUtilityScript(); 1294 } 1295#endif // V8_SHARED 1296 RunShell(); 1297 } 1298 1299 V8::Dispose(); 1300 1301#ifndef V8_SHARED 1302 OnExit(); 1303#endif // V8_SHARED 1304 1305 return result; 1306} 1307 1308} // namespace v8 1309 1310 1311#ifndef GOOGLE3 1312int main(int argc, char* argv[]) { 1313 return v8::Shell::Main(argc, argv); 1314} 1315#endif 1316