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.

at mir/inline-method 480 lines 18 kB view raw
1#include <stdlib.h> 2#include <stdio.h> 3#include <string.h> 4 5#include "errors.h" 6#include "internal.h" 7#include "runtime.h" 8#include "silver/engine.h" 9#include "descriptors.h" 10 11#include "modules/symbol.h" 12#include "modules/observable.h" 13 14 15 16static bool subscription_closed(ant_t *js, ant_value_t subscription) { 17 ant_value_t observer = js_get_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER); 18 return vtype(observer) == T_UNDEF; 19} 20 21static void cleanup_subscription(ant_t *js, ant_value_t subscription) { 22 ant_value_t cleanup = js_get_slot(subscription, SLOT_SUBSCRIPTION_CLEANUP); 23 if (vtype(cleanup) == T_UNDEF) return; 24 if (!is_callable(cleanup)) return; 25 26 js_set_slot(subscription, SLOT_SUBSCRIPTION_CLEANUP, js_mkundef()); 27 ant_value_t result = sv_vm_call(js->vm, js, cleanup, js_mkundef(), NULL, 0, NULL, false); 28 29 if (vtype(result) == T_ERR) fprintf(stderr, "Error in subscription cleanup: %s\n", js_str(js, result)); 30} 31 32static ant_value_t create_subscription(ant_t *js, ant_value_t observer) { 33 ant_value_t subscription = js_mkobj(js); 34 js_set_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER, observer); 35 js_set_slot(subscription, SLOT_SUBSCRIPTION_CLEANUP, js_mkundef()); 36 js_set_sym(js, subscription, get_toStringTag_sym(), js_mkstr(js, "Subscription", 12)); 37 return subscription; 38} 39 40static ant_value_t js_subscription_get_closed(ant_t *js, ant_value_t *args, int nargs) { 41 (void)args; (void)nargs; 42 ant_value_t subscription = js_getthis(js); 43 44 if (!is_special_object(subscription)) { 45 return js_mkerr_typed(js, JS_ERR_TYPE, "Subscription.closed getter called on non-object"); 46 } 47 return js_bool(subscription_closed(js, subscription)); 48} 49 50static ant_value_t js_subscription_unsubscribe(ant_t *js, ant_value_t *args, int nargs) { 51 (void)args; (void)nargs; 52 ant_value_t subscription = js_getthis(js); 53 54 if (!is_special_object(subscription)) { 55 return js_mkerr_typed(js, JS_ERR_TYPE, "Subscription.unsubscribe called on non-object"); 56 } 57 58 if (subscription_closed(js, subscription)) return js_mkundef(); 59 60 js_set_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); 61 cleanup_subscription(js, subscription); 62 63 return js_mkundef(); 64} 65 66static void setup_subscription_methods(ant_t *js, ant_value_t subscription) { 67 js_set(js, subscription, "unsubscribe", js_mkfun(js_subscription_unsubscribe)); 68 ant_value_t closed_getter = js_mkfun(js_subscription_get_closed); 69 js_set_getter_desc(js, subscription, "closed", 6, closed_getter, JS_DESC_E | JS_DESC_C); 70} 71 72static ant_value_t js_subobs_get_closed(ant_t *js, ant_value_t *args, int nargs) { 73 (void)args; (void)nargs; 74 ant_value_t O = js_getthis(js); 75 76 if (!is_special_object(O)) { 77 return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.closed getter called on non-object"); 78 } 79 80 ant_value_t subscription = js_get_slot(O, SLOT_DATA); 81 if (!is_special_object(subscription)) { 82 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); 83 } 84 85 return js_bool(subscription_closed(js, subscription)); 86} 87 88static ant_value_t js_subobs_next(ant_t *js, ant_value_t *args, int nargs) { 89 ant_value_t O = js_getthis(js); 90 91 if (!is_special_object(O)) { 92 return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.next called on non-object"); 93 } 94 95 ant_value_t subscription = js_get_slot(O, SLOT_DATA); 96 if (!is_special_object(subscription)) { 97 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); 98 } 99 100 if (subscription_closed(js, subscription)) return js_mkundef(); 101 102 ant_value_t observer = js_get_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER); 103 if (!is_special_object(observer)) return js_mkundef(); 104 105 ant_value_t nextMethod = js_get(js, observer, "next"); 106 if (is_callable(nextMethod)) { 107 ant_value_t value = (nargs > 0) ? args[0] : js_mkundef(); 108 ant_value_t call_args[1] = {value}; 109 ant_value_t result = sv_vm_call(js->vm, js, nextMethod, observer, call_args, 1, NULL, false); 110 if (vtype(result) == T_ERR) fprintf(stderr, "Error in observer.next: %s\n", js_str(js, result)); 111 } 112 113 return js_mkundef(); 114} 115 116static ant_value_t js_subobs_error(ant_t *js, ant_value_t *args, int nargs) { 117 ant_value_t O = js_getthis(js); 118 119 if (!is_special_object(O)) { 120 return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.error called on non-object"); 121 } 122 123 ant_value_t subscription = js_get_slot(O, SLOT_DATA); 124 if (!is_special_object(subscription)) { 125 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); 126 } 127 128 if (subscription_closed(js, subscription)) return js_mkundef(); 129 130 ant_value_t observer = js_get_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER); 131 js_set_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); 132 133 if (is_special_object(observer)) { 134 ant_value_t errorMethod = js_get(js, observer, "error"); 135 if (is_callable(errorMethod)) { 136 ant_value_t exception = (nargs > 0) ? args[0] : js_mkundef(); 137 ant_value_t call_args[1] = {exception}; 138 ant_value_t result = sv_vm_call(js->vm, js, errorMethod, observer, call_args, 1, NULL, false); 139 if (vtype(result) == T_ERR) fprintf(stderr, "Error in observer.error: %s\n", js_str(js, result)); 140 } 141 } 142 143 cleanup_subscription(js, subscription); 144 return js_mkundef(); 145} 146 147static ant_value_t js_subobs_complete(ant_t *js, ant_value_t *args, int nargs) { 148 (void)args; (void)nargs; 149 ant_value_t O = js_getthis(js); 150 151 if (!is_special_object(O)) { 152 return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.complete called on non-object"); 153 } 154 155 ant_value_t subscription = js_get_slot(O, SLOT_DATA); 156 if (!is_special_object(subscription)) { 157 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); 158 } 159 160 if (subscription_closed(js, subscription)) return js_mkundef(); 161 162 ant_value_t observer = js_get_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER); 163 js_set_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); 164 165 if (is_special_object(observer)) { 166 ant_value_t completeMethod = js_get(js, observer, "complete"); 167 if (is_callable(completeMethod)) { 168 ant_value_t result = sv_vm_call(js->vm, js, completeMethod, observer, NULL, 0, NULL, false); 169 if (vtype(result) == T_ERR) fprintf(stderr, "Error in observer.complete: %s\n", js_str(js, result)); 170 } 171 } 172 173 cleanup_subscription(js, subscription); 174 return js_mkundef(); 175} 176 177static ant_value_t create_subscription_observer(ant_t *js, ant_value_t subscription) { 178 ant_value_t subobs = js_mkobj(js); 179 180 js_set_slot(subobs, SLOT_DATA, subscription); 181 js_set(js, subobs, "next", js_mkfun(js_subobs_next)); 182 js_set(js, subobs, "error", js_mkfun(js_subobs_error)); 183 js_set(js, subobs, "complete", js_mkfun(js_subobs_complete)); 184 js_set_sym(js, subobs, get_toStringTag_sym(), js_mkstr(js, "SubscriptionObserver", 20)); 185 186 ant_value_t closed_getter = js_mkfun(js_subobs_get_closed); 187 js_set_getter_desc(js, subobs, "closed", 6, closed_getter, JS_DESC_E | JS_DESC_C); 188 189 return subobs; 190} 191 192static ant_value_t js_cleanup_fn(ant_t *js, ant_value_t *args, int nargs) { 193 (void)args; (void)nargs; 194 ant_value_t F = js_getcurrentfunc(js); 195 ant_value_t subscription = js_get_slot(F, SLOT_DATA); 196 197 if (!is_special_object(subscription)) return js_mkundef(); 198 199 ant_value_t unsubscribe = js_get(js, subscription, "unsubscribe"); 200 if (is_callable(unsubscribe)) { 201 return sv_vm_call(js->vm, js, unsubscribe, subscription, NULL, 0, NULL, false); 202 } 203 204 return js_mkundef(); 205} 206 207static ant_value_t execute_subscriber(ant_t *js, ant_value_t subscriber, ant_value_t observer) { 208 ant_value_t call_args[1] = {observer}; 209 ant_value_t subscriberResult = sv_vm_call(js->vm, js, subscriber, js_mkundef(), call_args, 1, NULL, false); 210 211 if (vtype(subscriberResult) == T_ERR) return subscriberResult; 212 if (vtype(subscriberResult) == T_NULL || vtype(subscriberResult) == T_UNDEF) return js_mkundef(); 213 if (is_callable(subscriberResult)) return subscriberResult; 214 215 if (is_special_object(subscriberResult)) { 216 ant_value_t result = js_get(js, subscriberResult, "unsubscribe"); 217 if (vtype(result) == T_UNDEF) { 218 return js_mkerr_typed(js, JS_ERR_TYPE, "Subscriber return value must have an unsubscribe method"); 219 } 220 221 ant_value_t cleanupFunction = js_mkobj(js); 222 js_set_slot(cleanupFunction, SLOT_DATA, subscriberResult); 223 js_set_slot(cleanupFunction, SLOT_CFUNC, js_mkfun(js_cleanup_fn)); 224 return js_obj_to_func(cleanupFunction); 225 } 226 227 return js_mkerr_typed(js, JS_ERR_TYPE, "Subscriber must return a function, an object with unsubscribe, or undefined"); 228} 229 230static ant_value_t js_observable_subscribe(ant_t *js, ant_value_t *args, int nargs) { 231 ant_value_t O = js_getthis(js); 232 233 if (!is_special_object(O)) { 234 return js_mkerr_typed(js, JS_ERR_TYPE, "Observable.prototype.subscribe called on non-object"); 235 } 236 237 ant_value_t subscriber = js_get_slot(O, SLOT_OBSERVABLE_SUBSCRIBER); 238 if (!is_callable(subscriber)) { 239 return js_mkerr_typed(js, JS_ERR_TYPE, "Observable has no [[Subscriber]] internal slot"); 240 } 241 242 ant_value_t observer; 243 244 if (nargs > 0 && is_callable(args[0])) { 245 ant_value_t nextCallback = args[0]; 246 ant_value_t errorCallback = (nargs > 1) ? args[1] : js_mkundef(); 247 ant_value_t completeCallback = (nargs > 2) ? args[2] : js_mkundef(); 248 249 observer = js_mkobj(js); 250 js_set(js, observer, "next", nextCallback); 251 js_set(js, observer, "error", errorCallback); 252 js_set(js, observer, "complete", completeCallback); 253 } else if (nargs > 0 && is_special_object(args[0])) { 254 observer = args[0]; 255 } else observer = js_mkobj(js); 256 257 ant_value_t subscription = create_subscription(js, observer); 258 setup_subscription_methods(js, subscription); 259 260 ant_value_t start = js_get(js, observer, "start"); 261 if (is_callable(start)) { 262 ant_value_t start_args[1] = {subscription}; 263 ant_value_t result = sv_vm_call(js->vm, js, start, observer, start_args, 1, NULL, false); 264 if (vtype(result) == T_ERR) { 265 fprintf(stderr, "Error in observer.start: %s\n", js_str(js, result)); 266 } 267 if (subscription_closed(js, subscription)) return subscription; 268 } 269 270 ant_value_t subscriptionObserver = create_subscription_observer(js, subscription); 271 ant_value_t subscriberResult = execute_subscriber(js, subscriber, subscriptionObserver); 272 273 if (vtype(subscriberResult) == T_ERR) { 274 ant_value_t thrown_error = js->thrown_value; 275 js->thrown_value = js_mkundef(); 276 js->thrown_exists = false; 277 278 ant_value_t error_args[1] = {thrown_error}; 279 ant_value_t error_method = js_get(js, subscriptionObserver, "error"); 280 if (is_callable(error_method)) sv_vm_call(js->vm, js, error_method, subscriptionObserver, error_args, 1, NULL, false); 281 } else js_set_slot_wb(js, subscription, SLOT_SUBSCRIPTION_CLEANUP, subscriberResult); 282 283 if (subscription_closed(js, subscription)) cleanup_subscription(js, subscription); 284 285 return subscription; 286} 287 288static ant_value_t js_observable_symbol_observable(ant_t *js, ant_value_t *args, int nargs) { 289 (void)args; (void)nargs; 290 return js_getthis(js); 291} 292 293static ant_value_t js_observable_constructor(ant_t *js, ant_value_t *args, int nargs) { 294 if (nargs < 1) { 295 return js_mkerr_typed(js, JS_ERR_TYPE, "Observable constructor requires a subscriber function"); 296 } 297 298 ant_value_t subscriber = args[0]; 299 if (!is_callable(subscriber)) { 300 return js_mkerr_typed(js, JS_ERR_TYPE, "Observable subscriber must be a function"); 301 } 302 303 ant_value_t proto = js_get_ctor_proto(js, "Observable", 10); 304 ant_value_t observable = js_mkobj(js); 305 306 js_set_proto_init(observable, proto); 307 js_set_slot(observable, SLOT_OBSERVABLE_SUBSCRIBER, subscriber); 308 309 return observable; 310} 311 312static ant_value_t js_of_subscriber(ant_t *js, ant_value_t *args, int nargs) { 313 ant_value_t F = js_getcurrentfunc(js); 314 ant_value_t items = js_get_slot(F, SLOT_DATA); 315 316 if (nargs < 1) return js_mkundef(); 317 318 ant_value_t observer = args[0]; 319 ant_value_t subscription = js_get_slot(observer, SLOT_DATA); 320 321 ant_value_t length_val = js_get(js, items, "length"); 322 int length = (vtype(length_val) == T_NUM) ? (int)js_getnum(length_val) : 0; 323 324 for (int i = 0; i < length; i++) { 325 char key[16]; 326 snprintf(key, sizeof(key), "%d", i); 327 ant_value_t value = js_get(js, items, key); 328 329 ant_value_t next = js_get(js, observer, "next"); 330 if (is_callable(next)) { 331 ant_value_t next_args[1] = {value}; 332 sv_vm_call(js->vm, js, next, observer, next_args, 1, NULL, false); 333 } 334 335 if (is_special_object(subscription) && subscription_closed(js, subscription)) return js_mkundef(); 336 } 337 338 ant_value_t complete = js_get(js, observer, "complete"); 339 if (is_callable(complete)) sv_vm_call(js->vm, js, complete, observer, NULL, 0, NULL, false); 340 341 return js_mkundef(); 342} 343 344static ant_value_t js_observable_of(ant_t *js, ant_value_t *args, int nargs) { 345 ant_value_t items = js_mkarr(js); 346 for (int i = 0; i < nargs; i++) js_arr_push(js, items, args[i]); 347 348 ant_value_t subscriber_func = js_heavy_mkfun(js, js_of_subscriber, items); 349 ant_value_t ctor_args[1] = {subscriber_func}; 350 351 return js_observable_constructor(js, ctor_args, 1); 352} 353 354static ant_value_t js_from_delegating(ant_t *js, ant_value_t *args, int nargs) { 355 ant_value_t F = js_getcurrentfunc(js); 356 357 ant_value_t observable = js_get_slot(F, SLOT_DATA); 358 if (!is_special_object(observable)) return js_mkundef(); 359 360 ant_value_t subscribe = js_get(js, observable, "subscribe"); 361 if (is_callable(subscribe)) { 362 return sv_vm_call(js->vm, js, subscribe, observable, args, nargs, NULL, false); 363 } 364 365 return js_mkundef(); 366} 367 368static ant_value_t js_from_iteration(ant_t *js, ant_value_t *args, int nargs) { 369 ant_value_t F = js_getcurrentfunc(js); 370 ant_value_t data = js_get_slot(F, SLOT_DATA); 371 372 ant_value_t iterable = js_get(js, data, "iterable"); 373 ant_value_t iteratorMethod = js_get(js, data, "iteratorMethod"); 374 375 if (nargs < 1) return js_mkundef(); 376 377 ant_value_t observer = args[0]; 378 ant_value_t subscription = js_get_slot(observer, SLOT_DATA); 379 380 if (!is_callable(iteratorMethod)) { 381 return js_mkerr_typed(js, JS_ERR_TYPE, "Object is not iterable"); 382 } 383 384 ant_value_t iterator = sv_vm_call(js->vm, js, iteratorMethod, iterable, NULL, 0, NULL, false); 385 if (!is_special_object(iterator)) { 386 return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator must return an object"); 387 } 388 389 ant_value_t nextMethod = js_getprop_fallback(js, iterator, "next"); 390 if (!is_callable(nextMethod)) { 391 return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator must have a next method"); 392 } 393 394 while (true) { 395 ant_value_t next = sv_vm_call(js->vm, js, nextMethod, iterator, NULL, 0, NULL, false); 396 if (vtype(next) == T_ERR) return next; 397 398 ant_value_t done = js_get(js, next, "done"); 399 if (js_truthy(js, done)) { 400 ant_value_t complete = js_get(js, observer, "complete"); 401 if (is_callable(complete)) sv_vm_call(js->vm, js, complete, observer, NULL, 0, NULL, false); 402 return js_mkundef(); 403 } 404 405 ant_value_t nextValue = js_get(js, next, "value"); 406 ant_value_t obs_next = js_get(js, observer, "next"); 407 if (is_callable(obs_next)) { 408 ant_value_t next_args[1] = {nextValue}; 409 sv_vm_call(js->vm, js, obs_next, observer, next_args, 1, NULL, false); 410 } 411 412 if (is_special_object(subscription) && subscription_closed(js, subscription)) { 413 ant_value_t returnMethod = js_getprop_fallback(js, iterator, "return"); 414 if (is_callable(returnMethod)) sv_vm_call(js->vm, js, returnMethod, iterator, NULL, 0, NULL, false); 415 return js_mkundef(); 416 } 417 } 418} 419 420static ant_value_t js_observable_from(ant_t *js, ant_value_t *args, int nargs) { 421 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Observable.from requires an argument"); 422 ant_value_t x = args[0]; 423 424 if (vtype(x) == T_NULL || vtype(x) == T_UNDEF) { 425 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert null or undefined to observable"); 426 } 427 428 ant_value_t observableMethod = js_get_sym(js, x, get_observable_sym()); 429 430 if (is_callable(observableMethod)) { 431 ant_value_t observable = sv_vm_call(js->vm, js, observableMethod, x, NULL, 0, NULL, false); 432 433 if (!is_special_object(observable)) { 434 return js_mkerr_typed(js, JS_ERR_TYPE, "@@observable must return an object"); 435 } 436 437 ant_value_t existing_subscriber = js_get_slot(observable, SLOT_OBSERVABLE_SUBSCRIBER); 438 if (is_callable(existing_subscriber)) return observable; 439 440 ant_value_t subscriber_func = js_heavy_mkfun(js, js_from_delegating, observable); 441 ant_value_t ctor_args[1] = {subscriber_func}; 442 443 return js_observable_constructor(js, ctor_args, 1); 444 } 445 446 ant_value_t iteratorMethod = js_get_sym(js, x, get_iterator_sym()); 447 if (!is_callable(iteratorMethod)) return js_mkerr_typed(js, JS_ERR_TYPE, "Object is not observable or iterable"); 448 449 ant_value_t data = js_mkobj(js); 450 js_set(js, data, "iterable", x); 451 js_set(js, data, "iteratorMethod", iteratorMethod); 452 453 ant_value_t subscriber_func = js_heavy_mkfun(js, js_from_iteration, data); 454 ant_value_t ctor_args[1] = {subscriber_func}; 455 456 return js_observable_constructor(js, ctor_args, 1); 457} 458 459void init_observable_module(void) { 460 ant_t *js = rt->js; 461 ant_value_t global = js_glob(js); 462 463 ant_value_t observable_ctor = js_mkobj(js); 464 ant_value_t observable_proto = js_mkobj(js); 465 466 js_set(js, observable_proto, "subscribe", js_mkfun(js_observable_subscribe)); 467 js_set_sym(js, observable_proto, get_observable_sym(), js_mkfun(js_observable_symbol_observable)); 468 js_set_sym(js, observable_proto, get_toStringTag_sym(), js_mkstr(js, "Observable", 10)); 469 470 js_set_slot(observable_ctor, SLOT_CFUNC, js_mkfun(js_observable_constructor)); 471 js_mkprop_fast(js, observable_ctor, "prototype", 9, observable_proto); 472 js_mkprop_fast(js, observable_ctor, "name", 4, ANT_STRING("Observable")); 473 js_set_descriptor(js, observable_ctor, "name", 4, 0); 474 js_set(js, observable_ctor, "of", js_mkfun(js_observable_of)); 475 js_set(js, observable_ctor, "from", js_mkfun(js_observable_from)); 476 477 ant_value_t Observable = js_obj_to_func(observable_ctor); 478 js_set(js, observable_proto, "constructor", Observable); 479 js_set(js, global, "Observable", Observable); 480}