Chromium Code Reviews| Index: src/objects.cc |
| diff --git a/src/objects.cc b/src/objects.cc |
| index aa0b6f227f25b9a74089097467f4742c26f59854..f7a5c7c767f8f43799c5f2a6783ea650ff976d5e 100644 |
| --- a/src/objects.cc |
| +++ b/src/objects.cc |
| @@ -3279,53 +3279,6 @@ MaybeObject* JSObject::NormalizeElements() { |
| } |
| -MaybeObject* JSObject::GetHiddenProperties(CreationFlag 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; |
| - } |
| - // Don't allow leakage of the hidden object through accessors |
| - // on Object.prototype. |
| - { |
| - MaybeObject* maybe_obj = |
| - JSObject::cast(hidden_obj)->SetPrototype(heap->null_value(), false); |
| - if (maybe_obj->IsFailure()) return maybe_obj; |
| - } |
| - return obj->SetHiddenPropertiesObject(hidden_obj); |
| - } else { |
| - return heap->undefined_value(); |
| - } |
| - } |
| - return obj->GetHiddenPropertiesObject(); |
| -} |
| - |
| - |
| Smi* JSReceiver::GenerateIdentityHash() { |
| Isolate* isolate = GetIsolate(); |
| @@ -3344,46 +3297,24 @@ Smi* JSReceiver::GenerateIdentityHash() { |
| MaybeObject* JSObject::SetIdentityHash(Object* hash, CreationFlag flag) { |
| - JSObject* hidden_props; |
| - MaybeObject* maybe = GetHiddenProperties(flag); |
| - if (!maybe->To<JSObject>(&hidden_props)) return maybe; |
| - maybe = hidden_props->SetLocalPropertyIgnoreAttributes( |
| - GetHeap()->identity_hash_symbol(), hash, NONE); |
| + MaybeObject* maybe = SetHiddenProperty(GetHeap()->identity_hash_symbol(), |
| + hash); |
| if (maybe->IsFailure()) return maybe; |
| return this; |
| } |
| MaybeObject* JSObject::GetIdentityHash(CreationFlag 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()); |
| - } |
| - } |
| + Object* stored_value = GetHiddenProperty(GetHeap()->identity_hash_symbol()); |
| + if (stored_value->IsSmi()) return stored_value; |
| Smi* hash = GenerateIdentityHash(); |
| - { MaybeObject* result = hidden_props->SetLocalPropertyIgnoreAttributes( |
| - hash_symbol, |
| - hash, |
| - static_cast<PropertyAttributes>(None)); |
| - if (result->IsFailure()) return result; |
| + MaybeObject* result = SetHiddenProperty(GetHeap()->identity_hash_symbol(), |
| + hash); |
| + if (result->IsFailure()) return result; |
| + if (result->ToObjectUnchecked()->IsUndefined()) { |
| + // Trying to get hash of detached proxy. |
| + return Smi::FromInt(0); |
| } |
| return hash; |
| } |
| @@ -3399,6 +3330,171 @@ MaybeObject* JSProxy::GetIdentityHash(CreationFlag flag) { |
| } |
| +Object* JSObject::GetHiddenProperty(String* key) { |
| + if (IsJSGlobalProxy()) { |
| + // For a proxy, use the prototype as target object. |
| + Object* proxy_parent = GetPrototype(); |
| + // If the proxy is detached, return undefined. |
| + if (proxy_parent->IsNull()) return GetHeap()->undefined_value(); |
| + ASSERT(proxy_parent->IsJSGlobalObject()); |
| + return JSObject::cast(proxy_parent)->GetHiddenProperty(key); |
| + } |
| + ASSERT(!IsJSGlobalProxy()); |
| + MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false); |
| + ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg. |
| + if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) { |
| + 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); |
| +} |
| + |
| + |
| +MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) { |
| + if (IsJSGlobalProxy()) { |
| + // For a proxy, use the prototype as target object. |
| + Object* proxy_parent = GetPrototype(); |
| + // If the proxy is detached, return undefined. |
| + if (proxy_parent->IsNull()) return GetHeap()->undefined_value(); |
| + ASSERT(proxy_parent->IsJSGlobalObject()); |
| + 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; |
| + } |
| + // 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) { |
| + // 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); |
| + if (store_result->IsFailure()) return store_result; |
| + } |
| + // Return this to mark success. |
| + return this; |
| +} |
| + |
| + |
| +void JSObject::DeleteHiddenProperty(String* key) { |
| + if (IsJSGlobalProxy()) { |
| + // For a proxy, use the prototype as target object. |
| + Object* proxy_parent = GetPrototype(); |
| + // If the proxy is detached, return immediately. |
| + if (proxy_parent->IsNull()) return; |
| + ASSERT(proxy_parent->IsJSGlobalObject()); |
| + JSObject::cast(proxy_parent)->DeleteHiddenProperty(key); |
| + return; |
| + } |
| + MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false); |
| + 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); |
| +} |
| + |
| + |
| +bool JSObject::HasHiddenProperties() { |
| + LookupResult lookup; |
| + LocalLookupRealNamedProperty(GetHeap()->hidden_symbol(), &lookup); |
| + return lookup.IsFound(); |
| +} |
| + |
| + |
| +MaybeObject* JSObject::GetHiddenPropertiesDictionary(bool create_if_absent) { |
| + ASSERT(!IsJSGlobalProxy()); |
| + if (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 = this->map()->instance_descriptors(); |
| + if ((descriptors->number_of_descriptors() > 0) && |
| + (descriptors->GetKey(0) == GetHeap()->hidden_symbol()) && |
| + descriptors->IsProperty(0)) { |
| + ASSERT(descriptors->GetType(0) == FIELD); |
| + Object* hidden_store = |
| + this->FastPropertyAt(descriptors->GetFieldIndex(0)); |
| + return StringDictionary::cast(hidden_store); |
| + } |
| + } 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 = |
| + 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; |
| + MaybeObject* store_result = |
|
Rico
2011/09/28 08:59:56
If we have fast properties shouldn't we set this o
Lasse Reichstein
2011/09/28 10:21:42
The SetPropertyPostInterceptor will put the new pr
|
| + SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), |
| + dictionary, |
| + DONT_ENUM, |
| + kNonStrictMode); |
| + if (store_result->IsFailure()) return store_result; |
| + return dictionary; |
| +} |
| + |
| + |
| +MaybeObject* JSObject::SetHiddenPropertiesDictionary( |
| + StringDictionary* dictionary) { |
| + ASSERT(!IsJSGlobalProxy()); |
| + ASSERT(HasHiddenProperties()); |
| + if (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 = this->map()->instance_descriptors(); |
| + if ((descriptors->number_of_descriptors() > 0) && |
| + (descriptors->GetKey(0) == GetHeap()->hidden_symbol()) && |
| + descriptors->IsProperty(0)) { |
| + ASSERT(descriptors->GetType(0) == FIELD); |
| + this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary); |
| + return this; |
| + } |
| + } |
| + MaybeObject* store_result = |
| + SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), |
| + dictionary, |
| + DONT_ENUM, |
| + kNonStrictMode); |
| + if (store_result->IsFailure()) return store_result; |
| + return this; |
| +} |
| + |
| + |
| MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name, |
| DeleteMode mode) { |
| // Check local property, ignore interceptor. |