V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
builtins-async-gen.cc
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 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  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  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  StoreMapNoWriteBarrier(closure_context, RootIndex::kAwaitContextMap);
47  StoreObjectFieldNoWriteBarrier(closure_context, Context::kLengthOffset,
48  SmiConstant(Context::MIN_CONTEXT_SLOTS));
49  Node* const empty_scope_info =
50  LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
51  StoreContextElementNoWriteBarrier(
52  closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
53  StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX,
54  native_context);
55  StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX,
56  generator);
57  StoreContextElementNoWriteBarrier(
58  closure_context, Context::NATIVE_CONTEXT_INDEX, native_context);
59  }
60 
61  // Let promiseCapability be ! NewPromiseCapability(%Promise%).
62  Node* const promise_fun =
63  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  kPointerSize)));
72  TNode<HeapObject> wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
73  {
74  // Initialize Promise
75  StoreMapNoWriteBarrier(wrapped_value, promise_map);
76  StoreObjectFieldRoot(wrapped_value, JSPromise::kPropertiesOrHashOffset,
77  RootIndex::kEmptyFixedArray);
78  StoreObjectFieldRoot(wrapped_value, JSPromise::kElementsOffset,
79  RootIndex::kEmptyFixedArray);
80  PromiseInit(wrapped_value);
81  }
82 
83  // Initialize resolve handler
84  TNode<HeapObject> on_resolve = InnerAllocate(base, kResolveClosureOffset);
85  InitializeNativeClosure(closure_context, native_context, on_resolve,
86  on_resolve_context_index);
87 
88  // Initialize reject handler
89  TNode<HeapObject> on_reject = InnerAllocate(base, kRejectClosureOffset);
90  InitializeNativeClosure(closure_context, native_context, on_reject,
91  on_reject_context_index);
92 
93  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  Label if_debugging(this, Label::kDeferred), do_resolve_promise(this);
100  Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
101  &if_debugging, &do_resolve_promise);
102  BIND(&if_debugging);
103  var_throwaway.Bind(CallRuntime(Runtime::kAwaitPromisesInitOld, context, value,
104  wrapped_value, outer_promise, on_reject,
105  is_predicted_as_caught));
106  Goto(&do_resolve_promise);
107  BIND(&do_resolve_promise);
108 
109  // Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
110  CallBuiltin(Builtins::kResolvePromise, context, wrapped_value, value);
111 
112  return CallBuiltin(Builtins::kPerformPromiseThen, context, wrapped_value,
113  on_resolve, on_reject, var_throwaway.value());
114 }
115 
116 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  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  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  StoreMapNoWriteBarrier(closure_context, RootIndex::kAwaitContextMap);
140  StoreObjectFieldNoWriteBarrier(closure_context, Context::kLengthOffset,
141  SmiConstant(Context::MIN_CONTEXT_SLOTS));
142  Node* const empty_scope_info =
143  LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
144  StoreContextElementNoWriteBarrier(
145  closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
146  StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX,
147  native_context);
148  StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX,
149  generator);
150  StoreContextElementNoWriteBarrier(
151  closure_context, Context::NATIVE_CONTEXT_INDEX, native_context);
152  }
153 
154  // Initialize resolve handler
155  TNode<HeapObject> on_resolve = InnerAllocate(base, kResolveClosureOffset);
156  InitializeNativeClosure(closure_context, native_context, on_resolve,
157  on_resolve_context_index);
158 
159  // Initialize reject handler
160  TNode<HeapObject> on_reject = InnerAllocate(base, kRejectClosureOffset);
161  InitializeNativeClosure(closure_context, native_context, on_reject,
162  on_reject_context_index);
163 
164  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  Label if_debugging(this, Label::kDeferred), do_perform_promise_then(this);
171  Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
172  &if_debugging, &do_perform_promise_then);
173  BIND(&if_debugging);
174  var_throwaway.Bind(CallRuntime(Runtime::kAwaitPromisesInit, context, promise,
175  promise, outer_promise, on_reject,
176  is_predicted_as_caught));
177  Goto(&do_perform_promise_then);
178  BIND(&do_perform_promise_then);
179 
180  return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise,
181  on_resolve, on_reject, var_throwaway.value());
182 }
183 
184 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  VARIABLE(result, MachineRepresentation::kTagged);
190  Label if_old(this), if_new(this), done(this),
191  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  ExternalConstant(
197  ExternalReference::address_of_harmony_await_optimization_flag())));
198  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  GotoIf(TaggedIsSmi(value), &if_old);
207  Node* const value_map = LoadMap(value);
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  Node* const native_context = LoadNativeContext(context);
214  Node* const promise_prototype =
215  LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
216  GotoIfNot(WordEqual(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  Node* const value_constructor =
226  GetProperty(context, value, isolate()->factory()->constructor_string());
227  Node* const promise_function =
228  LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
229  Branch(WordEqual(value_constructor, promise_function), &if_new, &if_old);
230  }
231 
232  BIND(&if_old);
233  result.Bind(AwaitOld(context, generator, value, outer_promise,
234  on_resolve_context_index, on_reject_context_index,
235  is_predicted_as_caught));
236  Goto(&done);
237 
238  BIND(&if_new);
239  result.Bind(AwaitOptimized(context, generator, value, outer_promise,
240  on_resolve_context_index, on_reject_context_index,
241  is_predicted_as_caught));
242  Goto(&done);
243 
244  BIND(&done);
245  return result.value();
246 }
247 
248 void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
249  Node* native_context,
250  Node* function,
251  Node* context_index) {
252  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  kPointerSize)));
259  STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
260  StoreMapNoWriteBarrier(function, function_map);
261  StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
262  RootIndex::kEmptyFixedArray);
263  StoreObjectFieldRoot(function, JSObject::kElementsOffset,
264  RootIndex::kEmptyFixedArray);
265  StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset,
266  RootIndex::kManyClosuresCell);
267 
268  TNode<SharedFunctionInfo> shared_info =
269  CAST(LoadContextElement(native_context, context_index));
270  StoreObjectFieldNoWriteBarrier(
271  function, JSFunction::kSharedFunctionInfoOffset, shared_info);
272  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  shared_info, SharedFunctionInfo::kFunctionDataOffset);
281  TNode<Code> code = LoadBuiltin(builtin_id);
282  StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
283 }
284 
285 Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
286  Node* done) {
287  Node* const map = LoadContextElement(
288  native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
289  Node* const on_fulfilled_shared = LoadContextElement(
290  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  AllocateAsyncIteratorValueUnwrapContext(native_context, done);
295  return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
296  closure_context);
297 }
298 
299 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  CreatePromiseContext(native_context, ValueUnwrapContext::kLength);
306  StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
307  done);
308  return context;
309 }
310 
311 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
312  Node* const value = Parameter(Descriptor::kValue);
313  Node* const context = Parameter(Descriptor::kContext);
314 
315  Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
316  CSA_ASSERT(this, IsBoolean(done));
317 
318  Node* const unwrapped_value =
319  CallBuiltin(Builtins::kCreateIterResultObject, context, value, done);
320 
321  Return(unwrapped_value);
322 }
323 
324 } // namespace internal
325 } // namespace v8
Definition: libplatform.h:13