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
13 namespace v8 {
14 namespace internal {
15
16 class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler {
17 public:
AsyncFunctionBuiltinsAssembler(compiler::CodeAssemblerState * state)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
AsyncFunctionAwaitResumeClosure(TNode<Context> context,TNode<Object> sent_value,JSGeneratorObject::ResumeMode resume_mode)30 void 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
TF_BUILTIN(AsyncFunctionEnter,AsyncFunctionBuiltinsAssembler)79 TF_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
TF_BUILTIN(AsyncFunctionReject,AsyncFunctionBuiltinsAssembler)156 TF_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
TF_BUILTIN(AsyncFunctionResolve,AsyncFunctionBuiltinsAssembler)179 TF_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.
TF_BUILTIN(AsyncFunctionLazyDeoptContinuation,AsyncFunctionBuiltinsAssembler)201 TF_BUILTIN(AsyncFunctionLazyDeoptContinuation, AsyncFunctionBuiltinsAssembler) {
202 auto promise = Parameter<JSPromise>(Descriptor::kPromise);
203 Return(promise);
204 }
205
TF_BUILTIN(AsyncFunctionAwaitRejectClosure,AsyncFunctionBuiltinsAssembler)206 TF_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
TF_BUILTIN(AsyncFunctionAwaitResolveClosure,AsyncFunctionBuiltinsAssembler)216 TF_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.
233 template <typename Descriptor>
AsyncFunctionAwait(const bool is_predicted_as_caught)234 void 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.
TF_BUILTIN(AsyncFunctionAwaitCaught,AsyncFunctionBuiltinsAssembler)257 TF_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.
TF_BUILTIN(AsyncFunctionAwaitUncaught,AsyncFunctionBuiltinsAssembler)264 TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) {
265 static const bool kIsPredictedAsCaught = false;
266 AsyncFunctionAwait<Descriptor>(kIsPredictedAsCaught);
267 }
268
269 } // namespace internal
270 } // namespace v8
271