[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:
ishell 2016-06-27 05:26:57 -07:00 committed by Commit bot
parent 705574970f
commit 23332fe829
8 changed files with 753 additions and 78 deletions

View File

@ -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);

View File

@ -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, &not_ok);
Bind(&not_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);
}
}
}

View File

@ -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,

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
};

View File

@ -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 };