• 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.h"
6  #include "src/builtins/builtins-utils.h"
7  #include "src/builtins/builtins.h"
8  #include "src/code-stub-assembler.h"
9  #include "src/objects-inl.h"
10  
11  namespace v8 {
12  namespace internal {
13  
14  typedef compiler::Node Node;
15  typedef CodeStubAssembler::ParameterMode ParameterMode;
16  typedef compiler::CodeAssemblerState CodeAssemblerState;
17  
18  class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler {
19   public:
AsyncFunctionBuiltinsAssembler(CodeAssemblerState * state)20    explicit AsyncFunctionBuiltinsAssembler(CodeAssemblerState* state)
21        : AsyncBuiltinsAssembler(state) {}
22  
23   protected:
24    void AsyncFunctionAwait(Node* const context, Node* const generator,
25                            Node* const awaited, Node* const outer_promise,
26                            const bool is_predicted_as_caught);
27  
28    void AsyncFunctionAwaitResumeClosure(
29        Node* const context, Node* const sent_value,
30        JSGeneratorObject::ResumeMode resume_mode);
31  };
32  
33  namespace {
34  
35  // Describe fields of Context associated with AsyncFunctionAwait resume
36  // closures.
37  // TODO(jgruber): Refactor to reuse code for upcoming async-generators.
38  class AwaitContext {
39   public:
40    enum Fields { kGeneratorSlot = Context::MIN_CONTEXT_SLOTS, kLength };
41  };
42  
43  }  // anonymous namespace
44  
AsyncFunctionAwaitResumeClosure(Node * context,Node * sent_value,JSGeneratorObject::ResumeMode resume_mode)45  void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitResumeClosure(
46      Node* context, Node* sent_value,
47      JSGeneratorObject::ResumeMode resume_mode) {
48    DCHECK(resume_mode == JSGeneratorObject::kNext ||
49           resume_mode == JSGeneratorObject::kThrow);
50  
51    Node* const generator =
52        LoadContextElement(context, AwaitContext::kGeneratorSlot);
53    CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE));
54  
55    // Inline version of GeneratorPrototypeNext / GeneratorPrototypeReturn with
56    // unnecessary runtime checks removed.
57    // TODO(jgruber): Refactor to reuse code from builtins-generator.cc.
58  
59    // Ensure that the generator is neither closed nor running.
60    CSA_SLOW_ASSERT(
61        this,
62        SmiGreaterThan(
63            LoadObjectField(generator, JSGeneratorObject::kContinuationOffset),
64            SmiConstant(JSGeneratorObject::kGeneratorClosed)));
65  
66    // Resume the {receiver} using our trampoline.
67    Callable callable = CodeFactory::ResumeGenerator(isolate());
68    CallStub(callable, context, sent_value, generator, SmiConstant(resume_mode));
69  
70    // The resulting Promise is a throwaway, so it doesn't matter what it
71    // resolves to. What is important is that we don't end up keeping the
72    // whole chain of intermediate Promises alive by returning the return value
73    // of ResumeGenerator, as that would create a memory leak.
74  }
75  
TF_BUILTIN(AsyncFunctionAwaitRejectClosure,AsyncFunctionBuiltinsAssembler)76  TF_BUILTIN(AsyncFunctionAwaitRejectClosure, AsyncFunctionBuiltinsAssembler) {
77    CSA_ASSERT_JS_ARGC_EQ(this, 1);
78    Node* const sentError = Parameter(1);
79    Node* const context = Parameter(4);
80  
81    AsyncFunctionAwaitResumeClosure(context, sentError,
82                                    JSGeneratorObject::kThrow);
83    Return(UndefinedConstant());
84  }
85  
TF_BUILTIN(AsyncFunctionAwaitResolveClosure,AsyncFunctionBuiltinsAssembler)86  TF_BUILTIN(AsyncFunctionAwaitResolveClosure, AsyncFunctionBuiltinsAssembler) {
87    CSA_ASSERT_JS_ARGC_EQ(this, 1);
88    Node* const sentValue = Parameter(1);
89    Node* const context = Parameter(4);
90  
91    AsyncFunctionAwaitResumeClosure(context, sentValue, JSGeneratorObject::kNext);
92    Return(UndefinedConstant());
93  }
94  
95  // ES#abstract-ops-async-function-await
96  // AsyncFunctionAwait ( value )
97  // Shared logic for the core of await. The parser desugars
98  //   await awaited
99  // into
100  //   yield AsyncFunctionAwait{Caught,Uncaught}(.generator, awaited, .promise)
101  // The 'awaited' parameter is the value; the generator stands in
102  // for the asyncContext, and .promise is the larger promise under
103  // construction by the enclosing async function.
AsyncFunctionAwait(Node * const context,Node * const generator,Node * const awaited,Node * const outer_promise,const bool is_predicted_as_caught)104  void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
105      Node* const context, Node* const generator, Node* const awaited,
106      Node* const outer_promise, const bool is_predicted_as_caught) {
107    CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE));
108    CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
109  
110    NodeGenerator1 create_closure_context = [&](Node* native_context) -> Node* {
111      Node* const context =
112          CreatePromiseContext(native_context, AwaitContext::kLength);
113      StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
114                                        generator);
115      return context;
116    };
117  
118    // TODO(jgruber): AsyncBuiltinsAssembler::Await currently does not reuse
119    // the awaited promise if it is already a promise. Reuse is non-spec compliant
120    // but part of our old behavior gives us a couple of percent
121    // performance boost.
122    // TODO(jgruber): Use a faster specialized version of
123    // InternalPerformPromiseThen.
124  
125    Node* const result = Await(
126        context, generator, awaited, outer_promise, create_closure_context,
127        Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN,
128        Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, is_predicted_as_caught);
129  
130    Return(result);
131  }
132  
133  // Called by the parser from the desugaring of 'await' when catch
134  // prediction indicates that there is a locally surrounding catch block.
TF_BUILTIN(AsyncFunctionAwaitCaught,AsyncFunctionBuiltinsAssembler)135  TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) {
136    CSA_ASSERT_JS_ARGC_EQ(this, 3);
137    Node* const generator = Parameter(1);
138    Node* const awaited = Parameter(2);
139    Node* const outer_promise = Parameter(3);
140    Node* const context = Parameter(6);
141  
142    static const bool kIsPredictedAsCaught = true;
143  
144    AsyncFunctionAwait(context, generator, awaited, outer_promise,
145                       kIsPredictedAsCaught);
146  }
147  
148  // Called by the parser from the desugaring of 'await' when catch
149  // prediction indicates no locally surrounding catch block.
TF_BUILTIN(AsyncFunctionAwaitUncaught,AsyncFunctionBuiltinsAssembler)150  TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) {
151    CSA_ASSERT_JS_ARGC_EQ(this, 3);
152    Node* const generator = Parameter(1);
153    Node* const awaited = Parameter(2);
154    Node* const outer_promise = Parameter(3);
155    Node* const context = Parameter(6);
156  
157    static const bool kIsPredictedAsCaught = false;
158  
159    AsyncFunctionAwait(context, generator, awaited, outer_promise,
160                       kIsPredictedAsCaught);
161  }
162  
TF_BUILTIN(AsyncFunctionPromiseCreate,AsyncFunctionBuiltinsAssembler)163  TF_BUILTIN(AsyncFunctionPromiseCreate, AsyncFunctionBuiltinsAssembler) {
164    CSA_ASSERT_JS_ARGC_EQ(this, 0);
165    Node* const context = Parameter(3);
166  
167    Node* const promise = AllocateAndInitJSPromise(context);
168  
169    Label if_is_debug_active(this, Label::kDeferred);
170    GotoIf(IsDebugActive(), &if_is_debug_active);
171  
172    // Early exit if debug is not active.
173    Return(promise);
174  
175    Bind(&if_is_debug_active);
176    {
177      // Push the Promise under construction in an async function on
178      // the catch prediction stack to handle exceptions thrown before
179      // the first await.
180      // Assign ID and create a recurring task to save stack for future
181      // resumptions from await.
182      CallRuntime(Runtime::kDebugAsyncFunctionPromiseCreated, context, promise);
183      Return(promise);
184    }
185  }
186  
TF_BUILTIN(AsyncFunctionPromiseRelease,AsyncFunctionBuiltinsAssembler)187  TF_BUILTIN(AsyncFunctionPromiseRelease, AsyncFunctionBuiltinsAssembler) {
188    CSA_ASSERT_JS_ARGC_EQ(this, 1);
189    Node* const promise = Parameter(1);
190    Node* const context = Parameter(4);
191  
192    Label if_is_debug_active(this, Label::kDeferred);
193    GotoIf(IsDebugActive(), &if_is_debug_active);
194  
195    // Early exit if debug is not active.
196    Return(UndefinedConstant());
197  
198    Bind(&if_is_debug_active);
199    {
200      // Pop the Promise under construction in an async function on
201      // from catch prediction stack.
202      CallRuntime(Runtime::kDebugPopPromise, context);
203      Return(promise);
204    }
205  }
206  
207  }  // namespace internal
208  }  // namespace v8
209