Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index a423ae4c25df2caaff6ade6de2f2684f94286522..ba5aa7765aa7463e0071f842afd0b1a8555ca641 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -2967,6 +2967,91 @@ MaybeObject* JSObject::NormalizeElements() { |
} |
+MaybeObject* JSObject::GetHiddenProperties(HiddenPropertiesFlag flag) { |
+ Isolate* isolate = GetIsolate(); |
+ Heap* heap = isolate->heap(); |
+ Object* holder = BypassGlobalProxy(); |
+ if (holder->IsUndefined()) return heap->undefined_value(); |
+ JSObject* obj = JSObject::cast(holder); |
+ if (obj->HasFastProperties()) { |
+ // If the object has fast properties, check whether the first slot |
+ // in the descriptor array matches the hidden symbol. Since the |
+ // hidden symbols hash code is zero (and no other string has hash |
+ // code zero) it will always occupy the first entry if present. |
+ DescriptorArray* descriptors = obj->map()->instance_descriptors(); |
+ if ((descriptors->number_of_descriptors() > 0) && |
+ (descriptors->GetKey(0) == heap->hidden_symbol()) && |
+ descriptors->IsProperty(0)) { |
+ ASSERT(descriptors->GetType(0) == FIELD); |
+ return obj->FastPropertyAt(descriptors->GetFieldIndex(0)); |
+ } |
+ } |
+ |
+ // Only attempt to find the hidden properties in the local object and not |
+ // in the prototype chain. |
+ if (!obj->HasHiddenPropertiesObject()) { |
+ // Hidden properties object not found. Allocate a new hidden properties |
+ // object if requested. Otherwise return the undefined value. |
+ if (flag == ALLOW_CREATION) { |
+ Object* hidden_obj; |
+ { MaybeObject* maybe_obj = heap->AllocateJSObject( |
+ isolate->context()->global_context()->object_function()); |
+ if (!maybe_obj->ToObject(&hidden_obj)) return maybe_obj; |
+ } |
+ return obj->SetHiddenPropertiesObject(hidden_obj); |
+ } else { |
+ return heap->undefined_value(); |
+ } |
+ } |
+ return obj->GetHiddenPropertiesObject(); |
+} |
+ |
+ |
+MaybeObject* JSObject::GetIdentityHash(HiddenPropertiesFlag flag) { |
+ Isolate* isolate = GetIsolate(); |
+ Object* hidden_props_obj; |
+ { MaybeObject* maybe_obj = GetHiddenProperties(flag); |
+ if (!maybe_obj->ToObject(&hidden_props_obj)) return maybe_obj; |
+ } |
+ if (!hidden_props_obj->IsJSObject()) { |
+ // We failed to create hidden properties. That's a detached |
+ // global proxy. |
+ ASSERT(hidden_props_obj->IsUndefined()); |
+ return Smi::FromInt(0); |
+ } |
+ JSObject* hidden_props = JSObject::cast(hidden_props_obj); |
+ String* hash_symbol = isolate->heap()->identity_hash_symbol(); |
+ { |
+ // Note that HasLocalProperty() can cause a GC in the general case in the |
+ // presence of interceptors. |
+ AssertNoAllocation no_alloc; |
+ if (hidden_props->HasLocalProperty(hash_symbol)) { |
+ MaybeObject* hash = hidden_props->GetProperty(hash_symbol); |
+ return Smi::cast(hash->ToObjectChecked()); |
+ } |
+ } |
+ |
+ int hash_value; |
+ int attempts = 0; |
+ do { |
+ // Generate a random 32-bit hash value but limit range to fit |
+ // within a smi. |
+ hash_value = V8::Random(isolate) & Smi::kMaxValue; |
+ attempts++; |
+ } while (hash_value == 0 && attempts < 30); |
+ hash_value = hash_value != 0 ? hash_value : 1; // never return 0 |
+ |
+ Smi* hash = Smi::FromInt(hash_value); |
+ { MaybeObject* result = hidden_props->SetLocalPropertyIgnoreAttributes( |
+ hash_symbol, |
+ hash, |
+ static_cast<PropertyAttributes>(None)); |
+ if (result->IsFailure()) return result; |
+ } |
+ return hash; |
+} |
+ |
+ |
MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name, |
DeleteMode mode) { |
// Check local property, ignore interceptor. |
@@ -10380,6 +10465,8 @@ template class HashTable<CompilationCacheShape, HashTableKey*>; |
template class HashTable<MapCacheShape, HashTableKey*>; |
+template class HashTable<ObjectHashTableShape, JSObject*>; |
+ |
template class Dictionary<StringDictionaryShape, String*>; |
template class Dictionary<NumberDictionaryShape, uint32_t>; |
@@ -11691,6 +11778,64 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( |
} |
+Object* ObjectHashTable::Lookup(JSObject* key) { |
+ // If the object does not have an identity hash, it was never used as a key. |
+ MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::OMIT_CREATION); |
+ if (maybe_hash->IsFailure()) return GetHeap()->undefined_value(); |
+ int entry = FindEntry(key); |
+ if (entry == kNotFound) return GetHeap()->undefined_value(); |
+ return get(EntryToIndex(entry) + 1); |
+} |
+ |
+ |
+MaybeObject* ObjectHashTable::Put(JSObject* key, Object* value) { |
+ // Make sure the key object has an identity hash code. |
+ int hash; |
+ { MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::ALLOW_CREATION); |
+ if (maybe_hash->IsFailure()) return maybe_hash; |
+ hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value(); |
+ } |
+ int entry = FindEntry(key); |
+ |
+ // Check whether to perform removal operation. |
+ if (value->IsUndefined()) { |
+ if (entry == kNotFound) return this; |
+ RemoveEntry(entry); |
+ return Shrink(key); |
+ } |
+ |
+ // Key is already in table, just overwrite value. |
+ if (entry != kNotFound) { |
+ set(EntryToIndex(entry) + 1, value); |
+ return this; |
+ } |
+ |
+ // Check whether the hash table should be extended. |
+ Object* obj; |
+ { MaybeObject* maybe_obj = EnsureCapacity(1, key); |
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
+ } |
+ ObjectHashTable* table = ObjectHashTable::cast(obj); |
+ table->AddEntry(table->FindInsertionEntry(hash), key, value); |
+ return table; |
+} |
+ |
+ |
+void ObjectHashTable::AddEntry(int entry, JSObject* key, Object* value) { |
+ set(EntryToIndex(entry), key); |
+ set(EntryToIndex(entry) + 1, value); |
+ ElementAdded(); |
+} |
+ |
+ |
+void ObjectHashTable::RemoveEntry(int entry) { |
+ Object* null_value = GetHeap()->null_value(); |
+ set(EntryToIndex(entry), null_value); |
+ set(EntryToIndex(entry) + 1, null_value); |
+ ElementRemoved(); |
+} |
+ |
+ |
#ifdef ENABLE_DEBUGGER_SUPPORT |
// Check if there is a break point at this code position. |
bool DebugInfo::HasBreakPoint(int code_position) { |