Separating transitions from descriptors.

In this design maps contain descriptor arrays, which in turn can contain transition arrays. If transitions are needed when no descriptor array is present, a descriptor array without real descriptors is inserted just so it can point at the transition array.

The transition array does not contain details about the field it transitions to. In order to weed out transitions to FIELDs from CONSTANT_FUNCTION (what used to be MAP_TRANSITION vs CONSTANT_TRANSITION), the transition needs to be followed and the details need to be looked up in the target map. CALLBACKS transitions are still easy to recognize since the transition targets are stored as an AccessorPair containing the maps, rather than the maps directly.

Currently AccessorPairs containing a transition and an accessor are shared between the descriptor array and the transition array. This simplifies lookup since we only have to look in one of both arrays. This will change in subsequent revisions, when descriptor arrays will become shared between multiple maps, since transitions cannot be shared.

Review URL: https://chromiumcodereview.appspot.com/10697015

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11994 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
verwaest@chromium.org 2012-07-05 13:54:20 +00:00
parent f97df41fcf
commit d7a5b7d5e2
33 changed files with 1509 additions and 1002 deletions

View File

@ -43,8 +43,8 @@ SOURCES = {
assembler.cc
ast.cc
atomicops_internals_x86_gcc.cc
bignum.cc
bignum-dtoa.cc
bignum.cc
bootstrapper.cc
builtins.cc
cached-powers.cc
@ -67,27 +67,29 @@ SOURCES = {
disassembler.cc
diy-fp.cc
dtoa.cc
elements.cc
elements-kind.cc
elements.cc
execution.cc
extensions/externalize-string-extension.cc
extensions/gc-extension.cc
factory.cc
fast-dtoa.cc
fixed-dtoa.cc
flags.cc
frames.cc
full-codegen.cc
func-name-inferrer.cc
gdb-jit.cc
global-handles.cc
fast-dtoa.cc
fixed-dtoa.cc
handles.cc
heap-profiler.cc
heap.cc
hydrogen.cc
hydrogen-instructions.cc
hydrogen.cc
ic.cc
incremental-marking.cc
interface.cc
inspector.cc
interface.cc
interpreter-irregexp.cc
isolate.cc
jsregexp.cc
@ -99,34 +101,36 @@ SOURCES = {
log.cc
mark-compact.cc
messages.cc
objects.cc
objects-printer.cc
objects-visiting.cc
objects.cc
once.cc
parser.cc
preparser.cc
preparse-data.cc
preparser.cc
profile-generator.cc
property.cc
regexp-macro-assembler-irregexp.cc
regexp-macro-assembler.cc
regexp-stack.cc
rewriter.cc
runtime.cc
runtime-profiler.cc
runtime.cc
safepoint-table.cc
scanner.cc
scanner-character-streams.cc
scanner.cc
scopeinfo.cc
scopes.cc
serialize.cc
snapshot-common.cc
spaces.cc
store-buffer.cc
string-search.cc
string-stream.cc
strtod.cc
stub-cache.cc
token.cc
transitions.cc
type-info.cc
unicode.cc
utils.cc
@ -137,10 +141,7 @@ SOURCES = {
v8utils.cc
variables.cc
version.cc
store-buffer.cc
zone.cc
extensions/gc-extension.cc
extensions/externalize-string-extension.cc
"""),
'arch:arm': Split("""
arm/builtins-arm.cc

View File

@ -2577,7 +2577,7 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
type->LookupInDescriptors(NULL, *name, &lookup);
type->LookupTransitionOrDescriptor(NULL, *name, &lookup);
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsField()) {
int index = lookup.GetLocalFieldIndexFromMap(*type);

View File

@ -503,7 +503,7 @@ bool Call::ComputeTarget(Handle<Map> type, Handle<String> name) {
}
LookupResult lookup(type->GetIsolate());
while (true) {
type->LookupInDescriptors(NULL, *name, &lookup);
type->LookupDescriptor(NULL, *name, &lookup);
if (lookup.IsFound()) {
switch (lookup.type()) {
case CONSTANT_FUNCTION:
@ -518,10 +518,7 @@ bool Call::ComputeTarget(Handle<Map> type, Handle<String> name) {
case INTERCEPTOR:
// We don't know the target.
return false;
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
// Perhaps something interesting is up in the prototype chain...
break;
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;

View File

@ -2191,14 +2191,11 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
JSObject::SetNormalizedProperty(to, key, callbacks, d);
break;
}
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
// Ignore non-properties.
break;
case NORMAL:
// Do not occur since the from object has fast properties.
case HANDLER:
case INTERCEPTOR:
case TRANSITION:
case NONEXISTENT:
// No element in instance descriptors have proxy or interceptor type.
UNREACHABLE();

View File

@ -894,7 +894,7 @@ MUST_USE_RESULT static inline MaybeObject* DoCopyInsert(
Object* value,
PropertyAttributes attributes) {
CallbacksDescriptor desc(key, value, attributes);
MaybeObject* obj = array->CopyInsert(&desc, REMOVE_TRANSITIONS);
MaybeObject* obj = array->CopyInsert(&desc);
return obj;
}
@ -944,7 +944,7 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
Handle<String> key =
SymbolFromString(Handle<String>(String::cast(entry->name())));
// Check if a descriptor with this name already exists before writing.
if (result->LinearSearch(EXPECT_UNSORTED, *key, descriptor_count) ==
if (LinearSearch(*result, EXPECT_UNSORTED, *key, descriptor_count) ==
DescriptorArray::kNotFound) {
CallbacksDescriptor desc(*key, *entry, entry->property_attributes());
result->Set(descriptor_count, &desc, witness);

View File

@ -729,7 +729,7 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
Handle<DescriptorArray>(object->map()->instance_descriptors(), isolate);
for (int i = 0; i < descs->number_of_descriptors(); i++) {
if (descs->IsProperty(i) && !descs->GetDetails(i).IsDontEnum()) {
if (!descs->GetDetails(i).IsDontEnum()) {
storage->set(index, descs->GetKey(i));
PropertyDetails details = descs->GetDetails(i);
sort_array->set(index, Smi::FromInt(details.index()));

View File

@ -1638,9 +1638,10 @@ static bool PrototypeChainCanNeverResolve(
}
LookupResult lookup(isolate);
JSObject::cast(current)->map()->LookupInDescriptors(NULL, *name, &lookup);
Map* map = JSObject::cast(current)->map();
map->LookupTransitionOrDescriptor(NULL, *name, &lookup);
if (lookup.IsFound()) {
if (lookup.type() != MAP_TRANSITION) return false;
if (!lookup.IsTransition()) return false;
} else if (!lookup.IsCacheable()) {
return false;
}
@ -1669,7 +1670,7 @@ HLoadNamedFieldPolymorphic::HLoadNamedFieldPolymorphic(HValue* context,
++i) {
Handle<Map> map = types->at(i);
LookupResult lookup(map->GetIsolate());
map->LookupInDescriptors(NULL, *name, &lookup);
map->LookupTransitionOrDescriptor(NULL, *name, &lookup);
if (lookup.IsFound()) {
switch (lookup.type()) {
case FIELD: {
@ -1685,12 +1686,18 @@ HLoadNamedFieldPolymorphic::HLoadNamedFieldPolymorphic(HValue* context,
case CONSTANT_FUNCTION:
types_.Add(types->at(i), zone);
break;
case MAP_TRANSITION:
case CALLBACKS:
break;
case TRANSITION:
if (PrototypeChainCanNeverResolve(map, name)) {
negative_lookups.Add(types->at(i), zone);
}
break;
default:
case INTERCEPTOR:
case NONEXISTENT:
case NORMAL:
case HANDLER:
UNREACHABLE();
break;
}
} else if (lookup.IsCacheable()) {

View File

@ -4917,17 +4917,18 @@ static bool ComputeLoadStoreField(Handle<Map> type,
Handle<String> name,
LookupResult* lookup,
bool is_store) {
type->LookupInDescriptors(NULL, *name, lookup);
type->LookupTransitionOrDescriptor(NULL, *name, lookup);
if (lookup->IsField()) return true;
return is_store && lookup->IsMapTransition() &&
(type->unused_property_fields() > 0);
return is_store &&
lookup->IsTransitionToField(*type) &&
(type->unused_property_fields() > 0);
}
static int ComputeLoadStoreFieldIndex(Handle<Map> type,
Handle<String> name,
LookupResult* lookup) {
ASSERT(lookup->IsField() || lookup->type() == MAP_TRANSITION);
ASSERT(lookup->IsField() || lookup->IsTransitionToField(*type));
if (lookup->IsField()) {
return lookup->GetLocalFieldIndexFromMap(*type);
} else {
@ -4988,7 +4989,7 @@ HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object,
}
HStoreNamedField* instr =
new(zone()) HStoreNamedField(object, name, value, is_in_object, offset);
if (lookup->type() == MAP_TRANSITION) {
if (lookup->IsTransitionToField(*type)) {
Handle<Map> transition(lookup->GetTransitionMapFromMap(*type));
instr->set_transition(transition);
// TODO(fschneider): Record the new map type of the object in the IR to
@ -5626,7 +5627,7 @@ HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj,
Handle<Map> map,
Handle<String> name) {
LookupResult lookup(isolate());
map->LookupInDescriptors(NULL, *name, &lookup);
map->LookupDescriptor(NULL, *name, &lookup);
if (lookup.IsField()) {
return BuildLoadNamedField(obj,
expr,

View File

@ -2410,7 +2410,7 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
type->LookupInDescriptors(NULL, *name, &lookup);
type->LookupTransitionOrDescriptor(NULL, *name, &lookup);
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsField()) {
int index = lookup.GetLocalFieldIndexFromMap(*type);
@ -2471,9 +2471,9 @@ static bool CompactEmit(SmallMapList* list,
Handle<Map> map = list->at(i);
// If the map has ElementsKind transitions, we will generate map checks
// for each kind in __ CompareMap(..., ALLOW_ELEMENTS_TRANSITION_MAPS).
if (map->elements_transition_map() != NULL) return false;
if (map->HasElementsTransition()) return false;
LookupResult lookup(isolate);
map->LookupInDescriptors(NULL, *name, &lookup);
map->LookupDescriptor(NULL, *name, &lookup);
return lookup.IsField() || lookup.IsConstantFunction();
}

View File

@ -1304,9 +1304,10 @@ static bool StoreICableLookup(LookupResult* lookup) {
if (!lookup->IsCacheable()) return false;
// If the property is read-only, we leave the IC in its current state.
if (lookup->IsReadOnly()) return false;
return true;
if (lookup->IsTransition()) {
return !lookup->GetTransitionDetails().IsReadOnly();
}
return !lookup->IsReadOnly();
}
@ -1466,14 +1467,6 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
Handle<Map>::null(),
strict_mode);
break;
case MAP_TRANSITION: {
if (lookup->GetAttributes() != NONE) return;
Handle<Map> transition(lookup->GetTransitionMap());
int index = transition->PropertyIndexFor(*name);
code = isolate()->stub_cache()->ComputeStoreField(
name, receiver, index, transition, strict_mode);
break;
}
case NORMAL:
if (receiver->IsGlobalObject()) {
// The stub generated for the global object picks the value directly
@ -1517,8 +1510,26 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
name, receiver, strict_mode);
break;
case CONSTANT_FUNCTION:
case CONSTANT_TRANSITION:
return;
case TRANSITION: {
Object* value = lookup->GetTransitionValue();
// Callbacks.
if (value->IsAccessorPair()) return;
Handle<Map> transition(Map::cast(value));
DescriptorArray* target_descriptors = transition->instance_descriptors();
int descriptor = target_descriptors->SearchWithCache(*name);
ASSERT(descriptor != DescriptorArray::kNotFound);
PropertyDetails details = target_descriptors->GetDetails(descriptor);
if (details.type() != FIELD || details.attributes() != NONE) return;
int field_index = target_descriptors->GetFieldIndex(descriptor);
code = isolate()->stub_cache()->ComputeStoreField(
name, receiver, field_index, transition, strict_mode);
break;
}
case NONEXISTENT:
case HANDLER:
UNREACHABLE();
@ -1967,20 +1978,34 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
name, receiver, lookup->GetFieldIndex(),
Handle<Map>::null(), strict_mode);
break;
case MAP_TRANSITION:
if (lookup->GetAttributes() == NONE) {
Handle<Map> transition(lookup->GetTransitionMap());
int index = transition->PropertyIndexFor(*name);
case TRANSITION: {
Object* value = lookup->GetTransitionValue();
// Callbacks transition.
if (value->IsAccessorPair()) {
code = (strict_mode == kStrictMode)
? generic_stub_strict()
: generic_stub();
break;
}
Handle<Map> transition(Map::cast(value));
DescriptorArray* target_descriptors = transition->instance_descriptors();
int descriptor = target_descriptors->SearchWithCache(*name);
ASSERT(descriptor != DescriptorArray::kNotFound);
PropertyDetails details = target_descriptors->GetDetails(descriptor);
if (details.type() == FIELD && details.attributes() == NONE) {
int field_index = target_descriptors->GetFieldIndex(descriptor);
code = isolate()->stub_cache()->ComputeKeyedStoreField(
name, receiver, index, transition, strict_mode);
name, receiver, field_index, transition, strict_mode);
break;
}
// fall through.
}
case NORMAL:
case CONSTANT_FUNCTION:
case CALLBACKS:
case INTERCEPTOR:
case CONSTANT_TRANSITION:
// Always rewrite to the generic case so that we do not
// repeatedly try to rewrite.
code = (strict_mode == kStrictMode)

View File

@ -1892,12 +1892,13 @@ void Marker<T>::MarkDescriptorArray(DescriptorArray* descriptors) {
enum_cache);
}
if (descriptors->elements_transition_map() != NULL) {
if (descriptors->HasTransitionArray()) {
Object** transitions_slot = descriptors->GetTransitionsSlot();
Object* transitions = *transitions_slot;
mark_compact_collector()->RecordSlot(descriptor_start,
transitions_slot,
transitions);
MarkTransitionArray(reinterpret_cast<TransitionArray*>(transitions));
}
// If the descriptor contains a transition (value is a Map), we don't mark the
@ -1906,7 +1907,7 @@ void Marker<T>::MarkDescriptorArray(DescriptorArray* descriptors) {
Object** key_slot = descriptors->GetKeySlot(i);
Object* key = *key_slot;
if (key->IsHeapObject()) {
base_marker()->MarkObjectAndPush(reinterpret_cast<HeapObject*>(key));
base_marker()->MarkObjectAndPush(HeapObject::cast(key));
mark_compact_collector()->RecordSlot(descriptor_start, key_slot, key);
}
@ -1937,9 +1938,7 @@ void Marker<T>::MarkDescriptorArray(DescriptorArray* descriptors) {
MarkAccessorPairSlot(accessors, AccessorPair::kSetterOffset);
}
break;
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
break;
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@ -1947,6 +1946,42 @@ void Marker<T>::MarkDescriptorArray(DescriptorArray* descriptors) {
}
}
template <class T>
void Marker<T>::MarkTransitionArray(TransitionArray* transitions) {
if (!base_marker()->MarkObjectWithoutPush(transitions)) return;
Object** transitions_start = transitions->data_start();
if (transitions->HasElementsTransition()) {
mark_compact_collector()->RecordSlot(transitions_start,
transitions->GetElementsSlot(),
transitions->elements_transition());
}
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
Object** key_slot = transitions->GetKeySlot(i);
Object* key = *key_slot;
if (key->IsHeapObject()) {
base_marker()->MarkObjectAndPush(HeapObject::cast(key));
mark_compact_collector()->RecordSlot(transitions_start, key_slot, key);
}
Object** value_slot = transitions->GetValueSlot(i);
if (!(*value_slot)->IsHeapObject()) continue;
HeapObject* value = HeapObject::cast(*value_slot);
if (value->IsAccessorPair()) {
mark_compact_collector()->RecordSlot(transitions_start,
value_slot,
value);
base_marker()->MarkObjectWithoutPush(value);
AccessorPair* accessors = AccessorPair::cast(value);
MarkAccessorPairSlot(accessors, AccessorPair::kGetterOffset);
MarkAccessorPairSlot(accessors, AccessorPair::kSetterOffset);
}
}
}
template <class T>
void Marker<T>::MarkAccessorPairSlot(AccessorPair* accessors, int offset) {

View File

@ -395,6 +395,7 @@ template<class BaseMarker> class Marker {
// treating transitions or back pointers weak.
void MarkMapContents(Map* map);
void MarkDescriptorArray(DescriptorArray* descriptors);
void MarkTransitionArray(TransitionArray* transitions);
void MarkAccessorPairSlot(AccessorPair* accessors, int offset);
private:

View File

@ -2321,7 +2321,7 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
type->LookupInDescriptors(NULL, *name, &lookup);
type->LookupDescriptor(NULL, *name, &lookup);
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsField()) {
int index = lookup.GetLocalFieldIndexFromMap(*type);

View File

@ -176,10 +176,8 @@ PropertyType.ConstantFunction = 2;
PropertyType.Callbacks = 3;
PropertyType.Handler = 4;
PropertyType.Interceptor = 5;
PropertyType.MapTransition = 6;
PropertyType.ExternalArrayTransition = 7;
PropertyType.ConstantTransition = 8;
PropertyType.NullDescriptor = 9;
PropertyType.Transition = 6;
PropertyType.Nonexistent = 7;
// Different attributes for a property.

View File

@ -303,7 +303,10 @@ void Map::MapVerify() {
VerifyHeapPointer(prototype());
VerifyHeapPointer(instance_descriptors());
SLOW_ASSERT(instance_descriptors()->IsSortedNoDuplicates());
SLOW_ASSERT(instance_descriptors()->IsConsistentWithBackPointers(this));
if (HasTransitionArray()) {
SLOW_ASSERT(transitions()->IsSortedNoDuplicates());
SLOW_ASSERT(transitions()->IsConsistentWithBackPointers(this));
}
}
@ -916,47 +919,46 @@ bool DescriptorArray::IsSortedNoDuplicates() {
}
bool TransitionArray::IsSortedNoDuplicates() {
String* current_key = NULL;
uint32_t current = 0;
for (int i = 0; i < number_of_transitions(); i++) {
String* key = GetKey(i);
if (key == current_key) {
PrintTransitions();
return false;
}
current_key = key;
uint32_t hash = GetKey(i)->Hash();
if (hash < current) {
PrintTransitions();
return false;
}
current = hash;
}
return true;
}
static bool CheckOneBackPointer(Map* current_map, Object* target) {
return !target->IsMap() || Map::cast(target)->GetBackPointer() == current_map;
}
bool DescriptorArray::IsConsistentWithBackPointers(Map* current_map) {
Map* elements_transition = elements_transition_map();
if (elements_transition != NULL &&
!CheckOneBackPointer(current_map, elements_transition)) {
bool TransitionArray::IsConsistentWithBackPointers(Map* current_map) {
if (HasElementsTransition() &&
!CheckOneBackPointer(current_map, elements_transition())) {
return false;
}
for (int i = 0; i < number_of_descriptors(); ++i) {
switch (GetType(i)) {
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
if (!CheckOneBackPointer(current_map, GetValue(i))) {
return false;
}
break;
case CALLBACKS: {
Object* object = GetValue(i);
if (object->IsAccessorPair()) {
AccessorPair* accessors = AccessorPair::cast(object);
if (!CheckOneBackPointer(current_map, accessors->getter())) {
return false;
}
if (!CheckOneBackPointer(current_map, accessors->setter())) {
return false;
}
}
break;
}
case NORMAL:
case FIELD:
case CONSTANT_FUNCTION:
case HANDLER:
case INTERCEPTOR:
break;
case NONEXISTENT:
UNREACHABLE();
break;
for (int i = 0; i < number_of_transitions(); ++i) {
Object* value = GetValue(i);
if (value->IsAccessorPair()) {
AccessorPair* accessors = AccessorPair::cast(value);
if (!CheckOneBackPointer(current_map, accessors->getter())) return false;
if (!CheckOneBackPointer(current_map, accessors->setter())) return false;
} else {
ASSERT(value->IsMap());
if (!CheckOneBackPointer(current_map, value)) return false;
}
}
return true;
@ -1004,12 +1006,12 @@ void NormalizedMapCache::NormalizedMapCacheVerify() {
}
void Map::ZapInstanceDescriptors() {
DescriptorArray* descriptors = instance_descriptors();
if (descriptors == GetHeap()->empty_descriptor_array()) return;
MemsetPointer(descriptors->data_start(),
void Map::ZapTransitions() {
TransitionArray* transition_array = transitions();
if (transition_array == NULL) return;
MemsetPointer(transition_array->data_start(),
GetHeap()->the_hole_value(),
descriptors->length());
transition_array->length());
}

View File

@ -47,6 +47,7 @@
#include "v8memory.h"
#include "factory.h"
#include "incremental-marking.h"
#include "transitions-inl.h"
namespace v8 {
namespace internal {
@ -524,6 +525,11 @@ bool Object::IsDescriptorArray() {
}
bool Object::IsTransitionArray() {
return IsFixedArray();
}
bool Object::IsDeoptimizationInputData() {
// Must be a fixed array.
if (!IsFixedArray()) return false;
@ -1885,13 +1891,18 @@ bool DescriptorArray::MayContainTransitions() {
}
bool DescriptorArray::HasTransitionArray() {
return MayContainTransitions() && !get(kTransitionsIndex)->IsSmi();
}
int DescriptorArray::bit_field3_storage() {
Object* storage = READ_FIELD(this, kBitField3StorageOffset);
return Smi::cast(storage)->value();
}
void DescriptorArray::set_bit_field3_storage(int value) {
ASSERT(this->MayContainTransitions());
ASSERT(length() > kBitField3StorageIndex);
WRITE_FIELD(this, kBitField3StorageOffset, Smi::FromInt(value));
}
@ -1905,63 +1916,107 @@ void DescriptorArray::NoIncrementalWriteBarrierSwap(FixedArray* array,
}
int DescriptorArray::Search(String* name) {
SLOW_ASSERT(IsSortedNoDuplicates());
// Perform a binary search in a fixed array. Low and high are entry indices. If
// there are three entries in this array it should be called with low=0 and
// high=2.
template<typename T>
int BinarySearch(T* array, String* name, int low, int high) {
uint32_t hash = name->Hash();
int limit = high;
ASSERT(low <= high);
while (low != high) {
int mid = (low + high) / 2;
String* mid_name = array->GetKey(mid);
uint32_t mid_hash = mid_name->Hash();
if (mid_hash >= hash) {
high = mid;
} else {
low = mid + 1;
}
}
for (; low <= limit && array->GetKey(low)->Hash() == hash; ++low) {
if (array->GetKey(low)->Equals(name)) return low;
}
return T::kNotFound;
}
// Perform a linear search in this fixed array. len is the number of entry
// indices that are valid.
template<typename T>
int LinearSearch(T* array, SearchMode mode, String* name, int len) {
uint32_t hash = name->Hash();
for (int number = 0; number < len; number++) {
String* entry = array->GetKey(number);
uint32_t current_hash = entry->Hash();
if (mode == EXPECT_SORTED && current_hash > hash) break;
if (current_hash == hash && name->Equals(entry)) return number;
}
return T::kNotFound;
}
template<typename T>
int Search(T* array, String* name) {
SLOW_ASSERT(array->IsSortedNoDuplicates());
// Check for empty descriptor array.
int nof = number_of_descriptors();
if (nof == 0) return kNotFound;
int nof = array->number_of_entries();
if (nof == 0) return T::kNotFound;
// Fast case: do linear search for small arrays.
const int kMaxElementsForLinearSearch = 8;
if (StringShape(name).IsSymbol() && nof < kMaxElementsForLinearSearch) {
return LinearSearch(EXPECT_SORTED, name, nof);
return LinearSearch(array, EXPECT_SORTED, name, nof);
}
// Slow case: perform binary search.
return BinarySearch(name, 0, nof - 1);
return BinarySearch(array, name, 0, nof - 1);
}
int DescriptorArray::Search(String* name) {
return internal::Search(this, name);
}
int DescriptorArray::SearchWithCache(String* name) {
int number = GetIsolate()->descriptor_lookup_cache()->Lookup(this, name);
DescriptorLookupCache* cache = GetIsolate()->descriptor_lookup_cache();
int number = cache->Lookup(this, name);
if (number == DescriptorLookupCache::kAbsent) {
number = Search(name);
GetIsolate()->descriptor_lookup_cache()->Update(this, name, number);
number = internal::Search(this, name);
cache->Update(this, name, number);
}
return number;
}
Map* DescriptorArray::elements_transition_map() {
if (!this->MayContainTransitions()) {
return NULL;
}
Object* transition_map = get(kTransitionsIndex);
if (transition_map == Smi::FromInt(0)) {
return NULL;
} else {
return Map::cast(transition_map);
}
TransitionArray* DescriptorArray::transitions() {
if (!this->MayContainTransitions()) return NULL;
Object* array = get(kTransitionsIndex);
return TransitionArray::cast(array);
}
void DescriptorArray::set_elements_transition_map(
Map* transition_map, WriteBarrierMode mode) {
ASSERT(this->length() > kTransitionsIndex);
Heap* heap = GetHeap();
WRITE_FIELD(this, kTransitionsOffset, transition_map);
CONDITIONAL_WRITE_BARRIER(
heap, this, kTransitionsOffset, transition_map, mode);
ASSERT(DescriptorArray::cast(this));
}
void DescriptorArray::ClearElementsTransition() {
void DescriptorArray::ClearTransitions() {
WRITE_FIELD(this, kTransitionsOffset, Smi::FromInt(0));
}
void DescriptorArray::set_transitions(TransitionArray* transitions_array,
WriteBarrierMode mode) {
Heap* heap = GetHeap();
WRITE_FIELD(this, kTransitionsOffset, transitions_array);
CONDITIONAL_WRITE_BARRIER(
heap, this, kTransitionsOffset, transitions_array, mode);
}
Object** DescriptorArray::GetKeySlot(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
return HeapObject::RawField(
@ -1976,17 +2031,6 @@ String* DescriptorArray::GetKey(int descriptor_number) {
}
void DescriptorArray::SetKeyUnchecked(Heap* heap,
int descriptor_number,
String* key) {
ASSERT(descriptor_number < number_of_descriptors());
set_unchecked(heap,
ToKeyIndex(descriptor_number),
key,
UPDATE_WRITE_BARRIER);
}
Object** DescriptorArray::GetValueSlot(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
return HeapObject::RawField(
@ -2001,24 +2045,6 @@ Object* DescriptorArray::GetValue(int descriptor_number) {
}
void DescriptorArray::SetNullValueUnchecked(Heap* heap, int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
set_null_unchecked(heap, ToValueIndex(descriptor_number));
}
void DescriptorArray::SetValueUnchecked(Heap* heap,
int descriptor_number,
Object* value) {
ASSERT(descriptor_number < number_of_descriptors());
set_unchecked(heap,
ToValueIndex(descriptor_number),
value,
UPDATE_WRITE_BARRIER);
}
PropertyDetails DescriptorArray::GetDetails(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
Object* details = get(ToDetailsIndex(descriptor_number));
@ -2026,12 +2052,6 @@ PropertyDetails DescriptorArray::GetDetails(int descriptor_number) {
}
void DescriptorArray::SetDetailsUnchecked(int descriptor_number, Smi* value) {
ASSERT(descriptor_number < number_of_descriptors());
set_unchecked(ToDetailsIndex(descriptor_number), value);
}
PropertyType DescriptorArray::GetType(int descriptor_number) {
return GetDetails(descriptor_number).type();
}
@ -2060,38 +2080,6 @@ AccessorDescriptor* DescriptorArray::GetCallbacks(int descriptor_number) {
}
bool DescriptorArray::IsProperty(int descriptor_number) {
Entry entry(this, descriptor_number);
return IsPropertyDescriptor(&entry);
}
bool DescriptorArray::IsTransitionOnly(int descriptor_number) {
switch (GetType(descriptor_number)) {
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
return true;
case CALLBACKS: {
Object* value = GetValue(descriptor_number);
if (!value->IsAccessorPair()) return false;
AccessorPair* accessors = AccessorPair::cast(value);
return accessors->getter()->IsMap() && accessors->setter()->IsMap();
}
case NORMAL:
case FIELD:
case CONSTANT_FUNCTION:
case HANDLER:
case INTERCEPTOR:
return false;
case NONEXISTENT:
UNREACHABLE();
break;
}
UNREACHABLE(); // Keep the compiler happy.
return false;
}
void DescriptorArray::Get(int descriptor_number, Descriptor* desc) {
desc->Init(GetKey(descriptor_number),
GetValue(descriptor_number),
@ -2129,16 +2117,14 @@ void DescriptorArray::NoIncrementalWriteBarrierSwapDescriptors(
}
DescriptorArray::WhitenessWitness::WhitenessWitness(DescriptorArray* array)
FixedArray::WhitenessWitness::WhitenessWitness(FixedArray* array)
: marking_(array->GetHeap()->incremental_marking()) {
marking_->EnterNoMarkingScope();
if (array->number_of_descriptors() > 0) {
ASSERT(Marking::Color(array) == Marking::WHITE_OBJECT);
}
ASSERT(Marking::Color(array) == Marking::WHITE_OBJECT);
}
DescriptorArray::WhitenessWitness::~WhitenessWitness() {
FixedArray::WhitenessWitness::~WhitenessWitness() {
marking_->LeaveNoMarkingScope();
}
@ -3431,12 +3417,8 @@ void Map::init_instance_descriptors() {
void Map::clear_instance_descriptors() {
Object* object = READ_FIELD(this,
kInstanceDescriptorsOrBitField3Offset);
Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
if (!object->IsSmi()) {
#ifdef DEBUG
ZapInstanceDescriptors();
#endif
WRITE_FIELD(
this,
kInstanceDescriptorsOrBitField3Offset,
@ -3462,11 +3444,6 @@ void Map::set_instance_descriptors(DescriptorArray* value,
}
}
ASSERT(!is_shared());
#ifdef DEBUG
if (value != instance_descriptors()) {
ZapInstanceDescriptors();
}
#endif
WRITE_FIELD(this, kInstanceDescriptorsOrBitField3Offset, value);
CONDITIONAL_WRITE_BARRIER(
heap, this, kInstanceDescriptorsOrBitField3Offset, value, mode);
@ -3489,7 +3466,7 @@ void Map::ClearDescriptorArray() {
#ifdef DEBUG
Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
if (!object->IsSmi()) {
ZapInstanceDescriptors();
ZapTransitions();
}
#endif
WRITE_FIELD(this,
@ -3521,13 +3498,93 @@ Object* Map::GetBackPointer() {
}
Map* Map::elements_transition_map() {
return instance_descriptors()->elements_transition_map();
bool Map::HasElementsTransition() {
return HasTransitionArray() && transitions()->HasElementsTransition();
}
void Map::set_elements_transition_map(Map* transitioned_map) {
return instance_descriptors()->set_elements_transition_map(transitioned_map);
bool Map::HasTransitionArray() {
return instance_descriptors()->HasTransitionArray();
}
Map* Map::elements_transition_map() {
return transitions()->elements_transition();
}
MaybeObject* Map::AddTransition(String* key, Object* value) {
if (HasTransitionArray()) return transitions()->CopyInsert(key, value);
return TransitionArray::NewWith(key, value);
}
// If the map does not have a descriptor array, install a new empty
// descriptor array that has room for a transition array.
static MaybeObject* AllowTransitions(Map* map) {
if (map->instance_descriptors()->MayContainTransitions()) return map;
DescriptorArray* descriptors;
MaybeObject* maybe_descriptors =
DescriptorArray::Allocate(0, DescriptorArray::CANNOT_BE_SHARED);
if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors;
map->set_instance_descriptors(descriptors);
return descriptors;
}
// If the descriptor does not have a transition array, install a new
// transition array that has room for an element transition.
static MaybeObject* AllowElementsTransition(Map* map) {
if (map->HasTransitionArray()) return map;
AllowTransitions(map);
TransitionArray* transitions;
MaybeObject* maybe_transitions = TransitionArray::Allocate(0);
if (!maybe_transitions->To(&transitions)) return maybe_transitions;
MaybeObject* added_transitions = map->set_transitions(transitions);
if (added_transitions->IsFailure()) return added_transitions;
return transitions;
}
MaybeObject* Map::set_elements_transition_map(Map* transitioned_map) {
MaybeObject* allow_elements = AllowElementsTransition(this);
if (allow_elements->IsFailure()) return allow_elements;
transitions()->set_elements_transition(transitioned_map);
return this;
}
TransitionArray* Map::transitions() {
return instance_descriptors()->transitions();
}
void Map::ClearTransitions() {
#ifdef DEBUG
ZapTransitions();
#endif
DescriptorArray* descriptors = instance_descriptors();
if (descriptors->number_of_descriptors() == 0) {
ClearDescriptorArray();
} else {
descriptors->ClearTransitions();
}
}
MaybeObject* Map::set_transitions(TransitionArray* transitions_array) {
MaybeObject* allow_transitions = AllowTransitions(this);
if (allow_transitions->IsFailure()) return allow_transitions;
#ifdef DEBUG
if (HasTransitionArray()) {
ASSERT(transitions() != transitions_array);
ZapTransitions();
}
#endif
instance_descriptors()->set_transitions(transitions_array);
return this;
}

View File

@ -273,15 +273,11 @@ void JSObject::PrintProperties(FILE* out) {
descs->GetCallbacksObject(i)->ShortPrint(out);
PrintF(out, " (callback)\n");
break;
case MAP_TRANSITION:
PrintF(out, "(map transition)\n");
break;
case CONSTANT_TRANSITION:
PrintF(out, "(constant transition)\n");
break;
case NORMAL: // only in slow mode
case HANDLER: // only in lookup results, not in descriptors
case INTERCEPTOR: // only in lookup results, not in descriptors
// There are no transitions in the descriptor array.
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@ -408,6 +404,37 @@ void JSObject::PrintElements(FILE* out) {
}
void JSObject::PrintTransitions(FILE* out) {
if (!map()->HasTransitionArray()) return;
TransitionArray* transitions = map()->transitions();
for (int i = 0; i < transitions->number_of_transitions(); i++) {
PrintF(out, " ");
transitions->GetKey(i)->StringPrint(out);
PrintF(out, ": ");
switch (transitions->GetTargetDetails(i).type()) {
case FIELD: {
PrintF(out, " (transition to field)\n");
break;
}
case CONSTANT_FUNCTION:
PrintF(out, " (transition to constant function)\n");
break;
case CALLBACKS:
PrintF(out, " (transition to callback)\n");
break;
// Values below are never in the target descriptor array.
case NORMAL:
case HANDLER:
case INTERCEPTOR:
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
}
}
}
void JSObject::JSObjectPrint(FILE* out) {
PrintF(out, "%p: [JSObject]\n", reinterpret_cast<void*>(this));
PrintF(out, " - map = %p [", reinterpret_cast<void*>(map()));
@ -417,11 +444,9 @@ void JSObject::JSObjectPrint(FILE* out) {
PrintF(out,
"]\n - prototype = %p\n",
reinterpret_cast<void*>(GetPrototype()));
PrintF(out,
" - elements transition to = %p\n",
reinterpret_cast<void*>(map()->elements_transition_map()));
PrintF(out, " {\n");
PrintProperties(out);
PrintTransitions(out);
PrintElements(out);
PrintF(out, " }\n");
}
@ -537,6 +562,10 @@ void Map::MapPrint(FILE* out) {
}
PrintF(out, " - instance descriptors: ");
instance_descriptors()->ShortPrint(out);
if (HasTransitionArray()) {
PrintF(out, "\n - transitions: ");
transitions()->ShortPrint(out);
}
PrintF(out, "\n - prototype: ");
prototype()->ShortPrint(out);
PrintF(out, "\n - constructor: ");
@ -1024,6 +1053,37 @@ void DescriptorArray::PrintDescriptors(FILE* out) {
}
void TransitionArray::PrintTransitions(FILE* out) {
PrintF(out, "Transition array %d\n", number_of_transitions());
for (int i = 0; i < number_of_transitions(); i++) {
PrintF(out, " %d: ", i);
GetKey(i)->StringPrint(out);
PrintF(out, ": ");
switch (GetTargetDetails(i).type()) {
case FIELD: {
PrintF(out, " (transition to field)\n");
break;
}
case CONSTANT_FUNCTION:
PrintF(out, " (transition to constant function)\n");
break;
case CALLBACKS:
PrintF(out, " (transition to callback)\n");
break;
// Values below are never in the target descriptor array.
case NORMAL:
case HANDLER:
case INTERCEPTOR:
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
}
}
PrintF(out, "\n");
}
#endif // OBJECT_PRINT

File diff suppressed because it is too large Load Diff

View File

@ -780,6 +780,7 @@ class MaybeObject BASE_EMBEDDED {
V(JSModule) \
V(Map) \
V(DescriptorArray) \
V(TransitionArray) \
V(DeoptimizationInputData) \
V(DeoptimizationOutputData) \
V(TypeFeedbackCells) \
@ -1901,7 +1902,8 @@ class JSObject: public JSReceiver {
// new_map.
MUST_USE_RESULT MaybeObject* AddFastPropertyUsingMap(Map* new_map,
String* name,
Object* value);
Object* value,
int field_index);
// Add a constant function property to a fast-case object.
// This leaves a CONSTANT_TRANSITION in the old map, and
@ -2059,6 +2061,10 @@ class JSObject: public JSReceiver {
PrintElements(stdout);
}
void PrintElements(FILE* out);
inline void PrintTransitions() {
PrintTransitions(stdout);
}
void PrintTransitions(FILE* out);
#endif
void PrintElementsTransition(
@ -2212,7 +2218,6 @@ class JSObject: public JSReceiver {
Object* getter,
Object* setter,
PropertyAttributes attributes);
void LookupInDescriptor(String* name, LookupResult* result);
// Returns the hidden properties backing store object, currently
// a StringDictionary, stored on this object.
@ -2247,6 +2252,8 @@ class FixedArrayBase: public HeapObject {
class FixedDoubleArray;
class IncrementalMarking;
// FixedArray describes fixed-sized arrays with element type Object*.
class FixedArray: public FixedArrayBase {
@ -2342,6 +2349,23 @@ class FixedArray: public FixedArrayBase {
}
};
// WhitenessWitness is used to prove that a descriptor array is white
// (unmarked), so incremental write barriers can be skipped because the
// marking invariant cannot be broken and slots pointing into evacuation
// candidates will be discovered when the object is scanned. A witness is
// always stack-allocated right after creating an array. By allocating a
// witness, incremental marking is globally disabled. The witness is then
// passed along wherever needed to statically prove that the array is known to
// be white.
class WhitenessWitness {
public:
inline explicit WhitenessWitness(FixedArray* array);
inline ~WhitenessWitness();
private:
IncrementalMarking* marking_;
};
protected:
// Set operation on FixedArray without using write barriers. Can
// only be used for storing old space objects or smis.
@ -2416,9 +2440,6 @@ class FixedDoubleArray: public FixedArrayBase {
};
class IncrementalMarking;
// DescriptorArrays are fixed arrays used to hold instance descriptors.
// The format of the these objects is:
// TODO(1399): It should be possible to make room for bit_field3 in the map
@ -2430,7 +2451,7 @@ class IncrementalMarking;
// [0]: next enumeration index (Smi)
// [1]: pointer to fixed array with enum cache
// [3]: first key
// [length() - 1]: last key
// [length() - kDescriptorSize]: last key
//
class DescriptorArray: public FixedArray {
public:
@ -2439,17 +2460,20 @@ class DescriptorArray: public FixedArray {
// yet used.
inline bool IsEmpty();
inline bool MayContainTransitions();
inline bool HasTransitionArray();
DECL_ACCESSORS(elements_transition_map, Map)
inline void ClearElementsTransition();
DECL_ACCESSORS(transitions, TransitionArray)
inline void ClearTransitions();
// Returns the number of descriptors in the array.
int number_of_descriptors() {
ASSERT(length() >= kFirstIndex || IsEmpty());
ASSERT(MayContainTransitions() || IsEmpty());
int len = length();
return len <= kFirstIndex ? 0 : (len - kFirstIndex) / kDescriptorSize;
}
inline int number_of_entries() { return number_of_descriptors(); }
int NextEnumerationIndex() {
if (IsEmpty()) return PropertyDetails::kInitialIndex;
Object* obj = get(kEnumerationIndexIndex);
@ -2484,7 +2508,6 @@ class DescriptorArray: public FixedArray {
}
Object** GetTransitionsSlot() {
ASSERT(elements_transition_map() != NULL);
return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
kTransitionsOffset);
}
@ -2504,39 +2527,14 @@ class DescriptorArray: public FixedArray {
// Accessors for fetching instance descriptor at descriptor number.
inline String* GetKey(int descriptor_number);
inline Object** GetKeySlot(int descriptor_number);
inline void SetKeyUnchecked(Heap* heap, int descriptor_number, String* value);
inline Object* GetValue(int descriptor_number);
inline Object** GetValueSlot(int descriptor_number);
inline void SetNullValueUnchecked(Heap* heap, int descriptor_number);
inline void SetValueUnchecked(Heap* heap,
int descriptor_number,
Object* value);
inline PropertyDetails GetDetails(int descriptor_number);
inline void SetDetailsUnchecked(int descriptor_number, Smi* value);
inline PropertyType GetType(int descriptor_number);
inline int GetFieldIndex(int descriptor_number);
inline JSFunction* GetConstantFunction(int descriptor_number);
inline Object* GetCallbacksObject(int descriptor_number);
inline AccessorDescriptor* GetCallbacks(int descriptor_number);
inline bool IsProperty(int descriptor_number);
inline bool IsTransitionOnly(int descriptor_number);
// WhitenessWitness is used to prove that a specific descriptor array is white
// (unmarked), so incremental write barriers can be skipped because the
// marking invariant cannot be broken and slots pointing into evacuation
// candidates will be discovered when the object is scanned. A witness is
// always stack-allocated right after creating a descriptor array. By
// allocating a witness, incremental marking is globally disabled. The witness
// is then passed along wherever needed to statically prove that the
// descriptor array is known to be white.
class WhitenessWitness {
public:
inline explicit WhitenessWitness(DescriptorArray* array);
inline ~WhitenessWitness();
private:
IncrementalMarking* marking_;
};
// Accessor for complete descriptor.
inline void Get(int descriptor_number, Descriptor* desc);
@ -2565,8 +2563,7 @@ class DescriptorArray: public FixedArray {
// or null), its enumeration index is kept as is.
// If adding a real property, map transitions must be removed. If adding
// a transition, they must not be removed. All null descriptors are removed.
MUST_USE_RESULT MaybeObject* CopyInsert(Descriptor* descriptor,
TransitionFlag transition_flag);
MUST_USE_RESULT MaybeObject* CopyInsert(Descriptor* descriptor);
// Indicates whether the search function should expect a sorted or an unsorted
// descriptor array as input.
@ -2577,7 +2574,7 @@ class DescriptorArray: public FixedArray {
// Return a copy of the array with all transitions and null descriptors
// removed. Return a Failure object in case of an allocation failure.
MUST_USE_RESULT MaybeObject* RemoveTransitions(SharedMode shared_mode);
MUST_USE_RESULT MaybeObject* Copy(SharedMode shared_mode);
// Sort the instance descriptors by the hash codes of their keys.
// Does not check for duplicates.
@ -2597,17 +2594,6 @@ class DescriptorArray: public FixedArray {
// Tells whether the name is present int the array.
bool Contains(String* name) { return kNotFound != Search(name); }
// Perform a binary search in the instance descriptors represented
// by this fixed array. low and high are descriptor indices. If there
// are three instance descriptors in this array it should be called
// with low=0 and high=2.
int BinarySearch(String* name, int low, int high);
// Perform a linear search in the instance descriptors represented
// by this fixed array. len is the number of descriptor indices that are
// valid.
int LinearSearch(SearchMode mode, String* name, int len);
// Allocates a DescriptorArray, but returns the singleton
// empty descriptor array object if number_of_descriptors is 0.
MUST_USE_RESULT static MaybeObject* Allocate(int number_of_descriptors,
@ -2632,8 +2618,8 @@ class DescriptorArray: public FixedArray {
// Layout description.
static const int kBitField3StorageOffset = FixedArray::kHeaderSize;
static const int kEnumerationIndexOffset =
kBitField3StorageOffset + kPointerSize;
static const int kEnumerationIndexOffset = kBitField3StorageOffset +
kPointerSize;
static const int kTransitionsOffset = kEnumerationIndexOffset + kPointerSize;
static const int kFirstOffset = kTransitionsOffset + kPointerSize;
@ -2709,7 +2695,7 @@ class DescriptorArray: public FixedArray {
static inline void NoIncrementalWriteBarrierSwap(
FixedArray* array, int first, int second);
// Swap descriptor first and second.
// Swap first and second descriptor.
inline void NoIncrementalWriteBarrierSwapDescriptors(
int first, int second);
@ -2717,6 +2703,14 @@ class DescriptorArray: public FixedArray {
};
template<typename T>
inline int LinearSearch(T* array, SearchMode mode, String* name, int len);
template<typename T>
inline int Search(T* array, String* name);
// HashTable is a subclass of FixedArray that implements a hash table
// that uses open addressing and quadratic probing.
//
@ -3181,8 +3175,6 @@ class StringDictionary: public Dictionary<StringDictionaryShape, String*> {
// Find entry for key, otherwise return kNotFound. Optimized version of
// HashTable::FindEntry.
int FindEntry(String* key);
bool ContainsTransition(int entry);
};
@ -4631,6 +4623,7 @@ class Map: public HeapObject {
// DescriptorArray when the map has one).
inline int bit_field3();
inline void set_bit_field3(int value);
inline void SetOwnBitField3(int value);
// Tells whether the object in the prototype property will be used
// for instances created from this function. If the prototype
@ -4751,8 +4744,17 @@ class Map: public HeapObject {
static bool IsValidElementsTransition(ElementsKind from_kind,
ElementsKind to_kind);
inline bool HasTransitionArray();
inline bool HasElementsTransition();
inline Map* elements_transition_map();
inline void set_elements_transition_map(Map* transitioned_map);
MUST_USE_RESULT inline MaybeObject* set_elements_transition_map(
Map* transitioned_map);
inline TransitionArray* transitions();
MUST_USE_RESULT inline MaybeObject* AddTransition(String* key,
Object* value);
MUST_USE_RESULT inline MaybeObject* set_transitions(
TransitionArray* transitions);
inline void ClearTransitions();
// Tells whether the map is attached to SharedFunctionInfo
// (for inobject slack tracking).
@ -4842,9 +4844,17 @@ class Map: public HeapObject {
// Lookup in the map's instance descriptors and fill out the result
// with the given holder if the name is found. The holder may be
// NULL when this function is used from the compiler.
void LookupInDescriptors(JSObject* holder,
String* name,
LookupResult* result);
void LookupDescriptor(JSObject* holder,
String* name,
LookupResult* result);
void LookupTransition(JSObject* holder,
String* name,
LookupResult* result);
void LookupTransitionOrDescriptor(JSObject* holder,
String* name,
LookupResult* result);
MUST_USE_RESULT MaybeObject* CopyDropDescriptors();
@ -4928,8 +4938,8 @@ class Map: public HeapObject {
// holding weak references when incremental marking is used, because it also
// iterates over objects that are otherwise unreachable.
#ifdef DEBUG
void ZapInstanceDescriptors();
void ZapPrototypeTransitions();
void ZapTransitions();
#endif
// Dispatched behavior.

View File

@ -2233,9 +2233,8 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
case NORMAL: // only in slow mode
case HANDLER: // only in lookup results, not in descriptors
case INTERCEPTOR: // only in lookup results, not in descriptors
case MAP_TRANSITION: // we do not care about transitions here...
case CONSTANT_TRANSITION:
break;
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;

View File

@ -55,17 +55,18 @@ class Smi;
// Must fit in the BitField PropertyDetails::TypeField.
// A copy of this is in mirror-debugger.js.
enum PropertyType {
NORMAL = 0, // only in slow mode
FIELD = 1, // only in fast mode
CONSTANT_FUNCTION = 2, // only in fast mode
// Only in slow mode.
NORMAL = 0,
// Only in fast mode.
FIELD = 1,
CONSTANT_FUNCTION = 2,
CALLBACKS = 3,
HANDLER = 4, // only in lookup results, not in descriptors
INTERCEPTOR = 5, // only in lookup results, not in descriptors
// All properties before MAP_TRANSITION are real.
MAP_TRANSITION = 6, // only in fast mode
CONSTANT_TRANSITION = 7, // only in fast mode
// Only in lookup results, not in descriptors.
HANDLER = 4,
INTERCEPTOR = 5,
TRANSITION = 6,
// Only used as a marker in LookupResult.
NONEXISTENT = 8
NONEXISTENT = 7
};
@ -112,10 +113,10 @@ class PropertyDetails BASE_EMBEDDED {
// Bit fields in value_ (type, shift, size). Must be public so the
// constants can be embedded in generated code.
class TypeField: public BitField<PropertyType, 0, 4> {};
class AttributesField: public BitField<PropertyAttributes, 4, 3> {};
class DeletedField: public BitField<uint32_t, 7, 1> {};
class StorageField: public BitField<uint32_t, 8, 32-8> {};
class TypeField: public BitField<PropertyType, 0, 3> {};
class AttributesField: public BitField<PropertyAttributes, 3, 3> {};
class DeletedField: public BitField<uint32_t, 6, 1> {};
class StorageField: public BitField<uint32_t, 7, 32-7> {};
static const int kInitialIndex = 1;

View File

@ -55,12 +55,6 @@ void LookupResult::Print(FILE* out) {
PrintF(out, " -type = normal\n");
PrintF(out, " -entry = %d", GetDictionaryEntry());
break;
case MAP_TRANSITION:
PrintF(out, " -type = map transition\n");
PrintF(out, " -map:\n");
GetTransitionMap()->Print(out);
PrintF(out, "\n");
break;
case CONSTANT_FUNCTION:
PrintF(out, " -type = constant function\n");
PrintF(out, " -function:\n");
@ -83,12 +77,29 @@ void LookupResult::Print(FILE* out) {
case INTERCEPTOR:
PrintF(out, " -type = lookup interceptor\n");
break;
case CONSTANT_TRANSITION:
PrintF(out, " -type = constant property transition\n");
PrintF(out, " -map:\n");
GetTransitionMap()->Print(out);
PrintF(out, "\n");
break;
case TRANSITION:
switch (GetTransitionDetails().type()) {
case FIELD:
PrintF(out, " -type = map transition\n");
PrintF(out, " -map:\n");
GetTransitionMap()->Print(out);
PrintF(out, "\n");
return;
case CONSTANT_FUNCTION:
PrintF(out, " -type = constant property transition\n");
PrintF(out, " -map:\n");
GetTransitionMap()->Print(out);
PrintF(out, "\n");
return;
case CALLBACKS:
PrintF(out, " -type = callbacks transition\n");
PrintF(out, " -callback object:\n");
GetCallbackObject()->Print(out);
return;
default:
UNREACHABLE();
return;
}
case NONEXISTENT:
UNREACHABLE();
break;
@ -108,29 +119,4 @@ void Descriptor::Print(FILE* out) {
#endif
bool Descriptor::ContainsTransition() {
switch (details_.type()) {
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
return true;
case CALLBACKS: {
if (!value_->IsAccessorPair()) return false;
AccessorPair* accessors = AccessorPair::cast(value_);
return accessors->getter()->IsMap() || accessors->setter()->IsMap();
}
case NORMAL:
case FIELD:
case CONSTANT_FUNCTION:
case HANDLER:
case INTERCEPTOR:
return false;
case NONEXISTENT:
UNREACHABLE();
break;
}
UNREACHABLE(); // Keep the compiler happy.
return false;
}
} } // namespace v8::internal

View File

@ -29,6 +29,7 @@
#define V8_PROPERTY_H_
#include "allocation.h"
#include "transitions.h"
namespace v8 {
namespace internal {
@ -68,8 +69,6 @@ class Descriptor BASE_EMBEDDED {
details_ = PropertyDetails(details_.attributes(), details_.type(), index);
}
bool ContainsTransition();
private:
String* key_;
Object* value_;
@ -101,27 +100,6 @@ class Descriptor BASE_EMBEDDED {
friend class DescriptorArray;
};
// A pointer from a map to the new map that is created by adding
// a named property. These are key to the speed and functioning of V8.
// The two maps should always have the same prototype, since
// MapSpace::CreateBackPointers depends on this.
class MapTransitionDescriptor: public Descriptor {
public:
MapTransitionDescriptor(String* key, Map* map, PropertyAttributes attributes)
: Descriptor(key, map, attributes, MAP_TRANSITION) { }
};
// Marks a field name in a map so that adding the field is guaranteed
// to create a FIELD descriptor in the new map. Used after adding
// a constant function the first time, creating a CONSTANT_FUNCTION
// descriptor in the new map. This avoids creating multiple maps with
// the same CONSTANT_FUNCTION field.
class ConstTransitionDescriptor: public Descriptor {
public:
explicit ConstTransitionDescriptor(String* key, Map* map)
: Descriptor(key, map, NONE, CONSTANT_TRANSITION) { }
};
class FieldDescriptor: public Descriptor {
public:
@ -153,36 +131,6 @@ class CallbacksDescriptor: public Descriptor {
};
template <class T>
bool IsPropertyDescriptor(T* desc) {
switch (desc->type()) {
case NORMAL:
case FIELD:
case CONSTANT_FUNCTION:
case HANDLER:
case INTERCEPTOR:
return true;
case CALLBACKS: {
Object* callback_object = desc->GetCallbackObject();
// Non-JavaScript (i.e. native) accessors are always a property, otherwise
// either the getter or the setter must be an accessor. Put another way:
// If we only see map transitions and holes in a pair, this is not a
// property.
return (!callback_object->IsAccessorPair() ||
AccessorPair::cast(callback_object)->ContainsAccessor());
}
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
return false;
case NONEXISTENT:
UNREACHABLE();
break;
}
UNREACHABLE(); // keep the compiler happy
return false;
}
class LookupResult BASE_EMBEDDED {
public:
explicit LookupResult(Isolate* isolate)
@ -207,6 +155,13 @@ class LookupResult BASE_EMBEDDED {
number_ = number;
}
void TransitionResult(JSObject* holder, int number) {
lookup_type_ = TRANSITION_TYPE;
details_ = PropertyDetails(NONE, TRANSITION);
holder_ = holder;
number_ = number;
}
void ConstantResult(JSObject* holder) {
lookup_type_ = CONSTANT_TYPE;
holder_ = holder;
@ -259,29 +214,41 @@ class LookupResult BASE_EMBEDDED {
}
PropertyAttributes GetAttributes() {
ASSERT(!IsTransition());
ASSERT(IsFound());
ASSERT(details_.type() != NONEXISTENT);
return details_.attributes();
}
PropertyDetails GetPropertyDetails() {
ASSERT(!IsTransition());
return details_;
}
bool IsFastPropertyType() {
ASSERT(IsFound());
return type() != NORMAL;
return IsTransition() || type() != NORMAL;
}
// Property callbacks does not include transitions to callbacks.
bool IsPropertyCallbacks() {
ASSERT(!(details_.type() == CALLBACKS && !IsFound()));
return details_.type() == CALLBACKS;
}
// Is callbacks contains both property callbacks and transitions to callbacks.
bool IsCallbacks() {
return IsPropertyCallbacks() ||
(IsTransition() && GetTransitionValue()->IsAccessorPair());
}
bool IsReadOnly() {
ASSERT(IsFound());
ASSERT(!IsTransition());
ASSERT(details_.type() != NONEXISTENT);
return details_.IsReadOnly();
}
bool IsCallbacks() {
ASSERT(!(details_.type() == CALLBACKS && !IsFound()));
return details_.type() == CALLBACKS;
}
bool IsField() {
ASSERT(!(details_.type() == FIELD && !IsFound()));
return details_.type() == FIELD;
@ -297,21 +264,17 @@ class LookupResult BASE_EMBEDDED {
return details_.type() == CONSTANT_FUNCTION;
}
bool IsMapTransition() {
ASSERT(!(details_.type() == MAP_TRANSITION && !IsFound()));
return details_.type() == MAP_TRANSITION;
}
bool IsDontDelete() { return details_.IsDontDelete(); }
bool IsDontEnum() { return details_.IsDontEnum(); }
bool IsDeleted() { return details_.IsDeleted(); }
bool IsFound() { return lookup_type_ != NOT_FOUND; }
bool IsTransition() { return lookup_type_ == TRANSITION_TYPE; }
bool IsHandler() { return lookup_type_ == HANDLER_TYPE; }
bool IsInterceptor() { return lookup_type_ == INTERCEPTOR_TYPE; }
// Is the result is a property excluding transitions and the null descriptor?
bool IsProperty() {
return IsFound() && IsPropertyDescriptor(this);
return IsFound() && !IsTransition();
}
bool IsCacheable() { return cacheable_; }
@ -336,17 +299,35 @@ class LookupResult BASE_EMBEDDED {
}
}
Object* GetTransitionValue() {
ASSERT(IsTransition());
TransitionArray* transitions = holder()->map()->transitions();
Object* value = transitions->GetValue(number_);
return value;
}
PropertyDetails GetTransitionDetails(Map* map) {
ASSERT(IsTransition());
TransitionArray* transitions = map->transitions();
return transitions->GetTargetDetails(number_);
}
PropertyDetails GetTransitionDetails() {
return GetTransitionDetails(holder()->map());
}
bool IsTransitionToField(Map* map) {
return IsTransition() && GetTransitionDetails(map).type() == FIELD;
}
Map* GetTransitionMap() {
ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
ASSERT(type() == MAP_TRANSITION ||
type() == CONSTANT_TRANSITION);
ASSERT(IsTransition());
return Map::cast(GetValue());
}
Map* GetTransitionMapFromMap(Map* map) {
ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
ASSERT(type() == MAP_TRANSITION);
return Map::cast(map->instance_descriptors()->GetValue(number_));
ASSERT(IsTransition());
return Map::cast(map->transitions()->GetValue(number_));
}
int GetFieldIndex() {
@ -380,11 +361,14 @@ class LookupResult BASE_EMBEDDED {
}
Object* GetCallbackObject() {
if (lookup_type_ == CONSTANT_TYPE) {
// For now we only have the __proto__ as constant type.
return HEAP->prototype_accessors();
switch (lookup_type_) {
case CONSTANT_TYPE:
return HEAP->prototype_accessors();
case TRANSITION_TYPE:
return GetTransitionValue();
default:
return GetValue();
}
return GetValue();
}
#ifdef OBJECT_PRINT
@ -411,6 +395,7 @@ class LookupResult BASE_EMBEDDED {
enum {
NOT_FOUND,
DESCRIPTOR_TYPE,
TRANSITION_TYPE,
DICTIONARY_TYPE,
HANDLER_TYPE,
INTERCEPTOR_TYPE,

View File

@ -1170,7 +1170,7 @@ static MaybeObject* GetOwnProperty(Isolate* isolate,
elms->set(ENUMERABLE_INDEX, heap->ToBoolean(!result.IsDontEnum()));
elms->set(CONFIGURABLE_INDEX, heap->ToBoolean(!result.IsDontDelete()));
bool is_js_accessor = result.IsCallbacks() &&
bool is_js_accessor = result.IsPropertyCallbacks() &&
(result.GetCallbackObject()->IsAccessorPair());
if (is_js_accessor) {
@ -1421,7 +1421,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) {
// as required for function declarations.
if (lookup.IsProperty() && lookup.IsDontDelete()) {
if (lookup.IsReadOnly() || lookup.IsDontEnum() ||
lookup.IsCallbacks()) {
lookup.IsPropertyCallbacks()) {
return ThrowRedeclarationError(
isolate, is_function ? "function" : "module", name);
}
@ -2182,7 +2182,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetReadOnlyPrototype) {
// Construct a new field descriptors array containing the new descriptor.
Object* descriptors_unchecked;
{ MaybeObject* maybe_descriptors_unchecked =
instance_desc->CopyInsert(&new_desc, REMOVE_TRANSITIONS);
instance_desc->CopyInsert(&new_desc);
if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
return maybe_descriptors_unchecked;
}
@ -4566,7 +4566,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) {
// correctly in the case where a property is a field and is reset with
// new attributes.
if (result.IsProperty() &&
(attr != result.GetAttributes() || result.IsCallbacks())) {
(attr != result.GetAttributes() || result.IsPropertyCallbacks())) {
// New attributes - normalize to avoid writing to instance descriptor
if (js_object->IsJSGlobalProxy()) {
// Since the result is a property, the prototype will exist so
@ -10338,8 +10338,7 @@ static MaybeObject* DebugLookupResultValue(Heap* heap,
}
}
case INTERCEPTOR:
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
case TRANSITION:
return heap->undefined_value();
case HANDLER:
case NONEXISTENT:
@ -10419,7 +10418,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetPropertyDetails) {
// GC can happen later in this code so put the required fields into
// local variables using handles when required for later use.
Handle<Object> result_callback_obj;
if (result.IsCallbacks()) {
if (result.IsPropertyCallbacks()) {
result_callback_obj = Handle<Object>(result.GetCallbackObject(),
isolate);
}
@ -10437,7 +10436,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetPropertyDetails) {
// If the callback object is a fixed array then it contains JavaScript
// getter and/or setter.
bool hasJavaScriptAccessors = result.IsCallbacks() &&
bool hasJavaScriptAccessors = result.IsPropertyCallbacks() &&
result_callback_obj->IsAccessorPair();
Handle<FixedArray> details =
isolate->factory()->NewFixedArray(hasJavaScriptAccessors ? 5 : 2);

182
src/transitions-inl.h Normal file
View File

@ -0,0 +1,182 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_TRANSITIONS_INL_H_
#define V8_TRANSITIONS_INL_H_
#include "objects-inl.h"
#include "transitions.h"
namespace v8 {
namespace internal {
#define FIELD_ADDR(p, offset) \
(reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
#define WRITE_FIELD(p, offset, value) \
(*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
#define CONDITIONAL_WRITE_BARRIER(heap, object, offset, value, mode) \
if (mode == UPDATE_WRITE_BARRIER) { \
heap->incremental_marking()->RecordWrite( \
object, HeapObject::RawField(object, offset), value); \
if (heap->InNewSpace(value)) { \
heap->RecordWrite(object->address(), offset); \
} \
}
TransitionArray* TransitionArray::cast(Object* object) {
ASSERT(object->IsTransitionArray());
return reinterpret_cast<TransitionArray*>(object);
}
Map* TransitionArray::elements_transition() {
Object* transition_map = get(kElementsTransitionIndex);
return Map::cast(transition_map);
}
void TransitionArray::ClearElementsTransition() {
WRITE_FIELD(this, kElementsTransitionOffset, Smi::FromInt(0));
}
bool TransitionArray::HasElementsTransition() {
return get(kElementsTransitionIndex) != Smi::FromInt(0);
}
void TransitionArray::set_elements_transition(Map* transition_map,
WriteBarrierMode mode) {
Heap* heap = GetHeap();
WRITE_FIELD(this, kElementsTransitionOffset, transition_map);
CONDITIONAL_WRITE_BARRIER(
heap, this, kElementsTransitionOffset, transition_map, mode);
}
Object** TransitionArray::GetKeySlot(int transition_number) {
ASSERT(transition_number < number_of_transitions());
return HeapObject::RawField(
reinterpret_cast<HeapObject*>(this),
OffsetOfElementAt(ToKeyIndex(transition_number)));
}
String* TransitionArray::GetKey(int transition_number) {
ASSERT(transition_number < number_of_transitions());
return String::cast(get(ToKeyIndex(transition_number)));
}
void TransitionArray::SetKey(int transition_number, String* key) {
ASSERT(transition_number < number_of_transitions());
set(ToKeyIndex(transition_number), key);
}
Object* TransitionArray::GetValue(int transition_number) {
ASSERT(transition_number < number_of_transitions());
return get(ToValueIndex(transition_number));
}
Object** TransitionArray::GetValueSlot(int transition_number) {
ASSERT(transition_number < number_of_transitions());
return HeapObject::RawField(
reinterpret_cast<HeapObject*>(this),
OffsetOfElementAt(ToValueIndex(transition_number)));
}
void TransitionArray::SetValue(int transition_number, Object* value) {
ASSERT(transition_number < number_of_transitions());
set(ToValueIndex(transition_number), value);
}
Map* TransitionArray::GetTargetMap(int transition_number) {
Object* value = GetValue(transition_number);
if (value->IsAccessorPair()) {
// TODO(verwaest): Currently details are always taken from the getter if it
// is a transition, otherwise from the setter which in that case has to be a
// transition. Details should be dependent on which component is requested.
AccessorPair* accessors = AccessorPair::cast(value);
if (accessors->getter()->IsMap()) {
return Map::cast(accessors->getter());
}
return Map::cast(accessors->setter());
}
return Map::cast(value);
}
PropertyDetails TransitionArray::GetTargetDetails(int transition_number) {
Map* map = GetTargetMap(transition_number);
DescriptorArray* descriptors = map->instance_descriptors();
String* key = GetKey(transition_number);
int descriptor = descriptors->SearchWithCache(key);
ASSERT(descriptor != DescriptorArray::kNotFound);
return descriptors->GetDetails(descriptor);
}
Object** TransitionArray::GetElementsSlot() {
return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
kElementsTransitionOffset);
}
int TransitionArray::Search(String* name) {
return internal::Search(this, name);
}
void TransitionArray::Set(int transition_number,
String* key,
Object* value,
const WhitenessWitness&) {
NoIncrementalWriteBarrierSet(this,
ToKeyIndex(transition_number),
key);
NoIncrementalWriteBarrierSet(this,
ToValueIndex(transition_number),
value);
}
#undef FIELD_ADDR
#undef WRITE_FIELD
#undef CONDITIONAL_WRITE_BARRIER
} } // namespace v8::internal
#endif // V8_TRANSITIONS_INL_H_

127
src/transitions.cc Normal file
View File

@ -0,0 +1,127 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "objects.h"
#include "transitions-inl.h"
#include "utils.h"
namespace v8 {
namespace internal {
MaybeObject* TransitionArray::Allocate(int number_of_transitions) {
Heap* heap = Isolate::Current()->heap();
// Use FixedArray to not use DescriptorArray::cast on incomplete object.
FixedArray* array;
{ MaybeObject* maybe_array =
heap->AllocateFixedArray(ToKeyIndex(number_of_transitions));
if (!maybe_array->To(&array)) return maybe_array;
}
array->set(kElementsTransitionIndex, Smi::FromInt(0));
return array;
}
void TransitionArray::CopyFrom(TransitionArray* origin,
int origin_transition,
int target_transition,
const WhitenessWitness& witness) {
this->Set(target_transition,
origin->GetKey(origin_transition),
origin->GetValue(origin_transition),
witness);
}
static bool InsertionPointFound(String* key1, String* key2) {
return key1->Hash() > key2->Hash();
}
MaybeObject* TransitionArray::NewWith(String* name, Object* value) {
TransitionArray* result;
{ MaybeObject* maybe_array;
maybe_array = TransitionArray::Allocate(1);
if (!maybe_array->To(&result)) return maybe_array;
}
FixedArray::WhitenessWitness witness(result);
result->Set(0, name, value, witness);
return result;
}
MaybeObject* TransitionArray::CopyInsert(String* name, Object* value) {
TransitionArray* result;
int number_of_transitions = this->number_of_transitions();
int new_size = number_of_transitions;
int insertion_index = this->Search(name);
if (insertion_index == kNotFound) ++new_size;
{ MaybeObject* maybe_array;
maybe_array = TransitionArray::Allocate(new_size);
if (!maybe_array->To(&result)) return maybe_array;
}
if (HasElementsTransition()) {
result->set_elements_transition(elements_transition());
}
FixedArray::WhitenessWitness witness(result);
if (insertion_index != kNotFound) {
for (int i = 0; i < number_of_transitions; ++i) {
if (i != insertion_index) result->CopyFrom(this, i, i, witness);
}
result->Set(insertion_index, name, value, witness);
return result;
}
insertion_index = 0;
for (; insertion_index < number_of_transitions; ++insertion_index) {
if (InsertionPointFound(GetKey(insertion_index), name)) break;
result->CopyFrom(this, insertion_index, insertion_index, witness);
}
result->Set(insertion_index, name, value, witness);
for (; insertion_index < number_of_transitions; ++insertion_index) {
result->CopyFrom(this, insertion_index, insertion_index + 1, witness);
}
return result;
}
} } // namespace v8::internal

156
src/transitions.h Normal file
View File

@ -0,0 +1,156 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_TRANSITIONS_H_
#define V8_TRANSITIONS_H_
#include "elements-kind.h"
#include "heap.h"
#include "isolate.h"
#include "objects.h"
#include "v8checks.h"
namespace v8 {
namespace internal {
// TransitionArrays are fixed arrays used to hold map transitions for property,
// constant, and element changes.
// The format of the these objects is:
// [0] Elements transition
// [1] First transition
// [length() - kTransitionSize] Last transition
class TransitionArray: public FixedArray {
public:
inline Map* elements_transition();
inline void set_elements_transition(
Map* value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
inline void ClearElementsTransition();
inline bool HasElementsTransition();
// Accessors for fetching instance transition at transition number.
inline String* GetKey(int transition_number);
inline Object** GetKeySlot(int transition_number);
inline void SetKey(int transition_number, String* value);
inline Object* GetValue(int transition_number);
inline Object** GetValueSlot(int transition_number);
inline void SetValue(int transition_number, Object* value);
inline Map* GetTargetMap(int transition_number);
inline PropertyDetails GetTargetDetails(int transition_number);
inline Object** GetElementsSlot();
// Returns the number of transitions in the array.
int number_of_transitions() {
ASSERT(length() >= kFirstIndex);
int len = length();
return len <= kFirstIndex ? 0 : (len - kFirstIndex) / kTransitionSize;
}
inline int number_of_entries() { return number_of_transitions(); }
// Allocate a new transition array with a single entry.
static MUST_USE_RESULT MaybeObject* NewWith(String* name, Object* map);
// Copy the transition array, inserting a new transition.
// TODO(verwaest): This should not cause an existing transition to be
// overwritten.
MUST_USE_RESULT MaybeObject* CopyInsert(String* name, Object* map);
// Copy a single transition from the origin array.
inline void CopyFrom(TransitionArray* origin,
int origin_transition,
int target_transition,
const WhitenessWitness& witness);
// Search a transition for a given property name.
inline int Search(String* name);
// Allocates a TransitionArray.
MUST_USE_RESULT static MaybeObject* Allocate(int number_of_transitions);
// Casting.
static inline TransitionArray* cast(Object* obj);
// Constant for denoting key was not found.
static const int kNotFound = -1;
static const int kElementsTransitionIndex = 0;
static const int kFirstIndex = 1;
// Layout transition array header.
static const int kElementsTransitionOffset = FixedArray::kHeaderSize;
static const int kFirstOffset = kElementsTransitionOffset + kPointerSize;
// Layout of map transition.
static const int kTransitionKey = 0;
static const int kTransitionValue = 1;
static const int kTransitionSize = 2;
#ifdef OBJECT_PRINT
// Print all the transitions.
inline void PrintTransitions() {
PrintTransitions(stdout);
}
void PrintTransitions(FILE* out);
#endif
#ifdef DEBUG
bool IsSortedNoDuplicates();
bool IsConsistentWithBackPointers(Map* current_map);
bool IsEqualTo(TransitionArray* other);
#endif
// The maximum number of transitions we want in a transition array (should
// fit in a page).
static const int kMaxNumberOfTransitions = 1024 + 512;
private:
// Conversion from transition number to array indices.
static int ToKeyIndex(int transition_number) {
return kFirstIndex +
(transition_number * kTransitionSize) +
kTransitionKey;
}
static int ToValueIndex(int transition_number) {
return kFirstIndex +
(transition_number * kTransitionSize) +
kTransitionValue;
}
inline void Set(int transition_number,
String* key,
Object* value,
const WhitenessWitness&);
DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray);
};
} } // namespace v8::internal
#endif // V8_TRANSITIONS_H_

View File

@ -126,6 +126,7 @@ class Debugger;
class DebugInfo;
class Descriptor;
class DescriptorArray;
class TransitionArray;
class ExternalReference;
class FixedArray;
class FunctionTemplateInfo;
@ -311,14 +312,6 @@ typedef void (*StoreBufferCallback)(Heap* heap,
StoreBufferEvent event);
// Whether to remove map transitions and constant transitions from a
// DescriptorArray.
enum TransitionFlag {
REMOVE_TRANSITIONS,
KEEP_TRANSITIONS
};
// Union used for fast testing of specific double values.
union DoubleRepresentation {
double value;

View File

@ -2284,7 +2284,7 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
Handle<String> name,
LEnvironment* env) {
LookupResult lookup(isolate());
type->LookupInDescriptors(NULL, *name, &lookup);
type->LookupDescriptor(NULL, *name, &lookup);
ASSERT(lookup.IsFound() || lookup.IsCacheable());
if (lookup.IsField()) {
int index = lookup.GetLocalFieldIndexFromMap(*type);
@ -2328,9 +2328,9 @@ static bool CompactEmit(SmallMapList* list,
Handle<Map> map = list->at(i);
// If the map has ElementsKind transitions, we will generate map checks
// for each kind in __ CompareMap(..., ALLOW_ELEMENTS_TRANSITION_MAPS).
if (map->elements_transition_map() != NULL) return false;
if (map->HasElementsTransition()) return false;
LookupResult lookup(isolate);
map->LookupInDescriptors(NULL, *name, &lookup);
map->LookupDescriptor(NULL, *name, &lookup);
return lookup.IsField() || lookup.IsConstantFunction();
}

View File

@ -4273,9 +4273,9 @@ TEST(InterceptorPropertyMirror) {
"named_values[%d] instanceof debug.PropertyMirror", i);
CHECK(CompileRun(buffer.start())->BooleanValue());
// 5 is PropertyType.Interceptor
OS::SNPrintF(buffer, "named_values[%d].propertyType()", i);
CHECK_EQ(5, CompileRun(buffer.start())->Int32Value());
CHECK_EQ(v8::internal::INTERCEPTOR,
CompileRun(buffer.start())->Int32Value());
OS::SNPrintF(buffer, "named_values[%d].isNative()", i);
CHECK(CompileRun(buffer.start())->BooleanValue());

View File

@ -1743,14 +1743,7 @@ TEST(OptimizedAllocationAlwaysInNewSpace) {
static int CountMapTransitions(Map* map) {
int result = 0;
DescriptorArray* descs = map->instance_descriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
if (descs->IsTransitionOnly(i)) {
result++;
}
}
return result;
return map->transitions()->number_of_transitions();
}

View File

@ -1107,7 +1107,13 @@ class FixedArray(HeapObject):
base_offset = self.ElementsOffset()
for i in xrange(self.length):
offset = base_offset + 4 * i
p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
try:
p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
except TypeError:
p.Dedent()
p.Print("...")
p.Print("}")
return
p.Dedent()
p.Print("}")

View File

@ -234,10 +234,10 @@
'../../src/ast.h',
'../../src/atomicops.h',
'../../src/atomicops_internals_x86_gcc.cc',
'../../src/bignum.cc',
'../../src/bignum.h',
'../../src/bignum-dtoa.cc',
'../../src/bignum-dtoa.h',
'../../src/bignum.cc',
'../../src/bignum.h',
'../../src/bootstrapper.cc',
'../../src/bootstrapper.h',
'../../src/builtins.cc',
@ -268,21 +268,21 @@
'../../src/conversions.h',
'../../src/counters.cc',
'../../src/counters.h',
'../../src/cpu.h',
'../../src/cpu-profiler-inl.h',
'../../src/cpu-profiler.cc',
'../../src/cpu-profiler.h',
'../../src/cpu.h',
'../../src/data-flow.cc',
'../../src/data-flow.h',
'../../src/date.cc',
'../../src/date.h',
'../../src/dateparser-inl.h',
'../../src/dateparser.cc',
'../../src/dateparser.h',
'../../src/dateparser-inl.h',
'../../src/debug.cc',
'../../src/debug.h',
'../../src/debug-agent.cc',
'../../src/debug-agent.h',
'../../src/debug.cc',
'../../src/debug.h',
'../../src/deoptimizer.cc',
'../../src/deoptimizer.h',
'../../src/disasm.h',
@ -293,19 +293,23 @@
'../../src/double.h',
'../../src/dtoa.cc',
'../../src/dtoa.h',
'../../src/elements.cc',
'../../src/elements.h',
'../../src/elements-kind.cc',
'../../src/elements-kind.h',
'../../src/elements.cc',
'../../src/elements.h',
'../../src/execution.cc',
'../../src/execution.h',
'../../src/extensions/externalize-string-extension.cc',
'../../src/extensions/externalize-string-extension.h',
'../../src/extensions/gc-extension.cc',
'../../src/extensions/gc-extension.h',
'../../src/factory.cc',
'../../src/factory.h',
'../../src/fast-dtoa.cc',
'../../src/fast-dtoa.h',
'../../src/flag-definitions.h',
'../../src/fixed-dtoa.cc',
'../../src/fixed-dtoa.h',
'../../src/flag-definitions.h',
'../../src/flags.cc',
'../../src/flags.h',
'../../src/frames-inl.h',
@ -323,14 +327,14 @@
'../../src/handles.h',
'../../src/hashmap.h',
'../../src/heap-inl.h',
'../../src/heap.cc',
'../../src/heap.h',
'../../src/heap-profiler.cc',
'../../src/heap-profiler.h',
'../../src/hydrogen.cc',
'../../src/hydrogen.h',
'../../src/heap.cc',
'../../src/heap.h',
'../../src/hydrogen-instructions.cc',
'../../src/hydrogen-instructions.h',
'../../src/hydrogen.cc',
'../../src/hydrogen.h',
'../../src/ic-inl.h',
'../../src/ic.cc',
'../../src/ic.h',
@ -342,19 +346,19 @@
'../../src/interface.h',
'../../src/interpreter-irregexp.cc',
'../../src/interpreter-irregexp.h',
'../../src/isolate.cc',
'../../src/isolate.h',
'../../src/json-parser.h',
'../../src/jsregexp.cc',
'../../src/jsregexp.h',
'../../src/isolate.cc',
'../../src/isolate.h',
'../../src/lazy-instance.h',
'../../src/list-inl.h',
'../../src/list.h',
'../../src/lithium.cc',
'../../src/lithium.h',
'../../src/lithium-allocator-inl.h',
'../../src/lithium-allocator.cc',
'../../src/lithium-allocator.h',
'../../src/lithium-allocator-inl.h',
'../../src/lithium.cc',
'../../src/lithium.h',
'../../src/liveedit.cc',
'../../src/liveedit.h',
'../../src/liveobjectlist-inl.h',
@ -372,8 +376,8 @@
'../../src/messages.h',
'../../src/natives.h',
'../../src/objects-debug.cc',
'../../src/objects-printer.cc',
'../../src/objects-inl.h',
'../../src/objects-printer.cc',
'../../src/objects-visiting.cc',
'../../src/objects-visiting.h',
'../../src/objects.cc',
@ -394,12 +398,12 @@
'../../src/preparser.h',
'../../src/prettyprinter.cc',
'../../src/prettyprinter.h',
'../../src/property.cc',
'../../src/property.h',
'../../src/property-details.h',
'../../src/profile-generator-inl.h',
'../../src/profile-generator.cc',
'../../src/profile-generator.h',
'../../src/property-details.h',
'../../src/property.cc',
'../../src/property.h',
'../../src/regexp-macro-assembler-irregexp-inl.h',
'../../src/regexp-macro-assembler-irregexp.cc',
'../../src/regexp-macro-assembler-irregexp.h',
@ -411,16 +415,16 @@
'../../src/regexp-stack.h',
'../../src/rewriter.cc',
'../../src/rewriter.h',
'../../src/runtime.cc',
'../../src/runtime.h',
'../../src/runtime-profiler.cc',
'../../src/runtime-profiler.h',
'../../src/runtime.cc',
'../../src/runtime.h',
'../../src/safepoint-table.cc',
'../../src/safepoint-table.h',
'../../src/scanner.cc',
'../../src/scanner.h',
'../../src/scanner-character-streams.cc',
'../../src/scanner-character-streams.h',
'../../src/scanner.cc',
'../../src/scanner.h',
'../../src/scopeinfo.cc',
'../../src/scopeinfo.h',
'../../src/scopes.cc',
@ -447,6 +451,9 @@
'../../src/stub-cache.h',
'../../src/token.cc',
'../../src/token.h',
'../../src/transitions-inl.h',
'../../src/transitions.cc',
'../../src/transitions.h',
'../../src/type-info.cc',
'../../src/type-info.h',
'../../src/unbound-queue-inl.h',
@ -479,10 +486,6 @@
'../../src/zone-inl.h',
'../../src/zone.cc',
'../../src/zone.h',
'../../src/extensions/externalize-string-extension.cc',
'../../src/extensions/externalize-string-extension.h',
'../../src/extensions/gc-extension.cc',
'../../src/extensions/gc-extension.h',
],
'conditions': [
['want_separate_host_toolset==1', {