• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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