Home | History | Annotate | Download | only in samples
      1 // Copyright 2012 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 <assert.h>
     30 #include <fcntl.h>
     31 #include <string.h>
     32 #include <stdio.h>
     33 #include <stdlib.h>
     34 
     35 #ifdef COMPRESS_STARTUP_DATA_BZ2
     36 #error Using compressed startup data is not supported for this sample
     37 #endif
     38 
     39 /**
     40  * This sample program shows how to implement a simple javascript shell
     41  * based on V8.  This includes initializing V8 with command line options,
     42  * creating global functions, compiling and executing strings.
     43  *
     44  * For a more sophisticated shell, consider using the debug shell D8.
     45  */
     46 
     47 
     48 v8::Handle<v8::Context> CreateShellContext(v8::Isolate* isolate);
     49 void RunShell(v8::Handle<v8::Context> context);
     50 int RunMain(v8::Isolate* isolate, int argc, char* argv[]);
     51 bool ExecuteString(v8::Isolate* isolate,
     52                    v8::Handle<v8::String> source,
     53                    v8::Handle<v8::Value> name,
     54                    bool print_result,
     55                    bool report_exceptions);
     56 void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
     57 void Read(const v8::FunctionCallbackInfo<v8::Value>& args);
     58 void Load(const v8::FunctionCallbackInfo<v8::Value>& args);
     59 void Quit(const v8::FunctionCallbackInfo<v8::Value>& args);
     60 void Version(const v8::FunctionCallbackInfo<v8::Value>& args);
     61 v8::Handle<v8::String> ReadFile(const char* name);
     62 void ReportException(v8::Isolate* isolate, v8::TryCatch* handler);
     63 
     64 
     65 static bool run_shell;
     66 
     67 
     68 int main(int argc, char* argv[]) {
     69   v8::V8::InitializeICU();
     70   v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
     71   v8::Isolate* isolate = v8::Isolate::GetCurrent();
     72   run_shell = (argc == 1);
     73   int result;
     74   {
     75     v8::HandleScope handle_scope(isolate);
     76     v8::Handle<v8::Context> context = CreateShellContext(isolate);
     77     if (context.IsEmpty()) {
     78       fprintf(stderr, "Error creating context\n");
     79       return 1;
     80     }
     81     context->Enter();
     82     result = RunMain(isolate, argc, argv);
     83     if (run_shell) RunShell(context);
     84     context->Exit();
     85   }
     86   v8::V8::Dispose();
     87   return result;
     88 }
     89 
     90 
     91 // Extracts a C string from a V8 Utf8Value.
     92 const char* ToCString(const v8::String::Utf8Value& value) {
     93   return *value ? *value : "<string conversion failed>";
     94 }
     95 
     96 
     97 // Creates a new execution environment containing the built-in
     98 // functions.
     99 v8::Handle<v8::Context> CreateShellContext(v8::Isolate* isolate) {
    100   // Create a template for the global object.
    101   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
    102   // Bind the global 'print' function to the C++ Print callback.
    103   global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
    104   // Bind the global 'read' function to the C++ Read callback.
    105   global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read));
    106   // Bind the global 'load' function to the C++ Load callback.
    107   global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load));
    108   // Bind the 'quit' function
    109   global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit));
    110   // Bind the 'version' function
    111   global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version));
    112 
    113   return v8::Context::New(isolate, NULL, global);
    114 }
    115 
    116 
    117 // The callback that is invoked by v8 whenever the JavaScript 'print'
    118 // function is called.  Prints its arguments on stdout separated by
    119 // spaces and ending with a newline.
    120 void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
    121   bool first = true;
    122   for (int i = 0; i < args.Length(); i++) {
    123     v8::HandleScope handle_scope(args.GetIsolate());
    124     if (first) {
    125       first = false;
    126     } else {
    127       printf(" ");
    128     }
    129     v8::String::Utf8Value str(args[i]);
    130     const char* cstr = ToCString(str);
    131     printf("%s", cstr);
    132   }
    133   printf("\n");
    134   fflush(stdout);
    135 }
    136 
    137 
    138 // The callback that is invoked by v8 whenever the JavaScript 'read'
    139 // function is called.  This function loads the content of the file named in
    140 // the argument into a JavaScript string.
    141 void Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
    142   if (args.Length() != 1) {
    143     v8::ThrowException(v8::String::New("Bad parameters"));
    144     return;
    145   }
    146   v8::String::Utf8Value file(args[0]);
    147   if (*file == NULL) {
    148     v8::ThrowException(v8::String::New("Error loading file"));
    149     return;
    150   }
    151   v8::Handle<v8::String> source = ReadFile(*file);
    152   if (source.IsEmpty()) {
    153     v8::ThrowException(v8::String::New("Error loading file"));
    154     return;
    155   }
    156   args.GetReturnValue().Set(source);
    157 }
    158 
    159 
    160 // The callback that is invoked by v8 whenever the JavaScript 'load'
    161 // function is called.  Loads, compiles and executes its argument
    162 // JavaScript file.
    163 void Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
    164   for (int i = 0; i < args.Length(); i++) {
    165     v8::HandleScope handle_scope(args.GetIsolate());
    166     v8::String::Utf8Value file(args[i]);
    167     if (*file == NULL) {
    168       v8::ThrowException(v8::String::New("Error loading file"));
    169       return;
    170     }
    171     v8::Handle<v8::String> source = ReadFile(*file);
    172     if (source.IsEmpty()) {
    173       v8::ThrowException(v8::String::New("Error loading file"));
    174       return;
    175     }
    176     if (!ExecuteString(args.GetIsolate(),
    177                        source,
    178                        v8::String::New(*file),
    179                        false,
    180                        false)) {
    181       v8::ThrowException(v8::String::New("Error executing file"));
    182       return;
    183     }
    184   }
    185 }
    186 
    187 
    188 // The callback that is invoked by v8 whenever the JavaScript 'quit'
    189 // function is called.  Quits.
    190 void Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
    191   // If not arguments are given args[0] will yield undefined which
    192   // converts to the integer value 0.
    193   int exit_code = args[0]->Int32Value();
    194   fflush(stdout);
    195   fflush(stderr);
    196   exit(exit_code);
    197 }
    198 
    199 
    200 void Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
    201   args.GetReturnValue().Set(v8::String::New(v8::V8::GetVersion()));
    202 }
    203 
    204 
    205 // Reads a file into a v8 string.
    206 v8::Handle<v8::String> ReadFile(const char* name) {
    207   FILE* file = fopen(name, "rb");
    208   if (file == NULL) return v8::Handle<v8::String>();
    209 
    210   fseek(file, 0, SEEK_END);
    211   int size = ftell(file);
    212   rewind(file);
    213 
    214   char* chars = new char[size + 1];
    215   chars[size] = '\0';
    216   for (int i = 0; i < size;) {
    217     int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
    218     i += read;
    219   }
    220   fclose(file);
    221   v8::Handle<v8::String> result = v8::String::New(chars, size);
    222   delete[] chars;
    223   return result;
    224 }
    225 
    226 
    227 // Process remaining command line arguments and execute files
    228 int RunMain(v8::Isolate* isolate, int argc, char* argv[]) {
    229   for (int i = 1; i < argc; i++) {
    230     const char* str = argv[i];
    231     if (strcmp(str, "--shell") == 0) {
    232       run_shell = true;
    233     } else if (strcmp(str, "-f") == 0) {
    234       // Ignore any -f flags for compatibility with the other stand-
    235       // alone JavaScript engines.
    236       continue;
    237     } else if (strncmp(str, "--", 2) == 0) {
    238       fprintf(stderr,
    239               "Warning: unknown flag %s.\nTry --help for options\n", str);
    240     } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
    241       // Execute argument given to -e option directly.
    242       v8::Handle<v8::String> file_name = v8::String::New("unnamed");
    243       v8::Handle<v8::String> source = v8::String::New(argv[++i]);
    244       if (!ExecuteString(isolate, source, file_name, false, true)) return 1;
    245     } else {
    246       // Use all other arguments as names of files to load and run.
    247       v8::Handle<v8::String> file_name = v8::String::New(str);
    248       v8::Handle<v8::String> source = ReadFile(str);
    249       if (source.IsEmpty()) {
    250         fprintf(stderr, "Error reading '%s'\n", str);
    251         continue;
    252       }
    253       if (!ExecuteString(isolate, source, file_name, false, true)) return 1;
    254     }
    255   }
    256   return 0;
    257 }
    258 
    259 
    260 // The read-eval-execute loop of the shell.
    261 void RunShell(v8::Handle<v8::Context> context) {
    262   fprintf(stderr, "V8 version %s [sample shell]\n", v8::V8::GetVersion());
    263   static const int kBufferSize = 256;
    264   // Enter the execution environment before evaluating any code.
    265   v8::Context::Scope context_scope(context);
    266   v8::Local<v8::String> name(v8::String::New("(shell)"));
    267   while (true) {
    268     char buffer[kBufferSize];
    269     fprintf(stderr, "> ");
    270     char* str = fgets(buffer, kBufferSize, stdin);
    271     if (str == NULL) break;
    272     v8::HandleScope handle_scope(context->GetIsolate());
    273     ExecuteString(context->GetIsolate(),
    274                   v8::String::New(str),
    275                   name,
    276                   true,
    277                   true);
    278   }
    279   fprintf(stderr, "\n");
    280 }
    281 
    282 
    283 // Executes a string within the current v8 context.
    284 bool ExecuteString(v8::Isolate* isolate,
    285                    v8::Handle<v8::String> source,
    286                    v8::Handle<v8::Value> name,
    287                    bool print_result,
    288                    bool report_exceptions) {
    289   v8::HandleScope handle_scope(isolate);
    290   v8::TryCatch try_catch;
    291   v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
    292   if (script.IsEmpty()) {
    293     // Print errors that happened during compilation.
    294     if (report_exceptions)
    295       ReportException(isolate, &try_catch);
    296     return false;
    297   } else {
    298     v8::Handle<v8::Value> result = script->Run();
    299     if (result.IsEmpty()) {
    300       assert(try_catch.HasCaught());
    301       // Print errors that happened during execution.
    302       if (report_exceptions)
    303         ReportException(isolate, &try_catch);
    304       return false;
    305     } else {
    306       assert(!try_catch.HasCaught());
    307       if (print_result && !result->IsUndefined()) {
    308         // If all went well and the result wasn't undefined then print
    309         // the returned value.
    310         v8::String::Utf8Value str(result);
    311         const char* cstr = ToCString(str);
    312         printf("%s\n", cstr);
    313       }
    314       return true;
    315     }
    316   }
    317 }
    318 
    319 
    320 void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
    321   v8::HandleScope handle_scope(isolate);
    322   v8::String::Utf8Value exception(try_catch->Exception());
    323   const char* exception_string = ToCString(exception);
    324   v8::Handle<v8::Message> message = try_catch->Message();
    325   if (message.IsEmpty()) {
    326     // V8 didn't provide any extra information about this error; just
    327     // print the exception.
    328     fprintf(stderr, "%s\n", exception_string);
    329   } else {
    330     // Print (filename):(line number): (message).
    331     v8::String::Utf8Value filename(message->GetScriptResourceName());
    332     const char* filename_string = ToCString(filename);
    333     int linenum = message->GetLineNumber();
    334     fprintf(stderr, "%s:%i: %s\n", filename_string, linenum, exception_string);
    335     // Print line of source code.
    336     v8::String::Utf8Value sourceline(message->GetSourceLine());
    337     const char* sourceline_string = ToCString(sourceline);
    338     fprintf(stderr, "%s\n", sourceline_string);
    339     // Print wavy underline (GetUnderline is deprecated).
    340     int start = message->GetStartColumn();
    341     for (int i = 0; i < start; i++) {
    342       fprintf(stderr, " ");
    343     }
    344     int end = message->GetEndColumn();
    345     for (int i = start; i < end; i++) {
    346       fprintf(stderr, "^");
    347     }
    348     fprintf(stderr, "\n");
    349     v8::String::Utf8Value stack_trace(try_catch->StackTrace());
    350     if (stack_trace.length() > 0) {
    351       const char* stack_trace_string = ToCString(stack_trace);
    352       fprintf(stderr, "%s\n", stack_trace_string);
    353     }
    354   }
    355 }
    356