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);
}
add(t0, t0, scratch);
// hash = hash ^ (hash >> 16);
eor(t0, t0, Operand(t0, LSR, 16));
+ bic(t0, t0, Operand(0xc0000000u));
}
Add(key, key, scratch);
// hash = hash ^ (hash >> 16);
Eor(key, key, Operand(key, LSR, 16));
+ Bic(key, key, Operand(0xc0000000u));
}
// 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;
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;
}
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;
}
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);
}
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);
}
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;
}
}
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;
}
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);
"forEach", MapForEach
]);
+// Expose to the global scope.
+$getHash = GetHash;
+$getExistingHash = GetExistingHash;
+
})
}
-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;
}
// 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();
{
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
}
}
+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:
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, "==") \
#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) \
// 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 {
mov(scratch, r0);
shr(scratch, 16);
xor_(r0, scratch);
+ and_(r0, 0x3fffffff);
}
#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());
}
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
var rngstate; // Initialized to a Uint32Array during genesis.
(function(global, utils) {
-
"use strict";
%CheckIsBootstrapping();
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));
to.MathAbs = MathAbs;
to.MathExp = MathExp;
to.MathFloor = MathFloorJS;
+ to.IntRandom = MathRandomRaw;
to.MathMax = MathMax;
to.MathMin = MathMin;
});
// hash = hash ^ (hash >> 16);
srl(at, reg0, 16);
xor_(reg0, reg0, at);
+ And(reg0, reg0, Operand(0x3fffffff));
}
// hash = hash ^ (hash >> 16);
srl(at, reg0, 16);
xor_(reg0, reg0, at);
+ And(reg0, reg0, Operand(0x3fffffff));
}
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;
// 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));
}
}
-// 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) {
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()) {
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());
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);
}
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();
}
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;
}
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.
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);
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.
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);
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;
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;
}
}
+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);
}
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) {
// 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;
}
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;
// 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.
// 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.
// 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;
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.
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.
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;
}
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);
}
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;
}
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)
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>);
hash = hash ^ (hash >> 4);
hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11);
hash = hash ^ (hash >> 16);
- return hash;
+ return hash & 0x3fffffff;
}
'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);
}
'WeakMap.prototype.set', this);
}
if (!IS_SPEC_OBJECT(key)) throw MakeTypeError(kInvalidWeakMapKey);
- return %WeakCollectionSet(this, key, value);
+ return %WeakCollectionSet(this, key, value, $getHash(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);
}
'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);
}
'WeakSet.prototype.add', this);
}
if (!IS_SPEC_OBJECT(value)) throw MakeTypeError(kInvalidWeakSetValue);
- return %WeakCollectionSet(this, value, true);
+ return %WeakCollectionSet(this, value, true, $getHash(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);
}
'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);
}
movl(scratch, r0);
shrl(scratch, Immediate(16));
xorl(r0, scratch);
+ andl(r0, Immediate(0x3fffffff));
}
}
-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();
}
+THREADED_TEST(GlobalProxyIdentityHash) {
+ GlobalProxyIdentityHash(true);
+ GlobalProxyIdentityHash(false);
+}
+
+
TEST(SymbolIdentityHash) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
#endif
uint32_t runtime_hash = ComputeIntegerHash(key, isolate->heap()->HashSeed());
- CHECK(runtime_hash == codegen_hash);
+ CHECK_EQ(runtime_hash, codegen_hash);
}
#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();
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());
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);
}
}
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);
}
}
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
{
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());
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);
}
}
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);
}
}
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
--- /dev/null
+// 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));