Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index 258390c4d5cb2b62ff079e31d90de3e5595b6fcb..b0b4bac8fc9d677f2cf258ff74fe9558970149db 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -708,6 +708,13 @@ Handle<Object> JSObject::DeleteNormalizedProperty(Handle<JSObject> object, |
// the hole value. |
Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); |
DCHECK(new_map->is_dictionary_map()); |
+#if TRACE_MAPS |
+ if (FLAG_trace_maps) { |
+ PrintF("[TraceMaps: GlobalDeleteNormalized from= %p to= %p ]\n", |
+ reinterpret_cast<void*>(object->map()), |
+ reinterpret_cast<void*>(*new_map)); |
+ } |
+#endif |
JSObject::MigrateToMap(object, new_map); |
} |
Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry))); |
@@ -2150,7 +2157,7 @@ Handle<Map> Map::CopyGeneralizeAllRepresentations(Handle<Map> map, |
PropertyAttributes attributes, |
const char* reason) { |
Isolate* isolate = map->GetIsolate(); |
- Handle<Map> new_map = Copy(map); |
+ Handle<Map> new_map = Copy(map, reason); |
DescriptorArray* descriptors = new_map->instance_descriptors(); |
int length = descriptors->number_of_descriptors(); |
@@ -2459,8 +2466,8 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map, |
// Check the state of the root map. |
Handle<Map> root_map(old_map->FindRootMap(), isolate); |
if (!old_map->EquivalentToForTransition(*root_map)) { |
- return CopyGeneralizeAllRepresentations( |
- old_map, modify_index, store_mode, "not equivalent"); |
+ return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode, |
+ "GenAll_NotEquivalent"); |
} |
int root_nof = root_map->NumberOfOwnDescriptors(); |
if (modify_index < root_nof) { |
@@ -2469,8 +2476,8 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map, |
(old_details.type() == FIELD && |
(!new_field_type->NowIs(old_descriptors->GetFieldType(modify_index)) || |
!new_representation.fits_into(old_details.representation())))) { |
- return CopyGeneralizeAllRepresentations( |
- old_map, modify_index, store_mode, "root modification"); |
+ return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode, |
+ "GenAll_RootModification"); |
} |
} |
@@ -2493,8 +2500,8 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map, |
if ((tmp_type == CALLBACKS || old_type == CALLBACKS) && |
(tmp_type != old_type || |
tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i))) { |
- return CopyGeneralizeAllRepresentations( |
- old_map, modify_index, store_mode, "incompatible"); |
+ return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode, |
+ "GenAll_Incompatible"); |
} |
Representation old_representation = old_details.representation(); |
Representation tmp_representation = tmp_details.representation(); |
@@ -2559,8 +2566,8 @@ Handle<Map> Map::GeneralizeRepresentation(Handle<Map> old_map, |
if ((tmp_details.type() == CALLBACKS || old_details.type() == CALLBACKS) && |
(tmp_details.type() != old_details.type() || |
tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i))) { |
- return CopyGeneralizeAllRepresentations( |
- old_map, modify_index, store_mode, "incompatible"); |
+ return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode, |
+ "GenAll_Incompatible"); |
} |
target_map = tmp_map; |
} |
@@ -4260,11 +4267,12 @@ void HeapObject::UpdateMapCodeCache(Handle<HeapObject> object, |
void JSObject::NormalizeProperties(Handle<JSObject> object, |
PropertyNormalizationMode mode, |
- int expected_additional_properties) { |
+ int expected_additional_properties, |
+ const char* reason) { |
if (!object->HasFastProperties()) return; |
Handle<Map> map(object->map()); |
- Handle<Map> new_map = Map::Normalize(map, mode); |
+ Handle<Map> new_map = Map::Normalize(map, mode, reason); |
MigrateFastToSlow(object, new_map, expected_additional_properties); |
} |
@@ -4372,7 +4380,8 @@ void JSObject::MigrateFastToSlow(Handle<JSObject> object, |
void JSObject::MigrateSlowToFast(Handle<JSObject> object, |
- int unused_property_fields) { |
+ int unused_property_fields, |
+ const char* reason) { |
if (object->HasFastProperties()) return; |
DCHECK(!object->IsGlobalObject()); |
Isolate* isolate = object->GetIsolate(); |
@@ -4414,6 +4423,14 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, |
Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); |
new_map->set_dictionary_map(false); |
+#if TRACE_MAPS |
+ if (FLAG_trace_maps) { |
+ PrintF("[TraceMaps: SlowToFast from= %p to= %p reason= %s ]\n", |
+ reinterpret_cast<void*>(object->map()), |
+ reinterpret_cast<void*>(*new_map), reason); |
+ } |
+#endif |
+ |
if (instance_descriptor_length == 0) { |
DisallowHeapAllocation no_gc; |
DCHECK_LE(unused_property_fields, inobject_props); |
@@ -5074,7 +5091,7 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object, |
!(object->IsJSGlobalProxy() && holder->IsJSGlobalObject())) { |
return it.isolate()->factory()->true_value(); |
} |
- NormalizeProperties(holder, mode, 0); |
+ NormalizeProperties(holder, mode, 0, "DeletingProperty"); |
Handle<Object> result = |
DeleteNormalizedProperty(holder, name, delete_mode); |
ReoptimizeIfPrototype(holder); |
@@ -5292,7 +5309,7 @@ MaybeHandle<Object> JSObject::PreventExtensions(Handle<JSObject> object) { |
// Do a map transition, other objects with this map may still |
// be extensible. |
// TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. |
- Handle<Map> new_map = Map::Copy(handle(object->map())); |
+ Handle<Map> new_map = Map::Copy(handle(object->map()), "PreventExtensions"); |
new_map->set_is_extensible(false); |
JSObject::MigrateToMap(object, new_map); |
@@ -5404,11 +5421,11 @@ MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) { |
} else { |
DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map()); |
// Slow path: need to normalize properties for safety |
- NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0); |
+ NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0, "SlowFreeze"); |
// Create a new map, since other objects with this map may be extensible. |
// TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. |
- Handle<Map> new_map = Map::Copy(handle(object->map())); |
+ Handle<Map> new_map = Map::Copy(handle(object->map()), "SlowCopyForFreeze"); |
new_map->freeze(); |
new_map->set_is_extensible(false); |
new_map->set_elements_kind(DICTIONARY_ELEMENTS); |
@@ -5450,7 +5467,7 @@ void JSObject::SetObserved(Handle<JSObject> object) { |
} else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) { |
new_map = Map::CopyForObserved(old_map); |
} else { |
- new_map = Map::Copy(old_map); |
+ new_map = Map::Copy(old_map, "SlowObserved"); |
new_map->set_is_observed(); |
} |
JSObject::MigrateToMap(object, new_map); |
@@ -6167,13 +6184,20 @@ void JSObject::SetPropertyCallback(Handle<JSObject> object, |
? KEEP_INOBJECT_PROPERTIES |
: CLEAR_INOBJECT_PROPERTIES; |
// Normalize object to make this operation simple. |
- NormalizeProperties(object, mode, 0); |
+ NormalizeProperties(object, mode, 0, "SetPropertyCallback"); |
// 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 (object->IsGlobalObject()) { |
Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); |
DCHECK(new_map->is_dictionary_map()); |
+#if TRACE_MAPS |
+ if (FLAG_trace_maps) { |
+ PrintF("[TraceMaps: GlobalPropertyCallback from= %p to= %p ]\n", |
+ reinterpret_cast<void*>(object->map()), |
+ reinterpret_cast<void*>(*new_map)); |
+ } |
+#endif |
JSObject::MigrateToMap(object, new_map); |
// When running crankshaft, changing the map is not enough. We |
@@ -6494,8 +6518,8 @@ Handle<Map> Map::RawCopy(Handle<Map> map, int instance_size) { |
} |
-Handle<Map> Map::Normalize(Handle<Map> fast_map, |
- PropertyNormalizationMode mode) { |
+Handle<Map> Map::Normalize(Handle<Map> fast_map, PropertyNormalizationMode mode, |
+ const char* reason) { |
DCHECK(!fast_map->is_dictionary_map()); |
Isolate* isolate = fast_map->GetIsolate(); |
@@ -6534,6 +6558,13 @@ Handle<Map> Map::Normalize(Handle<Map> fast_map, |
cache->Set(fast_map, new_map); |
isolate->counters()->normalized_maps()->Increment(); |
} |
+#if TRACE_MAPS |
+ if (FLAG_trace_maps) { |
+ PrintF("[TraceMaps: Normalize from= %p to= %p reason= %s ]\n", |
+ reinterpret_cast<void*>(*fast_map), |
+ reinterpret_cast<void*>(*new_map), reason); |
+ } |
+#endif |
} |
fast_map->NotifyLeafMapLayoutChange(); |
return new_map; |
@@ -6616,11 +6647,41 @@ Handle<Map> Map::ShareDescriptor(Handle<Map> map, |
} |
+#if TRACE_MAPS |
+ |
+// static |
+void Map::TraceTransition(const char* what, Map* from, Map* to, Name* name) { |
+ if (FLAG_trace_maps) { |
+ PrintF("[TraceMaps: %s from= %p to= %p name= ", what, |
+ reinterpret_cast<void*>(from), reinterpret_cast<void*>(to)); |
+ name->NameShortPrint(); |
+ PrintF(" ]\n"); |
+ } |
+} |
+ |
+ |
+// static |
+void Map::TraceAllTransitions(Map* map) { |
+ if (!map->HasTransitionArray()) return; |
+ TransitionArray* transitions = map->transitions(); |
+ for (int i = 0; i < transitions->number_of_transitions(); ++i) { |
+ Map* target = transitions->GetTarget(i); |
+ Map::TraceTransition("Transition", map, target, transitions->GetKey(i)); |
+ Map::TraceAllTransitions(target); |
+ } |
+} |
+ |
+#endif // TRACE_MAPS |
+ |
+ |
void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child, |
Handle<Name> name, SimpleTransitionFlag flag) { |
parent->set_owns_descriptors(false); |
if (parent->is_prototype_map()) { |
DCHECK(child->is_prototype_map()); |
+#if TRACE_MAPS |
+ Map::TraceTransition("NoTransition", *parent, *child, *name); |
+#endif |
} else { |
Handle<TransitionArray> transitions = |
TransitionArray::Insert(parent, name, child, flag); |
@@ -6629,6 +6690,9 @@ void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child, |
parent->set_transitions(*transitions); |
} |
child->SetBackPointer(*parent); |
+#if TRACE_MAPS |
+ Map::TraceTransition("Transition", *parent, *child, *name); |
+#endif |
} |
} |
@@ -6637,6 +6701,7 @@ Handle<Map> Map::CopyReplaceDescriptors(Handle<Map> map, |
Handle<DescriptorArray> descriptors, |
TransitionFlag flag, |
MaybeHandle<Name> maybe_name, |
+ const char* reason, |
SimpleTransitionFlag simple_flag) { |
DCHECK(descriptors->IsSortedNoDuplicates()); |
@@ -6658,6 +6723,16 @@ Handle<Map> Map::CopyReplaceDescriptors(Handle<Map> map, |
} |
} |
} |
+#if TRACE_MAPS |
+ if (FLAG_trace_maps && |
+ // Mirror conditions above that did not call ConnectTransition(). |
+ (map->is_prototype_map() || |
+ !(flag == INSERT_TRANSITION && map->CanHaveMoreTransitions()))) { |
+ PrintF("[TraceMaps: ReplaceDescriptors from= %p to= %p reason= %s ]\n", |
+ reinterpret_cast<void*>(*map), reinterpret_cast<void*>(*result), |
+ reason); |
+ } |
+#endif |
return result; |
} |
@@ -6726,7 +6801,7 @@ Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind, |
// In case the map did not own its own descriptors, a split is forced by |
// copying the map; creating a new descriptor array cell. |
// Create a new free-floating map only if we are not allowed to store it. |
- Handle<Map> new_map = Copy(map); |
+ Handle<Map> new_map = Copy(map, "CopyAsElementsKind"); |
new_map->set_elements_kind(kind); |
@@ -6750,7 +6825,7 @@ Handle<Map> Map::CopyForObserved(Handle<Map> map) { |
new_map = CopyDropDescriptors(map); |
} else { |
DCHECK(!map->is_prototype_map()); |
- new_map = Copy(map); |
+ new_map = Copy(map, "CopyForObserved"); |
} |
new_map->set_is_observed(); |
@@ -6766,18 +6841,20 @@ Handle<Map> Map::CopyForObserved(Handle<Map> map) { |
} |
-Handle<Map> Map::Copy(Handle<Map> map) { |
+Handle<Map> Map::Copy(Handle<Map> map, const char* reason) { |
Handle<DescriptorArray> descriptors(map->instance_descriptors()); |
int number_of_own_descriptors = map->NumberOfOwnDescriptors(); |
Handle<DescriptorArray> new_descriptors = |
DescriptorArray::CopyUpTo(descriptors, number_of_own_descriptors); |
return CopyReplaceDescriptors(map, new_descriptors, OMIT_TRANSITION, |
- MaybeHandle<Name>(), SPECIAL_TRANSITION); |
+ MaybeHandle<Name>(), reason, |
+ SPECIAL_TRANSITION); |
} |
Handle<Map> Map::Create(Isolate* isolate, int inobject_properties) { |
- Handle<Map> copy = Copy(handle(isolate->object_function()->initial_map())); |
+ Handle<Map> copy = |
+ Copy(handle(isolate->object_function()->initial_map()), "MapCreate"); |
// Check that we do not overflow the instance size when adding the extra |
// inobject properties. If the instance size overflows, we allocate as many |
@@ -6808,7 +6885,7 @@ Handle<Map> Map::CopyForFreeze(Handle<Map> map) { |
handle(map->instance_descriptors(), isolate), num_descriptors, FROZEN); |
Handle<Map> new_map = CopyReplaceDescriptors( |
map, new_desc, INSERT_TRANSITION, isolate->factory()->frozen_symbol(), |
- SPECIAL_TRANSITION); |
+ "CopyForFreeze", SPECIAL_TRANSITION); |
new_map->freeze(); |
new_map->set_is_extensible(false); |
new_map->set_elements_kind(DICTIONARY_ELEMENTS); |
@@ -6898,7 +6975,17 @@ Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name, |
Handle<Map> result; |
if (!maybe_map.ToHandle(&result)) { |
- return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES); |
+#if TRACE_MAPS |
+ if (FLAG_trace_maps) { |
+ Vector<char> name_buffer = Vector<char>::New(100); |
+ name->NameShortPrint(name_buffer); |
+ Vector<char> buffer = Vector<char>::New(128); |
+ SNPrintF(buffer, "TooManyFastProperties %s", name_buffer.start()); |
+ return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, buffer.start()); |
+ } |
+#endif |
+ return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, |
+ "TooManyFastProperties"); |
} |
return result; |
@@ -6912,8 +6999,8 @@ Handle<Map> Map::ReconfigureDataProperty(Handle<Map> map, int descriptor, |
// For now, give up on transitioning and just create a unique map. |
// TODO(verwaest/ishell): Cache transitions with different attributes. |
- return CopyGeneralizeAllRepresentations(map, descriptor, FORCE_FIELD, |
- attributes, "attributes mismatch"); |
+ return CopyGeneralizeAllRepresentations( |
+ map, descriptor, FORCE_FIELD, attributes, "GenAll_AttributesMismatch"); |
} |
@@ -6928,7 +7015,7 @@ Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map, |
if (map->is_dictionary_map()) { |
// For global objects, property cells are inlined. We need to change the |
// map. |
- if (map->IsGlobalObjectMap()) return Copy(map); |
+ if (map->IsGlobalObjectMap()) return Copy(map, "GlobalAccessor"); |
return map; |
} |
@@ -6951,12 +7038,12 @@ Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map, |
Handle<Object> maybe_pair(descriptors->GetValue(descriptor), isolate); |
if (!maybe_pair->IsAccessorPair()) { |
- return Map::Normalize(map, mode); |
+ return Map::Normalize(map, mode, "TransitionToAccessorFromNonPair"); |
} |
Handle<AccessorPair> pair = Handle<AccessorPair>::cast(maybe_pair); |
if (pair->get(component) != *accessor) { |
- return Map::Normalize(map, mode); |
+ return Map::Normalize(map, mode, "TransitionToDifferentAccessor"); |
} |
return transition; |
@@ -6967,33 +7054,33 @@ Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map, |
int descriptor = old_descriptors->SearchWithCache(*name, *map); |
if (descriptor != DescriptorArray::kNotFound) { |
if (descriptor != map->LastAdded()) { |
- return Map::Normalize(map, mode); |
+ return Map::Normalize(map, mode, "AccessorsOverwritingNonLast"); |
} |
PropertyDetails old_details = old_descriptors->GetDetails(descriptor); |
if (old_details.type() != CALLBACKS) { |
- return Map::Normalize(map, mode); |
+ return Map::Normalize(map, mode, "AccessorsOverwritingNonAccessors"); |
} |
if (old_details.attributes() != attributes) { |
- return Map::Normalize(map, mode); |
+ return Map::Normalize(map, mode, "AccessorsWithAttributes"); |
} |
Handle<Object> maybe_pair(old_descriptors->GetValue(descriptor), isolate); |
if (!maybe_pair->IsAccessorPair()) { |
- return Map::Normalize(map, mode); |
+ return Map::Normalize(map, mode, "AccessorsOverwritingNonPair"); |
} |
Object* current = Handle<AccessorPair>::cast(maybe_pair)->get(component); |
if (current == *accessor) return map; |
if (!current->IsTheHole()) { |
- return Map::Normalize(map, mode); |
+ return Map::Normalize(map, mode, "AccessorsOverwritingAccessors"); |
} |
pair = AccessorPair::Copy(Handle<AccessorPair>::cast(maybe_pair)); |
} else if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors || |
map->TooManyFastProperties(CERTAINLY_NOT_STORE_FROM_KEYED)) { |
- return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES); |
+ return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, "TooManyAccessors"); |
} else { |
pair = isolate->factory()->NewAccessorPair(); |
} |
@@ -7024,7 +7111,7 @@ Handle<Map> Map::CopyAddDescriptor(Handle<Map> map, |
new_descriptors->Append(descriptor); |
return CopyReplaceDescriptors(map, new_descriptors, flag, |
- descriptor->GetKey(), |
+ descriptor->GetKey(), "CopyAddDescriptor", |
SIMPLE_PROPERTY_TRANSITION); |
} |
@@ -7121,7 +7208,8 @@ Handle<Map> Map::CopyReplaceDescriptor(Handle<Map> map, |
(insertion_index == descriptors->number_of_descriptors() - 1) |
? SIMPLE_PROPERTY_TRANSITION |
: PROPERTY_TRANSITION; |
- return CopyReplaceDescriptors(map, new_descriptors, flag, key, simple_flag); |
+ return CopyReplaceDescriptors(map, new_descriptors, flag, key, |
+ "CopyReplaceDescriptor", simple_flag); |
} |
@@ -9414,14 +9502,15 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object, |
if (object->IsJSGlobalProxy()) return; |
if (mode == FAST_PROTOTYPE && !object->map()->is_prototype_map()) { |
// First normalize to ensure all JSFunctions are CONSTANT. |
- JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0); |
+ JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0, |
+ "NormalizeAsPrototype"); |
} |
if (!object->HasFastProperties()) { |
- JSObject::MigrateSlowToFast(object, 0); |
+ JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype"); |
} |
if (mode == FAST_PROTOTYPE && object->HasFastProperties() && |
!object->map()->is_prototype_map()) { |
- Handle<Map> new_map = Map::Copy(handle(object->map())); |
+ Handle<Map> new_map = Map::Copy(handle(object->map()), "CopyAsPrototype"); |
JSObject::MigrateToMap(object, new_map); |
object->map()->set_is_prototype_map(true); |
} |
@@ -9491,7 +9580,7 @@ void JSFunction::SetInstancePrototype(Handle<JSFunction> function, |
// into the initial map where it belongs. |
function->set_prototype_or_initial_map(*value); |
} else { |
- Handle<Map> new_map = Map::Copy(initial_map); |
+ Handle<Map> new_map = Map::Copy(initial_map, "SetInstancePrototype"); |
JSFunction::SetInitialMap(function, new_map, value); |
// If the function is used as the global Array function, cache the |
@@ -9531,7 +9620,7 @@ void JSFunction::SetPrototype(Handle<JSFunction> function, |
// Copy the map so this does not affect unrelated functions. |
// Remove map transitions because they point to maps with a |
// different prototype. |
- Handle<Map> new_map = Map::Copy(handle(function->map())); |
+ Handle<Map> new_map = Map::Copy(handle(function->map()), "SetPrototype"); |
JSObject::MigrateToMap(function, new_map); |
new_map->set_constructor(*value); |
@@ -9579,6 +9668,13 @@ void JSFunction::SetInitialMap(Handle<JSFunction> function, Handle<Map> map, |
map->set_prototype(*prototype); |
function->set_prototype_or_initial_map(*map); |
map->set_constructor(*function); |
+#if TRACE_MAPS |
+ if (FLAG_trace_maps) { |
+ PrintF("[TraceMaps: InitialMap map= %p SFI= %d_%s ]\n", |
+ reinterpret_cast<void*>(*map), function->shared()->unique_id(), |
+ function->shared()->DebugName()->ToCString().get()); |
+ } |
+#endif |
} |
@@ -11700,7 +11796,7 @@ Handle<Map> Map::TransitionToPrototype(Handle<Map> map, |
Handle<Object> prototype) { |
Handle<Map> new_map = GetPrototypeTransition(map, prototype); |
if (new_map.is_null()) { |
- new_map = Copy(map); |
+ new_map = Copy(map, "TransitionToPrototype"); |
PutPrototypeTransition(map, prototype, new_map); |
new_map->set_prototype(*prototype); |
} |