• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright JS Foundation and other contributors, http://js.foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <assert.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include "jerryscript.h"
22 #include "jerryscript-ext/debugger.h"
23 #include "jerryscript-ext/handler.h"
24 #include "jerryscript-port.h"
25 #include "jerryscript-port-default.h"
26 
27 #include "cli.h"
28 
29 /**
30  * Maximum size of source code
31  */
32 #define JERRY_BUFFER_SIZE (1048576)
33 
34 /**
35  * Maximum size of snapshots buffer
36  */
37 #define JERRY_SNAPSHOT_BUFFER_SIZE (JERRY_BUFFER_SIZE / sizeof (uint32_t))
38 
39 /**
40  * Standalone Jerry exit codes
41  */
42 #define JERRY_STANDALONE_EXIT_CODE_OK   (0)
43 #define JERRY_STANDALONE_EXIT_CODE_FAIL (1)
44 
45 /**
46  * Context size of the SYNTAX_ERROR
47  */
48 #define SYNTAX_ERROR_CONTEXT_SIZE 2
49 
50 static uint8_t buffer[ JERRY_BUFFER_SIZE ];
51 
52 static const uint32_t *
read_file(const char * file_name,size_t * out_size_p)53 read_file (const char *file_name,
54            size_t *out_size_p)
55 {
56   if (file_name == NULL)
57   {
58     jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to open file, missing filename\n");
59     return NULL;
60   }
61 
62   FILE *file;
63   if (!strcmp ("-", file_name))
64   {
65     file = stdin;
66   }
67   else
68   {
69     file = fopen (file_name, "rb");
70     if (file == NULL)
71     {
72       jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to open file: %s\n", file_name);
73       return NULL;
74     }
75   }
76 
77   size_t bytes_read = fread (buffer, 1u, sizeof (buffer), file);
78   if (!bytes_read)
79   {
80     jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to read file: %s\n", file_name);
81     fclose (file);
82     return NULL;
83   }
84 
85   fclose (file);
86 
87   *out_size_p = bytes_read;
88   return (const uint32_t *) buffer;
89 } /* read_file */
90 
91 /**
92  * Print error value
93  */
94 static void
print_unhandled_exception(jerry_value_t error_value)95 print_unhandled_exception (jerry_value_t error_value) /**< error value */
96 {
97   assert (!jerry_value_is_error (error_value));
98 
99   jerry_char_t err_str_buf[256];
100 
101   if (jerry_value_is_object (error_value))
102   {
103     jerry_value_t stack_str = jerry_create_string ((const jerry_char_t *) "stack");
104     jerry_value_t backtrace_val = jerry_get_property (error_value, stack_str);
105     jerry_release_value (stack_str);
106 
107     if (!jerry_value_is_error (backtrace_val)
108         && jerry_value_is_array (backtrace_val))
109     {
110       printf ("Exception backtrace:\n");
111 
112       uint32_t length = jerry_get_array_length (backtrace_val);
113 
114       /* This length should be enough. */
115       if (length > 32)
116       {
117         length = 32;
118       }
119 
120       for (uint32_t i = 0; i < length; i++)
121       {
122         jerry_value_t item_val = jerry_get_property_by_index (backtrace_val, i);
123 
124         if (!jerry_value_is_error (item_val)
125             && jerry_value_is_string (item_val))
126         {
127           jerry_size_t str_size = jerry_get_utf8_string_size (item_val);
128 
129           if (str_size >= 256)
130           {
131             printf ("%3u: [Backtrace string too long]\n", i);
132           }
133           else
134           {
135             jerry_size_t string_end = jerry_string_to_utf8_char_buffer (item_val, err_str_buf, str_size);
136             assert (string_end == str_size);
137             err_str_buf[string_end] = 0;
138 
139             printf ("%3u: %s\n", i, err_str_buf);
140           }
141         }
142 
143         jerry_release_value (item_val);
144       }
145     }
146     jerry_release_value (backtrace_val);
147   }
148 
149   jerry_value_t err_str_val = jerry_value_to_string (error_value);
150   jerry_size_t err_str_size = jerry_get_utf8_string_size (err_str_val);
151 
152   if (err_str_size >= 256)
153   {
154     const char msg[] = "[Error message too long]";
155     err_str_size = sizeof (msg) / sizeof (char) - 1;
156     memcpy (err_str_buf, msg, err_str_size + 1);
157   }
158   else
159   {
160     jerry_size_t string_end = jerry_string_to_utf8_char_buffer (err_str_val, err_str_buf, err_str_size);
161     assert (string_end == err_str_size);
162     err_str_buf[string_end] = 0;
163 
164     if (jerry_is_feature_enabled (JERRY_FEATURE_ERROR_MESSAGES)
165         && jerry_get_error_type (error_value) == JERRY_ERROR_SYNTAX)
166     {
167       jerry_char_t *string_end_p = err_str_buf + string_end;
168       unsigned int err_line = 0;
169       unsigned int err_col = 0;
170       char *path_str_p = NULL;
171       char *path_str_end_p = NULL;
172 
173       /* 1. parse column and line information */
174       for (jerry_char_t *current_p = err_str_buf; current_p < string_end_p; current_p++)
175       {
176         if (*current_p == '[')
177         {
178           current_p++;
179 
180           if (*current_p == '<')
181           {
182             break;
183           }
184 
185           path_str_p = (char *) current_p;
186           while (current_p < string_end_p && *current_p != ':')
187           {
188             current_p++;
189           }
190 
191           path_str_end_p = (char *) current_p++;
192 
193           err_line = (unsigned int) strtol ((char *) current_p, (char **) &current_p, 10);
194 
195           current_p++;
196 
197           err_col = (unsigned int) strtol ((char *) current_p, NULL, 10);
198           break;
199         }
200       } /* for */
201 
202       if (err_line != 0 && err_col != 0)
203       {
204         unsigned int curr_line = 1;
205 
206         bool is_printing_context = false;
207         unsigned int pos = 0;
208 
209         size_t source_size;
210 
211         /* Temporarily modify the error message, so we can use the path. */
212         *path_str_end_p = '\0';
213 
214         read_file (path_str_p, &source_size);
215 
216         /* Revert the error message. */
217         *path_str_end_p = ':';
218 
219         /* 2. seek and print */
220         while ((pos < source_size) && (buffer[pos] != '\0'))
221         {
222           if (buffer[pos] == '\n')
223           {
224             curr_line++;
225           }
226 
227           if (err_line < SYNTAX_ERROR_CONTEXT_SIZE
228               || (err_line >= curr_line
229                   && (err_line - curr_line) <= SYNTAX_ERROR_CONTEXT_SIZE))
230           {
231             /* context must be printed */
232             is_printing_context = true;
233           }
234 
235           if (curr_line > err_line)
236           {
237             break;
238           }
239 
240           if (is_printing_context)
241           {
242             jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%c", buffer[pos]);
243           }
244 
245           pos++;
246         }
247 
248         jerry_port_log (JERRY_LOG_LEVEL_ERROR, "\n");
249 
250         while (--err_col)
251         {
252           jerry_port_log (JERRY_LOG_LEVEL_ERROR, "~");
253         }
254 
255         jerry_port_log (JERRY_LOG_LEVEL_ERROR, "^\n");
256       }
257     }
258   }
259 
260   jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Script Error: %s\n", err_str_buf);
261   jerry_release_value (err_str_val);
262 } /* print_unhandled_exception */
263 
264 /**
265  * Register a JavaScript function in the global object.
266  */
267 static void
register_js_function(const char * name_p,jerry_external_handler_t handler_p)268 register_js_function (const char *name_p, /**< name of the function */
269                       jerry_external_handler_t handler_p) /**< function callback */
270 {
271   jerry_value_t result_val = jerryx_handler_register_global ((const jerry_char_t *) name_p, handler_p);
272 
273   if (jerry_value_is_error (result_val))
274   {
275     jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Warning: failed to register '%s' method.", name_p);
276     result_val = jerry_get_value_from_error (result_val, true);
277     print_unhandled_exception (result_val);
278   }
279 
280   jerry_release_value (result_val);
281 } /* register_js_function */
282 
283 /**
284  * Runs the source code received by jerry_debugger_wait_for_client_source.
285  *
286  * @return result fo the source code execution
287  */
288 static jerry_value_t
wait_for_source_callback(const jerry_char_t * resource_name_p,size_t resource_name_size,const jerry_char_t * source_p,size_t source_size,void * user_p)289 wait_for_source_callback (const jerry_char_t *resource_name_p, /**< resource name */
290                           size_t resource_name_size, /**< size of resource name */
291                           const jerry_char_t *source_p, /**< source code */
292                           size_t source_size, /**< source code size */
293                           void *user_p) /**< user pointer */
294 {
295   (void) user_p; /* unused */
296   jerry_value_t ret_val = jerry_parse (resource_name_p,
297                                        resource_name_size,
298                                        source_p,
299                                        source_size,
300                                        JERRY_PARSE_NO_OPTS);
301 
302   if (!jerry_value_is_error (ret_val))
303   {
304     jerry_value_t func_val = ret_val;
305     ret_val = jerry_run (func_val);
306     jerry_release_value (func_val);
307   }
308 
309   return ret_val;
310 } /* wait_for_source_callback */
311 
312 /**
313  * Command line option IDs
314  */
315 typedef enum
316 {
317   OPT_HELP,
318   OPT_VERSION,
319   OPT_MEM_STATS,
320   OPT_PARSE_ONLY,
321   OPT_SHOW_OP,
322   OPT_SHOW_RE_OP,
323   OPT_DEBUG_SERVER,
324   OPT_DEBUG_PORT,
325   OPT_DEBUG_CHANNEL,
326   OPT_DEBUG_PROTOCOL,
327   OPT_DEBUG_SERIAL_CONFIG,
328   OPT_DEBUGGER_WAIT_SOURCE,
329   OPT_EXEC_SNAP,
330   OPT_EXEC_SNAP_FUNC,
331   OPT_LOG_LEVEL,
332   OPT_NO_PROMPT,
333   OPT_CALL_ON_EXIT
334 } main_opt_id_t;
335 
336 /**
337  * Command line options
338  */
339 static const cli_opt_t main_opts[] =
340 {
341   CLI_OPT_DEF (.id = OPT_HELP, .opt = "h", .longopt = "help",
342                .help = "print this help and exit"),
343   CLI_OPT_DEF (.id = OPT_VERSION, .opt = "v", .longopt = "version",
344                .help = "print tool and library version and exit"),
345   CLI_OPT_DEF (.id = OPT_MEM_STATS, .longopt = "mem-stats",
346                .help = "dump memory statistics"),
347   CLI_OPT_DEF (.id = OPT_PARSE_ONLY, .longopt = "parse-only",
348                .help = "don't execute JS input"),
349   CLI_OPT_DEF (.id = OPT_SHOW_OP, .longopt = "show-opcodes",
350                .help = "dump parser byte-code"),
351   CLI_OPT_DEF (.id = OPT_SHOW_RE_OP, .longopt = "show-regexp-opcodes",
352                .help = "dump regexp byte-code"),
353   CLI_OPT_DEF (.id = OPT_DEBUG_SERVER, .longopt = "start-debug-server",
354                .help = "start debug server and wait for a connecting client"),
355   CLI_OPT_DEF (.id = OPT_DEBUG_PORT, .longopt = "debug-port", .meta = "NUM",
356                .help = "debug server port (default: 5001)"),
357   CLI_OPT_DEF (.id = OPT_DEBUG_CHANNEL, .longopt = "debug-channel", .meta = "[websocket|rawpacket]",
358                .help = "Specify the debugger transmission channel (default: websocket)"),
359   CLI_OPT_DEF (.id = OPT_DEBUG_PROTOCOL, .longopt = "debug-protocol", .meta = "PROTOCOL",
360                .help = "Specify the transmission protocol over the communication channel (tcp|serial, default: tcp)"),
361   CLI_OPT_DEF (.id = OPT_DEBUG_SERIAL_CONFIG, .longopt = "serial-config", .meta = "OPTIONS_STRING",
362                .help = "Configure parameters for serial port (default: /dev/ttyS0,115200,8,N,1)"),
363   CLI_OPT_DEF (.id = OPT_DEBUGGER_WAIT_SOURCE, .longopt = "debugger-wait-source",
364                .help = "wait for an executable source from the client"),
365   CLI_OPT_DEF (.id = OPT_EXEC_SNAP, .longopt = "exec-snapshot", .meta = "FILE",
366                .help = "execute input snapshot file(s)"),
367   CLI_OPT_DEF (.id = OPT_EXEC_SNAP_FUNC, .longopt = "exec-snapshot-func", .meta = "FILE NUM",
368                .help = "execute specific function from input snapshot file(s)"),
369   CLI_OPT_DEF (.id = OPT_LOG_LEVEL, .longopt = "log-level", .meta = "NUM",
370                .help = "set log level (0-3)"),
371   CLI_OPT_DEF (.id = OPT_NO_PROMPT, .longopt = "no-prompt",
372                .help = "don't print prompt in REPL mode"),
373   CLI_OPT_DEF (.id = OPT_CALL_ON_EXIT, .longopt = "call-on-exit", .meta = "STRING",
374                .help = "invoke the specified function when the process is just about to exit"),
375   CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE",
376                .help = "input JS file(s) (If file is -, read standard input.)")
377 };
378 
379 /**
380  * Check whether JerryScript has a requested feature enabled or not. If not,
381  * print a warning message.
382  *
383  * @return the status of the feature.
384  */
385 static bool
check_feature(jerry_feature_t feature,const char * option)386 check_feature (jerry_feature_t feature, /**< feature to check */
387                const char *option) /**< command line option that triggered this check */
388 {
389   if (!jerry_is_feature_enabled (feature))
390   {
391     jerry_port_default_set_log_level (JERRY_LOG_LEVEL_WARNING);
392     jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Ignoring '%s' option because this feature is disabled!\n", option);
393     return false;
394   }
395   return true;
396 } /* check_feature */
397 
398 /**
399  * Check whether a usage-related condition holds. If not, print an error
400  * message, print the usage, and terminate the application.
401  */
402 static void
check_usage(bool condition,const char * name,const char * msg,const char * opt)403 check_usage (bool condition, /**< the condition that must hold */
404              const char *name, /**< name of the application (argv[0]) */
405              const char *msg, /**< error message to print if condition does not hold */
406              const char *opt) /**< optional part of the error message */
407 {
408   if (!condition)
409   {
410     jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%s: %s%s\n", name, msg, opt != NULL ? opt : "");
411     exit (JERRY_STANDALONE_EXIT_CODE_FAIL);
412   }
413 } /* check_usage */
414 
415 #if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1)
416 
417 /**
418  * The alloc function passed to jerry_create_context
419  */
420 static void *
context_alloc(size_t size,void * cb_data_p)421 context_alloc (size_t size,
422                void *cb_data_p)
423 {
424   (void) cb_data_p; /* unused */
425   return malloc (size);
426 } /* context_alloc */
427 
428 #endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */
429 
430 /**
431  * Inits the engine and the debugger
432  */
433 static void
init_engine(jerry_init_flag_t flags,char * debug_channel,char * debug_protocol,uint16_t debug_port,char * debug_serial_config)434 init_engine (jerry_init_flag_t flags, /**< initialized flags for the engine */
435              char *debug_channel, /**< enable the debugger init or not */
436              char *debug_protocol, /**< enable the debugger init or not */
437              uint16_t debug_port, /**< the debugger port for tcp protocol */
438              char *debug_serial_config) /**< configuration string for serial protocol */
439 {
440   jerry_init (flags);
441   if (strcmp (debug_channel, ""))
442   {
443     bool protocol = false;
444 
445     if (!strcmp (debug_protocol, "tcp"))
446     {
447       protocol = jerryx_debugger_tcp_create (debug_port);
448     }
449     else
450     {
451       assert (!strcmp (debug_protocol, "serial"));
452       protocol = jerryx_debugger_serial_create (debug_serial_config);
453     }
454 
455     if (!strcmp (debug_channel, "rawpacket"))
456     {
457       jerryx_debugger_after_connect (protocol && jerryx_debugger_rp_create ());
458     }
459     else
460     {
461       assert (!strcmp (debug_channel, "websocket"));
462       jerryx_debugger_after_connect (protocol && jerryx_debugger_ws_create ());
463     }
464   }
465 
466   register_js_function ("assert", jerryx_handler_assert);
467   register_js_function ("gc", jerryx_handler_gc);
468   register_js_function ("print", jerryx_handler_print);
469   register_js_function ("resourceName", jerryx_handler_resource_name);
470 } /* init_engine */
471 
472 int
main(int argc,char ** argv)473 main (int argc,
474       char **argv)
475 {
476   union
477   {
478     double d;
479     unsigned u;
480   } now = { .d = jerry_port_get_current_time () };
481   srand (now.u);
482   JERRY_VLA (const char *, file_names, argc);
483   int files_counter = 0;
484 
485   jerry_init_flag_t flags = JERRY_INIT_EMPTY;
486 
487   JERRY_VLA (const char *, exec_snapshot_file_names, argc);
488   JERRY_VLA (uint32_t, exec_snapshot_file_indices, argc);
489   int exec_snapshots_count = 0;
490 
491   bool is_parse_only = false;
492 
493   bool start_debug_server = false;
494   uint16_t debug_port = 5001;
495   char *debug_channel = "websocket";
496   char *debug_protocol = "tcp";
497   char *debug_serial_config = "/dev/ttyS0,115200,8,N,1";
498 
499   bool is_repl_mode = false;
500   bool is_wait_mode = false;
501   bool no_prompt = false;
502 
503   const char *exit_cb = NULL;
504 
505   cli_state_t cli_state = cli_init (main_opts, argc - 1, argv + 1);
506   for (int id = cli_consume_option (&cli_state); id != CLI_OPT_END; id = cli_consume_option (&cli_state))
507   {
508     switch (id)
509     {
510       case OPT_HELP:
511       {
512         cli_help (argv[0], NULL, main_opts);
513         return JERRY_STANDALONE_EXIT_CODE_OK;
514       }
515       case OPT_VERSION:
516       {
517         printf ("Version: %d.%d.%d%s\n",
518                 JERRY_API_MAJOR_VERSION,
519                 JERRY_API_MINOR_VERSION,
520                 JERRY_API_PATCH_VERSION,
521                 JERRY_COMMIT_HASH);
522         return JERRY_STANDALONE_EXIT_CODE_OK;
523       }
524       case OPT_MEM_STATS:
525       {
526         if (check_feature (JERRY_FEATURE_MEM_STATS, cli_state.arg))
527         {
528           jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG);
529           flags |= JERRY_INIT_MEM_STATS;
530         }
531         break;
532       }
533       case OPT_PARSE_ONLY:
534       {
535         is_parse_only = true;
536         break;
537       }
538       case OPT_SHOW_OP:
539       {
540         if (check_feature (JERRY_FEATURE_PARSER_DUMP, cli_state.arg))
541         {
542           jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG);
543           flags |= JERRY_INIT_SHOW_OPCODES;
544         }
545         break;
546       }
547       case OPT_CALL_ON_EXIT:
548       {
549         exit_cb = cli_consume_string (&cli_state);
550         break;
551       }
552       case OPT_SHOW_RE_OP:
553       {
554         if (check_feature (JERRY_FEATURE_REGEXP_DUMP, cli_state.arg))
555         {
556           jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG);
557           flags |= JERRY_INIT_SHOW_REGEXP_OPCODES;
558         }
559         break;
560       }
561       case OPT_DEBUG_SERVER:
562       {
563         if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
564         {
565           start_debug_server = true;
566         }
567         break;
568       }
569       case OPT_DEBUG_PORT:
570       {
571         if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
572         {
573           debug_port = (uint16_t) cli_consume_int (&cli_state);
574         }
575         break;
576       }
577       case OPT_DEBUG_CHANNEL:
578       {
579         if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
580         {
581           debug_channel = (char *) cli_consume_string (&cli_state);
582           check_usage (!strcmp (debug_channel, "websocket") || !strcmp (debug_channel, "rawpacket"),
583                        argv[0], "Error: invalid value for --debug-channel: ", cli_state.arg);
584         }
585         break;
586       }
587       case OPT_DEBUG_PROTOCOL:
588       {
589         if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
590         {
591           debug_protocol = (char *) cli_consume_string (&cli_state);
592           check_usage (!strcmp (debug_protocol, "tcp") || !strcmp (debug_protocol, "serial"),
593                        argv[0], "Error: invalid value for --debug-protocol: ", cli_state.arg);
594         }
595         break;
596       }
597       case OPT_DEBUG_SERIAL_CONFIG:
598       {
599         if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
600         {
601           debug_serial_config = (char *) cli_consume_string (&cli_state);
602         }
603         break;
604       }
605       case OPT_DEBUGGER_WAIT_SOURCE:
606       {
607         if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
608         {
609           is_wait_mode = true;
610         }
611         break;
612       }
613       case OPT_EXEC_SNAP:
614       {
615         if (check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg))
616         {
617           exec_snapshot_file_names[exec_snapshots_count] = cli_consume_string (&cli_state);
618           exec_snapshot_file_indices[exec_snapshots_count++] = 0;
619         }
620         else
621         {
622           cli_consume_string (&cli_state);
623         }
624         break;
625       }
626       case OPT_EXEC_SNAP_FUNC:
627       {
628         if (check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg))
629         {
630           exec_snapshot_file_names[exec_snapshots_count] = cli_consume_string (&cli_state);
631           exec_snapshot_file_indices[exec_snapshots_count++] = (uint32_t) cli_consume_int (&cli_state);
632         }
633         else
634         {
635           cli_consume_string (&cli_state);
636         }
637         break;
638       }
639       case OPT_LOG_LEVEL:
640       {
641         long int log_level = cli_consume_int (&cli_state);
642         check_usage (log_level >= 0 && log_level <= 3,
643                      argv[0], "Error: invalid value for --log-level: ", cli_state.arg);
644 
645         jerry_port_default_set_log_level ((jerry_log_level_t) log_level);
646         break;
647       }
648       case OPT_NO_PROMPT:
649       {
650         no_prompt = true;
651         break;
652       }
653       case CLI_OPT_DEFAULT:
654       {
655         file_names[files_counter++] = cli_consume_string (&cli_state);
656         break;
657       }
658       default:
659       {
660         cli_state.error = "Internal error";
661         break;
662       }
663     }
664   }
665 
666   if (cli_state.error != NULL)
667   {
668     if (cli_state.arg != NULL)
669     {
670       jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s %s\n", cli_state.error, cli_state.arg);
671     }
672     else
673     {
674       jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", cli_state.error);
675     }
676 
677     return JERRY_STANDALONE_EXIT_CODE_FAIL;
678   }
679 
680   if (files_counter == 0
681       && exec_snapshots_count == 0)
682   {
683     is_repl_mode = true;
684   }
685 
686 #if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1)
687 
688   jerry_context_t *context_p = jerry_create_context (JERRY_GLOBAL_HEAP_SIZE * 1024, context_alloc, NULL);
689   jerry_port_default_set_current_context (context_p);
690 
691 #endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */
692 
693   if (!start_debug_server)
694   {
695     debug_channel = "";
696   }
697 
698   init_engine (flags, debug_channel, debug_protocol, debug_port, debug_serial_config);
699 
700   jerry_value_t ret_value = jerry_create_undefined ();
701 
702   if (jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_EXEC))
703   {
704     for (int i = 0; i < exec_snapshots_count; i++)
705     {
706       size_t snapshot_size;
707       const uint32_t *snapshot_p = read_file (exec_snapshot_file_names[i], &snapshot_size);
708 
709       if (snapshot_p == NULL)
710       {
711         ret_value = jerry_create_error (JERRY_ERROR_COMMON, (jerry_char_t *) "Snapshot file load error");
712       }
713       else
714       {
715         ret_value = jerry_exec_snapshot (snapshot_p,
716                                          snapshot_size,
717                                          exec_snapshot_file_indices[i],
718                                          JERRY_SNAPSHOT_EXEC_COPY_DATA);
719       }
720 
721       if (jerry_value_is_error (ret_value))
722       {
723         break;
724       }
725     }
726   }
727 
728   while (true)
729   {
730 
731     if (!jerry_value_is_error (ret_value))
732     {
733       for (int i = 0; i < files_counter; i++)
734       {
735         size_t source_size;
736         const jerry_char_t *source_p = (jerry_char_t *) read_file (file_names[i], &source_size);
737 
738         if (source_p == NULL)
739         {
740           ret_value = jerry_create_error (JERRY_ERROR_COMMON, (jerry_char_t *) "Source file load error");
741           break;
742         }
743 
744         if (!jerry_is_valid_utf8_string (source_p, (jerry_size_t) source_size))
745         {
746           ret_value = jerry_create_error (JERRY_ERROR_COMMON, (jerry_char_t *) ("Input must be a valid UTF-8 string."));
747           break;
748         }
749 
750         ret_value = jerry_parse ((jerry_char_t *) file_names[i],
751                                  strlen (file_names[i]),
752                                  source_p,
753                                  source_size,
754                                  JERRY_PARSE_NO_OPTS);
755 
756         if (!jerry_value_is_error (ret_value) && !is_parse_only)
757         {
758           jerry_value_t func_val = ret_value;
759           ret_value = jerry_run (func_val);
760           jerry_release_value (func_val);
761         }
762 
763         if (jerry_value_is_error (ret_value))
764         {
765           break;
766         }
767 
768         jerry_release_value (ret_value);
769         ret_value = jerry_create_undefined ();
770       }
771     }
772 
773     if (is_wait_mode)
774     {
775       is_repl_mode = false;
776 
777       if (jerry_is_feature_enabled (JERRY_FEATURE_DEBUGGER))
778       {
779         while (true)
780         {
781           jerry_debugger_wait_for_source_status_t receive_status;
782 
783           do
784           {
785             jerry_value_t run_result;
786 
787             receive_status = jerry_debugger_wait_for_client_source (wait_for_source_callback,
788                                                                     NULL,
789                                                                     &run_result);
790 
791             if (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED)
792             {
793               ret_value = jerry_create_error (JERRY_ERROR_COMMON,
794                                               (jerry_char_t *) "Connection aborted before source arrived.");
795             }
796 
797             if (receive_status == JERRY_DEBUGGER_SOURCE_END)
798             {
799               jerry_port_log (JERRY_LOG_LEVEL_DEBUG, "No more client source.\n");
800             }
801 
802             if (jerry_value_is_abort (run_result))
803             {
804               ret_value = jerry_acquire_value (run_result);
805             }
806 
807             jerry_release_value (run_result);
808           }
809           while (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVED);
810 
811           if (receive_status != JERRY_DEBUGGER_CONTEXT_RESET_RECEIVED)
812           {
813             break;
814           }
815 
816           init_engine (flags, debug_channel, debug_protocol, debug_port, debug_serial_config);
817 
818           ret_value = jerry_create_undefined ();
819         }
820       }
821 
822     }
823 
824     bool restart = false;
825 
826     if (jerry_is_feature_enabled (JERRY_FEATURE_DEBUGGER) && jerry_value_is_abort (ret_value))
827     {
828       jerry_value_t abort_value = jerry_get_value_from_error (ret_value, false);
829       if (jerry_value_is_string (abort_value))
830       {
831         static const char restart_str[] = "r353t";
832 
833         jerry_value_t str_val = jerry_value_to_string (abort_value);
834         jerry_size_t str_size = jerry_get_string_size (str_val);
835 
836         if (str_size == sizeof (restart_str) - 1)
837         {
838           JERRY_VLA (jerry_char_t, str_buf, str_size);
839           jerry_string_to_char_buffer (str_val, str_buf, str_size);
840           if (memcmp (restart_str, (char *) (str_buf), str_size) == 0)
841           {
842             jerry_release_value (ret_value);
843             restart = true;
844           }
845         }
846 
847         jerry_release_value (str_val);
848       }
849 
850       jerry_release_value (abort_value);
851     }
852 
853     if (!restart)
854     {
855       break;
856     }
857 
858     jerry_cleanup ();
859 
860     init_engine (flags, debug_channel, debug_protocol, debug_port, debug_serial_config);
861 
862     ret_value = jerry_create_undefined ();
863   }
864 
865   if (is_repl_mode)
866   {
867     const char *prompt = !no_prompt ? "jerry> " : "";
868     bool is_done = false;
869 
870     while (!is_done)
871     {
872       uint8_t *source_buffer_tail = buffer;
873       size_t len = 0;
874 
875       printf ("%s", prompt);
876 
877       /* Read a line */
878       while (true)
879       {
880         if (fread (source_buffer_tail, 1, 1, stdin) != 1)
881         {
882           is_done = true;
883           break;
884         }
885         if (*source_buffer_tail == '\n')
886         {
887           break;
888         }
889         source_buffer_tail ++;
890         len ++;
891       }
892       *source_buffer_tail = 0;
893 
894       if (len > 0)
895       {
896         if (!jerry_is_valid_utf8_string (buffer, (jerry_size_t) len))
897         {
898           jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Input must be a valid UTF-8 string.\n");
899           return JERRY_STANDALONE_EXIT_CODE_FAIL;
900         }
901 
902         /* Evaluate the line */
903         jerry_value_t ret_val = jerry_parse (NULL,
904                                              0,
905                                              buffer,
906                                              len,
907                                              JERRY_PARSE_NO_OPTS);
908 
909         if (!jerry_value_is_error (ret_val))
910         {
911           jerry_value_t func_val = ret_val;
912           ret_val = jerry_run (func_val);
913           jerry_release_value (func_val);
914         }
915 
916         if (!jerry_value_is_error (ret_val))
917         {
918           /* Print return value */
919           const jerry_value_t args[] = { ret_val };
920           jerry_value_t ret_val_print = jerryx_handler_print (jerry_create_undefined (),
921                                                               jerry_create_undefined (),
922                                                               args,
923                                                               1);
924           jerry_release_value (ret_val_print);
925           jerry_release_value (ret_val);
926           ret_val = jerry_run_all_enqueued_jobs ();
927 
928           if (jerry_value_is_error (ret_val))
929           {
930             ret_val = jerry_get_value_from_error (ret_val, true);
931             print_unhandled_exception (ret_val);
932           }
933         }
934         else
935         {
936           ret_val = jerry_get_value_from_error (ret_val, true);
937           print_unhandled_exception (ret_val);
938         }
939 
940         jerry_release_value (ret_val);
941       }
942     }
943   }
944 
945   int ret_code = JERRY_STANDALONE_EXIT_CODE_OK;
946 
947   if (jerry_value_is_error (ret_value))
948   {
949     ret_value = jerry_get_value_from_error (ret_value, true);
950     print_unhandled_exception (ret_value);
951 
952     ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL;
953   }
954 
955   jerry_release_value (ret_value);
956 
957   ret_value = jerry_run_all_enqueued_jobs ();
958 
959   if (jerry_value_is_error (ret_value))
960   {
961     ret_value = jerry_get_value_from_error (ret_value, true);
962     print_unhandled_exception (ret_value);
963     ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL;
964   }
965 
966   jerry_release_value (ret_value);
967 
968   if (exit_cb != NULL)
969   {
970     jerry_value_t global = jerry_get_global_object ();
971     jerry_value_t fn_str = jerry_create_string ((jerry_char_t *) exit_cb);
972     jerry_value_t callback_fn = jerry_get_property (global, fn_str);
973 
974     jerry_release_value (global);
975     jerry_release_value (fn_str);
976 
977     if (jerry_value_is_function (callback_fn))
978     {
979       jerry_value_t ret_val = jerry_call_function (callback_fn, jerry_create_undefined (), NULL, 0);
980 
981       if (jerry_value_is_error (ret_val))
982       {
983         ret_val = jerry_get_value_from_error (ret_val, true);
984         print_unhandled_exception (ret_val);
985         ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL;
986       }
987 
988       jerry_release_value (ret_val);
989     }
990 
991     jerry_release_value (callback_fn);
992   }
993 
994   jerry_cleanup ();
995 #if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1)
996   free (context_p);
997 #endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */
998   return ret_code;
999 } /* main */
1000