OLD | NEW |
| (Empty) |
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: | |
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 | |
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 | |
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 | |
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. | |
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. | |
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. | |
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 | |
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 | |
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 | |
OLD | NEW |