Refactor TransitionArray access
in preparation for caching StoreIC-Transition handlers in there. This CL should not change behavior or performance. The TransitionArray class no longer serves a dual purpose; it is now simply the data structure serving that role. Further, it now supports storing transitioning handlers in its "target" slot, which in turn have a WeakCell pointing to the transition target (but this functionality is not being used yet). The interface for accessing a map's transitions, previously implemented as a set of static functions, is now handled by the TransitionsAccessor class. It distinguishes the following internal states: - kPrototypeInfo: map is a prototype map, will never cache any transitions. - kUninitialized: map can cache transitions, but doesn't have any. - kWeakCell: map caches a single transition, stored inline. Formerly known as "IsSimpleTransition". - kFullTransitionArray: map uses a TransitionArray to store transitions. - kTuple3Handler, kFixedArrayHandler: to be used in the future for caching transitioning handlers. Change-Id: If2aa68390981f96f317b958445a6e0b935c2a14e Reviewed-on: https://chromium-review.googlesource.com/550118 Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/master@{#46981}
This commit is contained in:
parent
c87a3ddaf1
commit
e567dd3ab4
1
BUILD.gn
1
BUILD.gn
@ -1613,6 +1613,7 @@ v8_source_set("v8_base") {
|
|||||||
"src/ic/handler-compiler.cc",
|
"src/ic/handler-compiler.cc",
|
||||||
"src/ic/handler-compiler.h",
|
"src/ic/handler-compiler.h",
|
||||||
"src/ic/handler-configuration-inl.h",
|
"src/ic/handler-configuration-inl.h",
|
||||||
|
"src/ic/handler-configuration.cc",
|
||||||
"src/ic/handler-configuration.h",
|
"src/ic/handler-configuration.h",
|
||||||
"src/ic/ic-inl.h",
|
"src/ic/ic-inl.h",
|
||||||
"src/ic/ic-state.cc",
|
"src/ic/ic-state.cc",
|
||||||
|
@ -1573,6 +1573,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
|||||||
// Implements DescriptorArray::ToKeyIndex.
|
// Implements DescriptorArray::ToKeyIndex.
|
||||||
// Returns an untagged IntPtr.
|
// Returns an untagged IntPtr.
|
||||||
Node* DescriptorArrayToKeyIndex(Node* descriptor_number);
|
Node* DescriptorArrayToKeyIndex(Node* descriptor_number);
|
||||||
|
// Implements DescriptorArray::GetKey.
|
||||||
|
Node* DescriptorArrayGetKey(Node* descriptors, Node* descriptor_number);
|
||||||
|
|
||||||
Node* CallGetterIfAccessor(Node* value, Node* details, Node* context,
|
Node* CallGetterIfAccessor(Node* value, Node* details, Node* context,
|
||||||
Node* receiver, Label* if_bailout);
|
Node* receiver, Label* if_bailout);
|
||||||
@ -1620,8 +1622,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
|||||||
// Returns an untagged int32.
|
// Returns an untagged int32.
|
||||||
Node* DescriptorArrayGetSortedKeyIndex(Node* descriptors,
|
Node* DescriptorArrayGetSortedKeyIndex(Node* descriptors,
|
||||||
Node* descriptor_number);
|
Node* descriptor_number);
|
||||||
// Implements DescriptorArray::GetKey.
|
|
||||||
Node* DescriptorArrayGetKey(Node* descriptors, Node* descriptor_number);
|
|
||||||
|
|
||||||
Node* CollectFeedbackForString(Node* instance_type);
|
Node* CollectFeedbackForString(Node* instance_type);
|
||||||
void GenerateEqual_Same(Node* value, Label* if_equal, Label* if_notequal,
|
void GenerateEqual_Same(Node* value, Label* if_equal, Label* if_notequal,
|
||||||
|
@ -595,58 +595,58 @@ bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name,
|
|||||||
MaybeHandle<JSObject> holder,
|
MaybeHandle<JSObject> holder,
|
||||||
PropertyAccessInfo* access_info) {
|
PropertyAccessInfo* access_info) {
|
||||||
// Check if the {map} has a data transition with the given {name}.
|
// Check if the {map} has a data transition with the given {name}.
|
||||||
Handle<Map> transition_map;
|
Map* transition =
|
||||||
if (TransitionArray::SearchTransition(map, kData, name, NONE)
|
TransitionsAccessor(map).SearchTransition(*name, kData, NONE);
|
||||||
.ToHandle(&transition_map)) {
|
if (transition == nullptr) return false;
|
||||||
int const number = transition_map->LastAdded();
|
|
||||||
PropertyDetails const details =
|
|
||||||
transition_map->instance_descriptors()->GetDetails(number);
|
|
||||||
// Don't bother optimizing stores to read-only properties.
|
|
||||||
if (details.IsReadOnly()) return false;
|
|
||||||
// TODO(bmeurer): Handle transition to data constant?
|
|
||||||
if (details.location() != kField) return false;
|
|
||||||
int const index = details.field_index();
|
|
||||||
Representation details_representation = details.representation();
|
|
||||||
FieldIndex field_index = FieldIndex::ForPropertyIndex(
|
|
||||||
*transition_map, index, details_representation.IsDouble());
|
|
||||||
Type* field_type = Type::NonInternal();
|
|
||||||
MaybeHandle<Map> field_map;
|
|
||||||
MachineRepresentation field_representation = MachineRepresentation::kTagged;
|
|
||||||
if (details_representation.IsSmi()) {
|
|
||||||
field_type = Type::SignedSmall();
|
|
||||||
field_representation = MachineRepresentation::kTaggedSigned;
|
|
||||||
} else if (details_representation.IsDouble()) {
|
|
||||||
field_type = type_cache_.kFloat64;
|
|
||||||
field_representation = MachineRepresentation::kFloat64;
|
|
||||||
} else if (details_representation.IsHeapObject()) {
|
|
||||||
// Extract the field type from the property details (make sure its
|
|
||||||
// representation is TaggedPointer to reflect the heap object case).
|
|
||||||
field_representation = MachineRepresentation::kTaggedPointer;
|
|
||||||
Handle<FieldType> descriptors_field_type(
|
|
||||||
transition_map->instance_descriptors()->GetFieldType(number),
|
|
||||||
isolate());
|
|
||||||
if (descriptors_field_type->IsNone()) {
|
|
||||||
// Store is not safe if the field type was cleared.
|
|
||||||
return false;
|
|
||||||
} else if (descriptors_field_type->IsClass()) {
|
|
||||||
// Add proper code dependencies in case of stable field map(s).
|
|
||||||
Handle<Map> field_owner_map(transition_map->FindFieldOwner(number),
|
|
||||||
isolate());
|
|
||||||
dependencies()->AssumeFieldOwner(field_owner_map);
|
|
||||||
|
|
||||||
// Remember the field map, and try to infer a useful type.
|
Handle<Map> transition_map(transition);
|
||||||
field_type = Type::For(descriptors_field_type->AsClass());
|
int const number = transition_map->LastAdded();
|
||||||
field_map = descriptors_field_type->AsClass();
|
PropertyDetails const details =
|
||||||
}
|
transition_map->instance_descriptors()->GetDetails(number);
|
||||||
|
// Don't bother optimizing stores to read-only properties.
|
||||||
|
if (details.IsReadOnly()) return false;
|
||||||
|
// TODO(bmeurer): Handle transition to data constant?
|
||||||
|
if (details.location() != kField) return false;
|
||||||
|
int const index = details.field_index();
|
||||||
|
Representation details_representation = details.representation();
|
||||||
|
FieldIndex field_index = FieldIndex::ForPropertyIndex(
|
||||||
|
*transition_map, index, details_representation.IsDouble());
|
||||||
|
Type* field_type = Type::NonInternal();
|
||||||
|
MaybeHandle<Map> field_map;
|
||||||
|
MachineRepresentation field_representation = MachineRepresentation::kTagged;
|
||||||
|
if (details_representation.IsSmi()) {
|
||||||
|
field_type = Type::SignedSmall();
|
||||||
|
field_representation = MachineRepresentation::kTaggedSigned;
|
||||||
|
} else if (details_representation.IsDouble()) {
|
||||||
|
field_type = type_cache_.kFloat64;
|
||||||
|
field_representation = MachineRepresentation::kFloat64;
|
||||||
|
} else if (details_representation.IsHeapObject()) {
|
||||||
|
// Extract the field type from the property details (make sure its
|
||||||
|
// representation is TaggedPointer to reflect the heap object case).
|
||||||
|
field_representation = MachineRepresentation::kTaggedPointer;
|
||||||
|
Handle<FieldType> descriptors_field_type(
|
||||||
|
transition_map->instance_descriptors()->GetFieldType(number),
|
||||||
|
isolate());
|
||||||
|
if (descriptors_field_type->IsNone()) {
|
||||||
|
// Store is not safe if the field type was cleared.
|
||||||
|
return false;
|
||||||
|
} else if (descriptors_field_type->IsClass()) {
|
||||||
|
// Add proper code dependencies in case of stable field map(s).
|
||||||
|
Handle<Map> field_owner_map(transition_map->FindFieldOwner(number),
|
||||||
|
isolate());
|
||||||
|
dependencies()->AssumeFieldOwner(field_owner_map);
|
||||||
|
|
||||||
|
// Remember the field map, and try to infer a useful type.
|
||||||
|
field_type = Type::For(descriptors_field_type->AsClass());
|
||||||
|
field_map = descriptors_field_type->AsClass();
|
||||||
}
|
}
|
||||||
dependencies()->AssumeMapNotDeprecated(transition_map);
|
|
||||||
// Transitioning stores are never stores to constant fields.
|
|
||||||
*access_info = PropertyAccessInfo::DataField(
|
|
||||||
kMutable, MapHandles{map}, field_index, field_representation,
|
|
||||||
field_type, field_map, holder, transition_map);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
dependencies()->AssumeMapNotDeprecated(transition_map);
|
||||||
|
// Transitioning stores are never stores to constant fields.
|
||||||
|
*access_info = PropertyAccessInfo::DataField(
|
||||||
|
kMutable, MapHandles{map}, field_index, field_representation, field_type,
|
||||||
|
field_map, holder, transition_map);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#define V8_FIELD_INDEX_INL_H_
|
#define V8_FIELD_INDEX_INL_H_
|
||||||
|
|
||||||
#include "src/field-index.h"
|
#include "src/field-index.h"
|
||||||
|
#include "src/objects-inl.h"
|
||||||
#include "src/objects/descriptor-array.h"
|
#include "src/objects/descriptor-array.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
|
@ -3448,10 +3448,11 @@ void Heap::RightTrimFixedArray(FixedArrayBase* object, int elements_to_trim) {
|
|||||||
int new_size = ByteArray::SizeFor(len - elements_to_trim);
|
int new_size = ByteArray::SizeFor(len - elements_to_trim);
|
||||||
bytes_to_trim = ByteArray::SizeFor(len) - new_size;
|
bytes_to_trim = ByteArray::SizeFor(len) - new_size;
|
||||||
DCHECK_GE(bytes_to_trim, 0);
|
DCHECK_GE(bytes_to_trim, 0);
|
||||||
|
} else if (object->IsFixedArray() || object->IsTransitionArray()) {
|
||||||
|
bytes_to_trim = elements_to_trim * kPointerSize;
|
||||||
} else {
|
} else {
|
||||||
const int element_size =
|
DCHECK(object->IsFixedDoubleArray());
|
||||||
object->IsFixedArray() ? kPointerSize : kDoubleSize;
|
bytes_to_trim = elements_to_trim * kDoubleSize;
|
||||||
bytes_to_trim = elements_to_trim * element_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "src/ic/ic.h"
|
#include "src/ic/ic.h"
|
||||||
#include "src/ic/stub-cache.h"
|
#include "src/ic/stub-cache.h"
|
||||||
#include "src/tracing/tracing-category-observer.h"
|
#include "src/tracing/tracing-category-observer.h"
|
||||||
|
#include "src/transitions-inl.h"
|
||||||
#include "src/utils-inl.h"
|
#include "src/utils-inl.h"
|
||||||
#include "src/v8.h"
|
#include "src/v8.h"
|
||||||
#include "src/v8threads.h"
|
#include "src/v8threads.h"
|
||||||
@ -2784,13 +2785,13 @@ void MarkCompactCollector::ClearNonLiveReferences() {
|
|||||||
heap()->ProcessAllWeakReferences(&mark_compact_object_retainer);
|
heap()->ProcessAllWeakReferences(&mark_compact_object_retainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
DependentCode* dependent_code_list;
|
|
||||||
ClearWeakCellsAndSimpleMapTransitions(&dependent_code_list);
|
|
||||||
{
|
{
|
||||||
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_MAPS);
|
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_MAPS);
|
||||||
|
// ClearFullMapTransitions must be called before WeakCells are cleared.
|
||||||
ClearFullMapTransitions();
|
ClearFullMapTransitions();
|
||||||
}
|
}
|
||||||
|
DependentCode* dependent_code_list;
|
||||||
|
ClearWeakCellsAndSimpleMapTransitions(&dependent_code_list);
|
||||||
MarkDependentCodeForDeoptimization(dependent_code_list);
|
MarkDependentCodeForDeoptimization(dependent_code_list);
|
||||||
|
|
||||||
ClearWeakCollections();
|
ClearWeakCollections();
|
||||||
@ -2863,8 +2864,10 @@ void MarkCompactCollector::ClearSimpleMapTransition(
|
|||||||
Object* potential_parent = dead_target->constructor_or_backpointer();
|
Object* potential_parent = dead_target->constructor_or_backpointer();
|
||||||
if (potential_parent->IsMap()) {
|
if (potential_parent->IsMap()) {
|
||||||
Map* parent = Map::cast(potential_parent);
|
Map* parent = Map::cast(potential_parent);
|
||||||
|
DisallowHeapAllocation no_gc_obviously;
|
||||||
if (ObjectMarking::IsBlackOrGrey(parent, marking_state(parent)) &&
|
if (ObjectMarking::IsBlackOrGrey(parent, marking_state(parent)) &&
|
||||||
parent->raw_transitions() == potential_transition) {
|
TransitionsAccessor(parent, &no_gc_obviously)
|
||||||
|
.HasSimpleTransitionTo(potential_transition)) {
|
||||||
ClearSimpleMapTransition(parent, dead_target);
|
ClearSimpleMapTransition(parent, dead_target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2885,16 +2888,15 @@ void MarkCompactCollector::ClearSimpleMapTransition(Map* map,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MarkCompactCollector::ClearFullMapTransitions() {
|
void MarkCompactCollector::ClearFullMapTransitions() {
|
||||||
HeapObject* undefined = heap()->undefined_value();
|
HeapObject* undefined = heap()->undefined_value();
|
||||||
Object* obj = heap()->encountered_transition_arrays();
|
Object* obj = heap()->encountered_transition_arrays();
|
||||||
while (obj != Smi::kZero) {
|
while (obj != Smi::kZero) {
|
||||||
TransitionArray* array = TransitionArray::cast(obj);
|
TransitionArray* array = TransitionArray::cast(obj);
|
||||||
int num_transitions = array->number_of_entries();
|
int num_transitions = array->number_of_entries();
|
||||||
DCHECK_EQ(TransitionArray::NumberOfTransitions(array), num_transitions);
|
|
||||||
if (num_transitions > 0) {
|
if (num_transitions > 0) {
|
||||||
Map* map = array->GetTarget(0);
|
Map* map = array->GetTarget(0);
|
||||||
|
DCHECK_NOT_NULL(map); // WeakCells aren't cleared yet.
|
||||||
Map* parent = Map::cast(map->constructor_or_backpointer());
|
Map* parent = Map::cast(map->constructor_or_backpointer());
|
||||||
bool parent_is_alive =
|
bool parent_is_alive =
|
||||||
ObjectMarking::IsBlackOrGrey(parent, MarkingState::Internal(parent));
|
ObjectMarking::IsBlackOrGrey(parent, MarkingState::Internal(parent));
|
||||||
@ -2912,7 +2914,6 @@ void MarkCompactCollector::ClearFullMapTransitions() {
|
|||||||
heap()->set_encountered_transition_arrays(Smi::kZero);
|
heap()->set_encountered_transition_arrays(Smi::kZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool MarkCompactCollector::CompactTransitionArray(
|
bool MarkCompactCollector::CompactTransitionArray(
|
||||||
Map* map, TransitionArray* transitions, DescriptorArray* descriptors) {
|
Map* map, TransitionArray* transitions, DescriptorArray* descriptors) {
|
||||||
int num_transitions = transitions->number_of_entries();
|
int num_transitions = transitions->number_of_entries();
|
||||||
@ -2933,8 +2934,14 @@ bool MarkCompactCollector::CompactTransitionArray(
|
|||||||
transitions->SetKey(transition_index, key);
|
transitions->SetKey(transition_index, key);
|
||||||
Object** key_slot = transitions->GetKeySlot(transition_index);
|
Object** key_slot = transitions->GetKeySlot(transition_index);
|
||||||
RecordSlot(transitions, key_slot, key);
|
RecordSlot(transitions, key_slot, key);
|
||||||
// Target slots do not need to be recorded since maps are not compacted.
|
Object* raw_target = transitions->GetRawTarget(i);
|
||||||
transitions->SetTarget(transition_index, transitions->GetTarget(i));
|
transitions->SetTarget(transition_index, raw_target);
|
||||||
|
// Maps are not compacted, but for cached handlers the target slot
|
||||||
|
// must be recorded.
|
||||||
|
if (!raw_target->IsMap()) {
|
||||||
|
Object** target_slot = transitions->GetTargetSlot(transition_index);
|
||||||
|
RecordSlot(transitions, target_slot, raw_target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
transition_index++;
|
transition_index++;
|
||||||
}
|
}
|
||||||
@ -2948,7 +2955,7 @@ bool MarkCompactCollector::CompactTransitionArray(
|
|||||||
// such that number_of_transitions() == 0. If this assumption changes,
|
// such that number_of_transitions() == 0. If this assumption changes,
|
||||||
// TransitionArray::Insert() will need to deal with the case that a transition
|
// TransitionArray::Insert() will need to deal with the case that a transition
|
||||||
// array disappeared during GC.
|
// array disappeared during GC.
|
||||||
int trim = TransitionArray::Capacity(transitions) - transition_index;
|
int trim = transitions->Capacity() - transition_index;
|
||||||
if (trim > 0) {
|
if (trim > 0) {
|
||||||
heap_->RightTrimFixedArray(transitions,
|
heap_->RightTrimFixedArray(transitions,
|
||||||
trim * TransitionArray::kTransitionSize);
|
trim * TransitionArray::kTransitionSize);
|
||||||
|
@ -199,9 +199,16 @@ int MarkingVisitor<ConcreteVisitor>::VisitTransitionArray(
|
|||||||
if (array->HasPrototypeTransitions()) {
|
if (array->HasPrototypeTransitions()) {
|
||||||
visitor->VisitPointer(array, array->GetPrototypeTransitionsSlot());
|
visitor->VisitPointer(array, array->GetPrototypeTransitionsSlot());
|
||||||
}
|
}
|
||||||
int num_transitions = TransitionArray::NumberOfTransitions(array);
|
int num_transitions = array->number_of_entries();
|
||||||
for (int i = 0; i < num_transitions; ++i) {
|
for (int i = 0; i < num_transitions; ++i) {
|
||||||
visitor->VisitPointer(array, array->GetKeySlot(i));
|
visitor->VisitPointer(array, array->GetKeySlot(i));
|
||||||
|
// A TransitionArray can hold maps or (transitioning StoreIC) handlers.
|
||||||
|
// Maps have custom weak handling; handlers (which in turn weakly point
|
||||||
|
// to maps) are marked strongly for now, and will be cleared during
|
||||||
|
// compaction when the maps they refer to are dead.
|
||||||
|
if (!array->GetRawTarget(i)->IsMap()) {
|
||||||
|
visitor->VisitPointer(array, array->GetTargetSlot(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Enqueue the array in linked list of encountered transition arrays if it is
|
// Enqueue the array in linked list of encountered transition arrays if it is
|
||||||
// not already in the list.
|
// not already in the list.
|
||||||
|
@ -165,6 +165,22 @@ Handle<Smi> StoreHandler::TransitionToConstant(Isolate* isolate,
|
|||||||
return handle(Smi::FromInt(config), isolate);
|
return handle(Smi::FromInt(config), isolate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
WeakCell* StoreHandler::GetTuple3TransitionCell(Object* tuple3_handler) {
|
||||||
|
STATIC_ASSERT(kTransitionCellOffset == Tuple3::kValue1Offset);
|
||||||
|
WeakCell* cell = WeakCell::cast(Tuple3::cast(tuple3_handler)->value1());
|
||||||
|
DCHECK(!cell->cleared());
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
WeakCell* StoreHandler::GetArrayTransitionCell(Object* array_handler) {
|
||||||
|
WeakCell* cell = WeakCell::cast(
|
||||||
|
FixedArray::cast(array_handler)->get(kTransitionCellIndex));
|
||||||
|
DCHECK(!cell->cleared());
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
|
||||||
|
82
src/ic/handler-configuration.cc
Normal file
82
src/ic/handler-configuration.cc
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// Copyright 2017 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.
|
||||||
|
|
||||||
|
#include "src/ic/handler-configuration.h"
|
||||||
|
|
||||||
|
#include "src/ic/handler-configuration-inl.h"
|
||||||
|
#include "src/transitions.h"
|
||||||
|
|
||||||
|
namespace v8 {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// |name| can be nullptr if no name/details check needs to be performed.
|
||||||
|
Object* StoreHandler::ValidTuple3HandlerOrNull(Object* handler, Name* name,
|
||||||
|
Handle<Map>* out_transition) {
|
||||||
|
DCHECK(handler->IsTuple3());
|
||||||
|
// Step 1: Check validity cell.
|
||||||
|
STATIC_ASSERT(kValidityCellOffset == Tuple3::kValue3Offset);
|
||||||
|
Object* raw_validity_cell = Tuple3::cast(handler)->value3();
|
||||||
|
Smi* valid = Smi::FromInt(Map::kPrototypeChainValid);
|
||||||
|
// |raw_valitity_cell| can be Smi::kZero if no validity cell is required
|
||||||
|
// (which counts as valid).
|
||||||
|
if (raw_validity_cell->IsCell() &&
|
||||||
|
Cell::cast(raw_validity_cell)->value() != valid) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// Step 2 (optional): Check transition key.
|
||||||
|
WeakCell* target_cell = StoreHandler::GetTuple3TransitionCell(handler);
|
||||||
|
if (name != nullptr) {
|
||||||
|
if (!TransitionsAccessor::IsMatchingMap(target_cell, name, kData, NONE)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Step 3: Check if the transition target is deprecated.
|
||||||
|
Map* transition = Map::cast(target_cell->value());
|
||||||
|
if (transition->is_deprecated()) return nullptr;
|
||||||
|
*out_transition = handle(transition);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* StoreHandler::ValidFixedArrayHandlerOrNull(
|
||||||
|
Object* raw_handler, Name* name, Handle<Map>* out_transition) {
|
||||||
|
DCHECK(raw_handler->IsFixedArray());
|
||||||
|
FixedArray* handler = FixedArray::cast(raw_handler);
|
||||||
|
// Step 1: Check validity cell.
|
||||||
|
Object* value = Cell::cast(handler->get(kValidityCellIndex))->value();
|
||||||
|
if (value != Smi::FromInt(Map::kPrototypeChainValid)) return nullptr;
|
||||||
|
// Step 2: Check transition key.
|
||||||
|
WeakCell* target_cell = StoreHandler::GetArrayTransitionCell(handler);
|
||||||
|
if (!TransitionsAccessor::IsMatchingMap(target_cell, name, kData, NONE)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// Step 3: Check prototypes.
|
||||||
|
Heap* heap = handler->GetHeap();
|
||||||
|
Isolate* isolate = heap->isolate();
|
||||||
|
Handle<Name> name_handle(name, isolate);
|
||||||
|
for (int i = kFirstPrototypeIndex; i < handler->length(); i++) {
|
||||||
|
// This mirrors AccessorAssembler::CheckPrototype.
|
||||||
|
WeakCell* prototype_cell = WeakCell::cast(handler->get(i));
|
||||||
|
if (prototype_cell->cleared()) return nullptr;
|
||||||
|
HeapObject* maybe_prototype = HeapObject::cast(prototype_cell->value());
|
||||||
|
if (maybe_prototype->IsPropertyCell()) {
|
||||||
|
Object* value = PropertyCell::cast(maybe_prototype)->value();
|
||||||
|
if (value != heap->the_hole_value()) return nullptr;
|
||||||
|
} else {
|
||||||
|
DCHECK(maybe_prototype->map()->is_dictionary_map());
|
||||||
|
// Do a negative dictionary lookup.
|
||||||
|
NameDictionary* dict =
|
||||||
|
JSObject::cast(maybe_prototype)->property_dictionary();
|
||||||
|
int number = dict->FindEntry(isolate, name_handle);
|
||||||
|
if (number != NameDictionary::kNotFound) return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Step 4: Check if the transition target is deprecated.
|
||||||
|
Map* transition = Map::cast(target_cell->value());
|
||||||
|
if (transition->is_deprecated()) return nullptr;
|
||||||
|
*out_transition = handle(transition);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace v8
|
@ -8,6 +8,7 @@
|
|||||||
#include "src/elements-kind.h"
|
#include "src/elements-kind.h"
|
||||||
#include "src/field-index.h"
|
#include "src/field-index.h"
|
||||||
#include "src/globals.h"
|
#include "src/globals.h"
|
||||||
|
#include "src/objects.h"
|
||||||
#include "src/utils.h"
|
#include "src/utils.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
@ -186,6 +187,10 @@ class StoreHandler {
|
|||||||
static const int kSmiHandlerOffset = Tuple3::kValue2Offset;
|
static const int kSmiHandlerOffset = Tuple3::kValue2Offset;
|
||||||
static const int kValidityCellOffset = Tuple3::kValue3Offset;
|
static const int kValidityCellOffset = Tuple3::kValue3Offset;
|
||||||
|
|
||||||
|
static inline WeakCell* GetTuple3TransitionCell(Object* tuple3_handler);
|
||||||
|
static Object* ValidTuple3HandlerOrNull(Object* handler, Name* name,
|
||||||
|
Handle<Map>* out_transition);
|
||||||
|
|
||||||
// The layout of an array handler representing a transitioning store
|
// The layout of an array handler representing a transitioning store
|
||||||
// when prototype chain checks include non-existing lookups and access checks.
|
// when prototype chain checks include non-existing lookups and access checks.
|
||||||
static const int kSmiHandlerIndex = 0;
|
static const int kSmiHandlerIndex = 0;
|
||||||
@ -193,6 +198,10 @@ class StoreHandler {
|
|||||||
static const int kTransitionCellIndex = 2;
|
static const int kTransitionCellIndex = 2;
|
||||||
static const int kFirstPrototypeIndex = 3;
|
static const int kFirstPrototypeIndex = 3;
|
||||||
|
|
||||||
|
static inline WeakCell* GetArrayTransitionCell(Object* array_handler);
|
||||||
|
static Object* ValidFixedArrayHandlerOrNull(Object* raw_handler, Name* name,
|
||||||
|
Handle<Map>* out_transition);
|
||||||
|
|
||||||
// Creates a Smi-handler for storing a field to fast object.
|
// Creates a Smi-handler for storing a field to fast object.
|
||||||
static inline Handle<Smi> StoreField(Isolate* isolate, int descriptor,
|
static inline Handle<Smi> StoreField(Isolate* isolate, int descriptor,
|
||||||
FieldIndex field_index,
|
FieldIndex field_index,
|
||||||
|
@ -368,19 +368,22 @@ Handle<Object> JsonParser<seq_one_byte>::ParseJsonObject() {
|
|||||||
bool follow_expected = false;
|
bool follow_expected = false;
|
||||||
Handle<Map> target;
|
Handle<Map> target;
|
||||||
if (seq_one_byte) {
|
if (seq_one_byte) {
|
||||||
key = TransitionArray::ExpectedTransitionKey(map);
|
DisallowHeapAllocation no_gc;
|
||||||
|
TransitionsAccessor transitions(*map, &no_gc);
|
||||||
|
key = transitions.ExpectedTransitionKey();
|
||||||
follow_expected = !key.is_null() && ParseJsonString(key);
|
follow_expected = !key.is_null() && ParseJsonString(key);
|
||||||
|
// If the expected transition hits, follow it.
|
||||||
|
if (follow_expected) {
|
||||||
|
target = transitions.ExpectedTransitionTarget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If the expected transition hits, follow it.
|
if (!follow_expected) {
|
||||||
if (follow_expected) {
|
|
||||||
target = TransitionArray::ExpectedTransitionTarget(map);
|
|
||||||
} else {
|
|
||||||
// If the expected transition failed, parse an internalized string and
|
// If the expected transition failed, parse an internalized string and
|
||||||
// try to find a matching transition.
|
// try to find a matching transition.
|
||||||
key = ParseJsonInternalizedString();
|
key = ParseJsonInternalizedString();
|
||||||
if (key.is_null()) return ReportUnexpectedCharacter();
|
if (key.is_null()) return ReportUnexpectedCharacter();
|
||||||
|
|
||||||
target = TransitionArray::FindTransitionToField(map, key);
|
target = TransitionsAccessor(map).FindTransitionToField(key);
|
||||||
// If a transition was found, follow it and continue.
|
// If a transition was found, follow it and continue.
|
||||||
transitioning = !target.is_null();
|
transitioning = !target.is_null();
|
||||||
}
|
}
|
||||||
|
@ -328,8 +328,9 @@ MapUpdater::State MapUpdater::FindTargetMap() {
|
|||||||
int root_nof = root_map_->NumberOfOwnDescriptors();
|
int root_nof = root_map_->NumberOfOwnDescriptors();
|
||||||
for (int i = root_nof; i < old_nof_; ++i) {
|
for (int i = root_nof; i < old_nof_; ++i) {
|
||||||
PropertyDetails old_details = GetDetails(i);
|
PropertyDetails old_details = GetDetails(i);
|
||||||
Map* transition = TransitionArray::SearchTransition(
|
Map* transition = TransitionsAccessor(target_map_)
|
||||||
*target_map_, old_details.kind(), GetKey(i), old_details.attributes());
|
.SearchTransition(GetKey(i), old_details.kind(),
|
||||||
|
old_details.attributes());
|
||||||
if (transition == NULL) break;
|
if (transition == NULL) break;
|
||||||
Handle<Map> tmp_map(transition, isolate_);
|
Handle<Map> tmp_map(transition, isolate_);
|
||||||
|
|
||||||
@ -410,8 +411,9 @@ MapUpdater::State MapUpdater::FindTargetMap() {
|
|||||||
// Find the last compatible target map in the transition tree.
|
// Find the last compatible target map in the transition tree.
|
||||||
for (int i = target_nof; i < old_nof_; ++i) {
|
for (int i = target_nof; i < old_nof_; ++i) {
|
||||||
PropertyDetails old_details = GetDetails(i);
|
PropertyDetails old_details = GetDetails(i);
|
||||||
Map* transition = TransitionArray::SearchTransition(
|
Map* transition = TransitionsAccessor(target_map_)
|
||||||
*target_map_, old_details.kind(), GetKey(i), old_details.attributes());
|
.SearchTransition(GetKey(i), old_details.kind(),
|
||||||
|
old_details.attributes());
|
||||||
if (transition == NULL) break;
|
if (transition == NULL) break;
|
||||||
Handle<Map> tmp_map(transition, isolate_);
|
Handle<Map> tmp_map(transition, isolate_);
|
||||||
Handle<DescriptorArray> tmp_descriptors(tmp_map->instance_descriptors(),
|
Handle<DescriptorArray> tmp_descriptors(tmp_map->instance_descriptors(),
|
||||||
@ -612,8 +614,9 @@ Handle<Map> MapUpdater::FindSplitMap(Handle<DescriptorArray> descriptors) {
|
|||||||
for (int i = root_nof; i < old_nof_; i++) {
|
for (int i = root_nof; i < old_nof_; i++) {
|
||||||
Name* name = descriptors->GetKey(i);
|
Name* name = descriptors->GetKey(i);
|
||||||
PropertyDetails details = descriptors->GetDetails(i);
|
PropertyDetails details = descriptors->GetDetails(i);
|
||||||
Map* next = TransitionArray::SearchTransition(current, details.kind(), name,
|
Map* next =
|
||||||
details.attributes());
|
TransitionsAccessor(current, &no_allocation)
|
||||||
|
.SearchTransition(name, details.kind(), details.attributes());
|
||||||
if (next == NULL) break;
|
if (next == NULL) break;
|
||||||
DescriptorArray* next_descriptors = next->instance_descriptors();
|
DescriptorArray* next_descriptors = next->instance_descriptors();
|
||||||
|
|
||||||
@ -648,11 +651,11 @@ MapUpdater::State MapUpdater::ConstructNewMap() {
|
|||||||
DCHECK_NE(old_nof_, split_nof);
|
DCHECK_NE(old_nof_, split_nof);
|
||||||
|
|
||||||
PropertyDetails split_details = GetDetails(split_nof);
|
PropertyDetails split_details = GetDetails(split_nof);
|
||||||
|
TransitionsAccessor transitions(split_map);
|
||||||
|
|
||||||
// Invalidate a transition target at |key|.
|
// Invalidate a transition target at |key|.
|
||||||
Map* maybe_transition = TransitionArray::SearchTransition(
|
Map* maybe_transition = transitions.SearchTransition(
|
||||||
*split_map, split_details.kind(), GetKey(split_nof),
|
GetKey(split_nof), split_details.kind(), split_details.attributes());
|
||||||
split_details.attributes());
|
|
||||||
if (maybe_transition != NULL) {
|
if (maybe_transition != NULL) {
|
||||||
maybe_transition->DeprecateTransitionTree();
|
maybe_transition->DeprecateTransitionTree();
|
||||||
}
|
}
|
||||||
@ -660,8 +663,7 @@ MapUpdater::State MapUpdater::ConstructNewMap() {
|
|||||||
// If |maybe_transition| is not NULL then the transition array already
|
// If |maybe_transition| is not NULL then the transition array already
|
||||||
// contains entry for given descriptor. This means that the transition
|
// contains entry for given descriptor. This means that the transition
|
||||||
// could be inserted regardless of whether transitions array is full or not.
|
// could be inserted regardless of whether transitions array is full or not.
|
||||||
if (maybe_transition == NULL &&
|
if (maybe_transition == NULL && !transitions.CanHaveMoreTransitions()) {
|
||||||
!TransitionArray::CanHaveMoreTransitions(split_map)) {
|
|
||||||
return CopyGeneralizeAllFields("GenAll_CantHaveMoreTransitions");
|
return CopyGeneralizeAllFields("GenAll_CantHaveMoreTransitions");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,11 +433,11 @@ void Map::MapVerify() {
|
|||||||
VerifyHeapPointer(prototype());
|
VerifyHeapPointer(prototype());
|
||||||
VerifyHeapPointer(instance_descriptors());
|
VerifyHeapPointer(instance_descriptors());
|
||||||
SLOW_DCHECK(instance_descriptors()->IsSortedNoDuplicates());
|
SLOW_DCHECK(instance_descriptors()->IsSortedNoDuplicates());
|
||||||
SLOW_DCHECK(TransitionArray::IsSortedNoDuplicates(this));
|
DisallowHeapAllocation no_gc;
|
||||||
SLOW_DCHECK(TransitionArray::IsConsistentWithBackPointers(this));
|
SLOW_DCHECK(TransitionsAccessor(this, &no_gc).IsSortedNoDuplicates());
|
||||||
// TODO(ishell): turn it back to SLOW_DCHECK.
|
SLOW_DCHECK(TransitionsAccessor(this, &no_gc).IsConsistentWithBackPointers());
|
||||||
CHECK(!FLAG_unbox_double_fields ||
|
SLOW_DCHECK(!FLAG_unbox_double_fields ||
|
||||||
layout_descriptor()->IsConsistentWithMap(this));
|
layout_descriptor()->IsConsistentWithMap(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1593,9 +1593,10 @@ bool TransitionArray::IsSortedNoDuplicates(int valid_entries) {
|
|||||||
uint32_t hash = key->Hash();
|
uint32_t hash = key->Hash();
|
||||||
PropertyKind kind = kData;
|
PropertyKind kind = kData;
|
||||||
PropertyAttributes attributes = NONE;
|
PropertyAttributes attributes = NONE;
|
||||||
if (!IsSpecialTransition(key)) {
|
if (!TransitionsAccessor::IsSpecialTransition(key)) {
|
||||||
Map* target = GetTarget(i);
|
Map* target = GetTarget(i);
|
||||||
PropertyDetails details = GetTargetDetails(key, target);
|
PropertyDetails details =
|
||||||
|
TransitionsAccessor::GetTargetDetails(key, target);
|
||||||
kind = details.kind();
|
kind = details.kind();
|
||||||
attributes = details.attributes();
|
attributes = details.attributes();
|
||||||
} else {
|
} else {
|
||||||
@ -1617,15 +1618,10 @@ bool TransitionArray::IsSortedNoDuplicates(int valid_entries) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TransitionsAccessor::IsSortedNoDuplicates() {
|
||||||
// static
|
|
||||||
bool TransitionArray::IsSortedNoDuplicates(Map* map) {
|
|
||||||
Object* raw_transitions = map->raw_transitions();
|
|
||||||
if (IsFullTransitionArray(raw_transitions)) {
|
|
||||||
return TransitionArray::cast(raw_transitions)->IsSortedNoDuplicates();
|
|
||||||
}
|
|
||||||
// Simple and non-existent transitions are always sorted.
|
// Simple and non-existent transitions are always sorted.
|
||||||
return true;
|
if (encoding() != kFullTransitionArray) return true;
|
||||||
|
return transitions()->IsSortedNoDuplicates();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1633,18 +1629,15 @@ static bool CheckOneBackPointer(Map* current_map, Object* target) {
|
|||||||
return !target->IsMap() || Map::cast(target)->GetBackPointer() == current_map;
|
return !target->IsMap() || Map::cast(target)->GetBackPointer() == current_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TransitionsAccessor::IsConsistentWithBackPointers() {
|
||||||
// static
|
int num_transitions = NumberOfTransitions();
|
||||||
bool TransitionArray::IsConsistentWithBackPointers(Map* map) {
|
for (int i = 0; i < num_transitions; i++) {
|
||||||
Object* transitions = map->raw_transitions();
|
Map* target = GetTarget(i);
|
||||||
for (int i = 0; i < TransitionArray::NumberOfTransitions(transitions); ++i) {
|
if (!CheckOneBackPointer(map_, target)) return false;
|
||||||
Map* target = TransitionArray::GetTarget(transitions, i);
|
|
||||||
if (!CheckOneBackPointer(map, target)) return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Estimates if there is a path from the object to a context.
|
// Estimates if there is a path from the object to a context.
|
||||||
// This function is not precise, and can return false even if
|
// This function is not precise, and can return false even if
|
||||||
// there is a path to a context.
|
// there is a path to a context.
|
||||||
|
@ -135,9 +135,7 @@ bool HeapObject::IsFixedArrayBase() const {
|
|||||||
|
|
||||||
bool HeapObject::IsFixedArray() const {
|
bool HeapObject::IsFixedArray() const {
|
||||||
InstanceType instance_type = map()->instance_type();
|
InstanceType instance_type = map()->instance_type();
|
||||||
return instance_type == FIXED_ARRAY_TYPE ||
|
return instance_type == FIXED_ARRAY_TYPE || instance_type == HASH_TABLE_TYPE;
|
||||||
instance_type == HASH_TABLE_TYPE ||
|
|
||||||
instance_type == TRANSITION_ARRAY_TYPE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HeapObject::IsSloppyArgumentsElements() const { return IsFixedArray(); }
|
bool HeapObject::IsSloppyArgumentsElements() const { return IsFixedArray(); }
|
||||||
@ -1730,7 +1728,7 @@ void FixedArray::set(int index, Smi* value) {
|
|||||||
|
|
||||||
void FixedArray::set(int index, Object* value) {
|
void FixedArray::set(int index, Object* value) {
|
||||||
DCHECK_NE(GetHeap()->fixed_cow_array_map(), map());
|
DCHECK_NE(GetHeap()->fixed_cow_array_map(), map());
|
||||||
DCHECK(IsFixedArray());
|
DCHECK(IsFixedArray() || IsTransitionArray());
|
||||||
DCHECK_GE(index, 0);
|
DCHECK_GE(index, 0);
|
||||||
DCHECK_LT(index, this->length());
|
DCHECK_LT(index, this->length());
|
||||||
int offset = kHeaderSize + index * kPointerSize;
|
int offset = kHeaderSize + index * kPointerSize;
|
||||||
@ -4242,9 +4240,10 @@ Object* Map::GetBackPointer() const {
|
|||||||
return GetIsolate()->heap()->undefined_value();
|
return GetIsolate()->heap()->undefined_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map* Map::ElementsTransitionMap() const {
|
Map* Map::ElementsTransitionMap() {
|
||||||
return TransitionArray::SearchSpecial(
|
DisallowHeapAllocation no_gc;
|
||||||
this, GetHeap()->elements_transition_symbol());
|
return TransitionsAccessor(this, &no_gc)
|
||||||
|
.SearchSpecial(GetHeap()->elements_transition_symbol());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "src/objects/debug-objects-inl.h"
|
#include "src/objects/debug-objects-inl.h"
|
||||||
#include "src/ostreams.h"
|
#include "src/ostreams.h"
|
||||||
#include "src/regexp/jsregexp.h"
|
#include "src/regexp/jsregexp.h"
|
||||||
|
#include "src/transitions-inl.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
@ -624,11 +625,15 @@ void Map::MapPrint(std::ostream& os) { // NOLINT
|
|||||||
os << "\n - layout descriptor: ";
|
os << "\n - layout descriptor: ";
|
||||||
layout_descriptor()->ShortPrint(os);
|
layout_descriptor()->ShortPrint(os);
|
||||||
}
|
}
|
||||||
int nof_transitions = TransitionArray::NumberOfTransitions(raw_transitions());
|
{
|
||||||
if (nof_transitions > 0) {
|
DisallowHeapAllocation no_gc;
|
||||||
os << "\n - transitions #" << nof_transitions << ": "
|
TransitionsAccessor transitions(this, &no_gc);
|
||||||
<< Brief(raw_transitions());
|
int nof_transitions = transitions.NumberOfTransitions();
|
||||||
TransitionArray::PrintTransitions(os, raw_transitions(), false);
|
if (nof_transitions > 0) {
|
||||||
|
os << "\n - transitions #" << nof_transitions << ": "
|
||||||
|
<< Brief(raw_transitions());
|
||||||
|
transitions.PrintTransitions(os);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
os << "\n - prototype: " << Brief(prototype());
|
os << "\n - prototype: " << Brief(prototype());
|
||||||
os << "\n - constructor: " << Brief(GetConstructor());
|
os << "\n - constructor: " << Brief(GetConstructor());
|
||||||
@ -1690,70 +1695,99 @@ void DescriptorArray::PrintDescriptorDetails(std::ostream& os, int descriptor,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void TransitionsAccessor::PrintOneTransition(std::ostream& os, Name* key,
|
||||||
|
Map* target, Object* raw_target) {
|
||||||
|
os << "\n ";
|
||||||
|
#ifdef OBJECT_PRINT
|
||||||
|
key->NamePrint(os);
|
||||||
|
#else
|
||||||
|
key->ShortPrint(os);
|
||||||
|
#endif
|
||||||
|
os << ": ";
|
||||||
|
Heap* heap = key->GetHeap();
|
||||||
|
if (key == heap->nonextensible_symbol()) {
|
||||||
|
os << "(transition to non-extensible)";
|
||||||
|
} else if (key == heap->sealed_symbol()) {
|
||||||
|
os << "(transition to sealed)";
|
||||||
|
} else if (key == heap->frozen_symbol()) {
|
||||||
|
os << "(transition to frozen)";
|
||||||
|
} else if (key == heap->elements_transition_symbol()) {
|
||||||
|
os << "(transition to " << ElementsKindToString(target->elements_kind())
|
||||||
|
<< ")";
|
||||||
|
} else if (key == heap->strict_function_transition_symbol()) {
|
||||||
|
os << " (transition to strict function)";
|
||||||
|
} else {
|
||||||
|
DCHECK(!IsSpecialTransition(key));
|
||||||
|
os << "(transition to ";
|
||||||
|
int descriptor = target->LastAdded();
|
||||||
|
DescriptorArray* descriptors = target->instance_descriptors();
|
||||||
|
descriptors->PrintDescriptorDetails(os, descriptor,
|
||||||
|
PropertyDetails::kForTransitions);
|
||||||
|
os << ")";
|
||||||
|
}
|
||||||
|
os << " -> " << Brief(target);
|
||||||
|
if (!raw_target->IsMap() && !raw_target->IsWeakCell()) {
|
||||||
|
os << " (handler: " << Brief(raw_target) << ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TransitionArray::Print() {
|
void TransitionArray::Print() {
|
||||||
OFStream os(stdout);
|
OFStream os(stdout);
|
||||||
TransitionArray::PrintTransitions(os, this);
|
Print(os);
|
||||||
os << "\n" << std::flush;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionArray::Print(std::ostream& os) {
|
||||||
void TransitionArray::PrintTransitions(std::ostream& os, Object* transitions,
|
int num_transitions = number_of_transitions();
|
||||||
bool print_header) { // NOLINT
|
os << "Transition array #" << num_transitions << ":";
|
||||||
int num_transitions = NumberOfTransitions(transitions);
|
|
||||||
if (print_header) {
|
|
||||||
os << "Transition array #" << num_transitions << ":";
|
|
||||||
}
|
|
||||||
for (int i = 0; i < num_transitions; i++) {
|
for (int i = 0; i < num_transitions; i++) {
|
||||||
Name* key = GetKey(transitions, i);
|
Name* key = GetKey(i);
|
||||||
Map* target = GetTarget(transitions, i);
|
Map* target = GetTarget(i);
|
||||||
os << "\n ";
|
Object* raw_target = GetRawTarget(i);
|
||||||
#ifdef OBJECT_PRINT
|
TransitionsAccessor::PrintOneTransition(os, key, target, raw_target);
|
||||||
key->NamePrint(os);
|
|
||||||
#else
|
|
||||||
key->ShortPrint(os);
|
|
||||||
#endif
|
|
||||||
os << ": ";
|
|
||||||
Heap* heap = key->GetHeap();
|
|
||||||
if (key == heap->nonextensible_symbol()) {
|
|
||||||
os << "(transition to non-extensible)";
|
|
||||||
} else if (key == heap->sealed_symbol()) {
|
|
||||||
os << "(transition to sealed)";
|
|
||||||
} else if (key == heap->frozen_symbol()) {
|
|
||||||
os << "(transition to frozen)";
|
|
||||||
} else if (key == heap->elements_transition_symbol()) {
|
|
||||||
os << "(transition to " << ElementsKindToString(target->elements_kind())
|
|
||||||
<< ")";
|
|
||||||
} else if (key == heap->strict_function_transition_symbol()) {
|
|
||||||
os << " (transition to strict function)";
|
|
||||||
} else {
|
|
||||||
DCHECK(!IsSpecialTransition(key));
|
|
||||||
os << "(transition to ";
|
|
||||||
int descriptor = target->LastAdded();
|
|
||||||
DescriptorArray* descriptors = target->instance_descriptors();
|
|
||||||
descriptors->PrintDescriptorDetails(os, descriptor,
|
|
||||||
PropertyDetails::kForTransitions);
|
|
||||||
os << ")";
|
|
||||||
}
|
|
||||||
os << " -> " << Brief(target);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void TransitionArray::PrintTransitionTree(Map* map) {
|
|
||||||
OFStream os(stdout);
|
|
||||||
os << "map= " << Brief(map);
|
|
||||||
PrintTransitionTree(os, map);
|
|
||||||
os << "\n" << std::flush;
|
os << "\n" << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
void TransitionsAccessor::PrintTransitions(std::ostream& os) { // NOLINT
|
||||||
void TransitionArray::PrintTransitionTree(std::ostream& os, Map* map,
|
WeakCell* cell = nullptr;
|
||||||
int level) {
|
switch (encoding()) {
|
||||||
Object* transitions = map->raw_transitions();
|
case kPrototypeInfo:
|
||||||
int num_transitions = NumberOfTransitions(transitions);
|
case kUninitialized:
|
||||||
|
return;
|
||||||
|
case kWeakCell:
|
||||||
|
cell = GetTargetCell<kWeakCell>();
|
||||||
|
break;
|
||||||
|
case kTuple3Handler:
|
||||||
|
cell = GetTargetCell<kTuple3Handler>();
|
||||||
|
break;
|
||||||
|
case kFixedArrayHandler:
|
||||||
|
cell = GetTargetCell<kFixedArrayHandler>();
|
||||||
|
break;
|
||||||
|
case kFullTransitionArray:
|
||||||
|
return transitions()->Print(os);
|
||||||
|
}
|
||||||
|
DCHECK(!cell->cleared());
|
||||||
|
Map* target = Map::cast(cell->value());
|
||||||
|
Name* key = GetSimpleTransitionKey(target);
|
||||||
|
PrintOneTransition(os, key, target, raw_transitions_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransitionsAccessor::PrintTransitionTree() {
|
||||||
|
OFStream os(stdout);
|
||||||
|
os << "map= " << Brief(map_);
|
||||||
|
DisallowHeapAllocation no_gc;
|
||||||
|
PrintTransitionTree(os, 0, &no_gc);
|
||||||
|
os << "\n" << std::flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransitionsAccessor::PrintTransitionTree(std::ostream& os, int level,
|
||||||
|
DisallowHeapAllocation* no_gc) {
|
||||||
|
int num_transitions = NumberOfTransitions();
|
||||||
if (num_transitions == 0) return;
|
if (num_transitions == 0) return;
|
||||||
for (int i = 0; i < num_transitions; i++) {
|
for (int i = 0; i < num_transitions; i++) {
|
||||||
Name* key = GetKey(transitions, i);
|
Name* key = GetKey(i);
|
||||||
Map* target = GetTarget(transitions, i);
|
Map* target = GetTarget(i);
|
||||||
os << std::endl
|
os << std::endl
|
||||||
<< " " << level << "/" << i << ":" << std::setw(level * 2 + 2) << " ";
|
<< " " << level << "/" << i << ":" << std::setw(level * 2 + 2) << " ";
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
@ -1785,16 +1819,17 @@ void TransitionArray::PrintTransitionTree(std::ostream& os, Map* map,
|
|||||||
descriptors->PrintDescriptorDetails(os, descriptor,
|
descriptors->PrintDescriptorDetails(os, descriptor,
|
||||||
PropertyDetails::kForTransitions);
|
PropertyDetails::kForTransitions);
|
||||||
}
|
}
|
||||||
TransitionArray::PrintTransitionTree(os, target, level + 1);
|
TransitionsAccessor transitions(target, no_gc);
|
||||||
|
transitions.PrintTransitionTree(os, level + 1, no_gc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JSObject::PrintTransitions(std::ostream& os) { // NOLINT
|
void JSObject::PrintTransitions(std::ostream& os) { // NOLINT
|
||||||
Object* transitions = map()->raw_transitions();
|
DisallowHeapAllocation no_gc;
|
||||||
int num_transitions = TransitionArray::NumberOfTransitions(transitions);
|
TransitionsAccessor ta(map(), &no_gc);
|
||||||
if (num_transitions == 0) return;
|
if (ta.NumberOfTransitions() == 0) return;
|
||||||
os << "\n - transitions";
|
os << "\n - transitions";
|
||||||
TransitionArray::PrintTransitions(os, transitions, false);
|
ta.PrintTransitions(os);
|
||||||
}
|
}
|
||||||
#endif // defined(DEBUG) || defined(OBJECT_PRINT)
|
#endif // defined(DEBUG) || defined(OBJECT_PRINT)
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
@ -1864,7 +1899,10 @@ extern void _v8_internal_Print_TransitionTree(void* object) {
|
|||||||
printf("Please provide a valid Map\n");
|
printf("Please provide a valid Map\n");
|
||||||
} else {
|
} else {
|
||||||
#if defined(DEBUG) || defined(OBJECT_PRINT)
|
#if defined(DEBUG) || defined(OBJECT_PRINT)
|
||||||
i::TransitionArray::PrintTransitionTree(reinterpret_cast<i::Map*>(object));
|
i::DisallowHeapAllocation no_gc;
|
||||||
|
i::TransitionsAccessor transitions(reinterpret_cast<i::Map*>(object),
|
||||||
|
&no_gc);
|
||||||
|
transitions.PrintTransitionTree();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4184,8 +4184,7 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
|
|||||||
old_map->GetHeap()->empty_descriptor_array(),
|
old_map->GetHeap()->empty_descriptor_array(),
|
||||||
LayoutDescriptor::FastPointerLayout());
|
LayoutDescriptor::FastPointerLayout());
|
||||||
// Ensure that no transition was inserted for prototype migrations.
|
// Ensure that no transition was inserted for prototype migrations.
|
||||||
DCHECK_EQ(
|
DCHECK_EQ(0, TransitionsAccessor(old_map).NumberOfTransitions());
|
||||||
0, TransitionArray::NumberOfTransitions(old_map->raw_transitions()));
|
|
||||||
DCHECK(new_map->GetBackPointer()->IsUndefined(new_map->GetIsolate()));
|
DCHECK(new_map->GetBackPointer()->IsUndefined(new_map->GetIsolate()));
|
||||||
#ifdef VERIFY_HEAP
|
#ifdef VERIFY_HEAP
|
||||||
// When verify heap is on, NotifyObjectLayoutChange checks that
|
// When verify heap is on, NotifyObjectLayoutChange checks that
|
||||||
@ -4298,10 +4297,11 @@ Handle<Map> Map::CopyGeneralizeAllFields(Handle<Map> map,
|
|||||||
|
|
||||||
void Map::DeprecateTransitionTree() {
|
void Map::DeprecateTransitionTree() {
|
||||||
if (is_deprecated()) return;
|
if (is_deprecated()) return;
|
||||||
Object* transitions = raw_transitions();
|
DisallowHeapAllocation no_gc;
|
||||||
int num_transitions = TransitionArray::NumberOfTransitions(transitions);
|
TransitionsAccessor transitions(this, &no_gc);
|
||||||
|
int num_transitions = transitions.NumberOfTransitions();
|
||||||
for (int i = 0; i < num_transitions; ++i) {
|
for (int i = 0; i < num_transitions; ++i) {
|
||||||
TransitionArray::GetTarget(transitions, i)->DeprecateTransitionTree();
|
transitions.GetTarget(i)->DeprecateTransitionTree();
|
||||||
}
|
}
|
||||||
DCHECK(!constructor_or_backpointer()->IsFunctionTemplateInfo());
|
DCHECK(!constructor_or_backpointer()->IsFunctionTemplateInfo());
|
||||||
deprecate();
|
deprecate();
|
||||||
@ -4388,10 +4388,10 @@ void Map::UpdateFieldType(int descriptor, Handle<Name> name,
|
|||||||
Map* current = backlog.front();
|
Map* current = backlog.front();
|
||||||
backlog.pop();
|
backlog.pop();
|
||||||
|
|
||||||
Object* transitions = current->raw_transitions();
|
TransitionsAccessor transitions(current, &no_allocation);
|
||||||
int num_transitions = TransitionArray::NumberOfTransitions(transitions);
|
int num_transitions = transitions.NumberOfTransitions();
|
||||||
for (int i = 0; i < num_transitions; ++i) {
|
for (int i = 0; i < num_transitions; ++i) {
|
||||||
Map* target = TransitionArray::GetTarget(transitions, i);
|
Map* target = transitions.GetTarget(i);
|
||||||
backlog.push(target);
|
backlog.push(target);
|
||||||
}
|
}
|
||||||
DescriptorArray* descriptors = current->instance_descriptors();
|
DescriptorArray* descriptors = current->instance_descriptors();
|
||||||
@ -4587,10 +4587,11 @@ Map* Map::TryReplayPropertyTransitions(Map* old_map) {
|
|||||||
Map* new_map = this;
|
Map* new_map = this;
|
||||||
for (int i = root_nof; i < old_nof; ++i) {
|
for (int i = root_nof; i < old_nof; ++i) {
|
||||||
PropertyDetails old_details = old_descriptors->GetDetails(i);
|
PropertyDetails old_details = old_descriptors->GetDetails(i);
|
||||||
Map* transition = TransitionArray::SearchTransition(
|
Map* transition =
|
||||||
new_map, old_details.kind(), old_descriptors->GetKey(i),
|
TransitionsAccessor(new_map, &no_allocation)
|
||||||
old_details.attributes());
|
.SearchTransition(old_descriptors->GetKey(i), old_details.kind(),
|
||||||
if (transition == NULL) return nullptr;
|
old_details.attributes());
|
||||||
|
if (transition == nullptr) return nullptr;
|
||||||
new_map = transition;
|
new_map = transition;
|
||||||
DescriptorArray* new_descriptors = new_map->instance_descriptors();
|
DescriptorArray* new_descriptors = new_map->instance_descriptors();
|
||||||
|
|
||||||
@ -8216,8 +8217,8 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<Map> old_map(object->map(), isolate);
|
Handle<Map> old_map(object->map(), isolate);
|
||||||
Map* transition =
|
TransitionsAccessor transitions(old_map);
|
||||||
TransitionArray::SearchSpecial(*old_map, *transition_marker);
|
Map* transition = transitions.SearchSpecial(*transition_marker);
|
||||||
if (transition != NULL) {
|
if (transition != NULL) {
|
||||||
Handle<Map> transition_map(transition, isolate);
|
Handle<Map> transition_map(transition, isolate);
|
||||||
DCHECK(transition_map->has_dictionary_elements() ||
|
DCHECK(transition_map->has_dictionary_elements() ||
|
||||||
@ -8225,7 +8226,7 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
|
|||||||
transition_map->elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS);
|
transition_map->elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS);
|
||||||
DCHECK(!transition_map->is_extensible());
|
DCHECK(!transition_map->is_extensible());
|
||||||
JSObject::MigrateToMap(object, transition_map);
|
JSObject::MigrateToMap(object, transition_map);
|
||||||
} else if (TransitionArray::CanHaveMoreTransitions(old_map)) {
|
} else if (transitions.CanHaveMoreTransitions()) {
|
||||||
// Create a new descriptor array with the appropriate property attributes
|
// Create a new descriptor array with the appropriate property attributes
|
||||||
Handle<Map> new_map = Map::CopyForPreventExtensions(
|
Handle<Map> new_map = Map::CopyForPreventExtensions(
|
||||||
old_map, attrs, transition_marker, "CopyForPreventExtensions");
|
old_map, attrs, transition_marker, "CopyForPreventExtensions");
|
||||||
@ -9034,11 +9035,12 @@ void Map::TraceTransition(const char* what, Map* from, Map* to, Name* name) {
|
|||||||
|
|
||||||
// static
|
// static
|
||||||
void Map::TraceAllTransitions(Map* map) {
|
void Map::TraceAllTransitions(Map* map) {
|
||||||
Object* transitions = map->raw_transitions();
|
DisallowHeapAllocation no_gc;
|
||||||
int num_transitions = TransitionArray::NumberOfTransitions(transitions);
|
TransitionsAccessor transitions(map, &no_gc);
|
||||||
|
int num_transitions = transitions.NumberOfTransitions();
|
||||||
for (int i = -0; i < num_transitions; ++i) {
|
for (int i = -0; i < num_transitions; ++i) {
|
||||||
Map* target = TransitionArray::GetTarget(transitions, i);
|
Map* target = transitions.GetTarget(i);
|
||||||
Name* key = TransitionArray::GetKey(transitions, i);
|
Name* key = transitions.GetKey(i);
|
||||||
Map::TraceTransition("Transition", map, target, key);
|
Map::TraceTransition("Transition", map, target, key);
|
||||||
Map::TraceAllTransitions(target);
|
Map::TraceAllTransitions(target);
|
||||||
}
|
}
|
||||||
@ -9069,7 +9071,7 @@ void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child,
|
|||||||
Map::TraceTransition("NoTransition", *parent, *child, *name);
|
Map::TraceTransition("NoTransition", *parent, *child, *name);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
TransitionArray::Insert(parent, name, child, flag);
|
TransitionsAccessor(parent).Insert(name, child, flag);
|
||||||
#if V8_TRACE_MAPS
|
#if V8_TRACE_MAPS
|
||||||
Map::TraceTransition("Transition", *parent, *child, *name);
|
Map::TraceTransition("Transition", *parent, *child, *name);
|
||||||
#endif
|
#endif
|
||||||
@ -9088,7 +9090,7 @@ Handle<Map> Map::CopyReplaceDescriptors(
|
|||||||
|
|
||||||
if (!map->is_prototype_map()) {
|
if (!map->is_prototype_map()) {
|
||||||
if (flag == INSERT_TRANSITION &&
|
if (flag == INSERT_TRANSITION &&
|
||||||
TransitionArray::CanHaveMoreTransitions(map)) {
|
TransitionsAccessor(map).CanHaveMoreTransitions()) {
|
||||||
result->InitializeDescriptors(*descriptors, *layout_descriptor);
|
result->InitializeDescriptors(*descriptors, *layout_descriptor);
|
||||||
|
|
||||||
Handle<Name> name;
|
Handle<Name> name;
|
||||||
@ -9107,7 +9109,7 @@ Handle<Map> Map::CopyReplaceDescriptors(
|
|||||||
// Mirror conditions above that did not call ConnectTransition().
|
// Mirror conditions above that did not call ConnectTransition().
|
||||||
(map->is_prototype_map() ||
|
(map->is_prototype_map() ||
|
||||||
!(flag == INSERT_TRANSITION &&
|
!(flag == INSERT_TRANSITION &&
|
||||||
TransitionArray::CanHaveMoreTransitions(map)))) {
|
TransitionsAccessor(map).CanHaveMoreTransitions()))) {
|
||||||
PrintF("[TraceMaps: ReplaceDescriptors from= %p to= %p reason= %s ]\n",
|
PrintF("[TraceMaps: ReplaceDescriptors from= %p to= %p reason= %s ]\n",
|
||||||
reinterpret_cast<void*>(*map), reinterpret_cast<void*>(*result),
|
reinterpret_cast<void*>(*map), reinterpret_cast<void*>(*result),
|
||||||
reason);
|
reason);
|
||||||
@ -9219,7 +9221,7 @@ Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool insert_transition = flag == INSERT_TRANSITION &&
|
bool insert_transition = flag == INSERT_TRANSITION &&
|
||||||
TransitionArray::CanHaveMoreTransitions(map) &&
|
TransitionsAccessor(map).CanHaveMoreTransitions() &&
|
||||||
maybe_elements_transition_map == NULL;
|
maybe_elements_transition_map == NULL;
|
||||||
|
|
||||||
if (insert_transition) {
|
if (insert_transition) {
|
||||||
@ -9255,7 +9257,7 @@ Handle<Map> Map::AsLanguageMode(Handle<Map> initial_map,
|
|||||||
Handle<Symbol> transition_symbol =
|
Handle<Symbol> transition_symbol =
|
||||||
isolate->factory()->strict_function_transition_symbol();
|
isolate->factory()->strict_function_transition_symbol();
|
||||||
Map* maybe_transition =
|
Map* maybe_transition =
|
||||||
TransitionArray::SearchSpecial(*initial_map, *transition_symbol);
|
TransitionsAccessor(initial_map).SearchSpecial(*transition_symbol);
|
||||||
if (maybe_transition != NULL) {
|
if (maybe_transition != NULL) {
|
||||||
return handle(maybe_transition, isolate);
|
return handle(maybe_transition, isolate);
|
||||||
}
|
}
|
||||||
@ -9270,7 +9272,7 @@ Handle<Map> Map::AsLanguageMode(Handle<Map> initial_map,
|
|||||||
map->SetConstructor(initial_map->GetConstructor());
|
map->SetConstructor(initial_map->GetConstructor());
|
||||||
map->set_prototype(initial_map->prototype());
|
map->set_prototype(initial_map->prototype());
|
||||||
|
|
||||||
if (TransitionArray::CanHaveMoreTransitions(initial_map)) {
|
if (TransitionsAccessor(initial_map).CanHaveMoreTransitions()) {
|
||||||
Map::ConnectTransition(initial_map, map, transition_symbol,
|
Map::ConnectTransition(initial_map, map, transition_symbol,
|
||||||
SPECIAL_TRANSITION);
|
SPECIAL_TRANSITION);
|
||||||
}
|
}
|
||||||
@ -9451,7 +9453,7 @@ Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name,
|
|||||||
map = Update(map);
|
map = Update(map);
|
||||||
|
|
||||||
Map* maybe_transition =
|
Map* maybe_transition =
|
||||||
TransitionArray::SearchTransition(*map, kData, *name, attributes);
|
TransitionsAccessor(map).SearchTransition(*name, kData, attributes);
|
||||||
if (maybe_transition != NULL) {
|
if (maybe_transition != NULL) {
|
||||||
Handle<Map> transition(maybe_transition);
|
Handle<Map> transition(maybe_transition);
|
||||||
int descriptor = transition->LastAdded();
|
int descriptor = transition->LastAdded();
|
||||||
@ -9573,7 +9575,7 @@ Handle<Map> Map::TransitionToAccessorProperty(Isolate* isolate, Handle<Map> map,
|
|||||||
: CLEAR_INOBJECT_PROPERTIES;
|
: CLEAR_INOBJECT_PROPERTIES;
|
||||||
|
|
||||||
Map* maybe_transition =
|
Map* maybe_transition =
|
||||||
TransitionArray::SearchTransition(*map, kAccessor, *name, attributes);
|
TransitionsAccessor(map).SearchTransition(*name, kAccessor, attributes);
|
||||||
if (maybe_transition != NULL) {
|
if (maybe_transition != NULL) {
|
||||||
Handle<Map> transition(maybe_transition, isolate);
|
Handle<Map> transition(maybe_transition, isolate);
|
||||||
DescriptorArray* descriptors = transition->instance_descriptors();
|
DescriptorArray* descriptors = transition->instance_descriptors();
|
||||||
@ -9658,7 +9660,7 @@ Handle<Map> Map::CopyAddDescriptor(Handle<Map> map,
|
|||||||
// Share descriptors only if map owns descriptors and it not an initial map.
|
// Share descriptors only if map owns descriptors and it not an initial map.
|
||||||
if (flag == INSERT_TRANSITION && map->owns_descriptors() &&
|
if (flag == INSERT_TRANSITION && map->owns_descriptors() &&
|
||||||
!map->GetBackPointer()->IsUndefined(map->GetIsolate()) &&
|
!map->GetBackPointer()->IsUndefined(map->GetIsolate()) &&
|
||||||
TransitionArray::CanHaveMoreTransitions(map)) {
|
TransitionsAccessor(map).CanHaveMoreTransitions()) {
|
||||||
return ShareDescriptor(map, descriptors, descriptor);
|
return ShareDescriptor(map, descriptors, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12321,12 +12323,14 @@ void Map::CompleteInobjectSlackTracking() {
|
|||||||
DCHECK(GetBackPointer()->IsUndefined(GetIsolate()));
|
DCHECK(GetBackPointer()->IsUndefined(GetIsolate()));
|
||||||
|
|
||||||
int slack = unused_property_fields();
|
int slack = unused_property_fields();
|
||||||
TransitionArray::TraverseTransitionTree(this, &GetMinInobjectSlack, &slack);
|
DisallowHeapAllocation no_gc;
|
||||||
|
TransitionsAccessor transitions(this, &no_gc);
|
||||||
|
transitions.TraverseTransitionTree(&GetMinInobjectSlack, &slack);
|
||||||
if (slack != 0) {
|
if (slack != 0) {
|
||||||
// Resize the initial map and all maps in its transition tree.
|
// Resize the initial map and all maps in its transition tree.
|
||||||
TransitionArray::TraverseTransitionTree(this, &ShrinkInstanceSize, &slack);
|
transitions.TraverseTransitionTree(&ShrinkInstanceSize, &slack);
|
||||||
} else {
|
} else {
|
||||||
TransitionArray::TraverseTransitionTree(this, &StopSlackTracking, nullptr);
|
transitions.TraverseTransitionTree(&StopSlackTracking, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15231,10 +15235,11 @@ const char* DependentCode::DependencyGroupName(DependencyGroup group) {
|
|||||||
|
|
||||||
Handle<Map> Map::TransitionToPrototype(Handle<Map> map,
|
Handle<Map> Map::TransitionToPrototype(Handle<Map> map,
|
||||||
Handle<Object> prototype) {
|
Handle<Object> prototype) {
|
||||||
Handle<Map> new_map = TransitionArray::GetPrototypeTransition(map, prototype);
|
Handle<Map> new_map =
|
||||||
|
TransitionsAccessor(map).GetPrototypeTransition(prototype);
|
||||||
if (new_map.is_null()) {
|
if (new_map.is_null()) {
|
||||||
new_map = Copy(map, "TransitionToPrototype");
|
new_map = Copy(map, "TransitionToPrototype");
|
||||||
TransitionArray::PutPrototypeTransition(map, prototype, new_map);
|
TransitionsAccessor(map).PutPrototypeTransition(prototype, new_map);
|
||||||
Map::SetPrototype(new_map, prototype);
|
Map::SetPrototype(new_map, prototype);
|
||||||
}
|
}
|
||||||
return new_map;
|
return new_map;
|
||||||
|
@ -161,9 +161,6 @@ class DescriptorArray : public FixedArray {
|
|||||||
// Is the descriptor array sorted and without duplicates?
|
// Is the descriptor array sorted and without duplicates?
|
||||||
bool IsSortedNoDuplicates(int valid_descriptors = -1);
|
bool IsSortedNoDuplicates(int valid_descriptors = -1);
|
||||||
|
|
||||||
// Is the descriptor array consistent with the back pointers in targets?
|
|
||||||
bool IsConsistentWithBackPointers(Map* current_map);
|
|
||||||
|
|
||||||
// Are two DescriptorArrays equal?
|
// Are two DescriptorArrays equal?
|
||||||
bool IsEqualTo(DescriptorArray* other);
|
bool IsEqualTo(DescriptorArray* other);
|
||||||
#endif
|
#endif
|
||||||
|
@ -276,7 +276,7 @@ class Map : public HeapObject {
|
|||||||
// map with DICTIONARY_ELEMENTS was found in the prototype chain.
|
// map with DICTIONARY_ELEMENTS was found in the prototype chain.
|
||||||
bool DictionaryElementsInPrototypeChainOnly();
|
bool DictionaryElementsInPrototypeChainOnly();
|
||||||
|
|
||||||
inline Map* ElementsTransitionMap() const;
|
inline Map* ElementsTransitionMap();
|
||||||
|
|
||||||
inline FixedArrayBase* GetInitialElements() const;
|
inline FixedArrayBase* GetInitialElements() const;
|
||||||
|
|
||||||
|
@ -1250,8 +1250,7 @@ void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
|
|||||||
|
|
||||||
void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
|
void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
|
||||||
Object* raw_transitions_or_prototype_info = map->raw_transitions();
|
Object* raw_transitions_or_prototype_info = map->raw_transitions();
|
||||||
if (TransitionArray::IsFullTransitionArray(
|
if (raw_transitions_or_prototype_info->IsTransitionArray()) {
|
||||||
raw_transitions_or_prototype_info)) {
|
|
||||||
TransitionArray* transitions =
|
TransitionArray* transitions =
|
||||||
TransitionArray::cast(raw_transitions_or_prototype_info);
|
TransitionArray::cast(raw_transitions_or_prototype_info);
|
||||||
if (map->CanTransition() && transitions->HasPrototypeTransitions()) {
|
if (map->CanTransition() && transitions->HasPrototypeTransitions()) {
|
||||||
@ -1262,8 +1261,9 @@ void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
|
|||||||
TagObject(transitions, "(transition array)");
|
TagObject(transitions, "(transition array)");
|
||||||
SetInternalReference(map, entry, "transitions", transitions,
|
SetInternalReference(map, entry, "transitions", transitions,
|
||||||
Map::kTransitionsOrPrototypeInfoOffset);
|
Map::kTransitionsOrPrototypeInfoOffset);
|
||||||
} else if (TransitionArray::IsSimpleTransition(
|
} else if (raw_transitions_or_prototype_info->IsWeakCell() ||
|
||||||
raw_transitions_or_prototype_info)) {
|
raw_transitions_or_prototype_info->IsTuple3() ||
|
||||||
|
raw_transitions_or_prototype_info->IsFixedArray()) {
|
||||||
TagObject(raw_transitions_or_prototype_info, "(transition)");
|
TagObject(raw_transitions_or_prototype_info, "(transition)");
|
||||||
SetInternalReference(map, entry, "transition",
|
SetInternalReference(map, entry, "transition",
|
||||||
raw_transitions_or_prototype_info,
|
raw_transitions_or_prototype_info,
|
||||||
|
@ -7,10 +7,33 @@
|
|||||||
|
|
||||||
#include "src/transitions.h"
|
#include "src/transitions.h"
|
||||||
|
|
||||||
|
#include "src/ic/handler-configuration-inl.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
template <TransitionsAccessor::Encoding enc>
|
||||||
|
WeakCell* TransitionsAccessor::GetTargetCell() {
|
||||||
|
DCHECK(!needs_reload_);
|
||||||
|
if (target_cell_ != nullptr) return target_cell_;
|
||||||
|
if (enc == kWeakCell) {
|
||||||
|
target_cell_ = WeakCell::cast(raw_transitions_);
|
||||||
|
} else if (enc == kTuple3Handler) {
|
||||||
|
target_cell_ = StoreHandler::GetTuple3TransitionCell(raw_transitions_);
|
||||||
|
} else if (enc == kFixedArrayHandler) {
|
||||||
|
target_cell_ = StoreHandler::GetArrayTransitionCell(raw_transitions_);
|
||||||
|
} else {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
return target_cell_;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransitionArray* TransitionsAccessor::transitions() {
|
||||||
|
DCHECK_EQ(kFullTransitionArray, encoding());
|
||||||
|
return TransitionArray::cast(raw_transitions_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
TransitionArray* TransitionArray::cast(Object* object) {
|
TransitionArray* TransitionArray::cast(Object* object) {
|
||||||
DCHECK(object->IsTransitionArray());
|
DCHECK(object->IsTransitionArray());
|
||||||
return reinterpret_cast<TransitionArray*>(object);
|
return reinterpret_cast<TransitionArray*>(object);
|
||||||
@ -59,41 +82,94 @@ Name* TransitionArray::GetKey(int transition_number) {
|
|||||||
return Name::cast(get(ToKeyIndex(transition_number)));
|
return Name::cast(get(ToKeyIndex(transition_number)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Name* TransitionsAccessor::GetKey(int transition_number) {
|
||||||
Name* TransitionArray::GetKey(Object* raw_transitions, int transition_number) {
|
WeakCell* cell = nullptr;
|
||||||
if (IsSimpleTransition(raw_transitions)) {
|
switch (encoding()) {
|
||||||
DCHECK(transition_number == 0);
|
case kPrototypeInfo:
|
||||||
return GetSimpleTransitionKey(GetSimpleTransition(raw_transitions));
|
case kUninitialized:
|
||||||
|
UNREACHABLE();
|
||||||
|
return nullptr;
|
||||||
|
case kWeakCell:
|
||||||
|
cell = GetTargetCell<kWeakCell>();
|
||||||
|
break;
|
||||||
|
case kTuple3Handler:
|
||||||
|
cell = GetTargetCell<kTuple3Handler>();
|
||||||
|
break;
|
||||||
|
case kFixedArrayHandler:
|
||||||
|
cell = GetTargetCell<kFixedArrayHandler>();
|
||||||
|
break;
|
||||||
|
case kFullTransitionArray:
|
||||||
|
return transitions()->GetKey(transition_number);
|
||||||
}
|
}
|
||||||
DCHECK(IsFullTransitionArray(raw_transitions));
|
DCHECK(!cell->cleared());
|
||||||
return TransitionArray::cast(raw_transitions)->GetKey(transition_number);
|
return GetSimpleTransitionKey(Map::cast(cell->value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TransitionArray::SetKey(int transition_number, Name* key) {
|
void TransitionArray::SetKey(int transition_number, Name* key) {
|
||||||
DCHECK(transition_number < number_of_transitions());
|
DCHECK(transition_number < number_of_transitions());
|
||||||
set(ToKeyIndex(transition_number), key);
|
set(ToKeyIndex(transition_number), key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object** TransitionArray::GetTargetSlot(int transition_number) {
|
||||||
|
DCHECK(transition_number < number_of_transitions());
|
||||||
|
return RawFieldOfElementAt(ToTargetIndex(transition_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
PropertyDetails TransitionsAccessor::GetTargetDetails(Name* name, Map* target) {
|
||||||
|
DCHECK(!IsSpecialTransition(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
Map* TransitionsAccessor::GetTargetFromRaw(Object* raw) {
|
||||||
|
if (raw->IsMap()) return Map::cast(raw);
|
||||||
|
if (raw->IsTuple3()) {
|
||||||
|
return Map::cast(StoreHandler::GetTuple3TransitionCell(raw)->value());
|
||||||
|
} else {
|
||||||
|
DCHECK(raw->IsFixedArray());
|
||||||
|
return Map::cast(StoreHandler::GetArrayTransitionCell(raw)->value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* TransitionArray::GetRawTarget(int transition_number) {
|
||||||
|
DCHECK(transition_number < number_of_transitions());
|
||||||
|
return get(ToTargetIndex(transition_number));
|
||||||
|
}
|
||||||
|
|
||||||
Map* TransitionArray::GetTarget(int transition_number) {
|
Map* TransitionArray::GetTarget(int transition_number) {
|
||||||
DCHECK(transition_number < number_of_transitions());
|
Object* raw = GetRawTarget(transition_number);
|
||||||
return Map::cast(get(ToTargetIndex(transition_number)));
|
return TransitionsAccessor::GetTargetFromRaw(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map* TransitionsAccessor::GetTarget(int transition_number) {
|
||||||
Map* TransitionArray::GetTarget(Object* raw_transitions,
|
WeakCell* cell = nullptr;
|
||||||
int transition_number) {
|
switch (encoding()) {
|
||||||
if (IsSimpleTransition(raw_transitions)) {
|
case kPrototypeInfo:
|
||||||
DCHECK(transition_number == 0);
|
case kUninitialized:
|
||||||
return GetSimpleTransition(raw_transitions);
|
UNREACHABLE();
|
||||||
|
return nullptr;
|
||||||
|
case kWeakCell:
|
||||||
|
cell = GetTargetCell<kWeakCell>();
|
||||||
|
break;
|
||||||
|
case kTuple3Handler:
|
||||||
|
cell = GetTargetCell<kTuple3Handler>();
|
||||||
|
break;
|
||||||
|
case kFixedArrayHandler:
|
||||||
|
cell = GetTargetCell<kFixedArrayHandler>();
|
||||||
|
break;
|
||||||
|
case kFullTransitionArray:
|
||||||
|
return transitions()->GetTarget(transition_number);
|
||||||
}
|
}
|
||||||
DCHECK(IsFullTransitionArray(raw_transitions));
|
DCHECK(!cell->cleared());
|
||||||
return TransitionArray::cast(raw_transitions)->GetTarget(transition_number);
|
return Map::cast(cell->value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionArray::SetTarget(int transition_number, Object* value) {
|
||||||
void TransitionArray::SetTarget(int transition_number, Map* value) {
|
|
||||||
DCHECK(transition_number < number_of_transitions());
|
DCHECK(transition_number < number_of_transitions());
|
||||||
set(ToTargetIndex(transition_number), value);
|
set(ToTargetIndex(transition_number), value);
|
||||||
}
|
}
|
||||||
@ -105,17 +181,6 @@ int TransitionArray::SearchName(Name* name, int* out_insertion_index) {
|
|||||||
out_insertion_index);
|
out_insertion_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool TransitionArray::IsSpecialTransition(Name* name) {
|
|
||||||
if (!name->IsSymbol()) return false;
|
|
||||||
Heap* heap = name->GetHeap();
|
|
||||||
return name == heap->nonextensible_symbol() ||
|
|
||||||
name == heap->sealed_symbol() || name == heap->frozen_symbol() ||
|
|
||||||
name == heap->elements_transition_symbol() ||
|
|
||||||
name == heap->strict_function_transition_symbol();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int TransitionArray::CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1,
|
int TransitionArray::CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1,
|
||||||
PropertyAttributes attributes1, Name* key2,
|
PropertyAttributes attributes1, Name* key2,
|
||||||
uint32_t hash2, PropertyKind kind2,
|
uint32_t hash2, PropertyKind kind2,
|
||||||
@ -126,7 +191,6 @@ int TransitionArray::CompareKeys(Name* key1, uint32_t hash1, PropertyKind kind1,
|
|||||||
return CompareDetails(kind1, attributes1, kind2, attributes2);
|
return CompareDetails(kind1, attributes1, kind2, attributes2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int TransitionArray::CompareNames(Name* key1, uint32_t hash1, Name* key2,
|
int TransitionArray::CompareNames(Name* key1, uint32_t hash1, Name* key2,
|
||||||
uint32_t hash2) {
|
uint32_t hash2) {
|
||||||
if (key1 != key2) {
|
if (key1 != key2) {
|
||||||
@ -137,7 +201,6 @@ int TransitionArray::CompareNames(Name* key1, uint32_t hash1, Name* key2,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int TransitionArray::CompareDetails(PropertyKind kind1,
|
int TransitionArray::CompareDetails(PropertyKind kind1,
|
||||||
PropertyAttributes attributes1,
|
PropertyAttributes attributes1,
|
||||||
PropertyKind kind2,
|
PropertyKind kind2,
|
||||||
@ -154,25 +217,18 @@ int TransitionArray::CompareDetails(PropertyKind kind1,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionArray::Set(int transition_number, Name* key, Object* target) {
|
||||||
PropertyDetails TransitionArray::GetTargetDetails(Name* name, Map* target) {
|
|
||||||
DCHECK(!IsSpecialTransition(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void TransitionArray::Set(int transition_number, Name* key, Map* target) {
|
|
||||||
set(ToKeyIndex(transition_number), key);
|
set(ToKeyIndex(transition_number), key);
|
||||||
set(ToTargetIndex(transition_number), target);
|
set(ToTargetIndex(transition_number), target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TransitionArray::Capacity() {
|
||||||
|
if (length() <= kFirstIndex) return 0;
|
||||||
|
return (length() - kFirstIndex) / kTransitionSize;
|
||||||
|
}
|
||||||
|
|
||||||
void TransitionArray::SetNumberOfTransitions(int number_of_transitions) {
|
void TransitionArray::SetNumberOfTransitions(int number_of_transitions) {
|
||||||
DCHECK(number_of_transitions <= Capacity(this));
|
DCHECK(number_of_transitions <= Capacity());
|
||||||
set(kTransitionLengthIndex, Smi::FromInt(number_of_transitions));
|
set(kTransitionLengthIndex, Smi::FromInt(number_of_transitions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,55 +11,111 @@
|
|||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
void TransitionsAccessor::Initialize() {
|
||||||
|
raw_transitions_ = map_->raw_transitions();
|
||||||
|
if (raw_transitions_->IsSmi()) {
|
||||||
|
encoding_ = kUninitialized;
|
||||||
|
} else if (HeapObject::cast(raw_transitions_)->IsWeakCell()) {
|
||||||
|
encoding_ = kWeakCell;
|
||||||
|
} else if (HeapObject::cast(raw_transitions_)->IsTuple3()) {
|
||||||
|
encoding_ = kTuple3Handler;
|
||||||
|
} else if (HeapObject::cast(raw_transitions_)->IsFixedArray()) {
|
||||||
|
encoding_ = kFixedArrayHandler;
|
||||||
|
} else if (HeapObject::cast(raw_transitions_)->IsTransitionArray()) {
|
||||||
|
encoding_ = kFullTransitionArray;
|
||||||
|
} else {
|
||||||
|
DCHECK(HeapObject::cast(raw_transitions_)->IsPrototypeInfo());
|
||||||
|
encoding_ = kPrototypeInfo;
|
||||||
|
}
|
||||||
|
target_cell_ = nullptr;
|
||||||
|
#if DEBUG
|
||||||
|
needs_reload_ = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
Map* TransitionsAccessor::GetSimpleTransition() {
|
||||||
void TransitionArray::Insert(Handle<Map> map, Handle<Name> name,
|
switch (encoding()) {
|
||||||
Handle<Map> target, SimpleTransitionFlag flag) {
|
case kWeakCell:
|
||||||
Isolate* isolate = map->GetIsolate();
|
return Map::cast(GetTargetCell<kWeakCell>()->value());
|
||||||
target->SetBackPointer(*map);
|
case kTuple3Handler:
|
||||||
|
return Map::cast(GetTargetCell<kTuple3Handler>()->value());
|
||||||
|
case kFixedArrayHandler:
|
||||||
|
return Map::cast(GetTargetCell<kFixedArrayHandler>()->value());
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransitionsAccessor::HasSimpleTransitionTo(WeakCell* cell) {
|
||||||
|
DCHECK(cell->value()->IsMap());
|
||||||
|
switch (encoding()) {
|
||||||
|
case kWeakCell:
|
||||||
|
return raw_transitions_ == cell;
|
||||||
|
case kTuple3Handler:
|
||||||
|
return StoreHandler::GetTuple3TransitionCell(raw_transitions_) == cell;
|
||||||
|
case kFixedArrayHandler:
|
||||||
|
return StoreHandler::GetArrayTransitionCell(raw_transitions_) == cell;
|
||||||
|
case kPrototypeInfo:
|
||||||
|
case kUninitialized:
|
||||||
|
case kFullTransitionArray:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return false; // Make GCC happy.
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransitionsAccessor::Insert(Handle<Name> name, Handle<Map> target,
|
||||||
|
SimpleTransitionFlag flag) {
|
||||||
|
DCHECK(!map_handle_.is_null());
|
||||||
|
Isolate* isolate = map_->GetIsolate();
|
||||||
|
target->SetBackPointer(map_);
|
||||||
|
|
||||||
// If the map doesn't have any transitions at all yet, install the new one.
|
// If the map doesn't have any transitions at all yet, install the new one.
|
||||||
if (CanStoreSimpleTransition(map->raw_transitions())) {
|
if (encoding() == kUninitialized) {
|
||||||
if (flag == SIMPLE_PROPERTY_TRANSITION) {
|
if (flag == SIMPLE_PROPERTY_TRANSITION) {
|
||||||
Handle<WeakCell> cell = Map::WeakCellForMap(target);
|
ReplaceTransitions(*Map::WeakCellForMap(target));
|
||||||
ReplaceTransitions(map, *cell);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If the flag requires a full TransitionArray, allocate one.
|
// If the flag requires a full TransitionArray, allocate one.
|
||||||
Handle<TransitionArray> result = Allocate(isolate, 0, 1);
|
Handle<TransitionArray> result = TransitionArray::Allocate(isolate, 0, 1);
|
||||||
ReplaceTransitions(map, *result);
|
ReplaceTransitions(*result);
|
||||||
|
Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_special_transition = flag == SPECIAL_TRANSITION;
|
bool is_special_transition = flag == SPECIAL_TRANSITION;
|
||||||
// If the map has a simple transition, check if it should be overwritten.
|
// If the map has a simple transition, check if it should be overwritten.
|
||||||
if (IsSimpleTransition(map->raw_transitions())) {
|
Map* simple_transition = GetSimpleTransition();
|
||||||
Map* old_target = GetSimpleTransition(map->raw_transitions());
|
if (simple_transition != nullptr) {
|
||||||
Name* key = GetSimpleTransitionKey(old_target);
|
Name* key = GetSimpleTransitionKey(simple_transition);
|
||||||
PropertyDetails old_details = GetSimpleTargetDetails(old_target);
|
PropertyDetails old_details = GetSimpleTargetDetails(simple_transition);
|
||||||
PropertyDetails new_details = is_special_transition
|
PropertyDetails new_details = is_special_transition
|
||||||
? PropertyDetails::Empty()
|
? PropertyDetails::Empty()
|
||||||
: GetTargetDetails(*name, *target);
|
: GetTargetDetails(*name, *target);
|
||||||
if (flag == SIMPLE_PROPERTY_TRANSITION && key->Equals(*name) &&
|
if (flag == SIMPLE_PROPERTY_TRANSITION && key->Equals(*name) &&
|
||||||
old_details.kind() == new_details.kind() &&
|
old_details.kind() == new_details.kind() &&
|
||||||
old_details.attributes() == new_details.attributes()) {
|
old_details.attributes() == new_details.attributes()) {
|
||||||
Handle<WeakCell> cell = Map::WeakCellForMap(target);
|
ReplaceTransitions(*Map::WeakCellForMap(target));
|
||||||
ReplaceTransitions(map, *cell);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Otherwise allocate a full TransitionArray with slack for a new entry.
|
// Otherwise allocate a full TransitionArray with slack for a new entry.
|
||||||
Handle<TransitionArray> result = Allocate(isolate, 1, 1);
|
Handle<TransitionArray> result = TransitionArray::Allocate(isolate, 1, 1);
|
||||||
// Re-read existing data; the allocation might have caused it to be cleared.
|
// Reload state; the allocation might have caused it to be cleared.
|
||||||
if (IsSimpleTransition(map->raw_transitions())) {
|
Reload();
|
||||||
old_target = GetSimpleTransition(map->raw_transitions());
|
simple_transition = GetSimpleTransition();
|
||||||
result->Set(0, GetSimpleTransitionKey(old_target), old_target);
|
if (simple_transition != nullptr) {
|
||||||
|
Object* value = raw_transitions_->IsWeakCell()
|
||||||
|
? WeakCell::cast(raw_transitions_)->value()
|
||||||
|
: raw_transitions_;
|
||||||
|
result->Set(0, GetSimpleTransitionKey(simple_transition), value);
|
||||||
} else {
|
} else {
|
||||||
result->SetNumberOfTransitions(0);
|
result->SetNumberOfTransitions(0);
|
||||||
}
|
}
|
||||||
ReplaceTransitions(map, *result);
|
ReplaceTransitions(*result);
|
||||||
|
Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, we know that the map has a full TransitionArray.
|
// At this point, we know that the map has a full TransitionArray.
|
||||||
DCHECK(IsFullTransitionArray(map->raw_transitions()));
|
DCHECK_EQ(kFullTransitionArray, encoding());
|
||||||
|
|
||||||
int number_of_transitions = 0;
|
int number_of_transitions = 0;
|
||||||
int new_nof = 0;
|
int new_nof = 0;
|
||||||
@ -71,7 +127,7 @@ void TransitionArray::Insert(Handle<Map> map, Handle<Name> name,
|
|||||||
|
|
||||||
{
|
{
|
||||||
DisallowHeapAllocation no_gc;
|
DisallowHeapAllocation no_gc;
|
||||||
TransitionArray* array = TransitionArray::cast(map->raw_transitions());
|
TransitionArray* array = transitions();
|
||||||
number_of_transitions = array->number_of_transitions();
|
number_of_transitions = array->number_of_transitions();
|
||||||
new_nof = number_of_transitions;
|
new_nof = number_of_transitions;
|
||||||
|
|
||||||
@ -91,11 +147,11 @@ void TransitionArray::Insert(Handle<Map> map, Handle<Name> name,
|
|||||||
DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
|
DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
|
||||||
|
|
||||||
// If there is enough capacity, insert new entry into the existing array.
|
// If there is enough capacity, insert new entry into the existing array.
|
||||||
if (new_nof <= Capacity(array)) {
|
if (new_nof <= array->Capacity()) {
|
||||||
array->SetNumberOfTransitions(new_nof);
|
array->SetNumberOfTransitions(new_nof);
|
||||||
for (index = number_of_transitions; index > insertion_index; --index) {
|
for (index = number_of_transitions; index > insertion_index; --index) {
|
||||||
array->SetKey(index, array->GetKey(index - 1));
|
array->SetKey(index, array->GetKey(index - 1));
|
||||||
array->SetTarget(index, array->GetTarget(index - 1));
|
array->SetTarget(index, array->GetRawTarget(index - 1));
|
||||||
}
|
}
|
||||||
array->SetKey(index, *name);
|
array->SetKey(index, *name);
|
||||||
array->SetTarget(index, *target);
|
array->SetTarget(index, *target);
|
||||||
@ -105,16 +161,16 @@ void TransitionArray::Insert(Handle<Map> map, Handle<Name> name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We're gonna need a bigger TransitionArray.
|
// We're gonna need a bigger TransitionArray.
|
||||||
Handle<TransitionArray> result = Allocate(
|
Handle<TransitionArray> result = TransitionArray::Allocate(
|
||||||
map->GetIsolate(), new_nof,
|
isolate, new_nof,
|
||||||
Map::SlackForArraySize(number_of_transitions, kMaxNumberOfTransitions));
|
Map::SlackForArraySize(number_of_transitions, kMaxNumberOfTransitions));
|
||||||
|
|
||||||
// The map's transition array may have shrunk during the allocation above as
|
// The map's transition array may have shrunk during the allocation above as
|
||||||
// it was weakly traversed, though it is guaranteed not to disappear. Trim the
|
// it was weakly traversed, though it is guaranteed not to disappear. Trim the
|
||||||
// result copy if needed, and recompute variables.
|
// result copy if needed, and recompute variables.
|
||||||
DCHECK(IsFullTransitionArray(map->raw_transitions()));
|
Reload();
|
||||||
DisallowHeapAllocation no_gc;
|
DisallowHeapAllocation no_gc;
|
||||||
TransitionArray* array = TransitionArray::cast(map->raw_transitions());
|
TransitionArray* array = transitions();
|
||||||
if (array->number_of_transitions() != number_of_transitions) {
|
if (array->number_of_transitions() != number_of_transitions) {
|
||||||
DCHECK(array->number_of_transitions() < number_of_transitions);
|
DCHECK(array->number_of_transitions() < number_of_transitions);
|
||||||
|
|
||||||
@ -134,7 +190,7 @@ void TransitionArray::Insert(Handle<Map> map, Handle<Name> name,
|
|||||||
}
|
}
|
||||||
DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
|
DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
|
||||||
|
|
||||||
result->Shrink(ToKeyIndex(new_nof));
|
result->Shrink(TransitionArray::ToKeyIndex(new_nof));
|
||||||
result->SetNumberOfTransitions(new_nof);
|
result->SetNumberOfTransitions(new_nof);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,53 +208,115 @@ void TransitionArray::Insert(Handle<Map> map, Handle<Name> name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SLOW_DCHECK(result->IsSortedNoDuplicates());
|
SLOW_DCHECK(result->IsSortedNoDuplicates());
|
||||||
ReplaceTransitions(map, *result);
|
ReplaceTransitions(*result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionsAccessor::UpdateHandler(Name* name, Object* handler) {
|
||||||
|
if (map_->is_dictionary_map()) return;
|
||||||
|
switch (encoding()) {
|
||||||
|
case kPrototypeInfo:
|
||||||
|
case kUninitialized:
|
||||||
|
UNREACHABLE();
|
||||||
|
return;
|
||||||
|
case kWeakCell:
|
||||||
|
case kTuple3Handler:
|
||||||
|
case kFixedArrayHandler:
|
||||||
|
DCHECK_EQ(GetSimpleTransition(), GetTargetFromRaw(handler));
|
||||||
|
ReplaceTransitions(handler);
|
||||||
|
return;
|
||||||
|
case kFullTransitionArray: {
|
||||||
|
PropertyAttributes attributes = name->IsPrivate() ? DONT_ENUM : NONE;
|
||||||
|
int transition = transitions()->Search(kData, name, attributes);
|
||||||
|
DCHECK_NE(kNotFound, transition);
|
||||||
|
DCHECK_EQ(transitions()->GetTarget(transition),
|
||||||
|
GetTargetFromRaw(handler));
|
||||||
|
transitions()->SetTarget(transition, handler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
Object* TransitionsAccessor::SearchHandler(Name* name,
|
||||||
Map* TransitionArray::SearchTransition(Map* map, PropertyKind kind, Name* name,
|
Handle<Map>* out_transition) {
|
||||||
PropertyAttributes attributes) {
|
switch (encoding()) {
|
||||||
|
case kPrototypeInfo:
|
||||||
|
case kUninitialized:
|
||||||
|
case kWeakCell:
|
||||||
|
return nullptr;
|
||||||
|
case kTuple3Handler:
|
||||||
|
return StoreHandler::ValidTuple3HandlerOrNull(raw_transitions_, name,
|
||||||
|
out_transition);
|
||||||
|
case kFixedArrayHandler:
|
||||||
|
return StoreHandler::ValidFixedArrayHandlerOrNull(raw_transitions_, name,
|
||||||
|
out_transition);
|
||||||
|
case kFullTransitionArray: {
|
||||||
|
int transition = transitions()->Search(kData, name, NONE);
|
||||||
|
if (transition == kNotFound) return nullptr;
|
||||||
|
Object* raw_handler = transitions()->GetRawTarget(transition);
|
||||||
|
if (raw_handler->IsTuple3()) {
|
||||||
|
return StoreHandler::ValidTuple3HandlerOrNull(raw_handler, nullptr,
|
||||||
|
out_transition);
|
||||||
|
}
|
||||||
|
if (raw_handler->IsFixedArray()) {
|
||||||
|
return StoreHandler::ValidFixedArrayHandlerOrNull(raw_handler, name,
|
||||||
|
out_transition);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return nullptr; // Make GCC happy.
|
||||||
|
}
|
||||||
|
|
||||||
|
Map* TransitionsAccessor::SearchTransition(Name* name, PropertyKind kind,
|
||||||
|
PropertyAttributes attributes) {
|
||||||
DCHECK(name->IsUniqueName());
|
DCHECK(name->IsUniqueName());
|
||||||
Object* raw_transitions = map->raw_transitions();
|
WeakCell* cell = nullptr;
|
||||||
if (IsSimpleTransition(raw_transitions)) {
|
switch (encoding()) {
|
||||||
Map* target = GetSimpleTransition(raw_transitions);
|
case kPrototypeInfo:
|
||||||
Name* key = GetSimpleTransitionKey(target);
|
case kUninitialized:
|
||||||
if (key != name) return nullptr;
|
return nullptr;
|
||||||
PropertyDetails details = GetSimpleTargetDetails(target);
|
case kWeakCell:
|
||||||
if (details.attributes() != attributes) return nullptr;
|
cell = GetTargetCell<kWeakCell>();
|
||||||
if (details.kind() != kind) return nullptr;
|
break;
|
||||||
return target;
|
case kTuple3Handler:
|
||||||
|
cell = GetTargetCell<kTuple3Handler>();
|
||||||
|
break;
|
||||||
|
case kFixedArrayHandler:
|
||||||
|
cell = GetTargetCell<kFixedArrayHandler>();
|
||||||
|
break;
|
||||||
|
case kFullTransitionArray: {
|
||||||
|
int transition = transitions()->Search(kind, name, attributes);
|
||||||
|
if (transition == kNotFound) return nullptr;
|
||||||
|
return transitions()->GetTarget(transition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (IsFullTransitionArray(raw_transitions)) {
|
DCHECK(!cell->cleared());
|
||||||
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
|
if (!IsMatchingMap(cell, name, kind, attributes)) return nullptr;
|
||||||
int transition = transitions->Search(kind, name, attributes);
|
return Map::cast(cell->value());
|
||||||
if (transition == kNotFound) return nullptr;
|
|
||||||
return transitions->GetTarget(transition);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map* TransitionsAccessor::SearchSpecial(Symbol* name) {
|
||||||
// static
|
if (encoding() != kFullTransitionArray) return nullptr;
|
||||||
Map* TransitionArray::SearchSpecial(const Map* map, Symbol* name) {
|
int transition = transitions()->SearchSpecial(name);
|
||||||
Object* raw_transitions = map->raw_transitions();
|
if (transition == kNotFound) return NULL;
|
||||||
if (IsFullTransitionArray(raw_transitions)) {
|
return transitions()->GetTarget(transition);
|
||||||
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
|
|
||||||
int transition = transitions->SearchSpecial(name);
|
|
||||||
if (transition == kNotFound) return NULL;
|
|
||||||
return transitions->GetTarget(transition);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
Handle<Map> TransitionArray::FindTransitionToField(Handle<Map> map,
|
bool TransitionsAccessor::IsSpecialTransition(Name* name) {
|
||||||
Handle<Name> name) {
|
if (!name->IsSymbol()) return false;
|
||||||
|
Heap* heap = name->GetHeap();
|
||||||
|
return name == heap->nonextensible_symbol() ||
|
||||||
|
name == heap->sealed_symbol() || name == heap->frozen_symbol() ||
|
||||||
|
name == heap->elements_transition_symbol() ||
|
||||||
|
name == heap->strict_function_transition_symbol();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Map> TransitionsAccessor::FindTransitionToField(Handle<Name> name) {
|
||||||
DCHECK(name->IsUniqueName());
|
DCHECK(name->IsUniqueName());
|
||||||
DisallowHeapAllocation no_gc;
|
DisallowHeapAllocation no_gc;
|
||||||
Map* target = SearchTransition(*map, kData, *name, NONE);
|
Map* target = SearchTransition(*name, kData, NONE);
|
||||||
if (target == NULL) return Handle<Map>::null();
|
if (target == NULL) return Handle<Map>::null();
|
||||||
PropertyDetails details = target->GetLastDescriptorDetails();
|
PropertyDetails details = target->GetLastDescriptorDetails();
|
||||||
DCHECK_EQ(NONE, details.attributes());
|
DCHECK_EQ(NONE, details.attributes());
|
||||||
@ -207,34 +325,60 @@ Handle<Map> TransitionArray::FindTransitionToField(Handle<Map> map,
|
|||||||
return Handle<Map>(target);
|
return Handle<Map>(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle<String> TransitionsAccessor::ExpectedTransitionKey() {
|
||||||
// static
|
|
||||||
Handle<String> TransitionArray::ExpectedTransitionKey(Handle<Map> map) {
|
|
||||||
DisallowHeapAllocation no_gc;
|
DisallowHeapAllocation no_gc;
|
||||||
Object* raw_transition = map->raw_transitions();
|
WeakCell* cell = nullptr;
|
||||||
if (!IsSimpleTransition(raw_transition)) return Handle<String>::null();
|
switch (encoding()) {
|
||||||
Map* target = GetSimpleTransition(raw_transition);
|
case kPrototypeInfo:
|
||||||
|
case kUninitialized:
|
||||||
|
case kFullTransitionArray:
|
||||||
|
return Handle<String>::null();
|
||||||
|
case kWeakCell:
|
||||||
|
cell = GetTargetCell<kWeakCell>();
|
||||||
|
break;
|
||||||
|
case kTuple3Handler:
|
||||||
|
cell = GetTargetCell<kTuple3Handler>();
|
||||||
|
break;
|
||||||
|
case kFixedArrayHandler:
|
||||||
|
cell = GetTargetCell<kFixedArrayHandler>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DCHECK(!cell->cleared());
|
||||||
|
Map* target = Map::cast(cell->value());
|
||||||
PropertyDetails details = GetSimpleTargetDetails(target);
|
PropertyDetails details = GetSimpleTargetDetails(target);
|
||||||
if (details.location() != kField) return Handle<String>::null();
|
if (details.location() != kField) return Handle<String>::null();
|
||||||
DCHECK_EQ(kData, details.kind());
|
DCHECK_EQ(kData, details.kind());
|
||||||
if (details.attributes() != NONE) return Handle<String>::null();
|
if (details.attributes() != NONE) return Handle<String>::null();
|
||||||
Name* name = GetSimpleTransitionKey(target);
|
Name* name = GetSimpleTransitionKey(target);
|
||||||
if (!name->IsString()) return Handle<String>::null();
|
if (!name->IsString()) return Handle<String>::null();
|
||||||
return Handle<String>(String::cast(name));
|
return handle(String::cast(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle<Map> TransitionsAccessor::ExpectedTransitionTarget() {
|
||||||
|
DCHECK(!ExpectedTransitionKey().is_null());
|
||||||
|
return handle(GetTarget(0));
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
bool TransitionsAccessor::CanHaveMoreTransitions() {
|
||||||
bool TransitionArray::CanHaveMoreTransitions(Handle<Map> map) {
|
if (map_->is_dictionary_map()) return false;
|
||||||
if (map->is_dictionary_map()) return false;
|
if (encoding() == kFullTransitionArray) {
|
||||||
Object* raw_transitions = map->raw_transitions();
|
return transitions()->number_of_transitions() < kMaxNumberOfTransitions;
|
||||||
if (IsFullTransitionArray(raw_transitions)) {
|
|
||||||
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
|
|
||||||
return transitions->number_of_transitions() < kMaxNumberOfTransitions;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool TransitionsAccessor::IsMatchingMap(WeakCell* target_cell, Name* name,
|
||||||
|
PropertyKind kind,
|
||||||
|
PropertyAttributes attributes) {
|
||||||
|
Map* target = Map::cast(target_cell->value());
|
||||||
|
int descriptor = target->LastAdded();
|
||||||
|
DescriptorArray* descriptors = target->instance_descriptors();
|
||||||
|
Name* key = descriptors->GetKey(descriptor);
|
||||||
|
if (key != name) return false;
|
||||||
|
PropertyDetails details = descriptors->GetDetails(descriptor);
|
||||||
|
return (details.kind() == kind && details.attributes() == attributes);
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool TransitionArray::CompactPrototypeTransitionArray(FixedArray* array) {
|
bool TransitionArray::CompactPrototypeTransitionArray(FixedArray* array) {
|
||||||
@ -282,61 +426,48 @@ Handle<FixedArray> TransitionArray::GrowPrototypeTransitionArray(
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionsAccessor::PutPrototypeTransition(Handle<Object> prototype,
|
||||||
// static
|
Handle<Map> target_map) {
|
||||||
int TransitionArray::NumberOfPrototypeTransitionsForTest(Map* map) {
|
|
||||||
FixedArray* transitions = GetPrototypeTransitions(map);
|
|
||||||
CompactPrototypeTransitionArray(transitions);
|
|
||||||
return TransitionArray::NumberOfPrototypeTransitions(transitions);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// static
|
|
||||||
void TransitionArray::PutPrototypeTransition(Handle<Map> map,
|
|
||||||
Handle<Object> prototype,
|
|
||||||
Handle<Map> target_map) {
|
|
||||||
DCHECK(HeapObject::cast(*prototype)->map()->IsMap());
|
DCHECK(HeapObject::cast(*prototype)->map()->IsMap());
|
||||||
// Don't cache prototype transition if this map is either shared, or a map of
|
// Don't cache prototype transition if this map is either shared, or a map of
|
||||||
// a prototype.
|
// a prototype.
|
||||||
if (map->is_prototype_map()) return;
|
if (map_->is_prototype_map()) return;
|
||||||
if (map->is_dictionary_map() || !FLAG_cache_prototype_transitions) return;
|
if (map_->is_dictionary_map() || !FLAG_cache_prototype_transitions) return;
|
||||||
|
|
||||||
const int header = kProtoTransitionHeaderSize;
|
const int header = TransitionArray::kProtoTransitionHeaderSize;
|
||||||
|
|
||||||
Handle<WeakCell> target_cell = Map::WeakCellForMap(target_map);
|
Handle<FixedArray> cache(GetPrototypeTransitions());
|
||||||
|
|
||||||
Handle<FixedArray> cache(GetPrototypeTransitions(*map));
|
|
||||||
int capacity = cache->length() - header;
|
int capacity = cache->length() - header;
|
||||||
int transitions = NumberOfPrototypeTransitions(*cache) + 1;
|
int transitions = TransitionArray::NumberOfPrototypeTransitions(*cache) + 1;
|
||||||
|
|
||||||
if (transitions > capacity) {
|
if (transitions > capacity) {
|
||||||
// Grow the array if compacting it doesn't free space.
|
// Grow the array if compacting it doesn't free space.
|
||||||
if (!CompactPrototypeTransitionArray(*cache)) {
|
if (!TransitionArray::CompactPrototypeTransitionArray(*cache)) {
|
||||||
if (capacity == kMaxCachedPrototypeTransitions) return;
|
if (capacity == TransitionArray::kMaxCachedPrototypeTransitions) return;
|
||||||
cache = GrowPrototypeTransitionArray(cache, 2 * transitions,
|
cache = TransitionArray::GrowPrototypeTransitionArray(
|
||||||
map->GetIsolate());
|
cache, 2 * transitions, target_map->GetIsolate());
|
||||||
SetPrototypeTransitions(map, cache);
|
Reload();
|
||||||
|
SetPrototypeTransitions(cache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload number of transitions as they might have been compacted.
|
// Reload number of transitions as they might have been compacted.
|
||||||
int last = NumberOfPrototypeTransitions(*cache);
|
int last = TransitionArray::NumberOfPrototypeTransitions(*cache);
|
||||||
int entry = header + last;
|
int entry = header + last;
|
||||||
|
|
||||||
|
Handle<WeakCell> target_cell = Map::WeakCellForMap(target_map);
|
||||||
cache->set(entry, *target_cell);
|
cache->set(entry, *target_cell);
|
||||||
SetNumberOfPrototypeTransitions(*cache, last + 1);
|
TransitionArray::SetNumberOfPrototypeTransitions(*cache, last + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle<Map> TransitionsAccessor::GetPrototypeTransition(
|
||||||
// static
|
Handle<Object> prototype) {
|
||||||
Handle<Map> TransitionArray::GetPrototypeTransition(Handle<Map> map,
|
|
||||||
Handle<Object> prototype) {
|
|
||||||
DisallowHeapAllocation no_gc;
|
DisallowHeapAllocation no_gc;
|
||||||
FixedArray* cache = GetPrototypeTransitions(*map);
|
FixedArray* cache = GetPrototypeTransitions();
|
||||||
int number_of_transitions = NumberOfPrototypeTransitions(cache);
|
int length = TransitionArray::NumberOfPrototypeTransitions(cache);
|
||||||
for (int i = 0; i < number_of_transitions; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
WeakCell* target_cell =
|
WeakCell* target_cell = WeakCell::cast(
|
||||||
WeakCell::cast(cache->get(kProtoTransitionHeaderSize + i));
|
cache->get(TransitionArray::kProtoTransitionHeaderSize + i));
|
||||||
if (!target_cell->cleared() &&
|
if (!target_cell->cleared() &&
|
||||||
Map::cast(target_cell->value())->prototype() == *prototype) {
|
Map::cast(target_cell->value())->prototype() == *prototype) {
|
||||||
return handle(Map::cast(target_cell->value()));
|
return handle(Map::cast(target_cell->value()));
|
||||||
@ -345,22 +476,14 @@ Handle<Map> TransitionArray::GetPrototypeTransition(Handle<Map> map,
|
|||||||
return Handle<Map>();
|
return Handle<Map>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FixedArray* TransitionsAccessor::GetPrototypeTransitions() {
|
||||||
// static
|
if (encoding() != kFullTransitionArray ||
|
||||||
FixedArray* TransitionArray::GetPrototypeTransitions(Map* map) {
|
!transitions()->HasPrototypeTransitions()) {
|
||||||
Object* raw_transitions = map->raw_transitions();
|
return map_->GetHeap()->empty_fixed_array();
|
||||||
Heap* heap = map->GetHeap();
|
|
||||||
if (!IsFullTransitionArray(raw_transitions)) {
|
|
||||||
return heap->empty_fixed_array();
|
|
||||||
}
|
}
|
||||||
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
|
return transitions()->GetPrototypeTransitions();
|
||||||
if (!transitions->HasPrototypeTransitions()) {
|
|
||||||
return heap->empty_fixed_array();
|
|
||||||
}
|
|
||||||
return transitions->GetPrototypeTransitions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void TransitionArray::SetNumberOfPrototypeTransitions(
|
void TransitionArray::SetNumberOfPrototypeTransitions(
|
||||||
FixedArray* proto_transitions, int value) {
|
FixedArray* proto_transitions, int value) {
|
||||||
@ -369,29 +492,22 @@ void TransitionArray::SetNumberOfPrototypeTransitions(
|
|||||||
Smi::FromInt(value));
|
Smi::FromInt(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TransitionsAccessor::NumberOfTransitions() {
|
||||||
// static
|
switch (encoding()) {
|
||||||
int TransitionArray::NumberOfTransitions(Object* raw_transitions) {
|
case kPrototypeInfo:
|
||||||
if (CanStoreSimpleTransition(raw_transitions)) return 0;
|
case kUninitialized:
|
||||||
if (IsSimpleTransition(raw_transitions)) return 1;
|
return 0;
|
||||||
// Prototype maps don't have transitions.
|
case kWeakCell:
|
||||||
if (raw_transitions->IsPrototypeInfo()) return 0;
|
case kTuple3Handler:
|
||||||
DCHECK(IsFullTransitionArray(raw_transitions));
|
case kFixedArrayHandler:
|
||||||
return TransitionArray::cast(raw_transitions)->number_of_transitions();
|
return 1;
|
||||||
|
case kFullTransitionArray:
|
||||||
|
return transitions()->number_of_transitions();
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return 0; // Make GCC happy.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
|
||||||
int TransitionArray::Capacity(Object* raw_transitions) {
|
|
||||||
if (!IsFullTransitionArray(raw_transitions)) return 1;
|
|
||||||
TransitionArray* t = TransitionArray::cast(raw_transitions);
|
|
||||||
if (t->length() <= kFirstIndex) return 0;
|
|
||||||
return (t->length() - kFirstIndex) / kTransitionSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Private static helper functions.
|
|
||||||
|
|
||||||
Handle<TransitionArray> TransitionArray::Allocate(Isolate* isolate,
|
Handle<TransitionArray> TransitionArray::Allocate(Isolate* isolate,
|
||||||
int number_of_transitions,
|
int number_of_transitions,
|
||||||
int slack) {
|
int slack) {
|
||||||
@ -402,111 +518,118 @@ Handle<TransitionArray> TransitionArray::Allocate(Isolate* isolate,
|
|||||||
return Handle<TransitionArray>::cast(array);
|
return Handle<TransitionArray>::cast(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionArray::Zap() {
|
||||||
// static
|
|
||||||
void TransitionArray::ZapTransitionArray(TransitionArray* transitions) {
|
|
||||||
// Do not zap the next link that is used by GC.
|
// Do not zap the next link that is used by GC.
|
||||||
STATIC_ASSERT(kNextLinkIndex + 1 == kPrototypeTransitionsIndex);
|
STATIC_ASSERT(kNextLinkIndex + 1 == kPrototypeTransitionsIndex);
|
||||||
MemsetPointer(transitions->data_start() + kPrototypeTransitionsIndex,
|
MemsetPointer(data_start() + kPrototypeTransitionsIndex,
|
||||||
transitions->GetHeap()->the_hole_value(),
|
GetHeap()->the_hole_value(),
|
||||||
transitions->length() - kPrototypeTransitionsIndex);
|
length() - kPrototypeTransitionsIndex);
|
||||||
transitions->SetNumberOfTransitions(0);
|
SetNumberOfTransitions(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionsAccessor::ReplaceTransitions(Object* new_transitions) {
|
||||||
void TransitionArray::ReplaceTransitions(Handle<Map> map,
|
if (encoding() == kFullTransitionArray) {
|
||||||
Object* new_transitions) {
|
TransitionArray* old_transitions = transitions();
|
||||||
Object* raw_transitions = map->raw_transitions();
|
#if DEBUG
|
||||||
if (IsFullTransitionArray(raw_transitions)) {
|
CheckNewTransitionsAreConsistent(old_transitions, new_transitions);
|
||||||
TransitionArray* old_transitions = TransitionArray::cast(raw_transitions);
|
|
||||||
#ifdef DEBUG
|
|
||||||
CheckNewTransitionsAreConsistent(map, old_transitions, new_transitions);
|
|
||||||
DCHECK(old_transitions != new_transitions);
|
DCHECK(old_transitions != new_transitions);
|
||||||
#endif
|
#endif
|
||||||
// Transition arrays are not shared. When one is replaced, it should not
|
// Transition arrays are not shared. When one is replaced, it should not
|
||||||
// keep referenced objects alive, so we zap it.
|
// keep referenced objects alive, so we zap it.
|
||||||
// When there is another reference to the array somewhere (e.g. a handle),
|
// When there is another reference to the array somewhere (e.g. a handle),
|
||||||
// not zapping turns from a waste of memory into a source of crashes.
|
// not zapping turns from a waste of memory into a source of crashes.
|
||||||
ZapTransitionArray(old_transitions);
|
old_transitions->Zap();
|
||||||
}
|
}
|
||||||
map->set_raw_transitions(new_transitions);
|
map_->set_raw_transitions(new_transitions);
|
||||||
|
MarkNeedsReload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionsAccessor::SetPrototypeTransitions(
|
||||||
void TransitionArray::SetPrototypeTransitions(
|
Handle<FixedArray> proto_transitions) {
|
||||||
Handle<Map> map, Handle<FixedArray> proto_transitions) {
|
EnsureHasFullTransitionArray();
|
||||||
EnsureHasFullTransitionArray(map);
|
transitions()->SetPrototypeTransitions(*proto_transitions);
|
||||||
TransitionArray* transitions = TransitionArray::cast(map->raw_transitions());
|
|
||||||
transitions->SetPrototypeTransitions(*proto_transitions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionsAccessor::EnsureHasFullTransitionArray() {
|
||||||
void TransitionArray::EnsureHasFullTransitionArray(Handle<Map> map) {
|
if (encoding() == kFullTransitionArray) return;
|
||||||
Object* raw_transitions = map->raw_transitions();
|
int nof = encoding() == kUninitialized ? 0 : 1;
|
||||||
if (IsFullTransitionArray(raw_transitions)) return;
|
Handle<TransitionArray> result =
|
||||||
int nof = IsSimpleTransition(raw_transitions) ? 1 : 0;
|
TransitionArray::Allocate(map_->GetIsolate(), nof);
|
||||||
Handle<TransitionArray> result = Allocate(map->GetIsolate(), nof);
|
|
||||||
DisallowHeapAllocation no_gc;
|
DisallowHeapAllocation no_gc;
|
||||||
// Reload pointer after the allocation that just happened.
|
Reload(); // Reload after possible GC.
|
||||||
raw_transitions = map->raw_transitions();
|
if (nof == 1) {
|
||||||
int new_nof = IsSimpleTransition(raw_transitions) ? 1 : 0;
|
Map* target = GetSimpleTransition();
|
||||||
if (new_nof != nof) {
|
if (target == nullptr) {
|
||||||
DCHECK(new_nof == 0);
|
// If allocation caused GC and cleared the target, trim the new array.
|
||||||
result->Shrink(ToKeyIndex(0));
|
result->Shrink(TransitionArray::ToKeyIndex(0));
|
||||||
result->SetNumberOfTransitions(0);
|
result->SetNumberOfTransitions(0);
|
||||||
} else if (nof == 1) {
|
} else {
|
||||||
Map* target = GetSimpleTransition(raw_transitions);
|
// Otherwise populate the new array.
|
||||||
Name* key = GetSimpleTransitionKey(target);
|
Name* key = GetSimpleTransitionKey(target);
|
||||||
result->Set(0, key, target);
|
result->Set(0, key, target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ReplaceTransitions(map, *result);
|
ReplaceTransitions(*result);
|
||||||
|
Reload(); // Reload after replacing transitions.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransitionsAccessor::TraverseTransitionTreeInternal(
|
||||||
void TransitionArray::TraverseTransitionTreeInternal(Map* map,
|
TraverseCallback callback, void* data, DisallowHeapAllocation* no_gc) {
|
||||||
TraverseCallback callback,
|
Map* simple_target = nullptr;
|
||||||
void* data) {
|
switch (encoding()) {
|
||||||
Object* raw_transitions = map->raw_transitions();
|
case kPrototypeInfo:
|
||||||
if (IsFullTransitionArray(raw_transitions)) {
|
case kUninitialized:
|
||||||
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
|
break;
|
||||||
if (transitions->HasPrototypeTransitions()) {
|
case kWeakCell:
|
||||||
FixedArray* proto_trans = transitions->GetPrototypeTransitions();
|
simple_target = Map::cast(GetTargetCell<kWeakCell>()->value());
|
||||||
for (int i = 0; i < NumberOfPrototypeTransitions(proto_trans); ++i) {
|
break;
|
||||||
int index = TransitionArray::kProtoTransitionHeaderSize + i;
|
case kTuple3Handler:
|
||||||
WeakCell* cell = WeakCell::cast(proto_trans->get(index));
|
simple_target = Map::cast(GetTargetCell<kTuple3Handler>()->value());
|
||||||
if (!cell->cleared()) {
|
break;
|
||||||
TraverseTransitionTreeInternal(Map::cast(cell->value()), callback,
|
case kFixedArrayHandler:
|
||||||
data);
|
simple_target = Map::cast(GetTargetCell<kFixedArrayHandler>()->value());
|
||||||
|
break;
|
||||||
|
case kFullTransitionArray: {
|
||||||
|
if (transitions()->HasPrototypeTransitions()) {
|
||||||
|
FixedArray* proto_trans = transitions()->GetPrototypeTransitions();
|
||||||
|
int length = TransitionArray::NumberOfPrototypeTransitions(proto_trans);
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
int index = TransitionArray::kProtoTransitionHeaderSize + i;
|
||||||
|
WeakCell* cell = WeakCell::cast(proto_trans->get(index));
|
||||||
|
if (cell->cleared()) continue;
|
||||||
|
TransitionsAccessor(Map::cast(cell->value()), no_gc)
|
||||||
|
.TraverseTransitionTreeInternal(callback, data, no_gc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < transitions()->number_of_transitions(); ++i) {
|
||||||
|
TransitionsAccessor(transitions()->GetTarget(i), no_gc)
|
||||||
|
.TraverseTransitionTreeInternal(callback, data, no_gc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
|
|
||||||
TraverseTransitionTreeInternal(transitions->GetTarget(i), callback, data);
|
|
||||||
}
|
|
||||||
} else if (IsSimpleTransition(raw_transitions)) {
|
|
||||||
TraverseTransitionTreeInternal(GetSimpleTransition(raw_transitions),
|
|
||||||
callback, data);
|
|
||||||
}
|
}
|
||||||
callback(map, data);
|
if (simple_target != nullptr) {
|
||||||
|
TransitionsAccessor(simple_target, no_gc)
|
||||||
|
.TraverseTransitionTreeInternal(callback, data, no_gc);
|
||||||
|
}
|
||||||
|
callback(map_, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void TransitionArray::CheckNewTransitionsAreConsistent(
|
void TransitionsAccessor::CheckNewTransitionsAreConsistent(
|
||||||
Handle<Map> map, TransitionArray* old_transitions, Object* transitions) {
|
TransitionArray* old_transitions, Object* transitions) {
|
||||||
// This function only handles full transition arrays.
|
// This function only handles full transition arrays.
|
||||||
DCHECK(IsFullTransitionArray(transitions));
|
DCHECK_EQ(kFullTransitionArray, encoding());
|
||||||
TransitionArray* new_transitions = TransitionArray::cast(transitions);
|
TransitionArray* new_transitions = TransitionArray::cast(transitions);
|
||||||
for (int i = 0; i < old_transitions->number_of_transitions(); i++) {
|
for (int i = 0; i < old_transitions->number_of_transitions(); i++) {
|
||||||
Map* target = old_transitions->GetTarget(i);
|
Map* target = old_transitions->GetTarget(i);
|
||||||
if (target->instance_descriptors() == map->instance_descriptors()) {
|
if (target->instance_descriptors() == map_->instance_descriptors()) {
|
||||||
Name* key = old_transitions->GetKey(i);
|
Name* key = old_transitions->GetKey(i);
|
||||||
int new_target_index;
|
int new_target_index;
|
||||||
if (TransitionArray::IsSpecialTransition(key)) {
|
if (IsSpecialTransition(key)) {
|
||||||
new_target_index = new_transitions->SearchSpecial(Symbol::cast(key));
|
new_target_index = new_transitions->SearchSpecial(Symbol::cast(key));
|
||||||
} else {
|
} else {
|
||||||
PropertyDetails details =
|
PropertyDetails details = GetTargetDetails(key, target);
|
||||||
TransitionArray::GetTargetDetails(key, target);
|
|
||||||
new_target_index =
|
new_target_index =
|
||||||
new_transitions->Search(details.kind(), key, details.attributes());
|
new_transitions->Search(details.kind(), key, details.attributes());
|
||||||
}
|
}
|
||||||
@ -517,7 +640,6 @@ void TransitionArray::CheckNewTransitionsAreConsistent(
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Private non-static helper functions (operating on full transition arrays).
|
// Private non-static helper functions (operating on full transition arrays).
|
||||||
|
|
||||||
int TransitionArray::SearchDetails(int transition, PropertyKind kind,
|
int TransitionArray::SearchDetails(int transition, PropertyKind kind,
|
||||||
@ -529,7 +651,8 @@ int TransitionArray::SearchDetails(int transition, PropertyKind kind,
|
|||||||
for (; transition < nof_transitions && GetKey(transition) == key;
|
for (; transition < nof_transitions && GetKey(transition) == key;
|
||||||
transition++) {
|
transition++) {
|
||||||
Map* target = GetTarget(transition);
|
Map* target = GetTarget(transition);
|
||||||
PropertyDetails target_details = GetTargetDetails(key, target);
|
PropertyDetails target_details =
|
||||||
|
TransitionsAccessor::GetTargetDetails(key, target);
|
||||||
|
|
||||||
int cmp = CompareDetails(kind, attributes, target_details.kind(),
|
int cmp = CompareDetails(kind, attributes, target_details.kind(),
|
||||||
target_details.attributes());
|
target_details.attributes());
|
||||||
@ -561,8 +684,9 @@ void TransitionArray::Sort() {
|
|||||||
Map* target = GetTarget(i);
|
Map* target = GetTarget(i);
|
||||||
PropertyKind kind = kData;
|
PropertyKind kind = kData;
|
||||||
PropertyAttributes attributes = NONE;
|
PropertyAttributes attributes = NONE;
|
||||||
if (!IsSpecialTransition(key)) {
|
if (!TransitionsAccessor::IsSpecialTransition(key)) {
|
||||||
PropertyDetails details = GetTargetDetails(key, target);
|
PropertyDetails details =
|
||||||
|
TransitionsAccessor::GetTargetDetails(key, target);
|
||||||
kind = details.kind();
|
kind = details.kind();
|
||||||
attributes = details.attributes();
|
attributes = details.attributes();
|
||||||
}
|
}
|
||||||
@ -572,8 +696,9 @@ void TransitionArray::Sort() {
|
|||||||
Map* temp_target = GetTarget(j);
|
Map* temp_target = GetTarget(j);
|
||||||
PropertyKind temp_kind = kData;
|
PropertyKind temp_kind = kData;
|
||||||
PropertyAttributes temp_attributes = NONE;
|
PropertyAttributes temp_attributes = NONE;
|
||||||
if (!IsSpecialTransition(temp_key)) {
|
if (!TransitionsAccessor::IsSpecialTransition(temp_key)) {
|
||||||
PropertyDetails details = GetTargetDetails(temp_key, temp_target);
|
PropertyDetails details =
|
||||||
|
TransitionsAccessor::GetTargetDetails(temp_key, temp_target);
|
||||||
temp_kind = details.kind();
|
temp_kind = details.kind();
|
||||||
temp_attributes = details.attributes();
|
temp_attributes = details.attributes();
|
||||||
}
|
}
|
||||||
|
@ -15,79 +15,82 @@
|
|||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
// TransitionsAccessor is a helper class to encapsulate access to the various
|
||||||
// TransitionArrays are fixed arrays used to hold map transitions for property,
|
// ways a Map can store transitions to other maps in its respective field at
|
||||||
// constant, and element changes. "Simple" transitions storing only a single
|
// Map::kTransitionsOrPrototypeInfo.
|
||||||
// property transition are stored inline (i.e. the target map is stored
|
// It caches state information internally, which becomes stale when a Map's
|
||||||
// directly); otherwise a full transition array is used that has
|
// transitions storage changes or when a GC cycle clears dead transitions;
|
||||||
// prototype transitions and multiple property transitons. The details related
|
// so while a TransitionsAccessor instance can be used for several read-only
|
||||||
// to property transitions are accessed in the descriptor array of the target
|
// operations in a row (provided no GC happens between them), it must be
|
||||||
// map. In the case of a simple transition, the key is also read from the
|
// discarded and recreated after "Insert" and "UpdateHandler" operations.
|
||||||
// descriptor array of the target map.
|
|
||||||
//
|
//
|
||||||
// This class provides a static interface that operates directly on maps
|
// Internal details: a Map's field either holds a WeakCell to a transition
|
||||||
// and handles the distinction between simple and full transitions storage.
|
// target, or a StoreIC handler for a transitioning store (which in turn points
|
||||||
//
|
// to its target map), or a TransitionArray for several target maps and/or
|
||||||
// The full format is:
|
// handlers as well as prototype and ElementsKind transitions.
|
||||||
// [0] Smi(0) or fixed array of prototype transitions
|
// Property details (and in case of inline target storage, the key) are
|
||||||
// [1] Number of transitions
|
// retrieved from the target map's descriptor array.
|
||||||
// [2] First transition
|
// Stored transitions are weak in the GC sense: both single transitions stored
|
||||||
// [2 + number of transitions * kTransitionSize]: start of slack
|
// inline and TransitionArray fields are cleared when the map they refer to
|
||||||
class TransitionArray: public FixedArray {
|
// is not otherwise reachable.
|
||||||
|
class TransitionsAccessor {
|
||||||
public:
|
public:
|
||||||
|
TransitionsAccessor(Map* map, DisallowHeapAllocation* no_gc) : map_(map) {
|
||||||
|
Initialize();
|
||||||
|
USE(no_gc);
|
||||||
|
}
|
||||||
|
explicit TransitionsAccessor(Handle<Map> map) : map_handle_(map), map_(*map) {
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
// Insert a new transition into |map|'s transition array, extending it
|
// Insert a new transition into |map|'s transition array, extending it
|
||||||
// as necessary.
|
// as necessary.
|
||||||
static void Insert(Handle<Map> map, Handle<Name> name, Handle<Map> target,
|
// Requires the constructor that takes a Handle<Map> to have been used.
|
||||||
SimpleTransitionFlag flag);
|
// This TransitionsAccessor instance is unusable after this operation.
|
||||||
|
void Insert(Handle<Name> name, Handle<Map> target, SimpleTransitionFlag flag);
|
||||||
|
|
||||||
static Map* SearchTransition(Map* map, PropertyKind kind, Name* name,
|
Map* SearchTransition(Name* name, PropertyKind kind,
|
||||||
PropertyAttributes attributes);
|
PropertyAttributes attributes);
|
||||||
static MaybeHandle<Map> SearchTransition(Handle<Map> map, PropertyKind kind,
|
|
||||||
Handle<Name> name,
|
|
||||||
PropertyAttributes attributes) {
|
|
||||||
if (Map* transition = SearchTransition(*map, kind, *name, attributes)) {
|
|
||||||
return handle(transition);
|
|
||||||
}
|
|
||||||
return MaybeHandle<Map>();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map* SearchSpecial(const Map* map, Symbol* name);
|
// This TransitionsAccessor instance is unusable after this operation.
|
||||||
|
void UpdateHandler(Name* name, Object* handler);
|
||||||
|
|
||||||
static Handle<Map> FindTransitionToField(Handle<Map> map, Handle<Name> name);
|
// If a valid handler is found, returns the transition target in
|
||||||
|
// |out_transition|.
|
||||||
|
Object* SearchHandler(Name* name, Handle<Map>* out_transition);
|
||||||
|
|
||||||
static Handle<String> ExpectedTransitionKey(Handle<Map> map);
|
Map* SearchSpecial(Symbol* name);
|
||||||
|
// Returns true for non-property transitions like elements kind, or
|
||||||
|
// or frozen/sealed transitions.
|
||||||
|
static bool IsSpecialTransition(Name* name);
|
||||||
|
|
||||||
static Handle<Map> ExpectedTransitionTarget(Handle<Map> map) {
|
Handle<Map> FindTransitionToField(Handle<Name> name);
|
||||||
DCHECK(!ExpectedTransitionKey(map).is_null());
|
|
||||||
return Handle<Map>(GetSimpleTransition(map->raw_transitions()));
|
|
||||||
}
|
|
||||||
// Returns true if |raw_transition| can be overwritten with a simple
|
|
||||||
// transition (because it's either uninitialized, or has been cleared).
|
|
||||||
static inline bool CanStoreSimpleTransition(Object* raw_transition) {
|
|
||||||
return raw_transition->IsSmi() ||
|
|
||||||
(raw_transition->IsWeakCell() &&
|
|
||||||
WeakCell::cast(raw_transition)->cleared());
|
|
||||||
}
|
|
||||||
static inline bool IsSimpleTransition(Object* raw_transition) {
|
|
||||||
DCHECK(!raw_transition->IsWeakCell() ||
|
|
||||||
WeakCell::cast(raw_transition)->cleared() ||
|
|
||||||
WeakCell::cast(raw_transition)->value()->IsMap());
|
|
||||||
return raw_transition->IsWeakCell() &&
|
|
||||||
!WeakCell::cast(raw_transition)->cleared();
|
|
||||||
}
|
|
||||||
static inline Map* GetSimpleTransition(Object* raw_transition) {
|
|
||||||
DCHECK(IsSimpleTransition(raw_transition));
|
|
||||||
DCHECK(raw_transition->IsWeakCell());
|
|
||||||
return Map::cast(WeakCell::cast(raw_transition)->value());
|
|
||||||
}
|
|
||||||
static inline bool IsFullTransitionArray(Object* raw_transitions) {
|
|
||||||
return raw_transitions->IsTransitionArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Handle<String> ExpectedTransitionKey();
|
||||||
|
Handle<Map> ExpectedTransitionTarget();
|
||||||
|
|
||||||
|
int NumberOfTransitions();
|
||||||
// The size of transition arrays are limited so they do not end up in large
|
// The size of transition arrays are limited so they do not end up in large
|
||||||
// object space. Otherwise ClearNonLiveReferences would leak memory while
|
// object space. Otherwise ClearNonLiveReferences would leak memory while
|
||||||
// applying in-place right trimming.
|
// applying in-place right trimming.
|
||||||
static bool CanHaveMoreTransitions(Handle<Map> map);
|
static const int kMaxNumberOfTransitions = 1024 + 512;
|
||||||
|
bool CanHaveMoreTransitions();
|
||||||
|
inline Name* GetKey(int transition_number);
|
||||||
|
inline Map* GetTarget(int transition_number);
|
||||||
|
static inline PropertyDetails GetTargetDetails(Name* name, Map* target);
|
||||||
|
|
||||||
|
static bool IsMatchingMap(WeakCell* target_cell, Name* name,
|
||||||
|
PropertyKind kind, PropertyAttributes attributes);
|
||||||
|
|
||||||
|
// ===== ITERATION =====
|
||||||
|
typedef void (*TraverseCallback)(Map* map, void* data);
|
||||||
|
|
||||||
|
// Traverse the transition tree in postorder.
|
||||||
|
void TraverseTransitionTree(TraverseCallback callback, void* data) {
|
||||||
|
// Make sure that we do not allocate in the callback.
|
||||||
|
DisallowHeapAllocation no_allocation;
|
||||||
|
TraverseTransitionTreeInternal(callback, data, &no_allocation);
|
||||||
|
}
|
||||||
|
|
||||||
// ===== PROTOTYPE TRANSITIONS =====
|
// ===== PROTOTYPE TRANSITIONS =====
|
||||||
// When you set the prototype of an object using the __proto__ accessor you
|
// When you set the prototype of an object using the __proto__ accessor you
|
||||||
@ -97,91 +100,149 @@ class TransitionArray: public FixedArray {
|
|||||||
// prototype is set, rather than creating a new map every time. The
|
// prototype is set, rather than creating a new map every time. The
|
||||||
// transitions are in the form of a map where the keys are prototype objects
|
// transitions are in the form of a map where the keys are prototype objects
|
||||||
// and the values are the maps they transition to.
|
// and the values are the maps they transition to.
|
||||||
// Cache format:
|
void PutPrototypeTransition(Handle<Object> prototype, Handle<Map> target_map);
|
||||||
// 0: finger - index of the first free cell in the cache
|
Handle<Map> GetPrototypeTransition(Handle<Object> prototype);
|
||||||
// 1 + i: target map
|
|
||||||
static const int kMaxCachedPrototypeTransitions = 256;
|
|
||||||
static void PutPrototypeTransition(Handle<Map> map, Handle<Object> prototype,
|
|
||||||
Handle<Map> target_map);
|
|
||||||
|
|
||||||
static Handle<Map> GetPrototypeTransition(Handle<Map> map,
|
#if DEBUG || OBJECT_PRINT
|
||||||
Handle<Object> prototype);
|
void PrintTransitions(std::ostream& os);
|
||||||
|
static void PrintOneTransition(std::ostream& os, Name* key, Map* target,
|
||||||
|
Object* raw_target);
|
||||||
|
void PrintTransitionTree();
|
||||||
|
void PrintTransitionTree(std::ostream& os, int level,
|
||||||
|
DisallowHeapAllocation* no_gc);
|
||||||
|
#endif
|
||||||
|
#if DEBUG
|
||||||
|
void CheckNewTransitionsAreConsistent(TransitionArray* old_transitions,
|
||||||
|
Object* transitions);
|
||||||
|
bool IsConsistentWithBackPointers();
|
||||||
|
bool IsSortedNoDuplicates();
|
||||||
|
#endif
|
||||||
|
|
||||||
static FixedArray* GetPrototypeTransitions(Map* map);
|
protected:
|
||||||
|
// Allow tests to use inheritance to access internals.
|
||||||
|
enum Encoding {
|
||||||
|
kPrototypeInfo,
|
||||||
|
kUninitialized,
|
||||||
|
kWeakCell,
|
||||||
|
kTuple3Handler,
|
||||||
|
kFixedArrayHandler,
|
||||||
|
kFullTransitionArray,
|
||||||
|
};
|
||||||
|
|
||||||
static int NumberOfPrototypeTransitions(FixedArray* proto_transitions) {
|
void Reload() {
|
||||||
if (proto_transitions->length() == 0) return 0;
|
DCHECK(!map_handle_.is_null());
|
||||||
Object* raw = proto_transitions->get(kProtoTransitionNumberOfEntriesOffset);
|
map_ = *map_handle_;
|
||||||
return Smi::ToInt(raw);
|
Initialize();
|
||||||
}
|
}
|
||||||
static int NumberOfPrototypeTransitionsForTest(Map* map);
|
|
||||||
|
|
||||||
static void SetNumberOfPrototypeTransitions(FixedArray* proto_transitions,
|
inline Encoding encoding() {
|
||||||
int value);
|
DCHECK(!needs_reload_);
|
||||||
|
return encoding_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class MarkCompactCollector; // For HasSimpleTransitionTo.
|
||||||
|
friend class TransitionArray;
|
||||||
|
|
||||||
|
static inline PropertyDetails GetSimpleTargetDetails(Map* transition) {
|
||||||
|
return transition->GetLastDescriptorDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Name* GetSimpleTransitionKey(Map* transition) {
|
||||||
|
int descriptor = transition->LastAdded();
|
||||||
|
return transition->instance_descriptors()->GetKey(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Map* GetTargetFromRaw(Object* raw);
|
||||||
|
|
||||||
|
void MarkNeedsReload() {
|
||||||
|
#if DEBUG
|
||||||
|
needs_reload_ = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
|
inline Map* GetSimpleTransition();
|
||||||
|
bool HasSimpleTransitionTo(WeakCell* cell);
|
||||||
|
|
||||||
|
void ReplaceTransitions(Object* new_transitions);
|
||||||
|
|
||||||
|
template <Encoding enc>
|
||||||
|
inline WeakCell* GetTargetCell();
|
||||||
|
|
||||||
|
void EnsureHasFullTransitionArray();
|
||||||
|
void SetPrototypeTransitions(Handle<FixedArray> proto_transitions);
|
||||||
|
FixedArray* GetPrototypeTransitions();
|
||||||
|
|
||||||
|
void TraverseTransitionTreeInternal(TraverseCallback callback, void* data,
|
||||||
|
DisallowHeapAllocation* no_gc);
|
||||||
|
|
||||||
|
inline TransitionArray* transitions();
|
||||||
|
|
||||||
|
Handle<Map> map_handle_;
|
||||||
|
Map* map_;
|
||||||
|
Object* raw_transitions_;
|
||||||
|
Encoding encoding_;
|
||||||
|
WeakCell* target_cell_;
|
||||||
|
#if DEBUG
|
||||||
|
bool needs_reload_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionsAccessor);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TransitionArrays are fixed arrays used to hold map transitions for property,
|
||||||
|
// constant, and element changes.
|
||||||
|
// The TransitionArray class exposes a very low-level interface. Most clients
|
||||||
|
// should use TransitionsAccessors.
|
||||||
|
// TransitionArrays have the following format:
|
||||||
|
// [0] Link to next TransitionArray (for weak handling support)
|
||||||
|
// [1] Smi(0) or fixed array of prototype transitions
|
||||||
|
// [2] Number of transitions (can be zero after trimming)
|
||||||
|
// [3] First transition key
|
||||||
|
// [4] First transition target
|
||||||
|
// ...
|
||||||
|
// [3 + number of transitions * kTransitionSize]: start of slack
|
||||||
|
class TransitionArray : public FixedArray {
|
||||||
|
public:
|
||||||
|
inline static TransitionArray* cast(Object* object);
|
||||||
|
|
||||||
inline FixedArray* GetPrototypeTransitions();
|
inline FixedArray* GetPrototypeTransitions();
|
||||||
inline void SetPrototypeTransitions(FixedArray* prototype_transitions);
|
|
||||||
inline Object** GetPrototypeTransitionsSlot();
|
inline Object** GetPrototypeTransitionsSlot();
|
||||||
inline bool HasPrototypeTransitions();
|
inline bool HasPrototypeTransitions();
|
||||||
|
|
||||||
// ===== ITERATION =====
|
|
||||||
|
|
||||||
typedef void (*TraverseCallback)(Map* map, void* data);
|
|
||||||
|
|
||||||
// Traverse the transition tree in postorder.
|
|
||||||
static void TraverseTransitionTree(Map* map, TraverseCallback callback,
|
|
||||||
void* data) {
|
|
||||||
// Make sure that we do not allocate in the callback.
|
|
||||||
DisallowHeapAllocation no_allocation;
|
|
||||||
TraverseTransitionTreeInternal(map, callback, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== LOW-LEVEL ACCESSORS =====
|
|
||||||
|
|
||||||
// Accessors for fetching instance transition at transition number.
|
// Accessors for fetching instance transition at transition number.
|
||||||
static inline Name* GetKey(Object* raw_transitions, int transition_number);
|
|
||||||
inline Name* GetKey(int transition_number);
|
|
||||||
inline void SetKey(int transition_number, Name* value);
|
inline void SetKey(int transition_number, Name* value);
|
||||||
|
inline Name* GetKey(int transition_number);
|
||||||
inline Object** GetKeySlot(int transition_number);
|
inline Object** GetKeySlot(int transition_number);
|
||||||
int GetSortedKeyIndex(int transition_number) { return transition_number; }
|
|
||||||
|
|
||||||
|
inline Map* GetTarget(int transition_number);
|
||||||
|
inline void SetTarget(int transition_number, Object* target);
|
||||||
|
inline Object* GetRawTarget(int transition_number);
|
||||||
|
inline Object** GetTargetSlot(int transition_number);
|
||||||
|
|
||||||
|
// Required for templatized Search interface.
|
||||||
|
static const int kNotFound = -1;
|
||||||
Name* GetSortedKey(int transition_number) {
|
Name* GetSortedKey(int transition_number) {
|
||||||
return GetKey(transition_number);
|
return GetKey(transition_number);
|
||||||
}
|
}
|
||||||
|
int GetSortedKeyIndex(int transition_number) { return transition_number; }
|
||||||
static inline Map* GetTarget(Object* raw_transitions, int transition_number);
|
|
||||||
inline Map* GetTarget(int transition_number);
|
|
||||||
inline void SetTarget(int transition_number, Map* target);
|
|
||||||
|
|
||||||
static inline PropertyDetails GetTargetDetails(Name* name, Map* target);
|
|
||||||
|
|
||||||
// Returns the number of transitions in the array.
|
|
||||||
static int NumberOfTransitions(Object* raw_transitions);
|
|
||||||
// Required for templatized Search interface.
|
|
||||||
inline int number_of_entries() { return number_of_transitions(); }
|
inline int number_of_entries() { return number_of_transitions(); }
|
||||||
|
#ifdef DEBUG
|
||||||
|
bool IsSortedNoDuplicates(int valid_entries = -1);
|
||||||
|
#endif
|
||||||
|
|
||||||
inline void SetNumberOfTransitions(int number_of_transitions);
|
void Sort();
|
||||||
|
|
||||||
static int Capacity(Object* raw_transitions);
|
// This field should be used only by the GC.
|
||||||
|
|
||||||
inline static TransitionArray* cast(Object* object);
|
|
||||||
|
|
||||||
// This field should be used only by GC.
|
|
||||||
inline void set_next_link(Object* next, WriteBarrierMode mode);
|
inline void set_next_link(Object* next, WriteBarrierMode mode);
|
||||||
inline Object* next_link();
|
inline Object* next_link();
|
||||||
|
|
||||||
static const int kTransitionSize = 2;
|
|
||||||
static const int kProtoTransitionHeaderSize = 1;
|
|
||||||
|
|
||||||
#if defined(DEBUG) || defined(OBJECT_PRINT)
|
#if defined(DEBUG) || defined(OBJECT_PRINT)
|
||||||
// For our gdb macros, we should perhaps change these in the future.
|
// For our gdb macros.
|
||||||
void Print();
|
void Print();
|
||||||
|
void Print(std::ostream& os);
|
||||||
// Print all the transitions.
|
|
||||||
static void PrintTransitions(std::ostream& os, Object* transitions,
|
|
||||||
bool print_header = true); // NOLINT
|
|
||||||
static void PrintTransitionTree(Map* map);
|
|
||||||
static void PrintTransitionTree(std::ostream& os, Map* map, int level = 0);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OBJECT_PRINT
|
#ifdef OBJECT_PRINT
|
||||||
@ -192,26 +253,33 @@ class TransitionArray: public FixedArray {
|
|||||||
void TransitionArrayVerify();
|
void TransitionArrayVerify();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Sort();
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
bool IsSortedNoDuplicates(int valid_entries = -1);
|
|
||||||
static bool IsSortedNoDuplicates(Map* map);
|
|
||||||
static bool IsConsistentWithBackPointers(Map* map);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Returns true for a non-property transitions like elements kind, observed
|
|
||||||
// or frozen transitions.
|
|
||||||
static inline bool IsSpecialTransition(Name* name);
|
|
||||||
|
|
||||||
// Constant for denoting key was not found.
|
|
||||||
static const int kNotFound = -1;
|
|
||||||
|
|
||||||
// The maximum number of transitions we want in a transition array (should
|
|
||||||
// fit in a page).
|
|
||||||
static const int kMaxNumberOfTransitions = 1024 + 512;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class MarkCompactCollector;
|
||||||
|
friend class TransitionsAccessor;
|
||||||
|
|
||||||
|
static const int kTransitionSize = 2;
|
||||||
|
|
||||||
|
inline void SetNumberOfTransitions(int number_of_transitions);
|
||||||
|
|
||||||
|
inline int Capacity();
|
||||||
|
|
||||||
|
// ===== PROTOTYPE TRANSITIONS =====
|
||||||
|
// Cache format:
|
||||||
|
// 0: finger - index of the first free cell in the cache
|
||||||
|
// 1 + i: target map
|
||||||
|
static const int kProtoTransitionHeaderSize = 1;
|
||||||
|
static const int kMaxCachedPrototypeTransitions = 256;
|
||||||
|
|
||||||
|
inline void SetPrototypeTransitions(FixedArray* prototype_transitions);
|
||||||
|
|
||||||
|
static int NumberOfPrototypeTransitions(FixedArray* proto_transitions) {
|
||||||
|
if (proto_transitions->length() == 0) return 0;
|
||||||
|
Object* raw = proto_transitions->get(kProtoTransitionNumberOfEntriesOffset);
|
||||||
|
return Smi::ToInt(raw);
|
||||||
|
}
|
||||||
|
static void SetNumberOfPrototypeTransitions(FixedArray* proto_transitions,
|
||||||
|
int value);
|
||||||
|
|
||||||
// Layout for full transition arrays.
|
// Layout for full transition arrays.
|
||||||
static const int kNextLinkIndex = 0;
|
static const int kNextLinkIndex = 0;
|
||||||
static const int kPrototypeTransitionsIndex = 1;
|
static const int kPrototypeTransitionsIndex = 1;
|
||||||
@ -250,9 +318,6 @@ class TransitionArray: public FixedArray {
|
|||||||
int number_of_transitions,
|
int number_of_transitions,
|
||||||
int slack = 0);
|
int slack = 0);
|
||||||
|
|
||||||
static void EnsureHasFullTransitionArray(Handle<Map> map);
|
|
||||||
static void ReplaceTransitions(Handle<Map> map, Object* new_transitions);
|
|
||||||
|
|
||||||
// Search a transition for a given kind, property name and attributes.
|
// Search a transition for a given kind, property name and attributes.
|
||||||
int Search(PropertyKind kind, Name* name, PropertyAttributes attributes,
|
int Search(PropertyKind kind, Name* name, PropertyAttributes attributes,
|
||||||
int* out_insertion_index = NULL);
|
int* out_insertion_index = NULL);
|
||||||
@ -272,22 +337,6 @@ class TransitionArray: public FixedArray {
|
|||||||
return Smi::ToInt(get(kTransitionLengthIndex));
|
return Smi::ToInt(get(kTransitionLengthIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline PropertyDetails GetSimpleTargetDetails(Map* transition) {
|
|
||||||
return transition->GetLastDescriptorDetails();
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline Name* GetSimpleTransitionKey(Map* transition) {
|
|
||||||
int descriptor = transition->LastAdded();
|
|
||||||
return transition->instance_descriptors()->GetKey(descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void TraverseTransitionTreeInternal(Map* map,
|
|
||||||
TraverseCallback callback,
|
|
||||||
void* data);
|
|
||||||
|
|
||||||
static void SetPrototypeTransitions(Handle<Map> map,
|
|
||||||
Handle<FixedArray> proto_transitions);
|
|
||||||
|
|
||||||
static bool CompactPrototypeTransitionArray(FixedArray* array);
|
static bool CompactPrototypeTransitionArray(FixedArray* array);
|
||||||
|
|
||||||
static Handle<FixedArray> GrowPrototypeTransitionArray(
|
static Handle<FixedArray> GrowPrototypeTransitionArray(
|
||||||
@ -312,14 +361,9 @@ class TransitionArray: public FixedArray {
|
|||||||
PropertyKind kind2,
|
PropertyKind kind2,
|
||||||
PropertyAttributes attributes2);
|
PropertyAttributes attributes2);
|
||||||
|
|
||||||
inline void Set(int transition_number, Name* key, Map* target);
|
inline void Set(int transition_number, Name* key, Object* target);
|
||||||
|
|
||||||
#ifdef DEBUG
|
void Zap();
|
||||||
static void CheckNewTransitionsAreConsistent(Handle<Map> map,
|
|
||||||
TransitionArray* old_transitions,
|
|
||||||
Object* transitions);
|
|
||||||
#endif
|
|
||||||
static void ZapTransitionArray(TransitionArray* transitions);
|
|
||||||
|
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray);
|
DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray);
|
||||||
};
|
};
|
||||||
|
@ -1060,6 +1060,7 @@
|
|||||||
'ic/call-optimization.h',
|
'ic/call-optimization.h',
|
||||||
'ic/handler-compiler.cc',
|
'ic/handler-compiler.cc',
|
||||||
'ic/handler-compiler.h',
|
'ic/handler-compiler.h',
|
||||||
|
'ic/handler-configuration.cc',
|
||||||
'ic/handler-configuration-inl.h',
|
'ic/handler-configuration-inl.h',
|
||||||
'ic/handler-configuration.h',
|
'ic/handler-configuration.h',
|
||||||
'ic/ic-inl.h',
|
'ic/ic-inl.h',
|
||||||
|
@ -1251,6 +1251,7 @@ MaybeHandle<String> ValueDeserializer::ReadTwoByteString() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ValueDeserializer::ReadExpectedString(Handle<String> expected) {
|
bool ValueDeserializer::ReadExpectedString(Handle<String> expected) {
|
||||||
|
DisallowHeapAllocation no_gc;
|
||||||
// In the case of failure, the position in the stream is reset.
|
// In the case of failure, the position in the stream is reset.
|
||||||
const uint8_t* original_position = position_;
|
const uint8_t* original_position = position_;
|
||||||
|
|
||||||
@ -1265,8 +1266,6 @@ bool ValueDeserializer::ReadExpectedString(Handle<String> expected) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
expected = String::Flatten(expected);
|
|
||||||
DisallowHeapAllocation no_gc;
|
|
||||||
String::FlatContent flat = expected->GetFlatContent();
|
String::FlatContent flat = expected->GetFlatContent();
|
||||||
|
|
||||||
// If the bytes are verbatim what is in the flattened string, then the string
|
// If the bytes are verbatim what is in the flattened string, then the string
|
||||||
@ -1775,10 +1774,11 @@ Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
|
|||||||
// transition was found.
|
// transition was found.
|
||||||
Handle<Object> key;
|
Handle<Object> key;
|
||||||
Handle<Map> target;
|
Handle<Map> target;
|
||||||
Handle<String> expected_key = TransitionArray::ExpectedTransitionKey(map);
|
TransitionsAccessor transitions(map);
|
||||||
|
Handle<String> expected_key = transitions.ExpectedTransitionKey();
|
||||||
if (!expected_key.is_null() && ReadExpectedString(expected_key)) {
|
if (!expected_key.is_null() && ReadExpectedString(expected_key)) {
|
||||||
key = expected_key;
|
key = expected_key;
|
||||||
target = TransitionArray::ExpectedTransitionTarget(map);
|
target = transitions.ExpectedTransitionTarget();
|
||||||
} else {
|
} else {
|
||||||
if (!ReadObject().ToHandle(&key) || !IsValidObjectKey(key)) {
|
if (!ReadObject().ToHandle(&key) || !IsValidObjectKey(key)) {
|
||||||
return Nothing<uint32_t>();
|
return Nothing<uint32_t>();
|
||||||
@ -1786,8 +1786,9 @@ Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
|
|||||||
if (key->IsString()) {
|
if (key->IsString()) {
|
||||||
key =
|
key =
|
||||||
isolate_->factory()->InternalizeString(Handle<String>::cast(key));
|
isolate_->factory()->InternalizeString(Handle<String>::cast(key));
|
||||||
target = TransitionArray::FindTransitionToField(
|
// Don't reuse |transitions| because it could be stale.
|
||||||
map, Handle<String>::cast(key));
|
target = TransitionsAccessor(map).FindTransitionToField(
|
||||||
|
Handle<String>::cast(key));
|
||||||
transitioning = !target.is_null();
|
transitioning = !target.is_null();
|
||||||
} else {
|
} else {
|
||||||
transitioning = false;
|
transitioning = false;
|
||||||
|
@ -174,6 +174,7 @@ v8_executable("cctest") {
|
|||||||
"test-trace-event.cc",
|
"test-trace-event.cc",
|
||||||
"test-traced-value.cc",
|
"test-traced-value.cc",
|
||||||
"test-transitions.cc",
|
"test-transitions.cc",
|
||||||
|
"test-transitions.h",
|
||||||
"test-typedarrays.cc",
|
"test-typedarrays.cc",
|
||||||
"test-types.cc",
|
"test-types.cc",
|
||||||
"test-unbound-queue.cc",
|
"test-unbound-queue.cc",
|
||||||
|
@ -192,6 +192,7 @@
|
|||||||
'test-trace-event.cc',
|
'test-trace-event.cc',
|
||||||
'test-traced-value.cc',
|
'test-traced-value.cc',
|
||||||
'test-transitions.cc',
|
'test-transitions.cc',
|
||||||
|
'test-transitions.h',
|
||||||
'test-typedarrays.cc',
|
'test-typedarrays.cc',
|
||||||
'test-types.cc',
|
'test-types.cc',
|
||||||
'test-unbound-queue.cc',
|
'test-unbound-queue.cc',
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
#include "test/cctest/heap/heap-tester.h"
|
#include "test/cctest/heap/heap-tester.h"
|
||||||
#include "test/cctest/heap/heap-utils.h"
|
#include "test/cctest/heap/heap-utils.h"
|
||||||
#include "test/cctest/test-feedback-vector.h"
|
#include "test/cctest/test-feedback-vector.h"
|
||||||
|
#include "test/cctest/test-transitions.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
@ -2855,7 +2855,8 @@ TEST(OptimizedAllocationArrayLiterals) {
|
|||||||
|
|
||||||
|
|
||||||
static int CountMapTransitions(Map* map) {
|
static int CountMapTransitions(Map* map) {
|
||||||
return TransitionArray::NumberOfTransitions(map->raw_transitions());
|
DisallowHeapAllocation no_gc;
|
||||||
|
return TransitionsAccessor(map, &no_gc).NumberOfTransitions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3044,11 +3045,14 @@ TEST(TransitionArraySimpleToFull) {
|
|||||||
CompileRun("o = new F;"
|
CompileRun("o = new F;"
|
||||||
"root = new F");
|
"root = new F");
|
||||||
root = GetByName("root");
|
root = GetByName("root");
|
||||||
CHECK(TransitionArray::IsSimpleTransition(root->map()->raw_transitions()));
|
{
|
||||||
|
DisallowHeapAllocation no_gc;
|
||||||
|
CHECK(TestTransitionsAccessor(root->map(), &no_gc).IsWeakCellEncoding());
|
||||||
|
}
|
||||||
AddPropertyTo(2, root, "happy");
|
AddPropertyTo(2, root, "happy");
|
||||||
|
|
||||||
// Count number of live transitions after marking. Note that one transition
|
// Count number of live transitions after marking. Note that one transition
|
||||||
// is left, because 'o' still holds an instance of one transition target.
|
// is left, because 'root' still holds an instance of one transition target.
|
||||||
int transitions_after = CountMapTransitions(
|
int transitions_after = CountMapTransitions(
|
||||||
Map::cast(root->map()->GetBackPointer()));
|
Map::cast(root->map()->GetBackPointer()));
|
||||||
CHECK_EQ(1, transitions_after);
|
CHECK_EQ(1, transitions_after);
|
||||||
|
@ -396,7 +396,7 @@ class Expectations {
|
|||||||
|
|
||||||
Handle<String> name = MakeName("prop", property_index);
|
Handle<String> name = MakeName("prop", property_index);
|
||||||
Map* target =
|
Map* target =
|
||||||
TransitionArray::SearchTransition(*map, kData, *name, attributes);
|
TransitionsAccessor(map).SearchTransition(*name, kData, attributes);
|
||||||
CHECK(target != NULL);
|
CHECK(target != NULL);
|
||||||
return handle(target);
|
return handle(target);
|
||||||
}
|
}
|
||||||
@ -2115,7 +2115,7 @@ TEST(ReconfigurePropertySplitMapTransitionsOverflow) {
|
|||||||
|
|
||||||
Handle<String> name = MakeName("prop", i);
|
Handle<String> name = MakeName("prop", i);
|
||||||
Map* target =
|
Map* target =
|
||||||
TransitionArray::SearchTransition(*map2, kData, *name, NONE);
|
TransitionsAccessor(map2).SearchTransition(*name, kData, NONE);
|
||||||
CHECK(target != NULL);
|
CHECK(target != NULL);
|
||||||
map2 = handle(target);
|
map2 = handle(target);
|
||||||
}
|
}
|
||||||
@ -2137,14 +2137,14 @@ TEST(ReconfigurePropertySplitMapTransitionsOverflow) {
|
|||||||
CHECK(!map2->is_deprecated());
|
CHECK(!map2->is_deprecated());
|
||||||
|
|
||||||
// Fill in transition tree of |map2| so that it can't have more transitions.
|
// Fill in transition tree of |map2| so that it can't have more transitions.
|
||||||
for (int i = 0; i < TransitionArray::kMaxNumberOfTransitions; i++) {
|
for (int i = 0; i < TransitionsAccessor::kMaxNumberOfTransitions; i++) {
|
||||||
CHECK(TransitionArray::CanHaveMoreTransitions(map2));
|
CHECK(TransitionsAccessor(map2).CanHaveMoreTransitions());
|
||||||
Handle<String> name = MakeName("foo", i);
|
Handle<String> name = MakeName("foo", i);
|
||||||
Map::CopyWithField(map2, name, any_type, NONE, kMutable,
|
Map::CopyWithField(map2, name, any_type, NONE, kMutable,
|
||||||
Representation::Smi(), INSERT_TRANSITION)
|
Representation::Smi(), INSERT_TRANSITION)
|
||||||
.ToHandleChecked();
|
.ToHandleChecked();
|
||||||
}
|
}
|
||||||
CHECK(!TransitionArray::CanHaveMoreTransitions(map2));
|
CHECK(!TransitionsAccessor(map2).CanHaveMoreTransitions());
|
||||||
|
|
||||||
// Try to update |map|, since there is no place for propX transition at |map2|
|
// Try to update |map|, since there is no place for propX transition at |map2|
|
||||||
// |map| should become "copy-generalized".
|
// |map| should become "copy-generalized".
|
||||||
|
@ -17,14 +17,10 @@
|
|||||||
#include "src/objects-inl.h"
|
#include "src/objects-inl.h"
|
||||||
#include "src/transitions.h"
|
#include "src/transitions.h"
|
||||||
#include "test/cctest/cctest.h"
|
#include "test/cctest/cctest.h"
|
||||||
|
#include "test/cctest/test-transitions.h"
|
||||||
|
|
||||||
using namespace v8::internal;
|
using namespace v8::internal;
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Helper functions.
|
|
||||||
//
|
|
||||||
|
|
||||||
TEST(TransitionArray_SimpleFieldTransitions) {
|
TEST(TransitionArray_SimpleFieldTransitions) {
|
||||||
CcTest::InitializeVM();
|
CcTest::InitializeVM();
|
||||||
v8::HandleScope scope(CcTest::isolate());
|
v8::HandleScope scope(CcTest::isolate());
|
||||||
@ -49,32 +45,36 @@ TEST(TransitionArray_SimpleFieldTransitions) {
|
|||||||
|
|
||||||
CHECK(map0->raw_transitions()->IsSmi());
|
CHECK(map0->raw_transitions()->IsSmi());
|
||||||
|
|
||||||
TransitionArray::Insert(map0, name1, map1, SIMPLE_PROPERTY_TRANSITION);
|
{
|
||||||
CHECK(TransitionArray::IsSimpleTransition(map0->raw_transitions()));
|
TestTransitionsAccessor transitions(map0);
|
||||||
CHECK_EQ(*map1,
|
transitions.Insert(name1, map1, SIMPLE_PROPERTY_TRANSITION);
|
||||||
TransitionArray::SearchTransition(*map0, kData, *name1, attributes));
|
|
||||||
CHECK_EQ(1, TransitionArray::NumberOfTransitions(map0->raw_transitions()));
|
|
||||||
CHECK_EQ(*name1, TransitionArray::GetKey(map0->raw_transitions(), 0));
|
|
||||||
CHECK_EQ(*map1, TransitionArray::GetTarget(map0->raw_transitions(), 0));
|
|
||||||
|
|
||||||
TransitionArray::Insert(map0, name2, map2, SIMPLE_PROPERTY_TRANSITION);
|
|
||||||
CHECK(TransitionArray::IsFullTransitionArray(map0->raw_transitions()));
|
|
||||||
|
|
||||||
CHECK_EQ(*map1,
|
|
||||||
TransitionArray::SearchTransition(*map0, kData, *name1, attributes));
|
|
||||||
CHECK_EQ(*map2,
|
|
||||||
TransitionArray::SearchTransition(*map0, kData, *name2, attributes));
|
|
||||||
CHECK_EQ(2, TransitionArray::NumberOfTransitions(map0->raw_transitions()));
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
Name* key = TransitionArray::GetKey(map0->raw_transitions(), i);
|
|
||||||
Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i);
|
|
||||||
CHECK((key == *name1 && target == *map1) ||
|
|
||||||
(key == *name2 && target == *map2));
|
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
TestTransitionsAccessor transitions(map0);
|
||||||
|
CHECK(transitions.IsWeakCellEncoding());
|
||||||
|
CHECK_EQ(*map1, transitions.SearchTransition(*name1, kData, attributes));
|
||||||
|
CHECK_EQ(1, transitions.NumberOfTransitions());
|
||||||
|
CHECK_EQ(*name1, transitions.GetKey(0));
|
||||||
|
CHECK_EQ(*map1, transitions.GetTarget(0));
|
||||||
|
|
||||||
#ifdef DEBUG
|
transitions.Insert(name2, map2, SIMPLE_PROPERTY_TRANSITION);
|
||||||
CHECK(TransitionArray::IsSortedNoDuplicates(*map0));
|
}
|
||||||
#endif
|
{
|
||||||
|
TestTransitionsAccessor transitions(map0);
|
||||||
|
CHECK(transitions.IsFullTransitionArrayEncoding());
|
||||||
|
|
||||||
|
CHECK_EQ(*map1, transitions.SearchTransition(*name1, kData, attributes));
|
||||||
|
CHECK_EQ(*map2, transitions.SearchTransition(*name2, kData, attributes));
|
||||||
|
CHECK_EQ(2, transitions.NumberOfTransitions());
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
Name* key = transitions.GetKey(i);
|
||||||
|
Map* target = transitions.GetTarget(i);
|
||||||
|
CHECK((key == *name1 && target == *map1) ||
|
||||||
|
(key == *name2 && target == *map2));
|
||||||
|
}
|
||||||
|
|
||||||
|
DCHECK(transitions.IsSortedNoDuplicates());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -102,32 +102,36 @@ TEST(TransitionArray_FullFieldTransitions) {
|
|||||||
|
|
||||||
CHECK(map0->raw_transitions()->IsSmi());
|
CHECK(map0->raw_transitions()->IsSmi());
|
||||||
|
|
||||||
TransitionArray::Insert(map0, name1, map1, PROPERTY_TRANSITION);
|
{
|
||||||
CHECK(TransitionArray::IsFullTransitionArray(map0->raw_transitions()));
|
TestTransitionsAccessor transitions(map0);
|
||||||
CHECK_EQ(*map1,
|
transitions.Insert(name1, map1, PROPERTY_TRANSITION);
|
||||||
TransitionArray::SearchTransition(*map0, kData, *name1, attributes));
|
|
||||||
CHECK_EQ(1, TransitionArray::NumberOfTransitions(map0->raw_transitions()));
|
|
||||||
CHECK_EQ(*name1, TransitionArray::GetKey(map0->raw_transitions(), 0));
|
|
||||||
CHECK_EQ(*map1, TransitionArray::GetTarget(map0->raw_transitions(), 0));
|
|
||||||
|
|
||||||
TransitionArray::Insert(map0, name2, map2, PROPERTY_TRANSITION);
|
|
||||||
CHECK(TransitionArray::IsFullTransitionArray(map0->raw_transitions()));
|
|
||||||
|
|
||||||
CHECK_EQ(*map1,
|
|
||||||
TransitionArray::SearchTransition(*map0, kData, *name1, attributes));
|
|
||||||
CHECK_EQ(*map2,
|
|
||||||
TransitionArray::SearchTransition(*map0, kData, *name2, attributes));
|
|
||||||
CHECK_EQ(2, TransitionArray::NumberOfTransitions(map0->raw_transitions()));
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
Name* key = TransitionArray::GetKey(map0->raw_transitions(), i);
|
|
||||||
Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i);
|
|
||||||
CHECK((key == *name1 && target == *map1) ||
|
|
||||||
(key == *name2 && target == *map2));
|
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
TestTransitionsAccessor transitions(map0);
|
||||||
|
CHECK(transitions.IsFullTransitionArrayEncoding());
|
||||||
|
CHECK_EQ(*map1, transitions.SearchTransition(*name1, kData, attributes));
|
||||||
|
CHECK_EQ(1, transitions.NumberOfTransitions());
|
||||||
|
CHECK_EQ(*name1, transitions.GetKey(0));
|
||||||
|
CHECK_EQ(*map1, transitions.GetTarget(0));
|
||||||
|
|
||||||
#ifdef DEBUG
|
transitions.Insert(name2, map2, PROPERTY_TRANSITION);
|
||||||
CHECK(TransitionArray::IsSortedNoDuplicates(*map0));
|
}
|
||||||
#endif
|
{
|
||||||
|
TestTransitionsAccessor transitions(map0);
|
||||||
|
CHECK(transitions.IsFullTransitionArrayEncoding());
|
||||||
|
|
||||||
|
CHECK_EQ(*map1, transitions.SearchTransition(*name1, kData, attributes));
|
||||||
|
CHECK_EQ(*map2, transitions.SearchTransition(*name2, kData, attributes));
|
||||||
|
CHECK_EQ(2, transitions.NumberOfTransitions());
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
Name* key = transitions.GetKey(i);
|
||||||
|
Map* target = transitions.GetTarget(i);
|
||||||
|
CHECK((key == *name1 && target == *map1) ||
|
||||||
|
(key == *name2 && target == *map2));
|
||||||
|
}
|
||||||
|
|
||||||
|
DCHECK(transitions.IsSortedNoDuplicates());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -157,16 +161,17 @@ TEST(TransitionArray_DifferentFieldNames) {
|
|||||||
names[i] = name;
|
names[i] = name;
|
||||||
maps[i] = map;
|
maps[i] = map;
|
||||||
|
|
||||||
TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION);
|
TransitionsAccessor(map0).Insert(name, map, PROPERTY_TRANSITION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransitionsAccessor transitions(map0);
|
||||||
for (int i = 0; i < PROPS_COUNT; i++) {
|
for (int i = 0; i < PROPS_COUNT; i++) {
|
||||||
CHECK_EQ(*maps[i], TransitionArray::SearchTransition(
|
CHECK_EQ(*maps[i],
|
||||||
*map0, kData, *names[i], attributes));
|
transitions.SearchTransition(*names[i], kData, attributes));
|
||||||
}
|
}
|
||||||
for (int i = 0; i < PROPS_COUNT; i++) {
|
for (int i = 0; i < PROPS_COUNT; i++) {
|
||||||
Name* key = TransitionArray::GetKey(map0->raw_transitions(), i);
|
Name* key = transitions.GetKey(i);
|
||||||
Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i);
|
Map* target = transitions.GetTarget(i);
|
||||||
for (int j = 0; j < PROPS_COUNT; j++) {
|
for (int j = 0; j < PROPS_COUNT; j++) {
|
||||||
if (*names[i] == key) {
|
if (*names[i] == key) {
|
||||||
CHECK_EQ(*maps[i], target);
|
CHECK_EQ(*maps[i], target);
|
||||||
@ -175,9 +180,7 @@ TEST(TransitionArray_DifferentFieldNames) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
DCHECK(transitions.IsSortedNoDuplicates());
|
||||||
CHECK(TransitionArray::IsSortedNoDuplicates(*map0));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -205,22 +208,21 @@ TEST(TransitionArray_SameFieldNamesDifferentAttributesSimple) {
|
|||||||
.ToHandleChecked();
|
.ToHandleChecked();
|
||||||
attr_maps[i] = map;
|
attr_maps[i] = map;
|
||||||
|
|
||||||
TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION);
|
TransitionsAccessor(map0).Insert(name, map, PROPERTY_TRANSITION);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that transitions for |name| field are valid.
|
// Ensure that transitions for |name| field are valid.
|
||||||
|
TransitionsAccessor transitions(map0);
|
||||||
for (int i = 0; i < ATTRS_COUNT; i++) {
|
for (int i = 0; i < ATTRS_COUNT; i++) {
|
||||||
PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
|
PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
|
||||||
CHECK_EQ(*attr_maps[i], TransitionArray::SearchTransition(
|
CHECK_EQ(*attr_maps[i],
|
||||||
*map0, kData, *name, attributes));
|
transitions.SearchTransition(*name, kData, attributes));
|
||||||
// All transitions use the same key, so this check doesn't need to
|
// All transitions use the same key, so this check doesn't need to
|
||||||
// care about ordering.
|
// care about ordering.
|
||||||
CHECK_EQ(*name, TransitionArray::GetKey(map0->raw_transitions(), i));
|
CHECK_EQ(*name, transitions.GetKey(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
DCHECK(transitions.IsSortedNoDuplicates());
|
||||||
CHECK(TransitionArray::IsSortedNoDuplicates(*map0));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -249,7 +251,7 @@ TEST(TransitionArray_SameFieldNamesDifferentAttributes) {
|
|||||||
names[i] = name;
|
names[i] = name;
|
||||||
maps[i] = map;
|
maps[i] = map;
|
||||||
|
|
||||||
TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION);
|
TransitionsAccessor(map0).Insert(name, map, PROPERTY_TRANSITION);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int ATTRS_COUNT = (READ_ONLY | DONT_ENUM | DONT_DELETE) + 1;
|
const int ATTRS_COUNT = (READ_ONLY | DONT_ENUM | DONT_DELETE) + 1;
|
||||||
@ -268,22 +270,21 @@ TEST(TransitionArray_SameFieldNamesDifferentAttributes) {
|
|||||||
.ToHandleChecked();
|
.ToHandleChecked();
|
||||||
attr_maps[i] = map;
|
attr_maps[i] = map;
|
||||||
|
|
||||||
TransitionArray::Insert(map0, name, map, PROPERTY_TRANSITION);
|
TransitionsAccessor(map0).Insert(name, map, PROPERTY_TRANSITION);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that transitions for |name| field are valid.
|
// Ensure that transitions for |name| field are valid.
|
||||||
|
TransitionsAccessor transitions(map0);
|
||||||
for (int i = 0; i < ATTRS_COUNT; i++) {
|
for (int i = 0; i < ATTRS_COUNT; i++) {
|
||||||
PropertyAttributes attr = static_cast<PropertyAttributes>(i);
|
PropertyAttributes attr = static_cast<PropertyAttributes>(i);
|
||||||
CHECK_EQ(*attr_maps[i],
|
CHECK_EQ(*attr_maps[i], transitions.SearchTransition(*name, kData, attr));
|
||||||
TransitionArray::SearchTransition(*map0, kData, *name, attr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that info about the other fields still valid.
|
// Ensure that info about the other fields still valid.
|
||||||
CHECK_EQ(PROPS_COUNT + ATTRS_COUNT,
|
CHECK_EQ(PROPS_COUNT + ATTRS_COUNT, transitions.NumberOfTransitions());
|
||||||
TransitionArray::NumberOfTransitions(map0->raw_transitions()));
|
|
||||||
for (int i = 0; i < PROPS_COUNT + ATTRS_COUNT; i++) {
|
for (int i = 0; i < PROPS_COUNT + ATTRS_COUNT; i++) {
|
||||||
Name* key = TransitionArray::GetKey(map0->raw_transitions(), i);
|
Name* key = transitions.GetKey(i);
|
||||||
Map* target = TransitionArray::GetTarget(map0->raw_transitions(), i);
|
Map* target = transitions.GetTarget(i);
|
||||||
if (key == *name) {
|
if (key == *name) {
|
||||||
// Attributes transition.
|
// Attributes transition.
|
||||||
PropertyAttributes attributes =
|
PropertyAttributes attributes =
|
||||||
@ -299,7 +300,5 @@ TEST(TransitionArray_SameFieldNamesDifferentAttributes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
DCHECK(transitions.IsSortedNoDuplicates());
|
||||||
CHECK(TransitionArray::IsSortedNoDuplicates(*map0));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
31
test/cctest/test-transitions.h
Normal file
31
test/cctest/test-transitions.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2017 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_TEST_CCTEST_TEST_TRANSITIONS_H_
|
||||||
|
#define V8_TEST_CCTEST_TEST_TRANSITIONS_H_
|
||||||
|
|
||||||
|
#include "src/transitions.h"
|
||||||
|
|
||||||
|
namespace v8 {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
class TestTransitionsAccessor : public TransitionsAccessor {
|
||||||
|
public:
|
||||||
|
TestTransitionsAccessor(Map* map, DisallowHeapAllocation* no_gc)
|
||||||
|
: TransitionsAccessor(map, no_gc) {}
|
||||||
|
explicit TestTransitionsAccessor(Handle<Map> map)
|
||||||
|
: TransitionsAccessor(map) {}
|
||||||
|
|
||||||
|
// Expose internals for tests.
|
||||||
|
bool IsWeakCellEncoding() { return encoding() == kWeakCell; }
|
||||||
|
|
||||||
|
bool IsFullTransitionArrayEncoding() {
|
||||||
|
return encoding() == kFullTransitionArray;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace v8
|
||||||
|
|
||||||
|
#endif // V8_TEST_CCTEST_TEST_TRANSITIONS_H_
|
Loading…
Reference in New Issue
Block a user