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 : #include "src/objects/js-generator.h"
11 : #include "src/objects/js-promise.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 448 : class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler {
17 : public:
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 : Node* const context, Node* const sent_value,
27 : JSGeneratorObject::ResumeMode resume_mode);
28 : };
29 :
30 112 : void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitResumeClosure(
31 : Node* context, Node* 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 112 : CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
38 :
39 : // Inline version of GeneratorPrototypeNext / GeneratorPrototypeReturn with
40 : // unnecessary runtime checks removed.
41 :
42 : // Ensure that the {async_function_object} is neither closed nor running.
43 : CSA_SLOW_ASSERT(
44 : this, SmiGreaterThan(
45 : LoadObjectField<Smi>(async_function_object,
46 : JSGeneratorObject::kContinuationOffset),
47 : SmiConstant(JSGeneratorObject::kGeneratorClosed)));
48 :
49 : // Remember the {resume_mode} for the {async_function_object}.
50 112 : StoreObjectFieldNoWriteBarrier(async_function_object,
51 : JSGeneratorObject::kResumeModeOffset,
52 : SmiConstant(resume_mode));
53 :
54 : // Resume the {receiver} using our trampoline.
55 112 : Callable callable = CodeFactory::ResumeGenerator(isolate());
56 112 : CallStub(callable, context, sent_value, async_function_object);
57 :
58 : // The resulting Promise is a throwaway, so it doesn't matter what it
59 : // resolves to. What is important is that we don't end up keeping the
60 : // whole chain of intermediate Promises alive by returning the return value
61 : // of ResumeGenerator, as that would create a memory leak.
62 112 : }
63 :
64 224 : TF_BUILTIN(AsyncFunctionEnter, AsyncFunctionBuiltinsAssembler) {
65 : TNode<JSFunction> closure = CAST(Parameter(Descriptor::kClosure));
66 : TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
67 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
68 :
69 : // Compute the number of registers and parameters.
70 : TNode<SharedFunctionInfo> shared = LoadObjectField<SharedFunctionInfo>(
71 56 : closure, JSFunction::kSharedFunctionInfoOffset);
72 : TNode<IntPtrT> formal_parameter_count = ChangeInt32ToIntPtr(
73 : LoadObjectField(shared, SharedFunctionInfo::kFormalParameterCountOffset,
74 112 : MachineType::Uint16()));
75 : TNode<BytecodeArray> bytecode_array =
76 56 : LoadSharedFunctionInfoBytecodeArray(shared);
77 : TNode<IntPtrT> frame_size = ChangeInt32ToIntPtr(LoadObjectField(
78 112 : bytecode_array, BytecodeArray::kFrameSizeOffset, MachineType::Int32()));
79 : TNode<IntPtrT> parameters_and_register_length =
80 112 : Signed(IntPtrAdd(WordSar(frame_size, IntPtrConstant(kTaggedSizeLog2)),
81 : formal_parameter_count));
82 :
83 : // Allocate space for the promise, the async function object
84 : // and the register file.
85 : TNode<IntPtrT> size = IntPtrAdd(
86 : IntPtrConstant(JSPromise::kSizeWithEmbedderFields +
87 : JSAsyncFunctionObject::kSize + FixedArray::kHeaderSize),
88 : Signed(WordShl(parameters_and_register_length,
89 168 : IntPtrConstant(kTaggedSizeLog2))));
90 56 : TNode<HeapObject> base = AllocateInNewSpace(size);
91 :
92 : // Initialize the register file.
93 : TNode<FixedArray> parameters_and_registers = UncheckedCast<FixedArray>(
94 : InnerAllocate(base, JSAsyncFunctionObject::kSize +
95 56 : JSPromise::kSizeWithEmbedderFields));
96 56 : StoreMapNoWriteBarrier(parameters_and_registers, RootIndex::kFixedArrayMap);
97 : StoreObjectFieldNoWriteBarrier(parameters_and_registers,
98 : FixedArray::kLengthOffset,
99 : SmiFromIntPtr(parameters_and_register_length));
100 : FillFixedArrayWithValue(HOLEY_ELEMENTS, parameters_and_registers,
101 112 : IntPtrConstant(0), parameters_and_register_length,
102 56 : RootIndex::kUndefinedValue);
103 :
104 : // Initialize the promise.
105 56 : TNode<Context> native_context = LoadNativeContext(context);
106 : TNode<JSFunction> promise_function =
107 56 : CAST(LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX));
108 : TNode<Map> promise_map = LoadObjectField<Map>(
109 : promise_function, JSFunction::kPrototypeOrInitialMapOffset);
110 : TNode<JSPromise> promise = UncheckedCast<JSPromise>(
111 56 : InnerAllocate(base, JSAsyncFunctionObject::kSize));
112 56 : StoreMapNoWriteBarrier(promise, promise_map);
113 : StoreObjectFieldRoot(promise, JSPromise::kPropertiesOrHashOffset,
114 56 : RootIndex::kEmptyFixedArray);
115 : StoreObjectFieldRoot(promise, JSPromise::kElementsOffset,
116 56 : RootIndex::kEmptyFixedArray);
117 56 : PromiseInit(promise);
118 :
119 : // Initialize the async function object.
120 56 : TNode<Map> async_function_object_map = CAST(LoadContextElement(
121 : native_context, Context::ASYNC_FUNCTION_OBJECT_MAP_INDEX));
122 : TNode<JSAsyncFunctionObject> async_function_object =
123 : UncheckedCast<JSAsyncFunctionObject>(base);
124 56 : StoreMapNoWriteBarrier(async_function_object, async_function_object_map);
125 : StoreObjectFieldRoot(async_function_object,
126 : JSAsyncFunctionObject::kPropertiesOrHashOffset,
127 56 : RootIndex::kEmptyFixedArray);
128 : StoreObjectFieldRoot(async_function_object,
129 : JSAsyncFunctionObject::kElementsOffset,
130 56 : RootIndex::kEmptyFixedArray);
131 : StoreObjectFieldNoWriteBarrier(
132 : async_function_object, JSAsyncFunctionObject::kFunctionOffset, closure);
133 : StoreObjectFieldNoWriteBarrier(
134 : async_function_object, JSAsyncFunctionObject::kContextOffset, context);
135 : StoreObjectFieldNoWriteBarrier(
136 : async_function_object, JSAsyncFunctionObject::kReceiverOffset, receiver);
137 56 : StoreObjectFieldNoWriteBarrier(async_function_object,
138 : JSAsyncFunctionObject::kInputOrDebugPosOffset,
139 : SmiConstant(0));
140 : StoreObjectFieldNoWriteBarrier(async_function_object,
141 : JSAsyncFunctionObject::kResumeModeOffset,
142 : SmiConstant(JSAsyncFunctionObject::kNext));
143 56 : StoreObjectFieldNoWriteBarrier(
144 : async_function_object, JSAsyncFunctionObject::kContinuationOffset,
145 : SmiConstant(JSAsyncFunctionObject::kGeneratorExecuting));
146 : StoreObjectFieldNoWriteBarrier(
147 : async_function_object,
148 : JSAsyncFunctionObject::kParametersAndRegistersOffset,
149 : parameters_and_registers);
150 : StoreObjectFieldNoWriteBarrier(
151 : async_function_object, JSAsyncFunctionObject::kPromiseOffset, promise);
152 :
153 : // Fire promise hooks if enabled and push the Promise under construction
154 : // in an async function on the catch prediction stack to handle exceptions
155 : // thrown before the first await.
156 56 : Label if_instrumentation(this, Label::kDeferred),
157 56 : if_instrumentation_done(this);
158 112 : Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
159 56 : &if_instrumentation, &if_instrumentation_done);
160 56 : BIND(&if_instrumentation);
161 : {
162 : CallRuntime(Runtime::kDebugAsyncFunctionEntered, context, promise);
163 56 : Goto(&if_instrumentation_done);
164 : }
165 56 : BIND(&if_instrumentation_done);
166 :
167 56 : Return(async_function_object);
168 56 : }
169 :
170 336 : TF_BUILTIN(AsyncFunctionReject, AsyncFunctionBuiltinsAssembler) {
171 : TNode<JSAsyncFunctionObject> async_function_object =
172 : CAST(Parameter(Descriptor::kAsyncFunctionObject));
173 56 : TNode<Object> reason = CAST(Parameter(Descriptor::kReason));
174 56 : TNode<Oddball> can_suspend = CAST(Parameter(Descriptor::kCanSuspend));
175 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
176 : TNode<JSPromise> promise = LoadObjectField<JSPromise>(
177 56 : async_function_object, JSAsyncFunctionObject::kPromiseOffset);
178 :
179 : // Reject the {promise} for the given {reason}, disabling the
180 : // additional debug event for the rejection since a debug event
181 : // already happend for the exception that got us here.
182 : CallBuiltin(Builtins::kRejectPromise, context, promise, reason,
183 112 : FalseConstant());
184 :
185 56 : Label if_debugging(this, Label::kDeferred);
186 112 : GotoIf(HasAsyncEventDelegate(), &if_debugging);
187 112 : GotoIf(IsDebugActive(), &if_debugging);
188 56 : Return(promise);
189 :
190 56 : BIND(&if_debugging);
191 56 : TailCallRuntime(Runtime::kDebugAsyncFunctionFinished, context, can_suspend,
192 56 : promise);
193 56 : }
194 :
195 336 : TF_BUILTIN(AsyncFunctionResolve, AsyncFunctionBuiltinsAssembler) {
196 : TNode<JSAsyncFunctionObject> async_function_object =
197 : CAST(Parameter(Descriptor::kAsyncFunctionObject));
198 56 : TNode<Object> value = CAST(Parameter(Descriptor::kValue));
199 56 : TNode<Oddball> can_suspend = CAST(Parameter(Descriptor::kCanSuspend));
200 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
201 : TNode<JSPromise> promise = LoadObjectField<JSPromise>(
202 56 : async_function_object, JSAsyncFunctionObject::kPromiseOffset);
203 :
204 56 : CallBuiltin(Builtins::kResolvePromise, context, promise, value);
205 :
206 56 : Label if_debugging(this, Label::kDeferred);
207 112 : GotoIf(HasAsyncEventDelegate(), &if_debugging);
208 112 : GotoIf(IsDebugActive(), &if_debugging);
209 56 : Return(promise);
210 :
211 56 : BIND(&if_debugging);
212 56 : TailCallRuntime(Runtime::kDebugAsyncFunctionFinished, context, can_suspend,
213 56 : promise);
214 56 : }
215 :
216 : // AsyncFunctionReject and AsyncFunctionResolve are both required to return
217 : // the promise instead of the result of RejectPromise or ResolvePromise
218 : // respectively from a lazy deoptimization.
219 224 : TF_BUILTIN(AsyncFunctionLazyDeoptContinuation, AsyncFunctionBuiltinsAssembler) {
220 : TNode<JSPromise> promise = CAST(Parameter(Descriptor::kPromise));
221 56 : Return(promise);
222 56 : }
223 :
224 224 : TF_BUILTIN(AsyncFunctionAwaitRejectClosure, AsyncFunctionBuiltinsAssembler) {
225 : CSA_ASSERT_JS_ARGC_EQ(this, 1);
226 : Node* const sentError = Parameter(Descriptor::kSentError);
227 : Node* const context = Parameter(Descriptor::kContext);
228 :
229 56 : AsyncFunctionAwaitResumeClosure(context, sentError,
230 56 : JSGeneratorObject::kThrow);
231 112 : Return(UndefinedConstant());
232 56 : }
233 :
234 224 : TF_BUILTIN(AsyncFunctionAwaitResolveClosure, AsyncFunctionBuiltinsAssembler) {
235 : CSA_ASSERT_JS_ARGC_EQ(this, 1);
236 : Node* const sentValue = Parameter(Descriptor::kSentValue);
237 : Node* const context = Parameter(Descriptor::kContext);
238 :
239 56 : AsyncFunctionAwaitResumeClosure(context, sentValue, JSGeneratorObject::kNext);
240 112 : Return(UndefinedConstant());
241 56 : }
242 :
243 : // ES#abstract-ops-async-function-await
244 : // AsyncFunctionAwait ( value )
245 : // Shared logic for the core of await. The parser desugars
246 : // await value
247 : // into
248 : // yield AsyncFunctionAwait{Caught,Uncaught}(.generator_object, value)
249 : // The 'value' parameter is the value; the .generator_object stands in
250 : // for the asyncContext.
251 : template <typename Descriptor>
252 112 : void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
253 : const bool is_predicted_as_caught) {
254 : TNode<JSAsyncFunctionObject> async_function_object =
255 112 : CAST(Parameter(Descriptor::kAsyncFunctionObject));
256 112 : TNode<Object> value = CAST(Parameter(Descriptor::kValue));
257 112 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
258 :
259 112 : Node* outer_promise = LoadObjectField(async_function_object,
260 : JSAsyncFunctionObject::kPromiseOffset);
261 :
262 112 : Label after_debug_hook(this), call_debug_hook(this, Label::kDeferred);
263 224 : GotoIf(HasAsyncEventDelegate(), &call_debug_hook);
264 112 : Goto(&after_debug_hook);
265 112 : BIND(&after_debug_hook);
266 :
267 112 : Await(context, async_function_object, value, outer_promise,
268 : Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN,
269 : Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN,
270 : is_predicted_as_caught);
271 :
272 : // Return outer promise to avoid adding an load of the outer promise before
273 : // suspending in BytecodeGenerator.
274 112 : Return(outer_promise);
275 :
276 112 : BIND(&call_debug_hook);
277 : CallRuntime(Runtime::kDebugAsyncFunctionSuspended, context, outer_promise);
278 112 : Goto(&after_debug_hook);
279 112 : }
280 :
281 : // Called by the parser from the desugaring of 'await' when catch
282 : // prediction indicates that there is a locally surrounding catch block.
283 168 : TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) {
284 : static const bool kIsPredictedAsCaught = true;
285 56 : AsyncFunctionAwait<Descriptor>(kIsPredictedAsCaught);
286 0 : }
287 :
288 : // Called by the parser from the desugaring of 'await' when catch
289 : // prediction indicates no locally surrounding catch block.
290 168 : TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) {
291 : static const bool kIsPredictedAsCaught = false;
292 56 : AsyncFunctionAwait<Descriptor>(kIsPredictedAsCaught);
293 0 : }
294 :
295 : } // namespace internal
296 59456 : } // namespace v8
|