diff --git a/__tests__/issues.ts b/__tests__/issues.ts index ee4963254b..9e079f0f92 100644 --- a/__tests__/issues.ts +++ b/__tests__/issues.ts @@ -101,3 +101,27 @@ describe('Issue #1293', () => { expect(secondState).toEqual(firstState); }); }); + +describe('Issue #1643', () => { + [ + ['a string', 'test'], + ['a number', 5], + ['null', null], + ['undefined', undefined], + ['a boolean', true], + ['an object', {}], + ['an array', []], + ['a function', () => null], + ].forEach(([label, value]) => { + class MyClass { + valueOf() { + return value; + } + } + + it(`Collection#hashCode() should handle objects that return ${label} for valueOf`, () => { + const set = Set().add(new MyClass()); + set.hashCode(); + }); + }); +}); diff --git a/src/Hash.js b/src/Hash.js index f15466ffb9..b76f1e1406 100644 --- a/src/Hash.js +++ b/src/Hash.js @@ -10,6 +10,10 @@ import { smi } from './Math'; const defaultValueOf = Object.prototype.valueOf; export function hash(o) { + if (o && o.valueOf !== defaultValueOf && typeof o.valueOf === 'function') { + o = o.valueOf(); + } + switch (typeof o) { case 'boolean': // The hash values for built-in constants are a 1 value for each 5-byte @@ -31,9 +35,6 @@ export function hash(o) { // Drop any high bits from accidentally long hash codes. return smi(o.hashCode(o)); } - if (o.valueOf !== defaultValueOf && typeof o.valueOf === 'function') { - o = o.valueOf(o); - } return hashJSObj(o); case 'undefined': return 0x42108423;