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