// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/builtins/builtins-async-gen.h" #include "src/builtins/builtins-utils-gen.h" #include "src/builtins/builtins.h" #include "src/codegen/code-factory.h" #include "src/codegen/code-stub-assembler.h" #include "src/execution/frames-inl.h" #include "src/objects/js-generator.h" #include "src/objects/js-promise.h" namespace v8 { namespace internal { using compiler::Node; namespace { class AsyncGeneratorBuiltinsAssembler : public AsyncBuiltinsAssembler { public: explicit AsyncGeneratorBuiltinsAssembler(CodeAssemblerState* state) : AsyncBuiltinsAssembler(state) {} inline TNode LoadGeneratorState( const TNode generator) { return LoadObjectField(generator, JSGeneratorObject::kContinuationOffset); } inline TNode IsGeneratorStateClosed(const TNode state) { return SmiEqual(state, SmiConstant(JSGeneratorObject::kGeneratorClosed)); } inline TNode IsGeneratorClosed( const TNode generator) { return IsGeneratorStateClosed(LoadGeneratorState(generator)); } inline TNode IsGeneratorStateSuspended(const TNode state) { return SmiGreaterThanOrEqual(state, SmiConstant(0)); } inline TNode IsGeneratorSuspended( const TNode generator) { return IsGeneratorStateSuspended(LoadGeneratorState(generator)); } inline TNode IsGeneratorStateSuspendedAtStart(const TNode state) { return SmiEqual(state, SmiConstant(0)); } inline TNode IsGeneratorStateNotExecuting(const TNode state) { return SmiNotEqual(state, SmiConstant(JSGeneratorObject::kGeneratorExecuting)); } inline TNode IsGeneratorNotExecuting( const TNode generator) { return IsGeneratorStateNotExecuting(LoadGeneratorState(generator)); } inline TNode IsGeneratorAwaiting( const TNode generator) { TNode is_generator_awaiting = LoadObjectField(generator, JSAsyncGeneratorObject::kIsAwaitingOffset); return TaggedEqual(is_generator_awaiting, SmiConstant(1)); } inline void SetGeneratorAwaiting(const TNode generator) { CSA_ASSERT(this, Word32BinaryNot(IsGeneratorAwaiting(generator))); StoreObjectFieldNoWriteBarrier( generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(1)); CSA_ASSERT(this, IsGeneratorAwaiting(generator)); } inline void SetGeneratorNotAwaiting( const TNode generator) { CSA_ASSERT(this, IsGeneratorAwaiting(generator)); StoreObjectFieldNoWriteBarrier( generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(0)); CSA_ASSERT(this, Word32BinaryNot(IsGeneratorAwaiting(generator))); } inline void CloseGenerator(const TNode generator) { StoreObjectFieldNoWriteBarrier( generator, JSGeneratorObject::kContinuationOffset, SmiConstant(JSGeneratorObject::kGeneratorClosed)); } inline TNode LoadFirstAsyncGeneratorRequestFromQueue( const TNode generator) { return LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset); } inline TNode LoadResumeTypeFromAsyncGeneratorRequest( const TNode request) { return LoadObjectField(request, AsyncGeneratorRequest::kResumeModeOffset); } inline TNode LoadPromiseFromAsyncGeneratorRequest( const TNode request) { return LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset); } inline TNode LoadValueFromAsyncGeneratorRequest( const TNode request) { return LoadObjectField(request, AsyncGeneratorRequest::kValueOffset); } inline TNode IsAbruptResumeType(const TNode resume_type) { return SmiNotEqual(resume_type, SmiConstant(JSGeneratorObject::kNext)); } void AsyncGeneratorEnqueue(CodeStubArguments* args, TNode context, TNode receiver, TNode value, JSAsyncGeneratorObject::ResumeMode resume_mode, const char* method_name); TNode TakeFirstAsyncGeneratorRequestFromQueue( TNode generator); void AddAsyncGeneratorRequestToQueue(TNode generator, TNode request); TNode AllocateAsyncGeneratorRequest( JSAsyncGeneratorObject::ResumeMode resume_mode, TNode resume_value, TNode promise); // Shared implementation of the catchable and uncatchable variations of Await // for AsyncGenerators. template void AsyncGeneratorAwait(bool is_catchable); void AsyncGeneratorAwaitResumeClosure( TNode context, TNode value, JSAsyncGeneratorObject::ResumeMode resume_mode); }; // Shared implementation for the 3 Async Iterator protocol methods of Async // Generators. void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorEnqueue( CodeStubArguments* args, TNode context, TNode receiver, TNode value, JSAsyncGeneratorObject::ResumeMode resume_mode, const char* method_name) { // AsyncGeneratorEnqueue produces a new Promise, and appends it to the list // of async generator requests to be executed. If the generator is not // presently executing, then this method will loop through, processing each // request from front to back. // This loop resides in AsyncGeneratorResumeNext. TNode promise = AllocateAndInitJSPromise(context); Label if_receiverisincompatible(this, Label::kDeferred); GotoIf(TaggedIsSmi(receiver), &if_receiverisincompatible); GotoIfNot(HasInstanceType(CAST(receiver), JS_ASYNC_GENERATOR_OBJECT_TYPE), &if_receiverisincompatible); { Label done(this); const TNode generator = CAST(receiver); const TNode req = AllocateAsyncGeneratorRequest(resume_mode, value, promise); AddAsyncGeneratorRequestToQueue(generator, req); // Let state be generator.[[AsyncGeneratorState]] // If state is not "executing", then // Perform AsyncGeneratorResumeNext(Generator) // Check if the {receiver} is running or already closed. TNode continuation = LoadGeneratorState(generator); GotoIf(SmiEqual(continuation, SmiConstant(JSAsyncGeneratorObject::kGeneratorExecuting)), &done); CallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator); Goto(&done); BIND(&done); args->PopAndReturn(promise); } BIND(&if_receiverisincompatible); { CallBuiltin(Builtins::kRejectPromise, context, promise, MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context, StringConstant(method_name), receiver), TrueConstant()); args->PopAndReturn(promise); } } TNode AsyncGeneratorBuiltinsAssembler::AllocateAsyncGeneratorRequest( JSAsyncGeneratorObject::ResumeMode resume_mode, TNode resume_value, TNode promise) { TNode request = Allocate(AsyncGeneratorRequest::kSize); StoreMapNoWriteBarrier(request, RootIndex::kAsyncGeneratorRequestMap); StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kNextOffset, UndefinedConstant()); StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kResumeModeOffset, SmiConstant(resume_mode)); StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kValueOffset, resume_value); StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kPromiseOffset, promise); StoreObjectFieldRoot(request, AsyncGeneratorRequest::kNextOffset, RootIndex::kUndefinedValue); return CAST(request); } void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure( TNode context, TNode value, JSAsyncGeneratorObject::ResumeMode resume_mode) { const TNode generator = CAST(LoadContextElement(context, Context::EXTENSION_INDEX)); SetGeneratorNotAwaiting(generator); CSA_SLOW_ASSERT(this, IsGeneratorSuspended(generator)); // Remember the {resume_mode} for the {generator}. StoreObjectFieldNoWriteBarrier(generator, JSGeneratorObject::kResumeModeOffset, SmiConstant(resume_mode)); CallStub(CodeFactory::ResumeGenerator(isolate()), context, value, generator); TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator); } template void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) { TNode async_generator_object = CAST(Parameter(Descriptor::kAsyncGeneratorObject)); TNode value = CAST(Parameter(Descriptor::kValue)); TNode context = CAST(Parameter(Descriptor::kContext)); TNode request = CAST(LoadFirstAsyncGeneratorRequestFromQueue(async_generator_object)); TNode outer_promise = LoadObjectField( request, AsyncGeneratorRequest::kPromiseOffset); const int resolve_index = Context::ASYNC_GENERATOR_AWAIT_RESOLVE_SHARED_FUN; const int reject_index = Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN; SetGeneratorAwaiting(async_generator_object); Await(context, async_generator_object, value, outer_promise, resolve_index, reject_index, is_catchable); Return(UndefinedConstant()); } void AsyncGeneratorBuiltinsAssembler::AddAsyncGeneratorRequestToQueue( TNode generator, TNode request) { TVARIABLE(HeapObject, var_current); Label empty(this), loop(this, &var_current), done(this); var_current = LoadObjectField( generator, JSAsyncGeneratorObject::kQueueOffset); Branch(IsUndefined(var_current.value()), &empty, &loop); BIND(&empty); { StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, request); Goto(&done); } BIND(&loop); { Label loop_next(this), next_empty(this); TNode current = CAST(var_current.value()); TNode next = LoadObjectField( current, AsyncGeneratorRequest::kNextOffset); Branch(IsUndefined(next), &next_empty, &loop_next); BIND(&next_empty); { StoreObjectField(current, AsyncGeneratorRequest::kNextOffset, request); Goto(&done); } BIND(&loop_next); { var_current = next; Goto(&loop); } } BIND(&done); } TNode AsyncGeneratorBuiltinsAssembler::TakeFirstAsyncGeneratorRequestFromQueue( TNode generator) { // Removes and returns the first AsyncGeneratorRequest from a // JSAsyncGeneratorObject's queue. Asserts that the queue is not empty. TNode request = LoadObjectField( generator, JSAsyncGeneratorObject::kQueueOffset); TNode next = LoadObjectField(request, AsyncGeneratorRequest::kNextOffset); StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, next); return request; } } // namespace // https://tc39.github.io/proposal-async-iteration/ // Section #sec-asyncgenerator-prototype-next TF_BUILTIN(AsyncGeneratorPrototypeNext, AsyncGeneratorBuiltinsAssembler) { const int kValueArg = 0; TNode argc = ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); CodeStubArguments args(this, argc); TNode generator = args.GetReceiver(); TNode value = args.GetOptionalArgumentValue(kValueArg); TNode context = CAST(Parameter(Descriptor::kContext)); AsyncGeneratorEnqueue(&args, context, generator, value, JSAsyncGeneratorObject::kNext, "[AsyncGenerator].prototype.next"); } // https://tc39.github.io/proposal-async-iteration/ // Section #sec-asyncgenerator-prototype-return TF_BUILTIN(AsyncGeneratorPrototypeReturn, AsyncGeneratorBuiltinsAssembler) { const int kValueArg = 0; TNode argc = ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); CodeStubArguments args(this, argc); TNode generator = args.GetReceiver(); TNode value = args.GetOptionalArgumentValue(kValueArg); TNode context = CAST(Parameter(Descriptor::kContext)); AsyncGeneratorEnqueue(&args, context, generator, value, JSAsyncGeneratorObject::kReturn, "[AsyncGenerator].prototype.return"); } // https://tc39.github.io/proposal-async-iteration/ // Section #sec-asyncgenerator-prototype-throw TF_BUILTIN(AsyncGeneratorPrototypeThrow, AsyncGeneratorBuiltinsAssembler) { const int kValueArg = 0; TNode argc = ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); CodeStubArguments args(this, argc); TNode generator = args.GetReceiver(); TNode value = args.GetOptionalArgumentValue(kValueArg); TNode context = CAST(Parameter(Descriptor::kContext)); AsyncGeneratorEnqueue(&args, context, generator, value, JSAsyncGeneratorObject::kThrow, "[AsyncGenerator].prototype.throw"); } TF_BUILTIN(AsyncGeneratorAwaitResolveClosure, AsyncGeneratorBuiltinsAssembler) { TNode value = CAST(Parameter(Descriptor::kValue)); TNode context = CAST(Parameter(Descriptor::kContext)); AsyncGeneratorAwaitResumeClosure(context, value, JSAsyncGeneratorObject::kNext); } TF_BUILTIN(AsyncGeneratorAwaitRejectClosure, AsyncGeneratorBuiltinsAssembler) { TNode value = CAST(Parameter(Descriptor::kValue)); TNode context = CAST(Parameter(Descriptor::kContext)); AsyncGeneratorAwaitResumeClosure(context, value, JSAsyncGeneratorObject::kThrow); } TF_BUILTIN(AsyncGeneratorAwaitUncaught, AsyncGeneratorBuiltinsAssembler) { const bool kIsCatchable = false; AsyncGeneratorAwait(kIsCatchable); } TF_BUILTIN(AsyncGeneratorAwaitCaught, AsyncGeneratorBuiltinsAssembler) { const bool kIsCatchable = true; AsyncGeneratorAwait(kIsCatchable); } TF_BUILTIN(AsyncGeneratorResumeNext, AsyncGeneratorBuiltinsAssembler) { using Descriptor = AsyncGeneratorResumeNextDescriptor; const TNode generator = CAST(Parameter(Descriptor::kGenerator)); const TNode context = CAST(Parameter(Descriptor::kContext)); // The penultimate step of proposal-async-iteration/#sec-asyncgeneratorresolve // and proposal-async-iteration/#sec-asyncgeneratorreject both recursively // invoke AsyncGeneratorResumeNext() again. // // This implementation does not implement this recursively, but instead // performs a loop in AsyncGeneratorResumeNext, which continues as long as // there is an AsyncGeneratorRequest in the queue, and as long as the // generator is not suspended due to an AwaitExpression. TVARIABLE(Smi, var_state, LoadGeneratorState(generator)); TVARIABLE(HeapObject, var_next, LoadFirstAsyncGeneratorRequestFromQueue(generator)); Label start(this, {&var_state, &var_next}); Goto(&start); BIND(&start); CSA_ASSERT(this, IsGeneratorNotExecuting(generator)); // Stop resuming if suspended for Await. ReturnIf(IsGeneratorAwaiting(generator), UndefinedConstant()); // Stop resuming if request queue is empty. ReturnIf(IsUndefined(var_next.value()), UndefinedConstant()); const TNode next = CAST(var_next.value()); const TNode resume_type = LoadResumeTypeFromAsyncGeneratorRequest(next); Label if_abrupt(this), if_normal(this), resume_generator(this); Branch(IsAbruptResumeType(resume_type), &if_abrupt, &if_normal); BIND(&if_abrupt); { Label settle_promise(this), if_return(this), if_throw(this); GotoIfNot(IsGeneratorStateSuspendedAtStart(var_state.value()), &settle_promise); CloseGenerator(generator); var_state = SmiConstant(JSGeneratorObject::kGeneratorClosed); Goto(&settle_promise); BIND(&settle_promise); TNode next_value = LoadValueFromAsyncGeneratorRequest(next); Branch(SmiEqual(resume_type, SmiConstant(JSGeneratorObject::kReturn)), &if_return, &if_throw); BIND(&if_return); // For "return" completions, await the sent value. If the Await succeeds, // and the generator is not closed, resume the generator with a "return" // completion to allow `finally` blocks to be evaluated. Otherwise, perform // AsyncGeneratorResolve(awaitedValue, true). If the await fails and the // generator is not closed, resume the generator with a "throw" completion. // If the generator was closed, perform AsyncGeneratorReject(thrownValue). // In all cases, the last step is to call AsyncGeneratorResumeNext. TNode is_caught = CallRuntime( Runtime::kAsyncGeneratorHasCatchHandlerForPC, context, generator); TailCallBuiltin(Builtins::kAsyncGeneratorReturn, context, generator, next_value, is_caught); BIND(&if_throw); GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator); CallBuiltin(Builtins::kAsyncGeneratorReject, context, generator, next_value); var_next = LoadFirstAsyncGeneratorRequestFromQueue(generator); Goto(&start); } BIND(&if_normal); { GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator); CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator, UndefinedConstant(), TrueConstant()); var_state = LoadGeneratorState(generator); var_next = LoadFirstAsyncGeneratorRequestFromQueue(generator); Goto(&start); } BIND(&resume_generator); { // Remember the {resume_type} for the {generator}. StoreObjectFieldNoWriteBarrier( generator, JSGeneratorObject::kResumeModeOffset, resume_type); CallStub(CodeFactory::ResumeGenerator(isolate()), context, LoadValueFromAsyncGeneratorRequest(next), generator); var_state = LoadGeneratorState(generator); var_next = LoadFirstAsyncGeneratorRequestFromQueue(generator); Goto(&start); } } TF_BUILTIN(AsyncGeneratorResolve, AsyncGeneratorBuiltinsAssembler) { const TNode generator = CAST(Parameter(Descriptor::kGenerator)); const TNode value = CAST(Parameter(Descriptor::kValue)); const TNode done = CAST(Parameter(Descriptor::kDone)); const TNode context = CAST(Parameter(Descriptor::kContext)); CSA_ASSERT(this, Word32BinaryNot(IsGeneratorAwaiting(generator))); // This operation should be called only when the `value` parameter has been // Await-ed. Typically, this means `value` is not a JSPromise value. However, // it may be a JSPromise value whose "then" method has been overridden to a // non-callable value. This can't be checked with assertions due to being // observable, but keep it in mind. const TNode next = TakeFirstAsyncGeneratorRequestFromQueue(generator); const TNode promise = LoadPromiseFromAsyncGeneratorRequest(next); // Let iteratorResult be CreateIterResultObject(value, done). const TNode iter_result = Allocate(JSIteratorResult::kSize); { TNode map = LoadContextElement(LoadNativeContext(context), Context::ITERATOR_RESULT_MAP_INDEX); StoreMapNoWriteBarrier(iter_result, map); StoreObjectFieldRoot(iter_result, JSIteratorResult::kPropertiesOrHashOffset, RootIndex::kEmptyFixedArray); StoreObjectFieldRoot(iter_result, JSIteratorResult::kElementsOffset, RootIndex::kEmptyFixedArray); StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kValueOffset, value); StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kDoneOffset, done); } // We know that {iter_result} itself doesn't have any "then" property (a // freshly allocated IterResultObject only has "value" and "done" properties) // and we also know that the [[Prototype]] of {iter_result} is the intrinsic // %ObjectPrototype%. So we can skip the [[Resolve]] logic here completely // and directly call into the FulfillPromise operation if we can prove // that the %ObjectPrototype% also doesn't have any "then" property. This // is guarded by the Promise#then() protector. // If the PromiseHooks are enabled, we cannot take the shortcut here, since // the "promiseResolve" hook would not be fired otherwise. Label if_fast(this), if_slow(this, Label::kDeferred), return_promise(this); GotoIfForceSlowPath(&if_slow); GotoIf(IsPromiseHookEnabled(), &if_slow); Branch(IsPromiseThenProtectorCellInvalid(), &if_slow, &if_fast); BIND(&if_fast); { // Skip the "then" on {iter_result} and directly fulfill the {promise} // with the {iter_result}. CallBuiltin(Builtins::kFulfillPromise, context, promise, iter_result); Goto(&return_promise); } BIND(&if_slow); { // Perform Call(promiseCapability.[[Resolve]], undefined, «iteratorResult»). CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result); Goto(&return_promise); } // Per spec, AsyncGeneratorResolve() returns undefined. However, for the // benefit of %TraceExit(), return the Promise. BIND(&return_promise); Return(promise); } TF_BUILTIN(AsyncGeneratorReject, AsyncGeneratorBuiltinsAssembler) { using Descriptor = AsyncGeneratorRejectDescriptor; const TNode generator = CAST(Parameter(Descriptor::kGenerator)); const TNode value = CAST(Parameter(Descriptor::kValue)); const TNode context = CAST(Parameter(Descriptor::kContext)); TNode next = TakeFirstAsyncGeneratorRequestFromQueue(generator); TNode promise = LoadPromiseFromAsyncGeneratorRequest(next); Return(CallBuiltin(Builtins::kRejectPromise, context, promise, value, TrueConstant())); } TF_BUILTIN(AsyncGeneratorYield, AsyncGeneratorBuiltinsAssembler) { const TNode generator = CAST(Parameter(Descriptor::kGenerator)); const TNode value = CAST(Parameter(Descriptor::kValue)); const TNode is_caught = CAST(Parameter(Descriptor::kIsCaught)); const TNode context = CAST(Parameter(Descriptor::kContext)); const TNode request = CAST(LoadFirstAsyncGeneratorRequestFromQueue(generator)); const TNode outer_promise = LoadPromiseFromAsyncGeneratorRequest(request); const int on_resolve = Context::ASYNC_GENERATOR_YIELD_RESOLVE_SHARED_FUN; const int on_reject = Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN; SetGeneratorAwaiting(generator); Await(context, generator, value, outer_promise, on_resolve, on_reject, is_caught); Return(UndefinedConstant()); } TF_BUILTIN(AsyncGeneratorYieldResolveClosure, AsyncGeneratorBuiltinsAssembler) { const TNode context = CAST(Parameter(Descriptor::kContext)); const TNode value = CAST(Parameter(Descriptor::kValue)); const TNode generator = CAST(LoadContextElement(context, Context::EXTENSION_INDEX)); SetGeneratorNotAwaiting(generator); // Per proposal-async-iteration/#sec-asyncgeneratoryield step 9 // Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *false*). CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator, value, FalseConstant()); TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator); } TF_BUILTIN(AsyncGeneratorReturn, AsyncGeneratorBuiltinsAssembler) { // AsyncGeneratorReturn is called when resuming requests with "return" resume // modes. It is similar to AsyncGeneratorAwait(), but selects different // resolve/reject closures depending on whether or not the generator is marked // as closed. // // In particular, non-closed generators will resume the generator with either // "return" or "throw" resume modes, allowing finally blocks or catch blocks // to be evaluated, as if the `await` were performed within the body of the // generator. (per proposal-async-iteration/#sec-asyncgeneratoryield step 8.b) // // Closed generators do not resume the generator in the resolve/reject // closures, but instead simply perform AsyncGeneratorResolve or // AsyncGeneratorReject with the awaited value // (per proposal-async-iteration/#sec-asyncgeneratorresumenext step 10.b.i) // // In all cases, the final step is to jump back to AsyncGeneratorResumeNext. const TNode generator = CAST(Parameter(Descriptor::kGenerator)); const TNode value = CAST(Parameter(Descriptor::kValue)); const TNode is_caught = CAST(Parameter(Descriptor::kIsCaught)); const TNode req = CAST(LoadFirstAsyncGeneratorRequestFromQueue(generator)); Label perform_await(this); TVARIABLE(IntPtrT, var_on_resolve, IntPtrConstant( Context::ASYNC_GENERATOR_RETURN_CLOSED_RESOLVE_SHARED_FUN)); TVARIABLE( IntPtrT, var_on_reject, IntPtrConstant(Context::ASYNC_GENERATOR_RETURN_CLOSED_REJECT_SHARED_FUN)); const TNode state = LoadGeneratorState(generator); GotoIf(IsGeneratorStateClosed(state), &perform_await); var_on_resolve = IntPtrConstant(Context::ASYNC_GENERATOR_RETURN_RESOLVE_SHARED_FUN); var_on_reject = IntPtrConstant(Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN); Goto(&perform_await); BIND(&perform_await); SetGeneratorAwaiting(generator); TNode context = CAST(Parameter(Descriptor::kContext)); const TNode outer_promise = LoadPromiseFromAsyncGeneratorRequest(req); Await(context, generator, value, outer_promise, var_on_resolve.value(), var_on_reject.value(), is_caught); Return(UndefinedConstant()); } // On-resolve closure for Await in AsyncGeneratorReturn // Resume the generator with "return" resume_mode, and finally perform // AsyncGeneratorResumeNext. Per // proposal-async-iteration/#sec-asyncgeneratoryield step 8.e TF_BUILTIN(AsyncGeneratorReturnResolveClosure, AsyncGeneratorBuiltinsAssembler) { const TNode context = CAST(Parameter(Descriptor::kContext)); const TNode value = CAST(Parameter(Descriptor::kValue)); AsyncGeneratorAwaitResumeClosure(context, value, JSGeneratorObject::kReturn); } // On-resolve closure for Await in AsyncGeneratorReturn // Perform AsyncGeneratorResolve({awaited_value}, true) and finally perform // AsyncGeneratorResumeNext. TF_BUILTIN(AsyncGeneratorReturnClosedResolveClosure, AsyncGeneratorBuiltinsAssembler) { const TNode context = CAST(Parameter(Descriptor::kContext)); const TNode value = CAST(Parameter(Descriptor::kValue)); const TNode generator = CAST(LoadContextElement(context, Context::EXTENSION_INDEX)); SetGeneratorNotAwaiting(generator); // https://tc39.github.io/proposal-async-iteration/ // #async-generator-resume-next-return-processor-fulfilled step 2: // Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *true*). CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator, value, TrueConstant()); TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator); } TF_BUILTIN(AsyncGeneratorReturnClosedRejectClosure, AsyncGeneratorBuiltinsAssembler) { const TNode context = CAST(Parameter(Descriptor::kContext)); const TNode value = CAST(Parameter(Descriptor::kValue)); const TNode generator = CAST(LoadContextElement(context, Context::EXTENSION_INDEX)); SetGeneratorNotAwaiting(generator); // https://tc39.github.io/proposal-async-iteration/ // #async-generator-resume-next-return-processor-rejected step 2: // Return ! AsyncGeneratorReject(_F_.[[Generator]], _reason_). CallBuiltin(Builtins::kAsyncGeneratorReject, context, generator, value); TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator); } } // namespace internal } // namespace v8