Skip to content
This repository was archived by the owner on Jul 23, 2021. It is now read-only.

Commit 38a01e3

Browse files
committed
Hash symbols as objects
1 parent 5d88d29 commit 38a01e3

File tree

2 files changed

+45
-10
lines changed

2 files changed

+45
-10
lines changed

__tests__/hash.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ describe('hash', () => {
3636
expect(hash(objA)).not.toBe(hash(objB));
3737
});
3838

39+
it('generates different hashes for different symbols', () => {
40+
const symA = Symbol();
41+
const symB = Symbol();
42+
expect(hash(symA)).toBe(hash(symA));
43+
expect(hash(symA)).not.toBe(hash(symB));
44+
});
45+
3946
it('generates different hashes for different functions', () => {
4047
const funA = () => {
4148
return;

src/Hash.js

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@ export function hash(o) {
2323
? cachedHashString(o)
2424
: hashString(o);
2525
case 'object':
26-
case 'function':
2726
if (o === null) {
2827
return 0x42108422;
2928
}
29+
// falls through
30+
case 'function':
3031
if (typeof o.hashCode === 'function') {
3132
// Drop any high bits from accidentally long hash codes.
3233
return smi(o.hashCode(o));
3334
}
34-
if (o.valueOf !== defaultValueOf && typeof o.valueOf === 'function') {
35-
o = o.valueOf(o);
36-
}
37-
return hashJSObj(o);
35+
return hashJSObj(asPrimitive(o));
36+
case 'symbol':
37+
return hashSymbol(asPrimitive(o));
3838
case 'undefined':
3939
return 0x42108423;
4040
default:
@@ -90,6 +90,19 @@ function hashString(string) {
9090
return smi(hashed);
9191
}
9292

93+
function hashSymbol(sym) {
94+
let hashed = symbolMap[sym];
95+
if (hashed !== undefined) {
96+
return hashed;
97+
}
98+
99+
hashed = nextHash();
100+
101+
symbolMap[sym] = hashed;
102+
103+
return hashed;
104+
}
105+
93106
function hashJSObj(obj) {
94107
let hashed;
95108
if (usingWeakMap) {
@@ -116,10 +129,7 @@ function hashJSObj(obj) {
116129
}
117130
}
118131

119-
hashed = ++objHashUID;
120-
if (objHashUID & 0x40000000) {
121-
objHashUID = 0;
122-
}
132+
hashed = nextHash();
123133

124134
if (usingWeakMap) {
125135
weakMap.set(obj, hashed);
@@ -186,14 +196,32 @@ function getIENodeHash(node) {
186196
}
187197
}
188198

199+
function asPrimitive(obj) {
200+
return (
201+
obj.valueOf !== defaultValueOf && typeof obj.valueOf === 'function'
202+
? obj.valueOf(obj)
203+
: obj
204+
);
205+
}
206+
207+
function nextHash() {
208+
const nextHash = ++_objHashUID;
209+
if (_objHashUID & 0x40000000) {
210+
_objHashUID = 0;
211+
}
212+
return nextHash;
213+
}
214+
189215
// If possible, use a WeakMap.
190216
const usingWeakMap = typeof WeakMap === 'function';
191217
let weakMap;
192218
if (usingWeakMap) {
193219
weakMap = new WeakMap();
194220
}
195221

196-
let objHashUID = 0;
222+
const symbolMap = Object.create(null);
223+
224+
let _objHashUID = 0;
197225

198226
let UID_HASH_KEY = '__immutablehash__';
199227
if (typeof Symbol === 'function') {

0 commit comments

Comments
 (0)