• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ExitShell(int exit_code)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:
SourceGroup()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 
Begin(char ** argv,int offset)94   void Begin(char** argv, int offset) {
95     argv_ = const_cast<const char**>(argv);
96     begin_offset_ = offset;
97   }
98 
End(int offset)99   void End(int offset) { end_offset_ = offset; }
100 
Execute()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
StartExecuteInThread()134   void StartExecuteInThread() {
135     if (thread_ == NULL) {
136       thread_ = new IsolateThread(this);
137       thread_->Start();
138     }
139     next_semaphore_->Signal();
140   }
141 
WaitForThread()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
GetThreadOptions()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:
IsolateThread(SourceGroup * group)168     explicit IsolateThread(SourceGroup* group)
169         : v8::internal::Thread(NULL, GetThreadOptions()), group_(group) {}
170 
Run()171     virtual void Run() {
172       group_->ExecuteInThread();
173     }
174 
175    private:
176     SourceGroup* group_;
177   };
178 
ExecuteInThread()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 
RunMain(int argc,char * argv[])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 
main(int argc,char * argv[])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.
ToCString(const v8::String::Utf8Value & value)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.
CreateShellContext()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.
Print(const v8::Arguments & args)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.
Read(const v8::Arguments & args)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.
Load(const v8::Arguments & args)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.
Quit(const v8::Arguments & args)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 
Version(const v8::Arguments & args)443 v8::Handle<v8::Value> Version(const v8::Arguments& args) {
444   return v8::String::New(v8::V8::GetVersion());
445 }
446 
447 
ExternalArrayWeakCallback(v8::Persistent<v8::Value> object,void * data)448 void ExternalArrayWeakCallback(v8::Persistent<v8::Value> object, void* data) {
449   free(data);
450   object.Dispose();
451 }
452 
453 
CreateExternalArray(const v8::Arguments & args,v8::ExternalArrayType type,int element_size)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 
Int8Array(const v8::Arguments & args)477 v8::Handle<v8::Value> Int8Array(const v8::Arguments& args) {
478   return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t));
479 }
480 
481 
Uint8Array(const v8::Arguments & args)482 v8::Handle<v8::Value> Uint8Array(const v8::Arguments& args) {
483   return CreateExternalArray(args, v8::kExternalUnsignedByteArray,
484                              sizeof(uint8_t));
485 }
486 
487 
Int16Array(const v8::Arguments & args)488 v8::Handle<v8::Value> Int16Array(const v8::Arguments& args) {
489   return CreateExternalArray(args, v8::kExternalShortArray, sizeof(int16_t));
490 }
491 
492 
Uint16Array(const v8::Arguments & args)493 v8::Handle<v8::Value> Uint16Array(const v8::Arguments& args) {
494   return CreateExternalArray(args, v8::kExternalUnsignedShortArray,
495                              sizeof(uint16_t));
496 }
497 
Int32Array(const v8::Arguments & args)498 v8::Handle<v8::Value> Int32Array(const v8::Arguments& args) {
499   return CreateExternalArray(args, v8::kExternalIntArray, sizeof(int32_t));
500 }
501 
502 
Uint32Array(const v8::Arguments & args)503 v8::Handle<v8::Value> Uint32Array(const v8::Arguments& args) {
504   return CreateExternalArray(args, v8::kExternalUnsignedIntArray,
505                              sizeof(uint32_t));
506 }
507 
508 
Float32Array(const v8::Arguments & args)509 v8::Handle<v8::Value> Float32Array(const v8::Arguments& args) {
510   return CreateExternalArray(args, v8::kExternalFloatArray,
511                              sizeof(float));  // NOLINT
512 }
513 
514 
PixelArray(const v8::Arguments & args)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.
ReadFile(const char * name)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.
RunShell(v8::Handle<v8::Context> context)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.
ExecuteString(v8::Handle<v8::String> source,v8::Handle<v8::Value> name,bool print_result,bool report_exceptions)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 
ReportException(v8::TryCatch * try_catch)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