v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-async-gen.cc
Go to the documentation of this file.
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
6
12
13namespace v8 {
14namespace internal {
15
17
18namespace {
19// Describe fields of Context associated with the AsyncIterator unwrap closure.
20class ValueUnwrapContext {
21 public:
22 enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
23};
24
25} // namespace
26
29 TNode<JSAny> value,
30 TNode<JSPromise> outer_promise,
31 RootIndex on_resolve_sfi,
32 RootIndex on_reject_sfi) {
33 return Await(
34 context, generator, value, outer_promise,
36 auto on_resolve = AllocateRootFunctionWithContext(
37 on_resolve_sfi, context, native_context);
38 auto on_reject = AllocateRootFunctionWithContext(on_reject_sfi, context,
40 return std::make_pair(on_resolve, on_reject);
41 });
42}
43
46 TNode<JSAny> value, TNode<JSPromise> outer_promise,
49
50 // We do the `PromiseResolve(%Promise%,value)` avoiding to unnecessarily
51 // create wrapper promises. Now if {value} is already a promise with the
52 // intrinsics %Promise% constructor as its "constructor", we don't need
53 // to allocate the wrapper promise.
54 {
55 TVARIABLE(JSAny, var_value, value);
56 Label if_slow_path(this, Label::kDeferred), if_done(this),
57 if_slow_constructor(this, Label::kDeferred);
58 GotoIf(TaggedIsSmi(value), &if_slow_path);
59 TNode<JSAnyNotSmi> value_object = CAST(value);
60 const TNode<Map> value_map = LoadMap(value_object);
61 GotoIfNot(IsJSPromiseMap(value_map), &if_slow_path);
62 // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
63 // is the (initial) Promise.prototype and the @@species protector is
64 // intact, as that guards the lookup path for "constructor" on
65 // JSPromise instances which have the (initial) Promise.prototype.
66 const TNode<Object> promise_prototype =
67 LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
68 GotoIfNot(TaggedEqual(LoadMapPrototype(value_map), promise_prototype),
69 &if_slow_constructor);
70 Branch(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor,
71 &if_done);
72
73 // At this point, {value} doesn't have the initial promise prototype or
74 // the promise @@species protector was invalidated, but {value} could still
75 // have the %Promise% as its "constructor", so we need to check that as
76 // well.
77 BIND(&if_slow_constructor);
78 {
79 const TNode<Object> value_constructor = GetProperty(
80 context, value, isolate()->factory()->constructor_string());
81 const TNode<Object> promise_function =
82 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
83 Branch(TaggedEqual(value_constructor, promise_function), &if_done,
84 &if_slow_path);
85 }
86
87 BIND(&if_slow_path);
88 {
89 // We need to mark the {value} wrapper as having {outer_promise}
90 // as its parent, which is why we need to inline a good chunk of
91 // logic from the `PromiseResolve` builtin here.
92 var_value = NewJSPromise(native_context, outer_promise);
93 CallBuiltin(Builtin::kResolvePromise, native_context, var_value.value(),
94 value);
95 Goto(&if_done);
96 }
97
98 BIND(&if_done);
99 value = var_value.value();
100 }
101
102 static const int kClosureContextSize =
104 TNode<Context> closure_context =
105 UncheckedCast<Context>(AllocateInNewSpace(kClosureContextSize));
106 {
107 // Initialize the await context, storing the {generator} as extension.
108 TNode<Map> map = CAST(
109 LoadContextElement(native_context, Context::AWAIT_CONTEXT_MAP_INDEX));
110 StoreMapNoWriteBarrier(closure_context, map);
112 closure_context, Context::kLengthOffset,
114 const TNode<Object> empty_scope_info =
115 LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
117 closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
121 generator);
122 }
123
124 // Allocate and initialize resolve and reject handlers
125 auto [on_resolve, on_reject] =
126 CreateClosures(closure_context, native_context);
127
128 // Deal with PromiseHooks and debug support in the runtime. This
129 // also allocates the throwaway promise, which is only needed in
130 // case of PromiseHooks or debugging.
131 TVARIABLE(Object, var_throwaway, UndefinedConstant());
132 Label if_instrumentation(this, Label::kDeferred),
133 if_instrumentation_done(this);
134 TNode<Uint32T> promiseHookFlags = PromiseHookFlags();
136 promiseHookFlags),
137 &if_instrumentation);
138#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
139 // This call to NewJSPromise is to keep behaviour parity with what happens
140 // in Runtime::kDebugAsyncFunctionSuspended below if native hooks are set.
141 // It creates a throwaway promise that will trigger an init event and get
142 // passed into Builtin::kPerformPromiseThen below.
143 GotoIfNot(IsContextPromiseHookEnabled(promiseHookFlags),
144 &if_instrumentation_done);
145 var_throwaway = NewJSPromise(context, value);
146#endif // V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
147 Goto(&if_instrumentation_done);
148 BIND(&if_instrumentation);
149 {
150 var_throwaway =
151 CallRuntime(Runtime::kDebugAsyncFunctionSuspended, native_context,
152 value, outer_promise, on_reject, generator);
153 Goto(&if_instrumentation_done);
154 }
155 BIND(&if_instrumentation_done);
156
157 return CallBuiltin(Builtin::kPerformPromiseThen, native_context, value,
158 on_resolve, on_reject, var_throwaway.value());
159}
160
163 const TNode<Context> closure_context =
166 RootIndex::kAsyncIteratorValueUnwrapSharedFun, closure_context,
168}
169
172 CSA_DCHECK(this, IsBoolean(done));
173
174 TNode<Context> context = AllocateSyntheticFunctionContext(
175 native_context, ValueUnwrapContext::kLength);
176 StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
177 done);
178 return context;
179}
180
181TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
182 auto value = Parameter<Object>(Descriptor::kValue);
183 auto context = Parameter<Context>(Descriptor::kContext);
184
185 const TNode<Object> done =
186 LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
187 CSA_DCHECK(this, IsBoolean(CAST(done)));
188
189 const TNode<Object> unwrapped_value =
190 CallBuiltin(Builtin::kCreateIterResultObject, context, value, done);
191
192 Return(unwrapped_value);
193}
194
196
197} // namespace internal
198} // namespace v8
#define BIND(label)
#define TVARIABLE(...)
#define CSA_DCHECK(csa,...)
#define TF_BUILTIN(Name, AssemblerBase)
std::function< std::pair< TNode< JSFunction >, TNode< JSFunction > >( TNode< Context >, TNode< NativeContext >)> CreateClosures
TNode< JSFunction > CreateUnwrapClosure(TNode< NativeContext > native_context, TNode< Boolean > done)
TNode< Object > Await(TNode< Context > context, TNode< JSGeneratorObject > generator, TNode< JSAny > value, TNode< JSPromise > outer_promise, const CreateClosures &CreateClosures)
TNode< Context > AllocateAsyncIteratorValueUnwrapContext(TNode< NativeContext > native_context, TNode< Boolean > done)
TNode< BoolT > IsBoolean(TNode< HeapObject > object)
TNode< BoolT > IsPromiseSpeciesProtectorCellInvalid()
TNode< HeapObject > AllocateInNewSpace(TNode< IntPtrT > size, AllocationFlags flags=AllocationFlag::kNone)
TNode< JSAny > GetProperty(TNode< Context > context, TNode< JSAny > receiver, Handle< Name > name)
TNode< BoolT > TaggedEqual(TNode< AnyTaggedT > a, TNode< AnyTaggedT > b)
TNode< JSFunction > AllocateRootFunctionWithContext(RootIndex function, TNode< Context > context, std::optional< TNode< NativeContext > > maybe_native_context)
TNode< JSPrototype > LoadMapPrototype(TNode< Map > map)
TNode< BoolT > IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate()
void StoreObjectFieldNoWriteBarrier(TNode< HeapObject > object, TNode< IntPtrT > offset, TNode< T > value)
TNode< NativeContext > LoadNativeContext(TNode< Context > context)
TNode< BoolT > TaggedIsSmi(TNode< MaybeObject > a)
void StoreContextElementNoWriteBarrier(TNode< Context > context, int slot_index, TNode< Object > value)
TNode< BoolT > IsJSPromiseMap(TNode< Map > map)
TNode< Map > LoadMap(TNode< HeapObject > object)
void StoreMapNoWriteBarrier(TNode< HeapObject > object, RootIndex map_root_index)
void GotoIfNot(TNode< IntegralT > condition, Label *false_label, GotoHint goto_hint=GotoHint::kNone)
TNode< Smi > SmiConstant(Tagged< Smi > value)
void GotoIf(TNode< IntegralT > condition, Label *true_label, GotoHint goto_hint=GotoHint::kNone)
TNode< T > CallRuntime(Runtime::FunctionId function, TNode< Object > context, TArgs... args)
TNode< T > CallBuiltin(Builtin id, TNode< Object > context, TArgs... args)
void Branch(TNode< IntegralT > condition, Label *true_label, Label *false_label, BranchHint branch_hint=BranchHint::kNone)
#define CAST(x)
TNode< Context > context
Handle< To > UncheckedCast(Handle< From > value)
Definition handles-inl.h:55
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877