@@ -10,41 +10,50 @@ import { smi } from './Math';
1010const defaultValueOf = Object . prototype . valueOf ;
1111
1212export function hash ( o ) {
13- switch ( typeof o ) {
13+ if ( o == null ) {
14+ return hashNullish ( o ) ;
15+ }
16+
17+ if ( typeof o . hashCode === 'function' ) {
18+ // Drop any high bits from accidentally long hash codes.
19+ return smi ( o . hashCode ( o ) ) ;
20+ }
21+
22+ const v = valueOf ( o ) ;
23+
24+ if ( v == null ) {
25+ return hashNullish ( v ) ;
26+ }
27+
28+ switch ( typeof v ) {
1429 case 'boolean' :
1530 // The hash values for built-in constants are a 1 value for each 5-byte
1631 // shift region expect for the first, which encodes the value. This
1732 // reduces the odds of a hash collision for these common values.
18- return o ? 0x42108421 : 0x42108420 ;
33+ return v ? 0x42108421 : 0x42108420 ;
1934 case 'number' :
20- return hashNumber ( o ) ;
35+ return hashNumber ( v ) ;
2136 case 'string' :
22- return o . length > STRING_HASH_CACHE_MIN_STRLEN
23- ? cachedHashString ( o )
24- : hashString ( o ) ;
37+ return v . length > STRING_HASH_CACHE_MIN_STRLEN
38+ ? cachedHashString ( v )
39+ : hashString ( v ) ;
2540 case 'object' :
2641 case 'function' :
27- if ( o === null ) {
28- return 0x42108422 ;
29- }
30- if ( typeof o . hashCode === 'function' ) {
31- // Drop any high bits from accidentally long hash codes.
32- return smi ( o . hashCode ( o ) ) ;
33- }
34- if ( o . valueOf !== defaultValueOf && typeof o . valueOf === 'function' ) {
35- o = o . valueOf ( o ) ;
36- }
37- return hashJSObj ( o ) ;
38- case 'undefined' :
39- return 0x42108423 ;
42+ return hashJSObj ( v ) ;
43+ case 'symbol' :
44+ return hashSymbol ( v ) ;
4045 default :
41- if ( typeof o . toString === 'function' ) {
42- return hashString ( o . toString ( ) ) ;
46+ if ( typeof v . toString === 'function' ) {
47+ return hashString ( v . toString ( ) ) ;
4348 }
44- throw new Error ( 'Value type ' + typeof o + ' cannot be hashed.' ) ;
49+ throw new Error ( 'Value type ' + typeof v + ' cannot be hashed.' ) ;
4550 }
4651}
4752
53+ function hashNullish ( nullish ) {
54+ return nullish === null ? 0x42108422 : /* undefined */ 0x42108423 ;
55+ }
56+
4857// Compress arbitrarily large numbers into smi hashes.
4958function hashNumber ( n ) {
5059 if ( n !== n || n === Infinity ) {
@@ -90,6 +99,19 @@ function hashString(string) {
9099 return smi ( hashed ) ;
91100}
92101
102+ function hashSymbol ( sym ) {
103+ let hashed = symbolMap [ sym ] ;
104+ if ( hashed !== undefined ) {
105+ return hashed ;
106+ }
107+
108+ hashed = nextHash ( ) ;
109+
110+ symbolMap [ sym ] = hashed ;
111+
112+ return hashed ;
113+ }
114+
93115function hashJSObj ( obj ) {
94116 let hashed ;
95117 if ( usingWeakMap ) {
@@ -116,10 +138,7 @@ function hashJSObj(obj) {
116138 }
117139 }
118140
119- hashed = ++ objHashUID ;
120- if ( objHashUID & 0x40000000 ) {
121- objHashUID = 0 ;
122- }
141+ hashed = nextHash ( ) ;
123142
124143 if ( usingWeakMap ) {
125144 weakMap . set ( obj , hashed ) ;
@@ -186,14 +205,30 @@ function getIENodeHash(node) {
186205 }
187206}
188207
208+ function valueOf ( obj ) {
209+ return obj . valueOf !== defaultValueOf && typeof obj . valueOf === 'function'
210+ ? obj . valueOf ( obj )
211+ : obj ;
212+ }
213+
214+ function nextHash ( ) {
215+ const nextHash = ++ _objHashUID ;
216+ if ( _objHashUID & 0x40000000 ) {
217+ _objHashUID = 0 ;
218+ }
219+ return nextHash ;
220+ }
221+
189222// If possible, use a WeakMap.
190223const usingWeakMap = typeof WeakMap === 'function' ;
191224let weakMap ;
192225if ( usingWeakMap ) {
193226 weakMap = new WeakMap ( ) ;
194227}
195228
196- let objHashUID = 0 ;
229+ const symbolMap = Object . create ( null ) ;
230+
231+ let _objHashUID = 0 ;
197232
198233let UID_HASH_KEY = '__immutablehash__' ;
199234if ( typeof Symbol === 'function' ) {
0 commit comments