• 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 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(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)25 TNode<Object> AsyncBuiltinsAssembler::Await(
26     TNode<Context> context, TNode<JSGeneratorObject> generator,
27     TNode<Object> value, TNode<JSPromise> outer_promise,
28     TNode<SharedFunctionInfo> on_resolve_sfi,
29     TNode<SharedFunctionInfo> on_reject_sfi,
30     TNode<Oddball> is_predicted_as_caught) {
31   const TNode<NativeContext> native_context = LoadNativeContext(context);
32 
33   // We do the `PromiseResolve(%Promise%,value)` avoiding to unnecessarily
34   // create wrapper promises. Now if {value} is already a promise with the
35   // intrinsics %Promise% constructor as its "constructor", we don't need
36   // to allocate the wrapper promise.
37   {
38     TVARIABLE(Object, var_value, value);
39     Label if_slow_path(this, Label::kDeferred), if_done(this),
40         if_slow_constructor(this, Label::kDeferred);
41     GotoIf(TaggedIsSmi(value), &if_slow_path);
42     TNode<HeapObject> value_object = CAST(value);
43     const TNode<Map> value_map = LoadMap(value_object);
44     GotoIfNot(IsJSPromiseMap(value_map), &if_slow_path);
45     // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
46     // is the (initial) Promise.prototype and the @@species protector is
47     // intact, as that guards the lookup path for "constructor" on
48     // JSPromise instances which have the (initial) Promise.prototype.
49     const TNode<Object> promise_prototype =
50         LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
51     GotoIfNot(TaggedEqual(LoadMapPrototype(value_map), promise_prototype),
52               &if_slow_constructor);
53     Branch(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor,
54            &if_done);
55 
56     // At this point, {value} doesn't have the initial promise prototype or
57     // the promise @@species protector was invalidated, but {value} could still
58     // have the %Promise% as its "constructor", so we need to check that as
59     // well.
60     BIND(&if_slow_constructor);
61     {
62       const TNode<Object> value_constructor = GetProperty(
63           context, value, isolate()->factory()->constructor_string());
64       const TNode<Object> promise_function =
65           LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
66       Branch(TaggedEqual(value_constructor, promise_function), &if_done,
67              &if_slow_path);
68     }
69 
70     BIND(&if_slow_path);
71     {
72       // We need to mark the {value} wrapper as having {outer_promise}
73       // as its parent, which is why we need to inline a good chunk of
74       // logic from the `PromiseResolve` builtin here.
75       var_value = NewJSPromise(native_context, outer_promise);
76       CallBuiltin(Builtin::kResolvePromise, native_context, var_value.value(),
77                   value);
78       Goto(&if_done);
79     }
80 
81     BIND(&if_done);
82     value = var_value.value();
83   }
84 
85   static const int kClosureContextSize =
86       FixedArray::SizeFor(Context::MIN_CONTEXT_EXTENDED_SLOTS);
87   TNode<Context> closure_context =
88       UncheckedCast<Context>(AllocateInNewSpace(kClosureContextSize));
89   {
90     // Initialize the await context, storing the {generator} as extension.
91     TNode<Map> map = CAST(
92         LoadContextElement(native_context, Context::AWAIT_CONTEXT_MAP_INDEX));
93     StoreMapNoWriteBarrier(closure_context, map);
94     StoreObjectFieldNoWriteBarrier(
95         closure_context, Context::kLengthOffset,
96         SmiConstant(Context::MIN_CONTEXT_EXTENDED_SLOTS));
97     const TNode<Object> empty_scope_info =
98         LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
99     StoreContextElementNoWriteBarrier(
100         closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
101     StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX,
102                                       native_context);
103     StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX,
104                                       generator);
105   }
106 
107   // Allocate and initialize resolve handler
108   TNode<HeapObject> on_resolve =
109       AllocateInNewSpace(JSFunction::kSizeWithoutPrototype);
110   InitializeNativeClosure(closure_context, native_context, on_resolve,
111                           on_resolve_sfi);
112 
113   // Allocate and initialize reject handler
114   TNode<HeapObject> on_reject =
115       AllocateInNewSpace(JSFunction::kSizeWithoutPrototype);
116   InitializeNativeClosure(closure_context, native_context, on_reject,
117                           on_reject_sfi);
118 
119   // Deal with PromiseHooks and debug support in the runtime. This
120   // also allocates the throwaway promise, which is only needed in
121   // case of PromiseHooks or debugging.
122   TVARIABLE(Object, var_throwaway, UndefinedConstant());
123   Label if_instrumentation(this, Label::kDeferred),
124       if_instrumentation_done(this);
125   TNode<Uint32T> promiseHookFlags = PromiseHookFlags();
126   GotoIf(IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(
127              promiseHookFlags),
128          &if_instrumentation);
129 #ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
130   // This call to NewJSPromise is to keep behaviour parity with what happens
131   // in Runtime::kDebugAsyncFunctionSuspended below if native hooks are set.
132   // It creates a throwaway promise that will trigger an init event and get
133   // passed into Builtin::kPerformPromiseThen below.
134   GotoIfNot(IsContextPromiseHookEnabled(promiseHookFlags),
135             &if_instrumentation_done);
136   var_throwaway = NewJSPromise(context, value);
137 #endif  // V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
138   Goto(&if_instrumentation_done);
139   BIND(&if_instrumentation);
140   {
141     var_throwaway = CallRuntime(Runtime::kDebugAsyncFunctionSuspended,
142                                 native_context, value, outer_promise, on_reject,
143                                 generator, is_predicted_as_caught);
144     Goto(&if_instrumentation_done);
145   }
146   BIND(&if_instrumentation_done);
147 
148   return CallBuiltin(Builtin::kPerformPromiseThen, native_context, value,
149                      on_resolve, on_reject, var_throwaway.value());
150 }
151 
InitializeNativeClosure(TNode<Context> context,TNode<NativeContext> native_context,TNode<HeapObject> function,TNode<SharedFunctionInfo> shared_info)152 void AsyncBuiltinsAssembler::InitializeNativeClosure(
153     TNode<Context> context, TNode<NativeContext> native_context,
154     TNode<HeapObject> function, TNode<SharedFunctionInfo> shared_info) {
155   TNode<Map> function_map = CAST(LoadContextElement(
156       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
157   // Ensure that we don't have to initialize prototype_or_initial_map field of
158   // JSFunction.
159   CSA_DCHECK(this,
160              IntPtrEqual(LoadMapInstanceSizeInWords(function_map),
161                          IntPtrConstant(JSFunction::kSizeWithoutPrototype /
162                                         kTaggedSize)));
163   STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
164   StoreMapNoWriteBarrier(function, function_map);
165   StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
166                        RootIndex::kEmptyFixedArray);
167   StoreObjectFieldRoot(function, JSObject::kElementsOffset,
168                        RootIndex::kEmptyFixedArray);
169   StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset,
170                        RootIndex::kManyClosuresCell);
171 
172   StoreObjectFieldNoWriteBarrier(
173       function, JSFunction::kSharedFunctionInfoOffset, shared_info);
174   StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
175 
176   // For the native closures that are initialized here (for `await`)
177   // we know that their SharedFunctionInfo::function_data(kAcquireLoad) slot
178   // contains a builtin index (as Smi), so there's no need to use
179   // CodeStubAssembler::GetSharedFunctionInfoCode() helper here,
180   // which almost doubles the size of `await` builtins (unnecessarily).
181   TNode<Smi> builtin_id = LoadObjectField<Smi>(
182       shared_info, SharedFunctionInfo::kFunctionDataOffset);
183   TNode<CodeT> code = LoadBuiltin(builtin_id);
184   StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
185 }
186 
CreateUnwrapClosure(TNode<NativeContext> native_context,TNode<Oddball> done)187 TNode<JSFunction> AsyncBuiltinsAssembler::CreateUnwrapClosure(
188     TNode<NativeContext> native_context, TNode<Oddball> done) {
189   const TNode<Map> map = CAST(LoadContextElement(
190       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
191   const TNode<SharedFunctionInfo> on_fulfilled_shared =
192       AsyncIteratorValueUnwrapSharedFunConstant();
193   const TNode<Context> closure_context =
194       AllocateAsyncIteratorValueUnwrapContext(native_context, done);
195   return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
196                                            closure_context);
197 }
198 
AllocateAsyncIteratorValueUnwrapContext(TNode<NativeContext> native_context,TNode<Oddball> done)199 TNode<Context> AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
200     TNode<NativeContext> native_context, TNode<Oddball> done) {
201   CSA_DCHECK(this, IsBoolean(done));
202 
203   TNode<Context> context = AllocateSyntheticFunctionContext(
204       native_context, ValueUnwrapContext::kLength);
205   StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
206                                     done);
207   return context;
208 }
209 
TF_BUILTIN(AsyncIteratorValueUnwrap,AsyncBuiltinsAssembler)210 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
211   auto value = Parameter<Object>(Descriptor::kValue);
212   auto context = Parameter<Context>(Descriptor::kContext);
213 
214   const TNode<Object> done =
215       LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
216   CSA_DCHECK(this, IsBoolean(CAST(done)));
217 
218   const TNode<Object> unwrapped_value =
219       CallBuiltin(Builtin::kCreateIterResultObject, context, value, done);
220 
221   Return(unwrapped_value);
222 }
223 
224 }  // namespace internal
225 }  // namespace v8
226