• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2009 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 
29 // This controls whether this sample is compiled with debugger support.
30 // You may trace its usages in source text to see what parts of program
31 // are responsible for debugging support.
32 // Note that V8 itself should be compiled with enabled debugger support
33 // to have it all working.
34 #define SUPPORT_DEBUGGING
35 
36 #include <v8.h>
37 
38 #ifdef SUPPORT_DEBUGGING
39 #include <v8-debug.h>
40 #endif
41 
42 #include <fcntl.h>
43 #include <string.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 
47 /**
48  * This sample program should demonstrate certain aspects of debugging
49  * standalone V8-based application.
50  *
51  * The program reads input stream, processes it line by line and print
52  * the result to output. The actual processing is done by custom JavaScript
53  * script. The script is specified with command line parameters.
54  *
55  * The main cycle of the program will sequentially read lines from standard
56  * input, process them and print to standard output until input closes.
57  * There are 2 possible configuration in regard to main cycle.
58  *
59  * 1. The main cycle is on C++ side. Program should be run with
60  * --main-cycle-in-cpp option. Script must declare a function named
61  * "ProcessLine". The main cycle in C++ reads lines and calls this function
62  * for processing every time. This is a sample script:
63 
64 function ProcessLine(input_line) {
65   return ">>>" + input_line + "<<<";
66 }
67 
68  *
69  * 2. The main cycle is in JavaScript. Program should be run with
70  * --main-cycle-in-js option. Script gets run one time at all and gets
71  * API of 2 global functions: "read_line" and "print". It should read input
72  * and print converted lines to output itself. This a sample script:
73 
74 while (true) {
75   var line = read_line();
76   if (!line) {
77     break;
78   }
79   var res = line + " | " + line;
80   print(res);
81 }
82 
83  *
84  * When run with "-p" argument, the program starts V8 Debugger Agent and
85  * allows remote debugger to attach and debug JavaScript code.
86  *
87  * Interesting aspects:
88  * 1. Wait for remote debugger to attach
89  * Normally the program compiles custom script and immediately runs it.
90  * If programmer needs to debug script from the very beginning, he should
91  * run this sample program with "--wait-for-connection" command line parameter.
92  * This way V8 will suspend on the first statement and wait for
93  * debugger to attach.
94  *
95  * 2. Unresponsive V8
96  * V8 Debugger Agent holds a connection with remote debugger, but it does
97  * respond only when V8 is running some script. In particular, when this program
98  * is waiting for input, all requests from debugger get deferred until V8
99  * is called again. See how "--callback" command-line parameter in this sample
100  * fixes this issue.
101  */
102 
103 enum MainCycleType {
104   CycleInCpp,
105   CycleInJs
106 };
107 
108 const char* ToCString(const v8::String::Utf8Value& value);
109 void ReportException(v8::TryCatch* handler);
110 v8::Handle<v8::String> ReadFile(const char* name);
111 v8::Handle<v8::String> ReadLine();
112 
113 v8::Handle<v8::Value> Print(const v8::Arguments& args);
114 v8::Handle<v8::Value> ReadLine(const v8::Arguments& args);
115 bool RunCppCycle(v8::Handle<v8::Script> script, v8::Local<v8::Context> context,
116                  bool report_exceptions);
117 
118 
119 #ifdef SUPPORT_DEBUGGING
120 v8::Persistent<v8::Context> debug_message_context;
121 
DispatchDebugMessages()122 void DispatchDebugMessages() {
123   // We are in some random thread. We should already have v8::Locker acquired
124   // (we requested this when registered this callback). We was called
125   // because new debug messages arrived; they may have already been processed,
126   // but we shouldn't worry about this.
127   //
128   // All we have to do is to set context and call ProcessDebugMessages.
129   //
130   // We should decide which V8 context to use here. This is important for
131   // "evaluate" command, because it must be executed some context.
132   // In our sample we have only one context, so there is nothing really to
133   // think about.
134   v8::Context::Scope scope(debug_message_context);
135 
136   v8::Debug::ProcessDebugMessages();
137 }
138 #endif
139 
140 
RunMain(int argc,char * argv[])141 int RunMain(int argc, char* argv[]) {
142   v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
143   v8::HandleScope handle_scope;
144 
145   v8::Handle<v8::String> script_source(NULL);
146   v8::Handle<v8::Value> script_name(NULL);
147   int script_param_counter = 0;
148 
149 #ifdef SUPPORT_DEBUGGING
150   int port_number = -1;
151   bool wait_for_connection = false;
152   bool support_callback = false;
153 #endif
154 
155   MainCycleType cycle_type = CycleInCpp;
156 
157   for (int i = 1; i < argc; i++) {
158     const char* str = argv[i];
159     if (strcmp(str, "-f") == 0) {
160       // Ignore any -f flags for compatibility with the other stand-
161       // alone JavaScript engines.
162       continue;
163     } else if (strcmp(str, "--main-cycle-in-cpp") == 0) {
164       cycle_type = CycleInCpp;
165     } else if (strcmp(str, "--main-cycle-in-js") == 0) {
166       cycle_type = CycleInJs;
167 #ifdef SUPPORT_DEBUGGING
168     } else if (strcmp(str, "--callback") == 0) {
169       support_callback = true;
170     } else if (strcmp(str, "--wait-for-connection") == 0) {
171       wait_for_connection = true;
172     } else if (strcmp(str, "-p") == 0 && i + 1 < argc) {
173       port_number = atoi(argv[i + 1]);  // NOLINT
174       i++;
175 #endif
176     } else if (strncmp(str, "--", 2) == 0) {
177       printf("Warning: unknown flag %s.\nTry --help for options\n", str);
178     } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
179       script_source = v8::String::New(argv[i + 1]);
180       script_name = v8::String::New("unnamed");
181       i++;
182       script_param_counter++;
183     } else {
184       // Use argument as a name of file to load.
185       script_source = ReadFile(str);
186       script_name = v8::String::New(str);
187       if (script_source.IsEmpty()) {
188         printf("Error reading '%s'\n", str);
189         return 1;
190       }
191       script_param_counter++;
192     }
193   }
194 
195   if (script_param_counter == 0) {
196     printf("Script is not specified\n");
197     return 1;
198   }
199   if (script_param_counter != 1) {
200     printf("Only one script may be specified\n");
201     return 1;
202   }
203 
204   // Create a template for the global object.
205   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
206 
207   // Bind the global 'print' function to the C++ Print callback.
208   global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
209 
210   if (cycle_type == CycleInJs) {
211     // Bind the global 'read_line' function to the C++ Print callback.
212     global->Set(v8::String::New("read_line"),
213                 v8::FunctionTemplate::New(ReadLine));
214   }
215 
216   // Create a new execution environment containing the built-in
217   // functions
218   v8::Handle<v8::Context> context = v8::Context::New(NULL, global);
219   // Enter the newly created execution environment.
220   v8::Context::Scope context_scope(context);
221 
222 #ifdef SUPPORT_DEBUGGING
223   debug_message_context = v8::Persistent<v8::Context>::New(context);
224 
225   v8::Locker locker;
226 
227   if (support_callback) {
228     v8::Debug::SetDebugMessageDispatchHandler(DispatchDebugMessages, true);
229   }
230 
231   if (port_number != -1) {
232     v8::Debug::EnableAgent("lineprocessor", port_number, wait_for_connection);
233   }
234 #endif
235 
236   bool report_exceptions = true;
237 
238   v8::Handle<v8::Script> script;
239   {
240     // Compile script in try/catch context.
241     v8::TryCatch try_catch;
242     script = v8::Script::Compile(script_source, script_name);
243     if (script.IsEmpty()) {
244       // Print errors that happened during compilation.
245       if (report_exceptions)
246         ReportException(&try_catch);
247       return 1;
248     }
249   }
250 
251   {
252     v8::TryCatch try_catch;
253 
254     script->Run();
255     if (try_catch.HasCaught()) {
256       if (report_exceptions)
257         ReportException(&try_catch);
258       return 1;
259     }
260   }
261 
262   if (cycle_type == CycleInCpp) {
263     bool res = RunCppCycle(script, v8::Context::GetCurrent(),
264                            report_exceptions);
265     return !res;
266   } else {
267     // All is already done.
268   }
269   return 0;
270 }
271 
272 
RunCppCycle(v8::Handle<v8::Script> script,v8::Local<v8::Context> context,bool report_exceptions)273 bool RunCppCycle(v8::Handle<v8::Script> script, v8::Local<v8::Context> context,
274                  bool report_exceptions) {
275 #ifdef SUPPORT_DEBUGGING
276   v8::Locker lock;
277 #endif
278 
279   v8::Handle<v8::String> fun_name = v8::String::New("ProcessLine");
280   v8::Handle<v8::Value> process_val =
281       v8::Context::GetCurrent()->Global()->Get(fun_name);
282 
283   // If there is no Process function, or if it is not a function,
284   // bail out
285   if (!process_val->IsFunction()) {
286     printf("Error: Script does not declare 'ProcessLine' global function.\n");
287     return 1;
288   }
289 
290   // It is a function; cast it to a Function
291   v8::Handle<v8::Function> process_fun =
292       v8::Handle<v8::Function>::Cast(process_val);
293 
294 
295   while (!feof(stdin)) {
296     v8::HandleScope handle_scope;
297 
298     v8::Handle<v8::String> input_line = ReadLine();
299     if (input_line == v8::Undefined()) {
300       continue;
301     }
302 
303     const int argc = 1;
304     v8::Handle<v8::Value> argv[argc] = { input_line };
305 
306     v8::Handle<v8::Value> result;
307     {
308       v8::TryCatch try_catch;
309       result = process_fun->Call(v8::Context::GetCurrent()->Global(),
310                                  argc, argv);
311       if (try_catch.HasCaught()) {
312         if (report_exceptions)
313           ReportException(&try_catch);
314         return false;
315       }
316     }
317     v8::String::Utf8Value str(result);
318     const char* cstr = ToCString(str);
319     printf("%s\n", cstr);
320   }
321 
322   return true;
323 }
324 
main(int argc,char * argv[])325 int main(int argc, char* argv[]) {
326   int result = RunMain(argc, argv);
327   v8::V8::Dispose();
328   return result;
329 }
330 
331 
332 // Extracts a C string from a V8 Utf8Value.
ToCString(const v8::String::Utf8Value & value)333 const char* ToCString(const v8::String::Utf8Value& value) {
334   return *value ? *value : "<string conversion failed>";
335 }
336 
337 
338 // Reads a file into a v8 string.
ReadFile(const char * name)339 v8::Handle<v8::String> ReadFile(const char* name) {
340   FILE* file = fopen(name, "rb");
341   if (file == NULL) return v8::Handle<v8::String>();
342 
343   fseek(file, 0, SEEK_END);
344   int size = ftell(file);
345   rewind(file);
346 
347   char* chars = new char[size + 1];
348   chars[size] = '\0';
349   for (int i = 0; i < size;) {
350     int read = fread(&chars[i], 1, size - i, file);
351     i += read;
352   }
353   fclose(file);
354   v8::Handle<v8::String> result = v8::String::New(chars, size);
355   delete[] chars;
356   return result;
357 }
358 
359 
ReportException(v8::TryCatch * try_catch)360 void ReportException(v8::TryCatch* try_catch) {
361   v8::HandleScope handle_scope;
362   v8::String::Utf8Value exception(try_catch->Exception());
363   const char* exception_string = ToCString(exception);
364   v8::Handle<v8::Message> message = try_catch->Message();
365   if (message.IsEmpty()) {
366     // V8 didn't provide any extra information about this error; just
367     // print the exception.
368     printf("%s\n", exception_string);
369   } else {
370     // Print (filename):(line number): (message).
371     v8::String::Utf8Value filename(message->GetScriptResourceName());
372     const char* filename_string = ToCString(filename);
373     int linenum = message->GetLineNumber();
374     printf("%s:%i: %s\n", filename_string, linenum, exception_string);
375     // Print line of source code.
376     v8::String::Utf8Value sourceline(message->GetSourceLine());
377     const char* sourceline_string = ToCString(sourceline);
378     printf("%s\n", sourceline_string);
379     // Print wavy underline (GetUnderline is deprecated).
380     int start = message->GetStartColumn();
381     for (int i = 0; i < start; i++) {
382       printf(" ");
383     }
384     int end = message->GetEndColumn();
385     for (int i = start; i < end; i++) {
386       printf("^");
387     }
388     printf("\n");
389   }
390 }
391 
392 
393 // The callback that is invoked by v8 whenever the JavaScript 'print'
394 // function is called.  Prints its arguments on stdout separated by
395 // spaces and ending with a newline.
Print(const v8::Arguments & args)396 v8::Handle<v8::Value> Print(const v8::Arguments& args) {
397   bool first = true;
398   for (int i = 0; i < args.Length(); i++) {
399     v8::HandleScope handle_scope;
400     if (first) {
401       first = false;
402     } else {
403       printf(" ");
404     }
405     v8::String::Utf8Value str(args[i]);
406     const char* cstr = ToCString(str);
407     printf("%s", cstr);
408   }
409   printf("\n");
410   fflush(stdout);
411   return v8::Undefined();
412 }
413 
414 
415 // The callback that is invoked by v8 whenever the JavaScript 'read_line'
416 // function is called. Reads a string from standard input and returns.
ReadLine(const v8::Arguments & args)417 v8::Handle<v8::Value> ReadLine(const v8::Arguments& args) {
418   if (args.Length() > 0) {
419     return v8::ThrowException(v8::String::New("Unexpected arguments"));
420   }
421   return ReadLine();
422 }
423 
ReadLine()424 v8::Handle<v8::String> ReadLine() {
425   const int kBufferSize = 1024 + 1;
426   char buffer[kBufferSize];
427 
428   char* res;
429   {
430 #ifdef SUPPORT_DEBUGGING
431     v8::Unlocker unlocker;
432 #endif
433     res = fgets(buffer, kBufferSize, stdin);
434   }
435   if (res == NULL) {
436     v8::Handle<v8::Primitive> t = v8::Undefined();
437     return reinterpret_cast<v8::Handle<v8::String>&>(t);
438   }
439   // remove newline char
440   for (char* pos = buffer; *pos != '\0'; pos++) {
441     if (*pos == '\n') {
442       *pos = '\0';
443       break;
444     }
445   }
446   return v8::String::New(buffer);
447 }
448