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
@@ -2,68 +2,283 @@
2
2
  * Node.js `fs/promises` for xbintsc.
3
3
  *
4
4
  * xbintsc has no asynchronous I/O scheduler, so these builtins reuse the
5
- * synchronous `fs` implementations and wrap the result in an immediately
6
- * settled promise. `await`/`.then` therefore work exactly as expected while
7
- * the actual work happens synchronously.
5
+ * synchronous `fs` implementations and wrap the result in a promise that is
6
+ * already settled. `await`/`.then` therefore work as expected while the actual
7
+ * work happens synchronously. A synchronous failure is turned into a rejected
8
+ * promise instead of a thrown exception (matching Node's contract).
9
+ *
10
+ * `open()` resolves to a `FileHandle`-shaped object (a plain object with an
11
+ * `fd` property and the usual methods); there is no shared prototype yet, so
12
+ * `handle instanceof FileHandle` is not supported.
8
13
  */
9
14
 
10
15
  #include "../node_common.h"
16
+ #include "fs_common.h"
17
+
18
+ /* ------------------------------------------------------------------------- */
19
+ /* FileHandle */
20
+ /* ------------------------------------------------------------------------- */
21
+
22
+ static xt_value xt_fh_method(xt_value thisValue, xt_value (*fn)(int32_t, xt_value *), int32_t argc, xt_value *argv,
23
+ int prefix) {
24
+ xt_value args[10];
25
+ int32_t count = 0;
26
+ if (prefix >= 1) args[count++] = xt_object_get_cstr(thisValue, "fd");
27
+ if (prefix >= 2) args[count++] = xt_object_get_cstr(thisValue, "fd2");
28
+ for (int32_t i = 0; i < argc && count < 10; i++) args[count++] = argv[i];
29
+ return xt_fs_promisify(fn, count, args);
30
+ }
31
+
32
+ static xt_value xt_fh_read(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
33
+ (void)env;
34
+ return xt_fh_method(thisValue, xt_node_read, argc, argv, 1);
35
+ }
36
+
37
+ static xt_value xt_fh_write(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
38
+ (void)env;
39
+ return xt_fh_method(thisValue, xt_node_write, argc, argv, 1);
40
+ }
41
+
42
+ static xt_value xt_fh_close(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
43
+ (void)env;
44
+ return xt_fh_method(thisValue, xt_node_close, argc, argv, 1);
45
+ }
46
+
47
+ static xt_value xt_fh_stat(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
48
+ (void)env;
49
+ return xt_fh_method(thisValue, xt_node_fstat, argc, argv, 1);
50
+ }
51
+
52
+ static xt_value xt_fh_truncate(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
53
+ (void)env;
54
+ return xt_fh_method(thisValue, xt_node_ftruncate, argc, argv, 1);
55
+ }
56
+
57
+ static xt_value xt_fh_chmod(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
58
+ (void)env;
59
+ return xt_fh_method(thisValue, xt_node_fchmod, argc, argv, 1);
60
+ }
61
+
62
+ static xt_value xt_fh_chown(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
63
+ (void)env;
64
+ return xt_fh_method(thisValue, xt_node_fchown, argc, argv, 1);
65
+ }
66
+
67
+ static xt_value xt_fh_utimes(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
68
+ (void)env;
69
+ return xt_fh_method(thisValue, xt_node_futimes, argc, argv, 1);
70
+ }
71
+
72
+ static xt_value xt_fh_sync(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
73
+ (void)env;
74
+ return xt_fh_method(thisValue, xt_node_fsync, argc, argv, 1);
75
+ }
76
+
77
+ static xt_value xt_fh_datasync(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
78
+ (void)env;
79
+ return xt_fh_method(thisValue, xt_node_fdatasync, argc, argv, 1);
80
+ }
81
+
82
+ /* Read from the handle's current position to EOF; caller frees. */
83
+ static unsigned char *xt_fh_slurp(int fd, size_t *outLength) {
84
+ size_t capacity = 8192;
85
+ size_t length = 0;
86
+ unsigned char *bytes = (unsigned char *)malloc(capacity);
87
+ if (!bytes) return NULL;
88
+ for (;;) {
89
+ if (length == capacity) {
90
+ capacity *= 2;
91
+ unsigned char *grown = (unsigned char *)realloc(bytes, capacity);
92
+ if (!grown) {
93
+ free(bytes);
94
+ return NULL;
95
+ }
96
+ bytes = grown;
97
+ }
98
+ ssize_t bytesRead = XT_FS_READ(fd, bytes + length, capacity - length);
99
+ if (bytesRead < 0) {
100
+ free(bytes);
101
+ return NULL;
102
+ }
103
+ if (bytesRead == 0) break;
104
+ length += (size_t)bytesRead;
105
+ }
106
+ *outLength = length;
107
+ return bytes;
108
+ }
109
+
110
+ static xt_value xt_fh_read_all(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
111
+ (void)env;
112
+ int fd = (int)xt_to_number(xt_object_get_cstr(thisValue, "fd"));
113
+ const char *encoding = argc > 0 ? xt_fs_encoding(argv[0]) : NULL;
114
+ size_t length = 0;
115
+ unsigned char *bytes = xt_fh_slurp(fd, &length);
116
+ if (!bytes) return xt_promise_reject(xt_fs_error_object(errno, "read", NULL));
117
+ xt_value result = xt_fs_encode_bytes(bytes, length, encoding);
118
+ free(bytes);
119
+ return xt_promise_resolve(result);
120
+ }
121
+
122
+ static xt_value xt_fh_write_all(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
123
+ (void)env;
124
+ xt_value args[2];
125
+ args[0] = xt_object_get_cstr(thisValue, "fd");
126
+ args[1] = argc > 0 ? argv[0] : XT_UNDEFINED;
127
+ return xt_fs_promisify(xt_node_write, 2, args);
128
+ }
129
+
130
+ static xt_value xt_fh_new(xt_value fdValue) {
131
+ xt_value handle = xt_object_new();
132
+ xt_node_set(handle, "fd", fdValue);
133
+ xt_node_define_method(handle, "read", (void *)xt_fh_read);
134
+ xt_node_define_method(handle, "write", (void *)xt_fh_write);
135
+ xt_node_define_method(handle, "readFile", (void *)xt_fh_read_all);
136
+ xt_node_define_method(handle, "writeFile", (void *)xt_fh_write_all);
137
+ xt_node_define_method(handle, "appendFile", (void *)xt_fh_write_all);
138
+ xt_node_define_method(handle, "close", (void *)xt_fh_close);
139
+ xt_node_define_method(handle, "stat", (void *)xt_fh_stat);
140
+ xt_node_define_method(handle, "truncate", (void *)xt_fh_truncate);
141
+ xt_node_define_method(handle, "chmod", (void *)xt_fh_chmod);
142
+ xt_node_define_method(handle, "chown", (void *)xt_fh_chown);
143
+ xt_node_define_method(handle, "utimes", (void *)xt_fh_utimes);
144
+ xt_node_define_method(handle, "sync", (void *)xt_fh_sync);
145
+ xt_node_define_method(handle, "datasync", (void *)xt_fh_datasync);
146
+ return handle;
147
+ }
148
+
149
+ /* ------------------------------------------------------------------------- */
150
+ /* Promise wrappers */
151
+ /* ------------------------------------------------------------------------- */
152
+
153
+ #define XT_FS_PROMISE(name, fn) \
154
+ xt_value name(int32_t argc, xt_value *argv) { return xt_fs_promisify((fn), argc, argv); }
155
+
156
+ XT_FS_PROMISE(xt_node_p_read_file, xt_node_read_text_file)
157
+ XT_FS_PROMISE(xt_node_p_write_file, xt_node_write_file)
158
+ XT_FS_PROMISE(xt_node_p_append_file, xt_node_append_file)
159
+ XT_FS_PROMISE(xt_node_p_mkdir, xt_node_mkdir)
160
+ XT_FS_PROMISE(xt_node_p_readdir, xt_node_read_dir)
161
+ XT_FS_PROMISE(xt_node_p_rm, xt_node_rm)
162
+ XT_FS_PROMISE(xt_node_p_unlink, xt_node_unlink)
163
+ XT_FS_PROMISE(xt_node_p_rmdir, xt_node_rmdir)
164
+ XT_FS_PROMISE(xt_node_p_rename, xt_node_rename)
165
+ XT_FS_PROMISE(xt_node_p_copy_file, xt_node_copy_file)
166
+ XT_FS_PROMISE(xt_node_p_realpath, xt_node_realpath)
167
+ XT_FS_PROMISE(xt_node_p_stat, xt_node_stat)
168
+ XT_FS_PROMISE(xt_node_p_lstat, xt_node_lstat)
169
+ XT_FS_PROMISE(xt_node_p_access, xt_node_access)
170
+ XT_FS_PROMISE(xt_node_p_cp, xt_node_cp)
171
+ XT_FS_PROMISE(xt_node_p_chmod, xt_node_chmod)
172
+ XT_FS_PROMISE(xt_node_p_chown, xt_node_chown)
173
+ XT_FS_PROMISE(xt_node_p_lchmod, xt_node_lchmod)
174
+ XT_FS_PROMISE(xt_node_p_lchown, xt_node_lchown)
175
+ XT_FS_PROMISE(xt_node_p_truncate, xt_node_truncate)
176
+ XT_FS_PROMISE(xt_node_p_utimes, xt_node_utimes)
177
+ XT_FS_PROMISE(xt_node_p_lutimes, xt_node_lutimes)
178
+ XT_FS_PROMISE(xt_node_p_link, xt_node_link)
179
+ XT_FS_PROMISE(xt_node_p_symlink, xt_node_symlink)
180
+ XT_FS_PROMISE(xt_node_p_readlink, xt_node_readlink)
181
+ XT_FS_PROMISE(xt_node_p_mkdtemp, xt_node_mkdtemp)
182
+ XT_FS_PROMISE(xt_node_p_statfs, xt_node_statfs)
183
+ XT_FS_PROMISE(xt_node_p_opendir, xt_node_opendir)
184
+ XT_FS_PROMISE(xt_node_p_glob, xt_node_glob)
185
+ XT_FS_PROMISE(xt_node_p_watch, xt_node_watch)
186
+
187
+ xt_value xt_node_p_open(int32_t argc, xt_value *argv) {
188
+ void *frame = xt_try_enter();
189
+ if (xt_try_setjmp(frame) == 0) {
190
+ xt_value fdValue = xt_node_open(argc, argv);
191
+ xt_try_leave(frame);
192
+ return xt_promise_resolve(xt_fh_new(fdValue));
193
+ }
194
+ xt_value exception = xt_try_exception(frame);
195
+ xt_try_leave(frame);
196
+ return xt_promise_reject(exception);
197
+ }
198
+
199
+ /* ------------------------------------------------------------------------- */
200
+ /* `fs.promises` facade */
201
+ /* ------------------------------------------------------------------------- */
202
+
203
+ #define XT_FS_PROMISES_FACADE(name, object) \
204
+ static xt_value xt_fs_promises_facade_##name(xt_value thisValue, xt_value env, int32_t argc, \
205
+ xt_value *argv) { \
206
+ (void)thisValue; \
207
+ (void)env; \
208
+ return name(argc, argv); \
209
+ }
210
+
211
+ XT_FS_PROMISES_FACADE(xt_node_p_read_file, _)
212
+ XT_FS_PROMISES_FACADE(xt_node_p_write_file, _)
213
+ XT_FS_PROMISES_FACADE(xt_node_p_append_file, _)
214
+ XT_FS_PROMISES_FACADE(xt_node_p_mkdir, _)
215
+ XT_FS_PROMISES_FACADE(xt_node_p_readdir, _)
216
+ XT_FS_PROMISES_FACADE(xt_node_p_rm, _)
217
+ XT_FS_PROMISES_FACADE(xt_node_p_unlink, _)
218
+ XT_FS_PROMISES_FACADE(xt_node_p_rmdir, _)
219
+ XT_FS_PROMISES_FACADE(xt_node_p_rename, _)
220
+ XT_FS_PROMISES_FACADE(xt_node_p_copy_file, _)
221
+ XT_FS_PROMISES_FACADE(xt_node_p_realpath, _)
222
+ XT_FS_PROMISES_FACADE(xt_node_p_stat, _)
223
+ XT_FS_PROMISES_FACADE(xt_node_p_lstat, _)
224
+ XT_FS_PROMISES_FACADE(xt_node_p_access, _)
225
+ XT_FS_PROMISES_FACADE(xt_node_p_cp, _)
226
+ XT_FS_PROMISES_FACADE(xt_node_p_chmod, _)
227
+ XT_FS_PROMISES_FACADE(xt_node_p_chown, _)
228
+ XT_FS_PROMISES_FACADE(xt_node_p_lchmod, _)
229
+ XT_FS_PROMISES_FACADE(xt_node_p_lchown, _)
230
+ XT_FS_PROMISES_FACADE(xt_node_p_truncate, _)
231
+ XT_FS_PROMISES_FACADE(xt_node_p_utimes, _)
232
+ XT_FS_PROMISES_FACADE(xt_node_p_lutimes, _)
233
+ XT_FS_PROMISES_FACADE(xt_node_p_link, _)
234
+ XT_FS_PROMISES_FACADE(xt_node_p_symlink, _)
235
+ XT_FS_PROMISES_FACADE(xt_node_p_readlink, _)
236
+ XT_FS_PROMISES_FACADE(xt_node_p_mkdtemp, _)
237
+ XT_FS_PROMISES_FACADE(xt_node_p_statfs, _)
238
+ XT_FS_PROMISES_FACADE(xt_node_p_opendir, _)
239
+ XT_FS_PROMISES_FACADE(xt_node_p_glob, _)
240
+ XT_FS_PROMISES_FACADE(xt_node_p_watch, _)
241
+ XT_FS_PROMISES_FACADE(xt_node_p_open, _)
11
242
 
12
- #include <errno.h>
13
- #include <string.h>
14
- #if defined(_WIN32)
15
- #include <io.h>
16
- #ifndef F_OK
17
- #define F_OK 0
18
- #endif
19
- #define xt_node_access _access
20
- #else
21
- #include <unistd.h>
22
- #define xt_node_access access
23
- #endif
24
-
25
- /* Synchronous implementations provided by the sibling `fs` translation units. */
26
- extern xt_value xt_node_read_text_file(int32_t argc, xt_value *argv);
27
- extern xt_value xt_node_write_file(int32_t argc, xt_value *argv);
28
- extern xt_value xt_node_append_file(int32_t argc, xt_value *argv);
29
- extern xt_value xt_node_mkdir(int32_t argc, xt_value *argv);
30
- extern xt_value xt_node_read_dir(int32_t argc, xt_value *argv);
31
- extern xt_value xt_node_rm(int32_t argc, xt_value *argv);
32
- extern xt_value xt_node_unlink(int32_t argc, xt_value *argv);
33
- extern xt_value xt_node_rmdir(int32_t argc, xt_value *argv);
34
- extern xt_value xt_node_rename(int32_t argc, xt_value *argv);
35
- extern xt_value xt_node_copy_file(int32_t argc, xt_value *argv);
36
- extern xt_value xt_node_realpath(int32_t argc, xt_value *argv);
37
- extern xt_value xt_node_stat(int32_t argc, xt_value *argv);
38
- extern xt_value xt_node_lstat(int32_t argc, xt_value *argv);
39
-
40
- static xt_value xt_node_promises_wrap(int32_t argc, xt_value *argv, xt_value (*fn)(int32_t, xt_value *)) {
41
- return xt_promise_resolve(fn(argc, argv));
42
- }
43
-
44
- xt_value xt_node_p_read_file(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_read_text_file); }
45
- xt_value xt_node_p_write_file(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_write_file); }
46
- xt_value xt_node_p_append_file(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_append_file); }
47
- xt_value xt_node_p_mkdir(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_mkdir); }
48
- xt_value xt_node_p_readdir(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_read_dir); }
49
- xt_value xt_node_p_rm(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_rm); }
50
- xt_value xt_node_p_unlink(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_unlink); }
51
- xt_value xt_node_p_rmdir(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_rmdir); }
52
- xt_value xt_node_p_rename(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_rename); }
53
- xt_value xt_node_p_copy_file(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_copy_file); }
54
- xt_value xt_node_p_realpath(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_realpath); }
55
- xt_value xt_node_p_stat(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_stat); }
56
- xt_value xt_node_p_lstat(int32_t argc, xt_value *argv) { return xt_node_promises_wrap(argc, argv, xt_node_lstat); }
57
-
58
- xt_value xt_node_p_access(int32_t argc, xt_value *argv) {
59
- if (argc < 1) return xt_promise_resolve(xt_undefined());
60
- const char *path = xt_string_data(xt_to_string(argv[0]));
61
- if (!path) return xt_promise_resolve(xt_undefined());
62
- if (xt_node_access(path, F_OK) == 0) return xt_promise_resolve(xt_undefined());
63
- xt_value error = xt_object_new();
64
- xt_node_set(error, "code", xt_string_from_cstr("ENOENT"));
65
- xt_node_set(error, "errno", xt_number(-2));
66
- xt_node_set(error, "path", xt_string_from_cstr(path));
67
- xt_node_set(error, "message", xt_string_from_cstr("ENOENT: no such file or directory"));
68
- return xt_promise_reject(error);
243
+ xt_value xt_fs_promises(void) {
244
+ static xt_value cached = 0;
245
+ if (XT_IS_OBJECT(cached)) return cached;
246
+ xt_value object = xt_object_new();
247
+ #define XT_FS_PROMISE_METHOD(jsName, name) \
248
+ xt_node_define_method(object, jsName, (void *)xt_fs_promises_facade_##name)
249
+ XT_FS_PROMISE_METHOD("readFile", xt_node_p_read_file);
250
+ XT_FS_PROMISE_METHOD("writeFile", xt_node_p_write_file);
251
+ XT_FS_PROMISE_METHOD("appendFile", xt_node_p_append_file);
252
+ XT_FS_PROMISE_METHOD("mkdir", xt_node_p_mkdir);
253
+ XT_FS_PROMISE_METHOD("readdir", xt_node_p_readdir);
254
+ XT_FS_PROMISE_METHOD("rm", xt_node_p_rm);
255
+ XT_FS_PROMISE_METHOD("unlink", xt_node_p_unlink);
256
+ XT_FS_PROMISE_METHOD("rmdir", xt_node_p_rmdir);
257
+ XT_FS_PROMISE_METHOD("rename", xt_node_p_rename);
258
+ XT_FS_PROMISE_METHOD("copyFile", xt_node_p_copy_file);
259
+ XT_FS_PROMISE_METHOD("realpath", xt_node_p_realpath);
260
+ XT_FS_PROMISE_METHOD("stat", xt_node_p_stat);
261
+ XT_FS_PROMISE_METHOD("lstat", xt_node_p_lstat);
262
+ XT_FS_PROMISE_METHOD("access", xt_node_p_access);
263
+ XT_FS_PROMISE_METHOD("cp", xt_node_p_cp);
264
+ XT_FS_PROMISE_METHOD("chmod", xt_node_p_chmod);
265
+ XT_FS_PROMISE_METHOD("chown", xt_node_p_chown);
266
+ XT_FS_PROMISE_METHOD("lchmod", xt_node_p_lchmod);
267
+ XT_FS_PROMISE_METHOD("lchown", xt_node_p_lchown);
268
+ XT_FS_PROMISE_METHOD("truncate", xt_node_p_truncate);
269
+ XT_FS_PROMISE_METHOD("utimes", xt_node_p_utimes);
270
+ XT_FS_PROMISE_METHOD("lutimes", xt_node_p_lutimes);
271
+ XT_FS_PROMISE_METHOD("link", xt_node_p_link);
272
+ XT_FS_PROMISE_METHOD("symlink", xt_node_p_symlink);
273
+ XT_FS_PROMISE_METHOD("readlink", xt_node_p_readlink);
274
+ XT_FS_PROMISE_METHOD("mkdtemp", xt_node_p_mkdtemp);
275
+ XT_FS_PROMISE_METHOD("statfs", xt_node_p_statfs);
276
+ XT_FS_PROMISE_METHOD("opendir", xt_node_p_opendir);
277
+ XT_FS_PROMISE_METHOD("glob", xt_node_p_glob);
278
+ XT_FS_PROMISE_METHOD("watch", xt_node_p_watch);
279
+ XT_FS_PROMISE_METHOD("open", xt_node_p_open);
280
+ #undef XT_FS_PROMISE_METHOD
281
+ xt_object_set(object, xt_string_from_cstr("constants"), xt_fs_constants());
282
+ cached = object;
283
+ return cached;
69
284
  }
@@ -7,9 +7,13 @@
7
7
  *
8
8
  * `readFileSync(path[, options])` reads a file and returns its contents as a
9
9
  * string. `options` may be an encoding string or an object with an `encoding`
10
- * property; `"utf8"` (the default), `"ascii"`, `"latin1"` and `"binary"` return
11
- * the raw bytes, while `"hex"` and `"base64"` return encoded strings. There is
12
- * no `Buffer` type yet, so binary reads always come back as text.
10
+ * property; `"utf8"` (the default), `"ascii"`, `"latin1"` and `"binary"`
11
+ * return the raw bytes, while `"hex"`, `"base64"` and `"base64url"` return
12
+ * encoded strings.
13
+ *
14
+ * Deviation: Node returns a `Buffer` when no encoding is given. xbintsc has no
15
+ * dedicated binary value type, so the default result is a UTF-8 string (raw
16
+ * byte values are preserved for ASCII/latin1 content).
13
17
  */
14
18
 
15
19
  #include "rt.h"
@@ -23,7 +27,7 @@ xt_value xt_node_read_text_file(int32_t argc, xt_value *argv) {
23
27
 
24
28
  FILE *file = fopen(path, "rb");
25
29
  if (!file) {
26
- fprintf(stderr, "xbintsc: cannot open '%s'\n", path);
30
+ xt_fs_error("open", path);
27
31
  return xt_undefined();
28
32
  }
29
33
  fseek(file, 0, SEEK_END);
@@ -0,0 +1,52 @@
1
+ /*
2
+ * `fs.watch` / `fs.watchFile` / `fs.unwatchFile` for xbintsc.
3
+ *
4
+ * The runtime has no event loop or timers, so file-change notifications can
5
+ * never fire. These builtins still return Node-shaped watcher objects (an
6
+ * emitter with a `close()` method) so that feature-detection and cleanup code
7
+ * keeps working; no `change`/`rename` events are delivered. This is a
8
+ * documented deviation.
9
+ */
10
+
11
+ #include "../node_common.h"
12
+ #include "fs_common.h"
13
+
14
+ static xt_value xt_fs_watch_close(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
15
+ (void)thisValue;
16
+ (void)env;
17
+ (void)argc;
18
+ (void)argv;
19
+ return xt_undefined();
20
+ }
21
+
22
+ static xt_value xt_fs_watch_make(xt_value filename, const char *listenerName) {
23
+ xt_value watcher = xt_object_new();
24
+ xt_node_install_emitter(watcher);
25
+ xt_node_define_method(watcher, "close", (void *)xt_fs_watch_close);
26
+ if (XT_IS_STRING(filename)) xt_node_set(watcher, "path", filename);
27
+ if (listenerName) xt_node_set(watcher, "kind", xt_string_from_cstr(listenerName));
28
+ return watcher;
29
+ }
30
+
31
+ xt_value xt_node_watch(int32_t argc, xt_value *argv) {
32
+ xt_value filename = argc > 0 ? argv[0] : XT_UNDEFINED;
33
+ for (int32_t i = 1; i < argc; i++) {
34
+ if (XT_IS_FUNCTION(argv[i])) {
35
+ xt_value watcher = xt_fs_watch_make(filename, NULL);
36
+ xt_node_listeners(watcher, "change", 1);
37
+ return watcher;
38
+ }
39
+ }
40
+ return xt_fs_watch_make(filename, NULL);
41
+ }
42
+
43
+ xt_value xt_node_watch_file(int32_t argc, xt_value *argv) {
44
+ xt_value filename = argc > 0 ? argv[0] : XT_UNDEFINED;
45
+ return xt_fs_watch_make(filename, "file");
46
+ }
47
+
48
+ xt_value xt_node_unwatch_file(int32_t argc, xt_value *argv) {
49
+ (void)argc;
50
+ (void)argv;
51
+ return xt_undefined();
52
+ }
@@ -2,9 +2,14 @@
2
2
  * Node.js `fs.writeFileSync` / `fs.appendFileSync` for xbintsc.
3
3
  *
4
4
  * `writeFileSync(path, data[, options])` truncates the file before writing;
5
- * `appendFileSync(path, data[, options])` appends. `data` is converted to a
6
- * string with the usual JS rules and `options` selects an encoding:
7
- * `"hex"`/`"base64"` decode the string, everything else writes its UTF-8 bytes.
5
+ * `appendFileSync(path, data[, options])` appends. `data` may be a string or a
6
+ * `Buffer`; strings are converted with `options.encoding` (`"hex"`/`"base64"`/
7
+ * `"base64url"` decode, everything else writes its UTF-8 bytes).
8
+ *
9
+ * Deviation: `options.mode` and `options.flag` are accepted but not applied
10
+ * (the file is always created with the process default mode and opened in the
11
+ * `"w"`/`"a"` binary mode). `TypedArray`/`DataView` inputs are not supported.
12
+ * Failures throw a Node-shaped `Error`.
8
13
  */
9
14
 
10
15
  #include "rt.h"
@@ -16,19 +21,23 @@ static xt_value xt_node_write_common(int32_t argc, xt_value *argv, const char *m
16
21
  const char *path = xt_string_data(pathValue);
17
22
  if (!path) return xt_undefined();
18
23
 
19
- xt_value dataValue = xt_to_string(argv[1]);
20
- const char *text = xt_string_data(dataValue);
21
- int32_t length = xt_string_length_value(dataValue);
22
- if (!text || length < 0) return xt_undefined();
23
-
24
- const char *encoding = argc > 2 ? xt_fs_encoding(argv[2]) : NULL;
25
24
  size_t byteLength = 0;
26
- unsigned char *bytes = xt_fs_decode_text(text, (size_t)length, encoding, &byteLength);
25
+ unsigned char *bytes;
26
+ if (xt_node_is_buffer(argv[1])) {
27
+ bytes = xt_node_buffer_bytes(argv[1], &byteLength);
28
+ } else {
29
+ xt_value dataValue = xt_to_string(argv[1]);
30
+ const char *text = xt_string_data(dataValue);
31
+ int32_t length = xt_string_length_value(dataValue);
32
+ if (!text || length < 0) return xt_undefined();
33
+ const char *encoding = argc > 2 ? xt_fs_encoding(argv[2]) : NULL;
34
+ bytes = xt_fs_decode_text(text, (size_t)length, encoding, &byteLength);
35
+ }
27
36
  if (!bytes) return xt_undefined();
28
37
 
29
38
  FILE *file = fopen(path, mode);
30
39
  if (!file) {
31
- fprintf(stderr, "xbintsc: cannot open '%s'\n", path);
40
+ xt_fs_error("open", path);
32
41
  free(bytes);
33
42
  return xt_undefined();
34
43
  }
@@ -145,6 +145,17 @@ static xt_value xt_stream_method_is_paused(xt_value thisValue, xt_value env, int
145
145
 
146
146
  static xt_value xt_stream_transform_flush(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv);
147
147
 
148
+ /* No-op write callback used when `end(chunk)` forwards the chunk to `write`:
149
+ * the caller's `write` hook may invoke its completion callback, which must not
150
+ * be the user's `end` callback and must not be missing. */
151
+ static xt_value xt_stream_noop(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
152
+ (void)thisValue;
153
+ (void)env;
154
+ (void)argc;
155
+ (void)argv;
156
+ return xt_undefined();
157
+ }
158
+
148
159
  static xt_value xt_stream_method_write(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
149
160
  (void)env;
150
161
  xt_value chunk = xt_arg(argc, argv, 0);
@@ -168,10 +179,12 @@ static xt_value xt_stream_method_write(xt_value thisValue, xt_value env, int32_t
168
179
 
169
180
  xt_value writeFn = xt_object_get_cstr(thisValue, "__write");
170
181
  if (XT_IS_FUNCTION(writeFn)) {
182
+ xt_value writeCallback =
183
+ XT_IS_FUNCTION(callback) ? callback : xt_closure_new((void *)xt_stream_noop, 0, NULL);
171
184
  xt_value args[3];
172
185
  args[0] = chunk;
173
186
  args[1] = encoding;
174
- args[2] = callback;
187
+ args[2] = writeCallback;
175
188
  xt_call_with_this(writeFn, thisValue, 3, args);
176
189
  }
177
190
 
@@ -190,7 +203,11 @@ static xt_value xt_stream_method_end(xt_value thisValue, xt_value env, int32_t a
190
203
  xt_value callback = XT_UNDEFINED;
191
204
  if (argc >= 1 && !xt_truthy(xt_is_nullish(chunk))) {
192
205
  xt_value writeFn = xt_object_get_cstr(thisValue, "write");
193
- if (XT_IS_FUNCTION(writeFn)) xt_call_with_this(writeFn, thisValue, 1, &chunk);
206
+ if (XT_IS_FUNCTION(writeFn)) {
207
+ xt_value noop = xt_closure_new((void *)xt_stream_noop, 0, NULL);
208
+ xt_value writeArgs[3] = {chunk, xt_undefined(), noop};
209
+ xt_call_with_this(writeFn, thisValue, 3, writeArgs);
210
+ }
194
211
  }
195
212
  for (int32_t i = 0; i < argc; i++) {
196
213
  if (XT_IS_FUNCTION(argv[i])) callback = argv[i];
package/runtime/rt.h CHANGED
@@ -17,6 +17,14 @@
17
17
  #include <stddef.h>
18
18
  #include <stdint.h>
19
19
 
20
+ /* POSIX `ssize_t` is not exposed by the MSVC/UCRT headers clang uses on
21
+ * Windows (every Unix libc defines it). Alias the Windows pointer-sized signed
22
+ * integer so the runtime's POSIX shims compile unchanged. */
23
+ #if defined(_WIN32) && !defined(_SSIZE_T_DEFINED)
24
+ typedef intptr_t ssize_t;
25
+ #define _SSIZE_T_DEFINED
26
+ #endif
27
+
20
28
  #ifdef __cplusplus
21
29
  extern "C" {
22
30
  #endif
@@ -224,31 +224,18 @@ typedef struct xt_try_frame {
224
224
  struct xt_try_frame *prev;
225
225
  xt_value exception;
226
226
  } xt_try_frame;
227
- /* Call `_setjmp` the way the active `<setjmp.h>` declares it. The two Windows
228
- * C runtimes differ:
227
+ /* Save a try frame with clang's `_setjmp` built-in.
229
228
  *
230
- * - MSVC/UCRT declares `_setjmp(jmp_buf)` with one visible argument and lets
231
- * clang treat it as a built-in that injects the caller's frame address
232
- * (`@llvm.frameaddress(0)`) as a hidden second argument. Writing the frame
233
- * by hand is a compile error ("too many arguments to function call").
234
- * - MinGW-w64 (the vendored llvm-mingw toolchain) declares
235
- * `_setjmp(jmp_buf, void *frame)` itself, so the frame must be passed
236
- * explicitly -- exactly what its own `setjmp` macro does.
237
- *
238
- * The generated IR emits the two-argument form, because it is already IR and
239
- * clang will not rewrite it. On Windows ARM64 it names `_setjmpex` (and the
240
- * entry stack pointer from `llvm.sponentry`) instead, matching clang's own
241
- * lowering there. `_setjmp`/`_setjmpex` (rather than the `setjmp` macro) is
242
- * used so it pairs with the plain `longjmp` in `xt_throw`. */
243
- #if defined(_WIN32) && !defined(__MINGW32__)
244
- #define xt_try_setjmp(framePtr) _setjmp(((xt_try_frame *)(framePtr))->buf)
245
- #elif defined(_WIN32) && defined(__aarch64__)
246
- #define xt_try_setjmp(framePtr) _setjmpex(((xt_try_frame *)(framePtr))->buf, __builtin_sponentry())
247
- #elif defined(_WIN32)
248
- #define xt_try_setjmp(framePtr) _setjmp(((xt_try_frame *)(framePtr))->buf, __builtin_frame_address(0))
249
- #else
229
+ * The generated IR passes the frame explicitly because it is already IR and
230
+ * clang will not rewrite it. This C helper instead calls `_setjmp(buf)` and lets
231
+ * clang supply the frame: on Windows x64 it injects the caller's frame address
232
+ * (`@llvm.frameaddress(0)`) into `_JUMP_BUFFER.Frame` so `longjmp` can feed it
233
+ * to `RtlUnwind`; on Windows ARM64, where the UCRT has no `_setjmp`, it lowers
234
+ * the call to `_setjmpex` with the entry stack pointer (`@llvm.sponentry`). On
235
+ * Linux/macOS `_setjmp` takes only the buffer and pairs with `longjmp`.
236
+ * `_setjmp` is used rather than the exported `setjmp` symbol, whose Windows ABI
237
+ * is an incompatible two-argument routine. */
250
238
  #define xt_try_setjmp(framePtr) _setjmp(((xt_try_frame *)(framePtr))->buf)
251
- #endif
252
239
  void *xt_try_mark(void);
253
240
  void xt_try_restore(void *mark);
254
241
 
package/runtime/xt_ext.h CHANGED
@@ -20,7 +20,6 @@
20
20
  * runtime directory on the include path:
21
21
  *
22
22
  * clang++ -O2 -fPIC -I<runtime> -c mathx.cpp -o mathx.o
23
- * ar rcs libmathx.a mathx.o
24
23
  */
25
24
  #ifndef XT_EXT_H
26
25
  #define XT_EXT_H
package/runtime/xt_io.c CHANGED
@@ -14,9 +14,9 @@
14
14
  * bogus target (`STATUS_BAD_FUNCTION_TABLE`, 0xC00000FF). Windows ARM64 has
15
15
  * no `_setjmp` at all: the generated IR uses `_setjmpex(buf, entry-sp)` with
16
16
  * the stack pointer on entry (`@llvm.sponentry`), exactly as clang lowers a C
17
- * `setjmp` there. The C runtime (see `xt_try_setjmp`) calls whatever the
18
- * active `<setjmp.h>` declares — one argument on MSVC (clang injects the
19
- * frame) and two on MinGW-w64. `_setjmp`/`_setjmpex` are used rather than the
17
+ * `setjmp` there. The C runtime (see `xt_try_setjmp`) calls clang's
18
+ * `_setjmp` built-in so it gets the same frame injection (lowered to
19
+ * `_setjmpex` on ARM64). `_setjmp`/`_setjmpex` are used rather than the
20
20
  * exported `setjmp` symbol, whose Windows ABI is an incompatible two-argument
21
21
  * routine; on Linux/macOS `_setjmp` takes only the buffer and pairs with
22
22
  * `longjmp` (the XSI `_longjmp` does not exist on Windows).
@@ -52,6 +52,26 @@ static xt_try_frame *g_try_top = NULL;
52
52
  int32_t xt_program_argc = 0;
53
53
  char **xt_program_argv = NULL;
54
54
 
55
+ #if defined(_WIN32)
56
+ /*
57
+ * The UCRT validates its low-level I/O arguments and, when one is out of
58
+ * range, calls the invalid-parameter handler. The default handler terminates
59
+ * the process through `_invoke_watson`, which fast-fails with exit status
60
+ * 0xC0000409. That makes POSIX-style calls such as `close(9999)` abort instead
61
+ * of reporting `EBADF`. Install a no-op handler so the CRT returns its normal
62
+ * error code (and sets `errno`) instead of crashing.
63
+ */
64
+ static void __cdecl xt_noop_invalid_parameter_handler(const wchar_t *expression, const wchar_t *function_name,
65
+ const wchar_t *file_name, unsigned int line_number,
66
+ uintptr_t reserved) {
67
+ (void)expression;
68
+ (void)function_name;
69
+ (void)file_name;
70
+ (void)line_number;
71
+ (void)reserved;
72
+ }
73
+ #endif
74
+
55
75
  void xt_set_program_args(int32_t argc, char **argv) {
56
76
  #if defined(_WIN32)
57
77
  /* Keep stdout/stderr in binary mode so `\n` is not translated to `\r\n`,
@@ -59,6 +79,7 @@ void xt_set_program_args(int32_t argc, char **argv) {
59
79
  sockets (the generated `main` calls this before running the program). */
60
80
  _setmode(_fileno(stdout), _O_BINARY);
61
81
  _setmode(_fileno(stderr), _O_BINARY);
82
+ _set_invalid_parameter_handler(xt_noop_invalid_parameter_handler);
62
83
  WSADATA wsaData;
63
84
  WSAStartup(MAKEWORD(2, 2), &wsaData);
64
85
  #endif
@@ -1,8 +1,6 @@
1
1
  /*
2
- * `timegm` and `gmtime_r` are POSIX extensions that Windows' CRT does not
3
- * provide. MinGW-w64 (the bundled Windows toolchain) is affected too: its
4
- * <time.h> only exposes `gmtime_r` when `_POSIX_C_SOURCE` is set and has no
5
- * `timegm` at all, so wrap the MSVC-style equivalents for every `_WIN32` build.
2
+ * `timegm` and `gmtime_r` are POSIX extensions that the MSVC/UCRT headers do
3
+ * not provide, so wrap the MSVC-style equivalents for every `_WIN32` build.
6
4
  */
7
5
  #if defined(_WIN32)
8
6
  static time_t xt_timegm(struct tm *tm) { return (time_t)_mkgmtime64(tm); }