Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394#include <compat.h> // IWYU pragma: keep
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#include <uv.h>
#include "errors.h"#include "cfunc.h"#include "builder.h"#include "ptr.h"#include "internal.h"#include "silver/call.h"
#include "gc/uv.h"#include "gc/weak.h"#include "gc/roots.h"#include "gc/modules.h"
#include "modules/abort.h"#include "loop.h"#include "modules/timer.h"#include "modules/process.h"#include "modules/generator.h"
typedef struct timer_entry { struct timer_entry *next; ant_value_t obj; ant_value_t callback; ant_value_t *args; int nargs; int active; bool young; struct timer_entry *prev; int timer_id; int closed; int is_interval; bool handle_closed; bool detached; uint64_t timeout_ms; uv_timer_t handle;} timer_entry_t;
enum { MT_CALLBACK = 0, MT_PROMISE_TRIGGER, MT_THENABLE_JOB, MT_AWAIT_RESUME, MT_GENERATOR_YIELD, MT_PROMISE_RESOLVE, MT_AWAIT_RESUME_THROW, MT_AWAIT_DEFER,};
static inline bool microtask_holds_coro(uint8_t kind) { return kind == MT_AWAIT_RESUME || kind == MT_AWAIT_RESUME_THROW || kind == MT_AWAIT_DEFER;}
typedef struct microtask_entry { ant_value_t callback; union { ant_value_t promise; ant_value_t this_val; coroutine_t *coro; } u; struct microtask_entry *next; uint8_t argc; uint8_t kind; ant_value_t argv[];} microtask_entry_t;
typedef struct immediate_entry { ant_value_t callback; int immediate_id; int active; bool refed; ant_value_t handle; struct immediate_entry *next; int argc; ant_value_t argv[];} immediate_entry_t;
static void add_timer_entry(ant_t *js, timer_entry_t *entry) { entry->young = true; entry->next = js->timers.timers; entry->prev = NULL; if (js->timers.timers) js->timers.timers->prev = entry; js->timers.timers = entry;}
static void remove_timer_entry(ant_t *js, timer_entry_t *entry) { if (entry->prev) entry->prev->next = entry->next; else js->timers.timers = entry->next; if (entry->next) entry->next->prev = entry->prev; entry->next = NULL; entry->prev = NULL;}
static void timer_entry_hold_young(ant_t *js, timer_entry_t *entry) { if (entry->young) return; remove_timer_entry(js, entry); add_timer_entry(js, entry);}
static timer_entry_t *find_timer_entry_by_id(ant_t *js, int timer_id) { for (timer_entry_t *entry = js->timers.timers; entry != NULL; entry = entry->next) if (entry->timer_id == timer_id) return entry; return NULL;}
static int timer_copy_args(timer_entry_t *entry, ant_value_t *args, int nargs) { entry->nargs = nargs > 2 ? nargs - 2 : 0; if (entry->nargs > 0) { entry->args = calloc(1, sizeof(ant_value_t) * entry->nargs); if (!entry->args) return -1; memcpy(entry->args, args + 2, sizeof(ant_value_t) * entry->nargs); } else entry->args = NULL; return 0;}
static void timer_release_args(timer_entry_t *entry) { if (!entry) return; if (entry->args) { free(entry->args); entry->args = NULL; } entry->nargs = 0;}
static void timer_release_callback_args(timer_entry_t *entry) { if (!entry) return; entry->callback = js_mkundef(); timer_release_args(entry);}
static int timer_copy_args_from_object(ant_t *js, timer_entry_t *entry, ant_value_t obj) { ant_value_t args_arr = js_get_slot(obj, SLOT_AUX); ant_offset_t len = vtype(args_arr) == kTypeArray ? js_arr_len(js, args_arr) : 0;
timer_release_args(entry); if (len == 0) return 0;
entry->args = calloc(1, sizeof(ant_value_t) * (size_t)len); if (!entry->args) return -1; entry->nargs = (int)len; for (ant_offset_t i = 0; i < len; i++) entry->args[i] = js_arr_get(js, args_arr, i); return 0;}
static ant_value_t timer_make_args_array(ant_native_params_t) { ant_value_t arr = js_mkundef(); int arg_count = nargs > 2 ? nargs - 2 : 0;
if (arg_count <= 0) return arr; arr = js_mkarr(js); for (int i = 0; i < arg_count; i++) js_arr_push(js, arr, args[i + 2]); return arr;}
static constexpr uint32_t TIMER_NATIVE_TAG = Ant_NativeTag("TIMR");static constexpr uint32_t IMMEDIATE_NATIVE_TAG = Ant_NativeTag("IMMD");
static timer_entry_t *timer_entry_of(ant_value_t handle) { return is_object_type(handle) ? js_get_native(handle, TIMER_NATIVE_TAG) : NULL;}
static timer_entry_t *timer_open_entry_of(ant_value_t handle) { timer_entry_t *entry = timer_entry_of(handle); return entry && !entry->closed ? entry : NULL;}
static ant_value_t timer_to_primitive(ant_params_t) { timer_entry_t *entry = timer_entry_of(js_getthis(js)); return entry ? js_mknum((double)entry->timer_id) : js_mkundef();}
static immediate_entry_t *immediate_entry_of(ant_value_t handle) { return is_object_type(handle) ? js_get_native(handle, IMMEDIATE_NATIVE_TAG) : NULL;}
static ant_value_t timer_inspect(ant_params_t) { ant_value_t this_obj = js_getthis(js); timer_entry_t *entry = timer_entry_of(this_obj); immediate_entry_t *immediate = immediate_entry_of(this_obj); ant_value_t refed = js_mknull(); bool destroyed = true; if (entry) { refed = js_bool(uv_has_ref((const uv_handle_t *)&entry->handle)); destroyed = entry->closed || !entry->active; } else if (immediate && immediate->active) { refed = js_bool(immediate->refed); destroyed = false; }
ant_value_t tag_val = js_get_sym(js, this_obj, js->sym.toStringTag_sym); const char *tag = vtype(tag_val) == kTypeString ? js_getstr(js, tag_val, NULL) : "Timeout";
js_inspect_builder_t builder; if (!js_inspect_builder_init_dynamic(&builder, js, 128)) return js_mkerr(js, "out of memory");
bool ok = entry ? js_inspect_header(&builder, "%s (%d)", tag, entry->timer_id) : js_inspect_header(&builder, "%s", tag); if (ok) ok = js_inspect_object_body(&builder, this_obj); if (ok) ok = js_inspect_field(&builder, "Symbol(refed)", refed); if (ok) ok = js_inspect_field(&builder, "Symbol(destroyed)", js_bool(destroyed)); if (ok) ok = js_inspect_close(&builder); if (!ok) { js_inspect_builder_dispose(&builder); return js_mkerr(js, "out of memory"); }
return js_inspect_builder_result(&builder);}
static ant_value_t js_timer_ref(ant_params_t) { ant_value_t this_obj = js_getthis(js); timer_entry_t *entry = timer_open_entry_of(this_obj); if (entry) { int was_refed = uv_has_ref((const uv_handle_t *)&entry->handle); uv_ref((uv_handle_t *)&entry->handle); if (entry->active && !was_refed) js->timers.active_refed_timer_count++; } return this_obj;}
static ant_value_t js_timer_unref(ant_params_t) { ant_value_t this_obj = js_getthis(js); timer_entry_t *entry = timer_open_entry_of(this_obj); if (entry) { int was_refed = uv_has_ref((const uv_handle_t *)&entry->handle); uv_unref((uv_handle_t *)&entry->handle); if (entry->active && was_refed) js->timers.active_refed_timer_count--; } return this_obj;}
static ant_value_t js_timer_has_ref(ant_params_t) { timer_entry_t *entry = timer_open_entry_of(js_getthis(js)); if (!entry) return js_false; return js_bool(uv_has_ref((const uv_handle_t *)&entry->handle) != 0);}
static void immediate_detach_handle(immediate_entry_t *entry) { if (is_object_type(entry->handle)) js_clear_native(entry->handle, IMMEDIATE_NATIVE_TAG); entry->handle = js_mkundef();}
static ant_value_t js_immediate_ref(ant_params_t) { immediate_entry_t *entry = immediate_entry_of(js_getthis(js)); if (entry) entry->refed = true; return js_getthis(js);}
static ant_value_t js_immediate_unref(ant_params_t) { immediate_entry_t *entry = immediate_entry_of(js_getthis(js)); if (entry) entry->refed = false; return js_getthis(js);}
static ant_value_t js_immediate_has_ref(ant_params_t) { immediate_entry_t *entry = immediate_entry_of(js_getthis(js)); return js_bool(entry && entry->active && entry->refed);}
static ant_value_t js_immediate_dispose(ant_params_t) { immediate_entry_t *entry = immediate_entry_of(js_getthis(js)); if (entry) entry->active = 0; return js_mkundef();}
static ant_value_t immediate_make_object(ant_t *js, immediate_entry_t *entry) { ant_value_t obj = js_mkobj_from_template(js, js->builtins.immediate_template); if (is_err(obj)) return obj; ant_object_t *ptr = js_obj_ptr(obj); ant_object_prop_set_unchecked(ptr, 0, entry->callback); gc_write_barrier(js, ptr, entry->callback); js_set_native(obj, entry, IMMEDIATE_NATIVE_TAG); entry->handle = obj; return obj;}
// uv_close completes on a later loop turn, possibly after the isolate is gone,// so the entry leaves its isolate's list when it is closed, not herestatic void timer_close_cb(uv_handle_t *h) { timer_entry_t *entry = (timer_entry_t *)h->data; if (!entry) return; timer_release_callback_args(entry); entry->handle_closed = true; if (entry->detached) free(entry);}
static void timer_close_entry(ant_t *js, timer_entry_t *entry) { if (!entry || entry->closed) return; if (entry->active) { entry->active = 0; js->timers.active_timer_count--; if (uv_has_ref((const uv_handle_t *)&entry->handle)) js->timers.active_refed_timer_count--; } remove_timer_entry(js, entry); entry->closed = 1; entry->obj = js_mkundef(); if (!uv_is_closing((uv_handle_t *)&entry->handle)) uv_close((uv_handle_t *)&entry->handle, timer_close_cb);}
// the entry outlives its uv handle while the timer object is alive, and is// freed by whichever of uv_close and the object's finalizer finishes laststatic void timer_object_finalize(ant_t *js, ant_object_t *obj) { timer_entry_t *entry = timer_entry_of(js_obj_from_ptr(obj)); if (!entry) return; entry->detached = true; if (!entry->closed) timer_close_entry(js, entry); else if (entry->handle_closed) free(entry);}
static ant_value_t timer_make_object(ant_t *js, timer_entry_t *entry, double delay_ms, ant_value_t timer_args) { ant_value_t obj = js_mkobj_from_template(js, entry->is_interval ? js->builtins.interval_template : js->builtins.timeout_template ); if (is_err(obj)) { entry->detached = true; timer_close_entry(js, entry); return obj; }
ant_object_t *ptr = js_obj_ptr(obj); ant_value_t delay = js_mknum(delay_ms); ant_object_prop_set_unchecked(ptr, 0, delay); ant_object_prop_set_unchecked(ptr, 1, entry->is_interval ? delay : js_mknull()); ant_object_prop_set_unchecked(ptr, 2, entry->callback); gc_write_barrier(js, ptr, entry->callback); if (vtype(timer_args) != kTypeUndefined) js_set_slot_wb(js, obj, SLOT_AUX, timer_args); js_set_native(obj, entry, TIMER_NATIVE_TAG); js_set_finalizer(obj, timer_object_finalize); entry->obj = obj; timer_entry_hold_young(js, entry);
return obj;}
static void timer_callback(uv_timer_t *handle) { GC_UV_CALLBACK(); timer_entry_t *entry = (timer_entry_t *)handle->data; if (!entry || entry->closed || !entry->active) return; ant_t *js = Ant_Loop_Isolate(handle->loop); ant_value_t callback = entry->callback; if (!entry->is_interval) { entry->active = 0; js->timers.active_timer_count--; if (uv_has_ref((const uv_handle_t *)&entry->handle)) js->timers.active_refed_timer_count--; }
ant_value_t self = entry->obj; GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, callback); GC_ROOT_PIN(js, self); for (int i = 0; i < entry->nargs; i++) GC_ROOT_PIN(js, entry->args[i]); sv_vm_call(js->vm, js, callback, self, entry->args, entry->nargs, NULL, js_mkundef()); GC_ROOT_RESTORE(js, root_mark); if (!entry->is_interval && !entry->active) timer_release_callback_args(entry); process_report_uncaught_exception_if_pending(js); process_microtasks(js);}
static void timer_start(ant_t *js, timer_entry_t *entry, uint64_t timeout, uint64_t repeat) { uv_update_time(js->loop); uv_timer_start(&entry->handle, timer_callback, timeout, repeat);}
static ant_value_t js_timer_refresh(ant_params_t) { ant_value_t this_obj = js_getthis(js); timer_entry_t *entry = timer_open_entry_of(this_obj); if (!entry) return this_obj;
if (!entry->active) { if (vtype(entry->callback) == kTypeUndefined) { entry->callback = js_get(js, this_obj, "callback"); if (!is_callable(entry->callback)) return this_obj; if (timer_copy_args_from_object(js, entry, this_obj) != 0) return js_mkerr(js, "failed to allocate timer args"); timer_entry_hold_young(js, entry); } entry->active = 1; js->timers.active_timer_count++; if (uv_has_ref((const uv_handle_t *)&entry->handle)) js->timers.active_refed_timer_count++; }
timer_start(js, entry, entry->timeout_ms, entry->is_interval ? entry->timeout_ms : 0); return this_obj;}
// setTimeout(callback, delay, ...args)static ant_value_t js_set_timeout(ant_params_t) { if (nargs < 1) return js_mkerr(js, "setTimeout requires at least 1 argument (callback)"); ant_value_t callback = args[0]; double delay_ms = nargs > 1 ? js_getnum(args[1]) : 0; uint64_t ms = delay_ms >= 1 ? (uint64_t)delay_ms : 0; ant_value_t timer_args = timer_make_args_array(js, args, nargs); timer_entry_t *entry = calloc(1, sizeof(timer_entry_t)); if (entry == NULL) return js_mkerr(js, "failed to allocate timer"); if (timer_copy_args(entry, args, nargs) < 0) { free(entry); return js_mkerr(js, "failed to allocate timer args"); } uv_timer_init(js->loop, &entry->handle); entry->handle.data = entry; entry->callback = callback; entry->timer_id = js->timers.next_timer_id++; entry->active = 1; entry->closed = 0; entry->is_interval = 0; entry->timeout_ms = ms; add_timer_entry(js, entry); js->timers.active_timer_count++; js->timers.active_refed_timer_count++; timer_start(js, entry, ms, 0);
return timer_make_object(js, entry, delay_ms, timer_args);}
// setInterval(callback, delay, ...args)static ant_value_t js_set_interval(ant_params_t) { if (nargs < 1) return js_mkerr(js, "setInterval requires at least 1 argument (callback)"); ant_value_t callback = args[0]; double delay_ms = nargs > 1 ? js_getnum(args[1]) : 0; uint64_t ms = delay_ms >= 1 ? (uint64_t)delay_ms : 1; ant_value_t timer_args = timer_make_args_array(js, args, nargs); timer_entry_t *entry = calloc(1, sizeof(timer_entry_t)); if (entry == NULL) return js_mkerr(js, "failed to allocate timer"); if (timer_copy_args(entry, args, nargs) < 0) { free(entry); return js_mkerr(js, "failed to allocate timer args"); } uv_timer_init(js->loop, &entry->handle); entry->handle.data = entry; entry->callback = callback; entry->timer_id = js->timers.next_timer_id++; entry->active = 1; entry->closed = 0; entry->is_interval = 1; entry->timeout_ms = ms; add_timer_entry(js, entry); js->timers.active_timer_count++; js->timers.active_refed_timer_count++; timer_start(js, entry, ms, ms);
return timer_make_object(js, entry, delay_ms, timer_args);}
// clearTimeout(timerId | timerObject)static ant_value_t js_clear_timeout(ant_params_t) { if (nargs < 1) return js_mkundef(); timer_entry_t *entry = timer_open_entry_of(args[0]); if (!entry && vtype(args[0]) == kTypeNumber) entry = find_timer_entry_by_id(js, (int)js_getnum(args[0])); if (entry) timer_close_entry(js, entry); return js_mkundef();}
// setImmediate(callback, ...args)static ant_value_t js_set_immediate(ant_params_t) { if (nargs < 1) { return js_mkerr(js, "setImmediate requires 1 argument (callback)"); } ant_value_t callback = args[0]; int argc = nargs - 1; immediate_entry_t *entry = calloc(1, sizeof(immediate_entry_t) + (size_t)argc * sizeof(ant_value_t)); if (entry == NULL) { return js_mkerr(js, "failed to allocate immediate"); } entry->callback = callback; entry->argc = argc; for (int i = 0; i < argc; i++) entry->argv[i] = args[i + 1]; entry->immediate_id = js->timers.next_immediate_id++; entry->active = 1; entry->refed = true; entry->handle = js_mkundef(); entry->next = NULL; if (js->timers.immediates_tail == NULL) { js->timers.immediates = entry; js->timers.immediates_tail = entry; } else { js->timers.immediates_tail->next = entry; js->timers.immediates_tail = entry; }
return immediate_make_object(js, entry);}
// clearImmediate(immediateId | immediateObject)static ant_value_t js_clear_immediate(ant_params_t) { if (nargs < 1) return js_mkundef(); immediate_entry_t *entry = immediate_entry_of(args[0]); if (entry) entry->active = 0; else if (vtype(args[0]) == kTypeNumber) { int immediate_id = (int)js_getnum(args[0]); immediate_entry_t *lists[2] = { js->timers.immediates_processing, js->timers.immediates }; for (int l = 0; l < 2 && !entry; l++) for (immediate_entry_t *it = lists[l]; it; it = it->next) if (it->immediate_id == immediate_id) { entry = it; break; } if (entry) entry->active = 0; } return js_mkundef();}
// queueMicrotask(callback)static ant_value_t js_queue_microtask(ant_params_t) { if (nargs < 1) { return js_mkerr(js, "queueMicrotask requires 1 argument (callback)"); } queue_microtask(js, args[0]); return js_mkundef();}
static ant_value_t timers_promises_get_state(ant_t *js) { return js_get_slot(js->current_func, SLOT_DATA);}
static void timers_promises_remove_abort_listener(ant_t *js, ant_value_t state) { ant_value_t signal = 0; ant_value_t listener = 0;
if (!is_object_type(state)) return;
signal = js_get(js, state, "signal"); listener = js_get(js, state, "abortListener");
if (abort_signal_is_signal(signal) && is_callable(listener)) abort_signal_remove_listener(js, signal, listener);
js_set(js, state, "abortListener", js_mkundef());}
static void timers_promises_settle(ant_t *js, ant_value_t state, bool reject, ant_value_t value) { ant_value_t settled = 0; ant_value_t promise = 0;
if (!is_object_type(state)) return;
settled = js_get(js, state, "settled"); if (js_truthy(js, settled)) return;
js_set(js, state, "settled", js_true); timers_promises_remove_abort_listener(js, state); js_set(js, state, "handle", js_mkundef());
promise = js_get(js, state, "promise"); if (reject) js_reject_promise(js, promise, value); else js_resolve_promise(js, promise, value);}
static ant_value_t timers_promises_resolve(ant_params_t) { ant_value_t state = timers_promises_get_state(js); ant_value_t value = js_mkundef(); if (!is_object_type(state)) return js_mkundef(); value = js_get(js, state, "value"); timers_promises_settle(js, state, false, value); return js_mkundef();}
static ant_value_t timers_promises_on_abort(ant_params_t) { ant_value_t state = timers_promises_get_state(js); ant_value_t signal = 0; ant_value_t handle = 0; ant_value_t is_immediate = 0; ant_value_t reason = js_mkundef(); ant_value_t clear_args[1];
if (!is_object_type(state)) return js_mkundef();
signal = js_get(js, state, "signal"); handle = js_get(js, state, "handle"); is_immediate = js_get(js, state, "isImmediate");
if (vtype(handle) != kTypeUndefined && vtype(handle) != kTypeNull) { clear_args[0] = handle; if (js_truthy(js, is_immediate)) js_clear_immediate(js, clear_args, 1, js_mkundef()); else js_clear_timeout(js, clear_args, 1, js_mkundef()); }
if (abort_signal_is_signal(signal)) reason = abort_signal_make_error(js, signal); else reason = Ant_Error_Create(js, JS_ERR_TYPE, "The operation was aborted");
GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, reason);
timers_promises_settle(js, state, true, reason); GC_ROOT_RESTORE(js, root_mark);
return js_mkundef();}
static bool timers_promises_parse_options( ant_t *js, ant_value_t value, ant_value_t *signal_out, ant_value_t *error_out) { ant_value_t signal = js_mkundef();
if (signal_out) *signal_out = js_mkundef(); if (error_out) *error_out = js_mkundef();
if (vtype(value) == kTypeUndefined || vtype(value) == kTypeNull) return true; if (vtype(value) != kTypeObject) { if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Timer options must be an object"); return false; }
signal = js_get(js, value, "signal"); if (is_err(signal)) { if (error_out) *error_out = signal; return false; }
if (vtype(signal) != kTypeUndefined && vtype(signal) != kTypeNull && !abort_signal_is_signal(signal)) { if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "options.signal must be an AbortSignal"); return false; }
if (signal_out) *signal_out = signal; return true;}
static ant_value_t timers_promises_schedule( ant_t *js, double delay_ms, ant_value_t value, ant_value_t signal, bool is_immediate) { ant_value_t promise = js_mkpromise(js); ant_value_t state = Ant_Object_CreateBare(js); ant_value_t callback = 0; ant_value_t handle = 0; ant_value_t args[2];
if (abort_signal_is_signal(signal) && abort_signal_is_aborted(signal)) { js_reject_promise(js, promise, abort_signal_make_error(js, signal)); return promise; }
js_set(js, state, "promise", promise); js_set(js, state, "value", value); js_set(js, state, "signal", signal); js_set(js, state, "abortListener", js_mkundef()); js_set(js, state, "handle", js_mkundef()); js_set(js, state, "settled", js_false); js_set(js, state, "isImmediate", js_bool(is_immediate));
callback = js_heavy_mkfun(js, timers_promises_resolve, state); if (is_immediate) handle = js_set_immediate(js, &callback, 1, js_mkundef()); else { args[0] = callback; args[1] = js_mknum(delay_ms); handle = js_set_timeout(js, args, 2, js_mkundef()); }
if (is_err(handle)) { js_reject_promise(js, promise, handle); return promise; }
js_set(js, state, "handle", handle);
if (abort_signal_is_signal(signal)) { ant_value_t listener = js_heavy_mkfun(js, timers_promises_on_abort, state); js_set(js, state, "abortListener", listener); abort_signal_add_listener(js, signal, listener); }
return promise;}
static ant_value_t js_timers_promises_setTimeout(ant_params_t) { double delay_ms = nargs > 0 ? js_getnum(args[0]) : 0; ant_value_t value = nargs > 1 ? args[1] : js_mkundef(); ant_value_t options = nargs > 2 ? args[2] : js_mkundef(); ant_value_t signal = js_mkundef(); ant_value_t error = js_mkundef();
if (!timers_promises_parse_options(js, options, &signal, &error)) return error; return timers_promises_schedule(js, delay_ms, value, signal, false);}
static ant_value_t js_timers_promises_setImmediate(ant_params_t) { ant_value_t value = nargs > 0 ? args[0] : js_mkundef(); ant_value_t options = nargs > 1 ? args[1] : js_mkundef(); ant_value_t signal = js_mkundef(); ant_value_t error = js_mkundef();
if (!timers_promises_parse_options(js, options, &signal, &error)) return error; return timers_promises_schedule(js, 0, value, signal, true);}
static ant_value_t js_timers_promises_setInterval(ant_params_t) { return js_mkerr_typed(js, JS_ERR_TYPE, "node:timers/promises setInterval() is not implemented yet");}
static ant_value_t js_timers_promises_scheduler_wait(ant_params_t) { return js_timers_promises_setTimeout(js, args, nargs, js_mkundef());}
static ant_value_t js_timers_promises_scheduler_yield(ant_params_t) { return js_timers_promises_setImmediate(js, args, nargs, js_mkundef());}
static constexpr int MICROTASK_POOLED_ARGS = 1;static constexpr uint32_t MICROTASK_FREE_JOBS_MAX = 256;static constexpr size_t MICROTASK_POOLED_BYTES = sizeof(microtask_entry_t) + MICROTASK_POOLED_ARGS * sizeof(ant_value_t);
static microtask_entry_t *microtask_entry_new(ant_t *js, int nargs) { if (nargs > MICROTASK_POOLED_ARGS) return calloc(1, sizeof(microtask_entry_t) + (size_t)nargs * sizeof(ant_value_t));
microtask_entry_t *entry = js->timers.free_jobs; if (!entry) return calloc(1, MICROTASK_POOLED_BYTES);
js->timers.free_jobs = entry->next; js->timers.free_job_count--; memset(entry, 0, MICROTASK_POOLED_BYTES); return entry;}
static void microtask_entry_free(ant_t *js, microtask_entry_t *entry) { if (entry->argc > MICROTASK_POOLED_ARGS || js->timers.free_job_count >= MICROTASK_FREE_JOBS_MAX) { free(entry); return; } entry->next = js->timers.free_jobs; js->timers.free_jobs = entry; js->timers.free_job_count++;}
static void queue_microtask_entry( microtask_entry_t **head, microtask_entry_t **tail, microtask_entry_t *entry) { if (*tail == NULL) goto empty;
(*tail)->next = entry; *tail = entry; return;
empty: *head = entry; *tail = entry;}
static inline void queue_job(ant_t *js, microtask_entry_t *entry) { ant_realm_t *own = js->microtask_realm; if (__builtin_expect(own != NULL, 0)) queue_microtask_entry(&own->microtasks, &own->microtasks_tail, entry); else queue_microtask_entry(&js->timers.microtasks, &js->timers.microtasks_tail, entry);}
void queue_microtask(ant_t *js, ant_value_t callback) { microtask_entry_t *entry = microtask_entry_new(js, 0); if (entry == NULL) return; entry->callback = callback; entry->u.promise = js_mkundef(); entry->next = NULL; entry->argc = 0; queue_job(js, entry);}
void queue_microtask_with_args(ant_t *js, ant_value_t callback, ant_value_t *args, int nargs) { if (nargs <= 0) { queue_microtask(js, callback); return; } microtask_entry_t *entry = microtask_entry_new(js, nargs); if (entry == NULL) return; entry->callback = callback; entry->u.promise = js_mkundef(); entry->next = NULL; entry->argc = (uint8_t)nargs; for (int i = 0; i < nargs; i++) entry->argv[i] = args[i]; queue_job(js, entry);}
bool queue_promise_thenable_job(ant_t *js, ant_value_t promise, ant_value_t thenable, ant_value_t then_fn) { microtask_entry_t *entry = microtask_entry_new(js, 1); if (entry == NULL) return false;
entry->callback = then_fn; entry->u.this_val = thenable; entry->next = NULL; entry->argc = 1; entry->kind = MT_THENABLE_JOB; entry->argv[0] = promise;
queue_job(js, entry); return true;}
bool queue_generator_yield_job(ant_t *js, ant_value_t gen, ant_value_t value) { microtask_entry_t *entry = microtask_entry_new(js, 0); if (entry == NULL) return false;
entry->callback = value; entry->u.this_val = gen; entry->kind = MT_GENERATOR_YIELD; queue_microtask_entry(&js->timers.microtasks, &js->timers.microtasks_tail, entry); return true;}
bool queue_promise_resolve_job(ant_t *js, ant_value_t promise, ant_value_t value) { microtask_entry_t *entry = microtask_entry_new(js, 0); if (entry == NULL) return false;
entry->callback = value; entry->u.promise = promise; entry->kind = MT_PROMISE_RESOLVE; queue_job(js, entry); return true;}
static bool queue_coro_job(coroutine_t *coro, ant_value_t value, uint8_t kind) { ant_t *js = coro->js; microtask_entry_t *entry = microtask_entry_new(js, 0); if (entry == NULL) return false;
entry->callback = value; entry->u.coro = coro; entry->next = NULL; entry->kind = kind;
coroutine_retain(coro); coro->await_resume_job = entry; queue_job(js, entry); return true;}
bool queue_await_resume_job(coroutine_t *coro, ant_value_t value) { return queue_coro_job(coro, value, MT_AWAIT_RESUME);}
bool queue_await_settle_job(coroutine_t *coro, ant_value_t value, bool is_error) { return queue_coro_job(coro, value, is_error ? MT_AWAIT_RESUME_THROW : MT_AWAIT_RESUME);}
bool queue_await_defer_job(coroutine_t *coro, ant_value_t value) { return queue_coro_job(coro, value, MT_AWAIT_DEFER);}
void queue_next_tick(ant_t *js, ant_value_t callback) { microtask_entry_t *entry = microtask_entry_new(js, 0); if (entry == NULL) return;
entry->callback = callback; entry->u.promise = js_mkundef(); entry->next = NULL; entry->argc = 0;
queue_microtask_entry(&js->timers.next_ticks, &js->timers.next_ticks_tail, entry);}
void queue_next_tick_with_args(ant_t *js, ant_value_t callback, ant_value_t *args, int nargs) { if (nargs <= 0) { queue_next_tick(js, callback); return; }
microtask_entry_t *entry = microtask_entry_new(js, nargs); if (entry == NULL) return;
entry->callback = callback; entry->u.promise = js_mkundef(); entry->next = NULL; entry->argc = (uint8_t)nargs;
for (int i = 0; i < nargs; i++) entry->argv[i] = args[i]; queue_microtask_entry(&js->timers.next_ticks, &js->timers.next_ticks_tail, entry);}
void queue_promise_trigger(ant_t *js, ant_value_t promise) { if (!js_mark_promise_trigger_queued(js, promise)) return;
microtask_entry_t *entry = microtask_entry_new(js, 0); if (entry == NULL) { js_mark_promise_trigger_dequeued(js, promise); return; } entry->callback = js_mkundef(); entry->u.promise = promise; entry->next = NULL; entry->kind = MT_PROMISE_TRIGGER;
queue_job(js, entry);}
static inline void process_microtask_entry(ant_t *js, microtask_entry_t *entry) { if (!entry) return;
if (entry->kind == MT_PROMISE_TRIGGER) { GC_ROOT_SAVE(root_mark, js); ant_value_t promise = entry->u.promise;
GC_ROOT_PIN(js, promise); js_mark_promise_trigger_dequeued(js, promise); js_process_promise_handlers(js, promise); GC_ROOT_RESTORE(js, root_mark);
return; }
if (entry->kind == MT_GENERATOR_YIELD) { Ant_Generator_SettleYield(js, entry->u.this_val, entry->callback); return; }
if (entry->kind == MT_THENABLE_JOB) { js_process_promise_thenable_job( js, entry->argv[0], entry->u.this_val, entry->callback ); return; }
if (entry->kind == MT_PROMISE_RESOLVE) { GC_ROOT_SAVE(root_mark, js); ant_value_t promise = entry->u.promise; ant_value_t value = entry->callback; GC_ROOT_PIN(js, promise); GC_ROOT_PIN(js, value); js_resolve_promise(js, promise, value); GC_ROOT_RESTORE(js, root_mark); return; }
if (entry->kind == MT_AWAIT_DEFER) { GC_ROOT_SAVE(root_mark, js); ant_value_t value = entry->callback; coroutine_t *coro = entry->u.coro; GC_ROOT_PIN(js, value);
if ( coro->await_registered && coro->await_resume_job == entry && !queue_await_resume_job(coro, value) ) Ant_Coroutine_ResumeAwaitJob(js, coro, value, false);
coroutine_release(coro); GC_ROOT_RESTORE(js, root_mark); return; }
if (entry->kind == MT_AWAIT_RESUME || entry->kind == MT_AWAIT_RESUME_THROW) { GC_ROOT_SAVE(root_mark, js); ant_value_t value = entry->callback; coroutine_t *coro = entry->u.coro; GC_ROOT_PIN(js, value);
if (coro->await_registered && coro->await_resume_job == entry) Ant_Coroutine_ResumeAwaitJob(js, coro, value, entry->kind == MT_AWAIT_RESUME_THROW);
coroutine_release(coro); GC_ROOT_RESTORE(js, root_mark); return; }
ant_value_t this_val = js_mkundef(); GC_ROOT_SAVE(root_mark, js); ant_value_t callback = entry->callback; GC_ROOT_PIN(js, callback); GC_ROOT_PIN(js, this_val);
for (uint8_t i = 0; i < entry->argc; i++) GC_ROOT_PIN(js, entry->argv[i]); sv_vm_call(js->vm, js, callback, this_val, entry->argv, entry->argc, NULL, js_mkundef());
GC_ROOT_RESTORE(js, root_mark); process_report_uncaught_exception_if_pending(js);}
static inline microtask_entry_t *take_microtask_batch(ant_t *js) { microtask_entry_t *batch = js->timers.microtasks;
js->timers.microtasks = NULL; js->timers.microtasks_tail = NULL; js->timers.microtasks_processing = batch; return batch;}
static inline microtask_entry_t *take_next_tick_batch(ant_t *js) { microtask_entry_t *batch = js->timers.next_ticks;
js->timers.next_ticks = NULL; js->timers.next_ticks_tail = NULL; js->timers.next_ticks_processing = batch; return batch;}
static inline void process_microtask_batch(ant_t *js, microtask_entry_t *batch) { while (batch != NULL) { microtask_entry_t *entry = batch; batch = entry->next; js->timers.microtasks_processing = batch; process_microtask_entry(js, entry); microtask_entry_free(js, entry); }}
static inline void process_next_tick_batch(ant_t *js, microtask_entry_t *batch) { while (batch != NULL) { microtask_entry_t *entry = batch; batch = entry->next; js->timers.next_ticks_processing = batch; process_microtask_entry(js, entry); microtask_entry_free(js, entry); }}
static void process_microtasks_internal(ant_t *js, bool check_unhandled_rejections) { microtask_entry_t *batch = NULL;
if (!js || js->microtasks_draining || js->destroying) return; bool at_job_boundary = js->vm_exec_depth == 0; js->microtasks_draining = true;
while (js->timers.next_ticks != NULL || js->timers.microtasks != NULL) { while ((batch = js->timers.next_ticks) != NULL) { batch = take_next_tick_batch(js); process_next_tick_batch(js, batch); } while ((batch = js->timers.microtasks) != NULL) { batch = take_microtask_batch(js); process_microtask_batch(js, batch); } }
js->timers.next_ticks_processing = NULL; js->timers.microtasks_processing = NULL; if (check_unhandled_rejections) js_check_unhandled_rejections(js); js->microtasks_draining = false; if (at_job_boundary) gc_weak_clear_kept_alive(js);}
void ant_realm_run_microtasks(ant_t *js, ant_realm_t *realm) {while (realm->microtasks) { microtask_entry_t *batch = realm->microtasks; realm->microtasks = realm->microtasks_tail = NULL; while (batch) { microtask_entry_t *entry = batch; batch = entry->next; realm->microtasks_processing = batch; process_microtask_entry(js, entry); microtask_entry_free(js, entry); }}}
void process_microtasks(ant_t *js) { process_microtasks_internal(js, true);}
bool js_maybe_drain_microtasks(ant_t *js) { if (!js) return false; if (js->microtasks_draining) return false; if (js->vm_exec_depth != 0) return false; if (!has_pending_microtasks(js)) return false; process_microtasks_internal(js, true); return true;}
bool js_maybe_drain_microtasks_after_async_settle(ant_t *js) { if (!js) return false;
if (js->microtasks_draining) return false; if (!has_pending_microtasks(js)) return false;
process_microtasks_internal(js, false); return true;}
void process_immediates(ant_t *js) { ant_timer_state_t *t = &js->timers; if (t->immediates_processing) return; t->immediates_processing = t->immediates; t->immediates = t->immediates_tail = NULL; while (t->immediates_processing) { immediate_entry_t *entry = t->immediates_processing; t->immediates_processing = entry->next; ant_value_t self = entry->handle; immediate_detach_handle(entry); if (entry->active) { GC_ROOT_SAVE(root_mark, js); GC_ROOT_PIN(js, entry->callback); GC_ROOT_PIN(js, self); for (int i = 0; i < entry->argc; i++) GC_ROOT_PIN(js, entry->argv[i]); sv_vm_call(js->vm, js, entry->callback, self, entry->argv, entry->argc, NULL, js_mkundef()); GC_ROOT_RESTORE(js, root_mark); process_report_uncaught_exception_if_pending(js); process_microtasks(js); } free(entry); }}
int has_pending_immediates(ant_t *js) { for ( immediate_entry_t *entry = js->timers.immediates; entry != NULL; entry = entry->next ) if (entry->active && entry->refed) return 1; return 0;}
int has_active_immediates(ant_t *js) { for ( immediate_entry_t *entry = js->timers.immediates; entry != NULL; entry = entry->next ) if (entry->active) return 1; return 0;}
int has_pending_timers(ant_t *js) { return js->timers.active_refed_timer_count > 0;}
int has_pending_microtasks(ant_t *js) { return (js->timers.next_ticks != NULL || js->timers.microtasks != NULL) ? 1 : 0;}
static void timers_define_common(ant_t *js, ant_value_t obj) { js_set(js, obj, "setTimeout", Ant_Function_CreateEx(js_set_timeout, 2, CFUNC_HAS_PROTOTYPE)); js_set(js, obj, "clearTimeout", Ant_Function_CreateEx(js_clear_timeout, 1, CFUNC_HAS_PROTOTYPE)); js_set(js, obj, "setInterval", Ant_Function_CreateEx(js_set_interval, 2, CFUNC_HAS_PROTOTYPE)); js_set(js, obj, "clearInterval", Ant_Function_CreateEx(js_clear_timeout, 1, CFUNC_HAS_PROTOTYPE)); js_set(js, obj, "setImmediate", Ant_Function_CreateEx(js_set_immediate, 1, CFUNC_HAS_PROTOTYPE)); js_set(js, obj, "clearImmediate", Ant_Function_CreateEx(js_clear_immediate, 1, CFUNC_HAS_PROTOTYPE)); js_set(js, obj, "queueMicrotask", Ant_Function_CreateWithArity(js_queue_microtask, 1));}
static ant_value_t timer_make_template(ant_t *js, ant_value_t proto, ant_value_t repeat) { ant_value_t template = Ant_Object_CreateBare(js); js_set_proto_init(template, proto); js_set(js, template, "delay", js_mknum(0)); js_set(js, template, "repeat", repeat); js_set(js, template, "callback", js_mkundef()); js_set_descriptor(js, template, "callback", 8, JS_DESC_W | JS_DESC_C); return template;}
void init_timer_module(ant_t *js) { js->timers.next_timer_id = 1; js->timers.next_immediate_id = 1;
js->builtins.timeout_proto = Ant_Object_CreateBare(js); js->builtins.interval_proto = Ant_Object_CreateBare(js);
js_set_proto_init(js->builtins.timeout_proto, js->sym.object_proto); js_set(js, js->builtins.timeout_proto, "ref", Ant_Function_Create(js_timer_ref)); js_set(js, js->builtins.timeout_proto, "unref", Ant_Function_Create(js_timer_unref)); js_set(js, js->builtins.timeout_proto, "hasRef", Ant_Function_Create(js_timer_has_ref)); js_set(js, js->builtins.timeout_proto, "refresh", Ant_Function_Create(js_timer_refresh)); js_set_tostringtag(js, js->builtins.timeout_proto, js_mkstr(js, "Timeout", 7)); js_set_sym(js, js->builtins.timeout_proto, js->sym.inspect_sym, Ant_Function_Create(timer_inspect)); js_set_sym(js, js->builtins.timeout_proto, js->sym.toPrimitive_sym, Ant_Function_Create(timer_to_primitive));
js->builtins.immediate_proto = Ant_Object_CreateBare(js); js_set_proto_init(js->builtins.immediate_proto, js->sym.object_proto); js_set(js, js->builtins.immediate_proto, "ref", Ant_Function_Create(js_immediate_ref)); js_set(js, js->builtins.immediate_proto, "unref", Ant_Function_Create(js_immediate_unref)); js_set(js, js->builtins.immediate_proto, "hasRef", Ant_Function_Create(js_immediate_has_ref)); js_set_sym(js, js->builtins.immediate_proto, js->sym.dispose_sym, Ant_Function_Create(js_immediate_dispose)); js_set_tostringtag(js, js->builtins.immediate_proto, js_mkstr(js, "Immediate", 9)); js_set_sym(js, js->builtins.immediate_proto, js->sym.inspect_sym, Ant_Function_Create(timer_inspect));
js->builtins.immediate_template = Ant_Object_CreateBare(js); js_set_proto_init(js->builtins.immediate_template, js->builtins.immediate_proto); js_set(js, js->builtins.immediate_template, "callback", js_mkundef()); js_set_descriptor(js, js->builtins.immediate_template, "callback", 8, JS_DESC_W | JS_DESC_C);
js_set_proto_init(js->builtins.interval_proto, js->sym.object_proto); js_set(js, js->builtins.interval_proto, "ref", Ant_Function_Create(js_timer_ref)); js_set(js, js->builtins.interval_proto, "unref", Ant_Function_Create(js_timer_unref)); js_set(js, js->builtins.interval_proto, "hasRef", Ant_Function_Create(js_timer_has_ref)); js_set(js, js->builtins.interval_proto, "refresh", Ant_Function_Create(js_timer_refresh)); js_set_tostringtag(js, js->builtins.interval_proto, js_mkstr(js, "Interval", 8)); js_set_sym(js, js->builtins.interval_proto, js->sym.inspect_sym, Ant_Function_Create(timer_inspect)); js_set_sym(js, js->builtins.interval_proto, js->sym.toPrimitive_sym, Ant_Function_Create(timer_to_primitive)); js->builtins.timeout_template = timer_make_template(js, js->builtins.timeout_proto, js_mknull()); js->builtins.interval_template = timer_make_template(js, js->builtins.interval_proto, js_mknum(0));
timers_define_common(js, js_glob(js));}
ant_value_t timers_library(ant_t *js) { ant_value_t lib = Ant_Object_CreateBare(js);
timers_define_common(js, lib); js_set_tostringtag(js, lib, js_mkstr(js, "timers", 6));
return lib;}
// TODO: mostly stubbedant_value_t timers_promises_library(ant_t *js) { ant_value_t lib = Ant_Object_CreateBare(js); ant_value_t scheduler = Ant_Object_CreateBare(js);
js_set(js, lib, "scheduler", scheduler); js_set(js, lib, "setTimeout", Ant_Function_CreateWithArity(js_timers_promises_setTimeout, 2)); js_set(js, lib, "setImmediate", Ant_Function_CreateWithArity(js_timers_promises_setImmediate, 1)); js_set(js, lib, "setInterval", Ant_Function_CreateWithArity(js_timers_promises_setInterval, 2)); js_set(js, scheduler, "wait", Ant_Function_Create(js_timers_promises_scheduler_wait)); js_set(js, scheduler, "yield", Ant_Function_Create(js_timers_promises_scheduler_yield)); js_set_tostringtag(js, lib, js_mkstr(js, "timers/promises", 16));
return lib;}
static void discard_jobs(ant_t *js, microtask_entry_t *entry) { while (entry) { microtask_entry_t *next = entry->next; if (microtask_holds_coro(entry->kind)) { coroutine_clear_await_registration(entry->u.coro); coroutine_release(entry->u.coro); } else if (entry->kind == MT_PROMISE_TRIGGER) js_mark_promise_trigger_dequeued(js, entry->u.promise); free(entry); entry = next; }}
void cleanup_timer_module(ant_t *js) { ant_timer_state_t *t = &js->timers; ANT_ASSERT( !t->microtasks_processing && !t->next_ticks_processing && !t->immediates_processing, "cannot destroy an isolate while it runs a job batch" );
while (t->timers) timer_close_entry(js, t->timers); discard_jobs(js, t->microtasks); discard_jobs(js, t->next_ticks); for (ant_realm_t *realm = js->realms; realm; realm = realm->next) { discard_jobs(js, realm->microtasks); realm->microtasks = realm->microtasks_tail = NULL; } t->microtasks = t->microtasks_tail = NULL; t->next_ticks = t->next_ticks_tail = NULL;
for (immediate_entry_t *entry = t->immediates, *next; entry; entry = next) { next = entry->next; free(entry); } t->immediates = t->immediates_tail = NULL;
while (t->free_jobs) { microtask_entry_t *next = t->free_jobs->next; free(t->free_jobs); t->free_jobs = next; } t->free_job_count = 0;}
static void gc_mark_jobs(ant_t *js, microtask_entry_t *m, gc_mark_fn mark) { for (; m; m = m->next) { mark(js, m->callback); if (microtask_holds_coro(m->kind)) gc_mark_coroutine(js, m->u.coro); else mark(js, m->u.promise); for (uint8_t i = 0; i < m->argc; i++) mark(js, m->argv[i]); }}
void gc_mark_timers(ant_t *js, gc_mark_fn mark) { bool minor = js->gc.minor; for (timer_entry_t *t = js->timers.timers; t; t = t->next) { if (minor && !t->young) break; if (minor) t->young = false; if (!t->active) continue; if (is_object_type(t->obj)) mark(js, t->obj); mark(js, t->callback); for (int i = 0; i < t->nargs; i++) mark(js, t->args[i]); } gc_mark_jobs(js, js->timers.microtasks, mark); gc_mark_jobs(js, js->timers.microtasks_processing, mark); for (ant_realm_t *realm = js->realms; realm; realm = realm->next) { gc_mark_jobs(js, realm->microtasks, mark); gc_mark_jobs(js, realm->microtasks_processing, mark); } for (microtask_entry_t *m = js->timers.next_ticks; m; m = m->next) { mark(js, m->callback); mark(js, m->u.promise); for (uint8_t i = 0; i < m->argc; i++) mark(js, m->argv[i]); } for (microtask_entry_t *m = js->timers.next_ticks_processing; m; m = m->next) { mark(js, m->callback); mark(js, m->u.promise); for (uint8_t i = 0; i < m->argc; i++) mark(js, m->argv[i]); } immediate_entry_t *immediate_lists[2] = { js->timers.immediates, js->timers.immediates_processing }; for (int l = 0; l < 2; l++) for (immediate_entry_t *i = immediate_lists[l]; i; i = i->next) { mark(js, i->callback); mark(js, i->handle); for (int a = 0; a < i->argc; a++) mark(js, i->argv[a]); }}