xbintsc 0.1.9 → 0.1.11

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 (96) 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 +2 -0
  5. package/dist/src/codegen/generator/module.js.map +1 -1
  6. package/dist/src/codegen/generator/state.js +19 -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 +25 -0
  10. package/dist/src/codegen/generator/tables.js.map +1 -1
  11. package/dist/src/driver/compiler.js +1 -0
  12. package/dist/src/driver/compiler.js.map +1 -1
  13. package/dist/src/extensions/node/buffer/index.d.ts +10 -0
  14. package/dist/src/extensions/node/buffer/index.js +15 -0
  15. package/dist/src/extensions/node/buffer/index.js.map +1 -0
  16. package/dist/src/extensions/node/dgram/index.d.ts +8 -0
  17. package/dist/src/extensions/node/dgram/index.js +13 -0
  18. package/dist/src/extensions/node/dgram/index.js.map +1 -0
  19. package/dist/src/extensions/node/fs/fs-ops.d.ts +6 -0
  20. package/dist/src/extensions/node/fs/fs-ops.js +18 -0
  21. package/dist/src/extensions/node/fs/fs-ops.js.map +1 -0
  22. package/dist/src/extensions/node/fs/index.d.ts +3 -1
  23. package/dist/src/extensions/node/fs/index.js +11 -3
  24. package/dist/src/extensions/node/fs/index.js.map +1 -1
  25. package/dist/src/extensions/node/fs/write-file.d.ts +5 -0
  26. package/dist/src/extensions/node/fs/write-file.js +8 -0
  27. package/dist/src/extensions/node/fs/write-file.js.map +1 -0
  28. package/dist/src/extensions/node/fs-promises/index.d.ts +10 -0
  29. package/dist/src/extensions/node/fs-promises/index.js +37 -0
  30. package/dist/src/extensions/node/fs-promises/index.js.map +1 -0
  31. package/dist/src/extensions/node/http/index.d.ts +10 -0
  32. package/dist/src/extensions/node/http/index.js +18 -0
  33. package/dist/src/extensions/node/http/index.js.map +1 -0
  34. package/dist/src/extensions/node/index.d.ts +3 -1
  35. package/dist/src/extensions/node/index.js +28 -4
  36. package/dist/src/extensions/node/index.js.map +1 -1
  37. package/dist/src/extensions/node/net/index.d.ts +9 -0
  38. package/dist/src/extensions/node/net/index.js +14 -0
  39. package/dist/src/extensions/node/net/index.js.map +1 -0
  40. package/dist/src/extensions/node/os/index.d.ts +9 -0
  41. package/dist/src/extensions/node/os/index.js +14 -0
  42. package/dist/src/extensions/node/os/index.js.map +1 -0
  43. package/dist/src/extensions/node/path/index.d.ts +10 -0
  44. package/dist/src/extensions/node/path/index.js +15 -0
  45. package/dist/src/extensions/node/path/index.js.map +1 -0
  46. package/dist/src/extensions/node/process/index.d.ts +10 -0
  47. package/dist/src/extensions/node/process/index.js +15 -0
  48. package/dist/src/extensions/node/process/index.js.map +1 -0
  49. package/dist/src/extensions/node/stream/index.d.ts +10 -0
  50. package/dist/src/extensions/node/stream/index.js +15 -0
  51. package/dist/src/extensions/node/stream/index.js.map +1 -0
  52. package/dist/tests/e2e/compile.test.js +55 -0
  53. package/dist/tests/e2e/compile.test.js.map +1 -1
  54. package/dist/tests/e2e/node-modules.test.d.ts +9 -0
  55. package/dist/tests/e2e/node-modules.test.js +179 -0
  56. package/dist/tests/e2e/node-modules.test.js.map +1 -0
  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/buffer/buffer.c +569 -0
  61. package/runtime/ext_node/dgram/dgram.c +337 -0
  62. package/runtime/ext_node/fs/fs_common.h +166 -0
  63. package/runtime/ext_node/fs/fs_ops.c +342 -0
  64. package/runtime/ext_node/fs/promises.c +60 -0
  65. package/runtime/ext_node/fs/read_file.c +12 -6
  66. package/runtime/ext_node/fs/write_file.c +47 -0
  67. package/runtime/ext_node/http/http.c +760 -0
  68. package/runtime/ext_node/net/net.c +546 -0
  69. package/runtime/ext_node/node_common.h +305 -0
  70. package/runtime/ext_node/os/os.c +165 -0
  71. package/runtime/ext_node/path/path.c +324 -0
  72. package/runtime/ext_node/process/process.c +137 -0
  73. package/runtime/ext_node/stream/stream.c +348 -0
  74. package/runtime/rt.h +19 -0
  75. package/runtime/xt_containers.c +2 -0
  76. package/runtime/xt_io.c +9 -0
  77. package/runtime/xt_loop.c +117 -0
  78. package/scripts/build-runtime.mjs +14 -0
  79. package/src/codegen/generator/calls.ts +13 -0
  80. package/src/codegen/generator/module.ts +2 -0
  81. package/src/codegen/generator/state.ts +19 -0
  82. package/src/codegen/generator/tables.ts +26 -0
  83. package/src/driver/compiler.ts +1 -0
  84. package/src/extensions/node/buffer/index.ts +17 -0
  85. package/src/extensions/node/dgram/index.ts +15 -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/fs-promises/index.ts +41 -0
  90. package/src/extensions/node/http/index.ts +20 -0
  91. package/src/extensions/node/index.ts +28 -4
  92. package/src/extensions/node/net/index.ts +16 -0
  93. package/src/extensions/node/os/index.ts +16 -0
  94. package/src/extensions/node/path/index.ts +17 -0
  95. package/src/extensions/node/process/index.ts +17 -0
  96. package/src/extensions/node/stream/index.ts +17 -0
@@ -0,0 +1,546 @@
1
+ /*
2
+ * Node.js `net` for xbintsc.
3
+ *
4
+ * TCP client and server support built directly on the core event loop
5
+ * (`xt_loop_add`). `net.connect` performs a conventional blocking connect and
6
+ * then registers the socket for readiness notifications, while
7
+ * `net.createServer(...).listen(...)` registers the listening descriptor and
8
+ * accepts connections as they arrive. Sockets are EventEmitters that expose
9
+ * `write`/`end`/`destroy` and emit `data`, `end`, `close`, `connect`.
10
+ */
11
+
12
+ #include "../node_common.h"
13
+
14
+ #include <errno.h>
15
+ #include <string.h>
16
+
17
+ #if defined(_WIN32)
18
+ #include <winsock2.h>
19
+ #include <ws2tcpip.h>
20
+ typedef int xt_socklen_t;
21
+ #define xt_close_socket closesocket
22
+ #else
23
+ #include <arpa/inet.h>
24
+ #include <fcntl.h>
25
+ #include <netdb.h>
26
+ #include <netinet/in.h>
27
+ #include <netinet/tcp.h>
28
+ #include <sys/socket.h>
29
+ #include <sys/types.h>
30
+ #include <unistd.h>
31
+ typedef socklen_t xt_socklen_t;
32
+ #define xt_close_socket close
33
+ #endif
34
+
35
+ /* Byte access helpers exported by the Buffer module. */
36
+ int xt_node_is_buffer(xt_value value);
37
+ unsigned char *xt_node_buffer_bytes(xt_value value, size_t *outLength);
38
+
39
+ static xt_value xt_net_socket_proto(void);
40
+ static xt_value xt_net_server_proto(void);
41
+
42
+ /* -- platform helpers ----------------------------------------------------- */
43
+
44
+ static void xt_net_set_nonblocking(int fd) {
45
+ #if defined(_WIN32)
46
+ u_long mode = 1;
47
+ ioctlsocket(fd, FIONBIO, &mode);
48
+ #else
49
+ int flags = fcntl(fd, F_GETFL, 0);
50
+ if (flags >= 0) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
51
+ #endif
52
+ }
53
+
54
+ static xt_value *xt_net_box(xt_value value) {
55
+ xt_value *handle = (xt_value *)malloc(sizeof(xt_value));
56
+ *handle = value;
57
+ return handle;
58
+ }
59
+
60
+ static int xt_net_fd(xt_value object) {
61
+ return (int)xt_to_number(xt_object_get_cstr(object, "__fd"));
62
+ }
63
+
64
+ static unsigned char *xt_net_bytes(xt_value data, size_t *outLength) {
65
+ if (xt_node_is_buffer(data)) return xt_node_buffer_bytes(data, outLength);
66
+ xt_value text = xt_to_string(data);
67
+ const char *string = xt_string_data(text);
68
+ size_t length = string ? (size_t)xt_string_length(xt_as_string(text)) : 0;
69
+ unsigned char *bytes = (unsigned char *)malloc(length + 1);
70
+ if (length > 0 && string) memcpy(bytes, string, length);
71
+ bytes[length] = '\0';
72
+ *outLength = length;
73
+ return bytes;
74
+ }
75
+
76
+ static int xt_net_send_all(int fd, const unsigned char *data, size_t length) {
77
+ size_t sent = 0;
78
+ while (sent < length) {
79
+ #if defined(_WIN32)
80
+ int written = send(fd, (const char *)data + sent, (int)(length - sent), 0);
81
+ #else
82
+ ssize_t written = send(fd, data + sent, length - sent, 0);
83
+ #endif
84
+ if (written > 0) {
85
+ sent += (size_t)written;
86
+ continue;
87
+ }
88
+ if (written < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
89
+ fd_set writeSet;
90
+ FD_ZERO(&writeSet);
91
+ FD_SET(fd, &writeSet);
92
+ struct timeval timeout;
93
+ timeout.tv_sec = 2;
94
+ timeout.tv_usec = 0;
95
+ if (select(fd + 1, NULL, &writeSet, NULL, &timeout) <= 0) return -1;
96
+ continue;
97
+ }
98
+ return -1;
99
+ }
100
+ return 0;
101
+ }
102
+
103
+ /* -- socket read loop ----------------------------------------------------- */
104
+
105
+ static void xt_net_socket_read(void *userdata, int events) {
106
+ (void)events;
107
+ xt_value socket = *(xt_value *)userdata;
108
+ int fd = xt_net_fd(socket);
109
+ if (fd < 0) return;
110
+
111
+ unsigned char buffer[65536];
112
+ #if defined(_WIN32)
113
+ int count = recv(fd, (char *)buffer, (int)sizeof(buffer), 0);
114
+ #else
115
+ ssize_t count = recv(fd, buffer, sizeof(buffer), 0);
116
+ #endif
117
+ if (count > 0) {
118
+ const char *encoding = xt_node_encoding(xt_object_get_cstr(socket, "__encoding"));
119
+ xt_value chunk = xt_node_encode(buffer, (size_t)count, encoding);
120
+ xt_node_emit1(socket, "data", chunk);
121
+ return;
122
+ }
123
+ if (count == 0) {
124
+ xt_loop_remove(fd);
125
+ xt_close_socket(fd);
126
+ xt_node_set(socket, "__fd", xt_number(-1));
127
+ xt_node_set(socket, "__closed", xt_bool(1));
128
+ xt_node_emit0(socket, "end");
129
+ xt_node_emit0(socket, "close");
130
+ return;
131
+ }
132
+ if (errno == EAGAIN || errno == EWOULDBLOCK) return;
133
+ xt_loop_remove(fd);
134
+ xt_close_socket(fd);
135
+ xt_node_set(socket, "__fd", xt_number(-1));
136
+ xt_node_set(socket, "__closed", xt_bool(1));
137
+ xt_value error = xt_string_from_cstr("ECONNRESET");
138
+ xt_node_emit1(socket, "error", error);
139
+ xt_node_emit0(socket, "close");
140
+ }
141
+
142
+ static xt_value xt_net_socket_from_fd(int fd, const struct sockaddr *peer, xt_socklen_t peerLength) {
143
+ xt_value socket = xt_object_new_with_proto(xt_net_socket_proto());
144
+ xt_node_set(socket, "__fd", xt_number((double)fd));
145
+ xt_node_set(socket, "__connected", xt_bool(1));
146
+ xt_node_set(socket, "__readable", xt_array_new(0, NULL));
147
+
148
+ char host[NI_MAXHOST];
149
+ char service[NI_MAXSERV];
150
+ if (peer && getnameinfo(peer, peerLength, host, sizeof(host), service, sizeof(service),
151
+ NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
152
+ xt_node_set(socket, "remoteAddress", xt_string_from_cstr(host));
153
+ xt_node_set(socket, "remotePort", xt_number((double)atoi(service)));
154
+ xt_node_set(socket, "remoteFamily", xt_string_from_cstr(strchr(host, ':') ? "IPv6" : "IPv4"));
155
+ }
156
+
157
+ xt_net_set_nonblocking(fd);
158
+ xt_loop_add(fd, XT_IO_READ, xt_net_socket_read, xt_net_box(socket));
159
+ return socket;
160
+ }
161
+
162
+ /* -- socket methods ------------------------------------------------------- */
163
+
164
+ static xt_value xt_net_socket_write(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
165
+ (void)env;
166
+ int fd = xt_net_fd(thisValue);
167
+ if (fd < 0) {
168
+ xt_value callback = xt_arg(argc, argv, argc > 1 ? 2 : 1);
169
+ xt_value error = xt_string_from_cstr("ERR_SOCKET_CLOSED");
170
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 1, &error);
171
+ return xt_bool(0);
172
+ }
173
+ size_t length = 0;
174
+ unsigned char *bytes = xt_net_bytes(xt_arg(argc, argv, 0), &length);
175
+ xt_net_send_all(fd, bytes, length);
176
+ free(bytes);
177
+ xt_value callback = XT_IS_FUNCTION(xt_arg(argc, argv, 1)) ? xt_arg(argc, argv, 1) : xt_arg(argc, argv, 2);
178
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
179
+ return xt_bool(1);
180
+ }
181
+
182
+ static xt_value xt_net_socket_end(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
183
+ (void)env;
184
+ int fd = xt_net_fd(thisValue);
185
+ if (fd < 0) return thisValue;
186
+ if (argc > 0 && !xt_truthy(xt_is_nullish(argv[0]))) {
187
+ xt_value writeFn = xt_object_get_cstr(thisValue, "write");
188
+ if (XT_IS_FUNCTION(writeFn)) xt_call_with_this(writeFn, thisValue, 1, &argv[0]);
189
+ }
190
+ #if defined(_WIN32)
191
+ shutdown(fd, SD_SEND);
192
+ #else
193
+ shutdown(fd, SHUT_WR);
194
+ #endif
195
+ return thisValue;
196
+ }
197
+
198
+ static xt_value xt_net_socket_destroy(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
199
+ (void)env;
200
+ (void)argc;
201
+ (void)argv;
202
+ int fd = xt_net_fd(thisValue);
203
+ if (fd >= 0) {
204
+ xt_loop_remove(fd);
205
+ xt_close_socket(fd);
206
+ }
207
+ xt_node_set(thisValue, "__fd", xt_number(-1));
208
+ if (!xt_truthy(xt_object_get_cstr(thisValue, "__closed"))) {
209
+ xt_node_set(thisValue, "__closed", xt_bool(1));
210
+ xt_node_emit0(thisValue, "close");
211
+ }
212
+ return thisValue;
213
+ }
214
+
215
+ static xt_value xt_net_socket_set_encoding(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
216
+ (void)env;
217
+ xt_node_set(thisValue, "__encoding", xt_arg(argc, argv, 0));
218
+ return thisValue;
219
+ }
220
+
221
+ static xt_value xt_net_socket_pause(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
222
+ (void)env;
223
+ (void)argc;
224
+ (void)argv;
225
+ return thisValue;
226
+ }
227
+
228
+ static xt_value xt_net_socket_resume(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
229
+ (void)env;
230
+ (void)argc;
231
+ (void)argv;
232
+ return thisValue;
233
+ }
234
+
235
+ static xt_value xt_net_socket_noop(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
236
+ (void)env;
237
+ (void)argc;
238
+ (void)argv;
239
+ return thisValue;
240
+ }
241
+
242
+ static xt_value xt_net_socket_address(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
243
+ (void)env;
244
+ (void)argc;
245
+ (void)argv;
246
+ int fd = xt_net_fd(thisValue);
247
+ if (fd < 0) return xt_null();
248
+ struct sockaddr_storage storage;
249
+ xt_socklen_t length = sizeof(storage);
250
+ if (getsockname(fd, (struct sockaddr *)&storage, &length) != 0) return xt_null();
251
+ char host[NI_MAXHOST];
252
+ char service[NI_MAXSERV];
253
+ if (getnameinfo((struct sockaddr *)&storage, length, host, sizeof(host), service, sizeof(service),
254
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
255
+ return xt_null();
256
+ xt_value object = xt_object_new();
257
+ xt_node_set(object, "address", xt_string_from_cstr(host));
258
+ xt_node_set(object, "port", xt_number((double)atoi(service)));
259
+ xt_node_set(object, "family", xt_string_from_cstr(strchr(host, ':') ? "IPv6" : "IPv4"));
260
+ return object;
261
+ }
262
+
263
+ /* -- server --------------------------------------------------------------- */
264
+
265
+ static void xt_net_server_accept(void *userdata, int events) {
266
+ (void)events;
267
+ xt_value server = *(xt_value *)userdata;
268
+ int listenFd = xt_net_fd(server);
269
+ if (listenFd < 0) return;
270
+ for (;;) {
271
+ struct sockaddr_storage storage;
272
+ xt_socklen_t length = sizeof(storage);
273
+ int fd = accept(listenFd, (struct sockaddr *)&storage, &length);
274
+ if (fd < 0) break;
275
+ xt_value socket = xt_net_socket_from_fd(fd, (struct sockaddr *)&storage, length);
276
+ xt_node_emit1(server, "connection", socket);
277
+ }
278
+ }
279
+
280
+ /* Parse the many listen() signatures into a port + host. */
281
+ static void xt_net_parse_listen(int32_t argc, xt_value *argv, int *port, const char **host, xt_value *callback) {
282
+ *port = 0;
283
+ *host = "0.0.0.0";
284
+ *callback = XT_UNDEFINED;
285
+ int index = 0;
286
+ if (argc > 0 && XT_IS_OBJECT(argv[0])) {
287
+ xt_value portValue = xt_object_get_cstr(argv[0], "port");
288
+ if (!xt_truthy(xt_is_nullish(portValue))) *port = (int)xt_to_number(portValue);
289
+ xt_value hostValue = xt_object_get_cstr(argv[0], "host");
290
+ if (XT_IS_STRING(hostValue)) *host = xt_string_data(hostValue);
291
+ index = 1;
292
+ } else if (argc > 0 && XT_IS_NUMBER(argv[0])) {
293
+ *port = (int)xt_to_number(argv[0]);
294
+ index = 1;
295
+ if (argc > 1 && XT_IS_STRING(argv[1])) {
296
+ *host = xt_string_data(argv[1]);
297
+ index = 2;
298
+ }
299
+ }
300
+ for (int32_t i = index; i < argc; i++) {
301
+ if (XT_IS_FUNCTION(argv[i])) *callback = argv[i];
302
+ }
303
+ }
304
+
305
+ static xt_value xt_net_server_listen(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
306
+ (void)env;
307
+ int port = 0;
308
+ const char *host = "0.0.0.0";
309
+ xt_value callback = XT_UNDEFINED;
310
+ xt_net_parse_listen(argc, argv, &port, &host, &callback);
311
+
312
+ char service[16];
313
+ snprintf(service, sizeof(service), "%d", port);
314
+ struct addrinfo hints;
315
+ memset(&hints, 0, sizeof(hints));
316
+ hints.ai_family = AF_UNSPEC;
317
+ hints.ai_socktype = SOCK_STREAM;
318
+ hints.ai_flags = AI_PASSIVE;
319
+ struct addrinfo *result = NULL;
320
+ if (getaddrinfo(host, service, &hints, &result) != 0) {
321
+ if (XT_IS_FUNCTION(callback)) {
322
+ xt_value error = xt_string_from_cstr("EADDRNOTAVAIL");
323
+ xt_call_with_this(callback, xt_undefined(), 1, &error);
324
+ }
325
+ return thisValue;
326
+ }
327
+
328
+ int listenFd = -1;
329
+ for (struct addrinfo *info = result; info; info = info->ai_next) {
330
+ int fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
331
+ if (fd < 0) continue;
332
+ int yes = 1;
333
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
334
+ if (bind(fd, info->ai_addr, info->ai_addrlen) == 0 && listen(fd, 511) == 0) {
335
+ listenFd = fd;
336
+ break;
337
+ }
338
+ xt_close_socket(fd);
339
+ }
340
+ freeaddrinfo(result);
341
+
342
+ if (listenFd < 0) {
343
+ if (XT_IS_FUNCTION(callback)) {
344
+ xt_value error = xt_string_from_cstr("EADDRINUSE");
345
+ xt_call_with_this(callback, xt_undefined(), 1, &error);
346
+ }
347
+ return thisValue;
348
+ }
349
+
350
+ xt_net_set_nonblocking(listenFd);
351
+ xt_node_set(thisValue, "__fd", xt_number((double)listenFd));
352
+ xt_node_set(thisValue, "__listening", xt_bool(1));
353
+ xt_loop_add(listenFd, XT_IO_READ, xt_net_server_accept, xt_net_box(thisValue));
354
+
355
+ xt_node_emit0(thisValue, "listening");
356
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
357
+ return thisValue;
358
+ }
359
+
360
+ static xt_value xt_net_server_close(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
361
+ (void)env;
362
+ int fd = xt_net_fd(thisValue);
363
+ if (fd >= 0) {
364
+ xt_loop_remove(fd);
365
+ xt_close_socket(fd);
366
+ }
367
+ xt_node_set(thisValue, "__fd", xt_number(-1));
368
+ xt_node_set(thisValue, "__listening", xt_bool(0));
369
+ xt_value callback = XT_IS_FUNCTION(xt_arg(argc, argv, 0)) ? xt_arg(argc, argv, 0) : XT_UNDEFINED;
370
+ xt_node_emit0(thisValue, "close");
371
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
372
+ return thisValue;
373
+ }
374
+
375
+ static xt_value xt_net_server_address(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
376
+ return xt_net_socket_address(thisValue, env, argc, argv);
377
+ }
378
+
379
+ static xt_value xt_net_server_get_connections(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
380
+ (void)thisValue;
381
+ (void)env;
382
+ (void)argc;
383
+ (void)argv;
384
+ xt_value callback = xt_arg(argc, argv, 0);
385
+ if (XT_IS_FUNCTION(callback)) {
386
+ xt_value count = xt_number(0);
387
+ xt_value args[2];
388
+ args[0] = xt_null();
389
+ args[1] = count;
390
+ xt_call_with_this(callback, xt_undefined(), 2, args);
391
+ }
392
+ return xt_undefined();
393
+ }
394
+
395
+ /* -- construction --------------------------------------------------------- */
396
+
397
+ xt_value xt_net_socket_ctor(int32_t argc, xt_value *argv) {
398
+ (void)argc;
399
+ (void)argv;
400
+ xt_value socket = xt_object_new_with_proto(xt_net_socket_proto());
401
+ xt_node_set(socket, "__fd", xt_number(-1));
402
+ xt_node_set(socket, "__readable", xt_array_new(0, NULL));
403
+ return socket;
404
+ }
405
+
406
+ xt_value xt_net_server_ctor(int32_t argc, xt_value *argv) {
407
+ xt_value server = xt_object_new_with_proto(xt_net_server_proto());
408
+ xt_node_set(server, "__fd", xt_number(-1));
409
+ for (int32_t i = 0; i < argc; i++) {
410
+ if (XT_IS_FUNCTION(argv[i])) {
411
+ xt_array_push(xt_node_listeners(server, "connection", 1), argv[i]);
412
+ break;
413
+ }
414
+ }
415
+ return server;
416
+ }
417
+
418
+ /* -- connect / static API ------------------------------------------------- */
419
+
420
+ static int xt_net_connect_fd(const char *host, int port) {
421
+ char service[16];
422
+ snprintf(service, sizeof(service), "%d", port);
423
+ struct addrinfo hints;
424
+ memset(&hints, 0, sizeof(hints));
425
+ hints.ai_family = AF_UNSPEC;
426
+ hints.ai_socktype = SOCK_STREAM;
427
+ struct addrinfo *result = NULL;
428
+ if (getaddrinfo(host, service, &hints, &result) != 0) return -1;
429
+ int fd = -1;
430
+ for (struct addrinfo *info = result; info; info = info->ai_next) {
431
+ int candidate = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
432
+ if (candidate < 0) continue;
433
+ if (connect(candidate, info->ai_addr, info->ai_addrlen) == 0) {
434
+ fd = candidate;
435
+ break;
436
+ }
437
+ xt_close_socket(candidate);
438
+ }
439
+ freeaddrinfo(result);
440
+ return fd;
441
+ }
442
+
443
+ xt_value xt_node_net_connect(int32_t argc, xt_value *argv) {
444
+ int port = 0;
445
+ const char *host = "127.0.0.1";
446
+ xt_value callback = XT_UNDEFINED;
447
+ int index = 0;
448
+ if (argc > 0 && XT_IS_OBJECT(argv[0])) {
449
+ xt_value portValue = xt_object_get_cstr(argv[0], "port");
450
+ if (!xt_truthy(xt_is_nullish(portValue))) port = (int)xt_to_number(portValue);
451
+ xt_value hostValue = xt_object_get_cstr(argv[0], "host");
452
+ if (XT_IS_STRING(hostValue)) host = xt_string_data(hostValue);
453
+ index = 1;
454
+ } else if (argc > 0 && XT_IS_NUMBER(argv[0])) {
455
+ port = (int)xt_to_number(argv[0]);
456
+ index = 1;
457
+ if (argc > 1 && XT_IS_STRING(argv[1])) {
458
+ host = xt_string_data(argv[1]);
459
+ index = 2;
460
+ }
461
+ }
462
+ for (int32_t i = index; i < argc; i++) {
463
+ if (XT_IS_FUNCTION(argv[i])) callback = argv[i];
464
+ }
465
+
466
+ int fd = xt_net_connect_fd(host, port);
467
+ if (fd < 0) {
468
+ xt_value socket = xt_net_socket_ctor(0, NULL);
469
+ xt_value error = xt_string_from_cstr("ECONNREFUSED");
470
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 1, &error);
471
+ else xt_node_emit1(socket, "error", error);
472
+ return socket;
473
+ }
474
+
475
+ struct sockaddr_storage storage;
476
+ xt_socklen_t length = sizeof(storage);
477
+ getpeername(fd, (struct sockaddr *)&storage, &length);
478
+ xt_value socket = xt_net_socket_from_fd(fd, (struct sockaddr *)&storage, length);
479
+ xt_node_set(socket, "__connecting", xt_bool(0));
480
+ xt_node_emit0(socket, "connect");
481
+ xt_node_emit0(socket, "ready");
482
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
483
+ return socket;
484
+ }
485
+
486
+ xt_value xt_net_static(xt_value name, int32_t argc, xt_value *argv) {
487
+ const char *fn = xt_string_data(name);
488
+ if (!fn) return xt_undefined();
489
+ if (strcmp(fn, "createServer") == 0) {
490
+ xt_value server = xt_net_server_ctor(0, NULL);
491
+ for (int32_t i = 0; i < argc; i++) {
492
+ if (XT_IS_FUNCTION(argv[i])) {
493
+ xt_array_push(xt_node_listeners(server, "connection", 1), argv[i]);
494
+ break;
495
+ }
496
+ }
497
+ return server;
498
+ }
499
+ if (strcmp(fn, "connect") == 0 || strcmp(fn, "createConnection") == 0) return xt_node_net_connect(argc, argv);
500
+ if (strcmp(fn, "isIP") == 0 || strcmp(fn, "isIPv4") == 0 || strcmp(fn, "isIPv6") == 0) {
501
+ const char *input = xt_string_data(xt_to_string(xt_arg(argc, argv, 0)));
502
+ if (!input) return xt_number(0);
503
+ if (strcmp(fn, "isIPv4") == 0) return xt_bool(strchr(input, '.') && !strchr(input, ':'));
504
+ if (strcmp(fn, "isIPv6") == 0) return xt_bool(strchr(input, ':') != NULL);
505
+ if (strchr(input, ':')) return xt_number(6);
506
+ if (strchr(input, '.')) return xt_number(4);
507
+ return xt_number(0);
508
+ }
509
+ return xt_undefined();
510
+ }
511
+
512
+ /* -- prototypes ----------------------------------------------------------- */
513
+
514
+ static xt_value xt_net_socket_proto(void) {
515
+ static xt_value proto = 0;
516
+ if (proto) return proto;
517
+ proto = xt_object_new();
518
+ xt_node_install_emitter(proto);
519
+ xt_node_define_method(proto, "write", (void *)xt_net_socket_write);
520
+ xt_node_define_method(proto, "end", (void *)xt_net_socket_end);
521
+ xt_node_define_method(proto, "destroy", (void *)xt_net_socket_destroy);
522
+ xt_node_define_method(proto, "setEncoding", (void *)xt_net_socket_set_encoding);
523
+ xt_node_define_method(proto, "pause", (void *)xt_net_socket_pause);
524
+ xt_node_define_method(proto, "resume", (void *)xt_net_socket_resume);
525
+ xt_node_define_method(proto, "setNoDelay", (void *)xt_net_socket_noop);
526
+ xt_node_define_method(proto, "setKeepAlive", (void *)xt_net_socket_noop);
527
+ xt_node_define_method(proto, "setTimeout", (void *)xt_net_socket_noop);
528
+ xt_node_define_method(proto, "ref", (void *)xt_net_socket_noop);
529
+ xt_node_define_method(proto, "unref", (void *)xt_net_socket_noop);
530
+ xt_node_define_method(proto, "address", (void *)xt_net_socket_address);
531
+ return proto;
532
+ }
533
+
534
+ static xt_value xt_net_server_proto(void) {
535
+ static xt_value proto = 0;
536
+ if (proto) return proto;
537
+ proto = xt_object_new();
538
+ xt_node_install_emitter(proto);
539
+ xt_node_define_method(proto, "listen", (void *)xt_net_server_listen);
540
+ xt_node_define_method(proto, "close", (void *)xt_net_server_close);
541
+ xt_node_define_method(proto, "address", (void *)xt_net_server_address);
542
+ xt_node_define_method(proto, "getConnections", (void *)xt_net_server_get_connections);
543
+ xt_node_define_method(proto, "ref", (void *)xt_net_socket_noop);
544
+ xt_node_define_method(proto, "unref", (void *)xt_net_socket_noop);
545
+ return proto;
546
+ }