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