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
55 static uint8_t input_buffer[JERRY_BUFFER_SIZE];
56 static uint32_t output_buffer[JERRY_BUFFER_SIZE / 4];
57 static jerry_char_t literal_buffer[JERRY_BUFFER_SIZE];
58 static const char *output_file_name_p = "js.snapshot";
59 static jerry_length_t magic_string_lengths[JERRY_LITERAL_LENGTH];
60 static 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 */
66 static void *
context_alloc(size_t size,void * cb_data_p)67 context_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 */
77 static void
context_init(void)78 context_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 */
92 static bool
check_feature(jerry_feature_t feature,const char * option)93 check_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 */
111 static bool
check_cli_error(const cli_state_t * const cli_state_p)112 check_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 */
137 static size_t
read_file(uint8_t * input_pos_p,const char * file_name)138 read_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 */
173 static void
print_unhandled_exception(jerry_value_t error_value)174 print_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 */
209 typedef 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 */
222 static 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 */
247 static int
process_generate(cli_state_t * cli_state_p,int argc,char * prog_name_p)248 process_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 */
451 typedef 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 */
461 static 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 */
479 static int
process_literal_dump(cli_state_t * cli_state_p,int argc,char * prog_name_p)480 process_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 */
643 typedef enum
644 {
645 OPT_MERGE_HELP,
646 OPT_MERGE_OUT,
647 } merge_opt_id_t;
648
649 /**
650 * Merge command line options
651 */
652 static 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 */
667 static int
process_merge(cli_state_t * cli_state_p,int argc,char * prog_name_p)668 process_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 */
779 typedef enum
780 {
781 OPT_HELP,
782 } main_opt_id_t;
783
784 /**
785 * Command line options
786 */
787 static 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 */
798 static void
print_commands(char * prog_name_p)799 print_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 */
815 int
main(int argc,char ** argv)816 main (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