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