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 86 : 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 86 : 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 86 : Callable callable = CodeFactory::ResumeGenerator(isolate());
64 : CallStub(callable, context, sent_value, generator, SmiConstant(resume_mode),
65 86 : SmiConstant(static_cast<int>(SuspendFlags::kGeneratorAwait)));
66 :
67 : // The resulting Promise is a throwaway, so it doesn't matter what it
68 : // resolves to. What is important is that we don't end up keeping the
69 : // whole chain of intermediate Promises alive by returning the return value
70 : // of ResumeGenerator, as that would create a memory leak.
71 86 : }
72 :
73 172 : TF_BUILTIN(AsyncFunctionAwaitRejectClosure, AsyncFunctionBuiltinsAssembler) {
74 : CSA_ASSERT_JS_ARGC_EQ(this, 1);
75 : Node* const sentError = Parameter(Descriptor::kSentError);
76 : Node* const context = Parameter(Descriptor::kContext);
77 :
78 : AsyncFunctionAwaitResumeClosure(context, sentError,
79 43 : JSGeneratorObject::kThrow);
80 43 : Return(UndefinedConstant());
81 43 : }
82 :
83 172 : TF_BUILTIN(AsyncFunctionAwaitResolveClosure, AsyncFunctionBuiltinsAssembler) {
84 : CSA_ASSERT_JS_ARGC_EQ(this, 1);
85 : Node* const sentValue = Parameter(Descriptor::kSentValue);
86 : Node* const context = Parameter(Descriptor::kContext);
87 :
88 43 : AsyncFunctionAwaitResumeClosure(context, sentValue, JSGeneratorObject::kNext);
89 43 : Return(UndefinedConstant());
90 43 : }
91 :
92 : // ES#abstract-ops-async-function-await
93 : // AsyncFunctionAwait ( value )
94 : // Shared logic for the core of await. The parser desugars
95 : // await awaited
96 : // into
97 : // yield AsyncFunctionAwait{Caught,Uncaught}(.generator, awaited, .promise)
98 : // The 'awaited' parameter is the value; the generator stands in
99 : // for the asyncContext, and .promise is the larger promise under
100 : // construction by the enclosing async function.
101 86 : void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
102 : Node* const context, Node* const generator, Node* const awaited,
103 : Node* const outer_promise, const bool is_predicted_as_caught) {
104 : CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE));
105 : CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
106 :
107 86 : NodeGenerator1 create_closure_context = [&](Node* native_context) -> Node* {
108 : Node* const context =
109 86 : CreatePromiseContext(native_context, AwaitContext::kLength);
110 : StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
111 86 : generator);
112 86 : return context;
113 : };
114 :
115 : // TODO(jgruber): AsyncBuiltinsAssembler::Await currently does not reuse
116 : // the awaited promise if it is already a promise. Reuse is non-spec compliant
117 : // but part of our old behavior gives us a couple of percent
118 : // performance boost.
119 : // TODO(jgruber): Use a faster specialized version of
120 : // InternalPerformPromiseThen.
121 :
122 : Node* const result = Await(
123 : context, generator, awaited, outer_promise, create_closure_context,
124 : Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN,
125 86 : Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, is_predicted_as_caught);
126 :
127 86 : Return(result);
128 86 : }
129 :
130 : // Called by the parser from the desugaring of 'await' when catch
131 : // prediction indicates that there is a locally surrounding catch block.
132 172 : TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) {
133 : CSA_ASSERT_JS_ARGC_EQ(this, 3);
134 : Node* const generator = Parameter(Descriptor::kGenerator);
135 : Node* const awaited = Parameter(Descriptor::kAwaited);
136 : Node* const outer_promise = Parameter(Descriptor::kOuterPromise);
137 : Node* const context = Parameter(Descriptor::kContext);
138 :
139 : static const bool kIsPredictedAsCaught = true;
140 :
141 : AsyncFunctionAwait(context, generator, awaited, outer_promise,
142 43 : kIsPredictedAsCaught);
143 43 : }
144 :
145 : // Called by the parser from the desugaring of 'await' when catch
146 : // prediction indicates no locally surrounding catch block.
147 172 : TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) {
148 : CSA_ASSERT_JS_ARGC_EQ(this, 3);
149 : Node* const generator = Parameter(Descriptor::kGenerator);
150 : Node* const awaited = Parameter(Descriptor::kAwaited);
151 : Node* const outer_promise = Parameter(Descriptor::kOuterPromise);
152 : Node* const context = Parameter(Descriptor::kContext);
153 :
154 : static const bool kIsPredictedAsCaught = false;
155 :
156 : AsyncFunctionAwait(context, generator, awaited, outer_promise,
157 43 : kIsPredictedAsCaught);
158 43 : }
159 :
160 172 : TF_BUILTIN(AsyncFunctionPromiseCreate, AsyncFunctionBuiltinsAssembler) {
161 : CSA_ASSERT_JS_ARGC_EQ(this, 0);
162 : Node* const context = Parameter(Descriptor::kContext);
163 :
164 43 : Node* const promise = AllocateAndInitJSPromise(context);
165 :
166 : Label if_is_debug_active(this, Label::kDeferred);
167 43 : GotoIf(IsDebugActive(), &if_is_debug_active);
168 :
169 : // Early exit if debug is not active.
170 43 : Return(promise);
171 :
172 43 : BIND(&if_is_debug_active);
173 : {
174 : // Push the Promise under construction in an async function on
175 : // the catch prediction stack to handle exceptions thrown before
176 : // the first await.
177 : // Assign ID and create a recurring task to save stack for future
178 : // resumptions from await.
179 43 : CallRuntime(Runtime::kDebugAsyncFunctionPromiseCreated, context, promise);
180 43 : Return(promise);
181 43 : }
182 43 : }
183 :
184 172 : TF_BUILTIN(AsyncFunctionPromiseRelease, AsyncFunctionBuiltinsAssembler) {
185 : CSA_ASSERT_JS_ARGC_EQ(this, 1);
186 : Node* const promise = Parameter(Descriptor::kPromise);
187 : Node* const context = Parameter(Descriptor::kContext);
188 :
189 : Label if_is_debug_active(this, Label::kDeferred);
190 43 : GotoIf(IsDebugActive(), &if_is_debug_active);
191 :
192 : // Early exit if debug is not active.
193 43 : Return(UndefinedConstant());
194 :
195 43 : BIND(&if_is_debug_active);
196 : {
197 : // Pop the Promise under construction in an async function on
198 : // from catch prediction stack.
199 43 : CallRuntime(Runtime::kDebugPopPromise, context);
200 43 : Return(promise);
201 43 : }
202 43 : }
203 :
204 : } // namespace internal
205 : } // namespace v8
|