xbintsc 0.3.25 → 0.3.46
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.
- package/README.md +39 -3
- package/dist/src/cli/main.js +124 -30
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.js +8 -1
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/module.js +10 -0
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +24 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +6 -0
- package/dist/src/codegen/generator/tables.js +38 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/commonjs.d.ts +52 -0
- package/dist/src/driver/bundler/commonjs.js +493 -0
- package/dist/src/driver/bundler/commonjs.js.map +1 -0
- package/dist/src/driver/bundler/graph.js +14 -0
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/bundler/merge.js +14 -0
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/bundler/types.d.ts +8 -0
- package/dist/src/driver/compiler.d.ts +8 -1
- package/dist/src/driver/compiler.js +73 -5
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/config.d.ts +53 -0
- package/dist/src/driver/config.js +141 -0
- package/dist/src/driver/config.js.map +1 -0
- package/dist/src/driver/icon.d.ts +62 -0
- package/dist/src/driver/icon.js +151 -0
- package/dist/src/driver/icon.js.map +1 -0
- package/dist/src/driver/index.d.ts +4 -0
- package/dist/src/driver/index.js +4 -0
- package/dist/src/driver/index.js.map +1 -1
- package/dist/src/driver/mac-bundle.d.ts +41 -0
- package/dist/src/driver/mac-bundle.js +123 -0
- package/dist/src/driver/mac-bundle.js.map +1 -0
- package/dist/src/driver/win-icon.d.ts +27 -0
- package/dist/src/driver/win-icon.js +80 -0
- package/dist/src/driver/win-icon.js.map +1 -0
- package/dist/src/parser/statements.js +17 -4
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/tests/cli/main.test.js +46 -1
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/driver/config.test.d.ts +1 -0
- package/dist/tests/driver/config.test.js +124 -0
- package/dist/tests/driver/config.test.js.map +1 -0
- package/dist/tests/driver/icon.test.d.ts +1 -0
- package/dist/tests/driver/icon.test.js +421 -0
- package/dist/tests/driver/icon.test.js.map +1 -0
- package/dist/tests/driver/modules.test.js +109 -0
- package/dist/tests/driver/modules.test.js.map +1 -1
- package/dist/tests/e2e/fetch.test.d.ts +12 -0
- package/dist/tests/e2e/fetch.test.js +272 -0
- package/dist/tests/e2e/fetch.test.js.map +1 -0
- package/dist/tests/e2e/gc.test.d.ts +1 -0
- package/dist/tests/e2e/gc.test.js +168 -0
- package/dist/tests/e2e/gc.test.js.map +1 -0
- package/dist/tests/e2e/gui-dom.test.d.ts +5 -0
- package/dist/tests/e2e/gui-dom.test.js +102 -0
- package/dist/tests/e2e/gui-dom.test.js.map +1 -0
- package/dist/tests/e2e/gui-events.test.d.ts +5 -0
- package/dist/tests/e2e/gui-events.test.js +60 -0
- package/dist/tests/e2e/gui-events.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +31 -0
- package/dist/tests/e2e/gui-helpers.js +74 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -0
- package/dist/tests/e2e/gui-layout.test.d.ts +4 -0
- package/dist/tests/e2e/gui-layout.test.js +88 -0
- package/dist/tests/e2e/gui-layout.test.js.map +1 -0
- package/dist/tests/e2e/gui-lifecycle.test.d.ts +5 -0
- package/dist/tests/e2e/gui-lifecycle.test.js +58 -0
- package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -0
- package/dist/tests/e2e/gui-paint.test.d.ts +5 -0
- package/dist/tests/e2e/gui-paint.test.js +111 -0
- package/dist/tests/e2e/gui-paint.test.js.map +1 -0
- package/dist/tests/e2e/gui-script.test.d.ts +5 -0
- package/dist/tests/e2e/gui-script.test.js +78 -0
- package/dist/tests/e2e/gui-script.test.js.map +1 -0
- package/dist/tests/e2e/gui-style.test.d.ts +5 -0
- package/dist/tests/e2e/gui-style.test.js +175 -0
- package/dist/tests/e2e/gui-style.test.js.map +1 -0
- package/dist/tests/e2e/harness.d.ts +2 -0
- package/dist/tests/e2e/harness.js +1 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/e2e/icon.test.d.ts +7 -0
- package/dist/tests/e2e/icon.test.js +80 -0
- package/dist/tests/e2e/icon.test.js.map +1 -0
- package/dist/tests/e2e/npm-modules.test.d.ts +4 -1
- package/dist/tests/e2e/npm-modules.test.js +47 -1
- package/dist/tests/e2e/npm-modules.test.js.map +1 -1
- package/dist/tests/e2e/runtime-coverage-typed-array.test.d.ts +9 -0
- package/dist/tests/e2e/runtime-coverage-typed-array.test.js +140 -0
- package/dist/tests/e2e/runtime-coverage-typed-array.test.js.map +1 -0
- package/dist/tests/parser/statements.test.js +11 -0
- package/dist/tests/parser/statements.test.js.map +1 -1
- package/package.json +3 -2
- package/runtime/ext_gui/css.cpp +4 -517
- package/runtime/ext_gui/css_internal.h +27 -0
- package/runtime/ext_gui/css_match.cpp +195 -0
- package/runtime/ext_gui/css_selector.cpp +207 -0
- package/runtime/ext_gui/css_sheet.cpp +129 -0
- package/runtime/ext_gui/dom.cpp +7 -403
- package/runtime/ext_gui/dom_api.cpp +63 -1146
- package/runtime/ext_gui/dom_api_document.cpp +96 -0
- package/runtime/ext_gui/dom_api_events.cpp +123 -0
- package/runtime/ext_gui/dom_api_internal.h +135 -0
- package/runtime/ext_gui/dom_api_node.cpp +329 -0
- package/runtime/ext_gui/dom_api_node_read.cpp +294 -0
- package/runtime/ext_gui/dom_api_proto.cpp +135 -0
- package/runtime/ext_gui/dom_api_style.cpp +192 -0
- package/runtime/ext_gui/dom_internal.h +39 -0
- package/runtime/ext_gui/dom_parse.cpp +348 -0
- package/runtime/ext_gui/dom_serialize.cpp +54 -0
- package/runtime/ext_gui/gui.cpp +56 -286
- package/runtime/ext_gui/gui_events.cpp +294 -0
- package/runtime/ext_gui/gui_internal.h +29 -0
- package/runtime/ext_gui/layout.cpp +6 -597
- package/runtime/ext_gui/layout_flex.cpp +200 -0
- package/runtime/ext_gui/layout_flow.cpp +191 -0
- package/runtime/ext_gui/layout_flow_internal.h +149 -0
- package/runtime/ext_gui/layout_inline.cpp +181 -0
- package/runtime/ext_gui/layout_internal.h +20 -0
- package/runtime/ext_gui/renderer.cpp +15 -679
- package/runtime/ext_gui/renderer_image.cpp +233 -0
- package/runtime/ext_gui/renderer_internal.h +112 -0
- package/runtime/ext_gui/renderer_shaders.h +108 -0
- package/runtime/ext_gui/renderer_text.cpp +342 -0
- package/runtime/ext_gui/style.cpp +12 -660
- package/runtime/ext_gui/style_internal.h +33 -0
- package/runtime/ext_gui/style_print.cpp +113 -0
- package/runtime/ext_gui/style_ua.cpp +98 -0
- package/runtime/ext_gui/style_values.cpp +468 -0
- package/runtime/ext_gui/window.cpp +1 -0
- package/runtime/ext_node/buffer/parts/prototype.inc +1 -0
- package/runtime/ext_node/dgram/dgram.c +1 -0
- package/runtime/ext_node/events/events.c +1 -0
- package/runtime/ext_node/fs/fs_ops.c +10 -25
- package/runtime/ext_node/fs/glob.c +13 -30
- package/runtime/ext_node/fs/promises.c +1 -0
- package/runtime/ext_node/http/parts/prototypes.inc +5 -0
- package/runtime/ext_node/net/parts/prototypes.inc +2 -0
- package/runtime/ext_node/process/process.c +9 -7
- package/runtime/ext_node/stream/stream.c +1 -0
- package/runtime/ext_node/util/util.c +6 -6
- package/runtime/rt.h +16 -0
- package/runtime/rt_internal.h +67 -2
- package/runtime/xt_alloc.c +442 -5
- package/runtime/xt_builtins.c +1 -0
- package/runtime/xt_containers/access.inc +32 -1
- package/runtime/xt_fetch/buffer.inc +64 -0
- package/runtime/xt_fetch/headers.inc +312 -0
- package/runtime/xt_fetch/request.inc +386 -0
- package/runtime/xt_fetch/response.inc +107 -0
- package/runtime/xt_fetch/socket.inc +72 -0
- package/runtime/xt_fetch/url.inc +108 -0
- package/runtime/xt_fetch.c +39 -0
- package/runtime/xt_generator.c +95 -1
- package/runtime/xt_loop.c +35 -1
- package/runtime/xt_promise.c +46 -0
- package/runtime/xt_stdlib2/error.inc +1 -0
- package/runtime/xt_stdlib2/regexp-match.inc +8 -7
- package/runtime/xt_stdlib2/set.inc +1 -1
- package/runtime/xt_symbol.c +2 -0
- package/runtime/xt_typed_array/construction.inc +142 -0
- package/runtime/xt_typed_array/elements.inc +92 -0
- package/runtime/xt_typed_array/methods.inc +329 -0
- package/runtime/xt_typed_array.c +72 -0
- package/runtime/xt_values/number-format.inc +26 -0
- package/scripts/check-file-length.ts +85 -0
- package/src/cli/main.ts +137 -29
- package/src/codegen/generator/calls/access.ts +9 -1
- package/src/codegen/generator/module.ts +10 -0
- package/src/codegen/generator/state.ts +24 -0
- package/src/codegen/generator/tables.ts +40 -0
- package/src/driver/bundler/commonjs.ts +553 -0
- package/src/driver/bundler/graph.ts +14 -0
- package/src/driver/bundler/merge.ts +15 -0
- package/src/driver/bundler/types.ts +8 -0
- package/src/driver/compiler.ts +83 -5
- package/src/driver/config.ts +173 -0
- package/src/driver/icon.ts +184 -0
- package/src/driver/index.ts +30 -0
- package/src/driver/mac-bundle.ts +156 -0
- package/src/driver/win-icon.ts +96 -0
- package/src/parser/statements.ts +17 -4
- package/dist/tests/e2e/gui.test.d.ts +0 -12
- package/dist/tests/e2e/gui.test.js +0 -702
- package/dist/tests/e2e/gui.test.js.map +0 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* -- Response ------------------------------------------------------------- */
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* `fetch` resolves with a plain object shaped like a `Response`: the status
|
|
5
|
+
* fields are own properties, `headers` is a Headers object, and the body is
|
|
6
|
+
* kept verbatim under `__body`. The body consumers (`text` / `json` /
|
|
7
|
+
* `arrayBuffer` / `bytes`) return promises, matching the platform API even
|
|
8
|
+
* though the value is already available.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
static void *xt_response_method_impl(xt_value name);
|
|
12
|
+
static xt_value xt_method_value_for_response(xt_value name);
|
|
13
|
+
|
|
14
|
+
static xt_value xt_response_body(xt_value response) {
|
|
15
|
+
xt_value body = xt_object_get_cstr(response, "__body");
|
|
16
|
+
return XT_IS_STRING(body) ? body : xt_string_from_cstr("");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static xt_value xt_response_method_text(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
20
|
+
(void)env;
|
|
21
|
+
(void)argc;
|
|
22
|
+
(void)argv;
|
|
23
|
+
return xt_promise_resolve(xt_response_body(thisValue));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static xt_value xt_response_method_json(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
27
|
+
(void)env;
|
|
28
|
+
(void)argc;
|
|
29
|
+
(void)argv;
|
|
30
|
+
xt_value body = xt_response_body(thisValue);
|
|
31
|
+
return xt_promise_resolve(xt_json_parse(1, &body));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static xt_value xt_response_method_array_buffer(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
35
|
+
(void)env;
|
|
36
|
+
(void)argc;
|
|
37
|
+
(void)argv;
|
|
38
|
+
xt_value body = xt_response_body(thisValue);
|
|
39
|
+
const char *data = xt_string_data(body);
|
|
40
|
+
int32_t length = xt_string_length_value(body);
|
|
41
|
+
if (!data || length < 0) length = 0;
|
|
42
|
+
xt_value bytes = xt_array_new(0, NULL);
|
|
43
|
+
for (int32_t i = 0; i < length; i++) {
|
|
44
|
+
xt_array_push(bytes, xt_number((double)(unsigned char)data[i]));
|
|
45
|
+
}
|
|
46
|
+
return xt_promise_resolve(bytes);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static xt_value xt_response_method_clone(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
50
|
+
(void)env;
|
|
51
|
+
(void)argc;
|
|
52
|
+
(void)argv;
|
|
53
|
+
xt_value clone = xt_object_new();
|
|
54
|
+
xt_value keys = xt_object_keys(thisValue);
|
|
55
|
+
int32_t count = (int32_t)xt_to_number(xt_array_length(keys));
|
|
56
|
+
for (int32_t i = 0; i < count; i++) {
|
|
57
|
+
xt_value key = xt_array_get(keys, xt_number((double)i));
|
|
58
|
+
xt_value value = xt_object_get(thisValue, key);
|
|
59
|
+
if (XT_IS_FUNCTION(value)) {
|
|
60
|
+
/* Re-use the method implementation with the new receiver. */
|
|
61
|
+
xt_value name = key;
|
|
62
|
+
xt_object_set(clone, name, xt_method_value_for_response(name));
|
|
63
|
+
} else {
|
|
64
|
+
xt_object_set(clone, key, value);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return clone;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Map a Response method name back to its implementation so `clone()` can copy
|
|
71
|
+
* the methods without duplicating the dispatch table. */
|
|
72
|
+
static void *xt_response_method_impl(xt_value name) {
|
|
73
|
+
const char *method = xt_string_data(name);
|
|
74
|
+
if (!method) return NULL;
|
|
75
|
+
if (strcmp(method, "text") == 0) return (void *)xt_response_method_text;
|
|
76
|
+
if (strcmp(method, "json") == 0) return (void *)xt_response_method_json;
|
|
77
|
+
if (strcmp(method, "arrayBuffer") == 0) return (void *)xt_response_method_array_buffer;
|
|
78
|
+
if (strcmp(method, "bytes") == 0) return (void *)xt_response_method_array_buffer;
|
|
79
|
+
if (strcmp(method, "clone") == 0) return (void *)xt_response_method_clone;
|
|
80
|
+
return NULL;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static xt_value xt_method_value_for_response(xt_value name) {
|
|
84
|
+
void *impl = xt_response_method_impl(name);
|
|
85
|
+
return impl ? xt_closure_new(impl, 0, NULL) : XT_UNDEFINED;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static xt_value xt_response_new(xt_value headers, int status, const char *statusText, const char *url,
|
|
89
|
+
const char *body, size_t bodyLength) {
|
|
90
|
+
xt_value response = xt_object_new();
|
|
91
|
+
xt_object_set(response, xt_string_from_cstr("ok"),
|
|
92
|
+
(status >= 200 && status < 300) ? XT_TRUE : XT_FALSE);
|
|
93
|
+
xt_object_set(response, xt_string_from_cstr("status"), xt_number((double)status));
|
|
94
|
+
xt_object_set(response, xt_string_from_cstr("statusText"), xt_string_from_cstr(statusText ? statusText : ""));
|
|
95
|
+
xt_object_set(response, xt_string_from_cstr("url"), xt_string_from_cstr(url ? url : ""));
|
|
96
|
+
xt_object_set(response, xt_string_from_cstr("redirected"), XT_FALSE);
|
|
97
|
+
xt_object_set(response, xt_string_from_cstr("type"), xt_string_from_cstr("basic"));
|
|
98
|
+
xt_object_set(response, xt_string_from_cstr("bodyUsed"), XT_FALSE);
|
|
99
|
+
xt_object_set(response, xt_string_from_cstr("headers"), headers);
|
|
100
|
+
xt_object_set(response, xt_string_from_cstr("__body"), xt_string_new(body ? body : "", bodyLength));
|
|
101
|
+
xt_fetch_set_method(response, "text", (void *)xt_response_method_text);
|
|
102
|
+
xt_fetch_set_method(response, "json", (void *)xt_response_method_json);
|
|
103
|
+
xt_fetch_set_method(response, "arrayBuffer", (void *)xt_response_method_array_buffer);
|
|
104
|
+
xt_fetch_set_method(response, "bytes", (void *)xt_response_method_array_buffer);
|
|
105
|
+
xt_fetch_set_method(response, "clone", (void *)xt_response_method_clone);
|
|
106
|
+
return response;
|
|
107
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* -- TCP socket helpers --------------------------------------------------- */
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* `fetch` performs a conventional blocking request/response exchange: connect,
|
|
5
|
+
* write the request, read until the response is complete (or the peer closes).
|
|
6
|
+
* The socket carries a receive timeout so a stalled server cannot hang the
|
|
7
|
+
* program forever; the timeout surfaces as a rejected promise.
|
|
8
|
+
*/
|
|
9
|
+
#define XT_FETCH_TIMEOUT_SECONDS 30
|
|
10
|
+
|
|
11
|
+
static void xt_fetch_set_timeouts(int fd) {
|
|
12
|
+
#if defined(_WIN32)
|
|
13
|
+
DWORD ms = (DWORD)XT_FETCH_TIMEOUT_SECONDS * 1000;
|
|
14
|
+
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&ms, (int)sizeof(ms));
|
|
15
|
+
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&ms, (int)sizeof(ms));
|
|
16
|
+
#else
|
|
17
|
+
struct timeval timeout;
|
|
18
|
+
timeout.tv_sec = XT_FETCH_TIMEOUT_SECONDS;
|
|
19
|
+
timeout.tv_usec = 0;
|
|
20
|
+
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
|
21
|
+
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
|
|
22
|
+
#endif
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static int xt_fetch_connect(const xt_fetch_url *url) {
|
|
26
|
+
struct addrinfo hints;
|
|
27
|
+
memset(&hints, 0, sizeof(hints));
|
|
28
|
+
hints.ai_family = AF_UNSPEC;
|
|
29
|
+
hints.ai_socktype = SOCK_STREAM;
|
|
30
|
+
struct addrinfo *result = NULL;
|
|
31
|
+
if (getaddrinfo(url->host, url->port, &hints, &result) != 0) return -1;
|
|
32
|
+
int fd = -1;
|
|
33
|
+
for (struct addrinfo *info = result; info; info = info->ai_next) {
|
|
34
|
+
int candidate = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
|
|
35
|
+
if (candidate < 0) continue;
|
|
36
|
+
if (connect(candidate, info->ai_addr, (xt_fetch_socklen_t)info->ai_addrlen) == 0) {
|
|
37
|
+
fd = candidate;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
xt_fetch_close_socket(candidate);
|
|
41
|
+
}
|
|
42
|
+
freeaddrinfo(result);
|
|
43
|
+
if (fd >= 0) xt_fetch_set_timeouts(fd);
|
|
44
|
+
return fd;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static int xt_fetch_send_all(int fd, const char *data, size_t length) {
|
|
48
|
+
size_t sent = 0;
|
|
49
|
+
while (sent < length) {
|
|
50
|
+
#if defined(_WIN32)
|
|
51
|
+
int written = send(fd, data + sent, (int)(length - sent), 0);
|
|
52
|
+
#else
|
|
53
|
+
ssize_t written = send(fd, data + sent, length - sent, 0);
|
|
54
|
+
#endif
|
|
55
|
+
if (written > 0) {
|
|
56
|
+
sent += (size_t)written;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
return -1;
|
|
60
|
+
}
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Receive one chunk; returns the byte count, `0` on close, `-1` on error. */
|
|
65
|
+
static int xt_fetch_recv(int fd, char *buffer, size_t capacity) {
|
|
66
|
+
#if defined(_WIN32)
|
|
67
|
+
return recv(fd, buffer, (int)capacity, 0);
|
|
68
|
+
#else
|
|
69
|
+
ssize_t count = recv(fd, buffer, capacity, 0);
|
|
70
|
+
return count > 2147483647 ? 2147483647 : (int)count;
|
|
71
|
+
#endif
|
|
72
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/* -- URL parsing ---------------------------------------------------------- */
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* `fetch` accepts absolute `http://` / `https://` URLs (there is no document
|
|
5
|
+
* base to resolve relative ones against). The parsed pieces are kept in a
|
|
6
|
+
* fixed-size struct because `fetch` hands them straight to `getaddrinfo`.
|
|
7
|
+
*/
|
|
8
|
+
typedef struct {
|
|
9
|
+
int secure;
|
|
10
|
+
char host[256];
|
|
11
|
+
char port[8];
|
|
12
|
+
char path[4096];
|
|
13
|
+
} xt_fetch_url;
|
|
14
|
+
|
|
15
|
+
/** Parse `url` into `out`. Returns 0 on success, -1 on a malformed URL. */
|
|
16
|
+
static int xt_fetch_parse_url(const char *url, xt_fetch_url *out) {
|
|
17
|
+
if (!url) return -1;
|
|
18
|
+
out->secure = 0;
|
|
19
|
+
const char *cursor = url;
|
|
20
|
+
if (strncmp(cursor, "http://", 7) == 0) {
|
|
21
|
+
cursor += 7;
|
|
22
|
+
snprintf(out->port, sizeof(out->port), "80");
|
|
23
|
+
} else if (strncmp(cursor, "https://", 8) == 0) {
|
|
24
|
+
cursor += 8;
|
|
25
|
+
out->secure = 1;
|
|
26
|
+
snprintf(out->port, sizeof(out->port), "443");
|
|
27
|
+
} else {
|
|
28
|
+
return -1;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* The authority ends at the first `/`, `?` or `#` (or the end of the URL). */
|
|
32
|
+
const char *authorityEnd = cursor;
|
|
33
|
+
while (*authorityEnd && *authorityEnd != '/' && *authorityEnd != '?' && *authorityEnd != '#') authorityEnd++;
|
|
34
|
+
|
|
35
|
+
/* Drop any `user:password@` prefix. */
|
|
36
|
+
const char *hostStart = cursor;
|
|
37
|
+
for (const char *p = cursor; p < authorityEnd; p++) {
|
|
38
|
+
if (*p == '@') hostStart = p + 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const char *hostEnd = authorityEnd;
|
|
42
|
+
const char *portStart = NULL;
|
|
43
|
+
if (hostStart < authorityEnd && *hostStart == '[') {
|
|
44
|
+
/* IPv6 literal: `[::1]:8080`. */
|
|
45
|
+
const char *close = (const char *)memchr(hostStart, ']', (size_t)(authorityEnd - hostStart));
|
|
46
|
+
if (!close) return -1;
|
|
47
|
+
hostEnd = close;
|
|
48
|
+
if (close + 1 < authorityEnd && close[1] == ':') portStart = close + 2;
|
|
49
|
+
hostStart++;
|
|
50
|
+
} else {
|
|
51
|
+
const char *colon = (const char *)memchr(hostStart, ':', (size_t)(authorityEnd - hostStart));
|
|
52
|
+
if (colon) {
|
|
53
|
+
hostEnd = colon;
|
|
54
|
+
portStart = colon + 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
size_t hostLength = (size_t)(hostEnd - hostStart);
|
|
59
|
+
if (hostLength == 0 || hostLength >= sizeof(out->host)) return -1;
|
|
60
|
+
memcpy(out->host, hostStart, hostLength);
|
|
61
|
+
out->host[hostLength] = '\0';
|
|
62
|
+
|
|
63
|
+
if (portStart && portStart < authorityEnd) {
|
|
64
|
+
size_t portLength = (size_t)(authorityEnd - portStart);
|
|
65
|
+
if (portLength >= sizeof(out->port)) return -1;
|
|
66
|
+
memcpy(out->port, portStart, portLength);
|
|
67
|
+
out->port[portLength] = '\0';
|
|
68
|
+
for (const char *p = out->port; *p; p++) {
|
|
69
|
+
if (*p < '0' || *p > '9') return -1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* The request target keeps the query string but drops the fragment. */
|
|
74
|
+
if (*authorityEnd == '\0' || *authorityEnd == '#') {
|
|
75
|
+
snprintf(out->path, sizeof(out->path), "/");
|
|
76
|
+
} else {
|
|
77
|
+
const char *pathEnd = authorityEnd;
|
|
78
|
+
while (*pathEnd && *pathEnd != '#') pathEnd++;
|
|
79
|
+
size_t pathLength = (size_t)(pathEnd - authorityEnd);
|
|
80
|
+
if (pathLength >= sizeof(out->path)) return -1;
|
|
81
|
+
memcpy(out->path, authorityEnd, pathLength);
|
|
82
|
+
out->path[pathLength] = '\0';
|
|
83
|
+
}
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* Resolve a `Location` header against the URL just requested. Only absolute
|
|
89
|
+
* URLs and root-relative paths are supported; anything else is reported as a
|
|
90
|
+
* failure so the caller can return the 3xx response unchanged.
|
|
91
|
+
*/
|
|
92
|
+
static int xt_fetch_resolve_location(const xt_fetch_url *base, const char *location, char *out, size_t size) {
|
|
93
|
+
if (strncmp(location, "http://", 7) == 0 || strncmp(location, "https://", 8) == 0) {
|
|
94
|
+
snprintf(out, size, "%s", location);
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
const char *scheme = base->secure ? "https" : "http";
|
|
98
|
+
if (location[0] == '/') {
|
|
99
|
+
int defaultPort = base->secure ? 443 : 80;
|
|
100
|
+
if (atoi(base->port) == defaultPort) {
|
|
101
|
+
snprintf(out, size, "%s://%s%s", scheme, base->host, location);
|
|
102
|
+
} else {
|
|
103
|
+
snprintf(out, size, "%s://%s:%s%s", scheme, base->host, base->port, location);
|
|
104
|
+
}
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
return -1;
|
|
108
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc runtime — the global `fetch` API.
|
|
3
|
+
*
|
|
4
|
+
* This is a small, dependency-free HTTP/1.1 client exposed as the platform
|
|
5
|
+
* `fetch` global. The runtime has no event loop (promises are drained
|
|
6
|
+
* synchronously by `await`), so the request is performed with blocking sockets
|
|
7
|
+
* and the returned promise is already settled. TLS is not implemented, so only
|
|
8
|
+
* `http://` URLs are accepted; `https://` rejects with a `TypeError`.
|
|
9
|
+
*
|
|
10
|
+
* The implementation is grouped by concern under `xt_fetch/` and `#include`d
|
|
11
|
+
* here as a single translation unit, following the other runtime modules.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
#include "rt_internal.h"
|
|
15
|
+
|
|
16
|
+
#include <stdio.h>
|
|
17
|
+
#include <stdlib.h>
|
|
18
|
+
#include <string.h>
|
|
19
|
+
|
|
20
|
+
#if defined(_WIN32)
|
|
21
|
+
#include <winsock2.h>
|
|
22
|
+
#include <ws2tcpip.h>
|
|
23
|
+
typedef int xt_fetch_socklen_t;
|
|
24
|
+
#define xt_fetch_close_socket closesocket
|
|
25
|
+
#else
|
|
26
|
+
#include <netdb.h>
|
|
27
|
+
#include <sys/socket.h>
|
|
28
|
+
#include <sys/time.h>
|
|
29
|
+
#include <unistd.h>
|
|
30
|
+
typedef socklen_t xt_fetch_socklen_t;
|
|
31
|
+
#define xt_fetch_close_socket close
|
|
32
|
+
#endif
|
|
33
|
+
|
|
34
|
+
#include "xt_fetch/buffer.inc"
|
|
35
|
+
#include "xt_fetch/url.inc"
|
|
36
|
+
#include "xt_fetch/socket.inc"
|
|
37
|
+
#include "xt_fetch/headers.inc"
|
|
38
|
+
#include "xt_fetch/response.inc"
|
|
39
|
+
#include "xt_fetch/request.inc"
|
package/runtime/xt_generator.c
CHANGED
|
@@ -45,14 +45,23 @@ typedef struct xt_generator {
|
|
|
45
45
|
int done;
|
|
46
46
|
int threw;
|
|
47
47
|
int booted;
|
|
48
|
+
/* Private stack bounds, registered as an active stack region while the
|
|
49
|
+
* generator runs and scanned conservatively while it is suspended. */
|
|
50
|
+
char *stack;
|
|
51
|
+
size_t stack_size;
|
|
52
|
+
int stack_pushed;
|
|
48
53
|
void *saved_try_top;
|
|
49
54
|
#ifdef _WIN32
|
|
50
55
|
void *fiber;
|
|
51
56
|
void *caller_fiber;
|
|
57
|
+
/* `SwitchToFiber` keeps the resumer's callee-saved registers in an opaque
|
|
58
|
+
* fiber context (POSIX stores them in `caller`), so snapshot them for the
|
|
59
|
+
* collector before switching away. */
|
|
60
|
+
jmp_buf caller_regs;
|
|
61
|
+
int has_caller_regs;
|
|
52
62
|
#else
|
|
53
63
|
ucontext_t ctx;
|
|
54
64
|
ucontext_t caller;
|
|
55
|
-
char *stack;
|
|
56
65
|
#endif
|
|
57
66
|
} xt_generator;
|
|
58
67
|
|
|
@@ -93,6 +102,27 @@ static void xt_generator_suspend(xt_generator *gen) {
|
|
|
93
102
|
|
|
94
103
|
/* Runs on the generator's private stack. */
|
|
95
104
|
static void xt_generator_run(xt_generator *gen) {
|
|
105
|
+
#ifdef _WIN32
|
|
106
|
+
/* The fiber stack is not observable from the outside; capture its bounds
|
|
107
|
+
* here (before the body allocates anything) so a collection during the body
|
|
108
|
+
* has a valid scan region. */
|
|
109
|
+
if (!gen->stack) {
|
|
110
|
+
MEMORY_BASIC_INFORMATION info;
|
|
111
|
+
uintptr_t here = (uintptr_t)&info;
|
|
112
|
+
if (VirtualQuery((LPCVOID)here, &info, sizeof(info))) {
|
|
113
|
+
/* Use the *committed* region containing the current frame: a Windows
|
|
114
|
+
* stack reserves more than it commits, and `AllocationBase + RegionSize`
|
|
115
|
+
* can fall in uncommitted memory. `BaseAddress + RegionSize` is the
|
|
116
|
+
* stack top. */
|
|
117
|
+
gen->stack = (char *)info.BaseAddress;
|
|
118
|
+
gen->stack_size = info.RegionSize;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
#endif
|
|
122
|
+
if (gen->stack && gen->stack_size && !gen->stack_pushed) {
|
|
123
|
+
xt_gc_push_stack((uintptr_t)gen->stack, (uintptr_t)gen->stack + gen->stack_size);
|
|
124
|
+
gen->stack_pushed = 1;
|
|
125
|
+
}
|
|
96
126
|
void *boundary = xt_try_enter();
|
|
97
127
|
if (xt_try_setjmp(boundary) == 0) {
|
|
98
128
|
xt_function *function = (xt_function *)XT_GET_PTR(gen->function);
|
|
@@ -127,6 +157,7 @@ static void xt_generator_boot(xt_generator *gen) {
|
|
|
127
157
|
size_t size = 1 << 20;
|
|
128
158
|
gen->stack = (char *)malloc(size);
|
|
129
159
|
if (!gen->stack) abort();
|
|
160
|
+
gen->stack_size = size;
|
|
130
161
|
if (getcontext(&gen->ctx) != 0) abort();
|
|
131
162
|
gen->ctx.uc_stack.ss_sp = gen->stack;
|
|
132
163
|
gen->ctx.uc_stack.ss_size = size;
|
|
@@ -146,6 +177,9 @@ static void xt_generator_dispose(xt_generator *gen) {
|
|
|
146
177
|
gen->fiber = NULL;
|
|
147
178
|
}
|
|
148
179
|
gen->caller_fiber = NULL;
|
|
180
|
+
/* `DeleteFiber` releases the stack: never scan it again. */
|
|
181
|
+
gen->stack = NULL;
|
|
182
|
+
gen->stack_size = 0;
|
|
149
183
|
#else
|
|
150
184
|
if (gen->stack) {
|
|
151
185
|
free(gen->stack);
|
|
@@ -158,9 +192,20 @@ static void xt_generator_resume(xt_generator *gen) {
|
|
|
158
192
|
if (!gen->booted) xt_generator_boot(gen);
|
|
159
193
|
xt_generator *previous = g_active_generator;
|
|
160
194
|
g_active_generator = gen;
|
|
195
|
+
/* Record where the current (calling) stack suspends before switching away.
|
|
196
|
+
* On Windows the first push happens inside the fiber once its stack bounds
|
|
197
|
+
* are known, so the caller's region must be marked suspended here, on the
|
|
198
|
+
* caller's own stack. */
|
|
199
|
+
xt_gc_suspend((uintptr_t)&gen);
|
|
200
|
+
if (gen->stack && gen->stack_size && !gen->stack_pushed) {
|
|
201
|
+
xt_gc_push_stack((uintptr_t)gen->stack, (uintptr_t)gen->stack + gen->stack_size);
|
|
202
|
+
gen->stack_pushed = 1;
|
|
203
|
+
}
|
|
161
204
|
void *caller_try = xt_try_mark();
|
|
162
205
|
xt_try_restore(gen->saved_try_top);
|
|
163
206
|
#ifdef _WIN32
|
|
207
|
+
_setjmp(gen->caller_regs);
|
|
208
|
+
gen->has_caller_regs = 1;
|
|
164
209
|
gen->caller_fiber = GetCurrentFiber();
|
|
165
210
|
SwitchToFiber(gen->fiber);
|
|
166
211
|
#else
|
|
@@ -168,15 +213,63 @@ static void xt_generator_resume(xt_generator *gen) {
|
|
|
168
213
|
#endif
|
|
169
214
|
gen->saved_try_top = xt_try_mark();
|
|
170
215
|
xt_try_restore(caller_try);
|
|
216
|
+
if (gen->stack_pushed) {
|
|
217
|
+
xt_gc_pop_stack();
|
|
218
|
+
gen->stack_pushed = 0;
|
|
219
|
+
}
|
|
171
220
|
if (gen->done) xt_generator_dispose(gen);
|
|
172
221
|
g_active_generator = previous;
|
|
173
222
|
}
|
|
174
223
|
|
|
224
|
+
/* The active generator is only reachable through this static pointer, and its
|
|
225
|
+
* private stack holds values that are invisible to a scan of the main stack. */
|
|
226
|
+
static void xt_generator_gc_scan_roots(void) {
|
|
227
|
+
if (!g_active_generator) return;
|
|
228
|
+
xt_gc_mark_header((xt_header *)g_active_generator);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
static void xt_generator_gc_register(void) {
|
|
232
|
+
static int registered = 0;
|
|
233
|
+
if (registered) return;
|
|
234
|
+
registered = 1;
|
|
235
|
+
xt_gc_register_root_provider(xt_generator_gc_scan_roots);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
void xt_generator_gc_trace(xt_header *header) {
|
|
239
|
+
xt_generator *gen = (xt_generator *)header;
|
|
240
|
+
size_t payload = header->size > sizeof(xt_header) ? header->size - sizeof(xt_header) : 0;
|
|
241
|
+
xt_gc_scan_region((const char *)header + sizeof(xt_header), payload);
|
|
242
|
+
if (gen->args && gen->argc > 0) {
|
|
243
|
+
xt_gc_scan_region(gen->args, sizeof(xt_value) * (size_t)gen->argc);
|
|
244
|
+
}
|
|
245
|
+
if (gen->stack && gen->stack_size) xt_gc_scan_region(gen->stack, gen->stack_size);
|
|
246
|
+
#ifndef _WIN32
|
|
247
|
+
/* Registers saved by `swapcontext` at the last yield are not on the stack. */
|
|
248
|
+
xt_gc_scan_region(&gen->ctx, sizeof(gen->ctx));
|
|
249
|
+
#else
|
|
250
|
+
if (gen->has_caller_regs) xt_gc_scan_region(&gen->caller_regs, sizeof(gen->caller_regs));
|
|
251
|
+
#endif
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
void xt_generator_gc_release(xt_header *header) {
|
|
255
|
+
xt_generator *gen = (xt_generator *)header;
|
|
256
|
+
free(gen->args);
|
|
257
|
+
#ifdef _WIN32
|
|
258
|
+
if (gen->fiber) DeleteFiber(gen->fiber);
|
|
259
|
+
#else
|
|
260
|
+
free(gen->stack);
|
|
261
|
+
#endif
|
|
262
|
+
}
|
|
263
|
+
|
|
175
264
|
xt_value xt_generator_new(xt_value function, xt_value thisValue, int32_t argc, xt_value *argv) {
|
|
176
265
|
xt_generator *gen = (xt_generator *)xt_alloc(sizeof(xt_generator), XT_OBJECT_KIND_GENERATOR);
|
|
177
266
|
gen->function = function;
|
|
178
267
|
gen->this_value = thisValue;
|
|
179
268
|
gen->argc = argc;
|
|
269
|
+
gen->args = NULL;
|
|
270
|
+
gen->stack = NULL;
|
|
271
|
+
gen->stack_size = 0;
|
|
272
|
+
gen->stack_pushed = 0;
|
|
180
273
|
gen->result = XT_UNDEFINED;
|
|
181
274
|
gen->sent = XT_UNDEFINED;
|
|
182
275
|
gen->iter_value = XT_UNDEFINED;
|
|
@@ -185,6 +278,7 @@ xt_value xt_generator_new(xt_value function, xt_value thisValue, int32_t argc, x
|
|
|
185
278
|
if (!gen->args) abort();
|
|
186
279
|
memcpy(gen->args, argv, sizeof(xt_value) * (size_t)argc);
|
|
187
280
|
}
|
|
281
|
+
xt_generator_gc_register();
|
|
188
282
|
return XT_FROM_PTR(XT_TAG_OBJECT, gen);
|
|
189
283
|
}
|
|
190
284
|
|
package/runtime/xt_loop.c
CHANGED
|
@@ -130,14 +130,22 @@ static int64_t xt_timer_next_due(void) {
|
|
|
130
130
|
static void xt_timer_compact(void) {
|
|
131
131
|
uint32_t write = 0;
|
|
132
132
|
for (uint32_t read = 0; read < xt_timer_count; read++) {
|
|
133
|
-
if (!xt_timers[read].active)
|
|
133
|
+
if (!xt_timers[read].active) {
|
|
134
|
+
free(xt_timers[read].args);
|
|
135
|
+
xt_timers[read].args = NULL;
|
|
136
|
+
xt_timers[read].argc = 0;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
134
139
|
if (write != read) xt_timers[write] = xt_timers[read];
|
|
135
140
|
write++;
|
|
136
141
|
}
|
|
137
142
|
xt_timer_count = write;
|
|
138
143
|
}
|
|
139
144
|
|
|
145
|
+
static void xt_loop_gc_register(void);
|
|
146
|
+
|
|
140
147
|
static uint64_t xt_timer_add(xt_value callback, int64_t delay_ms, int64_t interval_ms, int32_t argc, xt_value *argv) {
|
|
148
|
+
xt_loop_gc_register();
|
|
141
149
|
if (xt_timer_count >= xt_timer_capacity) {
|
|
142
150
|
xt_timer_capacity = xt_timer_capacity == 0 ? 16 : xt_timer_capacity * 2;
|
|
143
151
|
xt_timer *grown = (xt_timer *)realloc(xt_timers, sizeof(xt_timer) * xt_timer_capacity);
|
|
@@ -170,6 +178,31 @@ static int xt_timer_cancel(uint64_t id) {
|
|
|
170
178
|
return 0;
|
|
171
179
|
}
|
|
172
180
|
|
|
181
|
+
/* Watchers and pending timers hold values outside the value graph: the timer
|
|
182
|
+
* callbacks/arguments live in the flat array, and extension watchers keep an
|
|
183
|
+
* `xt_value *` box in their opaque userdata (the `xt_loop_add` convention). */
|
|
184
|
+
static void xt_loop_gc_scan_roots(void) {
|
|
185
|
+
for (int i = 0; i < xt_loop_watcher_count; i++) {
|
|
186
|
+
if (!xt_loop_watchers[i].active) continue;
|
|
187
|
+
xt_value *boxed = (xt_value *)xt_loop_watchers[i].userdata;
|
|
188
|
+
if (boxed) xt_gc_mark_value(*boxed);
|
|
189
|
+
}
|
|
190
|
+
for (uint32_t i = 0; i < xt_timer_count; i++) {
|
|
191
|
+
if (!xt_timers[i].active) continue;
|
|
192
|
+
xt_gc_mark_value(xt_timers[i].callback);
|
|
193
|
+
if (xt_timers[i].args && xt_timers[i].argc > 0) {
|
|
194
|
+
xt_gc_scan_region(xt_timers[i].args, sizeof(xt_value) * (size_t)xt_timers[i].argc);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
static void xt_loop_gc_register(void) {
|
|
200
|
+
static int registered = 0;
|
|
201
|
+
if (registered) return;
|
|
202
|
+
registered = 1;
|
|
203
|
+
xt_gc_register_root_provider(xt_loop_gc_scan_roots);
|
|
204
|
+
}
|
|
205
|
+
|
|
173
206
|
/* Invoke every timer whose deadline has passed. Timers scheduled by a callback
|
|
174
207
|
* (or by a microtask) are left for the next reactor iteration, so a recursive
|
|
175
208
|
* `setTimeout(fn, 0)` cannot spin forever inside a single call. */
|
|
@@ -232,6 +265,7 @@ xt_value xt_clear_interval(int32_t argc, xt_value *argv) {
|
|
|
232
265
|
|
|
233
266
|
int xt_loop_add(int fd, int events, xt_io_handler handler, void *userdata) {
|
|
234
267
|
if (fd < 0 || !handler) return -1;
|
|
268
|
+
xt_loop_gc_register();
|
|
235
269
|
xt_loop_watcher *existing = xt_loop_find(fd);
|
|
236
270
|
if (existing) {
|
|
237
271
|
existing->events = events;
|
package/runtime/xt_promise.c
CHANGED
|
@@ -63,6 +63,8 @@ static xt_value xt_promise_value(xt_promise *promise) {
|
|
|
63
63
|
return XT_FROM_PTR(XT_TAG_OBJECT, promise);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
static void xt_promise_gc_register(void);
|
|
67
|
+
|
|
66
68
|
static xt_promise *xt_promise_new(void) {
|
|
67
69
|
xt_promise *promise = (xt_promise *)xt_alloc(sizeof(xt_promise), XT_OBJECT_KIND_PROMISE);
|
|
68
70
|
promise->state = XT_PROMISE_PENDING;
|
|
@@ -70,9 +72,52 @@ static xt_promise *xt_promise_new(void) {
|
|
|
70
72
|
promise->reactions = NULL;
|
|
71
73
|
promise->reaction_count = 0;
|
|
72
74
|
promise->reaction_capacity = 0;
|
|
75
|
+
xt_promise_gc_register();
|
|
73
76
|
return promise;
|
|
74
77
|
}
|
|
75
78
|
|
|
79
|
+
/* Reactions are plain `malloc` blocks referenced from promises and from the
|
|
80
|
+
* microtask queue, so neither can be found by scanning the heap: the queue is
|
|
81
|
+
* a root provider and a promise traces the reactions it still owns. */
|
|
82
|
+
static void xt_promise_gc_scan_roots(void) {
|
|
83
|
+
for (uint32_t i = xt_microtask_head; i < xt_microtask_count; i++) {
|
|
84
|
+
xt_reaction *reaction = xt_microtasks[i];
|
|
85
|
+
if (!reaction) continue;
|
|
86
|
+
xt_gc_mark_header((xt_header *)reaction->promise);
|
|
87
|
+
xt_gc_mark_value(reaction->on_fulfilled);
|
|
88
|
+
xt_gc_mark_value(reaction->on_rejected);
|
|
89
|
+
xt_gc_mark_header((xt_header *)reaction->result);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static void xt_promise_gc_register(void) {
|
|
94
|
+
static int registered = 0;
|
|
95
|
+
if (registered) return;
|
|
96
|
+
registered = 1;
|
|
97
|
+
xt_gc_register_root_provider(xt_promise_gc_scan_roots);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
void xt_promise_gc_trace(xt_header *header) {
|
|
101
|
+
xt_promise *promise = (xt_promise *)header;
|
|
102
|
+
xt_gc_mark_value(promise->value);
|
|
103
|
+
for (uint32_t i = 0; i < promise->reaction_count; i++) {
|
|
104
|
+
xt_reaction *reaction = promise->reactions[i];
|
|
105
|
+
if (!reaction) continue;
|
|
106
|
+
xt_gc_mark_header((xt_header *)reaction->promise);
|
|
107
|
+
xt_gc_mark_value(reaction->on_fulfilled);
|
|
108
|
+
xt_gc_mark_value(reaction->on_rejected);
|
|
109
|
+
xt_gc_mark_header((xt_header *)reaction->result);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
void xt_promise_gc_release(xt_header *header) {
|
|
114
|
+
xt_promise *promise = (xt_promise *)header;
|
|
115
|
+
/* Pending reactions are owned solely by the promise; queued ones live in
|
|
116
|
+
* `xt_microtasks` and are freed as they run. */
|
|
117
|
+
for (uint32_t i = 0; i < promise->reaction_count; i++) free(promise->reactions[i]);
|
|
118
|
+
free(promise->reactions);
|
|
119
|
+
}
|
|
120
|
+
|
|
76
121
|
static void xt_promise_settle(xt_promise *promise, int state, xt_value value);
|
|
77
122
|
|
|
78
123
|
static void xt_promise_resolve_into(xt_promise *result, xt_value value) {
|
|
@@ -122,6 +167,7 @@ static xt_value xt_promise_run_reactions(void) {
|
|
|
122
167
|
} else {
|
|
123
168
|
xt_promise_settle(reaction->result, XT_PROMISE_REJECTED, promise->value);
|
|
124
169
|
}
|
|
170
|
+
free(reaction);
|
|
125
171
|
}
|
|
126
172
|
xt_microtask_head = 0;
|
|
127
173
|
xt_microtask_count = 0;
|
|
@@ -19,6 +19,7 @@ static int xt_error_is_family(const char *name) {
|
|
|
19
19
|
static xt_value xt_error_root_proto = XT_UNDEFINED;
|
|
20
20
|
|
|
21
21
|
static xt_value xt_error_proto_for(const char *name) {
|
|
22
|
+
xt_gc_add_root(&xt_error_root_proto);
|
|
22
23
|
if (!XT_IS_OBJECT(xt_error_root_proto)) {
|
|
23
24
|
xt_error_root_proto = xt_object_new();
|
|
24
25
|
xt_object_set(xt_error_root_proto, xt_string_from_cstr("name"),
|
|
@@ -326,8 +326,10 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
|
|
|
326
326
|
int32_t end = (int32_t)matches[0].rm_eo;
|
|
327
327
|
xt_regexp_append(&out, &length, &capacity, text->data + cursor, (size_t)(start - cursor));
|
|
328
328
|
if (useFunction) {
|
|
329
|
-
|
|
330
|
-
|
|
329
|
+
/* Accumulate in an `xt_array` so the earlier match/group strings stay
|
|
330
|
+
reachable while later groups (and the callback itself) allocate. */
|
|
331
|
+
xt_value argArray = xt_array_new(0, NULL);
|
|
332
|
+
xt_array_push(argArray, xt_string_new(text->data + start, (int64_t)(end - start)));
|
|
331
333
|
for (int g = 1; g <= jsTotal; g++) {
|
|
332
334
|
xt_value groupValue = XT_UNDEFINED;
|
|
333
335
|
for (int i = 0; i < groupCount; i++) {
|
|
@@ -337,12 +339,11 @@ xt_value xt_regexp_replace(xt_value value, xt_value regexpValue, xt_value replac
|
|
|
337
339
|
}
|
|
338
340
|
break;
|
|
339
341
|
}
|
|
340
|
-
|
|
342
|
+
xt_array_push(argArray, groupValue);
|
|
341
343
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
xt_value produced = xt_closure_call(replacementValue, jsTotal + 3,
|
|
345
|
-
free(args);
|
|
344
|
+
xt_array_push(argArray, xt_number((double)start));
|
|
345
|
+
xt_array_push(argArray, xt_to_string(value));
|
|
346
|
+
xt_value produced = xt_closure_call(replacementValue, jsTotal + 3, xt_as_array(argArray)->items);
|
|
346
347
|
xt_string *producedText = xt_as_string(xt_to_string(produced));
|
|
347
348
|
if (producedText) xt_regexp_append(&out, &length, &capacity, producedText->data, producedText->length);
|
|
348
349
|
} else {
|
|
@@ -39,7 +39,7 @@ static int xt_is_iterator_state(xt_value value) {
|
|
|
39
39
|
* any other object with a `Symbol.iterator` method is wrapped. */
|
|
40
40
|
xt_value xt_iter_open(xt_value value) {
|
|
41
41
|
if (XT_IS_ARRAY(value) || XT_IS_STRING(value) || xt_is_map(value) || xt_is_set(value) ||
|
|
42
|
-
xt_is_generator(value))
|
|
42
|
+
xt_is_generator(value) || xt_typed_array_kind_of(value) >= 0)
|
|
43
43
|
return value;
|
|
44
44
|
if (XT_IS_OBJECT(value)) {
|
|
45
45
|
xt_value method = xt_object_get(value, xt_symbol_well_known("iterator"));
|