1// Copyright 2016 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/builtins/builtins-async-gen.h" 6 7#include "src/builtins/builtins-utils-gen.h" 8#include "src/heap/factory-inl.h" 9#include "src/objects/js-generator.h" 10#include "src/objects/js-promise.h" 11#include "src/objects/shared-function-info.h" 12 13namespace v8 { 14namespace internal { 15 16namespace { 17// Describe fields of Context associated with the AsyncIterator unwrap closure. 18class ValueUnwrapContext { 19 public: 20 enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength }; 21}; 22 23} // namespace 24 25TNode<Object> AsyncBuiltinsAssembler::Await( 26 TNode<Context> context, TNode<JSGeneratorObject> generator, 27 TNode<Object> value, TNode<JSPromise> outer_promise, 28 TNode<SharedFunctionInfo> on_resolve_sfi, 29 TNode<SharedFunctionInfo> on_reject_sfi, 30 TNode<Oddball> is_predicted_as_caught) { 31 const TNode<NativeContext> native_context = LoadNativeContext(context); 32 33 // We do the `PromiseResolve(%Promise%,value)` avoiding to unnecessarily 34 // create wrapper promises. Now if {value} is already a promise with the 35 // intrinsics %Promise% constructor as its "constructor", we don't need 36 // to allocate the wrapper promise. 37 { 38 TVARIABLE(Object, var_value, value); 39 Label if_slow_path(this, Label::kDeferred), if_done(this), 40 if_slow_constructor(this, Label::kDeferred); 41 GotoIf(TaggedIsSmi(value), &if_slow_path); 42 TNode<HeapObject> value_object = CAST(value); 43 const TNode<Map> value_map = LoadMap(value_object); 44 GotoIfNot(IsJSPromiseMap(value_map), &if_slow_path); 45 // We can skip the "constructor" lookup on {value} if it's [[Prototype]] 46 // is the (initial) Promise.prototype and the @@species protector is 47 // intact, as that guards the lookup path for "constructor" on 48 // JSPromise instances which have the (initial) Promise.prototype. 49 const TNode<Object> promise_prototype = 50 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX); 51 GotoIfNot(TaggedEqual(LoadMapPrototype(value_map), promise_prototype), 52 &if_slow_constructor); 53 Branch(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor, 54 &if_done); 55 56 // At this point, {value} doesn't have the initial promise prototype or 57 // the promise @@species protector was invalidated, but {value} could still 58 // have the %Promise% as its "constructor", so we need to check that as 59 // well. 60 BIND(&if_slow_constructor); 61 { 62 const TNode<Object> value_constructor = GetProperty( 63 context, value, isolate()->factory()->constructor_string()); 64 const TNode<Object> promise_function = 65 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 66 Branch(TaggedEqual(value_constructor, promise_function), &if_done, 67 &if_slow_path); 68 } 69 70 BIND(&if_slow_path); 71 { 72 // We need to mark the {value} wrapper as having {outer_promise} 73 // as its parent, which is why we need to inline a good chunk of 74 // logic from the `PromiseResolve` builtin here. 75 var_value = NewJSPromise(native_context, outer_promise); 76 CallBuiltin(Builtin::kResolvePromise, native_context, var_value.value(), 77 value); 78 Goto(&if_done); 79 } 80 81 BIND(&if_done); 82 value = var_value.value(); 83 } 84 85 static const int kClosureContextSize = 86 FixedArray::SizeFor(Context::MIN_CONTEXT_EXTENDED_SLOTS); 87 TNode<Context> closure_context = 88 UncheckedCast<Context>(AllocateInNewSpace(kClosureContextSize)); 89 { 90 // Initialize the await context, storing the {generator} as extension. 91 TNode<Map> map = CAST( 92 LoadContextElement(native_context, Context::AWAIT_CONTEXT_MAP_INDEX)); 93 StoreMapNoWriteBarrier(closure_context, map); 94 StoreObjectFieldNoWriteBarrier( 95 closure_context, Context::kLengthOffset, 96 SmiConstant(Context::MIN_CONTEXT_EXTENDED_SLOTS)); 97 const TNode<Object> empty_scope_info = 98 LoadContextElement(native_context, Context::SCOPE_INFO_INDEX); 99 StoreContextElementNoWriteBarrier( 100 closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info); 101 StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX, 102 native_context); 103 StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX, 104 generator); 105 } 106 107 // Allocate and initialize resolve handler 108 TNode<HeapObject> on_resolve = 109 AllocateInNewSpace(JSFunction::kSizeWithoutPrototype); 110 InitializeNativeClosure(closure_context, native_context, on_resolve, 111 on_resolve_sfi); 112 113 // Allocate and initialize reject handler 114 TNode<HeapObject> on_reject = 115 AllocateInNewSpace(JSFunction::kSizeWithoutPrototype); 116 InitializeNativeClosure(closure_context, native_context, on_reject, 117 on_reject_sfi); 118 119 // Deal with PromiseHooks and debug support in the runtime. This 120 // also allocates the throwaway promise, which is only needed in 121 // case of PromiseHooks or debugging. 122 TVARIABLE(Object, var_throwaway, UndefinedConstant()); 123 Label if_instrumentation(this, Label::kDeferred), 124 if_instrumentation_done(this); 125 TNode<Uint32T> promiseHookFlags = PromiseHookFlags(); 126 GotoIf(IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate( 127 promiseHookFlags), 128 &if_instrumentation); 129#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS 130 // This call to NewJSPromise is to keep behaviour parity with what happens 131 // in Runtime::kDebugAsyncFunctionSuspended below if native hooks are set. 132 // It creates a throwaway promise that will trigger an init event and get 133 // passed into Builtin::kPerformPromiseThen below. 134 GotoIfNot(IsContextPromiseHookEnabled(promiseHookFlags), 135 &if_instrumentation_done); 136 var_throwaway = NewJSPromise(context, value); 137#endif // V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS 138 Goto(&if_instrumentation_done); 139 BIND(&if_instrumentation); 140 { 141 var_throwaway = CallRuntime(Runtime::kDebugAsyncFunctionSuspended, 142 native_context, value, outer_promise, on_reject, 143 generator, is_predicted_as_caught); 144 Goto(&if_instrumentation_done); 145 } 146 BIND(&if_instrumentation_done); 147 148 return CallBuiltin(Builtin::kPerformPromiseThen, native_context, value, 149 on_resolve, on_reject, var_throwaway.value()); 150} 151 152void AsyncBuiltinsAssembler::InitializeNativeClosure( 153 TNode<Context> context, TNode<NativeContext> native_context, 154 TNode<HeapObject> function, TNode<SharedFunctionInfo> shared_info) { 155 TNode<Map> function_map = CAST(LoadContextElement( 156 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX)); 157 // Ensure that we don't have to initialize prototype_or_initial_map field of 158 // JSFunction. 159 CSA_DCHECK(this, 160 IntPtrEqual(LoadMapInstanceSizeInWords(function_map), 161 IntPtrConstant(JSFunction::kSizeWithoutPrototype / 162 kTaggedSize))); 163 STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize); 164 StoreMapNoWriteBarrier(function, function_map); 165 StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset, 166 RootIndex::kEmptyFixedArray); 167 StoreObjectFieldRoot(function, JSObject::kElementsOffset, 168 RootIndex::kEmptyFixedArray); 169 StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset, 170 RootIndex::kManyClosuresCell); 171 172 StoreObjectFieldNoWriteBarrier( 173 function, JSFunction::kSharedFunctionInfoOffset, shared_info); 174 StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context); 175 176 // For the native closures that are initialized here (for `await`) 177 // we know that their SharedFunctionInfo::function_data(kAcquireLoad) slot 178 // contains a builtin index (as Smi), so there's no need to use 179 // CodeStubAssembler::GetSharedFunctionInfoCode() helper here, 180 // which almost doubles the size of `await` builtins (unnecessarily). 181 TNode<Smi> builtin_id = LoadObjectField<Smi>( 182 shared_info, SharedFunctionInfo::kFunctionDataOffset); 183 TNode<CodeT> code = LoadBuiltin(builtin_id); 184 StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code); 185} 186 187TNode<JSFunction> AsyncBuiltinsAssembler::CreateUnwrapClosure( 188 TNode<NativeContext> native_context, TNode<Oddball> done) { 189 const TNode<Map> map = CAST(LoadContextElement( 190 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX)); 191 const TNode<SharedFunctionInfo> on_fulfilled_shared = 192 AsyncIteratorValueUnwrapSharedFunConstant(); 193 const TNode<Context> closure_context = 194 AllocateAsyncIteratorValueUnwrapContext(native_context, done); 195 return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared, 196 closure_context); 197} 198 199TNode<Context> AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext( 200 TNode<NativeContext> native_context, TNode<Oddball> done) { 201 CSA_DCHECK(this, IsBoolean(done)); 202 203 TNode<Context> context = AllocateSyntheticFunctionContext( 204 native_context, ValueUnwrapContext::kLength); 205 StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot, 206 done); 207 return context; 208} 209 210TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) { 211 auto value = Parameter<Object>(Descriptor::kValue); 212 auto context = Parameter<Context>(Descriptor::kContext); 213 214 const TNode<Object> done = 215 LoadContextElement(context, ValueUnwrapContext::kDoneSlot); 216 CSA_DCHECK(this, IsBoolean(CAST(done))); 217 218 const TNode<Object> unwrapped_value = 219 CallBuiltin(Builtin::kCreateIterResultObject, context, value, done); 220 221 Return(unwrapped_value); 222} 223 224} // namespace internal 225} // namespace v8 226