Home | History | Annotate | Download | only in samples
      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