Move hash code from hidden string to a private symbol
authorerikcorry <erikcorry@chromium.org>
Tue, 26 May 2015 11:26:26 +0000 (04:26 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 26 May 2015 11:26:36 +0000 (11:26 +0000)
* 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}

30 files changed:
src/api.cc
src/arm/macro-assembler-arm.cc
src/arm64/macro-assembler-arm64.cc
src/collection.js
src/factory.cc
src/factory.h
src/heap/heap.cc
src/heap/heap.h
src/ia32/macro-assembler-ia32.cc
src/isolate.cc
src/isolate.h
src/math.js
src/mips/macro-assembler-mips.cc
src/mips64/macro-assembler-mips64.cc
src/object-observe.js
src/objects-inl.h
src/objects.cc
src/objects.h
src/runtime/runtime-collections.cc
src/runtime/runtime-symbol.cc
src/runtime/runtime.h
src/utils.h
src/weak-collection.js
src/x64/macro-assembler-x64.cc
test/cctest/test-api.cc
test/cctest/test-hashing.cc
test/cctest/test-heap.cc
test/cctest/test-weakmaps.cc
test/cctest/test-weaksets.cc
test/mjsunit/global-hash.js [new file with mode: 0644]

index 959e8a0..52f373a 100644 (file)
@@ -2540,7 +2540,8 @@ void NativeWeakMap::Set(Handle<Value> v8_key, Handle<Value> 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);
 }
 
 
index 12108a0..31d2f73 100644 (file)
@@ -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));
 }
 
 
index 721720f..e15edc9 100644 (file)
@@ -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));
 }
 
 
index 70a88fb..6b468f5 100644 (file)
@@ -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();
 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;
+
 })
index 59ab64d..fc8919c 100644 (file)
@@ -705,10 +705,15 @@ Handle<Symbol> Factory::NewPrivateSymbol() {
 }
 
 
-Handle<Symbol> Factory::NewPrivateOwnSymbol() {
+Handle<Symbol> Factory::NewPrivateOwnSymbol(Handle<Object> name) {
   Handle<Symbol> 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;
 }
 
index 4702cc7..01a2474 100644 (file)
@@ -233,7 +233,7 @@ class Factory final {
   // Create a symbol.
   Handle<Symbol> NewSymbol();
   Handle<Symbol> NewPrivateSymbol();
-  Handle<Symbol> NewPrivateOwnSymbol();
+  Handle<Symbol> NewPrivateOwnSymbol(Handle<Object> name);
 
   // Create a global (but otherwise uninitialized) context.
   Handle<Context> NewNativeContext();
index 7bf3c4b..e54ec1e 100644 (file)
@@ -3060,9 +3060,12 @@ void Heap::CreateInitialObjects() {
 
   {
     HandleScope scope(isolate());
-#define SYMBOL_INIT(name)                               \
-  Handle<Symbol> name = factory->NewPrivateOwnSymbol(); \
-  roots_[k##name##RootIndex] = *name;
+#define SYMBOL_INIT(name)                                                      \
+  {                                                                            \
+    Handle<String> name##d = factory->NewStringFromStaticChars(#name);         \
+    Handle<Object> 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<Object> private_intern_table) {
+#define ADD_SYMBOL_TO_PRIVATE_INTERN_TABLE(name_arg)                     \
+  {                                                                      \
+    Handle<Symbol> symbol(Symbol::cast(roots_[k##name_arg##RootIndex])); \
+    Handle<String> name_arg##d(String::cast(symbol->name()));            \
+    JSObject::AddProperty(Handle<JSObject>::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:
index d398599..e5f7fe9 100644 (file)
@@ -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<Object> private_intern_table);
+
   // GC callback function, called before and after mark-compact GC.
   // Allocations in the callback function are disallowed.
   struct GCPrologueCallbackPair {
index 4d599ee..654f7f5 100644 (file)
@@ -1150,6 +1150,7 @@ void MacroAssembler::GetNumberHash(Register r0, Register scratch) {
   mov(scratch, r0);
   shr(scratch, 16);
   xor_(r0, scratch);
+  and_(r0, 0x3fffffff);
 }
 
 
index 1b031f8..0b24786 100644 (file)
@@ -2522,21 +2522,29 @@ ISOLATE_INIT_ARRAY_LIST(ISOLATE_FIELD_OFFSET)
 #endif
 
 
+Handle<JSObject> Isolate::SetUpSubregistry(Handle<JSObject> registry,
+                                           Handle<Map> map, const char* cname) {
+  Handle<String> name = factory()->InternalizeUtf8String(cname);
+  Handle<JSObject> obj = factory()->NewJSObjectFromMap(map);
+  JSObject::NormalizeProperties(obj, CLEAR_INOBJECT_PROPERTIES, 0,
+                                "SetupSymbolRegistry");
+  JSObject::AddProperty(registry, name, obj, NONE);
+  return obj;
+}
+
+
 Handle<JSObject> Isolate::GetSymbolRegistry() {
   if (heap()->symbol_registry()->IsSmi()) {
     Handle<Map> map = factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
     Handle<JSObject> 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<String> name = factory()->InternalizeUtf8String(nested[i]);
-      Handle<JSObject> 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<JSObject>::cast(factory()->symbol_registry());
 }
index 2a37a3a..bdc3210 100644 (file)
@@ -1134,6 +1134,8 @@ class Isolate {
  private:
   friend struct GlobalState;
   friend struct InitializeGlobalState;
+  Handle<JSObject> SetUpSubregistry(Handle<JSObject> registry, Handle<Map> 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
index 29846b0..fc3bf2f 100644 (file)
@@ -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;
 });
index 0c3e0ce..b735157 100644 (file)
@@ -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));
 }
 
 
index 1542766..31b35d7 100644 (file)
@@ -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));
 }
 
 
index 219d7b7..56859a1 100644 (file)
@@ -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));
 }
 
 
index 91a8766..f2c21ac 100644 (file)
@@ -3300,11 +3300,18 @@ int HashTable<Derived, Shape, Key>::FindEntry(Key key) {
 }
 
 
-// Find entry for key otherwise return kNotFound.
 template<typename Derived, typename Shape, typename Key>
 int HashTable<Derived, Shape, Key>::FindEntry(Isolate* isolate, Key key) {
+  return FindEntry(isolate, key, HashTable::Hash(key));
+}
+
+
+// Find entry for key otherwise return kNotFound.
+template <typename Derived, typename Shape, typename Key>
+int HashTable<Derived, Shape, Key>::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) {
index 4301775..2f8aec5 100644 (file)
@@ -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<Smi> Object::GetOrCreateHash(Isolate* isolate, Handle<Object> object) {
-  Handle<Object> hash(object->GetHash(), isolate);
+  Handle<Object> hash(object->GetSimpleHash(), isolate);
   if (hash->IsSmi()) return Handle<Smi>::cast(hash);
 
   DCHECK(object->IsJSReceiver());
@@ -5035,7 +5044,8 @@ static Smi* GenerateIdentityHash(Isolate* isolate) {
 void JSObject::SetIdentityHash(Handle<JSObject> object, Handle<Smi> hash) {
   DCHECK(!object->IsJSGlobalProxy());
   Isolate* isolate = object->GetIsolate();
-  SetHiddenProperty(object, isolate->factory()->identity_hash_string(), hash);
+  Handle<Name> 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<Name> hash_code_symbol(isolate->heap()->hash_code_symbol());
+  Handle<Object> stored_value =
+      Object::GetPropertyOrElement(Handle<Object>(this, isolate),
+                                   hash_code_symbol).ToHandleChecked();
+  return stored_value->IsSmi() ? *stored_value
+                               : isolate->heap()->undefined_value();
 }
 
 
@@ -5077,7 +5088,8 @@ Handle<Smi> JSObject::GetOrCreateIdentityHash(Handle<JSObject> object) {
   if (maybe_hash->IsSmi()) return Handle<Smi>::cast(maybe_hash);
 
   Handle<Smi> hash(GenerateIdentityHash(isolate), isolate);
-  SetHiddenProperty(object, isolate->factory()->identity_hash_string(), hash);
+  Handle<Name> 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<Name> 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<Name> 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<Object> JSObject::SetHiddenProperty(Handle<JSObject> 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<Object> JSObject::SetHiddenProperty(Handle<JSObject> object,
 
   Handle<Object> 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<ObjectHashTable> hashtable =
       GetOrCreateHiddenPropertiesHashtable(object);
 
@@ -5185,9 +5177,7 @@ void JSObject::DeleteHiddenProperty(Handle<JSObject> object, Handle<Name> 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<ObjectHashTable> hashtable(ObjectHashTable::cast(inline_value));
   bool was_present = false;
@@ -5251,14 +5241,7 @@ Handle<ObjectHashTable> JSObject::GetOrCreateHiddenPropertiesHashtable(
   Handle<ObjectHashTable> 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<Derived, Shape, Key>::SlowReverseLookup(Object* value) {
 }
 
 
+Object* ObjectHashTable::Lookup(Isolate* isolate, Handle<Object> 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<Object> 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<Object> key, int32_t hash) {
+  return Lookup(GetIsolate(), key, hash);
 }
 
 
@@ -16346,11 +16345,23 @@ Handle<ObjectHashTable> ObjectHashTable::Put(Handle<ObjectHashTable> table,
   DCHECK(!value->IsTheHole());
 
   Isolate* isolate = table->GetIsolate();
-
   // Make sure the key object has an identity hash code.
-  Handle<Smi> 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> ObjectHashTable::Put(Handle<ObjectHashTable> table,
+                                             Handle<Object> key,
+                                             Handle<Object> 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> ObjectHashTable::Put(Handle<ObjectHashTable> 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> ObjectHashTable::Remove(Handle<ObjectHashTable> table,
     return table;
   }
 
-  int entry = table->FindEntry(key);
+  return Remove(table, key, was_present, Smi::cast(hash)->value());
+}
+
+
+Handle<ObjectHashTable> ObjectHashTable::Remove(Handle<ObjectHashTable> table,
+                                                Handle<Object> 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;
index ce7f785..a973f12 100644 (file)
@@ -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<ObjectHashTable,
   // Looks up the value associated with the given key. The hole value is
   // returned in case the key is not present.
   Object* Lookup(Handle<Object> key);
+  Object* Lookup(Handle<Object> key, int32_t hash);
+  Object* Lookup(Isolate* isolate, Handle<Object> key, int32_t hash);
 
   // Adds (or overwrites) the value associated with the given key.
   static Handle<ObjectHashTable> Put(Handle<ObjectHashTable> table,
                                      Handle<Object> key,
                                      Handle<Object> value);
+  static Handle<ObjectHashTable> Put(Handle<ObjectHashTable> table,
+                                     Handle<Object> key, Handle<Object> value,
+                                     int32_t hash);
 
   // Returns an ObjectHashTable (possibly |table|) where |key| has been removed.
   static Handle<ObjectHashTable> Remove(Handle<ObjectHashTable> table,
                                         Handle<Object> key,
                                         bool* was_present);
+  static Handle<ObjectHashTable> Remove(Handle<ObjectHashTable> table,
+                                        Handle<Object> key, bool* was_present,
+                                        int32_t hash);
 
  protected:
   friend class MarkCompactCollector;
index aedc1bc..1eef189 100644 (file)
@@ -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<ObjectHashTable> table(
       ObjectHashTable::cast(weak_collection->table()));
   RUNTIME_ASSERT(table->IsKey(*key));
-  Handle<Object> lookup(table->Lookup(key), isolate);
+  Handle<Object> 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<ObjectHashTable> table(
       ObjectHashTable::cast(weak_collection->table()));
   RUNTIME_ASSERT(table->IsKey(*key));
-  Handle<Object> lookup(table->Lookup(key), isolate);
+  Handle<Object> lookup(table->Lookup(key, hash), isolate);
   return isolate->heap()->ToBoolean(!lookup->IsTheHole());
 }
 
 
 bool Runtime::WeakCollectionDelete(Handle<JSWeakCollection> weak_collection,
                                    Handle<Object> key) {
+  int32_t hash =
+      Object::GetOrCreateHash(weak_collection->GetIsolate(), key)->value();
+  return WeakCollectionDelete(weak_collection, key, hash);
+}
+
+
+bool Runtime::WeakCollectionDelete(Handle<JSWeakCollection> weak_collection,
+                                   Handle<Object> key, int32_t hash) {
   DCHECK(key->IsJSReceiver() || key->IsSymbol());
   Handle<ObjectHashTable> table(
       ObjectHashTable::cast(weak_collection->table()));
   DCHECK(table->IsKey(*key));
   bool was_present = false;
   Handle<ObjectHashTable> 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<JSWeakCollection> 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<ObjectHashTable> 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<JSWeakCollection> weak_collection,
-                                Handle<Object> key, Handle<Object> value) {
+                                Handle<Object> key, Handle<Object> value,
+                                int32_t hash) {
   DCHECK(key->IsJSReceiver() || key->IsSymbol());
   Handle<ObjectHashTable> table(
       ObjectHashTable::cast(weak_collection->table()));
   DCHECK(table->IsKey(*key));
-  Handle<ObjectHashTable> new_table = ObjectHashTable::Put(table, key, value);
+  Handle<ObjectHashTable> 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<JSWeakCollection> 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<ObjectHashTable> 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;
 }
 
index e44d15d..7ff7eb9 100644 (file)
@@ -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> 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<JSObject> registry = isolate->GetSymbolRegistry();
   Handle<String> part = isolate->factory()->private_intern_string();
   Handle<Object> privates;
-  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
-      isolate, privates, Object::GetPropertyOrElement(registry, part));
+  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, privates,
+                                     Object::GetProperty(registry, part));
   Handle<Object> 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<Symbol>::cast(symbol)->set_name(*name);
-    Handle<Symbol>::cast(symbol)->set_is_own(true);
-    JSObject::SetProperty(Handle<JSObject>::cast(privates), name, symbol,
-                          STRICT).Assert();
+    symbol = isolate->factory()->NewPrivateOwnSymbol(name);
+    JSObject::AddProperty(Handle<JSObject>::cast(privates), name, symbol, NONE);
   }
   return *symbol;
 }
index 797f3d0..b8d0307 100644 (file)
@@ -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<JSWeakCollection> weak_collection);
   static void WeakCollectionSet(Handle<JSWeakCollection> weak_collection,
-                                Handle<Object> key, Handle<Object> value);
+                                Handle<Object> key, Handle<Object> value,
+                                int32_t hash);
   static bool WeakCollectionDelete(Handle<JSWeakCollection> weak_collection,
                                    Handle<Object> key);
+  static bool WeakCollectionDelete(Handle<JSWeakCollection> weak_collection,
+                                   Handle<Object> key, int32_t hash);
 
   static MaybeHandle<JSArray> GetInternalProperties(Isolate* isolate,
                                                     Handle<Object>);
index dbb80e7..d8879ac 100644 (file)
@@ -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;
 }
 
 
index 0e71c7c..7535093 100644 (file)
@@ -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);
 }
 
 
index 9adf6a4..f2c6a38 100644 (file)
@@ -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));
 }
 
 
index 4890b80..f2e5d6f 100644 (file)
@@ -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<i::Isolate*>(isolate);
   v8::HandleScope scope(isolate);
   Handle<Object> global_proxy = env->Global();
-  int hash1 = global_proxy->GetIdentityHash();
+  i::Handle<i::Object> i_global_proxy = v8::Utils::OpenHandle(*global_proxy);
+  env->Global()->Set(v8_str("global"), global_proxy);
+  i::Handle<i::Object> original_hash;
+  if (set_in_js) {
+    CompileRun("var m = new Set(); m.add(global);");
+    original_hash = i::Handle<i::Object>(i_global_proxy->GetHash(), i_isolate);
+  } else {
+    original_hash = i::Handle<i::Object>(
+        i::Object::GetOrCreateHash(i_isolate, i_global_proxy));
+  }
+  CHECK(original_hash->IsSmi());
+  int32_t hash1 = i::Handle<i::Smi>::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();
index c8ae4f3..9e5de2e 100644 (file)
@@ -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);
 }
 
 
index e0665ce..482d111 100644 (file)
@@ -3291,41 +3291,6 @@ TEST(PrintSharedFunctionInfo) {
 #endif  // OBJECT_PRINT
 
 
-TEST(Regress2211) {
-  CcTest::InitializeVM();
-  v8::HandleScope scope(CcTest::isolate());
-
-  v8::Handle<v8::String> 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<v8::Object> obj = v8::Object::New(CcTest::isolate());
-    Handle<JSObject> 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();
index 64d2876..1255578 100644 (file)
@@ -89,8 +89,10 @@ TEST(Weakness) {
     Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
     Handle<JSObject> object = factory->NewJSObjectFromMap(map);
     Handle<Smi> 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<JSObject> object = factory->NewJSObjectFromMap(map);
       Handle<Smi> 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<JSObject> 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<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
   for (int i = 0; i < 32; i++) {
     Handle<Smi> 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
index dbb7311..8ed1812 100644 (file)
@@ -90,7 +90,8 @@ TEST(WeakSet_Weakness) {
   {
     HandleScope scope(isolate);
     Handle<Smi> 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<JSObject> object = factory->NewJSObjectFromMap(map);
       Handle<Smi> 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<JSObject> 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<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
   for (int i = 0; i < 32; i++) {
     Handle<Smi> 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 (file)
index 0000000..54dfc4c
--- /dev/null
@@ -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));