| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index 89bc3c91c941c765ff346f068f93ce38a9044970..a7a5c480f32cb374fcf43d6b4f9c02422684e382 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -2812,59 +2812,7 @@ void JSObject::UpdatePrototypeUserRegistration(Handle<Map> old_map,
|
| }
|
| }
|
|
|
| -
|
| -void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
|
| - int expected_additional_properties) {
|
| - if (object->map() == *new_map) return;
|
| - // If this object is a prototype (the callee will check), invalidate any
|
| - // prototype chains involving it.
|
| - InvalidatePrototypeChains(object->map());
|
| - Handle<Map> old_map(object->map());
|
| -
|
| - // If the map was registered with its prototype before, ensure that it
|
| - // registers with its new prototype now. This preserves the invariant that
|
| - // when a map on a prototype chain is registered with its prototype, then
|
| - // all prototypes further up the chain are also registered with their
|
| - // respective prototypes.
|
| - UpdatePrototypeUserRegistration(old_map, new_map, new_map->GetIsolate());
|
| -
|
| - if (object->HasFastProperties()) {
|
| - if (!new_map->is_dictionary_map()) {
|
| - MigrateFastToFast(object, new_map);
|
| - if (old_map->is_prototype_map()) {
|
| - DCHECK(!old_map->is_stable());
|
| - DCHECK(new_map->is_stable());
|
| - // Clear out the old descriptor array to avoid problems to sharing
|
| - // the descriptor array without using an explicit.
|
| - old_map->InitializeDescriptors(
|
| - old_map->GetHeap()->empty_descriptor_array(),
|
| - LayoutDescriptor::FastPointerLayout());
|
| - // Ensure that no transition was inserted for prototype migrations.
|
| - DCHECK_EQ(0, TransitionArray::NumberOfTransitions(
|
| - old_map->raw_transitions()));
|
| - DCHECK(new_map->GetBackPointer()->IsUndefined());
|
| - }
|
| - } else {
|
| - MigrateFastToSlow(object, new_map, expected_additional_properties);
|
| - }
|
| - } else {
|
| - // For slow-to-fast migrations JSObject::MigrateSlowToFast()
|
| - // must be used instead.
|
| - CHECK(new_map->is_dictionary_map());
|
| -
|
| - // Slow-to-slow migration is trivial.
|
| - object->set_map(*new_map);
|
| - }
|
| -
|
| - // Careful: Don't allocate here!
|
| - // For some callers of this method, |object| might be in an inconsistent
|
| - // state now: the new map might have a new elements_kind, but the object's
|
| - // elements pointer hasn't been updated yet. Callers will fix this, but in
|
| - // the meantime, (indirectly) calling JSObjectVerify() must be avoided.
|
| - // When adding code here, add a DisallowHeapAllocation too.
|
| -}
|
| -
|
| -
|
| +namespace {
|
| // To migrate a fast instance to a fast map:
|
| // - First check whether the instance needs to be rewritten. If not, simply
|
| // change the map.
|
| @@ -2880,7 +2828,7 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
|
| // to temporarily store the inobject properties.
|
| // * If there are properties left in the backing store, install the backing
|
| // store.
|
| -void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
|
| +void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
|
| Isolate* isolate = object->GetIsolate();
|
| Handle<Map> old_map(object->map());
|
| // In case of a regular transition.
|
| @@ -3084,6 +3032,173 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
|
| object->synchronized_set_map(*new_map);
|
| }
|
|
|
| +void MigrateFastToSlow(Handle<JSObject> object, Handle<Map> new_map,
|
| + int expected_additional_properties) {
|
| + // The global object is always normalized.
|
| + DCHECK(!object->IsJSGlobalObject());
|
| + // JSGlobalProxy must never be normalized
|
| + DCHECK(!object->IsJSGlobalProxy());
|
| +
|
| + Isolate* isolate = object->GetIsolate();
|
| + HandleScope scope(isolate);
|
| + Handle<Map> map(object->map());
|
| +
|
| + // Allocate new content.
|
| + int real_size = map->NumberOfOwnDescriptors();
|
| + int property_count = real_size;
|
| + if (expected_additional_properties > 0) {
|
| + property_count += expected_additional_properties;
|
| + } else {
|
| + property_count += 2; // Make space for two more properties.
|
| + }
|
| + Handle<NameDictionary> dictionary =
|
| + NameDictionary::New(isolate, property_count);
|
| +
|
| + Handle<DescriptorArray> descs(map->instance_descriptors());
|
| + for (int i = 0; i < real_size; i++) {
|
| + PropertyDetails details = descs->GetDetails(i);
|
| + Handle<Name> key(descs->GetKey(i));
|
| + switch (details.type()) {
|
| + case DATA_CONSTANT: {
|
| + Handle<Object> value(descs->GetConstant(i), isolate);
|
| + PropertyDetails d(details.attributes(), DATA, i + 1,
|
| + PropertyCellType::kNoCell);
|
| + dictionary = NameDictionary::Add(dictionary, key, value, d);
|
| + break;
|
| + }
|
| + case DATA: {
|
| + FieldIndex index = FieldIndex::ForDescriptor(*map, i);
|
| + Handle<Object> value;
|
| + if (object->IsUnboxedDoubleField(index)) {
|
| + double old_value = object->RawFastDoublePropertyAt(index);
|
| + value = isolate->factory()->NewHeapNumber(old_value);
|
| + } else {
|
| + value = handle(object->RawFastPropertyAt(index), isolate);
|
| + if (details.representation().IsDouble()) {
|
| + DCHECK(value->IsMutableHeapNumber());
|
| + Handle<HeapNumber> old = Handle<HeapNumber>::cast(value);
|
| + value = isolate->factory()->NewHeapNumber(old->value());
|
| + }
|
| + }
|
| + PropertyDetails d(details.attributes(), DATA, i + 1,
|
| + PropertyCellType::kNoCell);
|
| + dictionary = NameDictionary::Add(dictionary, key, value, d);
|
| + break;
|
| + }
|
| + case ACCESSOR: {
|
| + FieldIndex index = FieldIndex::ForDescriptor(*map, i);
|
| + Handle<Object> value(object->RawFastPropertyAt(index), isolate);
|
| + PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1,
|
| + PropertyCellType::kNoCell);
|
| + dictionary = NameDictionary::Add(dictionary, key, value, d);
|
| + break;
|
| + }
|
| + case ACCESSOR_CONSTANT: {
|
| + Handle<Object> value(descs->GetCallbacksObject(i), isolate);
|
| + PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1,
|
| + PropertyCellType::kNoCell);
|
| + dictionary = NameDictionary::Add(dictionary, key, value, d);
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Copy the next enumeration index from instance descriptor.
|
| + dictionary->SetNextEnumerationIndex(real_size + 1);
|
| +
|
| + // From here on we cannot fail and we shouldn't GC anymore.
|
| + DisallowHeapAllocation no_allocation;
|
| +
|
| + // Resize the object in the heap if necessary.
|
| + int new_instance_size = new_map->instance_size();
|
| + int instance_size_delta = map->instance_size() - new_instance_size;
|
| + DCHECK(instance_size_delta >= 0);
|
| +
|
| + if (instance_size_delta > 0) {
|
| + Heap* heap = isolate->heap();
|
| + heap->CreateFillerObjectAt(object->address() + new_instance_size,
|
| + instance_size_delta);
|
| + heap->AdjustLiveBytes(*object, -instance_size_delta,
|
| + Heap::CONCURRENT_TO_SWEEPER);
|
| + }
|
| +
|
| + // We are storing the new map using release store after creating a filler for
|
| + // the left-over space to avoid races with the sweeper thread.
|
| + object->synchronized_set_map(*new_map);
|
| +
|
| + object->set_properties(*dictionary);
|
| +
|
| + // Ensure that in-object space of slow-mode object does not contain random
|
| + // garbage.
|
| + int inobject_properties = new_map->GetInObjectProperties();
|
| + for (int i = 0; i < inobject_properties; i++) {
|
| + FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i);
|
| + object->RawFastPropertyAtPut(index, Smi::FromInt(0));
|
| + }
|
| +
|
| + isolate->counters()->props_to_dictionary()->Increment();
|
| +
|
| +#ifdef DEBUG
|
| + if (FLAG_trace_normalization) {
|
| + OFStream os(stdout);
|
| + os << "Object properties have been normalized:\n";
|
| + object->Print(os);
|
| + }
|
| +#endif
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
|
| + int expected_additional_properties) {
|
| + if (object->map() == *new_map) return;
|
| + // If this object is a prototype (the callee will check), invalidate any
|
| + // prototype chains involving it.
|
| + InvalidatePrototypeChains(object->map());
|
| + Handle<Map> old_map(object->map());
|
| +
|
| + // If the map was registered with its prototype before, ensure that it
|
| + // registers with its new prototype now. This preserves the invariant that
|
| + // when a map on a prototype chain is registered with its prototype, then
|
| + // all prototypes further up the chain are also registered with their
|
| + // respective prototypes.
|
| + UpdatePrototypeUserRegistration(old_map, new_map, new_map->GetIsolate());
|
| +
|
| + if (object->HasFastProperties()) {
|
| + if (!new_map->is_dictionary_map()) {
|
| + MigrateFastToFast(object, new_map);
|
| + if (old_map->is_prototype_map()) {
|
| + DCHECK(!old_map->is_stable());
|
| + DCHECK(new_map->is_stable());
|
| + // Clear out the old descriptor array to avoid problems to sharing
|
| + // the descriptor array without using an explicit.
|
| + old_map->InitializeDescriptors(
|
| + old_map->GetHeap()->empty_descriptor_array(),
|
| + LayoutDescriptor::FastPointerLayout());
|
| + // Ensure that no transition was inserted for prototype migrations.
|
| + DCHECK_EQ(0, TransitionArray::NumberOfTransitions(
|
| + old_map->raw_transitions()));
|
| + DCHECK(new_map->GetBackPointer()->IsUndefined());
|
| + }
|
| + } else {
|
| + MigrateFastToSlow(object, new_map, expected_additional_properties);
|
| + }
|
| + } else {
|
| + // For slow-to-fast migrations JSObject::MigrateSlowToFast()
|
| + // must be used instead.
|
| + CHECK(new_map->is_dictionary_map());
|
| +
|
| + // Slow-to-slow migration is trivial.
|
| + object->set_map(*new_map);
|
| + }
|
| +
|
| + // Careful: Don't allocate here!
|
| + // For some callers of this method, |object| might be in an inconsistent
|
| + // state now: the new map might have a new elements_kind, but the object's
|
| + // elements pointer hasn't been updated yet. Callers will fix this, but in
|
| + // the meantime, (indirectly) calling JSObjectVerify() must be avoided.
|
| + // When adding code here, add a DisallowHeapAllocation too.
|
| +}
|
|
|
| int Map::NumberOfFields() {
|
| DescriptorArray* descriptors = instance_descriptors();
|
| @@ -5566,123 +5681,6 @@ void JSObject::NormalizeProperties(Handle<JSObject> object,
|
| }
|
|
|
|
|
| -void JSObject::MigrateFastToSlow(Handle<JSObject> object,
|
| - Handle<Map> new_map,
|
| - int expected_additional_properties) {
|
| - // The global object is always normalized.
|
| - DCHECK(!object->IsJSGlobalObject());
|
| - // JSGlobalProxy must never be normalized
|
| - DCHECK(!object->IsJSGlobalProxy());
|
| -
|
| - Isolate* isolate = object->GetIsolate();
|
| - HandleScope scope(isolate);
|
| - Handle<Map> map(object->map());
|
| -
|
| - // Allocate new content.
|
| - int real_size = map->NumberOfOwnDescriptors();
|
| - int property_count = real_size;
|
| - if (expected_additional_properties > 0) {
|
| - property_count += expected_additional_properties;
|
| - } else {
|
| - property_count += 2; // Make space for two more properties.
|
| - }
|
| - Handle<NameDictionary> dictionary =
|
| - NameDictionary::New(isolate, property_count);
|
| -
|
| - Handle<DescriptorArray> descs(map->instance_descriptors());
|
| - for (int i = 0; i < real_size; i++) {
|
| - PropertyDetails details = descs->GetDetails(i);
|
| - Handle<Name> key(descs->GetKey(i));
|
| - switch (details.type()) {
|
| - case DATA_CONSTANT: {
|
| - Handle<Object> value(descs->GetConstant(i), isolate);
|
| - PropertyDetails d(details.attributes(), DATA, i + 1,
|
| - PropertyCellType::kNoCell);
|
| - dictionary = NameDictionary::Add(dictionary, key, value, d);
|
| - break;
|
| - }
|
| - case DATA: {
|
| - FieldIndex index = FieldIndex::ForDescriptor(*map, i);
|
| - Handle<Object> value;
|
| - if (object->IsUnboxedDoubleField(index)) {
|
| - double old_value = object->RawFastDoublePropertyAt(index);
|
| - value = isolate->factory()->NewHeapNumber(old_value);
|
| - } else {
|
| - value = handle(object->RawFastPropertyAt(index), isolate);
|
| - if (details.representation().IsDouble()) {
|
| - DCHECK(value->IsMutableHeapNumber());
|
| - Handle<HeapNumber> old = Handle<HeapNumber>::cast(value);
|
| - value = isolate->factory()->NewHeapNumber(old->value());
|
| - }
|
| - }
|
| - PropertyDetails d(details.attributes(), DATA, i + 1,
|
| - PropertyCellType::kNoCell);
|
| - dictionary = NameDictionary::Add(dictionary, key, value, d);
|
| - break;
|
| - }
|
| - case ACCESSOR: {
|
| - FieldIndex index = FieldIndex::ForDescriptor(*map, i);
|
| - Handle<Object> value(object->RawFastPropertyAt(index), isolate);
|
| - PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1,
|
| - PropertyCellType::kNoCell);
|
| - dictionary = NameDictionary::Add(dictionary, key, value, d);
|
| - break;
|
| - }
|
| - case ACCESSOR_CONSTANT: {
|
| - Handle<Object> value(descs->GetCallbacksObject(i), isolate);
|
| - PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1,
|
| - PropertyCellType::kNoCell);
|
| - dictionary = NameDictionary::Add(dictionary, key, value, d);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Copy the next enumeration index from instance descriptor.
|
| - dictionary->SetNextEnumerationIndex(real_size + 1);
|
| -
|
| - // From here on we cannot fail and we shouldn't GC anymore.
|
| - DisallowHeapAllocation no_allocation;
|
| -
|
| - // Resize the object in the heap if necessary.
|
| - int new_instance_size = new_map->instance_size();
|
| - int instance_size_delta = map->instance_size() - new_instance_size;
|
| - DCHECK(instance_size_delta >= 0);
|
| -
|
| - if (instance_size_delta > 0) {
|
| - Heap* heap = isolate->heap();
|
| - heap->CreateFillerObjectAt(object->address() + new_instance_size,
|
| - instance_size_delta);
|
| - heap->AdjustLiveBytes(*object, -instance_size_delta,
|
| - Heap::CONCURRENT_TO_SWEEPER);
|
| - }
|
| -
|
| - // We are storing the new map using release store after creating a filler for
|
| - // the left-over space to avoid races with the sweeper thread.
|
| - object->synchronized_set_map(*new_map);
|
| -
|
| - object->set_properties(*dictionary);
|
| -
|
| - // Ensure that in-object space of slow-mode object does not contain random
|
| - // garbage.
|
| - int inobject_properties = new_map->GetInObjectProperties();
|
| - for (int i = 0; i < inobject_properties; i++) {
|
| - FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i);
|
| - object->RawFastPropertyAtPut(index, Smi::FromInt(0));
|
| - }
|
| -
|
| - isolate->counters()->props_to_dictionary()->Increment();
|
| -
|
| -#ifdef DEBUG
|
| - if (FLAG_trace_normalization) {
|
| - OFStream os(stdout);
|
| - os << "Object properties have been normalized:\n";
|
| - object->Print(os);
|
| - }
|
| -#endif
|
| -}
|
| -
|
| -
|
| void JSObject::MigrateSlowToFast(Handle<JSObject> object,
|
| int unused_property_fields,
|
| const char* reason) {
|
|
|