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/shared-function-info.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 using compiler::Node;
15 
16 namespace {
17 // Describe fields of Context associated with the AsyncIterator unwrap closure.
18 class ValueUnwrapContext {
19  public:
20   enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
21 };
22 
23 }  // namespace
24 
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)25 Node* AsyncBuiltinsAssembler::Await(
26     Node* context, Node* generator, Node* value, Node* outer_promise,
27     int context_length, const ContextInitializer& init_closure_context,
28     Node* on_resolve_context_index, Node* on_reject_context_index,
29     Node* is_predicted_as_caught) {
30   DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);
31 
32   Node* const native_context = LoadNativeContext(context);
33 
34   static const int kWrappedPromiseOffset = FixedArray::SizeFor(context_length);
35   static const int kThrowawayPromiseOffset =
36       kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
37   static const int kResolveClosureOffset =
38       kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
39   static const int kRejectClosureOffset =
40       kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
41   static const int kTotalSize =
42       kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
43 
44   Node* const base = AllocateInNewSpace(kTotalSize);
45   Node* const closure_context = base;
46   {
47     // Initialize closure context
48     InitializeFunctionContext(native_context, closure_context, context_length);
49     init_closure_context(closure_context);
50   }
51 
52   // Let promiseCapability be ! NewPromiseCapability(%Promise%).
53   Node* const promise_fun =
54       LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
55   CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
56   Node* const promise_map =
57       LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
58   // Assert that the JSPromise map has an instance size is
59   // JSPromise::kSizeWithEmbedderFields.
60   CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
61                              IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
62                                             kPointerSize)));
63   Node* const wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
64   {
65     // Initialize Promise
66     StoreMapNoWriteBarrier(wrapped_value, promise_map);
67     InitializeJSObjectFromMap(
68         wrapped_value, promise_map,
69         IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
70     PromiseInit(wrapped_value);
71   }
72 
73   Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
74   {
75     // Initialize throwawayPromise
76     StoreMapNoWriteBarrier(throwaway, promise_map);
77     InitializeJSObjectFromMap(
78         throwaway, promise_map,
79         IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
80     PromiseInit(throwaway);
81   }
82 
83   Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
84   {
85     // Initialize resolve handler
86     InitializeNativeClosure(closure_context, native_context, on_resolve,
87                             on_resolve_context_index);
88   }
89 
90   Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
91   {
92     // Initialize reject handler
93     InitializeNativeClosure(closure_context, native_context, on_reject,
94                             on_reject_context_index);
95   }
96 
97   {
98     // Add PromiseHooks if needed
99     Label next(this);
100     GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &next);
101     CallRuntime(Runtime::kPromiseHookInit, context, wrapped_value,
102                 outer_promise);
103     CallRuntime(Runtime::kPromiseHookInit, context, throwaway, wrapped_value);
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       CallRuntime(Runtime::kSetProperty, context, on_reject, key,
127                   TrueConstant(), SmiConstant(LanguageMode::kStrict));
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     CallRuntime(Runtime::kSetProperty, context, throwaway, key, outer_promise,
141                 SmiConstant(LanguageMode::kStrict));
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 
InitializeNativeClosure(Node * context,Node * native_context,Node * function,Node * context_index)150 void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
151                                                      Node* native_context,
152                                                      Node* function,
153                                                      Node* context_index) {
154   Node* const function_map = LoadContextElement(
155       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
156   // Ensure that we don't have to initialize prototype_or_initial_map field of
157   // JSFunction.
158   CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(function_map),
159                              IntPtrConstant(JSFunction::kSizeWithoutPrototype /
160                                             kPointerSize)));
161   STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
162   StoreMapNoWriteBarrier(function, function_map);
163   StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
164                        Heap::kEmptyFixedArrayRootIndex);
165   StoreObjectFieldRoot(function, JSObject::kElementsOffset,
166                        Heap::kEmptyFixedArrayRootIndex);
167   StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset,
168                        Heap::kManyClosuresCellRootIndex);
169 
170   Node* shared_info = LoadContextElement(native_context, context_index);
171   CSA_ASSERT(this, IsSharedFunctionInfo(shared_info));
172   StoreObjectFieldNoWriteBarrier(
173       function, JSFunction::kSharedFunctionInfoOffset, shared_info);
174   StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
175 
176   Node* const code = GetSharedFunctionInfoCode(shared_info);
177   StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
178 }
179 
CreateUnwrapClosure(Node * native_context,Node * done)180 Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
181                                                   Node* done) {
182   Node* const map = LoadContextElement(
183       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
184   Node* const on_fulfilled_shared = LoadContextElement(
185       native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN);
186   CSA_ASSERT(this,
187              HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE));
188   Node* const closure_context =
189       AllocateAsyncIteratorValueUnwrapContext(native_context, done);
190   return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
191                                            closure_context);
192 }
193 
AllocateAsyncIteratorValueUnwrapContext(Node * native_context,Node * done)194 Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
195     Node* native_context, Node* done) {
196   CSA_ASSERT(this, IsNativeContext(native_context));
197   CSA_ASSERT(this, IsBoolean(done));
198 
199   Node* const context =
200       CreatePromiseContext(native_context, ValueUnwrapContext::kLength);
201   StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
202                                     done);
203   return context;
204 }
205 
TF_BUILTIN(AsyncIteratorValueUnwrap,AsyncBuiltinsAssembler)206 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
207   Node* const value = Parameter(Descriptor::kValue);
208   Node* const context = Parameter(Descriptor::kContext);
209 
210   Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
211   CSA_ASSERT(this, IsBoolean(done));
212 
213   Node* const unwrapped_value =
214       CallBuiltin(Builtins::kCreateIterResultObject, context, value, done);
215 
216   Return(unwrapped_value);
217 }
218 
219 }  // namespace internal
220 }  // namespace v8
221