Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index f5bd2b4f2f3a607ac0f1cc9d304f9bd4f4cb6df0..64f83ee5e370a6930c85ffcd348ee44b298e2ea9 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -542,41 +542,30 @@ void JSObject::SetNormalizedProperty(Handle<JSObject> object, |
int entry = property_dictionary->FindEntry(name); |
if (entry == NameDictionary::kNotFound) { |
- Handle<Object> store_value = value; |
if (object->IsGlobalObject()) { |
- store_value = object->GetIsolate()->factory()->NewPropertyCell(value); |
- } |
- |
- property_dictionary = NameDictionary::Add( |
- property_dictionary, name, store_value, details); |
+ auto cell = object->GetIsolate()->factory()->NewPropertyCell(); |
+ cell->set_value(*value); |
+ auto cell_type = value->IsUndefined() ? PropertyCellType::kUndefined |
+ : PropertyCellType::kConstant; |
+ details = details.set_cell_type(cell_type); |
+ value = cell; |
+ } |
+ property_dictionary = |
+ NameDictionary::Add(property_dictionary, name, value, details); |
object->set_properties(*property_dictionary); |
return; |
} |
- PropertyDetails original_details = property_dictionary->DetailsAt(entry); |
- int enumeration_index = original_details.dictionary_index(); |
- |
- if (!object->IsGlobalObject()) { |
- DCHECK(enumeration_index > 0); |
- details = PropertyDetails(details.attributes(), details.type(), |
- enumeration_index); |
- property_dictionary->SetEntry(entry, name, value, details); |
+ if (object->IsGlobalObject()) { |
+ PropertyCell::UpdateCell(property_dictionary, entry, value, details); |
return; |
} |
- Handle<PropertyCell> cell( |
- PropertyCell::cast(property_dictionary->ValueAt(entry))); |
- // Preserve the enumeration index unless the property was deleted. |
- if (cell->value()->IsTheHole()) { |
- enumeration_index = property_dictionary->NextEnumerationIndex(); |
- property_dictionary->SetNextEnumerationIndex(enumeration_index + 1); |
- } |
+ PropertyDetails original_details = property_dictionary->DetailsAt(entry); |
+ int enumeration_index = original_details.dictionary_index(); |
DCHECK(enumeration_index > 0); |
- details = PropertyDetails( |
- details.attributes(), details.type(), enumeration_index); |
- PropertyCell::SetValueInferType(cell, value); |
- // Please note we have to update the property details. |
- property_dictionary->DetailsAtPut(entry, details); |
+ details = details.set_index(enumeration_index); |
+ property_dictionary->SetEntry(entry, name, value, details); |
} |
@@ -1778,23 +1767,27 @@ void JSObject::AddSlowProperty(Handle<JSObject> object, |
DCHECK(!object->HasFastProperties()); |
Isolate* isolate = object->GetIsolate(); |
Handle<NameDictionary> dict(object->property_dictionary()); |
+ PropertyDetails details(attributes, DATA, 0, PropertyCellType::kInvalid); |
if (object->IsGlobalObject()) { |
- // In case name is an orphaned property reuse the cell. |
int entry = dict->FindEntry(name); |
+ // If there's a cell there, just invalidate and set the property. |
if (entry != NameDictionary::kNotFound) { |
- Handle<PropertyCell> cell(PropertyCell::cast(dict->ValueAt(entry))); |
- PropertyCell::SetValueInferType(cell, value); |
- // Assign an enumeration index to the property and update |
- // SetNextEnumerationIndex. |
+ PropertyCell::UpdateCell(dict, entry, value, details); |
+ // TODO(dcarney): move this to UpdateCell. |
+ // Need to adjust the details. |
int index = dict->NextEnumerationIndex(); |
- PropertyDetails details(attributes, DATA, index); |
dict->SetNextEnumerationIndex(index + 1); |
- dict->SetEntry(entry, name, cell, details); |
+ details = dict->DetailsAt(entry).set_index(index); |
+ dict->DetailsAtPut(entry, details); |
return; |
} |
- value = isolate->factory()->NewPropertyCell(value); |
+ auto cell = isolate->factory()->NewPropertyCell(); |
+ cell->set_value(*value); |
+ auto cell_type = value->IsUndefined() ? PropertyCellType::kUndefined |
+ : PropertyCellType::kConstant; |
+ details = details.set_cell_type(cell_type); |
+ value = cell; |
} |
- PropertyDetails details(attributes, DATA, 0); |
Handle<NameDictionary> result = |
NameDictionary::Add(dict, name, value, details); |
if (*dict != *result) object->set_properties(*result); |
@@ -4556,7 +4549,8 @@ void JSObject::MigrateFastToSlow(Handle<JSObject> object, |
switch (details.type()) { |
case DATA_CONSTANT: { |
Handle<Object> value(descs->GetConstant(i), isolate); |
- PropertyDetails d(details.attributes(), DATA, i + 1); |
+ PropertyDetails d(details.attributes(), DATA, i + 1, |
+ PropertyCellType::kInvalid); |
dictionary = NameDictionary::Add(dictionary, key, value, d); |
break; |
} |
@@ -4574,20 +4568,23 @@ void JSObject::MigrateFastToSlow(Handle<JSObject> object, |
value = isolate->factory()->NewHeapNumber(old->value()); |
} |
} |
- PropertyDetails d(details.attributes(), DATA, i + 1); |
+ PropertyDetails d(details.attributes(), DATA, i + 1, |
+ PropertyCellType::kInvalid); |
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); |
+ PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1, |
+ PropertyCellType::kInvalid); |
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); |
+ PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1, |
+ PropertyCellType::kInvalid); |
dictionary = NameDictionary::Add(dictionary, key, value, d); |
break; |
} |
@@ -4820,7 +4817,7 @@ static Handle<SeededNumberDictionary> CopyFastElementsToDictionary( |
value = handle(Handle<FixedArray>::cast(array)->get(i), isolate); |
} |
if (!value->IsTheHole()) { |
- PropertyDetails details(NONE, DATA, 0); |
+ PropertyDetails details = PropertyDetails::Empty(); |
dictionary = |
SeededNumberDictionary::AddNumberEntry(dictionary, i, value, details); |
} |
@@ -5294,12 +5291,13 @@ void JSObject::DeleteNormalizedProperty(Handle<JSObject> object, |
int entry = dictionary->FindEntry(name); |
DCHECK_NE(NameDictionary::kNotFound, entry); |
- // If we have a global object set the cell to the hole. |
+ // If we have a global object, invalidate the cell and swap in a new one. |
if (object->IsGlobalObject()) { |
- DCHECK(dictionary->DetailsAt(entry).IsConfigurable()); |
- Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry))); |
- Handle<Object> value = isolate->factory()->the_hole_value(); |
- PropertyCell::SetValueInferType(cell, value); |
+ auto cell = PropertyCell::InvalidateEntry(dictionary, entry); |
+ cell->set_value(isolate->heap()->the_hole_value()); |
+ // TODO(dcarney): InvalidateForDelete |
+ dictionary->DetailsAtPut(entry, dictionary->DetailsAt(entry).set_cell_type( |
+ PropertyCellType::kDeleted)); |
return; |
} |
@@ -6372,7 +6370,8 @@ static bool UpdateGetterSetterInDictionary( |
DCHECK(details.IsConfigurable()); |
if (details.attributes() != attributes) { |
dictionary->DetailsAtPut( |
- entry, PropertyDetails(attributes, ACCESSOR_CONSTANT, index)); |
+ entry, PropertyDetails(attributes, ACCESSOR_CONSTANT, index, |
+ PropertyCellType::kInvalid)); |
} |
AccessorPair::cast(result)->SetComponents(getter, setter); |
return true; |
@@ -6474,7 +6473,8 @@ void JSObject::SetElementCallback(Handle<JSObject> object, |
Handle<Object> structure, |
PropertyAttributes attributes) { |
Heap* heap = object->GetHeap(); |
- PropertyDetails details = PropertyDetails(attributes, ACCESSOR_CONSTANT, 0); |
+ PropertyDetails details = PropertyDetails(attributes, ACCESSOR_CONSTANT, 0, |
+ PropertyCellType::kInvalid); |
// Normalize elements to make this operation simple. |
bool had_dictionary_elements = object->HasDictionaryElements(); |
@@ -6519,28 +6519,10 @@ void JSObject::SetPropertyCallback(Handle<JSObject> object, |
// Normalize object to make this operation simple. |
NormalizeProperties(object, mode, 0, "SetPropertyCallback"); |
- // For the global object allocate a new map to invalidate the global inline |
- // caches which have a global property cell reference directly in the code. |
- if (object->IsGlobalObject()) { |
- Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); |
- DCHECK(new_map->is_dictionary_map()); |
-#if TRACE_MAPS |
- if (FLAG_trace_maps) { |
- PrintF("[TraceMaps: GlobalPropertyCallback from= %p to= %p ]\n", |
- reinterpret_cast<void*>(object->map()), |
- reinterpret_cast<void*>(*new_map)); |
- } |
-#endif |
- JSObject::MigrateToMap(object, new_map); |
- |
- // When running crankshaft, changing the map is not enough. We |
- // need to deoptimize all functions that rely on this global |
- // object. |
- Deoptimizer::DeoptimizeGlobalObject(*object); |
- } |
// Update the dictionary with the new ACCESSOR_CONSTANT property. |
- PropertyDetails details = PropertyDetails(attributes, ACCESSOR_CONSTANT, 0); |
+ PropertyDetails details = PropertyDetails(attributes, ACCESSOR_CONSTANT, 0, |
+ PropertyCellType::kMutable); |
SetNormalizedProperty(object, name, structure, details); |
ReoptimizeIfPrototype(object); |
@@ -12733,7 +12715,8 @@ MaybeHandle<Object> JSObject::SetDictionaryElement( |
element->IsTheHole()); |
dictionary->UpdateMaxNumberKey(index); |
if (set_mode == DEFINE_PROPERTY) { |
- details = PropertyDetails(attributes, DATA, details.dictionary_index()); |
+ details = PropertyDetails(attributes, DATA, details.dictionary_index(), |
+ PropertyCellType::kInvalid); |
dictionary->DetailsAtPut(entry, details); |
} |
@@ -12776,7 +12759,7 @@ MaybeHandle<Object> JSObject::SetDictionaryElement( |
} |
} |
- PropertyDetails details(attributes, DATA, 0); |
+ PropertyDetails details(attributes, DATA, 0, PropertyCellType::kInvalid); |
Handle<SeededNumberDictionary> new_dictionary = |
SeededNumberDictionary::AddNumberEntry(dictionary, index, value, |
details); |
@@ -14861,7 +14844,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort( |
} |
uint32_t result = pos; |
- PropertyDetails no_details(NONE, DATA, 0); |
+ PropertyDetails no_details = PropertyDetails::Empty(); |
while (undefs > 0) { |
if (pos > static_cast<uint32_t>(Smi::kMaxValue)) { |
// Adding an entry with the key beyond smi-range requires |
@@ -15222,46 +15205,36 @@ Handle<Object> ExternalFloat64Array::SetValue( |
void GlobalObject::InvalidatePropertyCell(Handle<GlobalObject> global, |
Handle<Name> name) { |
DCHECK(!global->HasFastProperties()); |
- Isolate* isolate = global->GetIsolate(); |
- int entry = global->property_dictionary()->FindEntry(name); |
- if (entry != NameDictionary::kNotFound) { |
- Handle<PropertyCell> cell( |
- PropertyCell::cast(global->property_dictionary()->ValueAt(entry))); |
- |
- Handle<Object> value(cell->value(), isolate); |
- Handle<PropertyCell> new_cell = isolate->factory()->NewPropertyCell(value); |
- global->property_dictionary()->ValueAtPut(entry, *new_cell); |
- |
- Handle<Object> hole = isolate->factory()->the_hole_value(); |
- if (*hole != *value) { |
- PropertyCell::SetValueInferType(cell, hole); |
- } else { |
- // If property value was the hole, set it to any other value, |
- // to ensure that LoadNonexistent ICs execute a miss. |
- Handle<Object> undefined = isolate->factory()->undefined_value(); |
- PropertyCell::SetValueInferType(cell, undefined); |
- } |
- } |
+ auto dictionary = handle(global->property_dictionary()); |
+ int entry = dictionary->FindEntry(name); |
+ if (entry == NameDictionary::kNotFound) return; |
+ PropertyCell::InvalidateEntry(dictionary, entry); |
} |
+// TODO(dcarney): rename to EnsureEmptyPropertyCell or something. |
Handle<PropertyCell> GlobalObject::EnsurePropertyCell( |
Handle<GlobalObject> global, Handle<Name> name) { |
DCHECK(!global->HasFastProperties()); |
- int entry = global->property_dictionary()->FindEntry(name); |
- if (entry == NameDictionary::kNotFound) { |
- Isolate* isolate = global->GetIsolate(); |
- Handle<PropertyCell> cell = isolate->factory()->NewPropertyCellWithHole(); |
- PropertyDetails details(NONE, DATA, 0); |
- Handle<NameDictionary> dictionary = NameDictionary::Add( |
- handle(global->property_dictionary()), name, cell, details); |
- global->set_properties(*dictionary); |
+ auto dictionary = handle(global->property_dictionary()); |
+ int entry = dictionary->FindEntry(name); |
+ Handle<PropertyCell> cell; |
+ if (entry != NameDictionary::kNotFound) { |
+ // This call should be idempotent. |
+ DCHECK(dictionary->DetailsAt(entry).cell_type() == |
+ PropertyCellType::kUninitialized || |
+ dictionary->DetailsAt(entry).cell_type() == |
+ PropertyCellType::kDeleted); |
+ cell = handle(PropertyCell::cast(dictionary->ValueAt(entry))); |
+ DCHECK(cell->value()->IsTheHole()); |
return cell; |
- } else { |
- Object* value = global->property_dictionary()->ValueAt(entry); |
- DCHECK(value->IsPropertyCell()); |
- return handle(PropertyCell::cast(value)); |
} |
+ Isolate* isolate = global->GetIsolate(); |
+ cell = isolate->factory()->NewPropertyCell(); |
+ PropertyDetails details(NONE, DATA, 0, PropertyCellType::kUninitialized); |
+ dictionary = NameDictionary::Add(dictionary, name, cell, details); |
+ global->set_properties(*dictionary); |
+ return cell; |
} |
@@ -15671,8 +15644,7 @@ Dictionary<Derived, Shape, Key>::GenerateNewEnumerationIndices( |
int enum_index = PropertyDetails::kInitialIndex + i; |
PropertyDetails details = dictionary->DetailsAt(index); |
- PropertyDetails new_details = |
- PropertyDetails(details.attributes(), details.type(), enum_index); |
+ PropertyDetails new_details = details.set_index(enum_index); |
dictionary->DetailsAtPut(index, new_details); |
} |
@@ -15725,7 +15697,7 @@ Handle<Derived> Dictionary<Derived, Shape, Key>::AtPut( |
#ifdef DEBUG |
USE(Shape::AsHandle(dictionary->GetIsolate(), key)); |
#endif |
- PropertyDetails details(NONE, DATA, 0); |
+ PropertyDetails details = PropertyDetails::Empty(); |
AddEntry(dictionary, key, value, details, dictionary->Hash(key)); |
return dictionary; |
@@ -15765,7 +15737,7 @@ void Dictionary<Derived, Shape, Key>::AddEntry( |
// Assign an enumeration index to the property and update |
// SetNextEnumerationIndex. |
int index = dictionary->NextEnumerationIndex(); |
- details = PropertyDetails(details.attributes(), details.type(), index); |
+ details = details.set_index(index); |
dictionary->SetNextEnumerationIndex(index + 1); |
} |
dictionary->SetEntry(entry, k, value, details); |
@@ -15811,7 +15783,7 @@ Handle<UnseededNumberDictionary> UnseededNumberDictionary::AddNumberEntry( |
uint32_t key, |
Handle<Object> value) { |
SLOW_DCHECK(dictionary->FindEntry(key) == kNotFound); |
- return Add(dictionary, key, value, PropertyDetails(NONE, DATA, 0)); |
+ return Add(dictionary, key, value, PropertyDetails::Empty()); |
} |
@@ -15842,9 +15814,7 @@ Handle<SeededNumberDictionary> SeededNumberDictionary::Set( |
return AddNumberEntry(dictionary, key, value, details); |
} |
// Preserve enumeration index. |
- details = PropertyDetails(details.attributes(), |
- details.type(), |
- dictionary->DetailsAt(entry).dictionary_index()); |
+ details = details.set_index(dictionary->DetailsAt(entry).dictionary_index()); |
Handle<Object> object_key = |
SeededNumberDictionaryShape::AsHandle(dictionary->GetIsolate(), key); |
dictionary->SetEntry(entry, object_key, value, details); |
@@ -17022,54 +16992,107 @@ Handle<JSArrayBuffer> JSTypedArray::GetBuffer() { |
} |
-HeapType* PropertyCell::type() { |
- return static_cast<HeapType*>(type_raw()); |
+Handle<PropertyCell> PropertyCell::InvalidateEntry( |
+ Handle<NameDictionary> dictionary, int entry) { |
+ Isolate* isolate = dictionary->GetIsolate(); |
+ // Swap with a copy. |
+ Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry))); |
+ auto new_cell = isolate->factory()->NewPropertyCell(); |
+ new_cell->set_value(cell->value()); |
+ dictionary->ValueAtPut(entry, *new_cell); |
+ bool is_the_hole = cell->value()->IsTheHole(); |
+ // Cell is officially mutable henceforth. |
+ auto details = dictionary->DetailsAt(entry); |
+ details = details.set_cell_type(is_the_hole ? PropertyCellType::kDeleted |
+ : PropertyCellType::kMutable); |
+ dictionary->DetailsAtPut(entry, details); |
+ // Old cell is ready for invalidation. |
+ if (is_the_hole) { |
+ cell->set_value(isolate->heap()->undefined_value()); |
+ } else { |
+ cell->set_value(isolate->heap()->the_hole_value()); |
+ } |
+ cell->dependent_code()->DeoptimizeDependentCodeGroup( |
+ isolate, DependentCode::kPropertyCellChangedGroup); |
+ return new_cell; |
} |
-void PropertyCell::set_type(HeapType* type, WriteBarrierMode ignored) { |
- DCHECK(IsPropertyCell()); |
- set_type_raw(type, ignored); |
+PropertyCellType PropertyCell::UpdatedType(Handle<PropertyCell> cell, |
+ Handle<Object> value, |
+ PropertyDetails details) { |
+ PropertyCellType type = details.cell_type(); |
+ DCHECK(!value->IsTheHole()); |
+ DCHECK_IMPLIES(cell->value()->IsTheHole(), |
+ type == PropertyCellType::kUninitialized || |
+ type == PropertyCellType::kDeleted); |
+ switch (type) { |
+ // Only allow a cell to transition once into constant state. |
+ case PropertyCellType::kUninitialized: |
+ if (value->IsUndefined()) return PropertyCellType::kUndefined; |
+ return PropertyCellType::kConstant; |
+ case PropertyCellType::kUndefined: |
+ return PropertyCellType::kConstant; |
+ case PropertyCellType::kConstant: |
+ // No transition. |
+ if (*value == cell->value()) return PropertyCellType::kConstant; |
+ // Fall through. |
+ case PropertyCellType::kMutable: |
+ return PropertyCellType::kMutable; |
+ } |
+ UNREACHABLE(); |
+ return PropertyCellType::kMutable; |
} |
-Handle<HeapType> PropertyCell::UpdatedType(Handle<PropertyCell> cell, |
- Handle<Object> value) { |
- Isolate* isolate = cell->GetIsolate(); |
- Handle<HeapType> old_type(cell->type(), isolate); |
- Handle<HeapType> new_type = HeapType::Constant(value, isolate); |
- |
- if (new_type->Is(old_type)) return old_type; |
- |
- cell->dependent_code()->DeoptimizeDependentCodeGroup( |
- isolate, DependentCode::kPropertyCellChangedGroup); |
- |
- if (old_type->Is(HeapType::None()) || old_type->Is(HeapType::Undefined())) { |
- return new_type; |
+Handle<Object> PropertyCell::UpdateCell(Handle<NameDictionary> dictionary, |
+ int entry, Handle<Object> value, |
+ PropertyDetails details) { |
+ DCHECK(!value->IsTheHole()); |
+ Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry))); |
+ const PropertyDetails original_details = dictionary->DetailsAt(entry); |
+ // Data accesses could be cached in ics or optimized code. |
+ bool invalidate = |
+ original_details.kind() == kData && details.kind() == kAccessor; |
+ int index = original_details.dictionary_index(); |
+ auto old_type = original_details.cell_type(); |
+ // Preserve the enumeration index unless the property was deleted or never |
+ // initialized. |
+ if (cell->value()->IsTheHole()) { |
+ index = dictionary->NextEnumerationIndex(); |
+ dictionary->SetNextEnumerationIndex(index + 1); |
+ // Negative lookup cells must be invalidated. |
+ invalidate = true; |
} |
+ DCHECK(index > 0); |
+ details = details.set_index(index); |
- return HeapType::Any(isolate); |
-} |
- |
- |
-Handle<Object> PropertyCell::SetValueInferType(Handle<PropertyCell> cell, |
- Handle<Object> value) { |
// Heuristic: if a small-ish string is stored in a previously uninitialized |
// property cell, internalize it. |
const int kMaxLengthForInternalization = 200; |
- if ((cell->type()->Is(HeapType::None()) || |
- cell->type()->Is(HeapType::Undefined())) && |
+ if ((old_type == PropertyCellType::kUninitialized || |
+ old_type == PropertyCellType::kUndefined) && |
value->IsString()) { |
auto string = Handle<String>::cast(value); |
- if (string->length() <= kMaxLengthForInternalization && |
- !string->map()->is_undetectable()) { |
+ if (string->length() <= kMaxLengthForInternalization) { |
value = cell->GetIsolate()->factory()->InternalizeString(string); |
} |
} |
+ |
+ auto new_type = UpdatedType(cell, value, original_details); |
+ if (invalidate) cell = PropertyCell::InvalidateEntry(dictionary, entry); |
+ |
+ // Install new property details and cell value. |
+ details = details.set_cell_type(new_type); |
+ dictionary->DetailsAtPut(entry, details); |
cell->set_value(*value); |
- if (!HeapType::Any()->Is(cell->type())) { |
- Handle<HeapType> new_type = UpdatedType(cell, value); |
- cell->set_type(*new_type); |
+ |
+ // Deopt when transitioning from a constant type. |
+ if (!invalidate && old_type == PropertyCellType::kConstant && |
+ new_type != PropertyCellType::kConstant) { |
+ auto isolate = dictionary->GetIsolate(); |
+ cell->dependent_code()->DeoptimizeDependentCodeGroup( |
+ isolate, DependentCode::kPropertyCellChangedGroup); |
} |
return value; |
} |