[stubs] Implementing CodeStubAssembler::GetOwnProperty().
This is a building block for GetPropertyStub. It supports querying fast, slow and global objects without native accessors and interceptors. BUG=v8:4911 LOG=Y Review-Url: https://codereview.chromium.org/2079823002 Cr-Commit-Position: refs/heads/master@{#37291}
This commit is contained in:
parent
705574970f
commit
23332fe829
@ -368,7 +368,7 @@ void Builtins::Generate_ObjectHasOwnProperty(CodeStubAssembler* assembler) {
|
||||
&call_runtime);
|
||||
|
||||
assembler->Bind(&if_iskeyunique);
|
||||
assembler->TryLookupProperty(object, map, instance_type, key, &return_true,
|
||||
assembler->TryHasOwnProperty(object, map, instance_type, key, &return_true,
|
||||
&return_false, &call_runtime);
|
||||
|
||||
assembler->Bind(&keyisindex);
|
||||
|
@ -28,12 +28,12 @@ CodeStubAssembler::CodeStubAssembler(Isolate* isolate, Zone* zone,
|
||||
void CodeStubAssembler::Assert(Node* condition) {
|
||||
#if defined(DEBUG)
|
||||
Label ok(this);
|
||||
Label not_ok(this);
|
||||
Branch(condition, &ok, ¬_ok);
|
||||
Bind(¬_ok);
|
||||
Comment("[ Assert");
|
||||
GotoIf(condition, &ok);
|
||||
DebugBreak();
|
||||
Goto(&ok);
|
||||
Bind(&ok);
|
||||
Comment("] Assert");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -494,6 +494,11 @@ Node* CodeStubAssembler::LoadObjectField(Node* object, int offset,
|
||||
return Load(rep, object, IntPtrConstant(offset - kHeapObjectTag));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadObjectField(Node* object, Node* offset,
|
||||
MachineType rep) {
|
||||
return Load(rep, object, IntPtrSub(offset, IntPtrConstant(kHeapObjectTag)));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadHeapNumberValue(Node* object) {
|
||||
return LoadObjectField(object, HeapNumber::kValueOffset,
|
||||
MachineType::Float64());
|
||||
@ -552,6 +557,16 @@ Node* CodeStubAssembler::LoadMapInstanceSize(Node* map) {
|
||||
return LoadObjectField(map, Map::kInstanceSizeOffset, MachineType::Uint8());
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadMapInobjectProperties(Node* map) {
|
||||
// See Map::GetInObjectProperties() for details.
|
||||
STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE);
|
||||
Assert(Int32GreaterThanOrEqual(LoadMapInstanceType(map),
|
||||
Int32Constant(FIRST_JS_OBJECT_TYPE)));
|
||||
return LoadObjectField(
|
||||
map, Map::kInObjectPropertiesOrConstructorFunctionIndexOffset,
|
||||
MachineType::Uint8());
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadNameHashField(Node* name) {
|
||||
return LoadObjectField(name, Name::kHashFieldOffset, MachineType::Uint32());
|
||||
}
|
||||
@ -1491,6 +1506,7 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
|
||||
Variable* var_index, Label* if_keyisunique,
|
||||
Label* if_bailout) {
|
||||
DCHECK_EQ(MachineRepresentation::kWord32, var_index->rep());
|
||||
Comment("TryToName");
|
||||
|
||||
Label if_keyissmi(this), if_keyisnotsmi(this);
|
||||
Branch(WordIsSmi(key), &if_keyissmi, &if_keyisnotsmi);
|
||||
@ -1532,16 +1548,21 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
|
||||
Goto(if_keyisindex);
|
||||
}
|
||||
|
||||
template <typename Dictionary>
|
||||
Node* CodeStubAssembler::EntryToIndex(Node* entry, int field_index) {
|
||||
Node* entry_index = Int32Mul(entry, Int32Constant(Dictionary::kEntrySize));
|
||||
return Int32Add(entry_index,
|
||||
Int32Constant(Dictionary::kElementsStartIndex + field_index));
|
||||
}
|
||||
|
||||
template <typename Dictionary>
|
||||
void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
|
||||
Node* unique_name, Label* if_found,
|
||||
Variable* var_entry,
|
||||
Variable* var_name_index,
|
||||
Label* if_not_found,
|
||||
int inlined_probes) {
|
||||
DCHECK_EQ(MachineRepresentation::kWord32, var_entry->rep());
|
||||
|
||||
const int kElementsStartOffset =
|
||||
Dictionary::kElementsStartIndex * kPointerSize;
|
||||
DCHECK_EQ(MachineRepresentation::kWord32, var_name_index->rep());
|
||||
Comment("NameDictionaryLookup");
|
||||
|
||||
Node* capacity = SmiToWord32(LoadFixedArrayElement(
|
||||
dictionary, Int32Constant(Dictionary::kCapacityIndex)));
|
||||
@ -1553,11 +1574,10 @@ void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
|
||||
Node* entry = Word32And(hash, mask);
|
||||
|
||||
for (int i = 0; i < inlined_probes; i++) {
|
||||
// See Dictionary::EntryToIndex()
|
||||
Node* index = Int32Mul(entry, Int32Constant(Dictionary::kEntrySize));
|
||||
Node* current =
|
||||
LoadFixedArrayElement(dictionary, index, kElementsStartOffset);
|
||||
var_entry->Bind(entry);
|
||||
Node* index = EntryToIndex<Dictionary>(entry);
|
||||
var_name_index->Bind(index);
|
||||
|
||||
Node* current = LoadFixedArrayElement(dictionary, index);
|
||||
GotoIf(WordEqual(current, unique_name), if_found);
|
||||
|
||||
// See Dictionary::NextProbe().
|
||||
@ -1568,20 +1588,21 @@ void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
|
||||
Node* undefined = UndefinedConstant();
|
||||
|
||||
Variable var_count(this, MachineRepresentation::kWord32);
|
||||
Variable* loop_vars[] = {&var_count, var_entry};
|
||||
Label loop(this, 2, loop_vars);
|
||||
Variable var_entry(this, MachineRepresentation::kWord32);
|
||||
Variable* loop_vars[] = {&var_count, &var_entry, var_name_index};
|
||||
Label loop(this, 3, loop_vars);
|
||||
var_count.Bind(count);
|
||||
var_entry->Bind(entry);
|
||||
var_entry.Bind(entry);
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
Node* count = var_count.value();
|
||||
Node* entry = var_entry->value();
|
||||
Node* entry = var_entry.value();
|
||||
|
||||
// See Dictionary::EntryToIndex()
|
||||
Node* index = Int32Mul(entry, Int32Constant(Dictionary::kEntrySize));
|
||||
Node* current =
|
||||
LoadFixedArrayElement(dictionary, index, kElementsStartOffset);
|
||||
Node* index = EntryToIndex<Dictionary>(entry);
|
||||
var_name_index->Bind(index);
|
||||
|
||||
Node* current = LoadFixedArrayElement(dictionary, index);
|
||||
GotoIf(WordEqual(current, undefined), if_not_found);
|
||||
GotoIf(WordEqual(current, unique_name), if_found);
|
||||
|
||||
@ -1590,7 +1611,7 @@ void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
|
||||
entry = Word32And(Int32Add(entry, count), mask);
|
||||
|
||||
var_count.Bind(count);
|
||||
var_entry->Bind(entry);
|
||||
var_entry.Bind(entry);
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
@ -1621,9 +1642,7 @@ void CodeStubAssembler::NumberDictionaryLookup(Node* dictionary, Node* key,
|
||||
Variable* var_entry,
|
||||
Label* if_not_found) {
|
||||
DCHECK_EQ(MachineRepresentation::kWord32, var_entry->rep());
|
||||
|
||||
const int kElementsStartOffset =
|
||||
Dictionary::kElementsStartIndex * kPointerSize;
|
||||
Comment("NumberDictionaryLookup");
|
||||
|
||||
Node* capacity = SmiToWord32(LoadFixedArrayElement(
|
||||
dictionary, Int32Constant(Dictionary::kCapacityIndex)));
|
||||
@ -1656,10 +1675,8 @@ void CodeStubAssembler::NumberDictionaryLookup(Node* dictionary, Node* key,
|
||||
Node* count = var_count.value();
|
||||
Node* entry = var_entry->value();
|
||||
|
||||
// See Dictionary::EntryToIndex()
|
||||
Node* index = Int32Mul(entry, Int32Constant(Dictionary::kEntrySize));
|
||||
Node* current =
|
||||
LoadFixedArrayElement(dictionary, index, kElementsStartOffset);
|
||||
Node* index = EntryToIndex<Dictionary>(entry);
|
||||
Node* current = LoadFixedArrayElement(dictionary, index);
|
||||
GotoIf(WordEqual(current, undefined), if_not_found);
|
||||
Label next_probe(this);
|
||||
{
|
||||
@ -1691,23 +1708,32 @@ void CodeStubAssembler::NumberDictionaryLookup(Node* dictionary, Node* key,
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::TryLookupProperty(Node* object, Node* map,
|
||||
Node* instance_type,
|
||||
Node* unique_name, Label* if_found,
|
||||
Label* if_not_found,
|
||||
Label* if_bailout) {
|
||||
void CodeStubAssembler::TryLookupProperty(
|
||||
Node* object, Node* map, Node* instance_type, Node* unique_name,
|
||||
Label* if_found_fast, Label* if_found_dict, Label* if_found_global,
|
||||
Variable* var_meta_storage, Variable* var_name_index, Label* if_not_found,
|
||||
Label* if_bailout) {
|
||||
DCHECK_EQ(MachineRepresentation::kTagged, var_meta_storage->rep());
|
||||
DCHECK_EQ(MachineRepresentation::kWord32, var_name_index->rep());
|
||||
|
||||
Label if_objectisspecial(this);
|
||||
STATIC_ASSERT(JS_GLOBAL_OBJECT_TYPE <= LAST_SPECIAL_RECEIVER_TYPE);
|
||||
GotoIf(Int32LessThanOrEqual(instance_type,
|
||||
Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)),
|
||||
&if_objectisspecial);
|
||||
|
||||
Node* bit_field = LoadMapBitField(map);
|
||||
Node* mask = Int32Constant(1 << Map::kHasNamedInterceptor |
|
||||
1 << Map::kIsAccessCheckNeeded);
|
||||
Assert(Word32Equal(Word32And(bit_field, mask), Int32Constant(0)));
|
||||
|
||||
Node* bit_field3 = LoadMapBitField3(map);
|
||||
Node* bit = BitFieldDecode<Map::DictionaryMap>(bit_field3);
|
||||
Label if_isfastmap(this), if_isslowmap(this);
|
||||
Branch(Word32Equal(bit, Int32Constant(0)), &if_isfastmap, &if_isslowmap);
|
||||
Bind(&if_isfastmap);
|
||||
{
|
||||
Comment("DescriptorArrayLookup");
|
||||
Node* nof = BitFieldDecode<Map::NumberOfOwnDescriptorsBits>(bit_field3);
|
||||
// Bail out to the runtime for large numbers of own descriptors. The stub
|
||||
// only does linear search, which becomes too expensive in that case.
|
||||
@ -1716,6 +1742,7 @@ void CodeStubAssembler::TryLookupProperty(Node* object, Node* map,
|
||||
GotoIf(Int32GreaterThan(nof, Int32Constant(kMaxLinear)), if_bailout);
|
||||
}
|
||||
Node* descriptors = LoadMapDescriptors(map);
|
||||
var_meta_storage->Bind(descriptors);
|
||||
|
||||
Variable var_descriptor(this, MachineRepresentation::kWord32);
|
||||
Label loop(this, &var_descriptor);
|
||||
@ -1724,13 +1751,15 @@ void CodeStubAssembler::TryLookupProperty(Node* object, Node* map,
|
||||
Bind(&loop);
|
||||
{
|
||||
Node* index = var_descriptor.value();
|
||||
Node* offset = Int32Constant(DescriptorArray::ToKeyIndex(0));
|
||||
Node* name_offset = Int32Constant(DescriptorArray::ToKeyIndex(0));
|
||||
Node* factor = Int32Constant(DescriptorArray::kDescriptorSize);
|
||||
GotoIf(Word32Equal(index, nof), if_not_found);
|
||||
|
||||
Node* array_index = Int32Add(offset, Int32Mul(index, factor));
|
||||
Node* current = LoadFixedArrayElement(descriptors, array_index);
|
||||
GotoIf(WordEqual(current, unique_name), if_found);
|
||||
Node* name_index = Int32Add(name_offset, Int32Mul(index, factor));
|
||||
Node* name = LoadFixedArrayElement(descriptors, name_index);
|
||||
|
||||
var_name_index->Bind(name_index);
|
||||
GotoIf(WordEqual(name, unique_name), if_found_fast);
|
||||
|
||||
var_descriptor.Bind(Int32Add(index, Int32Constant(1)));
|
||||
Goto(&loop);
|
||||
@ -1738,22 +1767,301 @@ void CodeStubAssembler::TryLookupProperty(Node* object, Node* map,
|
||||
}
|
||||
Bind(&if_isslowmap);
|
||||
{
|
||||
Variable var_entry(this, MachineRepresentation::kWord32);
|
||||
Node* dictionary = LoadProperties(object);
|
||||
var_meta_storage->Bind(dictionary);
|
||||
|
||||
NameDictionaryLookup<NameDictionary>(dictionary, unique_name, if_found,
|
||||
&var_entry, if_not_found);
|
||||
NameDictionaryLookup<NameDictionary>(dictionary, unique_name, if_found_dict,
|
||||
var_name_index, if_not_found);
|
||||
}
|
||||
Bind(&if_objectisspecial);
|
||||
{
|
||||
// Handle global object here and other special objects in runtime.
|
||||
GotoUnless(Word32Equal(instance_type, Int32Constant(JS_GLOBAL_OBJECT_TYPE)),
|
||||
if_bailout);
|
||||
Variable var_entry(this, MachineRepresentation::kWord32);
|
||||
Node* dictionary = LoadProperties(object);
|
||||
|
||||
NameDictionaryLookup<GlobalDictionary>(dictionary, unique_name, if_found,
|
||||
&var_entry, if_not_found);
|
||||
// Handle interceptors and access checks in runtime.
|
||||
Node* bit_field = LoadMapBitField(map);
|
||||
Node* mask = Int32Constant(1 << Map::kHasNamedInterceptor |
|
||||
1 << Map::kIsAccessCheckNeeded);
|
||||
GotoIf(Word32NotEqual(Word32And(bit_field, mask), Int32Constant(0)),
|
||||
if_bailout);
|
||||
|
||||
Node* dictionary = LoadProperties(object);
|
||||
var_meta_storage->Bind(dictionary);
|
||||
|
||||
NameDictionaryLookup<GlobalDictionary>(
|
||||
dictionary, unique_name, if_found_global, var_name_index, if_not_found);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::TryHasOwnProperty(compiler::Node* object,
|
||||
compiler::Node* map,
|
||||
compiler::Node* instance_type,
|
||||
compiler::Node* unique_name,
|
||||
Label* if_found, Label* if_not_found,
|
||||
Label* if_bailout) {
|
||||
Comment("TryHasOwnProperty");
|
||||
Variable var_meta_storage(this, MachineRepresentation::kTagged);
|
||||
Variable var_name_index(this, MachineRepresentation::kWord32);
|
||||
|
||||
Label if_found_global(this);
|
||||
TryLookupProperty(object, map, instance_type, unique_name, if_found, if_found,
|
||||
&if_found_global, &var_meta_storage, &var_name_index,
|
||||
if_not_found, if_bailout);
|
||||
Bind(&if_found_global);
|
||||
{
|
||||
Variable var_value(this, MachineRepresentation::kTagged);
|
||||
Variable var_details(this, MachineRepresentation::kWord32);
|
||||
// Check if the property cell is not deleted.
|
||||
LoadPropertyFromGlobalDictionary(var_meta_storage.value(),
|
||||
var_name_index.value(), &var_value,
|
||||
&var_details, if_not_found);
|
||||
Goto(if_found);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::LoadPropertyFromFastObject(Node* object, Node* map,
|
||||
Node* descriptors,
|
||||
Node* name_index,
|
||||
Variable* var_details,
|
||||
Variable* var_value) {
|
||||
DCHECK_EQ(MachineRepresentation::kWord32, var_details->rep());
|
||||
DCHECK_EQ(MachineRepresentation::kTagged, var_value->rep());
|
||||
Comment("[ LoadPropertyFromFastObject");
|
||||
|
||||
const int name_to_details_offset =
|
||||
(DescriptorArray::kDescriptorDetails - DescriptorArray::kDescriptorKey) *
|
||||
kPointerSize;
|
||||
const int name_to_value_offset =
|
||||
(DescriptorArray::kDescriptorValue - DescriptorArray::kDescriptorKey) *
|
||||
kPointerSize;
|
||||
|
||||
Node* details = SmiToWord32(
|
||||
LoadFixedArrayElement(descriptors, name_index, name_to_details_offset));
|
||||
var_details->Bind(details);
|
||||
|
||||
Node* location = BitFieldDecode<PropertyDetails::LocationField>(details);
|
||||
|
||||
Label if_in_field(this), if_in_descriptor(this), done(this);
|
||||
Branch(Word32Equal(location, Int32Constant(kField)), &if_in_field,
|
||||
&if_in_descriptor);
|
||||
Bind(&if_in_field);
|
||||
{
|
||||
Node* field_index =
|
||||
BitFieldDecode<PropertyDetails::FieldIndexField>(details);
|
||||
Node* representation =
|
||||
BitFieldDecode<PropertyDetails::RepresentationField>(details);
|
||||
|
||||
Node* inobject_properties = LoadMapInobjectProperties(map);
|
||||
|
||||
Label if_inobject(this), if_backing_store(this);
|
||||
Variable var_double_value(this, MachineRepresentation::kFloat64);
|
||||
Label rebox_double(this, &var_double_value);
|
||||
BranchIfInt32LessThan(field_index, inobject_properties, &if_inobject,
|
||||
&if_backing_store);
|
||||
Bind(&if_inobject);
|
||||
{
|
||||
Comment("if_inobject");
|
||||
Node* field_offset = ChangeInt32ToIntPtr(
|
||||
Int32Mul(Int32Sub(LoadMapInstanceSize(map),
|
||||
Int32Sub(inobject_properties, field_index)),
|
||||
Int32Constant(kPointerSize)));
|
||||
|
||||
Label if_double(this), if_tagged(this);
|
||||
BranchIfWord32NotEqual(representation,
|
||||
Int32Constant(Representation::kDouble), &if_tagged,
|
||||
&if_double);
|
||||
Bind(&if_tagged);
|
||||
{
|
||||
var_value->Bind(LoadObjectField(object, field_offset));
|
||||
Goto(&done);
|
||||
}
|
||||
Bind(&if_double);
|
||||
{
|
||||
if (FLAG_unbox_double_fields) {
|
||||
var_double_value.Bind(
|
||||
LoadObjectField(object, field_offset, MachineType::Float64()));
|
||||
} else {
|
||||
Node* mutable_heap_number = LoadObjectField(object, field_offset);
|
||||
var_double_value.Bind(LoadHeapNumberValue(mutable_heap_number));
|
||||
}
|
||||
Goto(&rebox_double);
|
||||
}
|
||||
}
|
||||
Bind(&if_backing_store);
|
||||
{
|
||||
Comment("if_backing_store");
|
||||
Node* properties = LoadProperties(object);
|
||||
field_index = Int32Sub(field_index, inobject_properties);
|
||||
Node* value = LoadFixedArrayElement(properties, field_index);
|
||||
|
||||
Label if_double(this), if_tagged(this);
|
||||
BranchIfWord32NotEqual(representation,
|
||||
Int32Constant(Representation::kDouble), &if_tagged,
|
||||
&if_double);
|
||||
Bind(&if_tagged);
|
||||
{
|
||||
var_value->Bind(value);
|
||||
Goto(&done);
|
||||
}
|
||||
Bind(&if_double);
|
||||
{
|
||||
var_double_value.Bind(LoadHeapNumberValue(value));
|
||||
Goto(&rebox_double);
|
||||
}
|
||||
}
|
||||
Bind(&rebox_double);
|
||||
{
|
||||
Comment("rebox_double");
|
||||
Node* heap_number = AllocateHeapNumber();
|
||||
StoreHeapNumberValue(heap_number, var_double_value.value());
|
||||
var_value->Bind(heap_number);
|
||||
Goto(&done);
|
||||
}
|
||||
}
|
||||
Bind(&if_in_descriptor);
|
||||
{
|
||||
Node* value =
|
||||
LoadFixedArrayElement(descriptors, name_index, name_to_value_offset);
|
||||
var_value->Bind(value);
|
||||
Goto(&done);
|
||||
}
|
||||
Bind(&done);
|
||||
|
||||
Comment("] LoadPropertyFromFastObject");
|
||||
}
|
||||
|
||||
void CodeStubAssembler::LoadPropertyFromNameDictionary(Node* dictionary,
|
||||
Node* name_index,
|
||||
Variable* var_details,
|
||||
Variable* var_value) {
|
||||
Comment("LoadPropertyFromNameDictionary");
|
||||
|
||||
const int name_to_details_offset =
|
||||
(NameDictionary::kEntryDetailsIndex - NameDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
const int name_to_value_offset =
|
||||
(NameDictionary::kEntryValueIndex - NameDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
|
||||
Node* details = SmiToWord32(
|
||||
LoadFixedArrayElement(dictionary, name_index, name_to_details_offset));
|
||||
|
||||
var_details->Bind(details);
|
||||
var_value->Bind(
|
||||
LoadFixedArrayElement(dictionary, name_index, name_to_value_offset));
|
||||
|
||||
Comment("] LoadPropertyFromNameDictionary");
|
||||
}
|
||||
|
||||
void CodeStubAssembler::LoadPropertyFromGlobalDictionary(Node* dictionary,
|
||||
Node* name_index,
|
||||
Variable* var_details,
|
||||
Variable* var_value,
|
||||
Label* if_deleted) {
|
||||
Comment("[ LoadPropertyFromGlobalDictionary");
|
||||
|
||||
const int name_to_value_offset =
|
||||
(GlobalDictionary::kEntryValueIndex - GlobalDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
|
||||
Node* property_cell =
|
||||
LoadFixedArrayElement(dictionary, name_index, name_to_value_offset);
|
||||
|
||||
Node* value = LoadObjectField(property_cell, PropertyCell::kValueOffset);
|
||||
GotoIf(WordEqual(value, TheHoleConstant()), if_deleted);
|
||||
|
||||
var_value->Bind(value);
|
||||
|
||||
Node* details =
|
||||
SmiToWord32(LoadObjectField(property_cell, PropertyCell::kDetailsOffset));
|
||||
var_details->Bind(details);
|
||||
|
||||
Comment("] LoadPropertyFromGlobalDictionary");
|
||||
}
|
||||
|
||||
void CodeStubAssembler::TryGetOwnProperty(
|
||||
Node* context, Node* receiver, Node* object, Node* map, Node* instance_type,
|
||||
Node* unique_name, Label* if_found_value, Variable* var_value,
|
||||
Label* if_not_found, Label* if_bailout) {
|
||||
DCHECK_EQ(MachineRepresentation::kTagged, var_value->rep());
|
||||
Comment("TryGetOwnProperty");
|
||||
|
||||
Variable var_meta_storage(this, MachineRepresentation::kTagged);
|
||||
Variable var_entry(this, MachineRepresentation::kWord32);
|
||||
|
||||
Label if_found_fast(this), if_found_dict(this), if_found_global(this);
|
||||
|
||||
Variable var_details(this, MachineRepresentation::kWord32);
|
||||
Variable* vars[] = {var_value, &var_details};
|
||||
Label if_found(this, 2, vars);
|
||||
|
||||
TryLookupProperty(object, map, instance_type, unique_name, &if_found_fast,
|
||||
&if_found_dict, &if_found_global, &var_meta_storage,
|
||||
&var_entry, if_not_found, if_bailout);
|
||||
Bind(&if_found_fast);
|
||||
{
|
||||
Node* descriptors = var_meta_storage.value();
|
||||
Node* name_index = var_entry.value();
|
||||
|
||||
LoadPropertyFromFastObject(object, map, descriptors, name_index,
|
||||
&var_details, var_value);
|
||||
Goto(&if_found);
|
||||
}
|
||||
Bind(&if_found_dict);
|
||||
{
|
||||
Node* dictionary = var_meta_storage.value();
|
||||
Node* entry = var_entry.value();
|
||||
LoadPropertyFromNameDictionary(dictionary, entry, &var_details, var_value);
|
||||
Goto(&if_found);
|
||||
}
|
||||
Bind(&if_found_global);
|
||||
{
|
||||
Node* dictionary = var_meta_storage.value();
|
||||
Node* entry = var_entry.value();
|
||||
|
||||
LoadPropertyFromGlobalDictionary(dictionary, entry, &var_details, var_value,
|
||||
if_not_found);
|
||||
Goto(&if_found);
|
||||
}
|
||||
// Here we have details and value which could be an accessor.
|
||||
Bind(&if_found);
|
||||
{
|
||||
Node* details = var_details.value();
|
||||
Node* kind = BitFieldDecode<PropertyDetails::KindField>(details);
|
||||
|
||||
Label if_accessor(this);
|
||||
Branch(Word32Equal(kind, Int32Constant(kData)), if_found_value,
|
||||
&if_accessor);
|
||||
Bind(&if_accessor);
|
||||
{
|
||||
Node* accessor_pair = var_value->value();
|
||||
GotoIf(Word32Equal(LoadInstanceType(accessor_pair),
|
||||
Int32Constant(ACCESSOR_INFO_TYPE)),
|
||||
if_bailout);
|
||||
AssertInstanceType(accessor_pair, ACCESSOR_PAIR_TYPE);
|
||||
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());
|
||||
GotoIf(Word32Equal(Word32And(LoadMapBitField(getter_map),
|
||||
Int32Constant(1 << Map::kIsCallable)),
|
||||
Int32Constant(0)),
|
||||
if_found_value);
|
||||
|
||||
// Call the accessor.
|
||||
Callable callable = CodeFactory::Call(isolate());
|
||||
Node* result = CallJS(callable, context, getter, receiver);
|
||||
var_value->Bind(result);
|
||||
Goto(if_found_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,6 +117,10 @@ class CodeStubAssembler : public compiler::CodeAssembler {
|
||||
// Load a field from an object on the heap.
|
||||
compiler::Node* LoadObjectField(compiler::Node* object, int offset,
|
||||
MachineType rep = MachineType::AnyTagged());
|
||||
compiler::Node* LoadObjectField(compiler::Node* object,
|
||||
compiler::Node* offset,
|
||||
MachineType rep = MachineType::AnyTagged());
|
||||
|
||||
// Load the floating point value of a HeapNumber.
|
||||
compiler::Node* LoadHeapNumberValue(compiler::Node* object);
|
||||
// Load the Map of an HeapObject.
|
||||
@ -145,6 +149,8 @@ class CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* LoadMapPrototype(compiler::Node* map);
|
||||
// Load the instance size of a Map.
|
||||
compiler::Node* LoadMapInstanceSize(compiler::Node* map);
|
||||
// Load the inobject properties count of a Map (valid only for JSObjects).
|
||||
compiler::Node* LoadMapInobjectProperties(compiler::Node* map);
|
||||
|
||||
// Load the hash field of a name.
|
||||
compiler::Node* LoadNameHashField(compiler::Node* name);
|
||||
@ -268,11 +274,24 @@ class CodeStubAssembler : public compiler::CodeAssembler {
|
||||
void TryToName(compiler::Node* key, Label* if_keyisindex, Variable* var_index,
|
||||
Label* if_keyisunique, Label* if_bailout);
|
||||
|
||||
// Calculates array index for given dictionary entry and entry field.
|
||||
// See Dictionary::EntryToIndex().
|
||||
template <typename Dictionary>
|
||||
compiler::Node* EntryToIndex(compiler::Node* entry, int field_index);
|
||||
template <typename Dictionary>
|
||||
compiler::Node* EntryToIndex(compiler::Node* entry) {
|
||||
return EntryToIndex<Dictionary>(entry, Dictionary::kEntryKeyIndex);
|
||||
}
|
||||
|
||||
// Looks up an entry in a NameDictionaryBase successor. If the entry is found
|
||||
// control goes to {if_found} and {var_name_index} contains an index of the
|
||||
// key field of the entry found. If the key is not found control goes to
|
||||
// {if_not_found}.
|
||||
static const int kInlinedDictionaryProbes = 4;
|
||||
template <typename Dictionary>
|
||||
void NameDictionaryLookup(compiler::Node* dictionary,
|
||||
compiler::Node* unique_name, Label* if_found,
|
||||
Variable* var_entry, Label* if_not_found,
|
||||
Variable* var_name_index, Label* if_not_found,
|
||||
int inlined_probes = kInlinedDictionaryProbes);
|
||||
|
||||
compiler::Node* ComputeIntegerHash(compiler::Node* key, compiler::Node* seed);
|
||||
@ -282,11 +301,56 @@ class CodeStubAssembler : public compiler::CodeAssembler {
|
||||
Label* if_found, Variable* var_entry,
|
||||
Label* if_not_found);
|
||||
|
||||
void TryLookupProperty(compiler::Node* object, compiler::Node* map,
|
||||
// Tries to check if {object} has own {unique_name} property.
|
||||
void TryHasOwnProperty(compiler::Node* object, compiler::Node* map,
|
||||
compiler::Node* instance_type,
|
||||
compiler::Node* unique_name, Label* if_found,
|
||||
Label* if_not_found, Label* if_bailout);
|
||||
|
||||
// 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.
|
||||
void TryGetOwnProperty(compiler::Node* context, compiler::Node* receiver,
|
||||
compiler::Node* object, compiler::Node* map,
|
||||
compiler::Node* instance_type,
|
||||
compiler::Node* unique_name, Label* if_found,
|
||||
Variable* var_value, Label* if_not_found,
|
||||
Label* if_bailout);
|
||||
|
||||
void LoadPropertyFromFastObject(compiler::Node* object, compiler::Node* map,
|
||||
compiler::Node* descriptors,
|
||||
compiler::Node* name_index,
|
||||
Variable* var_details, Variable* var_value);
|
||||
|
||||
void LoadPropertyFromNameDictionary(compiler::Node* dictionary,
|
||||
compiler::Node* entry,
|
||||
Variable* var_details,
|
||||
Variable* var_value);
|
||||
|
||||
void LoadPropertyFromGlobalDictionary(compiler::Node* dictionary,
|
||||
compiler::Node* entry,
|
||||
Variable* var_details,
|
||||
Variable* var_value, Label* if_deleted);
|
||||
|
||||
// Generic property lookup generator. If the {object} is fast and
|
||||
// {unique_name} property is found then the control goes to {if_found_fast}
|
||||
// label and {var_meta_storage} and {var_name_index} will contain
|
||||
// DescriptorArray and an index of the descriptor's name respectively.
|
||||
// If the {object} is slow or global then the control goes to {if_found_dict}
|
||||
// or {if_found_global} and the {var_meta_storage} and {var_name_index} will
|
||||
// contain a dictionary and an index of the key field of the found entry.
|
||||
// If property is not found or given lookup is not supported then
|
||||
// the control goes to {if_not_found} or {if_bailout} respectively.
|
||||
//
|
||||
// Note: this code does not check if the global dictionary points to deleted
|
||||
// entry! This has to be done by the caller.
|
||||
void TryLookupProperty(compiler::Node* object, compiler::Node* map,
|
||||
compiler::Node* instance_type,
|
||||
compiler::Node* unique_name, Label* if_found_fast,
|
||||
Label* if_found_dict, Label* if_found_global,
|
||||
Variable* var_meta_storage, Variable* var_name_index,
|
||||
Label* if_not_found, Label* if_bailout);
|
||||
|
||||
void TryLookupElement(compiler::Node* object, compiler::Node* map,
|
||||
compiler::Node* instance_type, compiler::Node* index,
|
||||
Label* if_found, Label* if_not_found,
|
||||
|
@ -4343,7 +4343,7 @@ compiler::Node* HasPropertyStub::Generate(CodeStubAssembler* assembler,
|
||||
assembler->Bind(&loop);
|
||||
{
|
||||
Label next_proto(assembler);
|
||||
assembler->TryLookupProperty(var_object.value(), var_map.value(),
|
||||
assembler->TryHasOwnProperty(var_object.value(), var_map.value(),
|
||||
var_instance_type.value(), key, &return_true,
|
||||
&next_proto, &call_runtime);
|
||||
assembler->Bind(&next_proto);
|
||||
|
@ -7424,9 +7424,9 @@ void BaseDictionaryShape<Key>::SetEntry(Dictionary* dict, int entry,
|
||||
int index = dict->EntryToIndex(entry);
|
||||
DisallowHeapAllocation no_gc;
|
||||
WriteBarrierMode mode = dict->GetWriteBarrierMode(no_gc);
|
||||
dict->set(index, *key, mode);
|
||||
dict->set(index + 1, *value, mode);
|
||||
dict->set(index + 2, details.AsSmi());
|
||||
dict->set(index + Dictionary::kEntryKeyIndex, *key, mode);
|
||||
dict->set(index + Dictionary::kEntryValueIndex, *value, mode);
|
||||
dict->set(index + Dictionary::kEntryDetailsIndex, details.AsSmi());
|
||||
}
|
||||
|
||||
|
||||
@ -7440,8 +7440,8 @@ void GlobalDictionaryShape::SetEntry(Dictionary* dict, int entry,
|
||||
int index = dict->EntryToIndex(entry);
|
||||
DisallowHeapAllocation no_gc;
|
||||
WriteBarrierMode mode = dict->GetWriteBarrierMode(no_gc);
|
||||
dict->set(index, *key, mode);
|
||||
dict->set(index + 1, *value, mode);
|
||||
dict->set(index + Dictionary::kEntryKeyIndex, *key, mode);
|
||||
dict->set(index + Dictionary::kEntryValueIndex, *value, mode);
|
||||
PropertyCell::cast(*value)->set_property_details(details);
|
||||
}
|
||||
|
||||
|
@ -16202,8 +16202,7 @@ int NameDictionaryBase<Derived, Shape>::FindEntry(Handle<Name> key) {
|
||||
uint32_t count = 1;
|
||||
Isolate* isolate = this->GetIsolate();
|
||||
while (true) {
|
||||
int index = Derived::EntryToIndex(entry);
|
||||
Object* element = this->get(index);
|
||||
Object* element = this->KeyAt(entry);
|
||||
if (element->IsUndefined(isolate)) break; // Empty entry.
|
||||
if (*key == element) return entry;
|
||||
DCHECK(element->IsTheHole(isolate) || element->IsUniqueName());
|
||||
@ -16299,11 +16298,11 @@ void HashTable<Derived, Shape, Key>::Rehash(Key key) {
|
||||
// are placed correctly. Other elements might need to be moved.
|
||||
done = true;
|
||||
for (uint32_t current = 0; current < capacity; current++) {
|
||||
Object* current_key = get(EntryToIndex(current));
|
||||
Object* current_key = KeyAt(current);
|
||||
if (IsKey(isolate, current_key)) {
|
||||
uint32_t target = EntryForProbe(key, current_key, probe, current);
|
||||
if (current == target) continue;
|
||||
Object* target_key = get(EntryToIndex(target));
|
||||
Object* target_key = KeyAt(target);
|
||||
if (!IsKey(target_key) ||
|
||||
EntryForProbe(key, target_key, probe, target) != target) {
|
||||
// Put the current element into the correct position.
|
||||
@ -16322,8 +16321,8 @@ void HashTable<Derived, Shape, Key>::Rehash(Key key) {
|
||||
Object* the_hole = isolate->heap()->the_hole_value();
|
||||
Object* undefined = isolate->heap()->undefined_value();
|
||||
for (uint32_t current = 0; current < capacity; current++) {
|
||||
if (get(EntryToIndex(current)) == the_hole) {
|
||||
set(EntryToIndex(current), undefined);
|
||||
if (KeyAt(current) == the_hole) {
|
||||
set(EntryToIndex(current) + Derived::kEntryKeyIndex, undefined);
|
||||
}
|
||||
}
|
||||
SetNumberOfDeletedElements(0);
|
||||
|
@ -3236,10 +3236,12 @@ class HashTable : public HashTableBase {
|
||||
void Rehash(Key key);
|
||||
|
||||
// Returns the key at entry.
|
||||
Object* KeyAt(int entry) { return get(EntryToIndex(entry)); }
|
||||
Object* KeyAt(int entry) { return get(EntryToIndex(entry) + kEntryKeyIndex); }
|
||||
|
||||
static const int kElementsStartIndex = kPrefixStartIndex + Shape::kPrefixSize;
|
||||
static const int kEntrySize = Shape::kEntrySize;
|
||||
STATIC_ASSERT(kEntrySize > 0);
|
||||
static const int kEntryKeyIndex = 0;
|
||||
static const int kElementsStartOffset =
|
||||
kHeaderSize + kElementsStartIndex * kPointerSize;
|
||||
static const int kCapacityOffset =
|
||||
@ -3554,15 +3556,16 @@ class BaseDictionaryShape : public BaseShape<Key> {
|
||||
static inline PropertyDetails DetailsAt(Dictionary* dict, int entry) {
|
||||
STATIC_ASSERT(Dictionary::kEntrySize == 3);
|
||||
DCHECK(entry >= 0); // Not found is -1, which is not caught by get().
|
||||
return PropertyDetails(
|
||||
Smi::cast(dict->get(Dictionary::EntryToIndex(entry) + 2)));
|
||||
return PropertyDetails(Smi::cast(dict->get(
|
||||
Dictionary::EntryToIndex(entry) + Dictionary::kEntryDetailsIndex)));
|
||||
}
|
||||
|
||||
template <typename Dictionary>
|
||||
static inline void DetailsAtPut(Dictionary* dict, int entry,
|
||||
PropertyDetails value) {
|
||||
STATIC_ASSERT(Dictionary::kEntrySize == 3);
|
||||
dict->set(Dictionary::EntryToIndex(entry) + 2, value.AsSmi());
|
||||
dict->set(Dictionary::EntryToIndex(entry) + Dictionary::kEntryDetailsIndex,
|
||||
value.AsSmi());
|
||||
}
|
||||
|
||||
template <typename Dictionary>
|
||||
@ -3584,6 +3587,8 @@ class NameDictionaryShape : public BaseDictionaryShape<Handle<Name> > {
|
||||
static inline Handle<Object> AsHandle(Isolate* isolate, Handle<Name> key);
|
||||
static const int kPrefixSize = 2;
|
||||
static const int kEntrySize = 3;
|
||||
static const int kEntryValueIndex = 1;
|
||||
static const int kEntryDetailsIndex = 2;
|
||||
static const bool kIsEnumerable = true;
|
||||
};
|
||||
|
||||
@ -3598,6 +3603,9 @@ class NameDictionary
|
||||
|
||||
inline static Handle<FixedArray> DoGenerateNewEnumerationIndices(
|
||||
Handle<NameDictionary> dictionary);
|
||||
|
||||
static const int kEntryValueIndex = 1;
|
||||
static const int kEntryDetailsIndex = 2;
|
||||
};
|
||||
|
||||
|
||||
@ -3625,6 +3633,8 @@ class GlobalDictionary
|
||||
: public NameDictionaryBase<GlobalDictionary, GlobalDictionaryShape> {
|
||||
public:
|
||||
DECLARE_CAST(GlobalDictionary)
|
||||
|
||||
static const int kEntryValueIndex = 1;
|
||||
};
|
||||
|
||||
|
||||
@ -3698,6 +3708,9 @@ class SeededNumberDictionary
|
||||
// requires_slow_elements returns false.
|
||||
inline uint32_t max_number_key();
|
||||
|
||||
static const int kEntryValueIndex = 1;
|
||||
static const int kEntryDetailsIndex = 2;
|
||||
|
||||
// Bit masks.
|
||||
static const int kRequiresSlowElementsMask = 1;
|
||||
static const int kRequiresSlowElementsTagSize = 1;
|
||||
@ -3728,6 +3741,9 @@ class UnseededNumberDictionary
|
||||
Handle<UnseededNumberDictionary> dictionary,
|
||||
uint32_t key,
|
||||
Handle<Object> value);
|
||||
|
||||
static const int kEntryValueIndex = 1;
|
||||
static const int kEntryDetailsIndex = 2;
|
||||
};
|
||||
|
||||
|
||||
|
@ -232,15 +232,15 @@ void TestNameDictionaryLookup() {
|
||||
|
||||
Label passed(&m), failed(&m);
|
||||
Label if_found(&m), if_not_found(&m);
|
||||
Variable var_entry(&m, MachineRepresentation::kWord32);
|
||||
Variable var_name_index(&m, MachineRepresentation::kWord32);
|
||||
|
||||
m.NameDictionaryLookup<Dictionary>(dictionary, unique_name, &if_found,
|
||||
&var_entry, &if_not_found);
|
||||
&var_name_index, &if_not_found);
|
||||
m.Bind(&if_found);
|
||||
m.GotoUnless(
|
||||
m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kFound))),
|
||||
&failed);
|
||||
m.Branch(m.Word32Equal(m.SmiToWord32(expected_arg), var_entry.value()),
|
||||
m.Branch(m.Word32Equal(m.SmiToWord32(expected_arg), var_name_index.value()),
|
||||
&passed, &failed);
|
||||
|
||||
m.Bind(&if_not_found);
|
||||
@ -284,10 +284,12 @@ void TestNameDictionaryLookup() {
|
||||
|
||||
for (size_t i = 0; i < arraysize(keys); i++) {
|
||||
int entry = dictionary->FindEntry(keys[i]);
|
||||
int name_index =
|
||||
Dictionary::EntryToIndex(entry) + Dictionary::kEntryKeyIndex;
|
||||
CHECK_NE(Dictionary::kNotFound, entry);
|
||||
|
||||
Handle<Object> expected_entry(Smi::FromInt(entry), isolate);
|
||||
ft.CheckTrue(dictionary, keys[i], expect_found, expected_entry);
|
||||
Handle<Object> expected_name_index(Smi::FromInt(name_index), isolate);
|
||||
ft.CheckTrue(dictionary, keys[i], expect_found, expected_name_index);
|
||||
}
|
||||
|
||||
Handle<Name> non_existing_keys[] = {
|
||||
@ -418,15 +420,46 @@ namespace {
|
||||
|
||||
void AddProperties(Handle<JSObject> object, Handle<Name> names[],
|
||||
size_t count) {
|
||||
Handle<Object> value(Smi::FromInt(42), object->GetIsolate());
|
||||
Isolate* isolate = object->GetIsolate();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
Handle<Object> value(Smi::FromInt(static_cast<int>(42 + i)), isolate);
|
||||
JSObject::AddProperty(object, names[i], value, NONE);
|
||||
}
|
||||
}
|
||||
|
||||
Handle<AccessorPair> CreateAccessorPair(FunctionTester* ft,
|
||||
const char* getter_body,
|
||||
const char* setter_body) {
|
||||
Handle<AccessorPair> pair = ft->isolate->factory()->NewAccessorPair();
|
||||
if (getter_body) {
|
||||
pair->set_getter(*ft->NewFunction(getter_body));
|
||||
}
|
||||
if (setter_body) {
|
||||
pair->set_setter(*ft->NewFunction(setter_body));
|
||||
}
|
||||
return pair;
|
||||
}
|
||||
|
||||
void AddProperties(Handle<JSObject> object, Handle<Name> names[],
|
||||
size_t names_count, Handle<Object> values[],
|
||||
size_t values_count, int seed = 0) {
|
||||
Isolate* isolate = object->GetIsolate();
|
||||
for (size_t i = 0; i < names_count; i++) {
|
||||
Handle<Object> value = values[(seed + i) % values_count];
|
||||
if (value->IsAccessorPair()) {
|
||||
Handle<AccessorPair> pair = Handle<AccessorPair>::cast(value);
|
||||
Handle<Object> getter(pair->getter(), isolate);
|
||||
Handle<Object> setter(pair->setter(), isolate);
|
||||
JSObject::DefineAccessor(object, names[i], getter, setter, NONE).Check();
|
||||
} else {
|
||||
JSObject::AddProperty(object, names[i], value, NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(TryLookupProperty) {
|
||||
TEST(TryHasOwnProperty) {
|
||||
typedef CodeStubAssembler::Label Label;
|
||||
Isolate* isolate(CcTest::InitIsolateOnce());
|
||||
|
||||
@ -445,7 +478,7 @@ TEST(TryLookupProperty) {
|
||||
Node* map = m.LoadMap(object);
|
||||
Node* instance_type = m.LoadMapInstanceType(map);
|
||||
|
||||
m.TryLookupProperty(object, map, instance_type, unique_name, &if_found,
|
||||
m.TryHasOwnProperty(object, map, instance_type, unique_name, &if_found,
|
||||
&if_not_found, &if_bailout);
|
||||
|
||||
m.Bind(&if_found);
|
||||
@ -477,6 +510,10 @@ TEST(TryLookupProperty) {
|
||||
Handle<Object> expect_bailout(Smi::FromInt(kBailout), isolate);
|
||||
|
||||
Factory* factory = isolate->factory();
|
||||
|
||||
Handle<Name> deleted_property_name =
|
||||
factory->InternalizeUtf8String("deleted");
|
||||
|
||||
Handle<Name> names[] = {
|
||||
factory->InternalizeUtf8String("a"),
|
||||
factory->InternalizeUtf8String("bb"),
|
||||
@ -492,25 +529,59 @@ TEST(TryLookupProperty) {
|
||||
std::vector<Handle<JSObject>> objects;
|
||||
|
||||
{
|
||||
Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
|
||||
Handle<JSObject> object = factory->NewJSObject(function);
|
||||
// Fast object, no inobject properties.
|
||||
int inobject_properties = 0;
|
||||
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
||||
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
||||
AddProperties(object, names, arraysize(names));
|
||||
CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type());
|
||||
CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties());
|
||||
CHECK(!object->map()->is_dictionary_map());
|
||||
objects.push_back(object);
|
||||
}
|
||||
|
||||
{
|
||||
// Fast object, all inobject properties.
|
||||
int inobject_properties = arraysize(names) * 2;
|
||||
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
||||
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
||||
AddProperties(object, names, arraysize(names));
|
||||
CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type());
|
||||
CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties());
|
||||
CHECK(!object->map()->is_dictionary_map());
|
||||
objects.push_back(object);
|
||||
}
|
||||
|
||||
{
|
||||
// Fast object, half inobject properties.
|
||||
int inobject_properties = arraysize(names) / 2;
|
||||
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
||||
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
||||
AddProperties(object, names, arraysize(names));
|
||||
CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type());
|
||||
CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties());
|
||||
CHECK(!object->map()->is_dictionary_map());
|
||||
objects.push_back(object);
|
||||
}
|
||||
|
||||
{
|
||||
// Dictionary mode object.
|
||||
Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
|
||||
Handle<JSObject> object = factory->NewJSObject(function);
|
||||
AddProperties(object, names, arraysize(names));
|
||||
JSObject::NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0, "test");
|
||||
|
||||
JSObject::AddProperty(object, deleted_property_name, object, NONE);
|
||||
CHECK(JSObject::DeleteProperty(object, deleted_property_name, SLOPPY)
|
||||
.FromJust());
|
||||
|
||||
CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type());
|
||||
CHECK(object->map()->is_dictionary_map());
|
||||
objects.push_back(object);
|
||||
}
|
||||
|
||||
{
|
||||
// Global object.
|
||||
Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
|
||||
JSFunction::EnsureHasInitialMap(function);
|
||||
function->initial_map()->set_instance_type(JS_GLOBAL_OBJECT_TYPE);
|
||||
@ -518,6 +589,11 @@ TEST(TryLookupProperty) {
|
||||
function->initial_map()->set_dictionary_map(true);
|
||||
Handle<JSObject> object = factory->NewJSGlobalObject(function);
|
||||
AddProperties(object, names, arraysize(names));
|
||||
|
||||
JSObject::AddProperty(object, deleted_property_name, object, NONE);
|
||||
CHECK(JSObject::DeleteProperty(object, deleted_property_name, SLOPPY)
|
||||
.FromJust());
|
||||
|
||||
CHECK_EQ(JS_GLOBAL_OBJECT_TYPE, object->map()->instance_type());
|
||||
CHECK(object->map()->is_dictionary_map());
|
||||
objects.push_back(object);
|
||||
@ -535,17 +611,20 @@ TEST(TryLookupProperty) {
|
||||
|
||||
{
|
||||
Handle<Name> non_existing_names[] = {
|
||||
factory->NewSymbol(),
|
||||
factory->InternalizeUtf8String("ne_a"),
|
||||
factory->InternalizeUtf8String("ne_bb"),
|
||||
factory->NewPrivateSymbol(),
|
||||
factory->InternalizeUtf8String("ne_ccc"),
|
||||
factory->InternalizeUtf8String("ne_dddd"),
|
||||
deleted_property_name,
|
||||
};
|
||||
for (Handle<JSObject> object : objects) {
|
||||
for (size_t key_index = 0; key_index < arraysize(non_existing_names);
|
||||
key_index++) {
|
||||
Handle<Name> key = non_existing_names[key_index];
|
||||
CHECK(!JSReceiver::HasProperty(object, key).FromJust());
|
||||
ft.CheckTrue(object, key, expect_not_found);
|
||||
Handle<Name> name = non_existing_names[key_index];
|
||||
CHECK(!JSReceiver::HasProperty(object, name).FromJust());
|
||||
ft.CheckTrue(object, name, expect_not_found);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -564,6 +643,215 @@ TEST(TryLookupProperty) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TryGetOwnProperty) {
|
||||
typedef CodeStubAssembler::Label Label;
|
||||
typedef CodeStubAssembler::Variable Variable;
|
||||
Isolate* isolate(CcTest::InitIsolateOnce());
|
||||
Factory* factory = isolate->factory();
|
||||
|
||||
const int kNumParams = 2;
|
||||
CodeStubAssemblerTester m(isolate, kNumParams);
|
||||
|
||||
Handle<Symbol> not_found_symbol = factory->NewSymbol();
|
||||
Handle<Symbol> bailout_symbol = factory->NewSymbol();
|
||||
{
|
||||
Node* object = m.Parameter(0);
|
||||
Node* unique_name = m.Parameter(1);
|
||||
Node* context = m.Parameter(kNumParams + 2);
|
||||
|
||||
Variable var_value(&m, MachineRepresentation::kTagged);
|
||||
Label if_found(&m), if_not_found(&m), if_bailout(&m);
|
||||
|
||||
Node* map = m.LoadMap(object);
|
||||
Node* instance_type = m.LoadMapInstanceType(map);
|
||||
|
||||
m.TryGetOwnProperty(context, object, object, map, instance_type,
|
||||
unique_name, &if_found, &var_value, &if_not_found,
|
||||
&if_bailout);
|
||||
|
||||
m.Bind(&if_found);
|
||||
m.Return(var_value.value());
|
||||
|
||||
m.Bind(&if_not_found);
|
||||
m.Return(m.HeapConstant(not_found_symbol));
|
||||
|
||||
m.Bind(&if_bailout);
|
||||
m.Return(m.HeapConstant(bailout_symbol));
|
||||
}
|
||||
|
||||
Handle<Code> code = m.GenerateCode();
|
||||
FunctionTester ft(code, kNumParams);
|
||||
|
||||
Handle<Name> deleted_property_name =
|
||||
factory->InternalizeUtf8String("deleted");
|
||||
|
||||
Handle<Name> names[] = {
|
||||
factory->InternalizeUtf8String("bb"),
|
||||
factory->NewSymbol(),
|
||||
factory->InternalizeUtf8String("a"),
|
||||
factory->InternalizeUtf8String("ccc"),
|
||||
factory->InternalizeUtf8String("esajefe"),
|
||||
factory->NewPrivateSymbol(),
|
||||
factory->InternalizeUtf8String("eeeee"),
|
||||
factory->InternalizeUtf8String("p1"),
|
||||
factory->InternalizeUtf8String("acshw23e"),
|
||||
factory->InternalizeUtf8String(""),
|
||||
factory->InternalizeUtf8String("dddd"),
|
||||
factory->NewPrivateSymbol(),
|
||||
factory->InternalizeUtf8String("name"),
|
||||
factory->InternalizeUtf8String("p2"),
|
||||
factory->InternalizeUtf8String("p3"),
|
||||
factory->InternalizeUtf8String("p4"),
|
||||
factory->NewPrivateSymbol(),
|
||||
};
|
||||
Handle<Object> values[] = {
|
||||
factory->NewFunction(factory->empty_string()),
|
||||
factory->NewSymbol(),
|
||||
factory->InternalizeUtf8String("a"),
|
||||
CreateAccessorPair(&ft, "() => 188;", "() => 199;"),
|
||||
factory->NewFunction(factory->InternalizeUtf8String("bb")),
|
||||
factory->InternalizeUtf8String("ccc"),
|
||||
CreateAccessorPair(&ft, "() => 88;", nullptr),
|
||||
handle(Smi::FromInt(1), isolate),
|
||||
factory->InternalizeUtf8String(""),
|
||||
CreateAccessorPair(&ft, nullptr, "() => 99;"),
|
||||
factory->NewHeapNumber(4.2),
|
||||
handle(Smi::FromInt(153), isolate),
|
||||
factory->NewJSObject(factory->NewFunction(factory->empty_string())),
|
||||
factory->NewPrivateSymbol(),
|
||||
};
|
||||
STATIC_ASSERT(arraysize(values) < arraysize(names));
|
||||
|
||||
base::RandomNumberGenerator rand_gen(FLAG_random_seed);
|
||||
|
||||
std::vector<Handle<JSObject>> objects;
|
||||
|
||||
{
|
||||
// Fast object, no inobject properties.
|
||||
int inobject_properties = 0;
|
||||
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
||||
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
||||
AddProperties(object, names, arraysize(names), values, arraysize(values),
|
||||
rand_gen.NextInt());
|
||||
CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type());
|
||||
CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties());
|
||||
CHECK(!object->map()->is_dictionary_map());
|
||||
objects.push_back(object);
|
||||
}
|
||||
|
||||
{
|
||||
// Fast object, all inobject properties.
|
||||
int inobject_properties = arraysize(names) * 2;
|
||||
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
||||
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
||||
AddProperties(object, names, arraysize(names), values, arraysize(values),
|
||||
rand_gen.NextInt());
|
||||
CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type());
|
||||
CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties());
|
||||
CHECK(!object->map()->is_dictionary_map());
|
||||
objects.push_back(object);
|
||||
}
|
||||
|
||||
{
|
||||
// Fast object, half inobject properties.
|
||||
int inobject_properties = arraysize(names) / 2;
|
||||
Handle<Map> map = Map::Create(isolate, inobject_properties);
|
||||
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
|
||||
AddProperties(object, names, arraysize(names), values, arraysize(values),
|
||||
rand_gen.NextInt());
|
||||
CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type());
|
||||
CHECK_EQ(inobject_properties, object->map()->GetInObjectProperties());
|
||||
CHECK(!object->map()->is_dictionary_map());
|
||||
objects.push_back(object);
|
||||
}
|
||||
|
||||
{
|
||||
// Dictionary mode object.
|
||||
Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
|
||||
Handle<JSObject> object = factory->NewJSObject(function);
|
||||
AddProperties(object, names, arraysize(names), values, arraysize(values),
|
||||
rand_gen.NextInt());
|
||||
JSObject::NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0, "test");
|
||||
|
||||
JSObject::AddProperty(object, deleted_property_name, object, NONE);
|
||||
CHECK(JSObject::DeleteProperty(object, deleted_property_name, SLOPPY)
|
||||
.FromJust());
|
||||
|
||||
CHECK_EQ(JS_OBJECT_TYPE, object->map()->instance_type());
|
||||
CHECK(object->map()->is_dictionary_map());
|
||||
objects.push_back(object);
|
||||
}
|
||||
|
||||
{
|
||||
// Global object.
|
||||
Handle<JSGlobalObject> object = isolate->global_object();
|
||||
AddProperties(object, names, arraysize(names), values, arraysize(values),
|
||||
rand_gen.NextInt());
|
||||
|
||||
JSObject::AddProperty(object, deleted_property_name, object, NONE);
|
||||
CHECK(JSObject::DeleteProperty(object, deleted_property_name, SLOPPY)
|
||||
.FromJust());
|
||||
|
||||
CHECK_EQ(JS_GLOBAL_OBJECT_TYPE, object->map()->instance_type());
|
||||
CHECK(object->map()->is_dictionary_map());
|
||||
objects.push_back(object);
|
||||
}
|
||||
|
||||
// TODO(ishell): test proxy and interceptors when they are supported.
|
||||
|
||||
{
|
||||
for (Handle<JSObject> object : objects) {
|
||||
for (size_t name_index = 0; name_index < arraysize(names); name_index++) {
|
||||
Handle<Name> name = names[name_index];
|
||||
Handle<Object> expected_value =
|
||||
JSReceiver::GetProperty(object, name).ToHandleChecked();
|
||||
Handle<Object> value = ft.Call(object, name).ToHandleChecked();
|
||||
CHECK(expected_value->SameValue(*value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Handle<Name> non_existing_names[] = {
|
||||
factory->NewSymbol(),
|
||||
factory->InternalizeUtf8String("ne_a"),
|
||||
factory->InternalizeUtf8String("ne_bb"),
|
||||
factory->NewPrivateSymbol(),
|
||||
factory->InternalizeUtf8String("ne_ccc"),
|
||||
factory->InternalizeUtf8String("ne_dddd"),
|
||||
deleted_property_name,
|
||||
};
|
||||
for (Handle<JSObject> object : objects) {
|
||||
for (size_t key_index = 0; key_index < arraysize(non_existing_names);
|
||||
key_index++) {
|
||||
Handle<Name> name = non_existing_names[key_index];
|
||||
Handle<Object> expected_value =
|
||||
JSReceiver::GetProperty(object, name).ToHandleChecked();
|
||||
CHECK(expected_value->IsUndefined(isolate));
|
||||
Handle<Object> value = ft.Call(object, name).ToHandleChecked();
|
||||
CHECK_EQ(*not_found_symbol, *value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Handle<JSFunction> function = factory->NewFunction(factory->empty_string());
|
||||
Handle<JSProxy> object = factory->NewJSProxy(function, objects[0]);
|
||||
CHECK_EQ(JS_PROXY_TYPE, object->map()->instance_type());
|
||||
Handle<Object> value = ft.Call(object, names[0]).ToHandleChecked();
|
||||
// Proxies are not supported yet.
|
||||
CHECK_EQ(*bailout_symbol, *value);
|
||||
}
|
||||
|
||||
{
|
||||
Handle<JSObject> object = isolate->global_proxy();
|
||||
CHECK_EQ(JS_GLOBAL_PROXY_TYPE, object->map()->instance_type());
|
||||
// Global proxies are not supported yet.
|
||||
Handle<Object> value = ft.Call(object, names[0]).ToHandleChecked();
|
||||
CHECK_EQ(*bailout_symbol, *value);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void AddElement(Handle<JSObject> object, uint32_t index, Handle<Object> value,
|
||||
@ -577,7 +865,7 @@ TEST(TryLookupElement) {
|
||||
typedef CodeStubAssembler::Label Label;
|
||||
Isolate* isolate(CcTest::InitIsolateOnce());
|
||||
|
||||
const int kNumParams = 4;
|
||||
const int kNumParams = 3;
|
||||
CodeStubAssemblerTester m(isolate, kNumParams);
|
||||
|
||||
enum Result { kFound, kNotFound, kBailout };
|
||||
|
Loading…
Reference in New Issue
Block a user