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) { ...@@ -1287,51 +1287,49 @@ TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) {
Node* constructor = Parameter(Descriptor::kConstructor); Node* constructor = Parameter(Descriptor::kConstructor);
Node* value = Parameter(Descriptor::kValue); Node* value = Parameter(Descriptor::kValue);
Node* context = Parameter(Descriptor::kContext); Node* context = Parameter(Descriptor::kContext);
Isolate* isolate = this->isolate();
CSA_ASSERT(this, IsJSReceiver(constructor));
Node* const native_context = LoadNativeContext(context); Node* const native_context = LoadNativeContext(context);
Node* const promise_fun = Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Label if_value_is_native_promise(this), Label if_slow_constructor(this, Label::kDeferred), if_need_to_allocate(this);
if_value_or_constructor_are_not_native_promise(this),
if_need_to_allocate(this);
// Check if {value} is a JSPromise.
GotoIf(TaggedIsSmi(value), &if_need_to_allocate); 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. // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &if_need_to_allocate); // is the (initial) Promise.prototype and the @@species protector is
// intact, as that guards the lookup path for "constructor" on
// This adds a fast path as non-subclassed native promises don't have // JSPromise instances which have the (initial) Promise.prototype.
// an observable constructor lookup. Node* const promise_prototype =
BranchIfFastPath(native_context, promise_fun, value, LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
&if_value_is_native_promise, GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype),
&if_value_or_constructor_are_not_native_promise); &if_slow_constructor);
GotoIf(IsSpeciesProtectorCellInvalid(), &if_slow_constructor);
BIND(&if_value_is_native_promise);
{ // If the {constructor} is the Promise function, we just immediately
GotoIfNot(WordEqual(promise_fun, constructor), // return the {value} here and don't bother wrapping it into a
&if_value_or_constructor_are_not_native_promise); // native Promise.
Return(value); GotoIfNot(WordEqual(promise_fun, constructor), &if_slow_constructor);
} Return(value);
// At this point, value or/and constructor are not native promises, but // At this point, value or/and constructor are not native promises, but
// they could be of the same subclass. // 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 value_constructor =
Node* const xConstructor = GetProperty(context, value, isolate()->factory()->constructor_string());
GetProperty(context, value, isolate->factory()->constructor_string()); GotoIfNot(WordEqual(value_constructor, constructor), &if_need_to_allocate);
BranchIfSameValue(xConstructor, constructor, &if_return,
&if_need_to_allocate);
BIND(&if_return);
Return(value); Return(value);
} }
BIND(&if_need_to_allocate); 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, Branch(WordEqual(promise_fun, constructor), &if_nativepromise,
&if_notnativepromise); &if_notnativepromise);
...@@ -1350,8 +1348,9 @@ TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) { ...@@ -1350,8 +1348,9 @@ TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) {
Node* const resolve = Node* const resolve =
LoadObjectField(capability, PromiseCapability::kResolveOffset); LoadObjectField(capability, PromiseCapability::kResolveOffset);
CallJS(CodeFactory::Call(isolate, ConvertReceiverMode::kNullOrUndefined), CallJS(
context, resolve, UndefinedConstant(), value); CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
context, resolve, UndefinedConstant(), value);
Node* const result = Node* const result =
LoadObjectField(capability, PromiseCapability::kPromiseOffset); LoadObjectField(capability, PromiseCapability::kPromiseOffset);
......
...@@ -4314,6 +4314,11 @@ Node* CodeStubAssembler::IsJSObject(Node* object) { ...@@ -4314,6 +4314,11 @@ Node* CodeStubAssembler::IsJSObject(Node* object) {
return IsJSObjectMap(LoadMap(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) { Node* CodeStubAssembler::IsJSProxy(Node* object) {
return HasInstanceType(object, JS_PROXY_TYPE); return HasInstanceType(object, JS_PROXY_TYPE);
} }
......
...@@ -1117,6 +1117,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -1117,6 +1117,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsJSObjectInstanceType(Node* instance_type); Node* IsJSObjectInstanceType(Node* instance_type);
Node* IsJSObjectMap(Node* map); Node* IsJSObjectMap(Node* map);
Node* IsJSObject(Node* object); Node* IsJSObject(Node* object);
Node* IsJSPromiseMap(Node* map);
Node* IsJSProxy(Node* object); Node* IsJSProxy(Node* object);
Node* IsJSReceiverInstanceType(Node* instance_type); Node* IsJSReceiverInstanceType(Node* instance_type);
Node* IsJSReceiverMap(Node* map); 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