Removed transitions from the accessor pair descriptors.
AccessorPair related transitions are now also stored as single map links, simplifying the code that handles transitions. AccessorPairs can now be shared between descriptor arrays, since they can only be mutated after another transition anyway; during which the pair is copied before writing. Review URL: https://chromiumcodereview.appspot.com/10784014 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12097 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
bb9f4470c6
commit
11e5c9e281
@ -1740,9 +1740,7 @@ bool Genesis::InstallNatives() {
|
||||
Handle<DescriptorArray> array_descriptors(
|
||||
array_function->initial_map()->instance_descriptors());
|
||||
int old = array_descriptors->SearchWithCache(heap()->length_symbol());
|
||||
MaybeObject* copy_result =
|
||||
reresult_descriptors->CopyFrom(0, *array_descriptors, old, witness);
|
||||
if (copy_result->IsFailure()) return false;
|
||||
reresult_descriptors->CopyFrom(0, *array_descriptors, old, witness);
|
||||
|
||||
reresult_descriptors->SetLastAdded(0);
|
||||
|
||||
|
@ -933,7 +933,7 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
|
||||
if (0 < descriptor_count) {
|
||||
result->SetLastAdded(array->LastAdded());
|
||||
for (int i = 0; i < descriptor_count; i++) {
|
||||
DescriptorArray::CopyFrom(result, i, array, i, witness);
|
||||
result->CopyFrom(i, *array, i, witness);
|
||||
}
|
||||
}
|
||||
|
||||
@ -967,7 +967,7 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
|
||||
Handle<DescriptorArray> new_result =
|
||||
NewDescriptorArray(new_number_of_descriptors);
|
||||
for (int i = 0; i < new_number_of_descriptors; i++) {
|
||||
DescriptorArray::CopyFrom(new_result, i, result, i, witness);
|
||||
new_result->CopyFrom(i, *result, i, witness);
|
||||
}
|
||||
result = new_result;
|
||||
}
|
||||
|
28
src/heap.cc
28
src/heap.cc
@ -5329,37 +5329,9 @@ void Heap::Verify() {
|
||||
cell_space_->Verify(&no_dirty_regions_visitor);
|
||||
|
||||
lo_space_->Verify();
|
||||
|
||||
VerifyNoAccessorPairSharing();
|
||||
}
|
||||
|
||||
|
||||
void Heap::VerifyNoAccessorPairSharing() {
|
||||
// Verification is done in 2 phases: First we mark all AccessorPairs, checking
|
||||
// that we mark only unmarked pairs, then we clear all marks, restoring the
|
||||
// initial state. We use the Smi tag of the AccessorPair's getter as the
|
||||
// marking bit, because we can never see a Smi as the getter.
|
||||
for (int phase = 0; phase < 2; phase++) {
|
||||
HeapObjectIterator iter(map_space());
|
||||
for (HeapObject* obj = iter.Next(); obj != NULL; obj = iter.Next()) {
|
||||
if (obj->IsMap()) {
|
||||
DescriptorArray* descs = Map::cast(obj)->instance_descriptors();
|
||||
for (int i = 0; i < descs->number_of_descriptors(); i++) {
|
||||
if (descs->GetType(i) == CALLBACKS &&
|
||||
descs->GetValue(i)->IsAccessorPair()) {
|
||||
AccessorPair* accessors = AccessorPair::cast(descs->GetValue(i));
|
||||
uintptr_t before = reinterpret_cast<intptr_t>(accessors->getter());
|
||||
uintptr_t after = (phase == 0) ?
|
||||
((before & ~kSmiTagMask) | kSmiTag) :
|
||||
((before & ~kHeapObjectTag) | kHeapObjectTag);
|
||||
CHECK(before != after);
|
||||
accessors->set_getter(reinterpret_cast<Object*>(after));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
|
||||
|
@ -1237,10 +1237,6 @@ class Heap {
|
||||
// Verify the heap is in its normal state before or after a GC.
|
||||
void Verify();
|
||||
|
||||
// Verify that AccessorPairs are not shared, i.e. make sure that they have
|
||||
// exactly one pointer to them.
|
||||
void VerifyNoAccessorPairSharing();
|
||||
|
||||
void OldPointerSpaceCheckStoreBuffer();
|
||||
void MapSpaceCheckStoreBuffer();
|
||||
void LargeObjectSpaceCheckStoreBuffer();
|
||||
|
19
src/ic.cc
19
src/ic.cc
@ -1322,7 +1322,7 @@ static bool LookupForWrite(Handle<JSObject> receiver,
|
||||
// that we explicitly exclude native accessors for now, because the stubs
|
||||
// are not yet prepared for this scenario.
|
||||
receiver->Lookup(*name, lookup);
|
||||
if (!lookup->IsCallbacks()) return false;
|
||||
if (!lookup->IsPropertyCallbacks()) return false;
|
||||
Handle<Object> callback(lookup->GetCallbackObject());
|
||||
return callback->IsAccessorPair() && StoreICableLookup(lookup);
|
||||
}
|
||||
@ -1513,10 +1513,7 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
|
||||
case CONSTANT_FUNCTION:
|
||||
return;
|
||||
case TRANSITION: {
|
||||
Object* value = lookup->GetTransitionValue();
|
||||
// Callbacks.
|
||||
if (value->IsAccessorPair()) return;
|
||||
|
||||
Map* value = lookup->GetTransitionTarget();
|
||||
Handle<Map> transition(Map::cast(value));
|
||||
DescriptorArray* target_descriptors = transition->instance_descriptors();
|
||||
int descriptor = target_descriptors->LastAdded();
|
||||
@ -1979,16 +1976,8 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
|
||||
Handle<Map>::null(), strict_mode);
|
||||
break;
|
||||
case TRANSITION: {
|
||||
Object* value = lookup->GetTransitionValue();
|
||||
// Callbacks transition.
|
||||
if (value->IsAccessorPair()) {
|
||||
code = (strict_mode == kStrictMode)
|
||||
? generic_stub_strict()
|
||||
: generic_stub();
|
||||
break;
|
||||
}
|
||||
Handle<Map> transition(lookup->GetTransitionTarget());
|
||||
|
||||
Handle<Map> transition(Map::cast(value));
|
||||
DescriptorArray* target_descriptors = transition->instance_descriptors();
|
||||
int descriptor = target_descriptors->LastAdded();
|
||||
PropertyDetails details = target_descriptors->GetDetails(descriptor);
|
||||
@ -2147,7 +2136,7 @@ RUNTIME_FUNCTION(MaybeObject*, StoreIC_ArrayLength) {
|
||||
// The length property has to be a writable callback property.
|
||||
LookupResult debug_lookup(isolate);
|
||||
receiver->LocalLookup(isolate->heap()->length_symbol(), &debug_lookup);
|
||||
ASSERT(debug_lookup.IsCallbacks() && !debug_lookup.IsReadOnly());
|
||||
ASSERT(debug_lookup.IsPropertyCallbacks() && !debug_lookup.IsReadOnly());
|
||||
#endif
|
||||
|
||||
Object* result;
|
||||
|
@ -1934,16 +1934,8 @@ void Marker<T>::MarkDescriptorArray(DescriptorArray* descriptors) {
|
||||
case CONSTANT_FUNCTION:
|
||||
case HANDLER:
|
||||
case INTERCEPTOR:
|
||||
base_marker()->MarkObjectAndPush(value);
|
||||
break;
|
||||
case CALLBACKS:
|
||||
if (!value->IsAccessorPair()) {
|
||||
base_marker()->MarkObjectAndPush(value);
|
||||
} else if (base_marker()->MarkObjectWithoutPush(value)) {
|
||||
AccessorPair* accessors = AccessorPair::cast(value);
|
||||
MarkAccessorPairSlot(accessors, AccessorPair::kGetterOffset);
|
||||
MarkAccessorPairSlot(accessors, AccessorPair::kSetterOffset);
|
||||
}
|
||||
base_marker()->MarkObjectAndPush(value);
|
||||
break;
|
||||
case TRANSITION:
|
||||
case NONEXISTENT:
|
||||
@ -1983,21 +1975,6 @@ void Marker<T>::MarkTransitionArray(TransitionArray* transitions) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -949,15 +949,7 @@ bool TransitionArray::IsConsistentWithBackPointers(Map* current_map) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (!CheckOneBackPointer(current_map, GetTarget(i))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -3534,13 +3534,14 @@ Map* Map::elements_transition_map() {
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* Map::AddTransition(String* key, Object* value) {
|
||||
if (HasTransitionArray()) return transitions()->CopyInsert(key, value);
|
||||
return TransitionArray::NewWith(key, value);
|
||||
MaybeObject* Map::AddTransition(String* key, Map* target) {
|
||||
if (HasTransitionArray()) return transitions()->CopyInsert(key, target);
|
||||
return TransitionArray::NewWith(key, target);
|
||||
}
|
||||
|
||||
void Map::SetTransition(int transition_index, Object* value) {
|
||||
transitions()->SetValue(transition_index, value);
|
||||
|
||||
void Map::SetTransition(int transition_index, Map* target) {
|
||||
transitions()->SetTarget(transition_index, target);
|
||||
}
|
||||
|
||||
|
||||
|
440
src/objects.cc
440
src/objects.cc
@ -2859,18 +2859,11 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
||||
attributes,
|
||||
strict_mode);
|
||||
case TRANSITION: {
|
||||
Object* transition = result->GetTransitionValue();
|
||||
Map* transition_map = result->GetTransitionTarget();
|
||||
|
||||
if (transition->IsAccessorPair()) {
|
||||
ASSERT(!AccessorPair::cast(transition)->ContainsAccessor());
|
||||
return ConvertDescriptorToField(*name, *value, attributes);
|
||||
}
|
||||
|
||||
Map* transition_map = Map::cast(transition);
|
||||
DescriptorArray* descriptors = transition_map->instance_descriptors();
|
||||
int descriptor = descriptors->LastAdded();
|
||||
PropertyDetails details = descriptors->GetDetails(descriptor);
|
||||
ASSERT(details.type() == FIELD || details.type() == CONSTANT_FUNCTION);
|
||||
|
||||
if (details.type() == FIELD) {
|
||||
if (attributes == details.attributes()) {
|
||||
@ -2881,9 +2874,12 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
||||
field_index);
|
||||
}
|
||||
return self->ConvertDescriptorToField(*name, *value, attributes);
|
||||
} else if (details.type() == CALLBACKS) {
|
||||
return ConvertDescriptorToField(*name, *value, attributes);
|
||||
}
|
||||
|
||||
// Is transition to CONSTANT_FUNCTION.
|
||||
ASSERT(details.type() == CONSTANT_FUNCTION);
|
||||
|
||||
Object* constant_function = descriptors->GetValue(descriptor);
|
||||
// If the same constant function is being added we can simply
|
||||
// transition to the target map.
|
||||
@ -2983,18 +2979,11 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
||||
// Override callback in clone
|
||||
return ConvertDescriptorToField(name, value, attributes);
|
||||
case TRANSITION: {
|
||||
Object* transition = result.GetTransitionValue();
|
||||
Map* transition_map = result.GetTransitionTarget();
|
||||
|
||||
if (transition->IsAccessorPair()) {
|
||||
ASSERT(!AccessorPair::cast(transition)->ContainsAccessor());
|
||||
return ConvertDescriptorToField(name, value, attributes);
|
||||
}
|
||||
|
||||
Map* transition_map = Map::cast(transition);
|
||||
DescriptorArray* descriptors = transition_map->instance_descriptors();
|
||||
int descriptor = descriptors->LastAdded();
|
||||
PropertyDetails details = descriptors->GetDetails(descriptor);
|
||||
ASSERT(details.type() == FIELD || details.type() == CONSTANT_FUNCTION);
|
||||
|
||||
if (details.type() == FIELD) {
|
||||
if (attributes == details.attributes()) {
|
||||
@ -3005,10 +2994,14 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
||||
field_index);
|
||||
}
|
||||
return ConvertDescriptorToField(name, value, attributes);
|
||||
} else if (details.type() == CALLBACKS) {
|
||||
return ConvertDescriptorToField(name, value, attributes);
|
||||
}
|
||||
|
||||
// Was transition to CONSTANT_FUNCTION. Replace with a map transition to a
|
||||
// new map with a FIELD, even if the value is a function.
|
||||
ASSERT(details.type() == CONSTANT_FUNCTION);
|
||||
|
||||
// Replace transition to CONSTANT FUNCTION with a map transition to a new
|
||||
// map with a FIELD, even if the value is a function.
|
||||
return ConvertTransitionToMapTransition(
|
||||
result.GetTransitionIndex(), name, value, attributes);
|
||||
}
|
||||
@ -3294,11 +3287,6 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
|
||||
}
|
||||
case CALLBACKS: {
|
||||
Object* value = descs->GetCallbacksObject(i);
|
||||
if (value->IsAccessorPair()) {
|
||||
MaybeObject* maybe_copy =
|
||||
AccessorPair::cast(value)->CopyWithoutTransitions();
|
||||
if (!maybe_copy->To(&value)) return maybe_copy;
|
||||
}
|
||||
MaybeObject* maybe_dictionary =
|
||||
dictionary->Add(descs->GetKey(i), value, details);
|
||||
if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
|
||||
@ -4231,7 +4219,7 @@ void JSObject::LookupCallback(String* name, LookupResult* result) {
|
||||
current != heap->null_value() && current->IsJSObject();
|
||||
current = JSObject::cast(current)->GetPrototype()) {
|
||||
JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
|
||||
if (result->IsCallbacks()) return;
|
||||
if (result->IsPropertyCallbacks()) return;
|
||||
}
|
||||
result->NotFound();
|
||||
}
|
||||
@ -4343,7 +4331,7 @@ MaybeObject* JSObject::CreateAccessorPairFor(String* name) {
|
||||
// DefinePropertyAccessor below.
|
||||
Object* obj = result.GetCallbackObject();
|
||||
if (obj->IsAccessorPair()) {
|
||||
return AccessorPair::cast(obj)->CopyWithoutTransitions();
|
||||
return AccessorPair::cast(obj)->Copy();
|
||||
}
|
||||
}
|
||||
return GetHeap()->AllocateAccessorPair();
|
||||
@ -4377,9 +4365,9 @@ MaybeObject* JSObject::DefinePropertyAccessor(String* name,
|
||||
}
|
||||
|
||||
AccessorPair* accessors;
|
||||
{ MaybeObject* maybe_accessors = CreateAccessorPairFor(name);
|
||||
if (!maybe_accessors->To(&accessors)) return maybe_accessors;
|
||||
}
|
||||
MaybeObject* maybe_accessors = CreateAccessorPairFor(name);
|
||||
if (!maybe_accessors->To(&accessors)) return maybe_accessors;
|
||||
|
||||
accessors->SetComponents(getter, setter);
|
||||
return SetPropertyCallback(name, accessors, attributes);
|
||||
}
|
||||
@ -4451,17 +4439,16 @@ MaybeObject* JSObject::SetPropertyCallback(String* name,
|
||||
Object* structure,
|
||||
PropertyAttributes attributes) {
|
||||
// Normalize object to make this operation simple.
|
||||
{ MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
|
||||
if (maybe_ok->IsFailure()) return maybe_ok;
|
||||
}
|
||||
MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
|
||||
if (maybe_ok->IsFailure()) return maybe_ok;
|
||||
|
||||
// For the global object allocate a new map to invalidate the global inline
|
||||
// caches which have a global property cell reference directly in the code.
|
||||
if (IsGlobalObject()) {
|
||||
Map* new_map;
|
||||
{ MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
|
||||
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
||||
}
|
||||
MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
|
||||
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
||||
|
||||
set_map(new_map);
|
||||
// When running crankshaft, changing the map is not enough. We
|
||||
// need to deoptimize all functions that rely on this global
|
||||
@ -4471,9 +4458,8 @@ MaybeObject* JSObject::SetPropertyCallback(String* name,
|
||||
|
||||
// Update the dictionary with the new CALLBACKS property.
|
||||
PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
|
||||
{ MaybeObject* maybe_ok = SetNormalizedProperty(name, structure, details);
|
||||
if (maybe_ok->IsFailure()) return maybe_ok;
|
||||
}
|
||||
maybe_ok = SetNormalizedProperty(name, structure, details);
|
||||
if (maybe_ok->IsFailure()) return maybe_ok;
|
||||
|
||||
return GetHeap()->undefined_value();
|
||||
}
|
||||
@ -4525,73 +4511,32 @@ MaybeObject* JSObject::DefineAccessor(String* name,
|
||||
}
|
||||
|
||||
|
||||
static MaybeObject* CreateFreshAccessor(JSObject* obj,
|
||||
String* name,
|
||||
AccessorComponent component,
|
||||
Object* accessor,
|
||||
PropertyAttributes attributes) {
|
||||
// step 1: create a new getter/setter pair with only the accessor in it
|
||||
Heap* heap = obj->GetHeap();
|
||||
AccessorPair* accessors2;
|
||||
{ MaybeObject* maybe_accessors2 = heap->AllocateAccessorPair();
|
||||
if (!maybe_accessors2->To(&accessors2)) return maybe_accessors2;
|
||||
}
|
||||
accessors2->set(component, accessor);
|
||||
|
||||
// step 2: create a copy of the descriptors, incl. the new getter/setter pair
|
||||
Map* map1 = obj->map();
|
||||
CallbacksDescriptor callbacks_descr2(name, accessors2, attributes, 0);
|
||||
DescriptorArray* descriptors2;
|
||||
{ MaybeObject* maybe_descriptors2 =
|
||||
map1->instance_descriptors()->CopyAdd(&callbacks_descr2);
|
||||
if (!maybe_descriptors2->To(&descriptors2)) return maybe_descriptors2;
|
||||
}
|
||||
|
||||
// step 3: create a new map with the new descriptors
|
||||
Map* map2;
|
||||
{ MaybeObject* maybe_map2 = map1->CopyReplaceDescriptors(descriptors2);
|
||||
if (!maybe_map2->To(&map2)) return maybe_map2;
|
||||
}
|
||||
|
||||
// step 4: create a new getter/setter pair with a transition to the new map
|
||||
AccessorPair* accessors1;
|
||||
{ MaybeObject* maybe_accessors1 = heap->AllocateAccessorPair();
|
||||
if (!maybe_accessors1->To(&accessors1)) return maybe_accessors1;
|
||||
}
|
||||
accessors1->set(component, map2);
|
||||
|
||||
// step 5: create a copy of the descriptors, incl. the new getter/setter pair
|
||||
// with the transition
|
||||
TransitionArray* new_transitions;
|
||||
{ MaybeObject* maybe_new_transitions = map1->AddTransition(name, accessors1);
|
||||
if (!maybe_new_transitions->To(&new_transitions)) {
|
||||
return maybe_new_transitions;
|
||||
}
|
||||
}
|
||||
|
||||
// step 6: everything went well so far, so we make our changes visible
|
||||
{ MaybeObject* transition_added = map1->set_transitions(new_transitions);
|
||||
if (transition_added->IsFailure()) return transition_added;
|
||||
}
|
||||
|
||||
map2->SetBackPointer(map1);
|
||||
obj->set_map(map2);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
static bool TransitionToSameAccessor(Object* map,
|
||||
String* name,
|
||||
AccessorComponent component,
|
||||
Object* accessor,
|
||||
PropertyAttributes attributes ) {
|
||||
Map* transitioned_map = Map::cast(map);
|
||||
static MaybeObject* TryAccessorTransition(JSObject* self,
|
||||
Map* transitioned_map,
|
||||
String* name,
|
||||
AccessorComponent component,
|
||||
Object* accessor,
|
||||
PropertyAttributes attributes) {
|
||||
DescriptorArray* descs = transitioned_map->instance_descriptors();
|
||||
int number = descs->LastAdded();
|
||||
PropertyDetails details = descs->GetDetails(number);
|
||||
|
||||
// If the transition target was not callbacks, fall back to the slow case.
|
||||
if (details.type() != CALLBACKS) return self->GetHeap()->null_value();
|
||||
|
||||
Object* target_accessor =
|
||||
AccessorPair::cast(descs->GetCallbacksObject(number))->get(component);
|
||||
PropertyAttributes target_attributes = descs->GetDetails(number).attributes();
|
||||
return target_accessor == accessor && target_attributes == attributes;
|
||||
PropertyAttributes target_attributes = details.attributes();
|
||||
|
||||
// Reuse transition if adding same accessor with same attributes.
|
||||
if (target_accessor == accessor && target_attributes == attributes) {
|
||||
self->set_map(transitioned_map);
|
||||
return self;
|
||||
}
|
||||
|
||||
// If either not the same accessor, or not the same attributes, fall back to
|
||||
// the slow case.
|
||||
return self->GetHeap()->null_value();
|
||||
}
|
||||
|
||||
|
||||
@ -4600,43 +4545,31 @@ static MaybeObject* NewCallbackTransition(JSObject* obj,
|
||||
AccessorComponent component,
|
||||
Object* accessor,
|
||||
PropertyAttributes attributes,
|
||||
AccessorPair* accessors2) {
|
||||
// step 1: copy the old getter/setter pair and set the new accessor
|
||||
AccessorPair* accessors3;
|
||||
{ MaybeObject* maybe_accessors3 = accessors2->CopyWithoutTransitions();
|
||||
if (!maybe_accessors3->To(&accessors3)) return maybe_accessors3;
|
||||
}
|
||||
accessors3->set(component, accessor);
|
||||
AccessorPair* new_accessors) {
|
||||
// step 1: create a copy of the descriptors, incl. the new getter/setter pair
|
||||
Map* old_map = obj->map();
|
||||
CallbacksDescriptor new_accessors_desc(name, new_accessors, attributes);
|
||||
DescriptorArray* descriptors;
|
||||
MaybeObject* maybe_descriptors =
|
||||
old_map->instance_descriptors()->CopyInsert(&new_accessors_desc);
|
||||
if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors;
|
||||
|
||||
// step 2: create a copy of the descriptors, incl. the new getter/setter pair
|
||||
Map* map2 = obj->map();
|
||||
CallbacksDescriptor callbacks_descr3(name, accessors3, attributes, 0);
|
||||
DescriptorArray* descriptors3;
|
||||
{ MaybeObject* maybe_descriptors3 =
|
||||
map2->instance_descriptors()->CopyInsert(&callbacks_descr3);
|
||||
if (!maybe_descriptors3->To(&descriptors3)) return maybe_descriptors3;
|
||||
}
|
||||
// step 2: create a new map with the new descriptors
|
||||
Map* new_map;
|
||||
MaybeObject* maybe_new_map = old_map->CopyReplaceDescriptors(descriptors);
|
||||
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
||||
|
||||
// step 3: create a new map with the new descriptors
|
||||
Map* map3;
|
||||
{ MaybeObject* maybe_map3 = map2->CopyReplaceDescriptors(descriptors3);
|
||||
if (!maybe_map3->To(&map3)) return maybe_map3;
|
||||
}
|
||||
// step 3: add a new transition to the new map
|
||||
TransitionArray* transitions;
|
||||
MaybeObject* maybe_transitions = old_map->AddTransition(name, new_map);
|
||||
if (!maybe_transitions->To(&transitions)) return maybe_transitions;
|
||||
|
||||
// step 4: add a new transition to the new map
|
||||
TransitionArray* new_transitions;
|
||||
{ MaybeObject* maybe_transitions = map2->AddTransition(name, accessors2);
|
||||
if (!maybe_transitions->To(&new_transitions)) return maybe_transitions;
|
||||
}
|
||||
// step 4: everything went well so far, so we make our changes visible
|
||||
MaybeObject* transition_added = old_map->set_transitions(transitions);
|
||||
if (transition_added->IsFailure()) return transition_added;
|
||||
|
||||
// step 5: everything went well so far, so we make our changes visible
|
||||
{ MaybeObject* transition_added = map2->set_transitions(new_transitions);
|
||||
if (transition_added->IsFailure()) return transition_added;
|
||||
}
|
||||
|
||||
map3->SetBackPointer(map2);
|
||||
obj->set_map(map3);
|
||||
accessors2->set(component, map3);
|
||||
new_map->SetBackPointer(old_map);
|
||||
obj->set_map(new_map);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -4649,40 +4582,54 @@ MaybeObject* JSObject::DefineFastAccessor(String* name,
|
||||
LookupResult result(GetIsolate());
|
||||
LocalLookup(name, &result);
|
||||
|
||||
// If we have a new property, create a fresh accessor plus a transition to it.
|
||||
if (!result.IsFound()) {
|
||||
return CreateFreshAccessor(this, name, component, accessor, attributes);
|
||||
if (result.IsFound()
|
||||
&& !result.IsPropertyCallbacks()
|
||||
&& !result.IsTransition()) return GetHeap()->null_value();
|
||||
|
||||
// Return success if the same accessor with the same attributes already exist.
|
||||
AccessorPair* source_accessors = NULL;
|
||||
if (result.IsPropertyCallbacks()) {
|
||||
Object* callback_value = result.GetCallbackObject();
|
||||
if (callback_value->IsAccessorPair()) {
|
||||
source_accessors = AccessorPair::cast(callback_value);
|
||||
Object* entry = source_accessors->get(component);
|
||||
if (entry == accessor && result.GetAttributes() == attributes) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the property is not a JavaScript accessor, fall back to the slow case.
|
||||
if (!result.IsCallbacks()) return GetHeap()->null_value();
|
||||
// If not, lookup a transition.
|
||||
map()->LookupTransition(this, name, &result);
|
||||
|
||||
Object* callback_value = result.GetCallbackObject();
|
||||
if (!callback_value->IsAccessorPair()) return GetHeap()->null_value();
|
||||
AccessorPair* accessors = AccessorPair::cast(callback_value);
|
||||
|
||||
// Follow a callback transition, if there is a fitting one.
|
||||
Object* entry = accessors->get(component);
|
||||
if (entry->IsMap() &&
|
||||
TransitionToSameAccessor(entry, name, component, accessor, attributes)) {
|
||||
set_map(Map::cast(entry));
|
||||
return this;
|
||||
// If there is a transition, try to follow it.
|
||||
if (result.IsFound()) {
|
||||
Map* target = result.GetTransitionTarget();
|
||||
return TryAccessorTransition(
|
||||
this, target, name, component, accessor, attributes);
|
||||
}
|
||||
|
||||
if (entry == accessor && result.GetAttributes() == attributes) return this;
|
||||
// If there is no transition yet, add a transition to the a new accessor pair
|
||||
// containing the accessor.
|
||||
AccessorPair* accessors;
|
||||
MaybeObject* maybe_accessors;
|
||||
|
||||
// Only the other accessor has been set so far, create a new transition.
|
||||
if (entry->IsTheHole()) {
|
||||
return NewCallbackTransition(this,
|
||||
name,
|
||||
component,
|
||||
accessor,
|
||||
attributes,
|
||||
accessors);
|
||||
// Allocate a new pair if there were no source accessors. Otherwise, copy the
|
||||
// pair and modify the accessor.
|
||||
if (source_accessors != NULL) {
|
||||
maybe_accessors = source_accessors->Copy();
|
||||
} else {
|
||||
maybe_accessors = GetHeap()->AllocateAccessorPair();
|
||||
}
|
||||
if (!maybe_accessors->To(&accessors)) return maybe_accessors;
|
||||
accessors->set(component, accessor);
|
||||
|
||||
// Nothing from the above worked, so we have to fall back to the slow case.
|
||||
return GetHeap()->null_value();
|
||||
return NewCallbackTransition(this,
|
||||
name,
|
||||
component,
|
||||
accessor,
|
||||
attributes,
|
||||
accessors);
|
||||
}
|
||||
|
||||
|
||||
@ -5012,44 +4959,17 @@ class IntrusiveMapTransitionIterator {
|
||||
|
||||
Map* Next() {
|
||||
ASSERT(IsIterating());
|
||||
// Attention, tricky index manipulation ahead: Two consecutive indices are
|
||||
// assigned to each descriptor. Most descriptors directly advance to the
|
||||
// next descriptor by adding 2 to the index. The exceptions are the
|
||||
// CALLBACKS entries: An even index means we look at its getter, and an odd
|
||||
// index means we look at its setter.
|
||||
int raw_index = Smi::cast(*TransitionArrayHeader())->value();
|
||||
int index = raw_index / 2;
|
||||
int index = Smi::cast(*TransitionArrayHeader())->value();
|
||||
int number_of_transitions = transition_array_->number_of_transitions();
|
||||
while (index < number_of_transitions) {
|
||||
Object* value = transition_array_->GetValue(index);
|
||||
|
||||
if (value->IsMap()) {
|
||||
*TransitionArrayHeader() = Smi::FromInt(raw_index + 2);
|
||||
return static_cast<Map*>(value);
|
||||
}
|
||||
|
||||
ASSERT(value->IsAccessorPair());
|
||||
|
||||
// We might have a map transition in a getter or in a setter.
|
||||
AccessorPair* accessors = static_cast<AccessorPair*>(value);
|
||||
Object* accessor;
|
||||
if ((raw_index & 1) == 0) {
|
||||
accessor = accessors->setter();
|
||||
} else {
|
||||
++index;
|
||||
accessor = accessors->getter();
|
||||
}
|
||||
++raw_index;
|
||||
if (accessor->IsMap()) {
|
||||
*TransitionArrayHeader() = Smi::FromInt(raw_index);
|
||||
return static_cast<Map*>(accessor);
|
||||
}
|
||||
*TransitionArrayHeader() = Smi::FromInt(index + 1);
|
||||
return transition_array_->GetTarget(index);
|
||||
}
|
||||
|
||||
if (index == transition_array_->number_of_transitions() &&
|
||||
if (index == number_of_transitions &&
|
||||
transition_array_->HasElementsTransition()) {
|
||||
Map* elements_transition = transition_array_->elements_transition();
|
||||
*TransitionArrayHeader() = Smi::FromInt(raw_index + 2);
|
||||
*TransitionArrayHeader() = Smi::FromInt(index + 1);
|
||||
return elements_transition;
|
||||
}
|
||||
*TransitionArrayHeader() = transition_array_->GetHeap()->fixed_array_map();
|
||||
@ -5786,30 +5706,14 @@ static bool InsertionPointFound(String* key1, String* key2) {
|
||||
}
|
||||
|
||||
|
||||
void DescriptorArray::CopyFrom(Handle<DescriptorArray> dst,
|
||||
int dst_index,
|
||||
Handle<DescriptorArray> src,
|
||||
void DescriptorArray::CopyFrom(int dst_index,
|
||||
DescriptorArray* src,
|
||||
int src_index,
|
||||
const WhitenessWitness& witness) {
|
||||
CALL_HEAP_FUNCTION_VOID(dst->GetIsolate(),
|
||||
dst->CopyFrom(dst_index, *src, src_index, witness));
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* DescriptorArray::CopyFrom(int dst_index,
|
||||
DescriptorArray* src,
|
||||
int src_index,
|
||||
const WhitenessWitness& witness) {
|
||||
Object* value = src->GetValue(src_index);
|
||||
PropertyDetails details = src->GetDetails(src_index);
|
||||
if (details.type() == CALLBACKS && value->IsAccessorPair()) {
|
||||
MaybeObject* maybe_copy =
|
||||
AccessorPair::cast(value)->CopyWithoutTransitions();
|
||||
if (!maybe_copy->To(&value)) return maybe_copy;
|
||||
}
|
||||
Descriptor desc(src->GetKey(src_index), value, details);
|
||||
Set(dst_index, &desc, witness);
|
||||
return this;
|
||||
}
|
||||
|
||||
MaybeObject* DescriptorArray::CopyReplace(Descriptor* descriptor,
|
||||
@ -5833,9 +5737,7 @@ MaybeObject* DescriptorArray::CopyReplace(Descriptor* descriptor,
|
||||
// Copy the descriptors, replacing a descriptor.
|
||||
for (int index = 0; index < size; ++index) {
|
||||
if (index == insertion_index) continue;
|
||||
MaybeObject* copy_result =
|
||||
new_descriptors->CopyFrom(index, this, index, witness);
|
||||
if (copy_result->IsFailure()) return copy_result;
|
||||
new_descriptors->CopyFrom(index, this, index, witness);
|
||||
}
|
||||
|
||||
descriptor->SetEnumerationIndex(GetDetails(insertion_index).index());
|
||||
@ -5850,9 +5752,8 @@ MaybeObject* DescriptorArray::CopyReplace(Descriptor* descriptor,
|
||||
|
||||
MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor) {
|
||||
// Ensure the key is a symbol.
|
||||
{ MaybeObject* maybe_result = descriptor->KeyToSymbol();
|
||||
if (maybe_result->IsFailure()) return maybe_result;
|
||||
}
|
||||
MaybeObject* maybe_result = descriptor->KeyToSymbol();
|
||||
if (maybe_result->IsFailure()) return maybe_result;
|
||||
|
||||
// We replace the key if it is already present.
|
||||
int index = SearchWithCache(descriptor->GetKey());
|
||||
@ -5863,9 +5764,8 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor) {
|
||||
|
||||
MaybeObject* DescriptorArray::CopyAdd(Descriptor* descriptor) {
|
||||
// Ensure the key is a symbol.
|
||||
{ MaybeObject* maybe_result = descriptor->KeyToSymbol();
|
||||
if (maybe_result->IsFailure()) return maybe_result;
|
||||
}
|
||||
MaybeObject* maybe_result = descriptor->KeyToSymbol();
|
||||
if (maybe_result->IsFailure()) return maybe_result;
|
||||
|
||||
String* key = descriptor->GetKey();
|
||||
ASSERT(Search(key) == kNotFound);
|
||||
@ -5873,9 +5773,8 @@ MaybeObject* DescriptorArray::CopyAdd(Descriptor* descriptor) {
|
||||
int new_size = number_of_descriptors() + 1;
|
||||
|
||||
DescriptorArray* new_descriptors;
|
||||
{ MaybeObject* maybe_result = Allocate(new_size, MAY_BE_SHARED);
|
||||
if (!maybe_result->To(&new_descriptors)) return maybe_result;
|
||||
}
|
||||
MaybeObject* maybe_descriptors = Allocate(new_size, MAY_BE_SHARED);
|
||||
if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
|
||||
|
||||
FixedArray::WhitenessWitness witness(new_descriptors);
|
||||
|
||||
@ -5886,9 +5785,7 @@ MaybeObject* DescriptorArray::CopyAdd(Descriptor* descriptor) {
|
||||
if (insertion_index < 0 && InsertionPointFound(GetKey(from), key)) {
|
||||
insertion_index = to++;
|
||||
}
|
||||
MaybeObject* copy_result =
|
||||
new_descriptors->CopyFrom(to++, this, from, witness);
|
||||
if (copy_result->IsFailure()) return copy_result;
|
||||
new_descriptors->CopyFrom(to++, this, from, witness);
|
||||
}
|
||||
if (insertion_index < 0) insertion_index = to++;
|
||||
|
||||
@ -5909,18 +5806,14 @@ MaybeObject* DescriptorArray::Copy(SharedMode shared_mode) {
|
||||
// Allocate the new descriptor array.
|
||||
int number_of_descriptors = this->number_of_descriptors();
|
||||
DescriptorArray* new_descriptors;
|
||||
{ MaybeObject* maybe_result = Allocate(number_of_descriptors,
|
||||
shared_mode);
|
||||
if (!maybe_result->To(&new_descriptors)) return maybe_result;
|
||||
}
|
||||
MaybeObject* maybe_result = Allocate(number_of_descriptors, shared_mode);
|
||||
if (!maybe_result->To(&new_descriptors)) return maybe_result;
|
||||
|
||||
// Copy the content.
|
||||
if (number_of_descriptors > 0) {
|
||||
FixedArray::WhitenessWitness witness(new_descriptors);
|
||||
for (int i = 0; i < number_of_descriptors; i++) {
|
||||
MaybeObject* copy_result =
|
||||
new_descriptors->CopyFrom(i, this, i, witness);
|
||||
if (copy_result->IsFailure()) return copy_result;
|
||||
new_descriptors->CopyFrom(i, this, i, witness);
|
||||
}
|
||||
new_descriptors->SetLastAdded(LastAdded());
|
||||
}
|
||||
@ -6020,14 +5913,14 @@ void DescriptorArray::Sort(const WhitenessWitness& witness) {
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* AccessorPair::CopyWithoutTransitions() {
|
||||
MaybeObject* AccessorPair::Copy() {
|
||||
Heap* heap = GetHeap();
|
||||
AccessorPair* copy;
|
||||
{ MaybeObject* maybe_copy = heap->AllocateAccessorPair();
|
||||
if (!maybe_copy->To(©)) return maybe_copy;
|
||||
}
|
||||
copy->set_getter(getter()->IsMap() ? heap->the_hole_value() : getter());
|
||||
copy->set_setter(setter()->IsMap() ? heap->the_hole_value() : setter());
|
||||
MaybeObject* maybe_copy = heap->AllocateAccessorPair();
|
||||
if (!maybe_copy->To(©)) return maybe_copy;
|
||||
|
||||
copy->set_getter(getter());
|
||||
copy->set_setter(setter());
|
||||
return copy;
|
||||
}
|
||||
|
||||
@ -7313,40 +7206,6 @@ static bool ClearBackPointer(Heap* heap, Object* target) {
|
||||
}
|
||||
|
||||
|
||||
static bool ClearAccessorComponent(Heap* heap,
|
||||
AccessorPair* accessors,
|
||||
AccessorComponent component) {
|
||||
Object* component_value = accessors->get(component);
|
||||
if (!component_value->IsMap()) return true;
|
||||
if (ClearBackPointer(heap, component_value)) {
|
||||
accessors->set(component, heap->the_hole_value());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool ClearNonLiveTransition(Heap* heap,
|
||||
TransitionArray* t,
|
||||
int transition_index) {
|
||||
// If the value is a map, check if the target is live. If not, clear the
|
||||
// transition. Also drop the back pointer for that map transition, so that
|
||||
// this map is not reached again by following a back pointer from that
|
||||
// non-live map.
|
||||
Object* value = t->GetValue(transition_index);
|
||||
if (value->IsMap()) {
|
||||
return ClearBackPointer(heap, t->GetValue(transition_index));
|
||||
}
|
||||
|
||||
ASSERT(value->IsAccessorPair());
|
||||
|
||||
AccessorPair* accessors = AccessorPair::cast(value);
|
||||
bool getter = ClearAccessorComponent(heap, accessors, ACCESSOR_GETTER);
|
||||
bool setter = ClearAccessorComponent(heap, accessors, ACCESSOR_SETTER);
|
||||
return getter && setter;
|
||||
}
|
||||
|
||||
|
||||
// TODO(mstarzinger): This method should be moved into MarkCompactCollector,
|
||||
// because it cannot be called from outside the GC and we already have methods
|
||||
// depending on the transitions layout in the GC anyways.
|
||||
@ -7362,17 +7221,17 @@ void Map::ClearNonLiveTransitions(Heap* heap) {
|
||||
|
||||
// Compact all live descriptors to the left.
|
||||
for (int i = 0; i < t->number_of_transitions(); ++i) {
|
||||
if (!ClearNonLiveTransition(heap, t, i)) {
|
||||
if (!ClearBackPointer(heap, t->GetTarget(i))) {
|
||||
if (i != transition_index) {
|
||||
String* key = t->GetKey(i);
|
||||
Object* value = t->GetValue(i);
|
||||
Map* target = t->GetTarget(i);
|
||||
t->SetKey(transition_index, key);
|
||||
t->SetValue(transition_index, value);
|
||||
t->SetTarget(transition_index, target);
|
||||
MarkCompactCollector* collector = heap->mark_compact_collector();
|
||||
Object** key_slot = t->GetKeySlot(transition_index);
|
||||
collector->RecordSlot(key_slot, key_slot, key);
|
||||
Object** value_slot = t->GetValueSlot(transition_index);
|
||||
collector->RecordSlot(value_slot, value_slot, value);
|
||||
Object** target_slot = t->GetTargetSlot(transition_index);
|
||||
collector->RecordSlot(target_slot, target_slot, target);
|
||||
}
|
||||
transition_index++;
|
||||
}
|
||||
@ -10472,7 +10331,7 @@ bool JSObject::HasRealNamedCallbackProperty(String* key) {
|
||||
|
||||
LookupResult result(isolate);
|
||||
LocalLookupRealNamedProperty(key, &result);
|
||||
return result.IsCallbacks();
|
||||
return result.IsPropertyCallbacks();
|
||||
}
|
||||
|
||||
|
||||
@ -12696,15 +12555,15 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
|
||||
if (IsKey(k)) {
|
||||
Object* value = ValueAt(i);
|
||||
// Ensure the key is a symbol before writing into the instance descriptor.
|
||||
Object* key;
|
||||
{ MaybeObject* maybe_key = heap->LookupSymbol(String::cast(k));
|
||||
if (!maybe_key->ToObject(&key)) return maybe_key;
|
||||
}
|
||||
String* key;
|
||||
MaybeObject* maybe_key = heap->LookupSymbol(String::cast(k));
|
||||
if (!maybe_key->To(&key)) return maybe_key;
|
||||
|
||||
PropertyDetails details = DetailsAt(i);
|
||||
PropertyType type = details.type();
|
||||
|
||||
if (value->IsJSFunction() && !heap->InNewSpace(value)) {
|
||||
ConstantFunctionDescriptor d(String::cast(key),
|
||||
ConstantFunctionDescriptor d(key,
|
||||
JSFunction::cast(value),
|
||||
details.attributes(),
|
||||
details.index());
|
||||
@ -12718,18 +12577,13 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
|
||||
int offset = current_offset - inobject_props;
|
||||
FixedArray::cast(fields)->set(offset, value);
|
||||
}
|
||||
FieldDescriptor d(String::cast(key),
|
||||
FieldDescriptor d(key,
|
||||
current_offset++,
|
||||
details.attributes(),
|
||||
details.index());
|
||||
descriptors->Set(next_descriptor, &d, witness);
|
||||
} else if (type == CALLBACKS) {
|
||||
if (value->IsAccessorPair()) {
|
||||
MaybeObject* maybe_copy =
|
||||
AccessorPair::cast(value)->CopyWithoutTransitions();
|
||||
if (!maybe_copy->To(&value)) return maybe_copy;
|
||||
}
|
||||
CallbacksDescriptor d(String::cast(key),
|
||||
CallbacksDescriptor d(key,
|
||||
value,
|
||||
details.attributes(),
|
||||
details.index());
|
||||
@ -12745,10 +12599,8 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
|
||||
descriptors->Sort(witness);
|
||||
// Allocate new map.
|
||||
Map* new_map;
|
||||
{ MaybeObject* maybe_new_map =
|
||||
obj->map()->CopyReplaceDescriptors(descriptors);
|
||||
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
||||
}
|
||||
MaybeObject* maybe_new_map = obj->map()->CopyReplaceDescriptors(descriptors);
|
||||
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
||||
|
||||
new_map->set_unused_property_fields(unused_property_fields);
|
||||
|
||||
|
@ -2548,20 +2548,12 @@ class DescriptorArray: public FixedArray {
|
||||
inline void Append(Descriptor* desc,
|
||||
const WhitenessWitness&);
|
||||
|
||||
// Transfer a complete descriptor from the src descriptor array to the dst
|
||||
// one, dropping map transitions in CALLBACKS.
|
||||
static void CopyFrom(Handle<DescriptorArray> dst,
|
||||
int dst_index,
|
||||
Handle<DescriptorArray> src,
|
||||
int src_index,
|
||||
const WhitenessWitness& witness);
|
||||
|
||||
// Transfer a complete descriptor from the src descriptor array to this
|
||||
// descriptor array, dropping map transitions in CALLBACKS.
|
||||
MUST_USE_RESULT MaybeObject* CopyFrom(int dst_index,
|
||||
DescriptorArray* src,
|
||||
int src_index,
|
||||
const WhitenessWitness&);
|
||||
// descriptor array.
|
||||
void CopyFrom(int dst_index,
|
||||
DescriptorArray* src,
|
||||
int src_index,
|
||||
const WhitenessWitness&);
|
||||
|
||||
// Copy the descriptor array, insert a new descriptor and optionally
|
||||
// remove map transitions. If the descriptor is already present, it is
|
||||
@ -4815,8 +4807,8 @@ class Map: public HeapObject {
|
||||
MUST_USE_RESULT inline MaybeObject* set_elements_transition_map(
|
||||
Map* transitioned_map);
|
||||
inline TransitionArray* transitions();
|
||||
inline void SetTransition(int index, Object* value);
|
||||
MUST_USE_RESULT inline MaybeObject* AddTransition(String* key, Object* value);
|
||||
inline void SetTransition(int index, Map* target);
|
||||
MUST_USE_RESULT inline MaybeObject* AddTransition(String* key, Map* target);
|
||||
MUST_USE_RESULT inline MaybeObject* set_transitions(
|
||||
TransitionArray* transitions);
|
||||
inline void ClearTransitions(Heap* heap,
|
||||
@ -8347,7 +8339,7 @@ class AccessorPair: public Struct {
|
||||
|
||||
static inline AccessorPair* cast(Object* obj);
|
||||
|
||||
MUST_USE_RESULT MaybeObject* CopyWithoutTransitions();
|
||||
MUST_USE_RESULT MaybeObject* Copy();
|
||||
|
||||
Object* get(AccessorComponent component) {
|
||||
return component == ACCESSOR_GETTER ? getter() : setter();
|
||||
|
@ -236,12 +236,6 @@ class LookupResult BASE_EMBEDDED {
|
||||
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());
|
||||
@ -299,11 +293,10 @@ class LookupResult BASE_EMBEDDED {
|
||||
}
|
||||
}
|
||||
|
||||
Object* GetTransitionValue() {
|
||||
Map* GetTransitionTarget() {
|
||||
ASSERT(IsTransition());
|
||||
TransitionArray* transitions = holder()->map()->transitions();
|
||||
Object* value = transitions->GetValue(number_);
|
||||
return value;
|
||||
return transitions->GetTarget(number_);
|
||||
}
|
||||
|
||||
PropertyDetails GetTransitionDetails(Map* map) {
|
||||
@ -327,7 +320,7 @@ class LookupResult BASE_EMBEDDED {
|
||||
|
||||
Map* GetTransitionMapFromMap(Map* map) {
|
||||
ASSERT(IsTransition());
|
||||
return Map::cast(map->transitions()->GetValue(number_));
|
||||
return map->transitions()->GetTarget(number_);
|
||||
}
|
||||
|
||||
int GetTransitionIndex() {
|
||||
@ -363,14 +356,11 @@ class LookupResult BASE_EMBEDDED {
|
||||
}
|
||||
|
||||
Object* GetCallbackObject() {
|
||||
switch (lookup_type_) {
|
||||
case CONSTANT_TYPE:
|
||||
return HEAP->prototype_accessors();
|
||||
case TRANSITION_TYPE:
|
||||
return GetTransitionValue();
|
||||
default:
|
||||
return GetValue();
|
||||
if (lookup_type_ == CONSTANT_TYPE) {
|
||||
return HEAP->prototype_accessors();
|
||||
}
|
||||
ASSERT(!IsTransition());
|
||||
return GetValue();
|
||||
}
|
||||
|
||||
#ifdef OBJECT_PRINT
|
||||
|
@ -1530,7 +1530,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareContextSlot) {
|
||||
!object->IsJSContextExtensionObject()) {
|
||||
LookupResult lookup(isolate);
|
||||
object->Lookup(*name, &lookup);
|
||||
if (lookup.IsCallbacks()) {
|
||||
if (lookup.IsPropertyCallbacks()) {
|
||||
return ThrowRedeclarationError(isolate, "const", name);
|
||||
}
|
||||
}
|
||||
@ -4531,7 +4531,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) {
|
||||
js_object->LocalLookupRealNamedProperty(*name, &result);
|
||||
|
||||
// Special case for callback properties.
|
||||
if (result.IsCallbacks()) {
|
||||
if (result.IsPropertyCallbacks()) {
|
||||
Object* callback = result.GetCallbackObject();
|
||||
// To be compatible with Safari we do not change the value on API objects
|
||||
// in Object.defineProperty(). Firefox disagrees here, and actually changes
|
||||
|
@ -138,44 +138,28 @@ void TransitionArray::SetKey(int transition_number, String* key) {
|
||||
}
|
||||
|
||||
|
||||
Object* TransitionArray::GetValue(int transition_number) {
|
||||
Map* TransitionArray::GetTarget(int transition_number) {
|
||||
ASSERT(transition_number < number_of_transitions());
|
||||
return get(ToValueIndex(transition_number));
|
||||
return Map::cast(get(ToTargetIndex(transition_number)));
|
||||
}
|
||||
|
||||
|
||||
Object** TransitionArray::GetValueSlot(int transition_number) {
|
||||
Object** TransitionArray::GetTargetSlot(int transition_number) {
|
||||
ASSERT(transition_number < number_of_transitions());
|
||||
return HeapObject::RawField(
|
||||
reinterpret_cast<HeapObject*>(this),
|
||||
OffsetOfElementAt(ToValueIndex(transition_number)));
|
||||
OffsetOfElementAt(ToTargetIndex(transition_number)));
|
||||
}
|
||||
|
||||
|
||||
void TransitionArray::SetValue(int transition_number, Object* value) {
|
||||
void TransitionArray::SetTarget(int transition_number, Map* 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);
|
||||
set(ToTargetIndex(transition_number), value);
|
||||
}
|
||||
|
||||
|
||||
PropertyDetails TransitionArray::GetTargetDetails(int transition_number) {
|
||||
Map* map = GetTargetMap(transition_number);
|
||||
Map* map = GetTarget(transition_number);
|
||||
DescriptorArray* descriptors = map->instance_descriptors();
|
||||
int descriptor = descriptors->LastAdded();
|
||||
ASSERT(descriptor != DescriptorArray::kNotFound);
|
||||
@ -196,14 +180,14 @@ int TransitionArray::Search(String* name) {
|
||||
|
||||
void TransitionArray::Set(int transition_number,
|
||||
String* key,
|
||||
Object* value,
|
||||
Map* target,
|
||||
const WhitenessWitness&) {
|
||||
NoIncrementalWriteBarrierSet(this,
|
||||
ToKeyIndex(transition_number),
|
||||
key);
|
||||
NoIncrementalWriteBarrierSet(this,
|
||||
ToValueIndex(transition_number),
|
||||
value);
|
||||
ToTargetIndex(transition_number),
|
||||
target);
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,10 +54,10 @@ 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);
|
||||
Set(target_transition,
|
||||
origin->GetKey(origin_transition),
|
||||
origin->GetTarget(origin_transition),
|
||||
witness);
|
||||
}
|
||||
|
||||
|
||||
@ -66,7 +66,7 @@ static bool InsertionPointFound(String* key1, String* key2) {
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* TransitionArray::NewWith(String* name, Object* value) {
|
||||
MaybeObject* TransitionArray::NewWith(String* name, Map* target) {
|
||||
TransitionArray* result;
|
||||
|
||||
{ MaybeObject* maybe_array;
|
||||
@ -76,12 +76,12 @@ MaybeObject* TransitionArray::NewWith(String* name, Object* value) {
|
||||
|
||||
FixedArray::WhitenessWitness witness(result);
|
||||
|
||||
result->Set(0, name, value, witness);
|
||||
result->Set(0, name, target, witness);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* TransitionArray::CopyInsert(String* name, Object* value) {
|
||||
MaybeObject* TransitionArray::CopyInsert(String* name, Map* target) {
|
||||
TransitionArray* result;
|
||||
|
||||
int number_of_transitions = this->number_of_transitions();
|
||||
@ -90,10 +90,9 @@ MaybeObject* TransitionArray::CopyInsert(String* name, Object* value) {
|
||||
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;
|
||||
}
|
||||
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());
|
||||
@ -109,7 +108,7 @@ MaybeObject* TransitionArray::CopyInsert(String* name, Object* value) {
|
||||
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);
|
||||
result->Set(insertion_index, name, target, witness);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -119,7 +118,7 @@ MaybeObject* TransitionArray::CopyInsert(String* name, Object* value) {
|
||||
result->CopyFrom(this, insertion_index, insertion_index, witness);
|
||||
}
|
||||
|
||||
result->Set(insertion_index, name, value, witness);
|
||||
result->Set(insertion_index, name, target, witness);
|
||||
|
||||
for (; insertion_index < number_of_transitions; ++insertion_index) {
|
||||
result->CopyFrom(this, insertion_index, insertion_index + 1, witness);
|
||||
|
@ -51,16 +51,15 @@ class TransitionArray: public FixedArray {
|
||||
inline void SetKey(int transition_number, String* value);
|
||||
inline Object** GetKeySlot(int transition_number);
|
||||
|
||||
inline Object* GetValue(int transition_number);
|
||||
inline void SetValue(int transition_number, Object* value);
|
||||
inline Object** GetValueSlot(int transition_number);
|
||||
inline Map* GetTarget(int transition_number);
|
||||
inline void SetTarget(int transition_number, Map* target);
|
||||
inline Object** GetTargetSlot(int transition_number);
|
||||
|
||||
inline Map* GetTargetMap(int transition_number);
|
||||
inline PropertyDetails GetTargetDetails(int transition_number);
|
||||
|
||||
inline Map* elements_transition();
|
||||
inline void set_elements_transition(
|
||||
Map* value,
|
||||
Map* target,
|
||||
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
|
||||
inline Object** GetElementsTransitionSlot();
|
||||
inline bool HasElementsTransition();
|
||||
@ -84,12 +83,12 @@ class TransitionArray: public FixedArray {
|
||||
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);
|
||||
static MUST_USE_RESULT MaybeObject* NewWith(String* name, Map* target);
|
||||
|
||||
// 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);
|
||||
MUST_USE_RESULT MaybeObject* CopyInsert(String* name, Map* target);
|
||||
|
||||
// Copy a single transition from the origin array.
|
||||
inline void CopyFrom(TransitionArray* origin,
|
||||
@ -121,7 +120,7 @@ class TransitionArray: public FixedArray {
|
||||
|
||||
// Layout of map transition.
|
||||
static const int kTransitionKey = 0;
|
||||
static const int kTransitionValue = 1;
|
||||
static const int kTransitionTarget = 1;
|
||||
static const int kTransitionSize = 2;
|
||||
|
||||
#ifdef OBJECT_PRINT
|
||||
@ -150,15 +149,15 @@ class TransitionArray: public FixedArray {
|
||||
kTransitionKey;
|
||||
}
|
||||
|
||||
static int ToValueIndex(int transition_number) {
|
||||
static int ToTargetIndex(int transition_number) {
|
||||
return kFirstIndex +
|
||||
(transition_number * kTransitionSize) +
|
||||
kTransitionValue;
|
||||
kTransitionTarget;
|
||||
}
|
||||
|
||||
inline void Set(int transition_number,
|
||||
String* key,
|
||||
Object* value,
|
||||
Map* target,
|
||||
const WhitenessWitness&);
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray);
|
||||
|
Loading…
Reference in New Issue
Block a user