| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index 57483177854d3928d838967bf9c8a98adc7f6a54..03f5e553a4bf65981fc10858d904a1499947224e 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -416,8 +416,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
|
| }
|
|
|
| case HANDLER:
|
| - case MAP_TRANSITION:
|
| - case CONSTANT_TRANSITION:
|
| + case TRANSITION:
|
| case NONEXISTENT:
|
| UNREACHABLE();
|
| }
|
| @@ -641,9 +640,7 @@ MaybeObject* Object::GetProperty(Object* receiver,
|
| return result->holder()->GetPropertyWithInterceptor(
|
| recvr, name, attributes);
|
| }
|
| - case MAP_TRANSITION:
|
| - case CONSTANT_TRANSITION:
|
| - break;
|
| + case TRANSITION:
|
| case NONEXISTENT:
|
| UNREACHABLE();
|
| break;
|
| @@ -1492,20 +1489,19 @@ String* JSReceiver::constructor_name() {
|
|
|
| MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
|
| String* name,
|
| - Object* value) {
|
| - int index = new_map->PropertyIndexFor(name);
|
| + Object* value,
|
| + int field_index) {
|
| if (map()->unused_property_fields() == 0) {
|
| - ASSERT(map()->unused_property_fields() == 0);
|
| int new_unused = new_map->unused_property_fields();
|
| - Object* values;
|
| + FixedArray* values;
|
| { MaybeObject* maybe_values =
|
| properties()->CopySize(properties()->length() + new_unused + 1);
|
| - if (!maybe_values->ToObject(&values)) return maybe_values;
|
| + if (!maybe_values->To(&values)) return maybe_values;
|
| }
|
| - set_properties(FixedArray::cast(values));
|
| + set_properties(values);
|
| }
|
| set_map(new_map);
|
| - return FastPropertyAtPut(index, value);
|
| + return FastPropertyAtPut(field_index, value);
|
| }
|
|
|
|
|
| @@ -1551,10 +1547,10 @@ MaybeObject* JSObject::AddFastProperty(String* name,
|
|
|
| // Allocate new instance descriptors with (name, index) added
|
| FieldDescriptor new_field(name, index, attributes);
|
| - Object* new_descriptors;
|
| + DescriptorArray* new_descriptors;
|
| { MaybeObject* maybe_new_descriptors =
|
| - old_descriptors->CopyInsert(&new_field, REMOVE_TRANSITIONS);
|
| - if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
|
| + old_descriptors->CopyInsert(&new_field);
|
| + if (!maybe_new_descriptors->To(&new_descriptors)) {
|
| return maybe_new_descriptors;
|
| }
|
| }
|
| @@ -1562,7 +1558,7 @@ MaybeObject* JSObject::AddFastProperty(String* name,
|
| // Only allow map transition if the object isn't the global object and there
|
| // is not a transition for the name, or there's a transition for the name but
|
| // it's unrelated to properties.
|
| - int descriptor_index = old_descriptors->Search(name);
|
| + int descriptor_index = old_descriptors->SearchWithCache(name);
|
|
|
| // Element transitions are stored in the descriptor for property "", which is
|
| // not a identifier and should have forced a switch to slow properties above.
|
| @@ -1580,14 +1576,13 @@ MaybeObject* JSObject::AddFastProperty(String* name,
|
| if (!maybe_r->ToObject(&r)) return maybe_r;
|
| }
|
| Map* new_map = Map::cast(r);
|
| +
|
| + TransitionArray* new_transitions = NULL;
|
| if (allow_map_transition) {
|
| - // Allocate new instance descriptors for the old map with map transition.
|
| - MapTransitionDescriptor d(name, Map::cast(new_map), attributes);
|
| - Object* r;
|
| - { MaybeObject* maybe_r = old_descriptors->CopyInsert(&d, KEEP_TRANSITIONS);
|
| - if (!maybe_r->ToObject(&r)) return maybe_r;
|
| + MaybeObject* maybe_new_transitions = map()->AddTransition(name, new_map);
|
| + if (!maybe_new_transitions->To(&new_transitions)) {
|
| + return maybe_new_transitions;
|
| }
|
| - old_descriptors = DescriptorArray::cast(r);
|
| }
|
|
|
| if (map()->unused_property_fields() == 0) {
|
| @@ -1610,13 +1605,13 @@ MaybeObject* JSObject::AddFastProperty(String* name,
|
| } else {
|
| new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
|
| }
|
| - // We have now allocated all the necessary objects.
|
| - // All the changes can be applied at once, so they are atomic.
|
| + // Apply all changes at once, so they are atomic.
|
| if (allow_map_transition) {
|
| - map()->set_instance_descriptors(old_descriptors);
|
| + MaybeObject* transition_added = map()->set_transitions(new_transitions);
|
| + if (transition_added->IsFailure()) return transition_added;
|
| }
|
| + new_map->set_instance_descriptors(new_descriptors);
|
| new_map->SetBackPointer(map());
|
| - new_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
|
| set_map(new_map);
|
| return FastPropertyAtPut(index, value);
|
| }
|
| @@ -1628,24 +1623,23 @@ MaybeObject* JSObject::AddConstantFunctionProperty(
|
| PropertyAttributes attributes) {
|
| // Allocate new instance descriptors with (name, function) added
|
| ConstantFunctionDescriptor d(name, function, attributes);
|
| - Object* new_descriptors;
|
| + DescriptorArray* new_descriptors;
|
| { MaybeObject* maybe_new_descriptors =
|
| - map()->instance_descriptors()->CopyInsert(&d, REMOVE_TRANSITIONS);
|
| - if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
|
| + map()->instance_descriptors()->CopyInsert(&d);
|
| + if (!maybe_new_descriptors->To(&new_descriptors)) {
|
| return maybe_new_descriptors;
|
| }
|
| }
|
|
|
| // Allocate a new map for the object.
|
| - Object* new_map;
|
| + Map* new_map;
|
| { MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
|
| - if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
|
| + if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
| }
|
|
|
| - DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
|
| - Map::cast(new_map)->set_instance_descriptors(descriptors);
|
| + new_map->set_instance_descriptors(new_descriptors);
|
| Map* old_map = map();
|
| - set_map(Map::cast(new_map));
|
| + set_map(new_map);
|
|
|
| // If the old map is the global object map (from new Object()),
|
| // then transitions are not added to it, so we are done.
|
| @@ -1655,29 +1649,36 @@ MaybeObject* JSObject::AddConstantFunctionProperty(
|
| return function;
|
| }
|
|
|
| - // Do not add CONSTANT_TRANSITIONS to global objects
|
| + // Do not add constant transitions to global objects
|
| if (IsGlobalObject()) {
|
| return function;
|
| }
|
|
|
| - // Add a CONSTANT_TRANSITION descriptor to the old map,
|
| - // so future assignments to this property on other objects
|
| - // of the same type will create a normal field, not a constant function.
|
| - // Don't do this for special properties, with non-trival attributes.
|
| + // Add a constant transition to the old map, so future assignments to this
|
| + // property on other objects of the same type will create a normal field, not
|
| + // a constant function. Don't do this for special properties, with non-trival
|
| + // attributes.
|
| if (attributes != NONE) {
|
| return function;
|
| }
|
| - ConstTransitionDescriptor mark(name, Map::cast(new_map));
|
| - { MaybeObject* maybe_new_descriptors =
|
| - old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
|
| - if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
|
| +
|
| + TransitionArray* new_transitions;
|
| + { MaybeObject* maybe_new_transitions =
|
| + old_map->AddTransition(name, new_map);
|
| + if (!maybe_new_transitions->To(&new_transitions)) {
|
| // We have accomplished the main goal, so return success.
|
| return function;
|
| }
|
| }
|
| - old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
|
| - Map::cast(new_map)->SetBackPointer(old_map);
|
|
|
| + { MaybeObject* transition_added = old_map->set_transitions(new_transitions);
|
| + // We have accomplished the main goal, so return success.
|
| + if (transition_added->IsFailure()) {
|
| + return function;
|
| + }
|
| + }
|
| +
|
| + new_map->SetBackPointer(old_map);
|
| return function;
|
| }
|
|
|
| @@ -1798,7 +1799,6 @@ MaybeObject* JSObject::ReplaceSlowProperty(String* name,
|
| int new_enumeration_index = 0; // 0 means "Use the next available index."
|
| if (old_index != -1) {
|
| // All calls to ReplaceSlowProperty have had all transitions removed.
|
| - ASSERT(!dictionary->ContainsTransition(old_index));
|
| new_enumeration_index = dictionary->DetailsAt(old_index).index();
|
| }
|
|
|
| @@ -1813,33 +1813,40 @@ MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition(
|
| PropertyAttributes attributes) {
|
| Map* old_map = map();
|
| Object* result;
|
| +
|
| { MaybeObject* maybe_result =
|
| ConvertDescriptorToField(name, new_value, attributes);
|
| - if (!maybe_result->ToObject(&result)) return maybe_result;
|
| + if (!maybe_result->To(&result)) return maybe_result;
|
| }
|
| +
|
| // If we get to this point we have succeeded - do not return failure
|
| // after this point. Later stuff is optional.
|
| if (!HasFastProperties()) {
|
| return result;
|
| }
|
| +
|
| // Do not add transitions to the map of "new Object()".
|
| if (map() == GetIsolate()->context()->global_context()->
|
| object_function()->map()) {
|
| return result;
|
| }
|
|
|
| - MapTransitionDescriptor transition(name,
|
| - map(),
|
| - attributes);
|
| - Object* new_descriptors;
|
| - { MaybeObject* maybe_new_descriptors = old_map->instance_descriptors()->
|
| - CopyInsert(&transition, KEEP_TRANSITIONS);
|
| - if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
|
| - return result; // Yes, return _result_.
|
| + TransitionArray* new_transitions;
|
| + { MaybeObject* maybe_new_transitions = old_map->AddTransition(name, map());
|
| + if (!maybe_new_transitions->To(&new_transitions)) {
|
| + // We have accomplished the main goal, so return success.
|
| + return result;
|
| + }
|
| + }
|
| +
|
| + { MaybeObject* transition_added = old_map->set_transitions(new_transitions);
|
| + // Return success if failure since we accomplished the main goal. Otherwise
|
| + // also set backpointer.
|
| + if (!transition_added->IsFailure()) {
|
| + map()->SetBackPointer(old_map);
|
| }
|
| }
|
| - old_map->set_instance_descriptors(DescriptorArray::cast(new_descriptors));
|
| - map()->SetBackPointer(old_map);
|
| +
|
| return result;
|
| }
|
|
|
| @@ -1861,8 +1868,8 @@ MaybeObject* JSObject::ConvertDescriptorToField(String* name,
|
| FieldDescriptor new_field(name, index, attributes);
|
| // Make a new DescriptorArray replacing an entry with FieldDescriptor.
|
| Object* descriptors_unchecked;
|
| - { MaybeObject* maybe_descriptors_unchecked = map()->instance_descriptors()->
|
| - CopyInsert(&new_field, REMOVE_TRANSITIONS);
|
| + { MaybeObject* maybe_descriptors_unchecked =
|
| + map()->instance_descriptors()->CopyInsert(&new_field);
|
| if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) {
|
| return maybe_descriptors_unchecked;
|
| }
|
| @@ -2156,9 +2163,7 @@ MaybeObject* JSObject::SetPropertyViaPrototypes(
|
| return result.proxy()->SetPropertyViaPrototypesWithHandler(
|
| this, name, value, attributes, strict_mode, done);
|
| }
|
| - case MAP_TRANSITION:
|
| - case CONSTANT_TRANSITION:
|
| - break;
|
| + case TRANSITION:
|
| case NONEXISTENT:
|
| UNREACHABLE();
|
| break;
|
| @@ -2177,33 +2182,44 @@ MaybeObject* JSObject::SetPropertyViaPrototypes(
|
| }
|
|
|
|
|
| -void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
|
| - DescriptorArray* descriptors = map()->instance_descriptors();
|
| +void Map::LookupDescriptor(JSObject* holder,
|
| + String* name,
|
| + LookupResult* result) {
|
| + DescriptorArray* descriptors = this->instance_descriptors();
|
| int number = descriptors->SearchWithCache(name);
|
| if (number != DescriptorArray::kNotFound) {
|
| - result->DescriptorResult(this, descriptors->GetDetails(number), number);
|
| + result->DescriptorResult(holder, descriptors->GetDetails(number), number);
|
| } else {
|
| result->NotFound();
|
| }
|
| }
|
|
|
|
|
| -void Map::LookupInDescriptors(JSObject* holder,
|
| - String* name,
|
| - LookupResult* result) {
|
| - DescriptorArray* descriptors = instance_descriptors();
|
| - DescriptorLookupCache* cache =
|
| - GetHeap()->isolate()->descriptor_lookup_cache();
|
| - int number = cache->Lookup(descriptors, name);
|
| - if (number == DescriptorLookupCache::kAbsent) {
|
| - number = descriptors->Search(name);
|
| - cache->Update(descriptors, name, number);
|
| - }
|
| - if (number != DescriptorArray::kNotFound) {
|
| - result->DescriptorResult(holder, descriptors->GetDetails(number), number);
|
| - } else {
|
| - result->NotFound();
|
| +void Map::LookupTransition(JSObject* holder,
|
| + String* name,
|
| + LookupResult* result) {
|
| + if (HasTransitionArray()) {
|
| + TransitionArray* transition_array = transitions();
|
| + int number = transition_array->Search(name);
|
| + if (number != TransitionArray::kNotFound) {
|
| + return result->TransitionResult(holder, number);
|
| + }
|
| }
|
| + result->NotFound();
|
| +}
|
| +
|
| +
|
| +void Map::LookupTransitionOrDescriptor(JSObject* holder,
|
| + String* name,
|
| + LookupResult* result) {
|
| + // AccessorPairs containing both a Descriptor and a Transition are shared
|
| + // between the DescriptorArray and the Transition array. This is why looking
|
| + // up the AccessorPair solely in the DescriptorArray works.
|
| + // TODO(verwaest) This should be implemented differently so the
|
| + // DescriptorArray is free of transitions; and so we can freely share it.
|
| + this->LookupDescriptor(holder, name, result);
|
| + if (result->IsFound()) return;
|
| + this->LookupTransition(holder, name, result);
|
| }
|
|
|
|
|
| @@ -2256,21 +2272,16 @@ static Map* FindClosestElementsTransition(Map* map, ElementsKind to_kind) {
|
| ASSERT(index <= to_index);
|
|
|
| for (; index < to_index; ++index) {
|
| - Map* next_map = current_map->elements_transition_map();
|
| - if (next_map == NULL) {
|
| - return current_map;
|
| - }
|
| - current_map = next_map;
|
| + if (!current_map->HasElementsTransition()) return current_map;
|
| + current_map = current_map->elements_transition_map();
|
| }
|
| - if (!IsFastElementsKind(to_kind)) {
|
| + if (!IsFastElementsKind(to_kind) && current_map->HasElementsTransition()) {
|
| Map* next_map = current_map->elements_transition_map();
|
| - if (next_map != NULL && next_map->elements_kind() == to_kind) {
|
| - return next_map;
|
| - }
|
| - ASSERT(current_map->elements_kind() == TERMINAL_FAST_ELEMENTS_KIND);
|
| - } else {
|
| - ASSERT(current_map->elements_kind() == to_kind);
|
| + if (next_map->elements_kind() == to_kind) return next_map;
|
| }
|
| + ASSERT(IsFastElementsKind(to_kind)
|
| + ? current_map->elements_kind() == to_kind
|
| + : current_map->elements_kind() == TERMINAL_FAST_ELEMENTS_KIND);
|
| return current_map;
|
| }
|
|
|
| @@ -2283,7 +2294,7 @@ Map* Map::LookupElementsTransitionMap(ElementsKind to_kind) {
|
|
|
|
|
| MaybeObject* Map::CreateNextElementsTransition(ElementsKind next_kind) {
|
| - ASSERT(elements_transition_map() == NULL ||
|
| + ASSERT(!HasElementsTransition() ||
|
| ((elements_transition_map()->elements_kind() == DICTIONARY_ELEMENTS ||
|
| IsExternalArrayElementsKind(
|
| elements_transition_map()->elements_kind())) &&
|
| @@ -2294,13 +2305,17 @@ MaybeObject* Map::CreateNextElementsTransition(ElementsKind next_kind) {
|
| ASSERT(next_kind != elements_kind());
|
|
|
| Map* next_map;
|
| - MaybeObject* maybe_next_map =
|
| - this->CopyDropTransitions(DescriptorArray::CANNOT_BE_SHARED);
|
| - if (!maybe_next_map->To(&next_map)) return maybe_next_map;
|
| + { MaybeObject* maybe_next_map =
|
| + this->CopyDropTransitions(DescriptorArray::CANNOT_BE_SHARED);
|
| + if (!maybe_next_map->To(&next_map)) return maybe_next_map;
|
| + }
|
| +
|
| + { MaybeObject* added_elements = this->set_elements_transition_map(next_map);
|
| + if (added_elements->IsFailure()) return added_elements;
|
| + }
|
|
|
| next_map->set_elements_kind(next_kind);
|
| next_map->SetBackPointer(this);
|
| - this->set_elements_transition_map(next_map);
|
| return next_map;
|
| }
|
|
|
| @@ -2346,22 +2361,6 @@ Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object,
|
| }
|
|
|
|
|
| -// If the map is using the empty descriptor array, install a new empty
|
| -// descriptor array that will contain an element transition.
|
| -// TODO(verwaest) Goes away once the descriptor array is immutable.
|
| -static MaybeObject* EnsureMayContainTransitions(Map* map) {
|
| - if (map->instance_descriptors()->MayContainTransitions()) return map;
|
| - DescriptorArray* descriptor_array;
|
| - MaybeObject* maybe_descriptor_array =
|
| - DescriptorArray::Allocate(0, DescriptorArray::CANNOT_BE_SHARED);
|
| - if (!maybe_descriptor_array->To(&descriptor_array)) {
|
| - return maybe_descriptor_array;
|
| - }
|
| - map->set_instance_descriptors(descriptor_array);
|
| - return map;
|
| -}
|
| -
|
| -
|
| MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) {
|
| Map* start_map = map();
|
| ElementsKind from_kind = start_map->elements_kind();
|
| @@ -2396,7 +2395,6 @@ MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) {
|
| return new_map;
|
| }
|
|
|
| - EnsureMayContainTransitions(start_map);
|
| Map* closest_map = FindClosestElementsTransition(start_map, to_kind);
|
|
|
| if (closest_map->elements_kind() == to_kind) {
|
| @@ -2418,41 +2416,40 @@ void JSObject::LocalLookupRealNamedProperty(String* name,
|
| }
|
|
|
| if (HasFastProperties()) {
|
| - LookupInDescriptor(name, result);
|
| - if (result->IsFound()) {
|
| - // A property, a map transition or a null descriptor was found.
|
| - // We return all of these result types because
|
| - // LocalLookupRealNamedProperty is used when setting properties
|
| - // where map transitions and null descriptors are handled.
|
| - ASSERT(result->holder() == this && result->IsFastPropertyType());
|
| - // Disallow caching for uninitialized constants. These can only
|
| - // occur as fields.
|
| - if (result->IsField() &&
|
| - result->IsReadOnly() &&
|
| - FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
|
| - result->DisallowCaching();
|
| - }
|
| - return;
|
| + map()->LookupTransitionOrDescriptor(this, name, result);
|
| + // A property or a map transition was found. We return all of these result
|
| + // types because LocalLookupRealNamedProperty is used when setting
|
| + // properties where map transitions are handled.
|
| + ASSERT(!result->IsFound() ||
|
| + (result->holder() == this && result->IsFastPropertyType()));
|
| + // Disallow caching for uninitialized constants. These can only
|
| + // occur as fields.
|
| + if (result->IsField() &&
|
| + result->IsReadOnly() &&
|
| + FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
|
| + result->DisallowCaching();
|
| }
|
| - } else {
|
| - int entry = property_dictionary()->FindEntry(name);
|
| - if (entry != StringDictionary::kNotFound) {
|
| - Object* value = property_dictionary()->ValueAt(entry);
|
| - if (IsGlobalObject()) {
|
| - PropertyDetails d = property_dictionary()->DetailsAt(entry);
|
| - if (d.IsDeleted()) {
|
| - result->NotFound();
|
| - return;
|
| - }
|
| - value = JSGlobalPropertyCell::cast(value)->value();
|
| + return;
|
| + }
|
| +
|
| + int entry = property_dictionary()->FindEntry(name);
|
| + if (entry != StringDictionary::kNotFound) {
|
| + Object* value = property_dictionary()->ValueAt(entry);
|
| + if (IsGlobalObject()) {
|
| + PropertyDetails d = property_dictionary()->DetailsAt(entry);
|
| + if (d.IsDeleted()) {
|
| + result->NotFound();
|
| + return;
|
| }
|
| - // Make sure to disallow caching for uninitialized constants
|
| - // found in the dictionary-mode objects.
|
| - if (value->IsTheHole()) result->DisallowCaching();
|
| - result->DictionaryResult(this, entry);
|
| - return;
|
| + value = JSGlobalPropertyCell::cast(value)->value();
|
| }
|
| + // Make sure to disallow caching for uninitialized constants
|
| + // found in the dictionary-mode objects.
|
| + if (value->IsTheHole()) result->DisallowCaching();
|
| + result->DictionaryResult(this, entry);
|
| + return;
|
| }
|
| +
|
| result->NotFound();
|
| }
|
|
|
| @@ -2879,7 +2876,7 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
| // Neither properties nor transitions found.
|
| return AddProperty(name, value, attributes, strict_mode, store_mode);
|
| }
|
| - if (result->IsReadOnly() && result->IsProperty()) {
|
| + if (result->IsProperty() && result->IsReadOnly()) {
|
| if (strict_mode == kStrictMode) {
|
| Handle<JSObject> self(this);
|
| Handle<String> hname(name);
|
| @@ -2890,6 +2887,7 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
| return value;
|
| }
|
| }
|
| +
|
| // This is a real property that is not read-only, or it is a
|
| // transition or null descriptor and there are no setters in the prototypes.
|
| switch (result->type()) {
|
| @@ -2897,14 +2895,6 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
| return SetNormalizedProperty(result, value);
|
| case FIELD:
|
| return FastPropertyAtPut(result->GetFieldIndex(), value);
|
| - case MAP_TRANSITION:
|
| - if (attributes == result->GetAttributes()) {
|
| - // Only use map transition if the attributes match.
|
| - return AddFastPropertyUsingMap(result->GetTransitionMap(),
|
| - name,
|
| - value);
|
| - }
|
| - return ConvertDescriptorToField(name, value, attributes);
|
| case CONSTANT_FUNCTION:
|
| // Only replace the function if necessary.
|
| if (value == result->GetConstantFunction()) return value;
|
| @@ -2913,10 +2903,6 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
| return ConvertDescriptorToField(name, value, attributes);
|
| case CALLBACKS: {
|
| Object* callback_object = result->GetCallbackObject();
|
| - if (callback_object->IsAccessorPair() &&
|
| - !AccessorPair::cast(callback_object)->ContainsAccessor()) {
|
| - return ConvertDescriptorToField(name, value, attributes);
|
| - }
|
| return SetPropertyWithCallback(callback_object,
|
| name,
|
| value,
|
| @@ -2925,23 +2911,49 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
| }
|
| case INTERCEPTOR:
|
| return SetPropertyWithInterceptor(name, value, attributes, strict_mode);
|
| - case CONSTANT_TRANSITION: {
|
| + case TRANSITION: {
|
| + Object* transition = result->GetTransitionValue();
|
| +
|
| + if (transition->IsAccessorPair()) {
|
| + if (!AccessorPair::cast(transition)->ContainsAccessor()) {
|
| + return ConvertDescriptorToField(name, value, attributes);
|
| + }
|
| + return SetPropertyWithCallback(transition,
|
| + name,
|
| + value,
|
| + result->holder(),
|
| + strict_mode);
|
| + }
|
| +
|
| + Map* transition_map = Map::cast(transition);
|
| + DescriptorArray* descriptors = transition_map->instance_descriptors();
|
| + int descriptor = descriptors->SearchWithCache(name);
|
| + PropertyDetails details = descriptors->GetDetails(descriptor);
|
| + ASSERT(details.type() == FIELD || details.type() == CONSTANT_FUNCTION);
|
| +
|
| + if (details.type() == FIELD) {
|
| + if (attributes == details.attributes()) {
|
| + int field_index = descriptors->GetFieldIndex(descriptor);
|
| + return AddFastPropertyUsingMap(transition_map,
|
| + name,
|
| + value,
|
| + field_index);
|
| + }
|
| + return ConvertDescriptorToField(name, value, attributes);
|
| + }
|
| +
|
| + // Is transition to CONSTANT_FUNCTION.
|
| + Object* constant_function = descriptors->GetValue(descriptor);
|
| // If the same constant function is being added we can simply
|
| // transition to the target map.
|
| - Map* target_map = result->GetTransitionMap();
|
| - DescriptorArray* target_descriptors = target_map->instance_descriptors();
|
| - int number = target_descriptors->SearchWithCache(name);
|
| - ASSERT(number != DescriptorArray::kNotFound);
|
| - ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
|
| - JSFunction* function =
|
| - JSFunction::cast(target_descriptors->GetValue(number));
|
| - if (value == function) {
|
| - set_map(target_map);
|
| - return value;
|
| + if (constant_function == value) {
|
| + set_map(transition_map);
|
| + return this;
|
| }
|
| - // Otherwise, replace with a MAP_TRANSITION to a new map with a
|
| - // FIELD, even if the value is a constant function.
|
| - return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
|
| + // Otherwise, replace with a map transition to a new map with a FIELD,
|
| + // even if the value is a constant function.
|
| + return ConvertDescriptorToFieldAndMapTransition(
|
| + name, value, attributes);
|
| }
|
| case HANDLER:
|
| case NONEXISTENT:
|
| @@ -3011,22 +3023,14 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
| return AddProperty(name, value, attributes, kNonStrictMode);
|
| }
|
|
|
| - PropertyDetails details = PropertyDetails(attributes, NORMAL);
|
| -
|
| // Check of IsReadOnly removed from here in clone.
|
| switch (result.type()) {
|
| - case NORMAL:
|
| + case NORMAL: {
|
| + PropertyDetails details = PropertyDetails(attributes, NORMAL);
|
| return SetNormalizedProperty(name, value, details);
|
| + }
|
| case FIELD:
|
| return FastPropertyAtPut(result.GetFieldIndex(), value);
|
| - case MAP_TRANSITION:
|
| - if (attributes == result.GetAttributes()) {
|
| - // Only use map transition if the attributes match.
|
| - return AddFastPropertyUsingMap(result.GetTransitionMap(),
|
| - name,
|
| - value);
|
| - }
|
| - return ConvertDescriptorToField(name, value, attributes);
|
| case CONSTANT_FUNCTION:
|
| // Only replace the function if necessary.
|
| if (value == result.GetConstantFunction()) return value;
|
| @@ -3037,10 +3041,34 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
| case INTERCEPTOR:
|
| // Override callback in clone
|
| return ConvertDescriptorToField(name, value, attributes);
|
| - case CONSTANT_TRANSITION:
|
| - // Replace with a MAP_TRANSITION to a new map with a FIELD, even
|
| - // if the value is a function.
|
| + case TRANSITION: {
|
| + Object* transition = result.GetTransitionValue();
|
| +
|
| + if (transition->IsAccessorPair()) {
|
| + return ConvertDescriptorToField(name, value, attributes);
|
| + }
|
| +
|
| + Map* transition_map = Map::cast(transition);
|
| + DescriptorArray* descriptors = transition_map->instance_descriptors();
|
| + int descriptor = descriptors->Search(name);
|
| + PropertyDetails details = descriptors->GetDetails(descriptor);
|
| + ASSERT(details.type() == FIELD || details.type() == CONSTANT_FUNCTION);
|
| +
|
| + if (details.type() == FIELD) {
|
| + if (attributes == details.attributes()) {
|
| + int field_index = descriptors->GetFieldIndex(descriptor);
|
| + return AddFastPropertyUsingMap(transition_map,
|
| + name,
|
| + value,
|
| + field_index);
|
| + }
|
| + 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.
|
| return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
|
| + }
|
| case HANDLER:
|
| case NONEXISTENT:
|
| UNREACHABLE();
|
| @@ -3164,7 +3192,8 @@ PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver,
|
| case INTERCEPTOR:
|
| return result->holder()->GetPropertyAttributeWithInterceptor(
|
| JSObject::cast(receiver), name, continue_search);
|
| - default:
|
| + case TRANSITION:
|
| + case NONEXISTENT:
|
| UNREACHABLE();
|
| }
|
| }
|
| @@ -3321,7 +3350,6 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
|
| break;
|
| }
|
| case CALLBACKS: {
|
| - if (!descs->IsProperty(i)) break;
|
| Object* value = descs->GetCallbacksObject(i);
|
| if (value->IsAccessorPair()) {
|
| MaybeObject* maybe_copy =
|
| @@ -3333,12 +3361,11 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
|
| if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
|
| break;
|
| }
|
| - case MAP_TRANSITION:
|
| - case CONSTANT_TRANSITION:
|
| case INTERCEPTOR:
|
| break;
|
| case HANDLER:
|
| case NORMAL:
|
| + case TRANSITION:
|
| case NONEXISTENT:
|
| UNREACHABLE();
|
| break;
|
| @@ -3678,13 +3705,10 @@ MaybeObject* JSObject::GetHiddenPropertiesDictionary(bool create_if_absent) {
|
| DescriptorArray* descriptors = this->map()->instance_descriptors();
|
| if ((descriptors->number_of_descriptors() > 0) &&
|
| (descriptors->GetKey(0) == GetHeap()->hidden_symbol())) {
|
| - if (descriptors->GetType(0) == FIELD) {
|
| - Object* hidden_store =
|
| - this->FastPropertyAt(descriptors->GetFieldIndex(0));
|
| - return StringDictionary::cast(hidden_store);
|
| - } else {
|
| - ASSERT(descriptors->GetType(0) == MAP_TRANSITION);
|
| - }
|
| + ASSERT(descriptors->GetType(0) == FIELD);
|
| + Object* hidden_store =
|
| + this->FastPropertyAt(descriptors->GetFieldIndex(0));
|
| + return StringDictionary::cast(hidden_store);
|
| }
|
| } else {
|
| PropertyAttributes attributes;
|
| @@ -3727,12 +3751,9 @@ MaybeObject* JSObject::SetHiddenPropertiesDictionary(
|
| DescriptorArray* descriptors = this->map()->instance_descriptors();
|
| if ((descriptors->number_of_descriptors() > 0) &&
|
| (descriptors->GetKey(0) == GetHeap()->hidden_symbol())) {
|
| - if (descriptors->GetType(0) == FIELD) {
|
| - this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary);
|
| - return this;
|
| - } else {
|
| - ASSERT(descriptors->GetType(0) == MAP_TRANSITION);
|
| - }
|
| + ASSERT(descriptors->GetType(0) == FIELD);
|
| + this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary);
|
| + return this;
|
| }
|
| }
|
| MaybeObject* store_result =
|
| @@ -4167,7 +4188,7 @@ int Map::NumberOfDescribedProperties(PropertyAttributes filter) {
|
| DescriptorArray* descs = instance_descriptors();
|
| for (int i = 0; i < descs->number_of_descriptors(); i++) {
|
| PropertyDetails details = descs->GetDetails(i);
|
| - if (descs->IsProperty(i) && (details.attributes() & filter) == 0) {
|
| + if ((details.attributes() & filter) == 0) {
|
| result++;
|
| }
|
| }
|
| @@ -4373,7 +4394,7 @@ MaybeObject* JSObject::DefineElementAccessor(uint32_t index,
|
| MaybeObject* JSObject::CreateAccessorPairFor(String* name) {
|
| LookupResult result(GetHeap()->isolate());
|
| LocalLookupRealNamedProperty(name, &result);
|
| - if (result.IsProperty() && result.IsCallbacks()) {
|
| + if (result.IsPropertyCallbacks()) {
|
| // Note that the result can actually have IsDontDelete() == true when we
|
| // e.g. have to fall back to the slow case while adding a setter after
|
| // successfully reusing a map transition for a getter. Nevertheless, this is
|
| @@ -4582,8 +4603,7 @@ static MaybeObject* CreateFreshAccessor(JSObject* obj,
|
| CallbacksDescriptor callbacks_descr2(name, accessors2, attributes);
|
| DescriptorArray* descriptors2;
|
| { MaybeObject* maybe_descriptors2 =
|
| - map1->instance_descriptors()->CopyInsert(&callbacks_descr2,
|
| - REMOVE_TRANSITIONS);
|
| + map1->instance_descriptors()->CopyInsert(&callbacks_descr2);
|
| if (!maybe_descriptors2->To(&descriptors2)) return maybe_descriptors2;
|
| }
|
|
|
| @@ -4603,18 +4623,20 @@ static MaybeObject* CreateFreshAccessor(JSObject* obj,
|
|
|
| // step 5: create a copy of the descriptors, incl. the new getter/setter pair
|
| // with the transition
|
| - CallbacksDescriptor callbacks_descr1(name, accessors1, attributes);
|
| - DescriptorArray* descriptors1;
|
| - { MaybeObject* maybe_descriptors1 =
|
| - map1->instance_descriptors()->CopyInsert(&callbacks_descr1,
|
| - KEEP_TRANSITIONS);
|
| - if (!maybe_descriptors1->To(&descriptors1)) return maybe_descriptors1;
|
| + 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
|
| - obj->set_map(map2);
|
| - map1->set_instance_descriptors(descriptors1);
|
| + { MaybeObject* transition_added = map1->set_transitions(new_transitions);
|
| + if (transition_added->IsFailure()) return transition_added;
|
| + }
|
| +
|
| map2->SetBackPointer(map1);
|
| + obj->set_map(map2);
|
| return obj;
|
| }
|
|
|
| @@ -4652,8 +4674,7 @@ static MaybeObject* NewCallbackTransition(JSObject* obj,
|
| CallbacksDescriptor callbacks_descr3(name, accessors3, attributes);
|
| DescriptorArray* descriptors3;
|
| { MaybeObject* maybe_descriptors3 =
|
| - map2->instance_descriptors()->CopyInsert(&callbacks_descr3,
|
| - REMOVE_TRANSITIONS);
|
| + map2->instance_descriptors()->CopyInsert(&callbacks_descr3);
|
| if (!maybe_descriptors3->To(&descriptors3)) return maybe_descriptors3;
|
| }
|
|
|
| @@ -4664,10 +4685,20 @@ static MaybeObject* NewCallbackTransition(JSObject* obj,
|
| }
|
| map3->set_instance_descriptors(descriptors3);
|
|
|
| - // step 4: everything went well so far, so we make our changes visible
|
| + // 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 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);
|
| - map3->SetBackPointer(map2);
|
| return obj;
|
| }
|
|
|
| @@ -4686,7 +4717,8 @@ MaybeObject* JSObject::DefineFastAccessor(String* name,
|
| }
|
|
|
| // If the property is not a JavaScript accessor, fall back to the slow case.
|
| - if (result.type() != CALLBACKS) return GetHeap()->null_value();
|
| + if (!result.IsCallbacks()) return GetHeap()->null_value();
|
| +
|
| Object* callback_value = result.GetCallbackObject();
|
| if (!callback_value->IsAccessorPair()) return GetHeap()->null_value();
|
| AccessorPair* accessors = AccessorPair::cast(callback_value);
|
| @@ -4699,7 +4731,6 @@ MaybeObject* JSObject::DefineFastAccessor(String* name,
|
| return this;
|
| }
|
|
|
| - // When we re-add the same accessor again, there is nothing to do.
|
| if (entry == accessor && result.GetAttributes() == attributes) return this;
|
|
|
| // Only the other accessor has been set so far, create a new transition.
|
| @@ -4741,9 +4772,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
|
| // Try to flatten before operating on the string.
|
| name->TryFlatten();
|
|
|
| - if (!CanSetCallback(name)) {
|
| - return isolate->heap()->undefined_value();
|
| - }
|
| + if (!CanSetCallback(name)) return isolate->heap()->undefined_value();
|
|
|
| uint32_t index = 0;
|
| bool is_element = name->AsArrayIndex(&index);
|
| @@ -4843,7 +4872,7 @@ Object* JSObject::LookupAccessor(String* name, AccessorComponent component) {
|
| JSObject::cast(obj)->LocalLookup(name, &result);
|
| if (result.IsProperty()) {
|
| if (result.IsReadOnly()) return heap->undefined_value();
|
| - if (result.IsCallbacks()) {
|
| + if (result.IsPropertyCallbacks()) {
|
| Object* obj = result.GetCallbackObject();
|
| if (obj->IsAccessorPair()) {
|
| return AccessorPair::cast(obj)->GetComponent(component);
|
| @@ -4903,7 +4932,7 @@ MaybeObject* Map::CopyDropDescriptors() {
|
| JSFunction* ctor = JSFunction::cast(constructor());
|
| Object* descriptors;
|
| { MaybeObject* maybe_descriptors =
|
| - ctor->initial_map()->instance_descriptors()->RemoveTransitions(
|
| + ctor->initial_map()->instance_descriptors()->Copy(
|
| DescriptorArray::MAY_BE_SHARED);
|
| if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
|
| }
|
| @@ -4966,7 +4995,7 @@ MaybeObject* Map::CopyDropTransitions(
|
| }
|
| Object* descriptors;
|
| { MaybeObject* maybe_descriptors =
|
| - instance_descriptors()->RemoveTransitions(shared_mode);
|
| + instance_descriptors()->Copy(shared_mode);
|
| if (!maybe_descriptors->ToObject(&descriptors)) return maybe_descriptors;
|
| }
|
| cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
|
| @@ -5030,18 +5059,16 @@ void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
|
| // field of the contens array while it is running.
|
| class IntrusiveMapTransitionIterator {
|
| public:
|
| - explicit IntrusiveMapTransitionIterator(DescriptorArray* descriptor_array)
|
| - : descriptor_array_(descriptor_array) { }
|
| + explicit IntrusiveMapTransitionIterator(TransitionArray* transition_array)
|
| + : transition_array_(transition_array) { }
|
|
|
| void Start() {
|
| ASSERT(!IsIterating());
|
| - if (descriptor_array_->MayContainTransitions())
|
| - *DescriptorArrayHeader() = Smi::FromInt(0);
|
| + *TransitionArrayHeader() = Smi::FromInt(0);
|
| }
|
|
|
| bool IsIterating() {
|
| - return descriptor_array_->MayContainTransitions() &&
|
| - (*DescriptorArrayHeader())->IsSmi();
|
| + return (*TransitionArrayHeader())->IsSmi();
|
| }
|
|
|
| Map* Next() {
|
| @@ -5051,66 +5078,51 @@ class IntrusiveMapTransitionIterator {
|
| // 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(*DescriptorArrayHeader())->value();
|
| + int raw_index = Smi::cast(*TransitionArrayHeader())->value();
|
| int index = raw_index / 2;
|
| - int number_of_descriptors = descriptor_array_->number_of_descriptors();
|
| - while (index < number_of_descriptors) {
|
| - PropertyDetails details(descriptor_array_->GetDetails(index));
|
| - switch (details.type()) {
|
| - case MAP_TRANSITION:
|
| - case CONSTANT_TRANSITION:
|
| - // We definitely have a map transition.
|
| - *DescriptorArrayHeader() = Smi::FromInt(raw_index + 2);
|
| - return static_cast<Map*>(descriptor_array_->GetValue(index));
|
| - case CALLBACKS: {
|
| - // We might have a map transition in a getter or in a setter.
|
| - AccessorPair* accessors =
|
| - static_cast<AccessorPair*>(descriptor_array_->GetValue(index));
|
| - Object* accessor;
|
| - if ((raw_index & 1) == 0) {
|
| - accessor = accessors->setter();
|
| - } else {
|
| - ++index;
|
| - accessor = accessors->getter();
|
| - }
|
| - ++raw_index;
|
| - if (accessor->IsMap()) {
|
| - *DescriptorArrayHeader() = Smi::FromInt(raw_index);
|
| - return static_cast<Map*>(accessor);
|
| - }
|
| - break;
|
| - }
|
| - case NORMAL:
|
| - case FIELD:
|
| - case CONSTANT_FUNCTION:
|
| - case HANDLER:
|
| - case INTERCEPTOR:
|
| - // We definitely have no map transition.
|
| - raw_index += 2;
|
| - ++index;
|
| - break;
|
| - case NONEXISTENT:
|
| - UNREACHABLE();
|
| - break;
|
| + 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);
|
| }
|
| - }
|
| - if (index == descriptor_array_->number_of_descriptors()) {
|
| - Map* elements_transition = descriptor_array_->elements_transition_map();
|
| - if (elements_transition != NULL) {
|
| - *DescriptorArrayHeader() = Smi::FromInt(raw_index + 2);
|
| - return elements_transition;
|
| +
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + if (index == transition_array_->number_of_transitions() &&
|
| + transition_array_->HasElementsTransition()) {
|
| + Map* elements_transition = transition_array_->elements_transition();
|
| + *TransitionArrayHeader() = Smi::FromInt(raw_index + 2);
|
| + return elements_transition;
|
| }
|
| - *DescriptorArrayHeader() = descriptor_array_->GetHeap()->fixed_array_map();
|
| + *TransitionArrayHeader() = transition_array_->GetHeap()->fixed_array_map();
|
| return NULL;
|
| }
|
|
|
| private:
|
| - Object** DescriptorArrayHeader() {
|
| - return HeapObject::RawField(descriptor_array_, DescriptorArray::kMapOffset);
|
| + Object** TransitionArrayHeader() {
|
| + return HeapObject::RawField(transition_array_, TransitionArray::kMapOffset);
|
| }
|
|
|
| - DescriptorArray* descriptor_array_;
|
| + TransitionArray* transition_array_;
|
| };
|
|
|
|
|
| @@ -5210,22 +5222,19 @@ class TraversableMap : public Map {
|
|
|
| // Can either be Smi (no instance descriptors), or a descriptor array with the
|
| // header overwritten as a Smi (thus iterating).
|
| - DescriptorArray* MutatedInstanceDescriptors() {
|
| - Object* object =
|
| - *HeapObject::RawField(this, kInstanceDescriptorsOrBitField3Offset);
|
| - if (object->IsSmi()) {
|
| - return GetHeap()->empty_descriptor_array();
|
| - } else {
|
| - DescriptorArray* descriptor_array =
|
| - static_cast<DescriptorArray*>(object);
|
| - return descriptor_array;
|
| - }
|
| + TransitionArray* MutatedTransitions() {
|
| + Object* object = *HeapObject::RawField(instance_descriptors(),
|
| + DescriptorArray::kTransitionsOffset);
|
| + TransitionArray* transition_array = static_cast<TransitionArray*>(object);
|
| + return transition_array;
|
| }
|
|
|
| // Start iterating over this map's children, possibly destroying a FixedArray
|
| // map (see explanation above).
|
| void ChildIteratorStart() {
|
| - IntrusiveMapTransitionIterator(instance_descriptors()).Start();
|
| + if (HasTransitionArray()) {
|
| + IntrusiveMapTransitionIterator(transitions()).Start();
|
| + }
|
| IntrusivePrototypeTransitionIterator(
|
| unchecked_prototype_transitions()).Start();
|
| }
|
| @@ -5239,11 +5248,13 @@ class TraversableMap : public Map {
|
| Map* next = proto_iterator.Next();
|
| if (next != NULL) return static_cast<TraversableMap*>(next);
|
| }
|
| - IntrusiveMapTransitionIterator
|
| - descriptor_iterator(MutatedInstanceDescriptors());
|
| - if (descriptor_iterator.IsIterating()) {
|
| - Map* next = descriptor_iterator.Next();
|
| - if (next != NULL) return static_cast<TraversableMap*>(next);
|
| + if (HasTransitionArray()) {
|
| + IntrusiveMapTransitionIterator
|
| + transitions_iterator(MutatedTransitions());
|
| + if (transitions_iterator.IsIterating()) {
|
| + Map* next = transitions_iterator.Next();
|
| + if (next != NULL) return static_cast<TraversableMap*>(next);
|
| + }
|
| }
|
| return NULL;
|
| }
|
| @@ -5872,71 +5883,46 @@ MaybeObject* DescriptorArray::CopyFrom(int dst_index,
|
| }
|
|
|
|
|
| -MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
|
| - TransitionFlag transition_flag) {
|
| - // Transitions are only kept when inserting another transition.
|
| - // This precondition is not required by this function's implementation, but
|
| - // is currently required by the semantics of maps, so we check it.
|
| - // Conversely, we filter after replacing, so replacing a transition and
|
| - // removing all other transitions is not supported.
|
| - bool remove_transitions = transition_flag == REMOVE_TRANSITIONS;
|
| - ASSERT(remove_transitions == !descriptor->ContainsTransition());
|
| -
|
| +MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor) {
|
| // Ensure the key is a symbol.
|
| { MaybeObject* maybe_result = descriptor->KeyToSymbol();
|
| if (maybe_result->IsFailure()) return maybe_result;
|
| }
|
|
|
| - int new_size = 0;
|
| - for (int i = 0; i < number_of_descriptors(); i++) {
|
| - if (remove_transitions && IsTransitionOnly(i)) continue;
|
| - new_size++;
|
| - }
|
| + int new_size = number_of_descriptors();
|
|
|
| // If key is in descriptor, we replace it in-place when filtering.
|
| // Count a null descriptor for key as inserted, not replaced.
|
| - int index = Search(descriptor->GetKey());
|
| + int index = SearchWithCache(descriptor->GetKey());
|
| const bool replacing = (index != kNotFound);
|
| bool keep_enumeration_index = false;
|
| - if (!replacing) {
|
| - ++new_size;
|
| - } else if (!IsTransitionOnly(index)) {
|
| + if (replacing) {
|
| // We are replacing an existing descriptor. We keep the enumeration index
|
| // of a visible property.
|
| keep_enumeration_index = true;
|
| - } else if (remove_transitions) {
|
| - // Replaced descriptor has been counted as removed if it is a transition
|
| - // that will be replaced. Adjust count in this case.
|
| + } else {
|
| ++new_size;
|
| }
|
|
|
| DescriptorArray* new_descriptors;
|
| - { SharedMode mode = remove_transitions ? MAY_BE_SHARED : CANNOT_BE_SHARED;
|
| - MaybeObject* maybe_result = Allocate(new_size, mode);
|
| + { MaybeObject* maybe_result = Allocate(new_size, MAY_BE_SHARED);
|
| if (!maybe_result->To(&new_descriptors)) return maybe_result;
|
| }
|
|
|
| - DescriptorArray::WhitenessWitness witness(new_descriptors);
|
| + FixedArray::WhitenessWitness witness(new_descriptors);
|
|
|
| // Set the enumeration index in the descriptors and set the enumeration index
|
| // in the result.
|
| int enumeration_index = NextEnumerationIndex();
|
| - if (!descriptor->ContainsTransition()) {
|
| - if (keep_enumeration_index) {
|
| - descriptor->SetEnumerationIndex(GetDetails(index).index());
|
| - } else {
|
| - descriptor->SetEnumerationIndex(enumeration_index);
|
| - ++enumeration_index;
|
| - }
|
| - }
|
| - Map* old_elements_transition = elements_transition_map();
|
| - if ((!remove_transitions) && (old_elements_transition != NULL)) {
|
| - new_descriptors->set_elements_transition_map(old_elements_transition);
|
| + if (keep_enumeration_index) {
|
| + descriptor->SetEnumerationIndex(GetDetails(index).index());
|
| + } else {
|
| + descriptor->SetEnumerationIndex(enumeration_index);
|
| + ++enumeration_index;
|
| }
|
| new_descriptors->SetNextEnumerationIndex(enumeration_index);
|
|
|
| - // Copy the descriptors, filtering out transitions and null descriptors,
|
| - // and inserting or replacing a descriptor.
|
| + // Copy the descriptors, inserting or replacing a descriptor.
|
| int to_index = 0;
|
| int insertion_index = -1;
|
| int from_index = 0;
|
| @@ -5946,11 +5932,9 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
|
| insertion_index = to_index++;
|
| if (replacing) from_index++;
|
| } else {
|
| - if (!(remove_transitions && IsTransitionOnly(from_index))) {
|
| - MaybeObject* copy_result =
|
| - new_descriptors->CopyFrom(to_index++, this, from_index, witness);
|
| - if (copy_result->IsFailure()) return copy_result;
|
| - }
|
| + MaybeObject* copy_result =
|
| + new_descriptors->CopyFrom(to_index++, this, from_index, witness);
|
| + if (copy_result->IsFailure()) return copy_result;
|
| from_index++;
|
| }
|
| }
|
| @@ -5966,31 +5950,25 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
|
| }
|
|
|
|
|
| -MaybeObject* DescriptorArray::RemoveTransitions(SharedMode shared_mode) {
|
| +MaybeObject* DescriptorArray::Copy(SharedMode shared_mode) {
|
| // Allocate the new descriptor array.
|
| - int new_number_of_descriptors = 0;
|
| - for (int i = 0; i < number_of_descriptors(); i++) {
|
| - if (IsProperty(i)) new_number_of_descriptors++;
|
| - }
|
| + int number_of_descriptors = this->number_of_descriptors();
|
| DescriptorArray* new_descriptors;
|
| - { MaybeObject* maybe_result = Allocate(new_number_of_descriptors,
|
| + { MaybeObject* maybe_result = Allocate(number_of_descriptors,
|
| shared_mode);
|
| if (!maybe_result->To(&new_descriptors)) return maybe_result;
|
| }
|
|
|
| // Copy the content.
|
| - DescriptorArray::WhitenessWitness witness(new_descriptors);
|
| - int next_descriptor = 0;
|
| - for (int i = 0; i < number_of_descriptors(); i++) {
|
| - if (IsProperty(i)) {
|
| + 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(next_descriptor++, this, i, witness);
|
| + new_descriptors->CopyFrom(i, this, i, witness);
|
| if (copy_result->IsFailure()) return copy_result;
|
| }
|
| }
|
| - ASSERT(next_descriptor == new_descriptors->number_of_descriptors());
|
| new_descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
|
| -
|
| return new_descriptors;
|
| }
|
|
|
| @@ -6057,43 +6035,6 @@ void DescriptorArray::Sort(const WhitenessWitness& witness) {
|
| }
|
|
|
|
|
| -int DescriptorArray::BinarySearch(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 = GetKey(mid);
|
| - uint32_t mid_hash = mid_name->Hash();
|
| -
|
| - if (mid_hash >= hash) {
|
| - high = mid;
|
| - } else {
|
| - low = mid + 1;
|
| - }
|
| - }
|
| -
|
| - for (; low <= limit && GetKey(low)->Hash() == hash; ++low) {
|
| - if (GetKey(low)->Equals(name)) return low;
|
| - }
|
| -
|
| - return kNotFound;
|
| -}
|
| -
|
| -
|
| -int DescriptorArray::LinearSearch(SearchMode mode, String* name, int len) {
|
| - uint32_t hash = name->Hash();
|
| - for (int number = 0; number < len; number++) {
|
| - String* entry = GetKey(number);
|
| - if (mode == EXPECT_SORTED && entry->Hash() > hash) break;
|
| - if (name->Equals(entry)) return number;
|
| - }
|
| - return kNotFound;
|
| -}
|
| -
|
| -
|
| MaybeObject* AccessorPair::CopyWithoutTransitions() {
|
| Heap* heap = GetHeap();
|
| AccessorPair* copy;
|
| @@ -7336,17 +7277,6 @@ void String::PrintOn(FILE* file) {
|
| }
|
|
|
|
|
| -// Clear a possible back pointer in case the transition leads to a dead map.
|
| -// Return true in case a back pointer has been cleared and false otherwise.
|
| -static bool ClearBackPointer(Heap* heap, Object* target) {
|
| - ASSERT(target->IsMap());
|
| - Map* map = Map::cast(target);
|
| - if (Marking::MarkBitFrom(map).Get()) return false;
|
| - map->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER);
|
| - return true;
|
| -}
|
| -
|
| -
|
| // This function should only be called from within the GC, since it uses
|
| // IncrementLiveBytesFromGC. If called from anywhere else, this results in an
|
| // inconsistent live-bytes count.
|
| @@ -7387,118 +7317,100 @@ static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) {
|
| }
|
|
|
|
|
| -// If the descriptor describes a transition to a dead map, the back pointer
|
| -// of this map is cleared and we return true. Otherwise we return false.
|
| -static bool ClearNonLiveTransitionsFromDescriptor(Heap* heap,
|
| - DescriptorArray* d,
|
| - int descriptor_index) {
|
| - // If the pair (value, details) is a map transition, check if the target is
|
| - // live. If not, null the descriptor. 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.
|
| - PropertyDetails details(d->GetDetails(descriptor_index));
|
| - switch (details.type()) {
|
| - case MAP_TRANSITION:
|
| - case CONSTANT_TRANSITION:
|
| - return ClearBackPointer(heap, d->GetValue(descriptor_index));
|
| - case CALLBACKS: {
|
| - Object* object = d->GetValue(descriptor_index);
|
| - if (object->IsAccessorPair()) {
|
| - bool cleared = true;
|
| - AccessorPair* accessors = AccessorPair::cast(object);
|
| - Object* getter = accessors->getter();
|
| - if (getter->IsMap()) {
|
| - if (ClearBackPointer(heap, getter)) {
|
| - accessors->set_getter(heap->the_hole_value());
|
| - } else {
|
| - cleared = false;
|
| - }
|
| - } else if (!getter->IsTheHole()) {
|
| - cleared = false;
|
| - }
|
| - Object* setter = accessors->setter();
|
| - if (setter->IsMap()) {
|
| - if (ClearBackPointer(heap, setter)) {
|
| - accessors->set_setter(heap->the_hole_value());
|
| - } else {
|
| - cleared = false;
|
| - }
|
| - } else if (!setter->IsTheHole()) {
|
| - cleared = false;
|
| - }
|
| - return cleared;
|
| - }
|
| - return false;
|
| - }
|
| - case NORMAL:
|
| - case FIELD:
|
| - case CONSTANT_FUNCTION:
|
| - case HANDLER:
|
| - case INTERCEPTOR:
|
| - return false;
|
| - case NONEXISTENT:
|
| - break;
|
| - }
|
| - UNREACHABLE();
|
| +// Clear a possible back pointer in case the transition leads to a dead map.
|
| +// Return true in case a back pointer has been cleared and false otherwise.
|
| +static bool ClearBackPointer(Heap* heap, Object* target) {
|
| + ASSERT(target->IsMap());
|
| + Map* map = Map::cast(target);
|
| + if (Marking::MarkBitFrom(map).Get()) return false;
|
| + map->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER);
|
| return true;
|
| }
|
|
|
|
|
| +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.
|
| void Map::ClearNonLiveTransitions(Heap* heap) {
|
| - Object* array = *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset);
|
| - // If there are no descriptors to be cleared, return.
|
| + TransitionArray* t = transitions();
|
| + // If there are no transitions to be cleared, return.
|
| // TODO(verwaest) Should be an assert, otherwise back pointers are not
|
| // properly cleared.
|
| - if (array->IsSmi()) return;
|
| - DescriptorArray* d = DescriptorArray::cast(array);
|
| + if (t == NULL) return;
|
| +
|
| + int transition_index = 0;
|
|
|
| - int descriptor_index = 0;
|
| // Compact all live descriptors to the left.
|
| - for (int i = 0; i < d->number_of_descriptors(); ++i) {
|
| - if (!ClearNonLiveTransitionsFromDescriptor(heap, d, i)) {
|
| - if (i != descriptor_index) {
|
| - String* key = d->GetKey(i);
|
| - Object* value = d->GetValue(i);
|
| - d->SetKeyUnchecked(heap, descriptor_index, key);
|
| - d->SetDetailsUnchecked(descriptor_index, d->GetDetails(i).AsSmi());
|
| - d->SetValueUnchecked(heap, descriptor_index, value);
|
| + for (int i = 0; i < t->number_of_transitions(); ++i) {
|
| + if (!ClearNonLiveTransition(heap, t, i)) {
|
| + if (i != transition_index) {
|
| + String* key = t->GetKey(i);
|
| + Object* value = t->GetValue(i);
|
| + t->SetKey(transition_index, key);
|
| + t->SetValue(transition_index, value);
|
| MarkCompactCollector* collector = heap->mark_compact_collector();
|
| - Object** key_slot = d->GetKeySlot(descriptor_index);
|
| + Object** key_slot = t->GetKeySlot(transition_index);
|
| collector->RecordSlot(key_slot, key_slot, key);
|
| - if (value->IsHeapObject()) {
|
| - Object** value_slot = d->GetValueSlot(descriptor_index);
|
| - collector->RecordSlot(value_slot, value_slot, value);
|
| - }
|
| + Object** value_slot = t->GetValueSlot(transition_index);
|
| + collector->RecordSlot(value_slot, value_slot, value);
|
| }
|
| - descriptor_index++;
|
| + transition_index++;
|
| }
|
| }
|
|
|
| - Map* elements_transition = d->elements_transition_map();
|
| - if (elements_transition != NULL &&
|
| - ClearBackPointer(heap, elements_transition)) {
|
| - elements_transition = NULL;
|
| - d->ClearElementsTransition();
|
| + if (t->HasElementsTransition() &&
|
| + ClearBackPointer(heap, t->elements_transition())) {
|
| + t->ClearElementsTransition();
|
| } else {
|
| - // If there are no descriptors to be cleared, return.
|
| + // If there are no transitions to be cleared, return.
|
| // TODO(verwaest) Should be an assert, otherwise back pointers are not
|
| // properly cleared.
|
| - if (descriptor_index == d->number_of_descriptors()) return;
|
| + if (transition_index == t->number_of_transitions()) return;
|
| }
|
|
|
| - // If the final descriptor array does not contain any live descriptors, remove
|
| - // the descriptor array from the map.
|
| - if (descriptor_index == 0 && elements_transition == NULL) {
|
| - ClearDescriptorArray();
|
| - return;
|
| + // If the final transition array does not contain any live transitions, remove
|
| + // the transition array from the map.
|
| + if (transition_index == 0 && !t->HasElementsTransition()) {
|
| + return ClearTransitions();
|
| }
|
|
|
| - int trim = d->number_of_descriptors() - descriptor_index;
|
| + int trim = t->number_of_transitions() - transition_index;
|
| if (trim > 0) {
|
| - RightTrimFixedArray(heap, d, trim * DescriptorArray::kDescriptorSize);
|
| + RightTrimFixedArray(heap, t, trim * TransitionArray::kTransitionSize);
|
| }
|
| }
|
|
|
| @@ -10693,10 +10605,10 @@ void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
|
| ASSERT(storage->length() >= (NumberOfLocalProperties() - index));
|
| if (HasFastProperties()) {
|
| DescriptorArray* descs = map()->instance_descriptors();
|
| + ASSERT(storage->length() >= index + descs->number_of_descriptors());
|
| for (int i = 0; i < descs->number_of_descriptors(); i++) {
|
| - if (descs->IsProperty(i)) storage->set(index++, descs->GetKey(i));
|
| + storage->set(index + i, descs->GetKey(i));
|
| }
|
| - ASSERT(storage->length() >= index);
|
| } else {
|
| property_dictionary()->CopyKeysTo(storage,
|
| index,
|
| @@ -11324,32 +11236,6 @@ int StringDictionary::FindEntry(String* key) {
|
| }
|
|
|
|
|
| -bool StringDictionary::ContainsTransition(int entry) {
|
| - switch (DetailsAt(entry).type()) {
|
| - case MAP_TRANSITION:
|
| - case CONSTANT_TRANSITION:
|
| - return true;
|
| - case CALLBACKS: {
|
| - Object* value = ValueAt(entry);
|
| - 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;
|
| -}
|
| -
|
| -
|
| template<typename Shape, typename Key>
|
| MaybeObject* HashTable<Shape, Key>::Rehash(HashTable* new_table, Key key) {
|
| ASSERT(NumberOfElements() < new_table->Capacity());
|
| @@ -12786,7 +12672,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
|
| }
|
| }
|
|
|
| - DescriptorArray::WhitenessWitness witness(descriptors);
|
| + FixedArray::WhitenessWitness witness(descriptors);
|
|
|
| int inobject_props = obj->map()->inobject_properties();
|
| int number_of_allocated_fields =
|
|
|