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 <stdlib.h> 18#include <string.h> 19 20#include "jerryscript.h" 21#include "jerryscript-port.h" 22#include "jerryscript-port-default.h" 23 24#include "cli.h" 25 26/** 27 * Maximum size for loaded snapshots 28 */ 29#define JERRY_BUFFER_SIZE (1048576) 30 31/** 32 * Maximum number of loaded literals 33 */ 34#define JERRY_LITERAL_LENGTH (4096) 35 36/** 37 * Standalone Jerry exit codes 38 */ 39#define JERRY_STANDALONE_EXIT_CODE_OK (0) 40#define JERRY_STANDALONE_EXIT_CODE_FAIL (1) 41 42#ifdef _WIN32 43# ifdef _WIN64 44# define PRI_SIZET "lu" 45# define SIZE_T_TYPE unsigned long 46# else 47# define PRI_SIZET "zu" 48# define SIZE_T_TYPE size_t 49# endif 50#else 51# define PRI_SIZET "zu" 52# define SIZE_T_TYPE size_t 53#endif 54 55static uint8_t input_buffer[JERRY_BUFFER_SIZE]; 56static uint32_t output_buffer[JERRY_BUFFER_SIZE / 4]; 57static jerry_char_t literal_buffer[JERRY_BUFFER_SIZE]; 58static const char *output_file_name_p = "js.snapshot"; 59static jerry_length_t magic_string_lengths[JERRY_LITERAL_LENGTH]; 60static const jerry_char_t *magic_string_items[JERRY_LITERAL_LENGTH]; 61 62#if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) 63/** 64 * The alloc function passed to jerry_create_context 65 */ 66static void * 67context_alloc (size_t size, 68 void *cb_data_p) 69{ 70 (void) cb_data_p; /* unused */ 71 return malloc (size); 72} /* context_alloc */ 73 74/** 75 * Create and set the default external context. 76 */ 77static void 78context_init (void) 79{ 80 jerry_context_t *context_p = jerry_create_context (JERRY_GLOBAL_HEAP_SIZE * 1024, context_alloc, NULL); 81 jerry_port_default_set_current_context (context_p); 82} /* context_init */ 83 84#endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */ 85 86/** 87 * Check whether JerryScript has a requested feature enabled or not. If not, 88 * print a warning message. 89 * 90 * @return the status of the feature. 91 */ 92static bool 93check_feature (jerry_feature_t feature, /**< feature to check */ 94 const char *option) /**< command line option that triggered this check */ 95{ 96 if (!jerry_is_feature_enabled (feature)) 97 { 98 jerry_port_default_set_log_level (JERRY_LOG_LEVEL_WARNING); 99 jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Ignoring '%s' option because this feature is disabled!\n", option); 100 return false; 101 } 102 return true; 103} /* check_feature */ 104 105/** 106 * Utility method to check and print error in the given cli state. 107 * 108 * @return true - if any error is detected 109 * false - if there is no error in the cli state 110 */ 111static bool 112check_cli_error (const cli_state_t *const cli_state_p) 113{ 114 if (cli_state_p->error != NULL) 115 { 116 if (cli_state_p->arg != NULL) 117 { 118 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s %s\n", cli_state_p->error, cli_state_p->arg); 119 } 120 else 121 { 122 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", cli_state_p->error); 123 } 124 125 return true; 126 } 127 128 return false; 129} /* check_cli_error */ 130 131/** 132 * Loading a single file into the memory. 133 * 134 * @return size of file - if loading is successful 135 * 0 - otherwise 136 */ 137static size_t 138read_file (uint8_t *input_pos_p, /**< next position in the input buffer */ 139 const char *file_name) /**< file name */ 140{ 141 FILE *file = fopen (file_name, "rb"); 142 143 if (file == NULL) 144 { 145 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to open file: %s\n", file_name); 146 return 0; 147 } 148 149 size_t max_size = (size_t) (input_buffer + JERRY_BUFFER_SIZE - input_pos_p); 150 151 size_t bytes_read = fread (input_pos_p, 1u, max_size, file); 152 fclose (file); 153 154 if (bytes_read == 0) 155 { 156 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to read file: %s\n", file_name); 157 return 0; 158 } 159 160 if (bytes_read == max_size) 161 { 162 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: file too large: %s\n", file_name); 163 return 0; 164 } 165 166 printf ("Input file '%s' (%"PRI_SIZET" bytes) loaded.\n", file_name, (SIZE_T_TYPE)bytes_read); 167 return bytes_read; 168} /* read_file */ 169 170/** 171 * Print error value 172 */ 173static void 174print_unhandled_exception (jerry_value_t error_value) /**< error value */ 175{ 176 assert (!jerry_value_is_error (error_value)); 177 178 jerry_value_t err_str_val = jerry_value_to_string (error_value); 179 180 if (jerry_value_is_error (err_str_val)) 181 { 182 /* Avoid recursive error throws. */ 183 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Snapshot error: [value cannot be converted to string]\n"); 184 jerry_release_value (err_str_val); 185 return; 186 } 187 188 jerry_size_t err_str_size = jerry_get_utf8_string_size (err_str_val); 189 190 if (err_str_size >= 256) 191 { 192 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Snapshot error: [value cannot be converted to string]\n"); 193 jerry_release_value (err_str_val); 194 return; 195 } 196 197 jerry_char_t err_str_buf[256]; 198 jerry_size_t string_end = jerry_string_to_utf8_char_buffer (err_str_val, err_str_buf, err_str_size); 199 assert (string_end == err_str_size); 200 err_str_buf[string_end] = 0; 201 202 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Snapshot error: %s\n", (char *) err_str_buf); 203 jerry_release_value (err_str_val); 204} /* print_unhandled_exception */ 205 206/** 207 * Generate command line option IDs 208 */ 209typedef enum 210{ 211 OPT_GENERATE_HELP, 212 OPT_GENERATE_STATIC, 213 OPT_GENERATE_SHOW_OP, 214 OPT_GENERATE_FUNCTION, 215 OPT_GENERATE_OUT, 216 OPT_IMPORT_LITERAL_LIST 217} generate_opt_id_t; 218 219/** 220 * Generate command line options 221 */ 222static const cli_opt_t generate_opts[] = 223{ 224 CLI_OPT_DEF (.id = OPT_GENERATE_HELP, .opt = "h", .longopt = "help", 225 .help = "print this help and exit"), 226 CLI_OPT_DEF (.id = OPT_GENERATE_STATIC, .opt = "s", .longopt = "static", 227 .help = "generate static snapshot"), 228 CLI_OPT_DEF (.id = OPT_GENERATE_FUNCTION, .opt = "f", .longopt = "generate-function-snapshot", 229 .meta = "ARGUMENTS", 230 .help = "generate function snapshot with given arguments"), 231 CLI_OPT_DEF (.id = OPT_IMPORT_LITERAL_LIST, .longopt = "load-literals-list-format", 232 .meta = "FILE", 233 .help = "import literals from list format (for static snapshots)"), 234 CLI_OPT_DEF (.id = OPT_GENERATE_SHOW_OP, .longopt = "show-opcodes", 235 .help = "print generated opcodes"), 236 CLI_OPT_DEF (.id = OPT_GENERATE_OUT, .opt = "o", .meta="FILE", 237 .help = "specify output file name (default: js.snapshot)"), 238 CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE", 239 .help = "input source file") 240}; 241 242/** 243 * Process 'generate' command. 244 * 245 * @return error code (0 - no error) 246 */ 247static int 248process_generate (cli_state_t *cli_state_p, /**< cli state */ 249 int argc, /**< number of arguments */ 250 char *prog_name_p) /**< program name */ 251{ 252 (void) argc; 253 254 uint32_t snapshot_flags = 0; 255 jerry_init_flag_t flags = JERRY_INIT_EMPTY; 256 257 const char *file_name_p = NULL; 258 uint8_t *source_p = input_buffer; 259 size_t source_length = 0; 260 const char *literals_file_name_p = NULL; 261 const char *function_args_p = NULL; 262 263 cli_change_opts (cli_state_p, generate_opts); 264 265 for (int id = cli_consume_option (cli_state_p); id != CLI_OPT_END; id = cli_consume_option (cli_state_p)) 266 { 267 switch (id) 268 { 269 case OPT_GENERATE_HELP: 270 { 271 cli_help (prog_name_p, "generate", generate_opts); 272 return JERRY_STANDALONE_EXIT_CODE_OK; 273 } 274 case OPT_GENERATE_STATIC: 275 { 276 snapshot_flags |= JERRY_SNAPSHOT_SAVE_STATIC; 277 break; 278 } 279 case OPT_GENERATE_FUNCTION: 280 { 281 function_args_p = cli_consume_string (cli_state_p); 282 break; 283 } 284 case OPT_IMPORT_LITERAL_LIST: 285 { 286 literals_file_name_p = cli_consume_string (cli_state_p); 287 break; 288 } 289 case OPT_GENERATE_SHOW_OP: 290 { 291 if (check_feature (JERRY_FEATURE_PARSER_DUMP, cli_state_p->arg)) 292 { 293 jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG); 294 flags |= JERRY_INIT_SHOW_OPCODES; 295 } 296 break; 297 } 298 case OPT_GENERATE_OUT: 299 { 300 output_file_name_p = cli_consume_string (cli_state_p); 301 break; 302 } 303 case CLI_OPT_DEFAULT: 304 { 305 if (file_name_p != NULL) 306 { 307 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Exactly one input file must be specified\n"); 308 return JERRY_STANDALONE_EXIT_CODE_FAIL; 309 } 310 311 file_name_p = cli_consume_string (cli_state_p); 312 313 if (cli_state_p->error == NULL) 314 { 315 source_length = read_file (source_p, file_name_p); 316 317 if (source_length == 0) 318 { 319 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Input file is empty\n"); 320 return JERRY_STANDALONE_EXIT_CODE_FAIL; 321 } 322 } 323 break; 324 } 325 default: 326 { 327 cli_state_p->error = "Internal error"; 328 break; 329 } 330 } 331 } 332 333 if (check_cli_error (cli_state_p)) 334 { 335 return JERRY_STANDALONE_EXIT_CODE_FAIL; 336 } 337 338 if (file_name_p == NULL) 339 { 340 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Exactly one input file must be specified\n"); 341 return JERRY_STANDALONE_EXIT_CODE_FAIL; 342 } 343 344#if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) 345 context_init (); 346#endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */ 347 348 jerry_init (flags); 349 350 if (!jerry_is_valid_utf8_string (source_p, (jerry_size_t) source_length)) 351 { 352 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Input must be a valid UTF-8 string.\n"); 353 jerry_cleanup (); 354 return JERRY_STANDALONE_EXIT_CODE_FAIL; 355 } 356 357 if (literals_file_name_p != NULL) 358 { 359 /* Import literal list */ 360 uint8_t *sp_buffer_start_p = source_p + source_length + 1; 361 size_t sp_buffer_size = read_file (sp_buffer_start_p, literals_file_name_p); 362 363 if (sp_buffer_size > 0) 364 { 365 const char *sp_buffer_p = (const char *) sp_buffer_start_p; 366 uint32_t num_of_lit = 0; 367 368 do 369 { 370 char *sp_buffer_end_p = NULL; 371 jerry_length_t mstr_size = (jerry_length_t) strtol (sp_buffer_p, &sp_buffer_end_p, 10); 372 if (mstr_size > 0) 373 { 374 magic_string_items[num_of_lit] = (jerry_char_t *) (sp_buffer_end_p + 1); 375 magic_string_lengths[num_of_lit] = mstr_size; 376 num_of_lit++; 377 } 378 sp_buffer_p = sp_buffer_end_p + mstr_size + 1; 379 } 380 while ((size_t) (sp_buffer_p - (char *) sp_buffer_start_p) < sp_buffer_size); 381 382 if (num_of_lit > 0) 383 { 384 jerry_register_magic_strings (magic_string_items, num_of_lit, 385 magic_string_lengths); 386 } 387 } 388 } 389 390 jerry_value_t snapshot_result; 391 392 if (function_args_p != NULL) 393 { 394 snapshot_result = jerry_generate_function_snapshot ((jerry_char_t *) file_name_p, 395 (size_t) strlen (file_name_p), 396 (jerry_char_t *) source_p, 397 source_length, 398 (const jerry_char_t *) function_args_p, 399 strlen (function_args_p), 400 snapshot_flags, 401 output_buffer, 402 sizeof (output_buffer) / sizeof (uint32_t)); 403 } 404 else 405 { 406 snapshot_result = jerry_generate_snapshot ((jerry_char_t *) file_name_p, 407 (size_t) strlen (file_name_p), 408 (jerry_char_t *) source_p, 409 source_length, 410 snapshot_flags, 411 output_buffer, 412 sizeof (output_buffer) / sizeof (uint32_t)); 413 } 414 415 if (jerry_value_is_error (snapshot_result)) 416 { 417 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Generating snapshot failed!\n"); 418 419 snapshot_result = jerry_get_value_from_error (snapshot_result, true); 420 421 print_unhandled_exception (snapshot_result); 422 423 jerry_release_value (snapshot_result); 424 jerry_cleanup (); 425 return JERRY_STANDALONE_EXIT_CODE_FAIL; 426 } 427 428 size_t snapshot_size = (size_t) jerry_get_number_value (snapshot_result); 429 jerry_release_value (snapshot_result); 430 431 FILE *snapshot_file_p = fopen (output_file_name_p, "wb"); 432 if (snapshot_file_p == NULL) 433 { 434 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Unable to write snapshot file: '%s'\n", output_file_name_p); 435 jerry_cleanup (); 436 return JERRY_STANDALONE_EXIT_CODE_FAIL; 437 } 438 439 fwrite (output_buffer, sizeof (uint8_t), snapshot_size, snapshot_file_p); 440 fclose (snapshot_file_p); 441 442 printf ("Created snapshot file: '%s' (%zu bytes)\n", output_file_name_p, snapshot_size); 443 444 jerry_cleanup (); 445 return JERRY_STANDALONE_EXIT_CODE_OK; 446} /* process_generate */ 447 448/** 449 * Literal dump command line option IDs 450 */ 451typedef enum 452{ 453 OPT_LITERAL_DUMP_HELP, 454 OPT_LITERAL_DUMP_FORMAT, 455 OPT_LITERAL_DUMP_OUT, 456} literal_dump_opt_id_t; 457 458/** 459 * Literal dump command line options 460 */ 461static const cli_opt_t literal_dump_opts[] = 462{ 463 CLI_OPT_DEF (.id = OPT_LITERAL_DUMP_HELP, .opt = "h", .longopt = "help", 464 .help = "print this help and exit"), 465 CLI_OPT_DEF (.id = OPT_LITERAL_DUMP_FORMAT, .longopt = "format", 466 .meta = "[c|list]", 467 .help = "specify output format (default: list)"), 468 CLI_OPT_DEF (.id = OPT_LITERAL_DUMP_OUT, .opt = "o", 469 .help = "specify output file name (default: literals.[h|list])"), 470 CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE(S)", 471 .help = "input snapshot files") 472}; 473 474/** 475 * Process 'litdump' command. 476 * 477 * @return error code (0 - no error) 478 */ 479static int 480process_literal_dump (cli_state_t *cli_state_p, /**< cli state */ 481 int argc, /**< number of arguments */ 482 char *prog_name_p) /**< program name */ 483{ 484 uint8_t *input_pos_p = input_buffer; 485 486 cli_change_opts (cli_state_p, literal_dump_opts); 487 488 JERRY_VLA (const uint32_t *, snapshot_buffers, argc); 489 JERRY_VLA (size_t, snapshot_buffer_sizes, argc); 490 uint32_t number_of_files = 0; 491 const char *literals_file_name_p = NULL; 492 bool is_c_format = false; 493 494 for (int id = cli_consume_option (cli_state_p); id != CLI_OPT_END; id = cli_consume_option (cli_state_p)) 495 { 496 switch (id) 497 { 498 case OPT_LITERAL_DUMP_HELP: 499 { 500 cli_help (prog_name_p, "litdump", literal_dump_opts); 501 return JERRY_STANDALONE_EXIT_CODE_OK; 502 } 503 case OPT_LITERAL_DUMP_FORMAT: 504 { 505 const char *fromat_str_p = cli_consume_string (cli_state_p); 506 if (!strcmp ("c", fromat_str_p)) 507 { 508 is_c_format = true; 509 } 510 else if (!strcmp ("list", fromat_str_p)) 511 { 512 is_c_format = false; 513 } 514 else 515 { 516 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: Unsupported literal dump format."); 517 return JERRY_STANDALONE_EXIT_CODE_FAIL; 518 } 519 break; 520 } 521 case OPT_LITERAL_DUMP_OUT: 522 { 523 literals_file_name_p = cli_consume_string (cli_state_p); 524 break; 525 } 526 case CLI_OPT_DEFAULT: 527 { 528 const char *file_name_p = cli_consume_string (cli_state_p); 529 530 if (cli_state_p->error == NULL) 531 { 532 size_t size = read_file (input_pos_p, file_name_p); 533 534 if (size == 0) 535 { 536 return JERRY_STANDALONE_EXIT_CODE_FAIL; 537 } 538 539 snapshot_buffers[number_of_files] = (const uint32_t *) input_pos_p; 540 snapshot_buffer_sizes[number_of_files] = size; 541 542 number_of_files++; 543 const uintptr_t mask = sizeof (uint32_t) - 1; 544 input_pos_p = (uint8_t *) ((((uintptr_t) input_pos_p) + size + mask) & ~mask); 545 } 546 break; 547 } 548 default: 549 { 550 cli_state_p->error = "Internal error"; 551 break; 552 } 553 } 554 } 555 556 if (check_cli_error (cli_state_p)) 557 { 558 return JERRY_STANDALONE_EXIT_CODE_FAIL; 559 } 560 561 if (number_of_files < 1) 562 { 563 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: at least one input file must be specified.\n"); 564 return JERRY_STANDALONE_EXIT_CODE_FAIL; 565 } 566 567#if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) 568 context_init (); 569#endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */ 570 571 jerry_init (JERRY_INIT_EMPTY); 572 573 size_t lit_buf_sz = 0; 574 if (number_of_files == 1) 575 { 576 lit_buf_sz = jerry_get_literals_from_snapshot (snapshot_buffers[0], 577 snapshot_buffer_sizes[0], 578 literal_buffer, 579 JERRY_BUFFER_SIZE, 580 is_c_format); 581 } 582 else 583 { 584 /* The input contains more than one input snapshot file, so we must merge them first. */ 585 const char *error_p = NULL; 586 size_t merged_snapshot_size = jerry_merge_snapshots (snapshot_buffers, 587 snapshot_buffer_sizes, 588 number_of_files, 589 output_buffer, 590 JERRY_BUFFER_SIZE, 591 &error_p); 592 593 if (merged_snapshot_size == 0) 594 { 595 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", error_p); 596 jerry_cleanup (); 597 return JERRY_STANDALONE_EXIT_CODE_FAIL; 598 } 599 600 printf ("Successfully merged the input snapshots (%"PRI_SIZET" bytes).\n", (SIZE_T_TYPE)merged_snapshot_size); 601 602 lit_buf_sz = jerry_get_literals_from_snapshot (output_buffer, 603 merged_snapshot_size, 604 literal_buffer, 605 JERRY_BUFFER_SIZE, 606 is_c_format); 607 } 608 609 if (lit_buf_sz == 0) 610 { 611 jerry_port_log (JERRY_LOG_LEVEL_ERROR, 612 "Error: Literal saving failed! No literals were found in the input snapshot(s).\n"); 613 jerry_cleanup (); 614 return JERRY_STANDALONE_EXIT_CODE_FAIL; 615 } 616 617 if (literals_file_name_p == NULL) 618 { 619 literals_file_name_p = is_c_format ? "literals.h" : "literals.list"; 620 } 621 622 FILE *file_p = fopen (literals_file_name_p, "wb"); 623 624 if (file_p == NULL) 625 { 626 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: cannot open file: '%s'\n", literals_file_name_p); 627 jerry_cleanup (); 628 return JERRY_STANDALONE_EXIT_CODE_FAIL; 629 } 630 631 fwrite (literal_buffer, sizeof (uint8_t), lit_buf_sz, file_p); 632 fclose (file_p); 633 634 printf ("Literals are saved into '%s' (%"PRI_SIZET" bytes).\n", literals_file_name_p, (SIZE_T_TYPE)lit_buf_sz); 635 636 jerry_cleanup (); 637 return JERRY_STANDALONE_EXIT_CODE_OK; 638} /* process_literal_dump */ 639 640/** 641 * Merge command line option IDs 642 */ 643typedef enum 644{ 645 OPT_MERGE_HELP, 646 OPT_MERGE_OUT, 647} merge_opt_id_t; 648 649/** 650 * Merge command line options 651 */ 652static const cli_opt_t merge_opts[] = 653{ 654 CLI_OPT_DEF (.id = OPT_MERGE_HELP, .opt = "h", .longopt = "help", 655 .help = "print this help and exit"), 656 CLI_OPT_DEF (.id = OPT_MERGE_OUT, .opt = "o", 657 .help = "specify output file name (default: js.snapshot)"), 658 CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE", 659 .help = "input snapshot files, minimum two") 660}; 661 662/** 663 * Process 'merge' command. 664 * 665 * @return error code (0 - no error) 666 */ 667static int 668process_merge (cli_state_t *cli_state_p, /**< cli state */ 669 int argc, /**< number of arguments */ 670 char *prog_name_p) /**< program name */ 671{ 672 uint8_t *input_pos_p = input_buffer; 673 674 cli_change_opts (cli_state_p, merge_opts); 675 676 JERRY_VLA (const uint32_t *, merge_buffers, argc); 677 JERRY_VLA (size_t, merge_buffer_sizes, argc); 678 uint32_t number_of_files = 0; 679 680 for (int id = cli_consume_option (cli_state_p); id != CLI_OPT_END; id = cli_consume_option (cli_state_p)) 681 { 682 switch (id) 683 { 684 case OPT_MERGE_HELP: 685 { 686 cli_help (prog_name_p, "merge", merge_opts); 687 return JERRY_STANDALONE_EXIT_CODE_OK; 688 } 689 case OPT_MERGE_OUT: 690 { 691 output_file_name_p = cli_consume_string (cli_state_p); 692 break; 693 } 694 case CLI_OPT_DEFAULT: 695 { 696 const char *file_name_p = cli_consume_string (cli_state_p); 697 698 if (cli_state_p->error == NULL) 699 { 700 size_t size = read_file (input_pos_p, file_name_p); 701 702 if (size == 0) 703 { 704 return JERRY_STANDALONE_EXIT_CODE_FAIL; 705 } 706 707 merge_buffers[number_of_files] = (const uint32_t *) input_pos_p; 708 merge_buffer_sizes[number_of_files] = size; 709 710 number_of_files++; 711 const uintptr_t mask = sizeof (uint32_t) - 1; 712 input_pos_p = (uint8_t *) ((((uintptr_t) input_pos_p) + size + mask) & ~mask); 713 } 714 break; 715 } 716 default: 717 { 718 cli_state_p->error = "Internal error"; 719 break; 720 } 721 } 722 } 723 724 if (check_cli_error (cli_state_p)) 725 { 726 return JERRY_STANDALONE_EXIT_CODE_FAIL; 727 } 728 729 if (number_of_files < 2) 730 { 731 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: at least two input files must be passed.\n"); 732 return JERRY_STANDALONE_EXIT_CODE_FAIL; 733 } 734 735#if defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) 736 context_init (); 737#endif /* defined (JERRY_EXTERNAL_CONTEXT) && (JERRY_EXTERNAL_CONTEXT == 1) */ 738 739 jerry_init (JERRY_INIT_EMPTY); 740 741 const char *error_p = NULL; 742 size_t merged_snapshot_size = jerry_merge_snapshots (merge_buffers, 743 merge_buffer_sizes, 744 number_of_files, 745 output_buffer, 746 JERRY_BUFFER_SIZE, 747 &error_p); 748 749 if (merged_snapshot_size == 0) 750 { 751 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", error_p); 752 jerry_cleanup (); 753 return JERRY_STANDALONE_EXIT_CODE_FAIL; 754 } 755 756 FILE *file_p = fopen (output_file_name_p, "wb"); 757 758 if (file_p == NULL) 759 { 760 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: cannot open file: '%s'\n", output_file_name_p); 761 jerry_cleanup (); 762 return JERRY_STANDALONE_EXIT_CODE_FAIL; 763 } 764 765 fwrite (output_buffer, 1u, merged_snapshot_size, file_p); 766 fclose (file_p); 767 768 printf ("Merge is completed. Merged snapshot is saved into '%s' (%"PRI_SIZET" bytes).\n", 769 output_file_name_p, 770 (SIZE_T_TYPE)merged_snapshot_size); 771 772 jerry_cleanup (); 773 return JERRY_STANDALONE_EXIT_CODE_OK; 774} /* process_merge */ 775 776/** 777 * Command line option IDs 778 */ 779typedef enum 780{ 781 OPT_HELP, 782} main_opt_id_t; 783 784/** 785 * Command line options 786 */ 787static const cli_opt_t main_opts[] = 788{ 789 CLI_OPT_DEF (.id = OPT_HELP, .opt = "h", .longopt = "help", 790 .help = "print this help and exit"), 791 CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "COMMAND", 792 .help = "specify the command") 793}; 794 795/** 796 * Print available commands. 797 */ 798static void 799print_commands (char *prog_name_p) /**< program name */ 800{ 801 cli_help (prog_name_p, NULL, main_opts); 802 803 printf ("\nAvailable commands:\n" 804 " generate\n" 805 " litdump\n" 806 " merge\n" 807 "\nPassing -h or --help after a command displays its help.\n"); 808} /* print_commands */ 809 810/** 811 * Main function. 812 * 813 * @return error code (0 - no error) 814 */ 815int 816main (int argc, /**< number of arguments */ 817 char **argv) /**< argument list */ 818{ 819 cli_state_t cli_state = cli_init (main_opts, argc - 1, argv + 1); 820 821 for (int id = cli_consume_option (&cli_state); id != CLI_OPT_END; id = cli_consume_option (&cli_state)) 822 { 823 switch (id) 824 { 825 case OPT_MERGE_HELP: 826 { 827 /* Help is always printed if no command is provided. */ 828 break; 829 } 830 case CLI_OPT_DEFAULT: 831 { 832 const char *command_p = cli_consume_string (&cli_state); 833 834 if (cli_state.error != NULL) 835 { 836 break; 837 } 838 839 if (!strcmp ("merge", command_p)) 840 { 841 return process_merge (&cli_state, argc, argv[0]); 842 } 843 else if (!strcmp ("litdump", command_p)) 844 { 845 return process_literal_dump (&cli_state, argc, argv[0]); 846 } 847 else if (!strcmp ("generate", command_p)) 848 { 849 return process_generate (&cli_state, argc, argv[0]); 850 } 851 852 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: unknown command: %s\n\n", command_p); 853 print_commands (argv[0]); 854 855 return JERRY_STANDALONE_EXIT_CODE_FAIL; 856 } 857 default: 858 { 859 cli_state.error = "Internal error"; 860 break; 861 } 862 } 863 } 864 865 if (check_cli_error (&cli_state)) 866 { 867 return JERRY_STANDALONE_EXIT_CODE_FAIL; 868 } 869 870 print_commands (argv[0]); 871 return JERRY_STANDALONE_EXIT_CODE_OK; 872} /* main */ 873