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 #include <v8.h> 29 #include <v8-testing.h> 30 #include <assert.h> 31 #include <fcntl.h> 32 #include <string.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 36 // When building with V8 in a shared library we cannot use functions which 37 // is not explicitly a part of the public V8 API. This extensive use of 38 // #ifndef USING_V8_SHARED/#endif is a hack until we can resolve whether to 39 // still use the shell sample for testing or change to use the developer 40 // shell d8 TODO(1272). 41 #ifndef USING_V8_SHARED 42 #include "../src/v8.h" 43 #endif // USING_V8_SHARED 44 45 #if !defined(_WIN32) && !defined(_WIN64) 46 #include <unistd.h> // NOLINT 47 #endif 48 49 static void ExitShell(int exit_code) { 50 // Use _exit instead of exit to avoid races between isolate 51 // threads and static destructors. 52 fflush(stdout); 53 fflush(stderr); 54 _exit(exit_code); 55 } 56 57 v8::Persistent<v8::Context> CreateShellContext(); 58 void RunShell(v8::Handle<v8::Context> context); 59 bool ExecuteString(v8::Handle<v8::String> source, 60 v8::Handle<v8::Value> name, 61 bool print_result, 62 bool report_exceptions); 63 v8::Handle<v8::Value> Print(const v8::Arguments& args); 64 v8::Handle<v8::Value> Read(const v8::Arguments& args); 65 v8::Handle<v8::Value> Load(const v8::Arguments& args); 66 v8::Handle<v8::Value> Quit(const v8::Arguments& args); 67 v8::Handle<v8::Value> Version(const v8::Arguments& args); 68 v8::Handle<v8::Value> Int8Array(const v8::Arguments& args); 69 v8::Handle<v8::Value> Uint8Array(const v8::Arguments& args); 70 v8::Handle<v8::Value> Int16Array(const v8::Arguments& args); 71 v8::Handle<v8::Value> Uint16Array(const v8::Arguments& args); 72 v8::Handle<v8::Value> Int32Array(const v8::Arguments& args); 73 v8::Handle<v8::Value> Uint32Array(const v8::Arguments& args); 74 v8::Handle<v8::Value> Float32Array(const v8::Arguments& args); 75 v8::Handle<v8::Value> PixelArray(const v8::Arguments& args); 76 v8::Handle<v8::String> ReadFile(const char* name); 77 void ReportException(v8::TryCatch* handler); 78 79 80 static bool last_run = true; 81 82 class SourceGroup { 83 public: 84 SourceGroup() : 85 #ifndef USING_V8_SHARED 86 next_semaphore_(v8::internal::OS::CreateSemaphore(0)), 87 done_semaphore_(v8::internal::OS::CreateSemaphore(0)), 88 thread_(NULL), 89 #endif // USING_V8_SHARED 90 argv_(NULL), 91 begin_offset_(0), 92 end_offset_(0) { } 93 94 void Begin(char** argv, int offset) { 95 argv_ = const_cast<const char**>(argv); 96 begin_offset_ = offset; 97 } 98 99 void End(int offset) { end_offset_ = offset; } 100 101 void Execute() { 102 for (int i = begin_offset_; i < end_offset_; ++i) { 103 const char* arg = argv_[i]; 104 if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) { 105 // Execute argument given to -e option directly. 106 v8::HandleScope handle_scope; 107 v8::Handle<v8::String> file_name = v8::String::New("unnamed"); 108 v8::Handle<v8::String> source = v8::String::New(argv_[i + 1]); 109 if (!ExecuteString(source, file_name, false, true)) { 110 ExitShell(1); 111 return; 112 } 113 ++i; 114 } else if (arg[0] == '-') { 115 // Ignore other options. They have been parsed already. 116 } else { 117 // Use all other arguments as names of files to load and run. 118 v8::HandleScope handle_scope; 119 v8::Handle<v8::String> file_name = v8::String::New(arg); 120 v8::Handle<v8::String> source = ReadFile(arg); 121 if (source.IsEmpty()) { 122 printf("Error reading '%s'\n", arg); 123 continue; 124 } 125 if (!ExecuteString(source, file_name, false, true)) { 126 ExitShell(1); 127 return; 128 } 129 } 130 } 131 } 132 133 #ifndef USING_V8_SHARED 134 void StartExecuteInThread() { 135 if (thread_ == NULL) { 136 thread_ = new IsolateThread(this); 137 thread_->Start(); 138 } 139 next_semaphore_->Signal(); 140 } 141 142 void WaitForThread() { 143 if (thread_ == NULL) return; 144 if (last_run) { 145 thread_->Join(); 146 thread_ = NULL; 147 } else { 148 done_semaphore_->Wait(); 149 } 150 } 151 #endif // USING_V8_SHARED 152 153 private: 154 #ifndef USING_V8_SHARED 155 static v8::internal::Thread::Options GetThreadOptions() { 156 v8::internal::Thread::Options options; 157 options.name = "IsolateThread"; 158 // On some systems (OSX 10.6) the stack size default is 0.5Mb or less 159 // which is not enough to parse the big literal expressions used in tests. 160 // The stack size should be at least StackGuard::kLimitSize + some 161 // OS-specific padding for thread startup code. 162 options.stack_size = 2 << 20; // 2 Mb seems to be enough 163 return options; 164 } 165 166 class IsolateThread : public v8::internal::Thread { 167 public: 168 explicit IsolateThread(SourceGroup* group) 169 : v8::internal::Thread(NULL, GetThreadOptions()), group_(group) {} 170 171 virtual void Run() { 172 group_->ExecuteInThread(); 173 } 174 175 private: 176 SourceGroup* group_; 177 }; 178 179 void ExecuteInThread() { 180 v8::Isolate* isolate = v8::Isolate::New(); 181 do { 182 if (next_semaphore_ != NULL) next_semaphore_->Wait(); 183 { 184 v8::Isolate::Scope iscope(isolate); 185 v8::HandleScope scope; 186 v8::Persistent<v8::Context> context = CreateShellContext(); 187 { 188 v8::Context::Scope cscope(context); 189 Execute(); 190 } 191 context.Dispose(); 192 } 193 if (done_semaphore_ != NULL) done_semaphore_->Signal(); 194 } while (!last_run); 195 isolate->Dispose(); 196 } 197 198 v8::internal::Semaphore* next_semaphore_; 199 v8::internal::Semaphore* done_semaphore_; 200 v8::internal::Thread* thread_; 201 #endif // USING_V8_SHARED 202 203 const char** argv_; 204 int begin_offset_; 205 int end_offset_; 206 }; 207 208 209 static SourceGroup* isolate_sources = NULL; 210 211 212 int RunMain(int argc, char* argv[]) { 213 v8::V8::SetFlagsFromCommandLine(&argc, argv, true); 214 v8::HandleScope handle_scope; 215 v8::Persistent<v8::Context> context = CreateShellContext(); 216 // Enter the newly created execution environment. 217 context->Enter(); 218 if (context.IsEmpty()) { 219 printf("Error creating context\n"); 220 return 1; 221 } 222 223 bool run_shell = (argc == 1); 224 int num_isolates = 1; 225 for (int i = 1; i < argc; i++) { 226 if (strcmp(argv[i], "--isolate") == 0) { 227 #ifndef USING_V8_SHARED 228 ++num_isolates; 229 #else // USING_V8_SHARED 230 printf("Error: --isolate not supported when linked with shared " 231 "library\n"); 232 ExitShell(1); 233 #endif // USING_V8_SHARED 234 } 235 } 236 if (isolate_sources == NULL) { 237 isolate_sources = new SourceGroup[num_isolates]; 238 SourceGroup* current = isolate_sources; 239 current->Begin(argv, 1); 240 for (int i = 1; i < argc; i++) { 241 const char* str = argv[i]; 242 if (strcmp(str, "--isolate") == 0) { 243 current->End(i); 244 current++; 245 current->Begin(argv, i + 1); 246 } else if (strcmp(str, "--shell") == 0) { 247 run_shell = true; 248 } else if (strcmp(str, "-f") == 0) { 249 // Ignore any -f flags for compatibility with the other stand- 250 // alone JavaScript engines. 251 continue; 252 } else if (strncmp(str, "--", 2) == 0) { 253 printf("Warning: unknown flag %s.\nTry --help for options\n", str); 254 } 255 } 256 current->End(argc); 257 } 258 #ifndef USING_V8_SHARED 259 for (int i = 1; i < num_isolates; ++i) { 260 isolate_sources[i].StartExecuteInThread(); 261 } 262 #endif // USING_V8_SHARED 263 isolate_sources[0].Execute(); 264 if (run_shell) RunShell(context); 265 #ifndef USING_V8_SHARED 266 for (int i = 1; i < num_isolates; ++i) { 267 isolate_sources[i].WaitForThread(); 268 } 269 #endif // USING_V8_SHARED 270 if (last_run) { 271 delete[] isolate_sources; 272 isolate_sources = NULL; 273 } 274 context->Exit(); 275 context.Dispose(); 276 return 0; 277 } 278 279 280 int main(int argc, char* argv[]) { 281 // Figure out if we're requested to stress the optimization 282 // infrastructure by running tests multiple times and forcing 283 // optimization in the last run. 284 bool FLAG_stress_opt = false; 285 bool FLAG_stress_deopt = false; 286 for (int i = 0; i < argc; i++) { 287 if (strcmp(argv[i], "--stress-opt") == 0) { 288 FLAG_stress_opt = true; 289 argv[i] = NULL; 290 } else if (strcmp(argv[i], "--stress-deopt") == 0) { 291 FLAG_stress_deopt = true; 292 argv[i] = NULL; 293 } else if (strcmp(argv[i], "--noalways-opt") == 0) { 294 // No support for stressing if we can't use --always-opt. 295 FLAG_stress_opt = false; 296 FLAG_stress_deopt = false; 297 break; 298 } 299 } 300 301 v8::V8::SetFlagsFromCommandLine(&argc, argv, true); 302 int result = 0; 303 if (FLAG_stress_opt || FLAG_stress_deopt) { 304 v8::Testing::SetStressRunType(FLAG_stress_opt 305 ? v8::Testing::kStressTypeOpt 306 : v8::Testing::kStressTypeDeopt); 307 int stress_runs = v8::Testing::GetStressRuns(); 308 for (int i = 0; i < stress_runs && result == 0; i++) { 309 printf("============ Stress %d/%d ============\n", 310 i + 1, stress_runs); 311 v8::Testing::PrepareStressRun(i); 312 last_run = (i == stress_runs - 1); 313 result = RunMain(argc, argv); 314 } 315 printf("======== Full Deoptimization =======\n"); 316 v8::Testing::DeoptimizeAll(); 317 } else { 318 result = RunMain(argc, argv); 319 } 320 v8::V8::Dispose(); 321 return result; 322 } 323 324 325 // Extracts a C string from a V8 Utf8Value. 326 const char* ToCString(const v8::String::Utf8Value& value) { 327 return *value ? *value : "<string conversion failed>"; 328 } 329 330 331 // Creates a new execution environment containing the built-in 332 // functions. 333 v8::Persistent<v8::Context> CreateShellContext() { 334 // Create a template for the global object. 335 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); 336 // Bind the global 'print' function to the C++ Print callback. 337 global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)); 338 // Bind the global 'read' function to the C++ Read callback. 339 global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read)); 340 // Bind the global 'load' function to the C++ Load callback. 341 global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load)); 342 // Bind the 'quit' function 343 global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit)); 344 // Bind the 'version' function 345 global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version)); 346 347 // Bind the handlers for external arrays. 348 global->Set(v8::String::New("Int8Array"), 349 v8::FunctionTemplate::New(Int8Array)); 350 global->Set(v8::String::New("Uint8Array"), 351 v8::FunctionTemplate::New(Uint8Array)); 352 global->Set(v8::String::New("Int16Array"), 353 v8::FunctionTemplate::New(Int16Array)); 354 global->Set(v8::String::New("Uint16Array"), 355 v8::FunctionTemplate::New(Uint16Array)); 356 global->Set(v8::String::New("Int32Array"), 357 v8::FunctionTemplate::New(Int32Array)); 358 global->Set(v8::String::New("Uint32Array"), 359 v8::FunctionTemplate::New(Uint32Array)); 360 global->Set(v8::String::New("Float32Array"), 361 v8::FunctionTemplate::New(Float32Array)); 362 global->Set(v8::String::New("PixelArray"), 363 v8::FunctionTemplate::New(PixelArray)); 364 365 return v8::Context::New(NULL, global); 366 } 367 368 369 // The callback that is invoked by v8 whenever the JavaScript 'print' 370 // function is called. Prints its arguments on stdout separated by 371 // spaces and ending with a newline. 372 v8::Handle<v8::Value> Print(const v8::Arguments& args) { 373 bool first = true; 374 for (int i = 0; i < args.Length(); i++) { 375 v8::HandleScope handle_scope; 376 if (first) { 377 first = false; 378 } else { 379 printf(" "); 380 } 381 v8::String::Utf8Value str(args[i]); 382 const char* cstr = ToCString(str); 383 printf("%s", cstr); 384 } 385 printf("\n"); 386 fflush(stdout); 387 return v8::Undefined(); 388 } 389 390 391 // The callback that is invoked by v8 whenever the JavaScript 'read' 392 // function is called. This function loads the content of the file named in 393 // the argument into a JavaScript string. 394 v8::Handle<v8::Value> Read(const v8::Arguments& args) { 395 if (args.Length() != 1) { 396 return v8::ThrowException(v8::String::New("Bad parameters")); 397 } 398 v8::String::Utf8Value file(args[0]); 399 if (*file == NULL) { 400 return v8::ThrowException(v8::String::New("Error loading file")); 401 } 402 v8::Handle<v8::String> source = ReadFile(*file); 403 if (source.IsEmpty()) { 404 return v8::ThrowException(v8::String::New("Error loading file")); 405 } 406 return source; 407 } 408 409 410 // The callback that is invoked by v8 whenever the JavaScript 'load' 411 // function is called. Loads, compiles and executes its argument 412 // JavaScript file. 413 v8::Handle<v8::Value> Load(const v8::Arguments& args) { 414 for (int i = 0; i < args.Length(); i++) { 415 v8::HandleScope handle_scope; 416 v8::String::Utf8Value file(args[i]); 417 if (*file == NULL) { 418 return v8::ThrowException(v8::String::New("Error loading file")); 419 } 420 v8::Handle<v8::String> source = ReadFile(*file); 421 if (source.IsEmpty()) { 422 return v8::ThrowException(v8::String::New("Error loading file")); 423 } 424 if (!ExecuteString(source, v8::String::New(*file), false, false)) { 425 return v8::ThrowException(v8::String::New("Error executing file")); 426 } 427 } 428 return v8::Undefined(); 429 } 430 431 432 // The callback that is invoked by v8 whenever the JavaScript 'quit' 433 // function is called. Quits. 434 v8::Handle<v8::Value> Quit(const v8::Arguments& args) { 435 // If not arguments are given args[0] will yield undefined which 436 // converts to the integer value 0. 437 int exit_code = args[0]->Int32Value(); 438 ExitShell(exit_code); 439 return v8::Undefined(); 440 } 441 442 443 v8::Handle<v8::Value> Version(const v8::Arguments& args) { 444 return v8::String::New(v8::V8::GetVersion()); 445 } 446 447 448 void ExternalArrayWeakCallback(v8::Persistent<v8::Value> object, void* data) { 449 free(data); 450 object.Dispose(); 451 } 452 453 454 v8::Handle<v8::Value> CreateExternalArray(const v8::Arguments& args, 455 v8::ExternalArrayType type, 456 int element_size) { 457 if (args.Length() != 1) { 458 return v8::ThrowException( 459 v8::String::New("Array constructor needs one parameter.")); 460 } 461 int length = args[0]->Int32Value(); 462 void* data = malloc(length * element_size); 463 memset(data, 0, length * element_size); 464 v8::Handle<v8::Object> array = v8::Object::New(); 465 v8::Persistent<v8::Object> persistent_array = 466 v8::Persistent<v8::Object>::New(array); 467 persistent_array.MakeWeak(data, ExternalArrayWeakCallback); 468 array->SetIndexedPropertiesToExternalArrayData(data, type, length); 469 array->Set(v8::String::New("length"), v8::Int32::New(length), 470 v8::ReadOnly); 471 array->Set(v8::String::New("BYTES_PER_ELEMENT"), 472 v8::Int32::New(element_size)); 473 return array; 474 } 475 476 477 v8::Handle<v8::Value> Int8Array(const v8::Arguments& args) { 478 return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t)); 479 } 480 481 482 v8::Handle<v8::Value> Uint8Array(const v8::Arguments& args) { 483 return CreateExternalArray(args, v8::kExternalUnsignedByteArray, 484 sizeof(uint8_t)); 485 } 486 487 488 v8::Handle<v8::Value> Int16Array(const v8::Arguments& args) { 489 return CreateExternalArray(args, v8::kExternalShortArray, sizeof(int16_t)); 490 } 491 492 493 v8::Handle<v8::Value> Uint16Array(const v8::Arguments& args) { 494 return CreateExternalArray(args, v8::kExternalUnsignedShortArray, 495 sizeof(uint16_t)); 496 } 497 498 v8::Handle<v8::Value> Int32Array(const v8::Arguments& args) { 499 return CreateExternalArray(args, v8::kExternalIntArray, sizeof(int32_t)); 500 } 501 502 503 v8::Handle<v8::Value> Uint32Array(const v8::Arguments& args) { 504 return CreateExternalArray(args, v8::kExternalUnsignedIntArray, 505 sizeof(uint32_t)); 506 } 507 508 509 v8::Handle<v8::Value> Float32Array(const v8::Arguments& args) { 510 return CreateExternalArray(args, v8::kExternalFloatArray, 511 sizeof(float)); // NOLINT 512 } 513 514 515 v8::Handle<v8::Value> PixelArray(const v8::Arguments& args) { 516 return CreateExternalArray(args, v8::kExternalPixelArray, sizeof(uint8_t)); 517 } 518 519 520 // Reads a file into a v8 string. 521 v8::Handle<v8::String> ReadFile(const char* name) { 522 FILE* file = fopen(name, "rb"); 523 if (file == NULL) return v8::Handle<v8::String>(); 524 525 fseek(file, 0, SEEK_END); 526 int size = ftell(file); 527 rewind(file); 528 529 char* chars = new char[size + 1]; 530 chars[size] = '\0'; 531 for (int i = 0; i < size;) { 532 int read = fread(&chars[i], 1, size - i, file); 533 i += read; 534 } 535 fclose(file); 536 v8::Handle<v8::String> result = v8::String::New(chars, size); 537 delete[] chars; 538 return result; 539 } 540 541 542 // The read-eval-execute loop of the shell. 543 void RunShell(v8::Handle<v8::Context> context) { 544 printf("V8 version %s\n", v8::V8::GetVersion()); 545 static const int kBufferSize = 256; 546 // Enter the execution environment before evaluating any code. 547 v8::Context::Scope context_scope(context); 548 while (true) { 549 char buffer[kBufferSize]; 550 printf("> "); 551 char* str = fgets(buffer, kBufferSize, stdin); 552 if (str == NULL) break; 553 v8::HandleScope handle_scope; 554 ExecuteString(v8::String::New(str), 555 v8::String::New("(shell)"), 556 true, 557 true); 558 } 559 printf("\n"); 560 } 561 562 563 // Executes a string within the current v8 context. 564 bool ExecuteString(v8::Handle<v8::String> source, 565 v8::Handle<v8::Value> name, 566 bool print_result, 567 bool report_exceptions) { 568 v8::HandleScope handle_scope; 569 v8::TryCatch try_catch; 570 v8::Handle<v8::Script> script = v8::Script::Compile(source, name); 571 if (script.IsEmpty()) { 572 // Print errors that happened during compilation. 573 if (report_exceptions) 574 ReportException(&try_catch); 575 return false; 576 } else { 577 v8::Handle<v8::Value> result = script->Run(); 578 if (result.IsEmpty()) { 579 assert(try_catch.HasCaught()); 580 // Print errors that happened during execution. 581 if (report_exceptions) 582 ReportException(&try_catch); 583 return false; 584 } else { 585 assert(!try_catch.HasCaught()); 586 if (print_result && !result->IsUndefined()) { 587 // If all went well and the result wasn't undefined then print 588 // the returned value. 589 v8::String::Utf8Value str(result); 590 const char* cstr = ToCString(str); 591 printf("%s\n", cstr); 592 } 593 return true; 594 } 595 } 596 } 597 598 599 void ReportException(v8::TryCatch* try_catch) { 600 v8::HandleScope handle_scope; 601 v8::String::Utf8Value exception(try_catch->Exception()); 602 const char* exception_string = ToCString(exception); 603 v8::Handle<v8::Message> message = try_catch->Message(); 604 if (message.IsEmpty()) { 605 // V8 didn't provide any extra information about this error; just 606 // print the exception. 607 printf("%s\n", exception_string); 608 } else { 609 // Print (filename):(line number): (message). 610 v8::String::Utf8Value filename(message->GetScriptResourceName()); 611 const char* filename_string = ToCString(filename); 612 int linenum = message->GetLineNumber(); 613 printf("%s:%i: %s\n", filename_string, linenum, exception_string); 614 // Print line of source code. 615 v8::String::Utf8Value sourceline(message->GetSourceLine()); 616 const char* sourceline_string = ToCString(sourceline); 617 printf("%s\n", sourceline_string); 618 // Print wavy underline (GetUnderline is deprecated). 619 int start = message->GetStartColumn(); 620 for (int i = 0; i < start; i++) { 621 printf(" "); 622 } 623 int end = message->GetEndColumn(); 624 for (int i = start; i < end; i++) { 625 printf("^"); 626 } 627 printf("\n"); 628 v8::String::Utf8Value stack_trace(try_catch->StackTrace()); 629 if (stack_trace.length() > 0) { 630 const char* stack_trace_string = ToCString(stack_trace); 631 printf("%s\n", stack_trace_string); 632 } 633 } 634 } 635