MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

support custom thenable

+146 -30
+30
src/ant.c
··· 10019 10019 return; 10020 10020 } 10021 10021 10022 + if (is_object_type(val)) { 10023 + ant_value_t res_fn = make_data_cfunc(js, p, builtin_resolve_internal); 10024 + GC_ROOT_PIN(js, res_fn); 10025 + 10026 + if (is_err(res_fn)) { 10027 + js_reject_promise(js, p, res_fn); 10028 + GC_ROOT_RESTORE(js, root_mark); 10029 + return; 10030 + } 10031 + 10032 + ant_value_t rej_fn = make_data_cfunc(js, p, builtin_reject_internal); 10033 + GC_ROOT_PIN(js, rej_fn); 10034 + 10035 + if (is_err(rej_fn)) { 10036 + js_reject_promise(js, p, rej_fn); 10037 + GC_ROOT_RESTORE(js, root_mark); 10038 + return; 10039 + } 10040 + 10041 + ant_value_t then_prop = js_get(js, val, "then"); 10042 + GC_ROOT_PIN(js, then_prop); 10043 + 10044 + if (vtype(then_prop) == T_FUNC || vtype(then_prop) == T_CFUNC) { 10045 + ant_value_t call_args[] = { res_fn, rej_fn }; 10046 + sv_vm_call(js->vm, js, then_prop, val, call_args, 2, NULL, false); 10047 + GC_ROOT_RESTORE(js, root_mark); 10048 + return; 10049 + } 10050 + } 10051 + 10022 10052 pd->state = 1; 10023 10053 pd->value = val; 10024 10054
+2
src/modules/server.c
··· 668 668 ant_http1_conn_parser_reset(&cs->parser); 669 669 cs->active_req = NULL; 670 670 ant_conn_set_timeout_ms(conn, cs->server->idle_timeout_ms); 671 + ant_conn_resume_read(conn); 671 672 server_request_release(req); 672 673 if (ant_conn_buffer_len(conn) > 0) server_schedule_drain(cs); 673 674 } ··· 801 802 req->next = server->requests; 802 803 server->requests = req; 803 804 cs->active_req = req; 805 + ant_conn_pause_read(conn); 804 806 ant_conn_set_timeout_ms(conn, server->request_timeout_ms); 805 807 806 808 result = server_call_fetch(server, request_obj);
+56 -30
src/modules/timer.c
··· 44 44 static struct { 45 45 ant_t *js; 46 46 timer_entry_t *timers; 47 + 47 48 microtask_entry_t *microtasks; 48 49 microtask_entry_t *microtasks_tail; 50 + microtask_entry_t *microtasks_processing; 49 51 immediate_entry_t *immediates; 50 52 immediate_entry_t *immediates_tail; 53 + 51 54 int next_timer_id; 52 55 int next_immediate_id; 53 56 int active_timer_count; 54 - } timer_state = {NULL, NULL, NULL, NULL, NULL, NULL, 1, 1, 0}; 57 + } timer_state = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, 1, 0}; 55 58 56 59 static void add_timer_entry(timer_entry_t *entry) { 57 60 entry->next = timer_state.timers; ··· 321 324 } 322 325 } 323 326 327 + static inline void process_microtask_entry(ant_t *js, microtask_entry_t *entry) { 328 + if (!entry) return; 329 + 330 + if (vtype(entry->promise) == T_PROMISE) { 331 + GC_ROOT_SAVE(root_mark, js); 332 + ant_value_t promise = entry->promise; 333 + 334 + GC_ROOT_PIN(js, promise); 335 + js_mark_promise_trigger_dequeued(js, promise); 336 + js_process_promise_handlers(js, promise); 337 + GC_ROOT_RESTORE(js, root_mark); 338 + 339 + return; 340 + } 341 + 342 + GC_ROOT_SAVE(root_mark, js); 343 + ant_value_t callback = entry->callback; 344 + GC_ROOT_PIN(js, callback); 345 + 346 + ant_value_t args[0]; 347 + sv_vm_call(js->vm, js, callback, js_mkundef(), args, 0, NULL, false); 348 + GC_ROOT_RESTORE(js, root_mark); 349 + } 350 + 351 + static inline microtask_entry_t *take_microtask_batch(void) { 352 + microtask_entry_t *batch = timer_state.microtasks; 353 + 354 + timer_state.microtasks = NULL; 355 + timer_state.microtasks_tail = NULL; 356 + timer_state.microtasks_processing = batch; 357 + return batch; 358 + } 359 + 360 + static inline void process_microtask_batch(ant_t *js, microtask_entry_t *batch) { 361 + while (batch != NULL) { 362 + microtask_entry_t *entry = batch; 363 + batch = entry->next; 364 + timer_state.microtasks_processing = batch; 365 + process_microtask_entry(js, entry); 366 + free(entry); 367 + }} 368 + 324 369 static void process_microtasks_internal(ant_t *js, bool check_unhandled_rejections) { 370 + microtask_entry_t *batch = NULL; 371 + 325 372 if (!js || js->microtasks_draining) return; 326 373 js->microtasks_draining = true; 327 374 328 - microtask_entry_t *batch_stop = timer_state.microtasks_tail; 329 - 330 - while (timer_state.microtasks != NULL) { 331 - microtask_entry_t *entry = timer_state.microtasks; 332 - timer_state.microtasks = entry->next; 333 - 334 - if (timer_state.microtasks == NULL) { 335 - timer_state.microtasks_tail = NULL; 336 - } 337 - 338 - if (vtype(entry->promise) == T_PROMISE) { 339 - GC_ROOT_SAVE(root_mark, js); 340 - ant_value_t promise = entry->promise; 341 - GC_ROOT_PIN(js, promise); 342 - js_mark_promise_trigger_dequeued(js, promise); 343 - js_process_promise_handlers(js, promise); 344 - GC_ROOT_RESTORE(js, root_mark); 345 - } else { 346 - GC_ROOT_SAVE(root_mark, js); 347 - ant_value_t callback = entry->callback; 348 - GC_ROOT_PIN(js, callback); 349 - ant_value_t args[0]; 350 - sv_vm_call(js->vm, js, callback, js_mkundef(), args, 0, NULL, false); 351 - GC_ROOT_RESTORE(js, root_mark); 352 - } 353 - 354 - bool reached_batch_end = (entry == batch_stop); 355 - free(entry); 356 - if (reached_batch_end) break; 375 + while ((batch = timer_state.microtasks) != NULL) { 376 + batch = take_microtask_batch(); 377 + process_microtask_batch(js, batch); 357 378 } 358 379 380 + timer_state.microtasks_processing = NULL; 359 381 if (check_unhandled_rejections) js_check_unhandled_rejections(js); 360 382 js->microtasks_draining = false; 361 383 } ··· 438 460 for (int i = 0; i < t->nargs; i++) mark(js, t->args[i]); 439 461 } 440 462 for (microtask_entry_t *m = timer_state.microtasks; m; m = m->next) { 463 + mark(js, m->callback); 464 + mark(js, m->promise); 465 + } 466 + for (microtask_entry_t *m = timer_state.microtasks_processing; m; m = m->next) { 441 467 mark(js, m->callback); 442 468 mark(js, m->promise); 443 469 }
+19
tests/test_microtask_order.js
··· 1 + let events = []; 2 + 3 + Promise.resolve().then(() => { 4 + events.push("outer"); 5 + Promise.resolve().then(() => { 6 + events.push("inner"); 7 + }); 8 + }); 9 + 10 + setTimeout(() => { 11 + events.push("timer"); 12 + console.log(events.join(",")); 13 + 14 + if (events.join(",") === "outer,inner,timer") { 15 + console.log("OK nested microtasks drain before timers"); 16 + } else { 17 + console.log("FAIL nested microtasks order changed"); 18 + } 19 + }, 0);
+39
tests/test_promise_thenables.js
··· 1 + let passed = 0; 2 + let failed = 0; 3 + 4 + function test(name, actual, expected) { 5 + if (actual === expected) { 6 + console.log(` OK ${name}`); 7 + passed++; 8 + return; 9 + } 10 + 11 + console.log(` FAIL ${name}: expected ${expected}, got ${actual}`); 12 + failed++; 13 + } 14 + 15 + async function returnsThenable() { 16 + return { 17 + then(resolve) { 18 + resolve(99); 19 + } 20 + }; 21 + } 22 + 23 + console.log("Promise thenable adoption\n"); 24 + 25 + Promise.resolve({ 26 + then(resolve) { 27 + resolve(42); 28 + } 29 + }).then((value) => { 30 + test("Promise.resolve adopts plain thenables", value, 42); 31 + }); 32 + 33 + returnsThenable().then((value) => { 34 + test("async return adopts plain thenables", value, 99); 35 + }); 36 + 37 + setTimeout(() => { 38 + console.log(`\n${passed} passed, ${failed} failed`); 39 + }, 20);