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 **) ¤t_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