From 5cd3417d4cc6d5f54b66911c4c0e07b72a4fda00 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Fri, 26 Aug 2016 03:24:55 +0300 Subject: [PATCH] Maps and sets: cleanup, simplify, refactor. #KT-12386 --- .../src/core/collections/AbstractHashMap.kt | 233 ++++++++---------- .../src/core/collections/AbstractMap.kt | 4 +- .../src/core/collections/HashMap.kt | 45 +--- .../src/core/collections/HashSet.kt | 55 ++--- .../core/collections/InternalHashCodeMap.kt | 152 ++++++------ .../src/core/collections/LinkedHashMap.kt | 203 +++++++-------- .../src/core/collections/LinkedHashSet.kt | 1 - js/js.libraries/src/core/core.kt | 3 + js/js.translator/testData/kotlin_lib.js | 4 + 9 files changed, 317 insertions(+), 383 deletions(-) diff --git a/js/js.libraries/src/core/collections/AbstractHashMap.kt b/js/js.libraries/src/core/collections/AbstractHashMap.kt index d35a929d5ee..2856f8410a7 100644 --- a/js/js.libraries/src/core/collections/AbstractHashMap.kt +++ b/js/js.libraries/src/core/collections/AbstractHashMap.kt @@ -20,42 +20,33 @@ package kotlin.collections +import kotlin.collections.Map.Entry +import kotlin.collections.MutableMap.MutableEntry + abstract class AbstractHashMap : AbstractMap { - private inner class EntrySet : AbstractSet>() { + private inner class EntrySet : AbstractSet>() { override fun clear() { this@AbstractHashMap.clear() } - override operator fun contains(o: Any?): Boolean { - if (o is Entry<*, *>) { - return containsEntry(o as Entry<*, *>?) - } - return false - } + override operator fun contains(element: MutableEntry): Boolean = containsEntry(element) - override operator fun iterator(): Iterator> { - return EntrySetIterator() - } + override operator fun iterator(): MutableIterator> = hashCodeMap.iterator() - override fun remove(entry: Any?): Boolean { + override fun remove(entry: MutableEntry): Boolean { if (contains(entry)) { - val key = (entry as Entry<*, *>).key - this@AbstractHashMap.remove(key) + this@AbstractHashMap.remove(entry.key) return true } return false } - override fun size(): Int { - return this@AbstractHashMap.size - } + override val size: Int get() = this@AbstractHashMap.size } - /** - * Iterator for `EntrySet`. - */ + /* private inner class EntrySetIterator : Iterator> { private val stringMapEntries = stringMap!!.iterator() private var current: MutableIterator> = stringMapEntries @@ -102,109 +93,103 @@ abstract class AbstractHashMap : AbstractMap { recordLastKnownStructure(this@AbstractHashMap, this) } - } + }*/ /** * A map of integral hashCodes onto entries. */ - @Transient private var hashCodeMap: InternalHashCodeMap? = null + private var hashCodeMap = InternalHashCodeMap(this) - /** - * A map of Strings onto values. - */ - @Transient private var stringMap: InternalStringMap? = null +// /** +// * A map of Strings onto values. +// */ +// @Transient private var stringMap: InternalStringMap? = null - constructor() { - reset() - } + constructor() : super() +// init { +// reset() +// } - @JvmOverloads constructor(ignored: Int, alsoIgnored: Float = 0f) { + constructor(capacity: Int, loadFactor: Float = 0f) : this() { // This implementation of HashMap has no need of load factors or capacities. - checkArgument(ignored >= 0, "Negative initial capacity") - checkArgument(alsoIgnored >= 0, "Non-positive load factor") - - reset() + require(capacity >= 0) { "Negative initial capacity" } + require(loadFactor >= 0) { "Non-positive load factor" } } - constructor(toBeCopied: Map) { - reset() - this.putAll(toBeCopied) + constructor(original: Map) : this() { + this.putAll(original) } override fun clear() { - reset() - } - - private fun reset() { +// reset() +// } +// +// private fun reset() { hashCodeMap = InternalHashCodeMap(this) - stringMap = InternalStringMap(this) - structureChanged(this) +// stringMap = InternalStringMap(this) +// structureChanged(this) } - @SpecializeMethod(params = { String.class }, target = "hasStringValue") - override fun containsKey(key: Any?): Boolean { - return if (key is String) - hasStringValue(JsUtils.unsafeCastToString(key)) - else - hasHashValue(key) +// @SpecializeMethod(params = { String.class }, target = "hasStringValue") + override fun containsKey(key: K): Boolean { + return hasHashValue(key) +// return if (key is String) +// hasStringValue(JsUtils.unsafeCastToString(key)) +// else +// hasHashValue(key) } - override fun containsValue(value: Any?): Boolean { - return containsValue(value, stringMap) || containsValue(value, hashCodeMap) + override fun containsValue(value: V): Boolean { + return /*containsValue(value, stringMap) || */ containsValue(value, hashCodeMap) } - private fun containsValue(value: Any, entries: Iterable>): Boolean { - for (entry in entries) { - if (equals(value, entry.value)) { - return true - } - } - return false + private fun containsValue(value: V, entries: Iterable>): Boolean = entries.any { equals(it.value, value) } + + override val entries: MutableSet> + get() = EntrySet() + +// @SpecializeMethod(params = { String.class }, target = "getStringValue") + override operator fun get(key: K): V? { + return getHashValue(key) +// return if (key is String) +// getStringValue(JsUtils.unsafeCastToString(key)) +// else +// getHashValue(key) } - override fun entrySet(): Set> { - return EntrySet() +// @SpecializeMethod(params = { String.class, Object .class }, target = "putStringValue") + override fun put(key: K, value: V): V? { + return putHashValue(key, value) +// return if (key is String) +// putStringValue(JsUtils.unsafeCastToString(key), value) +// else +// putHashValue(key, value) } - @SpecializeMethod(params = { String.class }, target = "getStringValue") - override operator fun get(key: Any?): V { - return if (key is String) - getStringValue(JsUtils.unsafeCastToString(key)) - else - getHashValue(key) +// @SpecializeMethod(params = { String.class }, target = "removeStringValue") + override fun remove(key: K): V? { + return removeHashValue(key) +// return if (key is String) +// removeStringValue(JsUtils.unsafeCastToString(key)) +// else +// removeHashValue(key) } - @SpecializeMethod(params = { String.class, Object .class }, target = "putStringValue") - override fun put(key: K?, value: V?): V { - return if (key is String) - putStringValue(JsUtils.unsafeCastToString(key), value) - else - putHashValue(key, value) - } - - @SpecializeMethod(params = { String.class }, target = "removeStringValue") - override fun remove(key: Any?): V { - return if (key is String) - removeStringValue(JsUtils.unsafeCastToString(key)) - else - removeHashValue(key) - } - - override fun size(): Int { - return hashCodeMap!!.size() + stringMap!!.size() + override val size: Int get() { + return hashCodeMap.size /*+ stringMap!!.size()*/ } /** * Subclasses must override to return a whether or not two keys or values are * equal. */ - internal abstract fun equals(value1: Any, value2: Any): Boolean + internal abstract fun equals(value1: Any?, value2: Any?): Boolean /** * Subclasses must override to return a hash code for a given key. The key is * guaranteed to be non-null and not a String. */ - internal abstract fun getHashCode(key: Any): Int + internal abstract fun getHashCode(key: K): Int /** * Returns the Map.Entry whose key is Object equal to `key`, @@ -212,51 +197,51 @@ abstract class AbstractHashMap : AbstractMap { * or `null` if no such Map.Entry exists at the specified * hashCode. */ - private fun getHashValue(key: Any?): V { - return getEntryValueOrNull(hashCodeMap!!.getEntry(key)) + private fun getHashValue(key: K): V? { + return hashCodeMap.getEntry(key)?.value } - /** - * Returns the value for the given key in the stringMap. Returns - * `null` if the specified key does not exist. - */ - private fun getStringValue(key: String?): V { - return if (key == null) getHashValue(null) else stringMap!!.get(key) - } +// /** +// * Returns the value for the given key in the stringMap. Returns +// * `null` if the specified key does not exist. +// */ +// private fun getStringValue(key: String?): V { +// return if (key == null) getHashValue(null) else stringMap!!.get(key) +// } /** * Returns true if the a key exists in the hashCodeMap that is Object equal to * `key`, provided that `key`'s hash code is * `hashCode`. */ - private fun hasHashValue(key: Any?): Boolean { - return hashCodeMap!!.getEntry(key) != null - } - - /** - * Returns true if the given key exists in the stringMap. - */ - private fun hasStringValue(key: String?): Boolean { - return if (key == null) hasHashValue(null) else stringMap!!.contains(key) + private fun hasHashValue(key: K): Boolean { + return hashCodeMap.getEntry(key) != null } +// +// /** +// * Returns true if the given key exists in the stringMap. +// */ +// private fun hasStringValue(key: String?): Boolean { +// return if (key == null) hasHashValue(null) else stringMap!!.contains(key) +// } /** * Sets the specified key to the specified value in the hashCodeMap. Returns * the value previously at that key. Returns `null` if the * specified key did not exist. */ - private fun putHashValue(key: K?, value: V): V { - return hashCodeMap!!.put(key, value) + private fun putHashValue(key: K, value: V): V? { + return hashCodeMap.put(key, value) } - /** - * Sets the specified key to the specified value in the stringMap. Returns the - * value previously at that key. Returns `null` if the specified - * key did not exist. - */ - private fun putStringValue(key: String?, value: V): V { - return if (key == null) putHashValue(null, value) else stringMap!!.put(key, value) - } +// /** +// * Sets the specified key to the specified value in the stringMap. Returns the +// * value previously at that key. Returns `null` if the specified +// * key did not exist. +// */ +// private fun putStringValue(key: String?, value: V): V { +// return if (key == null) putHashValue(null, value) else stringMap!!.put(key, value) +// } /** * Removes the pair whose key is Object equal to `key` from @@ -264,16 +249,16 @@ abstract class AbstractHashMap : AbstractMap { * is `hashCode`. Returns the value that was associated with the * removed key, or null if no such key existed. */ - private fun removeHashValue(key: Any?): V { - return hashCodeMap!!.remove(key) + private fun removeHashValue(key: K): V? { + return hashCodeMap.remove(key) } - /** - * Removes the specified key from the stringMap and returns the value that was - * previously there. Returns `null` if the specified key does not - * exist. - */ - private fun removeStringValue(key: String?): V { - return if (key == null) removeHashValue(null) else stringMap!!.remove(key) - } -}// This implementation of HashMap has no need of initial capacities. \ No newline at end of file +// /** +// * Removes the specified key from the stringMap and returns the value that was +// * previously there. Returns `null` if the specified key does not +// * exist. +// */ +// private fun removeStringValue(key: String?): V { +// return if (key == null) removeHashValue(null) else stringMap!!.remove(key) +// } +} \ No newline at end of file diff --git a/js/js.libraries/src/core/collections/AbstractMap.kt b/js/js.libraries/src/core/collections/AbstractMap.kt index 62e4fc39c56..0da59e83259 100644 --- a/js/js.libraries/src/core/collections/AbstractMap.kt +++ b/js/js.libraries/src/core/collections/AbstractMap.kt @@ -66,7 +66,9 @@ abstract class AbstractMap protected constructor() : MutableMap { override fun containsValue(value: V): Boolean = entries.any { it.value == value } - internal fun containsEntry(entry: Map.Entry<*, *>): Boolean { + internal fun containsEntry(entry: Map.Entry<*, *>?): Boolean { + // since entry comes from @UnsafeVariance parameters it can be virtually anything + if (entry !is Map.Entry<*, *>) return false val key = entry.key val value = entry.value val ourValue = get(key) diff --git a/js/js.libraries/src/core/collections/HashMap.kt b/js/js.libraries/src/core/collections/HashMap.kt index 6f4999cada6..ab6aeeb22f7 100644 --- a/js/js.libraries/src/core/collections/HashMap.kt +++ b/js/js.libraries/src/core/collections/HashMap.kt @@ -20,45 +20,18 @@ package kotlin.collections -open class HashMap : AbstractHashMap, Cloneable, Serializable { - /** - * Ensures that RPC will consider type parameter K to be exposed. It will be - * pruned by dead code elimination. - */ - @SuppressWarnings("unused") - private val exposeKey: K? = null +open class HashMap : AbstractHashMap { - /** - * Ensures that RPC will consider type parameter V to be exposed. It will be - * pruned by dead code elimination. - */ - @SuppressWarnings("unused") - private val exposeValue: V? = null + constructor() : super() + constructor(capacity: Int, loadFactor: Float = 0f) : super(capacity, loadFactor) + constructor(original: Map) : super(original) - constructor() { - } +// public override fun clone(): Any { +// return HashMap(this) +// } - constructor(ignored: Int) : super(ignored) { - } + override fun equals(value1: Any?, value2: Any?): Boolean = value1 == value2 - constructor(ignored: Int, alsoIgnored: Float) : super(ignored, alsoIgnored) { - } - - constructor(toBeCopied: Map) : super(toBeCopied) { - } - - public override fun clone(): Any { - return HashMap(this) - } - - internal fun equals(value1: Any, value2: Any): Boolean { - return value1 == value2 - } - - internal fun getHashCode(key: Any): Int { - val hashCode = key.hashCode() - // Coerce to int -- our classes all do this, but a user-written class might not. - return ensureInt(hashCode) - } + override fun getHashCode(key: K): Int = key?.hashCode() ?: 0 } diff --git a/js/js.libraries/src/core/collections/HashSet.kt b/js/js.libraries/src/core/collections/HashSet.kt index 523bb79ba41..4a10d5e51b0 100644 --- a/js/js.libraries/src/core/collections/HashSet.kt +++ b/js/js.libraries/src/core/collections/HashSet.kt @@ -21,22 +21,9 @@ package kotlin.collections -/** - * Implements a set in terms of a hash table. [[Sun - * docs]](http://java.sun.com/j2se/1.5.0/docs/api/java/util/HashSet.html) +open class HashSet : AbstractSet { - * @param element type. - */ -open class HashSet : AbstractSet, Set, Cloneable, Serializable { - - @Transient private var map: HashMap? = null - - /** - * Ensures that RPC will consider type parameter E to be exposed. It will be - * pruned by dead code elimination. - */ - @SuppressWarnings("unused") - private val exposeElement: E? = null + private val map: HashMap constructor() { map = HashMap() @@ -47,11 +34,7 @@ open class HashSet : AbstractSet, Set, Cloneable, Serializable { addAll(c) } - constructor(initialCapacity: Int) { - map = HashMap(initialCapacity) - } - - constructor(initialCapacity: Int, loadFactor: Float) { + constructor(initialCapacity: Int, loadFactor: Float = 0.0f) { map = HashMap(initialCapacity, loadFactor) } @@ -65,37 +48,27 @@ open class HashSet : AbstractSet, Set, Cloneable, Serializable { this.map = map } - override fun add(o: E?): Boolean { - val old = map!!.put(o, this) + override fun add(element: E): Boolean { + val old = map.put(element, this) return old == null } override fun clear() { - map!!.clear() + map.clear() } - public override fun clone(): Any { - return HashSet(this) - } +// public override fun clone(): Any { +// return HashSet(this) +// } - override operator fun contains(o: Any?): Boolean { - return map!!.containsKey(o) - } + override operator fun contains(element: E): Boolean = map.containsKey(element) - override fun isEmpty(): Boolean { - return map!!.isEmpty() - } + override fun isEmpty(): Boolean = map.isEmpty() - override fun iterator(): Iterator { - return map!!.keys.iterator() - } + override fun iterator(): MutableIterator = map.keys.iterator() - override fun remove(o: Any?): Boolean { - return map!!.remove(o) != null - } + override fun remove(element: E): Boolean = map.remove(element) != null - override fun size(): Int { - return map!!.size - } + override val size: Int get() = map.size } diff --git a/js/js.libraries/src/core/collections/InternalHashCodeMap.kt b/js/js.libraries/src/core/collections/InternalHashCodeMap.kt index 6cedb1c64be..ca05c7954a8 100644 --- a/js/js.libraries/src/core/collections/InternalHashCodeMap.kt +++ b/js/js.libraries/src/core/collections/InternalHashCodeMap.kt @@ -20,11 +20,8 @@ package kotlin.collections -import java.util.ConcurrentModificationDetector.structureChanged - -import java.util.AbstractMap.SimpleEntry - -import javaemul.internal.ArrayHelper +import kotlin.collections.MutableMap.MutableEntry +import kotlin.collections.AbstractMap.SimpleEntry /** * A simple wrapper around JavaScriptObject to provide [java.util.Map]-like semantics for any @@ -38,126 +35,117 @@ import javaemul.internal.ArrayHelper * have the same hash, each value in hashCodeMap is actually an array containing all entries whose * keys share the same hash. */ -private class InternalHashCodeMap(private val host: AbstractHashMap) : Iterable> { +internal class InternalHashCodeMap(private val host: AbstractHashMap) : MutableIterable> { - private val backingMap = InternalJsMapFactory.newJsMap() - private var size: Int = 0 + private val backingMap: dynamic = js("new Object()") + var size: Int = 0 + private set fun put(key: K, value: V): V? { - val hashCode = hash(key) - val chain = getChainOrEmpty(hashCode) - - if (chain.size == 0) { + val hashCode = host.getHashCode(key) + val chain = getChainOrNull(hashCode) + if (chain == null) { // This is a new chain, put it to the map. - backingMap.set(hashCode, chain) + backingMap[hashCode] = arrayOf(SimpleEntry(key, value)) } else { // Chain already exists, perhaps key also exists. - val entry = findEntryInChain(key, chain) + val entry = chain.findEntryInChain(key) if (entry != null) { - return entry!!.setValue(value) + return entry.setValue(value) } + chain.asDynamic().push(SimpleEntry(key, value)) } - chain[chain.size] = SimpleEntry(key, value) size++ - structureChanged(host) +// structureChanged(host) return null } - fun remove(key: Any): V? { - val hashCode = hash(key) - val chain = getChainOrEmpty(hashCode) - for (i in chain.indices) { - val entry = chain[i] + fun remove(key: K): V? { + val hashCode = host.getHashCode(key) + val chain = getChainOrNull(hashCode) ?: return null + for (index in 0..chain.size-1) { + val entry = chain[index] if (host.equals(key, entry.key)) { if (chain.size == 1) { - ArrayHelper.setLength(chain, 0) + chain.asDynamic().length = 0 // remove the whole array - backingMap.delete(hashCode) + deleteProperty(backingMap, hashCode) } else { // splice out the entry we're removing - ArrayHelper.removeFrom(chain, i, 1) + chain.asDynamic().splice(index, 1) } size-- - structureChanged(host) +// structureChanged(host) return entry.value } } return null } - fun getEntry(key: Any): Entry { - return findEntryInChain(key, getChainOrEmpty(hash(key))) - } + fun getEntry(key: K): MutableEntry? = + getChainOrNull(host.getHashCode(key))?.findEntryInChain(key) - private fun findEntryInChain(key: Any, chain: Array>): Entry? { - for (entry in chain) { - if (host.equals(key, entry.key)) { - return entry + private fun Array>.findEntryInChain(key: K): MutableEntry? = + firstOrNull { entry -> host.equals(entry.key, key) } + + override fun iterator(): MutableIterator> { + + return object : MutableIterator> { + var state = -1 // -1 not ready, 0 - ready, 1 - done + + val keys: Array = js("Object").keys(backingMap) + var keyIndex = -1 + + var chain: Array>? = null + var itemIndex = -1 + var lastEntry: MutableEntry? = null + + private fun computeNext(): Int { + if (chain != null) { + if (++itemIndex < chain!!.size) + return 0 + } + + if (++keyIndex < keys.size) { + chain = backingMap[keys[keyIndex]] + itemIndex = 0 + return 0 + } + else { + chain = null + return 1 + } } - } - return null - } - - fun size(): Int { - return size - } - - override fun iterator(): Iterator> { - return object : Iterator> { - internal val chains = backingMap.entries() - internal var itemIndex = 0 - internal var chain = newEntryChain() - internal var lastEntry: Entry? = null override fun hasNext(): Boolean { - if (itemIndex < chain.size) { - return true - } - val current = chains.next() - if (!current.done) { - // Move to the beginning of next chain - chain = unsafeCastToArray(current.getValue()) - itemIndex = 0 - return true - } - return false + if (state == -1) + state = computeNext() + return state == 0 } - override fun next(): Entry { - lastEntry = chain[itemIndex++] + override fun next(): MutableEntry { + if (!hasNext()) throw NoSuchElementException() + val lastEntry = chain!![itemIndex] + this.lastEntry = lastEntry + state = -1 return lastEntry } override fun remove() { + checkNotNull(lastEntry) this@InternalHashCodeMap.remove(lastEntry!!.key) - // Unless we are in a new chain, all items have shifted so our itemIndex should as well... - if (itemIndex != 0) { - itemIndex-- - } + lastEntry = null + // the chain being iterated just got modified by InternalHashCodeMap.remove + itemIndex-- } } } - private fun getChainOrEmpty(hashCode: Int): Array> { - val chain = unsafeCastToArray(backingMap.get(hashCode)) - return chain ?: newEntryChain() + private fun getChainOrNull(hashCode: Int): Array>? { + val chain = backingMap[hashCode] + return if (chain !== undefined) chain else null // satisfying { it != undefined } } - private fun newEntryChain(/*-{ - return []; - }-*/): Array> - - private fun unsafeCastToArray(arr: Any /*-{ - return arr; - }-*/): Array>? - - /** - * Returns hash code of the key as calculated by [AbstractHashMap.getHashCode] but - * also handles null keys as well. - */ - private fun hash(key: Any?): Int { - return if (key == null) 0 else host.getHashCode(key) - } } diff --git a/js/js.libraries/src/core/collections/LinkedHashMap.kt b/js/js.libraries/src/core/collections/LinkedHashMap.kt index d66647cc521..7b541d08c79 100644 --- a/js/js.libraries/src/core/collections/LinkedHashMap.kt +++ b/js/js.libraries/src/core/collections/LinkedHashMap.kt @@ -19,6 +19,8 @@ */ package kotlin.collections +import kotlin.collections.MutableMap.MutableEntry + open class LinkedHashMap : HashMap, Map { /** @@ -33,41 +35,59 @@ open class LinkedHashMap : HashMap, Map { * small modifications. Paying a small storage cost only if you use * LinkedHashMap and minimizing code size seemed like a better tradeoff */ - private inner class ChainEntry @JvmOverloads constructor(key: K? = null, value: V? = null) : AbstractMap.SimpleEntry(key, value) { - @Transient private var next: ChainEntry? = null - @Transient private var prev: ChainEntry? = null + private inner class ChainEntry(key: K, value: V) : AbstractMap.SimpleEntry(key, value) { + internal var next: ChainEntry? = null + internal var prev: ChainEntry? = null /** * Add this node to the end of the chain. */ fun addToEnd() { - val tail = head.prev - - // Chain is valid. - assert(head != null && tail != null) - // This entry is not in the list. - assert(next == null && prev == null) + check(next == null && prev == null) + + if (head == null) { + head = this + next = this + prev = this + } else { + // Chain is valid. + val tail = checkNotNull(head).prev + checkNotNull(tail) + // Update me. + prev = tail + next = head + // Update my new siblings: current head and old tail + head!!.prev = this + tail!!.next = this + } - // Update me. - prev = tail - next = head - tail!!.next = head.prev = this } /** * Remove this node from any list it may be a part of. */ fun remove() { - next!!.prev = prev - prev!!.next = next - next = prev = null + if (this.next === this) { + // if this is single element, remove head + head = null + } + else { + if (head === this) { + // if this is first element, move head to next + head = next + } + next!!.prev = prev + prev!!.next = next + } + next = null + prev = null } } - private inner class EntrySet : AbstractSet>() { + private inner class EntrySet : AbstractSet>() { - private inner class EntryIterator : Iterator> { + private inner class EntryIterator : MutableIterator> { // The last entry that was returned from this iterator. private var last: ChainEntry? = null @@ -75,30 +95,32 @@ open class LinkedHashMap : HashMap, Map { private var next: ChainEntry? = null init { - next = head.next - recordLastKnownStructure(map, this) + next = head +// recordLastKnownStructure(map, this) } override fun hasNext(): Boolean { - return next !== head + return next !== null } - override fun next(): Entry { - checkStructuralChange(map, this) - checkCriticalElement(hasNext()) + override fun next(): MutableEntry { +// checkStructuralChange(map, this) + if (!hasNext()) throw NoSuchElementException() - last = next - next = next!!.next - return last + val current = next!! + last = current + next = current.next + if (next === head) next = null // satisfying { it != head } + return current } override fun remove() { - checkState(last != null) - checkStructuralChange(map, this) + check(last != null) +// checkStructuralChange(map, this) last!!.remove() map.remove(last!!.key) - recordLastKnownStructure(map, this) +// recordLastKnownStructure(map, this) last = null } } @@ -107,34 +129,24 @@ open class LinkedHashMap : HashMap, Map { this@LinkedHashMap.clear() } - override operator fun contains(o: Any?): Boolean { - if (o is Entry<*, *>) { - return containsEntry(o as Entry<*, *>?) - } - return false - } + override operator fun contains(element: MutableEntry): Boolean = containsEntry(element) - override operator fun iterator(): Iterator> { - return EntryIterator() - } + override operator fun iterator(): MutableIterator> = EntryIterator() - override fun remove(entry: Any?): Boolean { + override fun remove(entry: MutableEntry): Boolean { if (contains(entry)) { - val key = (entry as Entry<*, *>).key - this@LinkedHashMap.remove(key) + this@LinkedHashMap.remove(entry.key) return true } return false } - override fun size(): Int { - return this@LinkedHashMap.size - } + override val size: Int get() = this@LinkedHashMap.size } - // True if we should use the access order (ie, for LRU caches) instead of - // insertion order. - @Transient private val accessOrder: Boolean +// // True if we should use the access order (ie, for LRU caches) instead of +// // insertion order. +// private val accessOrder: Boolean /* * The head of the LRU/insert order chain, which is a doubly-linked circular @@ -143,31 +155,31 @@ open class LinkedHashMap : HashMap, Map { * The most recently inserted/accessed node is at the end of the chain, ie. * chain.prev. */ - @Transient private val head = ChainEntry() + private var head: ChainEntry? = null /* * The hashmap that keeps track of our entries and the chain. Note that we * duplicate the key here to eliminate changes to HashMap and minimize the * code here, at the expense of additional space. */ - @Transient private val map = HashMap() + private val map = HashMap() constructor() { resetChainEntries() } - @JvmOverloads constructor(ignored: Int, alsoIgnored: Float = 0f) : super(ignored, alsoIgnored) { + constructor(ignored: Int, alsoIgnored: Float = 0f) : super(ignored, alsoIgnored) { resetChainEntries() } - constructor(ignored: Int, alsoIgnored: Float, accessOrder: Boolean) : super(ignored, alsoIgnored) { - this.accessOrder = accessOrder - resetChainEntries() - } +// constructor(ignored: Int, alsoIgnored: Float, accessOrder: Boolean) : super(ignored, alsoIgnored) { +// this.accessOrder = accessOrder +// resetChainEntries() +// } - constructor(toBeCopied: Map) { + constructor(original: Map) { resetChainEntries() - this.putAll(toBeCopied) + this.putAll(original) } override fun clear() { @@ -176,85 +188,80 @@ open class LinkedHashMap : HashMap, Map { } private fun resetChainEntries() { - head.prev = head - head.next = head + head = null } +// +// override fun clone(): Any { +// return LinkedHashMap(this) +// } - override fun clone(): Any { - return LinkedHashMap(this) - } + override fun containsKey(key: K): Boolean = map.containsKey(key) - override fun containsKey(key: Any?): Boolean { - return map.containsKey(key) - } - - override fun containsValue(value: Any?): Boolean { - var node: ChainEntry = head.next - while (node !== head) { + override fun containsValue(value: V): Boolean { + var node: ChainEntry = head ?: return false + do { if (node.value == value) { return true } - node = node.next - } + node = node.next!! + } while (node !== head) return false } - override fun entrySet(): Set> { - return EntrySet() - } - override operator fun get(key: Any?): V? { + override val entries: MutableSet> + get() = EntrySet() + + override operator fun get(key: K): V? { val entry = map.get(key) if (entry != null) { recordAccess(entry) - return entry!!.value + return entry.value } return null } - override fun put(key: K?, value: V?): V? { + override fun put(key: K, value: V): V? { val old = map.get(key) if (old == null) { val newEntry = ChainEntry(key, value) map.put(key, newEntry) newEntry.addToEnd() - val eldest = head.next - if (removeEldestEntry(eldest)) { - eldest!!.remove() - map.remove(eldest.key) - } +// val eldest = head.next!! +// if (removeEldestEntry(eldest)) { +// eldest.remove() +// map.remove(eldest.key) +// } return null } else { - val oldValue = old!!.setValue(value) + val oldValue = old.setValue(value) recordAccess(old) return oldValue } } - override fun remove(key: Any?): V? { + override fun remove(key: K): V? { val entry = map.remove(key) if (entry != null) { - entry!!.remove() - return entry!!.value + entry.remove() + return entry.value } return null } - override fun size(): Int { - return map.size - } + override val size: Int get() = map.size - @SuppressWarnings("unused") - protected fun removeEldestEntry(eldest: Entry): Boolean { - return false - } +// @SuppressWarnings("unused") +// protected fun removeEldestEntry(eldest: Entry): Boolean { +// return false +// } private fun recordAccess(entry: ChainEntry) { - if (accessOrder) { - // Move to the tail of the chain on access. - entry.remove() - entry.addToEnd() - } +// if (accessOrder) { +// // Move to the tail of the chain on access. +// entry.remove() +// entry.addToEnd() +// } } } diff --git a/js/js.libraries/src/core/collections/LinkedHashSet.kt b/js/js.libraries/src/core/collections/LinkedHashSet.kt index d24ba8e9d7c..4ef18eb3d86 100644 --- a/js/js.libraries/src/core/collections/LinkedHashSet.kt +++ b/js/js.libraries/src/core/collections/LinkedHashSet.kt @@ -28,7 +28,6 @@ open class LinkedHashSet : HashSet { addAll(c) } - constructor(capacity: Int) : super(LinkedHashMap(capacity)) constructor(capacity: Int, loadFactor: Float = 0.0f) : super(LinkedHashMap(capacity, loadFactor)) // public override fun clone(): Any { diff --git a/js/js.libraries/src/core/core.kt b/js/js.libraries/src/core/core.kt index cc9214b3d82..4eafffa68d7 100644 --- a/js/js.libraries/src/core/core.kt +++ b/js/js.libraries/src/core/core.kt @@ -40,3 +40,6 @@ public fun js(code: String): dynamic = noImpl * Function corresponding to JavaScript's `typeof` operator */ public inline fun jsTypeOf(a: Any?): String = js("typeof a") + +@library +internal fun deleteProperty(`object`: Any, property: Any): Unit = noImpl \ No newline at end of file diff --git a/js/js.translator/testData/kotlin_lib.js b/js/js.translator/testData/kotlin_lib.js index 75a453236f3..74dabdd0b08 100644 --- a/js/js.translator/testData/kotlin_lib.js +++ b/js/js.translator/testData/kotlin_lib.js @@ -902,6 +902,10 @@ return new Kotlin.ArrayIterator(array); }; + Kotlin.deleteProperty = function (object, property) { + delete object[property]; + }; + Kotlin.jsonAddProperties = function (obj1, obj2) { for (var p in obj2) { if (obj2.hasOwnProperty(p)) {