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 
RunMain(int argc,char * argv[])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 
RunCppCycle(v8::Handle<v8::Script> script,v8::Local<v8::Context> context,bool report_exceptions)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 
main(int argc,char * argv[])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.
ToCString(const v8::String::Utf8Value & value)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.
ReadFile(v8::Isolate * isolate,const char * name)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 
ReportException(v8::Isolate * isolate,v8::TryCatch * try_catch)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.
Print(const v8::FunctionCallbackInfo<v8::Value> & args)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.
ReadLine(const v8::FunctionCallbackInfo<v8::Value> & args)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 
ReadLine()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