From: yangguo@chromium.org Date: Tue, 12 Jul 2011 15:13:57 +0000 (+0000) Subject: Added dictionary that can use objects as keys. X-Git-Tag: upstream/4.7.83~18920 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c0044bb7e2663295dba2b0e49b9ab5043704175c;p=platform%2Fupstream%2Fv8.git Added dictionary that can use objects as keys. R=vegorov@chromium.org TEST=cctest/test-dictionary Review URL: http://codereview.chromium.org/7349005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8619 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/api.cc b/src/api.cc index 71a715c..a59b934 100644 --- a/src/api.cc +++ b/src/api.cc @@ -3092,39 +3092,7 @@ int v8::Object::GetIdentityHash() { ENTER_V8(isolate); i::HandleScope scope(isolate); i::Handle self = Utils::OpenHandle(this); - i::Handle hidden_props_obj(i::GetHiddenProperties(self, true)); - if (!hidden_props_obj->IsJSObject()) { - // We failed to create hidden properties. That's a detached - // global proxy. - ASSERT(hidden_props_obj->IsUndefined()); - return 0; - } - i::Handle hidden_props = - i::Handle::cast(hidden_props_obj); - i::Handle hash_symbol = isolate->factory()->identity_hash_symbol(); - if (hidden_props->HasLocalProperty(*hash_symbol)) { - i::Handle hash = i::GetProperty(hidden_props, hash_symbol); - CHECK(!hash.is_null()); - CHECK(hash->IsSmi()); - return i::Smi::cast(*hash)->value(); - } - - int hash_value; - int attempts = 0; - do { - // Generate a random 32-bit hash value but limit range to fit - // within a smi. - hash_value = i::V8::Random(self->GetIsolate()) & i::Smi::kMaxValue; - attempts++; - } while (hash_value == 0 && attempts < 30); - hash_value = hash_value != 0 ? hash_value : 1; // never return 0 - CHECK(!i::SetLocalPropertyIgnoreAttributes( - hidden_props, - hash_symbol, - i::Handle(i::Smi::FromInt(hash_value)), - static_cast(None)).is_null()); - - return hash_value; + return i::GetIdentityHash(self)->value(); } diff --git a/src/handles.cc b/src/handles.cc index d73aaf0..c553f6e 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -427,38 +427,14 @@ Handle GetHiddenProperties(Handle obj, Object* holder = obj->BypassGlobalProxy(); if (holder->IsUndefined()) return isolate->factory()->undefined_value(); obj = Handle(JSObject::cast(holder), isolate); + CALL_HEAP_FUNCTION(isolate, + obj->GetHiddenProperties(create_if_needed), + Object); +} - 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) == isolate->heap()->hidden_symbol()) && - descriptors->IsProperty(0)) { - ASSERT(descriptors->GetType(0) == FIELD); - return Handle(obj->FastPropertyAt(descriptors->GetFieldIndex(0)), - isolate); - } - } - // Only attempt to find the hidden properties in the local object and not - // in the prototype chain. Note that HasLocalProperty() can cause a GC in - // the general case in the presence of interceptors. - if (!obj->HasHiddenPropertiesObject()) { - // Hidden properties object not found. Allocate a new hidden properties - // object if requested. Otherwise return the undefined value. - if (create_if_needed) { - Handle hidden_obj = - isolate->factory()->NewJSObject(isolate->object_function()); - CALL_HEAP_FUNCTION(isolate, - obj->SetHiddenPropertiesObject(*hidden_obj), Object); - } else { - return isolate->factory()->undefined_value(); - } - } - return Handle(obj->GetHiddenPropertiesObject(), isolate); +Handle GetIdentityHash(Handle obj) { + CALL_HEAP_FUNCTION(obj->GetIsolate(), obj->GetIdentityHash(), Smi); } diff --git a/src/handles.h b/src/handles.h index 13c6dd6..a4ee6e7 100644 --- a/src/handles.h +++ b/src/handles.h @@ -268,6 +268,8 @@ Handle SetPrototype(Handle obj, Handle value); // will be allocated. Otherwise the Heap::undefined_value is returned. Handle GetHiddenProperties(Handle obj, bool create_if_needed); +Handle GetIdentityHash(Handle obj); + Handle DeleteElement(Handle obj, uint32_t index); Handle DeleteProperty(Handle obj, Handle prop); diff --git a/src/objects-inl.h b/src/objects-inl.h index eb537c7..6c81f0a 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -4302,6 +4302,32 @@ MaybeObject* StringDictionaryShape::AsObject(String* key) { } +bool ObjectDictionaryShape::IsMatch(JSObject* key, Object* other) { + ASSERT(other->IsJSObject()); + return key == JSObject::cast(other); +} + + +uint32_t ObjectDictionaryShape::Hash(JSObject* key) { + MaybeObject* maybe_hash = key->GetIdentityHash(); + ASSERT(!maybe_hash->IsFailure()); + return Smi::cast(maybe_hash->ToObjectUnchecked())->value(); +} + + +uint32_t ObjectDictionaryShape::HashForObject(JSObject* key, Object* other) { + ASSERT(other->IsJSObject()); + MaybeObject* maybe_hash = JSObject::cast(other)->GetIdentityHash(); + ASSERT(!maybe_hash->IsFailure()); + return Smi::cast(maybe_hash->ToObjectUnchecked())->value(); +} + + +MaybeObject* ObjectDictionaryShape::AsObject(JSObject* key) { + return key; +} + + void Map::ClearCodeCache(Heap* heap) { // No write barrier is needed since empty_fixed_array is not in new space. // Please note this function is used during marking: diff --git a/src/objects.cc b/src/objects.cc index ca780db..7c21172 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -2890,6 +2890,84 @@ MaybeObject* JSObject::NormalizeElements() { } +MaybeObject* JSObject::GetHiddenProperties(bool create_if_needed) { + Isolate* isolate = GetIsolate(); + Heap* heap = isolate->heap(); + 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 = map()->instance_descriptors(); + if ((descriptors->number_of_descriptors() > 0) && + (descriptors->GetKey(0) == heap->hidden_symbol()) && + descriptors->IsProperty(0)) { + ASSERT(descriptors->GetType(0) == FIELD); + return FastPropertyAt(descriptors->GetFieldIndex(0)); + } + } + + // Only attempt to find the hidden properties in the local object and not + // in the prototype chain. Note that HasLocalProperty() can cause a GC in + // the general case in the presence of interceptors. + if (!HasHiddenPropertiesObject()) { + // Hidden properties object not found. Allocate a new hidden properties + // object if requested. Otherwise return the undefined value. + if (create_if_needed) { + Object* hidden_obj; + { MaybeObject* maybe_obj = heap->AllocateJSObject( + isolate->context()->global_context()->object_function()); + if (!maybe_obj->ToObject(&hidden_obj)) return maybe_obj; + } + return SetHiddenPropertiesObject(hidden_obj); + } else { + return heap->undefined_value(); + } + } + return GetHiddenPropertiesObject(); +} + + +MaybeObject* JSObject::GetIdentityHash() { + Isolate* isolate = GetIsolate(); + Object* hidden_props_obj; + { MaybeObject* maybe_obj = GetHiddenProperties(true); + 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(); + 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(None)); + if (result->IsFailure()) return result; + } + return hash; +} + + MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) { // Check local property, ignore interceptor. @@ -10135,12 +10213,17 @@ template class Dictionary; template class Dictionary; +template class Dictionary; + template MaybeObject* Dictionary::Allocate( int); template MaybeObject* Dictionary::Allocate( int); +template MaybeObject* Dictionary::Allocate( + int); + template MaybeObject* Dictionary::AtPut( uint32_t, Object*); @@ -10178,6 +10261,9 @@ Dictionary::NumberOfElementsFilterAttributes( template MaybeObject* Dictionary::Add( String*, Object*, PropertyDetails); +template MaybeObject* Dictionary::Add( + JSObject*, Object*, PropertyDetails); + template MaybeObject* Dictionary::GenerateNewEnumerationIndices(); @@ -11137,7 +11223,8 @@ MaybeObject* Dictionary::AddEntry(Key key, } SetEntry(entry, k, value, details); ASSERT((Dictionary::KeyAt(entry)->IsNumber() - || Dictionary::KeyAt(entry)->IsString())); + || Dictionary::KeyAt(entry)->IsString() + || Dictionary::KeyAt(entry)->IsJSObject())); HashTable::ElementAdded(); return this; } @@ -11442,6 +11529,15 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( } +MaybeObject* ObjectDictionary::AddChecked(JSObject* key, Object* value) { + // Make sure the key object has an identity hash code. + MaybeObject* maybe_hash = key->GetIdentityHash(); + if (maybe_hash->IsFailure()) return maybe_hash; + PropertyDetails details(NONE, NORMAL); + return Add(key, value, details); +} + + #ifdef ENABLE_DEBUGGER_SUPPORT // Check if there is a break point at this code position. bool DebugInfo::HasBreakPoint(int code_position) { diff --git a/src/objects.h b/src/objects.h index 9765fe2..a7b591c 100644 --- a/src/objects.h +++ b/src/objects.h @@ -1640,6 +1640,11 @@ class JSObject: public JSReceiver { MUST_USE_RESULT inline MaybeObject* SetHiddenPropertiesObject( Object* hidden_obj); + MUST_USE_RESULT MaybeObject* GetHiddenProperties(bool create_if_needed); + + // Retrieves a permanent object identity hash code. + MUST_USE_RESULT MaybeObject* GetIdentityHash(); + MUST_USE_RESULT MaybeObject* DeleteProperty(String* name, DeleteMode mode); MUST_USE_RESULT MaybeObject* DeleteElement(uint32_t index, DeleteMode mode); @@ -2921,6 +2926,29 @@ class NumberDictionary: public Dictionary { }; +class ObjectDictionaryShape { + public: + static inline bool IsMatch(JSObject* key, Object* other); + static inline uint32_t Hash(JSObject* key); + static inline uint32_t HashForObject(JSObject* key, Object* object); + MUST_USE_RESULT static inline MaybeObject* AsObject(JSObject* key); + static const int kPrefixSize = 2; + static const int kEntrySize = 3; + static const bool kIsEnumerable = false; +}; + + +class ObjectDictionary: public Dictionary { + public: + static inline ObjectDictionary* cast(Object* obj) { + ASSERT(obj->IsDictionary()); + return reinterpret_cast(obj); + } + + MUST_USE_RESULT MaybeObject* AddChecked(JSObject* key, Object* value); +}; + + // JSFunctionResultCache caches results of some JSFunction invocation. // It is a fixed array with fixed structure: // [0]: factory function diff --git a/test/cctest/SConscript b/test/cctest/SConscript index c3614b6..fbaf5bf 100644 --- a/test/cctest/SConscript +++ b/test/cctest/SConscript @@ -51,6 +51,7 @@ SOURCES = { 'test-debug.cc', 'test-decls.cc', 'test-deoptimization.cc', + 'test-dictionary.cc', 'test-diy-fp.cc', 'test-double.cc', 'test-dtoa.cc', diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index 1d54e8c..a45963c 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -56,6 +56,7 @@ 'test-debug.cc', 'test-decls.cc', 'test-deoptimization.cc', + 'test-dictionary.cc', 'test-diy-fp.cc', 'test-double.cc', 'test-dtoa.cc', diff --git a/test/cctest/test-dictionary.cc b/test/cctest/test-dictionary.cc new file mode 100644 index 0000000..2d31b37 --- /dev/null +++ b/test/cctest/test-dictionary.cc @@ -0,0 +1,63 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "api.h" +#include "debug.h" +#include "execution.h" +#include "factory.h" +#include "macro-assembler.h" +#include "objects.h" +#include "global-handles.h" +#include "cctest.h" + +using namespace v8::internal; + +static Handle NewObjectDictionary(int at_least_space_for) { + ASSERT(0 <= at_least_space_for); + CALL_HEAP_FUNCTION(Isolate::Current(), + ObjectDictionary::Allocate(at_least_space_for), + ObjectDictionary); +} + +TEST(ObjectDictionary) { + v8::HandleScope scope; + LocalContext context; + Handle dict = NewObjectDictionary(23); + Handle a = FACTORY->NewJSArray(7); + Handle b = FACTORY->NewJSArray(11); + MaybeObject* result = dict->AddChecked(*a, *b); + CHECK(!result->IsFailure()); + CHECK_NE(dict->FindEntry(*a), ObjectDictionary::kNotFound); + CHECK_EQ(dict->FindEntry(*b), ObjectDictionary::kNotFound); + + // Keys still have to be valid after objects were moved. + HEAP->CollectGarbage(NEW_SPACE); + CHECK_NE(dict->FindEntry(*a), ObjectDictionary::kNotFound); + CHECK_EQ(dict->FindEntry(*b), ObjectDictionary::kNotFound); +}