// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_OBJECTS_TRANSITIONS_INL_H_ #define V8_OBJECTS_TRANSITIONS_INL_H_ #include "src/objects/transitions.h" #include "src/ic/handler-configuration-inl.h" #include "src/objects/fixed-array-inl.h" #include "src/objects/maybe-object-inl.h" #include "src/objects/slots.h" #include "src/objects/smi.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" namespace v8 { namespace internal { TransitionArray TransitionsAccessor::transitions() { DCHECK_EQ(kFullTransitionArray, encoding()); return TransitionArray::cast(raw_transitions_->GetHeapObjectAssumeStrong()); } OBJECT_CONSTRUCTORS_IMPL(TransitionArray, WeakFixedArray) CAST_ACCESSOR(TransitionArray) bool TransitionArray::HasPrototypeTransitions() { return Get(kPrototypeTransitionsIndex) != MaybeObject::FromSmi(Smi::zero()); } WeakFixedArray TransitionArray::GetPrototypeTransitions() { DCHECK(HasPrototypeTransitions()); // Callers must check first. Object prototype_transitions = Get(kPrototypeTransitionsIndex)->GetHeapObjectAssumeStrong(); return WeakFixedArray::cast(prototype_transitions); } HeapObjectSlot TransitionArray::GetKeySlot(int transition_number) { DCHECK(transition_number < number_of_transitions()); return HeapObjectSlot(RawFieldOfElementAt(ToKeyIndex(transition_number))); } void TransitionArray::SetPrototypeTransitions(WeakFixedArray transitions) { DCHECK(transitions.IsWeakFixedArray()); WeakFixedArray::Set(kPrototypeTransitionsIndex, HeapObjectReference::Strong(transitions)); } int TransitionArray::NumberOfPrototypeTransitions( WeakFixedArray proto_transitions) { if (proto_transitions.length() == 0) return 0; MaybeObject raw = proto_transitions.Get(kProtoTransitionNumberOfEntriesOffset); return raw.ToSmi().value(); } Name TransitionArray::GetKey(int transition_number) { DCHECK(transition_number < number_of_transitions()); return Name::cast( Get(ToKeyIndex(transition_number))->GetHeapObjectAssumeStrong()); } Name TransitionsAccessor::GetKey(int transition_number) { switch (encoding()) { case kPrototypeInfo: case kUninitialized: case kMigrationTarget: UNREACHABLE(); return Name(); case kWeakRef: { Map map = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak()); return GetSimpleTransitionKey(map); } case kFullTransitionArray: return transitions().GetKey(transition_number); } UNREACHABLE(); } void TransitionArray::SetKey(int transition_number, Name key) { DCHECK(transition_number < number_of_transitions()); WeakFixedArray::Set(ToKeyIndex(transition_number), HeapObjectReference::Strong(key)); } HeapObjectSlot TransitionArray::GetTargetSlot(int transition_number) { DCHECK(transition_number < number_of_transitions()); return HeapObjectSlot(RawFieldOfElementAt(ToTargetIndex(transition_number))); } // static PropertyDetails TransitionsAccessor::GetTargetDetails(Name name, Map target) { DCHECK(!IsSpecialTransition(name.GetReadOnlyRoots(), name)); int descriptor = target.LastAdded(); DescriptorArray descriptors = target.instance_descriptors(); // Transitions are allowed only for the last added property. DCHECK(descriptors.GetKey(descriptor).Equals(name)); return descriptors.GetDetails(descriptor); } PropertyDetails TransitionsAccessor::GetSimpleTargetDetails(Map transition) { return transition.GetLastDescriptorDetails(isolate_); } // static Name TransitionsAccessor::GetSimpleTransitionKey(Map transition) { int descriptor = transition.LastAdded(); return transition.instance_descriptors().GetKey(descriptor); } // static Map TransitionsAccessor::GetTargetFromRaw(MaybeObject raw) { return Map::cast(raw->GetHeapObjectAssumeWeak()); } MaybeObject TransitionArray::GetRawTarget(int transition_number) { DCHECK(transition_number < number_of_transitions()); return Get(ToTargetIndex(transition_number)); } Map TransitionArray::GetTarget(int transition_number) { MaybeObject raw = GetRawTarget(transition_number); return TransitionsAccessor::GetTargetFromRaw(raw); } Map TransitionsAccessor::GetTarget(int transition_number) { switch (encoding()) { case kPrototypeInfo: case kUninitialized: case kMigrationTarget: UNREACHABLE(); return Map(); case kWeakRef: return Map::cast(raw_transitions_->GetHeapObjectAssumeWeak()); case kFullTransitionArray: return transitions().GetTarget(transition_number); } UNREACHABLE(); } void TransitionArray::SetRawTarget(int transition_number, MaybeObject value) { DCHECK(transition_number < number_of_transitions()); DCHECK(value->IsWeak()); DCHECK(value->GetHeapObjectAssumeWeak().IsMap()); WeakFixedArray::Set(ToTargetIndex(transition_number), value); } bool TransitionArray::GetTargetIfExists(int transition_number, Isolate* isolate, Map* target) { MaybeObject raw = GetRawTarget(transition_number); HeapObject heap_object; if (raw->GetHeapObjectIfStrong(&heap_object) && heap_object.IsUndefined(isolate)) { return false; } *target = TransitionsAccessor::GetTargetFromRaw(raw); return true; } int TransitionArray::SearchNameForTesting(Name name, int* out_insertion_index) { return SearchName(name, out_insertion_index); } int TransitionArray::SearchSpecial(Symbol symbol, int* out_insertion_index) { return SearchName(symbol, out_insertion_index); } int TransitionArray::SearchName(Name name, int* out_insertion_index) { DCHECK(name.IsUniqueName()); return internal::Search(this, name, number_of_entries(), out_insertion_index); } TransitionsAccessor::TransitionsAccessor(Isolate* isolate, Map map, DisallowHeapAllocation* no_gc) : isolate_(isolate), map_(map) { Initialize(); USE(no_gc); } TransitionsAccessor::TransitionsAccessor(Isolate* isolate, Handle map) : isolate_(isolate), map_handle_(map), map_(*map) { Initialize(); } void TransitionsAccessor::Reload() { DCHECK(!map_handle_.is_null()); map_ = *map_handle_; Initialize(); } void TransitionsAccessor::Initialize() { raw_transitions_ = map_.raw_transitions(isolate_); HeapObject heap_object; if (raw_transitions_->IsSmi() || raw_transitions_->IsCleared()) { encoding_ = kUninitialized; } else if (raw_transitions_->IsWeak()) { encoding_ = kWeakRef; } else if (raw_transitions_->GetHeapObjectIfStrong(isolate_, &heap_object)) { if (heap_object.IsTransitionArray()) { encoding_ = kFullTransitionArray; } else if (heap_object.IsPrototypeInfo()) { encoding_ = kPrototypeInfo; } else { DCHECK(map_.is_deprecated()); DCHECK(heap_object.IsMap()); encoding_ = kMigrationTarget; } } else { UNREACHABLE(); } #if DEBUG needs_reload_ = false; #endif } int TransitionArray::number_of_transitions() const { if (length() < kFirstIndex) return 0; return Get(kTransitionLengthIndex).ToSmi().value(); } int TransitionArray::CompareKeys(Name key1, uint32_t hash1, PropertyKind kind1, PropertyAttributes attributes1, Name key2, uint32_t hash2, PropertyKind kind2, PropertyAttributes attributes2) { int cmp = CompareNames(key1, hash1, key2, hash2); if (cmp != 0) return cmp; return CompareDetails(kind1, attributes1, kind2, attributes2); } int TransitionArray::CompareNames(Name key1, uint32_t hash1, Name key2, uint32_t hash2) { if (key1 != key2) { // In case of hash collisions key1 is always "less" than key2. return hash1 <= hash2 ? -1 : 1; } return 0; } int TransitionArray::CompareDetails(PropertyKind kind1, PropertyAttributes attributes1, PropertyKind kind2, PropertyAttributes attributes2) { if (kind1 != kind2) { return static_cast(kind1) < static_cast(kind2) ? -1 : 1; } if (attributes1 != attributes2) { return static_cast(attributes1) < static_cast(attributes2) ? -1 : 1; } return 0; } void TransitionArray::Set(int transition_number, Name key, MaybeObject target) { WeakFixedArray::Set(ToKeyIndex(transition_number), MaybeObject::FromObject(key)); WeakFixedArray::Set(ToTargetIndex(transition_number), target); } Name TransitionArray::GetSortedKey(int transition_number) { return GetKey(transition_number); } int TransitionArray::number_of_entries() const { return number_of_transitions(); } int TransitionArray::Capacity() { if (length() <= kFirstIndex) return 0; return (length() - kFirstIndex) / kEntrySize; } void TransitionArray::SetNumberOfTransitions(int number_of_transitions) { DCHECK(number_of_transitions <= Capacity()); WeakFixedArray::Set( kTransitionLengthIndex, MaybeObject::FromSmi(Smi::FromInt(number_of_transitions))); } Handle TransitionsAccessor::ExpectedTransitionKey() { DisallowHeapAllocation no_gc; switch (encoding()) { case kPrototypeInfo: case kUninitialized: case kMigrationTarget: case kFullTransitionArray: return Handle::null(); case kWeakRef: { Map target = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak()); PropertyDetails details = GetSimpleTargetDetails(target); if (details.location() != kField) return Handle::null(); DCHECK_EQ(kData, details.kind()); if (details.attributes() != NONE) return Handle::null(); Name name = GetSimpleTransitionKey(target); if (!name.IsString()) return Handle::null(); return handle(String::cast(name), isolate_); } } UNREACHABLE(); } Handle TransitionsAccessor::ExpectedTransitionTarget() { DCHECK(!ExpectedTransitionKey().is_null()); return handle(GetTarget(0), isolate_); } } // namespace internal } // namespace v8 #include "src/objects/object-macros-undef.h" #endif // V8_OBJECTS_TRANSITIONS_INL_H_