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