Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index dd2d9b68c1826f6a6ed4931e155ac4ea8e89d82f..5f1b6e6b868ab2de399e1e9b88b1a549c72c5ca9 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -657,8 +657,8 @@ MaybeObject* JSObject::SetNormalizedProperty(Name* name, |
ASSERT(enumeration_index > 0); |
} |
- details = PropertyDetails( |
- details.attributes(), details.type(), enumeration_index); |
+ details = PropertyDetails(details.attributes(), details.type(), |
+ Representation::None(), enumeration_index); |
if (IsGlobalObject()) { |
JSGlobalPropertyCell* cell = |
@@ -1715,10 +1715,10 @@ MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map, |
if (map()->unused_property_fields() == 0) { |
int new_unused = new_map->unused_property_fields(); |
FixedArray* values; |
- { MaybeObject* maybe_values = |
- properties()->CopySize(properties()->length() + new_unused + 1); |
- if (!maybe_values->To(&values)) return maybe_values; |
- } |
+ MaybeObject* maybe_values = |
+ properties()->CopySize(properties()->length() + new_unused + 1); |
+ if (!maybe_values->To(&values)) return maybe_values; |
+ |
set_properties(values); |
} |
set_map(new_map); |
@@ -1774,7 +1774,8 @@ MaybeObject* JSObject::AddFastProperty(Name* name, |
int index = map()->NextFreePropertyIndex(); |
// Allocate new instance descriptors with (name, index) added |
- FieldDescriptor new_field(name, index, attributes, 0); |
+ FieldDescriptor new_field( |
+ name, index, attributes, value->OptimalRepresentation(), 0); |
ASSERT(index < map()->inobject_properties() || |
(index - map()->inobject_properties()) < properties()->length() || |
@@ -1849,7 +1850,8 @@ MaybeObject* JSObject::AddSlowProperty(Name* name, |
// Assign an enumeration index to the property and update |
// SetNextEnumerationIndex. |
int index = dict->NextEnumerationIndex(); |
- PropertyDetails details = PropertyDetails(attributes, NORMAL, index); |
+ PropertyDetails details = PropertyDetails( |
+ attributes, NORMAL, Representation::None(), index); |
dict->SetNextEnumerationIndex(index + 1); |
dict->SetEntry(entry, name, store_value, details); |
return value; |
@@ -1861,7 +1863,8 @@ MaybeObject* JSObject::AddSlowProperty(Name* name, |
} |
JSGlobalPropertyCell::cast(store_value)->set_value(value); |
} |
- PropertyDetails details = PropertyDetails(attributes, NORMAL); |
+ PropertyDetails details = PropertyDetails( |
+ attributes, NORMAL, Representation::None()); |
Object* result; |
{ MaybeObject* maybe_result = dict->Add(name, store_value, details); |
if (!maybe_result->ToObject(&result)) return maybe_result; |
@@ -2002,7 +2005,8 @@ MaybeObject* JSObject::ReplaceSlowProperty(Name* name, |
new_enumeration_index = dictionary->DetailsAt(old_index).dictionary_index(); |
} |
- PropertyDetails new_details(attributes, NORMAL, new_enumeration_index); |
+ PropertyDetails new_details( |
+ attributes, NORMAL, Representation::None(), new_enumeration_index); |
return SetNormalizedProperty(name, value, new_details); |
} |
@@ -2028,7 +2032,6 @@ MaybeObject* JSObject::ConvertTransitionToMapTransition( |
// TODO(verwaest): From here on we lose existing map transitions, causing |
// invalid back pointers. This will change once we can store multiple |
// transitions with the same key. |
- |
bool owned_descriptors = old_map->owns_descriptors(); |
if (owned_descriptors || |
old_target->instance_descriptors() == old_map->instance_descriptors()) { |
@@ -2049,6 +2052,8 @@ MaybeObject* JSObject::ConvertTransitionToMapTransition( |
old_map->set_owns_descriptors(false); |
} |
+ old_target->DeprecateTransitionTree(); |
+ |
old_map->SetTransition(transition_index, new_map); |
new_map->SetBackPointer(old_map); |
return result; |
@@ -2067,7 +2072,8 @@ MaybeObject* JSObject::ConvertDescriptorToField(Name* name, |
} |
int index = map()->NextFreePropertyIndex(); |
- FieldDescriptor new_field(name, index, attributes, 0); |
+ FieldDescriptor new_field( |
+ name, index, attributes, new_value->OptimalRepresentation(), 0); |
// Make a new map for the object. |
Map* new_map; |
@@ -2096,6 +2102,406 @@ MaybeObject* JSObject::ConvertDescriptorToField(Name* name, |
} |
+const char* Representation::Mnemonic() const { |
+ switch (kind_) { |
+ case kNone: return "v"; |
+ case kTagged: return "t"; |
+ case kSmi: return "s"; |
+ case kDouble: return "d"; |
+ case kInteger32: return "i"; |
+ case kExternal: return "x"; |
+ default: |
+ UNREACHABLE(); |
+ return NULL; |
+ } |
+} |
+ |
+ |
+enum RightTrimMode { FROM_GC, FROM_MUTATOR }; |
+ |
+ |
+static void ZapEndOfFixedArray(Address new_end, int to_trim) { |
+ // If we are doing a big trim in old space then we zap the space. |
+ Object** zap = reinterpret_cast<Object**>(new_end); |
+ zap++; // Header of filler must be at least one word so skip that. |
+ for (int i = 1; i < to_trim; i++) { |
+ *zap++ = Smi::FromInt(0); |
+ } |
+} |
+ |
+ |
+template<RightTrimMode trim_mode> |
+static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) { |
+ ASSERT(elms->map() != HEAP->fixed_cow_array_map()); |
+ // For now this trick is only applied to fixed arrays in new and paged space. |
+ ASSERT(!HEAP->lo_space()->Contains(elms)); |
+ |
+ const int len = elms->length(); |
+ |
+ ASSERT(to_trim < len); |
+ |
+ Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim); |
+ |
+ if (trim_mode != FROM_GC || Heap::ShouldZapGarbage()) { |
+ ZapEndOfFixedArray(new_end, to_trim); |
+ } |
+ |
+ int size_delta = to_trim * kPointerSize; |
+ |
+ // Technically in new space this write might be omitted (except for |
+ // debug mode which iterates through the heap), but to play safer |
+ // we still do it. |
+ heap->CreateFillerObjectAt(new_end, size_delta); |
+ |
+ elms->set_length(len - to_trim); |
+ |
+ // Maintain marking consistency for IncrementalMarking. |
+ if (Marking::IsBlack(Marking::MarkBitFrom(elms))) { |
+ if (trim_mode == FROM_GC) { |
+ MemoryChunk::IncrementLiveBytesFromGC(elms->address(), -size_delta); |
+ } else { |
+ MemoryChunk::IncrementLiveBytesFromMutator(elms->address(), -size_delta); |
+ } |
+ } |
+} |
+ |
+ |
+bool Map::InstancesNeedRewriting(int target_number_of_fields, |
+ int target_inobject, |
+ int target_unused) { |
+ // If fields were added (or removed), rewrite the instance. |
+ int number_of_fields = NumberOfFields(); |
+ ASSERT(target_number_of_fields >= number_of_fields); |
+ if (target_number_of_fields != number_of_fields) return true; |
+ // If no fields were added, and no inobject properties were removed, setting |
+ // the map is sufficient. |
+ if (target_inobject == inobject_properties()) return false; |
+ // In-object slack tracking may have reduced the object size of the new map. |
+ // In that case, succeed if all existing fields were inobject, and they still |
+ // fit within the new inobject size. |
+ ASSERT(target_inobject < inobject_properties()); |
+ if (target_number_of_fields <= target_inobject) { |
+ ASSERT(target_number_of_fields + target_unused == target_inobject); |
+ return false; |
+ } |
+ // Otherwise, properties will need to be moved to the backing store. |
+ return true; |
+} |
+ |
+ |
+// To migrate an instance to a map: |
+// - First check whether the instance needs to be rewritten. If not, simply |
+// change the map. |
+// - Otherwise, allocate a fixed array large enough to hold all fields, in |
+// addition to unused space. |
+// - Copy all existing properties in, in the following order: backing store |
+// properties, unused fields, inobject properties. |
+// - If all allocation succeeded, commit the state atomically: |
+// * Copy inobject properties from the backing store back into the object. |
+// * Trim the difference in instance size of the object. This also cleanly |
+// frees inobject properties that moved to the backing store. |
+// * If there are properties left in the backing store, trim of the space used |
+// to temporarily store the inobject properties. |
+// * If there are properties left in the backing store, install the backing |
+// store. |
+MaybeObject* JSObject::MigrateToMap(Map* new_map) { |
+ Heap* heap = GetHeap(); |
+ Map* old_map = map(); |
+ int number_of_fields = new_map->NumberOfFields(); |
+ int inobject = new_map->inobject_properties(); |
+ int unused = new_map->unused_property_fields(); |
+ |
+ // Nothing to do if no functions were converted to fields. |
+ if (!old_map->InstancesNeedRewriting(number_of_fields, inobject, unused)) { |
+ set_map(new_map); |
+ return this; |
+ } |
+ |
+ int total_size = number_of_fields + unused; |
+ int external = total_size - inobject; |
+ FixedArray* array; |
+ MaybeObject* maybe_array = heap->AllocateFixedArray(total_size); |
+ if (!maybe_array->To(&array)) return maybe_array; |
+ |
+ DescriptorArray* old_descriptors = old_map->instance_descriptors(); |
+ DescriptorArray* new_descriptors = new_map->instance_descriptors(); |
+ int descriptors = new_map->NumberOfOwnDescriptors(); |
+ |
+ for (int i = 0; i < descriptors; i++) { |
+ PropertyDetails details = new_descriptors->GetDetails(i); |
+ if (details.type() != FIELD) continue; |
+ PropertyDetails old_details = old_descriptors->GetDetails(i); |
+ ASSERT(old_details.type() == CONSTANT_FUNCTION || |
+ old_details.type() == FIELD); |
+ Object* value = old_details.type() == CONSTANT_FUNCTION |
+ ? old_descriptors->GetValue(i) |
+ : FastPropertyAt(old_descriptors->GetFieldIndex(i)); |
+ int target_index = new_descriptors->GetFieldIndex(i) - inobject; |
+ if (target_index < 0) target_index += total_size; |
+ array->set(target_index, value); |
+ } |
+ |
+ // From here on we cannot fail anymore. |
+ |
+ // Copy (real) inobject properties. If necessary, stop at number_of_fields to |
+ // avoid overwriting |one_pointer_filler_map|. |
+ int limit = Min(inobject, number_of_fields); |
+ for (int i = 0; i < limit; i++) { |
+ FastPropertyAtPut(i, array->get(external + i)); |
+ } |
+ |
+ // Create filler object past the new instance size. |
+ int new_instance_size = new_map->instance_size(); |
+ int instance_size_delta = old_map->instance_size() - new_instance_size; |
+ ASSERT(instance_size_delta >= 0); |
+ Address address = this->address() + new_instance_size; |
+ heap->CreateFillerObjectAt(address, instance_size_delta); |
+ |
+ // If there are properties in the new backing store, trim it to the correct |
+ // size and install the backing store into the object. |
+ if (external > 0) { |
+ RightTrimFixedArray<FROM_MUTATOR>(heap, array, inobject); |
+ set_properties(array); |
+ } |
+ |
+ set_map(new_map); |
+ |
+ return this; |
+} |
+ |
+ |
+MaybeObject* JSObject::GeneralizeFieldRepresentation( |
+ int modify_index, |
+ Representation new_representation) { |
+ Map* new_map; |
+ MaybeObject* maybe_new_map = |
+ map()->GeneralizeRepresentation(modify_index, new_representation); |
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map; |
+ ASSERT(map() != new_map || new_map->FindRootMap()->is_deprecated()); |
+ |
+ return MigrateToMap(new_map); |
+} |
+ |
+ |
+int Map::NumberOfFields() { |
+ DescriptorArray* descriptors = instance_descriptors(); |
+ int result = 0; |
+ for (int i = 0; i < NumberOfOwnDescriptors(); i++) { |
+ if (descriptors->GetDetails(i).type() == FIELD) result++; |
+ } |
+ return result; |
+} |
+ |
+ |
+MaybeObject* Map::CopyGeneralizeAllRepresentations() { |
+ Map* new_map; |
+ MaybeObject* maybe_map = this->Copy(); |
+ if (!maybe_map->To(&new_map)) return maybe_map; |
+ |
+ new_map->instance_descriptors()->InitializeRepresentations( |
+ Representation::Tagged()); |
+ return new_map; |
+} |
+ |
+ |
+void Map::DeprecateTransitionTree() { |
+ if (!FLAG_track_fields) return; |
+ if (is_deprecated()) return; |
+ if (HasTransitionArray()) { |
+ TransitionArray* transitions = this->transitions(); |
+ for (int i = 0; i < transitions->number_of_transitions(); i++) { |
+ transitions->GetTarget(i)->DeprecateTransitionTree(); |
+ } |
+ } |
+ deprecate(); |
+ dependent_code()->DeoptimizeDependentCodeGroup( |
+ GetIsolate(), DependentCode::kTransitionGroup); |
+ dependent_code()->DeoptimizeDependentCodeGroup( |
+ GetIsolate(), DependentCode::kPrototypeCheckGroup); |
+} |
+ |
+ |
+// Invalidates a transition target at |key|, and installs |new_descriptors| over |
+// the current instance_descriptors to ensure proper sharing of descriptor |
+// arrays. |
+void Map::DeprecateTarget(Name* key, DescriptorArray* new_descriptors) { |
+ if (HasTransitionArray()) { |
+ TransitionArray* transitions = this->transitions(); |
+ int transition = transitions->Search(key); |
+ if (transition != TransitionArray::kNotFound) { |
+ transitions->GetTarget(transition)->DeprecateTransitionTree(); |
+ } |
+ } |
+ |
+ // Don't overwrite the empty descriptor array. |
+ if (NumberOfOwnDescriptors() == 0) return; |
+ |
+ DescriptorArray* to_replace = instance_descriptors(); |
+ Map* current = this; |
+ while (current->instance_descriptors() == to_replace) { |
+ current->SetEnumLength(Map::kInvalidEnumCache); |
+ current->set_instance_descriptors(new_descriptors); |
+ Object* next = current->GetBackPointer(); |
+ if (next->IsUndefined()) break; |
+ current = Map::cast(next); |
+ } |
+ |
+ set_owns_descriptors(false); |
+} |
+ |
+ |
+Map* Map::FindRootMap() { |
+ Map* result = this; |
+ while (true) { |
+ Object* back = result->GetBackPointer(); |
+ if (back->IsUndefined()) return result; |
+ result = Map::cast(back); |
+ } |
+} |
+ |
+ |
+Map* Map::FindUpdatedMap(int verbatim, |
+ int length, |
+ DescriptorArray* descriptors) { |
+ // This can only be called on roots of transition trees. |
+ ASSERT(GetBackPointer()->IsUndefined()); |
+ |
+ Map* current = this; |
+ |
+ for (int i = verbatim; i < length; i++) { |
+ if (!current->HasTransitionArray()) break; |
+ Name* name = descriptors->GetKey(i); |
+ TransitionArray* transitions = current->transitions(); |
+ int transition = transitions->Search(name); |
+ if (transition == TransitionArray::kNotFound) break; |
+ current = transitions->GetTarget(transition); |
+ } |
+ |
+ return current; |
+} |
+ |
+ |
+Map* Map::FindLastMatchMap(int verbatim, |
+ int length, |
+ DescriptorArray* descriptors) { |
+ // This can only be called on roots of transition trees. |
+ ASSERT(GetBackPointer()->IsUndefined()); |
+ |
+ Map* current = this; |
+ |
+ for (int i = verbatim; i < length; i++) { |
+ if (!current->HasTransitionArray()) break; |
+ Name* name = descriptors->GetKey(i); |
+ TransitionArray* transitions = current->transitions(); |
+ int transition = transitions->Search(name); |
+ if (transition == TransitionArray::kNotFound) break; |
+ |
+ Map* next = transitions->GetTarget(transition); |
+ DescriptorArray* next_descriptors = next->instance_descriptors(); |
+ |
+ if (next_descriptors->GetValue(i) != descriptors->GetValue(i)) break; |
+ |
+ PropertyDetails details = descriptors->GetDetails(i); |
+ PropertyDetails next_details = next_descriptors->GetDetails(i); |
+ if (details.type() != next_details.type()) break; |
+ if (details.attributes() != next_details.attributes()) break; |
+ if (!details.representation().Equals(next_details.representation())) break; |
+ ASSERT(!details.IsDeleted()); |
+ ASSERT(!next_details.IsDeleted()); |
+ |
+ current = next; |
+ } |
+ return current; |
+} |
+ |
+ |
+// Generalize the representation of the descriptor at |modify_index|. |
+// This method rewrites the transition tree to reflect the new change. To avoid |
+// high degrees over polymorphism, and to stabilize quickly, on every rewrite |
+// the new type is deduced by merging the current type with any potential new |
+// (partial) version of the type in the transition tree. |
+// To do this, on each rewrite: |
+// - Search the root of the transition tree using FindRootMap. |
+// - Find |updated|, the newest matching version of this map using |
+// FindUpdatedMap. This uses the keys in the own map's descriptor array to |
+// walk the transition tree. |
+// - Merge/generalize the descriptor array of the current map and |updated|. |
+// - Generalize the |modify_index| descriptor using |new_representation|. |
+// - Walk the tree again starting from the root towards |updated|. Stop at |
+// |split_map|, the first map who's descriptor array does not match the merged |
+// descriptor array. |
+// - If |updated| == |split_map|, |updated| is in the expected state. Return it. |
+// - Otherwise, invalidate the outdated transition target from |updated|, and |
+// replace its transition tree with a new branch for the updated descriptors. |
+MaybeObject* Map::GeneralizeRepresentation(int modify_index, |
+ Representation new_representation) { |
+ Map* old_map = this; |
+ DescriptorArray* old_descriptors = old_map->instance_descriptors(); |
+ Representation old_reprepresentation = |
+ old_descriptors->GetDetails(modify_index).representation(); |
+ |
+ if (old_reprepresentation.IsNone()) { |
+ UNREACHABLE(); |
+ old_descriptors->SetRepresentation(modify_index, new_representation); |
+ return this; |
+ } |
+ |
+ int descriptors = old_map->NumberOfOwnDescriptors(); |
+ Map* root_map = old_map->FindRootMap(); |
+ |
+ if (!old_map->EquivalentToForTransition(root_map)) { |
+ return CopyGeneralizeAllRepresentations(); |
+ } |
+ |
+ int verbatim = root_map->NumberOfOwnDescriptors(); |
+ |
+ Map* updated = root_map->FindUpdatedMap( |
+ verbatim, descriptors, old_descriptors); |
+ // Check the state of the root map. |
+ DescriptorArray* updated_descriptors = updated->instance_descriptors(); |
+ |
+ DescriptorArray* new_descriptors; |
+ MaybeObject* maybe_descriptors = updated_descriptors->Merge( |
+ verbatim, |
+ updated->NumberOfOwnDescriptors(), |
+ descriptors, |
+ old_descriptors); |
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; |
+ |
+ old_reprepresentation = |
+ new_descriptors->GetDetails(modify_index).representation(); |
+ new_representation = new_representation.generalize(old_reprepresentation); |
+ new_descriptors->SetRepresentation(modify_index, new_representation); |
+ |
+ Map* split_map = root_map->FindLastMatchMap( |
+ verbatim, descriptors, new_descriptors); |
+ |
+ int split_descriptors = split_map->NumberOfOwnDescriptors(); |
+ // Check whether |split_map| matches what we were looking for. If so, return |
+ // it. |
+ if (descriptors == split_descriptors) return split_map; |
+ |
+ int descriptor = split_descriptors; |
+ split_map->DeprecateTarget( |
+ old_descriptors->GetKey(descriptor), new_descriptors); |
+ |
+ Map* new_map = split_map; |
+ // Add missing transitions. |
+ for (; descriptor < descriptors; descriptor++) { |
+ MaybeObject* maybe_map = new_map->CopyInstallDescriptors( |
+ descriptor, new_descriptors); |
+ if (!maybe_map->To(&new_map)) { |
+ // Create a handle for the last created map to ensure it stays alive |
+ // during GC. Its descriptor array is too large, but it will be |
+ // overwritten during retry anyway. |
+ Handle<Map>(new_map); |
+ } |
+ } |
+ |
+ new_map->set_owns_descriptors(true); |
+ return new_map; |
+} |
+ |
MaybeObject* JSObject::SetPropertyWithInterceptor( |
Name* name, |
@@ -2391,55 +2797,6 @@ MaybeObject* JSObject::SetPropertyViaPrototypes( |
} |
-enum RightTrimMode { FROM_GC, FROM_MUTATOR }; |
- |
- |
-static void ZapEndOfFixedArray(Address new_end, int to_trim) { |
- // If we are doing a big trim in old space then we zap the space. |
- Object** zap = reinterpret_cast<Object**>(new_end); |
- zap++; // Header of filler must be at least one word so skip that. |
- for (int i = 1; i < to_trim; i++) { |
- *zap++ = Smi::FromInt(0); |
- } |
-} |
- |
- |
-template<RightTrimMode trim_mode> |
-static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) { |
- ASSERT(elms->map() != HEAP->fixed_cow_array_map()); |
- // For now this trick is only applied to fixed arrays in new and paged space. |
- ASSERT(!HEAP->lo_space()->Contains(elms)); |
- |
- const int len = elms->length(); |
- |
- ASSERT(to_trim < len); |
- |
- Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim); |
- |
- if (trim_mode != FROM_GC || Heap::ShouldZapGarbage()) { |
- ZapEndOfFixedArray(new_end, to_trim); |
- } |
- |
- int size_delta = to_trim * kPointerSize; |
- |
- // Technically in new space this write might be omitted (except for |
- // debug mode which iterates through the heap), but to play safer |
- // we still do it. |
- heap->CreateFillerObjectAt(new_end, size_delta); |
- |
- elms->set_length(len - to_trim); |
- |
- // Maintain marking consistency for IncrementalMarking. |
- if (Marking::IsBlack(Marking::MarkBitFrom(elms))) { |
- if (trim_mode == FROM_GC) { |
- MemoryChunk::IncrementLiveBytesFromGC(elms->address(), -size_delta); |
- } else { |
- MemoryChunk::IncrementLiveBytesFromMutator(elms->address(), -size_delta); |
- } |
- } |
-} |
- |
- |
void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) { |
Handle<DescriptorArray> descriptors(map->instance_descriptors()); |
if (slack <= descriptors->NumberOfSlackDescriptors()) return; |
@@ -3103,18 +3460,27 @@ MUST_USE_RESULT Handle<Object> JSProxy::CallTrap(const char* name, |
} |
-void JSObject::AddFastPropertyUsingMap(Handle<JSObject> object, |
- Handle<Map> map) { |
+void JSObject::TransitionToMap(Handle<JSObject> object, Handle<Map> map) { |
CALL_HEAP_FUNCTION_VOID( |
object->GetIsolate(), |
- object->AddFastPropertyUsingMap(*map)); |
+ object->TransitionToMap(*map)); |
} |
-void JSObject::TransitionToMap(Handle<JSObject> object, Handle<Map> map) { |
+void JSObject::MigrateInstance(Handle<JSObject> object) { |
CALL_HEAP_FUNCTION_VOID( |
object->GetIsolate(), |
- object->TransitionToMap(*map)); |
+ object->MigrateInstance()); |
+} |
+ |
+ |
+Handle<Map> Map::GeneralizeRepresentation(Handle<Map> map, |
+ int modify_index, |
+ Representation new_representation) { |
+ CALL_HEAP_FUNCTION( |
+ map->GetIsolate(), |
+ map->GeneralizeRepresentation(modify_index, new_representation), |
+ Map); |
} |
@@ -3206,10 +3572,18 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup, |
case NORMAL: |
result = lookup->holder()->SetNormalizedProperty(lookup, *value); |
break; |
- case FIELD: |
+ case FIELD: { |
+ Representation representation = lookup->representation(); |
+ if (!value->FitsRepresentation(representation)) { |
+ MaybeObject* maybe_failure = |
+ lookup->holder()->GeneralizeFieldRepresentation( |
+ lookup->GetDescriptorIndex(), value->OptimalRepresentation()); |
+ if (maybe_failure->IsFailure()) return maybe_failure; |
+ } |
result = lookup->holder()->FastPropertyAtPut( |
lookup->GetFieldIndex().field_index(), *value); |
break; |
+ } |
case CONSTANT_FUNCTION: |
// Only replace the function if necessary. |
if (*value == lookup->GetConstantFunction()) return *value; |
@@ -3236,6 +3610,17 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup, |
if (details.type() == FIELD) { |
if (attributes == details.attributes()) { |
+ if (!value->FitsRepresentation(details.representation())) { |
+ MaybeObject* maybe_map = transition_map->GeneralizeRepresentation( |
+ descriptor, value->OptimalRepresentation()); |
+ if (!maybe_map->To(&transition_map)) return maybe_map; |
+ Object* back = transition_map->GetBackPointer(); |
+ if (back->IsMap()) { |
+ MaybeObject* maybe_failure = |
+ lookup->holder()->MigrateToMap(Map::cast(back)); |
+ if (maybe_failure->IsFailure()) return maybe_failure; |
+ } |
+ } |
int field_index = descriptors->GetFieldIndex(descriptor); |
result = lookup->holder()->AddFastPropertyUsingMap( |
transition_map, *name, *value, field_index); |
@@ -3366,14 +3751,22 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( |
MaybeObject* result = *value; |
switch (lookup.type()) { |
case NORMAL: { |
- PropertyDetails details = PropertyDetails(attributes, NORMAL); |
+ PropertyDetails details = PropertyDetails( |
+ attributes, NORMAL, Representation::None()); |
result = self->SetNormalizedProperty(*name, *value, details); |
break; |
} |
- case FIELD: |
+ case FIELD: { |
+ Representation representation = lookup.representation(); |
+ if (!value->FitsRepresentation(representation)) { |
+ MaybeObject* maybe_failure = self->GeneralizeFieldRepresentation( |
+ lookup.GetDescriptorIndex(), value->OptimalRepresentation()); |
+ if (maybe_failure->IsFailure()) return maybe_failure; |
+ } |
result = self->FastPropertyAtPut( |
lookup.GetFieldIndex().field_index(), *value); |
break; |
+ } |
case CONSTANT_FUNCTION: |
// Only replace the function if necessary. |
if (*value != lookup.GetConstantFunction()) { |
@@ -3396,6 +3789,16 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( |
if (details.type() == FIELD) { |
if (attributes == details.attributes()) { |
+ if (!value->FitsRepresentation(details.representation())) { |
+ MaybeObject* maybe_map = transition_map->GeneralizeRepresentation( |
+ descriptor, value->OptimalRepresentation()); |
+ if (!maybe_map->To(&transition_map)) return maybe_map; |
+ Object* back = transition_map->GetBackPointer(); |
+ if (back->IsMap()) { |
+ MaybeObject* maybe_failure = self->MigrateToMap(Map::cast(back)); |
+ if (maybe_failure->IsFailure()) return maybe_failure; |
+ } |
+ } |
int field_index = descriptors->GetFieldIndex(descriptor); |
result = self->AddFastPropertyUsingMap( |
transition_map, *name, *value, field_index); |
@@ -3807,6 +4210,7 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, |
case CONSTANT_FUNCTION: { |
PropertyDetails d = PropertyDetails(details.attributes(), |
NORMAL, |
+ Representation::None(), |
details.descriptor_index()); |
Object* value = descs->GetConstantFunction(i); |
MaybeObject* maybe_dictionary = |
@@ -3817,6 +4221,7 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, |
case FIELD: { |
PropertyDetails d = PropertyDetails(details.attributes(), |
NORMAL, |
+ Representation::None(), |
details.descriptor_index()); |
Object* value = FastPropertyAt(descs->GetFieldIndex(i)); |
MaybeObject* maybe_dictionary = |
@@ -3826,9 +4231,12 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, |
} |
case CALLBACKS: { |
Object* value = descs->GetCallbacksObject(i); |
- details = details.set_pointer(0); |
+ PropertyDetails d = PropertyDetails(details.attributes(), |
+ CALLBACKS, |
+ Representation::None(), |
+ details.descriptor_index()); |
MaybeObject* maybe_dictionary = |
- dictionary->Add(descs->GetKey(i), value, details); |
+ dictionary->Add(descs->GetKey(i), value, d); |
if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; |
break; |
} |
@@ -3962,7 +4370,8 @@ MaybeObject* JSObject::NormalizeElements() { |
ASSERT(old_map->has_fast_smi_or_object_elements()); |
value = FixedArray::cast(array)->get(i); |
} |
- PropertyDetails details = PropertyDetails(NONE, NORMAL); |
+ PropertyDetails details = PropertyDetails( |
+ NONE, NORMAL, Representation::None()); |
if (!value->IsTheHole()) { |
Object* result; |
MaybeObject* maybe_result = |
@@ -4918,16 +5327,6 @@ int Map::NumberOfDescribedProperties(DescriptorFlag which, |
} |
-int Map::PropertyIndexFor(Name* name) { |
- DescriptorArray* descs = instance_descriptors(); |
- int limit = NumberOfOwnDescriptors(); |
- for (int i = 0; i < limit; i++) { |
- if (name->Equals(descs->GetKey(i))) return descs->GetFieldIndex(i); |
- } |
- return -1; |
-} |
- |
- |
int Map::NextFreePropertyIndex() { |
int max_index = -1; |
int number_of_own_descriptors = NumberOfOwnDescriptors(); |
@@ -5041,8 +5440,10 @@ static bool UpdateGetterSetterInDictionary( |
if (details.type() == CALLBACKS && result->IsAccessorPair()) { |
ASSERT(!details.IsDontDelete()); |
if (details.attributes() != attributes) { |
- dictionary->DetailsAtPut(entry, |
- PropertyDetails(attributes, CALLBACKS, index)); |
+ dictionary->DetailsAtPut( |
+ entry, |
+ PropertyDetails( |
+ attributes, CALLBACKS, Representation::None(), index)); |
} |
AccessorPair::cast(result)->SetComponents(getter, setter); |
return true; |
@@ -5203,7 +5604,8 @@ bool JSObject::CanSetCallback(Name* name) { |
MaybeObject* JSObject::SetElementCallback(uint32_t index, |
Object* structure, |
PropertyAttributes attributes) { |
- PropertyDetails details = PropertyDetails(attributes, CALLBACKS); |
+ PropertyDetails details = PropertyDetails( |
+ attributes, CALLBACKS, Representation::None()); |
// Normalize elements to make this operation simple. |
SeededNumberDictionary* dictionary; |
@@ -5261,7 +5663,8 @@ MaybeObject* JSObject::SetPropertyCallback(Name* name, |
} |
// Update the dictionary with the new CALLBACKS property. |
- PropertyDetails details = PropertyDetails(attributes, CALLBACKS); |
+ PropertyDetails details = PropertyDetails( |
+ attributes, CALLBACKS, Representation::None()); |
maybe_ok = SetNormalizedProperty(name, structure, details); |
if (maybe_ok->IsFailure()) return maybe_ok; |
@@ -5778,6 +6181,7 @@ MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors, |
(descriptor_index == descriptors->number_of_descriptors() - 1) |
? SIMPLE_TRANSITION |
: FULL_TRANSITION; |
+ ASSERT(name == descriptors->GetKey(descriptor_index)); |
MaybeObject* maybe_transitions = AddTransition(name, result, simple_flag); |
if (!maybe_transitions->To(&transitions)) return maybe_transitions; |
@@ -5789,6 +6193,43 @@ MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors, |
} |
+MaybeObject* Map::CopyInstallDescriptors(int new_descriptor, |
+ DescriptorArray* descriptors) { |
+ ASSERT(descriptors->IsSortedNoDuplicates()); |
+ |
+ Map* result; |
+ MaybeObject* maybe_result = CopyDropDescriptors(); |
+ if (!maybe_result->To(&result)) return maybe_result; |
+ |
+ result->InitializeDescriptors(descriptors); |
+ result->SetNumberOfOwnDescriptors(new_descriptor + 1); |
+ |
+ int unused_property_fields = this->unused_property_fields(); |
+ if (descriptors->GetDetails(new_descriptor).type() == FIELD) { |
+ unused_property_fields = this->unused_property_fields() - 1; |
+ if (unused_property_fields < 0) { |
+ unused_property_fields += JSObject::kFieldsAdded; |
+ } |
+ } |
+ |
+ result->set_unused_property_fields(unused_property_fields); |
+ result->set_owns_descriptors(false); |
+ |
+ if (CanHaveMoreTransitions()) { |
+ Name* name = descriptors->GetKey(new_descriptor); |
+ TransitionArray* transitions; |
+ MaybeObject* maybe_transitions = |
+ AddTransition(name, result, SIMPLE_TRANSITION); |
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions; |
+ |
+ set_transitions(transitions); |
+ result->SetBackPointer(this); |
+ } |
+ |
+ return result; |
+} |
+ |
+ |
MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) { |
if (flag == INSERT_TRANSITION) { |
ASSERT(!HasElementsTransition() || |
@@ -5857,6 +6298,8 @@ MaybeObject* Map::CopyWithPreallocatedFieldDescriptors() { |
descriptors->CopyUpTo(number_of_own_descriptors); |
if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors; |
+ new_descriptors->InitializeRepresentations(Representation::Tagged()); |
+ |
return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION, 0); |
} |
@@ -6812,6 +7255,89 @@ void DescriptorArray::CopyFrom(int dst_index, |
} |
+// Generalize the |other| descriptor array by merging it with the (at least |
+// partly) updated |this| descriptor array. |
+// The method merges two descriptor array in three parts. Both descriptor arrays |
+// are identical up to |verbatim|. They also overlap in keys up to |valid|. |
+// Between |verbatim| and |valid|, the resulting descriptor type as well as the |
+// representation are generalized from both |this| and |other|. Beyond |valid|, |
+// the descriptors are copied verbatim from |other| up to |new_size|. |
+// In case of incompatible types, the type and representation of |other| is |
+// used. |
+MaybeObject* DescriptorArray::Merge(int verbatim, |
+ int valid, |
+ int new_size, |
+ DescriptorArray* other) { |
+ ASSERT(verbatim <= valid); |
+ ASSERT(valid <= new_size); |
+ |
+ DescriptorArray* result; |
+ // Allocate a new descriptor array large enough to hold the required |
+ // descriptors, with minimally the exact same size as this descriptor array. |
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate( |
+ new_size, Max(new_size, number_of_descriptors()) - new_size); |
+ if (!maybe_descriptors->To(&result)) return maybe_descriptors; |
+ ASSERT(result->length() > length() || |
+ result->NumberOfSlackDescriptors() > 0 || |
+ result->number_of_descriptors() == other->number_of_descriptors()); |
+ ASSERT(result->number_of_descriptors() == new_size); |
+ |
+ DescriptorArray::WhitenessWitness witness(result); |
+ |
+ int descriptor; |
+ |
+ // 0 -> |verbatim| |
+ int current_offset = 0; |
+ for (descriptor = 0; descriptor < verbatim; descriptor++) { |
+ if (GetDetails(descriptor).type() == FIELD) current_offset++; |
+ result->CopyFrom(descriptor, this, descriptor, witness); |
+ } |
+ |
+ // |verbatim| -> |valid| |
+ for (; descriptor < valid; descriptor++) { |
+ Name* key = GetKey(descriptor); |
+ PropertyDetails details = GetDetails(descriptor); |
+ PropertyDetails other_details = other->GetDetails(descriptor); |
+ ASSERT(details.attributes() == other_details.attributes()); |
+ |
+ if (details.type() == FIELD || other_details.type() == FIELD || |
+ (details.type() == CONSTANT_FUNCTION && |
+ other_details.type() == CONSTANT_FUNCTION && |
+ GetValue(descriptor) != other->GetValue(descriptor))) { |
+ Representation representation = |
+ details.representation().generalize(other_details.representation()); |
+ FieldDescriptor d(key, |
+ current_offset++, |
+ details.attributes(), |
+ representation, |
+ descriptor + 1); |
+ result->Set(descriptor, &d, witness); |
+ } else { |
+ result->CopyFrom(descriptor, other, descriptor, witness); |
+ } |
+ } |
+ |
+ // |valid| -> |new_size| |
+ for (; descriptor < new_size; descriptor++) { |
+ PropertyDetails details = other->GetDetails(descriptor); |
+ if (details.type() == FIELD) { |
+ Name* key = other->GetKey(descriptor); |
+ FieldDescriptor d(key, |
+ current_offset++, |
+ details.attributes(), |
+ details.representation(), |
+ descriptor + 1); |
+ result->Set(descriptor, &d, witness); |
+ } else { |
+ result->CopyFrom(descriptor, other, descriptor, witness); |
+ } |
+ } |
+ |
+ result->Sort(); |
+ return result; |
+} |
+ |
+ |
// We need the whiteness witness since sort will reshuffle the entries in the |
// descriptor array. If the descriptor array were to be black, the shuffling |
// would move a slot that was already recorded as pointing into an evacuation |
@@ -8173,19 +8699,28 @@ int Map::Hash() { |
} |
+static bool CheckEquivalent(Map* first, Map* second) { |
+ return |
+ first->constructor() == second->constructor() && |
+ first->prototype() == second->prototype() && |
+ first->instance_type() == second->instance_type() && |
+ first->bit_field() == second->bit_field() && |
+ first->bit_field2() == second->bit_field2() && |
+ first->is_observed() == second->is_observed() && |
+ first->function_with_prototype() == second->function_with_prototype(); |
+} |
+ |
+ |
+bool Map::EquivalentToForTransition(Map* other) { |
+ return CheckEquivalent(this, other); |
+} |
+ |
+ |
bool Map::EquivalentToForNormalization(Map* other, |
PropertyNormalizationMode mode) { |
- return |
- constructor() == other->constructor() && |
- prototype() == other->prototype() && |
- inobject_properties() == ((mode == CLEAR_INOBJECT_PROPERTIES) ? |
- 0 : |
- other->inobject_properties()) && |
- instance_type() == other->instance_type() && |
- bit_field() == other->bit_field() && |
- bit_field2() == other->bit_field2() && |
- is_observed() == other->is_observed() && |
- function_with_prototype() == other->function_with_prototype(); |
+ int properties = mode == CLEAR_INOBJECT_PROPERTIES |
+ ? 0 : other->inobject_properties(); |
+ return CheckEquivalent(this, other) && inobject_properties() == properties; |
} |
@@ -10600,8 +11135,8 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, |
// is read-only (a declared const that has not been initialized). If a |
// value is being defined we skip attribute checks completely. |
if (set_mode == DEFINE_PROPERTY) { |
- details = PropertyDetails( |
- attributes, NORMAL, details.dictionary_index()); |
+ details = PropertyDetails(attributes, NORMAL, Representation::None(), |
+ details.dictionary_index()); |
dictionary->DetailsAtPut(entry, details); |
} else if (details.IsReadOnly() && !element->IsTheHole()) { |
if (strict_mode == kNonStrictMode) { |
@@ -10653,7 +11188,8 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, |
} |
} |
FixedArrayBase* new_dictionary; |
- PropertyDetails details = PropertyDetails(attributes, NORMAL); |
+ PropertyDetails details = PropertyDetails( |
+ attributes, NORMAL, Representation::None()); |
MaybeObject* maybe = dictionary->AddNumberEntry(index, *value, details); |
if (!maybe->To(&new_dictionary)) return maybe; |
if (*dictionary != SeededNumberDictionary::cast(new_dictionary)) { |
@@ -12647,7 +13183,8 @@ MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) { |
} |
uint32_t result = pos; |
- PropertyDetails no_details = PropertyDetails(NONE, NORMAL); |
+ PropertyDetails no_details = PropertyDetails( |
+ NONE, NORMAL, Representation::None()); |
Heap* heap = GetHeap(); |
while (undefs > 0) { |
if (pos > static_cast<uint32_t>(Smi::kMaxValue)) { |
@@ -13005,7 +13542,7 @@ MaybeObject* GlobalObject::EnsurePropertyCell(Name* name) { |
heap->AllocateJSGlobalPropertyCell(heap->the_hole_value()); |
if (!maybe_cell->ToObject(&cell)) return maybe_cell; |
} |
- PropertyDetails details(NONE, NORMAL); |
+ PropertyDetails details(NONE, NORMAL, Representation::None()); |
details = details.AsDeleted(); |
Object* dictionary; |
{ MaybeObject* maybe_dictionary = |
@@ -13447,8 +13984,9 @@ MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() { |
if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) { |
int enum_index = Smi::cast(enumeration_order->get(pos++))->value(); |
PropertyDetails details = DetailsAt(i); |
- PropertyDetails new_details = |
- PropertyDetails(details.attributes(), details.type(), enum_index); |
+ PropertyDetails new_details = PropertyDetails( |
+ details.attributes(), details.type(), |
+ Representation::None(), enum_index); |
DetailsAtPut(i, new_details); |
} |
} |
@@ -13514,7 +14052,8 @@ MaybeObject* Dictionary<Shape, Key>::AtPut(Key key, Object* value) { |
{ MaybeObject* maybe_k = Shape::AsObject(this->GetHeap(), key); |
if (!maybe_k->ToObject(&k)) return maybe_k; |
} |
- PropertyDetails details = PropertyDetails(NONE, NORMAL); |
+ PropertyDetails details = PropertyDetails( |
+ NONE, NORMAL, Representation::None()); |
return Dictionary<Shape, Key>::cast(obj)->AddEntry(key, value, details, |
Dictionary<Shape, Key>::Hash(key)); |
@@ -13560,7 +14099,8 @@ MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key, |
// Assign an enumeration index to the property and update |
// SetNextEnumerationIndex. |
int index = NextEnumerationIndex(); |
- details = PropertyDetails(details.attributes(), details.type(), index); |
+ details = PropertyDetails(details.attributes(), details.type(), |
+ Representation::None(), index); |
SetNextEnumerationIndex(index + 1); |
} |
SetEntry(entry, k, value, details); |
@@ -13602,7 +14142,7 @@ MaybeObject* SeededNumberDictionary::AddNumberEntry(uint32_t key, |
MaybeObject* UnseededNumberDictionary::AddNumberEntry(uint32_t key, |
Object* value) { |
SLOW_ASSERT(this->FindEntry(key) == kNotFound); |
- return Add(key, value, PropertyDetails(NONE, NORMAL)); |
+ return Add(key, value, PropertyDetails(NONE, NORMAL, Representation::None())); |
} |
@@ -13647,6 +14187,7 @@ MaybeObject* SeededNumberDictionary::Set(uint32_t key, |
// Preserve enumeration index. |
details = PropertyDetails(details.attributes(), |
details.type(), |
+ Representation::None(), |
DetailsAt(entry).dictionary_index()); |
MaybeObject* maybe_object_key = |
SeededNumberDictionaryShape::AsObject(GetHeap(), key); |
@@ -13921,6 +14462,8 @@ MaybeObject* NameDictionary::TransformPropertiesToFastFor( |
FieldDescriptor d(key, |
current_offset++, |
details.attributes(), |
+ // TODO(verwaest): value->OptimalRepresentation(); |
+ Representation::Tagged(), |
enumeration_index); |
descriptors->Set(enumeration_index - 1, &d, witness); |
} else if (type == CALLBACKS) { |