diff --git a/js/js.libraries/src/core/collections/InternalHashCodeMap.kt b/js/js.libraries/src/core/collections/InternalHashCodeMap.kt index d46872d8ab7..2efae41dfa3 100644 --- a/js/js.libraries/src/core/collections/InternalHashCodeMap.kt +++ b/js/js.libraries/src/core/collections/InternalHashCodeMap.kt @@ -37,24 +37,39 @@ import kotlin.collections.AbstractMutableMap.SimpleEntry */ internal class InternalHashCodeMap(override val equality: EqualityComparator) : InternalMap { - private var backingMap: dynamic = js("Object.create(null)") + private var backingMap: dynamic = createJsMap() override var size: Int = 0 private set override fun put(key: K, value: V): V? { val hashCode = equality.getHashCode(key) - val chain = getChainOrNull(hashCode) - if (chain == null) { + val chainOrEntry = getChainOrEntryOrNull(hashCode) + if (chainOrEntry == null) { // This is a new chain, put it to the map. - backingMap[hashCode] = arrayOf(SimpleEntry(key, value)) + backingMap[hashCode] = SimpleEntry(key, value) } else { - // Chain already exists, perhaps key also exists. - val entry = chain.findEntryInChain(key) - if (entry != null) { - return entry.setValue(value) + if (chainOrEntry !is Array<*>) { + // It is an entry + val entry: SimpleEntry = chainOrEntry + if (equality.equals(entry.key, key)) { + return entry.setValue(value) + } + else { + backingMap[hashCode] = arrayOf(entry, SimpleEntry(key, value)) + size++ + return null + } + } + else { + // Chain already exists, perhaps key also exists. + val chain: Array> = chainOrEntry + val entry = chain.findEntryInChain(key) + if (entry != null) { + return entry.setValue(value) + } + chain.asDynamic().push(SimpleEntry(key, value)) } - chain.asDynamic().push(SimpleEntry(key, value)) } size++ // structureChanged(host) @@ -63,29 +78,42 @@ internal class InternalHashCodeMap(override val equality: EqualityComparat override fun remove(key: K): V? { val hashCode = equality.getHashCode(key) - val chain = getChainOrNull(hashCode) ?: return null - for (index in 0..chain.size-1) { - val entry = chain[index] - if (equality.equals(key, entry.key)) { - if (chain.size == 1) { - chain.asDynamic().length = 0 - // remove the whole array - deleteProperty(backingMap, hashCode) - } - else { - // splice out the entry we're removing - chain.asDynamic().splice(index, 1) - } + val chainOrEntry = getChainOrEntryOrNull(hashCode) ?: return null + if (chainOrEntry !is Array<*>) { + val entry: MutableEntry = chainOrEntry + if (equality.equals(entry.key, key)) { + deleteProperty(backingMap, hashCode) size-- -// structureChanged(host) return entry.value } + else { + return null + } + } + else { + val chain: Array> = chainOrEntry + for (index in 0..chain.size - 1) { + val entry = chain[index] + if (equality.equals(key, entry.key)) { + if (chain.size == 1) { + chain.asDynamic().length = 0 + // remove the whole array + deleteProperty(backingMap, hashCode) + } else { + // splice out the entry we're removing + chain.asDynamic().splice(index, 1) + } + size-- +// structureChanged(host) + return entry.value + } + } } return null } override fun clear() { - backingMap = js("Object.create(null)") + backingMap = createJsMap() size = 0 } @@ -93,8 +121,22 @@ internal class InternalHashCodeMap(override val equality: EqualityComparat override fun get(key: K): V? = getEntry(key)?.value - private fun getEntry(key: K): MutableEntry? = - getChainOrNull(equality.getHashCode(key))?.findEntryInChain(key) + private fun getEntry(key: K): MutableEntry? { + val chainOrEntry = getChainOrEntryOrNull(equality.getHashCode(key)) ?: return null + if (chainOrEntry !is Array<*>) { + val entry: MutableEntry = chainOrEntry + if (equality.equals(entry.key, key)) { + return entry + } + else { + return null + } + } + else { + val chain: Array> = chainOrEntry + return chain.findEntryInChain(key) + } + } private fun Array>.findEntryInChain(key: K): MutableEntry? = firstOrNull { entry -> equality.equals(entry.key, key) } @@ -107,23 +149,26 @@ internal class InternalHashCodeMap(override val equality: EqualityComparat val keys: Array = js("Object").keys(backingMap) var keyIndex = -1 - var chain: Array>? = null + var chainOrEntry: dynamic = null + var isChain = false var itemIndex = -1 var lastEntry: MutableEntry? = null private fun computeNext(): Int { - if (chain != null) { - if (++itemIndex < chain!!.size) + if (chainOrEntry != null && isChain) { + val chainSize: Int = chainOrEntry.unsafeCast>>().size + if (++itemIndex < chainSize) return 0 } if (++keyIndex < keys.size) { - chain = backingMap[keys[keyIndex]] + chainOrEntry = backingMap[keys[keyIndex]] + isChain = chainOrEntry is Array<*> itemIndex = 0 return 0 } else { - chain = null + chainOrEntry = null return 1 } } @@ -136,7 +181,12 @@ internal class InternalHashCodeMap(override val equality: EqualityComparat override fun next(): MutableEntry { if (!hasNext()) throw NoSuchElementException() - val lastEntry = chain!![itemIndex] + val lastEntry = if (isChain) { + chainOrEntry.unsafeCast>>()[itemIndex] + } + else { + chainOrEntry.unsafeCast>() + } this.lastEntry = lastEntry state = -1 return lastEntry @@ -152,9 +202,9 @@ internal class InternalHashCodeMap(override val equality: EqualityComparat } } - private fun getChainOrNull(hashCode: Int): Array>? { - val chain = backingMap[hashCode].unsafeCast>?>() - return chain.takeIf { it !== undefined } + private fun getChainOrEntryOrNull(hashCode: Int): dynamic { + val chainOrEntry = backingMap[hashCode] + return if (chainOrEntry === undefined) null else chainOrEntry } } diff --git a/js/js.libraries/src/core/collections/InternalMap.kt b/js/js.libraries/src/core/collections/InternalMap.kt index bb8cdc01f98..33e6b649b19 100644 --- a/js/js.libraries/src/core/collections/InternalMap.kt +++ b/js/js.libraries/src/core/collections/InternalMap.kt @@ -28,4 +28,12 @@ internal interface InternalMap : MutableIterable(override val equality: EqualityComparator) : InternalMap { - private var backingMap: dynamic = js("Object.create(null)") + private var backingMap: dynamic = createJsMap() override var size: Int = 0 private set @@ -87,7 +87,7 @@ internal class InternalStringMap(override val equality: EqualityComparator override fun clear() { - backingMap = js("Object.create(null)") + backingMap = createJsMap() size = 0 }