Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index a2b35ef45dd3a814351d7d5fa82c95bbec2ec7ac..ac5b0d478ffb1594686a157277ee19d805cefed3 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -1282,12 +1282,21 @@ MaybeObject* JSObject::AddFastProperty(String* name, |
} |
} |
- // Only allow map transition if the object's map is NOT equal to the |
- // global object_function's map and there is not a transition for 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); |
+ |
+ // External array transitions are stored in the descriptor for property "", |
+ // which is not a identifier and should have forced a switch to slow |
+ // properties above. |
+ ASSERT(descriptor_index == DescriptorArray::kNotFound || |
+ old_descriptors->GetType(descriptor_index) != EXTERNAL_ARRAY_TRANSITION); |
+ bool can_insert_transition = descriptor_index == DescriptorArray::kNotFound || |
+ old_descriptors->GetType(descriptor_index) == EXTERNAL_ARRAY_TRANSITION; |
bool allow_map_transition = |
- !old_descriptors->Contains(name) && |
- (isolate->context()->global_context()->object_function()-> |
- map() != map()); |
+ can_insert_transition && |
+ (isolate->context()->global_context()->object_function()->map() != map()); |
ASSERT(index < map()->inobject_properties() || |
(index - map()->inobject_properties()) < properties()->length() || |
@@ -1819,6 +1828,77 @@ void Map::LookupInDescriptors(JSObject* holder, |
} |
+MaybeObject* Map::GetExternalArrayElementsMap(ExternalArrayType array_type, |
+ bool safe_to_add_transition) { |
+ DescriptorArray* descriptors = instance_descriptors(); |
+ String* external_array_sentinel_name = GetIsolate()->heap()->empty_symbol(); |
+ |
+ if (safe_to_add_transition) { |
+ // It's only safe to manipulate the descriptor array if it would be |
+ // safe to add a transition. |
+ |
+ ASSERT(!is_shared()); // no transitions can be added to shared maps. |
+ // Check if the external array transition already exists. |
+ DescriptorLookupCache* cache = heap()->isolate()->descriptor_lookup_cache(); |
+ int index = cache->Lookup(descriptors, external_array_sentinel_name); |
+ if (index == DescriptorLookupCache::kAbsent) { |
+ index = descriptors->Search(external_array_sentinel_name); |
+ cache->Update(descriptors, |
+ external_array_sentinel_name, |
+ index); |
+ } |
+ |
+ // If the transition already exists, check the type. If there is a match, |
+ // return it. |
+ if (index != DescriptorArray::kNotFound) { |
+ PropertyDetails details(PropertyDetails(descriptors->GetDetails(index))); |
+ if (details.type() == EXTERNAL_ARRAY_TRANSITION && |
+ details.array_type() == array_type) { |
+ return descriptors->GetValue(index); |
+ } else { |
+ safe_to_add_transition = false; |
+ } |
+ } |
+ } |
+ |
+ // No transition to an existing external array map. Make a new one. |
+ Object* obj; |
+ { MaybeObject* maybe_map = CopyDropTransitions(); |
+ if (!maybe_map->ToObject(&obj)) return maybe_map; |
+ } |
+ Map* new_map = Map::cast(obj); |
+ |
+ new_map->set_has_fast_elements(false); |
+ new_map->set_has_external_array_elements(true); |
+ GetIsolate()->counters()->map_to_external_array_elements()->Increment(); |
+ |
+ // Only remember the map transition if the object's map is NOT equal to the |
+ // global object_function's map and there is not an already existing |
+ // non-matching external array transition. |
+ bool allow_map_transition = |
+ safe_to_add_transition && |
+ (GetIsolate()->context()->global_context()->object_function()->map() != |
+ map()); |
+ if (allow_map_transition) { |
+ // Allocate new instance descriptors for the old map with map transition. |
+ ExternalArrayTransitionDescriptor desc(external_array_sentinel_name, |
+ Map::cast(new_map), |
+ array_type); |
+ Object* new_descriptors; |
+ MaybeObject* maybe_new_descriptors = descriptors->CopyInsert( |
+ &desc, |
+ KEEP_TRANSITIONS); |
+ if (!maybe_new_descriptors->ToObject(&new_descriptors)) { |
+ return maybe_new_descriptors; |
+ } |
+ descriptors = DescriptorArray::cast(new_descriptors); |
+ set_instance_descriptors(descriptors); |
+ } |
+ |
+ return new_map; |
+} |
+ |
+ |
void JSObject::LocalLookupRealNamedProperty(String* name, |
LookupResult* result) { |
if (IsJSGlobalProxy()) { |
@@ -2051,6 +2131,7 @@ MaybeObject* JSObject::SetProperty(LookupResult* result, |
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); |
} |
case NULL_DESCRIPTOR: |
+ case EXTERNAL_ARRAY_TRANSITION: |
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); |
default: |
UNREACHABLE(); |
@@ -2130,6 +2211,7 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( |
// if the value is a function. |
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); |
case NULL_DESCRIPTOR: |
+ case EXTERNAL_ARRAY_TRANSITION: |
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); |
default: |
UNREACHABLE(); |
@@ -5484,6 +5566,7 @@ void Map::CreateBackPointers() { |
DescriptorArray* descriptors = instance_descriptors(); |
for (int i = 0; i < descriptors->number_of_descriptors(); i++) { |
if (descriptors->GetType(i) == MAP_TRANSITION || |
+ descriptors->GetType(i) == EXTERNAL_ARRAY_TRANSITION || |
descriptors->GetType(i) == CONSTANT_TRANSITION) { |
// Get target. |
Map* target = Map::cast(descriptors->GetValue(i)); |
@@ -5526,6 +5609,7 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) { |
// non-live object. |
PropertyDetails details(Smi::cast(contents->get(i + 1))); |
if (details.type() == MAP_TRANSITION || |
+ details.type() == EXTERNAL_ARRAY_TRANSITION || |
details.type() == CONSTANT_TRANSITION) { |
Map* target = reinterpret_cast<Map*>(contents->get(i)); |
ASSERT(target->IsHeapObject()); |
@@ -6399,6 +6483,7 @@ const char* Code::PropertyType2String(PropertyType type) { |
case CALLBACKS: return "CALLBACKS"; |
case INTERCEPTOR: return "INTERCEPTOR"; |
case MAP_TRANSITION: return "MAP_TRANSITION"; |
+ case EXTERNAL_ARRAY_TRANSITION: return "EXTERNAL_ARRAY_TRANSITION"; |
case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION"; |
case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR"; |
} |