1 // Copyright 2016 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
7 #include "src/builtins/builtins-utils-gen.h"
8 #include "src/heap/factory-inl.h"
9 #include "src/objects/js-promise.h"
10 #include "src/objects/shared-function-info.h"
11
12 namespace v8 {
13 namespace internal {
14
15 using compiler::Node;
16
17 namespace {
18 // Describe fields of Context associated with the AsyncIterator unwrap closure.
19 class ValueUnwrapContext {
20 public:
21 enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
22 };
23
24 } // namespace
25
Await(Node * context,Node * generator,Node * value,Node * outer_promise,int context_length,const ContextInitializer & init_closure_context,Node * on_resolve_context_index,Node * on_reject_context_index,Node * is_predicted_as_caught)26 Node* AsyncBuiltinsAssembler::Await(
27 Node* context, Node* generator, Node* value, Node* outer_promise,
28 int context_length, const ContextInitializer& init_closure_context,
29 Node* on_resolve_context_index, Node* on_reject_context_index,
30 Node* is_predicted_as_caught) {
31 DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);
32
33 Node* const native_context = LoadNativeContext(context);
34
35 static const int kWrappedPromiseOffset = FixedArray::SizeFor(context_length);
36 static const int kThrowawayPromiseOffset =
37 kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
38 static const int kResolveClosureOffset =
39 kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
40 static const int kRejectClosureOffset =
41 kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
42 static const int kTotalSize =
43 kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
44
45 Node* const base = AllocateInNewSpace(kTotalSize);
46 Node* const closure_context = base;
47 {
48 // Initialize closure context
49 InitializeFunctionContext(native_context, closure_context, context_length);
50 init_closure_context(closure_context);
51 }
52
53 // Let promiseCapability be ! NewPromiseCapability(%Promise%).
54 Node* const promise_fun =
55 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
56 CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
57 Node* const promise_map =
58 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
59 // Assert that the JSPromise map has an instance size is
60 // JSPromise::kSizeWithEmbedderFields.
61 CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
62 IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
63 kPointerSize)));
64 Node* const wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
65 {
66 // Initialize Promise
67 StoreMapNoWriteBarrier(wrapped_value, promise_map);
68 InitializeJSObjectFromMap(
69 wrapped_value, promise_map,
70 IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
71 PromiseInit(wrapped_value);
72 }
73
74 Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
75 {
76 // Initialize throwawayPromise
77 StoreMapNoWriteBarrier(throwaway, promise_map);
78 InitializeJSObjectFromMap(
79 throwaway, promise_map,
80 IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
81 PromiseInit(throwaway);
82 }
83
84 Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
85 {
86 // Initialize resolve handler
87 InitializeNativeClosure(closure_context, native_context, on_resolve,
88 on_resolve_context_index);
89 }
90
91 Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
92 {
93 // Initialize reject handler
94 InitializeNativeClosure(closure_context, native_context, on_reject,
95 on_reject_context_index);
96 }
97
98 {
99 // Add PromiseHooks if needed
100 Label next(this);
101 GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next);
102 CallRuntime(Runtime::kAwaitPromisesInit, context, wrapped_value,
103 outer_promise, throwaway);
104 Goto(&next);
105 BIND(&next);
106 }
107
108 // Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
109 CallBuiltin(Builtins::kResolvePromise, context, wrapped_value, value);
110
111 // The Promise will be thrown away and not handled, but it shouldn't trigger
112 // unhandled reject events as its work is done
113 PromiseSetHasHandler(throwaway);
114
115 Label do_perform_promise_then(this);
116 GotoIfNot(IsDebugActive(), &do_perform_promise_then);
117 {
118 Label common(this);
119 GotoIf(TaggedIsSmi(value), &common);
120 GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
121 {
122 // Mark the reject handler callback to be a forwarding edge, rather
123 // than a meaningful catch handler
124 Node* const key =
125 HeapConstant(factory()->promise_forwarding_handler_symbol());
126 SetPropertyStrict(CAST(context), CAST(on_reject), CAST(key),
127 TrueConstant());
128
129 GotoIf(IsFalse(is_predicted_as_caught), &common);
130 PromiseSetHandledHint(value);
131 }
132
133 Goto(&common);
134 BIND(&common);
135 // Mark the dependency to outer Promise in case the throwaway Promise is
136 // found on the Promise stack
137 CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
138
139 Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
140 SetPropertyStrict(CAST(context), CAST(throwaway), CAST(key),
141 CAST(outer_promise));
142 }
143
144 Goto(&do_perform_promise_then);
145 BIND(&do_perform_promise_then);
146 return CallBuiltin(Builtins::kPerformPromiseThen, context, wrapped_value,
147 on_resolve, on_reject, throwaway);
148 }
149
AwaitOptimized(Node * context,Node * generator,Node * value,Node * outer_promise,int context_length,const ContextInitializer & init_closure_context,Node * on_resolve_context_index,Node * on_reject_context_index,Node * is_predicted_as_caught)150 Node* AsyncBuiltinsAssembler::AwaitOptimized(
151 Node* context, Node* generator, Node* value, Node* outer_promise,
152 int context_length, const ContextInitializer& init_closure_context,
153 Node* on_resolve_context_index, Node* on_reject_context_index,
154 Node* is_predicted_as_caught) {
155 DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);
156
157 Node* const native_context = LoadNativeContext(context);
158 Node* const promise_fun =
159 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
160 CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
161 CSA_ASSERT(this, IsConstructor(promise_fun));
162
163 static const int kThrowawayPromiseOffset =
164 FixedArray::SizeFor(context_length);
165 static const int kResolveClosureOffset =
166 kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
167 static const int kRejectClosureOffset =
168 kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
169 static const int kTotalSize =
170 kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
171
172 // 2. Let promise be ? PromiseResolve(« promise »).
173 Node* const promise =
174 CallBuiltin(Builtins::kPromiseResolve, context, promise_fun, value);
175
176 Node* const base = AllocateInNewSpace(kTotalSize);
177 Node* const closure_context = base;
178 {
179 // Initialize closure context
180 InitializeFunctionContext(native_context, closure_context, context_length);
181 init_closure_context(closure_context);
182 }
183
184 Node* const promise_map =
185 LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
186 // Assert that the JSPromise map has an instance size is
187 // JSPromise::kSizeWithEmbedderFields.
188 CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
189 IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
190 kPointerSize)));
191 Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
192 {
193 // Initialize throwawayPromise
194 StoreMapNoWriteBarrier(throwaway, promise_map);
195 InitializeJSObjectFromMap(
196 throwaway, promise_map,
197 IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
198 PromiseInit(throwaway);
199 }
200
201 Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
202 {
203 // Initialize resolve handler
204 InitializeNativeClosure(closure_context, native_context, on_resolve,
205 on_resolve_context_index);
206 }
207
208 Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
209 {
210 // Initialize reject handler
211 InitializeNativeClosure(closure_context, native_context, on_reject,
212 on_reject_context_index);
213 }
214
215 {
216 // Add PromiseHooks if needed
217 Label next(this);
218 GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next);
219 CallRuntime(Runtime::kAwaitPromisesInit, context, promise, outer_promise,
220 throwaway);
221 Goto(&next);
222 BIND(&next);
223 }
224
225 // The Promise will be thrown away and not handled, but it shouldn't trigger
226 // unhandled reject events as its work is done
227 PromiseSetHasHandler(throwaway);
228
229 Label do_perform_promise_then(this);
230 GotoIfNot(IsDebugActive(), &do_perform_promise_then);
231 {
232 Label common(this);
233 GotoIf(TaggedIsSmi(value), &common);
234 GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
235 {
236 // Mark the reject handler callback to be a forwarding edge, rather
237 // than a meaningful catch handler
238 Node* const key =
239 HeapConstant(factory()->promise_forwarding_handler_symbol());
240 SetPropertyStrict(CAST(context), CAST(on_reject), CAST(key),
241 TrueConstant());
242
243 GotoIf(IsFalse(is_predicted_as_caught), &common);
244 PromiseSetHandledHint(value);
245 }
246
247 Goto(&common);
248 BIND(&common);
249 // Mark the dependency to outer Promise in case the throwaway Promise is
250 // found on the Promise stack
251 CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
252
253 Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
254 SetPropertyStrict(CAST(context), CAST(throwaway), CAST(key),
255 CAST(outer_promise));
256 }
257
258 Goto(&do_perform_promise_then);
259 BIND(&do_perform_promise_then);
260 return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise,
261 on_resolve, on_reject, throwaway);
262 }
263
InitializeNativeClosure(Node * context,Node * native_context,Node * function,Node * context_index)264 void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
265 Node* native_context,
266 Node* function,
267 Node* context_index) {
268 Node* const function_map = LoadContextElement(
269 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
270 // Ensure that we don't have to initialize prototype_or_initial_map field of
271 // JSFunction.
272 CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(function_map),
273 IntPtrConstant(JSFunction::kSizeWithoutPrototype /
274 kPointerSize)));
275 STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
276 StoreMapNoWriteBarrier(function, function_map);
277 StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
278 Heap::kEmptyFixedArrayRootIndex);
279 StoreObjectFieldRoot(function, JSObject::kElementsOffset,
280 Heap::kEmptyFixedArrayRootIndex);
281 StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset,
282 Heap::kManyClosuresCellRootIndex);
283
284 Node* shared_info = LoadContextElement(native_context, context_index);
285 CSA_ASSERT(this, IsSharedFunctionInfo(shared_info));
286 StoreObjectFieldNoWriteBarrier(
287 function, JSFunction::kSharedFunctionInfoOffset, shared_info);
288 StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
289
290 Node* const code = GetSharedFunctionInfoCode(shared_info);
291 StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
292 }
293
CreateUnwrapClosure(Node * native_context,Node * done)294 Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
295 Node* done) {
296 Node* const map = LoadContextElement(
297 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
298 Node* const on_fulfilled_shared = LoadContextElement(
299 native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN);
300 CSA_ASSERT(this,
301 HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE));
302 Node* const closure_context =
303 AllocateAsyncIteratorValueUnwrapContext(native_context, done);
304 return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
305 closure_context);
306 }
307
AllocateAsyncIteratorValueUnwrapContext(Node * native_context,Node * done)308 Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
309 Node* native_context, Node* done) {
310 CSA_ASSERT(this, IsNativeContext(native_context));
311 CSA_ASSERT(this, IsBoolean(done));
312
313 Node* const context =
314 CreatePromiseContext(native_context, ValueUnwrapContext::kLength);
315 StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
316 done);
317 return context;
318 }
319
TF_BUILTIN(AsyncIteratorValueUnwrap,AsyncBuiltinsAssembler)320 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
321 Node* const value = Parameter(Descriptor::kValue);
322 Node* const context = Parameter(Descriptor::kContext);
323
324 Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
325 CSA_ASSERT(this, IsBoolean(done));
326
327 Node* const unwrapped_value =
328 CallBuiltin(Builtins::kCreateIterResultObject, context, value, done);
329
330 Return(unwrapped_value);
331 }
332
333 } // namespace internal
334 } // namespace v8
335