xbintsc 0.1.8 → 0.1.9
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/dist/src/binder/binder.d.ts +35 -1
- package/dist/src/binder/binder.js +138 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/codegen/generator/calls.d.ts +4 -0
- package/dist/src/codegen/generator/calls.js +104 -32
- package/dist/src/codegen/generator/calls.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +2 -0
- package/dist/src/codegen/generator/context.js +36 -0
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions.d.ts +4 -1
- package/dist/src/codegen/generator/expressions.js +73 -2
- package/dist/src/codegen/generator/expressions.js.map +1 -1
- package/dist/src/codegen/generator/module.d.ts +5 -1
- package/dist/src/codegen/generator/module.js +106 -6
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.d.ts +2 -0
- package/dist/src/codegen/generator/state.js +41 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements.js +9 -1
- package/dist/src/codegen/generator/statements.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +4 -0
- package/dist/src/codegen/generator/tables.js +33 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.js +23 -7
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +24 -0
- package/dist/src/driver/modules.js +347 -0
- package/dist/src/driver/modules.js.map +1 -0
- package/dist/src/parser/expressions.js +3 -0
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +1 -1
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +86 -0
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/package.json +1 -1
- package/runtime/rt.h +55 -4
- package/runtime/rt_internal.h +63 -0
- package/runtime/xt_builtins.c +47 -1
- package/runtime/xt_containers.c +95 -8
- package/runtime/xt_io.c +113 -0
- package/runtime/xt_promise.c +292 -0
- package/runtime/xt_stdlib.c +59 -5
- package/runtime/xt_stdlib2.c +1043 -0
- package/src/binder/binder.ts +196 -0
- package/src/codegen/generator/calls.ts +111 -33
- package/src/codegen/generator/context.ts +35 -1
- package/src/codegen/generator/expressions.ts +79 -2
- package/src/codegen/generator/module.ts +111 -6
- package/src/codegen/generator/state.ts +43 -0
- package/src/codegen/generator/statements.ts +7 -1
- package/src/codegen/generator/tables.ts +35 -0
- package/src/driver/compiler.ts +28 -7
- package/src/driver/modules.ts +377 -0
- package/src/parser/expressions.ts +3 -0
package/runtime/xt_containers.c
CHANGED
|
@@ -28,14 +28,30 @@ static void xt_object_reserve(xt_object *obj, uint32_t needed) {
|
|
|
28
28
|
obj->capacity = capacity;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
xt_value xt_object_new(void) {
|
|
31
|
+
xt_value xt_object_new(void) { return xt_object_new_with_proto(XT_UNDEFINED); }
|
|
32
|
+
|
|
33
|
+
xt_value xt_object_new_with_proto(xt_value proto) {
|
|
32
34
|
xt_object *obj = (xt_object *)xt_alloc(sizeof(xt_object), XT_OBJECT_KIND_OBJECT);
|
|
33
35
|
obj->count = 0;
|
|
34
36
|
obj->capacity = 0;
|
|
35
37
|
obj->properties = NULL;
|
|
38
|
+
obj->prototype = proto;
|
|
39
|
+
obj->frozen = 0;
|
|
40
|
+
obj->is_array_base = 0;
|
|
36
41
|
return XT_FROM_PTR(XT_TAG_OBJECT, obj);
|
|
37
42
|
}
|
|
38
43
|
|
|
44
|
+
xt_object *xt_as_object(xt_value value) {
|
|
45
|
+
if (XT_IS_OBJECT(value)) return (xt_object *)XT_GET_PTR(value);
|
|
46
|
+
if (XT_IS_FUNCTION(value)) {
|
|
47
|
+
/* Functions carry their own property bag (used for static members). */
|
|
48
|
+
xt_function *fn = (xt_function *)XT_GET_PTR(value);
|
|
49
|
+
if (!fn->properties) fn->properties = (xt_object *)XT_GET_PTR(xt_object_new_with_proto(XT_UNDEFINED));
|
|
50
|
+
return fn->properties;
|
|
51
|
+
}
|
|
52
|
+
return NULL;
|
|
53
|
+
}
|
|
54
|
+
|
|
39
55
|
static xt_property *xt_object_find(xt_object *obj, xt_string *key) {
|
|
40
56
|
for (uint32_t i = 0; i < obj->count; i++) {
|
|
41
57
|
if (xt_string_equals(obj->properties[i].key, key)) return &obj->properties[i];
|
|
@@ -43,17 +59,30 @@ static xt_property *xt_object_find(xt_object *obj, xt_string *key) {
|
|
|
43
59
|
return NULL;
|
|
44
60
|
}
|
|
45
61
|
|
|
62
|
+
xt_property *xt_object_find_property(xt_object *obj, xt_string *key) {
|
|
63
|
+
return obj ? xt_object_find(obj, key) : NULL;
|
|
64
|
+
}
|
|
65
|
+
|
|
46
66
|
xt_value xt_object_get(xt_value value, xt_value key) {
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
xt_object *obj = xt_as_object(value);
|
|
68
|
+
if (!obj) return XT_UNDEFINED;
|
|
49
69
|
xt_value keyString = xt_to_string(key);
|
|
50
|
-
|
|
51
|
-
|
|
70
|
+
xt_string *k = xt_as_string(keyString);
|
|
71
|
+
/* Walk the prototype chain like JavaScript property lookup. */
|
|
72
|
+
xt_object *cur = obj;
|
|
73
|
+
while (cur) {
|
|
74
|
+
xt_property *prop = xt_object_find(cur, k);
|
|
75
|
+
if (prop) return prop->value;
|
|
76
|
+
if (!XT_IS_OBJECT(cur->prototype)) break;
|
|
77
|
+
cur = (xt_object *)XT_GET_PTR(cur->prototype);
|
|
78
|
+
}
|
|
79
|
+
return XT_UNDEFINED;
|
|
52
80
|
}
|
|
53
81
|
|
|
54
82
|
xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
|
|
55
|
-
|
|
56
|
-
|
|
83
|
+
xt_object *obj = xt_as_object(value);
|
|
84
|
+
if (!obj) return newValue;
|
|
85
|
+
if (obj->frozen) return newValue;
|
|
57
86
|
xt_value keyString = xt_to_string(key);
|
|
58
87
|
xt_property *prop = xt_object_find(obj, xt_as_string(keyString));
|
|
59
88
|
if (prop) {
|
|
@@ -67,6 +96,48 @@ xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
|
|
|
67
96
|
return newValue;
|
|
68
97
|
}
|
|
69
98
|
|
|
99
|
+
xt_value xt_object_get_prototype(xt_value value) {
|
|
100
|
+
xt_object *obj = xt_as_object(value);
|
|
101
|
+
if (obj) return obj->prototype;
|
|
102
|
+
return XT_UNDEFINED;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
xt_value xt_object_set_prototype(xt_value value, xt_value proto) {
|
|
106
|
+
xt_object *obj = xt_as_object(value);
|
|
107
|
+
if (obj && !obj->frozen) obj->prototype = proto;
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
xt_value xt_object_freeze(xt_value value) {
|
|
112
|
+
xt_object *obj = xt_as_object(value);
|
|
113
|
+
if (obj) obj->frozen = 1;
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
int xt_object_is_frozen(xt_value value) {
|
|
118
|
+
xt_object *obj = xt_as_object(value);
|
|
119
|
+
return obj ? obj->frozen : 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
xt_value xt_object_has_own(xt_value value, xt_value key) {
|
|
123
|
+
xt_object *obj = xt_as_object(value);
|
|
124
|
+
if (!obj) return XT_FALSE;
|
|
125
|
+
xt_value keyString = xt_to_string(key);
|
|
126
|
+
return xt_bool(xt_object_find(obj, xt_as_string(keyString)) != NULL);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
xt_value xt_object_from_entries(xt_value entries) {
|
|
130
|
+
xt_value result = xt_object_new();
|
|
131
|
+
xt_array *array = xt_as_array(entries);
|
|
132
|
+
if (!array) return result;
|
|
133
|
+
for (uint32_t i = 0; i < array->length; i++) {
|
|
134
|
+
xt_array *pair = xt_as_array(array->items[i]);
|
|
135
|
+
if (!pair || pair->length < 2) continue;
|
|
136
|
+
xt_object_set(result, pair->items[0], pair->items[1]);
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
|
|
70
141
|
xt_value xt_object_has(xt_value value, xt_value key) {
|
|
71
142
|
if (!XT_IS_OBJECT(value)) return XT_FALSE;
|
|
72
143
|
xt_object *obj = (xt_object *)XT_GET_PTR(value);
|
|
@@ -190,7 +261,22 @@ xt_value xt_array_spread(xt_value target, xt_value source) {
|
|
|
190
261
|
|
|
191
262
|
xt_value xt_get(xt_value target, xt_value key) {
|
|
192
263
|
if (XT_IS_ARRAY(target)) return xt_array_get(target, key);
|
|
193
|
-
if (
|
|
264
|
+
if (XT_IS_FUNCTION(target)) return xt_object_get(target, key);
|
|
265
|
+
if (XT_IS_OBJECT(target)) {
|
|
266
|
+
/* Map/Set expose `.size`; promises, dates and regexps have their own
|
|
267
|
+
* getters implemented in the standard library. */
|
|
268
|
+
xt_object *obj = (xt_object *)XT_GET_PTR(target);
|
|
269
|
+
if (obj->header.kind == XT_OBJECT_KIND_MAP || obj->header.kind == XT_OBJECT_KIND_SET) {
|
|
270
|
+
if (XT_IS_STRING(key)) {
|
|
271
|
+
xt_string *k = xt_as_string(key);
|
|
272
|
+
if (k->length == 4 && memcmp(k->data, "size", 4) == 0) {
|
|
273
|
+
int32_t size = obj->header.kind == XT_OBJECT_KIND_MAP ? xt_map_size(target) : xt_set_size(target);
|
|
274
|
+
return xt_number((double)size);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return xt_object_get(target, key);
|
|
279
|
+
}
|
|
194
280
|
if (XT_IS_STRING(target)) {
|
|
195
281
|
/* Property access on a string: `.length` and numeric indexing. */
|
|
196
282
|
xt_string *s = xt_as_string(target);
|
|
@@ -207,6 +293,7 @@ xt_value xt_get(xt_value target, xt_value key) {
|
|
|
207
293
|
|
|
208
294
|
xt_value xt_set(xt_value target, xt_value key, xt_value value) {
|
|
209
295
|
if (XT_IS_ARRAY(target)) return xt_array_set(target, key, value);
|
|
296
|
+
if (XT_IS_FUNCTION(target)) return xt_object_set(target, key, value);
|
|
210
297
|
if (XT_IS_OBJECT(target)) return xt_object_set(target, key, value);
|
|
211
298
|
return value;
|
|
212
299
|
}
|
package/runtime/xt_io.c
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#include <stdio.h>
|
|
13
13
|
#include <stdlib.h>
|
|
14
14
|
#include <string.h>
|
|
15
|
+
#include <time.h>
|
|
15
16
|
|
|
16
17
|
/* ------------------------------------------------------------------------- */
|
|
17
18
|
/* Exceptions and output */
|
|
@@ -131,3 +132,115 @@ static void xt_console_write(int32_t argc, xt_value *argv, FILE *out) {
|
|
|
131
132
|
void xt_console_info(int32_t argc, xt_value *argv) { xt_console_write(argc, argv, stdout); }
|
|
132
133
|
void xt_console_warn(int32_t argc, xt_value *argv) { xt_console_write(argc, argv, stderr); }
|
|
133
134
|
void xt_console_error(int32_t argc, xt_value *argv) { xt_console_write(argc, argv, stderr); }
|
|
135
|
+
|
|
136
|
+
xt_value xt_console_dir(int32_t argc, xt_value *argv) {
|
|
137
|
+
xt_console_write(argc, argv, stdout);
|
|
138
|
+
return XT_UNDEFINED;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
xt_value xt_console_trace(int32_t argc, xt_value *argv) {
|
|
142
|
+
fputs("Trace", stderr);
|
|
143
|
+
for (int32_t i = 0; i < argc; i++) {
|
|
144
|
+
fputc(' ', stderr);
|
|
145
|
+
xt_inspect(argv[i], stderr);
|
|
146
|
+
}
|
|
147
|
+
fputc('\n', stderr);
|
|
148
|
+
return XT_UNDEFINED;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
xt_value xt_console_assert(int32_t argc, xt_value *argv) {
|
|
152
|
+
if (argc == 0 || !xt_truthy(argv[0])) {
|
|
153
|
+
fputs("Assertion failed", stderr);
|
|
154
|
+
if (argc > 1) fputs(":", stderr);
|
|
155
|
+
for (int32_t i = 1; i < argc; i++) {
|
|
156
|
+
fputc(' ', stderr);
|
|
157
|
+
xt_inspect(argv[i], stderr);
|
|
158
|
+
}
|
|
159
|
+
fputc('\n', stderr);
|
|
160
|
+
}
|
|
161
|
+
return XT_UNDEFINED;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#define XT_CONSOLE_MAX_LABELS 32
|
|
165
|
+
static char *xt_console_labels[XT_CONSOLE_MAX_LABELS];
|
|
166
|
+
static long xt_console_counts[XT_CONSOLE_MAX_LABELS];
|
|
167
|
+
static clock_t xt_console_timers[XT_CONSOLE_MAX_LABELS];
|
|
168
|
+
static int xt_console_label_count = 0;
|
|
169
|
+
static int xt_console_depth = 0;
|
|
170
|
+
|
|
171
|
+
static int xt_console_find_label(const char *label) {
|
|
172
|
+
for (int i = 0; i < xt_console_label_count; i++) {
|
|
173
|
+
if (strcmp(xt_console_labels[i], label) == 0) return i;
|
|
174
|
+
}
|
|
175
|
+
if (xt_console_label_count >= XT_CONSOLE_MAX_LABELS) return -1;
|
|
176
|
+
int index = xt_console_label_count++;
|
|
177
|
+
xt_console_labels[index] = (char *)malloc(strlen(label) + 1);
|
|
178
|
+
strcpy(xt_console_labels[index], label);
|
|
179
|
+
xt_console_counts[index] = 0;
|
|
180
|
+
xt_console_timers[index] = 0;
|
|
181
|
+
return index;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
static const char *xt_console_label(int32_t argc, xt_value *argv) {
|
|
185
|
+
if (argc == 0) return "default";
|
|
186
|
+
return xt_string_data(xt_to_string(argv[0]));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
xt_value xt_console_count(int32_t argc, xt_value *argv) {
|
|
190
|
+
const char *label = xt_console_label(argc, argv);
|
|
191
|
+
int index = xt_console_find_label(label ? label : "default");
|
|
192
|
+
if (index < 0) return XT_UNDEFINED;
|
|
193
|
+
xt_console_counts[index]++;
|
|
194
|
+
printf("%s: %ld\n", xt_console_labels[index], xt_console_counts[index]);
|
|
195
|
+
return XT_UNDEFINED;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
xt_value xt_console_count_reset(int32_t argc, xt_value *argv) {
|
|
199
|
+
const char *label = xt_console_label(argc, argv);
|
|
200
|
+
int index = xt_console_find_label(label ? label : "default");
|
|
201
|
+
if (index >= 0) xt_console_counts[index] = 0;
|
|
202
|
+
return XT_UNDEFINED;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
xt_value xt_console_group(int32_t argc, xt_value *argv) {
|
|
206
|
+
xt_console_write(argc, argv, stdout);
|
|
207
|
+
xt_console_depth++;
|
|
208
|
+
return XT_UNDEFINED;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
xt_value xt_console_group_end(int32_t argc, xt_value *argv) {
|
|
212
|
+
(void)argc;
|
|
213
|
+
(void)argv;
|
|
214
|
+
if (xt_console_depth > 0) xt_console_depth--;
|
|
215
|
+
return XT_UNDEFINED;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
xt_value xt_console_table(int32_t argc, xt_value *argv) {
|
|
219
|
+
xt_console_write(argc, argv, stdout);
|
|
220
|
+
return XT_UNDEFINED;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
xt_value xt_console_time(int32_t argc, xt_value *argv) {
|
|
224
|
+
const char *label = xt_console_label(argc, argv);
|
|
225
|
+
int index = xt_console_find_label(label ? label : "default");
|
|
226
|
+
if (index >= 0) xt_console_timers[index] = clock();
|
|
227
|
+
return XT_UNDEFINED;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
xt_value xt_console_time_log(int32_t argc, xt_value *argv) {
|
|
231
|
+
const char *label = xt_console_label(argc, argv);
|
|
232
|
+
int index = xt_console_find_label(label ? label : "default");
|
|
233
|
+
if (index >= 0 && xt_console_timers[index] != 0) {
|
|
234
|
+
double ms = (double)(clock() - xt_console_timers[index]) * 1000.0 / CLOCKS_PER_SEC;
|
|
235
|
+
printf("%s: %.3fms\n", xt_console_labels[index], ms);
|
|
236
|
+
}
|
|
237
|
+
return XT_UNDEFINED;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
xt_value xt_console_time_end(int32_t argc, xt_value *argv) {
|
|
241
|
+
xt_console_time_log(argc, argv);
|
|
242
|
+
const char *label = xt_console_label(argc, argv);
|
|
243
|
+
int index = xt_console_find_label(label ? label : "default");
|
|
244
|
+
if (index >= 0) xt_console_timers[index] = 0;
|
|
245
|
+
return XT_UNDEFINED;
|
|
246
|
+
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc runtime — promises and async support.
|
|
3
|
+
*
|
|
4
|
+
* There is no event loop in the runtime, so promises use a microtask queue
|
|
5
|
+
* that the compiler drains when the program body finishes (and that `await`
|
|
6
|
+
* drains on demand). This gives synchronous-looking `async`/`await` semantics
|
|
7
|
+
* for the common case where every awaited value is already settled.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "rt_internal.h"
|
|
11
|
+
|
|
12
|
+
#include <stdlib.h>
|
|
13
|
+
#include <string.h>
|
|
14
|
+
|
|
15
|
+
#define XT_PROMISE_PENDING 0
|
|
16
|
+
#define XT_PROMISE_FULFILLED 1
|
|
17
|
+
#define XT_PROMISE_REJECTED 2
|
|
18
|
+
|
|
19
|
+
typedef struct xt_promise xt_promise;
|
|
20
|
+
|
|
21
|
+
typedef struct {
|
|
22
|
+
xt_promise *promise;
|
|
23
|
+
xt_value on_fulfilled;
|
|
24
|
+
xt_value on_rejected;
|
|
25
|
+
xt_promise *result;
|
|
26
|
+
} xt_reaction;
|
|
27
|
+
|
|
28
|
+
struct xt_promise {
|
|
29
|
+
xt_header header;
|
|
30
|
+
int state;
|
|
31
|
+
xt_value value;
|
|
32
|
+
xt_reaction **reactions;
|
|
33
|
+
uint32_t reaction_count;
|
|
34
|
+
uint32_t reaction_capacity;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/* -- microtask queue ------------------------------------------------------ */
|
|
38
|
+
|
|
39
|
+
static xt_reaction **xt_microtasks = NULL;
|
|
40
|
+
static uint32_t xt_microtask_count = 0;
|
|
41
|
+
static uint32_t xt_microtask_head = 0;
|
|
42
|
+
static uint32_t xt_microtask_capacity = 0;
|
|
43
|
+
|
|
44
|
+
static void xt_microtask_push(xt_reaction *reaction) {
|
|
45
|
+
if (xt_microtask_count >= xt_microtask_capacity) {
|
|
46
|
+
xt_microtask_capacity = xt_microtask_capacity == 0 ? 16 : xt_microtask_capacity * 2;
|
|
47
|
+
xt_microtasks = (xt_reaction **)realloc(xt_microtasks, sizeof(xt_reaction *) * xt_microtask_capacity);
|
|
48
|
+
if (!xt_microtasks) abort();
|
|
49
|
+
}
|
|
50
|
+
xt_microtasks[xt_microtask_count++] = reaction;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
int xt_is_promise(xt_value value) {
|
|
54
|
+
return XT_IS_OBJECT(value) && ((xt_object *)XT_GET_PTR(value))->header.kind == XT_OBJECT_KIND_PROMISE;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static xt_promise *xt_as_promise(xt_value value) {
|
|
58
|
+
if (!xt_is_promise(value)) return NULL;
|
|
59
|
+
return (xt_promise *)XT_GET_PTR(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static xt_value xt_promise_value(xt_promise *promise) {
|
|
63
|
+
return XT_FROM_PTR(XT_TAG_OBJECT, promise);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static xt_promise *xt_promise_new(void) {
|
|
67
|
+
xt_promise *promise = (xt_promise *)xt_alloc(sizeof(xt_promise), XT_OBJECT_KIND_PROMISE);
|
|
68
|
+
promise->state = XT_PROMISE_PENDING;
|
|
69
|
+
promise->value = XT_UNDEFINED;
|
|
70
|
+
promise->reactions = NULL;
|
|
71
|
+
promise->reaction_count = 0;
|
|
72
|
+
promise->reaction_capacity = 0;
|
|
73
|
+
return promise;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static void xt_promise_settle(xt_promise *promise, int state, xt_value value);
|
|
77
|
+
|
|
78
|
+
static void xt_promise_resolve_into(xt_promise *result, xt_value value) {
|
|
79
|
+
xt_promise *other = xt_as_promise(value);
|
|
80
|
+
if (!other) {
|
|
81
|
+
xt_promise_settle(result, XT_PROMISE_FULFILLED, value);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (other->state == XT_PROMISE_PENDING) {
|
|
85
|
+
xt_reaction *reaction = (xt_reaction *)malloc(sizeof(xt_reaction));
|
|
86
|
+
if (!reaction) abort();
|
|
87
|
+
reaction->promise = other;
|
|
88
|
+
reaction->on_fulfilled = XT_UNDEFINED;
|
|
89
|
+
reaction->on_rejected = XT_UNDEFINED;
|
|
90
|
+
reaction->result = result;
|
|
91
|
+
if (other->reaction_count >= other->reaction_capacity) {
|
|
92
|
+
other->reaction_capacity = other->reaction_capacity == 0 ? 4 : other->reaction_capacity * 2;
|
|
93
|
+
other->reactions = (xt_reaction **)realloc(other->reactions, sizeof(xt_reaction *) * other->reaction_capacity);
|
|
94
|
+
if (!other->reactions) abort();
|
|
95
|
+
}
|
|
96
|
+
other->reactions[other->reaction_count++] = reaction;
|
|
97
|
+
} else {
|
|
98
|
+
xt_promise_settle(result, other->state, other->value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static void xt_promise_settle(xt_promise *promise, int state, xt_value value) {
|
|
103
|
+
if (promise->state != XT_PROMISE_PENDING) return;
|
|
104
|
+
promise->state = state;
|
|
105
|
+
promise->value = value;
|
|
106
|
+
for (uint32_t i = 0; i < promise->reaction_count; i++) xt_microtask_push(promise->reactions[i]);
|
|
107
|
+
promise->reaction_count = 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static xt_value xt_promise_run_reactions(void) {
|
|
111
|
+
while (xt_microtask_head < xt_microtask_count) {
|
|
112
|
+
xt_reaction *reaction = xt_microtasks[xt_microtask_head++];
|
|
113
|
+
xt_promise *promise = reaction->promise;
|
|
114
|
+
xt_value handler = promise->state == XT_PROMISE_FULFILLED ? reaction->on_fulfilled : reaction->on_rejected;
|
|
115
|
+
if (XT_IS_FUNCTION(handler)) {
|
|
116
|
+
xt_value arg = promise->value;
|
|
117
|
+
xt_value out;
|
|
118
|
+
out = xt_closure_call(handler, 1, &arg);
|
|
119
|
+
xt_promise_resolve_into(reaction->result, out);
|
|
120
|
+
} else if (promise->state == XT_PROMISE_FULFILLED) {
|
|
121
|
+
xt_promise_settle(reaction->result, XT_PROMISE_FULFILLED, promise->value);
|
|
122
|
+
} else {
|
|
123
|
+
xt_promise_settle(reaction->result, XT_PROMISE_REJECTED, promise->value);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
xt_microtask_head = 0;
|
|
127
|
+
xt_microtask_count = 0;
|
|
128
|
+
return XT_UNDEFINED;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
void xt_drain_microtasks(void) {
|
|
132
|
+
for (int guard = 0; guard < 100000; guard++) {
|
|
133
|
+
if (xt_microtask_head >= xt_microtask_count) break;
|
|
134
|
+
xt_promise_run_reactions();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
xt_value xt_await(xt_value value) {
|
|
139
|
+
xt_promise *promise = xt_as_promise(value);
|
|
140
|
+
if (!promise) return value;
|
|
141
|
+
int guard = 0;
|
|
142
|
+
while (promise->state == XT_PROMISE_PENDING && guard++ < 100000) xt_drain_microtasks();
|
|
143
|
+
if (promise->state == XT_PROMISE_REJECTED) {
|
|
144
|
+
xt_throw(promise->value);
|
|
145
|
+
return XT_UNDEFINED;
|
|
146
|
+
}
|
|
147
|
+
return promise->value;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* -- C-callable resolve / reject ------------------------------------------ */
|
|
151
|
+
|
|
152
|
+
static xt_value xt_promise_resolve_cb(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
153
|
+
(void)thisValue;
|
|
154
|
+
xt_value promiseValue = xt_closure_env(env, 0);
|
|
155
|
+
xt_promise *promise = xt_as_promise(promiseValue);
|
|
156
|
+
if (promise) xt_promise_resolve_into(promise, xt_arg_at(argc, argv, 0));
|
|
157
|
+
return XT_UNDEFINED;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static xt_value xt_promise_reject_cb(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
161
|
+
(void)thisValue;
|
|
162
|
+
xt_value promiseValue = xt_closure_env(env, 0);
|
|
163
|
+
xt_promise *promise = xt_as_promise(promiseValue);
|
|
164
|
+
if (promise) xt_promise_settle(promise, XT_PROMISE_REJECTED, xt_arg_at(argc, argv, 0));
|
|
165
|
+
return XT_UNDEFINED;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static xt_value xt_make_callback(xt_code_fn fn, xt_value promiseValue) {
|
|
169
|
+
xt_value env[1] = {promiseValue};
|
|
170
|
+
return xt_closure_new((void *)fn, 1, env);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
xt_value xt_promise_ctor(int32_t argc, xt_value *argv) {
|
|
174
|
+
xt_promise *promise = xt_promise_new();
|
|
175
|
+
xt_value promiseValue = xt_promise_value(promise);
|
|
176
|
+
xt_value executor = xt_arg_at(argc, argv, 0);
|
|
177
|
+
if (XT_IS_FUNCTION(executor)) {
|
|
178
|
+
xt_value args[2];
|
|
179
|
+
args[0] = xt_make_callback(xt_promise_resolve_cb, promiseValue);
|
|
180
|
+
args[1] = xt_make_callback(xt_promise_reject_cb, promiseValue);
|
|
181
|
+
xt_closure_call(executor, 2, args);
|
|
182
|
+
}
|
|
183
|
+
return promiseValue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
xt_value xt_promise_resolve(xt_value value) {
|
|
187
|
+
xt_promise *existing = xt_as_promise(value);
|
|
188
|
+
if (existing) return value;
|
|
189
|
+
xt_promise *promise = xt_promise_new();
|
|
190
|
+
xt_promise_resolve_into(promise, value);
|
|
191
|
+
return xt_promise_value(promise);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
xt_value xt_promise_reject(xt_value value) {
|
|
195
|
+
xt_promise *promise = xt_promise_new();
|
|
196
|
+
xt_promise_settle(promise, XT_PROMISE_REJECTED, value);
|
|
197
|
+
return xt_promise_value(promise);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
xt_value xt_promise_static(xt_value name, int32_t argc, xt_value *argv) {
|
|
201
|
+
const char *fn = xt_string_data(name);
|
|
202
|
+
if (!fn) return XT_UNDEFINED;
|
|
203
|
+
if (strcmp(fn, "resolve") == 0) return xt_promise_resolve(xt_arg_at(argc, argv, 0));
|
|
204
|
+
if (strcmp(fn, "reject") == 0) return xt_promise_reject(xt_arg_at(argc, argv, 0));
|
|
205
|
+
if (strcmp(fn, "all") == 0 || strcmp(fn, "allSettled") == 0 || strcmp(fn, "race") == 0 || strcmp(fn, "any") == 0) {
|
|
206
|
+
xt_value iterable = xt_arg_at(argc, argv, 0);
|
|
207
|
+
int all = strcmp(fn, "all") == 0 || strcmp(fn, "allSettled") == 0;
|
|
208
|
+
xt_promise *result = xt_promise_new();
|
|
209
|
+
xt_value values = xt_array_new(0, NULL);
|
|
210
|
+
if (XT_IS_ARRAY(iterable)) {
|
|
211
|
+
xt_array *array = xt_as_array(iterable);
|
|
212
|
+
int anyRejected = 0;
|
|
213
|
+
for (uint32_t i = 0; i < array->length; i++) {
|
|
214
|
+
xt_value item = array->items[i];
|
|
215
|
+
xt_promise *inner = xt_is_promise(item) ? xt_as_promise(item) : NULL;
|
|
216
|
+
int rejected = inner && inner->state == XT_PROMISE_REJECTED;
|
|
217
|
+
if (strcmp(fn, "allSettled") == 0) {
|
|
218
|
+
xt_value record = xt_object_new();
|
|
219
|
+
xt_object_set(record, xt_string_from_cstr("status"), xt_string_from_cstr(rejected ? "rejected" : "fulfilled"));
|
|
220
|
+
xt_object_set(record, xt_string_from_cstr(rejected ? "reason" : "value"), rejected ? inner->value : item);
|
|
221
|
+
xt_array_push(values, record);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (rejected) {
|
|
225
|
+
if (strcmp(fn, "any") == 0) {
|
|
226
|
+
anyRejected++;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
xt_promise_settle(result, XT_PROMISE_REJECTED, inner->value);
|
|
230
|
+
return xt_promise_value(result);
|
|
231
|
+
}
|
|
232
|
+
xt_value settled = xt_await(item);
|
|
233
|
+
if (strcmp(fn, "any") == 0 || strcmp(fn, "race") == 0) {
|
|
234
|
+
xt_promise_settle(result, XT_PROMISE_FULFILLED, settled);
|
|
235
|
+
return xt_promise_value(result);
|
|
236
|
+
}
|
|
237
|
+
xt_array_push(values, settled);
|
|
238
|
+
}
|
|
239
|
+
if (strcmp(fn, "any") == 0 && anyRejected > 0) {
|
|
240
|
+
xt_promise_settle(result, XT_PROMISE_REJECTED, xt_string_from_cstr("AggregateError: all promises rejected"));
|
|
241
|
+
return xt_promise_value(result);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (all) xt_promise_settle(result, XT_PROMISE_FULFILLED, values);
|
|
245
|
+
else xt_promise_settle(result, XT_PROMISE_REJECTED, xt_string_from_cstr("AggregateError: no promises"));
|
|
246
|
+
return xt_promise_value(result);
|
|
247
|
+
}
|
|
248
|
+
return XT_UNDEFINED;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* -- instance methods ----------------------------------------------------- */
|
|
252
|
+
|
|
253
|
+
xt_value xt_promise_method(xt_value target, const char *method, int32_t argc, xt_value *argv, int *handled) {
|
|
254
|
+
*handled = 1;
|
|
255
|
+
xt_promise *promise = xt_as_promise(target);
|
|
256
|
+
if (!promise) {
|
|
257
|
+
*handled = 0;
|
|
258
|
+
return XT_UNDEFINED;
|
|
259
|
+
}
|
|
260
|
+
if (strcmp(method, "then") == 0) {
|
|
261
|
+
xt_promise *result = xt_promise_new();
|
|
262
|
+
xt_reaction *reaction = (xt_reaction *)malloc(sizeof(xt_reaction));
|
|
263
|
+
if (!reaction) abort();
|
|
264
|
+
reaction->promise = promise;
|
|
265
|
+
reaction->on_fulfilled = xt_arg_at(argc, argv, 0);
|
|
266
|
+
reaction->on_rejected = xt_arg_at(argc, argv, 1);
|
|
267
|
+
reaction->result = result;
|
|
268
|
+
if (promise->state == XT_PROMISE_PENDING) {
|
|
269
|
+
if (promise->reaction_count >= promise->reaction_capacity) {
|
|
270
|
+
promise->reaction_capacity = promise->reaction_capacity == 0 ? 4 : promise->reaction_capacity * 2;
|
|
271
|
+
promise->reactions = (xt_reaction **)realloc(promise->reactions, sizeof(xt_reaction *) * promise->reaction_capacity);
|
|
272
|
+
if (!promise->reactions) abort();
|
|
273
|
+
}
|
|
274
|
+
promise->reactions[promise->reaction_count++] = reaction;
|
|
275
|
+
} else {
|
|
276
|
+
xt_microtask_push(reaction);
|
|
277
|
+
}
|
|
278
|
+
return xt_promise_value(result);
|
|
279
|
+
}
|
|
280
|
+
if (strcmp(method, "catch") == 0) {
|
|
281
|
+
xt_value args[2] = {XT_UNDEFINED, xt_arg_at(argc, argv, 0)};
|
|
282
|
+
return xt_promise_method(target, "then", 2, args, handled);
|
|
283
|
+
}
|
|
284
|
+
if (strcmp(method, "finally") == 0) {
|
|
285
|
+
xt_value fn = xt_arg_at(argc, argv, 0);
|
|
286
|
+
/* Approximate: run the handler, then propagate the original value. */
|
|
287
|
+
if (XT_IS_FUNCTION(fn)) xt_closure_call(fn, 0, NULL);
|
|
288
|
+
return target;
|
|
289
|
+
}
|
|
290
|
+
*handled = 0;
|
|
291
|
+
return XT_UNDEFINED;
|
|
292
|
+
}
|
package/runtime/xt_stdlib.c
CHANGED
|
@@ -379,7 +379,8 @@ static xt_value xt_string_concat_args(xt_value value, int32_t argc, xt_value *ar
|
|
|
379
379
|
|
|
380
380
|
/* -- method dispatch ------------------------------------------------------ */
|
|
381
381
|
|
|
382
|
-
static xt_value xt_array_method(xt_value target, const char *method, int32_t argc, xt_value *argv) {
|
|
382
|
+
static xt_value xt_array_method(xt_value target, const char *method, int32_t argc, xt_value *argv, int *handled) {
|
|
383
|
+
*handled = 1;
|
|
383
384
|
if (strcmp(method, "push") == 0) {
|
|
384
385
|
xt_value result = xt_number(0);
|
|
385
386
|
for (int32_t i = 0; i < argc; i++) result = xt_array_push(target, argv[i]);
|
|
@@ -410,10 +411,12 @@ static xt_value xt_array_method(xt_value target, const char *method, int32_t arg
|
|
|
410
411
|
}
|
|
411
412
|
return target;
|
|
412
413
|
}
|
|
414
|
+
*handled = 0;
|
|
413
415
|
return XT_UNDEFINED;
|
|
414
416
|
}
|
|
415
417
|
|
|
416
|
-
static xt_value xt_string_method(xt_value target, const char *method, int32_t argc, xt_value *argv) {
|
|
418
|
+
static xt_value xt_string_method(xt_value target, const char *method, int32_t argc, xt_value *argv, int *handled) {
|
|
419
|
+
*handled = 1;
|
|
417
420
|
if (strcmp(method, "charAt") == 0) return xt_string_char_at(target, xt_arg_at(argc, argv, 0));
|
|
418
421
|
if (strcmp(method, "charCodeAt") == 0) return xt_string_char_code_at(target, xt_arg_at(argc, argv, 0));
|
|
419
422
|
if (strcmp(method, "indexOf") == 0) return xt_string_index_of(target, xt_arg_at(argc, argv, 0), xt_arg_at(argc, argv, 1));
|
|
@@ -430,18 +433,52 @@ static xt_value xt_string_method(xt_value target, const char *method, int32_t ar
|
|
|
430
433
|
if (strcmp(method, "startsWith") == 0) return xt_string_starts_with(target, xt_arg_at(argc, argv, 0));
|
|
431
434
|
if (strcmp(method, "endsWith") == 0) return xt_string_ends_with(target, xt_arg_at(argc, argv, 0));
|
|
432
435
|
if (strcmp(method, "concat") == 0) return xt_string_concat_args(target, argc, argv);
|
|
436
|
+
*handled = 0;
|
|
433
437
|
return XT_UNDEFINED;
|
|
434
438
|
}
|
|
435
439
|
|
|
436
440
|
xt_value xt_call_method(xt_value target, xt_value name, int32_t argc, xt_value *argv) {
|
|
437
441
|
const char *method = xt_string_data(name);
|
|
438
442
|
if (!method) return XT_UNDEFINED;
|
|
443
|
+
int handled = 0;
|
|
439
444
|
if (XT_IS_OBJECT(target)) {
|
|
445
|
+
xt_object *obj = (xt_object *)XT_GET_PTR(target);
|
|
446
|
+
int kind = obj->header.kind;
|
|
447
|
+
if (kind == XT_OBJECT_KIND_PROMISE) {
|
|
448
|
+
xt_value result = xt_promise_method(target, method, argc, argv, &handled);
|
|
449
|
+
if (handled) return result;
|
|
450
|
+
}
|
|
451
|
+
if (kind != XT_OBJECT_KIND_OBJECT && kind != XT_OBJECT_KIND_FUNCTION) {
|
|
452
|
+
xt_value result = xt_ext_container_method(target, method, argc, argv, &handled);
|
|
453
|
+
if (handled) return result;
|
|
454
|
+
}
|
|
455
|
+
xt_value fn = xt_object_get(target, name);
|
|
456
|
+
if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
|
|
457
|
+
xt_value result = xt_ext_object_method(target, method, argc, argv, &handled);
|
|
458
|
+
if (handled) return result;
|
|
459
|
+
xt_throw(xt_string_from_cstr("TypeError: target does not have a callable property of that name"));
|
|
460
|
+
return XT_UNDEFINED;
|
|
461
|
+
}
|
|
462
|
+
if (XT_IS_FUNCTION(target)) {
|
|
440
463
|
xt_value fn = xt_object_get(target, name);
|
|
441
|
-
if (XT_IS_FUNCTION(fn)) return
|
|
464
|
+
if (XT_IS_FUNCTION(fn)) return xt_call_with_this(fn, target, argc, argv);
|
|
465
|
+
xt_throw(xt_string_from_cstr("TypeError: target does not have a callable property of that name"));
|
|
466
|
+
return XT_UNDEFINED;
|
|
467
|
+
}
|
|
468
|
+
if (XT_IS_ARRAY(target)) {
|
|
469
|
+
xt_value result = xt_ext_array_method(target, method, argc, argv, &handled);
|
|
470
|
+
if (handled) return result;
|
|
471
|
+
return xt_array_method(target, method, argc, argv, &handled);
|
|
472
|
+
}
|
|
473
|
+
if (XT_IS_STRING(target)) {
|
|
474
|
+
xt_value result = xt_ext_string_method(target, method, argc, argv, &handled);
|
|
475
|
+
if (handled) return result;
|
|
476
|
+
return xt_string_method(target, method, argc, argv, &handled);
|
|
477
|
+
}
|
|
478
|
+
if (XT_IS_NUMBER(target)) {
|
|
479
|
+
xt_value result = xt_ext_number_method(target, method, argc, argv, &handled);
|
|
480
|
+
if (handled) return result;
|
|
442
481
|
}
|
|
443
|
-
if (XT_IS_ARRAY(target)) return xt_array_method(target, method, argc, argv);
|
|
444
|
-
if (XT_IS_STRING(target)) return xt_string_method(target, method, argc, argv);
|
|
445
482
|
return XT_UNDEFINED;
|
|
446
483
|
}
|
|
447
484
|
|
|
@@ -466,6 +503,23 @@ xt_value xt_math_call(xt_value name, int32_t argc, xt_value *argv) {
|
|
|
466
503
|
if (strcmp(fn, "log") == 0) return xt_number(log(a));
|
|
467
504
|
if (strcmp(fn, "log2") == 0) return xt_number(log2(a));
|
|
468
505
|
if (strcmp(fn, "log10") == 0) return xt_number(log10(a));
|
|
506
|
+
if (strcmp(fn, "log1p") == 0) return xt_number(log1p(a));
|
|
507
|
+
if (strcmp(fn, "expm1") == 0) return xt_number(expm1(a));
|
|
508
|
+
if (strcmp(fn, "sinh") == 0) return xt_number(sinh(a));
|
|
509
|
+
if (strcmp(fn, "cosh") == 0) return xt_number(cosh(a));
|
|
510
|
+
if (strcmp(fn, "tanh") == 0) return xt_number(tanh(a));
|
|
511
|
+
if (strcmp(fn, "asinh") == 0) return xt_number(asinh(a));
|
|
512
|
+
if (strcmp(fn, "acosh") == 0) return xt_number(acosh(a));
|
|
513
|
+
if (strcmp(fn, "atanh") == 0) return xt_number(atanh(a));
|
|
514
|
+
if (strcmp(fn, "fround") == 0) return xt_number((double)(float)a);
|
|
515
|
+
if (strcmp(fn, "imul") == 0) return xt_number((double)((int32_t)xt_to_int32(xt_arg_at(argc, argv, 0)) * (int32_t)xt_to_int32(xt_arg_at(argc, argv, 1))));
|
|
516
|
+
if (strcmp(fn, "clz32") == 0) {
|
|
517
|
+
uint32_t v = (uint32_t)xt_to_int32(xt_arg_at(argc, argv, 0));
|
|
518
|
+
int count = 0;
|
|
519
|
+
if (v == 0) count = 32;
|
|
520
|
+
else { while (!(v & 0x80000000u)) { v <<= 1; count++; } }
|
|
521
|
+
return xt_number((double)count);
|
|
522
|
+
}
|
|
469
523
|
if (strcmp(fn, "sin") == 0) return xt_number(sin(a));
|
|
470
524
|
if (strcmp(fn, "cos") == 0) return xt_number(cos(a));
|
|
471
525
|
if (strcmp(fn, "tan") == 0) return xt_number(tan(a));
|