MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

clean error handling

+188 -60
+1 -1
meson.build
··· 74 74 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 75 75 76 76 version_conf = configuration_data() 77 - version_conf.set('ANT_VERSION', '0.1.0.7') 77 + version_conf.set('ANT_VERSION', '0.1.0.8') 78 78 version_conf.set('ANT_GIT_HASH', git_hash) 79 79 version_conf.set('ANT_BUILD_DATE', build_date) 80 80
+174 -54
src/ant.c
··· 204 204 jsoff_t css; // max observed C stack size 205 205 jsoff_t lwm; // JS ram low watermark: min free ram observed 206 206 const char *code; // currently parsed code snippet 207 - char errmsg[256]; // error message placeholder (increased from 33) 207 + char *errmsg; // dynamic error message buffer 208 + size_t errmsg_size; // size of error message buffer 208 209 const char *filename; // current filename for error reporting 209 210 uint8_t tok; // last parsed token value 210 211 uint8_t consumed; // indicator that last parsed token was consumed ··· 1359 1360 *line_start_col = (int)(pos - line_start) + 1; 1360 1361 } 1361 1362 1363 + static void format_error_stack(struct js *js, size_t *n, int line, int col, bool include_source_line, const char *error_line, int error_col) { 1364 + if (!js->errmsg) { 1365 + js->errmsg_size = 4096; 1366 + js->errmsg = (char *)malloc(js->errmsg_size); 1367 + if (!js->errmsg) return; 1368 + } 1369 + 1370 + const char *dim = "\x1b[90m"; 1371 + const char *reset = "\x1b[0m"; 1372 + size_t remaining; 1373 + 1374 + if (include_source_line && error_line && *n < js->errmsg_size) { 1375 + remaining = js->errmsg_size - *n; 1376 + *n += (size_t) snprintf(js->errmsg + *n, remaining, "\n%s\n", error_line); 1377 + 1378 + if (*n < js->errmsg_size - 1) { 1379 + remaining = js->errmsg_size - *n; 1380 + for (int i = 1; i < error_col && remaining > 1; i++) { 1381 + js->errmsg[(*n)++] = ' '; 1382 + remaining--; 1383 + } 1384 + if (remaining > 1) { 1385 + js->errmsg[(*n)++] = '^'; 1386 + } 1387 + js->errmsg[*n] = '\0'; 1388 + } 1389 + } 1390 + 1391 + remaining = js->errmsg_size - *n; 1392 + if (remaining > 20) { 1393 + const char *file = js->filename ? js->filename : "<eval>"; 1394 + 1395 + for (int i = global_call_stack.depth - 1; i >= 0 && remaining > 20; i--) { 1396 + call_frame_t *frame = &global_call_stack.frames[i]; 1397 + const char *fname = frame->function_name ? frame->function_name : "<anonymous>"; 1398 + const char *ffile = frame->filename ? frame->filename : "<eval>"; 1399 + 1400 + *n += (size_t) snprintf(js->errmsg + *n, remaining, "\n at %s %s(%s:%d:%d)%s", fname, dim, ffile, frame->line, frame->col, reset); 1401 + remaining = js->errmsg_size - *n; 1402 + } 1403 + 1404 + if (global_call_stack.depth > 0 && remaining > 60) { 1405 + *n += (size_t) snprintf(js->errmsg + *n, remaining, "\n at Object.<anonymous> %s(%s:1:1)%s", dim, file, reset); 1406 + remaining = js->errmsg_size - *n; 1407 + } 1408 + 1409 + if (global_call_stack.depth == 0 && remaining > 20) { 1410 + *n += (size_t) snprintf(js->errmsg + *n, remaining, "\n at %s%s:%d:%d%s", dim, file, line, col, reset); 1411 + remaining = js->errmsg_size - *n; 1412 + } 1413 + 1414 + if (remaining > 60 && js->filename && strcmp(js->filename, "[eval]") != 0) { 1415 + *n += (size_t) snprintf(js->errmsg + *n, remaining, "\n at Module.executeUserEntryPoint [as runMain] %s(ant:internal/modules/run_main:70:5)%s", dim, reset); 1416 + remaining = js->errmsg_size - *n; 1417 + } 1418 + 1419 + if (remaining > 40 && js->filename && strcmp(js->filename, "[eval]") != 0) { 1420 + *n += (size_t) snprintf(js->errmsg + *n, remaining, "\n at %sant:internal/main:217:5%s", dim, reset); 1421 + } 1422 + } 1423 + 1424 + js->errmsg[js->errmsg_size - 1] = '\0'; 1425 + } 1426 + 1362 1427 jsval_t js_mkerr(struct js *js, const char *xx, ...) { 1363 1428 va_list ap; 1364 1429 int line = 0, col = 0; 1365 1430 char error_line[256] = {0}; 1366 1431 int error_col = 0; 1432 + char error_msg[256] = {0}; 1433 + 1434 + if (!js->errmsg) { 1435 + js->errmsg_size = 4096; 1436 + js->errmsg = (char *)malloc(js->errmsg_size); 1437 + if (!js->errmsg) return mkval(T_ERR, 0); 1438 + } 1439 + 1367 1440 get_line_col(js->code, js->toff > 0 ? js->toff : js->pos, &line, &col); 1368 1441 get_error_line(js->code, js->clen, js->toff > 0 ? js->toff : js->pos, error_line, sizeof(error_line), &error_col); 1442 + 1443 + va_start(ap, xx); 1444 + vsnprintf(error_msg, sizeof(error_msg), xx, ap); 1445 + va_end(ap); 1446 + 1369 1447 size_t n = 0; 1370 1448 if (js->filename) { 1371 - n = (size_t) snprintf(js->errmsg, sizeof(js->errmsg), "Panic: %s:%d:%d\n", js->filename, line, col); 1449 + n = (size_t) snprintf(js->errmsg, js->errmsg_size, "%s:%d\n", js->filename, line); 1372 1450 } else { 1373 - n = (size_t) snprintf(js->errmsg, sizeof(js->errmsg), "Panic: <eval>:%d:%d\n", line, col); 1451 + n = (size_t) snprintf(js->errmsg, js->errmsg_size, "<eval>:%d\n", line); 1374 1452 } 1375 - va_start(ap, xx); 1376 - vsnprintf(js->errmsg + n, sizeof(js->errmsg) - n, xx, ap); 1377 - va_end(ap); 1378 - n = strlen(js->errmsg); 1379 - if (n < sizeof(js->errmsg) - 1) { 1380 - js->errmsg[n++] = '\n'; 1381 - } 1382 - size_t remaining = sizeof(js->errmsg) - n; 1383 - size_t added = (size_t) snprintf(js->errmsg + n, remaining, "%s\n", error_line); 1384 - n += added; 1385 - if (n < sizeof(js->errmsg) - 1) { 1386 - remaining = sizeof(js->errmsg) - n; 1387 - for (int i = 1; i < error_col && remaining > 1; i++) { 1388 - js->errmsg[n++] = ' '; 1389 - remaining--; 1390 - } 1391 - if (remaining > 3) { 1392 - js->errmsg[n++] = '^'; 1393 - js->errmsg[n++] = '^'; 1394 - js->errmsg[n++] = '^'; 1395 - } 1396 - js->errmsg[n] = '\0'; 1453 + 1454 + size_t remaining = js->errmsg_size - n; 1455 + if (remaining > 1) { 1456 + n += (size_t) snprintf(js->errmsg + n, remaining, "\x1b[31mTypeError\x1b[0m: \x1b[1m%s\x1b[0m", error_msg); 1397 1457 } 1398 - js->errmsg[sizeof(js->errmsg) - 1] = '\0'; 1458 + 1459 + format_error_stack(js, &n, line, col, true, error_line, error_col); 1460 + 1399 1461 js->pos = js->clen, js->tok = TOK_EOF, js->consumed = 0; 1400 1462 return mkval(T_ERR, 0); 1401 1463 } 1402 1464 1403 1465 static jsval_t js_throw(struct js *js, jsval_t value) { 1404 1466 int line = 0, col = 0; 1467 + char error_line[256] = {0}; 1468 + int error_col = 0; 1469 + 1405 1470 get_line_col(js->code, js->toff > 0 ? js->toff : js->pos, &line, &col); 1471 + get_error_line(js->code, js->clen, js->toff > 0 ? js->toff : js->pos, error_line, sizeof(error_line), &error_col); 1472 + 1473 + if (!js->errmsg) { 1474 + js->errmsg_size = 4096; 1475 + js->errmsg = (char *)malloc(js->errmsg_size); 1476 + if (!js->errmsg) return mkval(T_ERR, 0); 1477 + } 1406 1478 1407 1479 size_t n = 0; 1480 + if (js->filename) { 1481 + n = (size_t) snprintf(js->errmsg, js->errmsg_size, "%s:%d\n", js->filename, line); 1482 + } else { 1483 + n = (size_t) snprintf(js->errmsg, js->errmsg_size, "<eval>:%d\n", line); 1484 + } 1485 + 1486 + size_t remaining = js->errmsg_size - n; 1408 1487 if (vtype(value) == T_STR) { 1409 1488 jsoff_t slen, off = vstr(js, value, &slen); 1410 - n = (size_t) snprintf(js->errmsg, sizeof(js->errmsg), "Uncaught: %.*s\n", (int)slen, (char *)&js->mem[off]); 1489 + n += (size_t) snprintf(js->errmsg + n, remaining, "\x1b[31mError\x1b[0m: \x1b[1m%.*s\x1b[0m", (int)slen, (char *)&js->mem[off]); 1411 1490 } else if (vtype(value) == T_OBJ) { 1412 1491 jsoff_t name_off = lkp(js, value, "name", 4); 1413 1492 jsoff_t msg_off = lkp(js, value, "message", 7); ··· 1433 1512 } 1434 1513 1435 1514 if (name_str && msg_str) { 1436 - n = (size_t) snprintf(js->errmsg, sizeof(js->errmsg), "Uncaught %.*s: %.*s\n", (int)name_len, name_str, (int)msg_len, msg_str); 1515 + n += (size_t) snprintf(js->errmsg + n, remaining, "\x1b[31m%.*s\x1b[0m: \x1b[1m%.*s\x1b[0m", (int)name_len, name_str, (int)msg_len, msg_str); 1437 1516 } else if (name_str) { 1438 - n = (size_t) snprintf(js->errmsg, sizeof(js->errmsg), "Uncaught %.*s\n", (int)name_len, name_str); 1517 + n += (size_t) snprintf(js->errmsg + n, remaining, "\x1b[31m%.*s\x1b[0m", (int)name_len, name_str); 1439 1518 } else if (msg_str) { 1440 - n = (size_t) snprintf(js->errmsg, sizeof(js->errmsg), "Uncaught: %.*s\n", (int)msg_len, msg_str); 1519 + n += (size_t) snprintf(js->errmsg + n, remaining, "\x1b[31mError\x1b[0m: \x1b[1m%.*s\x1b[0m", (int)msg_len, msg_str); 1441 1520 } else { 1442 1521 const char *str = js_str(js, value); 1443 - n = (size_t) snprintf(js->errmsg, sizeof(js->errmsg), "Uncaught: %s\n", str); 1522 + n += (size_t) snprintf(js->errmsg + n, remaining, "\x1b[31mError\x1b[0m: \x1b[1m%s\x1b[0m", str); 1444 1523 } 1445 1524 } else { 1446 1525 const char *str = js_str(js, value); 1447 - n = (size_t) snprintf(js->errmsg, sizeof(js->errmsg), "Uncaught: %s\n", str); 1526 + n += (size_t) snprintf(js->errmsg + n, remaining, "\x1b[31mError\x1b[0m: \x1b[1m%s\x1b[0m", str); 1448 1527 } 1449 1528 1450 - size_t remaining = sizeof(js->errmsg) - n; 1451 - if (remaining > 20) { 1452 - if (js->filename) { 1453 - n += (size_t) snprintf(js->errmsg + n, remaining, " at %s:%d:%d\n", js->filename, line, col); 1454 - } else { 1455 - n += (size_t) snprintf(js->errmsg + n, remaining, " at <eval>:%d:%d\n", line, col); 1456 - } 1457 - } 1529 + format_error_stack(js, &n, line, col, true, error_line, error_col); 1458 1530 1459 - remaining = sizeof(js->errmsg) - n; 1460 - for (int i = global_call_stack.depth - 1; i >= 0 && remaining > 20; i--) { 1461 - call_frame_t *frame = &global_call_stack.frames[i]; 1462 - const char *fname = frame->function_name ? frame->function_name : "<anonymous>"; 1463 - const char *file = frame->filename ? frame->filename : "<eval>"; 1464 - 1465 - size_t added = (size_t) snprintf(js->errmsg + n, remaining, " at %s (%s:%d:%d)\n", fname, file, frame->line, frame->col); 1466 - n += added; 1467 - remaining = sizeof(js->errmsg) - n; 1468 - } 1469 - 1470 - js->errmsg[sizeof(js->errmsg) - 1] = '\0'; 1471 1531 js->flags |= F_THROW; 1472 1532 js->pos = js->clen; 1473 1533 js->tok = TOK_EOF; ··· 3260 3320 } 3261 3321 3262 3322 if (t != T_OBJ && t != T_ARR) { 3263 - return js_mkerr(js, "lookup in non-obj"); 3323 + char errbuf[256]; 3324 + jsoff_t saved_toff = js->toff; 3325 + jsoff_t saved_tlen = js->tlen; 3326 + js->toff = coderefoff(r); 3327 + js->tlen = codereflen(r); 3328 + snprintf(errbuf, sizeof(errbuf), "Cannot read properties of %s (reading '%.*s')", 3329 + t == T_UNDEF ? "undefined" : t == T_NULL ? "null" : typestr(t), 3330 + (int)plen, ptr); 3331 + jsval_t err = js_mkerr(js, errbuf); 3332 + js->toff = saved_toff; 3333 + js->tlen = saved_tlen; 3334 + return err; 3264 3335 } 3265 3336 3266 3337 jsoff_t own_off = lkp(js, l, ptr, plen); ··· 3322 3393 bool is_spread = (next(js) == TOK_REST); 3323 3394 if (is_spread) js->consumed = 1; 3324 3395 jsval_t arg = resolveprop(js, js_expr(js)); 3396 + if (is_err(arg)) return arg; 3325 3397 if (is_spread && vtype(arg) == T_ARR) { 3326 3398 jsoff_t len = arr_length(js, arg); 3327 3399 for (jsoff_t i = 0; i < len; i++) { ··· 3774 3846 int call_line = 0, call_col = 0; 3775 3847 get_line_col(code, pos, &call_line, &call_col); 3776 3848 3849 + static char full_func_name[256]; 3777 3850 const char *func_name = NULL; 3851 + const char *this_name = NULL; 3852 + 3778 3853 jsoff_t name_off = lkp(js, func_obj, "name", 4); 3779 3854 if (name_off != 0) { 3780 3855 jsval_t name_val = resolveprop(js, mkval(T_PROP, name_off)); ··· 3784 3859 } 3785 3860 } 3786 3861 3862 + if (vtype(target_this) != T_OBJ) goto skip_constructor_name; 3863 + 3864 + jsoff_t ctor_off = lkp(js, target_this, "constructor", 11); 3865 + if (ctor_off == 0) goto default_object_name; 3866 + 3867 + jsval_t ctor_val = resolveprop(js, mkval(T_PROP, ctor_off)); 3868 + if (vtype(ctor_val) != T_FUNC) goto default_object_name; 3869 + 3870 + jsval_t ctor_obj = mkval(T_OBJ, vdata(ctor_val)); 3871 + jsoff_t ctor_name_off = lkp(js, ctor_obj, "name", 4); 3872 + if (ctor_name_off == 0) goto default_object_name; 3873 + 3874 + jsval_t ctor_name_val = resolveprop(js, mkval(T_PROP, ctor_name_off)); 3875 + if (vtype(ctor_name_val) != T_STR) goto default_object_name; 3876 + 3877 + jsoff_t ctor_name_len, ctor_name_offset = vstr(js, ctor_name_val, &ctor_name_len); 3878 + this_name = (const char *)&js->mem[ctor_name_offset]; 3879 + if (this_name && strlen(this_name) > 0) goto skip_constructor_name; 3880 + 3881 + default_object_name: 3882 + this_name = "Object"; 3883 + 3884 + skip_constructor_name: 3885 + 3886 + const char *final_name; 3887 + if (this_name && func_name) { 3888 + snprintf(full_func_name, sizeof(full_func_name), "%s.%s", this_name, func_name); 3889 + final_name = full_func_name; 3890 + } else if (func_name) { 3891 + final_name = func_name; 3892 + } else if (this_name) { 3893 + snprintf(full_func_name, sizeof(full_func_name), "%s.<anonymous>", this_name); 3894 + final_name = full_func_name; 3895 + } else { 3896 + final_name = "<anonymous>"; 3897 + } 3898 + 3787 3899 push_call_frame( 3788 3900 js->filename, 3789 - func_name ? func_name : "<anonymous>", 3901 + final_name, 3790 3902 call_line, call_col 3791 3903 ); 3792 3904 ··· 11733 11845 js->lwm = js->size; 11734 11846 js->gct = js->size / 2; 11735 11847 js->this_val = js->scope; 11848 + js->errmsg_size = 4096; 11849 + js->errmsg = (char *)malloc(js->errmsg_size); 11850 + if (js->errmsg) js->errmsg[0] = '\0'; 11736 11851 11737 11852 jsval_t glob = js->scope; 11738 11853 jsval_t object_proto = js_mkobj(js); ··· 12029 12144 void js_destroy(struct js *js) { 12030 12145 if (js == NULL) return; 12031 12146 esm_cleanup_module_cache(); 12147 + 12148 + if (js->errmsg) { 12149 + free(js->errmsg); 12150 + js->errmsg = NULL; 12151 + } 12032 12152 12033 12153 if (js->owns_mem) { 12034 12154 ANT_GC_FREE((void *)((uint8_t *)js - 0));
+10 -2
src/main.c
··· 97 97 fclose(fp); 98 98 buffer[len] = '\0'; 99 99 100 - js_set_filename(js, filename); 101 - js_setup_import_meta(js, filename); 100 + char abs_path[4096]; 101 + char *use_path = NULL; 102 + if (realpath(filename, abs_path) != NULL) { 103 + use_path = strdup(abs_path); 104 + } else use_path = strdup(filename); 105 + 106 + js_set_filename(js, use_path); 107 + js_setup_import_meta(js, use_path); 102 108 103 109 js_mkscope(js); 104 110 js_protect_init_memory(js); ··· 108 114 109 115 if (js_type(result) == JS_ERR) { 110 116 fprintf(stderr, "%s\n", js_str(js, result)); 117 + free(use_path); 111 118 return EXIT_FAILURE; 112 119 } 113 120 121 + free(use_path); 114 122 return EXIT_SUCCESS; 115 123 } 116 124
+3 -3
tests/test_this.js
··· 4 4 console.log('Hello, ' + this.name); 5 5 console.log(this); 6 6 7 - console.log('\nexpecting error below'); 8 - console.log(this.crypto.randomUUID()); 9 - 10 7 console.log(globalThis.Ant.version); 11 8 console.log(window.crypto.randomUUID()); 9 + 10 + console.log('\nexpecting error below'); 11 + console.log(this.crypto.randomUUID()); 12 12 } 13 13 }; 14 14