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 
     30 #include <v8-debug.h>
     31 
     32 #include <fcntl.h>
     33 #include <string.h>
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 
     37 /**
     38  * This sample program should demonstrate certain aspects of debugging
     39  * standalone V8-based application.
     40  *
     41  * The program reads input stream, processes it line by line and print
     42  * the result to output. The actual processing is done by custom JavaScript
     43  * script. The script is specified with command line parameters.
     44  *
     45  * The main cycle of the program will sequentially read lines from standard
     46  * input, process them and print to standard output until input closes.
     47  * There are 2 possible configuration in regard to main cycle.
     48  *
     49  * 1. The main cycle is on C++ side. Program should be run with
     50  * --main-cycle-in-cpp option. Script must declare a function named
     51  * "ProcessLine". The main cycle in C++ reads lines and calls this function
     52  * for processing every time. This is a sample script:
     53 
     54 function ProcessLine(input_line) {
     55   return ">>>" + input_line + "<<<";
     56 }
     57 
     58  *
     59  * 2. The main cycle is in JavaScript. Program should be run with
     60  * --main-cycle-in-js option. Script gets run one time at all and gets
     61  * API of 2 global functions: "read_line" and "print". It should read input
     62  * and print converted lines to output itself. This a sample script:
     63 
     64 while (true) {
     65   var line = read_line();
     66   if (!line) {
     67     break;
     68   }
     69   var res = line + " | " + line;
     70   print(res);
     71 }
     72  */
     73 
     74 enum MainCycleType {
     75   CycleInCpp,
     76   CycleInJs
     77 };
     78 
     79 const char* ToCString(const v8::String::Utf8Value& value);
     80 void ReportException(v8::Isolate* isolate, v8::TryCatch* handler);
     81 v8::Handle<v8::String> ReadFile(v8::Isolate* isolate, const char* name);
     82 v8::Handle<v8::String> ReadLine();
     83 
     84 void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
     85 void ReadLine(const v8::FunctionCallbackInfo<v8::Value>& args);
     86 bool RunCppCycle(v8::Handle<v8::Script> script,
     87                  v8::Local<v8::Context> context,
     88                  bool report_exceptions);
     89 
     90 
     91 v8::Persistent<v8::Context> debug_message_context;
     92 
     93 int RunMain(int argc, char* argv[]) {
     94   v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
     95   v8::Isolate* isolate = v8::Isolate::New();
     96   v8::Isolate::Scope isolate_scope(isolate);
     97   v8::HandleScope handle_scope(isolate);
     98 
     99   v8::Handle<v8::String> script_source;
    100   v8::Handle<v8::Value> script_name;
    101   int script_param_counter = 0;
    102 
    103   MainCycleType cycle_type = CycleInCpp;
    104 
    105   for (int i = 1; i < argc; i++) {
    106     const char* str = argv[i];
    107     if (strcmp(str, "-f") == 0) {
    108       // Ignore any -f flags for compatibility with the other stand-
    109       // alone JavaScript engines.
    110       continue;
    111     } else if (strcmp(str, "--main-cycle-in-cpp") == 0) {
    112       cycle_type = CycleInCpp;
    113     } else if (strcmp(str, "--main-cycle-in-js") == 0) {
    114       cycle_type = CycleInJs;
    115     } else if (strncmp(str, "--", 2) == 0) {
    116       printf("Warning: unknown flag %s.\nTry --help for options\n", str);
    117     } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
    118       script_source = v8::String::NewFromUtf8(isolate, argv[i + 1]);
    119       script_name = v8::String::NewFromUtf8(isolate, "unnamed");
    120       i++;
    121       script_param_counter++;
    122     } else {
    123       // Use argument as a name of file to load.
    124       script_source = ReadFile(isolate, str);
    125       script_name = v8::String::NewFromUtf8(isolate, str);
    126       if (script_source.IsEmpty()) {
    127         printf("Error reading '%s'\n", str);
    128         return 1;
    129       }
    130       script_param_counter++;
    131     }
    132   }
    133 
    134   if (script_param_counter == 0) {
    135     printf("Script is not specified\n");
    136     return 1;
    137   }
    138   if (script_param_counter != 1) {
    139     printf("Only one script may be specified\n");
    140     return 1;
    141   }
    142 
    143   // Create a template for the global object.
    144   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
    145 
    146   // Bind the global 'print' function to the C++ Print callback.
    147   global->Set(v8::String::NewFromUtf8(isolate, "print"),
    148               v8::FunctionTemplate::New(isolate, Print));
    149 
    150   if (cycle_type == CycleInJs) {
    151     // Bind the global 'read_line' function to the C++ Print callback.
    152     global->Set(v8::String::NewFromUtf8(isolate, "read_line"),
    153                 v8::FunctionTemplate::New(isolate, ReadLine));
    154   }
    155 
    156   // Create a new execution environment containing the built-in
    157   // functions
    158   v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL, global);
    159   // Enter the newly created execution environment.
    160   v8::Context::Scope context_scope(context);
    161 
    162   debug_message_context.Reset(isolate, context);
    163 
    164   bool report_exceptions = true;
    165 
    166   v8::Handle<v8::Script> script;
    167   {
    168     // Compile script in try/catch context.
    169     v8::TryCatch try_catch;
    170     v8::ScriptOrigin origin(script_name);
    171     script = v8::Script::Compile(script_source, &origin);
    172     if (script.IsEmpty()) {
    173       // Print errors that happened during compilation.
    174       if (report_exceptions)
    175         ReportException(isolate, &try_catch);
    176       return 1;
    177     }
    178   }
    179 
    180   {
    181     v8::TryCatch try_catch;
    182 
    183     script->Run();
    184     if (try_catch.HasCaught()) {
    185       if (report_exceptions)
    186         ReportException(isolate, &try_catch);
    187       return 1;
    188     }
    189   }
    190 
    191   if (cycle_type == CycleInCpp) {
    192     bool res = RunCppCycle(script,
    193                            isolate->GetCurrentContext(),
    194                            report_exceptions);
    195     return !res;
    196   } else {
    197     // All is already done.
    198   }
    199   return 0;
    200 }
    201 
    202 
    203 bool RunCppCycle(v8::Handle<v8::Script> script,
    204                  v8::Local<v8::Context> context,
    205                  bool report_exceptions) {
    206   v8::Isolate* isolate = context->GetIsolate();
    207 
    208   v8::Handle<v8::String> fun_name =
    209       v8::String::NewFromUtf8(isolate, "ProcessLine");
    210   v8::Handle<v8::Value> process_val = context->Global()->Get(fun_name);
    211 
    212   // If there is no Process function, or if it is not a function,
    213   // bail out
    214   if (!process_val->IsFunction()) {
    215     printf("Error: Script does not declare 'ProcessLine' global function.\n");
    216     return 1;
    217   }
    218 
    219   // It is a function; cast it to a Function
    220   v8::Handle<v8::Function> process_fun =
    221       v8::Handle<v8::Function>::Cast(process_val);
    222 
    223 
    224   while (!feof(stdin)) {
    225     v8::HandleScope handle_scope(isolate);
    226 
    227     v8::Handle<v8::String> input_line = ReadLine();
    228     if (input_line == v8::Undefined(isolate)) {
    229       continue;
    230     }
    231 
    232     const int argc = 1;
    233     v8::Handle<v8::Value> argv[argc] = { input_line };
    234 
    235     v8::Handle<v8::Value> result;
    236     {
    237       v8::TryCatch try_catch;
    238       result = process_fun->Call(isolate->GetCurrentContext()->Global(),
    239                                  argc, argv);
    240       if (try_catch.HasCaught()) {
    241         if (report_exceptions)
    242           ReportException(isolate, &try_catch);
    243         return false;
    244       }
    245     }
    246     v8::String::Utf8Value str(result);
    247     const char* cstr = ToCString(str);
    248     printf("%s\n", cstr);
    249   }
    250 
    251   return true;
    252 }
    253 
    254 
    255 int main(int argc, char* argv[]) {
    256   v8::V8::InitializeICU();
    257   int result = RunMain(argc, argv);
    258   v8::V8::Dispose();
    259   return result;
    260 }
    261 
    262 
    263 // Extracts a C string from a V8 Utf8Value.
    264 const char* ToCString(const v8::String::Utf8Value& value) {
    265   return *value ? *value : "<string conversion failed>";
    266 }
    267 
    268 
    269 // Reads a file into a v8 string.
    270 v8::Handle<v8::String> ReadFile(v8::Isolate* isolate, const char* name) {
    271   FILE* file = fopen(name, "rb");
    272   if (file == NULL) return v8::Handle<v8::String>();
    273 
    274   fseek(file, 0, SEEK_END);
    275   int size = ftell(file);
    276   rewind(file);
    277 
    278   char* chars = new char[size + 1];
    279   chars[size] = '\0';
    280   for (int i = 0; i < size;) {
    281     int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
    282     i += read;
    283   }
    284   fclose(file);
    285   v8::Handle<v8::String> result =
    286       v8::String::NewFromUtf8(isolate, chars, v8::String::kNormalString, size);
    287   delete[] chars;
    288   return result;
    289 }
    290 
    291 
    292 void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
    293   v8::HandleScope handle_scope(isolate);
    294   v8::String::Utf8Value exception(try_catch->Exception());
    295   const char* exception_string = ToCString(exception);
    296   v8::Handle<v8::Message> message = try_catch->Message();
    297   if (message.IsEmpty()) {
    298     // V8 didn't provide any extra information about this error; just
    299     // print the exception.
    300     printf("%s\n", exception_string);
    301   } else {
    302     // Print (filename):(line number): (message).
    303     v8::String::Utf8Value filename(message->GetScriptResourceName());
    304     const char* filename_string = ToCString(filename);
    305     int linenum = message->GetLineNumber();
    306     printf("%s:%i: %s\n", filename_string, linenum, exception_string);
    307     // Print line of source code.
    308     v8::String::Utf8Value sourceline(message->GetSourceLine());
    309     const char* sourceline_string = ToCString(sourceline);
    310     printf("%s\n", sourceline_string);
    311     // Print wavy underline (GetUnderline is deprecated).
    312     int start = message->GetStartColumn();
    313     for (int i = 0; i < start; i++) {
    314       printf(" ");
    315     }
    316     int end = message->GetEndColumn();
    317     for (int i = start; i < end; i++) {
    318       printf("^");
    319     }
    320     printf("\n");
    321   }
    322 }
    323 
    324 
    325 // The callback that is invoked by v8 whenever the JavaScript 'print'
    326 // function is called.  Prints its arguments on stdout separated by
    327 // spaces and ending with a newline.
    328 void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
    329   bool first = true;
    330   for (int i = 0; i < args.Length(); i++) {
    331     v8::HandleScope handle_scope(args.GetIsolate());
    332     if (first) {
    333       first = false;
    334     } else {
    335       printf(" ");
    336     }
    337     v8::String::Utf8Value str(args[i]);
    338     const char* cstr = ToCString(str);
    339     printf("%s", cstr);
    340   }
    341   printf("\n");
    342   fflush(stdout);
    343 }
    344 
    345 
    346 // The callback that is invoked by v8 whenever the JavaScript 'read_line'
    347 // function is called. Reads a string from standard input and returns.
    348 void ReadLine(const v8::FunctionCallbackInfo<v8::Value>& args) {
    349   if (args.Length() > 0) {
    350     args.GetIsolate()->ThrowException(
    351         v8::String::NewFromUtf8(args.GetIsolate(), "Unexpected arguments"));
    352     return;
    353   }
    354   args.GetReturnValue().Set(ReadLine());
    355 }
    356 
    357 
    358 v8::Handle<v8::String> ReadLine() {
    359   const int kBufferSize = 1024 + 1;
    360   char buffer[kBufferSize];
    361 
    362   char* res;
    363   {
    364     res = fgets(buffer, kBufferSize, stdin);
    365   }
    366   v8::Isolate* isolate = v8::Isolate::GetCurrent();
    367   if (res == NULL) {
    368     v8::Handle<v8::Primitive> t = v8::Undefined(isolate);
    369     return v8::Handle<v8::String>::Cast(t);
    370   }
    371   // Remove newline char
    372   for (char* pos = buffer; *pos != '\0'; pos++) {
    373     if (*pos == '\n') {
    374       *pos = '\0';
    375       break;
    376     }
    377   }
    378   return v8::String::NewFromUtf8(isolate, buffer);
    379 }
    380