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.

add proper ERRNO path for all fs modules

+207 -111
+1 -1
.github/agents/route_validation.js
··· 133 133 filePath.startsWith('src/streams/') 134 134 ) { 135 135 addRecommendation(recommendations, 'meson compile -C build', 'runtime-facing modules or I/O code changed'); 136 - addRecommendation(recommendations, './build/ant examples/spec/run.js', 'shared runtime semantics may have shifted'); 136 + addRecommendation(recommendations, './build/ant examples/spec/run.js <spec_name or --all>', 'shared runtime semantics may have shifted'); 137 137 138 138 const stem = path.basename(filePath, path.extname(filePath)); 139 139 if (stem) {
+4
docs/repo/testing.md
··· 41 41 42 42 - Keep new tests close to the behavior they protect so future agent runs can 43 43 discover the expected pattern quickly. 44 + - In sandboxed agent sessions, `./build/ant examples/spec/run.js --all` can fail 45 + in the `fetch` spec because outbound network access is blocked. Treat that as 46 + an expected sandbox limitation, and prefer targeted spec files when the 47 + change does not need networked coverage. 44 48 - If the right validation is expensive or unavailable, document the gap in the 45 49 associated [execution plan](../exec-plans/index.md).
+2 -6
examples/demo/kat.js
··· 3 3 import fs from 'node:fs'; 4 4 import path from 'node:path'; 5 5 6 - function exit(error) { 7 - console.log(error); 8 - process.exit(1); 9 - } 10 - 11 6 const file = process.argv[2]; 12 7 if (!file) exit('usage: kat <file>'); 13 8 ··· 17 12 console.log(Ant.highlight(content)); 18 13 } else console.log(content); 19 14 } catch (err) { 20 - exit(err.message); 15 + console.log(`file '${err.path}' not found`); 16 + process.exit(1); 21 17 }
+165 -104
src/modules/fs.c
··· 33 33 #include "modules/stream.h" 34 34 #include "modules/symbol.h" 35 35 36 - #define fs_err_code(js, code, op, path) ({ \ 37 - ant_value_t _props = js_mkobj(js); \ 38 - js_set(js, _props, "code", js_mkstr(js, code, strlen(code))); \ 39 - js_mkerr_props(js, JS_ERR_TYPE, _props, "%s: %s, %s '%s'", code, strerror(errno), op, path); \ 40 - }) 41 - 42 36 typedef enum { 43 37 FS_ENC_NONE = 0, 44 38 FS_ENC_UTF8, ··· 200 194 201 195 static int parse_open_flags(ant_t *js, ant_value_t arg); 202 196 static ant_value_t fs_coerce_path(ant_t *js, ant_value_t arg); 197 + 198 + static ant_value_t fs_mk_errno_error( 199 + ant_t *js, int err_num, 200 + const char *syscall, const char *path, const char *dest 201 + ); 203 202 204 203 static ant_value_t fs_stream_error(ant_t *js, ant_value_t stream_obj, const char *op, int uv_code) { 205 204 ant_value_t props = js_mkobj(js); ··· 1688 1687 1689 1688 FILE *file = fopen(path_cstr, "rb"); 1690 1689 if (!file) { 1691 - char err_msg[256]; 1692 - snprintf(err_msg, sizeof(err_msg), "Failed to open file: %s", strerror(errno)); 1693 - free(path_cstr); return js_mkerr(js, "%s", err_msg); 1690 + ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); 1691 + free(path_cstr); 1692 + return err; 1694 1693 } 1695 1694 1696 1695 fseek(file, 0, SEEK_END); ··· 1741 1740 1742 1741 FILE *file = fopen(path_cstr, "rb"); 1743 1742 if (!file) { 1744 - char err_msg[256]; 1745 - snprintf(err_msg, sizeof(err_msg), "Failed to open file: %s", strerror(errno)); 1746 - free(path_cstr); return js_mkerr(js, "%s", err_msg); 1743 + ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); 1744 + free(path_cstr); 1745 + return err; 1747 1746 } 1748 1747 1749 1748 fseek(file, 0, SEEK_END); ··· 1858 1857 1859 1858 FILE *file = fopen(path_cstr, "wb"); 1860 1859 if (!file) { 1861 - char err_msg[256]; 1862 - snprintf(err_msg, sizeof(err_msg), "Failed to open file: %s", strerror(errno)); 1863 - free(path_cstr); return js_mkerr(js, "%s", err_msg); 1860 + ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); 1861 + free(path_cstr); 1862 + return err; 1864 1863 } 1865 1864 1866 1865 size_t bytes_written = fwrite(data, 1, data_len, file); ··· 1896 1895 1897 1896 FILE *in = fopen(src_cstr, "rb"); 1898 1897 if (!in) { 1899 - char err_msg[256]; 1900 - snprintf(err_msg, sizeof(err_msg), "Failed to open source file: %s", strerror(errno)); 1898 + ant_value_t err = fs_mk_errno_error(js, errno, "copyfile", src_cstr, dest_cstr); 1901 1899 free(src_cstr); free(dest_cstr); 1902 - return js_mkerr(js, "%s", err_msg); 1900 + return err; 1903 1901 } 1904 1902 1905 1903 FILE *out = fopen(dest_cstr, "wb"); 1906 1904 if (!out) { 1907 - char err_msg[256]; 1908 - snprintf(err_msg, sizeof(err_msg), "Failed to open dest file: %s", strerror(errno)); 1905 + ant_value_t err = fs_mk_errno_error(js, errno, "copyfile", src_cstr, dest_cstr); 1909 1906 fclose(in); 1910 1907 free(src_cstr); free(dest_cstr); 1911 - return js_mkerr(js, "%s", err_msg); 1908 + return err; 1912 1909 } 1913 1910 1914 1911 char buf[8192]; 1915 1912 size_t n; 1913 + 1916 1914 while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { 1917 - if (fwrite(buf, 1, n, out) != n) { 1918 - fclose(in); 1919 - fclose(out); 1920 - free(src_cstr); 1921 - free(dest_cstr); 1922 - return js_mkerr(js, "Failed to write to dest file"); 1923 - } 1924 - } 1915 + if (fwrite(buf, 1, n, out) != n) { 1916 + ant_value_t err = fs_mk_errno_error(js, errno ? errno : EIO, "copyfile", src_cstr, dest_cstr); 1917 + fclose(in); fclose(out); 1918 + free(src_cstr); free(dest_cstr); 1919 + return err; 1920 + }} 1925 1921 1926 - fclose(in); 1927 - fclose(out); 1928 - free(src_cstr); 1929 - free(dest_cstr); 1922 + fclose(in); fclose(out); 1923 + free(src_cstr); free(dest_cstr); 1930 1924 1931 1925 return js_mkundef(); 1932 1926 } ··· 1952 1946 } 1953 1947 1954 1948 int result = rename(old_cstr, new_cstr); 1955 - free(old_cstr); 1956 - free(new_cstr); 1957 1949 1958 1950 if (result != 0) { 1959 - char err_msg[256]; 1960 - snprintf(err_msg, sizeof(err_msg), "Failed to rename: %s", strerror(errno)); 1961 - return js_mkerr(js, "%s", err_msg); 1951 + ant_value_t err = fs_mk_errno_error(js, errno, "rename", old_cstr, new_cstr); 1952 + free(old_cstr); 1953 + free(new_cstr); 1954 + return err; 1962 1955 } 1956 + 1957 + free(old_cstr); 1958 + free(new_cstr); 1963 1959 1964 1960 return js_mkundef(); 1965 1961 } ··· 2027 2023 2028 2024 FILE *file = fopen(path_cstr, "ab"); 2029 2025 if (!file) { 2030 - char err_msg[256]; 2031 - snprintf(err_msg, sizeof(err_msg), "Failed to open file: %s", strerror(errno)); 2032 - free(path_cstr); return js_mkerr(js, "%s", err_msg); 2026 + ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); 2027 + free(path_cstr); 2028 + return err; 2033 2029 } 2034 2030 2035 2031 size_t bytes_written = fwrite(data, 1, data_len, file); ··· 2097 2093 2098 2094 char *path_cstr = strndup(path, path_len); 2099 2095 if (!path_cstr) return js_mkerr(js, "Out of memory"); 2100 - 2101 2096 int result = unlink(path_cstr); 2102 - free(path_cstr); 2103 2097 2104 2098 if (result != 0) { 2105 - char err_msg[256]; 2106 - snprintf(err_msg, sizeof(err_msg), "Failed to unlink file: %s", strerror(errno)); 2107 - return js_mkerr(js, "%s", err_msg); 2099 + ant_value_t err = fs_mk_errno_error(js, errno, "unlink", path_cstr, NULL); 2100 + free(path_cstr); 2101 + return err; 2108 2102 } 2109 2103 2104 + free(path_cstr); 2110 2105 return js_mkundef(); 2111 2106 } 2112 2107 ··· 2184 2179 result = mkdir(path_cstr, (mode_t)mode); 2185 2180 #endif 2186 2181 } 2187 - free(path_cstr); 2188 - 2189 2182 if (result != 0) { 2190 - char err_msg[256]; 2191 - snprintf(err_msg, sizeof(err_msg), "Failed to create directory: %s", strerror(errno)); 2192 - return js_mkerr(js, "%s", err_msg); 2183 + ant_value_t err = fs_mk_errno_error(js, errno, "mkdir", path_cstr, NULL); 2184 + free(path_cstr); 2185 + return err; 2193 2186 } 2187 + 2188 + free(path_cstr); 2194 2189 2195 2190 return js_mkundef(); 2196 2191 } ··· 2207 2202 int mode = 0755; 2208 2203 int recursive = 0; 2209 2204 if (nargs >= 2) { 2210 - switch (vtype(args[1])) { 2211 - case T_NUM: 2212 - mode = (int)js_getnum(args[1]); 2213 - break; 2214 - case T_OBJ: { 2215 - ant_value_t opt = args[1]; 2216 - recursive = js_get(js, opt, "recursive") == js_true; 2217 - ant_value_t mode_val = js_get(js, opt, "mode"); 2218 - if (vtype(mode_val) == T_NUM) mode = (int)js_getnum(mode_val); 2219 - break; 2220 - } 2221 - } 2205 + switch (vtype(args[1])) { 2206 + case T_NUM: 2207 + mode = (int)js_getnum(args[1]); 2208 + break; 2209 + case T_OBJ: { 2210 + ant_value_t opt = args[1]; 2211 + recursive = js_get(js, opt, "recursive") == js_true; 2212 + ant_value_t mode_val = js_get(js, opt, "mode"); 2213 + if (vtype(mode_val) == T_NUM) mode = (int)js_getnum(mode_val); 2214 + break; 2215 + }} 2222 2216 } 2223 2217 2224 2218 if (recursive) { ··· 2339 2333 #else 2340 2334 int result = rmdir(path_cstr); 2341 2335 #endif 2342 - free(path_cstr); 2343 - 2344 2336 if (result != 0) { 2345 - char err_msg[256]; 2346 - snprintf(err_msg, sizeof(err_msg), "Failed to remove directory: %s", strerror(errno)); 2347 - return js_mkerr(js, "%s", err_msg); 2337 + ant_value_t err = fs_mk_errno_error(js, errno, "rmdir", path_cstr, NULL); 2338 + free(path_cstr); 2339 + return err; 2348 2340 } 2349 2341 2342 + free(path_cstr); 2350 2343 return js_mkundef(); 2351 2344 } 2352 2345 ··· 2473 2466 } 2474 2467 2475 2468 static const char *errno_to_code(int err_num) { 2476 - switch (err_num) { 2477 - case ENOENT: return "ENOENT"; 2478 - case EACCES: return "EACCES"; 2479 - case ENOTDIR: return "ENOTDIR"; 2480 - case ELOOP: return "ELOOP"; 2481 - case ENAMETOOLONG: return "ENAMETOOLONG"; 2482 - case EOVERFLOW: return "EOVERFLOW"; 2483 - case EROFS: return "EROFS"; 2484 - case ETXTBSY: return "ETXTBSY"; 2485 - case EEXIST: return "EEXIST"; 2486 - case ENOTEMPTY: return "ENOTEMPTY"; 2487 - case EISDIR: return "EISDIR"; 2488 - case EBUSY: return "EBUSY"; 2489 - case EINVAL: return "EINVAL"; 2490 - case EPERM: return "EPERM"; 2491 - case EIO: return "EIO"; 2492 - default: return "UNKNOWN"; 2493 - } 2469 + switch (err_num) { 2470 + case ENOENT: return "ENOENT"; 2471 + case EACCES: return "EACCES"; 2472 + case ENOTDIR: return "ENOTDIR"; 2473 + case ELOOP: return "ELOOP"; 2474 + case ENAMETOOLONG: return "ENAMETOOLONG"; 2475 + case EOVERFLOW: return "EOVERFLOW"; 2476 + case EROFS: return "EROFS"; 2477 + case ETXTBSY: return "ETXTBSY"; 2478 + case EEXIST: return "EEXIST"; 2479 + case ENOTEMPTY: return "ENOTEMPTY"; 2480 + case EISDIR: return "EISDIR"; 2481 + case EBUSY: return "EBUSY"; 2482 + case EINVAL: return "EINVAL"; 2483 + case EPERM: return "EPERM"; 2484 + case EIO: return "EIO"; 2485 + default: return "UNKNOWN"; 2486 + }} 2487 + 2488 + static ant_value_t fs_mk_syscall_error( 2489 + ant_t *js, const char *code, double err_num, 2490 + const char *message, const char *syscall, 2491 + const char *path, const char *dest 2492 + ) { 2493 + ant_value_t props = js_mkobj(js); 2494 + 2495 + if (!code) code = "UNKNOWN"; 2496 + if (!message) message = "Unknown error"; 2497 + 2498 + js_set(js, props, "code", js_mkstr(js, code, strlen(code))); 2499 + js_set(js, props, "errno", js_mknum(err_num)); 2500 + 2501 + if (syscall) js_set(js, props, "syscall", js_mkstr(js, syscall, strlen(syscall))); 2502 + if (path) js_set(js, props, "path", js_mkstr(js, path, strlen(path))); 2503 + if (dest) js_set(js, props, "dest", js_mkstr(js, dest, strlen(dest))); 2504 + 2505 + if (path && dest) return js_mkerr_props( 2506 + js, JS_ERR_GENERIC, 2507 + props, "%s: %s, %s '%s' -> '%s'", 2508 + code, message, syscall, path, dest 2509 + ); 2510 + 2511 + if (path) return js_mkerr_props( 2512 + js, JS_ERR_GENERIC, 2513 + props, "%s: %s, %s '%s'", 2514 + code, message, syscall, path 2515 + ); 2516 + 2517 + return js_mkerr_props( 2518 + js, JS_ERR_GENERIC, 2519 + props, "%s: %s, %s", 2520 + code, message, syscall 2521 + ); 2522 + } 2523 + 2524 + static ant_value_t fs_mk_errno_error( 2525 + ant_t *js, int err_num, 2526 + const char *syscall, const char *path, const char *dest 2527 + ) { 2528 + int uv_code = uv_translate_sys_error(err_num); 2529 + if (uv_code == 0) uv_code = -err_num; 2530 + 2531 + return fs_mk_syscall_error( 2532 + js, errno_to_code(err_num), 2533 + (double)uv_code, 2534 + uv_strerror(uv_code), 2535 + syscall, path, dest 2536 + ); 2537 + } 2538 + 2539 + static ant_value_t fs_mk_uv_error( 2540 + ant_t *js, int uv_code, 2541 + const char *syscall, const char *path, const char *dest 2542 + ) { 2543 + return fs_mk_syscall_error( 2544 + js, uv_err_name(uv_code), 2545 + (double)uv_code, 2546 + uv_strerror(uv_code), 2547 + syscall, path, dest 2548 + ); 2494 2549 } 2495 2550 2496 2551 static ant_value_t builtin_fs_statSync(ant_t *js, ant_value_t *args, int nargs) { ··· 2508 2563 int result = stat(path_cstr, &st); 2509 2564 2510 2565 if (result != 0) { 2511 - const char *code = errno_to_code(errno); 2512 - ant_value_t err = fs_err_code(js, code, "stat", path_cstr); 2566 + ant_value_t err = fs_mk_errno_error(js, errno, "stat", path_cstr, NULL); 2513 2567 free(path_cstr); return err; 2514 2568 } 2515 2569 ··· 2562 2616 int result = uv_fs_lstat(NULL, &req, path_cstr, NULL); 2563 2617 2564 2618 if (result < 0) { 2565 - ant_value_t err = fs_err_code(js, uv_err_name(result), "lstat", path_cstr); 2619 + ant_value_t err = fs_mk_uv_error(js, result, "lstat", path_cstr, NULL); 2566 2620 uv_fs_req_cleanup(&req); 2567 2621 free(path_cstr); 2568 2622 return err; ··· 2672 2726 int result = access(path_cstr, mode); 2673 2727 2674 2728 if (result != 0) { 2675 - const char *code = errno_to_code(errno); 2676 - ant_value_t err = fs_err_code(js, code, "access", path_cstr); 2729 + ant_value_t err = fs_mk_errno_error(js, errno, "access", path_cstr, NULL); 2677 2730 free(path_cstr); return err; 2678 2731 } 2679 2732 ··· 2789 2842 2790 2843 uv_fs_t req; 2791 2844 int result = uv_fs_scandir(NULL, &req, path_cstr, 0, NULL); 2792 - free(path_cstr); 2793 2845 2794 2846 if (result < 0) { 2795 - char err_msg[256]; 2796 - snprintf(err_msg, sizeof(err_msg), "Failed to read directory: %s", uv_strerror(result)); 2797 - uv_fs_req_cleanup(&req); return js_mkerr(js, "%s", err_msg); 2847 + ant_value_t err = fs_mk_uv_error(js, result, "scandir", path_cstr, NULL); 2848 + uv_fs_req_cleanup(&req); 2849 + free(path_cstr); 2850 + return err; 2798 2851 } 2852 + 2853 + free(path_cstr); 2799 2854 2800 2855 ant_value_t arr = js_mkarr(js); 2801 2856 uv_dirent_t dirent; 2802 2857 2803 2858 while (uv_fs_scandir_next(&req, &dirent) != UV_EOF) { 2804 - if (with_file_types) { 2805 - ant_value_t entry = create_dirent_object(js, dirent.name, strlen(dirent.name), dirent.type); 2806 - js_arr_push(js, arr, entry); 2807 - } else { 2808 - ant_value_t name = js_mkstr(js, dirent.name, strlen(dirent.name)); 2809 - js_arr_push(js, arr, name); 2810 - } 2811 - } 2859 + if (with_file_types) { 2860 + ant_value_t entry = create_dirent_object(js, dirent.name, strlen(dirent.name), dirent.type); 2861 + js_arr_push(js, arr, entry); 2862 + } else { 2863 + ant_value_t name = js_mkstr(js, dirent.name, strlen(dirent.name)); 2864 + js_arr_push(js, arr, name); 2865 + }} 2812 2866 2813 2867 uv_fs_req_cleanup(&req); 2814 2868 return arr; ··· 3296 3350 3297 3351 uv_fs_t req; 3298 3352 int result = uv_fs_open(uv_default_loop(), &req, path_cstr, flags, mode, NULL); 3353 + 3354 + if (result < 0) { 3355 + ant_value_t err = fs_mk_uv_error(js, result, "open", path_cstr, NULL); 3356 + uv_fs_req_cleanup(&req); 3357 + free(path_cstr); 3358 + return err; 3359 + } 3360 + 3299 3361 uv_fs_req_cleanup(&req); 3300 3362 free(path_cstr); 3301 3363 3302 - if (result < 0) return js_mkerr(js, "openSync failed: %s", uv_strerror(result)); 3303 3364 return js_mknum((double)result); 3304 3365 } 3305 3366
+35
tests/test_fs_error_messages.cjs
··· 1 + const fs = require('node:fs'); 2 + 3 + function assert(condition, message) { 4 + if (!condition) throw new Error(message); 5 + } 6 + 7 + function expectThrow(fn, label) { 8 + try { 9 + fn(); 10 + } catch (error) { 11 + return error; 12 + } 13 + throw new Error(`${label} did not throw`); 14 + } 15 + 16 + const missingPath = 'tests/.fs_missing_file'; 17 + const renameDest = `${missingPath}.renamed`; 18 + 19 + const readError = expectThrow(() => fs.readFileSync(missingPath, 'utf8'), 'readFileSync'); 20 + assert(readError.code === 'ENOENT', `expected readFileSync code ENOENT, got ${readError.code}`); 21 + assert(readError.syscall === 'open', `expected readFileSync syscall open, got ${readError.syscall}`); 22 + assert(readError.path === missingPath, `expected readFileSync path ${missingPath}, got ${readError.path}`); 23 + assert(readError.message === `ENOENT: no such file or directory, open '${missingPath}'`, `unexpected readFileSync message: ${readError.message}`); 24 + 25 + const renameError = expectThrow(() => fs.renameSync(missingPath, renameDest), 'renameSync'); 26 + assert(renameError.code === 'ENOENT', `expected renameSync code ENOENT, got ${renameError.code}`); 27 + assert(renameError.syscall === 'rename', `expected renameSync syscall rename, got ${renameError.syscall}`); 28 + assert(renameError.path === missingPath, `expected renameSync path ${missingPath}, got ${renameError.path}`); 29 + assert(renameError.dest === renameDest, `expected renameSync dest ${renameDest}, got ${renameError.dest}`); 30 + assert( 31 + renameError.message === `ENOENT: no such file or directory, rename '${missingPath}' -> '${renameDest}'`, 32 + `unexpected renameSync message: ${renameError.message}` 33 + ); 34 + 35 + console.log('fs error message test passed');