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