Line data Source code
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 :
26 336 : Node* AsyncBuiltinsAssembler::AwaitOld(Node* context, Node* generator,
27 : Node* value, Node* outer_promise,
28 : Node* on_resolve_context_index,
29 : Node* on_reject_context_index,
30 : Node* is_predicted_as_caught) {
31 672 : Node* const native_context = LoadNativeContext(context);
32 :
33 : static const int kWrappedPromiseOffset =
34 : FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS);
35 : static const int kResolveClosureOffset =
36 : kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
37 : static const int kRejectClosureOffset =
38 : kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
39 : static const int kTotalSize =
40 : kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
41 :
42 336 : TNode<HeapObject> base = AllocateInNewSpace(kTotalSize);
43 : TNode<Context> closure_context = UncheckedCast<Context>(base);
44 : {
45 : // Initialize the await context, storing the {generator} as extension.
46 336 : StoreMapNoWriteBarrier(closure_context, RootIndex::kAwaitContextMap);
47 336 : StoreObjectFieldNoWriteBarrier(closure_context, Context::kLengthOffset,
48 : SmiConstant(Context::MIN_CONTEXT_SLOTS));
49 : Node* const empty_scope_info =
50 672 : LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
51 336 : StoreContextElementNoWriteBarrier(
52 336 : closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
53 336 : StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX,
54 336 : native_context);
55 336 : StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX,
56 336 : generator);
57 336 : StoreContextElementNoWriteBarrier(
58 336 : closure_context, Context::NATIVE_CONTEXT_INDEX, native_context);
59 : }
60 :
61 : // Let promiseCapability be ! NewPromiseCapability(%Promise%).
62 : Node* const promise_fun =
63 672 : LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
64 : CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
65 : Node* const promise_map =
66 : LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
67 : // Assert that the JSPromise map has an instance size is
68 : // JSPromise::kSizeWithEmbedderFields.
69 : CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
70 : IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
71 : kTaggedSize)));
72 336 : TNode<HeapObject> wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
73 : {
74 : // Initialize Promise
75 336 : StoreMapNoWriteBarrier(wrapped_value, promise_map);
76 : StoreObjectFieldRoot(wrapped_value, JSPromise::kPropertiesOrHashOffset,
77 336 : RootIndex::kEmptyFixedArray);
78 : StoreObjectFieldRoot(wrapped_value, JSPromise::kElementsOffset,
79 336 : RootIndex::kEmptyFixedArray);
80 336 : PromiseInit(wrapped_value);
81 : }
82 :
83 : // Initialize resolve handler
84 336 : TNode<HeapObject> on_resolve = InnerAllocate(base, kResolveClosureOffset);
85 : InitializeNativeClosure(closure_context, native_context, on_resolve,
86 336 : on_resolve_context_index);
87 :
88 : // Initialize reject handler
89 336 : TNode<HeapObject> on_reject = InnerAllocate(base, kRejectClosureOffset);
90 : InitializeNativeClosure(closure_context, native_context, on_reject,
91 336 : on_reject_context_index);
92 :
93 1008 : VARIABLE(var_throwaway, MachineRepresentation::kTaggedPointer,
94 : UndefinedConstant());
95 :
96 : // Deal with PromiseHooks and debug support in the runtime. This
97 : // also allocates the throwaway promise, which is only needed in
98 : // case of PromiseHooks or debugging.
99 336 : Label if_debugging(this, Label::kDeferred), do_resolve_promise(this);
100 672 : Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
101 336 : &if_debugging, &do_resolve_promise);
102 336 : BIND(&if_debugging);
103 : var_throwaway.Bind(CallRuntime(Runtime::kAwaitPromisesInitOld, context, value,
104 : wrapped_value, outer_promise, on_reject,
105 336 : is_predicted_as_caught));
106 336 : Goto(&do_resolve_promise);
107 336 : BIND(&do_resolve_promise);
108 :
109 : // Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
110 336 : CallBuiltin(Builtins::kResolvePromise, context, wrapped_value, value);
111 :
112 672 : return CallBuiltin(Builtins::kPerformPromiseThen, context, wrapped_value,
113 1008 : on_resolve, on_reject, var_throwaway.value());
114 : }
115 :
116 336 : Node* AsyncBuiltinsAssembler::AwaitOptimized(Node* context, Node* generator,
117 : Node* promise, Node* outer_promise,
118 : Node* on_resolve_context_index,
119 : Node* on_reject_context_index,
120 : Node* is_predicted_as_caught) {
121 672 : Node* const native_context = LoadNativeContext(context);
122 : CSA_ASSERT(this, IsJSPromise(promise));
123 :
124 : static const int kResolveClosureOffset =
125 : FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS);
126 : static const int kRejectClosureOffset =
127 : kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
128 : static const int kTotalSize =
129 : kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
130 :
131 : // 2. Let promise be ? PromiseResolve(« promise »).
132 : // Node* const promise =
133 : // CallBuiltin(Builtins::kPromiseResolve, context, promise_fun, value);
134 :
135 336 : TNode<HeapObject> base = AllocateInNewSpace(kTotalSize);
136 : TNode<Context> closure_context = UncheckedCast<Context>(base);
137 : {
138 : // Initialize the await context, storing the {generator} as extension.
139 336 : StoreMapNoWriteBarrier(closure_context, RootIndex::kAwaitContextMap);
140 336 : StoreObjectFieldNoWriteBarrier(closure_context, Context::kLengthOffset,
141 : SmiConstant(Context::MIN_CONTEXT_SLOTS));
142 : Node* const empty_scope_info =
143 672 : LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
144 336 : StoreContextElementNoWriteBarrier(
145 336 : closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
146 336 : StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX,
147 336 : native_context);
148 336 : StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX,
149 336 : generator);
150 336 : StoreContextElementNoWriteBarrier(
151 336 : closure_context, Context::NATIVE_CONTEXT_INDEX, native_context);
152 : }
153 :
154 : // Initialize resolve handler
155 336 : TNode<HeapObject> on_resolve = InnerAllocate(base, kResolveClosureOffset);
156 : InitializeNativeClosure(closure_context, native_context, on_resolve,
157 336 : on_resolve_context_index);
158 :
159 : // Initialize reject handler
160 336 : TNode<HeapObject> on_reject = InnerAllocate(base, kRejectClosureOffset);
161 : InitializeNativeClosure(closure_context, native_context, on_reject,
162 336 : on_reject_context_index);
163 :
164 1008 : VARIABLE(var_throwaway, MachineRepresentation::kTaggedPointer,
165 : UndefinedConstant());
166 :
167 : // Deal with PromiseHooks and debug support in the runtime. This
168 : // also allocates the throwaway promise, which is only needed in
169 : // case of PromiseHooks or debugging.
170 336 : Label if_debugging(this, Label::kDeferred), do_perform_promise_then(this);
171 672 : Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
172 336 : &if_debugging, &do_perform_promise_then);
173 336 : BIND(&if_debugging);
174 : var_throwaway.Bind(CallRuntime(Runtime::kAwaitPromisesInit, context, promise,
175 : promise, outer_promise, on_reject,
176 336 : is_predicted_as_caught));
177 336 : Goto(&do_perform_promise_then);
178 336 : BIND(&do_perform_promise_then);
179 :
180 672 : return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise,
181 1008 : on_resolve, on_reject, var_throwaway.value());
182 : }
183 :
184 336 : Node* AsyncBuiltinsAssembler::Await(Node* context, Node* generator, Node* value,
185 : Node* outer_promise,
186 : Node* on_resolve_context_index,
187 : Node* on_reject_context_index,
188 : Node* is_predicted_as_caught) {
189 672 : VARIABLE(result, MachineRepresentation::kTagged);
190 336 : Label if_old(this), if_new(this), done(this),
191 336 : if_slow_constructor(this, Label::kDeferred);
192 :
193 : STATIC_ASSERT(sizeof(FLAG_harmony_await_optimization) == 1);
194 : TNode<Word32T> flag_value = UncheckedCast<Word32T>(Load(
195 : MachineType::Uint8(),
196 672 : ExternalConstant(
197 672 : ExternalReference::address_of_harmony_await_optimization_flag())));
198 1008 : GotoIf(Word32Equal(flag_value, Int32Constant(0)), &if_old);
199 :
200 : // We're running with --harmony-await-optimization enabled, which means
201 : // we do the `PromiseResolve(%Promise%,value)` avoiding to unnecessarily
202 : // create wrapper promises. Now if {value} is already a promise with the
203 : // intrinsics %Promise% constructor as its "constructor", we don't need
204 : // to allocate the wrapper promise and can just use the `AwaitOptimized`
205 : // logic.
206 672 : GotoIf(TaggedIsSmi(value), &if_old);
207 672 : Node* const value_map = LoadMap(value);
208 672 : GotoIfNot(IsJSPromiseMap(value_map), &if_old);
209 : // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
210 : // is the (initial) Promise.prototype and the @@species protector is
211 : // intact, as that guards the lookup path for "constructor" on
212 : // JSPromise instances which have the (initial) Promise.prototype.
213 672 : Node* const native_context = LoadNativeContext(context);
214 : Node* const promise_prototype =
215 672 : LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
216 672 : GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype),
217 336 : &if_slow_constructor);
218 672 : Branch(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor, &if_new);
219 :
220 : // At this point, {value} doesn't have the initial promise prototype or
221 : // the promise @@species protector was invalidated, but {value} could still
222 : // have the %Promise% as its "constructor", so we need to check that as well.
223 336 : BIND(&if_slow_constructor);
224 : {
225 : Node* const value_constructor =
226 1008 : GetProperty(context, value, isolate()->factory()->constructor_string());
227 : Node* const promise_function =
228 672 : LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
229 672 : Branch(WordEqual(value_constructor, promise_function), &if_new, &if_old);
230 : }
231 :
232 336 : BIND(&if_old);
233 336 : result.Bind(AwaitOld(context, generator, value, outer_promise,
234 : on_resolve_context_index, on_reject_context_index,
235 336 : is_predicted_as_caught));
236 336 : Goto(&done);
237 :
238 336 : BIND(&if_new);
239 336 : result.Bind(AwaitOptimized(context, generator, value, outer_promise,
240 : on_resolve_context_index, on_reject_context_index,
241 336 : is_predicted_as_caught));
242 336 : Goto(&done);
243 :
244 336 : BIND(&done);
245 672 : return result.value();
246 : }
247 :
248 1344 : void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
249 : Node* native_context,
250 : Node* function,
251 : Node* context_index) {
252 1344 : TNode<Map> function_map = CAST(LoadContextElement(
253 : native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
254 : // Ensure that we don't have to initialize prototype_or_initial_map field of
255 : // JSFunction.
256 : CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(function_map),
257 : IntPtrConstant(JSFunction::kSizeWithoutPrototype /
258 : kTaggedSize)));
259 : STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
260 1344 : StoreMapNoWriteBarrier(function, function_map);
261 : StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
262 1344 : RootIndex::kEmptyFixedArray);
263 : StoreObjectFieldRoot(function, JSObject::kElementsOffset,
264 1344 : RootIndex::kEmptyFixedArray);
265 : StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset,
266 1344 : RootIndex::kManyClosuresCell);
267 :
268 : TNode<SharedFunctionInfo> shared_info =
269 1344 : CAST(LoadContextElement(native_context, context_index));
270 : StoreObjectFieldNoWriteBarrier(
271 1344 : function, JSFunction::kSharedFunctionInfoOffset, shared_info);
272 1344 : StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
273 :
274 : // For the native closures that are initialized here (for `await`)
275 : // we know that their SharedFunctionInfo::function_data() slot
276 : // contains a builtin index (as Smi), so there's no need to use
277 : // CodeStubAssembler::GetSharedFunctionInfoCode() helper here,
278 : // which almost doubles the size of `await` builtins (unnecessarily).
279 : TNode<Smi> builtin_id = LoadObjectField<Smi>(
280 1344 : shared_info, SharedFunctionInfo::kFunctionDataOffset);
281 1344 : TNode<Code> code = LoadBuiltin(builtin_id);
282 1344 : StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
283 1344 : }
284 :
285 168 : Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
286 : Node* done) {
287 336 : Node* const map = LoadContextElement(
288 336 : native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
289 336 : Node* const on_fulfilled_shared = LoadContextElement(
290 168 : native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN);
291 : CSA_ASSERT(this,
292 : HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE));
293 : Node* const closure_context =
294 168 : AllocateAsyncIteratorValueUnwrapContext(native_context, done);
295 : return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
296 168 : closure_context);
297 : }
298 :
299 168 : Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
300 : Node* native_context, Node* done) {
301 : CSA_ASSERT(this, IsNativeContext(native_context));
302 : CSA_ASSERT(this, IsBoolean(done));
303 :
304 : Node* const context =
305 168 : CreatePromiseContext(native_context, ValueUnwrapContext::kLength);
306 336 : StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
307 168 : done);
308 168 : return context;
309 : }
310 :
311 224 : TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
312 : Node* const value = Parameter(Descriptor::kValue);
313 : Node* const context = Parameter(Descriptor::kContext);
314 :
315 112 : Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
316 : CSA_ASSERT(this, IsBoolean(done));
317 :
318 : Node* const unwrapped_value =
319 112 : CallBuiltin(Builtins::kCreateIterResultObject, context, value, done);
320 :
321 56 : Return(unwrapped_value);
322 56 : }
323 :
324 : } // namespace internal
325 59456 : } // namespace v8
|