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::kDebugAsyncFunctionResumed, 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<NativeContext> native_context = LoadNativeContext(context);
107 TNode<JSFunction> promise_function =
108 CAST(LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX));
109 TNode<Map> promise_map = LoadObjectField<Map>(
110 promise_function, JSFunction::kPrototypeOrInitialMapOffset);
111 TNode<JSPromise> promise = UncheckedCast<JSPromise>(
112 AllocateInNewSpace(JSPromise::kSizeWithEmbedderFields));
113 StoreMapNoWriteBarrier(promise, promise_map);
114 StoreObjectFieldRoot(promise, JSPromise::kPropertiesOrHashOffset,
115 RootIndex::kEmptyFixedArray);
116 StoreObjectFieldRoot(promise, JSPromise::kElementsOffset,
117 RootIndex::kEmptyFixedArray);
118 PromiseInit(promise);
119
120 // Allocate and initialize the async function object.
121 TNode<Map> async_function_object_map = CAST(LoadContextElement(
122 native_context, Context::ASYNC_FUNCTION_OBJECT_MAP_INDEX));
123 TNode<JSAsyncFunctionObject> async_function_object =
124 UncheckedCast<JSAsyncFunctionObject>(
125 AllocateInNewSpace(JSAsyncFunctionObject::kHeaderSize));
126 StoreMapNoWriteBarrier(async_function_object, async_function_object_map);
127 StoreObjectFieldRoot(async_function_object,
128 JSAsyncFunctionObject::kPropertiesOrHashOffset,
129 RootIndex::kEmptyFixedArray);
130 StoreObjectFieldRoot(async_function_object,
131 JSAsyncFunctionObject::kElementsOffset,
132 RootIndex::kEmptyFixedArray);
133 StoreObjectFieldNoWriteBarrier(
134 async_function_object, JSAsyncFunctionObject::kFunctionOffset, closure);
135 StoreObjectFieldNoWriteBarrier(
136 async_function_object, JSAsyncFunctionObject::kContextOffset, context);
137 StoreObjectFieldNoWriteBarrier(
138 async_function_object, JSAsyncFunctionObject::kReceiverOffset, receiver);
139 StoreObjectFieldNoWriteBarrier(async_function_object,
140 JSAsyncFunctionObject::kInputOrDebugPosOffset,
141 SmiConstant(0));
142 StoreObjectFieldNoWriteBarrier(async_function_object,
143 JSAsyncFunctionObject::kResumeModeOffset,
144 SmiConstant(JSAsyncFunctionObject::kNext));
145 StoreObjectFieldNoWriteBarrier(
146 async_function_object, JSAsyncFunctionObject::kContinuationOffset,
147 SmiConstant(JSAsyncFunctionObject::kGeneratorExecuting));
148 StoreObjectFieldNoWriteBarrier(
149 async_function_object,
150 JSAsyncFunctionObject::kParametersAndRegistersOffset,
151 parameters_and_registers);
152 StoreObjectFieldNoWriteBarrier(
153 async_function_object, JSAsyncFunctionObject::kPromiseOffset, promise);
154
155 RunContextPromiseHookInit(context, promise, UndefinedConstant());
156
157 // Fire promise hooks if enabled and push the Promise under construction
158 // in an async function on the catch prediction stack to handle exceptions
159 // thrown before the first await.
160 Label if_instrumentation(this, Label::kDeferred),
161 if_instrumentation_done(this);
162 Branch(IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
163 &if_instrumentation, &if_instrumentation_done);
164 BIND(&if_instrumentation);
165 {
166 CallRuntime(Runtime::kDebugAsyncFunctionEntered, context, promise);
167 Goto(&if_instrumentation_done);
168 }
169 BIND(&if_instrumentation_done);
170
171 Return(async_function_object);
172 }
173
TF_BUILTIN(AsyncFunctionReject,AsyncFunctionBuiltinsAssembler)174 TF_BUILTIN(AsyncFunctionReject, AsyncFunctionBuiltinsAssembler) {
175 auto async_function_object =
176 Parameter<JSAsyncFunctionObject>(Descriptor::kAsyncFunctionObject);
177 auto reason = Parameter<Object>(Descriptor::kReason);
178 auto can_suspend = Parameter<Oddball>(Descriptor::kCanSuspend);
179 auto context = Parameter<Context>(Descriptor::kContext);
180 TNode<JSPromise> promise = LoadObjectField<JSPromise>(
181 async_function_object, JSAsyncFunctionObject::kPromiseOffset);
182
183 // Reject the {promise} for the given {reason}, disabling the
184 // additional debug event for the rejection since a debug event
185 // already happend for the exception that got us here.
186 CallBuiltin(Builtin::kRejectPromise, context, promise, reason,
187 FalseConstant());
188
189 Label if_debugging(this, Label::kDeferred);
190 GotoIf(HasAsyncEventDelegate(), &if_debugging);
191 GotoIf(IsDebugActive(), &if_debugging);
192 Return(promise);
193
194 BIND(&if_debugging);
195 TailCallRuntime(Runtime::kDebugAsyncFunctionFinished, context, can_suspend,
196 promise);
197 }
198
TF_BUILTIN(AsyncFunctionResolve,AsyncFunctionBuiltinsAssembler)199 TF_BUILTIN(AsyncFunctionResolve, AsyncFunctionBuiltinsAssembler) {
200 auto async_function_object =
201 Parameter<JSAsyncFunctionObject>(Descriptor::kAsyncFunctionObject);
202 auto value = Parameter<Object>(Descriptor::kValue);
203 auto can_suspend = Parameter<Oddball>(Descriptor::kCanSuspend);
204 auto context = Parameter<Context>(Descriptor::kContext);
205 TNode<JSPromise> promise = LoadObjectField<JSPromise>(
206 async_function_object, JSAsyncFunctionObject::kPromiseOffset);
207
208 CallBuiltin(Builtin::kResolvePromise, context, promise, value);
209
210 Label if_debugging(this, Label::kDeferred);
211 GotoIf(HasAsyncEventDelegate(), &if_debugging);
212 GotoIf(IsDebugActive(), &if_debugging);
213 Return(promise);
214
215 BIND(&if_debugging);
216 TailCallRuntime(Runtime::kDebugAsyncFunctionFinished, context, can_suspend,
217 promise);
218 }
219
220 // AsyncFunctionReject and AsyncFunctionResolve are both required to return
221 // the promise instead of the result of RejectPromise or ResolvePromise
222 // respectively from a lazy deoptimization.
TF_BUILTIN(AsyncFunctionLazyDeoptContinuation,AsyncFunctionBuiltinsAssembler)223 TF_BUILTIN(AsyncFunctionLazyDeoptContinuation, AsyncFunctionBuiltinsAssembler) {
224 auto promise = Parameter<JSPromise>(Descriptor::kPromise);
225 Return(promise);
226 }
227
TF_BUILTIN(AsyncFunctionAwaitRejectClosure,AsyncFunctionBuiltinsAssembler)228 TF_BUILTIN(AsyncFunctionAwaitRejectClosure, AsyncFunctionBuiltinsAssembler) {
229 CSA_DCHECK_JS_ARGC_EQ(this, 1);
230 const auto sentError = Parameter<Object>(Descriptor::kSentError);
231 const auto context = Parameter<Context>(Descriptor::kContext);
232
233 AsyncFunctionAwaitResumeClosure(context, sentError,
234 JSGeneratorObject::kThrow);
235 Return(UndefinedConstant());
236 }
237
TF_BUILTIN(AsyncFunctionAwaitResolveClosure,AsyncFunctionBuiltinsAssembler)238 TF_BUILTIN(AsyncFunctionAwaitResolveClosure, AsyncFunctionBuiltinsAssembler) {
239 CSA_DCHECK_JS_ARGC_EQ(this, 1);
240 const auto sentValue = Parameter<Object>(Descriptor::kSentValue);
241 const auto context = Parameter<Context>(Descriptor::kContext);
242
243 AsyncFunctionAwaitResumeClosure(context, sentValue, JSGeneratorObject::kNext);
244 Return(UndefinedConstant());
245 }
246
247 // ES#abstract-ops-async-function-await
248 // AsyncFunctionAwait ( value )
249 // Shared logic for the core of await. The parser desugars
250 // await value
251 // into
252 // yield AsyncFunctionAwait{Caught,Uncaught}(.generator_object, value)
253 // The 'value' parameter is the value; the .generator_object stands in
254 // for the asyncContext.
255 template <typename Descriptor>
AsyncFunctionAwait(const bool is_predicted_as_caught)256 void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
257 const bool is_predicted_as_caught) {
258 auto async_function_object =
259 Parameter<JSAsyncFunctionObject>(Descriptor::kAsyncFunctionObject);
260 auto value = Parameter<Object>(Descriptor::kValue);
261 auto context = Parameter<Context>(Descriptor::kContext);
262
263 TNode<JSPromise> outer_promise = LoadObjectField<JSPromise>(
264 async_function_object, JSAsyncFunctionObject::kPromiseOffset);
265
266 Label after_debug_hook(this), call_debug_hook(this, Label::kDeferred);
267 GotoIf(HasAsyncEventDelegate(), &call_debug_hook);
268 Goto(&after_debug_hook);
269 BIND(&after_debug_hook);
270
271 TNode<SharedFunctionInfo> on_resolve_sfi =
272 AsyncFunctionAwaitResolveSharedFunConstant();
273 TNode<SharedFunctionInfo> on_reject_sfi =
274 AsyncFunctionAwaitRejectSharedFunConstant();
275 Await(context, async_function_object, value, outer_promise, on_resolve_sfi,
276 on_reject_sfi, is_predicted_as_caught);
277
278 // Return outer promise to avoid adding an load of the outer promise before
279 // suspending in BytecodeGenerator.
280 Return(outer_promise);
281
282 BIND(&call_debug_hook);
283 CallRuntime(Runtime::kDebugAsyncFunctionSuspended, context, outer_promise);
284 Goto(&after_debug_hook);
285 }
286
287 // Called by the parser from the desugaring of 'await' when catch
288 // prediction indicates that there is a locally surrounding catch block.
TF_BUILTIN(AsyncFunctionAwaitCaught,AsyncFunctionBuiltinsAssembler)289 TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) {
290 static const bool kIsPredictedAsCaught = true;
291 AsyncFunctionAwait<Descriptor>(kIsPredictedAsCaught);
292 }
293
294 // Called by the parser from the desugaring of 'await' when catch
295 // prediction indicates no locally surrounding catch block.
TF_BUILTIN(AsyncFunctionAwaitUncaught,AsyncFunctionBuiltinsAssembler)296 TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) {
297 static const bool kIsPredictedAsCaught = false;
298 AsyncFunctionAwait<Descriptor>(kIsPredictedAsCaught);
299 }
300
301 } // namespace internal
302 } // namespace v8
303