• 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 <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 
20 #include "jerryscript.h"
21 #include "jerryscript-ext/debugger.h"
22 #include "jerryscript-ext/handler.h"
23 #include "jerryscript-port.h"
24 #include "setjmp.h"
25 
26 /**
27  * Maximum command line arguments number.
28  */
29 #define JERRY_MAX_COMMAND_LINE_ARGS (16)
30 
31 /**
32  * Standalone Jerry exit codes.
33  */
34 #define JERRY_STANDALONE_EXIT_CODE_OK   (0)
35 #define JERRY_STANDALONE_EXIT_CODE_FAIL (1)
36 
37 /**
38  * Context size of the SYNTAX_ERROR
39  */
40 #define SYNTAX_ERROR_CONTEXT_SIZE 2
41 
42 void set_log_level (jerry_log_level_t level);
43 
44 /**
45  * Print usage and available options
46  */
47 static void
print_help(char * name)48 print_help (char *name)
49 {
50   printf ("Usage: %s [OPTION]... [FILE]...\n"
51           "\n"
52           "Options:\n"
53           "  --log-level [0-3]\n"
54           "  --mem-stats\n"
55           "  --mem-stats-separate\n"
56           "  --show-opcodes\n"
57           "  --start-debug-server\n"
58           "  --debug-server-port [port]\n"
59           "\n",
60           name);
61 } /* print_help */
62 
63 /**
64  * Read source code into buffer.
65  *
66  * Returned value must be freed with jmem_heap_free_block if it's not NULL.
67  * @return NULL, if read or allocation has failed
68  *         pointer to the allocated memory block, otherwise
69  */
70 static const uint8_t *
read_file(const char * file_name,size_t * out_size_p)71 read_file (const char *file_name, /**< source code */
72            size_t *out_size_p) /**< [out] number of bytes successfully read from source */
73 {
74   FILE *file = fopen (file_name, "r");
75   if (file == NULL)
76   {
77     jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: cannot open file: %s\n", file_name);
78     return NULL;
79   }
80 
81   int fseek_status = fseek (file, 0, SEEK_END);
82   if (fseek_status != 0)
83   {
84     jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Failed to seek (error: %d)\n", fseek_status);
85     fclose (file);
86     return NULL;
87   }
88 
89   long script_len = ftell (file);
90   if (script_len < 0)
91   {
92     jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Failed to get the file size(error %ld)\n", script_len);
93     fclose (file);
94     return NULL;
95   }
96 
97   rewind (file);
98 
99   uint8_t *buffer = (uint8_t *) malloc (script_len);
100 
101   if (buffer == NULL)
102   {
103     jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Out of memory error\n");
104     fclose (file);
105     return NULL;
106   }
107 
108   size_t bytes_read = fread (buffer, 1u, script_len, file);
109 
110   if (!bytes_read || bytes_read != script_len)
111   {
112     jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to read file: %s\n", file_name);
113     free ((void*) buffer);
114 
115     fclose (file);
116     return NULL;
117   }
118 
119   fclose (file);
120 
121   *out_size_p = bytes_read;
122   return (const uint8_t *) buffer;
123 } /* read_file */
124 
125 /**
126  * Convert string into unsigned integer
127  *
128  * @return converted number
129  */
130 static uint32_t
str_to_uint(const char * num_str_p,char ** out_p)131 str_to_uint (const char *num_str_p, /**< string to convert */
132              char **out_p) /**< [out] end of the number */
133 {
134   assert (jerry_is_feature_enabled (JERRY_FEATURE_ERROR_MESSAGES));
135 
136   uint32_t result = 0;
137 
138   while (*num_str_p >= '0' && *num_str_p <= '9')
139   {
140     result *= 10;
141     result += (uint32_t) (*num_str_p - '0');
142     num_str_p++;
143   }
144 
145   if (out_p != NULL)
146   {
147     *out_p = num_str_p;
148   }
149 
150   return result;
151 } /* str_to_uint */
152 
153 /**
154  * Print error value
155  */
156 static void
print_unhandled_exception(jerry_value_t error_value)157 print_unhandled_exception (jerry_value_t error_value) /**< error value */
158 {
159   assert (jerry_value_is_error (error_value));
160 
161   error_value = jerry_get_value_from_error (error_value, false);
162   jerry_value_t err_str_val = jerry_value_to_string (error_value);
163   jerry_size_t err_str_size = jerry_get_utf8_string_size (err_str_val);
164   jerry_char_t err_str_buf[256];
165 
166   jerry_release_value (error_value);
167 
168   if (err_str_size >= 256)
169   {
170     const char msg[] = "[Error message too long]";
171     err_str_size = sizeof (msg) / sizeof (char) - 1;
172     memcpy (err_str_buf, msg, err_str_size);
173   }
174   else
175   {
176     jerry_size_t sz = jerry_string_to_utf8_char_buffer (err_str_val, err_str_buf, err_str_size);
177     assert (sz == err_str_size);
178     err_str_buf[err_str_size] = 0;
179 
180     if (jerry_is_feature_enabled (JERRY_FEATURE_ERROR_MESSAGES)
181         && jerry_get_error_type (error_value) == JERRY_ERROR_SYNTAX)
182     {
183       jerry_char_t *string_end_p = err_str_buf + sz;
184       uint32_t err_line = 0;
185       uint32_t err_col = 0;
186       char *path_str_p = NULL;
187       char *path_str_end_p = NULL;
188 
189       /* 1. parse column and line information */
190       for (jerry_char_t *current_p = err_str_buf; current_p < string_end_p; current_p++)
191       {
192         if (*current_p == '[')
193         {
194           current_p++;
195 
196           if (*current_p == '<')
197           {
198             break;
199           }
200 
201           path_str_p = (char *) current_p;
202           while (current_p < string_end_p && *current_p != ':')
203           {
204             current_p++;
205           }
206 
207           path_str_end_p = (char *) current_p++;
208 
209           err_line = str_to_uint ((char *) current_p, (char **) &current_p);
210 
211           current_p++;
212 
213           err_col = str_to_uint ((char *) current_p, NULL);
214           break;
215         }
216       } /* for */
217 
218       if (err_line != 0 && err_col != 0)
219       {
220         uint32_t curr_line = 1;
221 
222         bool is_printing_context = false;
223         uint32_t pos = 0;
224 
225         /* Temporarily modify the error message, so we can use the path. */
226         *path_str_end_p = '\0';
227 
228         size_t source_size;
229         const jerry_char_t *source_p = read_file (path_str_p, &source_size);
230 
231         /* Revert the error message. */
232         *path_str_end_p = ':';
233 
234         /* 2. seek and print */
235         while (source_p[pos] != '\0')
236         {
237           if (source_p[pos] == '\n')
238           {
239             curr_line++;
240           }
241 
242           if (err_line < SYNTAX_ERROR_CONTEXT_SIZE
243               || (err_line >= curr_line
244                   && (err_line - curr_line) <= SYNTAX_ERROR_CONTEXT_SIZE))
245           {
246             /* context must be printed */
247             is_printing_context = true;
248           }
249 
250           if (curr_line > err_line)
251           {
252             break;
253           }
254 
255           if (is_printing_context)
256           {
257             jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%c", source_p[pos]);
258           }
259 
260           pos++;
261         }
262 
263         jerry_port_log (JERRY_LOG_LEVEL_ERROR, "\n");
264 
265         while (--err_col)
266         {
267           jerry_port_log (JERRY_LOG_LEVEL_ERROR, "~");
268         }
269 
270         jerry_port_log (JERRY_LOG_LEVEL_ERROR, "^\n");
271       }
272     }
273   }
274 
275   jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Script Error: %s\n", err_str_buf);
276   jerry_release_value (err_str_val);
277 } /* print_unhandled_exception */
278 
279 /**
280  * Register a JavaScript function in the global object.
281  */
282 static void
register_js_function(const char * name_p,jerry_external_handler_t handler_p)283 register_js_function (const char *name_p, /**< name of the function */
284                       jerry_external_handler_t handler_p) /**< function callback */
285 {
286   jerry_value_t result_val = jerryx_handler_register_global ((const jerry_char_t *) name_p, handler_p);
287 
288   if (jerry_value_is_error (result_val))
289   {
290     jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Warning: failed to register '%s' method.", name_p);
291   }
292 
293   jerry_release_value (result_val);
294 } /* register_js_function */
295 
296 
297 /**
298  * Main program.
299  *
300  * @return 0 if success, error code otherwise
301  */
302 #ifdef CONFIG_BUILD_KERNEL
main(int argc,FAR char * argv[])303 int main (int argc, FAR char *argv[])
304 #else
305 int jerry_main (int argc, char *argv[])
306 #endif
307 {
308   if (argc > JERRY_MAX_COMMAND_LINE_ARGS)
309   {
310     jerry_port_log (JERRY_LOG_LEVEL_ERROR,
311                     "Too many command line arguments. Current maximum is %d\n",
312                     JERRY_MAX_COMMAND_LINE_ARGS);
313 
314     return JERRY_STANDALONE_EXIT_CODE_FAIL;
315   }
316 
317   const char *file_names[JERRY_MAX_COMMAND_LINE_ARGS];
318   int i;
319   int files_counter = 0;
320   bool start_debug_server = false;
321   uint16_t debug_port = 5001;
322 
323   jerry_init_flag_t flags = JERRY_INIT_EMPTY;
324 
325   for (i = 1; i < argc; i++)
326   {
327     if (!strcmp ("-h", argv[i]) || !strcmp ("--help", argv[i]))
328     {
329       print_help (argv[0]);
330       return JERRY_STANDALONE_EXIT_CODE_OK;
331     }
332     else if (!strcmp ("--mem-stats", argv[i]))
333     {
334       flags |= JERRY_INIT_MEM_STATS;
335       set_log_level (JERRY_LOG_LEVEL_DEBUG);
336     }
337     else if (!strcmp ("--mem-stats-separate", argv[i]))
338     {
339       flags |= JERRY_INIT_MEM_STATS_SEPARATE;
340       set_log_level (JERRY_LOG_LEVEL_DEBUG);
341     }
342     else if (!strcmp ("--show-opcodes", argv[i]))
343     {
344       flags |= JERRY_INIT_SHOW_OPCODES | JERRY_INIT_SHOW_REGEXP_OPCODES;
345       set_log_level (JERRY_LOG_LEVEL_DEBUG);
346     }
347     else if (!strcmp ("--log-level", argv[i]))
348     {
349       if (++i < argc && strlen (argv[i]) == 1 && argv[i][0] >='0' && argv[i][0] <= '3')
350       {
351         set_log_level (argv[i][0] - '0');
352       }
353       else
354       {
355         jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: wrong format or invalid argument\n");
356         return JERRY_STANDALONE_EXIT_CODE_FAIL;
357       }
358     }
359     else if (!strcmp ("--start-debug-server", argv[i]))
360     {
361       start_debug_server = true;
362     }
363     else if (!strcmp ("--debug-server-port", argv[i]))
364     {
365       if (++i < argc)
366       {
367         debug_port = str_to_uint (argv[i], NULL);
368       }
369       else
370       {
371         jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: wrong format or invalid argument\n");
372         return JERRY_STANDALONE_EXIT_CODE_FAIL;
373       }
374     }
375     else
376     {
377       file_names[files_counter++] = argv[i];
378     }
379   }
380 
381   jerry_init (flags);
382 
383   if (start_debug_server)
384   {
385     jerryx_debugger_after_connect (jerryx_debugger_tcp_create (debug_port)
386                                    && jerryx_debugger_ws_create ());
387   }
388 
389   register_js_function ("assert", jerryx_handler_assert);
390   register_js_function ("gc", jerryx_handler_gc);
391   register_js_function ("print", jerryx_handler_print);
392 
393   jerry_value_t ret_value = jerry_create_undefined ();
394 
395   if (files_counter == 0)
396   {
397     printf ("No input files, running a hello world demo:\n");
398     const jerry_char_t script[] = "var str = 'Hello World'; print(str + ' from JerryScript')";
399 
400     ret_value = jerry_parse (NULL, 0, script, sizeof (script) - 1, JERRY_PARSE_NO_OPTS);
401 
402     if (!jerry_value_is_error (ret_value))
403     {
404       ret_value = jerry_run (ret_value);
405     }
406   }
407   else
408   {
409     for (i = 0; i < files_counter; i++)
410     {
411       size_t source_size;
412       const jerry_char_t *source_p = read_file (file_names[i], &source_size);
413 
414       if (source_p == NULL)
415       {
416         jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Source file load error\n");
417         return JERRY_STANDALONE_EXIT_CODE_FAIL;
418       }
419 
420       ret_value = jerry_parse ((jerry_char_t *) file_names[i],
421                                strlen (file_names[i]),
422                                source_p,
423                                source_size,
424                                JERRY_PARSE_NO_OPTS);
425       free ((void*) source_p);
426 
427       if (!jerry_value_is_error (ret_value))
428       {
429         jerry_value_t func_val = ret_value;
430         ret_value = jerry_run (func_val);
431         jerry_release_value (func_val);
432       }
433 
434       if (jerry_value_is_error (ret_value))
435       {
436         print_unhandled_exception (ret_value);
437         break;
438       }
439 
440       jerry_release_value (ret_value);
441       ret_value = jerry_create_undefined ();
442     }
443   }
444 
445   int ret_code = JERRY_STANDALONE_EXIT_CODE_OK;
446 
447   if (jerry_value_is_error (ret_value))
448   {
449     ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL;
450   }
451 
452   jerry_release_value (ret_value);
453 
454   ret_value = jerry_run_all_enqueued_jobs ();
455 
456   if (jerry_value_is_error (ret_value))
457   {
458     ret_code = JERRY_STANDALONE_EXIT_CODE_FAIL;
459   }
460 
461   jerry_release_value (ret_value);
462   jerry_cleanup ();
463 
464   return ret_code;
465 } /* main */
466