Commit b5acda73 authored by Maya Lekova's avatar Maya Lekova Committed by Commit Bot

Reland "Add fast path to ObjectGetOwnPropertyDescriptor"

Bug: v8:6557
Change-Id: I01f065b74e3c568e577a3ee2caca68f24293c1cb
Reviewed-on: https://chromium-review.googlesource.com/686763Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48495}
parent b9340619
......@@ -27,6 +27,13 @@ class ObjectBuiltinsAssembler : public CodeStubAssembler {
void AddToDictionaryIf(Node* condition, Node* name_dictionary,
Handle<Name> name, Node* value, Label* bailout);
Node* FromPropertyDescriptor(Node* context, Node* desc);
Node* FromPropertyDetails(Node* context, Node* raw_value, Node* details,
Label* if_bailout);
Node* ConstructAccessorDescriptor(Node* context, Node* getter, Node* setter,
Node* enumerable, Node* configurable);
Node* ConstructDataDescriptor(Node* context, Node* value, Node* writable,
Node* enumerable, Node* configurable);
Node* GetAccessorOrUndefined(Node* accessor, Label* if_bailout);
};
void ObjectBuiltinsAssembler::ReturnToStringFormat(Node* context,
......@@ -41,6 +48,55 @@ void ObjectBuiltinsAssembler::ReturnToStringFormat(Node* context,
rhs));
}
Node* ObjectBuiltinsAssembler::ConstructAccessorDescriptor(Node* context,
Node* getter,
Node* setter,
Node* enumerable,
Node* configurable) {
Node* native_context = LoadNativeContext(context);
Node* map = LoadContextElement(
native_context, Context::ACCESSOR_PROPERTY_DESCRIPTOR_MAP_INDEX);
Node* js_desc = AllocateJSObjectFromMap(map);
StoreObjectFieldNoWriteBarrier(
js_desc, JSAccessorPropertyDescriptor::kGetOffset, getter);
StoreObjectFieldNoWriteBarrier(
js_desc, JSAccessorPropertyDescriptor::kSetOffset, setter);
StoreObjectFieldNoWriteBarrier(
js_desc, JSAccessorPropertyDescriptor::kEnumerableOffset,
SelectBooleanConstant(enumerable));
StoreObjectFieldNoWriteBarrier(
js_desc, JSAccessorPropertyDescriptor::kConfigurableOffset,
SelectBooleanConstant(configurable));
return js_desc;
}
Node* ObjectBuiltinsAssembler::ConstructDataDescriptor(Node* context,
Node* value,
Node* writable,
Node* enumerable,
Node* configurable) {
Node* native_context = LoadNativeContext(context);
Node* map = LoadContextElement(native_context,
Context::DATA_PROPERTY_DESCRIPTOR_MAP_INDEX);
Node* js_desc = AllocateJSObjectFromMap(map);
StoreObjectFieldNoWriteBarrier(js_desc,
JSDataPropertyDescriptor::kValueOffset, value);
StoreObjectFieldNoWriteBarrier(js_desc,
JSDataPropertyDescriptor::kWritableOffset,
SelectBooleanConstant(writable));
StoreObjectFieldNoWriteBarrier(js_desc,
JSDataPropertyDescriptor::kEnumerableOffset,
SelectBooleanConstant(enumerable));
StoreObjectFieldNoWriteBarrier(js_desc,
JSDataPropertyDescriptor::kConfigurableOffset,
SelectBooleanConstant(configurable));
return js_desc;
}
TF_BUILTIN(ObjectPrototypeHasOwnProperty, ObjectBuiltinsAssembler) {
Node* object = Parameter(Descriptor::kReceiver);
Node* key = Parameter(Descriptor::kKey);
......@@ -716,27 +772,90 @@ TF_BUILTIN(ObjectGetOwnPropertyDescriptor, ObjectBuiltinsAssembler) {
UndefinedConstant()));
CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
Node* obj = args.GetOptionalArgumentValue(0);
Node* object = args.GetOptionalArgumentValue(0);
Node* key = args.GetOptionalArgumentValue(1);
// 1. Let obj be ? ToObject(O).
Node* object = CallBuiltin(Builtins::kToObject, context, obj);
object = CallBuiltin(Builtins::kToObject, context, object);
// 2. Let key be ? ToPropertyKey(P).
Node* name = ToName(context, key);
key = ToName(context, key);
// 3. Let desc be ? obj.[[GetOwnProperty]](key).
Node* desc =
CallRuntime(Runtime::kGetOwnPropertyDescriptor, context, object, name);
Label if_keyisindex(this), if_iskeyunique(this),
call_runtime(this, Label::kDeferred),
return_undefined(this, Label::kDeferred), if_notunique_name(this);
Node* map = LoadMap(object);
Node* instance_type = LoadMapInstanceType(map);
GotoIf(Int32LessThanOrEqual(instance_type,
Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)),
&call_runtime);
{
VARIABLE(var_index, MachineType::PointerRepresentation(),
IntPtrConstant(0));
VARIABLE(var_name, MachineRepresentation::kTagged);
Label return_undefined(this, Label::kDeferred);
GotoIf(IsUndefined(desc), &return_undefined);
TryToName(key, &if_keyisindex, &var_index, &if_iskeyunique, &var_name,
&call_runtime, &if_notunique_name);
CSA_ASSERT(this, IsFixedArray(desc));
BIND(&if_notunique_name);
{
Label not_in_string_table(this);
TryInternalizeString(key, &if_keyisindex, &var_index, &if_iskeyunique,
&var_name, &not_in_string_table, &call_runtime);
// 4. Return FromPropertyDescriptor(desc).
args.PopAndReturn(FromPropertyDescriptor(context, desc));
BIND(&not_in_string_table);
{
// If the string was not found in the string table, then no regular
// object can have a property with that name, so return |undefined|.
Goto(&return_undefined);
}
}
BIND(&if_iskeyunique);
{
Label if_found_value(this), return_empty(this), if_not_found(this);
VARIABLE(var_value, MachineRepresentation::kTagged);
VARIABLE(var_details, MachineRepresentation::kWord32);
VARIABLE(var_raw_value, MachineRepresentation::kTagged);
TryGetOwnProperty(context, object, object, map, instance_type,
var_name.value(), &if_found_value, &var_value,
&var_details, &var_raw_value, &return_empty,
&if_not_found, kReturnAccessorPair);
BIND(&if_found_value);
// 4. Return FromPropertyDescriptor(desc).
Node* js_desc = FromPropertyDetails(context, var_value.value(),
var_details.value(), &call_runtime);
args.PopAndReturn(js_desc);
BIND(&return_empty);
var_value.Bind(UndefinedConstant());
args.PopAndReturn(UndefinedConstant());
BIND(&if_not_found);
Goto(&call_runtime);
}
}
BIND(&if_keyisindex);
Goto(&call_runtime);
BIND(&call_runtime);
{
Node* desc =
CallRuntime(Runtime::kGetOwnPropertyDescriptor, context, object, key);
GotoIf(IsUndefined(desc), &return_undefined);
CSA_ASSERT(this, IsFixedArray(desc));
// 4. Return FromPropertyDescriptor(desc).
Node* js_desc = FromPropertyDescriptor(context, desc);
args.PopAndReturn(js_desc);
}
BIND(&return_undefined);
args.PopAndReturn(UndefinedConstant());
}
......@@ -779,54 +898,21 @@ Node* ObjectBuiltinsAssembler::FromPropertyDescriptor(Node* context,
BIND(&if_accessor_desc);
{
Node* native_context = LoadNativeContext(context);
Node* map = LoadContextElement(
native_context, Context::ACCESSOR_PROPERTY_DESCRIPTOR_MAP_INDEX);
Node* js_desc = AllocateJSObjectFromMap(map);
StoreObjectFieldNoWriteBarrier(
js_desc, JSAccessorPropertyDescriptor::kGetOffset,
LoadObjectField(desc, PropertyDescriptorObject::kGetOffset));
StoreObjectFieldNoWriteBarrier(
js_desc, JSAccessorPropertyDescriptor::kSetOffset,
LoadObjectField(desc, PropertyDescriptorObject::kSetOffset));
StoreObjectFieldNoWriteBarrier(
js_desc, JSAccessorPropertyDescriptor::kEnumerableOffset,
SelectBooleanConstant(
IsSetWord32<PropertyDescriptorObject::IsEnumerableBit>(flags)));
StoreObjectFieldNoWriteBarrier(
js_desc, JSAccessorPropertyDescriptor::kConfigurableOffset,
SelectBooleanConstant(
IsSetWord32<PropertyDescriptorObject::IsConfigurableBit>(flags)));
js_descriptor.Bind(js_desc);
js_descriptor.Bind(ConstructAccessorDescriptor(
context, LoadObjectField(desc, PropertyDescriptorObject::kGetOffset),
LoadObjectField(desc, PropertyDescriptorObject::kSetOffset),
IsSetWord32<PropertyDescriptorObject::IsEnumerableBit>(flags),
IsSetWord32<PropertyDescriptorObject::IsConfigurableBit>(flags)));
Goto(&return_desc);
}
BIND(&if_data_desc);
{
Node* native_context = LoadNativeContext(context);
Node* map = LoadContextElement(native_context,
Context::DATA_PROPERTY_DESCRIPTOR_MAP_INDEX);
Node* js_desc = AllocateJSObjectFromMap(map);
StoreObjectFieldNoWriteBarrier(
js_desc, JSDataPropertyDescriptor::kValueOffset,
LoadObjectField(desc, PropertyDescriptorObject::kValueOffset));
StoreObjectFieldNoWriteBarrier(
js_desc, JSDataPropertyDescriptor::kWritableOffset,
SelectBooleanConstant(
IsSetWord32<PropertyDescriptorObject::IsWritableBit>(flags)));
StoreObjectFieldNoWriteBarrier(
js_desc, JSDataPropertyDescriptor::kEnumerableOffset,
SelectBooleanConstant(
IsSetWord32<PropertyDescriptorObject::IsEnumerableBit>(flags)));
StoreObjectFieldNoWriteBarrier(
js_desc, JSDataPropertyDescriptor::kConfigurableOffset,
SelectBooleanConstant(
IsSetWord32<PropertyDescriptorObject::IsConfigurableBit>(flags)));
js_descriptor.Bind(js_desc);
js_descriptor.Bind(ConstructDataDescriptor(
context, LoadObjectField(desc, PropertyDescriptorObject::kValueOffset),
IsSetWord32<PropertyDescriptorObject::IsWritableBit>(flags),
IsSetWord32<PropertyDescriptorObject::IsEnumerableBit>(flags),
IsSetWord32<PropertyDescriptorObject::IsConfigurableBit>(flags)));
Goto(&return_desc);
}
......@@ -884,5 +970,60 @@ Node* ObjectBuiltinsAssembler::FromPropertyDescriptor(Node* context,
BIND(&return_desc);
return js_descriptor.value();
}
Node* ObjectBuiltinsAssembler::FromPropertyDetails(Node* context,
Node* raw_value,
Node* details,
Label* if_bailout) {
VARIABLE(js_descriptor, MachineRepresentation::kTagged);
Label if_accessor_desc(this), if_data_desc(this), return_desc(this);
BranchIfAccessorPair(raw_value, &if_accessor_desc, &if_data_desc);
BIND(&if_accessor_desc);
{
Node* getter = LoadObjectField(raw_value, AccessorPair::kGetterOffset);
Node* setter = LoadObjectField(raw_value, AccessorPair::kSetterOffset);
js_descriptor.Bind(ConstructAccessorDescriptor(
context, GetAccessorOrUndefined(getter, if_bailout),
GetAccessorOrUndefined(setter, if_bailout),
IsNotSetWord32(details, PropertyDetails::kAttributesDontEnumMask),
IsNotSetWord32(details, PropertyDetails::kAttributesDontDeleteMask)));
Goto(&return_desc);
}
BIND(&if_data_desc);
{
js_descriptor.Bind(ConstructDataDescriptor(
context, raw_value,
IsNotSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
IsNotSetWord32(details, PropertyDetails::kAttributesDontEnumMask),
IsNotSetWord32(details, PropertyDetails::kAttributesDontDeleteMask)));
Goto(&return_desc);
}
BIND(&return_desc);
return js_descriptor.value();
}
Node* ObjectBuiltinsAssembler::GetAccessorOrUndefined(Node* accessor,
Label* if_bailout) {
Label bind_undefined(this, Label::kDeferred), return_result(this);
VARIABLE(result, MachineRepresentation::kTagged);
GotoIf(IsNull(accessor), &bind_undefined);
result.Bind(accessor);
Node* map = LoadMap(accessor);
// TODO(ishell): probe template instantiations cache.
GotoIf(IsFunctionTemplateInfoMap(map), if_bailout);
Goto(&return_result);
BIND(&bind_undefined);
result.Bind(UndefinedConstant());
Goto(&return_result);
BIND(&return_result);
return result.value();
}
} // namespace internal
} // namespace v8
......@@ -16,12 +16,6 @@ class ProxiesCodeStubAssembler : public CodeStubAssembler {
explicit ProxiesCodeStubAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
void BranchIfAccessorPair(Node* value, Label* if_accessor_pair,
Label* if_not_accessor_pair) {
GotoIf(TaggedIsSmi(value), if_not_accessor_pair);
Branch(IsAccessorPair(value), if_accessor_pair, if_not_accessor_pair);
}
// ES6 section 9.5.8 [[Get]] ( P, Receiver )
// name should not be an index.
Node* ProxyGetProperty(Node* context, Node* proxy, Node* name,
......
......@@ -6204,7 +6204,8 @@ void CodeStubAssembler::LoadPropertyFromGlobalDictionary(Node* dictionary,
// Returns either the original value, or the result of the getter call.
Node* CodeStubAssembler::CallGetterIfAccessor(Node* value, Node* details,
Node* context, Node* receiver,
Label* if_bailout) {
Label* if_bailout,
GetOwnPropertyMode mode) {
VARIABLE(var_value, MachineRepresentation::kTagged, value);
Label done(this), if_accessor_info(this, Label::kDeferred);
......@@ -6216,23 +6217,26 @@ Node* CodeStubAssembler::CallGetterIfAccessor(Node* value, Node* details,
// AccessorPair case.
{
Node* accessor_pair = value;
Node* getter = LoadObjectField(accessor_pair, AccessorPair::kGetterOffset);
Node* getter_map = LoadMap(getter);
Node* instance_type = LoadMapInstanceType(getter_map);
// FunctionTemplateInfo getters are not supported yet.
GotoIf(
Word32Equal(instance_type, Int32Constant(FUNCTION_TEMPLATE_INFO_TYPE)),
if_bailout);
// Return undefined if the {getter} is not callable.
var_value.Bind(UndefinedConstant());
GotoIfNot(IsCallableMap(getter_map), &done);
// Call the accessor.
Callable callable = CodeFactory::Call(isolate());
Node* result = CallJS(callable, context, getter, receiver);
var_value.Bind(result);
if (mode == kCallJSGetter) {
Node* accessor_pair = value;
Node* getter =
LoadObjectField(accessor_pair, AccessorPair::kGetterOffset);
Node* getter_map = LoadMap(getter);
Node* instance_type = LoadMapInstanceType(getter_map);
// FunctionTemplateInfo getters are not supported yet.
GotoIf(Word32Equal(instance_type,
Int32Constant(FUNCTION_TEMPLATE_INFO_TYPE)),
if_bailout);
// Return undefined if the {getter} is not callable.
var_value.Bind(UndefinedConstant());
GotoIfNot(IsCallableMap(getter_map), &done);
// Call the accessor.
Callable callable = CodeFactory::Call(isolate());
Node* result = CallJS(callable, context, getter, receiver);
var_value.Bind(result);
}
Goto(&done);
}
......@@ -6309,7 +6313,7 @@ void CodeStubAssembler::TryGetOwnProperty(
Node* context, Node* receiver, Node* object, Node* map, Node* instance_type,
Node* unique_name, Label* if_found_value, Variable* var_value,
Variable* var_details, Variable* var_raw_value, Label* if_not_found,
Label* if_bailout) {
Label* if_bailout, GetOwnPropertyMode mode) {
DCHECK_EQ(MachineRepresentation::kTagged, var_value->rep());
Comment("TryGetOwnProperty");
......@@ -6356,11 +6360,12 @@ void CodeStubAssembler::TryGetOwnProperty(
// Here we have details and value which could be an accessor.
BIND(&if_found);
{
// TODO(ishell): Execute C++ accessor in case of accessor info
if (var_raw_value) {
var_raw_value->Bind(var_value->value());
}
Node* value = CallGetterIfAccessor(var_value->value(), var_details->value(),
context, receiver, if_bailout);
context, receiver, if_bailout, mode);
var_value->Bind(value);
Goto(if_found_value);
}
......
......@@ -1113,6 +1113,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Int32Constant(0));
}
// Returns true if none of the mask's bits in given |word32| are set.
TNode<BoolT> IsNotSetWord32(SloppyTNode<Word32T> word32, uint32_t mask) {
return Word32Equal(Word32And(word32, Int32Constant(mask)),
Int32Constant(0));
}
// Returns true if any of the |T|'s bits in given |word| are set.
template <typename T>
Node* IsSetWord(Node* word) {
......@@ -1325,6 +1331,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* unique_name, Label* if_found,
Label* if_not_found, Label* if_bailout);
// Operating mode for TryGetOwnProperty and CallGetterIfAccessor
// kReturnAccessorPair is used when we're only getting the property descriptor
enum GetOwnPropertyMode { kCallJSGetter, kReturnAccessorPair };
// Tries to get {object}'s own {unique_name} property value. If the property
// is an accessor then it also calls a getter. If the property is a double
// field it re-wraps value in an immutable heap number.
......@@ -1336,7 +1345,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* instance_type, Node* unique_name,
Label* if_found, Variable* var_value,
Variable* var_details, Variable* var_raw_value,
Label* if_not_found, Label* if_bailout);
Label* if_not_found, Label* if_bailout,
GetOwnPropertyMode mode = kCallJSGetter);
Node* GetProperty(Node* context, Node* receiver, Handle<Name> name) {
return GetProperty(context, receiver, HeapConstant(name));
......@@ -1579,6 +1589,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* lhs, Node* rhs, Label* if_true,
Label* if_false);
void BranchIfAccessorPair(Node* value, Label* if_accessor_pair,
Label* if_not_accessor_pair) {
GotoIf(TaggedIsSmi(value), if_not_accessor_pair);
Branch(IsAccessorPair(value), if_accessor_pair, if_not_accessor_pair);
}
void GotoIfNumberGreaterThanOrEqual(Node* lhs, Node* rhs, Label* if_false);
Node* Equal(Node* lhs, Node* rhs, Node* context,
......@@ -1673,7 +1689,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* DescriptorArrayGetKey(Node* descriptors, Node* descriptor_number);
Node* CallGetterIfAccessor(Node* value, Node* details, Node* context,
Node* receiver, Label* if_bailout);
Node* receiver, Label* if_bailout,
GetOwnPropertyMode mode = kCallJSGetter);
Node* TryToIntptr(Node* key, Label* miss);
......
......@@ -342,6 +342,8 @@ class PropertyDetails BASE_EMBEDDED {
(READ_ONLY << AttributesField::kShift);
static const int kAttributesDontDeleteMask =
(DONT_DELETE << AttributesField::kShift);
static const int kAttributesDontEnumMask =
(DONT_ENUM << AttributesField::kShift);
// Bit fields for normalized objects.
class PropertyCellTypeField
......
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