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(v8::Isolate* isolate, 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::NewFromUtf8(isolate, "print"),
    104               v8::FunctionTemplate::New(Print));
    105   // Bind the global 'read' function to the C++ Read callback.
    106   global->Set(v8::String::NewFromUtf8(isolate, "read"),
    107               v8::FunctionTemplate::New(Read));
    108   // Bind the global 'load' function to the C++ Load callback.
    109   global->Set(v8::String::NewFromUtf8(isolate, "load"),
    110               v8::FunctionTemplate::New(Load));
    111   // Bind the 'quit' function
    112   global->Set(v8::String::NewFromUtf8(isolate, "quit"),
    113               v8::FunctionTemplate::New(Quit));
    114   // Bind the 'version' function
    115   global->Set(v8::String::NewFromUtf8(isolate, "version"),
    116               v8::FunctionTemplate::New(Version));
    117 
    118   return v8::Context::New(isolate, NULL, global);
    119 }
    120 
    121 
    122 // The callback that is invoked by v8 whenever the JavaScript 'print'
    123 // function is called.  Prints its arguments on stdout separated by
    124 // spaces and ending with a newline.
    125 void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
    126   bool first = true;
    127   for (int i = 0; i < args.Length(); i++) {
    128     v8::HandleScope handle_scope(args.GetIsolate());
    129     if (first) {
    130       first = false;
    131     } else {
    132       printf(" ");
    133     }
    134     v8::String::Utf8Value str(args[i]);
    135     const char* cstr = ToCString(str);
    136     printf("%s", cstr);
    137   }
    138   printf("\n");
    139   fflush(stdout);
    140 }
    141 
    142 
    143 // The callback that is invoked by v8 whenever the JavaScript 'read'
    144 // function is called.  This function loads the content of the file named in
    145 // the argument into a JavaScript string.
    146 void Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
    147   if (args.Length() != 1) {
    148     args.GetIsolate()->ThrowException(
    149         v8::String::NewFromUtf8(args.GetIsolate(), "Bad parameters"));
    150     return;
    151   }
    152   v8::String::Utf8Value file(args[0]);
    153   if (*file == NULL) {
    154     args.GetIsolate()->ThrowException(
    155         v8::String::NewFromUtf8(args.GetIsolate(), "Error loading file"));
    156     return;
    157   }
    158   v8::Handle<v8::String> source = ReadFile(args.GetIsolate(), *file);
    159   if (source.IsEmpty()) {
    160     args.GetIsolate()->ThrowException(
    161         v8::String::NewFromUtf8(args.GetIsolate(), "Error loading file"));
    162     return;
    163   }
    164   args.GetReturnValue().Set(source);
    165 }
    166 
    167 
    168 // The callback that is invoked by v8 whenever the JavaScript 'load'
    169 // function is called.  Loads, compiles and executes its argument
    170 // JavaScript file.
    171 void Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
    172   for (int i = 0; i < args.Length(); i++) {
    173     v8::HandleScope handle_scope(args.GetIsolate());
    174     v8::String::Utf8Value file(args[i]);
    175     if (*file == NULL) {
    176       args.GetIsolate()->ThrowException(
    177           v8::String::NewFromUtf8(args.GetIsolate(), "Error loading file"));
    178       return;
    179     }
    180     v8::Handle<v8::String> source = ReadFile(args.GetIsolate(), *file);
    181     if (source.IsEmpty()) {
    182       args.GetIsolate()->ThrowException(
    183            v8::String::NewFromUtf8(args.GetIsolate(), "Error loading file"));
    184       return;
    185     }
    186     if (!ExecuteString(args.GetIsolate(),
    187                        source,
    188                        v8::String::NewFromUtf8(args.GetIsolate(), *file),
    189                        false,
    190                        false)) {
    191       args.GetIsolate()->ThrowException(
    192           v8::String::NewFromUtf8(args.GetIsolate(), "Error executing file"));
    193       return;
    194     }
    195   }
    196 }
    197 
    198 
    199 // The callback that is invoked by v8 whenever the JavaScript 'quit'
    200 // function is called.  Quits.
    201 void Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
    202   // If not arguments are given args[0] will yield undefined which
    203   // converts to the integer value 0.
    204   int exit_code = args[0]->Int32Value();
    205   fflush(stdout);
    206   fflush(stderr);
    207   exit(exit_code);
    208 }
    209 
    210 
    211 void Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
    212   args.GetReturnValue().Set(
    213       v8::String::NewFromUtf8(args.GetIsolate(), v8::V8::GetVersion()));
    214 }
    215 
    216 
    217 // Reads a file into a v8 string.
    218 v8::Handle<v8::String> ReadFile(v8::Isolate* isolate, const char* name) {
    219   FILE* file = fopen(name, "rb");
    220   if (file == NULL) return v8::Handle<v8::String>();
    221 
    222   fseek(file, 0, SEEK_END);
    223   int size = ftell(file);
    224   rewind(file);
    225 
    226   char* chars = new char[size + 1];
    227   chars[size] = '\0';
    228   for (int i = 0; i < size;) {
    229     int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
    230     i += read;
    231   }
    232   fclose(file);
    233   v8::Handle<v8::String> result =
    234       v8::String::NewFromUtf8(isolate, chars, v8::String::kNormalString, size);
    235   delete[] chars;
    236   return result;
    237 }
    238 
    239 
    240 // Process remaining command line arguments and execute files
    241 int RunMain(v8::Isolate* isolate, int argc, char* argv[]) {
    242   for (int i = 1; i < argc; i++) {
    243     const char* str = argv[i];
    244     if (strcmp(str, "--shell") == 0) {
    245       run_shell = true;
    246     } else if (strcmp(str, "-f") == 0) {
    247       // Ignore any -f flags for compatibility with the other stand-
    248       // alone JavaScript engines.
    249       continue;
    250     } else if (strncmp(str, "--", 2) == 0) {
    251       fprintf(stderr,
    252               "Warning: unknown flag %s.\nTry --help for options\n", str);
    253     } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
    254       // Execute argument given to -e option directly.
    255       v8::Handle<v8::String> file_name =
    256           v8::String::NewFromUtf8(isolate, "unnamed");
    257       v8::Handle<v8::String> source =
    258           v8::String::NewFromUtf8(isolate, argv[++i]);
    259       if (!ExecuteString(isolate, source, file_name, false, true)) return 1;
    260     } else {
    261       // Use all other arguments as names of files to load and run.
    262       v8::Handle<v8::String> file_name = v8::String::NewFromUtf8(isolate, str);
    263       v8::Handle<v8::String> source = ReadFile(isolate, str);
    264       if (source.IsEmpty()) {
    265         fprintf(stderr, "Error reading '%s'\n", str);
    266         continue;
    267       }
    268       if (!ExecuteString(isolate, source, file_name, false, true)) return 1;
    269     }
    270   }
    271   return 0;
    272 }
    273 
    274 
    275 // The read-eval-execute loop of the shell.
    276 void RunShell(v8::Handle<v8::Context> context) {
    277   fprintf(stderr, "V8 version %s [sample shell]\n", v8::V8::GetVersion());
    278   static const int kBufferSize = 256;
    279   // Enter the execution environment before evaluating any code.
    280   v8::Context::Scope context_scope(context);
    281   v8::Local<v8::String> name(
    282       v8::String::NewFromUtf8(context->GetIsolate(), "(shell)"));
    283   while (true) {
    284     char buffer[kBufferSize];
    285     fprintf(stderr, "> ");
    286     char* str = fgets(buffer, kBufferSize, stdin);
    287     if (str == NULL) break;
    288     v8::HandleScope handle_scope(context->GetIsolate());
    289     ExecuteString(context->GetIsolate(),
    290                   v8::String::NewFromUtf8(context->GetIsolate(), str),
    291                   name,
    292                   true,
    293                   true);
    294   }
    295   fprintf(stderr, "\n");
    296 }
    297 
    298 
    299 // Executes a string within the current v8 context.
    300 bool ExecuteString(v8::Isolate* isolate,
    301                    v8::Handle<v8::String> source,
    302                    v8::Handle<v8::Value> name,
    303                    bool print_result,
    304                    bool report_exceptions) {
    305   v8::HandleScope handle_scope(isolate);
    306   v8::TryCatch try_catch;
    307   v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
    308   if (script.IsEmpty()) {
    309     // Print errors that happened during compilation.
    310     if (report_exceptions)
    311       ReportException(isolate, &try_catch);
    312     return false;
    313   } else {
    314     v8::Handle<v8::Value> result = script->Run();
    315     if (result.IsEmpty()) {
    316       assert(try_catch.HasCaught());
    317       // Print errors that happened during execution.
    318       if (report_exceptions)
    319         ReportException(isolate, &try_catch);
    320       return false;
    321     } else {
    322       assert(!try_catch.HasCaught());
    323       if (print_result && !result->IsUndefined()) {
    324         // If all went well and the result wasn't undefined then print
    325         // the returned value.
    326         v8::String::Utf8Value str(result);
    327         const char* cstr = ToCString(str);
    328         printf("%s\n", cstr);
    329       }
    330       return true;
    331     }
    332   }
    333 }
    334 
    335 
    336 void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
    337   v8::HandleScope handle_scope(isolate);
    338   v8::String::Utf8Value exception(try_catch->Exception());
    339   const char* exception_string = ToCString(exception);
    340   v8::Handle<v8::Message> message = try_catch->Message();
    341   if (message.IsEmpty()) {
    342     // V8 didn't provide any extra information about this error; just
    343     // print the exception.
    344     fprintf(stderr, "%s\n", exception_string);
    345   } else {
    346     // Print (filename):(line number): (message).
    347     v8::String::Utf8Value filename(message->GetScriptResourceName());
    348     const char* filename_string = ToCString(filename);
    349     int linenum = message->GetLineNumber();
    350     fprintf(stderr, "%s:%i: %s\n", filename_string, linenum, exception_string);
    351     // Print line of source code.
    352     v8::String::Utf8Value sourceline(message->GetSourceLine());
    353     const char* sourceline_string = ToCString(sourceline);
    354     fprintf(stderr, "%s\n", sourceline_string);
    355     // Print wavy underline (GetUnderline is deprecated).
    356     int start = message->GetStartColumn();
    357     for (int i = 0; i < start; i++) {
    358       fprintf(stderr, " ");
    359     }
    360     int end = message->GetEndColumn();
    361     for (int i = start; i < end; i++) {
    362       fprintf(stderr, "^");
    363     }
    364     fprintf(stderr, "\n");
    365     v8::String::Utf8Value stack_trace(try_catch->StackTrace());
    366     if (stack_trace.length() > 0) {
    367       const char* stack_trace_string = ToCString(stack_trace);
    368       fprintf(stderr, "%s\n", stack_trace_string);
    369     }
    370   }
    371 }
    372