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.

add observable module with subscription and observer functionalities - update events to use new data model

+714 -161
+124 -3
examples/spec/events.js
··· 6 6 const ee = new EventEmitter(); 7 7 8 8 let received = null; 9 - ee.on('test', (data) => { 9 + ee.on('test', data => { 10 10 received = data; 11 11 }); 12 12 ee.emit('test', 'hello'); ··· 19 19 test('multiple listeners', count, 2); 20 20 21 21 let onceValue = 0; 22 - ee.once('once', (val) => { 22 + ee.once('once', val => { 23 23 onceValue = val; 24 24 }); 25 25 ee.emit('once', 42); ··· 27 27 test('once only fires once', onceValue, 42); 28 28 29 29 let removed = false; 30 - const handler = () => { removed = true; }; 30 + const handler = () => { 31 + removed = true; 32 + }; 31 33 ee.on('remove', handler); 32 34 ee.off('remove', handler); 33 35 ee.emit('remove'); ··· 44 46 45 47 const names = ee.eventNames(); 46 48 test('eventNames includes count', names.includes('count'), true); 49 + 50 + const ee2 = new EventEmitter(); 51 + let args = []; 52 + ee2.on('multi', (a, b, c) => { 53 + args = [a, b, c]; 54 + }); 55 + ee2.emit('multi', 1, 2, 3); 56 + test('multiple emit args', args.join(','), '1,2,3'); 57 + 58 + const ee3 = new EventEmitter(); 59 + test('emit returns false with no listeners', ee3.emit('none'), false); 60 + ee3.on('exists', () => {}); 61 + test('emit returns true with listeners', ee3.emit('exists'), true); 62 + 63 + const ee4 = new EventEmitter(); 64 + const chain = ee4 65 + .on('a', () => {}) 66 + .once('b', () => {}) 67 + .off('b', () => {}); 68 + test('methods return this for chaining', chain, ee4); 69 + 70 + const ee5 = new EventEmitter(); 71 + let aliasCount = 0; 72 + ee5.addListener('alias', () => aliasCount++); 73 + ee5.emit('alias'); 74 + test('addListener alias works', aliasCount, 1); 75 + 76 + const ee6 = new EventEmitter(); 77 + let removeAliasVal = 0; 78 + const removeAliasHandler = () => { 79 + removeAliasVal = 1; 80 + }; 81 + ee6.on('rem', removeAliasHandler); 82 + ee6.removeListener('rem', removeAliasHandler); 83 + ee6.emit('rem'); 84 + test('removeListener alias works', removeAliasVal, 0); 85 + 86 + const ee7 = new EventEmitter(); 87 + let onceCount = 0; 88 + ee7.once('multi-once', () => onceCount++); 89 + ee7.once('multi-once', () => onceCount++); 90 + ee7.emit('multi-once'); 91 + test('multiple once listeners fire', onceCount, 2); 92 + ee7.emit('multi-once'); 93 + test('multiple once listeners only fire once', onceCount, 2); 94 + 95 + const ee8 = new EventEmitter(); 96 + test('listenerCount for non-existent event', ee8.listenerCount('nope'), 0); 97 + 98 + const ee9 = new EventEmitter(); 99 + const h = () => {}; 100 + ee9.on('temp', h); 101 + ee9.off('temp', h); 102 + test('eventNames excludes removed events', ee9.eventNames().includes('temp'), false); 103 + 104 + const eeA = new EventEmitter(); 105 + const eeB = new EventEmitter(); 106 + let aVal = 0, 107 + bVal = 0; 108 + eeA.on('x', () => aVal++); 109 + eeB.on('x', () => bVal++); 110 + eeA.emit('x'); 111 + test('separate instances are isolated (A)', aVal, 1); 112 + test('separate instances are isolated (B)', bVal, 0); 113 + 114 + console.log('\nEventTarget Tests\n'); 115 + 116 + const et = new EventTarget(); 117 + let etReceived = null; 118 + et.addEventListener('click', e => { 119 + etReceived = e.type; 120 + }); 121 + et.dispatchEvent('click'); 122 + test('EventTarget addEventListener and dispatchEvent', etReceived, 'click'); 123 + 124 + let etEvent = null; 125 + const et2 = new EventTarget(); 126 + et2.addEventListener('custom', e => { 127 + etEvent = e; 128 + }); 129 + et2.dispatchEvent('custom', { foo: 'bar' }); 130 + test('event.type', etEvent.type, 'custom'); 131 + test('event.target is EventTarget', etEvent.target, et2); 132 + test('event.detail', etEvent.detail.foo, 'bar'); 133 + 134 + const et3 = new EventTarget(); 135 + let et3Val = 0; 136 + const et3Handler = () => { 137 + et3Val++; 138 + }; 139 + et3.addEventListener('rem', et3Handler); 140 + et3.removeEventListener('rem', et3Handler); 141 + et3.dispatchEvent('rem'); 142 + test('EventTarget removeEventListener', et3Val, 0); 143 + 144 + const et4 = new EventTarget(); 145 + let et4Count = 0; 146 + et4.addEventListener('once', () => et4Count++, { once: true }); 147 + et4.dispatchEvent('once'); 148 + et4.dispatchEvent('once'); 149 + test('EventTarget once option', et4Count, 1); 150 + 151 + console.log('\nGlobal Event Tests\n'); 152 + 153 + let globalReceived = null; 154 + addEventListener('global-test', e => { 155 + globalReceived = e.type; 156 + }); 157 + dispatchEvent('global-test'); 158 + test('global addEventListener and dispatchEvent', globalReceived, 'global-test'); 159 + 160 + let globalRemoved = 0; 161 + const globalHandler = () => { 162 + globalRemoved++; 163 + }; 164 + addEventListener('global-rem', globalHandler); 165 + removeEventListener('global-rem', globalHandler); 166 + dispatchEvent('global-rem'); 167 + test('global removeEventListener', globalRemoved, 0); 47 168 48 169 summary();
+3
include/config.h
··· 57 57 SLOT_SUPER, 58 58 SLOT_DEFAULT_CTOR, 59 59 SLOT_ERR_TYPE, 60 + SLOT_OBSERVABLE_SUBSCRIBER, 61 + SLOT_SUBSCRIPTION_OBSERVER, 62 + SLOT_SUBSCRIPTION_CLEANUP, 60 63 SLOT_MAX = 255 61 64 } internal_slot_t; 62 65
+3
include/config.h.in
··· 46 46 SLOT_SUPER, 47 47 SLOT_DEFAULT_CTOR, 48 48 SLOT_ERR_TYPE, 49 + SLOT_OBSERVABLE_SUBSCRIBER, 50 + SLOT_SUBSCRIPTION_OBSERVER, 51 + SLOT_SUBSCRIPTION_CLEANUP, 49 52 SLOT_MAX = 255 50 53 } internal_slot_t; 51 54
+6
include/modules/observable.h
··· 1 + #ifndef OBSERVABLE_H 2 + #define OBSERVABLE_H 3 + 4 + void init_observable_module(void); 5 + 6 + #endif
+3
include/modules/symbol.h
··· 4 4 #include "ant.h" 5 5 6 6 void init_symbol_module(void); 7 + 7 8 jsval_t get_iterator_symbol(void); 8 9 jsval_t get_asyncIterator_symbol(void); 10 + jsval_t get_observable_symbol(void); 9 11 10 12 const char *get_iterator_sym_key(void); 11 13 const char *get_asyncIterator_sym_key(void); 12 14 const char *get_toStringTag_sym_key(void); 15 + const char *get_observable_sym_key(void); 13 16 const char *get_symbol_description_from_key(const char *sym_key, size_t key_len); 14 17 15 18 #endif
+2
src/main.c
··· 41 41 #include "modules/navigator.h" 42 42 #include "modules/child_process.h" 43 43 #include "modules/readline.h" 44 + #include "modules/observable.h" 44 45 45 46 int js_result = EXIT_SUCCESS; 46 47 ··· 233 234 init_sessionstorage_module(); 234 235 init_localstorage_module(); 235 236 init_navigator_module(); 237 + init_observable_module(); 236 238 237 239 ant_register_library(shell_library, "ant:shell", NULL); 238 240 ant_register_library(ffi_library, "ant:ffi", NULL);
+60 -158
src/modules/events.c
··· 16 16 typedef struct { 17 17 jsval_t listener; 18 18 bool once; 19 - } EventListener; 19 + } __attribute__((packed)) EventListener; 20 20 21 21 typedef struct { 22 + EventListener listeners[MAX_LISTENERS_PER_EVENT]; 22 23 char *event_type; 23 - EventListener listeners[MAX_LISTENERS_PER_EVENT]; 24 + UT_hash_handle hh; 24 25 int listener_count; 25 - UT_hash_handle hh; 26 26 } EventType; 27 27 28 - typedef struct { 29 - uint64_t target_id; 30 - EventType *events; 31 - UT_hash_handle hh; 32 - } TargetEvents; 33 - 34 - static TargetEvents *target_events_map = NULL; 35 - static uint64_t next_emitter_id = 1; 28 + static EventType *global_events = NULL; 36 29 37 - static TargetEvents *get_or_create_target_events(jsval_t target) { 38 - uint64_t target_id = target; 39 - TargetEvents *te = NULL; 40 - 41 - HASH_FIND(hh, target_events_map, &target_id, sizeof(uint64_t), te); 42 - 43 - if (te == NULL) { 44 - te = ANT_GC_MALLOC(sizeof(TargetEvents)); 45 - te->target_id = target_id; 46 - te->events = NULL; 47 - HASH_ADD(hh, target_events_map, target_id, sizeof(uint64_t), te); 30 + static inline void remove_listener_at(EventType *evt, int i) { 31 + int remaining = evt->listener_count - i - 1; 32 + if (remaining > 0) { 33 + memmove(&evt->listeners[i], &evt->listeners[i + 1], remaining * sizeof(EventListener)); 48 34 } 49 - 50 - return te; 35 + evt->listener_count--; 51 36 } 52 37 53 - static TargetEvents *get_or_create_emitter_events(struct js *js, jsval_t this_obj) { 54 - jsval_t id_val = js_get(js, this_obj, "_emitter_id"); 55 - uint64_t emitter_id; 56 - TargetEvents *te = NULL; 38 + static EventType **get_or_create_emitter_events(struct js *js, jsval_t this_obj) { 39 + jsval_t slot = js_get_slot(js, this_obj, SLOT_DATA); 57 40 58 - if (js_type(id_val) != JS_NUM) { 59 - emitter_id = next_emitter_id++; 60 - js_set(js, this_obj, "_emitter_id", js_mknum((double)emitter_id)); 61 - 62 - te = ANT_GC_MALLOC(sizeof(TargetEvents)); 63 - te->target_id = emitter_id; 64 - te->events = NULL; 65 - HASH_ADD(hh, target_events_map, target_id, sizeof(uint64_t), te); 66 - return te; 41 + if (js_type(slot) == JS_UNDEF) { 42 + EventType **events = ANT_GC_MALLOC(sizeof(EventType *)); 43 + *events = NULL; 44 + js_set_slot(js, this_obj, SLOT_DATA, ANT_PTR(events)); 45 + return events; 67 46 } 68 47 69 - emitter_id = (uint64_t)js_getnum(id_val); 70 - HASH_FIND(hh, target_events_map, &emitter_id, sizeof(uint64_t), te); 71 - return te; 48 + return (EventType **)(uintptr_t)js_getnum(slot); 72 49 } 73 50 74 51 static EventType *find_emitter_event_type(struct js *js, jsval_t this_obj, const char *event_type) { 75 - TargetEvents *te = get_or_create_emitter_events(js, this_obj); 76 - if (te == NULL) return NULL; 52 + EventType **events = get_or_create_emitter_events(js, this_obj); 53 + if (events == NULL) return NULL; 77 54 78 55 EventType *evt = NULL; 79 - HASH_FIND_STR(te->events, event_type, evt); 56 + HASH_FIND_STR(*events, event_type, evt); 80 57 return evt; 81 58 } 82 59 83 60 static EventType *find_or_create_emitter_event_type(struct js *js, jsval_t this_obj, const char *event_type) { 84 - TargetEvents *te = get_or_create_emitter_events(js, this_obj); 85 - if (te == NULL) return NULL; 61 + EventType **events = get_or_create_emitter_events(js, this_obj); 62 + if (events == NULL) return NULL; 86 63 87 64 EventType *evt = NULL; 88 - HASH_FIND_STR(te->events, event_type, evt); 65 + HASH_FIND_STR(*events, event_type, evt); 89 66 90 67 if (evt == NULL) { 91 68 size_t etlen = strlen(event_type); ··· 93 70 evt->event_type = (char *)(evt + 1); 94 71 memcpy(evt->event_type, event_type, etlen + 1); 95 72 evt->listener_count = 0; 96 - HASH_ADD_KEYPTR(hh, te->events, evt->event_type, etlen, evt); 73 + HASH_ADD_KEYPTR(hh, *events, evt->event_type, etlen, evt); 97 74 } 98 75 99 76 return evt; 100 77 } 101 78 102 - static EventType *find_or_create_event_type(jsval_t target, const char *event_type) { 103 - TargetEvents *te = get_or_create_target_events(target); 79 + static EventType *find_or_create_global_event_type(const char *event_type) { 104 80 EventType *evt = NULL; 105 - 106 - HASH_FIND_STR(te->events, event_type, evt); 81 + HASH_FIND_STR(global_events, event_type, evt); 107 82 108 83 if (evt == NULL) { 109 84 size_t etlen = strlen(event_type); ··· 111 86 evt->event_type = (char *)(evt + 1); 112 87 memcpy(evt->event_type, event_type, etlen + 1); 113 88 evt->listener_count = 0; 114 - HASH_ADD_KEYPTR(hh, te->events, evt->event_type, etlen, evt); 89 + HASH_ADD_KEYPTR(hh, global_events, evt->event_type, etlen, evt); 115 90 } 116 91 117 92 return evt; 118 93 } 119 94 120 - static EventType *find_event_type(jsval_t target, const char *event_type) { 121 - uint64_t target_id = target; 122 - TargetEvents *te = NULL; 123 - 124 - HASH_FIND(hh, target_events_map, &target_id, sizeof(uint64_t), te); 125 - 126 - if (te == NULL) { 127 - return NULL; 128 - } 129 - 95 + static inline EventType *find_global_event_type(const char *event_type) { 130 96 EventType *evt = NULL; 131 - HASH_FIND_STR(te->events, event_type, evt); 132 - 97 + HASH_FIND_STR(global_events, event_type, evt); 133 98 return evt; 134 99 } 135 100 ··· 142 107 } 143 108 144 109 char *event_type = js_getstr(js, args[0], NULL); 145 - if (event_type == NULL) { 146 - return js_mkerr(js, "eventType must be a string"); 147 - } 148 - 149 - if (js_type(args[1]) != JS_FUNC) { 150 - return js_mkerr(js, "listener must be a function"); 151 - } 110 + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 111 + if (js_type(args[1]) != JS_FUNC) return js_mkerr(js, "listener must be a function"); 152 112 153 - EventType *evt = find_or_create_event_type(this_obj, event_type); 154 - if (evt == NULL) { 155 - return js_mkerr(js, "failed to create event type"); 156 - } 113 + EventType *evt = find_or_create_emitter_event_type(js, this_obj, event_type); 114 + if (evt == NULL) return js_mkerr(js, "failed to create event type"); 157 115 158 116 if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { 159 117 return js_mkerr(js, "maximum number of listeners for event type '%s' reached", event_type); ··· 174 132 175 133 // addEventListener(eventType, listener, options) 176 134 static jsval_t js_add_event_listener(struct js *js, jsval_t *args, int nargs) { 177 - jsval_t global = js_glob(js); 178 - 179 135 if (nargs < 2) { 180 136 return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); 181 137 } ··· 189 145 return js_mkerr(js, "listener must be a function"); 190 146 } 191 147 192 - EventType *evt = find_or_create_event_type(global, event_type); 148 + EventType *evt = find_or_create_global_event_type(event_type); 193 149 if (evt == NULL) { 194 150 return js_mkerr(js, "failed to create event type"); 195 151 } ··· 220 176 } 221 177 222 178 char *event_type = js_getstr(js, args[0], NULL); 223 - if (event_type == NULL) { 224 - return js_mkerr(js, "eventType must be a string"); 225 - } 179 + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 226 180 227 - EventType *evt = find_event_type(this_obj, event_type); 228 - if (evt == NULL) { 229 - return js_mkundef(); 230 - } 181 + EventType *evt = find_emitter_event_type(js, this_obj, event_type); 182 + if (evt == NULL) return js_mkundef(); 231 183 232 184 for (int i = 0; i < evt->listener_count; i++) { 233 185 if (evt->listeners[i].listener == args[1]) { 234 - for (int j = i; j < evt->listener_count - 1; j++) { 235 - evt->listeners[j] = evt->listeners[j + 1]; 236 - } 237 - evt->listener_count--; 238 - break; 186 + remove_listener_at(evt, i); break; 239 187 } 240 188 } 241 189 ··· 244 192 245 193 // removeEventListener(eventType, listener) 246 194 static jsval_t js_remove_event_listener(struct js *js, jsval_t *args, int nargs) { 247 - jsval_t global = js_glob(js); 248 - 249 195 if (nargs < 2) { 250 196 return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); 251 197 } 252 198 253 199 char *event_type = js_getstr(js, args[0], NULL); 254 - if (event_type == NULL) { 255 - return js_mkerr(js, "eventType must be a string"); 256 - } 200 + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 257 201 258 - EventType *evt = find_event_type(global, event_type); 259 - if (evt == NULL) { 260 - return js_mkundef(); 261 - } 202 + EventType *evt = find_global_event_type(event_type); 203 + if (evt == NULL) return js_mkundef(); 262 204 263 205 for (int i = 0; i < evt->listener_count; i++) { 264 206 if (evt->listeners[i].listener == args[1]) { 265 - for (int j = i; j < evt->listener_count - 1; j++) { 266 - evt->listeners[j] = evt->listeners[j + 1]; 267 - } 268 - evt->listener_count--; 269 - break; 207 + remove_listener_at(evt, i); break; 270 208 } 271 209 } 272 210 ··· 282 220 } 283 221 284 222 char *event_type = js_getstr(js, args[0], NULL); 285 - if (event_type == NULL) { 286 - return js_mkerr(js, "eventType must be a string"); 287 - } 223 + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 288 224 289 - EventType *evt = find_event_type(this_obj, event_type); 290 - if (evt == NULL || evt->listener_count == 0) { 291 - return js_mktrue(); 292 - } 225 + EventType *evt = find_emitter_event_type(js, this_obj, event_type); 226 + if (evt == NULL || evt->listener_count == 0) return js_mktrue(); 293 227 294 228 jsval_t event_obj = js_mkobj(js); 295 229 js_set(js, event_obj, "type", args[0]); ··· 311 245 } 312 246 313 247 if (listener->once) { 314 - for (int j = i; j < evt->listener_count - 1; j++) { 315 - evt->listeners[j] = evt->listeners[j + 1]; 316 - } 317 - evt->listener_count--; 318 - } else { 319 - i++; 320 - } 248 + remove_listener_at(evt, i); 249 + } else i++; 321 250 } 322 251 323 252 return js_mktrue(); ··· 325 254 326 255 // dispatchEvent(eventType, eventData) 327 256 static jsval_t js_dispatch_event(struct js *js, jsval_t *args, int nargs) { 328 - jsval_t global = js_glob(js); 329 - 330 257 if (nargs < 1) { 331 258 return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); 332 259 } 333 260 334 261 char *event_type = js_getstr(js, args[0], NULL); 335 - if (event_type == NULL) { 336 - return js_mkerr(js, "eventType must be a string"); 337 - } 262 + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 338 263 339 - EventType *evt = find_event_type(global, event_type); 264 + EventType *evt = find_global_event_type(event_type); 340 265 if (evt == NULL || evt->listener_count == 0) { 341 266 return js_mktrue(); 342 267 } ··· 360 285 } 361 286 362 287 if (listener->once) { 363 - for (int j = i; j < evt->listener_count - 1; j++) { 364 - evt->listeners[j] = evt->listeners[j + 1]; 365 - } 366 - evt->listener_count--; 367 - } else { 368 - i++; 369 - } 288 + remove_listener_at(evt, i); 289 + } else i++; 370 290 } 371 291 372 292 return js_mktrue(); 373 293 } 374 294 375 295 static jsval_t js_get_event_listeners(struct js *js, jsval_t *args, int nargs) { 376 - jsval_t target = (nargs > 0) ? args[0] : js_glob(js); 296 + (void)args; (void)nargs; 377 297 378 298 EventType *evt, *tmp; 379 299 jsval_t result = js_mkobj(js); 380 300 381 - uint64_t target_id = target; 382 - TargetEvents *te = NULL; 383 - 384 - HASH_FIND(hh, target_events_map, &target_id, sizeof(uint64_t), te); 385 - 386 - if (te == NULL) { 387 - return result; 388 - } 389 - 390 - HASH_ITER(hh, te->events, evt, tmp) { 301 + HASH_ITER(hh, global_events, evt, tmp) { 391 302 jsval_t listeners_array = js_mkobj(js); 392 303 393 304 for (int j = 0; j < evt->listener_count; j++) { ··· 494 405 495 406 for (int i = 0; i < evt->listener_count; i++) { 496 407 if (evt->listeners[i].listener == args[1]) { 497 - for (int j = i; j < evt->listener_count - 1; j++) { 498 - evt->listeners[j] = evt->listeners[j + 1]; 499 - } 500 - evt->listener_count--; 501 - break; 408 + remove_listener_at(evt, i); break; 502 409 } 503 410 } 504 411 ··· 536 443 } 537 444 538 445 if (listener->once) { 539 - for (int j = i; j < evt->listener_count - 1; j++) { 540 - evt->listeners[j] = evt->listeners[j + 1]; 541 - } 542 - evt->listener_count--; 543 - } else { 544 - i++; 545 - } 446 + remove_listener_at(evt, i); 447 + } else i++; 546 448 } 547 449 548 450 return js_mktrue(); ··· 593 495 // EventEmitter.prototype.eventNames() 594 496 static jsval_t js_eventemitter_eventNames(struct js *js, jsval_t *args, int nargs) { 595 497 (void)args; (void)nargs; 596 - jsval_t this_obj = js_getthis(js); 597 498 499 + jsval_t this_obj = js_getthis(js); 598 500 jsval_t result = js_mkarr(js); 599 501 600 - TargetEvents *te = get_or_create_emitter_events(js, this_obj); 502 + EventType **events = get_or_create_emitter_events(js, this_obj); 601 503 602 - if (te != NULL && te->events != NULL) { 504 + if (events != NULL && *events != NULL) { 603 505 EventType *evt, *tmp; 604 - HASH_ITER(hh, te->events, evt, tmp) { 506 + HASH_ITER(hh, *events, evt, tmp) { 605 507 if (evt->listener_count > 0) { 606 508 jsval_t name = js_mkstr(js, evt->event_type, strlen(evt->event_type)); 607 509 js_arr_push(js, result, name);
+506
src/modules/observable.c
··· 1 + #include <stdlib.h> 2 + #include <stdio.h> 3 + #include <string.h> 4 + 5 + #include "ant.h" 6 + #include "runtime.h" 7 + 8 + #include "modules/symbol.h" 9 + #include "modules/observable.h" 10 + 11 + static bool subscription_closed(struct js *js, jsval_t subscription) { 12 + jsval_t observer = js_get_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER); 13 + return js_type(observer) == JS_UNDEF; 14 + } 15 + 16 + static void cleanup_subscription(struct js *js, jsval_t subscription) { 17 + jsval_t cleanup = js_get_slot(js, subscription, SLOT_SUBSCRIPTION_CLEANUP); 18 + if (js_type(cleanup) == JS_UNDEF) return; 19 + if (js_type(cleanup) != JS_FUNC) return; 20 + 21 + js_set_slot(js, subscription, SLOT_SUBSCRIPTION_CLEANUP, js_mkundef()); 22 + jsval_t result = js_call(js, cleanup, NULL, 0); 23 + 24 + if (js_type(result) == JS_ERR) { 25 + fprintf(stderr, "Error in subscription cleanup: %s\n", js_str(js, result)); 26 + } 27 + } 28 + 29 + static jsval_t create_subscription(struct js *js, jsval_t observer) { 30 + jsval_t subscription = js_mkobj(js); 31 + js_set_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER, observer); 32 + js_set_slot(js, subscription, SLOT_SUBSCRIPTION_CLEANUP, js_mkundef()); 33 + js_set(js, subscription, get_toStringTag_sym_key(), js_mkstr(js, "Subscription", 12)); 34 + return subscription; 35 + } 36 + 37 + static jsval_t js_subscription_get_closed(struct js *js, jsval_t *args, int nargs) { 38 + (void)args; (void)nargs; 39 + jsval_t subscription = js_getthis(js); 40 + 41 + if (js_type(subscription) != JS_OBJ) { 42 + return js_mkerr_typed(js, JS_ERR_TYPE, "Subscription.closed getter called on non-object"); 43 + } 44 + 45 + return subscription_closed(js, subscription) ? js_mktrue() : js_mkfalse(); 46 + } 47 + 48 + static jsval_t js_subscription_unsubscribe(struct js *js, jsval_t *args, int nargs) { 49 + (void)args; (void)nargs; 50 + jsval_t subscription = js_getthis(js); 51 + 52 + if (js_type(subscription) != JS_OBJ) { 53 + return js_mkerr_typed(js, JS_ERR_TYPE, "Subscription.unsubscribe called on non-object"); 54 + } 55 + 56 + if (subscription_closed(js, subscription)) return js_mkundef(); 57 + 58 + js_set_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); 59 + cleanup_subscription(js, subscription); 60 + 61 + return js_mkundef(); 62 + } 63 + 64 + static void setup_subscription_methods(struct js *js, jsval_t subscription) { 65 + js_set(js, subscription, "unsubscribe", js_mkfun(js_subscription_unsubscribe)); 66 + jsval_t closed_getter = js_mkfun(js_subscription_get_closed); 67 + js_set_getter_desc(js, subscription, "closed", 6, closed_getter, JS_DESC_E | JS_DESC_C); 68 + } 69 + 70 + static jsval_t js_subobs_get_closed(struct js *js, jsval_t *args, int nargs) { 71 + (void)args; (void)nargs; 72 + jsval_t O = js_getthis(js); 73 + 74 + if (js_type(O) != JS_OBJ) { 75 + return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.closed getter called on non-object"); 76 + } 77 + 78 + jsval_t subscription = js_get_slot(js, O, SLOT_DATA); 79 + if (js_type(subscription) != JS_OBJ) { 80 + return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); 81 + } 82 + 83 + return subscription_closed(js, subscription) ? js_mktrue() : js_mkfalse(); 84 + } 85 + 86 + static jsval_t js_subobs_next(struct js *js, jsval_t *args, int nargs) { 87 + jsval_t O = js_getthis(js); 88 + 89 + if (js_type(O) != JS_OBJ) { 90 + return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.next called on non-object"); 91 + } 92 + 93 + jsval_t subscription = js_get_slot(js, O, SLOT_DATA); 94 + if (js_type(subscription) != JS_OBJ) { 95 + return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); 96 + } 97 + 98 + if (subscription_closed(js, subscription)) return js_mkundef(); 99 + 100 + jsval_t observer = js_get_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER); 101 + if (js_type(observer) != JS_OBJ) return js_mkundef(); 102 + 103 + jsval_t nextMethod = js_get(js, observer, "next"); 104 + if (js_type(nextMethod) == JS_FUNC) { 105 + jsval_t value = (nargs > 0) ? args[0] : js_mkundef(); 106 + jsval_t call_args[1] = {value}; 107 + jsval_t result = js_call_with_this(js, nextMethod, observer, call_args, 1); 108 + if (js_type(result) == JS_ERR) { 109 + fprintf(stderr, "Error in observer.next: %s\n", js_str(js, result)); 110 + } 111 + } 112 + 113 + return js_mkundef(); 114 + } 115 + 116 + static jsval_t js_subobs_error(struct js *js, jsval_t *args, int nargs) { 117 + jsval_t O = js_getthis(js); 118 + 119 + if (js_type(O) != JS_OBJ) { 120 + return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.error called on non-object"); 121 + } 122 + 123 + jsval_t subscription = js_get_slot(js, O, SLOT_DATA); 124 + if (js_type(subscription) != JS_OBJ) { 125 + return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); 126 + } 127 + 128 + if (subscription_closed(js, subscription)) return js_mkundef(); 129 + 130 + jsval_t observer = js_get_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER); 131 + js_set_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); 132 + 133 + if (js_type(observer) == JS_OBJ) { 134 + jsval_t errorMethod = js_get(js, observer, "error"); 135 + if (js_type(errorMethod) == JS_FUNC) { 136 + jsval_t exception = (nargs > 0) ? args[0] : js_mkundef(); 137 + jsval_t call_args[1] = {exception}; 138 + jsval_t result = js_call_with_this(js, errorMethod, observer, call_args, 1); 139 + if (js_type(result) == JS_ERR) { 140 + fprintf(stderr, "Error in observer.error: %s\n", js_str(js, result)); 141 + } 142 + } 143 + } 144 + 145 + cleanup_subscription(js, subscription); 146 + return js_mkundef(); 147 + } 148 + 149 + static jsval_t js_subobs_complete(struct js *js, jsval_t *args, int nargs) { 150 + (void)args; (void)nargs; 151 + jsval_t O = js_getthis(js); 152 + 153 + if (js_type(O) != JS_OBJ) { 154 + return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.complete called on non-object"); 155 + } 156 + 157 + jsval_t subscription = js_get_slot(js, O, SLOT_DATA); 158 + if (js_type(subscription) != JS_OBJ) { 159 + return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); 160 + } 161 + 162 + if (subscription_closed(js, subscription)) return js_mkundef(); 163 + 164 + jsval_t observer = js_get_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER); 165 + js_set_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); 166 + 167 + if (js_type(observer) == JS_OBJ) { 168 + jsval_t completeMethod = js_get(js, observer, "complete"); 169 + if (js_type(completeMethod) == JS_FUNC) { 170 + jsval_t result = js_call_with_this(js, completeMethod, observer, NULL, 0); 171 + if (js_type(result) == JS_ERR) { 172 + fprintf(stderr, "Error in observer.complete: %s\n", js_str(js, result)); 173 + } 174 + } 175 + } 176 + 177 + cleanup_subscription(js, subscription); 178 + return js_mkundef(); 179 + } 180 + 181 + static jsval_t create_subscription_observer(struct js *js, jsval_t subscription) { 182 + jsval_t subobs = js_mkobj(js); 183 + js_set_slot(js, subobs, SLOT_DATA, subscription); 184 + js_set(js, subobs, "next", js_mkfun(js_subobs_next)); 185 + js_set(js, subobs, "error", js_mkfun(js_subobs_error)); 186 + js_set(js, subobs, "complete", js_mkfun(js_subobs_complete)); 187 + js_set(js, subobs, get_toStringTag_sym_key(), js_mkstr(js, "SubscriptionObserver", 20)); 188 + jsval_t closed_getter = js_mkfun(js_subobs_get_closed); 189 + js_set_getter_desc(js, subobs, "closed", 6, closed_getter, JS_DESC_E | JS_DESC_C); 190 + return subobs; 191 + } 192 + 193 + static jsval_t js_cleanup_fn(struct js *js, jsval_t *args, int nargs) { 194 + (void)args; (void)nargs; 195 + jsval_t F = js_getcurrentfunc(js); 196 + jsval_t subscription = js_get_slot(js, F, SLOT_DATA); 197 + 198 + if (js_type(subscription) != JS_OBJ) return js_mkundef(); 199 + 200 + jsval_t unsubscribe = js_get(js, subscription, "unsubscribe"); 201 + if (js_type(unsubscribe) == JS_FUNC) { 202 + return js_call_with_this(js, unsubscribe, subscription, NULL, 0); 203 + } 204 + 205 + return js_mkundef(); 206 + } 207 + 208 + static jsval_t execute_subscriber(struct js *js, jsval_t subscriber, jsval_t observer) { 209 + jsval_t call_args[1] = {observer}; 210 + jsval_t subscriberResult = js_call(js, subscriber, call_args, 1); 211 + 212 + if (js_type(subscriberResult) == JS_ERR) return subscriberResult; 213 + if (js_type(subscriberResult) == JS_NULL || js_type(subscriberResult) == JS_UNDEF) return js_mkundef(); 214 + if (js_type(subscriberResult) == JS_FUNC) return subscriberResult; 215 + 216 + if (js_type(subscriberResult) == JS_OBJ) { 217 + jsval_t result = js_get(js, subscriberResult, "unsubscribe"); 218 + if (js_type(result) == JS_UNDEF) { 219 + return js_mkerr_typed(js, JS_ERR_TYPE, "Subscriber return value must have an unsubscribe method"); 220 + } 221 + 222 + jsval_t cleanupFunction = js_mkobj(js); 223 + js_set_slot(js, cleanupFunction, SLOT_DATA, subscriberResult); 224 + js_set_slot(js, cleanupFunction, SLOT_CFUNC, js_mkfun(js_cleanup_fn)); 225 + return js_obj_to_func(cleanupFunction); 226 + } 227 + 228 + return js_mkerr_typed(js, JS_ERR_TYPE, "Subscriber must return a function, an object with unsubscribe, or undefined"); 229 + } 230 + 231 + static jsval_t js_observable_subscribe(struct js *js, jsval_t *args, int nargs) { 232 + jsval_t O = js_getthis(js); 233 + 234 + if (js_type(O) != JS_OBJ) { 235 + return js_mkerr_typed(js, JS_ERR_TYPE, "Observable.prototype.subscribe called on non-object"); 236 + } 237 + 238 + jsval_t subscriber = js_get_slot(js, O, SLOT_OBSERVABLE_SUBSCRIBER); 239 + if (js_type(subscriber) != JS_FUNC) { 240 + return js_mkerr_typed(js, JS_ERR_TYPE, "Observable has no [[Subscriber]] internal slot"); 241 + } 242 + 243 + jsval_t observer; 244 + 245 + if (nargs > 0 && js_type(args[0]) == JS_FUNC) { 246 + jsval_t nextCallback = args[0]; 247 + jsval_t errorCallback = (nargs > 1) ? args[1] : js_mkundef(); 248 + jsval_t completeCallback = (nargs > 2) ? args[2] : js_mkundef(); 249 + 250 + observer = js_mkobj(js); 251 + js_set(js, observer, "next", nextCallback); 252 + js_set(js, observer, "error", errorCallback); 253 + js_set(js, observer, "complete", completeCallback); 254 + } else if (nargs > 0 && js_type(args[0]) == JS_OBJ) { 255 + observer = args[0]; 256 + } else { 257 + observer = js_mkobj(js); 258 + } 259 + 260 + jsval_t subscription = create_subscription(js, observer); 261 + setup_subscription_methods(js, subscription); 262 + 263 + jsval_t start = js_get(js, observer, "start"); 264 + if (js_type(start) == JS_FUNC) { 265 + jsval_t start_args[1] = {subscription}; 266 + jsval_t result = js_call_with_this(js, start, observer, start_args, 1); 267 + if (js_type(result) == JS_ERR) { 268 + fprintf(stderr, "Error in observer.start: %s\n", js_str(js, result)); 269 + } 270 + if (subscription_closed(js, subscription)) return subscription; 271 + } 272 + 273 + jsval_t subscriptionObserver = create_subscription_observer(js, subscription); 274 + jsval_t subscriberResult = execute_subscriber(js, subscriber, subscriptionObserver); 275 + 276 + if (js_type(subscriberResult) == JS_ERR) { 277 + jsval_t error_args[1] = {subscriberResult}; 278 + jsval_t error_method = js_get(js, subscriptionObserver, "error"); 279 + if (js_type(error_method) == JS_FUNC) { 280 + js_call_with_this(js, error_method, subscriptionObserver, error_args, 1); 281 + } 282 + } else { 283 + js_set_slot(js, subscription, SLOT_SUBSCRIPTION_CLEANUP, subscriberResult); 284 + } 285 + 286 + if (subscription_closed(js, subscription)) { 287 + cleanup_subscription(js, subscription); 288 + } 289 + 290 + return subscription; 291 + } 292 + 293 + static jsval_t js_observable_symbol_observable(struct js *js, jsval_t *args, int nargs) { 294 + (void)args; (void)nargs; 295 + return js_getthis(js); 296 + } 297 + 298 + static jsval_t js_observable_constructor(struct js *js, jsval_t *args, int nargs) { 299 + if (nargs < 1) { 300 + return js_mkerr_typed(js, JS_ERR_TYPE, "Observable constructor requires a subscriber function"); 301 + } 302 + 303 + jsval_t subscriber = args[0]; 304 + if (js_type(subscriber) != JS_FUNC) { 305 + return js_mkerr_typed(js, JS_ERR_TYPE, "Observable subscriber must be a function"); 306 + } 307 + 308 + jsval_t observable = js_mkobj(js); 309 + js_set_slot(js, observable, SLOT_OBSERVABLE_SUBSCRIBER, subscriber); 310 + js_set(js, observable, "subscribe", js_mkfun(js_observable_subscribe)); 311 + js_set(js, observable, get_observable_sym_key(), js_mkfun(js_observable_symbol_observable)); 312 + js_set(js, observable, get_toStringTag_sym_key(), js_mkstr(js, "Observable", 10)); 313 + 314 + return observable; 315 + } 316 + 317 + static jsval_t js_of_subscriber(struct js *js, jsval_t *args, int nargs) { 318 + jsval_t F = js_getcurrentfunc(js); 319 + jsval_t items = js_get_slot(js, F, SLOT_DATA); 320 + 321 + if (nargs < 1) return js_mkundef(); 322 + 323 + jsval_t observer = args[0]; 324 + jsval_t subscription = js_get_slot(js, observer, SLOT_DATA); 325 + 326 + jsval_t length_val = js_get(js, items, "length"); 327 + int length = (js_type(length_val) == JS_NUM) ? (int)js_getnum(length_val) : 0; 328 + 329 + for (int i = 0; i < length; i++) { 330 + char key[16]; 331 + snprintf(key, sizeof(key), "%d", i); 332 + jsval_t value = js_get(js, items, key); 333 + 334 + jsval_t next = js_get(js, observer, "next"); 335 + if (js_type(next) == JS_FUNC) { 336 + jsval_t next_args[1] = {value}; 337 + js_call_with_this(js, next, observer, next_args, 1); 338 + } 339 + 340 + if (js_type(subscription) == JS_OBJ && subscription_closed(js, subscription)) { 341 + return js_mkundef(); 342 + } 343 + } 344 + 345 + jsval_t complete = js_get(js, observer, "complete"); 346 + if (js_type(complete) == JS_FUNC) { 347 + js_call_with_this(js, complete, observer, NULL, 0); 348 + } 349 + 350 + return js_mkundef(); 351 + } 352 + 353 + static jsval_t js_observable_of(struct js *js, jsval_t *args, int nargs) { 354 + jsval_t items = js_mkarr(js); 355 + for (int i = 0; i < nargs; i++) { 356 + js_arr_push(js, items, args[i]); 357 + } 358 + 359 + jsval_t subscriber = js_mkobj(js); 360 + js_set_slot(js, subscriber, SLOT_DATA, items); 361 + js_set_slot(js, subscriber, SLOT_CFUNC, js_mkfun(js_of_subscriber)); 362 + jsval_t subscriber_func = js_obj_to_func(subscriber); 363 + 364 + jsval_t ctor_args[1] = {subscriber_func}; 365 + return js_observable_constructor(js, ctor_args, 1); 366 + } 367 + 368 + static jsval_t js_from_delegating(struct js *js, jsval_t *args, int nargs) { 369 + jsval_t F = js_getcurrentfunc(js); 370 + jsval_t observable = js_get_slot(js, F, SLOT_DATA); 371 + 372 + if (js_type(observable) != JS_OBJ) return js_mkundef(); 373 + 374 + jsval_t subscribe = js_get(js, observable, "subscribe"); 375 + if (js_type(subscribe) == JS_FUNC) { 376 + return js_call_with_this(js, subscribe, observable, args, nargs); 377 + } 378 + 379 + return js_mkundef(); 380 + } 381 + 382 + static jsval_t js_from_iteration(struct js *js, jsval_t *args, int nargs) { 383 + jsval_t F = js_getcurrentfunc(js); 384 + jsval_t data = js_get_slot(js, F, SLOT_DATA); 385 + 386 + jsval_t iterable = js_get(js, data, "iterable"); 387 + jsval_t iteratorMethod = js_get(js, data, "iteratorMethod"); 388 + 389 + if (nargs < 1) return js_mkundef(); 390 + 391 + jsval_t observer = args[0]; 392 + jsval_t subscription = js_get_slot(js, observer, SLOT_DATA); 393 + 394 + if (js_type(iteratorMethod) != JS_FUNC) { 395 + return js_mkerr_typed(js, JS_ERR_TYPE, "Object is not iterable"); 396 + } 397 + 398 + jsval_t iterator = js_call_with_this(js, iteratorMethod, iterable, NULL, 0); 399 + if (js_type(iterator) != JS_OBJ) { 400 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator must return an object"); 401 + } 402 + 403 + jsval_t nextMethod = js_get(js, iterator, "next"); 404 + if (js_type(nextMethod) != JS_FUNC) { 405 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator must have a next method"); 406 + } 407 + 408 + while (true) { 409 + jsval_t next = js_call_with_this(js, nextMethod, iterator, NULL, 0); 410 + if (js_type(next) == JS_ERR) return next; 411 + 412 + jsval_t done = js_get(js, next, "done"); 413 + if (js_truthy(js, done)) { 414 + jsval_t complete = js_get(js, observer, "complete"); 415 + if (js_type(complete) == JS_FUNC) { 416 + js_call_with_this(js, complete, observer, NULL, 0); 417 + } 418 + return js_mkundef(); 419 + } 420 + 421 + jsval_t nextValue = js_get(js, next, "value"); 422 + jsval_t obs_next = js_get(js, observer, "next"); 423 + if (js_type(obs_next) == JS_FUNC) { 424 + jsval_t next_args[1] = {nextValue}; 425 + js_call_with_this(js, obs_next, observer, next_args, 1); 426 + } 427 + 428 + if (js_type(subscription) == JS_OBJ && subscription_closed(js, subscription)) { 429 + jsval_t returnMethod = js_get(js, iterator, "return"); 430 + if (js_type(returnMethod) == JS_FUNC) { 431 + js_call_with_this(js, returnMethod, iterator, NULL, 0); 432 + } 433 + return js_mkundef(); 434 + } 435 + } 436 + } 437 + 438 + static jsval_t js_observable_from(struct js *js, jsval_t *args, int nargs) { 439 + if (nargs < 1) { 440 + return js_mkerr_typed(js, JS_ERR_TYPE, "Observable.from requires an argument"); 441 + } 442 + 443 + jsval_t x = args[0]; 444 + jsval_t observableMethod = js_get(js, x, get_observable_sym_key()); 445 + 446 + if (js_type(observableMethod) == JS_FUNC) { 447 + jsval_t observable = js_call_with_this(js, observableMethod, x, NULL, 0); 448 + 449 + if (js_type(observable) != JS_OBJ) { 450 + return js_mkerr_typed(js, JS_ERR_TYPE, "@@observable must return an object"); 451 + } 452 + 453 + jsval_t constructor = js_get(js, observable, "constructor"); 454 + jsval_t C = js_get(js, js_glob(js), "Observable"); 455 + 456 + if (constructor == C) return observable; 457 + 458 + jsval_t subscriber = js_mkobj(js); 459 + js_set_slot(js, subscriber, SLOT_DATA, observable); 460 + js_set_slot(js, subscriber, SLOT_CFUNC, js_mkfun(js_from_delegating)); 461 + jsval_t subscriber_func = js_obj_to_func(subscriber); 462 + 463 + jsval_t ctor_args[1] = {subscriber_func}; 464 + return js_observable_constructor(js, ctor_args, 1); 465 + } 466 + 467 + jsval_t iteratorMethod = js_get(js, x, get_iterator_sym_key()); 468 + 469 + if (js_type(iteratorMethod) != JS_FUNC) { 470 + return js_mkerr_typed(js, JS_ERR_TYPE, "Object is not observable or iterable"); 471 + } 472 + 473 + jsval_t data = js_mkobj(js); 474 + js_set(js, data, "iterable", x); 475 + js_set(js, data, "iteratorMethod", iteratorMethod); 476 + 477 + jsval_t subscriber = js_mkobj(js); 478 + js_set_slot(js, subscriber, SLOT_DATA, data); 479 + js_set_slot(js, subscriber, SLOT_CFUNC, js_mkfun(js_from_iteration)); 480 + jsval_t subscriber_func = js_obj_to_func(subscriber); 481 + 482 + jsval_t ctor_args[1] = {subscriber_func}; 483 + return js_observable_constructor(js, ctor_args, 1); 484 + } 485 + 486 + void init_observable_module(void) { 487 + struct js *js = rt->js; 488 + jsval_t global = js_glob(js); 489 + 490 + jsval_t observable_ctor = js_mkobj(js); 491 + js_set_slot(js, observable_ctor, SLOT_CFUNC, js_mkfun(js_observable_constructor)); 492 + js_set(js, observable_ctor, "of", js_mkfun(js_observable_of)); 493 + js_set(js, observable_ctor, "from", js_mkfun(js_observable_from)); 494 + 495 + jsval_t observable_proto = js_mkobj(js); 496 + js_set(js, observable_proto, "subscribe", js_mkfun(js_observable_subscribe)); 497 + js_set(js, observable_proto, get_observable_sym_key(), js_mkfun(js_observable_symbol_observable)); 498 + js_set(js, observable_proto, "constructor", js_obj_to_func(observable_ctor)); 499 + js_set(js, observable_proto, get_toStringTag_sym_key(), js_mkstr(js, "Observable", 10)); 500 + 501 + js_set(js, observable_ctor, "prototype", observable_proto); 502 + js_setprop_nonconfigurable(js, observable_ctor, "prototype", 9, observable_proto); 503 + 504 + jsval_t Observable = js_obj_to_func(observable_ctor); 505 + js_set(js, global, "Observable", Observable); 506 + }
+7
src/modules/symbol.c
··· 10 10 static jsval_t g_asyncIterator_sym = 0; 11 11 static jsval_t g_toStringTag_sym = 0; 12 12 static jsval_t g_hasInstance_sym = 0; 13 + static jsval_t g_observable_sym = 0; 13 14 14 15 static char g_iter_sym_key[32] = {0}; 15 16 static char g_asyncIter_sym_key[32] = {0}; 16 17 static char g_toStringTag_sym_key[32] = {0}; 18 + static char g_observable_sym_key[32] = {0}; 17 19 18 20 jsval_t get_iterator_symbol(void) { return g_iterator_sym; } 19 21 jsval_t get_asyncIterator_symbol(void) { return g_asyncIterator_sym; } 22 + jsval_t get_observable_symbol(void) { return g_observable_sym; } 20 23 21 24 const char *get_iterator_sym_key(void) { return g_iter_sym_key; } 22 25 const char *get_asyncIterator_sym_key(void) { return g_asyncIter_sym_key; } 23 26 const char *get_toStringTag_sym_key(void) { return g_toStringTag_sym_key; } 27 + const char *get_observable_sym_key(void) { return g_observable_sym_key; } 24 28 25 29 static jsval_t builtin_Symbol(struct js *js, jsval_t *args, int nargs) { 26 30 const char *desc = NULL; ··· 158 162 g_asyncIterator_sym = js_mksym(js, "Symbol.asyncIterator"); 159 163 g_toStringTag_sym = js_mksym(js, "Symbol.toStringTag"); 160 164 g_hasInstance_sym = js_mksym(js, "Symbol.hasInstance"); 165 + g_observable_sym = js_mksym(js, "Symbol.observable"); 161 166 162 167 snprintf(g_iter_sym_key, sizeof(g_iter_sym_key), "__sym_%llu__", (unsigned long long)js_sym_id(g_iterator_sym)); 163 168 snprintf(g_asyncIter_sym_key, sizeof(g_asyncIter_sym_key), "__sym_%llu__", (unsigned long long)js_sym_id(g_asyncIterator_sym)); 164 169 snprintf(g_toStringTag_sym_key, sizeof(g_toStringTag_sym_key), "__sym_%llu__", (unsigned long long)js_sym_id(g_toStringTag_sym)); 170 + snprintf(g_observable_sym_key, sizeof(g_observable_sym_key), "__sym_%llu__", (unsigned long long)js_sym_id(g_observable_sym)); 165 171 166 172 jsval_t symbol_ctor = js_mkobj(js); 167 173 js_set_slot(js, symbol_ctor, SLOT_CFUNC, js_mkfun(builtin_Symbol)); ··· 172 178 js_set(js, symbol_ctor, "asyncIterator", g_asyncIterator_sym); 173 179 js_set(js, symbol_ctor, "toStringTag", g_toStringTag_sym); 174 180 js_set(js, symbol_ctor, "hasInstance", g_hasInstance_sym); 181 + js_set(js, symbol_ctor, "observable", g_observable_sym); 175 182 176 183 jsval_t func_symbol = js_obj_to_func(symbol_ctor); 177 184 js_set(js, js_glob(js), "Symbol", func_symbol);