From eca5b5d7abc0a9028cb9832087fbf2ed59dadf92 Mon Sep 17 00:00:00 2001 From: erikcorry Date: Tue, 26 May 2015 04:26:26 -0700 Subject: [PATCH] Move hash code from hidden string to a private symbol * Hash code is now just done with a private own symbol instead of the hidden string, which predates symbols. * In the long run we should do all hidden properties this way and get rid of the hidden magic 0-length string with the zero hash code. The advantages include less complexity and being able to do things from JS in a natural way. * Initially, the performance of weak set regressed, because it's a little harder to do the lookup in C++. Instead of heroics in C++ to make things faster I moved some functionality into JS and got the performance back. JS is supposed to be good at looking up named properties on objects. * This also changes hash codes of Smis so that they are always Smis. Performance figures are in the comments to the code review. Summary: Most of js-perf-test/Collections is neutral. Set and Map with object keys are 40-50% better. WeakMap is -5% and WeakSet is +9%. After the measurements, I fixed global proxies, which cost 1% on most tests and 5% on the weak ones :-(. In the code review comments is a patch with an example of the heroics we could do in C++ to make lookup faster (I hope we don't have to do this. Instead of checking for the property, then doing a new lookup to insert it, we could do one lookup and handle the addition immediately). With the current benchmarks above this buys us nothing, but if we go back to doing more lookups in C++ instead of in stubs and JS then it's a win. In a similar vein we could give the magic zero hash code to the hash code symbol. Then when we look up the hash code we would sometimes see the table with all the hidden properties. This dual use of the field for either the hash code or the table with all hidden properties and the hash code is rather ugly, and this CL gets rid of it. I'd be loath to bring it back. On the benchmarks quoted above it's slightly slower than moving the hash code lookup to JS like in this CL. One worry is that the benchmark results above are more monomorphic than real world code, so may be overstating the performance benefits of moving to JS. I think this is part of a general issue we have with handling polymorphic code in JS and any solutions there will benefit this solution, which boils down to regular property access. Any improvement there will lift all boats. R=adamk@chromium.org, verwaest@chromium.org BUG= Review URL: https://codereview.chromium.org/1149863005 Cr-Commit-Position: refs/heads/master@{#28622} --- src/api.cc | 3 +- src/arm/macro-assembler-arm.cc | 1 + src/arm64/macro-assembler-arm64.cc | 1 + src/collection.js | 74 +++++++++++++++------ src/factory.cc | 7 +- src/factory.h | 2 +- src/heap/heap.cc | 22 ++++++- src/heap/heap.h | 4 +- src/ia32/macro-assembler-ia32.cc | 1 + src/isolate.cc | 26 +++++--- src/isolate.h | 2 + src/math.js | 11 +++- src/mips/macro-assembler-mips.cc | 1 + src/mips64/macro-assembler-mips64.cc | 1 + src/object-observe.js | 17 +++-- src/objects-inl.h | 11 +++- src/objects.cc | 123 ++++++++++++++++++++--------------- src/objects.h | 14 ++++ src/runtime/runtime-collections.cc | 36 ++++++---- src/runtime/runtime-symbol.cc | 19 ++---- src/runtime/runtime.h | 13 ++-- src/utils.h | 2 +- src/weak-collection.js | 24 +++++-- src/x64/macro-assembler-x64.cc | 1 + test/cctest/test-api.cc | 22 ++++++- test/cctest/test-hashing.cc | 2 +- test/cctest/test-heap.cc | 35 ---------- test/cctest/test-weakmaps.cc | 15 +++-- test/cctest/test-weaksets.cc | 12 ++-- test/mjsunit/global-hash.js | 19 ++++++ 30 files changed, 340 insertions(+), 181 deletions(-) create mode 100644 test/mjsunit/global-hash.js diff --git a/src/api.cc b/src/api.cc index 959e8a0..52f373a 100644 --- a/src/api.cc +++ b/src/api.cc @@ -2540,7 +2540,8 @@ void NativeWeakMap::Set(Handle v8_key, Handle v8_value) { DCHECK(false); return; } - i::Runtime::WeakCollectionSet(weak_collection, key, value); + int32_t hash = i::Object::GetOrCreateHash(isolate, key)->value(); + i::Runtime::WeakCollectionSet(weak_collection, key, value, hash); } diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 12108a0..31d2f73 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -1559,6 +1559,7 @@ void MacroAssembler::GetNumberHash(Register t0, Register scratch) { add(t0, t0, scratch); // hash = hash ^ (hash >> 16); eor(t0, t0, Operand(t0, LSR, 16)); + bic(t0, t0, Operand(0xc0000000u)); } diff --git a/src/arm64/macro-assembler-arm64.cc b/src/arm64/macro-assembler-arm64.cc index 721720f..e15edc9 100644 --- a/src/arm64/macro-assembler-arm64.cc +++ b/src/arm64/macro-assembler-arm64.cc @@ -3928,6 +3928,7 @@ void MacroAssembler::GetNumberHash(Register key, Register scratch) { Add(key, key, scratch); // hash = hash ^ (hash >> 16); Eor(key, key, Operand(key, LSR, 16)); + Bic(key, key, Operand(0xc0000000u)); } diff --git a/src/collection.js b/src/collection.js index 70a88fb..6b468f5 100644 --- a/src/collection.js +++ b/src/collection.js @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -(function(global, utils) { +var $getHash; +var $getExistingHash; +(function(global, utils) { "use strict"; %CheckIsBootstrapping(); @@ -14,6 +16,11 @@ var GlobalMap = global.Map; var GlobalObject = global.Object; var GlobalSet = global.Set; +var IntRandom; + +utils.Import(function(from) { + IntRandom = from.IntRandom; +}); var NumberIsNaN; @@ -31,17 +38,19 @@ function HashToEntry(table, hash, numBuckets) { function SetFindEntry(table, numBuckets, key, hash) { + var entry = HashToEntry(table, hash, numBuckets); + if (entry === NOT_FOUND) return entry; + var candidate = ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets); + if (key === candidate) return entry; var keyIsNaN = NumberIsNaN(key); - for (var entry = HashToEntry(table, hash, numBuckets); - entry !== NOT_FOUND; - entry = ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets)) { - var candidate = ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets); - if (key === candidate) { - return entry; - } + while (true) { if (keyIsNaN && NumberIsNaN(candidate)) { return entry; } + entry = ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets); + if (entry === NOT_FOUND) return entry; + candidate = ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets); + if (key === candidate) return entry; } return NOT_FOUND; } @@ -49,17 +58,19 @@ function SetFindEntry(table, numBuckets, key, hash) { function MapFindEntry(table, numBuckets, key, hash) { + var entry = HashToEntry(table, hash, numBuckets); + if (entry === NOT_FOUND) return entry; + var candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets); + if (key === candidate) return entry; var keyIsNaN = NumberIsNaN(key); - for (var entry = HashToEntry(table, hash, numBuckets); - entry !== NOT_FOUND; - entry = ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets)) { - var candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets); - if (key === candidate) { - return entry; - } + while (true) { if (keyIsNaN && NumberIsNaN(candidate)) { return entry; } + entry = ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets); + if (entry === NOT_FOUND) return entry; + candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets); + if (key === candidate) return entry; } return NOT_FOUND; } @@ -75,12 +86,13 @@ function ComputeIntegerHash(key, seed) { hash = hash ^ (hash >>> 4); hash = (hash * 2057) | 0; // hash = (hash + (hash << 3)) + (hash << 11); hash = hash ^ (hash >>> 16); - return hash; + return hash & 0x3fffffff; } %SetForceInlineFlag(ComputeIntegerHash); +var hashCodeSymbol = GLOBAL_PRIVATE("hash_code_symbol"); -function GetHash(key) { +function GetExistingHash(key) { if (%_IsSmi(key)) { return ComputeIntegerHash(key, 0); } @@ -89,9 +101,24 @@ function GetHash(key) { if ((field & 1 /* Name::kHashNotComputedMask */) === 0) { return field >>> 2 /* Name::kHashShift */; } + } else if (IS_SPEC_OBJECT(key) && !%_IsJSProxy(key) && !IS_GLOBAL(key)) { + var hash = GET_PRIVATE(key, hashCodeSymbol); + return hash; } return %GenericHash(key); } +%SetForceInlineFlag(GetExistingHash); + + +function GetHash(key) { + var hash = GetExistingHash(key); + if (IS_UNDEFINED(hash)) { + hash = IntRandom() | 0; + if (hash === 0) hash = 1; + SET_PRIVATE(key, hashCodeSymbol, hash); + } + return hash; +} %SetForceInlineFlag(GetHash); @@ -164,7 +191,8 @@ function SetHas(key) { } var table = %_JSCollectionGetTable(this); var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); - var hash = GetHash(key); + var hash = GetExistingHash(key); + if (IS_UNDEFINED(hash)) return false; return SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND; } @@ -176,7 +204,8 @@ function SetDelete(key) { } var table = %_JSCollectionGetTable(this); var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); - var hash = GetHash(key); + var hash = GetExistingHash(key); + if (IS_UNDEFINED(hash)) return false; var entry = SetFindEntry(table, numBuckets, key, hash); if (entry === NOT_FOUND) return false; @@ -291,7 +320,8 @@ function MapGet(key) { } var table = %_JSCollectionGetTable(this); var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table); - var hash = GetHash(key); + var hash = GetExistingHash(key); + if (IS_UNDEFINED(hash)) return UNDEFINED; var entry = MapFindEntry(table, numBuckets, key, hash); if (entry === NOT_FOUND) return UNDEFINED; return ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets); @@ -446,4 +476,8 @@ utils.InstallFunctions(GlobalMap.prototype, DONT_ENUM, [ "forEach", MapForEach ]); +// Expose to the global scope. +$getHash = GetHash; +$getExistingHash = GetExistingHash; + }) diff --git a/src/factory.cc b/src/factory.cc index 59ab64d..fc8919c 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -705,10 +705,15 @@ Handle Factory::NewPrivateSymbol() { } -Handle Factory::NewPrivateOwnSymbol() { +Handle Factory::NewPrivateOwnSymbol(Handle name) { Handle symbol = NewSymbol(); symbol->set_is_private(true); symbol->set_is_own(true); + if (name->IsString()) { + symbol->set_name(*name); + } else { + DCHECK(name->IsUndefined()); + } return symbol; } diff --git a/src/factory.h b/src/factory.h index 4702cc7..01a2474 100644 --- a/src/factory.h +++ b/src/factory.h @@ -233,7 +233,7 @@ class Factory final { // Create a symbol. Handle NewSymbol(); Handle NewPrivateSymbol(); - Handle NewPrivateOwnSymbol(); + Handle NewPrivateOwnSymbol(Handle name); // Create a global (but otherwise uninitialized) context. Handle NewNativeContext(); diff --git a/src/heap/heap.cc b/src/heap/heap.cc index 7bf3c4b..e54ec1e 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc @@ -3060,9 +3060,12 @@ void Heap::CreateInitialObjects() { { HandleScope scope(isolate()); -#define SYMBOL_INIT(name) \ - Handle name = factory->NewPrivateOwnSymbol(); \ - roots_[k##name##RootIndex] = *name; +#define SYMBOL_INIT(name) \ + { \ + Handle name##d = factory->NewStringFromStaticChars(#name); \ + Handle symbol(isolate()->factory()->NewPrivateOwnSymbol(name##d)); \ + roots_[k##name##RootIndex] = *symbol; \ + } PRIVATE_SYMBOL_LIST(SYMBOL_INIT) #undef SYMBOL_INIT } @@ -3171,6 +3174,19 @@ void Heap::CreateInitialObjects() { } +void Heap::AddPrivateGlobalSymbols(Handle private_intern_table) { +#define ADD_SYMBOL_TO_PRIVATE_INTERN_TABLE(name_arg) \ + { \ + Handle symbol(Symbol::cast(roots_[k##name_arg##RootIndex])); \ + Handle name_arg##d(String::cast(symbol->name())); \ + JSObject::AddProperty(Handle::cast(private_intern_table), \ + name_arg##d, symbol, NONE); \ + } + PRIVATE_SYMBOL_LIST(ADD_SYMBOL_TO_PRIVATE_INTERN_TABLE) +#undef ADD_SYMBOL_TO_PRIVATE_INTERN_TABLE +} + + bool Heap::RootCanBeWrittenAfterInitialization(Heap::RootListIndex root_index) { switch (root_index) { case kStoreBufferTopRootIndex: diff --git a/src/heap/heap.h b/src/heap/heap.h index d398599..e5f7fe9 100644 --- a/src/heap/heap.h +++ b/src/heap/heap.h @@ -271,7 +271,6 @@ namespace internal { V(illegal_access_string, "illegal access") \ V(cell_value_string, "%cell_value") \ V(illegal_argument_string, "illegal argument") \ - V(identity_hash_string, "v8::IdentityHash") \ V(closure_string, "(closure)") \ V(dot_string, ".") \ V(compare_ic_string, "==") \ @@ -294,6 +293,7 @@ namespace internal { #define PRIVATE_SYMBOL_LIST(V) \ V(nonextensible_symbol) \ V(sealed_symbol) \ + V(hash_code_symbol) \ V(frozen_symbol) \ V(nonexistent_symbol) \ V(elements_transition_symbol) \ @@ -1754,6 +1754,8 @@ class Heap { // any string when looked up in properties. String* hidden_string_; + void AddPrivateGlobalSymbols(Handle private_intern_table); + // GC callback function, called before and after mark-compact GC. // Allocations in the callback function are disallowed. struct GCPrologueCallbackPair { diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 4d599ee..654f7f5 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -1150,6 +1150,7 @@ void MacroAssembler::GetNumberHash(Register r0, Register scratch) { mov(scratch, r0); shr(scratch, 16); xor_(r0, scratch); + and_(r0, 0x3fffffff); } diff --git a/src/isolate.cc b/src/isolate.cc index 1b031f8..0b24786 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -2522,21 +2522,29 @@ ISOLATE_INIT_ARRAY_LIST(ISOLATE_FIELD_OFFSET) #endif +Handle Isolate::SetUpSubregistry(Handle registry, + Handle map, const char* cname) { + Handle name = factory()->InternalizeUtf8String(cname); + Handle obj = factory()->NewJSObjectFromMap(map); + JSObject::NormalizeProperties(obj, CLEAR_INOBJECT_PROPERTIES, 0, + "SetupSymbolRegistry"); + JSObject::AddProperty(registry, name, obj, NONE); + return obj; +} + + Handle Isolate::GetSymbolRegistry() { if (heap()->symbol_registry()->IsSmi()) { Handle map = factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); Handle registry = factory()->NewJSObjectFromMap(map); heap()->set_symbol_registry(*registry); - static const char* nested[] = {"for", "for_api", "keyFor", "private_api", - "private_intern"}; - for (unsigned i = 0; i < arraysize(nested); ++i) { - Handle name = factory()->InternalizeUtf8String(nested[i]); - Handle obj = factory()->NewJSObjectFromMap(map); - JSObject::NormalizeProperties(obj, KEEP_INOBJECT_PROPERTIES, 8, - "SetupSymbolRegistry"); - JSObject::SetProperty(registry, name, obj, STRICT).Assert(); - } + SetUpSubregistry(registry, map, "for"); + SetUpSubregistry(registry, map, "for_api"); + SetUpSubregistry(registry, map, "keyFor"); + SetUpSubregistry(registry, map, "private_api"); + heap()->AddPrivateGlobalSymbols( + SetUpSubregistry(registry, map, "private_intern")); } return Handle::cast(factory()->symbol_registry()); } diff --git a/src/isolate.h b/src/isolate.h index 2a37a3a..bdc3210 100644 --- a/src/isolate.h +++ b/src/isolate.h @@ -1134,6 +1134,8 @@ class Isolate { private: friend struct GlobalState; friend struct InitializeGlobalState; + Handle SetUpSubregistry(Handle registry, Handle map, + const char* name); // These fields are accessed through the API, offsets must be kept in sync // with v8::internal::Internals (in include/v8.h) constants. This is also diff --git a/src/math.js b/src/math.js index 29846b0..fc3bf2f 100644 --- a/src/math.js +++ b/src/math.js @@ -5,7 +5,6 @@ var rngstate; // Initialized to a Uint32Array during genesis. (function(global, utils) { - "use strict"; %CheckIsBootstrapping(); @@ -138,6 +137,15 @@ function MathRandom() { return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10; } +function MathRandomRaw() { + var r0 = (MathImul(18030, rngstate[0] & 0xFFFF) + (rngstate[0] >>> 16)) | 0; + rngstate[0] = r0; + var r1 = (MathImul(36969, rngstate[1] & 0xFFFF) + (rngstate[1] >>> 16)) | 0; + rngstate[1] = r1; + var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0; + return x & 0x3fffffff; +} + // ECMA 262 - 15.8.2.15 function MathRound(x) { return %RoundNumber(TO_NUMBER_INLINE(x)); @@ -358,6 +366,7 @@ utils.Export(function(to) { to.MathAbs = MathAbs; to.MathExp = MathExp; to.MathFloor = MathFloorJS; + to.IntRandom = MathRandomRaw; to.MathMax = MathMax; to.MathMin = MathMin; }); diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc index 0c3e0ce..b735157 100644 --- a/src/mips/macro-assembler-mips.cc +++ b/src/mips/macro-assembler-mips.cc @@ -523,6 +523,7 @@ void MacroAssembler::GetNumberHash(Register reg0, Register scratch) { // hash = hash ^ (hash >> 16); srl(at, reg0, 16); xor_(reg0, reg0, at); + And(reg0, reg0, Operand(0x3fffffff)); } diff --git a/src/mips64/macro-assembler-mips64.cc b/src/mips64/macro-assembler-mips64.cc index 1542766..31b35d7 100644 --- a/src/mips64/macro-assembler-mips64.cc +++ b/src/mips64/macro-assembler-mips64.cc @@ -528,6 +528,7 @@ void MacroAssembler::GetNumberHash(Register reg0, Register scratch) { // hash = hash ^ (hash >> 16); srl(at, reg0, 16); xor_(reg0, reg0, at); + And(reg0, reg0, Operand(0x3fffffff)); } diff --git a/src/object-observe.js b/src/object-observe.js index 219d7b7..56859a1 100644 --- a/src/object-observe.js +++ b/src/object-observe.js @@ -208,28 +208,30 @@ function ObjectInfoGetOrCreate(object) { performingCount: 0, }; %WeakCollectionSet(GetObservationStateJS().objectInfoMap, - object, objectInfo); + object, objectInfo, $getHash(object)); } return objectInfo; } function ObjectInfoGet(object) { - return %WeakCollectionGet(GetObservationStateJS().objectInfoMap, object); + return %WeakCollectionGet(GetObservationStateJS().objectInfoMap, object, + $getHash(object)); } function ObjectInfoGetFromNotifier(notifier) { return %WeakCollectionGet(GetObservationStateJS().notifierObjectInfoMap, - notifier); + notifier, $getHash(notifier)); } function ObjectInfoGetNotifier(objectInfo) { if (IS_NULL(objectInfo.notifier)) { - objectInfo.notifier = { __proto__: notifierPrototype }; + var notifier = { __proto__: notifierPrototype }; + objectInfo.notifier = notifier; %WeakCollectionSet(GetObservationStateJS().notifierObjectInfoMap, - objectInfo.notifier, objectInfo); + notifier, objectInfo, $getHash(notifier)); } return objectInfo.notifier; @@ -340,13 +342,14 @@ function ConvertAcceptListToTypeMap(arg) { // priority. When a change record must be enqueued for the callback, it // normalizes. When delivery clears any pending change records, it re-optimizes. function CallbackInfoGet(callback) { - return %WeakCollectionGet(GetObservationStateJS().callbackInfoMap, callback); + return %WeakCollectionGet(GetObservationStateJS().callbackInfoMap, callback, + $getHash(callback)); } function CallbackInfoSet(callback, callbackInfo) { %WeakCollectionSet(GetObservationStateJS().callbackInfoMap, - callback, callbackInfo); + callback, callbackInfo, $getHash(callback)); } diff --git a/src/objects-inl.h b/src/objects-inl.h index 91a8766..f2c21ac 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -3300,11 +3300,18 @@ int HashTable::FindEntry(Key key) { } -// Find entry for key otherwise return kNotFound. template int HashTable::FindEntry(Isolate* isolate, Key key) { + return FindEntry(isolate, key, HashTable::Hash(key)); +} + + +// Find entry for key otherwise return kNotFound. +template +int HashTable::FindEntry(Isolate* isolate, Key key, + int32_t hash) { uint32_t capacity = Capacity(); - uint32_t entry = FirstProbe(HashTable::Hash(key), capacity); + uint32_t entry = FirstProbe(hash, capacity); uint32_t count = 1; // EnsureCapacity will guarantee the hash table is never full. while (true) { diff --git a/src/objects.cc b/src/objects.cc index 4301775..2f8aec5 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -810,6 +810,15 @@ Map* Object::GetRootMap(Isolate* isolate) { Object* Object::GetHash() { + Object* hash = GetSimpleHash(); + if (hash->IsSmi()) return hash; + + DCHECK(IsJSReceiver()); + return JSReceiver::cast(this)->GetIdentityHash(); +} + + +Object* Object::GetSimpleHash() { // The object is either a Smi, a HeapNumber, a name, an odd-ball, // a real JS object, or a Harmony proxy. if (IsSmi()) { @@ -834,14 +843,14 @@ Object* Object::GetHash() { uint32_t hash = Oddball::cast(this)->to_string()->Hash(); return Smi::FromInt(hash); } - DCHECK(IsJSReceiver()); - return JSReceiver::cast(this)->GetIdentityHash(); + JSReceiver* receiver = JSReceiver::cast(this); + return receiver->GetHeap()->undefined_value(); } Handle Object::GetOrCreateHash(Isolate* isolate, Handle object) { - Handle hash(object->GetHash(), isolate); + Handle hash(object->GetSimpleHash(), isolate); if (hash->IsSmi()) return Handle::cast(hash); DCHECK(object->IsJSReceiver()); @@ -5035,7 +5044,8 @@ static Smi* GenerateIdentityHash(Isolate* isolate) { void JSObject::SetIdentityHash(Handle object, Handle hash) { DCHECK(!object->IsJSGlobalProxy()); Isolate* isolate = object->GetIsolate(); - SetHiddenProperty(object, isolate->factory()->identity_hash_string(), hash); + Handle hash_code_symbol(isolate->heap()->hash_code_symbol()); + JSObject::AddProperty(object, hash_code_symbol, hash, NONE); } @@ -5058,11 +5068,12 @@ Object* JSObject::GetIdentityHash() { if (IsJSGlobalProxy()) { return JSGlobalProxy::cast(this)->hash(); } - Object* stored_value = - GetHiddenProperty(isolate->factory()->identity_hash_string()); - return stored_value->IsSmi() - ? stored_value - : isolate->heap()->undefined_value(); + Handle hash_code_symbol(isolate->heap()->hash_code_symbol()); + Handle stored_value = + Object::GetPropertyOrElement(Handle(this, isolate), + hash_code_symbol).ToHandleChecked(); + return stored_value->IsSmi() ? *stored_value + : isolate->heap()->undefined_value(); } @@ -5077,7 +5088,8 @@ Handle JSObject::GetOrCreateIdentityHash(Handle object) { if (maybe_hash->IsSmi()) return Handle::cast(maybe_hash); Handle hash(GenerateIdentityHash(isolate), isolate); - SetHiddenProperty(object, isolate->factory()->identity_hash_string(), hash); + Handle hash_code_symbol(isolate->heap()->hash_code_symbol()); + JSObject::AddProperty(object, hash_code_symbol, hash, NONE); return hash; } @@ -5096,8 +5108,6 @@ Object* JSObject::GetHiddenProperty(Handle key) { DisallowHeapAllocation no_gc; DCHECK(key->IsUniqueName()); if (IsJSGlobalProxy()) { - // JSGlobalProxies store their hash internally. - DCHECK(*key != GetHeap()->identity_hash_string()); // For a proxy, use the prototype as target object. PrototypeIterator iter(GetIsolate(), this); // If the proxy is detached, return undefined. @@ -5108,15 +5118,6 @@ Object* JSObject::GetHiddenProperty(Handle key) { DCHECK(!IsJSGlobalProxy()); Object* inline_value = GetHiddenPropertiesHashTable(); - if (inline_value->IsSmi()) { - // Handle inline-stored identity hash. - if (*key == GetHeap()->identity_hash_string()) { - return inline_value; - } else { - return GetHeap()->the_hole_value(); - } - } - if (inline_value->IsUndefined()) return GetHeap()->the_hole_value(); ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value); @@ -5132,8 +5133,6 @@ Handle JSObject::SetHiddenProperty(Handle object, DCHECK(key->IsUniqueName()); if (object->IsJSGlobalProxy()) { - // JSGlobalProxies store their hash internally. - DCHECK(*key != *isolate->factory()->identity_hash_string()); // For a proxy, use the prototype as target object. PrototypeIterator iter(isolate, object); // If the proxy is detached, return undefined. @@ -5147,13 +5146,6 @@ Handle JSObject::SetHiddenProperty(Handle object, Handle inline_value(object->GetHiddenPropertiesHashTable(), isolate); - // If there is no backing store yet, store the identity hash inline. - if (value->IsSmi() && - *key == *isolate->factory()->identity_hash_string() && - (inline_value->IsUndefined() || inline_value->IsSmi())) { - return JSObject::SetHiddenPropertiesHashTable(object, value); - } - Handle hashtable = GetOrCreateHiddenPropertiesHashtable(object); @@ -5185,9 +5177,7 @@ void JSObject::DeleteHiddenProperty(Handle object, Handle key) { Object* inline_value = object->GetHiddenPropertiesHashTable(); - // We never delete (inline-stored) identity hashes. - DCHECK(*key != *isolate->factory()->identity_hash_string()); - if (inline_value->IsUndefined() || inline_value->IsSmi()) return; + if (inline_value->IsUndefined()) return; Handle hashtable(ObjectHashTable::cast(inline_value)); bool was_present = false; @@ -5251,14 +5241,7 @@ Handle JSObject::GetOrCreateHiddenPropertiesHashtable( Handle hashtable = ObjectHashTable::New( isolate, kInitialCapacity, USE_CUSTOM_MINIMUM_CAPACITY); - 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. - hashtable = ObjectHashTable::Put(hashtable, - isolate->factory()->identity_hash_string(), - inline_value); - } - + DCHECK(inline_value->IsUndefined()); SetHiddenPropertiesHashTable(object, hashtable); return hashtable; } @@ -16324,18 +16307,34 @@ Object* Dictionary::SlowReverseLookup(Object* value) { } +Object* ObjectHashTable::Lookup(Isolate* isolate, Handle key, + int32_t hash) { + DisallowHeapAllocation no_gc; + DCHECK(IsKey(*key)); + + int entry = FindEntry(isolate, key, hash); + if (entry == kNotFound) return isolate->heap()->the_hole_value(); + return get(EntryToIndex(entry) + 1); +} + + Object* ObjectHashTable::Lookup(Handle key) { DisallowHeapAllocation no_gc; DCHECK(IsKey(*key)); + Isolate* isolate = GetIsolate(); + // If the object does not have an identity hash, it was never used as a key. Object* hash = key->GetHash(); if (hash->IsUndefined()) { - return GetHeap()->the_hole_value(); + return isolate->heap()->the_hole_value(); } - int entry = FindEntry(key); - if (entry == kNotFound) return GetHeap()->the_hole_value(); - return get(EntryToIndex(entry) + 1); + return Lookup(isolate, key, Smi::cast(hash)->value()); +} + + +Object* ObjectHashTable::Lookup(Handle key, int32_t hash) { + return Lookup(GetIsolate(), key, hash); } @@ -16346,11 +16345,23 @@ Handle ObjectHashTable::Put(Handle table, DCHECK(!value->IsTheHole()); Isolate* isolate = table->GetIsolate(); - // Make sure the key object has an identity hash code. - Handle hash = Object::GetOrCreateHash(isolate, key); + int32_t hash = Object::GetOrCreateHash(isolate, key)->value(); - int entry = table->FindEntry(key); + return Put(table, key, value, hash); +} + + +Handle ObjectHashTable::Put(Handle table, + Handle key, + Handle value, + int32_t hash) { + DCHECK(table->IsKey(*key)); + DCHECK(!value->IsTheHole()); + + Isolate* isolate = table->GetIsolate(); + + int entry = table->FindEntry(isolate, key, hash); // Key is already in table, just overwrite value. if (entry != kNotFound) { @@ -16360,9 +16371,7 @@ Handle ObjectHashTable::Put(Handle table, // Check whether the hash table should be extended. table = EnsureCapacity(table, 1, key); - table->AddEntry(table->FindInsertionEntry(hash->value()), - *key, - *value); + table->AddEntry(table->FindInsertionEntry(hash), *key, *value); return table; } @@ -16378,7 +16387,17 @@ Handle ObjectHashTable::Remove(Handle table, return table; } - int entry = table->FindEntry(key); + return Remove(table, key, was_present, Smi::cast(hash)->value()); +} + + +Handle ObjectHashTable::Remove(Handle table, + Handle key, + bool* was_present, + int32_t hash) { + DCHECK(table->IsKey(*key)); + + int entry = table->FindEntry(table->GetIsolate(), key, hash); if (entry == kNotFound) { *was_present = false; return table; diff --git a/src/objects.h b/src/objects.h index ce7f785..a973f12 100644 --- a/src/objects.h +++ b/src/objects.h @@ -1223,6 +1223,11 @@ class Object { // undefined if not yet created. Object* GetHash(); + // Returns undefined for JSObjects, but returns the hash code for simple + // objects. This avoids a double lookup in the cases where we know we will + // add the hash to the JSObject if it does not already exist. + Object* GetSimpleHash(); + // Returns the permanent hash code associated with this object depending on // the actual object type. May create and store a hash code if needed and none // exists. @@ -3434,6 +3439,7 @@ class HashTable : public HashTableBase { // Find entry for key otherwise return kNotFound. inline int FindEntry(Key key); + inline int FindEntry(Isolate* isolate, Key key, int32_t hash); int FindEntry(Isolate* isolate, Key key); // Rehashes the table in-place. @@ -3940,16 +3946,24 @@ class ObjectHashTable: public HashTable key); + Object* Lookup(Handle key, int32_t hash); + Object* Lookup(Isolate* isolate, Handle key, int32_t hash); // Adds (or overwrites) the value associated with the given key. static Handle Put(Handle table, Handle key, Handle value); + static Handle Put(Handle table, + Handle key, Handle value, + int32_t hash); // Returns an ObjectHashTable (possibly |table|) where |key| has been removed. static Handle Remove(Handle table, Handle key, bool* was_present); + static Handle Remove(Handle table, + Handle key, bool* was_present, + int32_t hash); protected: friend class MarkCompactCollector; diff --git a/src/runtime/runtime-collections.cc b/src/runtime/runtime-collections.cc index aedc1bc..1eef189 100644 --- a/src/runtime/runtime-collections.cc +++ b/src/runtime/runtime-collections.cc @@ -296,41 +296,51 @@ RUNTIME_FUNCTION(Runtime_WeakCollectionInitialize) { RUNTIME_FUNCTION(Runtime_WeakCollectionGet) { HandleScope scope(isolate); - DCHECK(args.length() == 2); + DCHECK(args.length() == 3); CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0); CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); + CONVERT_SMI_ARG_CHECKED(hash, 2) RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol()); Handle table( ObjectHashTable::cast(weak_collection->table())); RUNTIME_ASSERT(table->IsKey(*key)); - Handle lookup(table->Lookup(key), isolate); + Handle lookup(table->Lookup(key, hash), isolate); return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup; } RUNTIME_FUNCTION(Runtime_WeakCollectionHas) { HandleScope scope(isolate); - DCHECK(args.length() == 2); + DCHECK(args.length() == 3); CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0); CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); + CONVERT_SMI_ARG_CHECKED(hash, 2) RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol()); Handle table( ObjectHashTable::cast(weak_collection->table())); RUNTIME_ASSERT(table->IsKey(*key)); - Handle lookup(table->Lookup(key), isolate); + Handle lookup(table->Lookup(key, hash), isolate); return isolate->heap()->ToBoolean(!lookup->IsTheHole()); } bool Runtime::WeakCollectionDelete(Handle weak_collection, Handle key) { + int32_t hash = + Object::GetOrCreateHash(weak_collection->GetIsolate(), key)->value(); + return WeakCollectionDelete(weak_collection, key, hash); +} + + +bool Runtime::WeakCollectionDelete(Handle weak_collection, + Handle key, int32_t hash) { DCHECK(key->IsJSReceiver() || key->IsSymbol()); Handle table( ObjectHashTable::cast(weak_collection->table())); DCHECK(table->IsKey(*key)); bool was_present = false; Handle new_table = - ObjectHashTable::Remove(table, key, &was_present); + ObjectHashTable::Remove(table, key, &was_present, hash); weak_collection->set_table(*new_table); if (*table != *new_table) { // Zap the old table since we didn't record slots for its elements. @@ -342,25 +352,28 @@ bool Runtime::WeakCollectionDelete(Handle weak_collection, RUNTIME_FUNCTION(Runtime_WeakCollectionDelete) { HandleScope scope(isolate); - DCHECK(args.length() == 2); + DCHECK(args.length() == 3); CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0); CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); + CONVERT_SMI_ARG_CHECKED(hash, 2) RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol()); Handle table( ObjectHashTable::cast(weak_collection->table())); RUNTIME_ASSERT(table->IsKey(*key)); - bool was_present = Runtime::WeakCollectionDelete(weak_collection, key); + bool was_present = Runtime::WeakCollectionDelete(weak_collection, key, hash); return isolate->heap()->ToBoolean(was_present); } void Runtime::WeakCollectionSet(Handle weak_collection, - Handle key, Handle value) { + Handle key, Handle value, + int32_t hash) { DCHECK(key->IsJSReceiver() || key->IsSymbol()); Handle table( ObjectHashTable::cast(weak_collection->table())); DCHECK(table->IsKey(*key)); - Handle new_table = ObjectHashTable::Put(table, key, value); + Handle new_table = + ObjectHashTable::Put(table, key, value, hash); weak_collection->set_table(*new_table); if (*table != *new_table) { // Zap the old table since we didn't record slots for its elements. @@ -371,15 +384,16 @@ void Runtime::WeakCollectionSet(Handle weak_collection, RUNTIME_FUNCTION(Runtime_WeakCollectionSet) { HandleScope scope(isolate); - DCHECK(args.length() == 3); + DCHECK(args.length() == 4); CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0); CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); RUNTIME_ASSERT(key->IsJSReceiver() || key->IsSymbol()); CONVERT_ARG_HANDLE_CHECKED(Object, value, 2); + CONVERT_SMI_ARG_CHECKED(hash, 3) Handle table( ObjectHashTable::cast(weak_collection->table())); RUNTIME_ASSERT(table->IsKey(*key)); - Runtime::WeakCollectionSet(weak_collection, key, value); + Runtime::WeakCollectionSet(weak_collection, key, value, hash); return *weak_collection; } diff --git a/src/runtime/runtime-symbol.cc b/src/runtime/runtime-symbol.cc index e44d15d..7ff7eb9 100644 --- a/src/runtime/runtime-symbol.cc +++ b/src/runtime/runtime-symbol.cc @@ -37,9 +37,7 @@ RUNTIME_FUNCTION(Runtime_CreatePrivateOwnSymbol) { DCHECK(args.length() == 1); CONVERT_ARG_HANDLE_CHECKED(Object, name, 0); RUNTIME_ASSERT(name->IsString() || name->IsUndefined()); - Handle symbol = isolate->factory()->NewPrivateOwnSymbol(); - if (name->IsString()) symbol->set_name(*name); - return *symbol; + return *isolate->factory()->NewPrivateOwnSymbol(name); } @@ -50,18 +48,15 @@ RUNTIME_FUNCTION(Runtime_CreateGlobalPrivateOwnSymbol) { Handle registry = isolate->GetSymbolRegistry(); Handle part = isolate->factory()->private_intern_string(); Handle privates; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, privates, Object::GetPropertyOrElement(registry, part)); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, privates, + Object::GetProperty(registry, part)); Handle symbol; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, symbol, Object::GetPropertyOrElement(privates, name)); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, symbol, + Object::GetProperty(privates, name)); if (!symbol->IsSymbol()) { DCHECK(symbol->IsUndefined()); - symbol = isolate->factory()->NewPrivateSymbol(); - Handle::cast(symbol)->set_name(*name); - Handle::cast(symbol)->set_is_own(true); - JSObject::SetProperty(Handle::cast(privates), name, symbol, - STRICT).Assert(); + symbol = isolate->factory()->NewPrivateOwnSymbol(name); + JSObject::AddProperty(Handle::cast(privates), name, symbol, NONE); } return *symbol; } diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 797f3d0..b8d0307 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -100,10 +100,10 @@ namespace internal { F(GetWeakMapEntries, 2, 1) \ F(MapIteratorNext, 2, 1) \ F(WeakCollectionInitialize, 1, 1) \ - F(WeakCollectionGet, 2, 1) \ - F(WeakCollectionHas, 2, 1) \ - F(WeakCollectionDelete, 2, 1) \ - F(WeakCollectionSet, 3, 1) \ + F(WeakCollectionGet, 3, 1) \ + F(WeakCollectionHas, 3, 1) \ + F(WeakCollectionDelete, 3, 1) \ + F(WeakCollectionSet, 4, 1) \ F(GetWeakSetValues, 2, 1) \ F(ObservationWeakMapCreate, 0, 1) @@ -855,9 +855,12 @@ class Runtime : public AllStatic { static void WeakCollectionInitialize( Isolate* isolate, Handle weak_collection); static void WeakCollectionSet(Handle weak_collection, - Handle key, Handle value); + Handle key, Handle value, + int32_t hash); static bool WeakCollectionDelete(Handle weak_collection, Handle key); + static bool WeakCollectionDelete(Handle weak_collection, + Handle key, int32_t hash); static MaybeHandle GetInternalProperties(Isolate* isolate, Handle); diff --git a/src/utils.h b/src/utils.h index dbb80e7..d8879ac 100644 --- a/src/utils.h +++ b/src/utils.h @@ -340,7 +340,7 @@ inline uint32_t ComputeIntegerHash(uint32_t key, uint32_t seed) { hash = hash ^ (hash >> 4); hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11); hash = hash ^ (hash >> 16); - return hash; + return hash & 0x3fffffff; } diff --git a/src/weak-collection.js b/src/weak-collection.js index 0e71c7c..7535093 100644 --- a/src/weak-collection.js +++ b/src/weak-collection.js @@ -43,7 +43,9 @@ function WeakMapGet(key) { 'WeakMap.prototype.get', this); } if (!IS_SPEC_OBJECT(key)) return UNDEFINED; - return %WeakCollectionGet(this, key); + var hash = $getExistingHash(key); + if (IS_UNDEFINED(hash)) return UNDEFINED; + return %WeakCollectionGet(this, key, hash); } @@ -53,7 +55,7 @@ function WeakMapSet(key, value) { 'WeakMap.prototype.set', this); } if (!IS_SPEC_OBJECT(key)) throw MakeTypeError(kInvalidWeakMapKey); - return %WeakCollectionSet(this, key, value); + return %WeakCollectionSet(this, key, value, $getHash(key)); } @@ -63,7 +65,9 @@ function WeakMapHas(key) { 'WeakMap.prototype.has', this); } if (!IS_SPEC_OBJECT(key)) return false; - return %WeakCollectionHas(this, key); + var hash = $getExistingHash(key); + if (IS_UNDEFINED(hash)) return false; + return %WeakCollectionHas(this, key, hash); } @@ -73,7 +77,9 @@ function WeakMapDelete(key) { 'WeakMap.prototype.delete', this); } if (!IS_SPEC_OBJECT(key)) return false; - return %WeakCollectionDelete(this, key); + var hash = $getExistingHash(key); + if (IS_UNDEFINED(hash)) return false; + return %WeakCollectionDelete(this, key, hash); } @@ -123,7 +129,7 @@ function WeakSetAdd(value) { 'WeakSet.prototype.add', this); } if (!IS_SPEC_OBJECT(value)) throw MakeTypeError(kInvalidWeakSetValue); - return %WeakCollectionSet(this, value, true); + return %WeakCollectionSet(this, value, true, $getHash(value)); } @@ -133,7 +139,9 @@ function WeakSetHas(value) { 'WeakSet.prototype.has', this); } if (!IS_SPEC_OBJECT(value)) return false; - return %WeakCollectionHas(this, value); + var hash = $getExistingHash(value); + if (IS_UNDEFINED(hash)) return false; + return %WeakCollectionHas(this, value, hash); } @@ -143,7 +151,9 @@ function WeakSetDelete(value) { 'WeakSet.prototype.delete', this); } if (!IS_SPEC_OBJECT(value)) return false; - return %WeakCollectionDelete(this, value); + var hash = $getExistingHash(value); + if (IS_UNDEFINED(hash)) return false; + return %WeakCollectionDelete(this, value, hash); } diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index 9adf6a4..f2c6a38 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -4010,6 +4010,7 @@ void MacroAssembler::GetNumberHash(Register r0, Register scratch) { movl(scratch, r0); shrl(scratch, Immediate(16)); xorl(r0, scratch); + andl(r0, Immediate(0x3fffffff)); } diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 4890b80..f2e5d6f 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -2259,12 +2259,24 @@ THREADED_TEST(IdentityHash) { } -THREADED_TEST(GlobalProxyIdentityHash) { +void GlobalProxyIdentityHash(bool set_in_js) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); + i::Isolate* i_isolate = reinterpret_cast(isolate); v8::HandleScope scope(isolate); Handle global_proxy = env->Global(); - int hash1 = global_proxy->GetIdentityHash(); + i::Handle i_global_proxy = v8::Utils::OpenHandle(*global_proxy); + env->Global()->Set(v8_str("global"), global_proxy); + i::Handle original_hash; + if (set_in_js) { + CompileRun("var m = new Set(); m.add(global);"); + original_hash = i::Handle(i_global_proxy->GetHash(), i_isolate); + } else { + original_hash = i::Handle( + i::Object::GetOrCreateHash(i_isolate, i_global_proxy)); + } + CHECK(original_hash->IsSmi()); + int32_t hash1 = i::Handle::cast(original_hash)->value(); // Hash should be retained after being detached. env->DetachGlobal(); int hash2 = global_proxy->GetIdentityHash(); @@ -2278,6 +2290,12 @@ THREADED_TEST(GlobalProxyIdentityHash) { } +THREADED_TEST(GlobalProxyIdentityHash) { + GlobalProxyIdentityHash(true); + GlobalProxyIdentityHash(false); +} + + TEST(SymbolIdentityHash) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); diff --git a/test/cctest/test-hashing.cc b/test/cctest/test-hashing.cc index c8ae4f3..9e5de2e 100644 --- a/test/cctest/test-hashing.cc +++ b/test/cctest/test-hashing.cc @@ -131,7 +131,7 @@ void check(uint32_t key) { #endif uint32_t runtime_hash = ComputeIntegerHash(key, isolate->heap()->HashSeed()); - CHECK(runtime_hash == codegen_hash); + CHECK_EQ(runtime_hash, codegen_hash); } diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index e0665ce..482d111 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -3291,41 +3291,6 @@ TEST(PrintSharedFunctionInfo) { #endif // OBJECT_PRINT -TEST(Regress2211) { - CcTest::InitializeVM(); - v8::HandleScope scope(CcTest::isolate()); - - v8::Handle value = v8_str("val string"); - Smi* hash = Smi::FromInt(321); - Factory* factory = CcTest::i_isolate()->factory(); - - for (int i = 0; i < 2; i++) { - // Store identity hash first and common hidden property second. - v8::Handle obj = v8::Object::New(CcTest::isolate()); - Handle internal_obj = v8::Utils::OpenHandle(*obj); - CHECK(internal_obj->HasFastProperties()); - - // In the first iteration, set hidden value first and identity hash second. - // In the second iteration, reverse the order. - if (i == 0) obj->SetHiddenValue(v8_str("key string"), value); - JSObject::SetIdentityHash(internal_obj, handle(hash, CcTest::i_isolate())); - if (i == 1) obj->SetHiddenValue(v8_str("key string"), value); - - // Check values. - CHECK_EQ(hash, - internal_obj->GetHiddenProperty(factory->identity_hash_string())); - CHECK(value->Equals(obj->GetHiddenValue(v8_str("key string")))); - - // Check size. - FieldIndex index = FieldIndex::ForDescriptor(internal_obj->map(), 0); - ObjectHashTable* hashtable = ObjectHashTable::cast( - internal_obj->RawFastPropertyAt(index)); - // HashTable header (5) and 4 initial entries (8). - CHECK_LE(hashtable->SizeFor(hashtable->length()), 13 * kPointerSize); - } -} - - TEST(IncrementalMarkingPreservesMonomorphicCallIC) { if (i::FLAG_always_opt) return; CcTest::InitializeVM(); diff --git a/test/cctest/test-weakmaps.cc b/test/cctest/test-weakmaps.cc index 64d2876..1255578 100644 --- a/test/cctest/test-weakmaps.cc +++ b/test/cctest/test-weakmaps.cc @@ -89,8 +89,10 @@ TEST(Weakness) { Handle map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); Handle object = factory->NewJSObjectFromMap(map); Handle smi(Smi::FromInt(23), isolate); - Runtime::WeakCollectionSet(weakmap, key, object); - Runtime::WeakCollectionSet(weakmap, object, smi); + int32_t hash = Object::GetOrCreateHash(isolate, key)->value(); + Runtime::WeakCollectionSet(weakmap, key, object, hash); + int32_t object_hash = Object::GetOrCreateHash(isolate, object)->value(); + Runtime::WeakCollectionSet(weakmap, object, smi, object_hash); } CHECK_EQ(2, ObjectHashTable::cast(weakmap->table())->NumberOfElements()); @@ -145,7 +147,8 @@ TEST(Shrinking) { for (int i = 0; i < 32; i++) { Handle object = factory->NewJSObjectFromMap(map); Handle smi(Smi::FromInt(i), isolate); - Runtime::WeakCollectionSet(weakmap, object, smi); + int32_t object_hash = Object::GetOrCreateHash(isolate, object)->value(); + Runtime::WeakCollectionSet(weakmap, object, smi, object_hash); } } @@ -193,7 +196,8 @@ TEST(Regress2060a) { Handle object = factory->NewJSObject(function, TENURED); CHECK(!heap->InNewSpace(object->address())); CHECK(!first_page->Contains(object->address())); - Runtime::WeakCollectionSet(weakmap, key, object); + int32_t hash = Object::GetOrCreateHash(isolate, key)->value(); + Runtime::WeakCollectionSet(weakmap, key, object, hash); } } @@ -235,7 +239,8 @@ TEST(Regress2060b) { Handle weakmap = AllocateJSWeakMap(isolate); for (int i = 0; i < 32; i++) { Handle smi(Smi::FromInt(i), isolate); - Runtime::WeakCollectionSet(weakmap, keys[i], smi); + int32_t hash = Object::GetOrCreateHash(isolate, keys[i])->value(); + Runtime::WeakCollectionSet(weakmap, keys[i], smi, hash); } // Force compacting garbage collection. The subsequent collections are used diff --git a/test/cctest/test-weaksets.cc b/test/cctest/test-weaksets.cc index dbb7311..8ed1812 100644 --- a/test/cctest/test-weaksets.cc +++ b/test/cctest/test-weaksets.cc @@ -90,7 +90,8 @@ TEST(WeakSet_Weakness) { { HandleScope scope(isolate); Handle smi(Smi::FromInt(23), isolate); - Runtime::WeakCollectionSet(weakset, key, smi); + int32_t hash = Object::GetOrCreateHash(isolate, key)->value(); + Runtime::WeakCollectionSet(weakset, key, smi, hash); } CHECK_EQ(1, ObjectHashTable::cast(weakset->table())->NumberOfElements()); @@ -145,7 +146,8 @@ TEST(WeakSet_Shrinking) { for (int i = 0; i < 32; i++) { Handle object = factory->NewJSObjectFromMap(map); Handle smi(Smi::FromInt(i), isolate); - Runtime::WeakCollectionSet(weakset, object, smi); + int32_t hash = Object::GetOrCreateHash(isolate, object)->value(); + Runtime::WeakCollectionSet(weakset, object, smi, hash); } } @@ -193,7 +195,8 @@ TEST(WeakSet_Regress2060a) { Handle object = factory->NewJSObject(function, TENURED); CHECK(!heap->InNewSpace(object->address())); CHECK(!first_page->Contains(object->address())); - Runtime::WeakCollectionSet(weakset, key, object); + int32_t hash = Object::GetOrCreateHash(isolate, key)->value(); + Runtime::WeakCollectionSet(weakset, key, object, hash); } } @@ -235,7 +238,8 @@ TEST(WeakSet_Regress2060b) { Handle weakset = AllocateJSWeakSet(isolate); for (int i = 0; i < 32; i++) { Handle smi(Smi::FromInt(i), isolate); - Runtime::WeakCollectionSet(weakset, keys[i], smi); + int32_t hash = Object::GetOrCreateHash(isolate, keys[i])->value(); + Runtime::WeakCollectionSet(weakset, keys[i], smi, hash); } // Force compacting garbage collection. The subsequent collections are used diff --git a/test/mjsunit/global-hash.js b/test/mjsunit/global-hash.js new file mode 100644 index 0000000..54dfc4c --- /dev/null +++ b/test/mjsunit/global-hash.js @@ -0,0 +1,19 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var global = this; + +assertEquals("object", typeof global); // Global object. + +var s = new Set(); +s.add(global); // Puts a hash code on the global object. +assertTrue(s.has(global)); +for (var i = 0; i < 100; i++) { + // Force rehash. Global object is placed according to the hash code that it + // gets in the C++ runtime. + s.add(i); +} + +// Hopefully still findable using the JS hash code. +assertTrue(s.has(global)); -- 2.7.4