PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/deps/v8/samples/lineprocessor.cc

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