• 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-generator.h"
10 #include "src/objects/js-promise.h"
11 #include "src/objects/shared-function-info.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 using compiler::Node;
17 
18 namespace {
19 // Describe fields of Context associated with the AsyncIterator unwrap closure.
20 class ValueUnwrapContext {
21  public:
22   enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
23 };
24 
25 }  // namespace
26 
AwaitOld(TNode<Context> context,TNode<JSGeneratorObject> generator,TNode<Object> value,TNode<JSPromise> outer_promise,TNode<SharedFunctionInfo> on_resolve_sfi,TNode<SharedFunctionInfo> on_reject_sfi,TNode<Oddball> is_predicted_as_caught)27 TNode<Object> AsyncBuiltinsAssembler::AwaitOld(
28     TNode<Context> context, TNode<JSGeneratorObject> generator,
29     TNode<Object> value, TNode<JSPromise> outer_promise,
30     TNode<SharedFunctionInfo> on_resolve_sfi,
31     TNode<SharedFunctionInfo> on_reject_sfi,
32     TNode<Oddball> is_predicted_as_caught) {
33   const TNode<NativeContext> native_context = LoadNativeContext(context);
34 
35   static const int kWrappedPromiseOffset =
36       FixedArray::SizeFor(Context::MIN_CONTEXT_EXTENDED_SLOTS);
37   static const int kResolveClosureOffset =
38       kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
39   static const int kRejectClosureOffset =
40       kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
41   static const int kTotalSize =
42       kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
43 
44   TNode<HeapObject> base = AllocateInNewSpace(kTotalSize);
45   TNode<Context> closure_context = UncheckedCast<Context>(base);
46   {
47     // Initialize the await context, storing the {generator} as extension.
48     TNode<Map> map = CAST(
49         LoadContextElement(native_context, Context::AWAIT_CONTEXT_MAP_INDEX));
50     StoreMapNoWriteBarrier(closure_context, map);
51     StoreObjectFieldNoWriteBarrier(
52         closure_context, Context::kLengthOffset,
53         SmiConstant(Context::MIN_CONTEXT_EXTENDED_SLOTS));
54     const TNode<Object> empty_scope_info =
55         LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
56     StoreContextElementNoWriteBarrier(
57         closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
58     StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX,
59                                       native_context);
60     StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX,
61                                       generator);
62   }
63 
64   // Let promiseCapability be ! NewPromiseCapability(%Promise%).
65   const TNode<JSFunction> promise_fun =
66       CAST(LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX));
67   CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
68   const TNode<Map> promise_map = CAST(
69       LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset));
70   // Assert that the JSPromise map has an instance size is
71   // JSPromise::kSizeWithEmbedderFields.
72   CSA_ASSERT(this,
73              IntPtrEqual(LoadMapInstanceSizeInWords(promise_map),
74                          IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
75                                         kTaggedSize)));
76   TNode<JSPromise> promise;
77   {
78     // Initialize Promise
79     TNode<HeapObject> wrapped_value =
80         InnerAllocate(base, kWrappedPromiseOffset);
81     StoreMapNoWriteBarrier(wrapped_value, promise_map);
82     StoreObjectFieldRoot(wrapped_value, JSPromise::kPropertiesOrHashOffset,
83                          RootIndex::kEmptyFixedArray);
84     StoreObjectFieldRoot(wrapped_value, JSPromise::kElementsOffset,
85                          RootIndex::kEmptyFixedArray);
86     promise = CAST(wrapped_value);
87     PromiseInit(promise);
88   }
89 
90   // Initialize resolve handler
91   TNode<HeapObject> on_resolve = InnerAllocate(base, kResolveClosureOffset);
92   InitializeNativeClosure(closure_context, native_context, on_resolve,
93                           on_resolve_sfi);
94 
95   // Initialize reject handler
96   TNode<HeapObject> on_reject = InnerAllocate(base, kRejectClosureOffset);
97   InitializeNativeClosure(closure_context, native_context, on_reject,
98                           on_reject_sfi);
99 
100   TVARIABLE(HeapObject, var_throwaway, UndefinedConstant());
101 
102   // Deal with PromiseHooks and debug support in the runtime. This
103   // also allocates the throwaway promise, which is only needed in
104   // case of PromiseHooks or debugging.
105   Label if_debugging(this, Label::kDeferred), do_resolve_promise(this);
106   Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
107          &if_debugging, &do_resolve_promise);
108   BIND(&if_debugging);
109   var_throwaway =
110       CAST(CallRuntime(Runtime::kAwaitPromisesInitOld, context, value, promise,
111                        outer_promise, on_reject, is_predicted_as_caught));
112   Goto(&do_resolve_promise);
113   BIND(&do_resolve_promise);
114 
115   // Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
116   CallBuiltin(Builtins::kResolvePromise, context, promise, value);
117 
118   return CallBuiltin(Builtins::kPerformPromiseThen, context, promise,
119                      on_resolve, on_reject, var_throwaway.value());
120 }
121 
AwaitOptimized(TNode<Context> context,TNode<JSGeneratorObject> generator,TNode<JSPromise> promise,TNode<JSPromise> outer_promise,TNode<SharedFunctionInfo> on_resolve_sfi,TNode<SharedFunctionInfo> on_reject_sfi,TNode<Oddball> is_predicted_as_caught)122 TNode<Object> AsyncBuiltinsAssembler::AwaitOptimized(
123     TNode<Context> context, TNode<JSGeneratorObject> generator,
124     TNode<JSPromise> promise, TNode<JSPromise> outer_promise,
125     TNode<SharedFunctionInfo> on_resolve_sfi,
126     TNode<SharedFunctionInfo> on_reject_sfi,
127     TNode<Oddball> is_predicted_as_caught) {
128   const TNode<NativeContext> native_context = LoadNativeContext(context);
129 
130   static const int kResolveClosureOffset =
131       FixedArray::SizeFor(Context::MIN_CONTEXT_EXTENDED_SLOTS);
132   static const int kRejectClosureOffset =
133       kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
134   static const int kTotalSize =
135       kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
136 
137   // 2. Let promise be ? PromiseResolve(« promise »).
138   // We skip this step, because promise is already guaranteed to be a
139   // JSPRomise at this point.
140 
141   TNode<HeapObject> base = AllocateInNewSpace(kTotalSize);
142   TNode<Context> closure_context = UncheckedCast<Context>(base);
143   {
144     // Initialize the await context, storing the {generator} as extension.
145     TNode<Map> map = CAST(
146         LoadContextElement(native_context, Context::AWAIT_CONTEXT_MAP_INDEX));
147     StoreMapNoWriteBarrier(closure_context, map);
148     StoreObjectFieldNoWriteBarrier(
149         closure_context, Context::kLengthOffset,
150         SmiConstant(Context::MIN_CONTEXT_EXTENDED_SLOTS));
151     const TNode<Object> empty_scope_info =
152         LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
153     StoreContextElementNoWriteBarrier(
154         closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
155     StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX,
156                                       native_context);
157     StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX,
158                                       generator);
159   }
160 
161   // Initialize resolve handler
162   TNode<HeapObject> on_resolve = InnerAllocate(base, kResolveClosureOffset);
163   InitializeNativeClosure(closure_context, native_context, on_resolve,
164                           on_resolve_sfi);
165 
166   // Initialize reject handler
167   TNode<HeapObject> on_reject = InnerAllocate(base, kRejectClosureOffset);
168   InitializeNativeClosure(closure_context, native_context, on_reject,
169                           on_reject_sfi);
170 
171   TVARIABLE(HeapObject, var_throwaway, UndefinedConstant());
172 
173   // Deal with PromiseHooks and debug support in the runtime. This
174   // also allocates the throwaway promise, which is only needed in
175   // case of PromiseHooks or debugging.
176   Label if_debugging(this, Label::kDeferred), do_perform_promise_then(this);
177   Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
178          &if_debugging, &do_perform_promise_then);
179   BIND(&if_debugging);
180   var_throwaway =
181       CAST(CallRuntime(Runtime::kAwaitPromisesInit, context, promise, promise,
182                        outer_promise, on_reject, is_predicted_as_caught));
183   Goto(&do_perform_promise_then);
184   BIND(&do_perform_promise_then);
185 
186   return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise,
187                      on_resolve, on_reject, var_throwaway.value());
188 }
189 
Await(TNode<Context> context,TNode<JSGeneratorObject> generator,TNode<Object> value,TNode<JSPromise> outer_promise,TNode<SharedFunctionInfo> on_resolve_sfi,TNode<SharedFunctionInfo> on_reject_sfi,TNode<Oddball> is_predicted_as_caught)190 TNode<Object> AsyncBuiltinsAssembler::Await(
191     TNode<Context> context, TNode<JSGeneratorObject> generator,
192     TNode<Object> value, TNode<JSPromise> outer_promise,
193     TNode<SharedFunctionInfo> on_resolve_sfi,
194     TNode<SharedFunctionInfo> on_reject_sfi,
195     TNode<Oddball> is_predicted_as_caught) {
196   TVARIABLE(Object, result);
197   Label if_old(this), if_new(this), done(this),
198       if_slow_constructor(this, Label::kDeferred);
199 
200   // We do the `PromiseResolve(%Promise%,value)` avoiding to unnecessarily
201   // create wrapper promises. Now if {value} is already a promise with the
202   // intrinsics %Promise% constructor as its "constructor", we don't need
203   // to allocate the wrapper promise and can just use the `AwaitOptimized`
204   // logic.
205   GotoIf(TaggedIsSmi(value), &if_old);
206   TNode<HeapObject> value_object = CAST(value);
207   const TNode<Map> value_map = LoadMap(value_object);
208   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   const TNode<NativeContext> native_context = LoadNativeContext(context);
214   const TNode<Object> promise_prototype =
215       LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
216   GotoIfNot(TaggedEqual(LoadMapPrototype(value_map), promise_prototype),
217             &if_slow_constructor);
218   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   BIND(&if_slow_constructor);
224   {
225     const TNode<Object> value_constructor =
226         GetProperty(context, value, isolate()->factory()->constructor_string());
227     const TNode<Object> promise_function =
228         LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
229     Branch(TaggedEqual(value_constructor, promise_function), &if_new, &if_old);
230   }
231 
232   BIND(&if_old);
233   result = AwaitOld(context, generator, value, outer_promise, on_resolve_sfi,
234                     on_reject_sfi, is_predicted_as_caught);
235   Goto(&done);
236 
237   BIND(&if_new);
238   result =
239       AwaitOptimized(context, generator, CAST(value), outer_promise,
240                      on_resolve_sfi, on_reject_sfi, is_predicted_as_caught);
241   Goto(&done);
242 
243   BIND(&done);
244   return result.value();
245 }
246 
InitializeNativeClosure(TNode<Context> context,TNode<NativeContext> native_context,TNode<HeapObject> function,TNode<SharedFunctionInfo> shared_info)247 void AsyncBuiltinsAssembler::InitializeNativeClosure(
248     TNode<Context> context, TNode<NativeContext> native_context,
249     TNode<HeapObject> function, TNode<SharedFunctionInfo> shared_info) {
250   TNode<Map> function_map = CAST(LoadContextElement(
251       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
252   // Ensure that we don't have to initialize prototype_or_initial_map field of
253   // JSFunction.
254   CSA_ASSERT(this,
255              IntPtrEqual(LoadMapInstanceSizeInWords(function_map),
256                          IntPtrConstant(JSFunction::kSizeWithoutPrototype /
257                                         kTaggedSize)));
258   STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
259   StoreMapNoWriteBarrier(function, function_map);
260   StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
261                        RootIndex::kEmptyFixedArray);
262   StoreObjectFieldRoot(function, JSObject::kElementsOffset,
263                        RootIndex::kEmptyFixedArray);
264   StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset,
265                        RootIndex::kManyClosuresCell);
266 
267   StoreObjectFieldNoWriteBarrier(
268       function, JSFunction::kSharedFunctionInfoOffset, shared_info);
269   StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
270 
271   // For the native closures that are initialized here (for `await`)
272   // we know that their SharedFunctionInfo::function_data(kAcquireLoad) slot
273   // contains a builtin index (as Smi), so there's no need to use
274   // CodeStubAssembler::GetSharedFunctionInfoCode() helper here,
275   // which almost doubles the size of `await` builtins (unnecessarily).
276   TNode<Smi> builtin_id = LoadObjectField<Smi>(
277       shared_info, SharedFunctionInfo::kFunctionDataOffset);
278   TNode<Code> code = LoadBuiltin(builtin_id);
279   StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
280 }
281 
CreateUnwrapClosure(TNode<NativeContext> native_context,TNode<Oddball> done)282 TNode<JSFunction> AsyncBuiltinsAssembler::CreateUnwrapClosure(
283     TNode<NativeContext> native_context, TNode<Oddball> done) {
284   const TNode<Map> map = CAST(LoadContextElement(
285       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
286   const TNode<SharedFunctionInfo> on_fulfilled_shared =
287       AsyncIteratorValueUnwrapSharedFunConstant();
288   const TNode<Context> closure_context =
289       AllocateAsyncIteratorValueUnwrapContext(native_context, done);
290   return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
291                                            closure_context);
292 }
293 
AllocateAsyncIteratorValueUnwrapContext(TNode<NativeContext> native_context,TNode<Oddball> done)294 TNode<Context> AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
295     TNode<NativeContext> native_context, TNode<Oddball> done) {
296   CSA_ASSERT(this, IsBoolean(done));
297 
298   TNode<Context> context = AllocateSyntheticFunctionContext(
299       native_context, ValueUnwrapContext::kLength);
300   StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
301                                     done);
302   return context;
303 }
304 
TF_BUILTIN(AsyncIteratorValueUnwrap,AsyncBuiltinsAssembler)305 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
306   auto value = Parameter<Object>(Descriptor::kValue);
307   auto context = Parameter<Context>(Descriptor::kContext);
308 
309   const TNode<Object> done =
310       LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
311   CSA_ASSERT(this, IsBoolean(CAST(done)));
312 
313   const TNode<Object> unwrapped_value =
314       CallBuiltin(Builtins::kCreateIterResultObject, context, value, done);
315 
316   Return(unwrapped_value);
317 }
318 
319 }  // namespace internal
320 }  // namespace v8
321