• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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