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}
This commit is contained in:
parent
b53c35a797
commit
eca5b5d7ab
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
})
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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 {
|
||||
|
@ -1150,6 +1150,7 @@ void MacroAssembler::GetNumberHash(Register r0, Register scratch) {
|
||||
mov(scratch, r0);
|
||||
shr(scratch, 16);
|
||||
xor_(r0, scratch);
|
||||
and_(r0, 0x3fffffff);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
|
11
src/math.js
11
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;
|
||||
});
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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) {
|
||||
|
123
src/objects.cc
123
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<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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
19
test/mjsunit/global-hash.js
Normal file
19
test/mjsunit/global-hash.js
Normal file
@ -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));
|
Loading…
Reference in New Issue
Block a user