| 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) {
|
|
|