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