xbintsc 0.1.9 → 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 (57) hide show
  1. package/README.md +12 -7
  2. package/dist/src/codegen/generator/calls.js +11 -1
  3. package/dist/src/codegen/generator/calls.js.map +1 -1
  4. package/dist/src/codegen/generator/module.js +1 -0
  5. package/dist/src/codegen/generator/module.js.map +1 -1
  6. package/dist/src/codegen/generator/state.js +5 -0
  7. package/dist/src/codegen/generator/state.js.map +1 -1
  8. package/dist/src/codegen/generator/tables.d.ts +2 -0
  9. package/dist/src/codegen/generator/tables.js +7 -0
  10. package/dist/src/codegen/generator/tables.js.map +1 -1
  11. package/dist/src/extensions/node/fs/fs-ops.d.ts +6 -0
  12. package/dist/src/extensions/node/fs/fs-ops.js +18 -0
  13. package/dist/src/extensions/node/fs/fs-ops.js.map +1 -0
  14. package/dist/src/extensions/node/fs/index.d.ts +3 -1
  15. package/dist/src/extensions/node/fs/index.js +11 -3
  16. package/dist/src/extensions/node/fs/index.js.map +1 -1
  17. package/dist/src/extensions/node/fs/write-file.d.ts +5 -0
  18. package/dist/src/extensions/node/fs/write-file.js +8 -0
  19. package/dist/src/extensions/node/fs/write-file.js.map +1 -0
  20. package/dist/src/extensions/node/index.d.ts +2 -1
  21. package/dist/src/extensions/node/index.js +7 -3
  22. package/dist/src/extensions/node/index.js.map +1 -1
  23. package/dist/src/extensions/node/os/index.d.ts +9 -0
  24. package/dist/src/extensions/node/os/index.js +14 -0
  25. package/dist/src/extensions/node/os/index.js.map +1 -0
  26. package/dist/src/extensions/node/path/index.d.ts +10 -0
  27. package/dist/src/extensions/node/path/index.js +15 -0
  28. package/dist/src/extensions/node/path/index.js.map +1 -0
  29. package/dist/src/extensions/node/process/index.d.ts +10 -0
  30. package/dist/src/extensions/node/process/index.js +15 -0
  31. package/dist/src/extensions/node/process/index.js.map +1 -0
  32. package/dist/tests/e2e/compile.test.js +55 -0
  33. package/dist/tests/e2e/compile.test.js.map +1 -1
  34. package/dist/tests/extensions/registry.test.js +9 -3
  35. package/dist/tests/extensions/registry.test.js.map +1 -1
  36. package/package.json +1 -1
  37. package/runtime/ext_node/fs/fs_common.h +166 -0
  38. package/runtime/ext_node/fs/fs_ops.c +342 -0
  39. package/runtime/ext_node/fs/read_file.c +12 -6
  40. package/runtime/ext_node/fs/write_file.c +47 -0
  41. package/runtime/ext_node/os/os.c +165 -0
  42. package/runtime/ext_node/path/path.c +324 -0
  43. package/runtime/ext_node/process/process.c +137 -0
  44. package/runtime/rt.h +6 -0
  45. package/runtime/xt_io.c +9 -0
  46. package/scripts/build-runtime.mjs +5 -0
  47. package/src/codegen/generator/calls.ts +13 -0
  48. package/src/codegen/generator/module.ts +1 -0
  49. package/src/codegen/generator/state.ts +5 -0
  50. package/src/codegen/generator/tables.ts +8 -0
  51. package/src/extensions/node/fs/fs-ops.ts +20 -0
  52. package/src/extensions/node/fs/index.ts +11 -3
  53. package/src/extensions/node/fs/write-file.ts +10 -0
  54. package/src/extensions/node/index.ts +7 -3
  55. package/src/extensions/node/os/index.ts +16 -0
  56. package/src/extensions/node/path/index.ts +17 -0
  57. package/src/extensions/node/process/index.ts +17 -0
@@ -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
+ }
@@ -0,0 +1,324 @@
1
+ /*
2
+ * Node.js `path` module for xbintsc.
3
+ *
4
+ * Exposed through namespace dispatch: the compiler lowers `path.<name>(...)`
5
+ * to `xt_path_static(<name>, argc, argv)`. Paths are handled with POSIX
6
+ * semantics (`/` separator), matching Node's `path.posix` on every platform.
7
+ *
8
+ * Implemented: join, resolve, normalize, dirname, basename, extname,
9
+ * isAbsolute, relative.
10
+ */
11
+
12
+ #include "rt.h"
13
+
14
+ #include <stdio.h>
15
+ #include <stdlib.h>
16
+ #include <string.h>
17
+ #include <unistd.h>
18
+
19
+ #define XT_PATH_MAX 4096
20
+
21
+ static int xt_path_is_absolute(const char *path) { return path && path[0] == '/'; }
22
+
23
+ static void xt_path_normalize(const char *input, char *out, size_t size) {
24
+ int absolute = input[0] == '/';
25
+ size_t length = strlen(input);
26
+ char *copy = (char *)malloc(length + 1);
27
+ if (!copy) {
28
+ out[0] = '\0';
29
+ return;
30
+ }
31
+ memcpy(copy, input, length + 1);
32
+
33
+ size_t capacity = 8;
34
+ size_t count = 0;
35
+ char **segments = (char **)malloc(sizeof(char *) * capacity);
36
+ if (!segments) {
37
+ free(copy);
38
+ out[0] = '\0';
39
+ return;
40
+ }
41
+
42
+ size_t i = 0;
43
+ while (i < length) {
44
+ while (i < length && copy[i] == '/') i++;
45
+ if (i >= length) break;
46
+ size_t start = i;
47
+ while (i < length && copy[i] != '/') i++;
48
+ if (i < length) {
49
+ copy[i] = '\0';
50
+ i++;
51
+ } else {
52
+ copy[i] = '\0';
53
+ }
54
+ char *segment = copy + start;
55
+ if (strcmp(segment, ".") == 0) continue;
56
+ if (strcmp(segment, "..") == 0) {
57
+ if (count > 0 && strcmp(segments[count - 1], "..") != 0) {
58
+ count--;
59
+ } else if (!absolute) {
60
+ if (count == capacity) {
61
+ capacity *= 2;
62
+ segments = (char **)realloc(segments, sizeof(char *) * capacity);
63
+ }
64
+ segments[count++] = segment;
65
+ }
66
+ continue;
67
+ }
68
+ if (count == capacity) {
69
+ capacity *= 2;
70
+ segments = (char **)realloc(segments, sizeof(char *) * capacity);
71
+ }
72
+ segments[count++] = segment;
73
+ }
74
+
75
+ size_t position = 0;
76
+ if (absolute && position < size) out[position++] = '/';
77
+ for (size_t s = 0; s < count; s++) {
78
+ size_t segmentLength = strlen(segments[s]);
79
+ if (position + segmentLength + 2 > size) break;
80
+ if (s > 0) out[position++] = '/';
81
+ memcpy(out + position, segments[s], segmentLength);
82
+ position += segmentLength;
83
+ }
84
+ out[position] = '\0';
85
+ if (position == 0) {
86
+ out[0] = '.';
87
+ out[1] = '\0';
88
+ }
89
+ free(segments);
90
+ free(copy);
91
+ }
92
+
93
+ static void xt_path_absolute(const char *path, char *out, size_t size) {
94
+ if (xt_path_is_absolute(path)) {
95
+ snprintf(out, size, "%s", path);
96
+ } else {
97
+ char cwd[XT_PATH_MAX];
98
+ if (!getcwd(cwd, sizeof(cwd))) cwd[0] = '\0';
99
+ snprintf(out, size, "%s/%s", cwd, path);
100
+ }
101
+ char normalized[XT_PATH_MAX];
102
+ xt_path_normalize(out, normalized, sizeof(normalized));
103
+ snprintf(out, size, "%s", normalized);
104
+ }
105
+
106
+ typedef struct {
107
+ char *storage;
108
+ char **segments;
109
+ size_t count;
110
+ } xt_path_parts;
111
+
112
+ static void xt_path_split(const char *path, xt_path_parts *parts) {
113
+ size_t length = strlen(path);
114
+ parts->storage = (char *)malloc(length + 1);
115
+ parts->count = 0;
116
+ parts->segments = NULL;
117
+ if (!parts->storage) return;
118
+ memcpy(parts->storage, path, length + 1);
119
+
120
+ size_t capacity = 8;
121
+ parts->segments = (char **)malloc(sizeof(char *) * capacity);
122
+ if (!parts->segments) return;
123
+
124
+ size_t i = 0;
125
+ while (i < length) {
126
+ while (i < length && parts->storage[i] == '/') i++;
127
+ if (i >= length) break;
128
+ size_t start = i;
129
+ while (i < length && parts->storage[i] != '/') i++;
130
+ if (i < length) {
131
+ parts->storage[i] = '\0';
132
+ i++;
133
+ } else {
134
+ parts->storage[i] = '\0';
135
+ }
136
+ if (parts->count == capacity) {
137
+ capacity *= 2;
138
+ parts->segments = (char **)realloc(parts->segments, sizeof(char *) * capacity);
139
+ }
140
+ parts->segments[parts->count++] = parts->storage + start;
141
+ }
142
+ }
143
+
144
+ static void xt_path_parts_free(xt_path_parts *parts) {
145
+ free(parts->segments);
146
+ free(parts->storage);
147
+ }
148
+
149
+ /* ------------------------------------------------------------------------- */
150
+ /* Individual operations */
151
+ /* ------------------------------------------------------------------------- */
152
+
153
+ static xt_value xt_path_join(int32_t argc, xt_value *argv) {
154
+ size_t total = 1;
155
+ for (int32_t i = 0; i < argc; i++) {
156
+ const char *part = xt_string_data(xt_to_string(argv[i]));
157
+ if (part) total += strlen(part) + 1;
158
+ }
159
+ char *joined = (char *)malloc(total);
160
+ if (!joined) return xt_undefined();
161
+ joined[0] = '\0';
162
+ for (int32_t i = 0; i < argc; i++) {
163
+ const char *part = xt_string_data(xt_to_string(argv[i]));
164
+ if (!part || part[0] == '\0') continue;
165
+ if (joined[0] != '\0') strcat(joined, "/");
166
+ strcat(joined, part);
167
+ }
168
+ char normalized[XT_PATH_MAX];
169
+ xt_path_normalize(joined, normalized, sizeof(normalized));
170
+ free(joined);
171
+ return xt_string_from_cstr(normalized);
172
+ }
173
+
174
+ static xt_value xt_path_resolve(int32_t argc, xt_value *argv) {
175
+ char result[XT_PATH_MAX];
176
+ result[0] = '\0';
177
+ for (int32_t i = 0; i < argc; i++) {
178
+ const char *part = xt_string_data(xt_to_string(argv[i]));
179
+ if (!part || part[0] == '\0') continue;
180
+ if (xt_path_is_absolute(part)) {
181
+ snprintf(result, sizeof(result), "%s", part);
182
+ } else if (result[0] == '\0') {
183
+ snprintf(result, sizeof(result), "%s", part);
184
+ } else {
185
+ size_t used = strlen(result);
186
+ snprintf(result + used, sizeof(result) - used, "/%s", part);
187
+ }
188
+ }
189
+ if (result[0] == '\0') {
190
+ if (!getcwd(result, sizeof(result))) result[0] = '\0';
191
+ } else if (!xt_path_is_absolute(result)) {
192
+ char cwd[XT_PATH_MAX];
193
+ if (!getcwd(cwd, sizeof(cwd))) cwd[0] = '\0';
194
+ char combined[XT_PATH_MAX];
195
+ snprintf(combined, sizeof(combined), "%s/%s", cwd, result);
196
+ snprintf(result, sizeof(result), "%s", combined);
197
+ }
198
+ char normalized[XT_PATH_MAX];
199
+ xt_path_normalize(result, normalized, sizeof(normalized));
200
+ return xt_string_from_cstr(normalized);
201
+ }
202
+
203
+ static xt_value xt_path_dirname(const char *path) {
204
+ size_t length = strlen(path);
205
+ while (length > 1 && path[length - 1] == '/') length--;
206
+ size_t i = length;
207
+ while (i > 0 && path[i - 1] != '/') i--;
208
+ if (i == 0) return xt_string_from_cstr(".");
209
+ size_t end = i - 1;
210
+ if (end == 0) return xt_string_from_cstr("/");
211
+ while (end > 1 && path[end - 1] == '/') end--;
212
+ return xt_string_new(path, end);
213
+ }
214
+
215
+ static xt_value xt_path_basename(const char *path, const char *extension) {
216
+ size_t length = strlen(path);
217
+ while (length > 0 && path[length - 1] == '/') length--;
218
+ if (length == 0) return xt_string_from_cstr("");
219
+ size_t start = length;
220
+ while (start > 0 && path[start - 1] != '/') start--;
221
+ size_t end = length;
222
+ if (extension && extension[0] != '\0') {
223
+ size_t extensionLength = strlen(extension);
224
+ if (end - start > extensionLength &&
225
+ strncmp(path + end - extensionLength, extension, extensionLength) == 0) {
226
+ end -= extensionLength;
227
+ }
228
+ }
229
+ return xt_string_new(path + start, end - start);
230
+ }
231
+
232
+ static xt_value xt_path_extname(const char *path) {
233
+ size_t length = strlen(path);
234
+ size_t start = 0;
235
+ for (size_t i = 0; i < length; i++) {
236
+ if (path[i] == '/') start = i + 1;
237
+ }
238
+ size_t dot = length;
239
+ for (size_t i = start; i < length; i++) {
240
+ if (path[i] == '.') dot = i;
241
+ }
242
+ if (dot == length || dot == start) return xt_string_from_cstr("");
243
+ return xt_string_new(path + dot, length - dot);
244
+ }
245
+
246
+ static xt_value xt_path_relative(const char *from, const char *to) {
247
+ char fromAbsolute[XT_PATH_MAX];
248
+ char toAbsolute[XT_PATH_MAX];
249
+ xt_path_absolute(from, fromAbsolute, sizeof(fromAbsolute));
250
+ xt_path_absolute(to, toAbsolute, sizeof(toAbsolute));
251
+
252
+ xt_path_parts fromParts;
253
+ xt_path_parts toParts;
254
+ xt_path_split(fromAbsolute, &fromParts);
255
+ xt_path_split(toAbsolute, &toParts);
256
+
257
+ size_t common = 0;
258
+ while (common < fromParts.count && common < toParts.count &&
259
+ strcmp(fromParts.segments[common], toParts.segments[common]) == 0) {
260
+ common++;
261
+ }
262
+
263
+ char result[XT_PATH_MAX];
264
+ result[0] = '\0';
265
+ size_t position = 0;
266
+ for (size_t i = common; i < fromParts.count && position + 3 < sizeof(result); i++) {
267
+ if (position > 0) result[position++] = '/';
268
+ result[position++] = '.';
269
+ result[position++] = '.';
270
+ }
271
+ for (size_t i = common; i < toParts.count; i++) {
272
+ size_t segmentLength = strlen(toParts.segments[i]);
273
+ if (position + segmentLength + 2 > sizeof(result)) break;
274
+ if (position > 0) result[position++] = '/';
275
+ memcpy(result + position, toParts.segments[i], segmentLength);
276
+ position += segmentLength;
277
+ }
278
+ result[position] = '\0';
279
+
280
+ xt_path_parts_free(&fromParts);
281
+ xt_path_parts_free(&toParts);
282
+ return xt_string_from_cstr(result);
283
+ }
284
+
285
+ /* ------------------------------------------------------------------------- */
286
+ /* Dispatch */
287
+ /* ------------------------------------------------------------------------- */
288
+
289
+ xt_value xt_path_static(xt_value name, int32_t argc, xt_value *argv) {
290
+ const char *method = xt_string_data(name);
291
+ if (!method) return xt_undefined();
292
+
293
+ if (strcmp(method, "join") == 0) return xt_path_join(argc, argv);
294
+ if (strcmp(method, "resolve") == 0) return xt_path_resolve(argc, argv);
295
+ if (strcmp(method, "isAbsolute") == 0) {
296
+ const char *path = argc > 0 ? xt_string_data(xt_to_string(argv[0])) : NULL;
297
+ return xt_bool(xt_path_is_absolute(path));
298
+ }
299
+ if (strcmp(method, "normalize") == 0) {
300
+ const char *path = argc > 0 ? xt_string_data(xt_to_string(argv[0])) : "";
301
+ char normalized[XT_PATH_MAX];
302
+ xt_path_normalize(path ? path : "", normalized, sizeof(normalized));
303
+ return xt_string_from_cstr(normalized);
304
+ }
305
+ if (strcmp(method, "dirname") == 0) {
306
+ const char *path = argc > 0 ? xt_string_data(xt_to_string(argv[0])) : "";
307
+ return xt_path_dirname(path ? path : "");
308
+ }
309
+ if (strcmp(method, "basename") == 0) {
310
+ const char *path = argc > 0 ? xt_string_data(xt_to_string(argv[0])) : "";
311
+ const char *extension = argc > 1 ? xt_string_data(xt_to_string(argv[1])) : NULL;
312
+ return xt_path_basename(path ? path : "", extension);
313
+ }
314
+ if (strcmp(method, "extname") == 0) {
315
+ const char *path = argc > 0 ? xt_string_data(xt_to_string(argv[0])) : "";
316
+ return xt_path_extname(path ? path : "");
317
+ }
318
+ if (strcmp(method, "relative") == 0) {
319
+ const char *from = argc > 0 ? xt_string_data(xt_to_string(argv[0])) : "";
320
+ const char *to = argc > 1 ? xt_string_data(xt_to_string(argv[1])) : "";
321
+ return xt_path_relative(from ? from : "", to ? to : "");
322
+ }
323
+ return xt_undefined();
324
+ }
@@ -0,0 +1,137 @@
1
+ /*
2
+ * Node.js `process` object for xbintsc.
3
+ *
4
+ * The compiler lowers `process.<method>(...)` to
5
+ * `xt_process_call(<method>, argc, argv)` and `process.<property>` to
6
+ * `xt_process_get(<property>)`. Provided: cwd, exit, uptime, hrtime and the
7
+ * platform/arch/pid/argv/env properties.
8
+ */
9
+
10
+ #include "rt.h"
11
+
12
+ #include <stdio.h>
13
+ #include <stdlib.h>
14
+ #include <string.h>
15
+ #include <time.h>
16
+
17
+ #if defined(_WIN32)
18
+ extern char **_environ;
19
+ #define XT_ENVIRON _environ
20
+ #include <process.h>
21
+ #define xt_getpid _getpid
22
+ #else
23
+ extern char **environ;
24
+ #define XT_ENVIRON environ
25
+ #include <unistd.h>
26
+ #define xt_getpid getpid
27
+ #endif
28
+
29
+ static const char *xt_process_platform(void) {
30
+ #if defined(_WIN32)
31
+ return "win32";
32
+ #elif defined(__APPLE__)
33
+ return "darwin";
34
+ #elif defined(__linux__)
35
+ return "linux";
36
+ #else
37
+ return "unknown";
38
+ #endif
39
+ }
40
+
41
+ static const char *xt_process_arch(void) {
42
+ #if defined(__x86_64__) || defined(_M_X64)
43
+ return "x64";
44
+ #elif defined(__aarch64__) || defined(_M_ARM64)
45
+ return "arm64";
46
+ #elif defined(__i386__) || defined(_M_IX86)
47
+ return "ia32";
48
+ #elif defined(__arm__)
49
+ return "arm";
50
+ #else
51
+ return "unknown";
52
+ #endif
53
+ }
54
+
55
+ static xt_value xt_process_env(void) {
56
+ xt_value object = xt_object_new();
57
+ if (!XT_ENVIRON) return object;
58
+ for (char **entry = XT_ENVIRON; *entry; entry++) {
59
+ const char *equals = strchr(*entry, '=');
60
+ if (!equals) continue;
61
+ xt_value key = xt_string_new(*entry, (size_t)(equals - *entry));
62
+ xt_object_set(object, key, xt_string_from_cstr(equals + 1));
63
+ }
64
+ return object;
65
+ }
66
+
67
+ static xt_value xt_process_argv(void) {
68
+ int32_t count = xt_program_argc;
69
+ if (count <= 0 || !xt_program_argv) return xt_array_new(0, NULL);
70
+ xt_value *items = (xt_value *)malloc(sizeof(xt_value) * (size_t)count);
71
+ if (!items) return xt_array_new(0, NULL);
72
+ for (int32_t i = 0; i < count; i++) items[i] = xt_string_from_cstr(xt_program_argv[i]);
73
+ xt_value result = xt_array_new(count, items);
74
+ free(items);
75
+ return result;
76
+ }
77
+
78
+ xt_value xt_process_call(xt_value name, int32_t argc, xt_value *argv) {
79
+ const char *method = xt_string_data(name);
80
+ if (!method) return xt_undefined();
81
+
82
+ if (strcmp(method, "cwd") == 0) {
83
+ char buffer[4096];
84
+ if (!getcwd(buffer, sizeof(buffer))) return xt_undefined();
85
+ return xt_string_from_cstr(buffer);
86
+ }
87
+ if (strcmp(method, "exit") == 0) {
88
+ int code = argc > 0 ? (int)xt_to_number(argv[0]) : 0;
89
+ exit(code);
90
+ return xt_undefined();
91
+ }
92
+ if (strcmp(method, "uptime") == 0) {
93
+ return xt_number((double)clock() / (double)CLOCKS_PER_SEC);
94
+ }
95
+ if (strcmp(method, "hrtime") == 0) {
96
+ struct timespec now;
97
+ #if defined(CLOCK_MONOTONIC)
98
+ clock_gettime(CLOCK_MONOTONIC, &now);
99
+ #else
100
+ now.tv_sec = (long)time(NULL);
101
+ now.tv_nsec = 0;
102
+ #endif
103
+ xt_value items[2];
104
+ items[0] = xt_number((double)now.tv_sec);
105
+ items[1] = xt_number((double)now.tv_nsec);
106
+ return xt_array_new(2, items);
107
+ }
108
+ if (strcmp(method, "getuid") == 0) {
109
+ #if defined(_WIN32)
110
+ return xt_number(0);
111
+ #else
112
+ return xt_number((double)getuid());
113
+ #endif
114
+ }
115
+ return xt_undefined();
116
+ }
117
+
118
+ xt_value xt_process_get(xt_value name) {
119
+ const char *key = xt_string_data(name);
120
+ if (!key) return xt_undefined();
121
+
122
+ if (strcmp(key, "platform") == 0) return xt_string_from_cstr(xt_process_platform());
123
+ if (strcmp(key, "arch") == 0) return xt_string_from_cstr(xt_process_arch());
124
+ if (strcmp(key, "pid") == 0) return xt_number((double)xt_getpid());
125
+ if (strcmp(key, "ppid") == 0) {
126
+ #if defined(_WIN32)
127
+ return xt_number(0);
128
+ #else
129
+ return xt_number((double)getppid());
130
+ #endif
131
+ }
132
+ if (strcmp(key, "version") == 0) return xt_string_from_cstr("v0.0.0");
133
+ if (strcmp(key, "argv") == 0) return xt_process_argv();
134
+ if (strcmp(key, "env") == 0) return xt_process_env();
135
+ if (strcmp(key, "title") == 0) return xt_string_from_cstr("xbintsc");
136
+ return xt_undefined();
137
+ }
package/runtime/rt.h CHANGED
@@ -207,6 +207,12 @@ xt_value xt_delete(xt_value obj, xt_value key);
207
207
  /** Collect `argv[start..argc)` into a new array (rest parameters). */
208
208
  xt_value xt_rest_args(int32_t argc, xt_value *argv, int32_t start);
209
209
 
210
+ /* -- program arguments ---------------------------------------------------- */
211
+ /* Captured from `main` so host extensions can expose `process.argv`. */
212
+ void xt_set_program_args(int32_t argc, char **argv);
213
+ extern int32_t xt_program_argc;
214
+ extern char **xt_program_argv;
215
+
210
216
  /* -- arrays --------------------------------------------------------------- */
211
217
  xt_value xt_array_new(int32_t count, xt_value *items);
212
218
  xt_value xt_array_get(xt_value arr, xt_value index);
package/runtime/xt_io.c CHANGED
@@ -32,6 +32,15 @@ typedef struct xt_try_frame {
32
32
 
33
33
  static xt_try_frame *g_try_top = NULL;
34
34
 
35
+ /* Program arguments, captured once by `xt_set_program_args` from `main`. */
36
+ int32_t xt_program_argc = 0;
37
+ char **xt_program_argv = NULL;
38
+
39
+ void xt_set_program_args(int32_t argc, char **argv) {
40
+ xt_program_argc = argc;
41
+ xt_program_argv = argv;
42
+ }
43
+
35
44
  void *xt_try_enter(void) {
36
45
  xt_try_frame *frame = (xt_try_frame *)calloc(1, sizeof(xt_try_frame));
37
46
  if (!frame) abort();
@@ -27,6 +27,11 @@ const sources = [
27
27
  ["xt_io.c", "xt_io.o"],
28
28
  // Node extension sources.
29
29
  ["ext_node/fs/read_file.c", "ext_node_fs_read_file.o"],
30
+ ["ext_node/fs/write_file.c", "ext_node_fs_write_file.o"],
31
+ ["ext_node/fs/fs_ops.c", "ext_node_fs_fs_ops.o"],
32
+ ["ext_node/path/path.c", "ext_node_path_path.o"],
33
+ ["ext_node/os/os.c", "ext_node_os_os.o"],
34
+ ["ext_node/process/process.c", "ext_node_process_process.o"],
30
35
  ];
31
36
 
32
37
  for (const [source, object] of sources) {
@@ -24,6 +24,7 @@ import {
24
24
  GLOBAL_FUNCTIONS,
25
25
  MATH_CONSTANTS,
26
26
  MATH_FUNCTIONS,
27
+ NAMESPACE_PROPERTIES,
27
28
  NAMESPACE_STATICS,
28
29
  propertyNameText,
29
30
  } from "./tables.js";
@@ -259,6 +260,18 @@ export const callMethods: CallMethods = {
259
260
  ) {
260
261
  return numberLiteral(MATH_CONSTANTS[node.name.text]!);
261
262
  }
263
+ if (
264
+ node.expression.kind === SyntaxKind.Identifier &&
265
+ !this.binding.symbolOfIdentifier.get(node.expression as Identifier)
266
+ ) {
267
+ const getter = NAMESPACE_PROPERTIES[(node.expression as Identifier).text];
268
+ if (getter) {
269
+ const key = this.stringValue(node.name.text);
270
+ const result = this.reg();
271
+ this.emit(` ${result} = call i64 @${getter}(i64 ${key})`);
272
+ return result;
273
+ }
274
+ }
262
275
  const object = this.emitExpression(node.expression);
263
276
  const access = (): string => {
264
277
  if (node.name.text === "length") {
@@ -155,6 +155,7 @@ export const moduleMethods: ModuleMethods = {
155
155
  this.functions.push(
156
156
  [
157
157
  "define i32 @main(i32 %argc, i8** %argv) {",
158
+ " call void @xt_set_program_args(i32 %argc, i8** %argv)",
158
159
  ` %result = call i64 @${moduleName}(i64 ${i64(XT_UNDEFINED)}, i64 ${i64(XT_UNDEFINED)}, i32 0, i64* null)`,
159
160
  " call void @xt_drain_microtasks()",
160
161
  " ret i32 0",