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
55function 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
65while (true) {
66  var line = read_line();
67  if (!line) {
68    break;
69  }
70  var res = line + " | " + line;
71  print(res);
72}
73 */
74
75enum MainCycleType {
76  CycleInCpp,
77  CycleInJs
78};
79
80const char* ToCString(const v8::String::Utf8Value& value);
81void ReportException(v8::Isolate* isolate, v8::TryCatch* handler);
82v8::Handle<v8::String> ReadFile(v8::Isolate* isolate, const char* name);
83v8::Handle<v8::String> ReadLine();
84
85void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
86void ReadLine(const v8::FunctionCallbackInfo<v8::Value>& args);
87bool RunCppCycle(v8::Handle<v8::Script> script,
88                 v8::Local<v8::Context> context,
89                 bool report_exceptions);
90
91
92v8::Persistent<v8::Context> debug_message_context;
93
94int 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
204bool 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
256int 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.
270const 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.
276v8::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
298void 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.
334void 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.
354void 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
364v8::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