MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <stdlib.h>
2#include <stdio.h>
3#include <string.h>
4#include <inttypes.h>
5#include <time.h>
6#include <uthash.h>
7#include <utarray.h>
8
9#include "ant.h"
10#include "errors.h"
11#include "runtime.h"
12#include "internal.h"
13#include "descriptors.h"
14#include "silver/engine.h"
15
16#include "gc/modules.h"
17#include "modules/abort.h"
18#include "modules/events.h"
19#include "modules/symbol.h"
20
21typedef struct {
22 bool canceled;
23 bool stop_immediate;
24 bool stop_propagation;
25 bool dispatching;
26} event_data_t;
27
28static ant_value_t g_isTrusted_getter = 0;
29static ant_value_t g_eventemitter_ctor = 0;
30static ant_value_t g_eventemitter_proto = 0;
31static ant_value_t g_eventtarget_proto = 0;
32static ant_value_t g_event_proto = 0;
33static ant_value_t g_customevent_proto = 0;
34static ant_value_t g_errorevent_proto = 0;
35static ant_value_t g_promiserejectionevent_proto = 0;
36
37static event_data_t *get_event_data(ant_value_t obj) {
38 ant_value_t slot = js_get_slot(obj, SLOT_DATA);
39 if (vtype(slot) != T_NUM) return NULL;
40 return (event_data_t *)(uintptr_t)js_getnum(slot);
41}
42
43static double get_timestamp_ms(void) {
44 struct timespec ts;
45 clock_gettime(CLOCK_MONOTONIC, &ts);
46 return (double)ts.tv_sec * 1e3 + (double)ts.tv_nsec / 1e6;
47}
48
49typedef struct {
50 ant_value_t callback;
51 ant_value_t raw_callback;
52 ant_value_t signal;
53 bool once;
54 bool capture;
55} EventListenerEntry;
56
57static const UT_icd event_listener_icd = {
58 sizeof(EventListenerEntry),
59 NULL, NULL, NULL
60};
61
62typedef struct {
63 unsigned char buf[48];
64 unsigned char *ptr;
65 size_t len;
66} evt_key_t;
67
68typedef struct {
69 UT_array *listeners;
70 ant_value_t js_key;
71 unsigned char *hash_key;
72 size_t hash_key_len;
73 bool warned_max_listeners;
74 UT_hash_handle hh;
75} EventType;
76
77typedef struct emitter_reg {
78 EventType **events;
79 struct emitter_reg *next;
80} emitter_reg_t;
81
82static EventType *global_events = NULL;
83static emitter_reg_t *emitter_registry = NULL;
84
85static EventType *make_event_type(ant_value_t js_key, const evt_key_t *ek) {
86 EventType *evt = ant_calloc(sizeof(EventType) + ek->len);
87 if (!evt) return NULL;
88 evt->js_key = js_key;
89 evt->hash_key = (unsigned char *)(evt + 1);
90 evt->hash_key_len = ek->len;
91 memcpy(evt->hash_key, ek->ptr, ek->len);
92 utarray_new(evt->listeners, &event_listener_icd);
93 return evt;
94}
95
96static EventType *evt_find(EventType *table, const evt_key_t *ek) {
97 EventType *evt = NULL;
98 HASH_FIND(hh, table, ek->ptr, ek->len, evt);
99 return evt;
100}
101
102static EventType *evt_find_or_create(EventType **table, ant_value_t js_key, const evt_key_t *ek) {
103 EventType *evt = evt_find(*table, ek);
104 if (!evt) {
105 evt = make_event_type(js_key, ek);
106 if (!evt) return NULL;
107 HASH_ADD_KEYPTR(hh, *table, evt->hash_key, evt->hash_key_len, evt);
108 }
109 return evt;
110}
111
112static void evt_key_reset(evt_key_t *k) {
113 k->ptr = k->buf;
114 k->len = 0;
115}
116
117static void evt_key_free(evt_key_t *k) {
118 if (k->ptr != k->buf) free(k->ptr);
119 evt_key_reset(k);
120}
121
122static bool evt_key_init(ant_t *js, ant_value_t arg, evt_key_t *out) {
123 evt_key_reset(out);
124 uint8_t tag = (uint8_t)vtype(arg);
125
126 if (tag == T_STR) {
127 size_t slen = 0;
128 const char *s = js_getstr(js, arg, &slen);
129 out->len = 1 + slen;
130
131 if (out->len > sizeof(out->buf)) {
132 out->ptr = malloc(out->len);
133 if (!out->ptr) { evt_key_reset(out); return false; }
134 }
135
136 out->ptr[0] = tag;
137 if (slen) memcpy(out->ptr + 1, s, slen);
138
139 return true;
140 }
141
142 if (tag == T_SYMBOL) {
143 out->len = 1 + sizeof(ant_value_t);
144 out->ptr[0] = tag;
145 memcpy(out->ptr + 1, &arg, sizeof(ant_value_t));
146 return true;
147 }
148
149 return false;
150}
151
152static EventType **get_or_create_emitter_events(ant_t *js, ant_value_t this_obj) {
153 ant_value_t slot = js_get_slot(this_obj, SLOT_DATA);
154 if (vtype(slot) == T_UNDEF) {
155 EventType **events = ant_calloc(sizeof(EventType *));
156 if (!events) return NULL;
157 *events = NULL;
158
159 emitter_reg_t *reg = ant_calloc(sizeof(emitter_reg_t));
160 if (!reg) { free(events); return NULL; }
161 reg->events = events;
162 reg->next = emitter_registry;
163 emitter_registry = reg;
164
165 js_set_slot(this_obj, SLOT_DATA, ANT_PTR(events));
166 return events;
167 }
168 return (EventType **)(uintptr_t)js_getnum(slot);
169}
170
171static EventType *find_or_create_global_event_type(ant_t *js, ant_value_t js_key) {
172 evt_key_t ek;
173 if (!evt_key_init(js, js_key, &ek)) return NULL;
174 EventType *evt = evt_find_or_create(&global_events, js_key, &ek);
175 evt_key_free(&ek);
176 return evt;
177}
178
179static EventType *find_global_event_type(ant_t *js, ant_value_t js_key) {
180 evt_key_t ek;
181 if (!evt_key_init(js, js_key, &ek)) return NULL;
182 EventType *evt = evt_find(global_events, &ek);
183 evt_key_free(&ek);
184 return evt;
185}
186
187static EventType *find_or_create_emitter_event_type(ant_t *js, ant_value_t this_obj, ant_value_t js_key) {
188 EventType **events = get_or_create_emitter_events(js, this_obj);
189 if (!events) return NULL;
190 evt_key_t ek;
191 if (!evt_key_init(js, js_key, &ek)) return NULL;
192 EventType *evt = evt_find_or_create(events, js_key, &ek);
193 evt_key_free(&ek);
194 return evt;
195}
196
197static EventType *find_emitter_event_type(ant_t *js, ant_value_t this_obj, ant_value_t js_key) {
198 EventType **events = get_or_create_emitter_events(js, this_obj);
199 if (!events) return NULL;
200 evt_key_t ek;
201 if (!evt_key_init(js, js_key, &ek)) return NULL;
202 EventType *evt = evt_find(*events, &ek);
203 evt_key_free(&ek);
204 return evt;
205}
206
207static inline ant_value_t evt_key_from_arg(ant_value_t arg) {
208 uint8_t t = vtype(arg);
209 return (t == T_STR || t == T_SYMBOL) ? arg : 0;
210}
211
212static bool is_eventemitter_instance(ant_value_t target) {
213 return js_check_brand(target, BRAND_EVENTEMITTER);
214}
215
216static bool is_eventtarget_instance(ant_value_t target) {
217 return js_check_brand(target, BRAND_EVENTTARGET);
218}
219
220static int eventemitter_get_max_listeners_impl(ant_value_t target) {
221 ant_value_t slot = js_get_slot(target, SLOT_EVENT_MAX_LISTENERS);
222 if (vtype(slot) == T_NUM) {
223 int n = (int)js_getnum(slot);
224 return n >= 0 ? n : EVENTS_DEFAULT_MAX_LISTENERS;
225 }
226 return EVENTS_DEFAULT_MAX_LISTENERS;
227}
228
229static ant_value_t eventemitter_call_listener(
230 ant_t *js,
231 ant_value_t listener,
232 ant_value_t this_val,
233 ant_value_t *args,
234 int nargs
235) {
236 if (!is_callable(listener)) return js_mkundef();
237 if (sv_check_c_stack_overflow(js))
238 return js_mkerr_typed(js, JS_ERR_RANGE | JS_ERR_NO_STACK, "Maximum call stack size exceeded");
239
240 sv_call_plan_t plan;
241 ant_value_t err = sv_prepare_call(
242 js->vm, js, listener, this_val, args, nargs,
243 NULL, SV_CALL_MODE_NORMAL, &plan
244 );
245
246 if (is_err(err)) return err;
247 return sv_execute_call_plan(js->vm, js, &plan, NULL);
248}
249
250static ant_value_t js_eventemitter_once_wrapper(ant_t *js, ant_value_t *args, int nargs) {
251 ant_value_t listener = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
252 if (!is_callable(listener)) return js_mkundef();
253 return eventemitter_call_listener(js, listener, js->this_val, args, nargs);
254}
255
256static ant_value_t eventemitter_get_listeners_array(ant_t *js, ant_value_t target, ant_value_t key, bool raw) {
257 ant_value_t result = js_mkarr(js);
258 EventType *evt = NULL;
259
260 if (!is_object_type(target) || !key) return result;
261 evt = find_emitter_event_type(js, target, key);
262 if (!evt) return result;
263
264 for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) {
265 EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i);
266 if (!entry) continue;
267 js_arr_push(js, result, raw && entry->once && is_callable(entry->raw_callback)
268 ? entry->raw_callback
269 : entry->callback
270 );
271 }
272
273 return result;
274}
275
276static void js_init_event_obj(ant_t *js, ant_value_t obj, ant_value_t type_val, bool bubbles, bool cancelable) {
277 js_set(js, obj, "type", type_val);
278 js_set(js, obj, "target", js_mknull());
279 js_set(js, obj, "srcElement", js_mknull());
280 js_set(js, obj, "currentTarget", js_mknull());
281 js_set(js, obj, "eventPhase", js_mknum(0));
282 js_set(js, obj, "bubbles", js_bool(bubbles));
283 js_set(js, obj, "cancelable", js_bool(cancelable));
284 js_set(js, obj, "defaultPrevented", js_false);
285 js_set(js, obj, "returnValue", js_true);
286 js_set(js, obj, "cancelBubble", js_false);
287 js_set(js, obj, "timeStamp", js_mknum(get_timestamp_ms()));
288
289 if (g_isTrusted_getter)
290 js_set_accessor_desc(js, obj, "isTrusted", 9, g_isTrusted_getter, js_mkundef(), 0);
291
292 event_data_t *data = ant_calloc(sizeof(event_data_t));
293 if (data) js_set_slot(obj, SLOT_DATA, ANT_PTR(data));
294}
295
296static ant_value_t js_event_ctor(ant_t *js, ant_value_t *args, int nargs) {
297 if (vtype(js->new_target) == T_UNDEF)
298 return js_mkerr_typed(js, JS_ERR_TYPE, "Event constructor requires 'new'");
299 if (nargs < 1 || vtype(args[0]) == T_UNDEF)
300 return js_mkerr_typed(js, JS_ERR_TYPE, "Event constructor: type argument is required");
301
302 ant_value_t type_val = args[0];
303 if (vtype(type_val) != T_STR) {
304 type_val = js_tostring_val(js, type_val);
305 if (is_err(type_val)) return type_val;
306 }
307
308 bool bubbles = false, cancelable = false;
309 if (nargs >= 2 && vtype(args[1]) == T_OBJ) {
310 ant_value_t b = js_get(js, args[1], "bubbles");
311 ant_value_t c = js_get(js, args[1], "cancelable");
312 if (is_err(b)) return b;
313 if (is_err(c)) return c;
314 bubbles = js_truthy(js, b);
315 cancelable = js_truthy(js, c);
316 }
317
318 ant_value_t this_obj = js_mkobj(js);
319 ant_value_t proto = js_instance_proto_from_new_target(js, g_event_proto);
320 if (is_object_type(proto)) js_set_proto_init(this_obj, proto);
321
322 js_init_event_obj(js, this_obj, type_val, bubbles, cancelable);
323 return this_obj;
324}
325
326static ant_value_t js_event_get_isTrusted(ant_t *js, ant_value_t *args, int nargs) {
327 return js_false;
328}
329
330static ant_value_t js_eventemitter_ctor(ant_t *js, ant_value_t *args, int nargs) {
331 ant_value_t this_obj = js_getthis(js);
332
333 if (is_object_type(this_obj)) {
334 js_set_slot(this_obj, SLOT_BRAND, js_mknum(BRAND_EVENTEMITTER));
335 return this_obj;
336 }
337
338 if (vtype(js->new_target) != T_UNDEF) {
339 ant_value_t obj = js_mkobj(js);
340 ant_value_t proto = js_instance_proto_from_new_target(js, g_eventemitter_proto);
341 if (is_object_type(proto)) js_set_proto_init(obj, proto);
342 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_EVENTEMITTER));
343 return obj;
344 }
345
346 return js_mkerr_typed(js, JS_ERR_TYPE, "EventEmitter constructor requires an object receiver or 'new'");
347}
348
349static ant_value_t js_event_preventDefault(ant_t *js, ant_value_t *args, int nargs) {
350 ant_value_t this_obj = js_getthis(js);
351 ant_value_t cancelable = js_get(js, this_obj, "cancelable");
352 if (!js_truthy(js, cancelable)) return js_mkundef();
353 event_data_t *data = get_event_data(this_obj);
354 if (data) data->canceled = true;
355 js_set(js, this_obj, "defaultPrevented", js_true);
356 js_set(js, this_obj, "returnValue", js_false);
357 return js_mkundef();
358}
359
360static ant_value_t js_event_stopPropagation(ant_t *js, ant_value_t *args, int nargs) {
361 ant_value_t this_obj = js_getthis(js);
362 event_data_t *data = get_event_data(this_obj);
363 if (data) data->stop_propagation = true;
364 js_set(js, this_obj, "cancelBubble", js_true);
365 return js_mkundef();
366}
367
368static ant_value_t js_event_stopImmediatePropagation(ant_t *js, ant_value_t *args, int nargs) {
369 ant_value_t this_obj = js_getthis(js);
370 event_data_t *data = get_event_data(this_obj);
371 if (data) { data->stop_immediate = true; data->stop_propagation = true; }
372 js_set(js, this_obj, "cancelBubble", js_true);
373 return js_mkundef();
374}
375
376static ant_value_t js_event_composedPath(ant_t *js, ant_value_t *args, int nargs) {
377 ant_value_t this_obj = js_getthis(js);
378 event_data_t *data = get_event_data(this_obj);
379 ant_value_t result = js_mkarr(js);
380 if (data && data->dispatching) {
381 ant_value_t ct = js_get(js, this_obj, "currentTarget");
382 if (is_object_type(ct)) js_arr_push(js, result, ct);
383 }
384 return result;
385}
386
387static ant_value_t js_event_initEvent(ant_t *js, ant_value_t *args, int nargs) {
388 ant_value_t this_obj = js_getthis(js);
389 if (nargs >= 1) {
390 ant_value_t type_val = vtype(args[0]) == T_STR ? args[0] : js_tostring_val(js, args[0]);
391 if (!is_err(type_val)) js_set(js, this_obj, "type", type_val);
392 }
393 if (nargs >= 2) js_set(js, this_obj, "bubbles", js_bool(js_truthy(js, args[1])));
394 if (nargs >= 3) js_set(js, this_obj, "cancelable", js_bool(js_truthy(js, args[2])));
395 js_set(js, this_obj, "defaultPrevented", js_false);
396 js_set(js, this_obj, "returnValue", js_true);
397 event_data_t *data = get_event_data(this_obj);
398 if (data) { data->canceled = false; data->stop_immediate = false; data->stop_propagation = false; }
399 return js_mkundef();
400}
401
402static ant_value_t js_customevent_ctor(ant_t *js, ant_value_t *args, int nargs) {
403 if (vtype(js->new_target) == T_UNDEF)
404 return js_mkerr_typed(js, JS_ERR_TYPE, "CustomEvent constructor requires 'new'");
405 if (nargs < 1 || vtype(args[0]) == T_UNDEF)
406 return js_mkerr_typed(js, JS_ERR_TYPE, "CustomEvent constructor: type argument is required");
407
408 ant_value_t type_val = args[0];
409 if (vtype(type_val) != T_STR) {
410 type_val = js_tostring_val(js, type_val);
411 if (is_err(type_val)) return type_val;
412 }
413
414 bool bubbles = false, cancelable = false;
415 ant_value_t detail = js_mknull();
416 if (nargs >= 2 && vtype(args[1]) == T_OBJ) {
417 ant_value_t b = js_get(js, args[1], "bubbles");
418 ant_value_t c = js_get(js, args[1], "cancelable");
419 ant_value_t d = js_get(js, args[1], "detail");
420 if (is_err(b)) return b;
421 if (is_err(c)) return c;
422 bubbles = js_truthy(js, b);
423 cancelable = js_truthy(js, c);
424 if (vtype(d) != T_UNDEF) detail = d;
425 }
426
427 ant_value_t this_obj = js_mkobj(js);
428 ant_value_t proto = js_instance_proto_from_new_target(js, g_customevent_proto);
429 if (is_object_type(proto)) js_set_proto_init(this_obj, proto);
430
431 js_init_event_obj(js, this_obj, type_val, bubbles, cancelable);
432 js_set(js, this_obj, "detail", detail);
433 return this_obj;
434}
435
436static ant_value_t js_errorevent_ctor(ant_t *js, ant_value_t *args, int nargs) {
437 if (vtype(js->new_target) == T_UNDEF)
438 return js_mkerr_typed(js, JS_ERR_TYPE, "ErrorEvent constructor requires 'new'");
439 if (nargs < 1 || vtype(args[0]) == T_UNDEF)
440 return js_mkerr_typed(js, JS_ERR_TYPE, "ErrorEvent constructor: type argument is required");
441
442 ant_value_t type_val = args[0];
443 if (vtype(type_val) != T_STR) {
444 type_val = js_tostring_val(js, type_val);
445 if (is_err(type_val)) return type_val;
446 }
447
448 bool bubbles = false, cancelable = false;
449 ant_value_t message = js_mkstr(js, "", 0);
450 ant_value_t filename = js_mkstr(js, "", 0);
451 ant_value_t lineno = js_mknum(0);
452 ant_value_t colno = js_mknum(0);
453 ant_value_t error = js_mknull();
454
455 if (nargs >= 2 && vtype(args[1]) == T_OBJ) {
456 ant_value_t b = js_get(js, args[1], "bubbles");
457 ant_value_t c = js_get(js, args[1], "cancelable");
458 ant_value_t m = js_get(js, args[1], "message");
459 ant_value_t f = js_get(js, args[1], "filename");
460 ant_value_t l = js_get(js, args[1], "lineno");
461 ant_value_t co = js_get(js, args[1], "colno");
462 ant_value_t e = js_get(js, args[1], "error");
463 if (is_err(b)) return b;
464 if (is_err(c)) return c;
465 bubbles = js_truthy(js, b);
466 cancelable = js_truthy(js, c);
467 if (vtype(m) != T_UNDEF) { message = js_tostring_val(js, m); if (is_err(message)) return message; }
468 if (vtype(f) != T_UNDEF) { filename = js_tostring_val(js, f); if (is_err(filename)) return filename; }
469 if (vtype(l) != T_UNDEF) lineno = l;
470 if (vtype(co) != T_UNDEF) colno = co;
471 if (vtype(e) != T_UNDEF) error = e;
472 }
473
474 ant_value_t this_obj = js_mkobj(js);
475 ant_value_t proto = js_instance_proto_from_new_target(js, g_errorevent_proto);
476 if (is_object_type(proto)) js_set_proto_init(this_obj, proto);
477
478 js_init_event_obj(js, this_obj, type_val, bubbles, cancelable);
479 js_set(js, this_obj, "message", message);
480 js_set(js, this_obj, "filename", filename);
481 js_set(js, this_obj, "lineno", lineno);
482 js_set(js, this_obj, "colno", colno);
483 js_set(js, this_obj, "error", error);
484 return this_obj;
485}
486
487static ant_value_t js_promiserejectionevent_ctor(ant_t *js, ant_value_t *args, int nargs) {
488 if (vtype(js->new_target) == T_UNDEF)
489 return js_mkerr_typed(js, JS_ERR_TYPE, "PromiseRejectionEvent constructor requires 'new'");
490 if (nargs < 1 || vtype(args[0]) == T_UNDEF)
491 return js_mkerr_typed(js, JS_ERR_TYPE, "PromiseRejectionEvent constructor: type argument is required");
492
493 ant_value_t type_val = args[0];
494 if (vtype(type_val) != T_STR) {
495 type_val = js_tostring_val(js, type_val);
496 if (is_err(type_val)) return type_val;
497 }
498
499 bool bubbles = false, cancelable = false;
500 ant_value_t promise = js_mkundef();
501 ant_value_t reason = js_mkundef();
502
503 if (nargs >= 2 && vtype(args[1]) == T_OBJ) {
504 ant_value_t b = js_get(js, args[1], "bubbles");
505 ant_value_t c = js_get(js, args[1], "cancelable");
506 ant_value_t p = js_get(js, args[1], "promise");
507 ant_value_t r = js_get(js, args[1], "reason");
508 if (is_err(b)) return b;
509 if (is_err(c)) return c;
510 bubbles = js_truthy(js, b);
511 cancelable = js_truthy(js, c);
512 if (vtype(p) != T_UNDEF) promise = p;
513 if (vtype(r) != T_UNDEF) reason = r;
514 }
515
516 ant_value_t this_obj = js_mkobj(js);
517 ant_value_t proto = js_instance_proto_from_new_target(js, g_promiserejectionevent_proto);
518 if (is_object_type(proto)) js_set_proto_init(this_obj, proto);
519
520 js_init_event_obj(js, this_obj, type_val, bubbles, cancelable);
521 js_set(js, this_obj, "promise", promise);
522 js_set(js, this_obj, "reason", reason);
523 return this_obj;
524}
525
526static ant_value_t js_eventtarget_ctor(ant_t *js, ant_value_t *args, int nargs) {
527 if (vtype(js->new_target) == T_UNDEF)
528 return js_mkerr_typed(js, JS_ERR_TYPE, "EventTarget constructor requires 'new'");
529
530 ant_value_t this_obj = js_getthis(js);
531 if (is_object_type(this_obj)) js_set_slot(this_obj, SLOT_BRAND, js_mknum(BRAND_EVENTTARGET));
532 return js_mkundef();
533}
534
535static bool parse_addEventListener_options(ant_t *js, ant_value_t *args, int nargs, bool *once, bool *capture, ant_value_t *signal) {
536 *once = false; *capture = false; *signal = js_mkundef();
537 if (nargs < 3) return true;
538 ant_value_t opts = args[2];
539 if (vtype(opts) == T_BOOL) {
540 *capture = js_truthy(js, opts);
541 return true;
542 }
543 if (vtype(opts) != T_OBJ) return true;
544
545 ant_value_t sig = js_get(js, opts, "signal");
546 if (vtype(sig) == T_NULL) return false;
547 if (vtype(sig) != T_UNDEF) {
548 if (!abort_signal_is_signal(sig)) return false;
549 *signal = sig;
550 }
551
552 ant_value_t o = js_get(js, opts, "once");
553 ant_value_t ca = js_get(js, opts, "capture");
554 if (vtype(o) != T_UNDEF) *once = js_truthy(js, o);
555 if (vtype(ca) != T_UNDEF) *capture = js_truthy(js, ca);
556 return true;
557}
558
559static ant_value_t add_listener_to(ant_t *js, ant_value_t *args, int nargs, EventType *evt) {
560 if (!evt) return js_mkerr(js, "failed to create event type");
561
562 bool once, capture;
563 ant_value_t signal;
564 if (!parse_addEventListener_options(js, args, nargs, &once, &capture, &signal))
565 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to execute 'addEventListener': signal is not an AbortSignal");
566
567 if (vtype(signal) != T_UNDEF && abort_signal_is_aborted(signal)) return js_mkundef();
568
569 ant_value_t cb = args[1];
570 uint8_t cbt = vtype(cb);
571 if (cbt == T_NULL || cbt == T_UNDEF) return js_mkundef();
572 if (cbt != T_FUNC && cbt != T_CFUNC) return js_mkundef();
573
574 for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) {
575 EventListenerEntry *e = (EventListenerEntry *)utarray_eltptr(evt->listeners, i);
576 if (e->callback == cb && e->capture == capture) return js_mkundef();
577 }
578
579 EventListenerEntry entry = { cb, js_mkundef(), signal, once, capture };
580 utarray_push_back(evt->listeners, &entry);
581 return js_mkundef();
582}
583
584static ant_value_t remove_listener_from(ant_t *js, ant_value_t *args, int nargs, EventType *evt) {
585 if (!evt) return js_mkundef();
586
587 bool capture = false;
588 if (nargs >= 3) {
589 ant_value_t opts = args[2];
590 if (vtype(opts) == T_BOOL) capture = js_truthy(js, opts);
591 else if (vtype(opts) == T_OBJ) {
592 ant_value_t ca = js_get(js, opts, "capture");
593 if (vtype(ca) != T_UNDEF) capture = js_truthy(js, ca);
594 }}
595
596 ant_value_t cb = (nargs >= 2) ? args[1] : js_mkundef();
597 uint8_t cbt = vtype(cb);
598 if (cbt == T_NULL || cbt == T_UNDEF) return js_mkundef();
599
600 for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) {
601 EventListenerEntry *e = (EventListenerEntry *)utarray_eltptr(evt->listeners, i);
602 if (e->callback == cb && e->capture == capture) {
603 utarray_erase(evt->listeners, i, 1);
604 return js_mkundef();
605 }
606 }
607 return js_mkundef();
608}
609
610static ant_value_t dispatch_event_to(ant_t *js, ant_value_t event_obj, EventType *evt, ant_value_t target) {
611 if (!evt) return js_true;
612
613 event_data_t *data = get_event_data(event_obj);
614 if (data) { data->dispatching = true; data->stop_immediate = false; }
615 js_set(js, event_obj, "target", target);
616 js_set(js, event_obj, "currentTarget", target);
617 js_set(js, event_obj, "eventPhase", js_mknum(2));
618
619 ant_value_t call_args[1] = { event_obj };
620 unsigned int n = utarray_len(evt->listeners);
621
622 for (unsigned int i = 0; i < n;) {
623 EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i);
624
625 if (vtype(entry->signal) != T_UNDEF && abort_signal_is_aborted(entry->signal)) {
626 utarray_erase(evt->listeners, i, 1);
627 n--; continue;
628 }
629
630 ant_value_t cb = entry->callback;
631 bool once = entry->once;
632 if (once) { utarray_erase(evt->listeners, i, 1); n--; } else i++;
633
634 uint8_t t = vtype(cb);
635 if (t != T_FUNC && t != T_CFUNC) continue;
636
637 eventemitter_call_listener(js, cb, js_mkundef(), call_args, 1);
638 if (data && data->stop_immediate) break;
639 }
640
641 if (data) data->dispatching = false;
642 js_set(js, event_obj, "currentTarget", js_mknull());
643 js_set(js, event_obj, "eventPhase", js_mknum(0));
644
645 bool canceled = data && data->canceled;
646 return js_bool(!canceled);
647}
648
649static ant_value_t js_add_event_listener_method(ant_t *js, ant_value_t *args, int nargs) {
650 if (nargs < 1) return js_mkundef();
651 ant_value_t key = evt_key_from_arg(args[0]);
652 if (!key) return js_mkundef();
653 return add_listener_to(js, args, nargs,
654 find_or_create_emitter_event_type(js, js_getthis(js), key));
655}
656
657static ant_value_t js_add_event_listener(ant_t *js, ant_value_t *args, int nargs) {
658 if (nargs < 1) return js_mkundef();
659 ant_value_t key = evt_key_from_arg(args[0]);
660 if (!key) return js_mkundef();
661 return add_listener_to(js, args, nargs, find_or_create_global_event_type(js, key));
662}
663
664static ant_value_t js_remove_event_listener_method(ant_t *js, ant_value_t *args, int nargs) {
665 if (nargs < 1) return js_mkundef();
666 ant_value_t key = evt_key_from_arg(args[0]);
667 if (!key) return js_mkundef();
668 return remove_listener_from(js, args, nargs,
669 find_emitter_event_type(js, js_getthis(js), key));
670}
671
672static ant_value_t js_remove_event_listener(ant_t *js, ant_value_t *args, int nargs) {
673 if (nargs < 1) return js_mkundef();
674 ant_value_t key = evt_key_from_arg(args[0]);
675 if (!key) return js_mkundef();
676 return remove_listener_from(js, args, nargs, find_global_event_type(js, key));
677}
678
679static ant_value_t js_dispatch_event_method(ant_t *js, ant_value_t *args, int nargs) {
680 if (nargs < 1 || !is_object_type(args[0])) return js_false;
681 ant_value_t this_obj = js_getthis(js);
682 ant_value_t key = js_get(js, args[0], "type");
683 if (!evt_key_from_arg(key)) return js_false;
684 return dispatch_event_to(js, args[0],
685 find_emitter_event_type(js, this_obj, key), this_obj);
686}
687
688static ant_value_t js_dispatch_event(ant_t *js, ant_value_t *args, int nargs) {
689 if (nargs < 1 || !is_object_type(args[0])) return js_false;
690 ant_value_t key = js_get(js, args[0], "type");
691 if (!evt_key_from_arg(key)) return js_false;
692 return dispatch_event_to(js, args[0],
693 find_global_event_type(js, key), js_glob(js));
694}
695
696void js_dispatch_global_event(ant_t *js, ant_value_t event_obj) {
697 ant_value_t key = js_get(js, event_obj, "type");
698 if (!evt_key_from_arg(key)) return;
699 EventType *evt = find_global_event_type(js, key);
700 if (!evt || utarray_len(evt->listeners) == 0) return;
701 dispatch_event_to(js, event_obj, evt, js_glob(js));
702}
703
704static bool eventemitter_add_listener_impl(
705 ant_t *js,
706 ant_value_t target, ant_value_t key,
707 ant_value_t listener, bool once, bool prepend
708) {
709 EventType *evt = NULL;
710 EventListenerEntry entry = {0};
711 uint8_t t = 0;
712
713 if (!is_object_type(target) || !key) return false;
714 t = vtype(listener);
715 if (t != T_FUNC && t != T_CFUNC) return false;
716
717 evt = find_or_create_emitter_event_type(js, target, key);
718 if (!evt) return false;
719
720 entry.callback = listener;
721 entry.raw_callback = js_mkundef();
722 entry.signal = js_mkundef();
723 entry.once = once;
724 entry.capture = false;
725
726 if (once) {
727 entry.raw_callback = js_heavy_mkfun(js, js_eventemitter_once_wrapper, listener);
728 if (is_callable(entry.raw_callback)) js_set(js, entry.raw_callback, "listener", listener);
729 }
730
731 if (!prepend || utarray_len(evt->listeners) == 0) utarray_push_back(evt->listeners, &entry);
732 else {
733 utarray_push_back(evt->listeners, &entry);
734 EventListenerEntry *items = (EventListenerEntry *)utarray_eltptr(evt->listeners, 0);
735 if (!items) return false;
736 memmove(items + 1, items, (utarray_len(evt->listeners) - 1u) * sizeof(*items));
737 items[0] = entry;
738 }
739
740 int max_listeners = eventemitter_get_max_listeners_impl(target);
741 if (
742 max_listeners > 0 &&
743 !evt->warned_max_listeners &&
744 (int)utarray_len(evt->listeners) > max_listeners
745 ) {
746 evt->warned_max_listeners = true;
747 if (vtype(key) == T_STR) {
748 fprintf(
749 stderr,
750 "Warning: Possible EventEmitter memory leak detected. "
751 "%u '%s' listeners added. Use emitter.setMaxListeners() to increase limit.\n",
752 (unsigned)utarray_len(evt->listeners),
753 js_str(js, key)
754 );
755 } else {
756 fprintf(
757 stderr,
758 "Warning: Possible EventEmitter memory leak detected. "
759 "%u listeners added for a Symbol event. Use emitter.setMaxListeners() to increase limit.\n",
760 (unsigned)utarray_len(evt->listeners)
761 );
762 }
763 }
764
765 return true;
766}
767
768static bool eventemitter_remove_listener_impl(
769 ant_t *js,
770 ant_value_t target, ant_value_t key,
771 ant_value_t listener
772) {
773 EventType *evt = NULL;
774 uint8_t t = 0;
775
776 if (!is_object_type(target) || !key) return false;
777 t = vtype(listener);
778 if (t != T_FUNC && t != T_CFUNC) return false;
779
780 evt = find_emitter_event_type(js, target, key);
781 if (!evt) return false;
782
783 for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) {
784 EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i);
785 if (entry->callback == listener || entry->raw_callback == listener) {
786 utarray_erase(evt->listeners, i, 1);
787 return true;
788 }}
789
790 return false;
791}
792
793static ant_offset_t eventemitter_listener_count_impl(
794 ant_t *js,
795 ant_value_t target, ant_value_t key
796) {
797 EventType *evt = NULL;
798
799 if (!is_object_type(target) || !key) return 0;
800 evt = find_emitter_event_type(js, target, key);
801 if (!evt) return 0;
802
803 return (ant_offset_t)utarray_len(evt->listeners);
804}
805
806static ant_value_t js_eventemitter_off(ant_t *js, ant_value_t *args, int nargs) {
807 if (nargs < 2) return js_mkerr(js, "off requires 2 arguments (event, listener)");
808 ant_value_t key = evt_key_from_arg(args[0]);
809
810 if (!key) return js_mkerr(js, "event must be a string or Symbol");
811 ant_value_t this_obj = js_getthis(js);
812
813 remove_listener_from(
814 js, args, nargs,
815 find_emitter_event_type(js, this_obj, key)
816 );
817
818 return this_obj;
819}
820
821static bool eventemitter_emit_args_impl(
822 ant_t *js,
823 ant_value_t target, ant_value_t key,
824 ant_value_t *args, int nargs
825) {
826 EventType *evt = NULL;
827
828 if (!is_object_type(target) || !key) return false;
829 evt = find_emitter_event_type(js, target, key);
830 if (!evt || utarray_len(evt->listeners) == 0) return false;
831
832 for (unsigned int i = 0; i < utarray_len(evt->listeners);) {
833 EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i);
834 ant_value_t cb = entry->callback;
835 bool once = entry->once;
836
837 if (once) utarray_erase(evt->listeners, i, 1);
838 else i++;
839
840 if (vtype(entry->signal) != T_UNDEF && abort_signal_is_aborted(entry->signal)) continue;
841 if (vtype(cb) != T_FUNC && vtype(cb) != T_CFUNC) continue;
842
843 ant_value_t result = eventemitter_call_listener(js, cb, target, args, nargs);
844 if (vtype(result) == T_ERR) {
845 if (vtype(key) == T_STR) fprintf(stderr, "Error in event listener for '%s': %s\n", js_str(js, key), js_str(js, result));
846 else fprintf(stderr, "Error in event listener: %s\n", js_str(js, result));
847 }
848 }
849
850 return true;
851}
852
853static ant_value_t js_eventemitter_emit(ant_t *js, ant_value_t *args, int nargs) {
854 if (nargs < 1) return js_mkerr(js, "emit requires at least 1 argument (event)");
855 ant_value_t key = evt_key_from_arg(args[0]);
856 if (!key) return js_mkerr(js, "event must be a string or Symbol");
857
858 return js_bool(eventemitter_emit_args_impl(
859 js, js_getthis(js), key,
860 nargs > 1 ? &args[1] : NULL, nargs - 1
861 ));
862}
863
864bool eventemitter_emit_args_val(
865 ant_t *js,
866 ant_value_t target, ant_value_t key,
867 ant_value_t *args, int nargs
868) {
869 return eventemitter_emit_args_impl(js, target, key, args, nargs);
870}
871
872bool eventemitter_emit_args(
873 ant_t *js,
874 ant_value_t target, const char *event_type,
875 ant_value_t *args, int nargs
876) {
877 return eventemitter_emit_args_val(js, target, js_mkstr(js, event_type, strlen(event_type)), args, nargs);
878}
879
880bool eventemitter_add_listener_val(
881 ant_t *js,
882 ant_value_t target, ant_value_t key,
883 ant_value_t listener, bool once
884) {
885 return eventemitter_add_listener_impl(js, target, key, listener, once, false);
886}
887
888bool eventemitter_add_listener(
889 ant_t *js,
890 ant_value_t target, const char *event_type,
891 ant_value_t listener, bool once
892) {
893 return eventemitter_add_listener_val(js, target, js_mkstr(js, event_type, strlen(event_type)), listener, once);
894}
895
896bool eventemitter_remove_listener_val(
897 ant_t *js,
898 ant_value_t target, ant_value_t key,
899 ant_value_t listener
900) {
901 return eventemitter_remove_listener_impl(js, target, key, listener);
902}
903
904bool eventemitter_remove_listener(
905 ant_t *js,
906 ant_value_t target, const char *event_type,
907 ant_value_t listener
908) {
909 return eventemitter_remove_listener_val(js, target, js_mkstr(js, event_type, strlen(event_type)), listener);
910}
911
912ant_offset_t eventemitter_listener_count_val(
913 ant_t *js,
914 ant_value_t target, ant_value_t key
915) {
916 return eventemitter_listener_count_impl(js, target, key);
917}
918
919ant_offset_t eventemitter_listener_count(
920 ant_t *js,
921 ant_value_t target, const char *event_type
922) {
923 return eventemitter_listener_count_val(js, target, js_mkstr(js, event_type, strlen(event_type)));
924}
925
926static ant_value_t js_eventemitter_add(ant_t *js, ant_value_t *args, int nargs, bool once, bool prepend) {
927 if (nargs < 2) return js_mkerr(js, "requires 2 arguments (event, listener)");
928 ant_value_t key = evt_key_from_arg(args[0]);
929
930 if (!key) return js_mkerr(js, "event must be a string or Symbol");
931 if (!eventemitter_add_listener_impl(js, js_getthis(js), key, args[1], once, prepend))
932 return js_mkerr(js, "listener must be a function");
933
934 return js_getthis(js);
935}
936
937static ant_value_t js_eventemitter_on(ant_t *js, ant_value_t *args, int nargs) {
938 return js_eventemitter_add(js, args, nargs, false, false);
939}
940
941static ant_value_t js_eventemitter_once(ant_t *js, ant_value_t *args, int nargs) {
942 return js_eventemitter_add(js, args, nargs, true, false);
943}
944
945static ant_value_t js_eventemitter_prepend_listener(ant_t *js, ant_value_t *args, int nargs) {
946 return js_eventemitter_add(js, args, nargs, false, true);
947}
948
949static ant_value_t js_eventemitter_prepend_once_listener(ant_t *js, ant_value_t *args, int nargs) {
950 return js_eventemitter_add(js, args, nargs, true, true);
951}
952
953static ant_value_t js_eventemitter_removeAllListeners(ant_t *js, ant_value_t *args, int nargs) {
954 ant_value_t this_obj = js_getthis(js);
955 EventType **events = NULL;
956
957 if (nargs < 1 || vtype(args[0]) == T_UNDEF) {
958 events = get_or_create_emitter_events(js, this_obj);
959 if (!events || !*events) return this_obj;
960
961 EventType *evt, *tmp;
962 HASH_ITER(hh, *events, evt, tmp) utarray_clear(evt->listeners);
963 return this_obj;
964 }
965
966 ant_value_t key = evt_key_from_arg(args[0]);
967 if (!key) return this_obj;
968
969 EventType *evt = find_emitter_event_type(js, this_obj, key);
970 if (evt) utarray_clear(evt->listeners);
971
972 return this_obj;
973}
974
975static ant_value_t js_eventemitter_listenerCount(ant_t *js, ant_value_t *args, int nargs) {
976 if (nargs < 1) return js_mknum(0);
977 ant_value_t key = evt_key_from_arg(args[0]);
978
979 if (!key) return js_mknum(0);
980 EventType *evt = find_emitter_event_type(js, js_getthis(js), key);
981
982 if (!evt) return js_mknum(0);
983 return js_mknum((double)utarray_len(evt->listeners));
984}
985
986static ant_value_t js_eventemitter_setMaxListeners(ant_t *js, ant_value_t *args, int nargs) {
987 if (nargs < 1) return js_mkerr(js, "setMaxListeners requires 1 argument");
988 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "n must be a number");
989
990 int n = (int)js_getnum(args[0]);
991 if (n < 0) return js_mkerr(js, "n must be non-negative");
992
993 ant_value_t this_obj = js_getthis(js);
994 if (!is_object_type(this_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "setMaxListeners requires an object receiver");
995 js_set_slot(this_obj, SLOT_EVENT_MAX_LISTENERS, js_mknum(n));
996 return this_obj;
997}
998
999static ant_value_t js_eventemitter_getMaxListeners(ant_t *js, ant_value_t *args, int nargs) {
1000 ant_value_t this_obj = js_getthis(js);
1001 if (!is_object_type(this_obj)) return js_mknum(EVENTS_DEFAULT_MAX_LISTENERS);
1002 return js_mknum(eventemitter_get_max_listeners_impl(this_obj));
1003}
1004
1005static ant_value_t js_eventemitter_rawListeners(ant_t *js, ant_value_t *args, int nargs) {
1006 if (nargs < 1) return js_mkarr(js);
1007 ant_value_t key = evt_key_from_arg(args[0]);
1008 if (!key) return js_mkarr(js);
1009 return eventemitter_get_listeners_array(js, js_getthis(js), key, true);
1010}
1011
1012static ant_value_t js_eventemitter_listeners(ant_t *js, ant_value_t *args, int nargs) {
1013 if (nargs < 1) return js_mkarr(js);
1014 ant_value_t key = evt_key_from_arg(args[0]);
1015 if (!key) return js_mkarr(js);
1016 return eventemitter_get_listeners_array(js, js_getthis(js), key, false);
1017}
1018
1019static ant_value_t js_eventemitter_eventNames(ant_t *js, ant_value_t *args, int nargs) {
1020 ant_value_t this_obj = js_getthis(js);
1021 ant_value_t result = js_mkarr(js);
1022
1023 EventType **events = get_or_create_emitter_events(js, this_obj);
1024 if (events && *events) {
1025 EventType *evt, *tmp;
1026 HASH_ITER(hh, *events, evt, tmp) if (utarray_len(evt->listeners) > 0)
1027 js_arr_push(js, result, evt->js_key);
1028 }
1029
1030 return result;
1031}
1032
1033static ant_value_t js_events_once_listener(ant_t *js, ant_value_t *args, int nargs) {
1034 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
1035 if (!is_object_type(state)) return js_mkundef();
1036
1037 ant_value_t promise = js_get_slot(state, SLOT_DATA);
1038 if (vtype(promise) != T_PROMISE) return js_mkundef();
1039
1040 ant_value_t settled = js_get_slot(state, SLOT_SETTLED);
1041 if (vtype(settled) == T_BOOL && settled == js_true) return js_mkundef();
1042 js_set_slot(state, SLOT_SETTLED, js_true);
1043
1044 ant_value_t signal = js_get(js, state, "signal");
1045 ant_value_t abort_listener = js_get(js, state, "abortListener");
1046 if (abort_signal_is_signal(signal) && is_callable(abort_listener))
1047 abort_signal_remove_listener(js, signal, abort_listener);
1048
1049 ant_value_t values = js_mkarr(js);
1050 for (int i = 0; i < nargs; i++) js_arr_push(js, values, args[i]);
1051 js_resolve_promise(js, promise, values);
1052
1053 return js_mkundef();
1054}
1055
1056static ant_value_t js_events_once_abort_listener(ant_t *js, ant_value_t *args, int nargs) {
1057 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
1058 if (!is_object_type(state)) return js_mkundef();
1059
1060 ant_value_t promise = js_get_slot(state, SLOT_DATA);
1061 if (vtype(promise) != T_PROMISE) return js_mkundef();
1062
1063 ant_value_t settled = js_get_slot(state, SLOT_SETTLED);
1064 if (vtype(settled) == T_BOOL && settled == js_true) return js_mkundef();
1065 js_set_slot(state, SLOT_SETTLED, js_true);
1066
1067 ant_value_t signal = js_get(js, state, "signal");
1068 ant_value_t abort_listener = js_get(js, state, "abortListener");
1069 if (abort_signal_is_signal(signal) && is_callable(abort_listener))
1070 abort_signal_remove_listener(js, signal, abort_listener);
1071
1072 ant_value_t reason = abort_signal_get_reason(signal);
1073 if (vtype(reason) == T_UNDEF) reason = js_mkerr(js, "The operation was aborted");
1074 js_reject_promise(js, promise, reason);
1075
1076 return js_mkundef();
1077}
1078
1079static bool js_events_once_is_abort_key(ant_t *js, ant_value_t key) {
1080 size_t key_len = 0;
1081 const char *key_str = vtype(key) == T_STR ? js_getstr(js, key, &key_len) : NULL;
1082 return key_str && key_len == 5 && memcmp(key_str, "abort", 5) == 0;
1083}
1084
1085static void js_events_once_reject_aborted(ant_t *js, ant_value_t promise, ant_value_t signal) {
1086 ant_value_t reason = abort_signal_get_reason(signal);
1087 if (vtype(reason) == T_UNDEF) reason = js_mkerr(js, "The operation was aborted");
1088 js_reject_promise(js, promise, reason);
1089}
1090
1091static ant_value_t js_events_once_attach(
1092 ant_t *js,
1093 ant_value_t promise,
1094 ant_value_t target,
1095 ant_value_t key,
1096 ant_value_t listener,
1097 ant_value_t signal
1098) {
1099 if (abort_signal_is_signal(target)) {
1100 if (!js_events_once_is_abort_key(js, key)) {
1101 js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "AbortSignal only supports the abort event"));
1102 return promise;
1103 }
1104 abort_signal_add_listener(js, target, listener);
1105 return promise;
1106 }
1107
1108 if (is_eventemitter_instance(target)) {
1109 if (!eventemitter_add_listener_val(js, target, key, listener, true))
1110 js_reject_promise(js, promise, js_mkerr(js, "listener must be a function"));
1111 return promise;
1112 }
1113
1114 if (is_eventtarget_instance(target)) {
1115 ant_value_t listener_options = js_mkobj(js);
1116 js_set(js, listener_options, "once", js_true);
1117 if (abort_signal_is_signal(signal)) js_set(js, listener_options, "signal", signal);
1118
1119 ant_value_t call_args[3] = { key, listener, listener_options };
1120 ant_value_t result = add_listener_to(js, call_args, 3, find_or_create_emitter_event_type(js, target, key));
1121 if (is_err(result)) js_reject_promise(js, promise, result);
1122 return promise;
1123 }
1124
1125 js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "target is not an EventEmitter or EventTarget"));
1126 return promise;
1127}
1128
1129static ant_value_t js_events_once(ant_t *js, ant_value_t *args, int nargs) {
1130 if (nargs < 2 || !is_object_type(args[0]))
1131 return js_mkerr_typed(js, JS_ERR_TYPE, "events.once requires an emitter and event name");
1132
1133 ant_value_t key = evt_key_from_arg(args[1]);
1134 if (!key) return js_mkerr_typed(js, JS_ERR_TYPE, "event must be a string or Symbol");
1135
1136 ant_value_t promise = js_mkpromise(js);
1137 if (is_err(promise)) return promise;
1138
1139 ant_value_t state = js_mkobj(js);
1140 js_set_slot(state, SLOT_DATA, promise);
1141 js_set_slot(state, SLOT_SETTLED, js_false);
1142
1143 ant_value_t listener = js_heavy_mkfun(js, js_events_once_listener, state);
1144 ant_value_t target = args[0];
1145 ant_value_t options = nargs >= 3 ? args[2] : js_mkundef();
1146 ant_value_t signal = js_mkundef();
1147
1148 if (is_object_type(options)) signal = js_get(js, options, "signal");
1149 if (abort_signal_is_signal(signal)) {
1150 if (abort_signal_is_aborted(signal)) {
1151 js_events_once_reject_aborted(js, promise, signal);
1152 return promise;
1153 }
1154 ant_value_t abort_listener = js_heavy_mkfun(js, js_events_once_abort_listener, state);
1155 js_set(js, state, "signal", signal);
1156 js_set(js, state, "abortListener", abort_listener);
1157 abort_signal_add_listener(js, signal, abort_listener);
1158 }
1159
1160 return js_events_once_attach(js, promise, target, key, listener, signal);
1161}
1162
1163static ant_value_t js_events_disposable_dispose(ant_t *js, ant_value_t *args, int nargs) {
1164 ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
1165 if (!is_object_type(state)) return js_mkundef();
1166
1167 ant_value_t disposed = js_get_slot(state, SLOT_SETTLED);
1168 if (vtype(disposed) == T_BOOL && disposed == js_true) return js_mkundef();
1169 js_set_slot(state, SLOT_SETTLED, js_true);
1170
1171 ant_value_t signal = js_get(js, state, "signal");
1172 ant_value_t listener = js_get(js, state, "listener");
1173 if (abort_signal_is_signal(signal) && is_callable(listener))
1174 abort_signal_remove_listener(js, signal, listener);
1175
1176 return js_mkundef();
1177}
1178
1179static ant_value_t js_events_add_abort_listener(ant_t *js, ant_value_t *args, int nargs) {
1180 if (nargs < 2 || !abort_signal_is_signal(args[0]) || !is_callable(args[1]))
1181 return js_mkerr_typed(js, JS_ERR_TYPE, "events.addAbortListener requires an AbortSignal and listener");
1182
1183 bool already_aborted = abort_signal_is_aborted(args[0]);
1184 if (already_aborted) {
1185 ant_value_t event = js_mkobj(js);
1186 js_set(js, event, "type", js_mkstr(js, "abort", 5));
1187 eventemitter_call_listener(js, args[1], args[0], &event, 1);
1188 } else abort_signal_add_listener(js, args[0], args[1]);
1189
1190 ant_value_t state = js_mkobj(js);
1191 js_set_slot(state, SLOT_SETTLED, js_bool(already_aborted));
1192 js_set(js, state, "signal", args[0]);
1193 js_set(js, state, "listener", args[1]);
1194
1195 ant_value_t disposable = js_mkobj(js);
1196 js_set(js, disposable, "dispose", js_heavy_mkfun(js, js_events_disposable_dispose, state));
1197
1198 return disposable;
1199}
1200
1201static ant_value_t js_events_set_max_listeners(ant_t *js, ant_value_t *args, int nargs) {
1202 if (nargs < 1) return js_mkerr(js, "setMaxListeners requires at least 1 argument");
1203 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "n must be a number");
1204
1205 int n = (int)js_getnum(args[0]);
1206 if (n < 0) return js_mkerr(js, "n must be non-negative");
1207
1208 for (int i = 1; i < nargs; i++) {
1209 if (!is_object_type(args[i])) continue;
1210 js_set_slot(args[i], SLOT_EVENT_MAX_LISTENERS, js_mknum(n));
1211 }
1212
1213 return js_mkundef();
1214}
1215
1216static ant_value_t js_events_get_max_listeners(ant_t *js, ant_value_t *args, int nargs) {
1217 if (nargs < 1) return js_mknum(EVENTS_DEFAULT_MAX_LISTENERS);
1218 if (!is_object_type(args[0])) return js_mknum(EVENTS_DEFAULT_MAX_LISTENERS);
1219 return js_mknum(eventemitter_get_max_listeners_impl(args[0]));
1220}
1221
1222// TODO: fix stub
1223static ant_value_t js_events_on(ant_t *js, ant_value_t *args, int nargs) {
1224 return js_mkerr_typed(js, JS_ERR_TYPE, "events.on async iterator is not implemented");
1225}
1226
1227ant_value_t events_library(ant_t *js) {
1228 ant_value_t lib = js_mkobj(js);
1229
1230 eventemitter_prototype(js);
1231 js_set_module_default(js, lib, g_eventemitter_ctor, "EventEmitter");
1232 js_set(js, lib, "once", js_mkfun(js_events_once));
1233 js_set(js, lib, "on", js_mkfun(js_events_on));
1234 js_set(js, lib, "addAbortListener", js_mkfun(js_events_add_abort_listener));
1235 js_set(js, lib, "setMaxListeners", js_mkfun(js_events_set_max_listeners));
1236 js_set(js, lib, "getMaxListeners", js_mkfun(js_events_get_max_listeners));
1237 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "events", 6));
1238
1239 return lib;
1240}
1241
1242ant_value_t eventemitter_prototype(ant_t *js) {
1243 if (g_eventemitter_proto) return g_eventemitter_proto;
1244
1245 ant_value_t object_proto = js->sym.object_proto;
1246 ant_value_t function_proto = js_get_slot(js_glob(js), SLOT_FUNC_PROTO);
1247 if (vtype(function_proto) == T_UNDEF) function_proto = js_get_ctor_proto(js, "Function", 8);
1248
1249 ant_value_t eventemitter_ctor = js_mkobj(js);
1250 ant_value_t eventemitter_proto = js_mkobj(js);
1251
1252 if (is_object_type(object_proto)) js_set_proto_init(eventemitter_proto, object_proto);
1253 if (is_object_type(function_proto)) js_set_proto_init(eventemitter_ctor, function_proto);
1254
1255 js_set(js, eventemitter_proto, "on", js_mkfun(js_eventemitter_on));
1256 js_set_exact(js, eventemitter_proto, "addListener", js_get(js, eventemitter_proto, "on"));
1257 js_set(js, eventemitter_proto, "once", js_mkfun(js_eventemitter_once));
1258 js_set(js, eventemitter_proto, "prependListener", js_mkfun(js_eventemitter_prepend_listener));
1259 js_set(js, eventemitter_proto, "prependOnceListener", js_mkfun(js_eventemitter_prepend_once_listener));
1260 js_set(js, eventemitter_proto, "off", js_mkfun(js_eventemitter_off));
1261 js_set_exact(js, eventemitter_proto, "removeListener", js_get(js, eventemitter_proto, "off"));
1262 js_set(js, eventemitter_proto, "emit", js_mkfun(js_eventemitter_emit));
1263 js_set(js, eventemitter_proto, "removeAllListeners", js_mkfun(js_eventemitter_removeAllListeners));
1264 js_set(js, eventemitter_proto, "listenerCount", js_mkfun(js_eventemitter_listenerCount));
1265 js_set(js, eventemitter_proto, "setMaxListeners", js_mkfun(js_eventemitter_setMaxListeners));
1266 js_set(js, eventemitter_proto, "getMaxListeners", js_mkfun(js_eventemitter_getMaxListeners));
1267 js_set(js, eventemitter_proto, "listeners", js_mkfun(js_eventemitter_listeners));
1268 js_set(js, eventemitter_proto, "rawListeners", js_mkfun(js_eventemitter_rawListeners));
1269 js_set(js, eventemitter_proto, "eventNames", js_mkfun(js_eventemitter_eventNames));
1270 js_set_sym(js, eventemitter_proto, get_toStringTag_sym(), js_mkstr(js, "EventEmitter", 12));
1271
1272 js_set_slot(eventemitter_ctor, SLOT_CFUNC, js_mkfun(js_eventemitter_ctor));
1273 js_mkprop_fast(js, eventemitter_ctor, "prototype", 9, eventemitter_proto);
1274 js_mkprop_fast(js, eventemitter_ctor, "name", 4, ANT_STRING("EventEmitter"));
1275 js_set_descriptor(js, eventemitter_ctor, "name", 4, 0);
1276
1277 g_eventemitter_proto = eventemitter_proto;
1278 g_eventemitter_ctor = js_obj_to_func(eventemitter_ctor);
1279 js_set(js, eventemitter_proto, "constructor", g_eventemitter_ctor);
1280 js_set_descriptor(js, eventemitter_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
1281
1282 return g_eventemitter_proto;
1283}
1284
1285void init_events_module(void) {
1286 ant_t *js = rt->js;
1287 ant_value_t global = js_glob(js);
1288 g_isTrusted_getter = js_mkfun(js_event_get_isTrusted);
1289
1290 g_event_proto = js_mkobj(js);
1291 js_set_sym(js, g_event_proto, get_toStringTag_sym(), js_mkstr(js, "Event", 5));
1292 js_set(js, g_event_proto, "preventDefault", js_mkfun(js_event_preventDefault));
1293 js_set(js, g_event_proto, "stopPropagation", js_mkfun(js_event_stopPropagation));
1294 js_set(js, g_event_proto, "stopImmediatePropagation", js_mkfun(js_event_stopImmediatePropagation));
1295 js_set(js, g_event_proto, "composedPath", js_mkfun(js_event_composedPath));
1296 js_set(js, g_event_proto, "initEvent", js_mkfun(js_event_initEvent));
1297 js_set(js, g_event_proto, "NONE", js_mknum(0));
1298 js_set(js, g_event_proto, "CAPTURING_PHASE", js_mknum(1));
1299 js_set(js, g_event_proto, "AT_TARGET", js_mknum(2));
1300 js_set(js, g_event_proto, "BUBBLING_PHASE", js_mknum(3));
1301
1302 ant_value_t event_fn = js_make_ctor(js, js_event_ctor, g_event_proto, "Event", 5);
1303 js_set(js, event_fn, "NONE", js_mknum(0));
1304 js_set(js, event_fn, "CAPTURING_PHASE", js_mknum(1));
1305 js_set(js, event_fn, "AT_TARGET", js_mknum(2));
1306 js_set(js, event_fn, "BUBBLING_PHASE", js_mknum(3));
1307 js_set(js, global, "Event", event_fn);
1308
1309 g_customevent_proto = js_mkobj(js);
1310 js_set_proto_init(g_customevent_proto, g_event_proto);
1311 js_set_sym(js, g_customevent_proto, get_toStringTag_sym(), js_mkstr(js, "CustomEvent", 11));
1312
1313 ant_value_t customevent_fn = js_make_ctor(js, js_customevent_ctor, g_customevent_proto, "CustomEvent", 11);
1314 js_set(js, global, "CustomEvent", customevent_fn);
1315
1316 g_errorevent_proto = js_mkobj(js);
1317 js_set_proto_init(g_errorevent_proto, g_event_proto);
1318 js_set_sym(js, g_errorevent_proto, get_toStringTag_sym(), js_mkstr(js, "ErrorEvent", 10));
1319
1320 ant_value_t errorevent_fn = js_make_ctor(js, js_errorevent_ctor, g_errorevent_proto, "ErrorEvent", 10);
1321 js_set(js, global, "ErrorEvent", errorevent_fn);
1322
1323 g_promiserejectionevent_proto = js_mkobj(js);
1324 js_set_proto_init(g_promiserejectionevent_proto, g_event_proto);
1325 js_set_sym(js, g_promiserejectionevent_proto, get_toStringTag_sym(), js_mkstr(js, "PromiseRejectionEvent", 21));
1326
1327 ant_value_t pre_fn = js_make_ctor(js, js_promiserejectionevent_ctor, g_promiserejectionevent_proto, "PromiseRejectionEvent", 21);
1328 js_set(js, global, "PromiseRejectionEvent", pre_fn);
1329
1330 ant_value_t object_proto = js->sym.object_proto;
1331 ant_value_t function_proto = js_get_slot(global, SLOT_FUNC_PROTO);
1332 if (vtype(function_proto) == T_UNDEF) function_proto = js_get_ctor_proto(js, "Function", 8);
1333
1334 ant_value_t eventtarget_proto = js_mkobj(js);
1335 g_eventtarget_proto = eventtarget_proto;
1336 if (is_object_type(object_proto)) js_set_proto_init(eventtarget_proto, object_proto);
1337 js_set(js, eventtarget_proto, "addEventListener", js_mkfun(js_add_event_listener_method));
1338 js_set(js, eventtarget_proto, "removeEventListener", js_mkfun(js_remove_event_listener_method));
1339 js_set(js, eventtarget_proto, "dispatchEvent", js_mkfun(js_dispatch_event_method));
1340 js_set_sym(js, eventtarget_proto, get_toStringTag_sym(), js_mkstr(js, "EventTarget", 11));
1341
1342 ant_value_t eventtarget_ctor = js_mkobj(js);
1343 if (is_object_type(function_proto)) js_set_proto_init(eventtarget_ctor, function_proto);
1344 js_set_slot(eventtarget_ctor, SLOT_CFUNC, js_mkfun(js_eventtarget_ctor));
1345 js_mkprop_fast(js, eventtarget_ctor, "prototype", 9, eventtarget_proto);
1346 js_mkprop_fast(js, eventtarget_ctor, "name", 4, ANT_STRING("EventTarget"));
1347 js_set_descriptor(js, eventtarget_ctor, "name", 4, 0);
1348 ant_value_t eventtarget_fn = js_obj_to_func(eventtarget_ctor);
1349 js_set(js, eventtarget_proto, "constructor", eventtarget_fn);
1350 js_set_descriptor(js, eventtarget_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
1351
1352 js_set(js, global, "addEventListener", js_mkfun(js_add_event_listener));
1353 js_set(js, global, "removeEventListener", js_mkfun(js_remove_event_listener));
1354 js_set(js, global, "dispatchEvent", js_mkfun(js_dispatch_event));
1355 js_set(js, global, "EventTarget", eventtarget_fn);
1356}
1357
1358static void mark_event_type_listeners(ant_t *js, gc_mark_fn mark, EventType *events) {
1359 EventType *evt, *tmp;
1360 HASH_ITER(hh, events, evt, tmp) {
1361 if (vtype(evt->js_key) == T_STR) mark(js, evt->js_key);
1362 for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) {
1363 EventListenerEntry *e = (EventListenerEntry *)utarray_eltptr(evt->listeners, i);
1364 mark(js, e->callback);
1365 if (vtype(e->raw_callback) != T_UNDEF) mark(js, e->raw_callback);
1366 if (vtype(e->signal) != T_UNDEF) mark(js, e->signal);
1367 }
1368}}
1369
1370void gc_mark_events(ant_t *js, gc_mark_fn mark) {
1371 mark_event_type_listeners(js, mark, global_events);
1372 for (emitter_reg_t *reg = emitter_registry; reg; reg = reg->next) {
1373 if (*reg->events) mark_event_type_listeners(js, mark, *reg->events);
1374 }
1375 if (g_isTrusted_getter) mark(js, g_isTrusted_getter);
1376 if (g_eventemitter_ctor) mark(js, g_eventemitter_ctor);
1377 if (g_eventemitter_proto) mark(js, g_eventemitter_proto);
1378 if (g_eventtarget_proto) mark(js, g_eventtarget_proto);
1379 if (g_event_proto) mark(js, g_event_proto);
1380 if (g_customevent_proto) mark(js, g_customevent_proto);
1381 if (g_errorevent_proto) mark(js, g_errorevent_proto);
1382 if (g_promiserejectionevent_proto) mark(js, g_promiserejectionevent_proto);
1383}