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 613 lines 20 kB view raw
1#include <stdlib.h> 2#include <string.h> 3 4#include "ant.h" 5#include "ptr.h" 6#include "errors.h" 7#include "internal.h" 8#include "runtime.h" 9#include "sugar.h" 10 11#include "gc/roots.h" 12#include "silver/engine.h" 13#include "modules/generator.h" 14#include "modules/iterator.h" 15#include "modules/symbol.h" 16 17enum { GENERATOR_NATIVE_TAG = 0x47454e52u }; // GENR 18 19typedef enum { 20 GEN_SUSPENDED_START = 0, 21 GEN_SUSPENDED_YIELD = 1, 22 GEN_EXECUTING = 2, 23 GEN_COMPLETED = 3, 24} generator_state_t; 25 26typedef struct generator_request { 27 sv_resume_kind_t kind; 28 ant_value_t value; 29 ant_value_t promise; 30 struct generator_request *next; 31} generator_request_t; 32 33typedef struct generator_data { 34 coroutine_t *coro; 35 generator_state_t state; 36 generator_request_t *queue_head; 37 generator_request_t *queue_tail; 38 bool is_async; 39} generator_data_t; 40 41static ant_value_t generator_resume_kind( 42 ant_t *js, ant_value_t gen, 43 ant_value_t resume_value, sv_resume_kind_t resume_kind 44); 45 46static ant_value_t generator_result(ant_t *js, bool done, ant_value_t value) { 47 ant_value_t result = js_mkobj(js); 48 js_set(js, result, "done", js_bool(done)); 49 js_set(js, result, "value", value); 50 return result; 51} 52 53static generator_data_t *generator_data(ant_value_t gen) { 54 if (!js_check_native_tag(gen, GENERATOR_NATIVE_TAG)) return NULL; 55 return (generator_data_t *)js_get_native_ptr(gen); 56} 57 58static generator_state_t generator_state(ant_value_t gen) { 59 generator_data_t *data = generator_data(gen); 60 return data ? data->state : GEN_COMPLETED; 61} 62 63static void generator_set_state(ant_value_t gen, generator_state_t state) { 64 generator_data_t *data = generator_data(gen); 65 if (data) data->state = state; 66} 67 68static bool generator_is_async(ant_value_t gen) { 69 generator_data_t *data = generator_data(gen); 70 return data && data->is_async; 71} 72 73static ant_value_t generator_async_wrap_result(ant_t *js, ant_value_t result) { 74 ant_value_t promise = js_mkpromise(js); 75 if (is_err(promise)) return promise; 76 77 if (is_err(result)) { 78 ant_value_t reject_value = js->thrown_exists ? js->thrown_value : result; 79 js->thrown_exists = false; 80 js->thrown_value = js_mkundef(); 81 js_reject_promise(js, promise, reject_value); 82 } else js_resolve_promise(js, promise, result); 83 84 return promise; 85} 86 87static generator_request_t *generator_dequeue_request(generator_data_t *data) { 88 if (!data || !data->queue_head) return NULL; 89 generator_request_t *req = data->queue_head; 90 data->queue_head = req->next; 91 if (!data->queue_head) data->queue_tail = NULL; 92 req->next = NULL; 93 return req; 94} 95 96static void generator_free_queue(generator_data_t *data) { 97 if (!data) return; 98 generator_request_t *req = data->queue_head; 99 100 while (req) { 101 generator_request_t *next = req->next; 102 free(req); 103 req = next; 104 } 105 106 data->queue_head = NULL; 107 data->queue_tail = NULL; 108} 109 110static ant_value_t generator_enqueue_request( 111 ant_t *js, ant_value_t gen, sv_resume_kind_t kind, ant_value_t value 112) { 113 generator_data_t *data = generator_data(gen); 114 if (!data || !data->is_async) return js_mkerr_typed(js, JS_ERR_TYPE, "Generator is already executing"); 115 116 ant_value_t promise = js_mkpromise(js); 117 if (is_err(promise)) return promise; 118 119 generator_request_t *req = (generator_request_t *)calloc(1, sizeof(*req)); 120 if (!req) return js_mkerr(js, "out of memory for generator request"); 121 122 *req = (generator_request_t){ 123 .kind = kind, 124 .value = value, 125 .promise = promise, 126 .next = NULL, 127 }; 128 129 if (data->queue_tail) data->queue_tail->next = req; 130 else data->queue_head = req; 131 data->queue_tail = req; 132 133 return promise; 134} 135 136static void generator_settle_request_promise(ant_t *js, ant_value_t promise, ant_value_t result) { 137 if (is_err(result)) { 138 ant_value_t reject_value = js->thrown_exists ? js->thrown_value : result; 139 js->thrown_exists = false; 140 js->thrown_value = js_mkundef(); 141 js_reject_promise(js, promise, reject_value); 142 } else js_resolve_promise(js, promise, result); 143} 144 145static void generator_process_queue(ant_t *js, ant_value_t gen) { 146 generator_data_t *data = generator_data(gen); 147 if (!data || !data->is_async) return; 148 149 GC_ROOT_SAVE(root_mark, js); 150 GC_ROOT_PIN(js, gen); 151 152 while (data->state != GEN_EXECUTING && data->queue_head) { 153 generator_request_t *req = generator_dequeue_request(data); 154 if (!req) break; 155 156 GC_ROOT_PIN(js, req->value); 157 GC_ROOT_PIN(js, req->promise); 158 159 coroutine_t *coro = data->coro; 160 if (coro) coro->async_promise = req->promise; 161 162 ant_value_t result = generator_resume_kind(js, gen, req->value, req->kind); 163 GC_ROOT_PIN(js, result); 164 165 if (vtype(result) != T_PROMISE || result != req->promise) { 166 generator_settle_request_promise(js, req->promise, result); 167 js_maybe_drain_microtasks_after_async_settle(js); 168 } 169 170 free(req); 171 data = generator_data(gen); 172 if (!data) break; 173 } 174 175 GC_ROOT_RESTORE(js, root_mark); 176} 177 178static coroutine_t *generator_coro(ant_value_t gen) { 179 generator_data_t *data = generator_data(gen); 180 return data ? data->coro : NULL; 181} 182 183static void generator_clear_coro(ant_value_t gen, coroutine_t *coro) { 184 generator_data_t *data = generator_data(gen); 185 if (data && data->coro == coro) data->coro = NULL; 186 if (coro) coroutine_unhold(coro, CORO_HOLD_GENERATOR); 187} 188 189coroutine_t *generator_get_coro_for_gc(ant_value_t gen) { 190 return generator_coro(gen); 191} 192 193void generator_mark_for_gc(ant_t *js, ant_value_t gen) { 194 generator_data_t *data = generator_data(gen); 195 if (!data) return; 196 for (generator_request_t *req = data->queue_head; req; req = req->next) { 197 gc_mark_value(js, req->value); 198 gc_mark_value(js, req->promise); 199 } 200} 201 202static ant_value_t generator_find_owner_in_list(ant_object_t *head, coroutine_t *coro) { 203 for (ant_object_t *obj = head; obj; obj = obj->next) { 204 ant_value_t candidate = js_obj_from_ptr(obj); 205 generator_data_t *data = generator_data(candidate); 206 if (data && data->coro == coro) return candidate; 207 } 208 return js_mkundef(); 209} 210 211static ant_value_t generator_find_owner(ant_t *js, coroutine_t *coro) { 212 ant_value_t gen = generator_find_owner_in_list(js->objects, coro); 213 if (vtype(gen) != T_UNDEF) return gen; 214 gen = generator_find_owner_in_list(js->objects_old, coro); 215 if (vtype(gen) != T_UNDEF) return gen; 216 return generator_find_owner_in_list(js->permanent_objects, coro); 217} 218 219bool generator_resume_pending_request(ant_t *js, coroutine_t *coro, ant_value_t result) { 220 if (!coro || coro->type != CORO_GENERATOR || vtype(coro->async_promise) != T_PROMISE) return false; 221 222 ant_value_t gen = generator_find_owner(js, coro); 223 generator_data_t *data = generator_data(gen); 224 if (!data || !data->is_async) return false; 225 226 GC_ROOT_SAVE(root_mark, js); 227 GC_ROOT_PIN(js, gen); 228 GC_ROOT_PIN(js, result); 229 230 ant_value_t pending = coro->async_promise; 231 GC_ROOT_PIN(js, pending); 232 233 if (is_err(result)) { 234 ant_value_t reject_value = js->thrown_exists ? js->thrown_value : result; 235 js->thrown_exists = false; 236 js->thrown_value = js_mkundef(); 237 238 GC_ROOT_PIN(js, reject_value); 239 coro->async_promise = js_mkundef(); 240 generator_set_state(gen, GEN_COMPLETED); 241 generator_clear_coro(gen, coro); 242 243 js_reject_promise(js, pending, reject_value); 244 js_maybe_drain_microtasks_after_async_settle(js); 245 generator_process_queue(js, gen); 246 GC_ROOT_RESTORE(js, root_mark); 247 248 return true; 249 } 250 251 if (coro->sv_vm && coro->sv_vm->suspended) { 252 if (vtype(coro->awaited_promise) != T_UNDEF) { 253 generator_set_state(gen, GEN_EXECUTING); 254 GC_ROOT_RESTORE(js, root_mark); 255 return true; 256 } 257 258 ant_value_t out = generator_result(js, false, result); 259 GC_ROOT_PIN(js, out); 260 coro->async_promise = js_mkundef(); 261 generator_set_state(gen, GEN_SUSPENDED_YIELD); 262 263 js_resolve_promise(js, pending, out); 264 js_maybe_drain_microtasks_after_async_settle(js); 265 generator_process_queue(js, gen); 266 GC_ROOT_RESTORE(js, root_mark); 267 268 return true; 269 } 270 271 ant_value_t out = generator_result(js, true, result); 272 GC_ROOT_PIN(js, out); 273 coro->async_promise = js_mkundef(); 274 275 generator_set_state(gen, GEN_COMPLETED); 276 generator_clear_coro(gen, coro); 277 278 js_resolve_promise(js, pending, out); 279 js_maybe_drain_microtasks_after_async_settle(js); 280 generator_process_queue(js, gen); 281 GC_ROOT_RESTORE(js, root_mark); 282 283 return true; 284} 285 286static void generator_finalize(ant_t *js, ant_object_t *obj) { 287 ant_value_t gen = js_obj_from_ptr(obj); 288 generator_data_t *data = (generator_data_t *)js_get_native_ptr(gen); 289 290 if (!data) return; 291 if (data->coro) generator_clear_coro(gen, data->coro); 292 293 js_set_native_ptr(gen, NULL); 294 js_set_native_tag(gen, 0); 295 296 generator_free_queue(data); 297 free(data); 298} 299 300static ant_value_t generator_resume_kind( 301 ant_t *js, ant_value_t gen, ant_value_t resume_value, sv_resume_kind_t resume_kind 302) { 303 GC_ROOT_SAVE(root_mark, js); 304 GC_ROOT_PIN(js, gen); 305 GC_ROOT_PIN(js, resume_value); 306 coroutine_t *coro = generator_coro(gen); 307 308 if (!coro || !coro->sv_vm) { 309 generator_set_state(gen, GEN_COMPLETED); 310 if (resume_kind == SV_RESUME_THROW) { 311 GC_ROOT_RESTORE(js, root_mark); 312 return js_throw(js, resume_value); 313 } 314 315 ant_value_t out = generator_result( 316 js, true, resume_kind == SV_RESUME_RETURN 317 ? resume_value : js_mkundef() 318 ); 319 320 GC_ROOT_RESTORE(js, root_mark); 321 return out; 322 } 323 324 generator_state_t state = generator_state(gen); 325 if (state == GEN_EXECUTING) { 326 ant_value_t queued = generator_enqueue_request(js, gen, resume_kind, resume_value); 327 GC_ROOT_RESTORE(js, root_mark); 328 return queued; 329 } 330 331 if (state == GEN_COMPLETED) { 332 if (resume_kind == SV_RESUME_THROW) { 333 GC_ROOT_RESTORE(js, root_mark); 334 return js_throw(js, resume_value); 335 } 336 337 ant_value_t out = generator_result( 338 js, true, resume_kind == SV_RESUME_RETURN 339 ? resume_value : js_mkundef() 340 ); 341 342 GC_ROOT_RESTORE(js, root_mark); 343 return out; 344 } 345 346 if (state == GEN_SUSPENDED_START && resume_kind == SV_RESUME_THROW) { 347 generator_set_state(gen, GEN_COMPLETED); 348 generator_clear_coro(gen, coro); 349 GC_ROOT_RESTORE(js, root_mark); 350 return js_throw(js, resume_value); 351 } 352 353 if (state == GEN_SUSPENDED_START && resume_kind == SV_RESUME_RETURN) { 354 generator_set_state(gen, GEN_COMPLETED); 355 generator_clear_coro(gen, coro); 356 ant_value_t out = generator_result(js, true, resume_value); 357 GC_ROOT_RESTORE(js, root_mark); 358 return out; 359 } 360 361 generator_set_state(gen, GEN_EXECUTING); 362 coroutine_t *saved_active = js->active_async_coro; 363 364 coro->active_parent = saved_active; 365 coro->active_prev = NULL; 366 367 if (saved_active) saved_active->active_prev = coro; 368 js->active_async_coro = coro; 369 coroutine_hold(coro, CORO_HOLD_ACTIVE); 370 371 ant_value_t result; 372 if (state == GEN_SUSPENDED_START) { 373 sv_closure_t *closure = (vtype(coro->async_func) == T_FUNC) ? js_func_closure(coro->async_func) : NULL; 374 if (!closure || !closure->func) result = js_mkerr(js, "invalid generator function"); 375 else result = sv_execute_closure_entry( 376 coro->sv_vm, closure, coro->async_func, 377 coro->super_val, coro->this_val, coro->args, coro->nargs, NULL 378 ); 379 } else { 380 coro->sv_vm->suspended_resume_value = resume_value; 381 coro->sv_vm->suspended_resume_is_error = (resume_kind == SV_RESUME_THROW); 382 coro->sv_vm->suspended_resume_kind = resume_kind; 383 coro->sv_vm->suspended_resume_pending = true; 384 result = sv_resume_suspended(coro->sv_vm); 385 } 386 387 GC_ROOT_PIN(js, result); 388 js->active_async_coro = saved_active; 389 if (saved_active) saved_active->active_prev = NULL; 390 391 coro->active_parent = NULL; 392 coro->active_prev = NULL; 393 coroutine_unhold(coro, CORO_HOLD_ACTIVE); 394 395 if (is_err(result)) { 396 generator_set_state(gen, GEN_COMPLETED); 397 generator_clear_coro(gen, coro); 398 GC_ROOT_RESTORE(js, root_mark); 399 return result; 400 } 401 402 if (coro->sv_vm && coro->sv_vm->suspended) { 403 if (generator_is_async(gen) && vtype(coro->awaited_promise) != T_UNDEF) { 404 generator_set_state(gen, GEN_EXECUTING); 405 if (vtype(coro->async_promise) != T_PROMISE) { 406 coro->async_promise = js_mkpromise(js); 407 GC_ROOT_PIN(js, coro->async_promise); 408 } 409 410 ant_value_t out = coro->async_promise; 411 GC_ROOT_RESTORE(js, root_mark); 412 return out; 413 } 414 415 generator_set_state(gen, GEN_SUSPENDED_YIELD); 416 ant_value_t out = generator_result(js, false, result); 417 GC_ROOT_RESTORE(js, root_mark); 418 419 return out; 420 } 421 422 generator_set_state(gen, GEN_COMPLETED); 423 generator_clear_coro(gen, coro); 424 425 ant_value_t out = generator_result(js, true, result); 426 GC_ROOT_RESTORE(js, root_mark); 427 428 return out; 429} 430 431static ant_value_t generator_resume(ant_t *js, ant_value_t gen, ant_value_t resume_value) { 432 return generator_resume_kind(js, gen, resume_value, SV_RESUME_NEXT); 433} 434 435static ant_value_t generator_next(ant_t *js, ant_value_t *args, int nargs) { 436 ant_value_t gen = js->this_val; 437 if (vtype(gen) != T_GENERATOR) 438 return js_mkerr_typed(js, JS_ERR_TYPE, "Generator.prototype.next called on incompatible receiver"); 439 440 ant_value_t resume_value = nargs > 0 ? args[0] : js_mkundef(); 441 ant_value_t result = generator_resume(js, gen, resume_value); 442 443 if (generator_is_async(gen) && vtype(result) == T_PROMISE) return result; 444 return generator_is_async(gen) ? generator_async_wrap_result(js, result) : result; 445} 446 447static ant_value_t generator_return(ant_t *js, ant_value_t *args, int nargs) { 448 ant_value_t gen = js->this_val; 449 if (vtype(gen) != T_GENERATOR) 450 return js_mkerr_typed(js, JS_ERR_TYPE, "Generator.prototype.return called on incompatible receiver"); 451 452 generator_state_t state = generator_state(gen); 453 if (state == GEN_EXECUTING && !generator_is_async(gen)) 454 return js_mkerr_typed(js, JS_ERR_TYPE, "Generator is already executing"); 455 456 ant_value_t value = nargs > 0 ? args[0] : js_mkundef(); 457 ant_value_t result = generator_resume_kind(js, gen, value, SV_RESUME_RETURN); 458 459 if (generator_is_async(gen) && vtype(result) == T_PROMISE) return result; 460 return generator_is_async(gen) ? generator_async_wrap_result(js, result) : result; 461} 462 463static ant_value_t generator_throw(ant_t *js, ant_value_t *args, int nargs) { 464 ant_value_t gen = js->this_val; 465 if (vtype(gen) != T_GENERATOR) 466 return js_mkerr_typed(js, JS_ERR_TYPE, "Generator.prototype.throw called on incompatible receiver"); 467 468 generator_state_t state = generator_state(gen); 469 if (state == GEN_EXECUTING && !generator_is_async(gen)) 470 return js_mkerr_typed(js, JS_ERR_TYPE, "Generator is already executing"); 471 472 ant_value_t value = nargs > 0 ? args[0] : js_mkundef(); 473 ant_value_t result = generator_resume_kind(js, gen, value, SV_RESUME_THROW); 474 475 if (generator_is_async(gen) && vtype(result) == T_PROMISE) return result; 476 return generator_is_async(gen) ? generator_async_wrap_result(js, result) : result; 477} 478 479static ant_value_t generator_async_dispose(ant_t *js, ant_value_t *args, int nargs) { 480 ant_value_t gen = js->this_val; 481 if (vtype(gen) != T_GENERATOR || !generator_is_async(gen)) return js_mkerr_typed( 482 js, JS_ERR_TYPE, 483 "AsyncGenerator.prototype[Symbol.asyncDispose] called on incompatible receiver" 484 ); 485 486 return generator_return(js, NULL, 0); 487} 488 489void init_generator_module(void) { 490 ant_t *js = rt->js; 491 ant_value_t proto = js_mkobj(js); 492 493 js->sym.generator_proto = proto; 494 js_set_proto_init(proto, js->sym.iterator_proto); 495 496 js_set(js, proto, "next", js_mkfun(generator_next)); 497 js_set(js, proto, "return", js_mkfun(generator_return)); 498 js_set(js, proto, "throw", js_mkfun(generator_throw)); 499 js_set_sym(js, proto, get_toStringTag_sym(), js_mkstr(js, "Generator", 9)); 500 501 ant_value_t async_proto = js_mkobj(js); 502 js->sym.async_generator_proto = async_proto; 503 js_set_proto_init(async_proto, js->sym.async_iterator_proto); 504 js_set(js, async_proto, "next", js_mkfun(generator_next)); 505 js_set(js, async_proto, "return", js_mkfun(generator_return)); 506 js_set(js, async_proto, "throw", js_mkfun(generator_throw)); 507 js_set_sym(js, async_proto, get_toStringTag_sym(), js_mkstr(js, "AsyncGenerator", 14)); 508 js_set_sym(js, async_proto, get_asyncDispose_sym(), js_mkfun(generator_async_dispose)); 509 510 ant_value_t async_generator_func_proto = js_get_slot(js_glob(js), SLOT_ASYNC_GENERATOR_PROTO); 511 if (is_object_type(async_generator_func_proto)) { 512 js_set(js, async_generator_func_proto, "prototype", async_proto); 513 js_set_descriptor(js, js_as_obj(async_generator_func_proto), "prototype", 9, JS_DESC_C); 514 js_set(js, async_proto, "constructor", async_generator_func_proto); 515 js_set_descriptor(js, async_proto, "constructor", 11, JS_DESC_C); 516 } 517 518 init_async_iterator_helpers(); 519} 520 521ant_value_t sv_call_generator_closure_dispatch( 522 sv_vm_t *caller_vm, ant_t *js, sv_closure_t *closure, 523 ant_value_t callee_func, ant_value_t super_val, 524 ant_value_t this_val, ant_value_t *args, int argc 525) { 526 if (!closure || !closure->func) return js_mkerr(js, "invalid generator function"); 527 528 sv_vm_t *gen_vm = sv_vm_create(js, SV_VM_ASYNC); 529 if (!gen_vm) return js_mkerr(js, "out of memory for generator VM"); 530 531 coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 532 if (!coro) { 533 sv_vm_destroy(gen_vm); 534 return js_mkerr(js, "out of memory for generator"); 535 } 536 537 ant_value_t *copied_args = NULL; 538 if (argc > 0 && args) { 539 copied_args = (ant_value_t *)CORO_MALLOC(sizeof(ant_value_t) * (size_t)argc); 540 if (!copied_args) { 541 sv_vm_destroy(gen_vm); 542 CORO_FREE(coro); 543 return js_mkerr(js, "out of memory for generator args"); 544 } 545 memcpy(copied_args, args, sizeof(ant_value_t) * (size_t)argc); 546 } 547 548 ant_value_t gen = js_mkgenerator(js); 549 if (is_err(gen)) { 550 if (copied_args) CORO_FREE(copied_args); 551 sv_vm_destroy(gen_vm); 552 CORO_FREE(coro); 553 return gen; 554 } 555 556 generator_data_t *data = (generator_data_t *)calloc(1, sizeof(*data)); 557 if (!data) { 558 if (copied_args) CORO_FREE(copied_args); 559 sv_vm_destroy(gen_vm); 560 CORO_FREE(coro); 561 return js_mkerr(js, "out of memory for generator data"); 562 } 563 564 *coro = (coroutine_t){ 565 .js = js, 566 .type = CORO_GENERATOR, 567 .this_val = this_val, 568 .super_val = super_val, 569 .new_target = js->new_target, 570 .awaited_promise = js_mkundef(), 571 .result = js_mkundef(), 572 .async_func = callee_func, 573 .args = copied_args, 574 .nargs = argc, 575 .active_parent = NULL, 576 .is_settled = false, 577 .is_error = false, 578 .is_done = false, 579 .resume_point = 0, 580 .yield_value = js_mkundef(), 581 .async_promise = js_mkundef(), 582 .next = NULL, 583 .mco = NULL, 584 .owner_vm = gen_vm, 585 .sv_vm = gen_vm, 586 .mco_started = false, 587 .is_ready = false, 588 .did_suspend = false, 589 .refcount = 1, 590 .hold_bits = 0, 591 .await_registered = false, 592 .destroy_requested = false, 593 }; 594 595 *data = (generator_data_t){ 596 .coro = coro, 597 .state = GEN_SUSPENDED_START, 598 .is_async = closure->func->is_async, 599 }; 600 coroutine_hold(coro, CORO_HOLD_GENERATOR); 601 coroutine_release(coro); 602 603 js_set_native_ptr(gen, data); 604 js_set_native_tag(gen, GENERATOR_NATIVE_TAG); 605 js_set_finalizer(gen, generator_finalize); 606 607 ant_value_t instance_proto = js_get(js, callee_func, "prototype"); 608 if (is_object_type(instance_proto)) js_set_proto_wb(js, gen, instance_proto); 609 else if (data->is_async && is_object_type(js->sym.async_generator_proto)) 610 js_set_proto_wb(js, gen, js->sym.async_generator_proto); 611 612 return gen; 613}