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 42void set_log_level (jerry_log_level_t level); 43 44/** 45 * Print usage and available options 46 */ 47static void 48print_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 */ 70static const uint8_t * 71read_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 */ 130static uint32_t 131str_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 */ 156static void 157print_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 */ 282static void 283register_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 303int main (int argc, FAR char *argv[]) 304#else 305int 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