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

Configure Feed

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

EventTarget compliance

+572 -335
+18 -16
examples/demo/events.js
··· 2 2 addEventListener('appReady', () => { 3 3 console.log(' App is ready!'); 4 4 }); 5 - dispatchEvent('appReady'); 5 + dispatchEvent(new Event('appReady')); 6 6 7 7 console.log('\nExample 2: Custom Event Data'); 8 8 addEventListener('userLogin', event => { 9 9 console.log(' User logged in:', event.detail.username); 10 10 console.log(' Login time:', event.detail.timestamp); 11 11 }); 12 - dispatchEvent('userLogin', { 13 - username: 'alice', 14 - timestamp: Date.now() 15 - }); 12 + dispatchEvent(new CustomEvent('userLogin', { 13 + detail: { 14 + username: 'alice', 15 + timestamp: Date.now() 16 + } 17 + })); 16 18 17 19 console.log('\nExample 3: Object Events'); 18 20 const button = new EventTarget(); ··· 23 25 console.log(' Button name:', event.target.name); 24 26 }); 25 27 26 - button.dispatchEvent('click'); 27 - button.dispatchEvent('click'); 28 + button.dispatchEvent(new Event('click')); 29 + button.dispatchEvent(new Event('click')); 28 30 29 31 console.log('\nExample 4: Multiple Event Targets'); 30 32 const user = new EventTarget(); ··· 41 43 console.log(' Socket message:', event.detail.data); 42 44 }); 43 45 44 - user.dispatchEvent('statusChange', { status: 'online' }); 45 - socket.dispatchEvent('message', { data: 'Hello!' }); 46 + user.dispatchEvent(new CustomEvent('statusChange', { detail: { status: 'online' } })); 47 + socket.dispatchEvent(new CustomEvent('message', { detail: { data: 'Hello!' } })); 46 48 47 49 console.log('\nExample 5: Once Listeners'); 48 50 const startup = new EventTarget(); ··· 57 59 { once: true } 58 60 ); 59 61 60 - startup.dispatchEvent('init'); 61 - startup.dispatchEvent('init'); 62 - startup.dispatchEvent('init'); 62 + startup.dispatchEvent(new Event('init')); 63 + startup.dispatchEvent(new Event('init')); 64 + startup.dispatchEvent(new Event('init')); 63 65 console.log(' Total init calls:', initCount); 64 66 65 67 console.log('\nExample 6: Event Emitter Pattern'); ··· 78 80 console.log(' Data received:', event.detail); 79 81 }); 80 82 81 - emitter.emit('data', { value: 42, type: 'number' }); 82 - emitter.emit('data', { value: 'hello', type: 'string' }); 83 + emitter.emit(new CustomEvent('data', { detail: { value: 42, type: 'number' } })); 84 + emitter.emit(new CustomEvent('data', { detail: { value: 'hello', type: 'string' } })); 83 85 84 86 console.log('\nExample 7: Removing Listeners'); 85 87 const temp = new EventTarget(); ··· 89 91 } 90 92 91 93 temp.addEventListener('test', handler); 92 - temp.dispatchEvent('test', 'first call'); 94 + temp.dispatchEvent(new CustomEvent('test', { detail: 'first call' })); 93 95 94 96 temp.removeEventListener('test', handler); 95 - temp.dispatchEvent('test', 'second call (not shown)'); 97 + temp.dispatchEvent(new CustomEvent('test', { detail: 'second call (not shown)' })); 96 98 97 99 console.log(' Handler was removed successfully'); 98 100
+7 -7
examples/spec/events.js
··· 118 118 et.addEventListener('click', e => { 119 119 etReceived = e.type; 120 120 }); 121 - et.dispatchEvent('click'); 121 + et.dispatchEvent(new Event('click')); 122 122 test('EventTarget addEventListener and dispatchEvent', etReceived, 'click'); 123 123 124 124 let etEvent = null; ··· 126 126 et2.addEventListener('custom', e => { 127 127 etEvent = e; 128 128 }); 129 - et2.dispatchEvent('custom', { foo: 'bar' }); 129 + et2.dispatchEvent(new CustomEvent('custom', { detail: { foo: 'bar' } })); 130 130 test('event.type', etEvent.type, 'custom'); 131 131 test('event.target is EventTarget', etEvent.target, et2); 132 132 test('event.detail', etEvent.detail.foo, 'bar'); ··· 138 138 }; 139 139 et3.addEventListener('rem', et3Handler); 140 140 et3.removeEventListener('rem', et3Handler); 141 - et3.dispatchEvent('rem'); 141 + et3.dispatchEvent(new Event('rem')); 142 142 test('EventTarget removeEventListener', et3Val, 0); 143 143 144 144 const et4 = new EventTarget(); 145 145 let et4Count = 0; 146 146 et4.addEventListener('once', () => et4Count++, { once: true }); 147 - et4.dispatchEvent('once'); 148 - et4.dispatchEvent('once'); 147 + et4.dispatchEvent(new Event('once')); 148 + et4.dispatchEvent(new Event('once')); 149 149 test('EventTarget once option', et4Count, 1); 150 150 151 151 console.log('\nGlobal Event Tests\n'); ··· 154 154 addEventListener('global-test', e => { 155 155 globalReceived = e.type; 156 156 }); 157 - dispatchEvent('global-test'); 157 + dispatchEvent(new Event('global-test')); 158 158 test('global addEventListener and dispatchEvent', globalReceived, 'global-test'); 159 159 160 160 let globalRemoved = 0; ··· 163 163 }; 164 164 addEventListener('global-rem', globalHandler); 165 165 removeEventListener('global-rem', globalHandler); 166 - dispatchEvent('global-rem'); 166 + dispatchEvent(new Event('global-rem')); 167 167 test('global removeEventListener', globalRemoved, 0); 168 168 169 169 console.log('\nCustomEvent Tests\n');
+3
include/types.h
··· 24 24 typedef uint64_t ant_offset_t; 25 25 typedef uint64_t ant_value_t; 26 26 27 + typedef ant_value_t (*ant_cfunc_t) 28 + (ant_t *, ant_value_t *, int); 29 + 27 30 #endif
+544 -312
src/modules/events.c
··· 2 2 #include <stdio.h> 3 3 #include <string.h> 4 4 #include <inttypes.h> 5 + #include <time.h> 5 6 #include <uthash.h> 7 + #include <utarray.h> 6 8 7 9 #include "ant.h" 8 10 #include "errors.h" ··· 12 14 #include "silver/engine.h" 13 15 14 16 #include "gc/modules.h" 17 + #include "modules/abort.h" 15 18 #include "modules/events.h" 16 19 #include "modules/symbol.h" 17 20 18 - #define MAX_LISTENERS_PER_EVENT 32 21 + typedef struct { 22 + bool canceled; 23 + bool stop_immediate; 24 + bool stop_propagation; 25 + bool dispatching; 26 + } event_data_t; 27 + 28 + static ant_value_t g_isTrusted_getter = 0; 29 + static ant_value_t g_event_proto = 0; 30 + static ant_value_t g_customevent_proto = 0; 31 + static ant_value_t g_errorevent_proto = 0; 32 + static ant_value_t g_promiserejectionevent_proto = 0; 33 + 34 + static event_data_t *get_event_data(ant_value_t obj) { 35 + ant_value_t slot = js_get_slot(obj, SLOT_DATA); 36 + if (vtype(slot) != T_NUM) return NULL; 37 + return (event_data_t *)(uintptr_t)js_getnum(slot); 38 + } 39 + 40 + static double get_timestamp_ms(void) { 41 + struct timespec ts; 42 + clock_gettime(CLOCK_MONOTONIC, &ts); 43 + return (double)ts.tv_sec * 1e3 + (double)ts.tv_nsec / 1e6; 44 + } 19 45 20 46 typedef struct { 21 - ant_value_t listener; 47 + ant_value_t callback; 48 + ant_value_t signal; 22 49 bool once; 23 - } __attribute__((packed)) EventListener; 50 + bool capture; 51 + } EventListenerEntry; 24 52 53 + static const UT_icd event_listener_icd = { 54 + sizeof(EventListenerEntry), 55 + NULL, NULL, NULL 56 + }; 25 57 26 58 typedef struct { 27 - EventListener listeners[MAX_LISTENERS_PER_EVENT]; 59 + UT_array *listeners; 28 60 char *event_type; 29 61 UT_hash_handle hh; 30 - int listener_count; 31 62 } EventType; 32 63 33 64 typedef struct emitter_reg { ··· 35 66 struct emitter_reg *next; 36 67 } emitter_reg_t; 37 68 38 - static EventType *global_events = NULL; 69 + static EventType *global_events = NULL; 39 70 static emitter_reg_t *emitter_registry = NULL; 40 71 41 - static inline void remove_listener_at(EventType *evt, int i) { 42 - int remaining = evt->listener_count - i - 1; 43 - if (remaining > 0) { 44 - memmove(&evt->listeners[i], &evt->listeners[i + 1], remaining * sizeof(EventListener)); 45 - } 46 - evt->listener_count--; 72 + static EventType *make_event_type(const char *event_type) { 73 + size_t etlen = strlen(event_type); 74 + EventType *evt = ant_calloc(sizeof(EventType) + etlen + 1); 75 + if (!evt) return NULL; 76 + evt->event_type = (char *)(evt + 1); 77 + memcpy(evt->event_type, event_type, etlen + 1); 78 + utarray_new(evt->listeners, &event_listener_icd); 79 + return evt; 47 80 } 48 81 49 82 static EventType **get_or_create_emitter_events(ant_t *js, ant_value_t this_obj) { 50 83 ant_value_t slot = js_get_slot(this_obj, SLOT_DATA); 51 - 52 84 if (vtype(slot) == T_UNDEF) { 53 85 EventType **events = ant_calloc(sizeof(EventType *)); 54 86 if (!events) return NULL; 55 87 *events = NULL; 56 - 88 + 57 89 emitter_reg_t *reg = ant_calloc(sizeof(emitter_reg_t)); 58 90 if (!reg) { free(events); return NULL; } 59 - 60 91 reg->events = events; 61 92 reg->next = emitter_registry; 62 93 emitter_registry = reg; 63 - 94 + 64 95 js_set_slot(this_obj, SLOT_DATA, ANT_PTR(events)); 65 96 return events; 66 97 } 67 - 68 98 return (EventType **)(uintptr_t)js_getnum(slot); 69 99 } 70 100 71 - static EventType *make_event_type(const char *event_type) { 72 - size_t etlen = strlen(event_type); 73 - EventType *evt = ant_calloc(sizeof(EventType) + etlen + 1); 74 - if (!evt) return NULL; 75 - 76 - evt->event_type = (char *)(evt + 1); 77 - memcpy(evt->event_type, event_type, etlen + 1); 78 - evt->listener_count = 0; 79 - 80 - return evt; 81 - } 82 - 83 101 static EventType *find_or_create_global_event_type(const char *event_type) { 84 102 EventType *evt = NULL; 85 103 HASH_FIND_STR(global_events, event_type, evt); 86 - 87 - if (evt == NULL) { 104 + if (!evt) { 88 105 evt = make_event_type(event_type); 89 106 if (!evt) return NULL; 90 107 HASH_ADD_KEYPTR(hh, global_events, evt->event_type, strlen(evt->event_type), evt); 91 108 } 92 - 93 109 return evt; 94 110 } 95 111 96 - static EventType *find_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { 97 - EventType **events = get_or_create_emitter_events(js, this_obj); 98 - if (events == NULL) return NULL; 99 - 112 + static EventType *find_global_event_type(const char *event_type) { 100 113 EventType *evt = NULL; 101 - HASH_FIND_STR(*events, event_type, evt); 102 - 114 + HASH_FIND_STR(global_events, event_type, evt); 103 115 return evt; 104 116 } 105 117 106 118 static EventType *find_or_create_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { 107 119 EventType **events = get_or_create_emitter_events(js, this_obj); 108 - if (events == NULL) return NULL; 109 - 120 + if (!events) return NULL; 110 121 EventType *evt = NULL; 111 122 HASH_FIND_STR(*events, event_type, evt); 112 - 113 - if (evt == NULL) { 123 + if (!evt) { 114 124 evt = make_event_type(event_type); 115 125 if (!evt) return NULL; 116 126 HASH_ADD_KEYPTR(hh, *events, evt->event_type, strlen(evt->event_type), evt); 117 127 } 118 - 119 128 return evt; 120 129 } 121 130 122 - static inline EventType *find_global_event_type(const char *event_type) { 131 + static EventType *find_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { 132 + EventType **events = get_or_create_emitter_events(js, this_obj); 133 + if (!events) return NULL; 123 134 EventType *evt = NULL; 124 - HASH_FIND_STR(global_events, event_type, evt); 135 + HASH_FIND_STR(*events, event_type, evt); 125 136 return evt; 126 137 } 127 138 128 - static ant_value_t add_event_listener_to(ant_t *js, ant_value_t *args, int nargs, EventType *evt) { 129 - if (evt == NULL) return js_mkerr(js, "failed to create event type"); 130 - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) 131 - return js_mkerr(js, "maximum number of listeners for event type '%s' reached", evt->event_type); 139 + static void js_init_event_obj(ant_t *js, ant_value_t obj, ant_value_t type_val, bool bubbles, bool cancelable) { 140 + js_set(js, obj, "type", type_val); 141 + js_set(js, obj, "target", js_mknull()); 142 + js_set(js, obj, "srcElement", js_mknull()); 143 + js_set(js, obj, "currentTarget", js_mknull()); 144 + js_set(js, obj, "eventPhase", js_mknum(0)); 145 + js_set(js, obj, "bubbles", js_bool(bubbles)); 146 + js_set(js, obj, "cancelable", js_bool(cancelable)); 147 + js_set(js, obj, "defaultPrevented", js_false); 148 + js_set(js, obj, "returnValue", js_true); 149 + js_set(js, obj, "cancelBubble", js_false); 150 + js_set(js, obj, "timeStamp", js_mknum(get_timestamp_ms())); 132 151 133 - bool once = false; 134 - if (nargs >= 3 && vtype(args[2]) != T_UNDEF) { 135 - ant_value_t once_val = js_get(js, args[2], "once"); 136 - if (vtype(once_val) != T_UNDEF) once = js_truthy(js, once_val); 152 + if (g_isTrusted_getter) 153 + js_set_accessor_desc(js, obj, "isTrusted", 9, g_isTrusted_getter, js_mkundef(), 0); 154 + 155 + event_data_t *data = ant_calloc(sizeof(event_data_t)); 156 + if (data) js_set_slot(obj, SLOT_DATA, ANT_PTR(data)); 157 + } 158 + 159 + static ant_value_t js_event_ctor(ant_t *js, ant_value_t *args, int nargs) { 160 + if (vtype(js->new_target) == T_UNDEF) 161 + return js_mkerr_typed(js, JS_ERR_TYPE, "Event constructor requires 'new'"); 162 + if (nargs < 1 || vtype(args[0]) == T_UNDEF) 163 + return js_mkerr_typed(js, JS_ERR_TYPE, "Event constructor: type argument is required"); 164 + 165 + ant_value_t type_val = args[0]; 166 + if (vtype(type_val) != T_STR) { 167 + type_val = js_tostring_val(js, type_val); 168 + if (is_err(type_val)) return type_val; 137 169 } 138 170 139 - EventListener *listener = &evt->listeners[evt->listener_count++]; 140 - listener->listener = args[1]; 141 - listener->once = once; 142 - 171 + bool bubbles = false, cancelable = false; 172 + if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 173 + ant_value_t b = js_get(js, args[1], "bubbles"); 174 + ant_value_t c = js_get(js, args[1], "cancelable"); 175 + if (is_err(b)) return b; 176 + if (is_err(c)) return c; 177 + bubbles = js_truthy(js, b); 178 + cancelable = js_truthy(js, c); 179 + } 180 + 181 + ant_value_t this_obj = js_mkobj(js); 182 + ant_value_t proto = js_instance_proto_from_new_target(js, g_event_proto); 183 + if (is_object_type(proto)) js_set_proto_init(this_obj, proto); 184 + 185 + js_init_event_obj(js, this_obj, type_val, bubbles, cancelable); 186 + return this_obj; 187 + } 188 + 189 + static ant_value_t js_event_get_isTrusted(ant_t *js, ant_value_t *args, int nargs) { 190 + return js_false; 191 + } 192 + 193 + static ant_value_t js_event_preventDefault(ant_t *js, ant_value_t *args, int nargs) { 194 + ant_value_t this_obj = js_getthis(js); 195 + ant_value_t cancelable = js_get(js, this_obj, "cancelable"); 196 + if (!js_truthy(js, cancelable)) return js_mkundef(); 197 + event_data_t *data = get_event_data(this_obj); 198 + if (data) data->canceled = true; 199 + js_set(js, this_obj, "defaultPrevented", js_true); 200 + js_set(js, this_obj, "returnValue", js_false); 143 201 return js_mkundef(); 144 202 } 145 203 146 - // addEventListener(eventType, listener, options) 147 - static ant_value_t js_add_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { 148 - if (nargs < 2) return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); 149 - char *event_type = js_getstr(js, args[0], NULL); 150 - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 151 - if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); 152 - return add_event_listener_to(js, args, nargs, find_or_create_emitter_event_type(js, js_getthis(js), event_type)); 204 + static ant_value_t js_event_stopPropagation(ant_t *js, ant_value_t *args, int nargs) { 205 + ant_value_t this_obj = js_getthis(js); 206 + event_data_t *data = get_event_data(this_obj); 207 + if (data) data->stop_propagation = true; 208 + js_set(js, this_obj, "cancelBubble", js_true); 209 + return js_mkundef(); 153 210 } 154 211 155 - // addEventListener(eventType, listener, options) 156 - static ant_value_t js_add_event_listener(ant_t *js, ant_value_t *args, int nargs) { 157 - if (nargs < 2) return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); 158 - char *event_type = js_getstr(js, args[0], NULL); 159 - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 160 - if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); 161 - return add_event_listener_to(js, args, nargs, find_or_create_global_event_type(event_type)); 212 + static ant_value_t js_event_stopImmediatePropagation(ant_t *js, ant_value_t *args, int nargs) { 213 + ant_value_t this_obj = js_getthis(js); 214 + event_data_t *data = get_event_data(this_obj); 215 + if (data) { data->stop_immediate = true; data->stop_propagation = true; } 216 + js_set(js, this_obj, "cancelBubble", js_true); 217 + return js_mkundef(); 162 218 } 163 219 164 - static ant_value_t remove_event_listener_from(ant_t *js, ant_value_t *args, int nargs, EventType *evt) { 165 - if (evt == NULL) goto done; 166 - 167 - for (int i = 0; i < evt->listener_count; i++) { 168 - if (evt->listeners[i].listener != args[1]) continue; 169 - remove_listener_at(evt, i); 170 - goto done; 220 + static ant_value_t js_event_composedPath(ant_t *js, ant_value_t *args, int nargs) { 221 + ant_value_t this_obj = js_getthis(js); 222 + event_data_t *data = get_event_data(this_obj); 223 + ant_value_t result = js_mkarr(js); 224 + if (data && data->dispatching) { 225 + ant_value_t ct = js_get(js, this_obj, "currentTarget"); 226 + if (is_object_type(ct)) js_arr_push(js, result, ct); 171 227 } 172 - 173 - done: return js_mkundef(); 228 + return result; 174 229 } 175 230 176 - // removeEventListener(eventType, listener) 177 - static ant_value_t js_remove_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { 178 - if (nargs < 2) return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); 179 - char *event_type = js_getstr(js, args[0], NULL); 180 - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 181 - return remove_event_listener_from(js, args, nargs, find_emitter_event_type(js, js_getthis(js), event_type)); 231 + static ant_value_t js_event_initEvent(ant_t *js, ant_value_t *args, int nargs) { 232 + ant_value_t this_obj = js_getthis(js); 233 + if (nargs >= 1) { 234 + ant_value_t type_val = vtype(args[0]) == T_STR ? args[0] : js_tostring_val(js, args[0]); 235 + if (!is_err(type_val)) js_set(js, this_obj, "type", type_val); 236 + } 237 + if (nargs >= 2) js_set(js, this_obj, "bubbles", js_bool(js_truthy(js, args[1]))); 238 + if (nargs >= 3) js_set(js, this_obj, "cancelable", js_bool(js_truthy(js, args[2]))); 239 + js_set(js, this_obj, "defaultPrevented", js_false); 240 + js_set(js, this_obj, "returnValue", js_true); 241 + event_data_t *data = get_event_data(this_obj); 242 + if (data) { data->canceled = false; data->stop_immediate = false; data->stop_propagation = false; } 243 + return js_mkundef(); 182 244 } 183 245 184 - // removeEventListener(eventType, listener) 185 - static ant_value_t js_remove_event_listener(ant_t *js, ant_value_t *args, int nargs) { 186 - if (nargs < 2) return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); 187 - char *event_type = js_getstr(js, args[0], NULL); 188 - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 189 - return remove_event_listener_from(js, args, nargs, find_global_event_type(event_type)); 246 + static ant_value_t js_customevent_ctor(ant_t *js, ant_value_t *args, int nargs) { 247 + if (vtype(js->new_target) == T_UNDEF) 248 + return js_mkerr_typed(js, JS_ERR_TYPE, "CustomEvent constructor requires 'new'"); 249 + if (nargs < 1 || vtype(args[0]) == T_UNDEF) 250 + return js_mkerr_typed(js, JS_ERR_TYPE, "CustomEvent constructor: type argument is required"); 251 + 252 + ant_value_t type_val = args[0]; 253 + if (vtype(type_val) != T_STR) { 254 + type_val = js_tostring_val(js, type_val); 255 + if (is_err(type_val)) return type_val; 256 + } 257 + 258 + bool bubbles = false, cancelable = false; 259 + ant_value_t detail = js_mknull(); 260 + if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 261 + ant_value_t b = js_get(js, args[1], "bubbles"); 262 + ant_value_t c = js_get(js, args[1], "cancelable"); 263 + ant_value_t d = js_get(js, args[1], "detail"); 264 + if (is_err(b)) return b; 265 + if (is_err(c)) return c; 266 + bubbles = js_truthy(js, b); 267 + cancelable = js_truthy(js, c); 268 + if (vtype(d) != T_UNDEF) detail = d; 269 + } 270 + 271 + ant_value_t this_obj = js_mkobj(js); 272 + ant_value_t proto = js_instance_proto_from_new_target(js, g_customevent_proto); 273 + if (is_object_type(proto)) js_set_proto_init(this_obj, proto); 274 + 275 + js_init_event_obj(js, this_obj, type_val, bubbles, cancelable); 276 + js_set(js, this_obj, "detail", detail); 277 + return this_obj; 190 278 } 191 279 192 - static ant_value_t dispatch_event_to(ant_t *js, ant_value_t *args, int nargs, EventType *evt, ant_value_t target) { 193 - if (evt == NULL || evt->listener_count == 0) return js_true; 280 + static ant_value_t js_errorevent_ctor(ant_t *js, ant_value_t *args, int nargs) { 281 + if (vtype(js->new_target) == T_UNDEF) 282 + return js_mkerr_typed(js, JS_ERR_TYPE, "ErrorEvent constructor requires 'new'"); 283 + if (nargs < 1 || vtype(args[0]) == T_UNDEF) 284 + return js_mkerr_typed(js, JS_ERR_TYPE, "ErrorEvent constructor: type argument is required"); 285 + 286 + ant_value_t type_val = args[0]; 287 + if (vtype(type_val) != T_STR) { 288 + type_val = js_tostring_val(js, type_val); 289 + if (is_err(type_val)) return type_val; 290 + } 194 291 195 - ant_value_t event_obj; 196 - const char *event_type; 292 + bool bubbles = false, cancelable = false; 293 + ant_value_t message = js_mkstr(js, "", 0); 294 + ant_value_t filename = js_mkstr(js, "", 0); 295 + ant_value_t lineno = js_mknum(0); 296 + ant_value_t colno = js_mknum(0); 297 + ant_value_t error = js_mknull(); 197 298 198 - if (vtype(args[0]) == T_OBJ) { 199 - ant_value_t type_val = js_get(js, args[0], "type"); 200 - event_type = js_getstr(js, type_val, NULL); 201 - if (event_type == NULL) return js_mkerr(js, "event.type must be a string"); 202 - event_obj = args[0]; 203 - } else { 204 - event_type = js_getstr(js, args[0], NULL); 205 - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 206 - event_obj = js_mkobj(js); 207 - js_set(js, event_obj, "type", args[0]); 208 - js_set_sym(js, event_obj, get_toStringTag_sym(), js_mkstr(js, "Event", 5)); 209 - if (nargs >= 2 && vtype(args[1]) != T_UNDEF) js_set(js, event_obj, "detail", args[1]); 299 + if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 300 + ant_value_t b = js_get(js, args[1], "bubbles"); 301 + ant_value_t c = js_get(js, args[1], "cancelable"); 302 + ant_value_t m = js_get(js, args[1], "message"); 303 + ant_value_t f = js_get(js, args[1], "filename"); 304 + ant_value_t l = js_get(js, args[1], "lineno"); 305 + ant_value_t co = js_get(js, args[1], "colno"); 306 + ant_value_t e = js_get(js, args[1], "error"); 307 + if (is_err(b)) return b; 308 + if (is_err(c)) return c; 309 + bubbles = js_truthy(js, b); 310 + cancelable = js_truthy(js, c); 311 + if (vtype(m) != T_UNDEF) { message = js_tostring_val(js, m); if (is_err(message)) return message; } 312 + if (vtype(f) != T_UNDEF) { filename = js_tostring_val(js, f); if (is_err(filename)) return filename; } 313 + if (vtype(l) != T_UNDEF) lineno = l; 314 + if (vtype(co) != T_UNDEF) colno = co; 315 + if (vtype(e) != T_UNDEF) error = e; 210 316 } 211 317 212 - if (is_object_type(target)) js_set(js, event_obj, "target", target); 213 - ant_value_t listener_args[1] = {event_obj}; 318 + ant_value_t this_obj = js_mkobj(js); 319 + ant_value_t proto = js_instance_proto_from_new_target(js, g_errorevent_proto); 320 + if (is_object_type(proto)) js_set_proto_init(this_obj, proto); 214 321 215 - for (int i = 0; i < evt->listener_count;) { 216 - EventListener *listener = &evt->listeners[i]; 217 - ant_value_t result = sv_vm_call(js->vm, js, listener->listener, js_mkundef(), listener_args, 1, NULL, false); 322 + js_init_event_obj(js, this_obj, type_val, bubbles, cancelable); 323 + js_set(js, this_obj, "message", message); 324 + js_set(js, this_obj, "filename", filename); 325 + js_set(js, this_obj, "lineno", lineno); 326 + js_set(js, this_obj, "colno", colno); 327 + js_set(js, this_obj, "error", error); 328 + return this_obj; 329 + } 218 330 219 - if (vtype(result) == T_ERR) 220 - fprintf(stderr, "Error in event listener for '%s': %s\n", event_type, js_str(js, result)); 331 + static ant_value_t js_promiserejectionevent_ctor(ant_t *js, ant_value_t *args, int nargs) { 332 + if (vtype(js->new_target) == T_UNDEF) 333 + return js_mkerr_typed(js, JS_ERR_TYPE, "PromiseRejectionEvent constructor requires 'new'"); 334 + if (nargs < 1 || vtype(args[0]) == T_UNDEF) 335 + return js_mkerr_typed(js, JS_ERR_TYPE, "PromiseRejectionEvent constructor: type argument is required"); 221 336 222 - if (listener->once) remove_listener_at(evt, i); 223 - else i++; 337 + ant_value_t type_val = args[0]; 338 + if (vtype(type_val) != T_STR) { 339 + type_val = js_tostring_val(js, type_val); 340 + if (is_err(type_val)) return type_val; 224 341 } 225 342 226 - return js_true; 343 + bool bubbles = false, cancelable = false; 344 + ant_value_t promise = js_mkundef(); 345 + ant_value_t reason = js_mkundef(); 346 + 347 + if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 348 + ant_value_t b = js_get(js, args[1], "bubbles"); 349 + ant_value_t c = js_get(js, args[1], "cancelable"); 350 + ant_value_t p = js_get(js, args[1], "promise"); 351 + ant_value_t r = js_get(js, args[1], "reason"); 352 + if (is_err(b)) return b; 353 + if (is_err(c)) return c; 354 + bubbles = js_truthy(js, b); 355 + cancelable = js_truthy(js, c); 356 + if (vtype(p) != T_UNDEF) promise = p; 357 + if (vtype(r) != T_UNDEF) reason = r; 358 + } 359 + 360 + ant_value_t this_obj = js_mkobj(js); 361 + ant_value_t proto = js_instance_proto_from_new_target(js, g_promiserejectionevent_proto); 362 + if (is_object_type(proto)) js_set_proto_init(this_obj, proto); 363 + 364 + js_init_event_obj(js, this_obj, type_val, bubbles, cancelable); 365 + js_set(js, this_obj, "promise", promise); 366 + js_set(js, this_obj, "reason", reason); 367 + return this_obj; 227 368 } 228 369 229 - // dispatchEvent(eventType, eventData) 230 - static ant_value_t js_dispatch_event_method(ant_t *js, ant_value_t *args, int nargs) { 231 - if (nargs < 1) return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); 370 + static bool parse_addEventListener_options(ant_t *js, ant_value_t *args, int nargs, bool *once, bool *capture, ant_value_t *signal) { 371 + *once = false; *capture = false; *signal = js_mkundef(); 372 + if (nargs < 3) return true; 373 + ant_value_t opts = args[2]; 374 + if (vtype(opts) == T_BOOL) { 375 + *capture = js_truthy(js, opts); 376 + return true; 377 + } 378 + if (vtype(opts) != T_OBJ) return true; 232 379 233 - ant_value_t this_obj = js_getthis(js); 234 - char *event_type = vtype(args[0]) == T_OBJ 235 - ? js_getstr(js, js_get(js, args[0], "type"), NULL) 236 - : js_getstr(js, args[0], NULL); 237 - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 380 + ant_value_t sig = js_get(js, opts, "signal"); 381 + if (vtype(sig) == T_NULL) return false; 382 + if (vtype(sig) != T_UNDEF) { 383 + if (!is_object_type(sig)) return false; 384 + *signal = sig; 385 + } 238 386 239 - return dispatch_event_to(js, args, nargs, find_emitter_event_type(js, this_obj, event_type), this_obj); 387 + ant_value_t o = js_get(js, opts, "once"); 388 + ant_value_t ca = js_get(js, opts, "capture"); 389 + if (vtype(o) != T_UNDEF) *once = js_truthy(js, o); 390 + if (vtype(ca) != T_UNDEF) *capture = js_truthy(js, ca); 391 + return true; 240 392 } 241 393 242 - // dispatchEvent(eventType, eventData) 243 - static ant_value_t js_dispatch_event(ant_t *js, ant_value_t *args, int nargs) { 244 - if (nargs < 1) return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); 394 + static ant_value_t add_listener_to(ant_t *js, ant_value_t *args, int nargs, EventType *evt) { 395 + if (!evt) return js_mkerr(js, "failed to create event type"); 245 396 246 - char *event_type = vtype(args[0]) == T_OBJ 247 - ? js_getstr(js, js_get(js, args[0], "type"), NULL) 248 - : js_getstr(js, args[0], NULL); 249 - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 397 + bool once, capture; 398 + ant_value_t signal; 399 + if (!parse_addEventListener_options(js, args, nargs, &once, &capture, &signal)) 400 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to execute 'addEventListener': signal is not an AbortSignal"); 250 401 251 - return dispatch_event_to(js, args, nargs, find_global_event_type(event_type), js_mkundef()); 402 + if (vtype(signal) != T_UNDEF && abort_signal_is_aborted(signal)) return js_mkundef(); 403 + 404 + ant_value_t cb = args[1]; 405 + uint8_t cbt = vtype(cb); 406 + if (cbt == T_NULL || cbt == T_UNDEF) return js_mkundef(); 407 + if (cbt != T_FUNC && cbt != T_CFUNC) return js_mkundef(); 408 + 409 + for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { 410 + EventListenerEntry *e = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); 411 + if (e->callback == cb && e->capture == capture) return js_mkundef(); 412 + } 413 + 414 + EventListenerEntry entry = { cb, signal, once, capture }; 415 + utarray_push_back(evt->listeners, &entry); 416 + return js_mkundef(); 252 417 } 253 418 254 - void js_dispatch_global_event(ant_t *js, ant_value_t event_obj) { 255 - ant_value_t type_val = js_get(js, event_obj, "type"); 256 - const char *event_type = js_getstr(js, type_val, NULL); 257 - if (!event_type) return; 419 + static ant_value_t remove_listener_from(ant_t *js, ant_value_t *args, int nargs, EventType *evt) { 420 + if (!evt) return js_mkundef(); 258 421 259 - EventType *evt = find_global_event_type(event_type); 260 - if (!evt || evt->listener_count == 0) return; 422 + bool capture = false; 423 + if (nargs >= 3) { 424 + ant_value_t opts = args[2]; 425 + if (vtype(opts) == T_BOOL) capture = js_truthy(js, opts); 426 + else if (vtype(opts) == T_OBJ) { 427 + ant_value_t ca = js_get(js, opts, "capture"); 428 + if (vtype(ca) != T_UNDEF) capture = js_truthy(js, ca); 429 + }} 261 430 262 - js_set(js, event_obj, "target", js_glob(js)); 263 - ant_value_t call_args[1] = { event_obj }; 431 + ant_value_t cb = (nargs >= 2) ? args[1] : js_mkundef(); 432 + uint8_t cbt = vtype(cb); 433 + if (cbt == T_NULL || cbt == T_UNDEF) return js_mkundef(); 264 434 265 - for (int i = 0; i < evt->listener_count;) { 266 - EventListener *listener = &evt->listeners[i]; 267 - sv_vm_call(js->vm, js, listener->listener, js_mkundef(), call_args, 1, NULL, false); 268 - if (listener->once) remove_listener_at(evt, i); 269 - else i++; 435 + for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { 436 + EventListenerEntry *e = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); 437 + if (e->callback == cb && e->capture == capture) { 438 + utarray_erase(evt->listeners, i, 1); 439 + return js_mkundef(); 440 + } 270 441 } 442 + return js_mkundef(); 271 443 } 272 444 273 - static ant_value_t js_get_event_listeners(ant_t *js, ant_value_t *args, int nargs) { 274 - EventType *evt, *tmp; 275 - ant_value_t result = js_mkobj(js); 276 - 277 - HASH_ITER(hh, global_events, evt, tmp) { 278 - ant_value_t listeners_array = js_mkobj(js); 279 - 280 - for (int j = 0; j < evt->listener_count; j++) { 281 - char key[16]; 282 - snprintf(key, sizeof(key), "%d", j); 283 - 284 - ant_value_t listener_info = js_mkobj(js); 285 - js_set(js, listener_info, "listener", evt->listeners[j].listener); 286 - js_set(js, listener_info, "once", js_bool(evt->listeners[j].once)); 287 - js_set(js, listeners_array, key, listener_info); 445 + static ant_value_t dispatch_event_to(ant_t *js, ant_value_t event_obj, EventType *evt, ant_value_t target) { 446 + if (!evt) return js_true; 447 + 448 + event_data_t *data = get_event_data(event_obj); 449 + if (data) { data->dispatching = true; data->stop_immediate = false; } 450 + js_set(js, event_obj, "target", target); 451 + js_set(js, event_obj, "currentTarget", target); 452 + js_set(js, event_obj, "eventPhase", js_mknum(2)); 453 + 454 + ant_value_t call_args[1] = { event_obj }; 455 + unsigned int n = utarray_len(evt->listeners); 456 + 457 + for (unsigned int i = 0; i < n;) { 458 + EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); 459 + 460 + if (vtype(entry->signal) != T_UNDEF && abort_signal_is_aborted(entry->signal)) { 461 + utarray_erase(evt->listeners, i, 1); 462 + n--; continue; 288 463 } 464 + 465 + ant_value_t cb = entry->callback; 466 + bool once = entry->once; 467 + if (once) { utarray_erase(evt->listeners, i, 1); n--; } else i++; 289 468 290 - js_set(js, listeners_array, "length", js_mknum(evt->listener_count)); 291 - js_set(js, result, evt->event_type, listeners_array); 469 + uint8_t t = vtype(cb); 470 + if (t != T_FUNC && t != T_CFUNC) continue; 471 + 472 + sv_vm_call(js->vm, js, cb, js_mkundef(), call_args, 1, NULL, false); 473 + if (data && data->stop_immediate) break; 292 474 } 293 - 294 - return result; 475 + 476 + if (data) data->dispatching = false; 477 + js_set(js, event_obj, "currentTarget", js_mknull()); 478 + js_set(js, event_obj, "eventPhase", js_mknum(0)); 479 + 480 + bool canceled = data && data->canceled; 481 + return js_bool(!canceled); 482 + } 483 + 484 + static ant_value_t js_add_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { 485 + if (nargs < 1) return js_mkundef(); 486 + const char *event_type = js_getstr(js, args[0], NULL); 487 + if (!event_type) return js_mkundef(); 488 + return add_listener_to(js, args, nargs, 489 + find_or_create_emitter_event_type(js, js_getthis(js), event_type)); 490 + } 491 + 492 + static ant_value_t js_add_event_listener(ant_t *js, ant_value_t *args, int nargs) { 493 + if (nargs < 1) return js_mkundef(); 494 + const char *event_type = js_getstr(js, args[0], NULL); 495 + if (!event_type) return js_mkundef(); 496 + return add_listener_to(js, args, nargs, find_or_create_global_event_type(event_type)); 497 + } 498 + 499 + static ant_value_t js_remove_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { 500 + if (nargs < 1) return js_mkundef(); 501 + const char *event_type = js_getstr(js, args[0], NULL); 502 + if (!event_type) return js_mkundef(); 503 + return remove_listener_from(js, args, nargs, 504 + find_emitter_event_type(js, js_getthis(js), event_type)); 505 + } 506 + 507 + static ant_value_t js_remove_event_listener(ant_t *js, ant_value_t *args, int nargs) { 508 + if (nargs < 1) return js_mkundef(); 509 + const char *event_type = js_getstr(js, args[0], NULL); 510 + if (!event_type) return js_mkundef(); 511 + return remove_listener_from(js, args, nargs, find_global_event_type(event_type)); 512 + } 513 + 514 + static ant_value_t js_dispatch_event_method(ant_t *js, ant_value_t *args, int nargs) { 515 + if (nargs < 1 || !is_object_type(args[0])) return js_false; 516 + ant_value_t this_obj = js_getthis(js); 517 + ant_value_t type_val = js_get(js, args[0], "type"); 518 + const char *event_type = js_getstr(js, type_val, NULL); 519 + if (!event_type) return js_false; 520 + return dispatch_event_to(js, args[0], 521 + find_emitter_event_type(js, this_obj, event_type), this_obj); 522 + } 523 + 524 + static ant_value_t js_dispatch_event(ant_t *js, ant_value_t *args, int nargs) { 525 + if (nargs < 1 || !is_object_type(args[0])) return js_false; 526 + ant_value_t type_val = js_get(js, args[0], "type"); 527 + const char *event_type = js_getstr(js, type_val, NULL); 528 + if (!event_type) return js_false; 529 + return dispatch_event_to(js, args[0], 530 + find_global_event_type(event_type), js_glob(js)); 531 + } 532 + 533 + void js_dispatch_global_event(ant_t *js, ant_value_t event_obj) { 534 + ant_value_t type_val = js_get(js, event_obj, "type"); 535 + const char *event_type = js_getstr(js, type_val, NULL); 536 + if (!event_type) return; 537 + EventType *evt = find_global_event_type(event_type); 538 + if (!evt || utarray_len(evt->listeners) == 0) return; 539 + dispatch_event_to(js, event_obj, evt, js_glob(js)); 295 540 } 296 541 297 542 static ant_value_t js_eventemitter_add(ant_t *js, ant_value_t *args, int nargs, bool once) { 298 543 if (nargs < 2) return js_mkerr(js, "requires 2 arguments (event, listener)"); 299 - char *event_type = js_getstr(js, args[0], NULL); 300 - if (event_type == NULL) return js_mkerr(js, "event must be a string"); 301 - if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); 544 + const char *event_type = js_getstr(js, args[0], NULL); 545 + if (!event_type) return js_mkerr(js, "event must be a string"); 546 + uint8_t t = vtype(args[1]); 547 + if (t != T_FUNC && t != T_CFUNC) return js_mkerr(js, "listener must be a function"); 302 548 303 549 ant_value_t this_obj = js_getthis(js); 304 550 EventType *evt = find_or_create_emitter_event_type(js, this_obj, event_type); 305 - if (evt == NULL) return js_mkerr(js, "failed to create event type"); 306 - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) 307 - return js_mkerr(js, "maximum number of listeners for event '%s' reached", event_type); 551 + if (!evt) return js_mkerr(js, "failed to create event type"); 308 552 309 - EventListener *listener = &evt->listeners[evt->listener_count++]; 310 - listener->listener = args[1]; 311 - listener->once = once; 553 + EventListenerEntry entry = { args[1], js_mkundef(), once, false }; 554 + utarray_push_back(evt->listeners, &entry); 312 555 return this_obj; 313 556 } 314 557 315 - // EventEmitter.prototype.on(event, listener) 316 558 static ant_value_t js_eventemitter_on(ant_t *js, ant_value_t *args, int nargs) { 317 559 return js_eventemitter_add(js, args, nargs, false); 318 560 } 319 561 320 - // EventEmitter.prototype.once(event, listener) 321 562 static ant_value_t js_eventemitter_once(ant_t *js, ant_value_t *args, int nargs) { 322 563 return js_eventemitter_add(js, args, nargs, true); 323 564 } 324 565 325 - // EventEmitter.prototype.off(event, listener) 326 566 static ant_value_t js_eventemitter_off(ant_t *js, ant_value_t *args, int nargs) { 327 567 if (nargs < 2) return js_mkerr(js, "off requires 2 arguments (event, listener)"); 328 - char *event_type = js_getstr(js, args[0], NULL); 329 - if (event_type == NULL) return js_mkerr(js, "event must be a string"); 568 + const char *event_type = js_getstr(js, args[0], NULL); 569 + if (!event_type) return js_mkerr(js, "event must be a string"); 330 570 ant_value_t this_obj = js_getthis(js); 331 - remove_event_listener_from(js, args, nargs, find_emitter_event_type(js, this_obj, event_type)); 571 + remove_listener_from(js, args, nargs, find_emitter_event_type(js, this_obj, event_type)); 332 572 return this_obj; 333 573 } 334 574 335 - // EventEmitter.prototype.emit(event, ...args) 336 575 static ant_value_t js_eventemitter_emit(ant_t *js, ant_value_t *args, int nargs) { 576 + if (nargs < 1) return js_mkerr(js, "emit requires at least 1 argument (event)"); 577 + const char *event_type = js_getstr(js, args[0], NULL); 578 + if (!event_type) return js_mkerr(js, "event must be a string"); 579 + 337 580 ant_value_t this_obj = js_getthis(js); 338 - 339 - if (nargs < 1) { 340 - return js_mkerr(js, "emit requires at least 1 argument (event)"); 341 - } 342 - 343 - char *event_type = js_getstr(js, args[0], NULL); 344 - if (event_type == NULL) { 345 - return js_mkerr(js, "event must be a string"); 346 - } 347 - 348 581 EventType *evt = find_emitter_event_type(js, this_obj, event_type); 349 - if (evt == NULL || evt->listener_count == 0) { 350 - return js_false; 351 - } 352 - 353 - int listener_nargs = nargs - 1; 354 - ant_value_t *listener_args = (listener_nargs > 0) ? &args[1] : NULL; 582 + if (!evt || utarray_len(evt->listeners) == 0) return js_false; 355 583 356 - for (int i = 0; i < evt->listener_count;) { 357 - EventListener *listener = &evt->listeners[i]; 358 - ant_value_t result = sv_vm_call(js->vm, js, listener->listener, js_mkundef(), listener_args, listener_nargs, NULL, false); 584 + int listener_nargs = nargs - 1; 585 + ant_value_t *listener_args = listener_nargs > 0 ? &args[1] : NULL; 359 586 587 + for (unsigned int i = 0; i < utarray_len(evt->listeners);) { 588 + EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); 589 + ant_value_t cb = entry->callback; 590 + bool once = entry->once; 591 + if (once) { utarray_erase(evt->listeners, i, 1); } else i++; 592 + uint8_t t = vtype(cb); 593 + if (t != T_FUNC && t != T_CFUNC) continue; 594 + ant_value_t result = sv_vm_call(js->vm, js, cb, js_mkundef(), listener_args, listener_nargs, NULL, false); 360 595 if (vtype(result) == T_ERR) 361 596 fprintf(stderr, "Error in event listener for '%s': %s\n", event_type, js_str(js, result)); 362 - 363 - if (listener->once) remove_listener_at(evt, i); 364 - else i++; 365 597 } 366 - 367 598 return js_true; 368 599 } 369 600 370 - // EventEmitter.prototype.removeAllListeners(event) 371 601 static ant_value_t js_eventemitter_removeAllListeners(ant_t *js, ant_value_t *args, int nargs) { 372 602 ant_value_t this_obj = js_getthis(js); 373 - 374 - if (nargs < 1) { 375 - return this_obj; 376 - } 377 - 378 - char *event_type = js_getstr(js, args[0], NULL); 379 - if (event_type == NULL) { 380 - return this_obj; 381 - } 382 - 603 + if (nargs < 1) return this_obj; 604 + const char *event_type = js_getstr(js, args[0], NULL); 605 + if (!event_type) return this_obj; 383 606 EventType *evt = find_emitter_event_type(js, this_obj, event_type); 384 - if (evt != NULL) { 385 - evt->listener_count = 0; 386 - } 387 - 607 + if (evt) utarray_clear(evt->listeners); 388 608 return this_obj; 389 609 } 390 610 391 - // EventEmitter.prototype.listenerCount(event) 392 611 static ant_value_t js_eventemitter_listenerCount(ant_t *js, ant_value_t *args, int nargs) { 393 - ant_value_t this_obj = js_getthis(js); 394 - 395 - if (nargs < 1) { 396 - return js_mknum(0); 397 - } 398 - 399 - char *event_type = js_getstr(js, args[0], NULL); 400 - if (event_type == NULL) { 401 - return js_mknum(0); 402 - } 403 - 404 - EventType *evt = find_emitter_event_type(js, this_obj, event_type); 405 - if (evt == NULL) { 406 - return js_mknum(0); 407 - } 408 - 409 - return js_mknum(evt->listener_count); 612 + if (nargs < 1) return js_mknum(0); 613 + const char *event_type = js_getstr(js, args[0], NULL); 614 + if (!event_type) return js_mknum(0); 615 + EventType *evt = find_emitter_event_type(js, js_getthis(js), event_type); 616 + if (!evt) return js_mknum(0); 617 + return js_mknum((double)utarray_len(evt->listeners)); 410 618 } 411 619 412 - // EventEmitter.prototype.eventNames() 413 620 static ant_value_t js_eventemitter_eventNames(ant_t *js, ant_value_t *args, int nargs) { 414 - (void)args; (void)nargs; 415 - 416 621 ant_value_t this_obj = js_getthis(js); 417 622 ant_value_t result = js_mkarr(js); 418 - 419 623 EventType **events = get_or_create_emitter_events(js, this_obj); 420 - 421 - if (events != NULL && *events != NULL) { 624 + if (events && *events) { 422 625 EventType *evt, *tmp; 423 626 HASH_ITER(hh, *events, evt, tmp) { 424 - if (evt->listener_count > 0) { 425 - ant_value_t name = js_mkstr(js, evt->event_type, strlen(evt->event_type)); 426 - js_arr_push(js, result, name); 427 - } 627 + if (utarray_len(evt->listeners) > 0) 628 + js_arr_push(js, result, js_mkstr(js, evt->event_type, strlen(evt->event_type))); 428 629 } 429 630 } 430 - 431 631 return result; 432 632 } 433 633 434 - // new CustomEvent(type, { detail: ... }) 435 - static ant_value_t js_customevent_ctor(ant_t *js, ant_value_t *args, int nargs) { 436 - if (nargs < 1) return js_mkerr(js, "CustomEvent requires a type argument"); 437 - 438 - char *event_type = js_getstr(js, args[0], NULL); 439 - if (event_type == NULL) return js_mkerr(js, "type must be a string"); 440 - 441 - ant_value_t event_obj = js_getthis(js); 442 - js_set(js, event_obj, "type", args[0]); 443 - js_set(js, event_obj, "detail", js_mknull()); 444 - 445 - if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 446 - ant_value_t detail = js_get(js, args[1], "detail"); 447 - if (vtype(detail) != T_UNDEF) js_set(js, event_obj, "detail", detail); 448 - } 449 - 450 - return js_mkundef(); 634 + static ant_value_t make_event_ctor(ant_t *js, ant_cfunc_t fn, ant_value_t proto, const char *name, size_t nlen) { 635 + ant_value_t ctor_obj = js_mkobj(js); 636 + js_set_slot(ctor_obj, SLOT_CFUNC, js_mkfun(fn)); 637 + 638 + js_mkprop_fast(js, ctor_obj, "prototype", 9, proto); 639 + js_mkprop_fast(js, ctor_obj, "name", 4, js_mkstr(js, name, nlen)); 640 + js_set_descriptor(js, ctor_obj, "name", 4, 0); 641 + 642 + ant_value_t fn_val = js_obj_to_func(ctor_obj); 643 + js_set(js, proto, "constructor", fn_val); 644 + js_set_descriptor(js, proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 645 + 646 + return fn_val; 451 647 } 452 648 453 649 ant_value_t events_library(ant_t *js) { 454 650 ant_value_t lib = js_mkobj(js); 455 - 456 - ant_value_t eventemitter_ctor = js_mkobj(js); 651 + ant_value_t eventemitter_ctor = js_mkobj(js); 457 652 ant_value_t eventemitter_proto = js_mkobj(js); 458 - 459 - js_set(js, eventemitter_proto, "on", js_mkfun(js_eventemitter_on)); 460 - js_set(js, eventemitter_proto, "addListener", js_mkfun(js_eventemitter_on)); 461 - js_set(js, eventemitter_proto, "once", js_mkfun(js_eventemitter_once)); 462 - js_set(js, eventemitter_proto, "off", js_mkfun(js_eventemitter_off)); 463 - js_set(js, eventemitter_proto, "removeListener", js_mkfun(js_eventemitter_off)); 464 - js_set(js, eventemitter_proto, "emit", js_mkfun(js_eventemitter_emit)); 653 + 654 + js_set(js, eventemitter_proto, "on", js_mkfun(js_eventemitter_on)); 655 + js_set(js, eventemitter_proto, "addListener", js_mkfun(js_eventemitter_on)); 656 + js_set(js, eventemitter_proto, "once", js_mkfun(js_eventemitter_once)); 657 + js_set(js, eventemitter_proto, "off", js_mkfun(js_eventemitter_off)); 658 + js_set(js, eventemitter_proto, "removeListener", js_mkfun(js_eventemitter_off)); 659 + js_set(js, eventemitter_proto, "emit", js_mkfun(js_eventemitter_emit)); 465 660 js_set(js, eventemitter_proto, "removeAllListeners", js_mkfun(js_eventemitter_removeAllListeners)); 466 - js_set(js, eventemitter_proto, "listenerCount", js_mkfun(js_eventemitter_listenerCount)); 467 - js_set(js, eventemitter_proto, "eventNames", js_mkfun(js_eventemitter_eventNames)); 661 + js_set(js, eventemitter_proto, "listenerCount", js_mkfun(js_eventemitter_listenerCount)); 662 + js_set(js, eventemitter_proto, "eventNames", js_mkfun(js_eventemitter_eventNames)); 468 663 js_set_sym(js, eventemitter_proto, get_toStringTag_sym(), js_mkstr(js, "EventEmitter", 12)); 469 - 664 + 470 665 js_mkprop_fast(js, eventemitter_ctor, "prototype", 9, eventemitter_proto); 471 666 js_mkprop_fast(js, eventemitter_ctor, "name", 4, ANT_STRING("EventEmitter")); 472 667 js_set_descriptor(js, eventemitter_ctor, "name", 4, 0); 473 - 668 + 474 669 ant_value_t ctor_fn = js_obj_to_func_ex(eventemitter_ctor, SV_CALL_IS_DEFAULT_CTOR); 475 670 js_set(js, lib, "EventEmitter", ctor_fn); 476 671 js_set(js, lib, "default", ctor_fn); ··· 479 674 return lib; 480 675 } 481 676 482 - void init_events_module() { 677 + void init_events_module(void) { 483 678 ant_t *js = rt->js; 484 679 ant_value_t global = js_glob(js); 485 - 486 - ant_value_t eventtarget_ctor = js_mkobj(js); 680 + g_isTrusted_getter = js_mkfun(js_event_get_isTrusted); 681 + 682 + g_event_proto = js_mkobj(js); 683 + js_set_sym(js, g_event_proto, get_toStringTag_sym(), js_mkstr(js, "Event", 5)); 684 + js_set(js, g_event_proto, "preventDefault", js_mkfun(js_event_preventDefault)); 685 + js_set(js, g_event_proto, "stopPropagation", js_mkfun(js_event_stopPropagation)); 686 + js_set(js, g_event_proto, "stopImmediatePropagation", js_mkfun(js_event_stopImmediatePropagation)); 687 + js_set(js, g_event_proto, "composedPath", js_mkfun(js_event_composedPath)); 688 + js_set(js, g_event_proto, "initEvent", js_mkfun(js_event_initEvent)); 689 + js_set(js, g_event_proto, "NONE", js_mknum(0)); 690 + js_set(js, g_event_proto, "CAPTURING_PHASE", js_mknum(1)); 691 + js_set(js, g_event_proto, "AT_TARGET", js_mknum(2)); 692 + js_set(js, g_event_proto, "BUBBLING_PHASE", js_mknum(3)); 693 + 694 + ant_value_t event_fn = make_event_ctor(js, js_event_ctor, g_event_proto, "Event", 5); 695 + js_set(js, event_fn, "NONE", js_mknum(0)); 696 + js_set(js, event_fn, "CAPTURING_PHASE", js_mknum(1)); 697 + js_set(js, event_fn, "AT_TARGET", js_mknum(2)); 698 + js_set(js, event_fn, "BUBBLING_PHASE", js_mknum(3)); 699 + js_set(js, global, "Event", event_fn); 700 + 701 + g_customevent_proto = js_mkobj(js); 702 + js_set_proto_init(g_customevent_proto, g_event_proto); 703 + js_set_sym(js, g_customevent_proto, get_toStringTag_sym(), js_mkstr(js, "CustomEvent", 11)); 704 + 705 + ant_value_t customevent_fn = make_event_ctor(js, js_customevent_ctor, g_customevent_proto, "CustomEvent", 11); 706 + js_set(js, global, "CustomEvent", customevent_fn); 707 + 708 + g_errorevent_proto = js_mkobj(js); 709 + js_set_proto_init(g_errorevent_proto, g_event_proto); 710 + js_set_sym(js, g_errorevent_proto, get_toStringTag_sym(), js_mkstr(js, "ErrorEvent", 10)); 711 + 712 + ant_value_t errorevent_fn = make_event_ctor(js, js_errorevent_ctor, g_errorevent_proto, "ErrorEvent", 10); 713 + js_set(js, global, "ErrorEvent", errorevent_fn); 714 + 715 + g_promiserejectionevent_proto = js_mkobj(js); 716 + js_set_proto_init(g_promiserejectionevent_proto, g_event_proto); 717 + js_set_sym(js, g_promiserejectionevent_proto, get_toStringTag_sym(), js_mkstr(js, "PromiseRejectionEvent", 21)); 718 + 719 + ant_value_t pre_fn = make_event_ctor(js, js_promiserejectionevent_ctor, g_promiserejectionevent_proto, "PromiseRejectionEvent", 21); 720 + js_set(js, global, "PromiseRejectionEvent", pre_fn); 721 + 487 722 ant_value_t eventtarget_proto = js_mkobj(js); 488 - ant_value_t customevent_ctor = js_mkobj(js); 489 - 490 - js_set(js, eventtarget_proto, "addEventListener", js_mkfun(js_add_event_listener_method)); 723 + js_set(js, eventtarget_proto, "addEventListener", js_mkfun(js_add_event_listener_method)); 491 724 js_set(js, eventtarget_proto, "removeEventListener", js_mkfun(js_remove_event_listener_method)); 492 - js_set(js, eventtarget_proto, "dispatchEvent", js_mkfun(js_dispatch_event_method)); 725 + js_set(js, eventtarget_proto, "dispatchEvent", js_mkfun(js_dispatch_event_method)); 493 726 js_set_sym(js, eventtarget_proto, get_toStringTag_sym(), js_mkstr(js, "EventTarget", 11)); 494 - 727 + 728 + ant_value_t eventtarget_ctor = js_mkobj(js); 495 729 js_mkprop_fast(js, eventtarget_ctor, "prototype", 9, eventtarget_proto); 496 730 js_mkprop_fast(js, eventtarget_ctor, "name", 4, ANT_STRING("EventTarget")); 497 731 js_set_descriptor(js, eventtarget_ctor, "name", 4, 0); 498 - 499 - ant_value_t customevent_proto = js_mkobj(js); 500 - js_set_sym(js, customevent_proto, get_toStringTag_sym(), js_mkstr(js, "CustomEvent", 11)); 732 + ant_value_t eventtarget_fn = js_obj_to_func_ex(eventtarget_ctor, SV_CALL_IS_DEFAULT_CTOR); 733 + js_set(js, eventtarget_proto, "constructor", eventtarget_fn); 734 + js_set_descriptor(js, eventtarget_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 501 735 502 - js_set_slot(customevent_ctor, SLOT_CFUNC, js_mkfun(js_customevent_ctor)); 503 - js_mkprop_fast(js, customevent_ctor, "prototype", 9, customevent_proto); 504 - js_mkprop_fast(js, customevent_ctor, "name", 4, ANT_STRING("CustomEvent")); 505 - js_set_descriptor(js, customevent_ctor, "name", 4, 0); 506 - 507 - ant_value_t customevent_fn = js_obj_to_func(customevent_ctor); 508 - js_set(js, customevent_proto, "constructor", customevent_fn); 509 - js_set_descriptor(js, customevent_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 510 - 511 - js_set(js, global, "addEventListener", js_mkfun(js_add_event_listener)); 736 + js_set(js, global, "addEventListener", js_mkfun(js_add_event_listener)); 512 737 js_set(js, global, "removeEventListener", js_mkfun(js_remove_event_listener)); 513 - js_set(js, global, "dispatchEvent", js_mkfun(js_dispatch_event)); 514 - js_set(js, global, "getEventListeners", js_mkfun(js_get_event_listeners)); 515 - 516 - js_set(js, global, "CustomEvent", customevent_fn); 517 - js_set(js, global, "EventTarget", js_obj_to_func_ex(eventtarget_ctor, SV_CALL_IS_DEFAULT_CTOR)); 738 + js_set(js, global, "dispatchEvent", js_mkfun(js_dispatch_event)); 739 + js_set(js, global, "EventTarget", eventtarget_fn); 518 740 } 519 741 520 - void gc_mark_events(ant_t *js, gc_mark_fn mark) { 742 + static void mark_event_type_listeners(ant_t *js, gc_mark_fn mark, EventType *events) { 521 743 EventType *evt, *tmp; 522 - HASH_ITER(hh, global_events, evt, tmp) { 523 - for (int i = 0; i < evt->listener_count; i++) mark(js, evt->listeners[i].listener); 744 + HASH_ITER(hh, events, evt, tmp) { 745 + for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { 746 + EventListenerEntry *e = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); 747 + mark(js, e->callback); 748 + if (vtype(e->signal) != T_UNDEF) mark(js, e->signal); 524 749 } 750 + }} 751 + 752 + void gc_mark_events(ant_t *js, gc_mark_fn mark) { 753 + mark_event_type_listeners(js, mark, global_events); 525 754 for (emitter_reg_t *reg = emitter_registry; reg; reg = reg->next) { 526 - HASH_ITER(hh, *reg->events, evt, tmp) 527 - for (int i = 0; i < evt->listener_count; i++) mark(js, evt->listeners[i].listener); 755 + if (*reg->events) mark_event_type_listeners(js, mark, *reg->events); 528 756 } 757 + if (g_isTrusted_getter) mark(js, g_isTrusted_getter); 758 + if (g_event_proto) mark(js, g_event_proto); 759 + if (g_customevent_proto) mark(js, g_customevent_proto); 760 + if (g_errorevent_proto) mark(js, g_errorevent_proto); 761 + if (g_promiserejectionevent_proto) mark(js, g_promiserejectionevent_proto); 529 762 } 530 -