AsyncIteration.cpp (64551B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "vm/AsyncIteration.h" 8 9 #include "builtin/Promise.h" // js::PromiseHandler, js::CreatePromiseObjectForAsyncGenerator, js::AsyncFromSyncIteratorMethod, js::ResolvePromiseInternal, js::RejectPromiseInternal, js::InternalAsyncGeneratorAwait 10 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 11 #include "js/PropertySpec.h" 12 #include "vm/CompletionKind.h" 13 #include "vm/FunctionFlags.h" // js::FunctionFlags 14 #include "vm/GeneratorObject.h" 15 #include "vm/GlobalObject.h" 16 #include "vm/Interpreter.h" 17 #include "vm/PlainObject.h" // js::PlainObject 18 #include "vm/PromiseObject.h" // js::PromiseObject 19 #include "vm/Realm.h" 20 #include "vm/SelfHosting.h" 21 22 #include "vm/JSObject-inl.h" 23 #include "vm/List-inl.h" 24 25 using namespace js; 26 27 // --------------- 28 // Async generator 29 // --------------- 30 31 const JSClass AsyncGeneratorObject::class_ = { 32 "AsyncGenerator", 33 JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorObject::Slots), 34 &classOps_, 35 }; 36 37 const JSClassOps AsyncGeneratorObject::classOps_ = { 38 nullptr, // addProperty 39 nullptr, // delProperty 40 nullptr, // enumerate 41 nullptr, // newEnumerate 42 nullptr, // resolve 43 nullptr, // mayResolve 44 nullptr, // finalize 45 nullptr, // call 46 nullptr, // construct 47 CallTraceMethod<AbstractGeneratorObject>, // trace 48 }; 49 50 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 51 // 52 // OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto 53 // [ , internalSlotsList ] ) 54 // https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor 55 // 56 // specialized for AsyncGeneratorObjects. 57 static AsyncGeneratorObject* OrdinaryCreateFromConstructorAsynGen( 58 JSContext* cx, HandleFunction constructor) { 59 // Step 1. Assert: intrinsicDefaultProto is this specification's name of an 60 // intrinsic object. The corresponding object must be an intrinsic 61 // that is intended to be used as the [[Prototype]] value of an 62 // object. 63 // (implicit) 64 65 // Step 2. Let proto be 66 // ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). 67 RootedValue protoVal(cx); 68 if (!GetProperty(cx, constructor, constructor, cx->names().prototype, 69 &protoVal)) { 70 return nullptr; 71 } 72 73 RootedObject proto(cx, protoVal.isObject() ? &protoVal.toObject() : nullptr); 74 if (!proto) { 75 proto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, cx->global()); 76 if (!proto) { 77 return nullptr; 78 } 79 } 80 81 // Step 3. If internalSlotsList is present, let slotsList be 82 // internalSlotsList. 83 // Step 4. Else, let slotsList be a new empty List. 84 // Step 5. Return OrdinaryObjectCreate(proto, slotsList). 85 return NewObjectWithGivenProto<AsyncGeneratorObject>(cx, proto); 86 } 87 88 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 89 // 90 // EvaluateAsyncGeneratorBody 91 // https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncgeneratorbody 92 // 93 // Steps 4-5. 94 // 95 // AsyncGeneratorStart ( generator, generatorBody ) 96 // https://tc39.es/ecma262/#sec-asyncgeneratorstart 97 // 98 // Steps 1, 7. 99 /* static */ 100 AsyncGeneratorObject* AsyncGeneratorObject::create(JSContext* cx, 101 HandleFunction asyncGen) { 102 MOZ_ASSERT(asyncGen->isAsync() && asyncGen->isGenerator()); 103 104 AsyncGeneratorObject* generator = 105 OrdinaryCreateFromConstructorAsynGen(cx, asyncGen); 106 if (!generator) { 107 return nullptr; 108 } 109 110 // EvaluateAsyncGeneratorBody 111 // Step 4. Set generator.[[AsyncGeneratorState]] to suspended-start. 112 generator->setSuspendedStart(); 113 114 // Step 5. Perform AsyncGeneratorStart(generator, FunctionBody). 115 116 // AsyncGeneratorStart 117 // Step 1. Assert: generator.[[AsyncGeneratorState]] is suspended-start. 118 119 // Step 7. Set generator.[[AsyncGeneratorQueue]] to a new empty List. 120 generator->clearSingleQueueRequest(); 121 122 generator->clearCachedRequest(); 123 124 return generator; 125 } 126 127 /* static */ 128 AsyncGeneratorRequest* AsyncGeneratorObject::createRequest( 129 JSContext* cx, Handle<AsyncGeneratorObject*> generator, 130 CompletionKind completionKind, HandleValue completionValue, 131 Handle<PromiseObject*> promise) { 132 if (!generator->hasCachedRequest()) { 133 return AsyncGeneratorRequest::create(cx, completionKind, completionValue, 134 promise); 135 } 136 137 AsyncGeneratorRequest* request = generator->takeCachedRequest(); 138 request->init(completionKind, completionValue, promise); 139 return request; 140 } 141 142 /* static */ [[nodiscard]] bool AsyncGeneratorObject::enqueueRequest( 143 JSContext* cx, Handle<AsyncGeneratorObject*> generator, 144 Handle<AsyncGeneratorRequest*> request) { 145 if (generator->isSingleQueue()) { 146 if (generator->isSingleQueueEmpty()) { 147 generator->setSingleQueueRequest(request); 148 return true; 149 } 150 151 ListObject* queue = ListObject::create(cx); 152 if (!queue) { 153 return false; 154 } 155 156 if (!queue->append(cx, ObjectValue(*generator->singleQueueRequest()))) { 157 return false; 158 } 159 if (!queue->append(cx, ObjectValue(*request))) { 160 return false; 161 } 162 163 generator->setQueue(queue); 164 return true; 165 } 166 167 return generator->queue()->append(cx, ObjectValue(*request)); 168 } 169 170 /* static */ 171 AsyncGeneratorRequest* AsyncGeneratorObject::dequeueRequest( 172 JSContext* cx, Handle<AsyncGeneratorObject*> generator) { 173 if (generator->isSingleQueue()) { 174 AsyncGeneratorRequest* request = generator->singleQueueRequest(); 175 generator->clearSingleQueueRequest(); 176 return request; 177 } 178 179 Rooted<ListObject*> queue(cx, generator->queue()); 180 return &queue->popFirstAs<AsyncGeneratorRequest>(cx); 181 } 182 183 /* static */ 184 AsyncGeneratorRequest* AsyncGeneratorObject::peekRequest( 185 Handle<AsyncGeneratorObject*> generator) { 186 if (generator->isSingleQueue()) { 187 return generator->singleQueueRequest(); 188 } 189 190 return &generator->queue()->getAs<AsyncGeneratorRequest>(0); 191 } 192 193 const JSClass AsyncGeneratorRequest::class_ = { 194 "AsyncGeneratorRequest", 195 JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots), 196 }; 197 198 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 199 // 200 // AsyncGeneratorRequest Records 201 // https://tc39.es/ecma262/#sec-asyncgeneratorrequest-records 202 /* static */ 203 AsyncGeneratorRequest* AsyncGeneratorRequest::create( 204 JSContext* cx, CompletionKind completionKind, HandleValue completionValue, 205 Handle<PromiseObject*> promise) { 206 AsyncGeneratorRequest* request = 207 NewObjectWithGivenProto<AsyncGeneratorRequest>(cx, nullptr); 208 if (!request) { 209 return nullptr; 210 } 211 212 request->init(completionKind, completionValue, promise); 213 return request; 214 } 215 216 [[nodiscard]] static bool AsyncGeneratorResume( 217 JSContext* cx, Handle<AsyncGeneratorObject*> generator, 218 CompletionKind completionKind, HandleValue argument); 219 220 [[nodiscard]] static bool AsyncGeneratorDrainQueue( 221 JSContext* cx, Handle<AsyncGeneratorObject*> generator); 222 223 [[nodiscard]] static bool AsyncGeneratorCompleteStepNormal( 224 JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value, 225 bool done); 226 227 [[nodiscard]] static bool AsyncGeneratorCompleteStepThrow( 228 JSContext* cx, Handle<AsyncGeneratorObject*> generator, 229 HandleValue exception); 230 231 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 232 // 233 // AsyncGeneratorStart ( generator, generatorBody ) 234 // https://tc39.es/ecma262/#sec-asyncgeneratorstart 235 // 236 // Steps 4.g-l. "return" case. 237 [[nodiscard]] static bool AsyncGeneratorReturned( 238 JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) { 239 // Step 4.g. Set acGenerator.[[AsyncGeneratorState]] to draining-queue. 240 generator->setDrainingQueue(); 241 242 // Step 4.i. If result is a return completion, set result to 243 // NormalCompletion(result.[[Value]]). 244 // (implicit) 245 246 // Step 4.j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). 247 if (!AsyncGeneratorCompleteStepNormal(cx, generator, value, true)) { 248 return false; 249 } 250 251 MOZ_ASSERT(!generator->isExecuting()); 252 MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn()); 253 if (generator->isDrainingQueue_AwaitingReturn()) { 254 return true; 255 } 256 257 // Step 4.k. Perform AsyncGeneratorDrainQueue(acGenerator). 258 // Step 4.l. Return undefined. 259 return AsyncGeneratorDrainQueue(cx, generator); 260 } 261 262 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 263 // 264 // AsyncGeneratorStart ( generator, generatorBody ) 265 // https://tc39.es/ecma262/#sec-asyncgeneratorstart 266 // 267 // Steps 4.g-l. "throw" case. 268 [[nodiscard]] static bool AsyncGeneratorThrown( 269 JSContext* cx, Handle<AsyncGeneratorObject*> generator) { 270 // Step 4.g. Set acGenerator.[[AsyncGeneratorState]] to draining-queue. 271 generator->setDrainingQueue(); 272 273 // Not much we can do about uncatchable exceptions, so just bail. 274 if (!cx->isExceptionPending()) { 275 return false; 276 } 277 278 // Step 4.j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). 279 RootedValue value(cx); 280 if (!GetAndClearException(cx, &value)) { 281 return false; 282 } 283 if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) { 284 return false; 285 } 286 287 MOZ_ASSERT(!generator->isExecuting()); 288 MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn()); 289 if (generator->isDrainingQueue_AwaitingReturn()) { 290 return true; 291 } 292 293 // Step 4.k. Perform AsyncGeneratorDrainQueue(acGenerator). 294 // Step 4.l. Return undefined. 295 return AsyncGeneratorDrainQueue(cx, generator); 296 } 297 298 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 299 // 300 // AsyncGeneratorUnwrapYieldResumption ( resumptionValue ) 301 // https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption 302 // 303 // Steps 4-5. 304 [[nodiscard]] static bool AsyncGeneratorYieldReturnAwaitedFulfilled( 305 JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) { 306 MOZ_ASSERT(generator->isExecuting_AwaitingYieldReturn(), 307 "YieldReturn-Await fulfilled when not in " 308 "'AwaitingYieldReturn' state"); 309 310 // Step 4. Assert: awaited is a normal completion. 311 // Step 5. Return ReturnCompletion(awaited.[[Value]]). 312 return AsyncGeneratorResume(cx, generator, CompletionKind::Return, value); 313 } 314 315 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 316 // 317 // AsyncGeneratorUnwrapYieldResumption ( resumptionValue ) 318 // https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption 319 // 320 // Step 3. 321 [[nodiscard]] static bool AsyncGeneratorYieldReturnAwaitedRejected( 322 JSContext* cx, Handle<AsyncGeneratorObject*> generator, 323 HandleValue reason) { 324 MOZ_ASSERT( 325 generator->isExecuting_AwaitingYieldReturn(), 326 "YieldReturn-Await rejected when not in 'AwaitingYieldReturn' state"); 327 328 // Step 3. If awaited is a throw completion, return ? awaited. 329 return AsyncGeneratorResume(cx, generator, CompletionKind::Throw, reason); 330 } 331 332 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 333 // 334 // AsyncGeneratorUnwrapYieldResumption ( resumptionValue ) 335 // https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption 336 // 337 // Step 2. 338 [[nodiscard]] static bool AsyncGeneratorUnwrapYieldResumptionWithReturn( 339 JSContext* cx, Handle<AsyncGeneratorObject*> generator, 340 JS::Handle<JS::Value> value) { 341 // Step 2. Let awaited be Completion(Await(resumptionValue.[[Value]])). 342 // 343 // NOTE: Given that Await needs to be performed asynchronously, 344 // we use an implementation-defined state "AwaitingYieldReturn" 345 // to wait for the result. 346 generator->setExecuting_AwaitingYieldReturn(); 347 348 return InternalAsyncGeneratorAwait( 349 cx, generator, value, 350 PromiseHandler::AsyncGeneratorYieldReturnAwaitedFulfilled, 351 PromiseHandler::AsyncGeneratorYieldReturnAwaitedRejected); 352 } 353 354 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 355 // 356 // AsyncGeneratorYield ( value ) 357 // https://tc39.es/ecma262/#sec-asyncgeneratoryield 358 // 359 // Stesp 9-12. 360 // 361 // AsyncGeneratorUnwrapYieldResumption ( resumptionValue ) 362 // https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption 363 // 364 // Step 1. 365 [[nodiscard]] static bool AsyncGeneratorYield( 366 JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value, 367 bool* resumeAgain, CompletionKind* resumeCompletionKind, 368 JS::MutableHandle<JS::Value> resumeValue) { 369 *resumeAgain = false; 370 371 // Step 9. Perform 372 // ! AsyncGeneratorCompleteStep(generator, completion, false, 373 // previousRealm). 374 if (!AsyncGeneratorCompleteStepNormal(cx, generator, value, false)) { 375 return false; 376 } 377 378 MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn()); 379 // NOTE: This transition doesn't basically happen, but could happen if 380 // Debugger API is used, or the job queue is forcibly drained. 381 if (generator->isDrainingQueue_AwaitingReturn()) { 382 return true; 383 } 384 385 // Step 10. Let queue be generator.[[AsyncGeneratorQueue]]. 386 // Step 11. If queue is not empty, then 387 if (!generator->isQueueEmpty()) { 388 // Step 11.a. NOTE: Execution continues without suspending the generator. 389 // Step 11.b. Let toYield be the first element of queue. 390 Rooted<AsyncGeneratorRequest*> toYield( 391 cx, AsyncGeneratorObject::peekRequest(generator)); 392 if (!toYield) { 393 return false; 394 } 395 396 CompletionKind completionKind = toYield->completionKind(); 397 398 // Step 11.c. Let resumptionValue be Completion(toYield.[[Completion]]). 399 RootedValue completionValue(cx, toYield->completionValue()); 400 401 // Step 11.d. Return ? 402 // AsyncGeneratorUnwrapYieldResumption(resumptionValue). 403 // 404 // AsyncGeneratorUnwrapYieldResumption 405 // Step 1. If resumptionValue is not a return completion, return ? 406 // resumptionValue. 407 if (completionKind != CompletionKind::Return) { 408 *resumeAgain = true; 409 *resumeCompletionKind = completionKind; 410 resumeValue.set(completionValue); 411 return true; 412 } 413 414 // Step 2. 415 return AsyncGeneratorUnwrapYieldResumptionWithReturn(cx, generator, 416 completionValue); 417 } 418 419 // Step 12. Else, 420 // Step 12.a. Set generator.[[AsyncGeneratorState]] to suspended-yield. 421 generator->setSuspendedYield(); 422 423 // Step 12.b. Remove genContext from the execution context stack and 424 // restore the execution context that is at the top of the 425 // execution context stack as the running execution context. 426 // Step 12.c. Let callerContext be the running execution context. 427 // Step 12.d. Resume callerContext passing undefined. If genContext is ever 428 // resumed again, let resumptionValue be the Completion Record with 429 // which it is resumed. 430 // (done as part of bytecode) 431 432 // Step 12.e. Assert: If control reaches here, then genContext is the 433 // running execution context again. 434 // Step 12.f. Return ? 435 // AsyncGeneratorUnwrapYieldResumption(resumptionValue). 436 // (done in AsyncGeneratorResume on the next resume) 437 438 return true; 439 } 440 441 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 442 // 443 // Await in async function 444 // https://tc39.es/ecma262/#await 445 // 446 // Steps 3.c-f. 447 [[nodiscard]] static bool AsyncGeneratorAwaitedFulfilled( 448 JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) { 449 MOZ_ASSERT(generator->isExecuting(), 450 "Await fulfilled when not in 'Executing' state"); 451 452 // Step 3.c. Push asyncContext onto the execution context stack; asyncContext 453 // is now the running execution context. 454 // Step 3.d. Resume the suspended evaluation of asyncContext using 455 // NormalCompletion(v) as the result of the operation that 456 // suspended it. 457 // Step 3.f. Return undefined. 458 return AsyncGeneratorResume(cx, generator, CompletionKind::Normal, value); 459 } 460 461 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 462 // 463 // Await in async function 464 // https://tc39.es/ecma262/#await 465 // 466 // Steps 5.c-f. 467 [[nodiscard]] static bool AsyncGeneratorAwaitedRejected( 468 JSContext* cx, Handle<AsyncGeneratorObject*> generator, 469 HandleValue reason) { 470 MOZ_ASSERT(generator->isExecuting(), 471 "Await rejected when not in 'Executing' state"); 472 473 // Step 5.c. Push asyncContext onto the execution context stack; asyncContext 474 // is now the running execution context. 475 // Step 5.d. Resume the suspended evaluation of asyncContext using 476 // ThrowCompletion(reason) as the result of the operation that 477 // suspended it. 478 // Step 5.f. Return undefined. 479 return AsyncGeneratorResume(cx, generator, CompletionKind::Throw, reason); 480 } 481 482 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 483 // 484 // Await in async function 485 // https://tc39.es/ecma262/#await 486 [[nodiscard]] static bool AsyncGeneratorAwait( 487 JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) { 488 return InternalAsyncGeneratorAwait( 489 cx, generator, value, PromiseHandler::AsyncGeneratorAwaitedFulfilled, 490 PromiseHandler::AsyncGeneratorAwaitedRejected); 491 } 492 493 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 494 // 495 // AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] ) 496 // https://tc39.es/ecma262/#sec-asyncgeneratorcompletestep 497 // 498 // "normal" case. 499 [[nodiscard]] static bool AsyncGeneratorCompleteStepNormal( 500 JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value, 501 bool done) { 502 // Step 1. Assert: generator.[[AsyncGeneratorQueue]] is not empty. 503 MOZ_ASSERT(!generator->isQueueEmpty()); 504 505 // Step 2. Let next be the first element of generator.[[AsyncGeneratorQueue]]. 506 // Step 3. Remove the first element from generator.[[AsyncGeneratorQueue]]. 507 AsyncGeneratorRequest* next = 508 AsyncGeneratorObject::dequeueRequest(cx, generator); 509 if (!next) { 510 return false; 511 } 512 513 // Step 4. Let promiseCapability be next.[[Capability]]. 514 Rooted<PromiseObject*> resultPromise(cx, next->promise()); 515 516 generator->cacheRequest(next); 517 518 // Step 5. Let value be completion.[[Value]]. 519 // (passed by caller) 520 521 // Step 6. If completion is a throw completion, then 522 // (done in AsyncGeneratorCompleteStepThrow) 523 524 // Step 7. Else, 525 // Step 7.a. Assert: completion is a normal completion. 526 527 // Step 7.b. If realm is present, then 528 // (skipped) 529 530 // Step 7.c. Else, 531 // Step 7.c.i. Let iteratorResult be CreateIteratorResultObject(value, 532 // done). 533 JSObject* resultObj = CreateIterResultObject(cx, value, done); 534 if (!resultObj) { 535 return false; 536 } 537 538 // Step 7.d. Perform 539 // ! Call(promiseCapability.[[Resolve]], undefined, 540 // « iteratorResult »). 541 // Step 8. Return unused. 542 RootedValue resultValue(cx, ObjectValue(*resultObj)); 543 return ResolvePromiseInternal(cx, resultPromise, resultValue); 544 } 545 546 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 547 // 548 // AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] ) 549 // https://tc39.es/ecma262/#sec-asyncgeneratorcompletestep 550 // 551 // "throw" case. 552 [[nodiscard]] static bool AsyncGeneratorCompleteStepThrow( 553 JSContext* cx, Handle<AsyncGeneratorObject*> generator, 554 HandleValue exception) { 555 // Step 1. Assert: generator.[[AsyncGeneratorQueue]] is not empty. 556 MOZ_ASSERT(!generator->isQueueEmpty()); 557 558 // Step 2. Let next be the first element of generator.[[AsyncGeneratorQueue]]. 559 // Step 3. Remove the first element from generator.[[AsyncGeneratorQueue]]. 560 AsyncGeneratorRequest* next = 561 AsyncGeneratorObject::dequeueRequest(cx, generator); 562 if (!next) { 563 return false; 564 } 565 566 // Step 4. Let promiseCapability be next.[[Capability]]. 567 Rooted<PromiseObject*> resultPromise(cx, next->promise()); 568 569 generator->cacheRequest(next); 570 571 // Step 5. Let value be completion.[[Value]]. 572 // (passed by caller) 573 574 // Step 6. If completion is a throw completion, then 575 // Step 6.a. Perform 576 // ! Call(promiseCapability.[[Reject]], undefined, « value »). 577 // Step 8. Return unused. 578 return RejectPromiseInternal(cx, resultPromise, exception); 579 } 580 581 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 582 // 583 // AsyncGeneratorAwaitReturn ( generator ) 584 // https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn 585 // 586 // Steps 11.a-e. 587 [[nodiscard]] static bool AsyncGeneratorAwaitReturnFulfilled( 588 JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) { 589 // Step 11.a. Assert: generator.[[AsyncGeneratorState]] is draining-queue. 590 // 591 // NOTE: We use the implementation-defined state DrainingQueue_AwaitingReturn 592 // for the Await during draining-queue, and it's set back to the 593 // original draining-queue when the await operation finishes. 594 MOZ_ASSERT(generator->isDrainingQueue_AwaitingReturn()); 595 generator->setDrainingQueue(); 596 597 // Step 11.b. Let result be NormalCompletion(value). 598 // Step 11.c. Perform AsyncGeneratorCompleteStep(generator, result, true). 599 if (!AsyncGeneratorCompleteStepNormal(cx, generator, value, true)) { 600 return false; 601 } 602 603 MOZ_ASSERT(!generator->isExecuting()); 604 MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn()); 605 if (generator->isDrainingQueue_AwaitingReturn()) { 606 return true; 607 } 608 609 // Step 11.d. Perform AsyncGeneratorDrainQueue(generator). 610 // Step 11.e. Return undefined. 611 return AsyncGeneratorDrainQueue(cx, generator); 612 } 613 614 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 615 // 616 // AsyncGeneratorAwaitReturn ( generator ) 617 // https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn 618 // 619 // Steps 13.a-e. 620 [[nodiscard]] static bool AsyncGeneratorAwaitReturnRejected( 621 JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) { 622 // Step 13.a. Assert: generator.[[AsyncGeneratorState]] is draining-queue. 623 // 624 // See the comment for AsyncGeneratorAwaitReturnFulfilled. 625 MOZ_ASSERT(generator->isDrainingQueue_AwaitingReturn()); 626 generator->setDrainingQueue(); 627 628 // Step 13.b. Let result be ThrowCompletion(reason). 629 // Step 13.c. Perform AsyncGeneratorCompleteStep(generator, result, true). 630 if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) { 631 return false; 632 } 633 634 MOZ_ASSERT(!generator->isExecuting()); 635 MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn()); 636 if (generator->isDrainingQueue_AwaitingReturn()) { 637 return true; 638 } 639 640 // Step 13.d. Perform AsyncGeneratorDrainQueue(generator). 641 // Step 13.e. Return undefined. 642 return AsyncGeneratorDrainQueue(cx, generator); 643 } 644 645 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 646 // 647 // AsyncGeneratorAwaitReturn ( generator ) 648 // https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn 649 [[nodiscard]] static bool AsyncGeneratorAwaitReturn( 650 JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue next) { 651 // Step 1. Assert: generator.[[AsyncGeneratorState]] is draining-queue. 652 MOZ_ASSERT(generator->isDrainingQueue()); 653 generator->setDrainingQueue_AwaitingReturn(); 654 655 // Step 2. Let queue be generator.[[AsyncGeneratorQueue]]. 656 // Step 3. Assert: queue is not empty. 657 MOZ_ASSERT(!generator->isQueueEmpty()); 658 659 // Step 4. Let next be the first element of queue. 660 // (passed by caller) 661 662 // Step 5. Let completion be Completion(next.[[Completion]]). 663 // Step 6. Assert: completion is a return completion. 664 // (implicit) 665 666 // Step 7. Let promiseCompletion be Completion(PromiseResolve(%Promise%, 667 // completion.[[Value]])). 668 669 // Step 9. Assert: promiseCompletion is a normal completion. 670 // Step 10. Let promise be promiseCompletion.[[Value]]. 671 // Step 11. Let fulfilledClosure be a new Abstract Closure with parameters 672 // (value) that captures generator and performs the following steps 673 // when called: 674 // Step 12. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, 675 // "", « »). 676 // Step 13. Let rejectedClosure be a new Abstract Closure with parameters 677 // (reason) that captures generator and performs the following steps 678 // when called: 679 // Step 14. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", 680 // « »). 681 // Step 15. Perform PerformPromiseThen(promise, onFulfilled, onRejected). 682 // Step 16. Return unused. 683 if (!InternalAsyncGeneratorAwait( 684 cx, generator, next, 685 PromiseHandler::AsyncGeneratorAwaitReturnFulfilled, 686 PromiseHandler::AsyncGeneratorAwaitReturnRejected)) { 687 // This branch can be taken with one of the following: 688 // * (a) abrupt completion in PromiseResolve at step 7, such as 689 // getting `compeltion.[[Value]].constructor` property throws 690 // * (b) OOM in PromiseResolve 691 // * (c) OOM in PerformPromiseThen 692 // 693 // (c) happens after step 8, but OOM is an implementation details and 694 // we can treat the OOM as if it happened during PromiseResolve, 695 // and thus performing the step 8 here is okay. 696 // 697 // Step 8. If promiseCompletion is an abrupt completion, then 698 699 // Not much we can do about uncatchable exceptions, so just bail. 700 if (!cx->isExceptionPending()) { 701 return false; 702 } 703 704 RootedValue value(cx); 705 if (!GetAndClearException(cx, &value)) { 706 return false; 707 } 708 709 // Step 8.a. Perform AsyncGeneratorCompleteStep(generator, 710 // promiseCompletion, true). 711 if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) { 712 return false; 713 } 714 715 MOZ_ASSERT(!generator->isExecuting()); 716 MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn()); 717 if (generator->isDrainingQueue_AwaitingReturn()) { 718 return true; 719 } 720 721 // Step 8.b. Perform AsyncGeneratorDrainQueue(generator). 722 // Step 8.c. Return unused. 723 return AsyncGeneratorDrainQueue(cx, generator); 724 } 725 726 return true; 727 } 728 729 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 730 // 731 // AsyncGeneratorDrainQueue ( generator ) 732 // https://tc39.es/ecma262/#sec-asyncgeneratordrainqueue 733 [[nodiscard]] static bool AsyncGeneratorDrainQueue( 734 JSContext* cx, Handle<AsyncGeneratorObject*> generator) { 735 // Step 1. Assert: generator.[[AsyncGeneratorState]] is draining-queue. 736 // 737 // NOTE: DrainingQueue_AwaitingReturn shouldn't reach here. 738 MOZ_ASSERT(generator->isDrainingQueue()); 739 740 // Step 2. Let queue be generator.[[AsyncGeneratorQueue]]. 741 // Step 3. Repeat, while queue is not empty, 742 while (!generator->isQueueEmpty()) { 743 // Step 3.a. Let next be the first element of queue. 744 Rooted<AsyncGeneratorRequest*> next( 745 cx, AsyncGeneratorObject::peekRequest(generator)); 746 if (!next) { 747 return false; 748 } 749 750 // Step 3.b. Let completion be Completion(next.[[Completion]]). 751 CompletionKind completionKind = next->completionKind(); 752 753 // Step 3.c. If completion is a return completion, then 754 if (completionKind == CompletionKind::Return) { 755 RootedValue value(cx, next->completionValue()); 756 757 // Step 3.c.i. Perform AsyncGeneratorAwaitReturn(generator). 758 // Step 3.c.ii. Return unused. 759 return AsyncGeneratorAwaitReturn(cx, generator, value); 760 } 761 762 // Step 3.d. Else, 763 if (completionKind == CompletionKind::Throw) { 764 RootedValue value(cx, next->completionValue()); 765 766 // Step 3.d.ii. Perform AsyncGeneratorCompleteStep(generator, completion, 767 // true). 768 if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) { 769 return false; 770 } 771 } else { 772 // Step 3.d.i. If completion is a normal completion, then 773 // Step 3.d.i.1. Set completion to NormalCompletion(undefined). 774 // Step 3.d.ii. Perform AsyncGeneratorCompleteStep(generator, completion, 775 // true). 776 if (!AsyncGeneratorCompleteStepNormal(cx, generator, UndefinedHandleValue, 777 true)) { 778 return false; 779 } 780 } 781 782 MOZ_ASSERT(!generator->isExecuting()); 783 MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn()); 784 if (generator->isDrainingQueue_AwaitingReturn()) { 785 return true; 786 } 787 } 788 789 // Step 4. Set generator.[[AsyncGeneratorState]] to completed. 790 generator->setCompleted(); 791 792 // Step 5. Return unused. 793 return true; 794 } 795 796 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 797 // 798 // AsyncGeneratorValidate ( generator, generatorBrand ) 799 // https://tc39.es/ecma262/#sec-asyncgeneratorvalidate 800 // 801 // Testing part. 802 [[nodiscard]] static bool IsAsyncGeneratorValid(HandleValue asyncGenVal) { 803 // Step 1. Perform 804 // ? RequireInternalSlot(generator, [[AsyncGeneratorContext]]). 805 // Step 2. Perform 806 // ? RequireInternalSlot(generator, [[AsyncGeneratorState]]). 807 // Step 3. Perform 808 // ? RequireInternalSlot(generator, [[AsyncGeneratorQueue]]). 809 // Step 4. If generator.[[GeneratorBrand]] is not generatorBrand, throw a 810 // TypeError exception. 811 return asyncGenVal.isObject() && 812 asyncGenVal.toObject().canUnwrapAs<AsyncGeneratorObject>(); 813 } 814 815 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 816 // 817 // AsyncGeneratorValidate ( generator, generatorBrand ) 818 // https://tc39.es/ecma262/#sec-asyncgeneratorvalidate 819 // 820 // Throwing part. 821 [[nodiscard]] static bool AsyncGeneratorValidateThrow( 822 JSContext* cx, MutableHandleValue result) { 823 Rooted<PromiseObject*> resultPromise( 824 cx, CreatePromiseObjectForAsyncGenerator(cx)); 825 if (!resultPromise) { 826 return false; 827 } 828 829 RootedValue badGeneratorError(cx); 830 if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_GENERATOR, &badGeneratorError)) { 831 return false; 832 } 833 834 if (!RejectPromiseInternal(cx, resultPromise, badGeneratorError)) { 835 return false; 836 } 837 838 result.setObject(*resultPromise); 839 return true; 840 } 841 842 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 843 // 844 // AsyncGeneratorEnqueue ( generator, completion, promiseCapability ) 845 // https://tc39.es/ecma262/#sec-asyncgeneratorenqueue 846 [[nodiscard]] static bool AsyncGeneratorEnqueue( 847 JSContext* cx, Handle<AsyncGeneratorObject*> generator, 848 CompletionKind completionKind, HandleValue completionValue, 849 Handle<PromiseObject*> resultPromise) { 850 // Step 1. Let request be 851 // AsyncGeneratorRequest { [[Completion]]: completion, 852 // [[Capability]]: promiseCapability }. 853 Rooted<AsyncGeneratorRequest*> request( 854 cx, AsyncGeneratorObject::createRequest(cx, generator, completionKind, 855 completionValue, resultPromise)); 856 if (!request) { 857 return false; 858 } 859 860 // Step 2. Append request to generator.[[AsyncGeneratorQueue]]. 861 // Step 3. Return unused. 862 return AsyncGeneratorObject::enqueueRequest(cx, generator, request); 863 } 864 865 class MOZ_STACK_CLASS MaybeEnterAsyncGeneratorRealm { 866 mozilla::Maybe<AutoRealm> ar_; 867 868 public: 869 MaybeEnterAsyncGeneratorRealm() = default; 870 ~MaybeEnterAsyncGeneratorRealm() = default; 871 872 // Enter async generator's realm, and wrap the method's argument value if 873 // necessary. 874 [[nodiscard]] bool maybeEnterAndWrap(JSContext* cx, 875 Handle<AsyncGeneratorObject*> generator, 876 MutableHandleValue value) { 877 if (generator->compartment() == cx->compartment()) { 878 return true; 879 } 880 881 ar_.emplace(cx, generator); 882 return cx->compartment()->wrap(cx, value); 883 } 884 885 // Leave async generator's realm, and wrap the method's result value if 886 // necessary. 887 [[nodiscard]] bool maybeLeaveAndWrap(JSContext* cx, 888 MutableHandleValue result) { 889 if (!ar_) { 890 return true; 891 } 892 ar_.reset(); 893 894 return cx->compartment()->wrap(cx, result); 895 } 896 }; 897 898 [[nodiscard]] static bool AsyncGeneratorMethodSanityCheck( 899 JSContext* cx, Handle<AsyncGeneratorObject*> generator) { 900 if (generator->isSuspendedStart() || generator->isSuspendedYield() || 901 generator->isCompleted()) { 902 // The spec assumes the queue is empty when async generator methods are 903 // called with those state, but our debugger allows calling those methods 904 // in unexpected state, such as before suspendedStart. 905 if (MOZ_UNLIKELY(!generator->isQueueEmpty())) { 906 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 907 JSMSG_SUSPENDED_QUEUE_NOT_EMPTY); 908 return false; 909 } 910 } 911 912 return true; 913 } 914 915 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 916 // 917 // %AsyncGeneratorPrototype%.next ( value ) 918 // https://tc39.es/ecma262/#sec-asyncgenerator-prototype-next 919 bool js::AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) { 920 CallArgs args = CallArgsFromVp(argc, vp); 921 922 // Step 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). 923 // Step 4. IfAbruptRejectPromise(result, promiseCapability). 924 // (reordered) 925 if (!IsAsyncGeneratorValid(args.thisv())) { 926 return AsyncGeneratorValidateThrow(cx, args.rval()); 927 } 928 929 // Step 1. Let generator be the this value. 930 // (implicit) 931 Rooted<AsyncGeneratorObject*> generator( 932 cx, &args.thisv().toObject().unwrapAs<AsyncGeneratorObject>()); 933 934 MaybeEnterAsyncGeneratorRealm maybeEnterRealm; 935 936 RootedValue completionValue(cx, args.get(0)); 937 if (!maybeEnterRealm.maybeEnterAndWrap(cx, generator, &completionValue)) { 938 return false; 939 } 940 941 // Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). 942 Rooted<PromiseObject*> resultPromise( 943 cx, CreatePromiseObjectForAsyncGenerator(cx)); 944 if (!resultPromise) { 945 return false; 946 } 947 948 if (!AsyncGeneratorMethodSanityCheck(cx, generator)) { 949 return false; 950 } 951 952 // Step 5. Let state be generator.[[AsyncGeneratorState]]. 953 // Step 6. If state is completed, then 954 if (generator->isCompleted()) { 955 MOZ_ASSERT(generator->isQueueEmpty()); 956 957 // Step 6.a. Let iteratorResult be CreateIteratorResultObject(undefined, 958 // true). 959 JSObject* resultObj = 960 CreateIterResultObject(cx, UndefinedHandleValue, true); 961 if (!resultObj) { 962 return false; 963 } 964 965 // Step 6.b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « 966 // iteratorResult »). 967 RootedValue resultValue(cx, ObjectValue(*resultObj)); 968 if (!ResolvePromiseInternal(cx, resultPromise, resultValue)) { 969 return false; 970 } 971 } else { 972 // Step 7. Let completion be NormalCompletion(value). 973 // Step 8. Perform AsyncGeneratorEnqueue(generator, completion, 974 // promiseCapability). 975 if (!AsyncGeneratorEnqueue(cx, generator, CompletionKind::Normal, 976 completionValue, resultPromise)) { 977 return false; 978 } 979 980 // Step 9. If state is either suspended-start or suspended-yield, then 981 if (generator->isSuspendedStart() || generator->isSuspendedYield()) { 982 MOZ_ASSERT(generator->isQueueLengthOne()); 983 984 // Step 9.a. Perform AsyncGeneratorResume(generator, completion). 985 if (!AsyncGeneratorResume(cx, generator, CompletionKind::Normal, 986 completionValue)) { 987 return false; 988 } 989 } else { 990 // Step 10. Else, 991 // Step 10.a. Assert: state is either executing or draining-queue. 992 MOZ_ASSERT(generator->isExecuting() || 993 generator->isExecuting_AwaitingYieldReturn() || 994 generator->isDrainingQueue() || 995 generator->isDrainingQueue_AwaitingReturn()); 996 } 997 } 998 999 // Step 6.c. Return promiseCapability.[[Promise]]. 1000 // and 1001 // Step 11. Return promiseCapability.[[Promise]]. 1002 args.rval().setObject(*resultPromise); 1003 1004 return maybeEnterRealm.maybeLeaveAndWrap(cx, args.rval()); 1005 } 1006 1007 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 1008 // 1009 // %AsyncGeneratorPrototype%.return ( value ) 1010 // https://tc39.es/ecma262/#sec-asyncgenerator-prototype-return 1011 bool js::AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) { 1012 CallArgs args = CallArgsFromVp(argc, vp); 1013 1014 // Step 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). 1015 // Step 4. IfAbruptRejectPromise(result, promiseCapability). 1016 // (reordered) 1017 if (!IsAsyncGeneratorValid(args.thisv())) { 1018 return AsyncGeneratorValidateThrow(cx, args.rval()); 1019 } 1020 1021 // Step 1. Let generator be the this value. 1022 Rooted<AsyncGeneratorObject*> generator( 1023 cx, &args.thisv().toObject().unwrapAs<AsyncGeneratorObject>()); 1024 1025 MaybeEnterAsyncGeneratorRealm maybeEnterRealm; 1026 1027 RootedValue completionValue(cx, args.get(0)); 1028 if (!maybeEnterRealm.maybeEnterAndWrap(cx, generator, &completionValue)) { 1029 return false; 1030 } 1031 1032 // Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). 1033 Rooted<PromiseObject*> resultPromise( 1034 cx, CreatePromiseObjectForAsyncGenerator(cx)); 1035 if (!resultPromise) { 1036 return false; 1037 } 1038 1039 if (!AsyncGeneratorMethodSanityCheck(cx, generator)) { 1040 return false; 1041 } 1042 1043 // Step 5. Let completion be ReturnCompletion(value). 1044 // Step 6. Perform AsyncGeneratorEnqueue(generator, completion, 1045 // promiseCapability). 1046 if (!AsyncGeneratorEnqueue(cx, generator, CompletionKind::Return, 1047 completionValue, resultPromise)) { 1048 return false; 1049 } 1050 1051 // Step 7. Let state be generator.[[AsyncGeneratorState]]. 1052 // Step 8. If state is either suspended-start or completed, then 1053 if (generator->isSuspendedStart() || generator->isCompleted()) { 1054 MOZ_ASSERT(generator->isQueueLengthOne()); 1055 1056 // Step 8.a. Set generator.[[AsyncGeneratorState]] to draining-queue. 1057 generator->setDrainingQueue(); 1058 1059 // Step 8.b. Perform AsyncGeneratorAwaitReturn(generator). 1060 if (!AsyncGeneratorAwaitReturn(cx, generator, completionValue)) { 1061 return false; 1062 } 1063 } else if (generator->isSuspendedYield()) { 1064 // Step 9. Else if state is suspended-yield, then 1065 MOZ_ASSERT(generator->isQueueLengthOne()); 1066 1067 // Step 9.a. Perform AsyncGeneratorResume(generator, completion). 1068 // 1069 // https://tc39.es/ecma262/#sec-asyncgeneratorresume 1070 // AsyncGeneratorResume ( generator, completion ) 1071 // 1072 // Step 7. Resume the suspended evaluation of genContext using completion as 1073 // the result of the operation that suspended it. Let result be the 1074 // Completion Record returned by the resumed computation. 1075 // Step 10. Return unused. 1076 // 1077 // AsyncGeneratorYield ( value ) 1078 // https://tc39.es/ecma262/#sec-asyncgeneratoryield 1079 // 1080 // Step 12.d. Resume callerContext passing undefined. If genContext is ever 1081 // resumed again, let resumptionValue be the Completion Record 1082 // with which it is resumed. 1083 // Step 12.e. Assert: If control reaches here, then genContext is the 1084 // running execution context again. 1085 // Step 12.f. Return ? 1086 // AsyncGeneratorUnwrapYieldResumption(resumptionValue). 1087 // 1088 if (!AsyncGeneratorUnwrapYieldResumptionWithReturn(cx, generator, 1089 completionValue)) { 1090 // The failure path here is for the Await inside 1091 // AsyncGeneratorUnwrapYieldResumption, where a corrupted Promise is 1092 // passed and called there. 1093 // 1094 // Per spec, the operation should be performed after resuming the 1095 // generator, but given that we're performing the Await before 1096 // resuming the generator, we need to handle the special throw completion 1097 // here. 1098 1099 // For uncatchable exception, there's nothing we can do. 1100 if (!cx->isExceptionPending()) { 1101 return false; 1102 } 1103 1104 // Resume the generator with throw completion, so that it behaves in the 1105 // same way as the Await throws. 1106 RootedValue exception(cx); 1107 if (!GetAndClearException(cx, &exception)) { 1108 return false; 1109 } 1110 if (!AsyncGeneratorResume(cx, generator, CompletionKind::Throw, 1111 exception)) { 1112 return false; 1113 } 1114 } 1115 } else { 1116 // Step 10. Else, 1117 // Step 10.a. Assert: state is either executing or draining-queue. 1118 MOZ_ASSERT(generator->isExecuting() || 1119 generator->isExecuting_AwaitingYieldReturn() || 1120 generator->isDrainingQueue() || 1121 generator->isDrainingQueue_AwaitingReturn()); 1122 } 1123 1124 // Step 11. Return promiseCapability.[[Promise]]. 1125 args.rval().setObject(*resultPromise); 1126 1127 return maybeEnterRealm.maybeLeaveAndWrap(cx, args.rval()); 1128 } 1129 1130 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 1131 // 1132 // %AsyncGeneratorPrototype%.throw ( exception ) 1133 // https://tc39.es/ecma262/#sec-asyncgenerator-prototype-throw 1134 bool js::AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp) { 1135 CallArgs args = CallArgsFromVp(argc, vp); 1136 1137 // Step 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). 1138 // Step 4. IfAbruptRejectPromise(result, promiseCapability). 1139 // (reordered) 1140 if (!IsAsyncGeneratorValid(args.thisv())) { 1141 return AsyncGeneratorValidateThrow(cx, args.rval()); 1142 } 1143 1144 // Step 1. Let generator be the this value. 1145 Rooted<AsyncGeneratorObject*> generator( 1146 cx, &args.thisv().toObject().unwrapAs<AsyncGeneratorObject>()); 1147 1148 MaybeEnterAsyncGeneratorRealm maybeEnterRealm; 1149 1150 RootedValue completionValue(cx, args.get(0)); 1151 if (!maybeEnterRealm.maybeEnterAndWrap(cx, generator, &completionValue)) { 1152 return false; 1153 } 1154 1155 // Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). 1156 Rooted<PromiseObject*> resultPromise( 1157 cx, CreatePromiseObjectForAsyncGenerator(cx)); 1158 if (!resultPromise) { 1159 return false; 1160 } 1161 1162 if (!AsyncGeneratorMethodSanityCheck(cx, generator)) { 1163 return false; 1164 } 1165 1166 // Step 5. Let state be generator.[[AsyncGeneratorState]]. 1167 // Step 6. If state is suspended-start, then 1168 if (generator->isSuspendedStart()) { 1169 // Step 6.a. Set generator.[[AsyncGeneratorState]] to completed. 1170 // Step 6.b. Set state to completed. 1171 generator->setCompleted(); 1172 } 1173 1174 // Step 7. If state is completed, then 1175 if (generator->isCompleted()) { 1176 MOZ_ASSERT(generator->isQueueEmpty()); 1177 1178 // Step 7.a. Perform ! Call(promiseCapability.[[Reject]], undefined, « 1179 // exception »). 1180 if (!RejectPromiseInternal(cx, resultPromise, completionValue)) { 1181 return false; 1182 } 1183 } else { 1184 // Step 8. Let completion be ThrowCompletion(exception). 1185 // Step 9. Perform AsyncGeneratorEnqueue(generator, completion, 1186 // promiseCapability). 1187 if (!AsyncGeneratorEnqueue(cx, generator, CompletionKind::Throw, 1188 completionValue, resultPromise)) { 1189 return false; 1190 } 1191 1192 // Step 10. If state is suspended-yield, then 1193 if (generator->isSuspendedYield()) { 1194 MOZ_ASSERT(generator->isQueueLengthOne()); 1195 1196 // Step 10.a. Perform AsyncGeneratorResume(generator, completion). 1197 if (!AsyncGeneratorResume(cx, generator, CompletionKind::Throw, 1198 completionValue)) { 1199 return false; 1200 } 1201 } else { 1202 // Step 11. Else, 1203 // Step 11.a. Assert: state is either executing or draining-queue. 1204 MOZ_ASSERT(generator->isExecuting() || 1205 generator->isExecuting_AwaitingYieldReturn() || 1206 generator->isDrainingQueue() || 1207 generator->isDrainingQueue_AwaitingReturn()); 1208 } 1209 } 1210 1211 // Step 7.b. Return promiseCapability.[[Promise]]. 1212 // and 1213 // Step 12. Return promiseCapability.[[Promise]]. 1214 args.rval().setObject(*resultPromise); 1215 1216 return maybeEnterRealm.maybeLeaveAndWrap(cx, args.rval()); 1217 } 1218 1219 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 1220 // 1221 // AsyncGeneratorResume ( generator, completion ) 1222 // https://tc39.es/ecma262/#sec-asyncgeneratorresume 1223 [[nodiscard]] static bool AsyncGeneratorResume( 1224 JSContext* cx, Handle<AsyncGeneratorObject*> generator, 1225 CompletionKind completionKind, HandleValue argument) { 1226 // Given that yield can resume again, we implement it as a loop. 1227 JS::Rooted<JS::Value> resumeArgument(cx, argument); 1228 while (true) { 1229 MOZ_ASSERT(!generator->isClosed(), 1230 "closed generator when resuming async generator"); 1231 MOZ_ASSERT(generator->isSuspended(), 1232 "non-suspended generator when resuming async generator"); 1233 1234 // Step 1. Assert: generator.[[AsyncGeneratorState]] is either 1235 // suspended-start or suspended-yield. 1236 // 1237 // NOTE: We're using suspend/resume also for await. and the state can be 1238 // anything. 1239 1240 // Step 2. Let genContext be generator.[[AsyncGeneratorContext]]. 1241 // Step 3. Let callerContext be the running execution context. 1242 // Step 4. Suspend callerContext. 1243 // (handled in generator) 1244 1245 // Step 5. Set generator.[[AsyncGeneratorState]] to executing. 1246 generator->setExecuting(); 1247 1248 // Step 6. Push genContext onto the execution context stack; genContext is 1249 // now the running execution context. 1250 // Step 7. Resume the suspended evaluation of genContext using completion as 1251 // the result of the operation that suspended it. Let result be the 1252 // Completion Record returned by the resumed computation. 1253 // Step 8. Assert: result is never an abrupt completion. 1254 // Step 9. Assert: When we return here, genContext has already been removed 1255 // from the execution context stack and callerContext is the 1256 // currently running execution context. 1257 // Step 10. Return unused. 1258 Handle<PropertyName*> funName = completionKind == CompletionKind::Normal 1259 ? cx->names().AsyncGeneratorNext 1260 : completionKind == CompletionKind::Throw 1261 ? cx->names().AsyncGeneratorThrow 1262 : cx->names().AsyncGeneratorReturn; 1263 FixedInvokeArgs<1> args(cx); 1264 args[0].set(resumeArgument); 1265 RootedValue thisOrRval(cx, ObjectValue(*generator)); 1266 if (!CallSelfHostedFunction(cx, funName, thisOrRval, args, &thisOrRval)) { 1267 if (!generator->isClosed()) { 1268 generator->setClosed(cx); 1269 } 1270 return AsyncGeneratorThrown(cx, generator); 1271 } 1272 1273 if (generator->isAfterAwait()) { 1274 return AsyncGeneratorAwait(cx, generator, thisOrRval); 1275 } 1276 1277 if (generator->isAfterYield()) { 1278 bool resumeAgain = false; 1279 if (!AsyncGeneratorYield(cx, generator, thisOrRval, &resumeAgain, 1280 &completionKind, &resumeArgument)) { 1281 return false; 1282 } 1283 if (resumeAgain) { 1284 MOZ_ASSERT(completionKind != CompletionKind::Return); 1285 continue; 1286 } 1287 return true; 1288 } 1289 1290 return AsyncGeneratorReturned(cx, generator, thisOrRval); 1291 } 1292 } 1293 1294 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 1295 /** 1296 * Explicit Resource Management Proposal 1297 * 27.1.3.1 %AsyncIteratorPrototype% [ @@asyncDispose ] ( ) 1298 * https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-%25asynciteratorprototype%25-%40%40asyncdispose 1299 */ 1300 static bool AsyncIteratorDispose(JSContext* cx, unsigned argc, Value* vp) { 1301 CallArgs args = CallArgsFromVp(argc, vp); 1302 1303 // Step 1. Let O be the this value. 1304 JS::Handle<JS::Value> O = args.thisv(); 1305 1306 // Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). 1307 JS::Rooted<PromiseObject*> promise(cx, 1308 PromiseObject::createSkippingExecutor(cx)); 1309 if (!promise) { 1310 return false; 1311 } 1312 1313 // Step 3. Let return be Completion(GetMethod(O, "return")). 1314 JS::Rooted<JS::Value> returnMethod(cx); 1315 if (!GetProperty(cx, O, cx->names().return_, &returnMethod)) { 1316 // Step 4. IfAbruptRejectPromise(return, promiseCapability). 1317 return AbruptRejectPromise(cx, args, promise, nullptr); 1318 } 1319 1320 // Step 5. If return is undefined, then 1321 // As per the spec GetMethod returns undefined if the property is either null 1322 // or undefined thus here we check for both. 1323 if (returnMethod.isNullOrUndefined()) { 1324 // Step 5.a. Perform ! Call(promiseCapability.[[Resolve]], undefined, « 1325 // undefined »). 1326 if (!PromiseObject::resolve(cx, promise, JS::UndefinedHandleValue)) { 1327 return false; 1328 } 1329 args.rval().setObject(*promise); 1330 return true; 1331 } 1332 1333 // GetMethod also throws a TypeError exception if the function is not callable 1334 // thus we perform that check here. 1335 if (!IsCallable(returnMethod)) { 1336 ReportIsNotFunction(cx, returnMethod); 1337 return AbruptRejectPromise(cx, args, promise, nullptr); 1338 } 1339 1340 // Step 6. Else, 1341 // Step 6.a. Let result be Completion(Call(return, O, « undefined »)). 1342 JS::Rooted<JS::Value> rval(cx); 1343 if (!Call(cx, returnMethod, O, JS::UndefinedHandleValue, &rval)) { 1344 // Step 6.b. IfAbruptRejectPromise(result, promiseCapability). 1345 return AbruptRejectPromise(cx, args, promise, nullptr); 1346 } 1347 1348 // Step 6.c-g. 1349 if (!InternalAsyncIteratorDisposeAwait(cx, rval, promise)) { 1350 return AbruptRejectPromise(cx, args, promise, nullptr); 1351 } 1352 1353 // Step 7. Return promiseCapability.[[Promise]]. 1354 args.rval().setObject(*promise); 1355 return true; 1356 } 1357 #endif 1358 1359 static const JSFunctionSpec async_generator_methods[] = { 1360 JS_FN("next", js::AsyncGeneratorNext, 1, 0), 1361 JS_FN("throw", js::AsyncGeneratorThrow, 1, 0), 1362 JS_FN("return", js::AsyncGeneratorReturn, 1, 0), 1363 JS_FS_END, 1364 }; 1365 1366 static JSObject* CreateAsyncGeneratorFunction(JSContext* cx, JSProtoKey key) { 1367 RootedObject proto(cx, &cx->global()->getFunctionConstructor()); 1368 Handle<PropertyName*> name = cx->names().AsyncGeneratorFunction; 1369 1370 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 1371 // 1372 // The AsyncGeneratorFunction Constructor 1373 // https://tc39.es/ecma262/#sec-asyncgeneratorfunction-constructor 1374 return NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1, 1375 FunctionFlags::NATIVE_CTOR, nullptr, name, proto, 1376 gc::AllocKind::FUNCTION, TenuredObject); 1377 } 1378 1379 static JSObject* CreateAsyncGeneratorFunctionPrototype(JSContext* cx, 1380 JSProtoKey key) { 1381 return NewTenuredObjectWithFunctionPrototype(cx, cx->global()); 1382 } 1383 1384 static bool AsyncGeneratorFunctionClassFinish(JSContext* cx, 1385 HandleObject asyncGenFunction, 1386 HandleObject asyncGenerator) { 1387 Handle<GlobalObject*> global = cx->global(); 1388 1389 // Change the "constructor" property to non-writable before adding any other 1390 // properties, so it's still the last property and can be modified without a 1391 // dictionary-mode transition. 1392 MOZ_ASSERT(asyncGenerator->as<NativeObject>().getLastProperty().key() == 1393 NameToId(cx->names().constructor)); 1394 MOZ_ASSERT(!asyncGenerator->as<NativeObject>().inDictionaryMode()); 1395 1396 RootedValue asyncGenFunctionVal(cx, ObjectValue(*asyncGenFunction)); 1397 if (!DefineDataProperty(cx, asyncGenerator, cx->names().constructor, 1398 asyncGenFunctionVal, JSPROP_READONLY)) { 1399 return false; 1400 } 1401 MOZ_ASSERT(!asyncGenerator->as<NativeObject>().inDictionaryMode()); 1402 1403 RootedObject asyncIterProto( 1404 cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global)); 1405 if (!asyncIterProto) { 1406 return false; 1407 } 1408 1409 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 1410 // 1411 // AsyncGenerator Objects 1412 // https://tc39.es/ecma262/#sec-asyncgenerator-objects 1413 RootedObject asyncGenProto(cx, GlobalObject::createBlankPrototypeInheriting( 1414 cx, &PlainObject::class_, asyncIterProto)); 1415 if (!asyncGenProto) { 1416 return false; 1417 } 1418 if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr, 1419 async_generator_methods) || 1420 !DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator)) { 1421 return false; 1422 } 1423 1424 // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 1425 // 1426 // Properties of the AsyncGeneratorFunction Prototype Object 1427 // https://tc39.es/ecma262/#sec-properties-of-asyncgeneratorfunction-prototype 1428 if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto, 1429 JSPROP_READONLY, JSPROP_READONLY) || 1430 !DefineToStringTag(cx, asyncGenerator, 1431 cx->names().AsyncGeneratorFunction)) { 1432 return false; 1433 } 1434 1435 global->setAsyncGeneratorPrototype(asyncGenProto); 1436 1437 return true; 1438 } 1439 1440 static const ClassSpec AsyncGeneratorFunctionClassSpec = { 1441 CreateAsyncGeneratorFunction, 1442 CreateAsyncGeneratorFunctionPrototype, 1443 nullptr, 1444 nullptr, 1445 nullptr, 1446 nullptr, 1447 AsyncGeneratorFunctionClassFinish, 1448 ClassSpec::DontDefineConstructor, 1449 }; 1450 1451 const JSClass js::AsyncGeneratorFunctionClass = { 1452 "AsyncGeneratorFunction", 1453 0, 1454 JS_NULL_CLASS_OPS, 1455 &AsyncGeneratorFunctionClassSpec, 1456 }; 1457 1458 [[nodiscard]] bool js::AsyncGeneratorPromiseReactionJob( 1459 JSContext* cx, PromiseHandler handler, 1460 Handle<AsyncGeneratorObject*> generator, HandleValue argument) { 1461 // Await's handlers don't return a value, nor throw any exceptions. 1462 // They fail only on OOM. 1463 switch (handler) { 1464 case PromiseHandler::AsyncGeneratorAwaitedFulfilled: 1465 return AsyncGeneratorAwaitedFulfilled(cx, generator, argument); 1466 1467 case PromiseHandler::AsyncGeneratorAwaitedRejected: 1468 return AsyncGeneratorAwaitedRejected(cx, generator, argument); 1469 1470 case PromiseHandler::AsyncGeneratorAwaitReturnFulfilled: 1471 return AsyncGeneratorAwaitReturnFulfilled(cx, generator, argument); 1472 1473 case PromiseHandler::AsyncGeneratorAwaitReturnRejected: 1474 return AsyncGeneratorAwaitReturnRejected(cx, generator, argument); 1475 1476 case PromiseHandler::AsyncGeneratorYieldReturnAwaitedFulfilled: 1477 return AsyncGeneratorYieldReturnAwaitedFulfilled(cx, generator, argument); 1478 1479 case PromiseHandler::AsyncGeneratorYieldReturnAwaitedRejected: 1480 return AsyncGeneratorYieldReturnAwaitedRejected(cx, generator, argument); 1481 1482 default: 1483 MOZ_CRASH("Bad handler in AsyncGeneratorPromiseReactionJob"); 1484 } 1485 } 1486 1487 // --------------------- 1488 // AsyncFromSyncIterator 1489 // --------------------- 1490 1491 const JSClass AsyncFromSyncIteratorObject::class_ = { 1492 "AsyncFromSyncIteratorObject", 1493 JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots), 1494 }; 1495 1496 /* 1497 * ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c 1498 * 1499 * CreateAsyncFromSyncIterator ( syncIteratorRecord ) 1500 * https://tc39.es/ecma262/#sec-createasyncfromsynciterator 1501 */ 1502 JSObject* js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter, 1503 HandleValue nextMethod) { 1504 // Steps 1-5. 1505 return AsyncFromSyncIteratorObject::create(cx, iter, nextMethod); 1506 } 1507 1508 /* 1509 * ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c 1510 * 1511 * CreateAsyncFromSyncIterator ( syncIteratorRecord ) 1512 * https://tc39.es/ecma262/#sec-createasyncfromsynciterator 1513 */ 1514 /* static */ 1515 JSObject* AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter, 1516 HandleValue nextMethod) { 1517 // Step 1. Let asyncIterator be 1518 // OrdinaryObjectCreate(%AsyncFromSyncIteratorPrototype%, « 1519 // [[SyncIteratorRecord]] »). 1520 RootedObject proto(cx, 1521 GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype( 1522 cx, cx->global())); 1523 if (!proto) { 1524 return nullptr; 1525 } 1526 1527 AsyncFromSyncIteratorObject* asyncIter = 1528 NewObjectWithGivenProto<AsyncFromSyncIteratorObject>(cx, proto); 1529 if (!asyncIter) { 1530 return nullptr; 1531 } 1532 1533 // Step 3. Let nextMethod be ! Get(asyncIterator, "next"). 1534 // (done in caller) 1535 1536 // Step 2. Set asyncIterator.[[SyncIteratorRecord]] to syncIteratorRecord. 1537 // Step 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: 1538 // asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }. 1539 asyncIter->init(iter, nextMethod); 1540 1541 // Step 5. Return iteratorRecord. 1542 return asyncIter; 1543 } 1544 1545 /** 1546 * ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c 1547 * 1548 * %AsyncFromSyncIteratorPrototype%.next ( [ value ] ) 1549 * https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.next 1550 */ 1551 static bool AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) { 1552 CallArgs args = CallArgsFromVp(argc, vp); 1553 return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal); 1554 } 1555 1556 /** 1557 * ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c 1558 * 1559 * %AsyncFromSyncIteratorPrototype%.return ( [ value ] ) 1560 * https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.return 1561 */ 1562 static bool AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, 1563 Value* vp) { 1564 CallArgs args = CallArgsFromVp(argc, vp); 1565 return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return); 1566 } 1567 1568 /** 1569 * ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c 1570 * 1571 * %AsyncFromSyncIteratorPrototype%.throw ( [ value ] ) 1572 * https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.throw 1573 */ 1574 static bool AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, 1575 Value* vp) { 1576 CallArgs args = CallArgsFromVp(argc, vp); 1577 return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw); 1578 } 1579 1580 static const JSFunctionSpec async_from_sync_iter_methods[] = { 1581 JS_FN("next", AsyncFromSyncIteratorNext, 1, 0), 1582 JS_FN("throw", AsyncFromSyncIteratorThrow, 1, 0), 1583 JS_FN("return", AsyncFromSyncIteratorReturn, 1, 0), 1584 JS_FS_END, 1585 }; 1586 1587 bool GlobalObject::initAsyncFromSyncIteratorProto( 1588 JSContext* cx, Handle<GlobalObject*> global) { 1589 if (global->hasBuiltinProto(ProtoKind::AsyncFromSyncIteratorProto)) { 1590 return true; 1591 } 1592 1593 RootedObject asyncIterProto( 1594 cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global)); 1595 if (!asyncIterProto) { 1596 return false; 1597 } 1598 1599 // ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c 1600 // 1601 // The %AsyncFromSyncIteratorPrototype% Object 1602 // https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%-object 1603 RootedObject asyncFromSyncIterProto( 1604 cx, GlobalObject::createBlankPrototypeInheriting(cx, &PlainObject::class_, 1605 asyncIterProto)); 1606 if (!asyncFromSyncIterProto) { 1607 return false; 1608 } 1609 if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr, 1610 async_from_sync_iter_methods) || 1611 !DefineToStringTag(cx, asyncFromSyncIterProto, 1612 cx->names().Async_from_Sync_Iterator_)) { 1613 return false; 1614 } 1615 1616 global->initBuiltinProto(ProtoKind::AsyncFromSyncIteratorProto, 1617 asyncFromSyncIterProto); 1618 return true; 1619 } 1620 1621 // ------------- 1622 // AsyncIterator 1623 // ------------- 1624 1625 static const JSFunctionSpec async_iterator_proto_methods[] = { 1626 JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0), 1627 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 1628 JS_SYM_FN(asyncDispose, AsyncIteratorDispose, 0, 0), 1629 #endif 1630 JS_FS_END, 1631 }; 1632 1633 static const JSFunctionSpec async_iterator_proto_methods_with_helpers[] = { 1634 JS_SELF_HOSTED_FN("map", "AsyncIteratorMap", 1, 0), 1635 JS_SELF_HOSTED_FN("filter", "AsyncIteratorFilter", 1, 0), 1636 JS_SELF_HOSTED_FN("take", "AsyncIteratorTake", 1, 0), 1637 JS_SELF_HOSTED_FN("drop", "AsyncIteratorDrop", 1, 0), 1638 JS_SELF_HOSTED_FN("asIndexedPairs", "AsyncIteratorAsIndexedPairs", 0, 0), 1639 JS_SELF_HOSTED_FN("flatMap", "AsyncIteratorFlatMap", 1, 0), 1640 JS_SELF_HOSTED_FN("reduce", "AsyncIteratorReduce", 1, 0), 1641 JS_SELF_HOSTED_FN("toArray", "AsyncIteratorToArray", 0, 0), 1642 JS_SELF_HOSTED_FN("forEach", "AsyncIteratorForEach", 1, 0), 1643 JS_SELF_HOSTED_FN("some", "AsyncIteratorSome", 1, 0), 1644 JS_SELF_HOSTED_FN("every", "AsyncIteratorEvery", 1, 0), 1645 JS_SELF_HOSTED_FN("find", "AsyncIteratorFind", 1, 0), 1646 JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0), 1647 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 1648 JS_SYM_FN(asyncDispose, AsyncIteratorDispose, 0, 0), 1649 #endif 1650 JS_FS_END, 1651 }; 1652 1653 bool GlobalObject::initAsyncIteratorProto(JSContext* cx, 1654 Handle<GlobalObject*> global) { 1655 if (global->hasBuiltinProto(ProtoKind::AsyncIteratorProto)) { 1656 return true; 1657 } 1658 1659 // 25.1.3 The %AsyncIteratorPrototype% Object 1660 RootedObject asyncIterProto( 1661 cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global)); 1662 if (!asyncIterProto) { 1663 return false; 1664 } 1665 if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, 1666 async_iterator_proto_methods)) { 1667 return false; 1668 } 1669 1670 global->initBuiltinProto(ProtoKind::AsyncIteratorProto, asyncIterProto); 1671 return true; 1672 } 1673 1674 // https://tc39.es/proposal-iterator-helpers/#sec-asynciterator as of revision 1675 // 8f10db5. 1676 static bool AsyncIteratorConstructor(JSContext* cx, unsigned argc, Value* vp) { 1677 CallArgs args = CallArgsFromVp(argc, vp); 1678 1679 // Step 1. 1680 if (!ThrowIfNotConstructing(cx, args, "AsyncIterator")) { 1681 return false; 1682 } 1683 // Throw TypeError if NewTarget is the active function object, preventing the 1684 // Iterator constructor from being used directly. 1685 if (args.callee() == args.newTarget().toObject()) { 1686 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1687 JSMSG_BOGUS_CONSTRUCTOR, "AsyncIterator"); 1688 return false; 1689 } 1690 1691 // Step 2. 1692 RootedObject proto(cx); 1693 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_AsyncIterator, 1694 &proto)) { 1695 return false; 1696 } 1697 1698 JSObject* obj = NewObjectWithClassProto<AsyncIteratorObject>(cx, proto); 1699 if (!obj) { 1700 return false; 1701 } 1702 1703 args.rval().setObject(*obj); 1704 return true; 1705 } 1706 1707 static const ClassSpec AsyncIteratorObjectClassSpec = { 1708 GenericCreateConstructor<AsyncIteratorConstructor, 0, 1709 gc::AllocKind::FUNCTION>, 1710 GenericCreatePrototype<AsyncIteratorObject>, 1711 nullptr, 1712 nullptr, 1713 async_iterator_proto_methods_with_helpers, 1714 nullptr, 1715 nullptr, 1716 }; 1717 1718 const JSClass AsyncIteratorObject::class_ = { 1719 "AsyncIterator", 1720 JSCLASS_HAS_CACHED_PROTO(JSProto_AsyncIterator), 1721 JS_NULL_CLASS_OPS, 1722 &AsyncIteratorObjectClassSpec, 1723 }; 1724 1725 const JSClass AsyncIteratorObject::protoClass_ = { 1726 "AsyncIterator.prototype", 1727 JSCLASS_HAS_CACHED_PROTO(JSProto_AsyncIterator), 1728 JS_NULL_CLASS_OPS, 1729 &AsyncIteratorObjectClassSpec, 1730 }; 1731 1732 // Iterator Helper proposal 1733 static const JSFunctionSpec async_iterator_helper_methods[] = { 1734 JS_SELF_HOSTED_FN("next", "AsyncIteratorHelperNext", 1, 0), 1735 JS_SELF_HOSTED_FN("return", "AsyncIteratorHelperReturn", 1, 0), 1736 JS_SELF_HOSTED_FN("throw", "AsyncIteratorHelperThrow", 1, 0), 1737 JS_FS_END, 1738 }; 1739 1740 static const JSClass AsyncIteratorHelperPrototypeClass = { 1741 "Async Iterator Helper", 1742 0, 1743 }; 1744 1745 const JSClass AsyncIteratorHelperObject::class_ = { 1746 "Async Iterator Helper", 1747 JSCLASS_HAS_RESERVED_SLOTS(AsyncIteratorHelperObject::SlotCount), 1748 }; 1749 1750 /* static */ 1751 NativeObject* GlobalObject::getOrCreateAsyncIteratorHelperPrototype( 1752 JSContext* cx, Handle<GlobalObject*> global) { 1753 return MaybeNativeObject( 1754 getOrCreateBuiltinProto(cx, global, ProtoKind::AsyncIteratorHelperProto, 1755 initAsyncIteratorHelperProto)); 1756 } 1757 1758 /* static */ 1759 bool GlobalObject::initAsyncIteratorHelperProto(JSContext* cx, 1760 Handle<GlobalObject*> global) { 1761 if (global->hasBuiltinProto(ProtoKind::AsyncIteratorHelperProto)) { 1762 return true; 1763 } 1764 1765 RootedObject asyncIterProto( 1766 cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global)); 1767 if (!asyncIterProto) { 1768 return false; 1769 } 1770 1771 RootedObject asyncIteratorHelperProto( 1772 cx, GlobalObject::createBlankPrototypeInheriting( 1773 cx, &AsyncIteratorHelperPrototypeClass, asyncIterProto)); 1774 if (!asyncIteratorHelperProto) { 1775 return false; 1776 } 1777 if (!DefinePropertiesAndFunctions(cx, asyncIteratorHelperProto, nullptr, 1778 async_iterator_helper_methods)) { 1779 return false; 1780 } 1781 1782 global->initBuiltinProto(ProtoKind::AsyncIteratorHelperProto, 1783 asyncIteratorHelperProto); 1784 return true; 1785 } 1786 1787 AsyncIteratorHelperObject* js::NewAsyncIteratorHelper(JSContext* cx) { 1788 RootedObject proto(cx, GlobalObject::getOrCreateAsyncIteratorHelperPrototype( 1789 cx, cx->global())); 1790 if (!proto) { 1791 return nullptr; 1792 } 1793 return NewObjectWithGivenProto<AsyncIteratorHelperObject>(cx, proto); 1794 }