1// Copyright 2017 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#include "src/builtins/builtins-utils-gen.h" 7#include "src/builtins/builtins.h" 8#include "src/codegen/code-stub-assembler.h" 9#include "src/objects/js-generator.h" 10#include "src/objects/js-promise.h" 11#include "src/objects/objects-inl.h" 12 13namespace v8 { 14namespace internal { 15 16class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler { 17 public: 18 explicit AsyncFunctionBuiltinsAssembler(compiler::CodeAssemblerState* state) 19 : AsyncBuiltinsAssembler(state) {} 20 21 protected: 22 template <typename Descriptor> 23 void AsyncFunctionAwait(const bool is_predicted_as_caught); 24 25 void AsyncFunctionAwaitResumeClosure( 26 const TNode<Context> context, const TNode<Object> sent_value, 27 JSGeneratorObject::ResumeMode resume_mode); 28}; 29 30void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitResumeClosure( 31 TNode<Context> context, TNode<Object> sent_value, 32 JSGeneratorObject::ResumeMode resume_mode) { 33 DCHECK(resume_mode == JSGeneratorObject::kNext || 34 resume_mode == JSGeneratorObject::kThrow); 35 36 TNode<JSAsyncFunctionObject> async_function_object = 37 CAST(LoadContextElement(context, Context::EXTENSION_INDEX)); 38 39 // Push the promise for the {async_function_object} back onto the catch 40 // prediction stack to handle exceptions thrown after resuming from the 41 // await properly. 42 Label if_instrumentation(this, Label::kDeferred), 43 if_instrumentation_done(this); 44 Branch(IsDebugActive(), &if_instrumentation, &if_instrumentation_done); 45 BIND(&if_instrumentation); 46 { 47 TNode<JSPromise> promise = LoadObjectField<JSPromise>( 48 async_function_object, JSAsyncFunctionObject::kPromiseOffset); 49 CallRuntime(Runtime::kDebugPushPromise, context, promise); 50 Goto(&if_instrumentation_done); 51 } 52 BIND(&if_instrumentation_done); 53 54 // Inline version of GeneratorPrototypeNext / GeneratorPrototypeReturn with 55 // unnecessary runtime checks removed. 56 57 // Ensure that the {async_function_object} is neither closed nor running. 58 CSA_SLOW_DCHECK( 59 this, SmiGreaterThan( 60 LoadObjectField<Smi>(async_function_object, 61 JSGeneratorObject::kContinuationOffset), 62 SmiConstant(JSGeneratorObject::kGeneratorClosed))); 63 64 // Remember the {resume_mode} for the {async_function_object}. 65 StoreObjectFieldNoWriteBarrier(async_function_object, 66 JSGeneratorObject::kResumeModeOffset, 67 SmiConstant(resume_mode)); 68 69 // Resume the {receiver} using our trampoline. 70 Callable callable = CodeFactory::ResumeGenerator(isolate()); 71 CallStub(callable, context, sent_value, async_function_object); 72 73 // The resulting Promise is a throwaway, so it doesn't matter what it 74 // resolves to. What is important is that we don't end up keeping the 75 // whole chain of intermediate Promises alive by returning the return value 76 // of ResumeGenerator, as that would create a memory leak. 77} 78 79TF_BUILTIN(AsyncFunctionEnter, AsyncFunctionBuiltinsAssembler) { 80 auto closure = Parameter<JSFunction>(Descriptor::kClosure); 81 auto receiver = Parameter<Object>(Descriptor::kReceiver); 82 auto context = Parameter<Context>(Descriptor::kContext); 83 84 // Compute the number of registers and parameters. 85 TNode<SharedFunctionInfo> shared = LoadObjectField<SharedFunctionInfo>( 86 closure, JSFunction::kSharedFunctionInfoOffset); 87 TNode<IntPtrT> formal_parameter_count = ChangeInt32ToIntPtr( 88 LoadSharedFunctionInfoFormalParameterCountWithoutReceiver(shared)); 89 TNode<BytecodeArray> bytecode_array = 90 LoadSharedFunctionInfoBytecodeArray(shared); 91 TNode<IntPtrT> frame_size = ChangeInt32ToIntPtr(LoadObjectField<Uint32T>( 92 bytecode_array, BytecodeArray::kFrameSizeOffset)); 93 TNode<IntPtrT> parameters_and_register_length = 94 Signed(IntPtrAdd(WordSar(frame_size, IntPtrConstant(kTaggedSizeLog2)), 95 formal_parameter_count)); 96 97 // Allocate and initialize the register file. 98 TNode<FixedArrayBase> parameters_and_registers = 99 AllocateFixedArray(HOLEY_ELEMENTS, parameters_and_register_length, 100 AllocationFlag::kAllowLargeObjectAllocation); 101 FillFixedArrayWithValue(HOLEY_ELEMENTS, parameters_and_registers, 102 IntPtrConstant(0), parameters_and_register_length, 103 RootIndex::kUndefinedValue); 104 105 // Allocate and initialize the promise. 106 TNode<JSPromise> promise = NewJSPromise(context); 107 108 // Allocate and initialize the async function object. 109 TNode<NativeContext> native_context = LoadNativeContext(context); 110 TNode<Map> async_function_object_map = CAST(LoadContextElement( 111 native_context, Context::ASYNC_FUNCTION_OBJECT_MAP_INDEX)); 112 TNode<JSAsyncFunctionObject> async_function_object = 113 UncheckedCast<JSAsyncFunctionObject>( 114 AllocateInNewSpace(JSAsyncFunctionObject::kHeaderSize)); 115 StoreMapNoWriteBarrier(async_function_object, async_function_object_map); 116 StoreObjectFieldRoot(async_function_object, 117 JSAsyncFunctionObject::kPropertiesOrHashOffset, 118 RootIndex::kEmptyFixedArray); 119 StoreObjectFieldRoot(async_function_object, 120 JSAsyncFunctionObject::kElementsOffset, 121 RootIndex::kEmptyFixedArray); 122 StoreObjectFieldNoWriteBarrier( 123 async_function_object, JSAsyncFunctionObject::kFunctionOffset, closure); 124 StoreObjectFieldNoWriteBarrier( 125 async_function_object, JSAsyncFunctionObject::kContextOffset, context); 126 StoreObjectFieldNoWriteBarrier( 127 async_function_object, JSAsyncFunctionObject::kReceiverOffset, receiver); 128 StoreObjectFieldNoWriteBarrier(async_function_object, 129 JSAsyncFunctionObject::kInputOrDebugPosOffset, 130 SmiConstant(0)); 131 StoreObjectFieldNoWriteBarrier(async_function_object, 132 JSAsyncFunctionObject::kResumeModeOffset, 133 SmiConstant(JSAsyncFunctionObject::kNext)); 134 StoreObjectFieldNoWriteBarrier( 135 async_function_object, JSAsyncFunctionObject::kContinuationOffset, 136 SmiConstant(JSAsyncFunctionObject::kGeneratorExecuting)); 137 StoreObjectFieldNoWriteBarrier( 138 async_function_object, 139 JSAsyncFunctionObject::kParametersAndRegistersOffset, 140 parameters_and_registers); 141 StoreObjectFieldNoWriteBarrier( 142 async_function_object, JSAsyncFunctionObject::kPromiseOffset, promise); 143 144 // While we are executing an async function, we need to have the implicit 145 // promise on the stack to get the catch prediction right, even before we 146 // awaited for the first time. 147 Label if_debugging(this); 148 GotoIf(IsDebugActive(), &if_debugging); 149 Return(async_function_object); 150 151 BIND(&if_debugging); 152 CallRuntime(Runtime::kDebugPushPromise, context, promise); 153 Return(async_function_object); 154} 155 156TF_BUILTIN(AsyncFunctionReject, AsyncFunctionBuiltinsAssembler) { 157 auto async_function_object = 158 Parameter<JSAsyncFunctionObject>(Descriptor::kAsyncFunctionObject); 159 auto reason = Parameter<Object>(Descriptor::kReason); 160 auto context = Parameter<Context>(Descriptor::kContext); 161 TNode<JSPromise> promise = LoadObjectField<JSPromise>( 162 async_function_object, JSAsyncFunctionObject::kPromiseOffset); 163 164 // Reject the {promise} for the given {reason}, disabling the 165 // additional debug event for the rejection since a debug event 166 // already happend for the exception that got us here. 167 CallBuiltin(Builtin::kRejectPromise, context, promise, reason, 168 FalseConstant()); 169 170 Label if_debugging(this); 171 GotoIf(IsDebugActive(), &if_debugging); 172 Return(promise); 173 174 BIND(&if_debugging); 175 CallRuntime(Runtime::kDebugPopPromise, context); 176 Return(promise); 177} 178 179TF_BUILTIN(AsyncFunctionResolve, AsyncFunctionBuiltinsAssembler) { 180 auto async_function_object = 181 Parameter<JSAsyncFunctionObject>(Descriptor::kAsyncFunctionObject); 182 auto value = Parameter<Object>(Descriptor::kValue); 183 auto context = Parameter<Context>(Descriptor::kContext); 184 TNode<JSPromise> promise = LoadObjectField<JSPromise>( 185 async_function_object, JSAsyncFunctionObject::kPromiseOffset); 186 187 CallBuiltin(Builtin::kResolvePromise, context, promise, value); 188 189 Label if_debugging(this); 190 GotoIf(IsDebugActive(), &if_debugging); 191 Return(promise); 192 193 BIND(&if_debugging); 194 CallRuntime(Runtime::kDebugPopPromise, context); 195 Return(promise); 196} 197 198// AsyncFunctionReject and AsyncFunctionResolve are both required to return 199// the promise instead of the result of RejectPromise or ResolvePromise 200// respectively from a lazy deoptimization. 201TF_BUILTIN(AsyncFunctionLazyDeoptContinuation, AsyncFunctionBuiltinsAssembler) { 202 auto promise = Parameter<JSPromise>(Descriptor::kPromise); 203 Return(promise); 204} 205 206TF_BUILTIN(AsyncFunctionAwaitRejectClosure, AsyncFunctionBuiltinsAssembler) { 207 CSA_DCHECK_JS_ARGC_EQ(this, 1); 208 const auto sentError = Parameter<Object>(Descriptor::kSentError); 209 const auto context = Parameter<Context>(Descriptor::kContext); 210 211 AsyncFunctionAwaitResumeClosure(context, sentError, 212 JSGeneratorObject::kThrow); 213 Return(UndefinedConstant()); 214} 215 216TF_BUILTIN(AsyncFunctionAwaitResolveClosure, AsyncFunctionBuiltinsAssembler) { 217 CSA_DCHECK_JS_ARGC_EQ(this, 1); 218 const auto sentValue = Parameter<Object>(Descriptor::kSentValue); 219 const auto context = Parameter<Context>(Descriptor::kContext); 220 221 AsyncFunctionAwaitResumeClosure(context, sentValue, JSGeneratorObject::kNext); 222 Return(UndefinedConstant()); 223} 224 225// ES#abstract-ops-async-function-await 226// AsyncFunctionAwait ( value ) 227// Shared logic for the core of await. The parser desugars 228// await value 229// into 230// yield AsyncFunctionAwait{Caught,Uncaught}(.generator_object, value) 231// The 'value' parameter is the value; the .generator_object stands in 232// for the asyncContext. 233template <typename Descriptor> 234void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait( 235 const bool is_predicted_as_caught) { 236 auto async_function_object = 237 Parameter<JSAsyncFunctionObject>(Descriptor::kAsyncFunctionObject); 238 auto value = Parameter<Object>(Descriptor::kValue); 239 auto context = Parameter<Context>(Descriptor::kContext); 240 241 TNode<SharedFunctionInfo> on_resolve_sfi = 242 AsyncFunctionAwaitResolveSharedFunConstant(); 243 TNode<SharedFunctionInfo> on_reject_sfi = 244 AsyncFunctionAwaitRejectSharedFunConstant(); 245 TNode<JSPromise> outer_promise = LoadObjectField<JSPromise>( 246 async_function_object, JSAsyncFunctionObject::kPromiseOffset); 247 Await(context, async_function_object, value, outer_promise, on_resolve_sfi, 248 on_reject_sfi, is_predicted_as_caught); 249 250 // Return outer promise to avoid adding an load of the outer promise before 251 // suspending in BytecodeGenerator. 252 Return(outer_promise); 253} 254 255// Called by the parser from the desugaring of 'await' when catch 256// prediction indicates that there is a locally surrounding catch block. 257TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) { 258 static const bool kIsPredictedAsCaught = true; 259 AsyncFunctionAwait<Descriptor>(kIsPredictedAsCaught); 260} 261 262// Called by the parser from the desugaring of 'await' when catch 263// prediction indicates no locally surrounding catch block. 264TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) { 265 static const bool kIsPredictedAsCaught = false; 266 AsyncFunctionAwait<Descriptor>(kIsPredictedAsCaught); 267} 268 269} // namespace internal 270} // namespace v8 271