Commit 9a6c54fc authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[builtins] Use @@species protector for the PromiseResolve fast-path.

The @@species protector guards the following (in case of Promises):

  - The initial Promise.prototype has the initial "constructor"
    pointing to the Promise constructor.
  - No JSPromise instance has a "constructor" property.

So this is sufficient to guard the fast-path in PromiseResolve, given
that we check whether the value is actually a JSPromise and that the
[[Prototype]] of value is the (initial) Promise.prototype.

Also refactor the code a bit and avoid the BranchIfSameValue, which
blows up the builtin quite a lot, since we already know that constructor
must be a valid JSReceiver and thus we can simply use WordEqual to
compare value's "constructor" to the constructor.

Bug: v8:7253
Change-Id: I6413882241c9648c95bb2299100a6c3a7c803110
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Reviewed-on: https://chromium-review.googlesource.com/890438Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50933}
parent 70c02375
......@@ -1287,51 +1287,49 @@ TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) {
Node* constructor = Parameter(Descriptor::kConstructor);
Node* value = Parameter(Descriptor::kValue);
Node* context = Parameter(Descriptor::kContext);
Isolate* isolate = this->isolate();
CSA_ASSERT(this, IsJSReceiver(constructor));
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Label if_value_is_native_promise(this),
if_value_or_constructor_are_not_native_promise(this),
if_need_to_allocate(this);
Label if_slow_constructor(this, Label::kDeferred), if_need_to_allocate(this);
// Check if {value} is a JSPromise.
GotoIf(TaggedIsSmi(value), &if_need_to_allocate);
Node* const value_map = LoadMap(value);
GotoIfNot(IsJSPromiseMap(value_map), &if_need_to_allocate);
// This shortcircuits the constructor lookups.
GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &if_need_to_allocate);
// This adds a fast path as non-subclassed native promises don't have
// an observable constructor lookup.
BranchIfFastPath(native_context, promise_fun, value,
&if_value_is_native_promise,
&if_value_or_constructor_are_not_native_promise);
BIND(&if_value_is_native_promise);
{
GotoIfNot(WordEqual(promise_fun, constructor),
&if_value_or_constructor_are_not_native_promise);
Return(value);
}
// 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 promise_prototype =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype),
&if_slow_constructor);
GotoIf(IsSpeciesProtectorCellInvalid(), &if_slow_constructor);
// If the {constructor} is the Promise function, we just immediately
// return the {value} here and don't bother wrapping it into a
// native Promise.
GotoIfNot(WordEqual(promise_fun, constructor), &if_slow_constructor);
Return(value);
// At this point, value or/and constructor are not native promises, but
// they could be of the same subclass.
BIND(&if_value_or_constructor_are_not_native_promise);
BIND(&if_slow_constructor);
{
Label if_return(this);
Node* const xConstructor =
GetProperty(context, value, isolate->factory()->constructor_string());
BranchIfSameValue(xConstructor, constructor, &if_return,
&if_need_to_allocate);
BIND(&if_return);
Node* const value_constructor =
GetProperty(context, value, isolate()->factory()->constructor_string());
GotoIfNot(WordEqual(value_constructor, constructor), &if_need_to_allocate);
Return(value);
}
BIND(&if_need_to_allocate);
{
Label if_nativepromise(this), if_notnativepromise(this);
Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred);
Branch(WordEqual(promise_fun, constructor), &if_nativepromise,
&if_notnativepromise);
......@@ -1350,8 +1348,9 @@ TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) {
Node* const resolve =
LoadObjectField(capability, PromiseCapability::kResolveOffset);
CallJS(CodeFactory::Call(isolate, ConvertReceiverMode::kNullOrUndefined),
context, resolve, UndefinedConstant(), value);
CallJS(
CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
context, resolve, UndefinedConstant(), value);
Node* const result =
LoadObjectField(capability, PromiseCapability::kPromiseOffset);
......
......@@ -4314,6 +4314,11 @@ Node* CodeStubAssembler::IsJSObject(Node* object) {
return IsJSObjectMap(LoadMap(object));
}
Node* CodeStubAssembler::IsJSPromiseMap(Node* map) {
CSA_ASSERT(this, IsMap(map));
return InstanceTypeEqual(LoadMapInstanceType(map), JS_PROMISE_TYPE);
}
Node* CodeStubAssembler::IsJSProxy(Node* object) {
return HasInstanceType(object, JS_PROXY_TYPE);
}
......
......@@ -1117,6 +1117,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsJSObjectInstanceType(Node* instance_type);
Node* IsJSObjectMap(Node* map);
Node* IsJSObject(Node* object);
Node* IsJSPromiseMap(Node* map);
Node* IsJSProxy(Node* object);
Node* IsJSReceiverInstanceType(Node* instance_type);
Node* IsJSReceiverMap(Node* map);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment