diff --git a/jerry-main/CMakeLists.txt b/jerry-main/CMakeLists.txt index 06323cc57..d532ef04b 100644 --- a/jerry-main/CMakeLists.txt +++ b/jerry-main/CMakeLists.txt @@ -64,7 +64,7 @@ endif() # Jerry standalones if(JERRY_CMDLINE) - jerry_create_executable("jerry" "main-unix.c" "cli.c") + jerry_create_executable("jerry" "main-unix.c" "main-utils.c" "main-options.c" "cli.c") target_link_libraries("jerry" jerry-ext jerry-port-default) endif() diff --git a/jerry-main/cli.c b/jerry-main/cli.c index 1f14ed867..cec01e815 100644 --- a/jerry-main/cli.c +++ b/jerry-main/cli.c @@ -70,6 +70,7 @@ cli_init (const cli_opt_t *options_p, /**< array of option definitions, terminat { .error = NULL, .arg = NULL, + .index = 1, .argc = argc, .argv = argv, .opts = options_p @@ -102,17 +103,17 @@ cli_consume_option (cli_state_t *state_p) /**< state of the command line option return CLI_OPT_END; } - if (state_p->argc <= 0) + if (state_p->index >= state_p->argc) { state_p->arg = NULL; return CLI_OPT_END; } - const char *arg = state_p->argv[0]; + const char *arg = state_p->argv[state_p->index]; state_p->arg = arg; - if (arg[0] != '-' || arg[1] == '\0') + if (arg[0] != '-') { return CLI_OPT_DEFAULT; } @@ -125,8 +126,7 @@ cli_consume_option (cli_state_t *state_p) /**< state of the command line option { if (opt->longopt != NULL && strcmp (arg, opt->longopt) == 0) { - state_p->argc--; - state_p->argv++; + state_p->index++; return opt->id; } } @@ -141,8 +141,7 @@ cli_consume_option (cli_state_t *state_p) /**< state of the command line option { if (opt->opt != NULL && strcmp (arg, opt->opt) == 0) { - state_p->argc--; - state_p->argv++; + state_p->index++; return opt->id; } } @@ -167,17 +166,16 @@ cli_consume_string (cli_state_t *state_p) /**< state of the command line option return NULL; } - if (state_p->argc <= 0) + if (state_p->index >= state_p->argc) { state_p->error = "Expected string argument"; state_p->arg = NULL; return NULL; } - state_p->arg = state_p->argv[0]; + state_p->arg = state_p->argv[state_p->index]; - state_p->argc--; - state_p->argv++; + state_p->index++; return state_p->arg; } /* cli_consume_string */ @@ -199,13 +197,13 @@ cli_consume_int (cli_state_t *state_p) /**< state of the command line option pro state_p->error = "Expected integer argument"; - if (state_p->argc <= 0) + if (state_p->index >= state_p->argc) { state_p->arg = NULL; return 0; } - state_p->arg = state_p->argv[0]; + state_p->arg = state_p->argv[state_p->index]; char *endptr; long int value = strtol (state_p->arg, &endptr, 10); @@ -216,11 +214,29 @@ cli_consume_int (cli_state_t *state_p) /**< state of the command line option pro } state_p->error = NULL; - state_p->argc--; - state_p->argv++; + state_p->index++; return (int) value; } /* cli_consume_int */ +/** + * Return next agument as path index. + * + * @return path index + */ +uint32_t +cli_consume_path (cli_state_t *state_p) /**< state of the command line option processor */ +{ + if (state_p->error != NULL) + { + return 0; + } + + uint32_t path_index = (uint32_t) state_p->index; + cli_consume_string (state_p); + + return path_index; +} /* cli_consume_path */ + /* * Print helper functions */ diff --git a/jerry-main/cli.h b/jerry-main/cli.h index f8f3836a2..256aa2b2d 100644 --- a/jerry-main/cli.h +++ b/jerry-main/cli.h @@ -17,6 +17,7 @@ #define CLI_H #include +#include /** * Command line option definition. @@ -52,6 +53,7 @@ typedef struct const char *arg; /**< last processed argument as string */ /* Private fields. */ + int index; /**< current argument index */ int argc; /**< remaining number of arguments */ char **argv; /**< remaining arguments */ const cli_opt_t *opts; /**< options */ @@ -71,6 +73,7 @@ void cli_change_opts (cli_state_t *state_p, const cli_opt_t *options_p); int cli_consume_option (cli_state_t *state_p); const char * cli_consume_string (cli_state_t *state_p); int cli_consume_int (cli_state_t *state_p); +uint32_t cli_consume_path (cli_state_t *state_p); void cli_help (const char *prog_name_p, const char *command_name_p, const cli_opt_t *options_p); #endif /* !CLI_H */ diff --git a/jerry-main/main-options.c b/jerry-main/main-options.c new file mode 100644 index 000000000..f5ffe855b --- /dev/null +++ b/jerry-main/main-options.c @@ -0,0 +1,351 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "jerryscript-port.h" +#include "jerryscript-port-default.h" + +#include "cli.h" +#include "main-utils.h" +#include "main-options.h" + +/** + * Command line option IDs + */ +typedef enum +{ + OPT_HELP, + OPT_VERSION, + OPT_MEM_STATS, + OPT_PARSE_ONLY, + OPT_SHOW_OP, + OPT_SHOW_RE_OP, + OPT_DEBUG_SERVER, + OPT_DEBUG_PORT, + OPT_DEBUG_CHANNEL, + OPT_DEBUG_PROTOCOL, + OPT_DEBUG_SERIAL_CONFIG, + OPT_DEBUGGER_WAIT_SOURCE, + OPT_EXEC_SNAP, + OPT_EXEC_SNAP_FUNC, + OPT_LOG_LEVEL, + OPT_NO_PROMPT, + OPT_CALL_ON_EXIT, + OPT_USE_STDIN, +} main_opt_id_t; + +/** + * Command line options + */ +static const cli_opt_t main_opts[] = +{ + CLI_OPT_DEF (.id = OPT_HELP, .opt = "h", .longopt = "help", + .help = "print this help and exit"), + CLI_OPT_DEF (.id = OPT_VERSION, .opt = "v", .longopt = "version", + .help = "print tool and library version and exit"), + CLI_OPT_DEF (.id = OPT_MEM_STATS, .longopt = "mem-stats", + .help = "dump memory statistics"), + CLI_OPT_DEF (.id = OPT_PARSE_ONLY, .longopt = "parse-only", + .help = "don't execute JS input"), + CLI_OPT_DEF (.id = OPT_SHOW_OP, .longopt = "show-opcodes", + .help = "dump parser byte-code"), + CLI_OPT_DEF (.id = OPT_SHOW_RE_OP, .longopt = "show-regexp-opcodes", + .help = "dump regexp byte-code"), + CLI_OPT_DEF (.id = OPT_DEBUG_SERVER, .longopt = "start-debug-server", + .help = "start debug server and wait for a connecting client"), + CLI_OPT_DEF (.id = OPT_DEBUG_PORT, .longopt = "debug-port", .meta = "NUM", + .help = "debug server port (default: 5001)"), + CLI_OPT_DEF (.id = OPT_DEBUG_CHANNEL, .longopt = "debug-channel", .meta = "[websocket|rawpacket]", + .help = "Specify the debugger transmission channel (default: websocket)"), + CLI_OPT_DEF (.id = OPT_DEBUG_PROTOCOL, .longopt = "debug-protocol", .meta = "PROTOCOL", + .help = "Specify the transmission protocol over the communication channel (tcp|serial, default: tcp)"), + CLI_OPT_DEF (.id = OPT_DEBUG_SERIAL_CONFIG, .longopt = "serial-config", .meta = "OPTIONS_STRING", + .help = "Configure parameters for serial port (default: /dev/ttyS0,115200,8,N,1)"), + CLI_OPT_DEF (.id = OPT_DEBUGGER_WAIT_SOURCE, .longopt = "debugger-wait-source", + .help = "wait for an executable source from the client"), + CLI_OPT_DEF (.id = OPT_EXEC_SNAP, .longopt = "exec-snapshot", .meta = "FILE", + .help = "execute input snapshot file(s)"), + CLI_OPT_DEF (.id = OPT_EXEC_SNAP_FUNC, .longopt = "exec-snapshot-func", .meta = "FILE NUM", + .help = "execute specific function from input snapshot file(s)"), + CLI_OPT_DEF (.id = OPT_LOG_LEVEL, .longopt = "log-level", .meta = "NUM", + .help = "set log level (0-3)"), + CLI_OPT_DEF (.id = OPT_NO_PROMPT, .longopt = "no-prompt", + .help = "don't print prompt in REPL mode"), + CLI_OPT_DEF (.id = OPT_CALL_ON_EXIT, .longopt = "call-on-exit", .meta = "STRING", + .help = "invoke the specified function when the process is just about to exit"), + CLI_OPT_DEF (.id = OPT_USE_STDIN, .opt = "", .help = "read from standard input"), + CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE", + .help = "input JS file(s)") +}; + +/** + * Check whether a usage-related condition holds. If not, print an error + * message, print the usage, and terminate the application. + */ +static void +check_usage (bool condition, /**< the condition that must hold */ + const char *name, /**< name of the application (argv[0]) */ + const char *msg, /**< error message to print if condition does not hold */ + const char *opt) /**< optional part of the error message */ +{ + if (!condition) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%s: %s%s\n", name, msg, opt != NULL ? opt : ""); + exit (JERRY_STANDALONE_EXIT_CODE_FAIL); + } +} /* check_usage */ + +/** + * Check whether JerryScript has a requested feature enabled or not. If not, + * print a warning message. + * + * @return the status of the feature. + */ +static bool +check_feature (jerry_feature_t feature, /**< feature to check */ + const char *option) /**< command line option that triggered this check */ +{ + if (!jerry_is_feature_enabled (feature)) + { + jerry_port_default_set_log_level (JERRY_LOG_LEVEL_WARNING); + jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Ignoring '%s' option because this feature is disabled!\n", option); + return false; + } + return true; +} /* check_feature */ + +/** + * parse input arguments + */ +void +main_parse_args (int argc, /**< argc */ + char **argv, /**< argv */ + main_args_t *arguments_p) /**< [in/out] arguments reference */ +{ + arguments_p->source_count = 0; + + arguments_p->debug_channel = "websocket"; + arguments_p->debug_protocol = "tcp"; + arguments_p->debug_serial_config = "/dev/ttyS0,115200,8,N,1"; + arguments_p->debug_port = 5001; + + arguments_p->exit_cb_name_p = NULL; + arguments_p->init_flags = JERRY_INIT_EMPTY; + arguments_p->option_flags = OPT_FLAG_EMPTY; + + cli_state_t cli_state = cli_init (main_opts, argc, argv); + for (int id = cli_consume_option (&cli_state); id != CLI_OPT_END; id = cli_consume_option (&cli_state)) + { + switch (id) + { + case OPT_HELP: + { + cli_help (argv[0], NULL, main_opts); + exit (JERRY_STANDALONE_EXIT_CODE_OK); + + break; + } + case OPT_VERSION: + { + printf ("Version: %d.%d.%d%s\n", + JERRY_API_MAJOR_VERSION, + JERRY_API_MINOR_VERSION, + JERRY_API_PATCH_VERSION, + JERRY_COMMIT_HASH); + exit (JERRY_STANDALONE_EXIT_CODE_OK); + + break; + } + case OPT_MEM_STATS: + { + if (check_feature (JERRY_FEATURE_MEM_STATS, cli_state.arg)) + { + jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG); + arguments_p->init_flags |= JERRY_INIT_MEM_STATS; + } + break; + } + case OPT_PARSE_ONLY: + { + arguments_p->option_flags |= OPT_FLAG_PARSE_ONLY; + break; + } + case OPT_SHOW_OP: + { + if (check_feature (JERRY_FEATURE_PARSER_DUMP, cli_state.arg)) + { + jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG); + arguments_p->init_flags |= JERRY_INIT_SHOW_OPCODES; + } + break; + } + case OPT_CALL_ON_EXIT: + { + arguments_p->exit_cb_name_p = cli_consume_string (&cli_state); + break; + } + case OPT_SHOW_RE_OP: + { + if (check_feature (JERRY_FEATURE_REGEXP_DUMP, cli_state.arg)) + { + jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG); + arguments_p->init_flags |= JERRY_INIT_SHOW_REGEXP_OPCODES; + } + break; + } + case OPT_DEBUG_SERVER: + { + if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) + { + arguments_p->option_flags |= OPT_FLAG_DEBUG_SERVER; + } + break; + } + case OPT_DEBUG_PORT: + { + if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) + { + arguments_p->debug_port = (uint16_t) cli_consume_int (&cli_state); + } + break; + } + case OPT_DEBUG_CHANNEL: + { + if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) + { + const char *debug_channel = cli_consume_string (&cli_state); + check_usage (!strcmp (debug_channel, "websocket") || !strcmp (debug_channel, "rawpacket"), + argv[0], "Error: invalid value for --debug-channel: ", cli_state.arg); + + arguments_p->debug_channel = debug_channel; + } + break; + } + case OPT_DEBUG_PROTOCOL: + { + if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) + { + const char *debug_protocol = cli_consume_string (&cli_state); + check_usage (!strcmp (debug_protocol, "tcp") || !strcmp (debug_protocol, "serial"), + argv[0], "Error: invalid value for --debug-protocol: ", cli_state.arg); + + arguments_p->debug_protocol = debug_protocol; + } + break; + } + case OPT_DEBUG_SERIAL_CONFIG: + { + if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) + { + arguments_p->debug_serial_config = cli_consume_string (&cli_state); + } + break; + } + case OPT_DEBUGGER_WAIT_SOURCE: + { + if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) + { + arguments_p->option_flags |= OPT_FLAG_WAIT_SOURCE; + } + break; + } + case OPT_EXEC_SNAP: + { + const bool is_enabled = check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg); + const uint32_t path_index = cli_consume_path (&cli_state); + + if (is_enabled) + { + main_source_t *source_p = arguments_p->sources_p + arguments_p->source_count; + arguments_p->source_count++; + + source_p->type = SOURCE_SNAPSHOT; + source_p->path_index = path_index; + source_p->snapshot_index = 0; + } + + break; + } + case OPT_EXEC_SNAP_FUNC: + { + const bool is_enabled = check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg); + const uint32_t path_index = cli_consume_path (&cli_state); + const uint16_t snapshot_index = (uint16_t) cli_consume_int (&cli_state); + + if (is_enabled) + { + main_source_t *source_p = arguments_p->sources_p + arguments_p->source_count; + arguments_p->source_count++; + + source_p->type = SOURCE_SNAPSHOT; + source_p->path_index = path_index; + source_p->snapshot_index = snapshot_index; + } + + break; + } + case OPT_LOG_LEVEL: + { + long int log_level = cli_consume_int (&cli_state); + check_usage (log_level >= 0 && log_level <= 3, + argv[0], "Error: invalid value for --log-level: ", cli_state.arg); + + jerry_port_default_set_log_level ((jerry_log_level_t) log_level); + break; + } + case OPT_NO_PROMPT: + { + arguments_p->option_flags |= OPT_FLAG_NO_PROMPT; + break; + } + case OPT_USE_STDIN: + { + arguments_p->option_flags |= OPT_FLAG_USE_STDIN; + break; + } + case CLI_OPT_DEFAULT: + { + main_source_t *source_p = arguments_p->sources_p + arguments_p->source_count; + arguments_p->source_count++; + + source_p->type = SOURCE_SCRIPT; + source_p->path_index = cli_consume_path (&cli_state); + break; + } + default: + { + cli_state.error = "Internal error"; + break; + } + } + } + + if (cli_state.error != NULL) + { + if (cli_state.arg != NULL) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s %s\n", cli_state.error, cli_state.arg); + } + else + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", cli_state.error); + } + + exit (JERRY_STANDALONE_EXIT_CODE_FAIL); + } +} /* main_parse_args */ diff --git a/jerry-main/main-options.h b/jerry-main/main-options.h new file mode 100644 index 000000000..62a1e12c2 --- /dev/null +++ b/jerry-main/main-options.h @@ -0,0 +1,75 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MAIN_OPTIONS_H +#define MAIN_OPTIONS_H + +#include + +/** + * Argument option flags. + */ +typedef enum +{ + OPT_FLAG_EMPTY = 0, + OPT_FLAG_PARSE_ONLY = (1 << 0), + OPT_FLAG_DEBUG_SERVER = (1 << 1), + OPT_FLAG_WAIT_SOURCE = (1 << 2), + OPT_FLAG_NO_PROMPT = (1 << 3), + OPT_FLAG_USE_STDIN = (1 << 4), +} main_option_flags_t; + +/** + * Source types + */ +typedef enum +{ + SOURCE_SNAPSHOT, + SOURCE_SCRIPT, +} main_source_type_t; + +/** + * Input source file. + */ +typedef struct +{ + uint32_t path_index; + uint16_t snapshot_index; + uint16_t type; +} main_source_t; + +/** + * Arguments struct to store parsed command line arguments. + */ +typedef struct +{ + main_source_t *sources_p; + uint32_t source_count; + + const char *debug_channel; + const char *debug_protocol; + const char *debug_serial_config; + uint16_t debug_port; + + const char *exit_cb_name_p; + + uint16_t option_flags; + uint16_t init_flags; +} main_args_t; + +void +main_parse_args (int argc, char **argv, main_args_t *arguments_p); + +#endif /* !MAIN_OPTIONS_H */ diff --git a/jerry-main/main-unix-snapshot.c b/jerry-main/main-unix-snapshot.c index 36c3754b0..5e382a7ba 100644 --- a/jerry-main/main-unix-snapshot.c +++ b/jerry-main/main-unix-snapshot.c @@ -803,7 +803,7 @@ int main (int argc, /**< number of arguments */ char **argv) /**< argument list */ { - cli_state_t cli_state = cli_init (main_opts, argc - 1, argv + 1); + cli_state_t cli_state = cli_init (main_opts, argc, argv); for (int id = cli_consume_option (&cli_state); id != CLI_OPT_END; id = cli_consume_option (&cli_state)) { diff --git a/jerry-main/main-unix.c b/jerry-main/main-unix.c index 367f50964..08075ce81 100644 --- a/jerry-main/main-unix.c +++ b/jerry-main/main-unix.c @@ -24,396 +24,15 @@ #include "jerryscript-port.h" #include "jerryscript-port-default.h" -#include "cli.h" +#include "main-utils.h" +#include "main-options.h" /** - * Maximum size of source code + * Temporal buffer size. */ -#define JERRY_BUFFER_SIZE (1048576) - -/** - * Maximum size of snapshots buffer - */ -#define JERRY_SNAPSHOT_BUFFER_SIZE (JERRY_BUFFER_SIZE / sizeof (uint32_t)) - -/** - * Standalone Jerry exit codes - */ -#define JERRY_STANDALONE_EXIT_CODE_OK (0) -#define JERRY_STANDALONE_EXIT_CODE_FAIL (1) - -/** - * Context size of the SYNTAX_ERROR - */ -#define SYNTAX_ERROR_CONTEXT_SIZE 2 - -static uint8_t buffer[ JERRY_BUFFER_SIZE ]; - -static const uint32_t * -read_file (const char *file_name, - size_t *out_size_p) -{ - if (file_name == NULL) - { - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to open file, missing filename\n"); - return NULL; - } - - FILE *file; - if (!strcmp ("-", file_name)) - { - file = stdin; - } - else - { - file = fopen (file_name, "rb"); - if (file == NULL) - { - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to open file: %s\n", file_name); - return NULL; - } - } - - size_t bytes_read = fread (buffer, 1u, sizeof (buffer), file); - if (!bytes_read) - { - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to read file: %s\n", file_name); - fclose (file); - return NULL; - } - - fclose (file); - - *out_size_p = bytes_read; - return (const uint32_t *) buffer; -} /* read_file */ - -/** - * Print error value - */ -static void -print_unhandled_exception (jerry_value_t error_value) /**< error value */ -{ - assert (!jerry_value_is_error (error_value)); - - jerry_char_t err_str_buf[256]; - - if (jerry_value_is_object (error_value)) - { - jerry_value_t stack_str = jerry_create_string ((const jerry_char_t *) "stack"); - jerry_value_t backtrace_val = jerry_get_property (error_value, stack_str); - jerry_release_value (stack_str); - - if (!jerry_value_is_error (backtrace_val) - && jerry_value_is_array (backtrace_val)) - { - printf ("Exception backtrace:\n"); - - uint32_t length = jerry_get_array_length (backtrace_val); - - /* This length should be enough. */ - if (length > 32) - { - length = 32; - } - - for (uint32_t i = 0; i < length; i++) - { - jerry_value_t item_val = jerry_get_property_by_index (backtrace_val, i); - - if (!jerry_value_is_error (item_val) - && jerry_value_is_string (item_val)) - { - jerry_size_t str_size = jerry_get_utf8_string_size (item_val); - - if (str_size >= 256) - { - printf ("%3u: [Backtrace string too long]\n", i); - } - else - { - jerry_size_t string_end = jerry_string_to_utf8_char_buffer (item_val, err_str_buf, str_size); - assert (string_end == str_size); - err_str_buf[string_end] = 0; - - printf ("%3u: %s\n", i, err_str_buf); - } - } - - jerry_release_value (item_val); - } - } - jerry_release_value (backtrace_val); - } - - jerry_value_t err_str_val = jerry_value_to_string (error_value); - jerry_size_t err_str_size = jerry_get_utf8_string_size (err_str_val); - - if (err_str_size >= 256) - { - const char msg[] = "[Error message too long]"; - err_str_size = sizeof (msg) / sizeof (char) - 1; - memcpy (err_str_buf, msg, err_str_size + 1); - } - else - { - jerry_size_t string_end = jerry_string_to_utf8_char_buffer (err_str_val, err_str_buf, err_str_size); - assert (string_end == err_str_size); - err_str_buf[string_end] = 0; - - if (jerry_is_feature_enabled (JERRY_FEATURE_ERROR_MESSAGES) - && jerry_get_error_type (error_value) == JERRY_ERROR_SYNTAX) - { - jerry_char_t *string_end_p = err_str_buf + string_end; - unsigned int err_line = 0; - unsigned int err_col = 0; - char *path_str_p = NULL; - char *path_str_end_p = NULL; - - /* 1. parse column and line information */ - for (jerry_char_t *current_p = err_str_buf; current_p < string_end_p; current_p++) - { - if (*current_p == '[') - { - current_p++; - - if (*current_p == '<') - { - break; - } - - path_str_p = (char *) current_p; - while (current_p < string_end_p && *current_p != ':') - { - current_p++; - } - - path_str_end_p = (char *) current_p++; - - err_line = (unsigned int) strtol ((char *) current_p, (char **) ¤t_p, 10); - - current_p++; - - err_col = (unsigned int) strtol ((char *) current_p, NULL, 10); - break; - } - } /* for */ - - if (err_line != 0 && err_col != 0) - { - unsigned int curr_line = 1; - - bool is_printing_context = false; - unsigned int pos = 0; - - size_t source_size; - - /* Temporarily modify the error message, so we can use the path. */ - *path_str_end_p = '\0'; - - read_file (path_str_p, &source_size); - - /* Revert the error message. */ - *path_str_end_p = ':'; - - /* 2. seek and print */ - while ((pos < source_size) && (buffer[pos] != '\0')) - { - if (buffer[pos] == '\n') - { - curr_line++; - } - - if (err_line < SYNTAX_ERROR_CONTEXT_SIZE - || (err_line >= curr_line - && (err_line - curr_line) <= SYNTAX_ERROR_CONTEXT_SIZE)) - { - /* context must be printed */ - is_printing_context = true; - } - - if (curr_line > err_line) - { - break; - } - - if (is_printing_context) - { - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%c", buffer[pos]); - } - - pos++; - } - - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "\n"); - - while (--err_col) - { - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "~"); - } - - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "^\n"); - } - } - } - - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Script Error: %s\n", err_str_buf); - jerry_release_value (err_str_val); -} /* print_unhandled_exception */ - -/** - * Register a JavaScript function in the global object. - */ -static void -register_js_function (const char *name_p, /**< name of the function */ - jerry_external_handler_t handler_p) /**< function callback */ -{ - jerry_value_t result_val = jerryx_handler_register_global ((const jerry_char_t *) name_p, handler_p); - - if (jerry_value_is_error (result_val)) - { - jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Warning: failed to register '%s' method.", name_p); - result_val = jerry_get_value_from_error (result_val, true); - print_unhandled_exception (result_val); - } - - jerry_release_value (result_val); -} /* register_js_function */ - -/** - * Runs the source code received by jerry_debugger_wait_for_client_source. - * - * @return result fo the source code execution - */ -static jerry_value_t -wait_for_source_callback (const jerry_char_t *resource_name_p, /**< resource name */ - size_t resource_name_size, /**< size of resource name */ - const jerry_char_t *source_p, /**< source code */ - size_t source_size, /**< source code size */ - void *user_p) /**< user pointer */ -{ - (void) user_p; /* unused */ - jerry_value_t ret_val = jerry_parse (resource_name_p, - resource_name_size, - source_p, - source_size, - JERRY_PARSE_NO_OPTS); - - if (!jerry_value_is_error (ret_val)) - { - jerry_value_t func_val = ret_val; - ret_val = jerry_run (func_val); - jerry_release_value (func_val); - } - - return ret_val; -} /* wait_for_source_callback */ - -/** - * Command line option IDs - */ -typedef enum -{ - OPT_HELP, - OPT_VERSION, - OPT_MEM_STATS, - OPT_PARSE_ONLY, - OPT_SHOW_OP, - OPT_SHOW_RE_OP, - OPT_DEBUG_SERVER, - OPT_DEBUG_PORT, - OPT_DEBUG_CHANNEL, - OPT_DEBUG_PROTOCOL, - OPT_DEBUG_SERIAL_CONFIG, - OPT_DEBUGGER_WAIT_SOURCE, - OPT_EXEC_SNAP, - OPT_EXEC_SNAP_FUNC, - OPT_LOG_LEVEL, - OPT_NO_PROMPT, - OPT_CALL_ON_EXIT -} main_opt_id_t; - -/** - * Command line options - */ -static const cli_opt_t main_opts[] = -{ - CLI_OPT_DEF (.id = OPT_HELP, .opt = "h", .longopt = "help", - .help = "print this help and exit"), - CLI_OPT_DEF (.id = OPT_VERSION, .opt = "v", .longopt = "version", - .help = "print tool and library version and exit"), - CLI_OPT_DEF (.id = OPT_MEM_STATS, .longopt = "mem-stats", - .help = "dump memory statistics"), - CLI_OPT_DEF (.id = OPT_PARSE_ONLY, .longopt = "parse-only", - .help = "don't execute JS input"), - CLI_OPT_DEF (.id = OPT_SHOW_OP, .longopt = "show-opcodes", - .help = "dump parser byte-code"), - CLI_OPT_DEF (.id = OPT_SHOW_RE_OP, .longopt = "show-regexp-opcodes", - .help = "dump regexp byte-code"), - CLI_OPT_DEF (.id = OPT_DEBUG_SERVER, .longopt = "start-debug-server", - .help = "start debug server and wait for a connecting client"), - CLI_OPT_DEF (.id = OPT_DEBUG_PORT, .longopt = "debug-port", .meta = "NUM", - .help = "debug server port (default: 5001)"), - CLI_OPT_DEF (.id = OPT_DEBUG_CHANNEL, .longopt = "debug-channel", .meta = "[websocket|rawpacket]", - .help = "Specify the debugger transmission channel (default: websocket)"), - CLI_OPT_DEF (.id = OPT_DEBUG_PROTOCOL, .longopt = "debug-protocol", .meta = "PROTOCOL", - .help = "Specify the transmission protocol over the communication channel (tcp|serial, default: tcp)"), - CLI_OPT_DEF (.id = OPT_DEBUG_SERIAL_CONFIG, .longopt = "serial-config", .meta = "OPTIONS_STRING", - .help = "Configure parameters for serial port (default: /dev/ttyS0,115200,8,N,1)"), - CLI_OPT_DEF (.id = OPT_DEBUGGER_WAIT_SOURCE, .longopt = "debugger-wait-source", - .help = "wait for an executable source from the client"), - CLI_OPT_DEF (.id = OPT_EXEC_SNAP, .longopt = "exec-snapshot", .meta = "FILE", - .help = "execute input snapshot file(s)"), - CLI_OPT_DEF (.id = OPT_EXEC_SNAP_FUNC, .longopt = "exec-snapshot-func", .meta = "FILE NUM", - .help = "execute specific function from input snapshot file(s)"), - CLI_OPT_DEF (.id = OPT_LOG_LEVEL, .longopt = "log-level", .meta = "NUM", - .help = "set log level (0-3)"), - CLI_OPT_DEF (.id = OPT_NO_PROMPT, .longopt = "no-prompt", - .help = "don't print prompt in REPL mode"), - CLI_OPT_DEF (.id = OPT_CALL_ON_EXIT, .longopt = "call-on-exit", .meta = "STRING", - .help = "invoke the specified function when the process is just about to exit"), - CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE", - .help = "input JS file(s) (If file is -, read standard input.)") -}; - -/** - * Check whether JerryScript has a requested feature enabled or not. If not, - * print a warning message. - * - * @return the status of the feature. - */ -static bool -check_feature (jerry_feature_t feature, /**< feature to check */ - const char *option) /**< command line option that triggered this check */ -{ - if (!jerry_is_feature_enabled (feature)) - { - jerry_port_default_set_log_level (JERRY_LOG_LEVEL_WARNING); - jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Ignoring '%s' option because this feature is disabled!\n", option); - return false; - } - return true; -} /* check_feature */ - -/** - * Check whether a usage-related condition holds. If not, print an error - * message, print the usage, and terminate the application. - */ -static void -check_usage (bool condition, /**< the condition that must hold */ - const char *name, /**< name of the application (argv[0]) */ - const char *msg, /**< error message to print if condition does not hold */ - const char *opt) /**< optional part of the error message */ -{ - if (!condition) - { - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%s: %s%s\n", name, msg, opt != NULL ? opt : ""); - exit (JERRY_STANDALONE_EXIT_CODE_FAIL); - } -} /* check_usage */ +#define JERRY_BUFFER_SIZE 256u #if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) - /** * The alloc function passed to jerry_create_context */ @@ -424,51 +43,8 @@ context_alloc (size_t size, (void) cb_data_p; /* unused */ return malloc (size); } /* context_alloc */ - #endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */ -/** - * Inits the engine and the debugger - */ -static void -init_engine (jerry_init_flag_t flags, /**< initialized flags for the engine */ - char *debug_channel, /**< enable the debugger init or not */ - char *debug_protocol, /**< enable the debugger init or not */ - uint16_t debug_port, /**< the debugger port for tcp protocol */ - char *debug_serial_config) /**< configuration string for serial protocol */ -{ - jerry_init (flags); - if (strcmp (debug_channel, "")) - { - bool protocol = false; - - if (!strcmp (debug_protocol, "tcp")) - { - protocol = jerryx_debugger_tcp_create (debug_port); - } - else - { - assert (!strcmp (debug_protocol, "serial")); - protocol = jerryx_debugger_serial_create (debug_serial_config); - } - - if (!strcmp (debug_channel, "rawpacket")) - { - jerryx_debugger_after_connect (protocol && jerryx_debugger_rp_create ()); - } - else - { - assert (!strcmp (debug_channel, "websocket")); - jerryx_debugger_after_connect (protocol && jerryx_debugger_ws_create ()); - } - } - - register_js_function ("assert", jerryx_handler_assert); - register_js_function ("gc", jerryx_handler_gc); - register_js_function ("print", jerryx_handler_print); - register_js_function ("resourceName", jerryx_handler_resource_name); -} /* init_engine */ - int main (int argc, char **argv) @@ -479,521 +55,277 @@ main (int argc, unsigned u; } now = { .d = jerry_port_get_current_time () }; srand (now.u); - JERRY_VLA (const char *, file_names, argc); - int files_counter = 0; - jerry_init_flag_t flags = JERRY_INIT_EMPTY; + JERRY_VLA (main_source_t, sources_p, argc); - JERRY_VLA (const char *, exec_snapshot_file_names, argc); - JERRY_VLA (uint32_t, exec_snapshot_file_indices, argc); - int exec_snapshots_count = 0; + main_args_t arguments; + arguments.sources_p = sources_p; - bool is_parse_only = false; + main_parse_args (argc, argv, &arguments); - bool start_debug_server = false; - uint16_t debug_port = 5001; - char *debug_channel = "websocket"; - char *debug_protocol = "tcp"; - char *debug_serial_config = "/dev/ttyS0,115200,8,N,1"; +#if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) + jerry_context_t *context_p = jerry_create_context (JERRY_GLOBAL_HEAP_SIZE * 1024, context_alloc, NULL); + jerry_port_default_set_current_context (context_p); +#endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */ - bool is_repl_mode = false; - bool is_wait_mode = false; - bool no_prompt = false; +restart: + main_init_engine (&arguments); + int return_code = JERRY_STANDALONE_EXIT_CODE_FAIL; + jerry_value_t ret_value; - const char *exit_cb = NULL; - - cli_state_t cli_state = cli_init (main_opts, argc - 1, argv + 1); - for (int id = cli_consume_option (&cli_state); id != CLI_OPT_END; id = cli_consume_option (&cli_state)) + for (uint32_t source_index = 0; source_index < arguments.source_count; source_index++) { - switch (id) - { - case OPT_HELP: - { - cli_help (argv[0], NULL, main_opts); - return JERRY_STANDALONE_EXIT_CODE_OK; - } - case OPT_VERSION: - { - printf ("Version: %d.%d.%d%s\n", - JERRY_API_MAJOR_VERSION, - JERRY_API_MINOR_VERSION, - JERRY_API_PATCH_VERSION, - JERRY_COMMIT_HASH); - return JERRY_STANDALONE_EXIT_CODE_OK; - } - case OPT_MEM_STATS: - { - if (check_feature (JERRY_FEATURE_MEM_STATS, cli_state.arg)) - { - jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG); - flags |= JERRY_INIT_MEM_STATS; - } - break; - } - case OPT_PARSE_ONLY: - { - is_parse_only = true; - break; - } - case OPT_SHOW_OP: - { - if (check_feature (JERRY_FEATURE_PARSER_DUMP, cli_state.arg)) - { - jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG); - flags |= JERRY_INIT_SHOW_OPCODES; - } - break; - } - case OPT_CALL_ON_EXIT: - { - exit_cb = cli_consume_string (&cli_state); - break; - } - case OPT_SHOW_RE_OP: - { - if (check_feature (JERRY_FEATURE_REGEXP_DUMP, cli_state.arg)) - { - jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG); - flags |= JERRY_INIT_SHOW_REGEXP_OPCODES; - } - break; - } - case OPT_DEBUG_SERVER: - { - if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) - { - start_debug_server = true; - } - break; - } - case OPT_DEBUG_PORT: - { - if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) - { - debug_port = (uint16_t) cli_consume_int (&cli_state); - } - break; - } - case OPT_DEBUG_CHANNEL: - { - if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) - { - debug_channel = (char *) cli_consume_string (&cli_state); - check_usage (!strcmp (debug_channel, "websocket") || !strcmp (debug_channel, "rawpacket"), - argv[0], "Error: invalid value for --debug-channel: ", cli_state.arg); - } - break; - } - case OPT_DEBUG_PROTOCOL: - { - if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) - { - debug_protocol = (char *) cli_consume_string (&cli_state); - check_usage (!strcmp (debug_protocol, "tcp") || !strcmp (debug_protocol, "serial"), - argv[0], "Error: invalid value for --debug-protocol: ", cli_state.arg); - } - break; - } - case OPT_DEBUG_SERIAL_CONFIG: - { - if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) - { - debug_serial_config = (char *) cli_consume_string (&cli_state); - } - break; - } - case OPT_DEBUGGER_WAIT_SOURCE: - { - if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) - { - is_wait_mode = true; - } - break; - } - case OPT_EXEC_SNAP: - { - if (check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg)) - { - exec_snapshot_file_names[exec_snapshots_count] = cli_consume_string (&cli_state); - exec_snapshot_file_indices[exec_snapshots_count++] = 0; - } - else - { - cli_consume_string (&cli_state); - } - break; - } - case OPT_EXEC_SNAP_FUNC: - { - if (check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg)) - { - exec_snapshot_file_names[exec_snapshots_count] = cli_consume_string (&cli_state); - exec_snapshot_file_indices[exec_snapshots_count++] = (uint32_t) cli_consume_int (&cli_state); - } - else - { - cli_consume_string (&cli_state); - } - break; - } - case OPT_LOG_LEVEL: - { - long int log_level = cli_consume_int (&cli_state); - check_usage (log_level >= 0 && log_level <= 3, - argv[0], "Error: invalid value for --log-level: ", cli_state.arg); + main_source_t *source_file_p = sources_p + source_index; + const char *file_path_p = argv[source_file_p->path_index]; - jerry_port_default_set_log_level ((jerry_log_level_t) log_level); - break; - } - case OPT_NO_PROMPT: + size_t source_size; + uint8_t *source_p = jerry_port_read_source (file_path_p, &source_size); + + if (source_p == NULL) + { + goto exit; + } + + switch (source_file_p->type) + { + case SOURCE_SNAPSHOT: { - no_prompt = true; - break; - } - case CLI_OPT_DEFAULT: - { - file_names[files_counter++] = cli_consume_string (&cli_state); + ret_value = jerry_exec_snapshot ((uint32_t *) source_p, + source_size, + source_file_p->snapshot_index, + JERRY_SNAPSHOT_EXEC_COPY_DATA); + + jerry_port_release_source (source_p); break; } default: { - cli_state.error = "Internal error"; - break; - } - } - } + assert (source_file_p->type == SOURCE_SCRIPT); - if (cli_state.error != NULL) - { - if (cli_state.arg != NULL) - { - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s %s\n", cli_state.error, cli_state.arg); - } - else - { - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", cli_state.error); - } - - return JERRY_STANDALONE_EXIT_CODE_FAIL; - } - - if (files_counter == 0 - && exec_snapshots_count == 0) - { - is_repl_mode = true; - } - -#if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) - - jerry_context_t *context_p = jerry_create_context (JERRY_GLOBAL_HEAP_SIZE * 1024, context_alloc, NULL); - jerry_port_default_set_current_context (context_p); - -#endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */ - - if (!start_debug_server) - { - debug_channel = ""; - } - - init_engine (flags, debug_channel, debug_protocol, debug_port, debug_serial_config); - - jerry_value_t ret_value = jerry_create_undefined (); - - if (jerry_is_feature_enabled (JERRY_FEATURE_SNAPSHOT_EXEC)) - { - for (int i = 0; i < exec_snapshots_count; i++) - { - size_t snapshot_size; - const uint32_t *snapshot_p = read_file (exec_snapshot_file_names[i], &snapshot_size); - - if (snapshot_p == NULL) - { - ret_value = jerry_create_error (JERRY_ERROR_COMMON, (jerry_char_t *) "Snapshot file load error"); - } - else - { - ret_value = jerry_exec_snapshot (snapshot_p, - snapshot_size, - exec_snapshot_file_indices[i], - JERRY_SNAPSHOT_EXEC_COPY_DATA); - } - - if (jerry_value_is_error (ret_value)) - { - break; - } - } - } - - while (true) - { - - if (!jerry_value_is_error (ret_value)) - { - for (int i = 0; i < files_counter; i++) - { - size_t source_size; - const jerry_char_t *source_p = (jerry_char_t *) read_file (file_names[i], &source_size); - - if (source_p == NULL) + if (!jerry_is_valid_utf8_string ((jerry_char_t *) source_p, (jerry_size_t) source_size)) { - ret_value = jerry_create_error (JERRY_ERROR_COMMON, (jerry_char_t *) "Source file load error"); - break; + jerry_port_release_source (source_p); + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Input must be a valid UTF-8 string."); + goto exit; } - if (!jerry_is_valid_utf8_string (source_p, (jerry_size_t) source_size)) - { - ret_value = jerry_create_error (JERRY_ERROR_COMMON, (jerry_char_t *) ("Input must be a valid UTF-8 string.")); - break; - } - - ret_value = jerry_parse ((jerry_char_t *) file_names[i], - strlen (file_names[i]), + ret_value = jerry_parse ((jerry_char_t *) file_path_p, + strlen (file_path_p), source_p, source_size, JERRY_PARSE_NO_OPTS); - if (!jerry_value_is_error (ret_value) && !is_parse_only) + jerry_port_release_source (source_p); + + if (!jerry_value_is_error (ret_value) && !(arguments.option_flags & OPT_FLAG_PARSE_ONLY)) { jerry_value_t func_val = ret_value; ret_value = jerry_run (func_val); jerry_release_value (func_val); } - if (jerry_value_is_error (ret_value)) - { - break; - } - - jerry_release_value (ret_value); - ret_value = jerry_create_undefined (); + break; } } - if (is_wait_mode) + if (jerry_value_is_error (ret_value)) { - is_repl_mode = false; - - if (jerry_is_feature_enabled (JERRY_FEATURE_DEBUGGER)) + if (main_is_value_reset (ret_value)) { - while (true) - { - jerry_debugger_wait_for_source_status_t receive_status; + jerry_cleanup (); - do - { - jerry_value_t run_result; - - receive_status = jerry_debugger_wait_for_client_source (wait_for_source_callback, - NULL, - &run_result); - - if (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) - { - ret_value = jerry_create_error (JERRY_ERROR_COMMON, - (jerry_char_t *) "Connection aborted before source arrived."); - } - - if (receive_status == JERRY_DEBUGGER_SOURCE_END) - { - jerry_port_log (JERRY_LOG_LEVEL_DEBUG, "No more client source.\n"); - } - - if (jerry_value_is_abort (run_result)) - { - ret_value = jerry_acquire_value (run_result); - } - - jerry_release_value (run_result); - } - while (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVED); - - if (receive_status != JERRY_DEBUGGER_CONTEXT_RESET_RECEIVED) - { - break; - } - - init_engine (flags, debug_channel, debug_protocol, debug_port, debug_serial_config); - - ret_value = jerry_create_undefined (); - } + goto restart; } + main_print_unhandled_exception (ret_value); + goto exit; } - bool restart = false; - - if (jerry_is_feature_enabled (JERRY_FEATURE_DEBUGGER) && jerry_value_is_abort (ret_value)) - { - jerry_value_t abort_value = jerry_get_value_from_error (ret_value, false); - if (jerry_value_is_string (abort_value)) - { - static const char restart_str[] = "r353t"; - - jerry_value_t str_val = jerry_value_to_string (abort_value); - jerry_size_t str_size = jerry_get_string_size (str_val); - - if (str_size == sizeof (restart_str) - 1) - { - JERRY_VLA (jerry_char_t, str_buf, str_size); - jerry_string_to_char_buffer (str_val, str_buf, str_size); - if (memcmp (restart_str, (char *) (str_buf), str_size) == 0) - { - jerry_release_value (ret_value); - restart = true; - } - } - - jerry_release_value (str_val); - } - - jerry_release_value (abort_value); - } - - if (!restart) - { - break; - } - - jerry_cleanup (); - - init_engine (flags, debug_channel, debug_protocol, debug_port, debug_serial_config); - - ret_value = jerry_create_undefined (); + jerry_release_value (ret_value); } - if (is_repl_mode) + if (arguments.option_flags & OPT_FLAG_WAIT_SOURCE) { - const char *prompt = !no_prompt ? "jerry> " : ""; - bool is_done = false; - - while (!is_done) + while (true) { - uint8_t *source_buffer_tail = buffer; - size_t len = 0; + jerry_debugger_wait_for_source_status_t receive_status; + receive_status = jerry_debugger_wait_for_client_source (main_wait_for_source_callback, + NULL, + &ret_value); + if (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVE_FAILED) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Connection aborted before source arrived."); + goto exit; + } + + if (receive_status == JERRY_DEBUGGER_SOURCE_END) + { + jerry_port_log (JERRY_LOG_LEVEL_DEBUG, "No more client source.\n"); + break; + } + + assert (receive_status == JERRY_DEBUGGER_CONTEXT_RESET_RECEIVED + || receive_status == JERRY_DEBUGGER_SOURCE_RECEIVED); + + if (receive_status == JERRY_DEBUGGER_CONTEXT_RESET_RECEIVED + || main_is_value_reset (ret_value)) + { + jerry_cleanup (); + goto restart; + } + + assert (receive_status == JERRY_DEBUGGER_SOURCE_RECEIVED); + jerry_release_value (ret_value); + } + } + else if (arguments.option_flags & OPT_FLAG_USE_STDIN) + { + char buffer[JERRY_BUFFER_SIZE]; + char *source_p = NULL; + size_t source_size = 0; + + while (!feof (stdin)) + { + size_t read_bytes = fread (buffer, 1u, JERRY_BUFFER_SIZE, stdin); + + size_t new_size = source_size + read_bytes; + source_p = realloc (source_p, new_size); + + memcpy (source_p + source_size, buffer, read_bytes); + source_size = new_size; + } + + ret_value = jerry_parse (NULL, 0, (jerry_char_t *) source_p, source_size, JERRY_PARSE_NO_OPTS); + free (source_p); + + if (jerry_value_is_error (ret_value)) + { + main_print_unhandled_exception (ret_value); + goto exit; + } + + jerry_value_t func_val = ret_value; + ret_value = jerry_run (func_val); + jerry_release_value (func_val); + + if (jerry_value_is_error (ret_value)) + { + main_print_unhandled_exception (ret_value); + goto exit; + } + + jerry_release_value (ret_value); + } + else if (arguments.source_count == 0) + { + const char *prompt = (arguments.option_flags & OPT_FLAG_NO_PROMPT) ? "" : "jerry> "; + char buffer[JERRY_BUFFER_SIZE]; + + while (true) + { printf ("%s", prompt); + char *str_p = fgets (buffer, JERRY_BUFFER_SIZE, stdin); - /* Read a line */ - while (true) + if (str_p == NULL) { - if (fread (source_buffer_tail, 1, 1, stdin) != 1) - { - is_done = true; - break; - } - if (*source_buffer_tail == '\n') - { - break; - } - source_buffer_tail ++; - len ++; + printf ("\n"); + break; } - *source_buffer_tail = 0; - if (len > 0) + size_t len = strlen (str_p); + + if (len == 0) { - if (!jerry_is_valid_utf8_string (buffer, (jerry_size_t) len)) - { - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Input must be a valid UTF-8 string.\n"); - return JERRY_STANDALONE_EXIT_CODE_FAIL; - } - - /* Evaluate the line */ - jerry_value_t ret_val = jerry_parse (NULL, - 0, - buffer, - len, - JERRY_PARSE_NO_OPTS); - - if (!jerry_value_is_error (ret_val)) - { - jerry_value_t func_val = ret_val; - ret_val = jerry_run (func_val); - jerry_release_value (func_val); - } - - if (!jerry_value_is_error (ret_val)) - { - /* Print return value */ - const jerry_value_t args[] = { ret_val }; - jerry_value_t ret_val_print = jerryx_handler_print (jerry_create_undefined (), - jerry_create_undefined (), - args, - 1); - jerry_release_value (ret_val_print); - jerry_release_value (ret_val); - ret_val = jerry_run_all_enqueued_jobs (); - - if (jerry_value_is_error (ret_val)) - { - ret_val = jerry_get_value_from_error (ret_val, true); - print_unhandled_exception (ret_val); - } - } - else - { - ret_val = jerry_get_value_from_error (ret_val, true); - print_unhandled_exception (ret_val); - } - - jerry_release_value (ret_val); + continue; } + + if (!jerry_is_valid_utf8_string ((jerry_char_t *) str_p, (jerry_size_t) len)) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Input must be a valid UTF-8 string.\n"); + continue; + } + + ret_value = jerry_parse (NULL, 0, (jerry_char_t *) str_p, len, JERRY_PARSE_NO_OPTS); + + if (jerry_value_is_error (ret_value)) + { + main_print_unhandled_exception (ret_value); + continue; + } + + jerry_value_t func_val = ret_value; + ret_value = jerry_run (func_val); + jerry_release_value (func_val); + + if (jerry_value_is_error (ret_value)) + { + main_print_unhandled_exception (ret_value); + continue; + } + + const jerry_value_t args[] = { ret_value }; + jerry_value_t ret_val_print = jerryx_handler_print (jerry_create_undefined (), + jerry_create_undefined (), + args, + 1); + jerry_release_value (ret_val_print); + jerry_release_value (ret_value); + ret_value = jerry_run_all_enqueued_jobs (); + + if (jerry_value_is_error (ret_value)) + { + main_print_unhandled_exception (ret_value); + continue; + } + + jerry_release_value (ret_value); } } - int ret_code = JERRY_STANDALONE_EXIT_CODE_OK; - - if (jerry_value_is_error (ret_value)) - { - ret_value = jerry_get_value_from_error (ret_value, true); - print_unhandled_exception (ret_value); - - ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL; - } - - jerry_release_value (ret_value); - ret_value = jerry_run_all_enqueued_jobs (); if (jerry_value_is_error (ret_value)) { - ret_value = jerry_get_value_from_error (ret_value, true); - print_unhandled_exception (ret_value); - ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL; + main_print_unhandled_exception (ret_value); + goto exit; } jerry_release_value (ret_value); - if (exit_cb != NULL) + if (arguments.exit_cb_name_p != NULL) { jerry_value_t global = jerry_get_global_object (); - jerry_value_t fn_str = jerry_create_string ((jerry_char_t *) exit_cb); - jerry_value_t callback_fn = jerry_get_property (global, fn_str); + jerry_value_t name_str = jerry_create_string ((jerry_char_t *) arguments.exit_cb_name_p); + jerry_value_t callback_fn = jerry_get_property (global, name_str); jerry_release_value (global); - jerry_release_value (fn_str); + jerry_release_value (name_str); if (jerry_value_is_function (callback_fn)) { - jerry_value_t ret_val = jerry_call_function (callback_fn, jerry_create_undefined (), NULL, 0); + ret_value = jerry_call_function (callback_fn, jerry_create_undefined (), NULL, 0); - if (jerry_value_is_error (ret_val)) + if (jerry_value_is_error (ret_value)) { - ret_val = jerry_get_value_from_error (ret_val, true); - print_unhandled_exception (ret_val); - ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL; + main_print_unhandled_exception (ret_value); + goto exit; } - jerry_release_value (ret_val); + jerry_release_value (ret_value); } jerry_release_value (callback_fn); } + return_code = JERRY_STANDALONE_EXIT_CODE_OK; + +exit: jerry_cleanup (); + #if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) free (context_p); #endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */ - return ret_code; + + return return_code; } /* main */ diff --git a/jerry-main/main-utils.c b/jerry-main/main-utils.c new file mode 100644 index 000000000..34faca7b9 --- /dev/null +++ b/jerry-main/main-utils.c @@ -0,0 +1,323 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "jerryscript.h" +#include "jerryscript-ext/debugger.h" +#include "jerryscript-ext/handler.h" +#include "jerryscript-port.h" +#include "jerryscript-port-default.h" + +#include "main-utils.h" +#include "main-options.h" + +/** + * Max line size that will be printed on a Syntax Error + */ +#define SYNTAX_ERROR_MAX_LINE_LENGTH 256 + +/** + * Register a JavaScript function in the global object. + */ +static void +main_register_global_function (const char *name_p, /**< name of the function */ + jerry_external_handler_t handler_p) /**< function callback */ +{ + jerry_value_t result_val = jerryx_handler_register_global ((const jerry_char_t *) name_p, handler_p); + assert (!jerry_value_is_error (result_val)); + jerry_release_value (result_val); +} /* main_register_global_function */ + +/** + * Inits the engine and the debugger + */ +void +main_init_engine (main_args_t *arguments_p) /** main arguments */ +{ + jerry_init (arguments_p->init_flags); + + if (arguments_p->option_flags & OPT_FLAG_DEBUG_SERVER) + { + bool protocol = false; + + if (!strcmp (arguments_p->debug_protocol, "tcp")) + { + protocol = jerryx_debugger_tcp_create (arguments_p->debug_port); + } + else + { + assert (!strcmp (arguments_p->debug_protocol, "serial")); + protocol = jerryx_debugger_serial_create (arguments_p->debug_serial_config); + } + + if (!strcmp (arguments_p->debug_channel, "rawpacket")) + { + jerryx_debugger_after_connect (protocol && jerryx_debugger_rp_create ()); + } + else + { + assert (!strcmp (arguments_p->debug_channel, "websocket")); + jerryx_debugger_after_connect (protocol && jerryx_debugger_ws_create ()); + } + } + + main_register_global_function ("assert", jerryx_handler_assert); + main_register_global_function ("gc", jerryx_handler_gc); + main_register_global_function ("print", jerryx_handler_print); + main_register_global_function ("resourceName", jerryx_handler_resource_name); +} /* main_init_engine */ + +/** + * Print an error value. + * + * Note: the error value will be released. + */ +void +main_print_unhandled_exception (jerry_value_t error_value) /**< error value */ +{ + assert (jerry_value_is_error (error_value)); + error_value = jerry_get_value_from_error (error_value, true); + + jerry_char_t err_str_buf[256]; + + jerry_value_t err_str_val = jerry_value_to_string (error_value); + jerry_size_t err_str_size = jerry_get_utf8_string_size (err_str_val); + + if (err_str_size >= 256) + { + const char msg[] = "[Error message too long]"; + err_str_size = sizeof (msg) / sizeof (char) - 1; + memcpy (err_str_buf, msg, err_str_size + 1); + } + else + { + jerry_size_t string_end = jerry_string_to_utf8_char_buffer (err_str_val, err_str_buf, err_str_size); + assert (string_end == err_str_size); + err_str_buf[string_end] = 0; + + if (jerry_is_feature_enabled (JERRY_FEATURE_ERROR_MESSAGES) + && jerry_get_error_type (error_value) == JERRY_ERROR_SYNTAX) + { + jerry_char_t *string_end_p = err_str_buf + string_end; + unsigned int err_line = 0; + unsigned int err_col = 0; + char *path_str_p = NULL; + char *path_str_end_p = NULL; + + /* 1. parse column and line information */ + for (jerry_char_t *current_p = err_str_buf; current_p < string_end_p; current_p++) + { + if (*current_p == '[') + { + current_p++; + + if (*current_p == '<') + { + break; + } + + path_str_p = (char *) current_p; + while (current_p < string_end_p && *current_p != ':') + { + current_p++; + } + + path_str_end_p = (char *) current_p++; + + err_line = (unsigned int) strtol ((char *) current_p, (char **) ¤t_p, 10); + + current_p++; + + err_col = (unsigned int) strtol ((char *) current_p, NULL, 10); + break; + } + } /* for */ + + if (err_line != 0 && err_col > 0 && err_col < SYNTAX_ERROR_MAX_LINE_LENGTH) + { + uint32_t curr_line = 1; + uint32_t pos = 0; + + /* Temporarily modify the error message, so we can use the path. */ + *path_str_end_p = '\0'; + + size_t source_size; + uint8_t *source_p = jerry_port_read_source (path_str_p, &source_size); + + /* Revert the error message. */ + *path_str_end_p = ':'; + + /* 2. seek and print */ + while (pos < source_size && curr_line < err_line) + { + if (source_p[pos] == '\n') + { + curr_line++; + } + + pos++; + } + + uint32_t char_count = 0; + uint8_t ch; + + do + { + ch = source_p[pos++]; + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%c", ch); + } + while (ch != '\n' && char_count++ < SYNTAX_ERROR_MAX_LINE_LENGTH); + + jerry_port_release_source (source_p); + + while (--err_col) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "~"); + } + + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "^\n\n"); + } + } + } + + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%s\n", err_str_buf); + jerry_release_value (err_str_val); + + if (jerry_value_is_object (error_value)) + { + jerry_value_t stack_str = jerry_create_string ((const jerry_char_t *) "stack"); + jerry_value_t backtrace_val = jerry_get_property (error_value, stack_str); + jerry_release_value (stack_str); + + if (jerry_value_is_array (backtrace_val)) + { + uint32_t length = jerry_get_array_length (backtrace_val); + + /* This length should be enough. */ + if (length > 32) + { + length = 32; + } + + for (uint32_t i = 0; i < length; i++) + { + jerry_value_t item_val = jerry_get_property_by_index (backtrace_val, i); + + if (jerry_value_is_string (item_val)) + { + jerry_size_t str_size = jerry_get_utf8_string_size (item_val); + + if (str_size >= 256) + { + printf ("%6u: [Backtrace string too long]\n", i); + } + else + { + jerry_size_t string_end = jerry_string_to_utf8_char_buffer (item_val, err_str_buf, str_size); + assert (string_end == str_size); + err_str_buf[string_end] = 0; + + printf ("%6u: %s\n", i, err_str_buf); + } + } + + jerry_release_value (item_val); + } + } + + jerry_release_value (backtrace_val); + } + + jerry_release_value (error_value); +} /* main_print_unhandled_exception */ + +/** + * Runs the source code received by jerry_debugger_wait_for_client_source. + * + * @return result fo the source code execution + */ +jerry_value_t +main_wait_for_source_callback (const jerry_char_t *resource_name_p, /**< resource name */ + size_t resource_name_size, /**< size of resource name */ + const jerry_char_t *source_p, /**< source code */ + size_t source_size, /**< source code size */ + void *user_p) /**< user pointer */ +{ + (void) user_p; /* unused */ + jerry_value_t ret_val = jerry_parse (resource_name_p, + resource_name_size, + source_p, + source_size, + JERRY_PARSE_NO_OPTS); + + if (!jerry_value_is_error (ret_val)) + { + jerry_value_t func_val = ret_val; + ret_val = jerry_run (func_val); + jerry_release_value (func_val); + } + + return ret_val; +} /* main_wait_for_source_callback */ + +/** + * Check that value contains the reset abort value. + * + * Note: if the value is the reset abort value, the value is release. + * + * return true, if reset abort + * false, otherwise + */ +bool +main_is_value_reset (jerry_value_t value) /**< jerry value */ +{ + if (!jerry_value_is_abort (value)) + { + return false; + } + + jerry_value_t abort_value = jerry_get_value_from_error (value, false); + + if (!jerry_value_is_string (abort_value)) + { + jerry_release_value (abort_value); + return false; + } + + static const char restart_str[] = "r353t"; + + jerry_size_t str_size = jerry_get_string_size (abort_value); + bool is_reset = false; + + if (str_size == sizeof (restart_str) - 1) + { + JERRY_VLA (jerry_char_t, str_buf, str_size); + jerry_string_to_char_buffer (abort_value, str_buf, str_size); + + is_reset = memcmp (restart_str, (char *) (str_buf), str_size) == 0; + + if (is_reset) + { + jerry_release_value (value); + } + } + + jerry_release_value (abort_value); + return is_reset; +} /* main_is_value_reset */ diff --git a/jerry-main/main-utils.h b/jerry-main/main-utils.h new file mode 100644 index 000000000..b3bad892d --- /dev/null +++ b/jerry-main/main-utils.h @@ -0,0 +1,42 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MAIN_UTILS_H +#define MAIN_UTILS_H + +#include "main-options.h" + +/** + * Standalone Jerry exit codes + */ +#define JERRY_STANDALONE_EXIT_CODE_OK (0) +#define JERRY_STANDALONE_EXIT_CODE_FAIL (1) + +void +main_init_engine (main_args_t *arguments_p); +void +main_print_unhandled_exception (jerry_value_t error_value); + +jerry_value_t +main_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); + +bool +main_is_value_reset (jerry_value_t value); + +#endif /* !MAIN_UTILS_H */ diff --git a/jerry-port/default/default-module.c b/jerry-port/default/default-module.c index b408764c0..8fab44787 100644 --- a/jerry-port/default/default-module.c +++ b/jerry-port/default/default-module.c @@ -50,7 +50,7 @@ jerry_port_read_source (const char *file_name_p, /**< file name */ if (file_p == NULL) { - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to open file: %s\n", file_name_p); + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Failed to open file: %s\n", file_name_p); return NULL; } @@ -61,18 +61,18 @@ jerry_port_read_source (const char *file_name_p, /**< file name */ { fclose (file_p); - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to allocate memory for module"); + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Failed to allocate memory for file: %s\n", file_name_p); return NULL; } size_t bytes_read = fread (buffer_p, 1u, file_size, file_p); - if (!bytes_read) + if (bytes_read != file_size) { fclose (file_p); free (buffer_p); - jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to read file: %s\n", file_name_p); + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Failed to read file: %s\n", file_name_p); return NULL; } diff --git a/tests/debugger/do_abort.expected b/tests/debugger/do_abort.expected index df8981f5c..7656e41ae 100644 --- a/tests/debugger/do_abort.expected +++ b/tests/debugger/do_abort.expected @@ -9,4 +9,4 @@ Stopped at tests/debugger/do_abort.js:20 (in g() at line:19, col:1) (jerry-debugger) s Stopped at tests/debugger/do_abort.js:16 (in f() at line:15, col:1) (jerry-debugger) abort new Error('Fatal error :)') -err: Script Error: Error: Fatal error :) +err: Error: Fatal error :) diff --git a/tests/debugger/do_throw.expected b/tests/debugger/do_throw.expected index ed966fbae..d191be816 100644 --- a/tests/debugger/do_throw.expected +++ b/tests/debugger/do_throw.expected @@ -17,4 +17,4 @@ Stopped at tests/debugger/do_throw.js:34 (jerry-debugger) eval e.message.length 538 (jerry-debugger) throw new Error('Exit') -err: Script Error: Error: Exit +err: Error: Exit diff --git a/tests/debugger/do_throw_adv.expected b/tests/debugger/do_throw_adv.expected index 9884f434a..aef65829d 100644 --- a/tests/debugger/do_throw_adv.expected +++ b/tests/debugger/do_throw_adv.expected @@ -39,4 +39,4 @@ Exception throw detected (to disable automatic stop type exception 0) Exception hint: 16 Stopped at tests/debugger/do_throw_adv.js:18 (in f() at line:17, col:1) (jerry-debugger) c -err: Script Error: 16 +err: 16