// Copyright 2016 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/factory-inl.h" #include "src/objects/shared-function-info.h" namespace v8 { namespace internal { using compiler::Node; namespace { // Describe fields of Context associated with the AsyncIterator unwrap closure. class ValueUnwrapContext { public: enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength }; }; } // namespace Node* AsyncBuiltinsAssembler::Await( Node* context, Node* generator, Node* value, Node* outer_promise, int context_length, const ContextInitializer& init_closure_context, Node* on_resolve_context_index, Node* on_reject_context_index, Node* is_predicted_as_caught) { DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS); Node* const native_context = LoadNativeContext(context); static const int kWrappedPromiseOffset = FixedArray::SizeFor(context_length); static const int kThrowawayPromiseOffset = kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields; static const int kResolveClosureOffset = kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields; static const int kRejectClosureOffset = kResolveClosureOffset + JSFunction::kSizeWithoutPrototype; static const int kTotalSize = kRejectClosureOffset + JSFunction::kSizeWithoutPrototype; Node* const base = AllocateInNewSpace(kTotalSize); Node* const closure_context = base; { // Initialize closure context InitializeFunctionContext(native_context, closure_context, context_length); init_closure_context(closure_context); } // Let promiseCapability be ! NewPromiseCapability(%Promise%). Node* const promise_fun = LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun))); Node* const promise_map = LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); // Assert that the JSPromise map has an instance size is // JSPromise::kSizeWithEmbedderFields. CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map), IntPtrConstant(JSPromise::kSizeWithEmbedderFields / kPointerSize))); Node* const wrapped_value = InnerAllocate(base, kWrappedPromiseOffset); { // Initialize Promise StoreMapNoWriteBarrier(wrapped_value, promise_map); InitializeJSObjectFromMap( wrapped_value, promise_map, IntPtrConstant(JSPromise::kSizeWithEmbedderFields)); PromiseInit(wrapped_value); } Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset); { // Initialize throwawayPromise StoreMapNoWriteBarrier(throwaway, promise_map); InitializeJSObjectFromMap( throwaway, promise_map, IntPtrConstant(JSPromise::kSizeWithEmbedderFields)); PromiseInit(throwaway); } Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset); { // Initialize resolve handler InitializeNativeClosure(closure_context, native_context, on_resolve, on_resolve_context_index); } Node* const on_reject = InnerAllocate(base, kRejectClosureOffset); { // Initialize reject handler InitializeNativeClosure(closure_context, native_context, on_reject, on_reject_context_index); } { // Add PromiseHooks if needed Label next(this); GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &next); CallRuntime(Runtime::kPromiseHookInit, context, wrapped_value, outer_promise); CallRuntime(Runtime::kPromiseHookInit, context, throwaway, wrapped_value); Goto(&next); BIND(&next); } // Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »). CallBuiltin(Builtins::kResolveNativePromise, context, wrapped_value, value); // The Promise will be thrown away and not handled, but it shouldn't trigger // unhandled reject events as its work is done PromiseSetHasHandler(throwaway); Label do_perform_promise_then(this); GotoIfNot(IsDebugActive(), &do_perform_promise_then); { Label common(this); GotoIf(TaggedIsSmi(value), &common); GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common); { // Mark the reject handler callback to be a forwarding edge, rather // than a meaningful catch handler Node* const key = HeapConstant(factory()->promise_forwarding_handler_symbol()); CallRuntime(Runtime::kSetProperty, context, on_reject, key, TrueConstant(), SmiConstant(LanguageMode::kStrict)); GotoIf(IsFalse(is_predicted_as_caught), &common); PromiseSetHandledHint(value); } Goto(&common); BIND(&common); // Mark the dependency to outer Promise in case the throwaway Promise is // found on the Promise stack CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE)); Node* const key = HeapConstant(factory()->promise_handled_by_symbol()); CallRuntime(Runtime::kSetProperty, context, throwaway, key, outer_promise, SmiConstant(LanguageMode::kStrict)); } Goto(&do_perform_promise_then); BIND(&do_perform_promise_then); CallBuiltin(Builtins::kPerformNativePromiseThen, context, wrapped_value, on_resolve, on_reject, throwaway); return wrapped_value; } void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context, Node* native_context, Node* function, Node* context_index) { Node* const function_map = LoadContextElement( native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); // Ensure that we don't have to initialize prototype_or_initial_map field of // JSFunction. CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(function_map), IntPtrConstant(JSFunction::kSizeWithoutPrototype / kPointerSize))); STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize); StoreMapNoWriteBarrier(function, function_map); StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset, Heap::kEmptyFixedArrayRootIndex); StoreObjectFieldRoot(function, JSObject::kElementsOffset, Heap::kEmptyFixedArrayRootIndex); StoreObjectFieldRoot(function, JSFunction::kFeedbackVectorOffset, Heap::kUndefinedCellRootIndex); Node* shared_info = LoadContextElement(native_context, context_index); CSA_ASSERT(this, IsSharedFunctionInfo(shared_info)); StoreObjectFieldNoWriteBarrier( function, JSFunction::kSharedFunctionInfoOffset, shared_info); StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context); Node* const code = LoadObjectField(shared_info, SharedFunctionInfo::kCodeOffset); StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code); } Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context, Node* done) { Node* const map = LoadContextElement( native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); Node* const on_fulfilled_shared = LoadContextElement( native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN); CSA_ASSERT(this, HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE)); Node* const closure_context = AllocateAsyncIteratorValueUnwrapContext(native_context, done); return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared, closure_context); } Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext( Node* native_context, Node* done) { CSA_ASSERT(this, IsNativeContext(native_context)); CSA_ASSERT(this, IsBoolean(done)); Node* const context = CreatePromiseContext(native_context, ValueUnwrapContext::kLength); StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot, done); return context; } TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) { Node* const value = Parameter(Descriptor::kValue); Node* const context = Parameter(Descriptor::kContext); Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot); CSA_ASSERT(this, IsBoolean(done)); Node* const unwrapped_value = CallBuiltin(Builtins::kCreateIterResultObject, context, value, done); Return(unwrapped_value); } } // namespace internal } // namespace v8