Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
298 changes: 276 additions & 22 deletions jerry-main/main-unix-snapshot.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "jerryscript.h"
#include "jerryscript-port.h"
#include "jerryscript-port-default.h"

#include "cli.h"

Expand All @@ -33,6 +34,52 @@

static uint8_t input_buffer[ JERRY_BUFFER_SIZE ];
static uint32_t output_buffer[ JERRY_BUFFER_SIZE / 4 ];
static const char *output_file_name_p = "js.snapshot";

/**
* 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 */

/**
* Utility method to check and print error in the given cli state.
*
* @return true - if any error is detected
* false - if there is no error in the cli state
*/
static bool
check_cli_error (const cli_state_t *const cli_state_p)
{
if (cli_state_p->error != NULL)
{
if (cli_state_p->arg != NULL)
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s %s\n", cli_state_p->error, cli_state_p->arg);
}
else
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", cli_state_p->error);
}

return true;
}

return false;
} /* check_cli_error */

/**
* Loading a single file into the memory.
Expand Down Expand Up @@ -73,6 +120,227 @@ read_file (uint8_t *input_pos_p, /**< next position in the input buffer */
return bytes_read;
} /* read_file */

/**
* Generate command line option IDs
*/
typedef enum
{
OPT_GENERATE_HELP,
OPT_GENERATE_CONTEXT,
OPT_GENERATE_SHOW_OP,
OPT_GENERATE_LITERAL_LIST,
OPT_GENERATE_LITERAL_C,
OPT_GENERATE_OUT,
} generate_opt_id_t;

/**
* Generate command line options
*/
static const cli_opt_t generate_opts[] =
{
CLI_OPT_DEF (.id = OPT_GENERATE_HELP, .opt = "h", .longopt = "help",
.help = "print this help and exit"),
CLI_OPT_DEF (.id = OPT_GENERATE_SHOW_OP, .longopt = "show-opcodes",
.help = "print generated opcodes"),
CLI_OPT_DEF (.id = OPT_GENERATE_CONTEXT, .opt = "c", .longopt = "context",
.meta = "MODE",
.help = "specify the execution context of the snapshot: "
"global or eval (default: global)."),
CLI_OPT_DEF (.id = OPT_GENERATE_LITERAL_LIST, .longopt = "save-literals-list-format",
.meta = "FILE",
.help = "export literals found in parsed JS input (in list format)"),
CLI_OPT_DEF (.id = OPT_GENERATE_LITERAL_C, .longopt = "save-literals-c-format",
.meta = "FILE",
.help = "export literals found in parsed JS input (in C source format)"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the original --save-snapshot-for-{global,eval} options have already been rewritten to a new --context={global,eval} format. wouldn't it make sense to have something similar for --save-literals-{c,list}-format, too? e.g., as simple as --save-literals={c,list}.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the logic is different, for --save-literals* you need to specify the output file as argument. This way you can generate the literal file and also the snapshot.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh that's right

CLI_OPT_DEF (.id = OPT_GENERATE_OUT, .opt = "o", .meta="FILE",
.help = "specify output file name (default: js.snapshot)"),
CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE",
.help = "input snapshot file")
};

/**
* Process 'generate' command.
*
* @return error code (0 - no error)
*/
static int
process_generate (cli_state_t *cli_state_p, /**< cli state */
int argc, /**< number of arguments */
char *prog_name_p) /**< program name */
{
(void) argc;

bool is_save_literals_mode_in_c_format = false;
bool is_snapshot_mode_for_global = true;
jerry_init_flag_t flags = JERRY_INIT_EMPTY;

uint32_t number_of_files = 0;
uint8_t *source_p = input_buffer;
size_t source_length = 0;
const char *save_literals_file_name_p = NULL;

cli_change_opts (cli_state_p, generate_opts);

for (int id = cli_consume_option (cli_state_p); id != CLI_OPT_END; id = cli_consume_option (cli_state_p))
{
switch (id)
{
case OPT_GENERATE_HELP:
{
cli_help (prog_name_p, "generate", generate_opts);
return JERRY_STANDALONE_EXIT_CODE_OK;
}
case OPT_GENERATE_OUT:
{
output_file_name_p = cli_consume_string (cli_state_p);
break;
}
case OPT_GENERATE_LITERAL_LIST:
case OPT_GENERATE_LITERAL_C:
{
if (save_literals_file_name_p != NULL)
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: literal file name already specified");
return JERRY_STANDALONE_EXIT_CODE_FAIL;
}

is_save_literals_mode_in_c_format = (id == OPT_GENERATE_LITERAL_C);
save_literals_file_name_p = cli_consume_string (cli_state_p);
break;
}
case OPT_GENERATE_SHOW_OP:
{
if (check_feature (JERRY_FEATURE_PARSER_DUMP, cli_state_p->arg))
{
jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG);
flags |= JERRY_INIT_SHOW_OPCODES;
}
break;
}
case OPT_GENERATE_CONTEXT:
{
const char *mode_str_p = cli_consume_string (cli_state_p);

if (cli_state_p->error != NULL)
{
break;
}

if (!strcmp ("global", mode_str_p))
{
is_snapshot_mode_for_global = true;
}
else if (!strcmp ("eval", mode_str_p))
{
is_snapshot_mode_for_global = false;
}
else
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Incorrect argument for context mode: %s\n", mode_str_p);
return JERRY_STANDALONE_EXIT_CODE_FAIL;
}
break;
}
case CLI_OPT_DEFAULT:
{
const char *file_name_p = cli_consume_string (cli_state_p);

if (cli_state_p->error == NULL)
{
source_length = read_file (source_p, file_name_p);

if (source_length == 0)
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Input file is empty\n");
return JERRY_STANDALONE_EXIT_CODE_FAIL;
}

number_of_files++;
}
break;
}
default:
{
cli_state_p->error = "Internal error";
break;
}
}
}

if (check_cli_error (cli_state_p))
{
return JERRY_STANDALONE_EXIT_CODE_FAIL;
}

if (number_of_files != 1)
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: No input file specified!\n");
return JERRY_STANDALONE_EXIT_CODE_FAIL;
}

jerry_init (flags);

if (!jerry_is_valid_utf8_string (source_p, (jerry_size_t) source_length))
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Input must be a valid UTF-8 string.\n");
return JERRY_STANDALONE_EXIT_CODE_FAIL;
}

size_t snapshot_size = jerry_parse_and_save_snapshot ((jerry_char_t *) source_p,
source_length,
is_snapshot_mode_for_global,
false,
output_buffer,
sizeof (output_buffer) / sizeof (uint32_t));
if (snapshot_size == 0)
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Generating snapshot failed!\n");
return JERRY_STANDALONE_EXIT_CODE_FAIL;
}

FILE *snapshot_file_p = fopen (output_file_name_p, "w");
if (snapshot_file_p == NULL)
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Unable to write snapshot file: '%s'\n", output_file_name_p);
return JERRY_STANDALONE_EXIT_CODE_FAIL;
}

fwrite (output_buffer, sizeof (uint8_t), snapshot_size, snapshot_file_p);
fclose (snapshot_file_p);

printf ("Created snapshot file: '%s' (%lu bytes)\n", output_file_name_p, (unsigned long) snapshot_size);

if (save_literals_file_name_p != NULL)
{
const size_t literal_buffer_size = jerry_parse_and_save_literals ((jerry_char_t *) source_p,
source_length,
false,
output_buffer,
sizeof (output_buffer) / sizeof (uint32_t),
is_save_literals_mode_in_c_format);
if (literal_buffer_size == 0)
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Literal saving failed!\n");
return JERRY_STANDALONE_EXIT_CODE_FAIL;
}

FILE *literal_file_p = fopen (save_literals_file_name_p, "w");

if (literal_file_p == NULL)
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Unable to write literal file: '%s'\n", save_literals_file_name_p);
return JERRY_STANDALONE_EXIT_CODE_FAIL;
}

fwrite (output_buffer, sizeof (uint8_t), literal_buffer_size, literal_file_p);
fclose (literal_file_p);

printf ("Created literal file: '%s' (%lu bytes)\n", save_literals_file_name_p, (unsigned long) literal_buffer_size);
}

return 0;
} /* process_generate */

/**
* Merge command line option IDs
*/
Expand All @@ -90,7 +358,7 @@ static const cli_opt_t merge_opts[] =
CLI_OPT_DEF (.id = OPT_MERGE_HELP, .opt = "h", .longopt = "help",
.help = "print this help and exit"),
CLI_OPT_DEF (.id = OPT_MERGE_OUT, .opt = "o",
.help = "specify output file name (default: merged.snapshot)"),
.help = "specify output file name (default: js.snapshot)"),
CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE",
.help = "input snapshot files, minimum two")
};
Expand All @@ -111,7 +379,6 @@ process_merge (cli_state_t *cli_state_p, /**< cli state */

cli_change_opts (cli_state_p, merge_opts);

const char *output_file_name_p = "merged.snapshot";
const uint32_t *merge_buffers[argc];
size_t merge_buffer_sizes[argc];
uint32_t number_of_files = 0;
Expand Down Expand Up @@ -160,17 +427,8 @@ process_merge (cli_state_t *cli_state_p, /**< cli state */
}
}

if (cli_state_p->error != NULL)
if (check_cli_error (cli_state_p))
{
if (cli_state_p->arg != NULL)
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s %s\n", cli_state_p->error, cli_state_p->arg);
}
else
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", cli_state_p->error);
}

return JERRY_STANDALONE_EXIT_CODE_FAIL;
}

Expand Down Expand Up @@ -238,6 +496,7 @@ print_commands (char *prog_name_p) /**< program name */
cli_help (prog_name_p, NULL, main_opts);

printf ("\nAvailable commands:\n"
" generate\n"
" merge\n"
"\nPassing -h or --help after a command displays its help.\n");
} /* print_commands */
Expand Down Expand Up @@ -275,6 +534,10 @@ main (int argc, /**< number of arguments */
{
return process_merge (&cli_state, argc, argv[0]);
}
else if (!strcmp ("generate", command_p))
{
return process_generate (&cli_state, argc, argv[0]);
}

jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: unknown command: %s\n\n", command_p);
print_commands (argv[0]);
Expand All @@ -289,17 +552,8 @@ main (int argc, /**< number of arguments */
}
}

if (cli_state.error != NULL)
if (check_cli_error (&cli_state))
{
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;
}

Expand Down
Loading