• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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