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-promise.h" 10 #include "src/objects/shared-function-info.h" 11 12 namespace v8 { 13 namespace internal { 14 15 using compiler::Node; 16 17 namespace { 18 // Describe fields of Context associated with the AsyncIterator unwrap closure. 19 class ValueUnwrapContext { 20 public: 21 enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength }; 22 }; 23 24 } // namespace 25 26 Node* AsyncBuiltinsAssembler::Await( 27 Node* context, Node* generator, Node* value, Node* outer_promise, 28 int context_length, const ContextInitializer& init_closure_context, 29 Node* on_resolve_context_index, Node* on_reject_context_index, 30 Node* is_predicted_as_caught) { 31 DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS); 32 33 Node* const native_context = LoadNativeContext(context); 34 35 static const int kWrappedPromiseOffset = FixedArray::SizeFor(context_length); 36 static const int kThrowawayPromiseOffset = 37 kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields; 38 static const int kResolveClosureOffset = 39 kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields; 40 static const int kRejectClosureOffset = 41 kResolveClosureOffset + JSFunction::kSizeWithoutPrototype; 42 static const int kTotalSize = 43 kRejectClosureOffset + JSFunction::kSizeWithoutPrototype; 44 45 Node* const base = AllocateInNewSpace(kTotalSize); 46 Node* const closure_context = base; 47 { 48 // Initialize closure context 49 InitializeFunctionContext(native_context, closure_context, context_length); 50 init_closure_context(closure_context); 51 } 52 53 // Let promiseCapability be ! NewPromiseCapability(%Promise%). 54 Node* const promise_fun = 55 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 56 CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun))); 57 Node* const promise_map = 58 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); 59 // Assert that the JSPromise map has an instance size is 60 // JSPromise::kSizeWithEmbedderFields. 61 CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map), 62 IntPtrConstant(JSPromise::kSizeWithEmbedderFields / 63 kPointerSize))); 64 Node* const wrapped_value = InnerAllocate(base, kWrappedPromiseOffset); 65 { 66 // Initialize Promise 67 StoreMapNoWriteBarrier(wrapped_value, promise_map); 68 InitializeJSObjectFromMap( 69 wrapped_value, promise_map, 70 IntPtrConstant(JSPromise::kSizeWithEmbedderFields)); 71 PromiseInit(wrapped_value); 72 } 73 74 Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset); 75 { 76 // Initialize throwawayPromise 77 StoreMapNoWriteBarrier(throwaway, promise_map); 78 InitializeJSObjectFromMap( 79 throwaway, promise_map, 80 IntPtrConstant(JSPromise::kSizeWithEmbedderFields)); 81 PromiseInit(throwaway); 82 } 83 84 Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset); 85 { 86 // Initialize resolve handler 87 InitializeNativeClosure(closure_context, native_context, on_resolve, 88 on_resolve_context_index); 89 } 90 91 Node* const on_reject = InnerAllocate(base, kRejectClosureOffset); 92 { 93 // Initialize reject handler 94 InitializeNativeClosure(closure_context, native_context, on_reject, 95 on_reject_context_index); 96 } 97 98 { 99 // Add PromiseHooks if needed 100 Label next(this); 101 GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next); 102 CallRuntime(Runtime::kAwaitPromisesInit, context, wrapped_value, 103 outer_promise, throwaway); 104 Goto(&next); 105 BIND(&next); 106 } 107 108 // Perform ! Call(promiseCapability.[[Resolve]], undefined, promise ). 109 CallBuiltin(Builtins::kResolvePromise, context, wrapped_value, value); 110 111 // The Promise will be thrown away and not handled, but it shouldn't trigger 112 // unhandled reject events as its work is done 113 PromiseSetHasHandler(throwaway); 114 115 Label do_perform_promise_then(this); 116 GotoIfNot(IsDebugActive(), &do_perform_promise_then); 117 { 118 Label common(this); 119 GotoIf(TaggedIsSmi(value), &common); 120 GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common); 121 { 122 // Mark the reject handler callback to be a forwarding edge, rather 123 // than a meaningful catch handler 124 Node* const key = 125 HeapConstant(factory()->promise_forwarding_handler_symbol()); 126 SetPropertyStrict(CAST(context), CAST(on_reject), CAST(key), 127 TrueConstant()); 128 129 GotoIf(IsFalse(is_predicted_as_caught), &common); 130 PromiseSetHandledHint(value); 131 } 132 133 Goto(&common); 134 BIND(&common); 135 // Mark the dependency to outer Promise in case the throwaway Promise is 136 // found on the Promise stack 137 CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE)); 138 139 Node* const key = HeapConstant(factory()->promise_handled_by_symbol()); 140 SetPropertyStrict(CAST(context), CAST(throwaway), CAST(key), 141 CAST(outer_promise)); 142 } 143 144 Goto(&do_perform_promise_then); 145 BIND(&do_perform_promise_then); 146 return CallBuiltin(Builtins::kPerformPromiseThen, context, wrapped_value, 147 on_resolve, on_reject, throwaway); 148 } 149 150 Node* AsyncBuiltinsAssembler::AwaitOptimized( 151 Node* context, Node* generator, Node* value, Node* outer_promise, 152 int context_length, const ContextInitializer& init_closure_context, 153 Node* on_resolve_context_index, Node* on_reject_context_index, 154 Node* is_predicted_as_caught) { 155 DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS); 156 157 Node* const native_context = LoadNativeContext(context); 158 Node* const promise_fun = 159 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 160 CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun))); 161 CSA_ASSERT(this, IsConstructor(promise_fun)); 162 163 static const int kThrowawayPromiseOffset = 164 FixedArray::SizeFor(context_length); 165 static const int kResolveClosureOffset = 166 kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields; 167 static const int kRejectClosureOffset = 168 kResolveClosureOffset + JSFunction::kSizeWithoutPrototype; 169 static const int kTotalSize = 170 kRejectClosureOffset + JSFunction::kSizeWithoutPrototype; 171 172 // 2. Let promise be ? PromiseResolve( promise ). 173 Node* const promise = 174 CallBuiltin(Builtins::kPromiseResolve, context, promise_fun, value); 175 176 Node* const base = AllocateInNewSpace(kTotalSize); 177 Node* const closure_context = base; 178 { 179 // Initialize closure context 180 InitializeFunctionContext(native_context, closure_context, context_length); 181 init_closure_context(closure_context); 182 } 183 184 Node* const promise_map = 185 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); 186 // Assert that the JSPromise map has an instance size is 187 // JSPromise::kSizeWithEmbedderFields. 188 CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map), 189 IntPtrConstant(JSPromise::kSizeWithEmbedderFields / 190 kPointerSize))); 191 Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset); 192 { 193 // Initialize throwawayPromise 194 StoreMapNoWriteBarrier(throwaway, promise_map); 195 InitializeJSObjectFromMap( 196 throwaway, promise_map, 197 IntPtrConstant(JSPromise::kSizeWithEmbedderFields)); 198 PromiseInit(throwaway); 199 } 200 201 Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset); 202 { 203 // Initialize resolve handler 204 InitializeNativeClosure(closure_context, native_context, on_resolve, 205 on_resolve_context_index); 206 } 207 208 Node* const on_reject = InnerAllocate(base, kRejectClosureOffset); 209 { 210 // Initialize reject handler 211 InitializeNativeClosure(closure_context, native_context, on_reject, 212 on_reject_context_index); 213 } 214 215 { 216 // Add PromiseHooks if needed 217 Label next(this); 218 GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next); 219 CallRuntime(Runtime::kAwaitPromisesInit, context, promise, outer_promise, 220 throwaway); 221 Goto(&next); 222 BIND(&next); 223 } 224 225 // The Promise will be thrown away and not handled, but it shouldn't trigger 226 // unhandled reject events as its work is done 227 PromiseSetHasHandler(throwaway); 228 229 Label do_perform_promise_then(this); 230 GotoIfNot(IsDebugActive(), &do_perform_promise_then); 231 { 232 Label common(this); 233 GotoIf(TaggedIsSmi(value), &common); 234 GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common); 235 { 236 // Mark the reject handler callback to be a forwarding edge, rather 237 // than a meaningful catch handler 238 Node* const key = 239 HeapConstant(factory()->promise_forwarding_handler_symbol()); 240 SetPropertyStrict(CAST(context), CAST(on_reject), CAST(key), 241 TrueConstant()); 242 243 GotoIf(IsFalse(is_predicted_as_caught), &common); 244 PromiseSetHandledHint(value); 245 } 246 247 Goto(&common); 248 BIND(&common); 249 // Mark the dependency to outer Promise in case the throwaway Promise is 250 // found on the Promise stack 251 CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE)); 252 253 Node* const key = HeapConstant(factory()->promise_handled_by_symbol()); 254 SetPropertyStrict(CAST(context), CAST(throwaway), CAST(key), 255 CAST(outer_promise)); 256 } 257 258 Goto(&do_perform_promise_then); 259 BIND(&do_perform_promise_then); 260 return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise, 261 on_resolve, on_reject, throwaway); 262 } 263 264 void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context, 265 Node* native_context, 266 Node* function, 267 Node* context_index) { 268 Node* const function_map = LoadContextElement( 269 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 270 // Ensure that we don't have to initialize prototype_or_initial_map field of 271 // JSFunction. 272 CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(function_map), 273 IntPtrConstant(JSFunction::kSizeWithoutPrototype / 274 kPointerSize))); 275 STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize); 276 StoreMapNoWriteBarrier(function, function_map); 277 StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset, 278 Heap::kEmptyFixedArrayRootIndex); 279 StoreObjectFieldRoot(function, JSObject::kElementsOffset, 280 Heap::kEmptyFixedArrayRootIndex); 281 StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset, 282 Heap::kManyClosuresCellRootIndex); 283 284 Node* shared_info = LoadContextElement(native_context, context_index); 285 CSA_ASSERT(this, IsSharedFunctionInfo(shared_info)); 286 StoreObjectFieldNoWriteBarrier( 287 function, JSFunction::kSharedFunctionInfoOffset, shared_info); 288 StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context); 289 290 Node* const code = GetSharedFunctionInfoCode(shared_info); 291 StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code); 292 } 293 294 Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context, 295 Node* done) { 296 Node* const map = LoadContextElement( 297 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); 298 Node* const on_fulfilled_shared = LoadContextElement( 299 native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN); 300 CSA_ASSERT(this, 301 HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE)); 302 Node* const closure_context = 303 AllocateAsyncIteratorValueUnwrapContext(native_context, done); 304 return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared, 305 closure_context); 306 } 307 308 Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext( 309 Node* native_context, Node* done) { 310 CSA_ASSERT(this, IsNativeContext(native_context)); 311 CSA_ASSERT(this, IsBoolean(done)); 312 313 Node* const context = 314 CreatePromiseContext(native_context, ValueUnwrapContext::kLength); 315 StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot, 316 done); 317 return context; 318 } 319 320 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) { 321 Node* const value = Parameter(Descriptor::kValue); 322 Node* const context = Parameter(Descriptor::kContext); 323 324 Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot); 325 CSA_ASSERT(this, IsBoolean(done)); 326 327 Node* const unwrapped_value = 328 CallBuiltin(Builtins::kCreateIterResultObject, context, value, done); 329 330 Return(unwrapped_value); 331 } 332 333 } // namespace internal 334 } // namespace v8 335