xbintsc 0.3.13 → 0.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +22 -22
  2. package/README.zh-CN.md +20 -19
  3. package/bin/xbintsc.js +1 -2
  4. package/dist/src/cli/main.js +4 -1
  5. package/dist/src/cli/main.js.map +1 -1
  6. package/dist/src/codegen/generator/calls/access.js +13 -0
  7. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  8. package/dist/src/codegen/generator/calls/invocation.js +8 -2
  9. package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
  10. package/dist/src/codegen/generator/context.d.ts +6 -0
  11. package/dist/src/codegen/generator/context.js +24 -2
  12. package/dist/src/codegen/generator/context.js.map +1 -1
  13. package/dist/src/codegen/generator/expressions/primary.js +5 -0
  14. package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
  15. package/dist/src/codegen/generator/tables.d.ts +5 -6
  16. package/dist/src/codegen/generator/tables.js +5 -6
  17. package/dist/src/codegen/generator/tables.js.map +1 -1
  18. package/dist/src/driver/compiler.js +15 -3
  19. package/dist/src/driver/compiler.js.map +1 -1
  20. package/dist/src/driver/index.d.ts +1 -2
  21. package/dist/src/driver/index.js +1 -2
  22. package/dist/src/driver/index.js.map +1 -1
  23. package/dist/src/driver/modules.d.ts +1 -1
  24. package/dist/src/driver/modules.js +235 -25
  25. package/dist/src/driver/modules.js.map +1 -1
  26. package/dist/src/driver/paths.d.ts +0 -9
  27. package/dist/src/driver/paths.js +0 -31
  28. package/dist/src/driver/paths.js.map +1 -1
  29. package/dist/src/driver/toolchain-provider.d.ts +10 -10
  30. package/dist/src/driver/toolchain-provider.js +9 -38
  31. package/dist/src/driver/toolchain-provider.js.map +1 -1
  32. package/dist/src/driver/toolchain.js +8 -2
  33. package/dist/src/driver/toolchain.js.map +1 -1
  34. package/dist/src/extensions/native.d.ts +2 -3
  35. package/dist/src/extensions/native.js +2 -3
  36. package/dist/src/extensions/native.js.map +1 -1
  37. package/dist/src/extensions/node/fs/fs-ops.d.ts +5 -2
  38. package/dist/src/extensions/node/fs/fs-ops.js +45 -6
  39. package/dist/src/extensions/node/fs/fs-ops.js.map +1 -1
  40. package/dist/src/extensions/node/fs/index.d.ts +6 -2
  41. package/dist/src/extensions/node/fs/index.js +22 -3
  42. package/dist/src/extensions/node/fs/index.js.map +1 -1
  43. package/dist/src/extensions/node/fs-promises/index.d.ts +4 -3
  44. package/dist/src/extensions/node/fs-promises/index.js +31 -3
  45. package/dist/src/extensions/node/fs-promises/index.js.map +1 -1
  46. package/dist/tests/cli/main.test.js +8 -0
  47. package/dist/tests/cli/main.test.js.map +1 -1
  48. package/dist/tests/driver/compiler.test.js +28 -1
  49. package/dist/tests/driver/compiler.test.js.map +1 -1
  50. package/dist/tests/driver/modules.test.js +91 -4
  51. package/dist/tests/driver/modules.test.js.map +1 -1
  52. package/dist/tests/driver/paths.test.js +1 -9
  53. package/dist/tests/driver/paths.test.js.map +1 -1
  54. package/dist/tests/driver/toolchain-provider.test.js +12 -79
  55. package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
  56. package/dist/tests/e2e/compile-node.test.js +240 -0
  57. package/dist/tests/e2e/compile-node.test.js.map +1 -1
  58. package/dist/tests/e2e/compile.test.js +12 -0
  59. package/dist/tests/e2e/compile.test.js.map +1 -1
  60. package/dist/tests/e2e/harness.js +11 -8
  61. package/dist/tests/e2e/harness.js.map +1 -1
  62. package/dist/tests/e2e/native-extensions.test.d.ts +4 -5
  63. package/dist/tests/e2e/native-extensions.test.js +6 -7
  64. package/dist/tests/e2e/native-extensions.test.js.map +1 -1
  65. package/dist/tests/e2e/node-modules.test.js +1 -1
  66. package/dist/tests/e2e/node-modules.test.js.map +1 -1
  67. package/dist/tests/e2e/npm-modules.test.d.ts +9 -0
  68. package/dist/tests/e2e/npm-modules.test.js +55 -0
  69. package/dist/tests/e2e/npm-modules.test.js.map +1 -0
  70. package/dist/tests/e2e/runtime-coverage.test.d.ts +11 -0
  71. package/dist/tests/e2e/runtime-coverage.test.js +646 -0
  72. package/dist/tests/e2e/runtime-coverage.test.js.map +1 -0
  73. package/package.json +1 -2
  74. package/runtime/ext_node/buffer/parts/prototype.inc +7 -0
  75. package/runtime/ext_node/buffer/parts/search.inc +8 -4
  76. package/runtime/ext_node/child_process/child_process.c +5 -0
  77. package/runtime/ext_node/fs/constants.c +113 -0
  78. package/runtime/ext_node/fs/copy_ops.c +145 -0
  79. package/runtime/ext_node/fs/dir.c +101 -0
  80. package/runtime/ext_node/fs/fd_ops.c +405 -0
  81. package/runtime/ext_node/fs/fs_common.h +388 -5
  82. package/runtime/ext_node/fs/fs_ops.c +100 -139
  83. package/runtime/ext_node/fs/glob.c +232 -0
  84. package/runtime/ext_node/fs/link_ops.c +91 -0
  85. package/runtime/ext_node/fs/meta_ops.c +403 -0
  86. package/runtime/ext_node/fs/promises.c +275 -60
  87. package/runtime/ext_node/fs/read_file.c +8 -4
  88. package/runtime/ext_node/fs/watch.c +52 -0
  89. package/runtime/ext_node/fs/write_file.c +20 -11
  90. package/runtime/ext_node/stream/stream.c +19 -2
  91. package/runtime/rt.h +8 -0
  92. package/runtime/rt_internal.h +10 -23
  93. package/runtime/xt_ext.h +0 -1
  94. package/runtime/xt_io.c +24 -3
  95. package/runtime/xt_stdlib2/helpers.inc +2 -4
  96. package/scripts/build-runtime.ts +7 -8
  97. package/scripts/coverage-runtime.ts +4 -4
  98. package/scripts/package-release.ts +12 -84
  99. package/src/cli/main.ts +4 -1
  100. package/src/codegen/generator/calls/access.ts +15 -0
  101. package/src/codegen/generator/calls/invocation.ts +8 -2
  102. package/src/codegen/generator/context.ts +30 -2
  103. package/src/codegen/generator/expressions/primary.ts +5 -0
  104. package/src/codegen/generator/tables.ts +5 -6
  105. package/src/driver/compiler.ts +16 -3
  106. package/src/driver/index.ts +1 -9
  107. package/src/driver/modules.ts +251 -26
  108. package/src/driver/paths.ts +0 -31
  109. package/src/driver/toolchain-provider.ts +12 -45
  110. package/src/driver/toolchain.ts +7 -2
  111. package/src/extensions/native.ts +2 -3
  112. package/src/extensions/node/fs/fs-ops.ts +48 -6
  113. package/src/extensions/node/fs/index.ts +25 -3
  114. package/src/extensions/node/fs-promises/index.ts +33 -4
  115. package/dist/src/driver/toolchain-download.d.ts +0 -50
  116. package/dist/src/driver/toolchain-download.js +0 -102
  117. package/dist/src/driver/toolchain-download.js.map +0 -1
  118. package/dist/tests/driver/toolchain-download.test.d.ts +0 -1
  119. package/dist/tests/driver/toolchain-download.test.js +0 -53
  120. package/dist/tests/driver/toolchain-download.test.js.map +0 -1
  121. package/scripts/fetch-toolchain.ts +0 -101
  122. package/src/driver/toolchain-download.ts +0 -135
@@ -10,11 +10,347 @@
10
10
  #ifndef XT_NODE_FS_COMMON_H
11
11
  #define XT_NODE_FS_COMMON_H
12
12
 
13
- #include "rt.h"
13
+ #include "rt_internal.h"
14
14
 
15
+ #include <errno.h>
15
16
  #include <stdio.h>
16
17
  #include <stdlib.h>
17
18
  #include <string.h>
19
+ #include <time.h>
20
+
21
+ /* -- platform shims ------------------------------------------------------- */
22
+
23
+ #if defined(_WIN32)
24
+ #include <direct.h>
25
+ #include <fcntl.h>
26
+ #include <io.h>
27
+ #include <sys/stat.h>
28
+ #include <sys/types.h>
29
+
30
+ /* Stat results are filled from Win32 directly. The CRT's `_stat` converts the
31
+ * FILETIME through the *local* time zone before producing `time_t`, so a time
32
+ * near the Unix epoch (e.g. `utimesSync(path, 1000, 2000)`) maps to 1969 on a
33
+ * machine west of UTC and the CRT reports an unrepresentable `st_mtime` (-1).
34
+ * Reading the Win32 FILETIME and converting it to Unix seconds in UTC like
35
+ * libuv/Node keeps the values exact and timezone independent. */
36
+ typedef struct {
37
+ long long st_size;
38
+ unsigned int st_mode;
39
+ unsigned int st_uid;
40
+ unsigned int st_gid;
41
+ unsigned int st_dev;
42
+ unsigned long long st_ino;
43
+ unsigned int st_nlink;
44
+ unsigned int st_rdev;
45
+ long long st_mtime; /* seconds since the Unix epoch */
46
+ long long st_atime;
47
+ long long st_ctime;
48
+ } xt_fs_stat_t;
49
+
50
+ int xt_fs_win_stat(const char *path, xt_fs_stat_t *out);
51
+ int xt_fs_win_lstat(const char *path, xt_fs_stat_t *out);
52
+ int xt_fs_win_fstat(int fd, xt_fs_stat_t *out);
53
+ #define xt_fs_stat_fn xt_fs_win_stat
54
+ #define xt_fs_lstat_fn xt_fs_win_lstat
55
+ #define xt_fs_fstat_fn xt_fs_win_fstat
56
+ #define xt_fs_access _access
57
+ #define xt_fs_mkdir_mode(path, mode) _mkdir(path)
58
+ #define xt_fs_mkdir_one(path) _mkdir(path)
59
+ #define xt_fs_rmdir_one(path) _rmdir(path)
60
+ #define XT_FS_OPEN _open
61
+ #define XT_FS_CLOSE _close
62
+ #define XT_FS_READ _read
63
+ #define XT_FS_WRITE _write
64
+
65
+ #ifndef S_ISREG
66
+ #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
67
+ #endif
68
+ #ifndef S_ISDIR
69
+ #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
70
+ #endif
71
+ #ifndef S_ISLNK
72
+ #define S_ISLNK(m) 0
73
+ #endif
74
+ #ifndef S_ISFIFO
75
+ #define S_ISFIFO(m) 0
76
+ #endif
77
+ #ifndef S_ISSOCK
78
+ #define S_ISSOCK(m) 0
79
+ #endif
80
+ #ifndef S_ISBLK
81
+ #define S_ISBLK(m) 0
82
+ #endif
83
+ #ifndef S_ISCHR
84
+ #define S_ISCHR(m) (((m) & _S_IFMT) == _S_IFCHR)
85
+ #endif
86
+ #ifndef F_OK
87
+ #define F_OK 0
88
+ #endif
89
+ #else
90
+ #include <fcntl.h>
91
+ #include <sys/stat.h>
92
+ #include <sys/types.h>
93
+ #include <unistd.h>
94
+
95
+ typedef struct stat xt_fs_stat_t;
96
+ #define xt_fs_stat_fn stat
97
+ #define xt_fs_lstat_fn lstat
98
+ #define xt_fs_access access
99
+ #define xt_fs_mkdir_mode(path, mode) mkdir((path), (mode))
100
+ #define xt_fs_mkdir_one(path) mkdir(path, 0777)
101
+ #define xt_fs_rmdir_one(path) rmdir(path)
102
+ #define XT_FS_OPEN open
103
+ #define XT_FS_CLOSE close
104
+ #define XT_FS_READ read
105
+ #define XT_FS_WRITE write
106
+ #endif
107
+
108
+ /* -- directory iteration -------------------------------------------------- */
109
+
110
+ #if defined(_WIN32)
111
+ /* Directory iteration backed by the CRT's _findfirst/_findnext. */
112
+ typedef struct {
113
+ intptr_t handle;
114
+ struct _finddata_t entry;
115
+ } xt_fs_dir;
116
+
117
+ static inline int xt_fs_dir_open(xt_fs_dir *dir, const char *path) {
118
+ size_t length = strlen(path);
119
+ char *pattern = (char *)malloc(length + 3);
120
+ if (!pattern) return 0;
121
+ memcpy(pattern, path, length);
122
+ pattern[length] = '/';
123
+ pattern[length + 1] = '*';
124
+ pattern[length + 2] = '\0';
125
+ dir->handle = _findfirst(pattern, &dir->entry);
126
+ free(pattern);
127
+ return dir->handle != -1;
128
+ }
129
+
130
+ static inline int xt_fs_dir_next(xt_fs_dir *dir) { return _findnext(dir->handle, &dir->entry) == 0; }
131
+ static inline const char *xt_fs_dir_name(xt_fs_dir *dir) { return dir->entry.name; }
132
+ static inline void xt_fs_dir_close(xt_fs_dir *dir) { _findclose(dir->handle); }
133
+ #else
134
+ #include <dirent.h>
135
+
136
+ typedef struct {
137
+ DIR *handle;
138
+ struct dirent *entry;
139
+ } xt_fs_dir;
140
+
141
+ static inline int xt_fs_dir_open(xt_fs_dir *dir, const char *path) {
142
+ dir->handle = opendir(path);
143
+ dir->entry = NULL;
144
+ return dir->handle != NULL;
145
+ }
146
+
147
+ static inline int xt_fs_dir_next(xt_fs_dir *dir) {
148
+ dir->entry = readdir(dir->handle);
149
+ return dir->entry != NULL;
150
+ }
151
+
152
+ static inline const char *xt_fs_dir_name(xt_fs_dir *dir) { return dir->entry->d_name; }
153
+ static inline void xt_fs_dir_close(xt_fs_dir *dir) { closedir(dir->handle); }
154
+ #endif
155
+
156
+ /* -- errors --------------------------------------------------------------- */
157
+
158
+ /* Node's `err.code` for the errno values the fs module can raise. */
159
+ static inline const char *xt_fs_errno_name(int err) {
160
+ switch (err) {
161
+ case ENOENT: return "ENOENT";
162
+ case EACCES: return "EACCES";
163
+ case EEXIST: return "EEXIST";
164
+ case ENOTDIR: return "ENOTDIR";
165
+ case EISDIR: return "EISDIR";
166
+ case EPERM: return "EPERM";
167
+ case EINVAL: return "EINVAL";
168
+ case EBADF: return "EBADF";
169
+ case ENOSPC: return "ENOSPC";
170
+ case EROFS: return "EROFS";
171
+ case EMFILE: return "EMFILE";
172
+ #ifdef ENOTEMPTY
173
+ case ENOTEMPTY: return "ENOTEMPTY";
174
+ #endif
175
+ #ifdef ELOOP
176
+ case ELOOP: return "ELOOP";
177
+ #endif
178
+ #ifdef ENAMETOOLONG
179
+ case ENAMETOOLONG: return "ENAMETOOLONG";
180
+ #endif
181
+ #ifdef EXDEV
182
+ case EXDEV: return "EXDEV";
183
+ #endif
184
+ default: return "UNKNOWN";
185
+ }
186
+ }
187
+
188
+ /* Build a Node-shaped Error (`code`/`errno`/`syscall`/`path`/`message`). */
189
+ static inline xt_value xt_fs_error_object(int err, const char *syscall, const char *path) {
190
+ const char *code = xt_fs_errno_name(err);
191
+ char buffer[1024];
192
+ snprintf(buffer, sizeof(buffer), "%s: %s, %s '%s'", code, strerror(err), syscall ? syscall : "",
193
+ path ? path : "");
194
+ xt_value error = xt_object_new();
195
+ ((xt_object *)XT_GET_PTR(error))->header.kind = XT_OBJECT_KIND_ERROR;
196
+ xt_object_set(error, xt_string_from_cstr("name"), xt_string_from_cstr("Error"));
197
+ xt_object_set(error, xt_string_from_cstr("message"), xt_string_from_cstr(buffer));
198
+ xt_object_set(error, xt_string_from_cstr("code"), xt_string_from_cstr(code));
199
+ xt_object_set(error, xt_string_from_cstr("errno"), xt_number((double)(-err)));
200
+ if (syscall) xt_object_set(error, xt_string_from_cstr("syscall"), xt_string_from_cstr(syscall));
201
+ if (path) xt_object_set(error, xt_string_from_cstr("path"), xt_string_from_cstr(path));
202
+ return error;
203
+ }
204
+
205
+ /* Raise `errno` (captured by the caller right after a failed syscall). */
206
+ static inline void xt_fs_raise_errno(int err, const char *syscall, const char *path) {
207
+ xt_throw(xt_fs_error_object(err, syscall, path));
208
+ }
209
+
210
+ static inline void xt_fs_raise_path(const char *syscall, const char *path) {
211
+ xt_fs_raise_errno(errno, syscall, path);
212
+ }
213
+
214
+ /* -- time helpers --------------------------------------------------------- */
215
+
216
+ /* Node accepts a Date or a number of seconds; Date stores milliseconds. */
217
+ static inline double xt_fs_time_seconds(xt_value value) {
218
+ if (XT_IS_OBJECT(value) && ((xt_object *)XT_GET_PTR(value))->header.kind == XT_OBJECT_KIND_DATE) {
219
+ return ((xt_date *)XT_GET_PTR(value))->time / 1000.0;
220
+ }
221
+ return xt_to_number(value);
222
+ }
223
+
224
+ /* -- open-flag parsing ---------------------------------------------------- */
225
+
226
+ #if defined(_WIN32)
227
+ #define XT_FS_BINARY_FLAG _O_BINARY
228
+ #define XT_FS_RDONLY _O_RDONLY
229
+ #define XT_FS_WRONLY _O_WRONLY
230
+ #define XT_FS_RDWR _O_RDWR
231
+ #define XT_FS_CREAT _O_CREAT
232
+ #define XT_FS_TRUNC _O_TRUNC
233
+ #define XT_FS_APPEND _O_APPEND
234
+ #define XT_FS_EXCL _O_EXCL
235
+ #else
236
+ #define XT_FS_BINARY_FLAG 0
237
+ #define XT_FS_RDONLY O_RDONLY
238
+ #define XT_FS_WRONLY O_WRONLY
239
+ #define XT_FS_RDWR O_RDWR
240
+ #define XT_FS_CREAT O_CREAT
241
+ #define XT_FS_TRUNC O_TRUNC
242
+ #define XT_FS_APPEND O_APPEND
243
+ #define XT_FS_EXCL O_EXCL
244
+ #endif
245
+
246
+ /* Translate a Node flag string (`"r"`, `"w+"`, `"a"`, ...) or number. */
247
+ static inline int xt_fs_parse_flags(xt_value value) {
248
+ if (XT_IS_STRING(value)) {
249
+ const char *flag = xt_string_data(value);
250
+ if (!flag) return XT_FS_RDONLY | XT_FS_BINARY_FLAG;
251
+ int plus = strchr(flag, '+') != NULL;
252
+ int excl = strchr(flag, 'x') != NULL;
253
+ int base;
254
+ if (flag[0] == 'a') base = XT_FS_APPEND | XT_FS_CREAT;
255
+ else if (flag[0] == 'w') base = XT_FS_CREAT | XT_FS_TRUNC;
256
+ else base = 0; /* 'r' and the numeric path below */
257
+ base |= (plus || flag[0] == 'w' || flag[0] == 'a') ? XT_FS_RDWR : XT_FS_RDONLY;
258
+ if (excl) base |= XT_FS_EXCL;
259
+ return base | XT_FS_BINARY_FLAG;
260
+ }
261
+ return (int)xt_to_number(value) | XT_FS_BINARY_FLAG;
262
+ }
263
+
264
+ /* -- shared object builders (defined in fs_ops.c) ------------------------- */
265
+
266
+ xt_value xt_node_stat_result(const xt_fs_stat_t *info);
267
+ xt_value xt_node_dirent_result(const char *dir, const char *name);
268
+
269
+ /* -- synchronous fs builtins (one translation unit per group) ------------- */
270
+
271
+ xt_value xt_node_read_text_file(int32_t argc, xt_value *argv);
272
+ xt_value xt_node_write_file(int32_t argc, xt_value *argv);
273
+ xt_value xt_node_append_file(int32_t argc, xt_value *argv);
274
+ xt_value xt_node_exists(int32_t argc, xt_value *argv);
275
+ xt_value xt_node_mkdir(int32_t argc, xt_value *argv);
276
+ xt_value xt_node_read_dir(int32_t argc, xt_value *argv);
277
+ xt_value xt_node_rm(int32_t argc, xt_value *argv);
278
+ xt_value xt_node_unlink(int32_t argc, xt_value *argv);
279
+ xt_value xt_node_rmdir(int32_t argc, xt_value *argv);
280
+ xt_value xt_node_rename(int32_t argc, xt_value *argv);
281
+ xt_value xt_node_copy_file(int32_t argc, xt_value *argv);
282
+ xt_value xt_node_realpath(int32_t argc, xt_value *argv);
283
+ xt_value xt_node_stat(int32_t argc, xt_value *argv);
284
+ xt_value xt_node_lstat(int32_t argc, xt_value *argv);
285
+ xt_value xt_node_cp(int32_t argc, xt_value *argv);
286
+
287
+ /* fd_ops.c */
288
+ xt_value xt_node_open(int32_t argc, xt_value *argv);
289
+ xt_value xt_node_close(int32_t argc, xt_value *argv);
290
+ xt_value xt_node_read(int32_t argc, xt_value *argv);
291
+ xt_value xt_node_write(int32_t argc, xt_value *argv);
292
+ xt_value xt_node_readv(int32_t argc, xt_value *argv);
293
+ xt_value xt_node_writev(int32_t argc, xt_value *argv);
294
+ xt_value xt_node_fstat(int32_t argc, xt_value *argv);
295
+ xt_value xt_node_fsync(int32_t argc, xt_value *argv);
296
+ xt_value xt_node_fdatasync(int32_t argc, xt_value *argv);
297
+ xt_value xt_node_ftruncate(int32_t argc, xt_value *argv);
298
+ xt_value xt_node_fchmod(int32_t argc, xt_value *argv);
299
+ xt_value xt_node_fchown(int32_t argc, xt_value *argv);
300
+ xt_value xt_node_futimes(int32_t argc, xt_value *argv);
301
+
302
+ /* meta_ops.c */
303
+ xt_value xt_node_access(int32_t argc, xt_value *argv);
304
+ xt_value xt_node_chmod(int32_t argc, xt_value *argv);
305
+ xt_value xt_node_chown(int32_t argc, xt_value *argv);
306
+ xt_value xt_node_lchmod(int32_t argc, xt_value *argv);
307
+ xt_value xt_node_lchown(int32_t argc, xt_value *argv);
308
+ xt_value xt_node_truncate(int32_t argc, xt_value *argv);
309
+ xt_value xt_node_utimes(int32_t argc, xt_value *argv);
310
+ xt_value xt_node_lutimes(int32_t argc, xt_value *argv);
311
+ xt_value xt_node_statfs(int32_t argc, xt_value *argv);
312
+ xt_value xt_node_mkdtemp(int32_t argc, xt_value *argv);
313
+
314
+ /* link_ops.c */
315
+ xt_value xt_node_link(int32_t argc, xt_value *argv);
316
+ xt_value xt_node_symlink(int32_t argc, xt_value *argv);
317
+ xt_value xt_node_readlink(int32_t argc, xt_value *argv);
318
+
319
+ /* dir.c */
320
+ xt_value xt_node_opendir(int32_t argc, xt_value *argv);
321
+
322
+ /* glob.c */
323
+ xt_value xt_node_glob(int32_t argc, xt_value *argv);
324
+
325
+ /* watch.c */
326
+ xt_value xt_node_watch(int32_t argc, xt_value *argv);
327
+ xt_value xt_node_watch_file(int32_t argc, xt_value *argv);
328
+ xt_value xt_node_unwatch_file(int32_t argc, xt_value *argv);
329
+
330
+ /* constants.c */
331
+ xt_value xt_fs_constants(void);
332
+ xt_value xt_fs_promises(void);
333
+
334
+ /* A promise wrapper over a synchronous builtin: an immediately settled
335
+ * promise that is rejected (rather than throwing) when the builtin raises. */
336
+ static inline xt_value xt_fs_promisify(xt_value (*fn)(int32_t, xt_value *), int32_t argc, xt_value *argv) {
337
+ void *frame = xt_try_enter();
338
+ if (xt_try_setjmp(frame) == 0) {
339
+ xt_value result = fn(argc, argv);
340
+ xt_try_leave(frame);
341
+ return xt_promise_resolve(result);
342
+ }
343
+ xt_value exception = xt_try_exception(frame);
344
+ xt_try_leave(frame);
345
+ return xt_promise_reject(exception);
346
+ }
347
+
348
+ /* -- Buffer bridge (defined in buffer/parts/prototype.inc) ---------------- */
349
+
350
+ int xt_node_is_buffer(xt_value value);
351
+ unsigned char *xt_node_buffer_bytes(xt_value value, size_t *outLength);
352
+ uint32_t xt_node_buffer_length(xt_value value);
353
+ void xt_node_buffer_set(xt_value value, uint32_t index, unsigned char byte);
18
354
 
19
355
  static const char xt_fs_base64_alphabet[] =
20
356
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -37,6 +373,15 @@ static inline int xt_fs_is_base64(const char *encoding) {
37
373
  return encoding && strcmp(encoding, "base64") == 0;
38
374
  }
39
375
 
376
+ static inline int xt_fs_is_base64url(const char *encoding) {
377
+ return encoding && strcmp(encoding, "base64url") == 0;
378
+ }
379
+
380
+ static inline int xt_fs_is_utf16(const char *encoding) {
381
+ return encoding && (strcmp(encoding, "utf16le") == 0 || strcmp(encoding, "ucs2") == 0 ||
382
+ strcmp(encoding, "ucs-2") == 0 || strcmp(encoding, "utf-16le") == 0);
383
+ }
384
+
40
385
  static inline xt_value xt_fs_encode_hex(const unsigned char *data, size_t length) {
41
386
  static const char digits[] = "0123456789abcdef";
42
387
  char *buffer = (char *)malloc(length * 2 + 1);
@@ -82,9 +427,42 @@ static inline xt_value xt_fs_encode_base64(const unsigned char *data, size_t len
82
427
  }
83
428
 
84
429
  /* Encode bytes for `readFileSync` according to `encoding` (default raw UTF-8). */
430
+ static inline xt_value xt_fs_encode_base64url(const unsigned char *data, size_t length) {
431
+ size_t outLength = ((length + 2) / 3) * 4 + 1;
432
+ char *buffer = (char *)malloc(outLength);
433
+ if (!buffer) return xt_undefined();
434
+ size_t i = 0, j = 0;
435
+ while (i + 2 < length) {
436
+ uint32_t n = ((uint32_t)data[i] << 16) | ((uint32_t)data[i + 1] << 8) | (uint32_t)data[i + 2];
437
+ buffer[j++] = xt_fs_base64_alphabet[(n >> 18) & 63];
438
+ buffer[j++] = xt_fs_base64_alphabet[(n >> 12) & 63];
439
+ buffer[j++] = xt_fs_base64_alphabet[(n >> 6) & 63];
440
+ buffer[j++] = xt_fs_base64_alphabet[n & 63];
441
+ i += 3;
442
+ }
443
+ if (length - i == 1) {
444
+ uint32_t n = (uint32_t)data[i] << 16;
445
+ buffer[j++] = xt_fs_base64_alphabet[(n >> 18) & 63];
446
+ buffer[j++] = xt_fs_base64_alphabet[(n >> 12) & 63];
447
+ } else if (length - i == 2) {
448
+ uint32_t n = ((uint32_t)data[i] << 16) | ((uint32_t)data[i + 1] << 8);
449
+ buffer[j++] = xt_fs_base64_alphabet[(n >> 18) & 63];
450
+ buffer[j++] = xt_fs_base64_alphabet[(n >> 12) & 63];
451
+ buffer[j++] = xt_fs_base64_alphabet[(n >> 6) & 63];
452
+ }
453
+ for (size_t k = 0; k < j; k++) {
454
+ if (buffer[k] == '+') buffer[k] = '-';
455
+ else if (buffer[k] == '/') buffer[k] = '_';
456
+ }
457
+ xt_value result = xt_string_new(buffer, j);
458
+ free(buffer);
459
+ return result;
460
+ }
461
+
85
462
  static inline xt_value xt_fs_encode_bytes(const unsigned char *data, size_t length, const char *encoding) {
86
463
  if (xt_fs_is_hex(encoding)) return xt_fs_encode_hex(data, length);
87
464
  if (xt_fs_is_base64(encoding)) return xt_fs_encode_base64(data, length);
465
+ if (xt_fs_is_base64url(encoding)) return xt_fs_encode_base64url(data, length);
88
466
  return xt_string_new((const char *)data, length);
89
467
  }
90
468
 
@@ -92,8 +470,8 @@ static inline int xt_fs_base64_value(char c) {
92
470
  if (c >= 'A' && c <= 'Z') return c - 'A';
93
471
  if (c >= 'a' && c <= 'z') return c - 'a' + 26;
94
472
  if (c >= '0' && c <= '9') return c - '0' + 52;
95
- if (c == '+') return 62;
96
- if (c == '/') return 63;
473
+ if (c == '+' || c == '-') return 62;
474
+ if (c == '/' || c == '_') return 63;
97
475
  return -1;
98
476
  }
99
477
 
@@ -111,7 +489,7 @@ static inline int xt_fs_hex_value(char c) {
111
489
  */
112
490
  static inline unsigned char *xt_fs_decode_text(const char *text, size_t length, const char *encoding, size_t *outLength) {
113
491
  unsigned char *bytes = NULL;
114
- if (xt_fs_is_base64(encoding)) {
492
+ if (xt_fs_is_base64(encoding) || xt_fs_is_base64url(encoding)) {
115
493
  size_t capacity = (length / 4 + 1) * 3;
116
494
  bytes = (unsigned char *)malloc(capacity + 1);
117
495
  if (!bytes) return NULL;
@@ -159,8 +537,13 @@ static inline unsigned char *xt_fs_decode_text(const char *text, size_t length,
159
537
  return bytes;
160
538
  }
161
539
 
540
+ /*
541
+ * Report a failed syscall. The fs module follows Node and *throws* a shaped
542
+ * Error (the runtime now has catchable exceptions), so callers must not rely
543
+ * on a return value after a failure.
544
+ */
162
545
  static inline void xt_fs_error(const char *action, const char *path) {
163
- fprintf(stderr, "xbintsc: cannot %s '%s'\n", action, path ? path : "");
546
+ xt_fs_raise_errno(errno, action, path);
164
547
  }
165
548
 
166
549
  #endif /* XT_NODE_FS_COMMON_H */