summaryrefslogtreecommitdiff
path: root/deps/v8/src/builtins/builtins-async-gen.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/builtins/builtins-async-gen.cc')
-rw-r--r--deps/v8/src/builtins/builtins-async-gen.cc300
1 files changed, 120 insertions, 180 deletions
diff --git a/deps/v8/src/builtins/builtins-async-gen.cc b/deps/v8/src/builtins/builtins-async-gen.cc
index dda781b1a8..7ba72844e8 100644
--- a/deps/v8/src/builtins/builtins-async-gen.cc
+++ b/deps/v8/src/builtins/builtins-async-gen.cc
@@ -32,17 +32,15 @@ Node* AsyncBuiltinsAssembler::AwaitOld(Node* context, Node* generator,
static const int kWrappedPromiseOffset =
FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS);
- static const int kThrowawayPromiseOffset =
- kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
static const int kResolveClosureOffset =
- kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
+ kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
static const int kRejectClosureOffset =
kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
static const int kTotalSize =
kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
- Node* const base = AllocateInNewSpace(kTotalSize);
- Node* const closure_context = base;
+ TNode<HeapObject> base = AllocateInNewSpace(kTotalSize);
+ TNode<Context> closure_context = UncheckedCast<Context>(base);
{
// Initialize the await context, storing the {generator} as extension.
StoreMapNoWriteBarrier(closure_context, RootIndex::kAwaitContextMap);
@@ -70,118 +68,72 @@ Node* AsyncBuiltinsAssembler::AwaitOld(Node* context, Node* generator,
// JSPromise::kSizeWithEmbedderFields.
CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
- kPointerSize)));
- Node* const wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
+ kTaggedSize)));
+ TNode<HeapObject> wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
{
// Initialize Promise
StoreMapNoWriteBarrier(wrapped_value, promise_map);
- InitializeJSObjectFromMap(
- wrapped_value, promise_map,
- IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
+ StoreObjectFieldRoot(wrapped_value, JSPromise::kPropertiesOrHashOffset,
+ RootIndex::kEmptyFixedArray);
+ StoreObjectFieldRoot(wrapped_value, JSPromise::kElementsOffset,
+ RootIndex::kEmptyFixedArray);
PromiseInit(wrapped_value);
}
- Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
- {
- // Initialize throwawayPromise
- StoreMapNoWriteBarrier(throwaway, promise_map);
- InitializeJSObjectFromMap(
- throwaway, promise_map,
- IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
- PromiseInit(throwaway);
- }
-
- Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
- {
- // Initialize resolve handler
- InitializeNativeClosure(closure_context, native_context, on_resolve,
- on_resolve_context_index);
- }
-
- Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
- {
- // Initialize reject handler
- InitializeNativeClosure(closure_context, native_context, on_reject,
- on_reject_context_index);
- }
-
- {
- // Add PromiseHooks if needed
- Label next(this);
- GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next);
- CallRuntime(Runtime::kAwaitPromisesInit, context, wrapped_value,
- outer_promise, throwaway);
- Goto(&next);
- BIND(&next);
- }
+ // Initialize resolve handler
+ TNode<HeapObject> on_resolve = InnerAllocate(base, kResolveClosureOffset);
+ InitializeNativeClosure(closure_context, native_context, on_resolve,
+ on_resolve_context_index);
+
+ // Initialize reject handler
+ TNode<HeapObject> on_reject = InnerAllocate(base, kRejectClosureOffset);
+ InitializeNativeClosure(closure_context, native_context, on_reject,
+ on_reject_context_index);
+
+ VARIABLE(var_throwaway, MachineRepresentation::kTaggedPointer,
+ UndefinedConstant());
+
+ // Deal with PromiseHooks and debug support in the runtime. This
+ // also allocates the throwaway promise, which is only needed in
+ // case of PromiseHooks or debugging.
+ Label if_debugging(this, Label::kDeferred), do_resolve_promise(this);
+ Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
+ &if_debugging, &do_resolve_promise);
+ BIND(&if_debugging);
+ var_throwaway.Bind(CallRuntime(Runtime::kAwaitPromisesInitOld, context, value,
+ wrapped_value, outer_promise, on_reject,
+ is_predicted_as_caught));
+ Goto(&do_resolve_promise);
+ BIND(&do_resolve_promise);
// Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
CallBuiltin(Builtins::kResolvePromise, context, wrapped_value, value);
- // The Promise will be thrown away and not handled, but it shouldn't trigger
- // unhandled reject events as its work is done
- PromiseSetHasHandler(throwaway);
-
- Label do_perform_promise_then(this);
- GotoIfNot(IsDebugActive(), &do_perform_promise_then);
- {
- Label common(this);
- GotoIf(TaggedIsSmi(value), &common);
- GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
- {
- // Mark the reject handler callback to be a forwarding edge, rather
- // than a meaningful catch handler
- Node* const key =
- HeapConstant(factory()->promise_forwarding_handler_symbol());
- SetPropertyStrict(CAST(context), CAST(on_reject), CAST(key),
- TrueConstant());
-
- GotoIf(IsFalse(is_predicted_as_caught), &common);
- PromiseSetHandledHint(value);
- }
-
- Goto(&common);
- BIND(&common);
- // Mark the dependency to outer Promise in case the throwaway Promise is
- // found on the Promise stack
- CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
-
- Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
- SetPropertyStrict(CAST(context), CAST(throwaway), CAST(key),
- CAST(outer_promise));
- }
-
- Goto(&do_perform_promise_then);
- BIND(&do_perform_promise_then);
return CallBuiltin(Builtins::kPerformPromiseThen, context, wrapped_value,
- on_resolve, on_reject, throwaway);
+ on_resolve, on_reject, var_throwaway.value());
}
-Node* AsyncBuiltinsAssembler::AwaitOptimized(
- Node* context, Node* generator, Node* value, Node* outer_promise,
- Node* on_resolve_context_index, Node* on_reject_context_index,
- Node* is_predicted_as_caught) {
+Node* AsyncBuiltinsAssembler::AwaitOptimized(Node* context, Node* generator,
+ Node* promise, Node* outer_promise,
+ Node* on_resolve_context_index,
+ Node* on_reject_context_index,
+ Node* is_predicted_as_caught) {
Node* const native_context = LoadNativeContext(context);
- Node* const promise_fun =
- LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
- CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
- CSA_ASSERT(this, IsConstructor(promise_fun));
+ CSA_ASSERT(this, IsJSPromise(promise));
- static const int kThrowawayPromiseOffset =
- FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS);
static const int kResolveClosureOffset =
- kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
+ FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS);
static const int kRejectClosureOffset =
kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
static const int kTotalSize =
kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
// 2. Let promise be ? PromiseResolve(« promise »).
- Node* const promise =
- CallBuiltin(Builtins::kPromiseResolve, context, promise_fun, value);
+ // Node* const promise =
+ // CallBuiltin(Builtins::kPromiseResolve, context, promise_fun, value);
- Node* const base = AllocateInNewSpace(kTotalSize);
- Node* const closure_context = base;
+ TNode<HeapObject> base = AllocateInNewSpace(kTotalSize);
+ TNode<Context> closure_context = UncheckedCast<Context>(base);
{
// Initialize the await context, storing the {generator} as extension.
StoreMapNoWriteBarrier(closure_context, RootIndex::kAwaitContextMap);
@@ -199,84 +151,34 @@ Node* AsyncBuiltinsAssembler::AwaitOptimized(
closure_context, Context::NATIVE_CONTEXT_INDEX, native_context);
}
- Node* const promise_map =
- LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
- // Assert that the JSPromise map has an instance size is
- // JSPromise::kSizeWithEmbedderFields.
- CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
- IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
- kPointerSize)));
- Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
- {
- // Initialize throwawayPromise
- StoreMapNoWriteBarrier(throwaway, promise_map);
- InitializeJSObjectFromMap(
- throwaway, promise_map,
- IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
- PromiseInit(throwaway);
- }
-
- Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
- {
- // Initialize resolve handler
- InitializeNativeClosure(closure_context, native_context, on_resolve,
- on_resolve_context_index);
- }
-
- Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
- {
- // Initialize reject handler
- InitializeNativeClosure(closure_context, native_context, on_reject,
- on_reject_context_index);
- }
-
- {
- // Add PromiseHooks if needed
- Label next(this);
- GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next);
- CallRuntime(Runtime::kAwaitPromisesInit, context, promise, outer_promise,
- throwaway);
- Goto(&next);
- BIND(&next);
- }
-
- // The Promise will be thrown away and not handled, but it shouldn't trigger
- // unhandled reject events as its work is done
- PromiseSetHasHandler(throwaway);
-
- Label do_perform_promise_then(this);
- GotoIfNot(IsDebugActive(), &do_perform_promise_then);
- {
- Label common(this);
- GotoIf(TaggedIsSmi(value), &common);
- GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
- {
- // Mark the reject handler callback to be a forwarding edge, rather
- // than a meaningful catch handler
- Node* const key =
- HeapConstant(factory()->promise_forwarding_handler_symbol());
- SetPropertyStrict(CAST(context), CAST(on_reject), CAST(key),
- TrueConstant());
-
- GotoIf(IsFalse(is_predicted_as_caught), &common);
- PromiseSetHandledHint(value);
- }
-
- Goto(&common);
- BIND(&common);
- // Mark the dependency to outer Promise in case the throwaway Promise is
- // found on the Promise stack
- CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
-
- Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
- SetPropertyStrict(CAST(context), CAST(throwaway), CAST(key),
- CAST(outer_promise));
- }
-
+ // Initialize resolve handler
+ TNode<HeapObject> on_resolve = InnerAllocate(base, kResolveClosureOffset);
+ InitializeNativeClosure(closure_context, native_context, on_resolve,
+ on_resolve_context_index);
+
+ // Initialize reject handler
+ TNode<HeapObject> on_reject = InnerAllocate(base, kRejectClosureOffset);
+ InitializeNativeClosure(closure_context, native_context, on_reject,
+ on_reject_context_index);
+
+ VARIABLE(var_throwaway, MachineRepresentation::kTaggedPointer,
+ UndefinedConstant());
+
+ // Deal with PromiseHooks and debug support in the runtime. This
+ // also allocates the throwaway promise, which is only needed in
+ // case of PromiseHooks or debugging.
+ Label if_debugging(this, Label::kDeferred), do_perform_promise_then(this);
+ Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
+ &if_debugging, &do_perform_promise_then);
+ BIND(&if_debugging);
+ var_throwaway.Bind(CallRuntime(Runtime::kAwaitPromisesInit, context, promise,
+ promise, outer_promise, on_reject,
+ is_predicted_as_caught));
Goto(&do_perform_promise_then);
BIND(&do_perform_promise_then);
+
return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise,
- on_resolve, on_reject, throwaway);
+ on_resolve, on_reject, var_throwaway.value());
}
Node* AsyncBuiltinsAssembler::Await(Node* context, Node* generator, Node* value,
@@ -285,16 +187,47 @@ Node* AsyncBuiltinsAssembler::Await(Node* context, Node* generator, Node* value,
Node* on_reject_context_index,
Node* is_predicted_as_caught) {
VARIABLE(result, MachineRepresentation::kTagged);
- Label if_old(this), if_new(this), done(this);
+ Label if_old(this), if_new(this), done(this),
+ if_slow_constructor(this, Label::kDeferred);
STATIC_ASSERT(sizeof(FLAG_harmony_await_optimization) == 1);
-
TNode<Word32T> flag_value = UncheckedCast<Word32T>(Load(
MachineType::Uint8(),
ExternalConstant(
ExternalReference::address_of_harmony_await_optimization_flag())));
-
- Branch(Word32Equal(flag_value, Int32Constant(0)), &if_old, &if_new);
+ GotoIf(Word32Equal(flag_value, Int32Constant(0)), &if_old);
+
+ // We're running with --harmony-await-optimization enabled, which means
+ // we do the `PromiseResolve(%Promise%,value)` avoiding to unnecessarily
+ // create wrapper promises. Now if {value} is already a promise with the
+ // intrinsics %Promise% constructor as its "constructor", we don't need
+ // to allocate the wrapper promise and can just use the `AwaitOptimized`
+ // logic.
+ GotoIf(TaggedIsSmi(value), &if_old);
+ Node* const value_map = LoadMap(value);
+ GotoIfNot(IsJSPromiseMap(value_map), &if_old);
+ // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
+ // is the (initial) Promise.prototype and the @@species protector is
+ // intact, as that guards the lookup path for "constructor" on
+ // JSPromise instances which have the (initial) Promise.prototype.
+ Node* const native_context = LoadNativeContext(context);
+ Node* const promise_prototype =
+ LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
+ GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype),
+ &if_slow_constructor);
+ Branch(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor, &if_new);
+
+ // At this point, {value} doesn't have the initial promise prototype or
+ // the promise @@species protector was invalidated, but {value} could still
+ // have the %Promise% as its "constructor", so we need to check that as well.
+ BIND(&if_slow_constructor);
+ {
+ Node* const value_constructor =
+ GetProperty(context, value, isolate()->factory()->constructor_string());
+ Node* const promise_function =
+ LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
+ Branch(WordEqual(value_constructor, promise_function), &if_new, &if_old);
+ }
BIND(&if_old);
result.Bind(AwaitOld(context, generator, value, outer_promise,
@@ -316,14 +249,14 @@ void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
Node* native_context,
Node* function,
Node* context_index) {
- Node* const function_map = LoadContextElement(
- native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
+ TNode<Map> function_map = CAST(LoadContextElement(
+ native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
// Ensure that we don't have to initialize prototype_or_initial_map field of
// JSFunction.
CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(function_map),
IntPtrConstant(JSFunction::kSizeWithoutPrototype /
- kPointerSize)));
- STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
+ kTaggedSize)));
+ STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
StoreMapNoWriteBarrier(function, function_map);
StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
RootIndex::kEmptyFixedArray);
@@ -332,13 +265,20 @@ void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset,
RootIndex::kManyClosuresCell);
- Node* shared_info = LoadContextElement(native_context, context_index);
- CSA_ASSERT(this, IsSharedFunctionInfo(shared_info));
+ TNode<SharedFunctionInfo> shared_info =
+ CAST(LoadContextElement(native_context, context_index));
StoreObjectFieldNoWriteBarrier(
function, JSFunction::kSharedFunctionInfoOffset, shared_info);
StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
- Node* const code = GetSharedFunctionInfoCode(shared_info);
+ // For the native closures that are initialized here (for `await`)
+ // we know that their SharedFunctionInfo::function_data() slot
+ // contains a builtin index (as Smi), so there's no need to use
+ // CodeStubAssembler::GetSharedFunctionInfoCode() helper here,
+ // which almost doubles the size of `await` builtins (unnecessarily).
+ TNode<Smi> builtin_id = LoadObjectField<Smi>(
+ shared_info, SharedFunctionInfo::kFunctionDataOffset);
+ TNode<Code> code = LoadBuiltin(builtin_id);
StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
}