recvr, name, attributes);
}
case MAP_TRANSITION:
- case ELEMENTS_TRANSITION:
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
break;
// Element transitions are stored in the descriptor for property "", which is
// not a identifier and should have forced a switch to slow properties above.
- ASSERT(descriptor_index == DescriptorArray::kNotFound ||
- old_descriptors->GetType(descriptor_index) != ELEMENTS_TRANSITION);
- bool can_insert_transition = descriptor_index == DescriptorArray::kNotFound ||
- old_descriptors->GetType(descriptor_index) == ELEMENTS_TRANSITION;
+ bool can_insert_transition = descriptor_index == DescriptorArray::kNotFound;
bool allow_map_transition =
can_insert_transition &&
(isolate->context()->global_context()->object_function()->map() != map());
}
// We have now allocated all the necessary objects.
// All the changes can be applied at once, so they are atomic.
- map()->set_instance_descriptors(old_descriptors);
+ if (allow_map_transition) {
+ map()->set_instance_descriptors(old_descriptors);
+ }
new_map->SetBackPointer(map());
new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
set_map(new_map);
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
- case ELEMENTS_TRANSITION:
break;
}
}
if (IsTransitionableFastElementsKind(kind)) {
while (CanTransitionToMoreGeneralFastElementsKind(kind, false)) {
kind = GetNextMoreGeneralFastElementsKind(kind, false);
- bool dummy = true;
Handle<Map> maybe_transitioned_map =
- MaybeNull(current_map->LookupElementsTransitionMap(kind, &dummy));
+ MaybeNull(current_map->LookupElementsTransitionMap(kind));
if (maybe_transitioned_map.is_null()) break;
if (ContainsMap(candidates, maybe_transitioned_map) &&
(packed || !IsFastPackedElementsKind(kind))) {
}
-static Map* GetElementsTransitionMapFromDescriptor(Object* descriptor_contents,
- ElementsKind elements_kind) {
- if (descriptor_contents->IsMap()) {
- Map* map = Map::cast(descriptor_contents);
- if (map->elements_kind() == elements_kind) {
- return map;
+static Map* FindClosestElementsTransition(Map* map, ElementsKind to_kind) {
+ Map* current_map = map;
+ int index = GetSequenceIndexFromFastElementsKind(map->elements_kind());
+ int to_index = GetSequenceIndexFromFastElementsKind(to_kind);
+ for (; index < to_index; ++index) {
+ Map* next_map = current_map->elements_transition_map();
+ if (next_map == NULL) {
+ return current_map;
}
- return NULL;
+ current_map = next_map;
}
-
- FixedArray* map_array = FixedArray::cast(descriptor_contents);
- for (int i = 0; i < map_array->length(); ++i) {
- Object* current = map_array->get(i);
- // Skip undefined slots, they are sentinels for reclaimed maps.
- if (!current->IsUndefined()) {
- Map* current_map = Map::cast(map_array->get(i));
- if (current_map->elements_kind() == elements_kind) {
- return current_map;
- }
- }
- }
-
- return NULL;
+ ASSERT(current_map->elements_kind() == to_kind);
+ return current_map;
}
-static MaybeObject* AddElementsTransitionMapToDescriptor(
- Object* descriptor_contents,
- Map* new_map) {
- // Nothing was in the descriptor for an ELEMENTS_TRANSITION,
- // simply add the map.
- if (descriptor_contents == NULL) {
- return new_map;
- }
-
- // There was already a map in the descriptor, create a 2-element FixedArray
- // to contain the existing map plus the new one.
- FixedArray* new_array;
- Heap* heap = new_map->GetHeap();
- if (descriptor_contents->IsMap()) {
- // Must tenure, DescriptorArray expects no new-space objects.
- MaybeObject* maybe_new_array = heap->AllocateFixedArray(2, TENURED);
- if (!maybe_new_array->To<FixedArray>(&new_array)) {
- return maybe_new_array;
- }
- new_array->set(0, descriptor_contents);
- new_array->set(1, new_map);
- return new_array;
- }
-
- // The descriptor already contained a list of maps for different ElementKinds
- // of ELEMENTS_TRANSITION, first check the existing array for an undefined
- // slot, and if that's not available, create a FixedArray to hold the existing
- // maps plus the new one and fill it in.
- FixedArray* array = FixedArray::cast(descriptor_contents);
- for (int i = 0; i < array->length(); ++i) {
- if (array->get(i)->IsUndefined()) {
- array->set(i, new_map);
- return array;
+Map* Map::LookupElementsTransitionMap(ElementsKind to_kind) {
+ if (this->instance_descriptors()->MayContainTransitions() &&
+ IsMoreGeneralElementsKindTransition(this->elements_kind(), to_kind)) {
+ Map* to_map = FindClosestElementsTransition(this, to_kind);
+ if (to_map->elements_kind() == to_kind) {
+ return to_map;
}
}
-
- // Must tenure, DescriptorArray expects no new-space objects.
- MaybeObject* maybe_new_array =
- heap->AllocateFixedArray(array->length() + 1, TENURED);
- if (!maybe_new_array->To<FixedArray>(&new_array)) {
- return maybe_new_array;
- }
- int i = 0;
- while (i < array->length()) {
- new_array->set(i, array->get(i));
- ++i;
- }
- new_array->set(i, new_map);
- return new_array;
+ return NULL;
}
-String* Map::elements_transition_sentinel_name() {
- return GetHeap()->empty_symbol();
-}
+MaybeObject* Map::CreateNextElementsTransition(ElementsKind next_kind) {
+ ASSERT(elements_transition_map() == NULL);
+ ASSERT(GetSequenceIndexFromFastElementsKind(elements_kind()) ==
+ (GetSequenceIndexFromFastElementsKind(next_kind) - 1));
+ Map* next_map;
+ MaybeObject* maybe_next_map =
+ this->CopyDropTransitions(DescriptorArray::CANNOT_BE_SHARED);
+ if (!maybe_next_map->To(&next_map)) return maybe_next_map;
-Object* Map::GetDescriptorContents(String* sentinel_name,
- bool* safe_to_add_transition) {
- // Get the cached index for the descriptors lookup, or find and cache it.
- DescriptorArray* descriptors = instance_descriptors();
- DescriptorLookupCache* cache = GetIsolate()->descriptor_lookup_cache();
- int index = cache->Lookup(descriptors, sentinel_name);
- if (index == DescriptorLookupCache::kAbsent) {
- index = descriptors->Search(sentinel_name);
- cache->Update(descriptors, sentinel_name, index);
- }
- // If the transition already exists, return its descriptor.
- if (index != DescriptorArray::kNotFound) {
- PropertyDetails details = descriptors->GetDetails(index);
- if (details.type() == ELEMENTS_TRANSITION) {
- return descriptors->GetValue(index);
- } else {
- if (safe_to_add_transition != NULL) {
- *safe_to_add_transition = false;
- }
- }
- }
- return NULL;
+ next_map->set_elements_kind(next_kind);
+ next_map->SetBackPointer(this);
+ this->set_elements_transition_map(next_map);
+ return next_map;
}
-Map* Map::LookupElementsTransitionMap(ElementsKind to_kind,
- bool* safe_to_add_transition) {
- ElementsKind from_kind = elements_kind();
- if (IsFastElementsKind(from_kind) && IsFastElementsKind(to_kind)) {
- if (!IsMoreGeneralElementsKindTransition(from_kind, to_kind)) {
- if (safe_to_add_transition) *safe_to_add_transition = false;
- return NULL;
- }
- ElementsKind transitioned_from_kind =
- GetNextMoreGeneralFastElementsKind(from_kind, false);
+static MaybeObject* AddMissingElementsTransitions(Map* map,
+ ElementsKind to_kind) {
+ int index = GetSequenceIndexFromFastElementsKind(map->elements_kind()) + 1;
+ int to_index = GetSequenceIndexFromFastElementsKind(to_kind);
+ ASSERT(index <= to_index);
+ Map* current_map = map;
- // If the transition is a single step in the transition sequence, fall
- // through to looking it up and returning it. If it requires several steps,
- // divide and conquer.
- if (transitioned_from_kind != to_kind) {
- // If the transition is several steps in the lattice, divide and conquer.
- Map* from_map = LookupElementsTransitionMap(transitioned_from_kind,
- safe_to_add_transition);
- if (from_map == NULL) return NULL;
- return from_map->LookupElementsTransitionMap(to_kind,
- safe_to_add_transition);
- }
- }
- Object* descriptor_contents = GetDescriptorContents(
- elements_transition_sentinel_name(), safe_to_add_transition);
- if (descriptor_contents != NULL) {
- Map* maybe_transition_map =
- GetElementsTransitionMapFromDescriptor(descriptor_contents,
- to_kind);
- ASSERT(maybe_transition_map == NULL || maybe_transition_map->IsMap());
- return maybe_transition_map;
+ for (; index <= to_index; ++index) {
+ ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(index);
+ MaybeObject* maybe_next_map =
+ current_map->CreateNextElementsTransition(next_kind);
+ if (!maybe_next_map->To(¤t_map)) return maybe_next_map;
}
- return NULL;
-}
-
-MaybeObject* Map::AddElementsTransition(ElementsKind to_kind,
- Map* transitioned_map) {
- ElementsKind from_kind = elements_kind();
- if (IsFastElementsKind(from_kind) && IsFastElementsKind(to_kind)) {
- ASSERT(IsMoreGeneralElementsKindTransition(from_kind, to_kind));
- ElementsKind transitioned_from_kind =
- GetNextMoreGeneralFastElementsKind(from_kind, false);
- // The map transitions graph should be a tree, therefore transitions to
- // ElementsKind that are not adjacent in the ElementsKind sequence are not
- // done directly, but instead by going through intermediate ElementsKinds
- // first.
- if (to_kind != transitioned_from_kind) {
- bool safe_to_add = true;
- Map* intermediate_map = LookupElementsTransitionMap(
- transitioned_from_kind, &safe_to_add);
- // This method is only called when safe_to_add has been found to be true
- // earlier.
- ASSERT(safe_to_add);
-
- if (intermediate_map == NULL) {
- MaybeObject* maybe_map = CopyDropTransitions();
- if (!maybe_map->To(&intermediate_map)) return maybe_map;
- intermediate_map->set_elements_kind(transitioned_from_kind);
- MaybeObject* maybe_transition = AddElementsTransition(
- transitioned_from_kind, intermediate_map);
- if (maybe_transition->IsFailure()) return maybe_transition;
- }
- return intermediate_map->AddElementsTransition(to_kind, transitioned_map);
- }
- }
-
- bool safe_to_add_transition = true;
- Object* descriptor_contents = GetDescriptorContents(
- elements_transition_sentinel_name(), &safe_to_add_transition);
- // This method is only called when safe_to_add_transition has been found
- // to be true earlier.
- ASSERT(safe_to_add_transition);
- MaybeObject* maybe_new_contents =
- AddElementsTransitionMapToDescriptor(descriptor_contents,
- transitioned_map);
- Object* new_contents;
- if (!maybe_new_contents->ToObject(&new_contents)) {
- return maybe_new_contents;
- }
-
- ElementsTransitionDescriptor desc(elements_transition_sentinel_name(),
- new_contents);
- Object* new_descriptors;
- MaybeObject* maybe_new_descriptors =
- instance_descriptors()->CopyInsert(&desc, KEEP_TRANSITIONS);
- if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
- return maybe_new_descriptors;
- }
- set_instance_descriptors(DescriptorArray::cast(new_descriptors));
- transitioned_map->SetBackPointer(this);
- return this;
+ ASSERT(current_map->elements_kind() == to_kind);
+ return current_map;
}
}
-MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) {
- Map* current_map = map();
- ElementsKind from_kind = current_map->elements_kind();
+// If the map is using the empty descriptor array, install a new empty
+// descriptor array that will contain an element transition.
+// TODO(verwaest) Goes away once the descriptor array is immutable.
+static MaybeObject* EnsureMayContainTransitions(Map* map) {
+ if (map->instance_descriptors()->MayContainTransitions()) return map;
+ DescriptorArray* descriptor_array;
+ MaybeObject* maybe_descriptor_array =
+ DescriptorArray::Allocate(0, DescriptorArray::CANNOT_BE_SHARED);
+ if (!maybe_descriptor_array->To(&descriptor_array)) {
+ return maybe_descriptor_array;
+ }
+ map->set_instance_descriptors(descriptor_array);
+ return map;
+}
- if (from_kind == to_kind) return current_map;
- // Only objects with FastProperties can have DescriptorArrays and can track
- // element-related maps. Also don't add descriptors to maps that are shared.
- bool safe_to_add_transition = HasFastProperties() &&
- !current_map->IsUndefined() &&
- !current_map->is_shared();
+MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) {
+ Map* start_map = map();
+ ElementsKind from_kind = start_map->elements_kind();
- // Prevent long chains of DICTIONARY -> FAST_*_ELEMENTS maps caused by objects
- // with elements that switch back and forth between dictionary and fast
- // element modes.
- if (from_kind == DICTIONARY_ELEMENTS &&
- IsFastElementsKind(to_kind)) {
- safe_to_add_transition = false;
+ if (from_kind == to_kind) {
+ return start_map;
}
- if (safe_to_add_transition) {
- // It's only safe to manipulate the descriptor array if it would be
- // safe to add a transition.
- Map* maybe_transition_map = current_map->LookupElementsTransitionMap(
- to_kind, &safe_to_add_transition);
- if (maybe_transition_map != NULL) {
- return maybe_transition_map;
- }
+ Context* global_context = GetIsolate()->context()->global_context();
+ bool allow_store_transition =
+ // Only remember the map transition if the object's map is NOT equal to
+ // the global object_function's map and there is not an already existing
+ // non-matching element transition.
+ (global_context->object_function()->map() != map()) &&
+ !start_map->IsUndefined() && !start_map->is_shared() &&
+ // Only store fast element maps in ascending generality.
+ IsTransitionableFastElementsKind(from_kind) &&
+ IsFastElementsKind(to_kind) &&
+ IsMoreGeneralElementsKindTransition(from_kind, to_kind);
+
+ if (!allow_store_transition) {
+ // Create a new free-floating map only if we are not allowed to store it.
+ Map* new_map = NULL;
+ MaybeObject* maybe_new_map =
+ start_map->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ new_map->set_elements_kind(to_kind);
+ return new_map;
}
- Map* new_map = NULL;
+ EnsureMayContainTransitions(start_map);
+ Map* closest_map = FindClosestElementsTransition(start_map, to_kind);
- // No transition to an existing map for the given ElementsKind. Make a new
- // one.
- { MaybeObject* maybe_map = current_map->CopyDropTransitions();
- if (!maybe_map->To(&new_map)) return maybe_map;
+ if (closest_map->elements_kind() == to_kind) {
+ return closest_map;
}
- new_map->set_elements_kind(to_kind);
-
- // Only remember the map transition if the object's map is NOT equal to the
- // global object_function's map and there is not an already existing
- // non-matching element transition.
- Context* global_context = GetIsolate()->context()->global_context();
- bool allow_map_transition = safe_to_add_transition &&
- (global_context->object_function()->map() != map());
- if (allow_map_transition) {
- MaybeObject* maybe_transition =
- current_map->AddElementsTransition(to_kind, new_map);
- if (maybe_transition->IsFailure()) return maybe_transition;
- }
- return new_map;
+ return AddMissingElementsTransitions(closest_map, to_kind);
}
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
}
case NULL_DESCRIPTOR:
- case ELEMENTS_TRANSITION:
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case HANDLER:
UNREACHABLE();
case CONSTANT_TRANSITION:
// Replace with a MAP_TRANSITION to a new map with a FIELD, even
// if the value is a function.
- return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case NULL_DESCRIPTOR:
- case ELEMENTS_TRANSITION:
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case HANDLER:
UNREACHABLE();
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
case INTERCEPTOR:
- case ELEMENTS_TRANSITION:
break;
case HANDLER:
case NORMAL:
// Do a map transition, other objects with this map may still
// be extensible.
Map* new_map;
- { MaybeObject* maybe = map()->CopyDropTransitions();
+ { MaybeObject* maybe =
+ map()->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
if (!maybe->To<Map>(&new_map)) return maybe;
}
new_map->set_is_extensible(false);
JSFunction* ctor = JSFunction::cast(constructor());
Object* descriptors;
{ MaybeObject* maybe_descriptors =
- ctor->initial_map()->instance_descriptors()->RemoveTransitions();
+ ctor->initial_map()->instance_descriptors()->RemoveTransitions(
+ DescriptorArray::MAY_BE_SHARED);
if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
}
Map::cast(result)->set_instance_descriptors(
}
-MaybeObject* Map::CopyDropTransitions() {
+MaybeObject* Map::CopyDropTransitions(
+ DescriptorArray::SharedMode shared_mode) {
Object* new_map;
{ MaybeObject* maybe_new_map = CopyDropDescriptors();
if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
}
Object* descriptors;
{ MaybeObject* maybe_descriptors =
- instance_descriptors()->RemoveTransitions();
+ instance_descriptors()->RemoveTransitions(shared_mode);
if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
}
cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
void Start() {
ASSERT(!IsIterating());
- if (HasDescriptors()) *DescriptorArrayHeader() = Smi::FromInt(0);
+ if (descriptor_array_->MayContainTransitions())
+ *DescriptorArrayHeader() = Smi::FromInt(0);
}
bool IsIterating() {
- return HasDescriptors() && (*DescriptorArrayHeader())->IsSmi();
+ return descriptor_array_->MayContainTransitions() &&
+ (*DescriptorArrayHeader())->IsSmi();
}
Map* Next() {
switch (details.type()) {
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
- case ELEMENTS_TRANSITION:
// We definitely have a map transition.
*DescriptorArrayHeader() = Smi::FromInt(raw_index + 2);
return static_cast<Map*>(descriptor_array_->GetValue(index));
break;
}
}
+ if (index == descriptor_array_->number_of_descriptors()) {
+ Map* elements_transition = descriptor_array_->elements_transition_map();
+ if (elements_transition != NULL) {
+ *DescriptorArrayHeader() = Smi::FromInt(index + 1);
+ return elements_transition;
+ }
+ }
*DescriptorArrayHeader() = descriptor_array_->GetHeap()->fixed_array_map();
return NULL;
}
private:
- bool HasDescriptors() {
- return descriptor_array_->length() > DescriptorArray::kFirstIndex;
- }
-
Object** DescriptorArrayHeader() {
return HeapObject::RawField(descriptor_array_, DescriptorArray::kMapOffset);
}
#endif
-MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) {
+MaybeObject* DescriptorArray::Allocate(int number_of_descriptors,
+ SharedMode shared_mode) {
Heap* heap = Isolate::Current()->heap();
+ // Do not use DescriptorArray::cast on incomplete object.
+ FixedArray* result;
if (number_of_descriptors == 0) {
- return heap->empty_descriptor_array();
- }
- // Allocate the array of keys.
- Object* array;
- { MaybeObject* maybe_array =
- heap->AllocateFixedArray(ToKeyIndex(number_of_descriptors));
- if (!maybe_array->ToObject(&array)) return maybe_array;
+ if (shared_mode == MAY_BE_SHARED) {
+ return heap->empty_descriptor_array();
+ }
+ { MaybeObject* maybe_array =
+ heap->AllocateFixedArray(kTransitionsIndex + 1);
+ if (!maybe_array->To(&result)) return maybe_array;
+ }
+ } else {
+ // Allocate the array of keys.
+ { MaybeObject* maybe_array =
+ heap->AllocateFixedArray(ToKeyIndex(number_of_descriptors));
+ if (!maybe_array->To(&result)) return maybe_array;
+ }
+ result->set(kEnumerationIndexIndex,
+ Smi::FromInt(PropertyDetails::kInitialIndex));
}
- // Do not use DescriptorArray::cast on incomplete object.
- FixedArray* result = FixedArray::cast(array);
-
result->set(kBitField3StorageIndex, Smi::FromInt(0));
- result->set(kEnumerationIndexIndex,
- Smi::FromInt(PropertyDetails::kInitialIndex));
+ result->set(kTransitionsIndex, Smi::FromInt(0));
return result;
}
}
DescriptorArray* new_descriptors;
- { MaybeObject* maybe_result = Allocate(new_size);
+ { SharedMode mode = remove_transitions ? MAY_BE_SHARED : CANNOT_BE_SHARED;
+ MaybeObject* maybe_result = Allocate(new_size, mode);
if (!maybe_result->To(&new_descriptors)) return maybe_result;
}
++enumeration_index;
}
}
+ Map* old_elements_transition = elements_transition_map();
+ if ((!remove_transitions) && (old_elements_transition != NULL)) {
+ new_descriptors->set_elements_transition_map(old_elements_transition);
+ }
new_descriptors->SetNextEnumerationIndex(enumeration_index);
// Copy the descriptors, filtering out transitions and null descriptors,
}
}
if (insertion_index < 0) insertion_index = to_index++;
+
+ ASSERT(insertion_index < new_descriptors->number_of_descriptors());
new_descriptors->Set(insertion_index, descriptor, witness);
ASSERT(to_index == new_descriptors->number_of_descriptors());
}
-MaybeObject* DescriptorArray::RemoveTransitions() {
+MaybeObject* DescriptorArray::RemoveTransitions(SharedMode shared_mode) {
// Allocate the new descriptor array.
int new_number_of_descriptors = 0;
for (int i = 0; i < number_of_descriptors(); i++) {
if (IsProperty(i)) new_number_of_descriptors++;
}
DescriptorArray* new_descriptors;
- { MaybeObject* maybe_result = Allocate(new_number_of_descriptors);
+ { MaybeObject* maybe_result = Allocate(new_number_of_descriptors,
+ shared_mode);
if (!maybe_result->To(&new_descriptors)) return maybe_result;
}
int DescriptorArray::BinarySearch(String* name, int low, int high) {
uint32_t hash = name->Hash();
+ int limit = high;
+
+ ASSERT(low <= high);
- while (low <= high) {
+ while (low != high) {
int mid = (low + high) / 2;
String* mid_name = GetKey(mid);
uint32_t mid_hash = mid_name->Hash();
- if (mid_hash > hash) {
- high = mid - 1;
- continue;
- }
- if (mid_hash < hash) {
+ if (mid_hash >= hash) {
+ high = mid;
+ } else {
low = mid + 1;
- continue;
- }
- // Found an element with the same hash-code.
- ASSERT(hash == mid_hash);
- // There might be more, so we find the first one and
- // check them all to see if we have a match.
- if (name == mid_name && !IsNullDescriptor(mid)) return mid;
- while ((mid > low) && (GetKey(mid - 1)->Hash() == hash)) mid--;
- for (; (mid <= high) && (GetKey(mid)->Hash() == hash); mid++) {
- if (GetKey(mid)->Equals(name) && !IsNullDescriptor(mid)) return mid;
}
- break;
}
+
+ for (; low <= limit && GetKey(low)->Hash() == hash; ++low) {
+ if (GetKey(low)->Equals(name) && !IsNullDescriptor(low))
+ return low;
+ }
+
return kNotFound;
}
-int DescriptorArray::LinearSearch(String* name, int len) {
+int DescriptorArray::LinearSearch(SearchMode mode, String* name, int len) {
uint32_t hash = name->Hash();
for (int number = 0; number < len; number++) {
String* entry = GetKey(number);
- if ((entry->Hash() == hash) &&
- name->Equals(entry) &&
- !IsNullDescriptor(number)) {
+ if (mode == EXPECT_SORTED && entry->Hash() > hash) break;
+ if (name->Equals(entry) && !IsNullDescriptor(number)) {
return number;
}
}
case CONSTANT_TRANSITION:
keep_entry = !ClearBackPointer(heap, d->GetValue(i));
break;
- case ELEMENTS_TRANSITION: {
- Object* object = d->GetValue(i);
- if (object->IsMap()) {
- keep_entry = !ClearBackPointer(heap, object);
- } else {
- FixedArray* array = FixedArray::cast(object);
- for (int j = 0; j < array->length(); ++j) {
- Object* target = array->get(j);
- if (target->IsMap()) {
- if (ClearBackPointer(heap, target)) {
- array->set_undefined(j);
- } else {
- keep_entry = true;
- }
- }
- }
- }
- break;
- }
case CALLBACKS: {
Object* object = d->GetValue(i);
if (object->IsAccessorPair()) {
Map* new_map =
proto_map->GetPrototypeTransition(heap->the_hole_value());
if (new_map == NULL) {
- MaybeObject* maybe_new_map = proto_map->CopyDropTransitions();
+ MaybeObject* maybe_new_map =
+ proto_map->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
if (!maybe_new_map->To<Map>(&new_map)) return maybe_new_map;
new_map->set_used_for_prototype(true);
MaybeObject* ok =
// If the function has allocated the initial map
// replace it with a copy containing the new prototype.
Map* new_map;
- MaybeObject* maybe_new_map = initial_map()->CopyDropTransitions();
+ MaybeObject* maybe_new_map =
+ initial_map()->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
new_map->set_prototype(value);
MaybeObject* maybe_object =
// Remove map transitions because they point to maps with a
// different prototype.
Map* new_map;
- { MaybeObject* maybe_new_map = map()->CopyDropTransitions();
+ { MaybeObject* maybe_new_map =
+ map()->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
}
Heap* heap = new_map->GetHeap();
case HANDLER: return "HANDLER";
case INTERCEPTOR: return "INTERCEPTOR";
case MAP_TRANSITION: return "MAP_TRANSITION";
- case ELEMENTS_TRANSITION: return "ELEMENTS_TRANSITION";
case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION";
case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR";
}
Map* new_map = map->GetPrototypeTransition(value);
if (new_map == NULL) {
- { MaybeObject* maybe_new_map = map->CopyDropTransitions();
- if (!maybe_new_map->To<Map>(&new_map)) return maybe_new_map;
+ { MaybeObject* maybe_new_map =
+ map->CopyDropTransitions(DescriptorArray::MAY_BE_SHARED);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
}
{ MaybeObject* maybe_new_cache =
switch (DetailsAt(entry).type()) {
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
- case ELEMENTS_TRANSITION:
return true;
case CALLBACKS: {
Object* value = ValueAt(entry);
// Allocate the instance descriptor.
DescriptorArray* descriptors;
{ MaybeObject* maybe_descriptors =
- DescriptorArray::Allocate(instance_descriptor_length);
+ DescriptorArray::Allocate(instance_descriptor_length,
+ DescriptorArray::MAY_BE_SHARED);
if (!maybe_descriptors->To<DescriptorArray>(&descriptors)) {
return maybe_descriptors;
}
};
+// Indicates whether the search function should expect a sorted or an unsorted
+// array as input.
+enum SearchMode {
+ EXPECT_SORTED,
+ EXPECT_UNSORTED
+};
+
+
// Instance size sentinel for objects of variable size.
const int kVariableSizeSentinel = 0;
// map uses to encode additional bit fields when the descriptor array is not
// yet used.
inline bool IsEmpty();
+ inline bool MayContainTransitions();
+
+ DECL_ACCESSORS(elements_transition_map, Map)
// Returns the number of descriptors in the array.
int number_of_descriptors() {
- ASSERT(length() > kFirstIndex || IsEmpty());
+ ASSERT(length() > kFirstIndex ||
+ length() == kTransitionsIndex ||
+ IsEmpty());
int len = length();
return len <= kFirstIndex ? 0 : (len - kFirstIndex) / kDescriptorSize;
}
kEnumerationIndexOffset);
}
+ Object** GetTransitionsSlot() {
+ ASSERT(elements_transition_map() != NULL);
+ return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
+ kTransitionsOffset);
+ }
+
// TODO(1399): It should be possible to make room for bit_field3 in the map
// without overloading the instance descriptors field in the map
// (and storing it in the DescriptorArray when the map has one).
MUST_USE_RESULT MaybeObject* CopyInsert(Descriptor* descriptor,
TransitionFlag transition_flag);
+ // Indicates whether the search function should expect a sorted or an unsorted
+ // descriptor array as input.
+ enum SharedMode {
+ MAY_BE_SHARED,
+ CANNOT_BE_SHARED
+ };
+
// Return a copy of the array with all transitions and null descriptors
// removed. Return a Failure object in case of an allocation failure.
- MUST_USE_RESULT MaybeObject* RemoveTransitions();
+ MUST_USE_RESULT MaybeObject* RemoveTransitions(SharedMode shared_mode);
// Sort the instance descriptors by the hash codes of their keys.
// Does not check for duplicates.
// Perform a linear search in the instance descriptors represented
// by this fixed array. len is the number of descriptor indices that are
- // valid. Does not require the descriptors to be sorted.
- int LinearSearch(String* name, int len);
+ // valid.
+ int LinearSearch(SearchMode mode, String* name, int len);
// Allocates a DescriptorArray, but returns the singleton
// empty descriptor array object if number_of_descriptors is 0.
- MUST_USE_RESULT static MaybeObject* Allocate(int number_of_descriptors);
+ MUST_USE_RESULT static MaybeObject* Allocate(int number_of_descriptors,
+ SharedMode shared_mode);
// Casting.
static inline DescriptorArray* cast(Object* obj);
static const int kNotFound = -1;
static const int kBitField3StorageIndex = 0;
- static const int kEnumerationIndexIndex = 1;
- static const int kFirstIndex = 2;
+ static const int kTransitionsIndex = 1;
+ static const int kEnumerationIndexIndex = 2;
+ static const int kFirstIndex = 3;
// The length of the "bridge" to the enum cache.
static const int kEnumCacheBridgeLength = 3;
// Layout description.
static const int kBitField3StorageOffset = FixedArray::kHeaderSize;
- static const int kEnumerationIndexOffset = kBitField3StorageOffset +
- kPointerSize;
+ static const int kTransitionsOffset = kBitField3StorageOffset + kPointerSize;
+ static const int kEnumerationIndexOffset = kTransitionsOffset + kPointerSize;
static const int kFirstOffset = kEnumerationIndexOffset + kPointerSize;
// Layout description for the bridge array.
static bool IsValidElementsTransition(ElementsKind from_kind,
ElementsKind to_kind);
+ inline Map* elements_transition_map();
+ inline void set_elements_transition_map(Map* transitioned_map);
+
// Tells whether the map is attached to SharedFunctionInfo
// (for inobject slack tracking).
inline void set_attached_to_shared_function_info(bool value);
// Returns a copy of the map, with all transitions dropped from the
// instance descriptors.
- MUST_USE_RESULT MaybeObject* CopyDropTransitions();
+ MUST_USE_RESULT MaybeObject* CopyDropTransitions(
+ DescriptorArray::SharedMode shared_mode);
// Returns the property index for name (only valid for FAST MODE).
int PropertyIndexFor(String* name);
// The "shared" flags of both this map and |other| are ignored.
bool EquivalentToForNormalization(Map* other, PropertyNormalizationMode mode);
- // Returns the contents of this map's descriptor array for the given string.
- // May return NULL. |safe_to_add_transition| is set to false and NULL
- // is returned if adding transitions is not allowed.
- Object* GetDescriptorContents(String* sentinel_name,
- bool* safe_to_add_transitions);
-
// Returns the map that this map transitions to if its elements_kind
// is changed to |elements_kind|, or NULL if no such map is cached yet.
// |safe_to_add_transitions| is set to false if adding transitions is not
// allowed.
- Map* LookupElementsTransitionMap(ElementsKind elements_kind,
- bool* safe_to_add_transition);
+ Map* LookupElementsTransitionMap(ElementsKind elements_kind);
- // Adds an entry to this map's descriptor array for a transition to
- // |transitioned_map| when its elements_kind is changed to |elements_kind|.
- MUST_USE_RESULT MaybeObject* AddElementsTransition(
- ElementsKind elements_kind, Map* transitioned_map);
+ // Adds a new transitions for changing the elements kind to |elements_kind|.
+ MUST_USE_RESULT MaybeObject* CreateNextElementsTransition(
+ ElementsKind elements_kind);
// Returns the transitioned map for this map with the most generic
// elements_kind that's found in |candidates|, or null handle if no match is
kSize> BodyDescriptor;
private:
- String* elements_transition_sentinel_name();
DISALLOW_IMPLICIT_CONSTRUCTORS(Map);
};