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.

at master 872 lines 28 kB view raw
1#include <compat.h> // IWYU pragma: keep 2 3#include <stdio.h> 4#include <stdlib.h> 5#include <string.h> 6#include <time.h> 7#include <uv.h> 8 9#include "errors.h" 10#include "runtime.h" 11#include "internal.h" 12 13#include "silver/engine.h" 14#include "gc/roots.h" 15#include "gc/modules.h" 16#include "modules/abort.h" 17#include "modules/timer.h" 18#include "modules/symbol.h" 19 20typedef struct timer_entry { 21 uv_timer_t handle; 22 ant_value_t callback; 23 ant_value_t *args; 24 int nargs; 25 int timer_id; 26 int active; 27 int closed; 28 int is_interval; 29 struct timer_entry *next; 30 struct timer_entry *prev; 31} timer_entry_t; 32 33typedef struct microtask_entry { 34 ant_value_t callback; 35 ant_value_t promise; 36 struct microtask_entry *next; 37 uint8_t argc; 38 ant_value_t argv[]; 39} microtask_entry_t; 40 41typedef struct immediate_entry { 42 ant_value_t callback; 43 int immediate_id; 44 int active; 45 struct immediate_entry *next; 46} immediate_entry_t; 47 48static struct { 49 ant_t *js; 50 timer_entry_t *timers; 51 52 microtask_entry_t *next_ticks; 53 microtask_entry_t *next_ticks_tail; 54 microtask_entry_t *next_ticks_processing; 55 microtask_entry_t *microtasks; 56 microtask_entry_t *microtasks_tail; 57 microtask_entry_t *microtasks_processing; 58 immediate_entry_t *immediates; 59 immediate_entry_t *immediates_tail; 60 61 int next_timer_id; 62 int next_immediate_id; 63 int active_timer_count; 64} timer_state = { 65 .js = NULL, 66 .timers = NULL, 67 .next_ticks = NULL, 68 .next_ticks_tail = NULL, 69 .next_ticks_processing = NULL, 70 .microtasks = NULL, 71 .microtasks_tail = NULL, 72 .microtasks_processing = NULL, 73 .immediates = NULL, 74 .immediates_tail = NULL, 75 .next_timer_id = 1, 76 .next_immediate_id = 1, 77 .active_timer_count = 0, 78}; 79 80static ant_value_t g_timeout_proto = 0; 81static ant_value_t g_interval_proto = 0; 82 83static void add_timer_entry(timer_entry_t *entry) { 84 entry->next = timer_state.timers; 85 entry->prev = NULL; 86 if (timer_state.timers) timer_state.timers->prev = entry; 87 timer_state.timers = entry; 88} 89 90static void remove_timer_entry(timer_entry_t *entry) { 91 if (entry->prev) entry->prev->next = entry->next; 92 else timer_state.timers = entry->next; 93 if (entry->next) entry->next->prev = entry->prev; 94} 95 96static int timer_entry_is_registered(timer_entry_t *entry) { 97 for (timer_entry_t *it = timer_state.timers; it != NULL; it = it->next) 98 if (it == entry) return 1; 99 return 0; 100} 101 102static timer_entry_t *find_timer_entry_by_id(int timer_id) { 103 for (timer_entry_t *entry = timer_state.timers; entry != NULL; entry = entry->next) 104 if (entry->timer_id == timer_id) return entry; 105 return NULL; 106} 107 108static int timer_copy_args(timer_entry_t *entry, ant_value_t *args, int nargs) { 109 entry->nargs = nargs > 2 ? nargs - 2 : 0; 110 if (entry->nargs > 0) { 111 entry->args = ant_calloc(sizeof(ant_value_t) * entry->nargs); 112 if (!entry->args) return -1; 113 memcpy(entry->args, args + 2, sizeof(ant_value_t) * entry->nargs); 114 } else entry->args = NULL; 115 return 0; 116} 117 118static ant_value_t timer_to_primitive(ant_t *js, ant_value_t *args, int nargs) { 119 return js_get_slot(js_getthis(js), SLOT_DATA); 120} 121 122static ant_value_t timer_inspect(ant_t *js, ant_value_t *args, int nargs) { 123 ant_value_t this_obj = js_getthis(js); 124 ant_value_t id_val = js_get_slot(this_obj, SLOT_DATA); 125 int timer_id = vtype(id_val) == T_NUM ? (int)js_getnum(id_val) : 0; 126 127 ant_value_t tag_val = js_get_sym(js, this_obj, get_toStringTag_sym()); 128 const char *tag = vtype(tag_val) == T_STR ? js_getstr(js, tag_val, NULL) : "Timeout"; 129 130 js_inspect_builder_t builder; 131 if (!js_inspect_builder_init_dynamic(&builder, js, 128)) { 132 return js_mkerr(js, "out of memory"); 133 } 134 135 bool ok = js_inspect_header(&builder, "%s (%d)", tag, timer_id); 136 if (ok) ok = js_inspect_object_body(&builder, this_obj); 137 if (ok) ok = js_inspect_close(&builder); 138 139 if (!ok) { 140 js_inspect_builder_dispose(&builder); 141 return js_mkerr(js, "out of memory"); 142 } 143 144 return js_inspect_builder_result(&builder); 145} 146 147static ant_value_t js_timer_ref(ant_t *js, ant_value_t *args, int nargs) { 148 ant_value_t this_obj = js_getthis(js); 149 timer_entry_t *entry = find_timer_entry_by_id((int)js_getnum(js_get_slot(this_obj, SLOT_DATA))); 150 if (entry && !entry->closed && !uv_is_closing((uv_handle_t *)&entry->handle)) 151 uv_ref((uv_handle_t *)&entry->handle); 152 return this_obj; 153} 154 155static ant_value_t js_timer_unref(ant_t *js, ant_value_t *args, int nargs) { 156 ant_value_t this_obj = js_getthis(js); 157 timer_entry_t *entry = find_timer_entry_by_id((int)js_getnum(js_get_slot(this_obj, SLOT_DATA))); 158 if (entry && !entry->closed && !uv_is_closing((uv_handle_t *)&entry->handle)) 159 uv_unref((uv_handle_t *)&entry->handle); 160 return this_obj; 161} 162 163static ant_value_t js_timer_has_ref(ant_t *js, ant_value_t *args, int nargs) { 164 timer_entry_t *entry = find_timer_entry_by_id((int)js_getnum(js_get_slot(js_getthis(js), SLOT_DATA))); 165 if (!entry || entry->closed || uv_is_closing((uv_handle_t *)&entry->handle)) return js_false; 166 return js_bool(uv_has_ref((const uv_handle_t *)&entry->handle) != 0); 167} 168 169static ant_value_t timer_make_object(ant_t *js, int id, double delay_ms, int is_interval, ant_value_t callback) { 170 ant_value_t obj = js_mkobj(js); 171 ant_value_t proto = is_interval ? g_interval_proto : g_timeout_proto; 172 173 if (is_object_type(proto)) js_set_proto_init(obj, proto); 174 175 js_set(js, obj, "delay", js_mknum(delay_ms)); 176 js_set(js, obj, "repeat", is_interval ? js_mknum(delay_ms) : js_mknull()); 177 178 js_set(js, obj, "callback", callback); 179 js_set_descriptor(js, obj, "callback", 8, JS_DESC_W | JS_DESC_C); 180 181 js_set_slot(obj, SLOT_DATA, js_mknum((double)id)); 182 js_set_sym(js, obj, get_toPrimitive_sym(), js_mkfun(timer_to_primitive)); 183 184 return obj; 185} 186 187static int timer_id_from_arg(ant_t *js, ant_value_t arg) { 188 if (vtype(arg) == T_NUM) return (int)js_getnum(arg); 189 return (int)js_getnum(js_get_slot(arg, SLOT_DATA)); 190} 191 192static void timer_close_cb(uv_handle_t *h) { 193 timer_entry_t *entry = (timer_entry_t *)h->data; 194 if (!entry) return; 195 if (entry->closed) return; 196 if (timer_entry_is_registered(entry)) remove_timer_entry(entry); 197 entry->closed = 1; 198 entry->active = 0; 199 entry->callback = 0; 200 entry->next = NULL; 201 entry->prev = NULL; 202 203 if (entry->args) { 204 free(entry->args); 205 entry->args = NULL; 206 } 207 entry->nargs = 0; 208} 209 210static void timer_callback(uv_timer_t *handle) { 211 timer_entry_t *entry = (timer_entry_t *)handle->data; 212 if (!entry || entry->closed || !timer_entry_is_registered(entry) || !entry->active) return; 213 214 ant_t *js = timer_state.js; 215 if (!entry->is_interval) { 216 entry->active = 0; 217 timer_state.active_timer_count--; 218 } 219 220 sv_vm_call(js->vm, js, entry->callback, js_mkundef(), entry->args, entry->nargs, NULL, false); 221 process_microtasks(js); 222 223 if (!entry->is_interval) if (!uv_is_closing((uv_handle_t *)&entry->handle)) uv_close( 224 (uv_handle_t *)&entry->handle, timer_close_cb 225 ); 226} 227 228// setTimeout(callback, delay, ...args) 229static ant_value_t js_set_timeout(ant_t *js, ant_value_t *args, int nargs) { 230 if (nargs < 1) { 231 return js_mkerr(js, "setTimeout requires at least 1 argument (callback)"); 232 } 233 234 ant_value_t callback = args[0]; 235 double delay_ms = nargs > 1 ? js_getnum(args[1]) : 0; 236 uint64_t ms = delay_ms >= 1 ? (uint64_t)delay_ms : 0; 237 238 timer_entry_t *entry = ant_calloc(sizeof(timer_entry_t)); 239 if (entry == NULL) return js_mkerr(js, "failed to allocate timer"); 240 241 if (timer_copy_args(entry, args, nargs) < 0) { 242 free(entry); 243 return js_mkerr(js, "failed to allocate timer args"); 244 } 245 246 uv_timer_init(uv_default_loop(), &entry->handle); 247 entry->handle.data = entry; 248 entry->callback = callback; 249 entry->timer_id = timer_state.next_timer_id++; 250 entry->active = 1; 251 entry->closed = 0; 252 entry->is_interval = 0; 253 254 add_timer_entry(entry); 255 timer_state.active_timer_count++; 256 uv_timer_start(&entry->handle, timer_callback, ms, 0); 257 258 return timer_make_object(js, entry->timer_id, delay_ms, 0, callback); 259} 260 261// setInterval(callback, delay, ...args) 262static ant_value_t js_set_interval(ant_t *js, ant_value_t *args, int nargs) { 263 if (nargs < 1) { 264 return js_mkerr(js, "setInterval requires at least 1 argument (callback)"); 265 } 266 267 ant_value_t callback = args[0]; 268 double delay_ms = nargs > 1 ? js_getnum(args[1]) : 0; 269 uint64_t ms = delay_ms >= 1 ? (uint64_t)delay_ms : 1; 270 271 timer_entry_t *entry = ant_calloc(sizeof(timer_entry_t)); 272 if (entry == NULL) return js_mkerr(js, "failed to allocate timer"); 273 274 if (timer_copy_args(entry, args, nargs) < 0) { 275 free(entry); 276 return js_mkerr(js, "failed to allocate timer args"); 277 } 278 279 uv_timer_init(uv_default_loop(), &entry->handle); 280 entry->handle.data = entry; 281 entry->callback = callback; 282 entry->timer_id = timer_state.next_timer_id++; 283 entry->active = 1; 284 entry->closed = 0; 285 entry->is_interval = 1; 286 287 add_timer_entry(entry); 288 timer_state.active_timer_count++; 289 uv_timer_start(&entry->handle, timer_callback, ms, ms); 290 291 return timer_make_object(js, entry->timer_id, delay_ms, 1, callback); 292} 293 294// clearTimeout(timerId | timerObject) 295static ant_value_t js_clear_timeout(ant_t *js, ant_value_t *args, int nargs) { 296 if (nargs < 1) return js_mkundef(); 297 int timer_id = timer_id_from_arg(js, args[0]); 298 299 for (timer_entry_t *entry = timer_state.timers; entry != NULL; entry = entry->next) { 300 if (entry->timer_id == timer_id && entry->active) { 301 entry->active = 0; timer_state.active_timer_count--; 302 if (!uv_is_closing((uv_handle_t *)&entry->handle)) uv_close((uv_handle_t *)&entry->handle, timer_close_cb); 303 break; 304 }} 305 306 return js_mkundef(); 307} 308 309// setImmediate(callback) 310static ant_value_t js_set_immediate(ant_t *js, ant_value_t *args, int nargs) { 311 if (nargs < 1) { 312 return js_mkerr(js, "setImmediate requires 1 argument (callback)"); 313 } 314 315 ant_value_t callback = args[0]; 316 317 immediate_entry_t *entry = ant_calloc(sizeof(immediate_entry_t)); 318 if (entry == NULL) { 319 return js_mkerr(js, "failed to allocate immediate"); 320 } 321 322 entry->callback = callback; 323 entry->immediate_id = timer_state.next_immediate_id++; 324 entry->active = 1; 325 entry->next = NULL; 326 327 if (timer_state.immediates_tail == NULL) { 328 timer_state.immediates = entry; 329 timer_state.immediates_tail = entry; 330 } else { 331 timer_state.immediates_tail->next = entry; 332 timer_state.immediates_tail = entry; 333 } 334 335 ant_value_t obj = js_mkobj(js); 336 js_set(js, obj, "id", js_mknum((double)entry->immediate_id)); 337 js_set(js, obj, "callback", callback); 338 339 return obj; 340} 341 342// clearImmediate(immediateId | immediateObject) 343static ant_value_t js_clear_immediate(ant_t *js, ant_value_t *args, int nargs) { 344 if (nargs < 1) return js_mkundef(); 345 int immediate_id = timer_id_from_arg(js, args[0]); 346 347 for (immediate_entry_t *entry = timer_state.immediates; entry != NULL; entry = entry->next) { 348 if (entry->immediate_id == immediate_id) { entry->active = 0; break; } 349 } 350 351 return js_mkundef(); 352} 353 354// queueMicrotask(callback) 355static ant_value_t js_queue_microtask(ant_t *js, ant_value_t *args, int nargs) { 356 if (nargs < 1) { 357 return js_mkerr(js, "queueMicrotask requires 1 argument (callback)"); 358 } 359 360 queue_microtask(js, args[0]); 361 return js_mkundef(); 362} 363 364static ant_value_t timers_promises_get_state(ant_t *js) { 365 return js_get_slot(js->current_func, SLOT_DATA); 366} 367 368static ant_value_t timers_promises_abort_reason(ant_t *js, ant_value_t signal) { 369 ant_value_t reason = abort_signal_get_reason(signal); 370 if (vtype(reason) != T_UNDEF && vtype(reason) != T_NULL) return reason; 371 return js_mkerr_typed(js, JS_ERR_TYPE, "The operation was aborted"); 372} 373 374static void timers_promises_remove_abort_listener(ant_t *js, ant_value_t state) { 375 ant_value_t signal = 0; 376 ant_value_t listener = 0; 377 378 if (!is_object_type(state)) return; 379 380 signal = js_get(js, state, "signal"); 381 listener = js_get(js, state, "abortListener"); 382 383 if (abort_signal_is_signal(signal) && is_callable(listener)) 384 abort_signal_remove_listener(js, signal, listener); 385 386 js_set(js, state, "abortListener", js_mkundef()); 387} 388 389static void timers_promises_settle(ant_t *js, ant_value_t state, bool reject, ant_value_t value) { 390 ant_value_t settled = 0; 391 ant_value_t promise = 0; 392 393 if (!is_object_type(state)) return; 394 395 settled = js_get(js, state, "settled"); 396 if (js_truthy(js, settled)) return; 397 398 js_set(js, state, "settled", js_true); 399 timers_promises_remove_abort_listener(js, state); 400 js_set(js, state, "handle", js_mkundef()); 401 402 promise = js_get(js, state, "promise"); 403 if (reject) js_reject_promise(js, promise, value); 404 else js_resolve_promise(js, promise, value); 405} 406 407static ant_value_t timers_promises_resolve(ant_t *js, ant_value_t *args, int nargs) { 408 ant_value_t state = timers_promises_get_state(js); 409 ant_value_t value = js_mkundef(); 410 if (!is_object_type(state)) return js_mkundef(); 411 value = js_get(js, state, "value"); 412 timers_promises_settle(js, state, false, value); 413 return js_mkundef(); 414} 415 416static ant_value_t timers_promises_on_abort(ant_t *js, ant_value_t *args, int nargs) { 417 ant_value_t state = timers_promises_get_state(js); 418 ant_value_t signal = 0; 419 ant_value_t handle = 0; 420 ant_value_t is_immediate = 0; 421 ant_value_t reason = js_mkundef(); 422 ant_value_t clear_args[1]; 423 424 if (!is_object_type(state)) return js_mkundef(); 425 426 signal = js_get(js, state, "signal"); 427 handle = js_get(js, state, "handle"); 428 is_immediate = js_get(js, state, "isImmediate"); 429 430 if (vtype(handle) != T_UNDEF && vtype(handle) != T_NULL) { 431 clear_args[0] = handle; 432 if (js_truthy(js, is_immediate)) js_clear_immediate(js, clear_args, 1); 433 else js_clear_timeout(js, clear_args, 1); 434 } 435 436 if (abort_signal_is_signal(signal)) reason = timers_promises_abort_reason(js, signal); 437 else reason = js_mkerr_typed(js, JS_ERR_TYPE, "The operation was aborted"); 438 439 timers_promises_settle(js, state, true, reason); 440 return js_mkundef(); 441} 442 443static bool timers_promises_parse_options( 444 ant_t *js, 445 ant_value_t value, 446 ant_value_t *signal_out, 447 ant_value_t *error_out 448) { 449 ant_value_t signal = js_mkundef(); 450 451 if (signal_out) *signal_out = js_mkundef(); 452 if (error_out) *error_out = js_mkundef(); 453 454 if (vtype(value) == T_UNDEF || vtype(value) == T_NULL) return true; 455 if (vtype(value) != T_OBJ) { 456 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Timer options must be an object"); 457 return false; 458 } 459 460 signal = js_get(js, value, "signal"); 461 if (vtype(signal) != T_UNDEF && vtype(signal) != T_NULL && !abort_signal_is_signal(signal)) { 462 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "options.signal must be an AbortSignal"); 463 return false; 464 } 465 466 if (signal_out) *signal_out = signal; 467 return true; 468} 469 470static ant_value_t timers_promises_schedule( 471 ant_t *js, 472 double delay_ms, 473 ant_value_t value, 474 ant_value_t signal, 475 bool is_immediate 476) { 477 ant_value_t promise = js_mkpromise(js); 478 ant_value_t state = js_mkobj(js); 479 ant_value_t callback = 0; 480 ant_value_t handle = 0; 481 ant_value_t args[2]; 482 483 if (abort_signal_is_signal(signal) && abort_signal_is_aborted(signal)) { 484 js_reject_promise(js, promise, timers_promises_abort_reason(js, signal)); 485 return promise; 486 } 487 488 js_set(js, state, "promise", promise); 489 js_set(js, state, "value", value); 490 js_set(js, state, "signal", signal); 491 js_set(js, state, "abortListener", js_mkundef()); 492 js_set(js, state, "handle", js_mkundef()); 493 js_set(js, state, "settled", js_false); 494 js_set(js, state, "isImmediate", js_bool(is_immediate)); 495 496 callback = js_heavy_mkfun(js, timers_promises_resolve, state); 497 if (is_immediate) handle = js_set_immediate(js, &callback, 1); 498 else { 499 args[0] = callback; 500 args[1] = js_mknum(delay_ms); 501 handle = js_set_timeout(js, args, 2); 502 } 503 504 if (is_err(handle)) { 505 js_reject_promise(js, promise, handle); 506 return promise; 507 } 508 509 js_set(js, state, "handle", handle); 510 511 if (abort_signal_is_signal(signal)) { 512 ant_value_t listener = js_heavy_mkfun(js, timers_promises_on_abort, state); 513 js_set(js, state, "abortListener", listener); 514 abort_signal_add_listener(js, signal, listener); 515 } 516 517 return promise; 518} 519 520static ant_value_t js_timers_promises_setTimeout(ant_t *js, ant_value_t *args, int nargs) { 521 double delay_ms = nargs > 0 ? js_getnum(args[0]) : 0; 522 ant_value_t value = nargs > 1 ? args[1] : js_mkundef(); 523 ant_value_t options = nargs > 2 ? args[2] : js_mkundef(); 524 ant_value_t signal = js_mkundef(); 525 ant_value_t error = js_mkundef(); 526 527 if (!timers_promises_parse_options(js, options, &signal, &error)) return error; 528 return timers_promises_schedule(js, delay_ms, value, signal, false); 529} 530 531static ant_value_t js_timers_promises_setImmediate(ant_t *js, ant_value_t *args, int nargs) { 532 ant_value_t value = nargs > 0 ? args[0] : js_mkundef(); 533 ant_value_t options = nargs > 1 ? args[1] : js_mkundef(); 534 ant_value_t signal = js_mkundef(); 535 ant_value_t error = js_mkundef(); 536 537 if (!timers_promises_parse_options(js, options, &signal, &error)) return error; 538 return timers_promises_schedule(js, 0, value, signal, true); 539} 540 541static ant_value_t js_timers_promises_setInterval(ant_t *js, ant_value_t *args, int nargs) { 542 return js_mkerr_typed(js, JS_ERR_TYPE, "node:timers/promises setInterval() is not implemented yet"); 543} 544 545static ant_value_t js_timers_promises_scheduler_wait(ant_t *js, ant_value_t *args, int nargs) { 546 return js_timers_promises_setTimeout(js, args, nargs); 547} 548 549static ant_value_t js_timers_promises_scheduler_yield(ant_t *js, ant_value_t *args, int nargs) { 550 return js_timers_promises_setImmediate(js, args, nargs); 551} 552 553static void queue_microtask_entry( 554 microtask_entry_t **head, 555 microtask_entry_t **tail, 556 microtask_entry_t *entry 557) { 558 if (*tail == NULL) goto empty; 559 560 (*tail)->next = entry; 561 *tail = entry; 562 return; 563 564empty: 565 *head = entry; 566 *tail = entry; 567} 568 569void queue_microtask(ant_t *js, ant_value_t callback) { 570 microtask_entry_t *entry = ant_calloc(sizeof(microtask_entry_t)); 571 if (entry == NULL) return; 572 573 entry->callback = callback; 574 entry->promise = js_mkundef(); 575 entry->next = NULL; 576 entry->argc = 0; 577 578 queue_microtask_entry(&timer_state.microtasks, &timer_state.microtasks_tail, entry); 579} 580 581void queue_microtask_with_args(ant_t *js, ant_value_t callback, ant_value_t *args, int nargs) { 582 if (nargs <= 0) { queue_microtask(js, callback); return; } 583 584 microtask_entry_t *entry = ant_calloc(sizeof(microtask_entry_t) + (size_t)nargs * sizeof(ant_value_t)); 585 if (entry == NULL) return; 586 587 entry->callback = callback; 588 entry->promise = js_mkundef(); 589 entry->next = NULL; 590 entry->argc = (uint8_t)nargs; 591 592 for (int i = 0; i < nargs; i++) entry->argv[i] = args[i]; 593 queue_microtask_entry(&timer_state.microtasks, &timer_state.microtasks_tail, entry); 594} 595 596void queue_next_tick(ant_t *js, ant_value_t callback) { 597 microtask_entry_t *entry = ant_calloc(sizeof(microtask_entry_t)); 598 if (entry == NULL) return; 599 600 entry->callback = callback; 601 entry->promise = js_mkundef(); 602 entry->next = NULL; 603 entry->argc = 0; 604 605 queue_microtask_entry(&timer_state.next_ticks, &timer_state.next_ticks_tail, entry); 606} 607 608void queue_next_tick_with_args(ant_t *js, ant_value_t callback, ant_value_t *args, int nargs) { 609 if (nargs <= 0) { queue_next_tick(js, callback); return; } 610 611 microtask_entry_t *entry = ant_calloc(sizeof(microtask_entry_t) + (size_t)nargs * sizeof(ant_value_t)); 612 if (entry == NULL) return; 613 614 entry->callback = callback; 615 entry->promise = js_mkundef(); 616 entry->next = NULL; 617 entry->argc = (uint8_t)nargs; 618 619 for (int i = 0; i < nargs; i++) entry->argv[i] = args[i]; 620 queue_microtask_entry(&timer_state.next_ticks, &timer_state.next_ticks_tail, entry); 621} 622 623void queue_promise_trigger(ant_t *js, ant_value_t promise) { 624 if (!js_mark_promise_trigger_queued(js, promise)) return; 625 626 microtask_entry_t *entry = ant_calloc(sizeof(microtask_entry_t)); 627 if (entry == NULL) { 628 js_mark_promise_trigger_dequeued(js, promise); 629 return; 630 } 631 632 entry->callback = js_mkundef(); 633 entry->promise = promise; 634 entry->next = NULL; 635 636 queue_microtask_entry(&timer_state.microtasks, &timer_state.microtasks_tail, entry); 637} 638 639static inline void process_microtask_entry(ant_t *js, microtask_entry_t *entry) { 640 if (!entry) return; 641 642 if (vtype(entry->promise) == T_PROMISE) { 643 GC_ROOT_SAVE(root_mark, js); 644 ant_value_t promise = entry->promise; 645 646 GC_ROOT_PIN(js, promise); 647 js_mark_promise_trigger_dequeued(js, promise); 648 js_process_promise_handlers(js, promise); 649 GC_ROOT_RESTORE(js, root_mark); 650 651 return; 652 } 653 654 GC_ROOT_SAVE(root_mark, js); 655 ant_value_t callback = entry->callback; 656 GC_ROOT_PIN(js, callback); 657 658 for (uint8_t i = 0; i < entry->argc; i++) GC_ROOT_PIN(js, entry->argv[i]); 659 sv_vm_call(js->vm, js, callback, js_mkundef(), entry->argv, entry->argc, NULL, false); 660 GC_ROOT_RESTORE(js, root_mark); 661} 662 663static inline microtask_entry_t *take_microtask_batch(void) { 664 microtask_entry_t *batch = timer_state.microtasks; 665 666 timer_state.microtasks = NULL; 667 timer_state.microtasks_tail = NULL; 668 timer_state.microtasks_processing = batch; 669 670 return batch; 671} 672 673static inline microtask_entry_t *take_next_tick_batch(void) { 674 microtask_entry_t *batch = timer_state.next_ticks; 675 676 timer_state.next_ticks = NULL; 677 timer_state.next_ticks_tail = NULL; 678 timer_state.next_ticks_processing = batch; 679 680 return batch; 681} 682 683static inline void process_microtask_batch(ant_t *js, microtask_entry_t *batch) { 684while (batch != NULL) { 685 microtask_entry_t *entry = batch; 686 batch = entry->next; 687 timer_state.microtasks_processing = batch; 688 process_microtask_entry(js, entry); 689 free(entry); 690}} 691 692static inline void process_next_tick_batch(ant_t *js, microtask_entry_t *batch) { 693while (batch != NULL) { 694 microtask_entry_t *entry = batch; 695 batch = entry->next; 696 timer_state.next_ticks_processing = batch; 697 process_microtask_entry(js, entry); 698 free(entry); 699}} 700 701static void process_microtasks_internal(ant_t *js, bool check_unhandled_rejections) { 702 microtask_entry_t *batch = NULL; 703 704 if (!js || js->microtasks_draining) return; 705 js->microtasks_draining = true; 706 707 while (timer_state.next_ticks != NULL || timer_state.microtasks != NULL) { 708 while ((batch = timer_state.next_ticks) != NULL) { 709 batch = take_next_tick_batch(); 710 process_next_tick_batch(js, batch); 711 } 712 while ((batch = timer_state.microtasks) != NULL) { 713 batch = take_microtask_batch(); 714 process_microtask_batch(js, batch); 715 }} 716 717 timer_state.next_ticks_processing = NULL; 718 timer_state.microtasks_processing = NULL; 719 if (check_unhandled_rejections) js_check_unhandled_rejections(js); 720 js->microtasks_draining = false; 721 reap_retired_coroutines(); 722} 723 724void process_microtasks(ant_t *js) { 725 process_microtasks_internal(js, true); 726} 727 728bool js_maybe_drain_microtasks(ant_t *js) { 729 if (!js) return false; 730 if (js->microtasks_draining) return false; 731 if (js->vm_exec_depth != 0) return false; 732 if (!has_pending_microtasks()) return false; 733 process_microtasks_internal(js, true); 734 return true; 735} 736 737bool js_maybe_drain_microtasks_after_async_settle(ant_t *js) { 738 if (!js) return false; 739 740 if (js->microtasks_draining) return false; 741 if (!has_pending_microtasks()) return false; 742 743 process_microtasks_internal(js, false); 744 return true; 745} 746 747void process_immediates(ant_t *js) { 748while (timer_state.immediates != NULL) { 749 immediate_entry_t *entry = timer_state.immediates; 750 timer_state.immediates = entry->next; 751 752 if (timer_state.immediates == NULL) { 753 timer_state.immediates_tail = NULL; 754 } 755 756 if (entry->active) { 757 ant_value_t args[0]; 758 sv_vm_call(js->vm, js, entry->callback, js_mkundef(), args, 0, NULL, false); 759 process_microtasks(js); 760 } 761 762 free(entry); 763}} 764 765int has_pending_immediates(void) { 766 for ( 767 immediate_entry_t *entry = timer_state.immediates; 768 entry != NULL; entry = entry->next 769 ) if (entry->active) return 1; 770 return 0; 771} 772 773int has_pending_timers(void) { 774 return timer_state.active_timer_count > 0; 775} 776 777int has_pending_microtasks(void) { 778 return (timer_state.next_ticks != NULL || timer_state.microtasks != NULL) ? 1 : 0; 779} 780 781static void timers_define_common(ant_t *js, ant_value_t obj) { 782 js_set(js, obj, "setTimeout", js_mkfun_flags(js_set_timeout, CFUNC_HAS_PROTOTYPE)); 783 js_set(js, obj, "clearTimeout", js_mkfun_flags(js_clear_timeout, CFUNC_HAS_PROTOTYPE)); 784 js_set(js, obj, "setInterval", js_mkfun_flags(js_set_interval, CFUNC_HAS_PROTOTYPE)); 785 js_set(js, obj, "clearInterval", js_mkfun_flags(js_clear_timeout, CFUNC_HAS_PROTOTYPE)); 786 js_set(js, obj, "setImmediate", js_mkfun_flags(js_set_immediate, CFUNC_HAS_PROTOTYPE)); 787 js_set(js, obj, "clearImmediate", js_mkfun_flags(js_clear_immediate, CFUNC_HAS_PROTOTYPE)); 788 js_set(js, obj, "queueMicrotask", js_mkfun(js_queue_microtask)); 789} 790 791void init_timer_module() { 792 ant_t *js = rt->js; 793 timer_state.js = js; 794 795 g_timeout_proto = js_mkobj(js); 796 g_interval_proto = js_mkobj(js); 797 gc_register_root(&g_timeout_proto); 798 gc_register_root(&g_interval_proto); 799 800 js_set_proto_init(g_timeout_proto, js->sym.object_proto); 801 js_set(js, g_timeout_proto, "ref", js_mkfun(js_timer_ref)); 802 js_set(js, g_timeout_proto, "unref", js_mkfun(js_timer_unref)); 803 js_set(js, g_timeout_proto, "hasRef", js_mkfun(js_timer_has_ref)); 804 js_set_sym(js, g_timeout_proto, get_toStringTag_sym(), js_mkstr(js, "Timeout", 7)); 805 js_set_sym(js, g_timeout_proto, get_inspect_sym(), js_mkfun(timer_inspect)); 806 807 js_set_proto_init(g_interval_proto, js->sym.object_proto); 808 js_set(js, g_interval_proto, "ref", js_mkfun(js_timer_ref)); 809 js_set(js, g_interval_proto, "unref", js_mkfun(js_timer_unref)); 810 js_set(js, g_interval_proto, "hasRef", js_mkfun(js_timer_has_ref)); 811 js_set_sym(js, g_interval_proto, get_toStringTag_sym(), js_mkstr(js, "Interval", 8)); 812 js_set_sym(js, g_interval_proto, get_inspect_sym(), js_mkfun(timer_inspect)); 813 814 timers_define_common(js, js_glob(js)); 815} 816 817ant_value_t timers_library(ant_t *js) { 818 ant_value_t lib = js_mkobj(js); 819 820 timers_define_common(js, lib); 821 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "timers", 6)); 822 823 return lib; 824} 825 826// TODO: mostly stubbed 827ant_value_t timers_promises_library(ant_t *js) { 828 ant_value_t lib = js_mkobj(js); 829 ant_value_t scheduler = js_mkobj(js); 830 831 js_set(js, lib, "scheduler", scheduler); 832 js_set(js, lib, "setTimeout", js_mkfun(js_timers_promises_setTimeout)); 833 js_set(js, lib, "setImmediate", js_mkfun(js_timers_promises_setImmediate)); 834 js_set(js, lib, "setInterval", js_mkfun(js_timers_promises_setInterval)); 835 js_set(js, scheduler, "wait", js_mkfun(js_timers_promises_scheduler_wait)); 836 js_set(js, scheduler, "yield", js_mkfun(js_timers_promises_scheduler_yield)); 837 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "timers/promises", 16)); 838 839 return lib; 840} 841 842void gc_mark_timers(ant_t *js, gc_mark_fn mark) { 843 if (is_object_type(g_timeout_proto)) mark(js, g_timeout_proto); 844 if (is_object_type(g_interval_proto)) mark(js, g_interval_proto); 845 for (timer_entry_t *t = timer_state.timers; t; t = t->next) { 846 mark(js, t->callback); 847 for (int i = 0; i < t->nargs; i++) mark(js, t->args[i]); 848 } 849 for (microtask_entry_t *m = timer_state.microtasks; m; m = m->next) { 850 mark(js, m->callback); 851 mark(js, m->promise); 852 for (uint8_t i = 0; i < m->argc; i++) mark(js, m->argv[i]); 853 } 854 for (microtask_entry_t *m = timer_state.microtasks_processing; m; m = m->next) { 855 mark(js, m->callback); 856 mark(js, m->promise); 857 for (uint8_t i = 0; i < m->argc; i++) mark(js, m->argv[i]); 858 } 859 for (microtask_entry_t *m = timer_state.next_ticks; m; m = m->next) { 860 mark(js, m->callback); 861 mark(js, m->promise); 862 for (uint8_t i = 0; i < m->argc; i++) mark(js, m->argv[i]); 863 } 864 for (microtask_entry_t *m = timer_state.next_ticks_processing; m; m = m->next) { 865 mark(js, m->callback); 866 mark(js, m->promise); 867 for (uint8_t i = 0; i < m->argc; i++) mark(js, m->argv[i]); 868 } 869 for (immediate_entry_t *i = timer_state.immediates; i; i = i->next) { 870 mark(js, i->callback); 871 } 872}