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