MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <stdlib.h>
2#include <string.h>
3
4#include "ant.h"
5#include "errors.h"
6#include "runtime.h"
7#include "internal.h"
8#include "ptr.h"
9#include "silver/engine.h"
10#include "descriptors.h"
11
12#include "modules/assert.h"
13#include "modules/iterator.h"
14#include "modules/symbol.h"
15
16enum {
17 WRAP_MAP = 0,
18 WRAP_FILTER = 1,
19 WRAP_TAKE = 2,
20 WRAP_DROP = 3,
21 WRAP_FLATMAP = 4,
22 WRAP_PASS = 5,
23 WRAP_FROM_SYNC = 6,
24};
25
26enum {
27 ASYNC_TERM_EVERY = 0,
28 ASYNC_TERM_SOME = 1,
29 ASYNC_TERM_FIND = 2,
30 ASYNC_TERM_FOREACH = 3,
31 ASYNC_TERM_REDUCE = 4,
32 ASYNC_TERM_TOARRAY = 5,
33};
34
35static ant_value_t g_wrap_iter_proto = 0;
36static ant_value_t g_async_wrap_iter_proto = 0;
37
38enum { ASYNC_TERMINAL_STATE_TAG = 0x41544954u }; // ATIT
39
40typedef struct {
41 double index;
42 int mode;
43 bool has_acc;
44} async_terminal_state_t;
45
46static inline ant_value_t call_indexed_callback(ant_t *js, ant_value_t fn, ant_value_t value, double index) {
47 ant_value_t call_args[2] = { value, js_mknum(index) };
48 return sv_vm_call(js->vm, js, fn, js_mkundef(), call_args, 2, NULL, false);
49}
50
51static inline ant_value_t set_iter_result(ant_t *js, ant_value_t result, ant_value_t value, bool done) {
52 js_set(js, result, "done", done ? js_true : js_false);
53 js_set(js, result, "value", value);
54 return result;
55}
56
57static ant_value_t wrap_iter_next(ant_t *js, ant_value_t *args, int nargs) {
58 ant_value_t self = js->this_val;
59 ant_value_t source = js_get_slot(self, SLOT_DATA);
60 ant_value_t state_v = js_get_slot(self, SLOT_ITER_STATE);
61
62 uint32_t state = (vtype(state_v) == T_NUM) ? (uint32_t)js_getnum(state_v) : 0;
63 uint32_t kind = ITER_STATE_KIND(state);
64 uint32_t count = ITER_STATE_INDEX(state);
65
66 ant_value_t result = js_mkobj(js);
67 ant_value_t cb = js_get_slot(self, SLOT_CTOR);
68 ant_value_t next_fn = js_getprop_fallback(js, source, "next");
69
70 for (;;) {
71 if (kind == WRAP_FLATMAP) {
72 ant_value_t inner = js_get_slot(self, SLOT_ENTRIES);
73
74 if (vtype(inner) != T_UNDEF) {
75 ant_value_t inner_next = js_getprop_fallback(js, inner, "next");
76 ant_value_t inner_step = sv_vm_call(js->vm, js, inner_next, inner, NULL, 0, NULL, false);
77 if (!is_err(inner_step)) {
78 ant_value_t inner_done = js_getprop_fallback(js, inner_step, "done");
79 if (!js_truthy(js, inner_done)) return set_iter_result(
80 js, result, js_getprop_fallback(js, inner_step, "value"
81 ), false);
82 }
83
84 js_set_slot(self, SLOT_ENTRIES, js_mkundef());
85 }}
86
87 ant_value_t step;
88 if (vtype(next_fn) == T_CFUNC) {
89 ant_value_t old_this = js->this_val;
90 js->this_val = source;
91 step = js_as_cfunc(next_fn)(js, NULL, 0);
92 js->this_val = old_this;
93 } else step = sv_vm_call(js->vm, js, next_fn, source, NULL, 0, NULL, false);
94
95 if (is_err(step)) return step;
96 ant_value_t done = js_getprop_fallback(js, step, "done");
97
98 if (js_truthy(js, done)) {
99 if (kind == WRAP_FLATMAP) {
100 ant_value_t inner = js_get_slot(self, SLOT_ENTRIES);
101 if (vtype(inner) != T_UNDEF) {
102 js_set_slot(self, SLOT_ENTRIES, js_mkundef());
103 }}
104
105 return set_iter_result(js, result, js_mkundef(), true);
106 }
107
108 ant_value_t value = js_getprop_fallback(js, step, "value");
109
110 switch (kind) {
111 case WRAP_MAP: {
112 ant_value_t out_val;
113 if (is_callable(cb)) {
114 out_val = call_indexed_callback(js, cb, value, (double)count);
115 if (is_err(out_val)) return out_val;
116 } else out_val = value;
117
118 count++;
119 js_set_slot(self, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count)));
120 return set_iter_result(js, result, out_val, false);
121 }
122
123 case WRAP_FILTER: {
124 ant_value_t test = call_indexed_callback(js, cb, value, (double)count);
125 if (is_err(test)) return test;
126 count++;
127 js_set_slot(self, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count)));
128 if (js_truthy(js, test)) {
129 return set_iter_result(js, result, value, false);
130 }
131 continue;
132 }
133
134 case WRAP_TAKE: {
135 uint32_t limit = (vtype(cb) == T_NUM) ? (uint32_t)js_getnum(cb) : 0;
136 if (count >= limit) {
137 return set_iter_result(js, result, js_mkundef(), true);
138 }
139 js_set_slot(self, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count + 1)));
140 return set_iter_result(js, result, value, false);
141 }
142
143 case WRAP_DROP: {
144 uint32_t limit = (vtype(cb) == T_NUM) ? (uint32_t)js_getnum(cb) : 0;
145 count++;
146 js_set_slot(self, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count)));
147 if (count <= limit) continue;
148 return set_iter_result(js, result, value, false);
149 }
150
151 case WRAP_FLATMAP: {
152 ant_value_t mapped = call_indexed_callback(js, cb, value, (double)count);
153 if (is_err(mapped)) return mapped;
154 count++;
155 js_set_slot(self, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count)));
156
157 ant_value_t iter_fn = js_get_sym(js, mapped, get_iterator_sym());
158 if (!is_callable(iter_fn)) {
159 return set_iter_result(js, result, mapped, false);
160 }
161
162 ant_value_t inner = sv_vm_call(js->vm, js, iter_fn, mapped, NULL, 0, NULL, false);
163 if (is_err(inner)) return inner;
164
165 ant_value_t inner_next = js_getprop_fallback(js, inner, "next");
166 ant_value_t inner_step = sv_vm_call(js->vm, js, inner_next, inner, NULL, 0, NULL, false);
167 if (is_err(inner_step)) return inner_step;
168 ant_value_t inner_done = js_getprop_fallback(js, inner_step, "done");
169 if (!js_truthy(js, inner_done)) {
170 js_set_slot_wb(js, self, SLOT_ENTRIES, inner);
171 return set_iter_result(js, result, js_getprop_fallback(js, inner_step, "value"), false);
172 }
173 continue;
174 }
175
176 default:
177 return set_iter_result(js, result, value, false);
178 }
179 }
180}
181
182static ant_value_t make_wrap_iter(ant_t *js, ant_value_t source, int kind, ant_value_t cb) {
183 ant_value_t iter = js_mkobj(js);
184
185 js_set_proto_init(iter, g_wrap_iter_proto);
186 js_set_slot_wb(js, iter, SLOT_DATA, source);
187 js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, 0)));
188 js_set_slot_wb(js, iter, SLOT_CTOR, cb);
189
190 return iter;
191}
192
193static ant_value_t get_source_iter(ant_t *js) {
194 ant_value_t self = js->this_val;
195 ant_value_t next = js_getprop_fallback(js, self, "next");
196 if (is_callable(next)) return self;
197
198 ant_value_t iter_fn = js_get_sym(js, self, get_iterator_sym());
199 if (!is_callable(iter_fn)) return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable");
200
201 return sv_vm_call(js->vm, js, iter_fn, self, NULL, 0, NULL, false);
202}
203
204static ant_value_t iter_make_helper(ant_t *js, int kind, ant_value_t cb) {
205 ant_value_t source = get_source_iter(js);
206 if (is_err(source)) return source;
207 return make_wrap_iter(js, source, kind, cb);
208}
209
210static ant_value_t iter_make_callable_helper(
211 ant_t *js,
212 ant_value_t *args,
213 int nargs,
214 int kind,
215 const char *method
216) {
217 if (nargs < 1 || !is_callable(args[0]))
218 return js_mkerr_typed(js, JS_ERR_TYPE, "%s requires a callable", method);
219 return iter_make_helper(js, kind, args[0]);
220}
221
222static ant_value_t iter_make_count_helper(
223 ant_t *js, ant_value_t *args,
224 int nargs, int kind,
225 const char *method
226) {
227 double limit = (nargs >= 1 && vtype(args[0]) == T_NUM) ? js_getnum(args[0]) : 0;
228 if (limit < 0) return js_mkerr(js, "%s requires a non-negative number", method);
229 return iter_make_helper(js, kind, js_mknum(limit));
230}
231
232static ant_value_t iter_map(ant_t *js, ant_value_t *args, int nargs) {
233 return iter_make_callable_helper(js, args, nargs, WRAP_MAP, "Iterator.prototype.map");
234}
235
236static ant_value_t iter_filter(ant_t *js, ant_value_t *args, int nargs) {
237 return iter_make_callable_helper(js, args, nargs, WRAP_FILTER, "Iterator.prototype.filter");
238}
239
240static ant_value_t iter_take(ant_t *js, ant_value_t *args, int nargs) {
241 return iter_make_count_helper(js, args, nargs, WRAP_TAKE, "Iterator.prototype.take");
242}
243
244static ant_value_t iter_drop(ant_t *js, ant_value_t *args, int nargs) {
245 return iter_make_count_helper(js, args, nargs, WRAP_DROP, "Iterator.prototype.drop");
246}
247
248static ant_value_t iter_flatMap(ant_t *js, ant_value_t *args, int nargs) {
249 return iter_make_callable_helper(js, args, nargs, WRAP_FLATMAP, "Iterator.prototype.flatMap");
250}
251
252static ant_value_t iter_every(ant_t *js, ant_value_t *args, int nargs) {
253 if (nargs < 1 || !is_callable(args[0]))
254 return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.every requires a callable");
255 ant_value_t fn = args[0];
256
257 js_iter_t it;
258 if (!js_iter_open(js, js->this_val, &it))
259 return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable");
260
261 ant_value_t value;
262 uint32_t counter = 0;
263 while (js_iter_next(js, &it, &value)) {
264 ant_value_t test = call_indexed_callback(js, fn, value, (double)counter++);
265 if (is_err(test)) { js_iter_close(js, &it); return test; }
266 if (!js_truthy(js, test)) { js_iter_close(js, &it); return js_false; }
267 }
268 return js_true;
269}
270
271static ant_value_t iter_some(ant_t *js, ant_value_t *args, int nargs) {
272 if (nargs < 1 || !is_callable(args[0]))
273 return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.some requires a callable");
274 ant_value_t fn = args[0];
275
276 js_iter_t it;
277 if (!js_iter_open(js, js->this_val, &it))
278 return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable");
279
280 ant_value_t value;
281 uint32_t counter = 0;
282 while (js_iter_next(js, &it, &value)) {
283 ant_value_t test = call_indexed_callback(js, fn, value, (double)counter++);
284 if (is_err(test)) { js_iter_close(js, &it); return test; }
285 if (js_truthy(js, test)) { js_iter_close(js, &it); return js_true; }
286 }
287 return js_false;
288}
289
290static ant_value_t iter_find(ant_t *js, ant_value_t *args, int nargs) {
291 if (nargs < 1 || !is_callable(args[0]))
292 return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.find requires a callable");
293 ant_value_t fn = args[0];
294
295 js_iter_t it;
296 if (!js_iter_open(js, js->this_val, &it))
297 return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable");
298
299 ant_value_t value;
300 uint32_t counter = 0;
301 while (js_iter_next(js, &it, &value)) {
302 ant_value_t test = call_indexed_callback(js, fn, value, (double)counter++);
303 if (is_err(test)) { js_iter_close(js, &it); return test; }
304 if (js_truthy(js, test)) { js_iter_close(js, &it); return value; }
305 }
306 return js_mkundef();
307}
308
309static ant_value_t iter_forEach(ant_t *js, ant_value_t *args, int nargs) {
310 if (nargs < 1 || !is_callable(args[0]))
311 return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.forEach requires a callable");
312 ant_value_t fn = args[0];
313
314 js_iter_t it;
315 if (!js_iter_open(js, js->this_val, &it))
316 return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable");
317
318 ant_value_t value;
319 uint32_t counter = 0;
320 while (js_iter_next(js, &it, &value)) {
321 ant_value_t r = call_indexed_callback(js, fn, value, (double)counter++);
322 if (is_err(r)) { js_iter_close(js, &it); return r; }
323 }
324 return js_mkundef();
325}
326
327static ant_value_t iter_reduce(ant_t *js, ant_value_t *args, int nargs) {
328 if (nargs < 1 || !is_callable(args[0]))
329 return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.prototype.reduce requires a callable");
330 ant_value_t fn = args[0];
331 bool has_init = (nargs >= 2);
332
333 js_iter_t it;
334 if (!js_iter_open(js, js->this_val, &it))
335 return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable");
336
337 ant_value_t acc = has_init ? args[1] : js_mkundef();
338 bool first = !has_init;
339 ant_value_t value;
340 uint32_t counter = 0;
341
342 while (js_iter_next(js, &it, &value)) {
343 if (first) { acc = value; first = false; counter++; continue; }
344 ant_value_t call_args[3] = { acc, value, js_mknum((double)counter++) };
345 acc = sv_vm_call(js->vm, js, fn, js_mkundef(), call_args, 3, NULL, false);
346 if (is_err(acc)) { js_iter_close(js, &it); return acc; }
347 }
348
349 if (first)
350 return js_mkerr_typed(js, JS_ERR_TYPE, "reduce of empty iterator with no initial value");
351 return acc;
352}
353
354static ant_value_t iter_toArray(ant_t *js, ant_value_t *args, int nargs) {
355 js_iter_t it;
356 if (!js_iter_open(js, js->this_val, &it))
357 return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable");
358
359 ant_value_t arr = js_mkarr(js);
360 ant_value_t value;
361 while (js_iter_next(js, &it, &value))
362 js_arr_push(js, arr, value);
363
364 return arr;
365}
366
367static ant_value_t iter_from(ant_t *js, ant_value_t *args, int nargs) {
368 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator.from requires an argument");
369 ant_value_t obj = args[0];
370
371 ant_value_t next = js_getprop_fallback(js, obj, "next");
372 if (is_callable(next)) {
373 return make_wrap_iter(js, obj, WRAP_MAP, js_mkundef());
374 }
375
376 ant_value_t iter_fn = js_get_sym(js, obj, get_iterator_sym());
377 if (!is_callable(iter_fn))
378 return js_mkerr_typed(js, JS_ERR_TYPE, "object is not iterable");
379
380 ant_value_t iterator = sv_vm_call(js->vm, js, iter_fn, obj, NULL, 0, NULL, false);
381 if (is_err(iterator)) return iterator;
382
383 return iterator;
384}
385
386static ant_value_t iter_ctor(ant_t *js, ant_value_t *args, int nargs) {
387 if (vtype(js->new_target) == T_UNDEF)
388 return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator is not directly constructable");
389
390 ant_value_t obj = js_mkobj(js);
391 ant_value_t proto = js_instance_proto_from_new_target(js, js->sym.iterator_proto);
392 if (is_object_type(proto)) js_set_proto_init(obj, proto);
393
394 return obj;
395}
396
397static ant_value_t async_iter_ctor(ant_t *js, ant_value_t *args, int nargs) {
398 if (vtype(js->new_target) == T_UNDEF)
399 return js_mkerr_typed(js, JS_ERR_TYPE, "AsyncIterator constructor requires 'new'");
400
401 ant_value_t obj = js_mkobj(js);
402 ant_value_t proto = js_instance_proto_from_new_target(js, js->sym.async_iterator_proto);
403 if (is_object_type(proto)) js_set_proto_init(obj, proto);
404
405 return obj;
406}
407
408static inline ant_value_t iter_result(ant_t *js, ant_value_t value, bool done) {
409 ant_value_t result = js_mkobj(js);
410 js_set(js, result, "done", done ? js_true : js_false);
411 js_set(js, result, "value", value);
412 return result;
413}
414
415static inline ant_value_t fulfilled_promise(ant_t *js, ant_value_t value) {
416 ant_value_t promise = js_mkpromise(js);
417 js_resolve_promise(js, promise, value);
418 return promise;
419}
420
421static inline ant_value_t rejected_promise(ant_t *js, ant_value_t reason) {
422 ant_value_t promise = js_mkpromise(js);
423 js_reject_promise(js, promise, reason);
424 return promise;
425}
426
427static inline ant_value_t promise_from_call_result(ant_t *js, ant_value_t result) {
428 if (vtype(result) == T_PROMISE) return result;
429 if (is_err(result)) return rejected_promise(js, result);
430 return fulfilled_promise(js, result);
431}
432
433static ant_value_t async_iter_call_method(
434 ant_t *js, ant_value_t receiver,
435 const char *name, ant_value_t *args, int nargs, bool *missing
436) {
437 ant_value_t fn = js_getprop_fallback(js, receiver, name);
438 if (missing) *missing = !is_callable(fn);
439 if (!is_callable(fn)) return js_mkundef();
440 return sv_vm_call(js->vm, js, fn, receiver, args, nargs, NULL, false);
441}
442
443static ant_value_t make_async_wrap_iter(ant_t *js, ant_value_t source, int kind, ant_value_t cb) {
444 ant_value_t iter = js_mkobj(js);
445 js_set_proto_init(iter, g_async_wrap_iter_proto);
446 js_set_slot_wb(js, iter, SLOT_DATA, source);
447 js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, 0)));
448 js_set_slot_wb(js, iter, SLOT_CTOR, cb);
449 js_set_slot(iter, SLOT_ENTRIES, js_mkundef());
450 return iter;
451}
452
453static ant_value_t async_wrap_advance(
454 ant_t *js,
455 ant_value_t iter,
456 ant_value_t promise
457);
458
459static ant_value_t async_wrap_handle_step(
460 ant_t *js,
461 ant_value_t iter,
462 ant_value_t promise,
463 ant_value_t step
464);
465
466static ant_value_t async_wrap_handle_callback_result(
467 ant_t *js,
468 ant_value_t iter,
469 ant_value_t promise,
470 uint32_t kind,
471 ant_value_t value,
472 ant_value_t result
473);
474
475static ant_value_t async_wrap_on_step(ant_t *js, ant_value_t *args, int nargs) {
476 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
477 ant_value_t iter = js_get(js, state, "iter");
478 ant_value_t promise = js_get(js, state, "promise");
479 ant_value_t step = nargs > 0 ? args[0] : js_mkundef();
480 return async_wrap_handle_step(js, iter, promise, step);
481}
482
483static ant_value_t async_wrap_on_reject(ant_t *js, ant_value_t *args, int nargs) {
484 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
485 ant_value_t promise = js_get(js, state, "promise");
486 js_reject_promise(js, promise, nargs > 0 ? args[0] : js_mkundef());
487 return js_mkundef();
488}
489
490static ant_value_t async_wrap_on_callback(ant_t *js, ant_value_t *args, int nargs) {
491 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
492 ant_value_t iter = js_get(js, state, "iter");
493 ant_value_t promise = js_get(js, state, "promise");
494 uint32_t kind = (uint32_t)js_getnum(js_get(js, state, "kind"));
495 ant_value_t value = js_get(js, state, "value");
496 ant_value_t result = nargs > 0 ? args[0] : js_mkundef();
497 return async_wrap_handle_callback_result(js, iter, promise, kind, value, result);
498}
499
500static ant_value_t async_wrap_on_sync_value(ant_t *js, ant_value_t *args, int nargs) {
501 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
502 ant_value_t promise = js_get(js, state, "promise");
503 bool done = js_truthy(js, js_get(js, state, "done"));
504 ant_value_t value = nargs > 0 ? args[0] : js_mkundef();
505 js_resolve_promise(js, promise, iter_result(js, value, done));
506 return js_mkundef();
507}
508
509static bool async_wrap_chain_step(ant_t *js, ant_value_t iter, ant_value_t promise, ant_value_t next_result) {
510 if (is_err(next_result)) {
511 js_reject_promise(js, promise, next_result);
512 return true;
513 }
514
515 if (vtype(next_result) != T_PROMISE) {
516 async_wrap_handle_step(js, iter, promise, next_result);
517 return true;
518 }
519
520 ant_value_t state = js_mkobj(js);
521 js_set(js, state, "iter", iter);
522 js_set(js, state, "promise", promise);
523 ant_value_t on_resolve = js_heavy_mkfun(js, async_wrap_on_step, state);
524 ant_value_t on_reject = js_heavy_mkfun(js, async_wrap_on_reject, state);
525 ant_value_t then_result = js_promise_then(js, next_result, on_resolve, on_reject);
526 promise_mark_handled(then_result);
527 return true;
528}
529
530static ant_value_t async_wrap_handle_inner_step(
531 ant_t *js,
532 ant_value_t iter,
533 ant_value_t promise,
534 ant_value_t step
535) {
536 if (!is_object_type(step)) {
537 js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "iterator result is not an object"));
538 return js_mkundef();
539 }
540
541 if (!js_truthy(js, js_getprop_fallback(js, step, "done"))) {
542 js_resolve_promise(js, promise, iter_result(js, js_getprop_fallback(js, step, "value"), false));
543 return js_mkundef();
544 }
545
546 js_set_slot(iter, SLOT_ENTRIES, js_mkundef());
547 async_wrap_advance(js, iter, promise);
548 return js_mkundef();
549}
550
551static ant_value_t async_wrap_on_inner_step(ant_t *js, ant_value_t *args, int nargs) {
552 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
553 ant_value_t iter = js_get(js, state, "iter");
554 ant_value_t promise = js_get(js, state, "promise");
555 ant_value_t step = nargs > 0 ? args[0] : js_mkundef();
556 return async_wrap_handle_inner_step(js, iter, promise, step);
557}
558
559static ant_value_t async_wrap_advance_inner(ant_t *js, ant_value_t iter, ant_value_t promise) {
560 ant_value_t inner = js_get_slot(iter, SLOT_ENTRIES);
561 if (vtype(inner) == T_UNDEF || vtype(inner) == T_NULL) return async_wrap_advance(js, iter, promise);
562
563 bool missing = false;
564 ant_value_t next_result = async_iter_call_method(js, inner, "next", NULL, 0, &missing);
565 if (missing) {
566 js_set_slot(iter, SLOT_ENTRIES, js_mkundef());
567 return async_wrap_advance(js, iter, promise);
568 }
569
570 if (is_err(next_result)) {
571 js_reject_promise(js, promise, next_result);
572 return js_mkundef();
573 }
574
575 if (vtype(next_result) == T_PROMISE) {
576 ant_value_t state = js_mkobj(js);
577 js_set(js, state, "iter", iter);
578 js_set(js, state, "promise", promise);
579 ant_value_t on_resolve = js_heavy_mkfun(js, async_wrap_on_inner_step, state);
580 ant_value_t on_reject = js_heavy_mkfun(js, async_wrap_on_reject, state);
581 ant_value_t then_result = js_promise_then(js, next_result, on_resolve, on_reject);
582 promise_mark_handled(then_result);
583 return js_mkundef();
584 }
585
586 return async_wrap_handle_inner_step(js, iter, promise, next_result);
587}
588
589static ant_value_t async_wrap_handle_step(
590 ant_t *js,
591 ant_value_t iter,
592 ant_value_t promise,
593 ant_value_t step
594) {
595 if (!is_object_type(step)) {
596 js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "iterator result is not an object"));
597 return js_mkundef();
598 }
599
600 bool done = js_truthy(js, js_getprop_fallback(js, step, "done"));
601 ant_value_t state_v = js_get_slot(iter, SLOT_ITER_STATE);
602
603 uint32_t state = (vtype(state_v) == T_NUM) ? (uint32_t)js_getnum(state_v) : 0;
604 uint32_t kind = ITER_STATE_KIND(state);
605 uint32_t count = ITER_STATE_INDEX(state);
606 ant_value_t value = js_getprop_fallback(js, step, "value");
607
608 if (kind == WRAP_FROM_SYNC && vtype(value) == T_PROMISE) {
609 ant_value_t state_obj = js_mkobj(js);
610 js_set(js, state_obj, "promise", promise);
611 js_set(js, state_obj, "done", done ? js_true : js_false);
612
613 ant_value_t on_resolve = js_heavy_mkfun(js, async_wrap_on_sync_value, state_obj);
614 ant_value_t on_reject = js_heavy_mkfun(js, async_wrap_on_reject, state_obj);
615 ant_value_t then_result = js_promise_then(js, value, on_resolve, on_reject);
616
617 promise_mark_handled(then_result);
618 return js_mkundef();
619 }
620
621 if (done) {
622 js_resolve_promise(
623 js, promise,
624 iter_result(js, kind == WRAP_FROM_SYNC ? value : js_mkundef(), true)
625 );
626 return js_mkundef();
627 }
628
629 ant_value_t cb = js_get_slot(iter, SLOT_CTOR);
630
631 switch (kind) {
632 case WRAP_MAP: {
633 ant_value_t mapped = call_indexed_callback(js, cb, value, (double)count);
634 if (is_err(mapped)) {
635 js_reject_promise(js, promise, mapped);
636 return js_mkundef();
637 }
638 js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count + 1)));
639 return async_wrap_handle_callback_result(js, iter, promise, kind, value, mapped);
640 }
641
642 case WRAP_FILTER: {
643 ant_value_t test = call_indexed_callback(js, cb, value, (double)count);
644 if (is_err(test)) {
645 js_reject_promise(js, promise, test);
646 return js_mkundef();
647 }
648 js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count + 1)));
649 return async_wrap_handle_callback_result(js, iter, promise, kind, value, test);
650 }
651
652 case WRAP_TAKE:
653 js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count + 1)));
654 js_resolve_promise(js, promise, iter_result(js, value, false));
655 return js_mkundef();
656
657 case WRAP_DROP: {
658 double limit = (vtype(cb) == T_NUM) ? js_getnum(cb) : 0;
659 js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count + 1)));
660 if ((double)(count + 1) <= limit) async_wrap_advance(js, iter, promise);
661 else js_resolve_promise(js, promise, iter_result(js, value, false));
662 return js_mkundef();
663 }
664
665 case WRAP_FLATMAP: {
666 ant_value_t mapped = call_indexed_callback(js, cb, value, (double)count);
667 if (is_err(mapped)) {
668 js_reject_promise(js, promise, mapped);
669 return js_mkundef();
670 }
671 js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, count + 1)));
672 return async_wrap_handle_callback_result(js, iter, promise, kind, value, mapped);
673 }
674
675 default:
676 js_resolve_promise(js, promise, iter_result(js, value, false));
677 return js_mkundef();
678 }
679}
680
681static ant_value_t async_wrap_handle_callback_result(
682 ant_t *js,
683 ant_value_t iter,
684 ant_value_t promise,
685 uint32_t kind,
686 ant_value_t value,
687 ant_value_t result
688) {
689 if (vtype(result) == T_PROMISE) {
690 ant_value_t state = js_mkobj(js);
691 js_set(js, state, "iter", iter);
692 js_set(js, state, "promise", promise);
693 js_set(js, state, "kind", js_mknum((double)kind));
694 js_set(js, state, "value", value);
695 ant_value_t on_resolve = js_heavy_mkfun(js, async_wrap_on_callback, state);
696 ant_value_t on_reject = js_heavy_mkfun(js, async_wrap_on_reject, state);
697 ant_value_t then_result = js_promise_then(js, result, on_resolve, on_reject);
698 promise_mark_handled(then_result);
699 return js_mkundef();
700 }
701
702 if (kind == WRAP_MAP) {
703 js_resolve_promise(js, promise, iter_result(js, result, false));
704 return js_mkundef();
705 }
706
707 if (kind == WRAP_FILTER) {
708 if (js_truthy(js, result)) js_resolve_promise(js, promise, iter_result(js, value, false));
709 else async_wrap_advance(js, iter, promise);
710 return js_mkundef();
711 }
712
713 if (kind == WRAP_FLATMAP) {
714 ant_value_t iter_fn = js_get_sym(js, result, get_asyncIterator_sym());
715 if (!is_callable(iter_fn)) iter_fn = js_get_sym(js, result, get_iterator_sym());
716 if (!is_callable(iter_fn)) {
717 js_resolve_promise(js, promise, iter_result(js, result, false));
718 return js_mkundef();
719 }
720
721 ant_value_t inner = sv_vm_call(js->vm, js, iter_fn, result, NULL, 0, NULL, false);
722 if (is_err(inner)) {
723 js_reject_promise(js, promise, inner);
724 return js_mkundef();
725 }
726 js_set_slot_wb(js, iter, SLOT_ENTRIES, inner);
727 return async_wrap_advance_inner(js, iter, promise);
728 }
729
730 js_resolve_promise(js, promise, iter_result(js, result, false));
731 return js_mkundef();
732}
733
734static ant_value_t async_wrap_advance(ant_t *js, ant_value_t iter, ant_value_t promise) {
735 ant_value_t state_v = js_get_slot(iter, SLOT_ITER_STATE);
736 uint32_t state = (vtype(state_v) == T_NUM) ? (uint32_t)js_getnum(state_v) : 0;
737 uint32_t kind = ITER_STATE_KIND(state);
738 ant_value_t cb = js_get_slot(iter, SLOT_CTOR);
739
740 if (kind == WRAP_TAKE) {
741 double limit = (vtype(cb) == T_NUM) ? js_getnum(cb) : 0;
742 if ((double)ITER_STATE_INDEX(state) >= limit) {
743 js_resolve_promise(js, promise, iter_result(js, js_mkundef(), true));
744 return js_mkundef();
745 }
746 }
747
748 if (kind == WRAP_FLATMAP && vtype(js_get_slot(iter, SLOT_ENTRIES)) != T_UNDEF)
749 return async_wrap_advance_inner(js, iter, promise);
750
751 ant_value_t source = js_get_slot(iter, SLOT_DATA);
752 bool missing = false;
753 ant_value_t next_result = async_iter_call_method(js, source, "next", NULL, 0, &missing);
754 if (missing) js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "object is not async iterable"));
755 else async_wrap_chain_step(js, iter, promise, next_result);
756 return js_mkundef();
757}
758
759static ant_value_t async_wrap_next(ant_t *js, ant_value_t *args, int nargs) {
760 (void)args; (void)nargs;
761 ant_value_t promise = js_mkpromise(js);
762 async_wrap_advance(js, js->this_val, promise);
763 return promise;
764}
765
766static ant_value_t async_wrap_return(ant_t *js, ant_value_t *args, int nargs) {
767 ant_value_t source = js_get_slot(js->this_val, SLOT_DATA);
768 bool missing = false;
769 ant_value_t result = async_iter_call_method(js, source, "return", args, nargs, &missing);
770 if (missing) return fulfilled_promise(js, iter_result(js, nargs > 0 ? args[0] : js_mkundef(), true));
771 ant_value_t state_v = js_get_slot(js->this_val, SLOT_ITER_STATE);
772 uint32_t state = (vtype(state_v) == T_NUM) ? (uint32_t)js_getnum(state_v) : 0;
773 if (ITER_STATE_KIND(state) == WRAP_FROM_SYNC) {
774 ant_value_t promise = js_mkpromise(js);
775 async_wrap_chain_step(js, js->this_val, promise, result);
776 return promise;
777 }
778 return promise_from_call_result(js, result);
779}
780
781static ant_value_t async_wrap_throw(ant_t *js, ant_value_t *args, int nargs) {
782 ant_value_t source = js_get_slot(js->this_val, SLOT_DATA);
783 bool missing = false;
784 ant_value_t result = async_iter_call_method(js, source, "throw", args, nargs, &missing);
785 if (missing) return rejected_promise(js, nargs > 0 ? args[0] : js_mkundef());
786 ant_value_t state_v = js_get_slot(js->this_val, SLOT_ITER_STATE);
787 uint32_t state = (vtype(state_v) == T_NUM) ? (uint32_t)js_getnum(state_v) : 0;
788 if (ITER_STATE_KIND(state) == WRAP_FROM_SYNC) {
789 ant_value_t promise = js_mkpromise(js);
790 async_wrap_chain_step(js, js->this_val, promise, result);
791 return promise;
792 }
793 return promise_from_call_result(js, result);
794}
795
796static ant_value_t async_iter_from(ant_t *js, ant_value_t *args, int nargs) {
797 if (nargs < 1 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL)
798 return js_mkerr_typed(js, JS_ERR_TYPE, "AsyncIterator.from requires an object");
799
800 ant_value_t obj = args[0];
801 ant_value_t iter_fn = js_get_sym(js, obj, get_asyncIterator_sym());
802 if (is_callable(iter_fn)) {
803 ant_value_t iterator = sv_vm_call(js->vm, js, iter_fn, obj, NULL, 0, NULL, false);
804 if (is_err(iterator)) return iterator;
805 return make_async_wrap_iter(js, iterator, WRAP_PASS, js_mkundef());
806 }
807
808 iter_fn = js_get_sym(js, obj, get_iterator_sym());
809 if (is_callable(iter_fn)) {
810 ant_value_t iterator = sv_vm_call(js->vm, js, iter_fn, obj, NULL, 0, NULL, false);
811 if (is_err(iterator)) return iterator;
812 return make_async_wrap_iter(js, iterator, WRAP_FROM_SYNC, js_mkundef());
813 }
814
815 ant_value_t next = js_getprop_fallback(js, obj, "next");
816 if (is_callable(next)) return make_async_wrap_iter(js, obj, WRAP_FROM_SYNC, js_mkundef());
817
818 return js_mkerr_typed(js, JS_ERR_TYPE, "object is not async iterable");
819}
820
821static ant_value_t get_async_source_iter(ant_t *js) {
822 ant_value_t self = js->this_val;
823 ant_value_t next = js_getprop_fallback(js, self, "next");
824 if (is_callable(next)) return self;
825
826 ant_value_t iter_fn = js_get_sym(js, self, get_asyncIterator_sym());
827 if (!is_callable(iter_fn)) return js_mkerr_typed(js, JS_ERR_TYPE, "object is not async iterable");
828
829 return sv_vm_call(js->vm, js, iter_fn, self, NULL, 0, NULL, false);
830}
831
832static ant_value_t async_iter_make_helper(ant_t *js, int kind, ant_value_t cb) {
833 ant_value_t source = get_async_source_iter(js);
834 if (is_err(source)) return source;
835 return make_async_wrap_iter(js, source, kind, cb);
836}
837
838static ant_value_t async_iter_make_callable_helper(
839 ant_t *js,
840 ant_value_t *args,
841 int nargs,
842 int kind,
843 const char *method
844) {
845 if (nargs < 1 || !is_callable(args[0]))
846 return js_mkerr_typed(js, JS_ERR_TYPE, "%s requires a callable", method);
847 return async_iter_make_helper(js, kind, args[0]);
848}
849
850static ant_value_t async_iter_make_count_helper(
851 ant_t *js,
852 ant_value_t *args,
853 int nargs,
854 int kind,
855 const char *method
856) {
857 double limit = (nargs >= 1 && vtype(args[0]) == T_NUM) ? js_getnum(args[0]) : 0;
858 if (limit < 0) return js_mkerr_typed(js, JS_ERR_TYPE, "%s requires a non-negative number", method);
859 return async_iter_make_helper(js, kind, js_mknum(limit));
860}
861
862static ant_value_t async_iter_map(ant_t *js, ant_value_t *args, int nargs) {
863 return async_iter_make_callable_helper(js, args, nargs, WRAP_MAP, "AsyncIterator.prototype.map");
864}
865
866static ant_value_t async_iter_filter(ant_t *js, ant_value_t *args, int nargs) {
867 return async_iter_make_callable_helper(js, args, nargs, WRAP_FILTER, "AsyncIterator.prototype.filter");
868}
869
870static ant_value_t async_iter_take(ant_t *js, ant_value_t *args, int nargs) {
871 return async_iter_make_count_helper(js, args, nargs, WRAP_TAKE, "AsyncIterator.prototype.take");
872}
873
874static ant_value_t async_iter_drop(ant_t *js, ant_value_t *args, int nargs) {
875 return async_iter_make_count_helper(js, args, nargs, WRAP_DROP, "AsyncIterator.prototype.drop");
876}
877
878static ant_value_t async_iter_flatMap(ant_t *js, ant_value_t *args, int nargs) {
879 return async_iter_make_callable_helper(js, args, nargs, WRAP_FLATMAP, "AsyncIterator.prototype.flatMap");
880}
881
882static ant_value_t async_terminal_advance(ant_t *js, ant_value_t state);
883static ant_value_t async_terminal_finish_callback(ant_t *js, ant_value_t state, ant_value_t result);
884static void async_terminal_close_and_reject(ant_t *js, ant_value_t state, ant_value_t reason);
885
886static void async_terminal_state_finalize(ant_t *js, ant_object_t *obj) {
887 free(obj->native.ptr);
888 obj->native.ptr = NULL;
889}
890
891static inline async_terminal_state_t *async_terminal_state(ant_value_t state) {
892 if (!js_check_native_tag(state, ASYNC_TERMINAL_STATE_TAG)) return NULL;
893 return (async_terminal_state_t *)js_get_native_ptr(state);
894}
895
896static inline int async_terminal_mode(ant_value_t state) {
897 async_terminal_state_t *st = async_terminal_state(state);
898 return st ? st->mode : ASYNC_TERM_TOARRAY;
899}
900
901static inline double async_terminal_index(ant_value_t state) {
902 async_terminal_state_t *st = async_terminal_state(state);
903 return st ? st->index : 0;
904}
905
906static inline bool async_terminal_has_acc(ant_value_t state) {
907 async_terminal_state_t *st = async_terminal_state(state);
908 return st && st->has_acc;
909}
910
911static ant_value_t async_terminal_on_reject(ant_t *js, ant_value_t *args, int nargs) {
912 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
913 ant_value_t promise = js_get_slot(state, SLOT_CTOR);
914 js_reject_promise(js, promise, nargs > 0 ? args[0] : js_mkundef());
915 return js_mkundef();
916}
917
918static ant_value_t async_terminal_on_callback_reject(ant_t *js, ant_value_t *args, int nargs) {
919 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
920 async_terminal_close_and_reject(js, state, nargs > 0 ? args[0] : js_mkundef());
921 return js_mkundef();
922}
923
924static ant_value_t async_terminal_on_callback(ant_t *js, ant_value_t *args, int nargs) {
925 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
926 ant_value_t result = nargs > 0 ? args[0] : js_mkundef();
927 return async_terminal_finish_callback(js, state, result);
928}
929
930static ant_value_t async_terminal_on_close(ant_t *js, ant_value_t *args, int nargs) {
931 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
932 ant_value_t promise = js_get_slot(state, SLOT_CTOR);
933 js_resolve_promise(js, promise, js_get_slot(state, SLOT_AUX));
934 return js_mkundef();
935}
936
937static ant_value_t async_terminal_on_close_reject(ant_t *js, ant_value_t *args, int nargs) {
938 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
939 ant_value_t promise = js_get_slot(state, SLOT_CTOR);
940 js_reject_promise(js, promise, js_get_slot(state, SLOT_AUX));
941 return js_mkundef();
942}
943
944static void async_terminal_close_and_resolve(ant_t *js, ant_value_t state, ant_value_t value) {
945 ant_value_t promise = js_get_slot(state, SLOT_CTOR);
946 ant_value_t iter = js_get_slot(state, SLOT_DATA);
947 js_set_slot_wb(js, state, SLOT_AUX, value);
948
949 bool missing = false;
950 ant_value_t result = async_iter_call_method(js, iter, "return", NULL, 0, &missing);
951 if (missing) {
952 js_resolve_promise(js, promise, value);
953 return;
954 }
955
956 if (is_err(result)) {
957 js_reject_promise(js, promise, result);
958 return;
959 }
960
961 if (vtype(result) == T_PROMISE) {
962 ant_value_t on_resolve = js_heavy_mkfun(js, async_terminal_on_close, state);
963 ant_value_t on_reject = js_heavy_mkfun(js, async_terminal_on_reject, state);
964 ant_value_t then_result = js_promise_then(js, result, on_resolve, on_reject);
965 promise_mark_handled(then_result);
966 return;
967 }
968
969 js_resolve_promise(js, promise, value);
970}
971
972static void async_terminal_close_and_reject(ant_t *js, ant_value_t state, ant_value_t reason) {
973 ant_value_t promise = js_get_slot(state, SLOT_CTOR);
974 ant_value_t iter = js_get_slot(state, SLOT_DATA);
975 js_set_slot_wb(js, state, SLOT_AUX, reason);
976
977 bool missing = false;
978 ant_value_t result = async_iter_call_method(js, iter, "return", NULL, 0, &missing);
979 if (missing) {
980 js_reject_promise(js, promise, reason);
981 return;
982 }
983
984 if (is_err(result)) {
985 js_reject_promise(js, promise, result);
986 return;
987 }
988
989 if (vtype(result) == T_PROMISE) {
990 ant_value_t on_resolve = js_heavy_mkfun(js, async_terminal_on_close_reject, state);
991 ant_value_t on_reject = js_heavy_mkfun(js, async_terminal_on_reject, state);
992 ant_value_t then_result = js_promise_then(js, result, on_resolve, on_reject);
993 promise_mark_handled(then_result);
994 return;
995 }
996
997 js_reject_promise(js, promise, reason);
998}
999
1000static bool async_terminal_apply_callback_result(ant_t *js, ant_value_t state, ant_value_t result) {
1001 int mode = async_terminal_mode(state);
1002
1003 if (mode == ASYNC_TERM_REDUCE) {
1004 js_set_slot_wb(js, state, SLOT_SET, result);
1005 async_terminal_state_t *st = async_terminal_state(state);
1006 if (st) st->has_acc = true;
1007 return true;
1008 }
1009
1010 switch (mode) {
1011 case ASYNC_TERM_EVERY:
1012 if (!js_truthy(js, result)) {
1013 async_terminal_close_and_resolve(js, state, js_false);
1014 return false;
1015 }
1016 return true;
1017 case ASYNC_TERM_SOME:
1018 if (js_truthy(js, result)) {
1019 async_terminal_close_and_resolve(js, state, js_true);
1020 return false;
1021 }
1022 return true;
1023 case ASYNC_TERM_FIND:
1024 if (js_truthy(js, result)) {
1025 async_terminal_close_and_resolve(js, state, js_get_slot(state, SLOT_AUX));
1026 return false;
1027 }
1028 return true;
1029 default:
1030 return true;
1031 }
1032}
1033
1034static bool async_terminal_handle_step(ant_t *js, ant_value_t state, ant_value_t step) {
1035 ant_value_t promise = js_get_slot(state, SLOT_CTOR);
1036
1037 if (!is_object_type(step)) {
1038 js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "iterator result is not an object"));
1039 return false;
1040 }
1041
1042 int mode = async_terminal_mode(state);
1043 ant_value_t done = js_getprop_fallback(js, step, "done");
1044 ant_value_t value = js_getprop_fallback(js, step, "value");
1045
1046 if (js_truthy(js, done)) {
1047 switch (mode) {
1048 case ASYNC_TERM_EVERY: js_resolve_promise(js, promise, js_true); break;
1049 case ASYNC_TERM_SOME: js_resolve_promise(js, promise, js_false); break;
1050
1051 case ASYNC_TERM_FIND: js_resolve_promise(js, promise, js_mkundef()); break;
1052 case ASYNC_TERM_FOREACH: js_resolve_promise(js, promise, js_mkundef()); break;
1053
1054 case ASYNC_TERM_REDUCE:
1055 if (!async_terminal_has_acc(state)) {
1056 js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "reduce of empty iterator with no initial value"));
1057 } else js_resolve_promise(js, promise, js_get_slot(state, SLOT_SET));
1058 break;
1059
1060 default:
1061 js_resolve_promise(js, promise, js_get_slot(state, SLOT_ENTRIES));
1062 break;
1063 }
1064
1065 return false;
1066 }
1067
1068 double index = async_terminal_index(state);
1069 async_terminal_state_t *st = async_terminal_state(state);
1070 if (st) st->index = index + 1;
1071
1072 if (mode == ASYNC_TERM_TOARRAY) {
1073 js_arr_push(js, js_get_slot(state, SLOT_ENTRIES), value);
1074 return true;
1075 }
1076
1077 ant_value_t fn = js_get_slot(state, SLOT_MAP);
1078 if (!is_callable(fn)) {
1079 js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "callback is not callable"));
1080 return false;
1081 }
1082
1083 if (mode == ASYNC_TERM_REDUCE) {
1084 if (!async_terminal_has_acc(state)) {
1085 js_set_slot_wb(js, state, SLOT_SET, value);
1086 if (st) st->has_acc = true;
1087 return true;
1088 }
1089
1090 ant_value_t call_args[3] = { js_get_slot(state, SLOT_SET), value, js_mknum(index) };
1091 ant_value_t next_acc = sv_vm_call(js->vm, js, fn, js_mkundef(), call_args, 3, NULL, false);
1092 if (is_err(next_acc)) {
1093 async_terminal_close_and_reject(js, state, next_acc);
1094 return false;
1095 }
1096
1097 if (vtype(next_acc) == T_PROMISE) {
1098 ant_value_t on_resolve = js_heavy_mkfun(js, async_terminal_on_callback, state);
1099 ant_value_t on_reject = js_heavy_mkfun(js, async_terminal_on_callback_reject, state);
1100 ant_value_t then_result = js_promise_then(js, next_acc, on_resolve, on_reject);
1101 promise_mark_handled(then_result);
1102 return false;
1103 }
1104 return async_terminal_apply_callback_result(js, state, next_acc);
1105 }
1106
1107 ant_value_t result = call_indexed_callback(js, fn, value, (double)index);
1108 if (is_err(result)) {
1109 async_terminal_close_and_reject(js, state, result);
1110 return false;
1111 }
1112
1113 js_set_slot_wb(js, state, SLOT_AUX, value);
1114 if (vtype(result) == T_PROMISE) {
1115 ant_value_t on_resolve = js_heavy_mkfun(js, async_terminal_on_callback, state);
1116 ant_value_t on_reject = js_heavy_mkfun(js, async_terminal_on_callback_reject, state);
1117 ant_value_t then_result = js_promise_then(js, result, on_resolve, on_reject);
1118 promise_mark_handled(then_result);
1119 return false;
1120 }
1121
1122 return async_terminal_apply_callback_result(js, state, result);
1123}
1124
1125static ant_value_t async_terminal_on_step(ant_t *js, ant_value_t *args, int nargs) {
1126 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
1127 ant_value_t step = nargs > 0 ? args[0] : js_mkundef();
1128 if (async_terminal_handle_step(js, state, step)) return async_terminal_advance(js, state);
1129 return js_mkundef();
1130}
1131
1132static ant_value_t async_terminal_finish_callback(
1133 ant_t *js,
1134 ant_value_t state,
1135 ant_value_t result
1136) {
1137 if (vtype(result) == T_PROMISE) {
1138 ant_value_t on_resolve = js_heavy_mkfun(js, async_terminal_on_callback, state);
1139 ant_value_t on_reject = js_heavy_mkfun(js, async_terminal_on_callback_reject, state);
1140 ant_value_t then_result = js_promise_then(js, result, on_resolve, on_reject);
1141 promise_mark_handled(then_result);
1142 return js_mkundef();
1143 }
1144
1145 if (async_terminal_apply_callback_result(js, state, result)) return async_terminal_advance(js, state);
1146 return js_mkundef();
1147}
1148
1149static ant_value_t async_terminal_advance(ant_t *js, ant_value_t state) {
1150for (;;) {
1151 ant_value_t iter = js_get_slot(state, SLOT_DATA);
1152 ant_value_t promise = js_get_slot(state, SLOT_CTOR);
1153 bool missing = false;
1154 ant_value_t next_result = async_iter_call_method(js, iter, "next", NULL, 0, &missing);
1155
1156 if (missing) {
1157 js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "object is not async iterable"));
1158 return js_mkundef();
1159 }
1160
1161 if (is_err(next_result)) {
1162 js_reject_promise(js, promise, next_result);
1163 return js_mkundef();
1164 }
1165
1166 if (vtype(next_result) == T_PROMISE) {
1167 ant_value_t on_resolve = js_heavy_mkfun(js, async_terminal_on_step, state);
1168 ant_value_t on_reject = js_heavy_mkfun(js, async_terminal_on_reject, state);
1169 ant_value_t then_result = js_promise_then(js, next_result, on_resolve, on_reject);
1170 promise_mark_handled(then_result);
1171 return js_mkundef();
1172 }
1173
1174 if (!async_terminal_handle_step(js, state, next_result)) return js_mkundef();
1175}}
1176
1177static ant_value_t async_iter_terminal(ant_t *js, ant_value_t *args, int nargs, int mode) {
1178 if (mode != ASYNC_TERM_TOARRAY && (nargs < 1 || !is_callable(args[0])))
1179 return js_mkerr_typed(js, JS_ERR_TYPE, "AsyncIterator helper requires a callable");
1180
1181 ant_value_t iter = get_async_source_iter(js);
1182 if (is_err(iter)) return rejected_promise(js, iter);
1183
1184 ant_value_t promise = js_mkpromise(js);
1185 ant_value_t state = js_mkobj(js);
1186 async_terminal_state_t *st = calloc(1, sizeof(*st));
1187
1188 if (!st) {
1189 js_reject_promise(js, promise, js_mkerr(js, "out of memory"));
1190 return promise;
1191 }
1192
1193 st->mode = mode;
1194 st->index = 0;
1195 st->has_acc = (mode == ASYNC_TERM_REDUCE && nargs > 1);
1196
1197 js_set_native_tag(state, ASYNC_TERMINAL_STATE_TAG);
1198 js_set_native_ptr(state, st);
1199 js_set_finalizer(state, async_terminal_state_finalize);
1200
1201 js_set_slot_wb(js, state, SLOT_DATA, iter);
1202 js_set_slot_wb(js, state, SLOT_CTOR, promise);
1203 js_set_slot_wb(js, state, SLOT_MAP, mode == ASYNC_TERM_TOARRAY ? js_mkundef() : args[0]);
1204 js_set_slot_wb(js, state, SLOT_ENTRIES, js_mkarr(js));
1205 js_set_slot_wb(js, state, SLOT_SET, (mode == ASYNC_TERM_REDUCE && nargs > 1) ? args[1] : js_mkundef());
1206 js_set_slot(state, SLOT_AUX, js_mkundef());
1207
1208 async_terminal_advance(js, state);
1209 return promise;
1210}
1211
1212static ant_value_t async_iter_every(ant_t *js, ant_value_t *args, int nargs) {
1213 return async_iter_terminal(js, args, nargs, ASYNC_TERM_EVERY);
1214}
1215
1216static ant_value_t async_iter_some(ant_t *js, ant_value_t *args, int nargs) {
1217 return async_iter_terminal(js, args, nargs, ASYNC_TERM_SOME);
1218}
1219
1220static ant_value_t async_iter_find(ant_t *js, ant_value_t *args, int nargs) {
1221 return async_iter_terminal(js, args, nargs, ASYNC_TERM_FIND);
1222}
1223
1224static ant_value_t async_iter_forEach(ant_t *js, ant_value_t *args, int nargs) {
1225 return async_iter_terminal(js, args, nargs, ASYNC_TERM_FOREACH);
1226}
1227
1228static ant_value_t async_iter_reduce(ant_t *js, ant_value_t *args, int nargs) {
1229 return async_iter_terminal(js, args, nargs, ASYNC_TERM_REDUCE);
1230}
1231
1232static ant_value_t async_iter_toArray(ant_t *js, ant_value_t *args, int nargs) {
1233 return async_iter_terminal(js, args, nargs, ASYNC_TERM_TOARRAY);
1234}
1235
1236void init_iterator_module(void) {
1237 ant_t *js = rt->js;
1238 ant_value_t g = js_glob(js);
1239 ant_value_t iter_proto = js->sym.iterator_proto;
1240
1241 g_wrap_iter_proto = js_mkobj(js);
1242 js_set_proto_init(g_wrap_iter_proto, iter_proto);
1243 js_set(js, g_wrap_iter_proto, "next", js_mkfun(wrap_iter_next));
1244
1245 js_set(js, iter_proto, "map", js_mkfun(iter_map));
1246 js_set(js, iter_proto, "filter", js_mkfun(iter_filter));
1247 js_set(js, iter_proto, "take", js_mkfun(iter_take));
1248 js_set(js, iter_proto, "drop", js_mkfun(iter_drop));
1249 js_set(js, iter_proto, "flatMap", js_mkfun(iter_flatMap));
1250 js_set(js, iter_proto, "every", js_mkfun(iter_every));
1251 js_set(js, iter_proto, "some", js_mkfun(iter_some));
1252 js_set(js, iter_proto, "find", js_mkfun(iter_find));
1253 js_set(js, iter_proto, "forEach", js_mkfun(iter_forEach));
1254 js_set(js, iter_proto, "reduce", js_mkfun(iter_reduce));
1255 js_set(js, iter_proto, "toArray", js_mkfun(iter_toArray));
1256 js_set_sym(js, iter_proto, get_toStringTag_sym(), js_mkstr(js, "Iterator", 8));
1257
1258 ant_value_t ctor_obj = js_mkobj(js);
1259 js_set_slot(ctor_obj, SLOT_CFUNC, js_mkfun(iter_ctor));
1260 js_mkprop_fast(js, ctor_obj, "prototype", 9, iter_proto);
1261 js_mkprop_fast(js, ctor_obj, "name", 4, js_mkstr(js, "Iterator", 8));
1262 js_set_descriptor(js, ctor_obj, "name", 4, 0);
1263
1264 ant_value_t ctor = js_obj_to_func(ctor_obj);
1265 js_set(js, ctor, "from", js_mkfun(iter_from));
1266 js_set(js, iter_proto, "constructor", ctor);
1267 js_set(js, g, "Iterator", ctor);
1268
1269 ant_value_t async_iter_proto = js_mkobj(js);
1270 js->sym.async_iterator_proto = async_iter_proto;
1271 js_set_proto_init(async_iter_proto, js->sym.object_proto);
1272 js_set_sym(js, async_iter_proto, get_asyncIterator_sym(), js_mkfun(sym_this_cb));
1273 js_set_sym(js, async_iter_proto, get_toStringTag_sym(), js_mkstr(js, "AsyncIterator", 13));
1274
1275 ant_value_t async_ctor_obj = js_mkobj(js);
1276 js_set_slot(async_ctor_obj, SLOT_CFUNC, js_mkfun(async_iter_ctor));
1277 js_mkprop_fast(js, async_ctor_obj, "prototype", 9, async_iter_proto);
1278 js_mkprop_fast(js, async_ctor_obj, "name", 4, js_mkstr(js, "AsyncIterator", 13));
1279 js_set_descriptor(js, async_ctor_obj, "name", 4, 0);
1280
1281 ant_value_t async_ctor = js_obj_to_func(async_ctor_obj);
1282 js_set(js, async_iter_proto, "constructor", async_ctor);
1283 js_set(js, g, "AsyncIterator", async_ctor);
1284
1285 g_async_wrap_iter_proto = js_mkobj(js);
1286 js_set_proto_init(g_async_wrap_iter_proto, async_iter_proto);
1287 js_set(js, g_async_wrap_iter_proto, "next", js_mkfun(async_wrap_next));
1288 js_set(js, g_async_wrap_iter_proto, "return", js_mkfun(async_wrap_return));
1289 js_set(js, g_async_wrap_iter_proto, "throw", js_mkfun(async_wrap_throw));
1290}
1291
1292void init_async_iterator_helpers(void) {
1293 ant_t *js = rt->js;
1294 ant_value_t g = js_glob(js);
1295
1296 ant_value_t ctor = js_get(js, g, "AsyncIterator");
1297 ant_value_t proto = js->sym.async_iterator_proto;
1298
1299 js_set(js, ctor, "from", js_mkfun(async_iter_from));
1300 js_set(js, proto, "map", js_mkfun(async_iter_map));
1301 js_set(js, proto, "filter", js_mkfun(async_iter_filter));
1302 js_set(js, proto, "take", js_mkfun(async_iter_take));
1303 js_set(js, proto, "drop", js_mkfun(async_iter_drop));
1304 js_set(js, proto, "flatMap", js_mkfun(async_iter_flatMap));
1305 js_set(js, proto, "every", js_mkfun(async_iter_every));
1306 js_set(js, proto, "some", js_mkfun(async_iter_some));
1307 js_set(js, proto, "find", js_mkfun(async_iter_find));
1308 js_set(js, proto, "forEach", js_mkfun(async_iter_forEach));
1309 js_set(js, proto, "reduce", js_mkfun(async_iter_reduce));
1310 js_set(js, proto, "toArray", js_mkfun(async_iter_toArray));
1311}