| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index 46e691a73553117cd1e4f13c40563f42706266a7..014bfecbeef74eb07813a0605d7d07bd97d24763 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -2761,7 +2761,7 @@ void JSProxy::Fix() {
|
| Object* hash;
|
| if (maybe_hash->To<Object>(&hash) && hash->IsSmi()) {
|
| Handle<JSObject> new_self(JSObject::cast(*self));
|
| - isolate->factory()->SetIdentityHash(new_self, hash);
|
| + isolate->factory()->SetIdentityHash(new_self, Smi::cast(hash));
|
| }
|
| }
|
|
|
| @@ -3505,7 +3505,7 @@ Smi* JSReceiver::GenerateIdentityHash() {
|
| }
|
|
|
|
|
| -MaybeObject* JSObject::SetIdentityHash(Object* hash, CreationFlag flag) {
|
| +MaybeObject* JSObject::SetIdentityHash(Smi* hash, CreationFlag flag) {
|
| MaybeObject* maybe = SetHiddenProperty(GetHeap()->identity_hash_symbol(),
|
| hash);
|
| if (maybe->IsFailure()) return maybe;
|
| @@ -3551,6 +3551,7 @@ MaybeObject* JSProxy::GetIdentityHash(CreationFlag flag) {
|
|
|
|
|
| Object* JSObject::GetHiddenProperty(String* key) {
|
| + ASSERT(key->IsSymbol());
|
| if (IsJSGlobalProxy()) {
|
| // For a proxy, use the prototype as target object.
|
| Object* proxy_parent = GetPrototype();
|
| @@ -3560,22 +3561,32 @@ Object* JSObject::GetHiddenProperty(String* key) {
|
| return JSObject::cast(proxy_parent)->GetHiddenProperty(key);
|
| }
|
| ASSERT(!IsJSGlobalProxy());
|
| - MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false);
|
| + MaybeObject* hidden_lookup =
|
| + GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE);
|
| ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg.
|
| - if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) {
|
| - return GetHeap()->undefined_value();
|
| + Object* inline_value = hidden_lookup->ToObjectUnchecked();
|
| +
|
| + if (inline_value->IsSmi()) {
|
| + // Handle inline-stored identity hash.
|
| + if (key == GetHeap()->identity_hash_symbol()) {
|
| + return inline_value;
|
| + } else {
|
| + return GetHeap()->undefined_value();
|
| + }
|
| }
|
| - StringDictionary* dictionary =
|
| - StringDictionary::cast(hidden_lookup->ToObjectUnchecked());
|
| - int entry = dictionary->FindEntry(key);
|
| - if (entry == StringDictionary::kNotFound) return GetHeap()->undefined_value();
|
| - return dictionary->ValueAt(entry);
|
| +
|
| + if (inline_value->IsUndefined()) return GetHeap()->undefined_value();
|
| +
|
| + ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value);
|
| + Object* entry = hashtable->Lookup(key);
|
| + if (entry->IsTheHole()) return GetHeap()->undefined_value();
|
| + return entry;
|
| }
|
|
|
|
|
| Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> obj,
|
| - Handle<String> key,
|
| - Handle<Object> value) {
|
| + Handle<String> key,
|
| + Handle<Object> value) {
|
| CALL_HEAP_FUNCTION(obj->GetIsolate(),
|
| obj->SetHiddenProperty(*key, *value),
|
| Object);
|
| @@ -3583,6 +3594,7 @@ Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> obj,
|
|
|
|
|
| MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) {
|
| + ASSERT(key->IsSymbol());
|
| if (IsJSGlobalProxy()) {
|
| // For a proxy, use the prototype as target object.
|
| Object* proxy_parent = GetPrototype();
|
| @@ -3592,27 +3604,31 @@ MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) {
|
| return JSObject::cast(proxy_parent)->SetHiddenProperty(key, value);
|
| }
|
| ASSERT(!IsJSGlobalProxy());
|
| - MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(true);
|
| - StringDictionary* dictionary;
|
| - if (!hidden_lookup->To<StringDictionary>(&dictionary)) return hidden_lookup;
|
|
|
| - // If it was found, check if the key is already in the dictionary.
|
| - int entry = dictionary->FindEntry(key);
|
| - if (entry != StringDictionary::kNotFound) {
|
| - // If key was found, just update the value.
|
| - dictionary->ValueAtPut(entry, value);
|
| - return this;
|
| + // If there is no backing store yet, store the identity hash inline.
|
| + MaybeObject* hidden_lookup =
|
| + GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE);
|
| + ASSERT(!hidden_lookup->IsFailure());
|
| + Object* inline_value = hidden_lookup->ToObjectUnchecked();
|
| +
|
| + if (value->IsSmi() &&
|
| + key == GetHeap()->identity_hash_symbol() &&
|
| + (inline_value->IsUndefined() || inline_value->IsSmi())) {
|
| + return SetHiddenPropertiesHashTable(value);
|
| }
|
| - // Key was not already in the dictionary, so add the entry.
|
| - MaybeObject* insert_result = dictionary->Add(key,
|
| - value,
|
| - PropertyDetails(NONE, NORMAL));
|
| - StringDictionary* new_dict;
|
| - if (!insert_result->To<StringDictionary>(&new_dict)) return insert_result;
|
| - if (new_dict != dictionary) {
|
| +
|
| + hidden_lookup = GetHiddenPropertiesHashTable(CREATE_NEW_IF_ABSENT);
|
| + ObjectHashTable* hashtable;
|
| + if (!hidden_lookup->To(&hashtable)) return hidden_lookup;
|
| +
|
| + // If it was found, check if the key is already in the dictionary.
|
| + MaybeObject* insert_result = hashtable->Put(key, value);
|
| + ObjectHashTable* new_table;
|
| + if (!insert_result->To(&new_table)) return insert_result;
|
| + if (new_table != hashtable) {
|
| // If adding the key expanded the dictionary (i.e., Add returned a new
|
| // dictionary), store it back to the object.
|
| - MaybeObject* store_result = SetHiddenPropertiesDictionary(new_dict);
|
| + MaybeObject* store_result = SetHiddenPropertiesHashTable(new_table);
|
| if (store_result->IsFailure()) return store_result;
|
| }
|
| // Return this to mark success.
|
| @@ -3621,6 +3637,7 @@ MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) {
|
|
|
|
|
| void JSObject::DeleteHiddenProperty(String* key) {
|
| + ASSERT(key->IsSymbol());
|
| if (IsJSGlobalProxy()) {
|
| // For a proxy, use the prototype as target object.
|
| Object* proxy_parent = GetPrototype();
|
| @@ -3630,18 +3647,18 @@ void JSObject::DeleteHiddenProperty(String* key) {
|
| JSObject::cast(proxy_parent)->DeleteHiddenProperty(key);
|
| return;
|
| }
|
| - MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false);
|
| + MaybeObject* hidden_lookup =
|
| + GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE);
|
| ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg.
|
| if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) return;
|
| - StringDictionary* dictionary =
|
| - StringDictionary::cast(hidden_lookup->ToObjectUnchecked());
|
| - int entry = dictionary->FindEntry(key);
|
| - if (entry == StringDictionary::kNotFound) {
|
| - // Key wasn't in dictionary. Deletion is a success.
|
| - return;
|
| - }
|
| - // Key was in the dictionary. Remove it.
|
| - dictionary->DeleteProperty(entry, JSReceiver::FORCE_DELETION);
|
| + // We never delete (inline-stored) identity hashes.
|
| + ASSERT(!hidden_lookup->ToObjectUnchecked()->IsSmi());
|
| +
|
| + ObjectHashTable* hashtable =
|
| + ObjectHashTable::cast(hidden_lookup->ToObjectUnchecked());
|
| + MaybeObject* delete_result = hashtable->Put(key, GetHeap()->the_hole_value());
|
| + USE(delete_result);
|
| + ASSERT(!delete_result->IsFailure()); // Delete does not cause GC.
|
| }
|
|
|
|
|
| @@ -3652,8 +3669,10 @@ bool JSObject::HasHiddenProperties() {
|
| }
|
|
|
|
|
| -MaybeObject* JSObject::GetHiddenPropertiesDictionary(bool create_if_absent) {
|
| +MaybeObject* JSObject::GetHiddenPropertiesHashTable(
|
| + InitializeHiddenProperties init_option) {
|
| ASSERT(!IsJSGlobalProxy());
|
| + Object* inline_value;
|
| if (HasFastProperties()) {
|
| // If the object has fast properties, check whether the first slot
|
| // in the descriptor array matches the hidden symbol. Since the
|
| @@ -3663,43 +3682,60 @@ MaybeObject* JSObject::GetHiddenPropertiesDictionary(bool create_if_absent) {
|
| if ((descriptors->number_of_descriptors() > 0) &&
|
| (descriptors->GetKey(0) == GetHeap()->hidden_symbol())) {
|
| ASSERT(descriptors->GetType(0) == FIELD);
|
| - Object* hidden_store =
|
| - this->FastPropertyAt(descriptors->GetFieldIndex(0));
|
| - return StringDictionary::cast(hidden_store);
|
| + inline_value = this->FastPropertyAt(descriptors->GetFieldIndex(0));
|
| + } else {
|
| + inline_value = GetHeap()->undefined_value();
|
| }
|
| } else {
|
| PropertyAttributes attributes;
|
| // You can't install a getter on a property indexed by the hidden symbol,
|
| // so we can be sure that GetLocalPropertyPostInterceptor returns a real
|
| // object.
|
| - Object* lookup =
|
| + inline_value =
|
| GetLocalPropertyPostInterceptor(this,
|
| GetHeap()->hidden_symbol(),
|
| &attributes)->ToObjectUnchecked();
|
| - if (!lookup->IsUndefined()) {
|
| - return StringDictionary::cast(lookup);
|
| - }
|
| }
|
| - if (!create_if_absent) return GetHeap()->undefined_value();
|
| - const int kInitialSize = 5;
|
| - MaybeObject* dict_alloc = StringDictionary::Allocate(kInitialSize);
|
| - StringDictionary* dictionary;
|
| - if (!dict_alloc->To<StringDictionary>(&dictionary)) return dict_alloc;
|
| +
|
| + if (init_option == ONLY_RETURN_INLINE_VALUE ||
|
| + inline_value->IsHashTable()) {
|
| + return inline_value;
|
| + }
|
| +
|
| + ObjectHashTable* hashtable;
|
| + static const int kInitialCapacity = 4;
|
| + MaybeObject* maybe_obj =
|
| + ObjectHashTable::Allocate(kInitialCapacity,
|
| + ObjectHashTable::USE_CUSTOM_MINIMUM_CAPACITY);
|
| + if (!maybe_obj->To<ObjectHashTable>(&hashtable)) return maybe_obj;
|
| +
|
| + if (inline_value->IsSmi()) {
|
| + // We were storing the identity hash inline and now allocated an actual
|
| + // dictionary. Put the identity hash into the new dictionary.
|
| + MaybeObject* insert_result =
|
| + hashtable->Put(GetHeap()->identity_hash_symbol(), inline_value);
|
| + ObjectHashTable* new_table;
|
| + if (!insert_result->To(&new_table)) return insert_result;
|
| + // We expect no resizing for the first insert.
|
| + ASSERT_EQ(hashtable, new_table);
|
| + }
|
| +
|
| MaybeObject* store_result =
|
| SetPropertyPostInterceptor(GetHeap()->hidden_symbol(),
|
| - dictionary,
|
| + hashtable,
|
| DONT_ENUM,
|
| kNonStrictMode,
|
| OMIT_EXTENSIBILITY_CHECK);
|
| if (store_result->IsFailure()) return store_result;
|
| - return dictionary;
|
| + return hashtable;
|
| }
|
|
|
|
|
| -MaybeObject* JSObject::SetHiddenPropertiesDictionary(
|
| - StringDictionary* dictionary) {
|
| +MaybeObject* JSObject::SetHiddenPropertiesHashTable(Object* value) {
|
| ASSERT(!IsJSGlobalProxy());
|
| - ASSERT(HasHiddenProperties());
|
| + // We can store the identity hash inline iff there is no backing store
|
| + // for hidden properties yet.
|
| + ASSERT(HasHiddenProperties() != value->IsSmi());
|
| if (HasFastProperties()) {
|
| // If the object has fast properties, check whether the first slot
|
| // in the descriptor array matches the hidden symbol. Since the
|
| @@ -3709,13 +3745,13 @@ MaybeObject* JSObject::SetHiddenPropertiesDictionary(
|
| if ((descriptors->number_of_descriptors() > 0) &&
|
| (descriptors->GetKey(0) == GetHeap()->hidden_symbol())) {
|
| ASSERT(descriptors->GetType(0) == FIELD);
|
| - this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary);
|
| + this->FastPropertyAtPut(descriptors->GetFieldIndex(0), value);
|
| return this;
|
| }
|
| }
|
| MaybeObject* store_result =
|
| SetPropertyPostInterceptor(GetHeap()->hidden_symbol(),
|
| - dictionary,
|
| + value,
|
| DONT_ENUM,
|
| kNonStrictMode,
|
| OMIT_EXTENSIBILITY_CHECK);
|
| @@ -11046,8 +11082,12 @@ void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
|
|
|
| template<typename Shape, typename Key>
|
| MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for,
|
| + MinimumCapacity capacity_option,
|
| PretenureFlag pretenure) {
|
| - int capacity = ComputeCapacity(at_least_space_for);
|
| + ASSERT(!capacity_option || IS_POWER_OF_TWO(at_least_space_for));
|
| + int capacity = (capacity_option == USE_CUSTOM_MINIMUM_CAPACITY)
|
| + ? at_least_space_for
|
| + : ComputeCapacity(at_least_space_for);
|
| if (capacity > HashTable::kMaxCapacity) {
|
| return Failure::OutOfMemoryException();
|
| }
|
| @@ -11156,7 +11196,9 @@ MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
|
| (capacity > kMinCapacityForPretenure) && !GetHeap()->InNewSpace(this);
|
| Object* obj;
|
| { MaybeObject* maybe_obj =
|
| - Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
|
| + Allocate(nof * 2,
|
| + USE_DEFAULT_MINIMUM_CAPACITY,
|
| + pretenure ? TENURED : NOT_TENURED);
|
| if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| }
|
|
|
| @@ -11185,7 +11227,9 @@ MaybeObject* HashTable<Shape, Key>::Shrink(Key key) {
|
| !GetHeap()->InNewSpace(this);
|
| Object* obj;
|
| { MaybeObject* maybe_obj =
|
| - Allocate(at_least_room_for, pretenure ? TENURED : NOT_TENURED);
|
| + Allocate(at_least_room_for,
|
| + USE_DEFAULT_MINIMUM_CAPACITY,
|
| + pretenure ? TENURED : NOT_TENURED);
|
| if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| }
|
|
|
|
|