#include #include #include #include #include #include #include #include "ant.h" #include "errors.h" #include "runtime.h" #include "internal.h" #include "descriptors.h" #include "silver/engine.h" #include "gc/modules.h" #include "modules/abort.h" #include "modules/events.h" #include "modules/symbol.h" typedef struct { bool canceled; bool stop_immediate; bool stop_propagation; bool dispatching; } event_data_t; static ant_value_t g_isTrusted_getter = 0; static ant_value_t g_eventemitter_ctor = 0; static ant_value_t g_eventemitter_proto = 0; static ant_value_t g_eventtarget_proto = 0; static ant_value_t g_event_proto = 0; static ant_value_t g_customevent_proto = 0; static ant_value_t g_errorevent_proto = 0; static ant_value_t g_promiserejectionevent_proto = 0; static event_data_t *get_event_data(ant_value_t obj) { ant_value_t slot = js_get_slot(obj, SLOT_DATA); if (vtype(slot) != T_NUM) return NULL; return (event_data_t *)(uintptr_t)js_getnum(slot); } static double get_timestamp_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (double)ts.tv_sec * 1e3 + (double)ts.tv_nsec / 1e6; } typedef struct { ant_value_t callback; ant_value_t raw_callback; ant_value_t signal; bool once; bool capture; } EventListenerEntry; static const UT_icd event_listener_icd = { sizeof(EventListenerEntry), NULL, NULL, NULL }; typedef struct { unsigned char buf[48]; unsigned char *ptr; size_t len; } evt_key_t; typedef struct { UT_array *listeners; ant_value_t js_key; unsigned char *hash_key; size_t hash_key_len; bool warned_max_listeners; UT_hash_handle hh; } EventType; typedef struct emitter_reg { EventType **events; struct emitter_reg *next; } emitter_reg_t; static EventType *global_events = NULL; static emitter_reg_t *emitter_registry = NULL; static EventType *make_event_type(ant_value_t js_key, const evt_key_t *ek) { EventType *evt = ant_calloc(sizeof(EventType) + ek->len); if (!evt) return NULL; evt->js_key = js_key; evt->hash_key = (unsigned char *)(evt + 1); evt->hash_key_len = ek->len; memcpy(evt->hash_key, ek->ptr, ek->len); utarray_new(evt->listeners, &event_listener_icd); return evt; } static EventType *evt_find(EventType *table, const evt_key_t *ek) { EventType *evt = NULL; HASH_FIND(hh, table, ek->ptr, ek->len, evt); return evt; } static EventType *evt_find_or_create(EventType **table, ant_value_t js_key, const evt_key_t *ek) { EventType *evt = evt_find(*table, ek); if (!evt) { evt = make_event_type(js_key, ek); if (!evt) return NULL; HASH_ADD_KEYPTR(hh, *table, evt->hash_key, evt->hash_key_len, evt); } return evt; } static void evt_key_reset(evt_key_t *k) { k->ptr = k->buf; k->len = 0; } static void evt_key_free(evt_key_t *k) { if (k->ptr != k->buf) free(k->ptr); evt_key_reset(k); } static bool evt_key_init(ant_t *js, ant_value_t arg, evt_key_t *out) { evt_key_reset(out); uint8_t tag = (uint8_t)vtype(arg); if (tag == T_STR) { size_t slen = 0; const char *s = js_getstr(js, arg, &slen); out->len = 1 + slen; if (out->len > sizeof(out->buf)) { out->ptr = malloc(out->len); if (!out->ptr) { evt_key_reset(out); return false; } } out->ptr[0] = tag; if (slen) memcpy(out->ptr + 1, s, slen); return true; } if (tag == T_SYMBOL) { out->len = 1 + sizeof(ant_value_t); out->ptr[0] = tag; memcpy(out->ptr + 1, &arg, sizeof(ant_value_t)); return true; } return false; } static EventType **get_or_create_emitter_events(ant_t *js, ant_value_t this_obj) { ant_value_t slot = js_get_slot(this_obj, SLOT_DATA); if (vtype(slot) == T_UNDEF) { EventType **events = ant_calloc(sizeof(EventType *)); if (!events) return NULL; *events = NULL; emitter_reg_t *reg = ant_calloc(sizeof(emitter_reg_t)); if (!reg) { free(events); return NULL; } reg->events = events; reg->next = emitter_registry; emitter_registry = reg; js_set_slot(this_obj, SLOT_DATA, ANT_PTR(events)); return events; } return (EventType **)(uintptr_t)js_getnum(slot); } static EventType *find_or_create_global_event_type(ant_t *js, ant_value_t js_key) { evt_key_t ek; if (!evt_key_init(js, js_key, &ek)) return NULL; EventType *evt = evt_find_or_create(&global_events, js_key, &ek); evt_key_free(&ek); return evt; } static EventType *find_global_event_type(ant_t *js, ant_value_t js_key) { evt_key_t ek; if (!evt_key_init(js, js_key, &ek)) return NULL; EventType *evt = evt_find(global_events, &ek); evt_key_free(&ek); return evt; } static EventType *find_or_create_emitter_event_type(ant_t *js, ant_value_t this_obj, ant_value_t js_key) { EventType **events = get_or_create_emitter_events(js, this_obj); if (!events) return NULL; evt_key_t ek; if (!evt_key_init(js, js_key, &ek)) return NULL; EventType *evt = evt_find_or_create(events, js_key, &ek); evt_key_free(&ek); return evt; } static EventType *find_emitter_event_type(ant_t *js, ant_value_t this_obj, ant_value_t js_key) { EventType **events = get_or_create_emitter_events(js, this_obj); if (!events) return NULL; evt_key_t ek; if (!evt_key_init(js, js_key, &ek)) return NULL; EventType *evt = evt_find(*events, &ek); evt_key_free(&ek); return evt; } static inline ant_value_t evt_key_from_arg(ant_value_t arg) { uint8_t t = vtype(arg); return (t == T_STR || t == T_SYMBOL) ? arg : 0; } static bool is_eventemitter_instance(ant_value_t target) { return js_check_brand(target, BRAND_EVENTEMITTER); } static bool is_eventtarget_instance(ant_value_t target) { return js_check_brand(target, BRAND_EVENTTARGET); } static int eventemitter_get_max_listeners_impl(ant_value_t target) { ant_value_t slot = js_get_slot(target, SLOT_EVENT_MAX_LISTENERS); if (vtype(slot) == T_NUM) { int n = (int)js_getnum(slot); return n >= 0 ? n : EVENTS_DEFAULT_MAX_LISTENERS; } return EVENTS_DEFAULT_MAX_LISTENERS; } static ant_value_t eventemitter_call_listener( ant_t *js, ant_value_t listener, ant_value_t this_val, ant_value_t *args, int nargs ) { if (!is_callable(listener)) return js_mkundef(); if (sv_check_c_stack_overflow(js)) return js_mkerr_typed(js, JS_ERR_RANGE | JS_ERR_NO_STACK, "Maximum call stack size exceeded"); sv_call_plan_t plan; ant_value_t err = sv_prepare_call( js->vm, js, listener, this_val, args, nargs, NULL, SV_CALL_MODE_NORMAL, &plan ); if (is_err(err)) return err; return sv_execute_call_plan(js->vm, js, &plan, NULL); } static ant_value_t js_eventemitter_once_wrapper(ant_t *js, ant_value_t *args, int nargs) { ant_value_t listener = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); if (!is_callable(listener)) return js_mkundef(); return eventemitter_call_listener(js, listener, js->this_val, args, nargs); } static ant_value_t eventemitter_get_listeners_array(ant_t *js, ant_value_t target, ant_value_t key, bool raw) { ant_value_t result = js_mkarr(js); EventType *evt = NULL; if (!is_object_type(target) || !key) return result; evt = find_emitter_event_type(js, target, key); if (!evt) return result; for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); if (!entry) continue; js_arr_push(js, result, raw && entry->once && is_callable(entry->raw_callback) ? entry->raw_callback : entry->callback ); } return result; } static void js_init_event_obj(ant_t *js, ant_value_t obj, ant_value_t type_val, bool bubbles, bool cancelable) { js_set(js, obj, "type", type_val); js_set(js, obj, "target", js_mknull()); js_set(js, obj, "srcElement", js_mknull()); js_set(js, obj, "currentTarget", js_mknull()); js_set(js, obj, "eventPhase", js_mknum(0)); js_set(js, obj, "bubbles", js_bool(bubbles)); js_set(js, obj, "cancelable", js_bool(cancelable)); js_set(js, obj, "defaultPrevented", js_false); js_set(js, obj, "returnValue", js_true); js_set(js, obj, "cancelBubble", js_false); js_set(js, obj, "timeStamp", js_mknum(get_timestamp_ms())); if (g_isTrusted_getter) js_set_accessor_desc(js, obj, "isTrusted", 9, g_isTrusted_getter, js_mkundef(), 0); event_data_t *data = ant_calloc(sizeof(event_data_t)); if (data) js_set_slot(obj, SLOT_DATA, ANT_PTR(data)); } static ant_value_t js_event_ctor(ant_t *js, ant_value_t *args, int nargs) { if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "Event constructor requires 'new'"); if (nargs < 1 || vtype(args[0]) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "Event constructor: type argument is required"); ant_value_t type_val = args[0]; if (vtype(type_val) != T_STR) { type_val = js_tostring_val(js, type_val); if (is_err(type_val)) return type_val; } bool bubbles = false, cancelable = false; if (nargs >= 2 && vtype(args[1]) == T_OBJ) { ant_value_t b = js_get(js, args[1], "bubbles"); ant_value_t c = js_get(js, args[1], "cancelable"); if (is_err(b)) return b; if (is_err(c)) return c; bubbles = js_truthy(js, b); cancelable = js_truthy(js, c); } ant_value_t this_obj = js_mkobj(js); ant_value_t proto = js_instance_proto_from_new_target(js, g_event_proto); if (is_object_type(proto)) js_set_proto_init(this_obj, proto); js_init_event_obj(js, this_obj, type_val, bubbles, cancelable); return this_obj; } static ant_value_t js_event_get_isTrusted(ant_t *js, ant_value_t *args, int nargs) { return js_false; } static ant_value_t js_eventemitter_ctor(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); if (is_object_type(this_obj)) { js_set_slot(this_obj, SLOT_BRAND, js_mknum(BRAND_EVENTEMITTER)); return this_obj; } if (vtype(js->new_target) != T_UNDEF) { ant_value_t obj = js_mkobj(js); ant_value_t proto = js_instance_proto_from_new_target(js, g_eventemitter_proto); if (is_object_type(proto)) js_set_proto_init(obj, proto); js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_EVENTEMITTER)); return obj; } return js_mkerr_typed(js, JS_ERR_TYPE, "EventEmitter constructor requires an object receiver or 'new'"); } static ant_value_t js_event_preventDefault(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); ant_value_t cancelable = js_get(js, this_obj, "cancelable"); if (!js_truthy(js, cancelable)) return js_mkundef(); event_data_t *data = get_event_data(this_obj); if (data) data->canceled = true; js_set(js, this_obj, "defaultPrevented", js_true); js_set(js, this_obj, "returnValue", js_false); return js_mkundef(); } static ant_value_t js_event_stopPropagation(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); event_data_t *data = get_event_data(this_obj); if (data) data->stop_propagation = true; js_set(js, this_obj, "cancelBubble", js_true); return js_mkundef(); } static ant_value_t js_event_stopImmediatePropagation(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); event_data_t *data = get_event_data(this_obj); if (data) { data->stop_immediate = true; data->stop_propagation = true; } js_set(js, this_obj, "cancelBubble", js_true); return js_mkundef(); } static ant_value_t js_event_composedPath(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); event_data_t *data = get_event_data(this_obj); ant_value_t result = js_mkarr(js); if (data && data->dispatching) { ant_value_t ct = js_get(js, this_obj, "currentTarget"); if (is_object_type(ct)) js_arr_push(js, result, ct); } return result; } static ant_value_t js_event_initEvent(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); if (nargs >= 1) { ant_value_t type_val = vtype(args[0]) == T_STR ? args[0] : js_tostring_val(js, args[0]); if (!is_err(type_val)) js_set(js, this_obj, "type", type_val); } if (nargs >= 2) js_set(js, this_obj, "bubbles", js_bool(js_truthy(js, args[1]))); if (nargs >= 3) js_set(js, this_obj, "cancelable", js_bool(js_truthy(js, args[2]))); js_set(js, this_obj, "defaultPrevented", js_false); js_set(js, this_obj, "returnValue", js_true); event_data_t *data = get_event_data(this_obj); if (data) { data->canceled = false; data->stop_immediate = false; data->stop_propagation = false; } return js_mkundef(); } static ant_value_t js_customevent_ctor(ant_t *js, ant_value_t *args, int nargs) { if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "CustomEvent constructor requires 'new'"); if (nargs < 1 || vtype(args[0]) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "CustomEvent constructor: type argument is required"); ant_value_t type_val = args[0]; if (vtype(type_val) != T_STR) { type_val = js_tostring_val(js, type_val); if (is_err(type_val)) return type_val; } bool bubbles = false, cancelable = false; ant_value_t detail = js_mknull(); if (nargs >= 2 && vtype(args[1]) == T_OBJ) { ant_value_t b = js_get(js, args[1], "bubbles"); ant_value_t c = js_get(js, args[1], "cancelable"); ant_value_t d = js_get(js, args[1], "detail"); if (is_err(b)) return b; if (is_err(c)) return c; bubbles = js_truthy(js, b); cancelable = js_truthy(js, c); if (vtype(d) != T_UNDEF) detail = d; } ant_value_t this_obj = js_mkobj(js); ant_value_t proto = js_instance_proto_from_new_target(js, g_customevent_proto); if (is_object_type(proto)) js_set_proto_init(this_obj, proto); js_init_event_obj(js, this_obj, type_val, bubbles, cancelable); js_set(js, this_obj, "detail", detail); return this_obj; } static ant_value_t js_errorevent_ctor(ant_t *js, ant_value_t *args, int nargs) { if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "ErrorEvent constructor requires 'new'"); if (nargs < 1 || vtype(args[0]) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "ErrorEvent constructor: type argument is required"); ant_value_t type_val = args[0]; if (vtype(type_val) != T_STR) { type_val = js_tostring_val(js, type_val); if (is_err(type_val)) return type_val; } bool bubbles = false, cancelable = false; ant_value_t message = js_mkstr(js, "", 0); ant_value_t filename = js_mkstr(js, "", 0); ant_value_t lineno = js_mknum(0); ant_value_t colno = js_mknum(0); ant_value_t error = js_mknull(); if (nargs >= 2 && vtype(args[1]) == T_OBJ) { ant_value_t b = js_get(js, args[1], "bubbles"); ant_value_t c = js_get(js, args[1], "cancelable"); ant_value_t m = js_get(js, args[1], "message"); ant_value_t f = js_get(js, args[1], "filename"); ant_value_t l = js_get(js, args[1], "lineno"); ant_value_t co = js_get(js, args[1], "colno"); ant_value_t e = js_get(js, args[1], "error"); if (is_err(b)) return b; if (is_err(c)) return c; bubbles = js_truthy(js, b); cancelable = js_truthy(js, c); if (vtype(m) != T_UNDEF) { message = js_tostring_val(js, m); if (is_err(message)) return message; } if (vtype(f) != T_UNDEF) { filename = js_tostring_val(js, f); if (is_err(filename)) return filename; } if (vtype(l) != T_UNDEF) lineno = l; if (vtype(co) != T_UNDEF) colno = co; if (vtype(e) != T_UNDEF) error = e; } ant_value_t this_obj = js_mkobj(js); ant_value_t proto = js_instance_proto_from_new_target(js, g_errorevent_proto); if (is_object_type(proto)) js_set_proto_init(this_obj, proto); js_init_event_obj(js, this_obj, type_val, bubbles, cancelable); js_set(js, this_obj, "message", message); js_set(js, this_obj, "filename", filename); js_set(js, this_obj, "lineno", lineno); js_set(js, this_obj, "colno", colno); js_set(js, this_obj, "error", error); return this_obj; } static ant_value_t js_promiserejectionevent_ctor(ant_t *js, ant_value_t *args, int nargs) { if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "PromiseRejectionEvent constructor requires 'new'"); if (nargs < 1 || vtype(args[0]) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "PromiseRejectionEvent constructor: type argument is required"); ant_value_t type_val = args[0]; if (vtype(type_val) != T_STR) { type_val = js_tostring_val(js, type_val); if (is_err(type_val)) return type_val; } bool bubbles = false, cancelable = false; ant_value_t promise = js_mkundef(); ant_value_t reason = js_mkundef(); if (nargs >= 2 && vtype(args[1]) == T_OBJ) { ant_value_t b = js_get(js, args[1], "bubbles"); ant_value_t c = js_get(js, args[1], "cancelable"); ant_value_t p = js_get(js, args[1], "promise"); ant_value_t r = js_get(js, args[1], "reason"); if (is_err(b)) return b; if (is_err(c)) return c; bubbles = js_truthy(js, b); cancelable = js_truthy(js, c); if (vtype(p) != T_UNDEF) promise = p; if (vtype(r) != T_UNDEF) reason = r; } ant_value_t this_obj = js_mkobj(js); ant_value_t proto = js_instance_proto_from_new_target(js, g_promiserejectionevent_proto); if (is_object_type(proto)) js_set_proto_init(this_obj, proto); js_init_event_obj(js, this_obj, type_val, bubbles, cancelable); js_set(js, this_obj, "promise", promise); js_set(js, this_obj, "reason", reason); return this_obj; } static ant_value_t js_eventtarget_ctor(ant_t *js, ant_value_t *args, int nargs) { if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "EventTarget constructor requires 'new'"); ant_value_t this_obj = js_getthis(js); if (is_object_type(this_obj)) js_set_slot(this_obj, SLOT_BRAND, js_mknum(BRAND_EVENTTARGET)); return js_mkundef(); } static bool parse_addEventListener_options(ant_t *js, ant_value_t *args, int nargs, bool *once, bool *capture, ant_value_t *signal) { *once = false; *capture = false; *signal = js_mkundef(); if (nargs < 3) return true; ant_value_t opts = args[2]; if (vtype(opts) == T_BOOL) { *capture = js_truthy(js, opts); return true; } if (vtype(opts) != T_OBJ) return true; ant_value_t sig = js_get(js, opts, "signal"); if (vtype(sig) == T_NULL) return false; if (vtype(sig) != T_UNDEF) { if (!abort_signal_is_signal(sig)) return false; *signal = sig; } ant_value_t o = js_get(js, opts, "once"); ant_value_t ca = js_get(js, opts, "capture"); if (vtype(o) != T_UNDEF) *once = js_truthy(js, o); if (vtype(ca) != T_UNDEF) *capture = js_truthy(js, ca); return true; } static ant_value_t add_listener_to(ant_t *js, ant_value_t *args, int nargs, EventType *evt) { if (!evt) return js_mkerr(js, "failed to create event type"); bool once, capture; ant_value_t signal; if (!parse_addEventListener_options(js, args, nargs, &once, &capture, &signal)) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to execute 'addEventListener': signal is not an AbortSignal"); if (vtype(signal) != T_UNDEF && abort_signal_is_aborted(signal)) return js_mkundef(); ant_value_t cb = args[1]; uint8_t cbt = vtype(cb); if (cbt == T_NULL || cbt == T_UNDEF) return js_mkundef(); if (cbt != T_FUNC && cbt != T_CFUNC) return js_mkundef(); for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { EventListenerEntry *e = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); if (e->callback == cb && e->capture == capture) return js_mkundef(); } EventListenerEntry entry = { cb, js_mkundef(), signal, once, capture }; utarray_push_back(evt->listeners, &entry); return js_mkundef(); } static ant_value_t remove_listener_from(ant_t *js, ant_value_t *args, int nargs, EventType *evt) { if (!evt) return js_mkundef(); bool capture = false; if (nargs >= 3) { ant_value_t opts = args[2]; if (vtype(opts) == T_BOOL) capture = js_truthy(js, opts); else if (vtype(opts) == T_OBJ) { ant_value_t ca = js_get(js, opts, "capture"); if (vtype(ca) != T_UNDEF) capture = js_truthy(js, ca); }} ant_value_t cb = (nargs >= 2) ? args[1] : js_mkundef(); uint8_t cbt = vtype(cb); if (cbt == T_NULL || cbt == T_UNDEF) return js_mkundef(); for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { EventListenerEntry *e = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); if (e->callback == cb && e->capture == capture) { utarray_erase(evt->listeners, i, 1); return js_mkundef(); } } return js_mkundef(); } static ant_value_t dispatch_event_to(ant_t *js, ant_value_t event_obj, EventType *evt, ant_value_t target) { if (!evt) return js_true; event_data_t *data = get_event_data(event_obj); if (data) { data->dispatching = true; data->stop_immediate = false; } js_set(js, event_obj, "target", target); js_set(js, event_obj, "currentTarget", target); js_set(js, event_obj, "eventPhase", js_mknum(2)); ant_value_t call_args[1] = { event_obj }; unsigned int n = utarray_len(evt->listeners); for (unsigned int i = 0; i < n;) { EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); if (vtype(entry->signal) != T_UNDEF && abort_signal_is_aborted(entry->signal)) { utarray_erase(evt->listeners, i, 1); n--; continue; } ant_value_t cb = entry->callback; bool once = entry->once; if (once) { utarray_erase(evt->listeners, i, 1); n--; } else i++; uint8_t t = vtype(cb); if (t != T_FUNC && t != T_CFUNC) continue; eventemitter_call_listener(js, cb, js_mkundef(), call_args, 1); if (data && data->stop_immediate) break; } if (data) data->dispatching = false; js_set(js, event_obj, "currentTarget", js_mknull()); js_set(js, event_obj, "eventPhase", js_mknum(0)); bool canceled = data && data->canceled; return js_bool(!canceled); } static ant_value_t js_add_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t key = evt_key_from_arg(args[0]); if (!key) return js_mkundef(); return add_listener_to(js, args, nargs, find_or_create_emitter_event_type(js, js_getthis(js), key)); } static ant_value_t js_add_event_listener(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t key = evt_key_from_arg(args[0]); if (!key) return js_mkundef(); return add_listener_to(js, args, nargs, find_or_create_global_event_type(js, key)); } static ant_value_t js_remove_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t key = evt_key_from_arg(args[0]); if (!key) return js_mkundef(); return remove_listener_from(js, args, nargs, find_emitter_event_type(js, js_getthis(js), key)); } static ant_value_t js_remove_event_listener(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t key = evt_key_from_arg(args[0]); if (!key) return js_mkundef(); return remove_listener_from(js, args, nargs, find_global_event_type(js, key)); } static ant_value_t js_dispatch_event_method(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1 || !is_object_type(args[0])) return js_false; ant_value_t this_obj = js_getthis(js); ant_value_t key = js_get(js, args[0], "type"); if (!evt_key_from_arg(key)) return js_false; return dispatch_event_to(js, args[0], find_emitter_event_type(js, this_obj, key), this_obj); } static ant_value_t js_dispatch_event(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1 || !is_object_type(args[0])) return js_false; ant_value_t key = js_get(js, args[0], "type"); if (!evt_key_from_arg(key)) return js_false; return dispatch_event_to(js, args[0], find_global_event_type(js, key), js_glob(js)); } void js_dispatch_global_event(ant_t *js, ant_value_t event_obj) { ant_value_t key = js_get(js, event_obj, "type"); if (!evt_key_from_arg(key)) return; EventType *evt = find_global_event_type(js, key); if (!evt || utarray_len(evt->listeners) == 0) return; dispatch_event_to(js, event_obj, evt, js_glob(js)); } static bool eventemitter_add_listener_impl( ant_t *js, ant_value_t target, ant_value_t key, ant_value_t listener, bool once, bool prepend ) { EventType *evt = NULL; EventListenerEntry entry = {0}; uint8_t t = 0; if (!is_object_type(target) || !key) return false; t = vtype(listener); if (t != T_FUNC && t != T_CFUNC) return false; evt = find_or_create_emitter_event_type(js, target, key); if (!evt) return false; entry.callback = listener; entry.raw_callback = js_mkundef(); entry.signal = js_mkundef(); entry.once = once; entry.capture = false; if (once) { entry.raw_callback = js_heavy_mkfun(js, js_eventemitter_once_wrapper, listener); if (is_callable(entry.raw_callback)) js_set(js, entry.raw_callback, "listener", listener); } if (!prepend || utarray_len(evt->listeners) == 0) utarray_push_back(evt->listeners, &entry); else { utarray_push_back(evt->listeners, &entry); EventListenerEntry *items = (EventListenerEntry *)utarray_eltptr(evt->listeners, 0); if (!items) return false; memmove(items + 1, items, (utarray_len(evt->listeners) - 1u) * sizeof(*items)); items[0] = entry; } int max_listeners = eventemitter_get_max_listeners_impl(target); if ( max_listeners > 0 && !evt->warned_max_listeners && (int)utarray_len(evt->listeners) > max_listeners ) { evt->warned_max_listeners = true; if (vtype(key) == T_STR) { fprintf( stderr, "Warning: Possible EventEmitter memory leak detected. " "%u '%s' listeners added. Use emitter.setMaxListeners() to increase limit.\n", (unsigned)utarray_len(evt->listeners), js_str(js, key) ); } else { fprintf( stderr, "Warning: Possible EventEmitter memory leak detected. " "%u listeners added for a Symbol event. Use emitter.setMaxListeners() to increase limit.\n", (unsigned)utarray_len(evt->listeners) ); } } return true; } static bool eventemitter_remove_listener_impl( ant_t *js, ant_value_t target, ant_value_t key, ant_value_t listener ) { EventType *evt = NULL; uint8_t t = 0; if (!is_object_type(target) || !key) return false; t = vtype(listener); if (t != T_FUNC && t != T_CFUNC) return false; evt = find_emitter_event_type(js, target, key); if (!evt) return false; for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); if (entry->callback == listener || entry->raw_callback == listener) { utarray_erase(evt->listeners, i, 1); return true; }} return false; } static ant_offset_t eventemitter_listener_count_impl( ant_t *js, ant_value_t target, ant_value_t key ) { EventType *evt = NULL; if (!is_object_type(target) || !key) return 0; evt = find_emitter_event_type(js, target, key); if (!evt) return 0; return (ant_offset_t)utarray_len(evt->listeners); } static ant_value_t js_eventemitter_off(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "off requires 2 arguments (event, listener)"); ant_value_t key = evt_key_from_arg(args[0]); if (!key) return js_mkerr(js, "event must be a string or Symbol"); ant_value_t this_obj = js_getthis(js); remove_listener_from( js, args, nargs, find_emitter_event_type(js, this_obj, key) ); return this_obj; } static bool eventemitter_emit_args_impl( ant_t *js, ant_value_t target, ant_value_t key, ant_value_t *args, int nargs ) { EventType *evt = NULL; if (!is_object_type(target) || !key) return false; evt = find_emitter_event_type(js, target, key); if (!evt || utarray_len(evt->listeners) == 0) return false; for (unsigned int i = 0; i < utarray_len(evt->listeners);) { EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); ant_value_t cb = entry->callback; bool once = entry->once; if (once) utarray_erase(evt->listeners, i, 1); else i++; if (vtype(entry->signal) != T_UNDEF && abort_signal_is_aborted(entry->signal)) continue; if (vtype(cb) != T_FUNC && vtype(cb) != T_CFUNC) continue; ant_value_t result = eventemitter_call_listener(js, cb, target, args, nargs); if (vtype(result) == T_ERR) { if (vtype(key) == T_STR) fprintf(stderr, "Error in event listener for '%s': %s\n", js_str(js, key), js_str(js, result)); else fprintf(stderr, "Error in event listener: %s\n", js_str(js, result)); } } return true; } static ant_value_t js_eventemitter_emit(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "emit requires at least 1 argument (event)"); ant_value_t key = evt_key_from_arg(args[0]); if (!key) return js_mkerr(js, "event must be a string or Symbol"); return js_bool(eventemitter_emit_args_impl( js, js_getthis(js), key, nargs > 1 ? &args[1] : NULL, nargs - 1 )); } bool eventemitter_emit_args_val( ant_t *js, ant_value_t target, ant_value_t key, ant_value_t *args, int nargs ) { return eventemitter_emit_args_impl(js, target, key, args, nargs); } bool eventemitter_emit_args( ant_t *js, ant_value_t target, const char *event_type, ant_value_t *args, int nargs ) { return eventemitter_emit_args_val(js, target, js_mkstr(js, event_type, strlen(event_type)), args, nargs); } bool eventemitter_add_listener_val( ant_t *js, ant_value_t target, ant_value_t key, ant_value_t listener, bool once ) { return eventemitter_add_listener_impl(js, target, key, listener, once, false); } bool eventemitter_add_listener( ant_t *js, ant_value_t target, const char *event_type, ant_value_t listener, bool once ) { return eventemitter_add_listener_val(js, target, js_mkstr(js, event_type, strlen(event_type)), listener, once); } bool eventemitter_remove_listener_val( ant_t *js, ant_value_t target, ant_value_t key, ant_value_t listener ) { return eventemitter_remove_listener_impl(js, target, key, listener); } bool eventemitter_remove_listener( ant_t *js, ant_value_t target, const char *event_type, ant_value_t listener ) { return eventemitter_remove_listener_val(js, target, js_mkstr(js, event_type, strlen(event_type)), listener); } ant_offset_t eventemitter_listener_count_val( ant_t *js, ant_value_t target, ant_value_t key ) { return eventemitter_listener_count_impl(js, target, key); } ant_offset_t eventemitter_listener_count( ant_t *js, ant_value_t target, const char *event_type ) { return eventemitter_listener_count_val(js, target, js_mkstr(js, event_type, strlen(event_type))); } static ant_value_t js_eventemitter_add(ant_t *js, ant_value_t *args, int nargs, bool once, bool prepend) { if (nargs < 2) return js_mkerr(js, "requires 2 arguments (event, listener)"); ant_value_t key = evt_key_from_arg(args[0]); if (!key) return js_mkerr(js, "event must be a string or Symbol"); if (!eventemitter_add_listener_impl(js, js_getthis(js), key, args[1], once, prepend)) return js_mkerr(js, "listener must be a function"); return js_getthis(js); } static ant_value_t js_eventemitter_on(ant_t *js, ant_value_t *args, int nargs) { return js_eventemitter_add(js, args, nargs, false, false); } static ant_value_t js_eventemitter_once(ant_t *js, ant_value_t *args, int nargs) { return js_eventemitter_add(js, args, nargs, true, false); } static ant_value_t js_eventemitter_prepend_listener(ant_t *js, ant_value_t *args, int nargs) { return js_eventemitter_add(js, args, nargs, false, true); } static ant_value_t js_eventemitter_prepend_once_listener(ant_t *js, ant_value_t *args, int nargs) { return js_eventemitter_add(js, args, nargs, true, true); } static ant_value_t js_eventemitter_removeAllListeners(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); EventType **events = NULL; if (nargs < 1 || vtype(args[0]) == T_UNDEF) { events = get_or_create_emitter_events(js, this_obj); if (!events || !*events) return this_obj; EventType *evt, *tmp; HASH_ITER(hh, *events, evt, tmp) utarray_clear(evt->listeners); return this_obj; } ant_value_t key = evt_key_from_arg(args[0]); if (!key) return this_obj; EventType *evt = find_emitter_event_type(js, this_obj, key); if (evt) utarray_clear(evt->listeners); return this_obj; } static ant_value_t js_eventemitter_listenerCount(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mknum(0); ant_value_t key = evt_key_from_arg(args[0]); if (!key) return js_mknum(0); EventType *evt = find_emitter_event_type(js, js_getthis(js), key); if (!evt) return js_mknum(0); return js_mknum((double)utarray_len(evt->listeners)); } static ant_value_t js_eventemitter_setMaxListeners(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "setMaxListeners requires 1 argument"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "n must be a number"); int n = (int)js_getnum(args[0]); if (n < 0) return js_mkerr(js, "n must be non-negative"); ant_value_t this_obj = js_getthis(js); if (!is_object_type(this_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "setMaxListeners requires an object receiver"); js_set_slot(this_obj, SLOT_EVENT_MAX_LISTENERS, js_mknum(n)); return this_obj; } static ant_value_t js_eventemitter_getMaxListeners(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); if (!is_object_type(this_obj)) return js_mknum(EVENTS_DEFAULT_MAX_LISTENERS); return js_mknum(eventemitter_get_max_listeners_impl(this_obj)); } static ant_value_t js_eventemitter_rawListeners(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkarr(js); ant_value_t key = evt_key_from_arg(args[0]); if (!key) return js_mkarr(js); return eventemitter_get_listeners_array(js, js_getthis(js), key, true); } static ant_value_t js_eventemitter_listeners(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkarr(js); ant_value_t key = evt_key_from_arg(args[0]); if (!key) return js_mkarr(js); return eventemitter_get_listeners_array(js, js_getthis(js), key, false); } static ant_value_t js_eventemitter_eventNames(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); ant_value_t result = js_mkarr(js); EventType **events = get_or_create_emitter_events(js, this_obj); if (events && *events) { EventType *evt, *tmp; HASH_ITER(hh, *events, evt, tmp) if (utarray_len(evt->listeners) > 0) js_arr_push(js, result, evt->js_key); } return result; } static ant_value_t js_events_once_listener(ant_t *js, ant_value_t *args, int nargs) { ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); if (!is_object_type(state)) return js_mkundef(); ant_value_t promise = js_get_slot(state, SLOT_DATA); if (vtype(promise) != T_PROMISE) return js_mkundef(); ant_value_t settled = js_get_slot(state, SLOT_SETTLED); if (vtype(settled) == T_BOOL && settled == js_true) return js_mkundef(); js_set_slot(state, SLOT_SETTLED, js_true); ant_value_t signal = js_get(js, state, "signal"); ant_value_t abort_listener = js_get(js, state, "abortListener"); if (abort_signal_is_signal(signal) && is_callable(abort_listener)) abort_signal_remove_listener(js, signal, abort_listener); ant_value_t values = js_mkarr(js); for (int i = 0; i < nargs; i++) js_arr_push(js, values, args[i]); js_resolve_promise(js, promise, values); return js_mkundef(); } static ant_value_t js_events_once_abort_listener(ant_t *js, ant_value_t *args, int nargs) { ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); if (!is_object_type(state)) return js_mkundef(); ant_value_t promise = js_get_slot(state, SLOT_DATA); if (vtype(promise) != T_PROMISE) return js_mkundef(); ant_value_t settled = js_get_slot(state, SLOT_SETTLED); if (vtype(settled) == T_BOOL && settled == js_true) return js_mkundef(); js_set_slot(state, SLOT_SETTLED, js_true); ant_value_t signal = js_get(js, state, "signal"); ant_value_t abort_listener = js_get(js, state, "abortListener"); if (abort_signal_is_signal(signal) && is_callable(abort_listener)) abort_signal_remove_listener(js, signal, abort_listener); ant_value_t reason = abort_signal_get_reason(signal); if (vtype(reason) == T_UNDEF) reason = js_mkerr(js, "The operation was aborted"); js_reject_promise(js, promise, reason); return js_mkundef(); } static bool js_events_once_is_abort_key(ant_t *js, ant_value_t key) { size_t key_len = 0; const char *key_str = vtype(key) == T_STR ? js_getstr(js, key, &key_len) : NULL; return key_str && key_len == 5 && memcmp(key_str, "abort", 5) == 0; } static void js_events_once_reject_aborted(ant_t *js, ant_value_t promise, ant_value_t signal) { ant_value_t reason = abort_signal_get_reason(signal); if (vtype(reason) == T_UNDEF) reason = js_mkerr(js, "The operation was aborted"); js_reject_promise(js, promise, reason); } static ant_value_t js_events_once_attach( ant_t *js, ant_value_t promise, ant_value_t target, ant_value_t key, ant_value_t listener, ant_value_t signal ) { if (abort_signal_is_signal(target)) { if (!js_events_once_is_abort_key(js, key)) { js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "AbortSignal only supports the abort event")); return promise; } abort_signal_add_listener(js, target, listener); return promise; } if (is_eventemitter_instance(target)) { if (!eventemitter_add_listener_val(js, target, key, listener, true)) js_reject_promise(js, promise, js_mkerr(js, "listener must be a function")); return promise; } if (is_eventtarget_instance(target)) { ant_value_t listener_options = js_mkobj(js); js_set(js, listener_options, "once", js_true); if (abort_signal_is_signal(signal)) js_set(js, listener_options, "signal", signal); ant_value_t call_args[3] = { key, listener, listener_options }; ant_value_t result = add_listener_to(js, call_args, 3, find_or_create_emitter_event_type(js, target, key)); if (is_err(result)) js_reject_promise(js, promise, result); return promise; } js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "target is not an EventEmitter or EventTarget")); return promise; } static ant_value_t js_events_once(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2 || !is_object_type(args[0])) return js_mkerr_typed(js, JS_ERR_TYPE, "events.once requires an emitter and event name"); ant_value_t key = evt_key_from_arg(args[1]); if (!key) return js_mkerr_typed(js, JS_ERR_TYPE, "event must be a string or Symbol"); ant_value_t promise = js_mkpromise(js); if (is_err(promise)) return promise; ant_value_t state = js_mkobj(js); js_set_slot(state, SLOT_DATA, promise); js_set_slot(state, SLOT_SETTLED, js_false); ant_value_t listener = js_heavy_mkfun(js, js_events_once_listener, state); ant_value_t target = args[0]; ant_value_t options = nargs >= 3 ? args[2] : js_mkundef(); ant_value_t signal = js_mkundef(); if (is_object_type(options)) signal = js_get(js, options, "signal"); if (abort_signal_is_signal(signal)) { if (abort_signal_is_aborted(signal)) { js_events_once_reject_aborted(js, promise, signal); return promise; } ant_value_t abort_listener = js_heavy_mkfun(js, js_events_once_abort_listener, state); js_set(js, state, "signal", signal); js_set(js, state, "abortListener", abort_listener); abort_signal_add_listener(js, signal, abort_listener); } return js_events_once_attach(js, promise, target, key, listener, signal); } static ant_value_t js_events_disposable_dispose(ant_t *js, ant_value_t *args, int nargs) { ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); if (!is_object_type(state)) return js_mkundef(); ant_value_t disposed = js_get_slot(state, SLOT_SETTLED); if (vtype(disposed) == T_BOOL && disposed == js_true) return js_mkundef(); js_set_slot(state, SLOT_SETTLED, js_true); ant_value_t signal = js_get(js, state, "signal"); ant_value_t listener = js_get(js, state, "listener"); if (abort_signal_is_signal(signal) && is_callable(listener)) abort_signal_remove_listener(js, signal, listener); return js_mkundef(); } static ant_value_t js_events_add_abort_listener(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2 || !abort_signal_is_signal(args[0]) || !is_callable(args[1])) return js_mkerr_typed(js, JS_ERR_TYPE, "events.addAbortListener requires an AbortSignal and listener"); bool already_aborted = abort_signal_is_aborted(args[0]); if (already_aborted) { ant_value_t event = js_mkobj(js); js_set(js, event, "type", js_mkstr(js, "abort", 5)); eventemitter_call_listener(js, args[1], args[0], &event, 1); } else abort_signal_add_listener(js, args[0], args[1]); ant_value_t state = js_mkobj(js); js_set_slot(state, SLOT_SETTLED, js_bool(already_aborted)); js_set(js, state, "signal", args[0]); js_set(js, state, "listener", args[1]); ant_value_t disposable = js_mkobj(js); js_set(js, disposable, "dispose", js_heavy_mkfun(js, js_events_disposable_dispose, state)); return disposable; } static ant_value_t js_events_set_max_listeners(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "setMaxListeners requires at least 1 argument"); if (vtype(args[0]) != T_NUM) return js_mkerr(js, "n must be a number"); int n = (int)js_getnum(args[0]); if (n < 0) return js_mkerr(js, "n must be non-negative"); for (int i = 1; i < nargs; i++) { if (!is_object_type(args[i])) continue; js_set_slot(args[i], SLOT_EVENT_MAX_LISTENERS, js_mknum(n)); } return js_mkundef(); } static ant_value_t js_events_get_max_listeners(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mknum(EVENTS_DEFAULT_MAX_LISTENERS); if (!is_object_type(args[0])) return js_mknum(EVENTS_DEFAULT_MAX_LISTENERS); return js_mknum(eventemitter_get_max_listeners_impl(args[0])); } // TODO: fix stub static ant_value_t js_events_on(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr_typed(js, JS_ERR_TYPE, "events.on async iterator is not implemented"); } ant_value_t events_library(ant_t *js) { ant_value_t lib = js_mkobj(js); eventemitter_prototype(js); js_set_module_default(js, lib, g_eventemitter_ctor, "EventEmitter"); js_set(js, lib, "once", js_mkfun(js_events_once)); js_set(js, lib, "on", js_mkfun(js_events_on)); js_set(js, lib, "addAbortListener", js_mkfun(js_events_add_abort_listener)); js_set(js, lib, "setMaxListeners", js_mkfun(js_events_set_max_listeners)); js_set(js, lib, "getMaxListeners", js_mkfun(js_events_get_max_listeners)); js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "events", 6)); return lib; } ant_value_t eventemitter_prototype(ant_t *js) { if (g_eventemitter_proto) return g_eventemitter_proto; ant_value_t object_proto = js->sym.object_proto; ant_value_t function_proto = js_get_slot(js_glob(js), SLOT_FUNC_PROTO); if (vtype(function_proto) == T_UNDEF) function_proto = js_get_ctor_proto(js, "Function", 8); ant_value_t eventemitter_ctor = js_mkobj(js); ant_value_t eventemitter_proto = js_mkobj(js); if (is_object_type(object_proto)) js_set_proto_init(eventemitter_proto, object_proto); if (is_object_type(function_proto)) js_set_proto_init(eventemitter_ctor, function_proto); js_set(js, eventemitter_proto, "on", js_mkfun(js_eventemitter_on)); js_set_exact(js, eventemitter_proto, "addListener", js_get(js, eventemitter_proto, "on")); js_set(js, eventemitter_proto, "once", js_mkfun(js_eventemitter_once)); js_set(js, eventemitter_proto, "prependListener", js_mkfun(js_eventemitter_prepend_listener)); js_set(js, eventemitter_proto, "prependOnceListener", js_mkfun(js_eventemitter_prepend_once_listener)); js_set(js, eventemitter_proto, "off", js_mkfun(js_eventemitter_off)); js_set_exact(js, eventemitter_proto, "removeListener", js_get(js, eventemitter_proto, "off")); js_set(js, eventemitter_proto, "emit", js_mkfun(js_eventemitter_emit)); js_set(js, eventemitter_proto, "removeAllListeners", js_mkfun(js_eventemitter_removeAllListeners)); js_set(js, eventemitter_proto, "listenerCount", js_mkfun(js_eventemitter_listenerCount)); js_set(js, eventemitter_proto, "setMaxListeners", js_mkfun(js_eventemitter_setMaxListeners)); js_set(js, eventemitter_proto, "getMaxListeners", js_mkfun(js_eventemitter_getMaxListeners)); js_set(js, eventemitter_proto, "listeners", js_mkfun(js_eventemitter_listeners)); js_set(js, eventemitter_proto, "rawListeners", js_mkfun(js_eventemitter_rawListeners)); js_set(js, eventemitter_proto, "eventNames", js_mkfun(js_eventemitter_eventNames)); js_set_sym(js, eventemitter_proto, get_toStringTag_sym(), js_mkstr(js, "EventEmitter", 12)); js_set_slot(eventemitter_ctor, SLOT_CFUNC, js_mkfun(js_eventemitter_ctor)); js_mkprop_fast(js, eventemitter_ctor, "prototype", 9, eventemitter_proto); js_mkprop_fast(js, eventemitter_ctor, "name", 4, ANT_STRING("EventEmitter")); js_set_descriptor(js, eventemitter_ctor, "name", 4, 0); g_eventemitter_proto = eventemitter_proto; g_eventemitter_ctor = js_obj_to_func(eventemitter_ctor); js_set(js, eventemitter_proto, "constructor", g_eventemitter_ctor); js_set_descriptor(js, eventemitter_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); return g_eventemitter_proto; } void init_events_module(void) { ant_t *js = rt->js; ant_value_t global = js_glob(js); g_isTrusted_getter = js_mkfun(js_event_get_isTrusted); g_event_proto = js_mkobj(js); js_set_sym(js, g_event_proto, get_toStringTag_sym(), js_mkstr(js, "Event", 5)); js_set(js, g_event_proto, "preventDefault", js_mkfun(js_event_preventDefault)); js_set(js, g_event_proto, "stopPropagation", js_mkfun(js_event_stopPropagation)); js_set(js, g_event_proto, "stopImmediatePropagation", js_mkfun(js_event_stopImmediatePropagation)); js_set(js, g_event_proto, "composedPath", js_mkfun(js_event_composedPath)); js_set(js, g_event_proto, "initEvent", js_mkfun(js_event_initEvent)); js_set(js, g_event_proto, "NONE", js_mknum(0)); js_set(js, g_event_proto, "CAPTURING_PHASE", js_mknum(1)); js_set(js, g_event_proto, "AT_TARGET", js_mknum(2)); js_set(js, g_event_proto, "BUBBLING_PHASE", js_mknum(3)); ant_value_t event_fn = js_make_ctor(js, js_event_ctor, g_event_proto, "Event", 5); js_set(js, event_fn, "NONE", js_mknum(0)); js_set(js, event_fn, "CAPTURING_PHASE", js_mknum(1)); js_set(js, event_fn, "AT_TARGET", js_mknum(2)); js_set(js, event_fn, "BUBBLING_PHASE", js_mknum(3)); js_set(js, global, "Event", event_fn); g_customevent_proto = js_mkobj(js); js_set_proto_init(g_customevent_proto, g_event_proto); js_set_sym(js, g_customevent_proto, get_toStringTag_sym(), js_mkstr(js, "CustomEvent", 11)); ant_value_t customevent_fn = js_make_ctor(js, js_customevent_ctor, g_customevent_proto, "CustomEvent", 11); js_set(js, global, "CustomEvent", customevent_fn); g_errorevent_proto = js_mkobj(js); js_set_proto_init(g_errorevent_proto, g_event_proto); js_set_sym(js, g_errorevent_proto, get_toStringTag_sym(), js_mkstr(js, "ErrorEvent", 10)); ant_value_t errorevent_fn = js_make_ctor(js, js_errorevent_ctor, g_errorevent_proto, "ErrorEvent", 10); js_set(js, global, "ErrorEvent", errorevent_fn); g_promiserejectionevent_proto = js_mkobj(js); js_set_proto_init(g_promiserejectionevent_proto, g_event_proto); js_set_sym(js, g_promiserejectionevent_proto, get_toStringTag_sym(), js_mkstr(js, "PromiseRejectionEvent", 21)); ant_value_t pre_fn = js_make_ctor(js, js_promiserejectionevent_ctor, g_promiserejectionevent_proto, "PromiseRejectionEvent", 21); js_set(js, global, "PromiseRejectionEvent", pre_fn); ant_value_t object_proto = js->sym.object_proto; ant_value_t function_proto = js_get_slot(global, SLOT_FUNC_PROTO); if (vtype(function_proto) == T_UNDEF) function_proto = js_get_ctor_proto(js, "Function", 8); ant_value_t eventtarget_proto = js_mkobj(js); g_eventtarget_proto = eventtarget_proto; if (is_object_type(object_proto)) js_set_proto_init(eventtarget_proto, object_proto); js_set(js, eventtarget_proto, "addEventListener", js_mkfun(js_add_event_listener_method)); js_set(js, eventtarget_proto, "removeEventListener", js_mkfun(js_remove_event_listener_method)); js_set(js, eventtarget_proto, "dispatchEvent", js_mkfun(js_dispatch_event_method)); js_set_sym(js, eventtarget_proto, get_toStringTag_sym(), js_mkstr(js, "EventTarget", 11)); ant_value_t eventtarget_ctor = js_mkobj(js); if (is_object_type(function_proto)) js_set_proto_init(eventtarget_ctor, function_proto); js_set_slot(eventtarget_ctor, SLOT_CFUNC, js_mkfun(js_eventtarget_ctor)); js_mkprop_fast(js, eventtarget_ctor, "prototype", 9, eventtarget_proto); js_mkprop_fast(js, eventtarget_ctor, "name", 4, ANT_STRING("EventTarget")); js_set_descriptor(js, eventtarget_ctor, "name", 4, 0); ant_value_t eventtarget_fn = js_obj_to_func(eventtarget_ctor); js_set(js, eventtarget_proto, "constructor", eventtarget_fn); js_set_descriptor(js, eventtarget_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); js_set(js, global, "addEventListener", js_mkfun(js_add_event_listener)); js_set(js, global, "removeEventListener", js_mkfun(js_remove_event_listener)); js_set(js, global, "dispatchEvent", js_mkfun(js_dispatch_event)); js_set(js, global, "EventTarget", eventtarget_fn); } static void mark_event_type_listeners(ant_t *js, gc_mark_fn mark, EventType *events) { EventType *evt, *tmp; HASH_ITER(hh, events, evt, tmp) { if (vtype(evt->js_key) == T_STR) mark(js, evt->js_key); for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { EventListenerEntry *e = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); mark(js, e->callback); if (vtype(e->raw_callback) != T_UNDEF) mark(js, e->raw_callback); if (vtype(e->signal) != T_UNDEF) mark(js, e->signal); } }} void gc_mark_events(ant_t *js, gc_mark_fn mark) { mark_event_type_listeners(js, mark, global_events); for (emitter_reg_t *reg = emitter_registry; reg; reg = reg->next) { if (*reg->events) mark_event_type_listeners(js, mark, *reg->events); } if (g_isTrusted_getter) mark(js, g_isTrusted_getter); if (g_eventemitter_ctor) mark(js, g_eventemitter_ctor); if (g_eventemitter_proto) mark(js, g_eventemitter_proto); if (g_eventtarget_proto) mark(js, g_eventtarget_proto); if (g_event_proto) mark(js, g_event_proto); if (g_customevent_proto) mark(js, g_customevent_proto); if (g_errorevent_proto) mark(js, g_errorevent_proto); if (g_promiserejectionevent_proto) mark(js, g_promiserejectionevent_proto); }