Reapply descriptor array sharing.
This reverts commit 12669
Review URL: https://chromiumcodereview.appspot.com/11093026
git-svn-id: http://v8.googlecode.com/svn/trunk@12683 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index b2f629b..de09516 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -2729,26 +2729,31 @@
__ b(eq, if_false);
// Look for valueOf symbol in the descriptor array, and indicate false if
- // found. The type is not checked, so if it is a transition it is a false
- // negative.
- __ LoadInstanceDescriptors(r1, r4, r3);
- __ ldr(r3, FieldMemOperand(r4, FixedArray::kLengthOffset));
- // r4: descriptor array
- // r3: length of descriptor array
- // Calculate the end of the descriptor array.
+ // found. Since we omit an enumeration index check, if it is added via a
+ // transition that shares its descriptor array, this is a false positive.
+ Label entry, loop, done;
+
+ // Skip loop if no descriptors are valid.
+ __ NumberOfOwnDescriptors(r3, r1);
+ __ cmp(r3, Operand(0));
+ __ b(eq, &done);
+
+ __ LoadInstanceDescriptors(r1, r4, r2);
+ // r4: descriptor array.
+ // r3: valid entries in the descriptor array.
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
STATIC_ASSERT(kPointerSize == 4);
- __ add(r2, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ mov(ip, Operand(DescriptorArray::kDescriptorSize));
+ __ mul(r3, r3, ip);
+ // Calculate location of the first key name.
+ __ add(r4, r4, Operand(DescriptorArray::kFirstOffset - kHeapObjectTag));
+ // Calculate the end of the descriptor array.
+ __ mov(r2, r4);
__ add(r2, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
- // Calculate location of the first key name.
- __ add(r4,
- r4,
- Operand(DescriptorArray::kFirstOffset - kHeapObjectTag));
// Loop through all the keys in the descriptor array. If one of these is the
// symbol valueOf the result is false.
- Label entry, loop;
// The use of ip to store the valueOf symbol asumes that it is not otherwise
// used in the loop below.
__ mov(ip, Operand(FACTORY->value_of_symbol()));
@@ -2762,7 +2767,8 @@
__ cmp(r4, Operand(r2));
__ b(ne, &loop);
- // If a valueOf property is not found on the object check that it's
+ __ bind(&done);
+ // If a valueOf property is not found on the object check that its
// prototype is the un-modified String prototype. If not result is false.
__ ldr(r2, FieldMemOperand(r1, Map::kPrototypeOffset));
__ JumpIfSmi(r2, if_false);
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index 2a677be..d266310 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -3703,20 +3703,37 @@
Register temp = descriptors;
ldr(temp, FieldMemOperand(map, Map::kTransitionsOrBackPointerOffset));
- Label ok, fail;
+ Label ok, fail, load_from_back_pointer;
CheckMap(temp,
scratch,
isolate()->factory()->fixed_array_map(),
&fail,
DONT_DO_SMI_CHECK);
- ldr(descriptors, FieldMemOperand(temp, TransitionArray::kDescriptorsOffset));
+ ldr(temp, FieldMemOperand(temp, TransitionArray::kDescriptorsPointerOffset));
+ ldr(descriptors, FieldMemOperand(temp, JSGlobalPropertyCell::kValueOffset));
jmp(&ok);
+
bind(&fail);
+ CompareRoot(temp, Heap::kUndefinedValueRootIndex);
+ b(ne, &load_from_back_pointer);
mov(descriptors, Operand(FACTORY->empty_descriptor_array()));
+ jmp(&ok);
+
+ bind(&load_from_back_pointer);
+ ldr(temp, FieldMemOperand(temp, Map::kTransitionsOrBackPointerOffset));
+ ldr(temp, FieldMemOperand(temp, TransitionArray::kDescriptorsPointerOffset));
+ ldr(descriptors, FieldMemOperand(temp, JSGlobalPropertyCell::kValueOffset));
+
bind(&ok);
}
+void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) {
+ ldr(dst, FieldMemOperand(map, Map::kBitFieldOffset));
+ DecodeField<Map::NumberOfOwnDescriptorsBits>(dst);
+}
+
+
void MacroAssembler::EnumLength(Register dst, Register map) {
STATIC_ASSERT(Map::EnumLengthBits::kShift == 0);
ldr(dst, FieldMemOperand(map, Map::kBitField3Offset));
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index 8eb9712..7d127b5 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -1273,6 +1273,15 @@
Register descriptors,
Register scratch);
void EnumLength(Register dst, Register map);
+ void NumberOfOwnDescriptors(Register dst, Register map);
+
+ template<typename Field>
+ void DecodeField(Register reg) {
+ static const int shift = Field::kShift;
+ static const int mask = (Field::kMask >> shift) << kSmiTagSize;
+ mov(reg, Operand(reg, LSR, shift));
+ and_(reg, reg, Operand(mask));
+ }
// Activation support.
void EnterFrame(StackFrame::Type type);
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 992659e..9de1058 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -637,7 +637,7 @@
Handle<String> name,
Handle<JSFunction> func) {
DescriptorArray* descs = map->instance_descriptors();
- int number = descs->Search(*name);
+ int number = descs->SearchWithCache(*name, *map);
AccessorPair* accessors = AccessorPair::cast(descs->GetValue(number));
accessors->set_getter(*func);
accessors->set_setter(*func);
@@ -1774,7 +1774,8 @@
Handle<DescriptorArray> array_descriptors(
array_function->initial_map()->instance_descriptors());
String* length = heap()->length_symbol();
- int old = array_descriptors->SearchWithCache(length);
+ int old = array_descriptors->SearchWithCache(
+ length, array_function->initial_map());
ASSERT(old != DescriptorArray::kNotFound);
CallbacksDescriptor desc(length,
array_descriptors->GetValue(old),
diff --git a/src/handles.cc b/src/handles.cc
index 6aa7a6a..7999a10 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -705,24 +705,46 @@
}
+Handle<FixedArray> ReduceFixedArrayTo(Handle<FixedArray> array, int length) {
+ ASSERT(array->length() >= length);
+ if (array->length() == length) return array;
+
+ Handle<FixedArray> new_array =
+ array->GetIsolate()->factory()->NewFixedArray(length);
+ for (int i = 0; i < length; ++i) new_array->set(i, array->get(i));
+ return new_array;
+}
+
+
Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
bool cache_result) {
Isolate* isolate = object->GetIsolate();
if (object->HasFastProperties()) {
if (object->map()->instance_descriptors()->HasEnumCache()) {
int own_property_count = object->map()->EnumLength();
+ // If we have an enum cache, but the enum length of the given map is set
+ // to kInvalidEnumCache, this means that the map itself has never used the
+ // present enum cache. The first step to using the cache is to set the
+ // enum length of the map by counting the number of own descriptors that
+ // are not DONT_ENUM.
+ if (own_property_count == Map::kInvalidEnumCache) {
+ own_property_count = object->map()->NumberOfDescribedProperties(
+ OWN_DESCRIPTORS, DONT_ENUM);
- // Mark that we have an enum cache if we are allowed to cache it.
- if (cache_result && own_property_count == Map::kInvalidEnumCache) {
- int num_enum = object->map()->NumberOfDescribedProperties(DONT_ENUM);
- object->map()->SetEnumLength(num_enum);
+ if (cache_result) object->map()->SetEnumLength(own_property_count);
}
DescriptorArray* desc = object->map()->instance_descriptors();
Handle<FixedArray> keys(FixedArray::cast(desc->GetEnumCache()), isolate);
- isolate->counters()->enum_cache_hits()->Increment();
- return keys;
+ // In case the number of properties required in the enum are actually
+ // present, we can reuse the enum cache. Otherwise, this means that the
+ // enum cache was generated for a previous (smaller) version of the
+ // Descriptor Array. In that case we regenerate the enum cache.
+ if (own_property_count <= keys->length()) {
+ isolate->counters()->enum_cache_hits()->Increment();
+ return ReduceFixedArrayTo(keys, own_property_count);
+ }
}
Handle<Map> map(object->map());
@@ -734,8 +756,7 @@
}
isolate->counters()->enum_cache_misses()->Increment();
-
- int num_enum = map->NumberOfDescribedProperties(DONT_ENUM);
+ int num_enum = map->NumberOfDescribedProperties(ALL_DESCRIPTORS, DONT_ENUM);
Handle<FixedArray> storage = isolate->factory()->NewFixedArray(num_enum);
Handle<FixedArray> indices = isolate->factory()->NewFixedArray(num_enum);
@@ -743,10 +764,14 @@
Handle<DescriptorArray> descs =
Handle<DescriptorArray>(object->map()->instance_descriptors(), isolate);
+ int real_size = map->NumberOfOwnDescriptors();
+ int enum_size = 0;
int index = 0;
+
for (int i = 0; i < descs->number_of_descriptors(); i++) {
PropertyDetails details = descs->GetDetails(i);
if (!details.IsDontEnum()) {
+ if (i < real_size) ++enum_size;
storage->set(index, descs->GetKey(i));
if (!indices.is_null()) {
if (details.type() != FIELD) {
@@ -773,9 +798,10 @@
indices.is_null() ? Object::cast(Smi::FromInt(0))
: Object::cast(*indices));
if (cache_result) {
- object->map()->SetEnumLength(index);
+ object->map()->SetEnumLength(enum_size);
}
- return storage;
+
+ return ReduceFixedArrayTo(storage, enum_size);
} else {
Handle<StringDictionary> dictionary(object->property_dictionary());
diff --git a/src/handles.h b/src/handles.h
index b35693e..a1d88c2 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -276,6 +276,7 @@
KeyCollectionType type,
bool* threw);
Handle<JSArray> GetKeysFor(Handle<JSReceiver> object, bool* threw);
+Handle<FixedArray> ReduceFixedArrayTo(Handle<FixedArray> array, int length);
Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
bool cache_result);
diff --git a/src/heap.cc b/src/heap.cc
index fc5fb36..bdb63bf 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -2052,7 +2052,9 @@
reinterpret_cast<Map*>(result)->set_unused_property_fields(0);
reinterpret_cast<Map*>(result)->set_bit_field(0);
reinterpret_cast<Map*>(result)->set_bit_field2(0);
- reinterpret_cast<Map*>(result)->set_bit_field3(0);
+ int bit_field3 = Map::EnumLengthBits::encode(Map::kInvalidEnumCache) |
+ Map::OwnsDescriptors::encode(true);
+ reinterpret_cast<Map*>(result)->set_bit_field3(bit_field3);
return result;
}
@@ -2079,7 +2081,8 @@
map->set_unused_property_fields(0);
map->set_bit_field(0);
map->set_bit_field2(1 << Map::kIsExtensible);
- int bit_field3 = Map::EnumLengthBits::encode(Map::kInvalidEnumCache);
+ int bit_field3 = Map::EnumLengthBits::encode(Map::kInvalidEnumCache) |
+ Map::OwnsDescriptors::encode(true);
map->set_bit_field3(bit_field3);
map->set_elements_kind(elements_kind);
@@ -7123,7 +7126,7 @@
void DescriptorLookupCache::Clear() {
- for (int index = 0; index < kLength; index++) keys_[index].array = NULL;
+ for (int index = 0; index < kLength; index++) keys_[index].source = NULL;
}
diff --git a/src/heap.h b/src/heap.h
index 2cdcf3f..90d0797 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -2376,7 +2376,7 @@
};
-// Cache for mapping (array, property name) into descriptor index.
+// Cache for mapping (map, property name) into descriptor index.
// The cache contains both positive and negative results.
// Descriptor index equals kNotFound means the property is absent.
// Cleared at startup and prior to any gc.
@@ -2384,21 +2384,21 @@
public:
// Lookup descriptor index for (map, name).
// If absent, kAbsent is returned.
- int Lookup(DescriptorArray* array, String* name) {
+ int Lookup(Map* source, String* name) {
if (!StringShape(name).IsSymbol()) return kAbsent;
- int index = Hash(array, name);
+ int index = Hash(source, name);
Key& key = keys_[index];
- if ((key.array == array) && (key.name == name)) return results_[index];
+ if ((key.source == source) && (key.name == name)) return results_[index];
return kAbsent;
}
// Update an element in the cache.
- void Update(DescriptorArray* array, String* name, int result) {
+ void Update(Map* source, String* name, int result) {
ASSERT(result != kAbsent);
if (StringShape(name).IsSymbol()) {
- int index = Hash(array, name);
+ int index = Hash(source, name);
Key& key = keys_[index];
- key.array = array;
+ key.source = source;
key.name = name;
results_[index] = result;
}
@@ -2412,26 +2412,26 @@
private:
DescriptorLookupCache() {
for (int i = 0; i < kLength; ++i) {
- keys_[i].array = NULL;
+ keys_[i].source = NULL;
keys_[i].name = NULL;
results_[i] = kAbsent;
}
}
- static int Hash(DescriptorArray* array, String* name) {
+ static int Hash(Object* source, String* name) {
// Uses only lower 32 bits if pointers are larger.
- uint32_t array_hash =
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(array))
+ uint32_t source_hash =
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(source))
>> kPointerSizeLog2;
uint32_t name_hash =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name))
>> kPointerSizeLog2;
- return (array_hash ^ name_hash) % kLength;
+ return (source_hash ^ name_hash) % kLength;
}
static const int kLength = 64;
struct Key {
- DescriptorArray* array;
+ Map* source;
String* name;
};
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index 7fb7cc3..a058701 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -2669,22 +2669,28 @@
__ j(equal, if_false);
// Look for valueOf symbol in the descriptor array, and indicate false if
- // found. The type is not checked, so if it is a transition it is a false
- // negative.
+ // found. Since we omit an enumeration index check, if it is added via a
+ // transition that shares its descriptor array, this is a false positive.
+ Label entry, loop, done;
+
+ // Skip loop if no descriptors are valid.
+ __ NumberOfOwnDescriptors(ecx, ebx);
+ __ cmp(ecx, 0);
+ __ j(equal, &done);
+
__ LoadInstanceDescriptors(ebx, ebx);
- __ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset));
- // ebx: descriptor array
- // ecx: length of descriptor array
+ // ebx: descriptor array.
+ // ecx: valid entries in the descriptor array.
// Calculate the end of the descriptor array.
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
STATIC_ASSERT(kPointerSize == 4);
- __ lea(ecx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize));
+ __ imul(ecx, ecx, DescriptorArray::kDescriptorSize);
+ __ lea(ecx, Operand(ebx, ecx, times_2, DescriptorArray::kFirstOffset));
// Calculate location of the first key name.
__ add(ebx, Immediate(DescriptorArray::kFirstOffset));
// Loop through all the keys in the descriptor array. If one of these is the
// symbol valueOf the result is false.
- Label entry, loop;
__ jmp(&entry);
__ bind(&loop);
__ mov(edx, FieldOperand(ebx, 0));
@@ -2695,10 +2701,12 @@
__ cmp(ebx, ecx);
__ j(not_equal, &loop);
+ __ bind(&done);
+
// Reload map as register ebx was used as temporary above.
__ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
- // If a valueOf property is not found on the object check that it's
+ // If a valueOf property is not found on the object check that its
// prototype is the un-modified String prototype. If not result is false.
__ mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
__ JumpIfSmi(ecx, if_false);
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index 9c5f31e..c71fa00 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -2576,19 +2576,36 @@
Register temp = descriptors;
mov(temp, FieldOperand(map, Map::kTransitionsOrBackPointerOffset));
- Label ok, fail;
+ Label ok, fail, load_from_back_pointer;
CheckMap(temp,
isolate()->factory()->fixed_array_map(),
&fail,
DONT_DO_SMI_CHECK);
- mov(descriptors, FieldOperand(temp, TransitionArray::kDescriptorsOffset));
+ mov(temp, FieldOperand(temp, TransitionArray::kDescriptorsPointerOffset));
+ mov(descriptors, FieldOperand(temp, JSGlobalPropertyCell::kValueOffset));
jmp(&ok);
+
bind(&fail);
+ cmp(temp, isolate()->factory()->undefined_value());
+ j(not_equal, &load_from_back_pointer, Label::kNear);
mov(descriptors, isolate()->factory()->empty_descriptor_array());
+ jmp(&ok);
+
+ bind(&load_from_back_pointer);
+ mov(temp, FieldOperand(temp, Map::kTransitionsOrBackPointerOffset));
+ mov(temp, FieldOperand(temp, TransitionArray::kDescriptorsPointerOffset));
+ mov(descriptors, FieldOperand(temp, JSGlobalPropertyCell::kValueOffset));
+
bind(&ok);
}
+void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) {
+ mov(dst, FieldOperand(map, Map::kBitField3Offset));
+ DecodeField<Map::NumberOfOwnDescriptorsBits>(dst);
+}
+
+
void MacroAssembler::LoadPowerOf2(XMMRegister dst,
Register scratch,
int power) {
diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h
index 7d475e7..909000e 100644
--- a/src/ia32/macro-assembler-ia32.h
+++ b/src/ia32/macro-assembler-ia32.h
@@ -493,13 +493,14 @@
void LoadInstanceDescriptors(Register map, Register descriptors);
void EnumLength(Register dst, Register map);
+ void NumberOfOwnDescriptors(Register dst, Register map);
template<typename Field>
void DecodeField(Register reg) {
- static const int full_shift = Field::kShift + kSmiTagSize;
- static const int low_mask = Field::kMask >> Field::kShift;
- sar(reg, full_shift);
- and_(reg, Immediate(low_mask));
+ static const int shift = Field::kShift;
+ static const int mask = (Field::kMask >> Field::kShift) << kSmiTagSize;
+ sar(reg, shift);
+ and_(reg, Immediate(mask));
}
void LoadPowerOf2(XMMRegister dst, Register scratch, int power);
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index a9a90bd..7c03a49 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -1548,7 +1548,8 @@
Map* map_obj = Map::cast(obj);
ASSERT(map->instance_type() == MAP_TYPE);
DescriptorArray* array = map_obj->instance_descriptors();
- if (array != heap->empty_descriptor_array()) {
+ if (map_obj->owns_descriptors() &&
+ array != heap->empty_descriptor_array()) {
int fixed_array_size = array->Size();
heap->RecordObjectStats(FIXED_ARRAY_TYPE,
DESCRIPTOR_ARRAY_SUB_TYPE,
@@ -1942,10 +1943,10 @@
if (!base_marker()->MarkObjectWithoutPush(transitions)) return;
Object** transitions_start = transitions->data_start();
- DescriptorArray* descriptors = transitions->descriptors();
- base_marker()->MarkObjectAndPush(descriptors);
- mark_compact_collector()->RecordSlot(
- transitions_start, transitions->GetDescriptorsSlot(), descriptors);
+ // We don't have to record the descriptors_pointer slot since the cell space
+ // is not compacted.
+ JSGlobalPropertyCell* descriptors_cell = transitions->descriptors_pointer();
+ base_marker()->MarkObjectAndPush(descriptors_cell);
if (transitions->HasPrototypeTransitions()) {
// Mark prototype transitions array but don't push it into marking stack.
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index 9761ed1..d569161 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -901,10 +901,11 @@
}
-bool DescriptorArray::IsSortedNoDuplicates() {
+bool DescriptorArray::IsSortedNoDuplicates(int valid_entries) {
+ if (valid_entries == -1) valid_entries = number_of_descriptors();
String* current_key = NULL;
uint32_t current = 0;
- for (int i = 0; i < number_of_descriptors(); i++) {
+ for (int i = 0; i < valid_entries; i++) {
String* key = GetSortedKey(i);
if (key == current_key) {
PrintDescriptors();
@@ -922,7 +923,8 @@
}
-bool TransitionArray::IsSortedNoDuplicates() {
+bool TransitionArray::IsSortedNoDuplicates(int valid_entries) {
+ ASSERT(valid_entries == -1);
String* current_key = NULL;
uint32_t current = 0;
for (int i = 0; i < number_of_transitions(); i++) {
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 7968c35..36d0b6c 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -1940,31 +1940,45 @@
// Perform a linear search in this fixed array. len is the number of entry
// indices that are valid.
-template<typename T>
-int LinearSearch(T* array, String* name, int len) {
+template<SearchMode search_mode, typename T>
+int LinearSearch(T* array, String* name, int len, int valid_entries) {
uint32_t hash = name->Hash();
- for (int number = 0; number < len; number++) {
- int sorted_index = array->GetSortedKeyIndex(number);
- String* entry = array->GetKey(sorted_index);
- uint32_t current_hash = entry->Hash();
- if (current_hash > hash) break;
- if (current_hash == hash && entry->Equals(name)) return sorted_index;
+ if (search_mode == ALL_ENTRIES) {
+ for (int number = 0; number < len; number++) {
+ int sorted_index = array->GetSortedKeyIndex(number);
+ String* entry = array->GetKey(sorted_index);
+ uint32_t current_hash = entry->Hash();
+ if (current_hash > hash) break;
+ if (current_hash == hash && entry->Equals(name)) return sorted_index;
+ }
+ } else {
+ ASSERT(len >= valid_entries);
+ for (int number = 0; number < valid_entries; number++) {
+ String* entry = array->GetKey(number);
+ uint32_t current_hash = entry->Hash();
+ if (current_hash == hash && entry->Equals(name)) return number;
+ }
}
return T::kNotFound;
}
-template<typename T>
-int Search(T* array, String* name) {
- SLOW_ASSERT(array->IsSortedNoDuplicates());
+template<SearchMode search_mode, typename T>
+int Search(T* array, String* name, int valid_entries) {
+ if (search_mode == VALID_ENTRIES) {
+ SLOW_ASSERT(array->IsSortedNoDuplicates(valid_entries));
+ } else {
+ SLOW_ASSERT(array->IsSortedNoDuplicates());
+ }
int nof = array->number_of_entries();
if (nof == 0) return T::kNotFound;
// Fast case: do linear search for small arrays.
const int kMaxElementsForLinearSearch = 8;
- if (nof < kMaxElementsForLinearSearch) {
- return LinearSearch(array, name, nof);
+ if (search_mode == VALID_ENTRIES ||
+ (search_mode == ALL_ENTRIES && nof < kMaxElementsForLinearSearch)) {
+ return LinearSearch<search_mode>(array, name, nof, valid_entries);
}
// Slow case: perform binary search.
@@ -1972,20 +1986,21 @@
}
-int DescriptorArray::Search(String* name) {
- return internal::Search(this, name);
+int DescriptorArray::Search(String* name, int valid_descriptors) {
+ return internal::Search<VALID_ENTRIES>(this, name, valid_descriptors);
}
-int DescriptorArray::SearchWithCache(String* name) {
- if (number_of_descriptors() == 0) return kNotFound;
+int DescriptorArray::SearchWithCache(String* name, Map* map) {
+ int number_of_own_descriptors = map->NumberOfOwnDescriptors();
+ if (number_of_own_descriptors == 0) return kNotFound;
DescriptorLookupCache* cache = GetIsolate()->descriptor_lookup_cache();
- int number = cache->Lookup(this, name);
+ int number = cache->Lookup(map, name);
if (number == DescriptorLookupCache::kAbsent) {
- number = Search(name);
- cache->Update(this, name, number);
+ number = Search(name, number_of_own_descriptors);
+ cache->Update(map, name, number);
}
return number;
@@ -1996,7 +2011,7 @@
String* name,
LookupResult* result) {
DescriptorArray* descriptors = this->instance_descriptors();
- int number = descriptors->SearchWithCache(name);
+ int number = descriptors->SearchWithCache(name, this);
if (number == DescriptorArray::kNotFound) return result->NotFound();
result->DescriptorResult(holder, descriptors->GetDetails(number), number);
}
@@ -2040,10 +2055,9 @@
}
-void DescriptorArray::SetSortedKey(int pointer, int descriptor_number) {
- int details_index = ToDetailsIndex(pointer);
- PropertyDetails details = PropertyDetails(Smi::cast(get(details_index)));
- set_unchecked(details_index, details.set_pointer(descriptor_number).AsSmi());
+void DescriptorArray::SetSortedKey(int descriptor_index, int pointer) {
+ PropertyDetails details = GetDetails(descriptor_index);
+ set(ToDetailsIndex(descriptor_index), details.set_pointer(pointer).AsSmi());
}
@@ -2127,21 +2141,22 @@
void DescriptorArray::Append(Descriptor* desc,
const WhitenessWitness& witness,
int number_of_set_descriptors) {
- int enumeration_index = number_of_set_descriptors + 1;
+ int descriptor_number = number_of_set_descriptors;
+ int enumeration_index = descriptor_number + 1;
desc->SetEnumerationIndex(enumeration_index);
- Set(number_of_set_descriptors, desc, witness);
+ Set(descriptor_number, desc, witness);
uint32_t hash = desc->GetKey()->Hash();
int insertion;
- for (insertion = number_of_set_descriptors; insertion > 0; --insertion) {
+ for (insertion = descriptor_number; insertion > 0; --insertion) {
String* key = GetSortedKey(insertion - 1);
if (key->Hash() <= hash) break;
SetSortedKey(insertion, GetSortedKeyIndex(insertion - 1));
}
- SetSortedKey(insertion, number_of_set_descriptors);
+ SetSortedKey(insertion, descriptor_number);
}
@@ -3040,6 +3055,16 @@
}
+void Map::set_owns_descriptors(bool is_shared) {
+ set_bit_field3(OwnsDescriptors::update(bit_field3(), is_shared));
+}
+
+
+bool Map::owns_descriptors() {
+ return OwnsDescriptors::decode(bit_field3());
+}
+
+
void Code::set_flags(Code::Flags flags) {
STATIC_ASSERT(Code::NUMBER_OF_KINDS <= KindField::kMax + 1);
// Make sure that all call stubs have an arguments count.
@@ -3475,9 +3500,17 @@
}
+JSGlobalPropertyCell* Map::descriptors_pointer() {
+ ASSERT(HasTransitionArray());
+ return transitions()->descriptors_pointer();
+}
+
+
DescriptorArray* Map::instance_descriptors() {
- if (!HasTransitionArray()) return GetHeap()->empty_descriptor_array();
- return transitions()->descriptors();
+ if (HasTransitionArray()) return transitions()->descriptors();
+ Object* back_pointer = GetBackPointer();
+ if (!back_pointer->IsMap()) return GetHeap()->empty_descriptor_array();
+ return Map::cast(back_pointer)->instance_descriptors();
}
@@ -3487,29 +3520,31 @@
if (map->HasTransitionArray()) return map;
TransitionArray* transitions;
- MaybeObject* maybe_transitions = TransitionArray::Allocate(0);
+ JSGlobalPropertyCell* pointer = map->RetrieveDescriptorsPointer();
+ MaybeObject* maybe_transitions = TransitionArray::Allocate(0, pointer);
if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+
+ transitions->set_back_pointer_storage(map->GetBackPointer());
map->set_transitions(transitions);
return transitions;
}
-MaybeObject* Map::SetDescriptors(DescriptorArray* value,
- WriteBarrierMode mode) {
+MaybeObject* Map::SetDescriptors(DescriptorArray* value) {
ASSERT(!is_shared());
MaybeObject* maybe_failure = EnsureHasTransitionArray(this);
if (maybe_failure->IsFailure()) return maybe_failure;
- transitions()->set_descriptors(value, mode);
+ ASSERT(NumberOfOwnDescriptors() <= value->number_of_descriptors());
+ transitions()->set_descriptors(value);
return this;
}
MaybeObject* Map::InitializeDescriptors(DescriptorArray* descriptors) {
-#ifdef DEBUG
int len = descriptors->number_of_descriptors();
+#ifdef DEBUG
ASSERT(len <= DescriptorArray::kMaxNumberOfDescriptors);
- SLOW_ASSERT(descriptors->IsSortedNoDuplicates());
bool used_indices[DescriptorArray::kMaxNumberOfDescriptors];
for (int i = 0; i < len; ++i) used_indices[i] = false;
@@ -3528,8 +3563,7 @@
MaybeObject* maybe_failure = SetDescriptors(descriptors);
if (maybe_failure->IsFailure()) return maybe_failure;
- SetNumberOfOwnDescriptors(descriptors->number_of_descriptors());
-
+ SetNumberOfOwnDescriptors(len);
return this;
}
@@ -3598,9 +3632,21 @@
}
+JSGlobalPropertyCell* Map::RetrieveDescriptorsPointer() {
+ if (!owns_descriptors()) return NULL;
+ Object* back_pointer = GetBackPointer();
+ if (back_pointer->IsUndefined()) return NULL;
+ Map* map = Map::cast(back_pointer);
+ ASSERT(map->HasTransitionArray());
+ return map->transitions()->descriptors_pointer();
+}
+
+
MaybeObject* Map::AddTransition(String* key, Map* target) {
if (HasTransitionArray()) return transitions()->CopyInsert(key, target);
- return TransitionArray::NewWith(key, target);
+ JSGlobalPropertyCell* descriptors_pointer = RetrieveDescriptorsPointer();
+ return TransitionArray::NewWith(
+ key, target, descriptors_pointer, GetBackPointer());
}
@@ -3609,6 +3655,11 @@
}
+Map* Map::GetTransition(int transition_index) {
+ return transitions()->GetTarget(transition_index);
+}
+
+
MaybeObject* Map::set_elements_transition_map(Map* transitioned_map) {
MaybeObject* allow_elements = EnsureHasTransitionArray(this);
if (allow_elements->IsFailure()) return allow_elements;
@@ -3654,8 +3705,6 @@
void Map::set_transitions(TransitionArray* transition_array,
WriteBarrierMode mode) {
- transition_array->set_descriptors(instance_descriptors());
- transition_array->set_back_pointer_storage(GetBackPointer());
#ifdef DEBUG
if (HasTransitionArray()) {
ASSERT(transitions() != transition_array);
diff --git a/src/objects-printer.cc b/src/objects-printer.cc
index 1ba0bb0..fc0d7be 100644
--- a/src/objects-printer.cc
+++ b/src/objects-printer.cc
@@ -254,7 +254,7 @@
void JSObject::PrintProperties(FILE* out) {
if (HasFastProperties()) {
DescriptorArray* descs = map()->instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ for (int i = 0; i < map()->NumberOfOwnDescriptors(); i++) {
PrintF(out, " ");
descs->GetKey(i)->StringPrint(out);
PrintF(out, ": ");
diff --git a/src/objects.cc b/src/objects.cc
index 9bbcb22..2206e64 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -487,11 +487,20 @@
set_properties(StringDictionary::cast(dict));
return value;
}
- // Preserve enumeration index.
+
+ PropertyDetails original_details = property_dictionary()->DetailsAt(entry);
+ int enumeration_index;
+ // Preserve the enumeration index unless the property was deleted.
+ if (original_details.IsDeleted()) {
+ enumeration_index = property_dictionary()->NextEnumerationIndex();
+ property_dictionary()->SetNextEnumerationIndex(enumeration_index + 1);
+ } else {
+ enumeration_index = original_details.dictionary_index();
+ ASSERT(enumeration_index > 0);
+ }
+
details = PropertyDetails(
- details.attributes(),
- details.type(),
- property_dictionary()->DetailsAt(entry).dictionary_index());
+ details.attributes(), details.type(), enumeration_index);
if (IsGlobalObject()) {
JSGlobalPropertyCell* cell =
@@ -1532,8 +1541,9 @@
PropertyAttributes attributes,
StoreFromKeyed store_mode) {
ASSERT(!IsJSGlobalProxy());
- ASSERT(map()->instance_descriptors()->Search(name) ==
- DescriptorArray::kNotFound);
+ ASSERT(DescriptorArray::kNotFound ==
+ map()->instance_descriptors()->Search(
+ name, map()->NumberOfOwnDescriptors()));
// Normalize the object if the name is an actual string (not the
// hidden symbols) and is not a real identifier.
@@ -1753,8 +1763,32 @@
Object* new_value,
PropertyAttributes attributes) {
Map* old_map = map();
+ Map* old_target = old_map->GetTransition(transition_index);
Object* result;
+ // To sever a transition to a map with which the descriptors are shared, the
+ // larger map (more descriptors) needs to store its own descriptors array.
+ // Both sides of the severed chain need to have their own descriptors pointer
+ // to store distinct descriptor arrays.
+
+ // If the old_target did not yet store its own descriptors, the new
+ // descriptors pointer is created for the old_target by temporarily clearing
+ // the back pointer and setting its descriptor array.
+
+ // This phase is executed before creating the new map since it requires
+ // allocation that may fail.
+ if (!old_target->StoresOwnDescriptors()) {
+ DescriptorArray* old_descriptors = old_map->instance_descriptors();
+
+ old_target->SetBackPointer(GetHeap()->undefined_value());
+ MaybeObject* maybe_failure = old_target->SetDescriptors(old_descriptors);
+ // Reset the backpointer before returning failure, otherwise the map ends up
+ // with an undefined backpointer and no descriptors, losing its own
+ // descriptors. Setting the backpointer always succeeds.
+ old_target->SetBackPointer(old_map);
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ }
+
MaybeObject* maybe_result =
ConvertDescriptorToField(name, new_value, attributes);
if (!maybe_result->To(&result)) return maybe_result;
@@ -1763,13 +1797,48 @@
// This method should only be used to convert existing transitions. Objects
// with the map of "new Object()" cannot have transitions in the first place.
- ASSERT(map() != GetIsolate()->empty_object_map());
+ Map* new_map = map();
+ ASSERT(new_map != GetIsolate()->empty_object_map());
// TODO(verwaest): From here on we lose existing map transitions, causing
// invalid back pointers. This will change once we can store multiple
// transitions with the same key.
- old_map->SetTransition(transition_index, map());
- map()->SetBackPointer(old_map);
+
+ if (old_map->owns_descriptors()) {
+ // If the old map owns its own descriptors, transfer ownership to the
+ // new_map and install its descriptors in the old_map. Since the old_map
+ // stores the descriptors for the new_map, remove the transition array of
+ // the new_map that is only in place to store the descriptors.
+ old_map->transitions()->descriptors_pointer()->set_value(
+ new_map->instance_descriptors());
+ new_map->ClearTransitions(GetHeap());
+ old_map->set_owns_descriptors(false);
+ } else if (old_target->instance_descriptors() ==
+ old_map->instance_descriptors()) {
+ // Since the conversion above generated a new fast map with an additional
+ // property which can be shared as well, install this descriptor pointer
+ // along the entire chain of smaller maps; and remove the transition array
+ // that is only in place to hold the descriptor array in the new map.
+ Map* map;
+ JSGlobalPropertyCell* new_pointer =
+ new_map->transitions()->descriptors_pointer();
+ JSGlobalPropertyCell* old_pointer =
+ old_map->transitions()->descriptors_pointer();
+ for (Object* current = old_map;
+ !current->IsUndefined();
+ current = map->GetBackPointer()) {
+ map = Map::cast(current);
+ if (!map->HasTransitionArray()) break;
+ TransitionArray* transitions = map->transitions();
+ if (transitions->descriptors_pointer() != old_pointer) break;
+ map->SetEnumLength(Map::kInvalidEnumCache);
+ transitions->set_descriptors_pointer(new_pointer);
+ }
+ new_map->ClearTransitions(GetHeap());
+ }
+
+ old_map->SetTransition(transition_index, new_map);
+ new_map->SetBackPointer(old_map);
return result;
}
@@ -2145,6 +2214,7 @@
v8::NeanderArray callbacks(descriptors);
int nof_callbacks = callbacks.length();
int descriptor_count = array->number_of_descriptors();
+ ASSERT(descriptor_count == map->NumberOfOwnDescriptors());
// Ensure the keys are symbols before writing them into the instance
// descriptor. Since it may cause a GC, it has to be done before we
@@ -2164,10 +2234,8 @@
DescriptorArray::WhitenessWitness witness(*result);
// Copy the descriptors from the array.
- if (0 < descriptor_count) {
- for (int i = 0; i < descriptor_count; i++) {
- result->CopyFrom(i, *array, i, witness);
- }
+ for (int i = 0; i < descriptor_count; i++) {
+ result->CopyFrom(i, *array, i, witness);
}
// After this point the GC is not allowed to run anymore until the map is in a
@@ -2178,26 +2246,28 @@
// Fill in new callback descriptors. Process the callbacks from
// back to front so that the last callback with a given name takes
// precedence over previously added callbacks with that name.
+ int nof = descriptor_count;
for (int i = nof_callbacks - 1; i >= 0; i--) {
AccessorInfo* entry = AccessorInfo::cast(callbacks.get(i));
String* key = String::cast(entry->name());
// Check if a descriptor with this name already exists before writing.
- if (LinearSearch(*result, key, map->NumberOfOwnDescriptors()) ==
- DescriptorArray::kNotFound) {
+ if (result->Search(key, nof) == DescriptorArray::kNotFound) {
CallbacksDescriptor desc(key, entry, entry->property_attributes());
map->AppendDescriptor(&desc, witness);
+ nof += 1;
}
}
- int new_number_of_descriptors = map->NumberOfOwnDescriptors();
+ ASSERT(nof == map->NumberOfOwnDescriptors());
+
// Reinstall the original descriptor array if no new elements were added.
- if (new_number_of_descriptors == descriptor_count) {
+ if (nof == descriptor_count) {
Map::SetDescriptors(map, array);
return;
}
// If duplicates were detected, trim the descriptor array to the right size.
- int new_array_size = DescriptorArray::LengthFor(new_number_of_descriptors);
+ int new_array_size = DescriptorArray::LengthFor(nof);
if (new_array_size < result->length()) {
RightTrimFixedArray<FROM_MUTATOR>(
isolate->heap(), *result, result->length() - new_array_size);
@@ -3264,19 +3334,20 @@
Map* map_of_this = map();
// Allocate new content.
- int property_count = map_of_this->NumberOfDescribedProperties();
+ int real_size = map_of_this->NumberOfOwnDescriptors();
+ int property_count =
+ map_of_this->NumberOfDescribedProperties(OWN_DESCRIPTORS);
if (expected_additional_properties > 0) {
property_count += expected_additional_properties;
} else {
property_count += 2; // Make space for two more properties.
}
StringDictionary* dictionary;
- { MaybeObject* maybe_dictionary = StringDictionary::Allocate(property_count);
- if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
- }
+ MaybeObject* maybe_dictionary = StringDictionary::Allocate(property_count);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
DescriptorArray* descs = map_of_this->instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ for (int i = 0; i < real_size; i++) {
PropertyDetails details = descs->GetDetails(i);
switch (details.type()) {
case CONSTANT_FUNCTION: {
@@ -3321,8 +3392,7 @@
Heap* current_heap = GetHeap();
// Copy the next enumeration index from instance descriptor.
- int index = map_of_this->instance_descriptors()->NextEnumerationIndex();
- dictionary->SetNextEnumerationIndex(index);
+ dictionary->SetNextEnumerationIndex(real_size + 1);
Map* new_map;
MaybeObject* maybe_map =
@@ -3668,10 +3738,11 @@
DescriptorArray* descriptors = this->map()->instance_descriptors();
if (descriptors->number_of_descriptors() > 0) {
int sorted_index = descriptors->GetSortedKeyIndex(0);
- if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol()) {
+ if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol() &&
+ sorted_index < map()->NumberOfOwnDescriptors()) {
ASSERT(descriptors->GetType(sorted_index) == FIELD);
- inline_value = this->FastPropertyAt(
- descriptors->GetFieldIndex(sorted_index));
+ inline_value =
+ this->FastPropertyAt(descriptors->GetFieldIndex(sorted_index));
} else {
inline_value = GetHeap()->undefined_value();
}
@@ -3736,7 +3807,8 @@
DescriptorArray* descriptors = this->map()->instance_descriptors();
if (descriptors->number_of_descriptors() > 0) {
int sorted_index = descriptors->GetSortedKeyIndex(0);
- if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol()) {
+ if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol() &&
+ sorted_index < map()->NumberOfOwnDescriptors()) {
ASSERT(descriptors->GetType(sorted_index) == FIELD);
this->FastPropertyAtPut(descriptors->GetFieldIndex(sorted_index),
value);
@@ -4178,14 +4250,15 @@
}
-int Map::NumberOfDescribedProperties(PropertyAttributes filter) {
+int Map::NumberOfDescribedProperties(DescriptorFlag which,
+ PropertyAttributes filter) {
int result = 0;
DescriptorArray* descs = instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
- PropertyDetails details = descs->GetDetails(i);
- if ((details.attributes() & filter) == 0) {
- result++;
- }
+ int limit = which == ALL_DESCRIPTORS
+ ? descs->number_of_descriptors()
+ : NumberOfOwnDescriptors();
+ for (int i = 0; i < limit; i++) {
+ if ((descs->GetDetails(i).attributes() & filter) == 0) result++;
}
return result;
}
@@ -4193,7 +4266,8 @@
int Map::PropertyIndexFor(String* name) {
DescriptorArray* descs = instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ int limit = NumberOfOwnDescriptors();
+ for (int i = 0; i < limit; i++) {
if (name->Equals(descs->GetKey(i))) return descs->GetFieldIndex(i);
}
return -1;
@@ -4202,8 +4276,9 @@
int Map::NextFreePropertyIndex() {
int max_index = -1;
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
DescriptorArray* descs = instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ for (int i = 0; i < number_of_own_descriptors; i++) {
if (descs->GetType(i) == FIELD) {
int current_index = descs->GetFieldIndex(i);
if (current_index > max_index) max_index = current_index;
@@ -4215,8 +4290,9 @@
AccessorDescriptor* Map::FindAccessor(String* name) {
DescriptorArray* descs = instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
- if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+ for (int i = 0; i < number_of_own_descriptors; i++) {
+ if (descs->GetType(i) == CALLBACKS && name->Equals(descs->GetKey(i))) {
return descs->GetCallbacks(i);
}
}
@@ -4640,9 +4716,10 @@
if (result.IsFound()) {
Map* target = result.GetTransitionTarget();
- ASSERT(target->instance_descriptors()->number_of_descriptors() ==
- map()->instance_descriptors()->number_of_descriptors());
- ASSERT(target->instance_descriptors()->GetKey(descriptor_number) == name);
+ ASSERT(target->NumberOfOwnDescriptors() ==
+ map()->NumberOfOwnDescriptors());
+ // This works since descriptors are sorted in order of addition.
+ ASSERT(map()->instance_descriptors()->GetKey(descriptor_number) == name);
return TryAccessorTransition(
this, target, descriptor_number, component, accessor, attributes);
}
@@ -4825,8 +4902,9 @@
Object* JSObject::SlowReverseLookup(Object* value) {
if (HasFastProperties()) {
+ int number_of_own_descriptors = map()->NumberOfOwnDescriptors();
DescriptorArray* descs = map()->instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ for (int i = 0; i < number_of_own_descriptors; i++) {
if (descs->GetType(i) == FIELD) {
if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
return descs->GetKey(i);
@@ -4855,8 +4933,11 @@
result->set_bit_field(bit_field());
result->set_bit_field2(bit_field2());
result->set_bit_field3(bit_field3());
- result->SetNumberOfOwnDescriptors(0);
- result->SetEnumLength(kInvalidEnumCache);
+ int new_bit_field3 = bit_field3();
+ new_bit_field3 = OwnsDescriptors::update(new_bit_field3, true);
+ new_bit_field3 = NumberOfOwnDescriptorsBits::update(new_bit_field3, 0);
+ new_bit_field3 = EnumLengthBits::update(new_bit_field3, kInvalidEnumCache);
+ result->set_bit_field3(new_bit_field3);
return result;
}
@@ -4906,14 +4987,71 @@
}
-MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors,
- String* name,
- TransitionFlag flag) {
+MaybeObject* Map::ShareDescriptor(Descriptor* descriptor) {
+ // Sanity check. This path is only to be taken if the map owns its descriptor
+ // array, implying that its NumberOfOwnDescriptors equals the number of
+ // descriptors in the descriptor array.
+ if (NumberOfOwnDescriptors() !=
+ instance_descriptors()->number_of_descriptors()) {
+ Isolate::Current()->PushStackTraceAndDie(
+ 0xDEAD0002, GetBackPointer(), this, 0xDEAD0003);
+ }
Map* result;
MaybeObject* maybe_result = CopyDropDescriptors();
if (!maybe_result->To(&result)) return maybe_result;
- if (descriptors->number_of_descriptors() != 0) {
+ String* name = descriptor->GetKey();
+
+ TransitionArray* transitions;
+ MaybeObject* maybe_transitions = AddTransition(name, result);
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+
+ DescriptorArray* descriptors = instance_descriptors();
+ int old_size = descriptors->number_of_descriptors();
+
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(old_size + 1);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+ DescriptorArray::WhitenessWitness witness(new_descriptors);
+
+ for (int i = 0; i < old_size; ++i) {
+ new_descriptors->CopyFrom(i, descriptors, i, witness);
+ }
+ new_descriptors->Append(descriptor, witness, old_size);
+
+ // If the source descriptors had an enum cache we copy it. This ensures that
+ // the maps to which we push the new descriptor array back can rely on a
+ // cache always being available once it is set. If the map has more
+ // enumerated descriptors than available in the original cache, the cache
+ // will be lazily replaced by the extended cache when needed.
+ if (descriptors->HasEnumCache()) {
+ new_descriptors->CopyEnumCacheFrom(descriptors);
+ }
+
+ transitions->set_descriptors(new_descriptors);
+ set_transitions(transitions);
+ result->SetBackPointer(this);
+ set_owns_descriptors(false);
+
+ result->SetNumberOfOwnDescriptors(new_descriptors->number_of_descriptors());
+ ASSERT(result->NumberOfOwnDescriptors() == NumberOfOwnDescriptors() + 1);
+
+ return result;
+}
+
+
+MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors,
+ String* name,
+ TransitionFlag flag) {
+ ASSERT(descriptors->IsSortedNoDuplicates());
+
+ Map* result;
+ MaybeObject* maybe_result = CopyDropDescriptors();
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ // Unless we are creating a map with no descriptors and no back pointer, we
+ // insert the descriptor array locally.
+ if (!descriptors->IsEmpty()) {
MaybeObject* maybe_failure = result->SetDescriptors(descriptors);
if (maybe_failure->IsFailure()) return maybe_failure;
result->SetNumberOfOwnDescriptors(descriptors->number_of_descriptors());
@@ -4924,6 +5062,23 @@
MaybeObject* maybe_transitions = AddTransition(name, result);
if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+ if (descriptors->IsEmpty()) {
+ if (owns_descriptors()) {
+ // If the copied map has no added fields, and the parent map owns its
+ // descriptors, those descriptors have to be empty. In that case,
+ // transfer ownership of the descriptors to the new child.
+ CHECK(instance_descriptors()->IsEmpty());
+ set_owns_descriptors(false);
+ } else {
+ // If the parent did not own its own descriptors, it may share a larger
+ // descriptors array already. In that case, force a split by setting
+ // the descriptor array of the new map to the empty descriptor array.
+ MaybeObject* maybe_failure =
+ result->SetDescriptors(GetHeap()->empty_descriptor_array());
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ }
+ }
+
set_transitions(transitions);
result->SetBackPointer(this);
}
@@ -4933,12 +5088,6 @@
MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) {
- // Create a new free-floating map only if we are not allowed to store it.
- Map* new_map = NULL;
- MaybeObject* maybe_new_map = Copy();
- if (!maybe_new_map->To(&new_map)) return maybe_new_map;
- new_map->set_elements_kind(kind);
-
if (flag == INSERT_TRANSITION) {
ASSERT(!HasElementsTransition() ||
((elements_transition_map()->elements_kind() == DICTIONARY_ELEMENTS ||
@@ -4949,7 +5098,46 @@
ASSERT(!IsFastElementsKind(kind) ||
IsMoreGeneralElementsKindTransition(elements_kind(), kind));
ASSERT(kind != elements_kind());
+ }
+ if (flag == INSERT_TRANSITION && owns_descriptors()) {
+ // In case the map owned its own descriptors, share the descriptors and
+ // transfer ownership to the new map.
+ Map* new_map;
+ MaybeObject* maybe_new_map = CopyDropDescriptors();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ MaybeObject* added_elements = set_elements_transition_map(new_map);
+ if (added_elements->IsFailure()) return added_elements;
+
+ new_map->set_elements_kind(kind);
+ new_map->SetBackPointer(this);
+ new_map->SetNumberOfOwnDescriptors(NumberOfOwnDescriptors());
+ set_owns_descriptors(false);
+ return new_map;
+ }
+
+ // In case the map did not own its own descriptors, a split is forced by
+ // copying the map; creating a new descriptor array cell.
+ // Create a new free-floating map only if we are not allowed to store it.
+ Map* new_map;
+ MaybeObject* maybe_new_map = Copy();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ ASSERT(new_map->NumberOfOwnDescriptors() == NumberOfOwnDescriptors());
+ new_map->set_elements_kind(kind);
+
+ if (flag == INSERT_TRANSITION) {
+ // Map::Copy does not store the descriptor array in case it is empty, since
+ // it does not insert a back pointer; implicitly indicating that its
+ // descriptor array is empty. Since in this case we do want to insert a back
+ // pointer, we have to manually set the empty descriptor array to force a
+ // split.
+ if (!new_map->StoresOwnDescriptors()) {
+ ASSERT(new_map->NumberOfOwnDescriptors() == 0);
+ MaybeObject* maybe_failure =
+ new_map->SetDescriptors(GetHeap()->empty_descriptor_array());
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ }
MaybeObject* added_elements = set_elements_transition_map(new_map);
if (added_elements->IsFailure()) return added_elements;
@@ -4970,13 +5158,25 @@
Map* map = ctor->initial_map();
DescriptorArray* descriptors = map->instance_descriptors();
- return CopyReplaceDescriptors(descriptors, NULL, OMIT_TRANSITION);
+ int number_of_own_descriptors = map->NumberOfOwnDescriptors();
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors =
+ descriptors->CopyUpTo(number_of_own_descriptors);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION);
}
MaybeObject* Map::Copy() {
DescriptorArray* descriptors = instance_descriptors();
- return CopyReplaceDescriptors(descriptors, NULL, OMIT_TRANSITION);
+ DescriptorArray* new_descriptors;
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+ MaybeObject* maybe_descriptors =
+ descriptors->CopyUpTo(number_of_own_descriptors);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION);
}
@@ -4988,14 +5188,18 @@
MaybeObject* maybe_failure = descriptor->KeyToSymbol();
if (maybe_failure->IsFailure()) return maybe_failure;
- String* key = descriptor->GetKey();
- ASSERT(descriptors->Search(key) == DescriptorArray::kNotFound);
-
- int old_size = descriptors->number_of_descriptors();
+ int old_size = NumberOfOwnDescriptors();
int new_size = old_size + 1;
+ descriptor->SetEnumerationIndex(new_size);
+
+ if (flag == INSERT_TRANSITION &&
+ owns_descriptors() &&
+ CanHaveMoreTransitions()) {
+ return ShareDescriptor(descriptor);
+ }
DescriptorArray* new_descriptors;
- MaybeObject* maybe_descriptors = DescriptorArray::Allocate(new_size);
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(old_size + 1);
if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
DescriptorArray::WhitenessWitness witness(new_descriptors);
@@ -5005,11 +5209,10 @@
new_descriptors->CopyFrom(i, descriptors, i, witness);
}
- new_descriptors->Append(descriptor, witness, old_size);
+ new_descriptors->Set(old_size, descriptor, witness);
+ new_descriptors->Sort();
- SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
-
- return CopyReplaceDescriptors(new_descriptors, key, flag);
+ return CopyReplaceDescriptors(new_descriptors, descriptor->GetKey(), flag);
}
@@ -5022,7 +5225,7 @@
if (maybe_result->IsFailure()) return maybe_result;
// We replace the key if it is already present.
- int index = old_descriptors->SearchWithCache(descriptor->GetKey());
+ int index = old_descriptors->SearchWithCache(descriptor->GetKey(), this);
if (index != DescriptorArray::kNotFound) {
return CopyReplaceDescriptor(descriptor, index, flag);
}
@@ -5030,39 +5233,60 @@
}
+MaybeObject* DescriptorArray::CopyUpTo(int enumeration_index) {
+ if (enumeration_index == 0) return GetHeap()->empty_descriptor_array();
+
+ int size = enumeration_index;
+
+ DescriptorArray* descriptors;
+ MaybeObject* maybe_descriptors = Allocate(size);
+ if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors;
+ DescriptorArray::WhitenessWitness witness(descriptors);
+
+ for (int i = 0; i < size; ++i) {
+ descriptors->CopyFrom(i, this, i, witness);
+ }
+
+ if (number_of_descriptors() != enumeration_index) descriptors->Sort();
+
+ return descriptors;
+}
+
+
MaybeObject* Map::CopyReplaceDescriptor(Descriptor* descriptor,
int insertion_index,
TransitionFlag flag) {
- DescriptorArray* descriptors = instance_descriptors();
- int size = descriptors->number_of_descriptors();
- ASSERT(0 <= insertion_index && insertion_index < size);
-
// Ensure the key is a symbol.
MaybeObject* maybe_failure = descriptor->KeyToSymbol();
if (maybe_failure->IsFailure()) return maybe_failure;
+ DescriptorArray* descriptors = instance_descriptors();
+
String* key = descriptor->GetKey();
ASSERT(key == descriptors->GetKey(insertion_index));
- DescriptorArray* new_descriptors;
- MaybeObject* maybe_descriptors = DescriptorArray::Allocate(size);
- if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+ int new_size = NumberOfOwnDescriptors();
+ ASSERT(0 <= insertion_index && insertion_index < new_size);
+ PropertyDetails details = descriptors->GetDetails(insertion_index);
+ ASSERT_LE(details.descriptor_index(), new_size);
+ descriptor->SetEnumerationIndex(details.descriptor_index());
+
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(new_size);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
DescriptorArray::WhitenessWitness witness(new_descriptors);
- // Copy the descriptors, replacing a descriptor.
- for (int index = 0; index < size; ++index) {
- if (index == insertion_index) continue;
- new_descriptors->CopyFrom(index, descriptors, index, witness);
+ for (int i = 0; i < new_size; ++i) {
+ if (i == insertion_index) {
+ new_descriptors->Set(i, descriptor, witness);
+ } else {
+ new_descriptors->CopyFrom(i, descriptors, i, witness);
+ }
}
- PropertyDetails original_details = descriptors->GetDetails(insertion_index);
- descriptor->SetEnumerationIndex(original_details.descriptor_index());
- descriptor->SetSortedKey(original_details.pointer());
-
- new_descriptors->Set(insertion_index, descriptor, witness);
-
- SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
+ // Re-sort if descriptors were removed.
+ if (new_size != descriptors->length()) new_descriptors->Sort();
return CopyReplaceDescriptors(new_descriptors, key, flag);
}
@@ -5854,18 +6078,24 @@
}
+void DescriptorArray::ClearEnumCache() {
+ set(kEnumCacheIndex, Smi::FromInt(0));
+}
+
+
void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
FixedArray* new_cache,
Object* new_index_cache) {
ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
ASSERT(new_index_cache->IsSmi() || new_index_cache->IsFixedArray());
if (HasEnumCache()) {
+ ASSERT(new_cache->length() > FixedArray::cast(GetEnumCache())->length());
FixedArray::cast(get(kEnumCacheIndex))->
set(kEnumCacheBridgeCacheIndex, new_cache);
FixedArray::cast(get(kEnumCacheIndex))->
set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache);
} else {
- if (IsEmpty()) return; // Do nothing for empty descriptor array.
+ ASSERT(!IsEmpty());
FixedArray::cast(bridge_storage)->
set(kEnumCacheBridgeCacheIndex, new_cache);
FixedArray::cast(bridge_storage)->
@@ -5941,6 +6171,7 @@
parent_index = child_index;
}
}
+ ASSERT(IsSortedNoDuplicates());
}
@@ -7188,11 +7419,9 @@
// Clear a possible back pointer in case the transition leads to a dead map.
// Return true in case a back pointer has been cleared and false otherwise.
-static bool ClearBackPointer(Heap* heap, Object* target) {
- ASSERT(target->IsMap());
- Map* map = Map::cast(target);
- if (Marking::MarkBitFrom(map).Get()) return false;
- map->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER);
+static bool ClearBackPointer(Heap* heap, Map* target) {
+ if (Marking::MarkBitFrom(target).Get()) return false;
+ target->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER);
return true;
}
@@ -7211,9 +7440,21 @@
int transition_index = 0;
+ DescriptorArray* descriptors = t->descriptors();
+ bool descriptors_owner_died = false;
+
// Compact all live descriptors to the left.
for (int i = 0; i < t->number_of_transitions(); ++i) {
- if (!ClearBackPointer(heap, t->GetTarget(i))) {
+ Map* target = t->GetTarget(i);
+ if (ClearBackPointer(heap, target)) {
+ ASSERT(!Marking::IsGrey(Marking::MarkBitFrom(target)));
+ DescriptorArray* target_descriptors = target->instance_descriptors();
+ if ((target_descriptors->number_of_descriptors() == 0 &&
+ target->NumberOfOwnDescriptors() > 0) ||
+ target_descriptors == descriptors) {
+ descriptors_owner_died = true;
+ }
+ } else {
if (i != transition_index) {
String* key = t->GetKey(i);
t->SetKey(transition_index, key);
@@ -7228,6 +7469,9 @@
if (t->HasElementsTransition() &&
ClearBackPointer(heap, t->elements_transition())) {
+ if (t->elements_transition()->instance_descriptors() == descriptors) {
+ descriptors_owner_died = true;
+ }
t->ClearElementsTransition();
} else {
// If there are no transitions to be cleared, return.
@@ -7236,12 +7480,46 @@
if (transition_index == t->number_of_transitions()) return;
}
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+
+ if (descriptors_owner_died) {
+ if (number_of_own_descriptors > 0) {
+ int number_of_descriptors = descriptors->number_of_descriptors();
+ int to_trim = number_of_descriptors - number_of_own_descriptors;
+ if (to_trim > 0) {
+ RightTrimFixedArray<FROM_GC>(
+ heap, descriptors, to_trim * DescriptorArray::kDescriptorSize);
+ if (descriptors->HasEnumCache()) {
+ int live_enum =
+ NumberOfDescribedProperties(OWN_DESCRIPTORS, DONT_ENUM);
+ if (live_enum == 0) {
+ descriptors->ClearEnumCache();
+ } else {
+ FixedArray* enum_cache =
+ FixedArray::cast(descriptors->GetEnumCache());
+ to_trim = enum_cache->length() - live_enum;
+ if (to_trim > 0) {
+ RightTrimFixedArray<FROM_GC>(
+ heap, FixedArray::cast(descriptors->GetEnumCache()), to_trim);
+ }
+ }
+ }
+ descriptors->Sort();
+ }
+ ASSERT(descriptors->number_of_descriptors() == number_of_own_descriptors);
+ } else {
+ t->set_descriptors(heap->empty_descriptor_array());
+ }
+ set_owns_descriptors(true);
+ }
+
// If the final transition array does not contain any live transitions, remove
// the transition array from the map.
if (transition_index == 0 &&
!t->HasElementsTransition() &&
!t->HasPrototypeTransitions() &&
- t->descriptors()->IsEmpty()) {
+ number_of_own_descriptors == 0) {
+ ASSERT(owns_descriptors());
return ClearTransitions(heap);
}
@@ -10366,7 +10644,7 @@
int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
return HasFastProperties() ?
- map()->NumberOfDescribedProperties(filter) :
+ map()->NumberOfDescribedProperties(OWN_DESCRIPTORS, filter) :
property_dictionary()->NumberOfElementsFilterAttributes(filter);
}
@@ -10490,9 +10768,10 @@
void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
ASSERT(storage->length() >= (NumberOfLocalProperties() - index));
if (HasFastProperties()) {
+ int real_size = map()->NumberOfOwnDescriptors();
DescriptorArray* descs = map()->instance_descriptors();
- ASSERT(storage->length() >= index + descs->number_of_descriptors());
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ ASSERT(storage->length() >= index + real_size);
+ for (int i = 0; i < real_size; i++) {
storage->set(index + i, descs->GetKey(i));
}
} else {
@@ -12195,8 +12474,8 @@
int pos = 0;
for (int i = 0; i < capacity; i++) {
if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
- enumeration_order->set(
- pos++, Smi::FromInt(DetailsAt(i).dictionary_index()));
+ int index = DetailsAt(i).dictionary_index();
+ enumeration_order->set(pos++, Smi::FromInt(index));
}
}
@@ -12325,7 +12604,8 @@
uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
// Insert element at empty or deleted entry
if (!details.IsDeleted() &&
- details.dictionary_index() == 0 && Shape::kIsEnumerable) {
+ details.dictionary_index() == 0 &&
+ Shape::kIsEnumerable) {
// Assign an enumeration index to the property and update
// SetNextEnumerationIndex.
int index = NextEnumerationIndex();
diff --git a/src/objects.h b/src/objects.h
index 04cf539..c222086 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -177,6 +177,13 @@
OMIT_TRANSITION
};
+// Indicates whether we are only interested in the descriptors of a particular
+// map, or in all descriptors in the descriptor array.
+enum DescriptorFlag {
+ ALL_DESCRIPTORS,
+ OWN_DESCRIPTORS
+};
+
// Instance size sentinel for objects of variable size.
const int kVariableSizeSentinel = 0;
@@ -2481,12 +2488,15 @@
}
inline int number_of_entries() { return number_of_descriptors(); }
- inline int NextEnumerationIndex() { return number_of_descriptors() + 1; }
bool HasEnumCache() {
return !IsEmpty() && !get(kEnumCacheIndex)->IsSmi();
}
+ void CopyEnumCacheFrom(DescriptorArray* array) {
+ set(kEnumCacheIndex, array->get(kEnumCacheIndex));
+ }
+
Object* GetEnumCache() {
ASSERT(HasEnumCache());
FixedArray* bridge = FixedArray::cast(get(kEnumCacheIndex));
@@ -2499,6 +2509,8 @@
kEnumCacheOffset);
}
+ void ClearEnumCache();
+
// Initialize or change the enum cache,
// using the supplied storage for the small "bridge".
void SetEnumCache(FixedArray* bridge_storage,
@@ -2541,19 +2553,17 @@
int src_index,
const WhitenessWitness&);
+ MUST_USE_RESULT MaybeObject* CopyUpTo(int enumeration_index);
+
// Sort the instance descriptors by the hash codes of their keys.
void Sort();
- inline void SwapSortedKeys(int first, int second);
// Search the instance descriptors for given name.
- INLINE(int Search(String* name));
+ INLINE(int Search(String* name, int number_of_own_descriptors));
// As the above, but uses DescriptorLookupCache and updates it when
// necessary.
- INLINE(int SearchWithCache(String* name));
-
- // Tells whether the name is present int the array.
- bool Contains(String* name) { return kNotFound != Search(name); }
+ INLINE(int SearchWithCache(String* name, Map* map));
// Allocates a DescriptorArray, but returns the singleton
// empty descriptor array object if number_of_descriptors is 0.
@@ -2596,7 +2606,7 @@
#ifdef DEBUG
// Is the descriptor array sorted and without duplicates?
- bool IsSortedNoDuplicates();
+ bool IsSortedNoDuplicates(int valid_descriptors = -1);
// Is the descriptor array consistent with the back pointers in targets?
bool IsConsistentWithBackPointers(Map* current_map);
@@ -2649,24 +2659,21 @@
kDescriptorValue;
}
- // Swap operation on FixedArray without using write barriers.
- static inline void NoIncrementalWriteBarrierSwap(
- FixedArray* array, int first, int second);
-
// Swap first and second descriptor.
- inline void NoIncrementalWriteBarrierSwapDescriptors(
- int first, int second);
+ inline void SwapSortedKeys(int first, int second);
DISALLOW_IMPLICIT_CONSTRUCTORS(DescriptorArray);
};
-template<typename T>
-inline int LinearSearch(T* array, String* name, int len);
+enum SearchMode { ALL_ENTRIES, VALID_ENTRIES };
+
+template<SearchMode search_mode, typename T>
+inline int LinearSearch(T* array, String* name, int len, int valid_entries);
-template<typename T>
-inline int Search(T* array, String* name);
+template<SearchMode search_mode, typename T>
+inline int Search(T* array, String* name, int valid_entries = 0);
// HashTable is a subclass of FixedArray that implements a hash table
@@ -4672,6 +4679,7 @@
class IsShared: public BitField<bool, 22, 1> {};
class FunctionWithPrototype: public BitField<bool, 23, 1> {};
class DictionaryMap: public BitField<bool, 24, 1> {};
+ class OwnsDescriptors: public BitField<bool, 25, 1> {};
// Tells whether the object in the prototype property will be used
// for instances created from this function. If the prototype
@@ -4792,12 +4800,14 @@
static bool IsValidElementsTransition(ElementsKind from_kind,
ElementsKind to_kind);
+ bool StoresOwnDescriptors() { return HasTransitionArray(); }
inline bool HasTransitionArray();
inline bool HasElementsTransition();
inline Map* elements_transition_map();
MUST_USE_RESULT inline MaybeObject* set_elements_transition_map(
Map* transitioned_map);
- inline void SetTransition(int index, Map* target);
+ inline void SetTransition(int transition_index, Map* target);
+ inline Map* GetTransition(int transition_index);
MUST_USE_RESULT inline MaybeObject* AddTransition(String* key, Map* target);
DECL_ACCESSORS(transitions, TransitionArray)
inline void ClearTransitions(Heap* heap,
@@ -4837,9 +4847,9 @@
// [instance descriptors]: describes the object.
inline DescriptorArray* instance_descriptors();
+ inline JSGlobalPropertyCell* descriptors_pointer();
MUST_USE_RESULT inline MaybeObject* SetDescriptors(
- DescriptorArray* descriptors,
- WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+ DescriptorArray* descriptors);
static void SetDescriptors(Handle<Map> map,
Handle<DescriptorArray> descriptors);
MUST_USE_RESULT inline MaybeObject* InitializeDescriptors(
@@ -4920,17 +4930,29 @@
}
void SetNumberOfOwnDescriptors(int number) {
+ ASSERT(number <= instance_descriptors()->number_of_descriptors());
set_bit_field3(NumberOfOwnDescriptorsBits::update(bit_field3(), number));
}
+ inline JSGlobalPropertyCell* RetrieveDescriptorsPointer();
+
int EnumLength() {
return EnumLengthBits::decode(bit_field3());
}
- void SetEnumLength(int index) {
- set_bit_field3(EnumLengthBits::update(bit_field3(), index));
+ void SetEnumLength(int length) {
+ if (length != kInvalidEnumCache) {
+ ASSERT(length >= 0);
+ ASSERT(length == 0 || instance_descriptors()->HasEnumCache());
+ ASSERT(length <= NumberOfOwnDescriptors());
+ }
+ set_bit_field3(EnumLengthBits::update(bit_field3(), length));
}
+
+ inline bool owns_descriptors();
+ inline void set_owns_descriptors(bool is_shared);
+
MUST_USE_RESULT MaybeObject* RawCopy(int instance_size);
MUST_USE_RESULT MaybeObject* CopyWithPreallocatedFieldDescriptors();
MUST_USE_RESULT MaybeObject* CopyDropDescriptors();
@@ -4938,6 +4960,7 @@
DescriptorArray* descriptors,
String* name,
TransitionFlag flag);
+ MUST_USE_RESULT MaybeObject* ShareDescriptor(Descriptor* descriptor);
MUST_USE_RESULT MaybeObject* CopyAddDescriptor(Descriptor* descriptor,
TransitionFlag flag);
MUST_USE_RESULT MaybeObject* CopyInsertDescriptor(Descriptor* descriptor,
@@ -4966,7 +4989,8 @@
// Returns the number of properties described in instance_descriptors
// filtering out properties with the specified attributes.
- int NumberOfDescribedProperties(PropertyAttributes filter = NONE);
+ int NumberOfDescribedProperties(DescriptorFlag which = OWN_DESCRIPTORS,
+ PropertyAttributes filter = NONE);
// Casting.
static inline Map* cast(Object* obj);
@@ -5070,7 +5094,7 @@
static const int kMaxPreAllocatedPropertyFields = 255;
- // Constant for denoting that the Enum Cache field was not yet used.
+ // Constant for denoting that the enum cache is not yet initialized.
static const int kInvalidEnumCache = EnumLengthBits::kMax;
// Layout description.
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index c3b7622..64abc7d 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -2009,20 +2009,33 @@
Map::kConstructorOffset);
if (map->HasTransitionArray()) {
TransitionArray* transitions = map->transitions();
- if (!transitions->descriptors()->IsEmpty()) {
- DescriptorArray* descriptors = transitions->descriptors();
- TagObject(descriptors, "(map descriptors)");
- SetInternalReference(transitions, entry,
- "descriptors", descriptors,
- TransitionArray::kDescriptorsOffset);
- IndexedReferencesExtractor refs_extractor(
- this, transitions, entry);
- transitions->Iterate(&refs_extractor);
- }
+ JSGlobalPropertyCell* pointer = transitions->descriptors_pointer();
+ DescriptorArray* descriptors = transitions->descriptors();
+ TagObject(descriptors, "(map descriptors)");
+ SetInternalReference(pointer, entry,
+ "descriptors", descriptors,
+ JSGlobalPropertyCell::kValueOffset);
+ IndexedReferencesExtractor pointer_refs(this, pointer, entry);
+ pointer->Iterate(&pointer_refs);
+
+ Object* back_pointer = transitions->back_pointer_storage();
+ TagObject(transitions->back_pointer_storage(), "(back pointer)");
+ SetInternalReference(transitions, entry,
+ "backpointer", back_pointer,
+ TransitionArray::kBackPointerStorageOffset);
+ IndexedReferencesExtractor transitions_refs(this, transitions, entry);
+ transitions->Iterate(&transitions_refs);
+
TagObject(transitions, "(transition array)");
SetInternalReference(map, entry,
"transitions", transitions,
Map::kTransitionsOrBackPointerOffset);
+ } else {
+ Object* back_pointer = map->GetBackPointer();
+ TagObject(back_pointer, "(back pointer)");
+ SetInternalReference(map, entry,
+ "backpointer", back_pointer,
+ Map::kTransitionsOrBackPointerOffset);
}
SetInternalReference(map, entry,
"code_cache", map->code_cache(),
@@ -2179,7 +2192,9 @@
void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
if (js_obj->HasFastProperties()) {
DescriptorArray* descs = js_obj->map()->instance_descriptors();
+ int real_size = js_obj->map()->NumberOfOwnDescriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ if (descs->GetDetails(i).descriptor_index() > real_size) continue;
switch (descs->GetType(i)) {
case FIELD: {
int index = descs->GetFieldIndex(i);
diff --git a/src/property.h b/src/property.h
index 6bf52a7..9eb4194 100644
--- a/src/property.h
+++ b/src/property.h
@@ -68,7 +68,7 @@
details_ = PropertyDetails(details_.attributes(), details_.type(), index);
}
- void SetSortedKey(int index) { details_ = details_.set_pointer(index); }
+ void SetSortedKeyIndex(int index) { details_ = details_.set_pointer(index); }
private:
String* key_;
@@ -384,6 +384,7 @@
Object* GetValueFromMap(Map* map) const {
ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
+ ASSERT(number_ < map->NumberOfOwnDescriptors());
return map->instance_descriptors()->GetValue(number_);
}
diff --git a/src/runtime.cc b/src/runtime.cc
index 192fed2..b822814 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -2166,7 +2166,7 @@
// Construct a new field descriptor with updated attributes.
DescriptorArray* instance_desc = function->map()->instance_descriptors();
- int index = instance_desc->SearchWithCache(name);
+ int index = instance_desc->SearchWithCache(name, function->map());
ASSERT(index != DescriptorArray::kNotFound);
PropertyDetails details = instance_desc->GetDetails(index);
diff --git a/src/string-stream.cc b/src/string-stream.cc
index 51aa2bb..30519b5 100644
--- a/src/string-stream.cc
+++ b/src/string-stream.cc
@@ -348,9 +348,12 @@
Add("<Invalid map>\n");
return;
}
+ int real_size = map->NumberOfOwnDescriptors();
DescriptorArray* descs = map->instance_descriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
- if (descs->GetType(i) == FIELD) {
+ PropertyDetails details = descs->GetDetails(i);
+ if (details.descriptor_index() > real_size) continue;
+ if (details.type() == FIELD) {
Object* key = descs->GetKey(i);
if (key->IsString() || key->IsNumber()) {
int len = 3;
diff --git a/src/transitions-inl.h b/src/transitions-inl.h
index f2f0c3b..5030616 100644
--- a/src/transitions-inl.h
+++ b/src/transitions-inl.h
@@ -83,22 +83,26 @@
DescriptorArray* TransitionArray::descriptors() {
- return DescriptorArray::cast(get(kDescriptorsIndex));
+ return DescriptorArray::cast(descriptors_pointer()->value());
}
-void TransitionArray::set_descriptors(DescriptorArray* descriptors,
- WriteBarrierMode mode) {
- Heap* heap = GetHeap();
- WRITE_FIELD(this, kDescriptorsOffset, descriptors);
- CONDITIONAL_WRITE_BARRIER(
- heap, this, kDescriptorsOffset, descriptors, mode);
+void TransitionArray::set_descriptors(DescriptorArray* descriptors) {
+ ASSERT(!this->descriptors()->IsDescriptorArray() ||
+ descriptors->number_of_descriptors() == 0 ||
+ descriptors->HasEnumCache() ||
+ !this->descriptors()->HasEnumCache());
+ descriptors_pointer()->set_value(descriptors);
}
-Object** TransitionArray::GetDescriptorsSlot() {
- return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
- kDescriptorsOffset);
+JSGlobalPropertyCell* TransitionArray::descriptors_pointer() {
+ return JSGlobalPropertyCell::cast(get(kDescriptorsPointerIndex));
+}
+
+
+void TransitionArray::set_descriptors_pointer(JSGlobalPropertyCell* pointer) {
+ set(kDescriptorsPointerIndex, pointer);
}
@@ -192,7 +196,7 @@
int TransitionArray::Search(String* name) {
- return internal::Search(this, name);
+ return internal::Search<ALL_ENTRIES>(this, name);
}
diff --git a/src/transitions.cc b/src/transitions.cc
index fecced7..199fcc2 100644
--- a/src/transitions.cc
+++ b/src/transitions.cc
@@ -35,14 +35,23 @@
namespace internal {
-MaybeObject* TransitionArray::Allocate(int number_of_transitions) {
+MaybeObject* TransitionArray::Allocate(int number_of_transitions,
+ JSGlobalPropertyCell* descriptors_cell) {
Heap* heap = Isolate::Current()->heap();
+
+ if (descriptors_cell == NULL) {
+ MaybeObject* maybe_cell =
+ heap->AllocateJSGlobalPropertyCell(heap->empty_descriptor_array());
+ if (!maybe_cell->To(&descriptors_cell)) return maybe_cell;
+ }
+
// Use FixedArray to not use DescriptorArray::cast on incomplete object.
FixedArray* array;
MaybeObject* maybe_array =
heap->AllocateFixedArray(ToKeyIndex(number_of_transitions));
if (!maybe_array->To(&array)) return maybe_array;
+ array->set(kDescriptorsPointerIndex, descriptors_cell);
array->set(kElementsTransitionIndex, Smi::FromInt(0));
array->set(kPrototypeTransitionsIndex, Smi::FromInt(0));
return array;
@@ -63,13 +72,18 @@
}
-MaybeObject* TransitionArray::NewWith(String* name, Map* target) {
+MaybeObject* TransitionArray::NewWith(
+ String* name,
+ Map* target,
+ JSGlobalPropertyCell* descriptors_pointer,
+ Object* back_pointer) {
TransitionArray* result;
- MaybeObject* maybe_array = TransitionArray::Allocate(1);
+ MaybeObject* maybe_array = TransitionArray::Allocate(1, descriptors_pointer);
if (!maybe_array->To(&result)) return maybe_array;
result->NoIncrementalWriteBarrierSet(0, name, target);
+ result->set_back_pointer_storage(back_pointer);
return result;
}
@@ -84,7 +98,7 @@
if (insertion_index == kNotFound) ++new_size;
MaybeObject* maybe_array;
- maybe_array = TransitionArray::Allocate(new_size);
+ maybe_array = TransitionArray::Allocate(new_size, descriptors_pointer());
if (!maybe_array->To(&result)) return maybe_array;
if (HasElementsTransition()) {
@@ -119,6 +133,7 @@
this, insertion_index, insertion_index + 1);
}
+ result->set_back_pointer_storage(back_pointer_storage());
return result;
}
diff --git a/src/transitions.h b/src/transitions.h
index aca877d..b87b973 100644
--- a/src/transitions.h
+++ b/src/transitions.h
@@ -72,9 +72,9 @@
inline void ClearElementsTransition();
inline DescriptorArray* descriptors();
- inline void set_descriptors(DescriptorArray* descriptors,
- WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
- inline Object** GetDescriptorsSlot();
+ inline void set_descriptors(DescriptorArray* descriptors);
+ inline JSGlobalPropertyCell* descriptors_pointer();
+ inline void set_descriptors_pointer(JSGlobalPropertyCell* pointer);
inline Object* back_pointer_storage();
inline void set_back_pointer_storage(
@@ -99,7 +99,11 @@
inline int number_of_entries() { return number_of_transitions(); }
// Allocate a new transition array with a single entry.
- static MUST_USE_RESULT MaybeObject* NewWith(String* name, Map* target);
+ static MUST_USE_RESULT MaybeObject* NewWith(
+ String* name,
+ Map* target,
+ JSGlobalPropertyCell* descriptor_pointer,
+ Object* back_pointer);
// Copy the transition array, inserting a new transition.
// TODO(verwaest): This should not cause an existing transition to be
@@ -115,7 +119,9 @@
inline int Search(String* name);
// Allocates a TransitionArray.
- MUST_USE_RESULT static MaybeObject* Allocate(int number_of_transitions);
+ MUST_USE_RESULT static MaybeObject* Allocate(
+ int number_of_transitions,
+ JSGlobalPropertyCell* descriptors_cell);
// Casting.
static inline TransitionArray* cast(Object* obj);
@@ -123,15 +129,15 @@
// Constant for denoting key was not found.
static const int kNotFound = -1;
- static const int kDescriptorsIndex = 0;
+ static const int kDescriptorsPointerIndex = 0;
static const int kBackPointerStorageIndex = 1;
static const int kElementsTransitionIndex = 2;
static const int kPrototypeTransitionsIndex = 3;
static const int kFirstIndex = 4;
// Layout transition array header.
- static const int kDescriptorsOffset = FixedArray::kHeaderSize;
- static const int kBackPointerStorageOffset = kDescriptorsOffset +
+ static const int kDescriptorsPointerOffset = FixedArray::kHeaderSize;
+ static const int kBackPointerStorageOffset = kDescriptorsPointerOffset +
kPointerSize;
static const int kElementsTransitionOffset = kBackPointerStorageOffset +
kPointerSize;
@@ -152,7 +158,7 @@
#endif
#ifdef DEBUG
- bool IsSortedNoDuplicates();
+ bool IsSortedNoDuplicates(int valid_entries = -1);
bool IsConsistentWithBackPointers(Map* current_map);
bool IsEqualTo(TransitionArray* other);
#endif
diff --git a/src/version.cc b/src/version.cc
index 49b65a3..9ec9024 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -35,7 +35,7 @@
#define MAJOR_VERSION 3
#define MINOR_VERSION 14
#define BUILD_NUMBER 1
-#define PATCH_LEVEL 3
+#define PATCH_LEVEL 4
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
#define IS_CANDIDATE_VERSION 0
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 78e1dec..954d043 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -2642,22 +2642,28 @@
__ j(equal, if_false);
// Look for valueOf symbol in the descriptor array, and indicate false if
- // found. The type is not checked, so if it is a transition it is a false
- // negative.
+ // found. Since we omit an enumeration index check, if it is added via a
+ // transition that shares its descriptor array, this is a false positive.
+ Label entry, loop, done;
+
+ // Skip loop if no descriptors are valid.
+ __ NumberOfOwnDescriptors(rcx, rbx);
+ __ cmpq(rcx, Immediate(0));
+ __ j(equal, &done);
+
__ LoadInstanceDescriptors(rbx, rbx);
- __ movq(rcx, FieldOperand(rbx, FixedArray::kLengthOffset));
- // rbx: descriptor array
- // rcx: length of descriptor array
+ // rbx: descriptor array.
+ // rcx: valid entries in the descriptor array.
// Calculate the end of the descriptor array.
+ __ imul(rcx, rcx, Immediate(DescriptorArray::kDescriptorSize));
SmiIndex index = masm_->SmiToIndex(rdx, rcx, kPointerSizeLog2);
__ lea(rcx,
Operand(
- rbx, index.reg, index.scale, FixedArray::kHeaderSize));
+ rbx, index.reg, index.scale, DescriptorArray::kFirstOffset));
// Calculate location of the first key name.
__ addq(rbx, Immediate(DescriptorArray::kFirstOffset));
// Loop through all the keys in the descriptor array. If one of these is the
// symbol valueOf the result is false.
- Label entry, loop;
__ jmp(&entry);
__ bind(&loop);
__ movq(rdx, FieldOperand(rbx, 0));
@@ -2668,10 +2674,11 @@
__ cmpq(rbx, rcx);
__ j(not_equal, &loop);
+ __ bind(&done);
// Reload map as register rbx was used as temporary above.
__ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
- // If a valueOf property is not found on the object check that it's
+ // If a valueOf property is not found on the object check that its
// prototype is the un-modified String prototype. If not result is false.
__ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
__ testq(rcx, Immediate(kSmiTagMask));
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index 1b0f2fa..6759b19 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -2920,19 +2920,36 @@
Register temp = descriptors;
movq(temp, FieldOperand(map, Map::kTransitionsOrBackPointerOffset));
- Label ok, fail;
+ Label ok, fail, load_from_back_pointer;
CheckMap(temp,
isolate()->factory()->fixed_array_map(),
&fail,
DONT_DO_SMI_CHECK);
- movq(descriptors, FieldOperand(temp, TransitionArray::kDescriptorsOffset));
+ movq(temp, FieldOperand(temp, TransitionArray::kDescriptorsPointerOffset));
+ movq(descriptors, FieldOperand(temp, JSGlobalPropertyCell::kValueOffset));
jmp(&ok);
+
bind(&fail);
+ CompareRoot(temp, Heap::kUndefinedValueRootIndex);
+ j(not_equal, &load_from_back_pointer, Label::kNear);
Move(descriptors, isolate()->factory()->empty_descriptor_array());
+ jmp(&ok);
+
+ bind(&load_from_back_pointer);
+ movq(temp, FieldOperand(temp, Map::kTransitionsOrBackPointerOffset));
+ movq(temp, FieldOperand(temp, TransitionArray::kDescriptorsPointerOffset));
+ movq(descriptors, FieldOperand(temp, JSGlobalPropertyCell::kValueOffset));
+
bind(&ok);
}
+void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) {
+ movq(dst, FieldOperand(map, Map::kBitField3Offset));
+ DecodeField<Map::NumberOfOwnDescriptorsBits>(dst);
+}
+
+
void MacroAssembler::EnumLength(Register dst, Register map) {
STATIC_ASSERT(Map::EnumLengthBits::kShift == 0);
movq(dst, FieldOperand(map, Map::kBitField3Offset));
diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h
index 5268fe2..89b7962 100644
--- a/src/x64/macro-assembler-x64.h
+++ b/src/x64/macro-assembler-x64.h
@@ -949,13 +949,15 @@
void LoadInstanceDescriptors(Register map, Register descriptors);
void EnumLength(Register dst, Register map);
+ void NumberOfOwnDescriptors(Register dst, Register map);
template<typename Field>
void DecodeField(Register reg) {
- static const int full_shift = Field::kShift + kSmiShift;
- static const int low_mask = Field::kMask >> Field::kShift;
- shr(reg, Immediate(full_shift));
- and_(reg, Immediate(low_mask));
+ static const int shift = Field::kShift + kSmiShift;
+ static const int mask = Field::kMask >> Field::kShift;
+ shr(reg, Immediate(shift));
+ and_(reg, Immediate(mask));
+ shl(reg, Immediate(kSmiShift));
}
// Abort execution if argument is not a number. Used in debug code.