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
50static uint8_t buffer[ JERRY_BUFFER_SIZE ];
51
52static const uint32_t *
53read_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 */
94static void
95print_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 */
267static void
268register_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 */
288static jerry_value_t
289wait_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 */
315typedef 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 */
339static 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 */
385static bool
386check_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 */
402static void
403check_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 */
420static void *
421context_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 */
433static void
434init_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
472int
473main (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