xbintsc 0.1.8 → 0.1.10

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 (93) hide show
  1. package/README.md +12 -7
  2. package/dist/src/binder/binder.d.ts +35 -1
  3. package/dist/src/binder/binder.js +138 -0
  4. package/dist/src/binder/binder.js.map +1 -1
  5. package/dist/src/codegen/generator/calls.d.ts +4 -0
  6. package/dist/src/codegen/generator/calls.js +114 -32
  7. package/dist/src/codegen/generator/calls.js.map +1 -1
  8. package/dist/src/codegen/generator/context.d.ts +2 -0
  9. package/dist/src/codegen/generator/context.js +36 -0
  10. package/dist/src/codegen/generator/context.js.map +1 -1
  11. package/dist/src/codegen/generator/expressions.d.ts +4 -1
  12. package/dist/src/codegen/generator/expressions.js +73 -2
  13. package/dist/src/codegen/generator/expressions.js.map +1 -1
  14. package/dist/src/codegen/generator/module.d.ts +5 -1
  15. package/dist/src/codegen/generator/module.js +107 -6
  16. package/dist/src/codegen/generator/module.js.map +1 -1
  17. package/dist/src/codegen/generator/state.d.ts +2 -0
  18. package/dist/src/codegen/generator/state.js +46 -0
  19. package/dist/src/codegen/generator/state.js.map +1 -1
  20. package/dist/src/codegen/generator/statements.js +9 -1
  21. package/dist/src/codegen/generator/statements.js.map +1 -1
  22. package/dist/src/codegen/generator/tables.d.ts +6 -0
  23. package/dist/src/codegen/generator/tables.js +40 -0
  24. package/dist/src/codegen/generator/tables.js.map +1 -1
  25. package/dist/src/driver/compiler.js +23 -7
  26. package/dist/src/driver/compiler.js.map +1 -1
  27. package/dist/src/driver/modules.d.ts +24 -0
  28. package/dist/src/driver/modules.js +347 -0
  29. package/dist/src/driver/modules.js.map +1 -0
  30. package/dist/src/extensions/node/fs/fs-ops.d.ts +6 -0
  31. package/dist/src/extensions/node/fs/fs-ops.js +18 -0
  32. package/dist/src/extensions/node/fs/fs-ops.js.map +1 -0
  33. package/dist/src/extensions/node/fs/index.d.ts +3 -1
  34. package/dist/src/extensions/node/fs/index.js +11 -3
  35. package/dist/src/extensions/node/fs/index.js.map +1 -1
  36. package/dist/src/extensions/node/fs/write-file.d.ts +5 -0
  37. package/dist/src/extensions/node/fs/write-file.js +8 -0
  38. package/dist/src/extensions/node/fs/write-file.js.map +1 -0
  39. package/dist/src/extensions/node/index.d.ts +2 -1
  40. package/dist/src/extensions/node/index.js +7 -3
  41. package/dist/src/extensions/node/index.js.map +1 -1
  42. package/dist/src/extensions/node/os/index.d.ts +9 -0
  43. package/dist/src/extensions/node/os/index.js +14 -0
  44. package/dist/src/extensions/node/os/index.js.map +1 -0
  45. package/dist/src/extensions/node/path/index.d.ts +10 -0
  46. package/dist/src/extensions/node/path/index.js +15 -0
  47. package/dist/src/extensions/node/path/index.js.map +1 -0
  48. package/dist/src/extensions/node/process/index.d.ts +10 -0
  49. package/dist/src/extensions/node/process/index.js +15 -0
  50. package/dist/src/extensions/node/process/index.js.map +1 -0
  51. package/dist/src/parser/expressions.js +3 -0
  52. package/dist/src/parser/expressions.js.map +1 -1
  53. package/dist/tests/codegen/llvm.test.js +1 -1
  54. package/dist/tests/codegen/llvm.test.js.map +1 -1
  55. package/dist/tests/e2e/compile.test.js +141 -0
  56. package/dist/tests/e2e/compile.test.js.map +1 -1
  57. package/dist/tests/extensions/registry.test.js +9 -3
  58. package/dist/tests/extensions/registry.test.js.map +1 -1
  59. package/package.json +1 -1
  60. package/runtime/ext_node/fs/fs_common.h +166 -0
  61. package/runtime/ext_node/fs/fs_ops.c +342 -0
  62. package/runtime/ext_node/fs/read_file.c +12 -6
  63. package/runtime/ext_node/fs/write_file.c +47 -0
  64. package/runtime/ext_node/os/os.c +165 -0
  65. package/runtime/ext_node/path/path.c +324 -0
  66. package/runtime/ext_node/process/process.c +137 -0
  67. package/runtime/rt.h +61 -4
  68. package/runtime/rt_internal.h +63 -0
  69. package/runtime/xt_builtins.c +47 -1
  70. package/runtime/xt_containers.c +95 -8
  71. package/runtime/xt_io.c +122 -0
  72. package/runtime/xt_promise.c +292 -0
  73. package/runtime/xt_stdlib.c +59 -5
  74. package/runtime/xt_stdlib2.c +1043 -0
  75. package/scripts/build-runtime.mjs +5 -0
  76. package/src/binder/binder.ts +196 -0
  77. package/src/codegen/generator/calls.ts +124 -33
  78. package/src/codegen/generator/context.ts +35 -1
  79. package/src/codegen/generator/expressions.ts +79 -2
  80. package/src/codegen/generator/module.ts +112 -6
  81. package/src/codegen/generator/state.ts +48 -0
  82. package/src/codegen/generator/statements.ts +7 -1
  83. package/src/codegen/generator/tables.ts +43 -0
  84. package/src/driver/compiler.ts +28 -7
  85. package/src/driver/modules.ts +377 -0
  86. package/src/extensions/node/fs/fs-ops.ts +20 -0
  87. package/src/extensions/node/fs/index.ts +11 -3
  88. package/src/extensions/node/fs/write-file.ts +10 -0
  89. package/src/extensions/node/index.ts +7 -3
  90. package/src/extensions/node/os/index.ts +16 -0
  91. package/src/extensions/node/path/index.ts +17 -0
  92. package/src/extensions/node/process/index.ts +17 -0
  93. package/src/parser/expressions.ts +3 -0
@@ -0,0 +1,342 @@
1
+ /*
2
+ * Node.js `fs` filesystem operations for xbintsc.
3
+ *
4
+ * Covers the synchronous, non-descriptor half of the module:
5
+ * existsSync, readdirSync, mkdirSync, rmSync, unlinkSync, rmdirSync,
6
+ * renameSync, copyFileSync, realpathSync, statSync, lstatSync
7
+ *
8
+ * Error handling is intentionally Node-compatible in spirit but not in
9
+ * mechanics: xbintsc has no try/catch that can catch native errors yet, so
10
+ * failures print to stderr and return `undefined` (or `false` for
11
+ * `existsSync`) instead of throwing `ENOENT`-style exceptions.
12
+ */
13
+
14
+ #include "rt.h"
15
+ #include "fs_common.h"
16
+
17
+ #include <dirent.h>
18
+ #include <errno.h>
19
+ #include <sys/stat.h>
20
+ #include <sys/types.h>
21
+ #include <unistd.h>
22
+
23
+ #if defined(_WIN32)
24
+ #include <direct.h>
25
+ #define xt_fs_mkdir_one(path) _mkdir(path)
26
+ #define xt_fs_rmdir_one(path) _rmdir(path)
27
+ #else
28
+ #define xt_fs_mkdir_one(path) mkdir(path, 0777)
29
+ #define xt_fs_rmdir_one(path) rmdir(path)
30
+ #endif
31
+
32
+ /* ------------------------------------------------------------------------- */
33
+ /* existsSync */
34
+ /* ------------------------------------------------------------------------- */
35
+
36
+ xt_value xt_node_exists(int32_t argc, xt_value *argv) {
37
+ if (argc < 1) return xt_bool(0);
38
+ const char *path = xt_string_data(xt_to_string(argv[0]));
39
+ if (!path) return xt_bool(0);
40
+ return xt_bool(access(path, F_OK) == 0);
41
+ }
42
+
43
+ /* ------------------------------------------------------------------------- */
44
+ /* readdirSync */
45
+ /* ------------------------------------------------------------------------- */
46
+
47
+ xt_value xt_node_read_dir(int32_t argc, xt_value *argv) {
48
+ if (argc < 1) return xt_undefined();
49
+ const char *path = xt_string_data(xt_to_string(argv[0]));
50
+ if (!path) return xt_undefined();
51
+
52
+ DIR *dir = opendir(path);
53
+ if (!dir) {
54
+ xt_fs_error("open directory", path);
55
+ return xt_undefined();
56
+ }
57
+
58
+ size_t count = 0;
59
+ size_t capacity = 16;
60
+ xt_value *items = (xt_value *)malloc(sizeof(xt_value) * capacity);
61
+ if (!items) {
62
+ closedir(dir);
63
+ return xt_undefined();
64
+ }
65
+
66
+ struct dirent *entry;
67
+ while ((entry = readdir(dir)) != NULL) {
68
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
69
+ if (count == capacity) {
70
+ capacity *= 2;
71
+ xt_value *grown = (xt_value *)realloc(items, sizeof(xt_value) * capacity);
72
+ if (!grown) break;
73
+ items = grown;
74
+ }
75
+ items[count++] = xt_string_from_cstr(entry->d_name);
76
+ }
77
+ closedir(dir);
78
+
79
+ xt_value result = xt_array_new((int32_t)count, items);
80
+ free(items);
81
+ return result;
82
+ }
83
+
84
+ /* ------------------------------------------------------------------------- */
85
+ /* mkdirSync */
86
+ /* ------------------------------------------------------------------------- */
87
+
88
+ /* True when the second argument asks for recursive creation. */
89
+ static int xt_fs_recursive(xt_value options) {
90
+ if (!xt_truthy(options)) return 0;
91
+ if (XT_IS_OBJECT(options)) return xt_truthy(xt_object_get_cstr(options, "recursive"));
92
+ return 1;
93
+ }
94
+
95
+ static int xt_fs_mkdir_recursive(const char *path) {
96
+ size_t length = strlen(path);
97
+ char *copy = (char *)malloc(length + 1);
98
+ if (!copy) return -1;
99
+ memcpy(copy, path, length + 1);
100
+
101
+ while (length > 1 && copy[length - 1] == '/') copy[--length] = '\0';
102
+ for (size_t i = 1; i <= length; i++) {
103
+ if (copy[i] != '/' && copy[i] != '\0') continue;
104
+ char saved = copy[i];
105
+ copy[i] = '\0';
106
+ if (xt_fs_mkdir_one(copy) != 0 && errno != EEXIST) {
107
+ free(copy);
108
+ return -1;
109
+ }
110
+ copy[i] = saved;
111
+ }
112
+ free(copy);
113
+ return 0;
114
+ }
115
+
116
+ xt_value xt_node_mkdir(int32_t argc, xt_value *argv) {
117
+ if (argc < 1) return xt_undefined();
118
+ const char *path = xt_string_data(xt_to_string(argv[0]));
119
+ if (!path) return xt_undefined();
120
+
121
+ if (argc > 1 && xt_fs_recursive(argv[1])) {
122
+ if (xt_fs_mkdir_recursive(path) != 0) xt_fs_error("create directory", path);
123
+ return xt_undefined();
124
+ }
125
+ if (xt_fs_mkdir_one(path) != 0 && errno != EEXIST) xt_fs_error("create directory", path);
126
+ return xt_undefined();
127
+ }
128
+
129
+ /* ------------------------------------------------------------------------- */
130
+ /* rmSync / unlinkSync / rmdirSync */
131
+ /* ------------------------------------------------------------------------- */
132
+
133
+ static int xt_fs_remove_recursive(const char *path) {
134
+ struct stat info;
135
+ if (lstat(path, &info) != 0) return -1;
136
+ if (!S_ISDIR(info.st_mode)) return remove(path);
137
+
138
+ DIR *dir = opendir(path);
139
+ if (!dir) return -1;
140
+ struct dirent *entry;
141
+ while ((entry = readdir(dir)) != NULL) {
142
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
143
+ size_t size = strlen(path) + strlen(entry->d_name) + 2;
144
+ char *child = (char *)malloc(size);
145
+ if (!child) continue;
146
+ snprintf(child, size, "%s/%s", path, entry->d_name);
147
+ xt_fs_remove_recursive(child);
148
+ free(child);
149
+ }
150
+ closedir(dir);
151
+ return xt_fs_rmdir_one(path);
152
+ }
153
+
154
+ xt_value xt_node_rm(int32_t argc, xt_value *argv) {
155
+ if (argc < 1) return xt_undefined();
156
+ const char *path = xt_string_data(xt_to_string(argv[0]));
157
+ if (!path) return xt_undefined();
158
+
159
+ int recursive = argc > 1 && xt_fs_recursive(argv[1]);
160
+ int status = recursive ? xt_fs_remove_recursive(path) : remove(path);
161
+ if (status != 0) xt_fs_error("remove", path);
162
+ return xt_undefined();
163
+ }
164
+
165
+ xt_value xt_node_unlink(int32_t argc, xt_value *argv) {
166
+ if (argc < 1) return xt_undefined();
167
+ const char *path = xt_string_data(xt_to_string(argv[0]));
168
+ if (!path) return xt_undefined();
169
+ if (remove(path) != 0) xt_fs_error("remove file", path);
170
+ return xt_undefined();
171
+ }
172
+
173
+ xt_value xt_node_rmdir(int32_t argc, xt_value *argv) {
174
+ if (argc < 1) return xt_undefined();
175
+ const char *path = xt_string_data(xt_to_string(argv[0]));
176
+ if (!path) return xt_undefined();
177
+ if (xt_fs_rmdir_one(path) != 0) xt_fs_error("remove directory", path);
178
+ return xt_undefined();
179
+ }
180
+
181
+ /* ------------------------------------------------------------------------- */
182
+ /* renameSync / copyFileSync / realpathSync */
183
+ /* ------------------------------------------------------------------------- */
184
+
185
+ xt_value xt_node_rename(int32_t argc, xt_value *argv) {
186
+ if (argc < 2) return xt_undefined();
187
+ const char *from = xt_string_data(xt_to_string(argv[0]));
188
+ const char *to = xt_string_data(xt_to_string(argv[1]));
189
+ if (!from || !to) return xt_undefined();
190
+ if (rename(from, to) != 0) xt_fs_error("rename", from);
191
+ return xt_undefined();
192
+ }
193
+
194
+ xt_value xt_node_copy_file(int32_t argc, xt_value *argv) {
195
+ if (argc < 2) return xt_undefined();
196
+ const char *from = xt_string_data(xt_to_string(argv[0]));
197
+ const char *to = xt_string_data(xt_to_string(argv[1]));
198
+ if (!from || !to) return xt_undefined();
199
+
200
+ FILE *source = fopen(from, "rb");
201
+ if (!source) {
202
+ xt_fs_error("open", from);
203
+ return xt_undefined();
204
+ }
205
+ FILE *destination = fopen(to, "wb");
206
+ if (!destination) {
207
+ fclose(source);
208
+ xt_fs_error("open", to);
209
+ return xt_undefined();
210
+ }
211
+
212
+ char buffer[8192];
213
+ size_t read;
214
+ while ((read = fread(buffer, 1, sizeof(buffer), source)) > 0) fwrite(buffer, 1, read, destination);
215
+ fclose(source);
216
+ fclose(destination);
217
+ return xt_undefined();
218
+ }
219
+
220
+ xt_value xt_node_realpath(int32_t argc, xt_value *argv) {
221
+ if (argc < 1) return xt_undefined();
222
+ const char *path = xt_string_data(xt_to_string(argv[0]));
223
+ if (!path) return xt_undefined();
224
+ #if defined(_WIN32)
225
+ char resolved[_MAX_PATH];
226
+ if (_fullpath(resolved, path, _MAX_PATH) == NULL) {
227
+ xt_fs_error("resolve", path);
228
+ return xt_undefined();
229
+ }
230
+ return xt_string_from_cstr(resolved);
231
+ #else
232
+ char *resolved = realpath(path, NULL);
233
+ if (!resolved) {
234
+ xt_fs_error("resolve", path);
235
+ return xt_undefined();
236
+ }
237
+ xt_value result = xt_string_from_cstr(resolved);
238
+ free(resolved);
239
+ return result;
240
+ #endif
241
+ }
242
+
243
+ /* ------------------------------------------------------------------------- */
244
+ /* statSync / lstatSync */
245
+ /* ------------------------------------------------------------------------- */
246
+
247
+ /*
248
+ * Stats methods (`isFile`, `isDirectory`, ...) are native closures capturing
249
+ * the raw mode; the closure environment carries `[mode, methodName]`.
250
+ */
251
+ static xt_value xt_fs_stat_method(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
252
+ (void)thisValue;
253
+ (void)argc;
254
+ (void)argv;
255
+ unsigned int mode = (unsigned int)xt_to_number(xt_closure_env(env, 0));
256
+ const char *method = xt_string_data(xt_closure_env(env, 1));
257
+ if (!method) return xt_bool(0);
258
+ int result = 0;
259
+ if (strcmp(method, "isFile") == 0) result = S_ISREG(mode);
260
+ else if (strcmp(method, "isDirectory") == 0) result = S_ISDIR(mode);
261
+ else if (strcmp(method, "isSymbolicLink") == 0) result = S_ISLNK(mode);
262
+ else if (strcmp(method, "isFIFO") == 0) result = S_ISFIFO(mode);
263
+ else if (strcmp(method, "isSocket") == 0) result = S_ISSOCK(mode);
264
+ else if (strcmp(method, "isBlockDevice") == 0) result = S_ISBLK(mode);
265
+ else if (strcmp(method, "isCharacterDevice") == 0) result = S_ISCHR(mode);
266
+ return xt_bool(result);
267
+ }
268
+
269
+ static void xt_fs_define_stat_method(xt_value object, const char *name, unsigned int mode) {
270
+ xt_value env[2];
271
+ env[0] = xt_number((double)mode);
272
+ env[1] = xt_string_from_cstr(name);
273
+ xt_value fn = xt_closure_new((void *)xt_fs_stat_method, 2, env);
274
+ xt_object_set(object, xt_string_from_cstr(name), fn);
275
+ }
276
+
277
+ static double xt_fs_seconds_ms(long seconds, long nanoseconds) {
278
+ return (double)seconds * 1000.0 + (double)nanoseconds / 1000000.0;
279
+ }
280
+
281
+ static xt_value xt_node_stat_result(const struct stat *info) {
282
+ xt_value object = xt_object_new();
283
+ xt_object_set(object, xt_string_from_cstr("size"), xt_number((double)info->st_size));
284
+ xt_object_set(object, xt_string_from_cstr("mode"), xt_number((double)info->st_mode));
285
+ xt_object_set(object, xt_string_from_cstr("uid"), xt_number((double)info->st_uid));
286
+ xt_object_set(object, xt_string_from_cstr("gid"), xt_number((double)info->st_gid));
287
+ xt_object_set(object, xt_string_from_cstr("dev"), xt_number((double)info->st_dev));
288
+ xt_object_set(object, xt_string_from_cstr("ino"), xt_number((double)info->st_ino));
289
+ xt_object_set(object, xt_string_from_cstr("nlink"), xt_number((double)info->st_nlink));
290
+ xt_object_set(object, xt_string_from_cstr("rdev"), xt_number((double)info->st_rdev));
291
+ #if defined(__APPLE__)
292
+ xt_object_set(object, xt_string_from_cstr("blocks"), xt_number((double)info->st_blocks));
293
+ xt_object_set(object, xt_string_from_cstr("blksize"), xt_number((double)info->st_blksize));
294
+ xt_object_set(object, xt_string_from_cstr("mtimeMs"),
295
+ xt_number(xt_fs_seconds_ms(info->st_mtimespec.tv_sec, info->st_mtimespec.tv_nsec)));
296
+ xt_object_set(object, xt_string_from_cstr("atimeMs"),
297
+ xt_number(xt_fs_seconds_ms(info->st_atimespec.tv_sec, info->st_atimespec.tv_nsec)));
298
+ xt_object_set(object, xt_string_from_cstr("ctimeMs"),
299
+ xt_number(xt_fs_seconds_ms(info->st_ctimespec.tv_sec, info->st_ctimespec.tv_nsec)));
300
+ #elif defined(_WIN32)
301
+ xt_object_set(object, xt_string_from_cstr("mtimeMs"), xt_number((double)info->st_mtime * 1000.0));
302
+ xt_object_set(object, xt_string_from_cstr("atimeMs"), xt_number((double)info->st_atime * 1000.0));
303
+ xt_object_set(object, xt_string_from_cstr("ctimeMs"), xt_number((double)info->st_ctime * 1000.0));
304
+ #else
305
+ xt_object_set(object, xt_string_from_cstr("blocks"), xt_number((double)info->st_blocks));
306
+ xt_object_set(object, xt_string_from_cstr("blksize"), xt_number((double)info->st_blksize));
307
+ xt_object_set(object, xt_string_from_cstr("mtimeMs"),
308
+ xt_number(xt_fs_seconds_ms(info->st_mtim.tv_sec, info->st_mtim.tv_nsec)));
309
+ xt_object_set(object, xt_string_from_cstr("atimeMs"),
310
+ xt_number(xt_fs_seconds_ms(info->st_atim.tv_sec, info->st_atim.tv_nsec)));
311
+ xt_object_set(object, xt_string_from_cstr("ctimeMs"),
312
+ xt_number(xt_fs_seconds_ms(info->st_ctim.tv_sec, info->st_ctim.tv_nsec)));
313
+ #endif
314
+
315
+ unsigned int mode = (unsigned int)info->st_mode;
316
+ xt_fs_define_stat_method(object, "isFile", mode);
317
+ xt_fs_define_stat_method(object, "isDirectory", mode);
318
+ xt_fs_define_stat_method(object, "isSymbolicLink", mode);
319
+ xt_fs_define_stat_method(object, "isFIFO", mode);
320
+ xt_fs_define_stat_method(object, "isSocket", mode);
321
+ xt_fs_define_stat_method(object, "isBlockDevice", mode);
322
+ xt_fs_define_stat_method(object, "isCharacterDevice", mode);
323
+ return object;
324
+ }
325
+
326
+ static xt_value xt_node_stat_common(int32_t argc, xt_value *argv, int followLinks) {
327
+ if (argc < 1) return xt_undefined();
328
+ const char *path = xt_string_data(xt_to_string(argv[0]));
329
+ if (!path) return xt_undefined();
330
+
331
+ struct stat info;
332
+ int status = followLinks ? stat(path, &info) : lstat(path, &info);
333
+ if (status != 0) {
334
+ xt_fs_error("stat", path);
335
+ return xt_undefined();
336
+ }
337
+ return xt_node_stat_result(&info);
338
+ }
339
+
340
+ xt_value xt_node_stat(int32_t argc, xt_value *argv) { return xt_node_stat_common(argc, argv, 1); }
341
+
342
+ xt_value xt_node_lstat(int32_t argc, xt_value *argv) { return xt_node_stat_common(argc, argv, 0); }
@@ -1,15 +1,19 @@
1
1
  /*
2
- * Node.js `fs` module for xbintsc.
2
+ * Node.js `fs.readFileSync` for xbintsc.
3
3
  *
4
4
  * Compiled and linked only when the `node` extension is registered. Following
5
- * the runtime calling convention, every builtin is:
5
+ * the runtime calling convention, the builtin is:
6
6
  * xt_value fn(int32_t argc, xt_value *argv)
7
+ *
8
+ * `readFileSync(path[, options])` reads a file and returns its contents as a
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.
7
13
  */
8
14
 
9
15
  #include "rt.h"
10
-
11
- #include <stdio.h>
12
- #include <stdlib.h>
16
+ #include "fs_common.h"
13
17
 
14
18
  xt_value xt_node_read_text_file(int32_t argc, xt_value *argv) {
15
19
  if (argc < 1) return xt_undefined();
@@ -37,7 +41,9 @@ xt_value xt_node_read_text_file(int32_t argc, xt_value *argv) {
37
41
  size_t read = fread(buffer, 1, (size_t)size, file);
38
42
  fclose(file);
39
43
  buffer[read] = '\0';
40
- xt_value result = xt_string_new(buffer, read);
44
+
45
+ const char *encoding = argc > 1 ? xt_fs_encoding(argv[1]) : NULL;
46
+ xt_value result = xt_fs_encode_bytes((const unsigned char *)buffer, read, encoding);
41
47
  free(buffer);
42
48
  return result;
43
49
  }
@@ -0,0 +1,47 @@
1
+ /*
2
+ * Node.js `fs.writeFileSync` / `fs.appendFileSync` for xbintsc.
3
+ *
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.
8
+ */
9
+
10
+ #include "rt.h"
11
+ #include "fs_common.h"
12
+
13
+ static xt_value xt_node_write_common(int32_t argc, xt_value *argv, const char *mode) {
14
+ if (argc < 2) return xt_undefined();
15
+ xt_value pathValue = xt_to_string(argv[0]);
16
+ const char *path = xt_string_data(pathValue);
17
+ if (!path) return xt_undefined();
18
+
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
+ size_t byteLength = 0;
26
+ unsigned char *bytes = xt_fs_decode_text(text, (size_t)length, encoding, &byteLength);
27
+ if (!bytes) return xt_undefined();
28
+
29
+ FILE *file = fopen(path, mode);
30
+ if (!file) {
31
+ fprintf(stderr, "xbintsc: cannot open '%s'\n", path);
32
+ free(bytes);
33
+ return xt_undefined();
34
+ }
35
+ if (byteLength > 0) fwrite(bytes, 1, byteLength, file);
36
+ fclose(file);
37
+ free(bytes);
38
+ return xt_undefined();
39
+ }
40
+
41
+ xt_value xt_node_write_file(int32_t argc, xt_value *argv) {
42
+ return xt_node_write_common(argc, argv, "wb");
43
+ }
44
+
45
+ xt_value xt_node_append_file(int32_t argc, xt_value *argv) {
46
+ return xt_node_write_common(argc, argv, "ab");
47
+ }
@@ -0,0 +1,165 @@
1
+ /*
2
+ * Node.js `os` module for xbintsc.
3
+ *
4
+ * Exposed through namespace dispatch: `os.<name>()` lowers to
5
+ * `xt_os_static(<name>, argc, argv)`. Implemented: platform, arch, type,
6
+ * release, endianness, homedir, tmpdir, hostname, totalmem, freemem.
7
+ */
8
+
9
+ #include "rt.h"
10
+
11
+ #include <stdio.h>
12
+ #include <stdlib.h>
13
+ #include <string.h>
14
+
15
+ #if defined(_WIN32)
16
+ #include <windows.h>
17
+ #else
18
+ #include <sys/utsname.h>
19
+ #include <unistd.h>
20
+ #endif
21
+
22
+ static const char *xt_os_platform(void) {
23
+ #if defined(_WIN32)
24
+ return "win32";
25
+ #elif defined(__APPLE__)
26
+ return "darwin";
27
+ #elif defined(__linux__)
28
+ return "linux";
29
+ #elif defined(__FreeBSD__)
30
+ return "freebsd";
31
+ #else
32
+ return "unknown";
33
+ #endif
34
+ }
35
+
36
+ static const char *xt_os_type(void) {
37
+ #if defined(_WIN32)
38
+ return "Windows_NT";
39
+ #elif defined(__APPLE__)
40
+ return "Darwin";
41
+ #elif defined(__linux__)
42
+ return "Linux";
43
+ #elif defined(__FreeBSD__)
44
+ return "FreeBSD";
45
+ #else
46
+ return "Unknown";
47
+ #endif
48
+ }
49
+
50
+ static const char *xt_os_arch(void) {
51
+ #if defined(__x86_64__) || defined(_M_X64)
52
+ return "x64";
53
+ #elif defined(__aarch64__) || defined(_M_ARM64)
54
+ return "arm64";
55
+ #elif defined(__i386__) || defined(_M_IX86)
56
+ return "ia32";
57
+ #elif defined(__arm__)
58
+ return "arm";
59
+ #else
60
+ return "unknown";
61
+ #endif
62
+ }
63
+
64
+ static xt_value xt_os_string(const char *value) {
65
+ return value ? xt_string_from_cstr(value) : xt_undefined();
66
+ }
67
+
68
+ static xt_value xt_os_totalmem(void) {
69
+ #if defined(_WIN32)
70
+ MEMORYSTATUSEX status;
71
+ status.dwLength = sizeof(status);
72
+ if (GlobalMemoryStatusEx(&status)) return xt_number((double)status.ullTotalPhys);
73
+ return xt_number(0);
74
+ #else
75
+ long pages = sysconf(_SC_PHYS_PAGES);
76
+ long pageSize = sysconf(_SC_PAGESIZE);
77
+ if (pages <= 0 || pageSize <= 0) return xt_number(0);
78
+ return xt_number((double)pages * (double)pageSize);
79
+ #endif
80
+ }
81
+
82
+ static xt_value xt_os_freemem(void) {
83
+ #if defined(_WIN32)
84
+ MEMORYSTATUSEX status;
85
+ status.dwLength = sizeof(status);
86
+ if (GlobalMemoryStatusEx(&status)) return xt_number((double)status.ullAvailPhys);
87
+ return xt_number(0);
88
+ #elif defined(_SC_AVPHYS_PAGES)
89
+ long pages = sysconf(_SC_AVPHYS_PAGES);
90
+ long pageSize = sysconf(_SC_PAGESIZE);
91
+ if (pages <= 0 || pageSize <= 0) return xt_number(0);
92
+ return xt_number((double)pages * (double)pageSize);
93
+ #else
94
+ /* macOS exposes free memory through Mach APIs only; report total memory. */
95
+ return xt_os_totalmem();
96
+ #endif
97
+ }
98
+
99
+ xt_value xt_os_static(xt_value name, int32_t argc, xt_value *argv) {
100
+ const char *method = xt_string_data(name);
101
+ if (!method) return xt_undefined();
102
+ (void)argc;
103
+ (void)argv;
104
+
105
+ if (strcmp(method, "platform") == 0) return xt_string_from_cstr(xt_os_platform());
106
+ if (strcmp(method, "type") == 0) return xt_string_from_cstr(xt_os_type());
107
+ if (strcmp(method, "arch") == 0) return xt_string_from_cstr(xt_os_arch());
108
+ if (strcmp(method, "endianness") == 0) {
109
+ #if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
110
+ return xt_string_from_cstr("BE");
111
+ #else
112
+ return xt_string_from_cstr("LE");
113
+ #endif
114
+ }
115
+ if (strcmp(method, "homedir") == 0) {
116
+ #if defined(_WIN32)
117
+ return xt_os_string(getenv("USERPROFILE"));
118
+ #else
119
+ return xt_os_string(getenv("HOME"));
120
+ #endif
121
+ }
122
+ if (strcmp(method, "tmpdir") == 0) {
123
+ #if defined(_WIN32)
124
+ const char *temp = getenv("TEMP");
125
+ return xt_os_string(temp ? temp : "C:\\Windows\\Temp");
126
+ #else
127
+ const char *temp = getenv("TMPDIR");
128
+ return xt_os_string(temp ? temp : "/tmp");
129
+ #endif
130
+ }
131
+ if (strcmp(method, "hostname") == 0) {
132
+ char buffer[256];
133
+ if (gethostname(buffer, sizeof(buffer)) != 0) return xt_undefined();
134
+ buffer[sizeof(buffer) - 1] = '\0';
135
+ return xt_string_from_cstr(buffer);
136
+ }
137
+ if (strcmp(method, "release") == 0) {
138
+ #if defined(_WIN32)
139
+ return xt_string_from_cstr("0.0.0");
140
+ #else
141
+ struct utsname info;
142
+ if (uname(&info) != 0) return xt_undefined();
143
+ return xt_string_from_cstr(info.release);
144
+ #endif
145
+ }
146
+ if (strcmp(method, "totalmem") == 0) return xt_os_totalmem();
147
+ if (strcmp(method, "freemem") == 0) return xt_os_freemem();
148
+ if (strcmp(method, "cpus") == 0) {
149
+ /* No per-core details yet; report the logical processor count as length. */
150
+ long processors = sysconf(_SC_NPROCESSORS_ONLN);
151
+ if (processors <= 0) processors = 1;
152
+ xt_value *items = (xt_value *)malloc(sizeof(xt_value) * (size_t)processors);
153
+ if (!items) return xt_array_new(0, NULL);
154
+ for (long i = 0; i < processors; i++) {
155
+ xt_value info = xt_object_new();
156
+ xt_object_set(info, xt_string_from_cstr("model"), xt_string_from_cstr("unknown"));
157
+ xt_object_set(info, xt_string_from_cstr("speed"), xt_number(0));
158
+ items[i] = info;
159
+ }
160
+ xt_value result = xt_array_new((int32_t)processors, items);
161
+ free(items);
162
+ return result;
163
+ }
164
+ return xt_undefined();
165
+ }