JS: fixed <Type>Array.iterator methods; added -Xtypedarray compiler key

The <Type>Array.iterator used to lack next<Type>() method (KT-16626).

The -Xtypedarray compiler key enables translation of primitive arrays
to TypedArrays, and primitive array`is`-checks (KT-15358, KT-14007,
KT-14614, KT-16056).
This commit is contained in:
Anton Bannykh
2017-01-31 17:16:50 +03:00
parent ba5e78a917
commit 9b34e21619
45 changed files with 1668 additions and 473 deletions
+47 -15
View File
@@ -19,22 +19,54 @@
external private fun <T> Array(size: Int): Array<T>
@JsName("newArray")
fun <T> newArray(size: Int, initValue: T): Array<T> {
return fillArray(Array(size), initValue)
}
private fun <T> fillArray(array: Array<T>, value: T): Array<T> {
for (i in 0..array.size - 1) {
array[i] = value
}
return array;
}
fun <T> newArray(size: Int, initValue: T) = fillArrayVal(Array<T>(size), initValue)
@JsName("newArrayF")
fun <T> arrayWithFun(size: Int, init: (Int) -> T): Array<T> {
var result = Array<T>(size)
for (i in 0..size - 1) {
result[i] = init(i)
fun <T> arrayWithFun(size: Int, init: (Int) -> T) = fillArrayFun(Array<T>(size), init)
@JsName("fillArray")
fun <T> fillArrayFun(array: Array<T>, init: (Int) -> T): Array<T> {
for (i in 0..array.size - 1) {
array[i] = init(i)
}
return result
return array
}
@JsName("booleanArray")
fun booleanArray(size: Int, init: dynamic): Array<Boolean> {
val result: dynamic = Array<Boolean>(size)
result.`$type$` = "BooleanArray"
return when (init) {
null, true -> fillArrayVal(result, false)
false -> result
else -> fillArrayFun<Boolean>(result, init)
}
}
@JsName("charArray")
fun charArray(size: Int, init: dynamic): Array<Char> {
val result = js("new Uint16Array(size)")
result.`$type$` = "CharArray"
return when (init) {
null, true, false -> result // For consistency
else -> fillArrayFun<Char>(result, init)
}
}
@JsName("longArray")
fun longArray(size: Int, init: dynamic): Array<Long> {
val result: dynamic = Array<Long>(size)
result.`$type$` = "LongArray"
return when (init) {
null, true -> fillArrayVal(result, 0L)
false -> result
else -> fillArrayFun<Long>(result, init)
}
}
private fun <T> fillArrayVal(array: Array<T>, initValue: T): Array<T> {
for (i in 0..array.size - 1) {
array[i] = initValue
}
return array
}
+129 -22
View File
@@ -15,21 +15,80 @@
*/
@JsName("arrayIterator")
internal fun arrayIterator(array: dynamic): MutableIterator<dynamic> {
return object : MutableIterator<dynamic> {
var index = 0;
override fun hasNext(): Boolean {
val length: Int = array.length
return index < length
}
override fun next() = array[index++]
override fun remove() {
array.splice(--index, 1)
internal fun arrayIterator(array: dynamic, type: String?) = when (type) {
null -> {
val arr: Array<dynamic> = array
object : Iterator<dynamic> {
var index = 0
override fun hasNext() = index < arr.size
override fun next() = if (index < arr.size) arr[index++] else throw IndexOutOfBoundsException("$index")
}
}
"BooleanArray" -> booleanArrayIterator(array)
"ByteArray" -> byteArrayIterator(array)
"ShortArray" -> shortArrayIterator(array)
"CharArray" -> charArrayIterator(array)
"IntArray" -> intArrayIterator(array)
"LongArray" -> longArrayIterator(array)
"FloatArray" -> floatArrayIterator(array)
"DoubleArray" -> doubleArrayIterator(array)
else -> throw IllegalStateException("Unsupported type argument for arrayIterator: $type")
}
@JsName("booleanArrayIterator")
internal fun booleanArrayIterator(array: BooleanArray) = object : BooleanIterator() {
var index = 0
override fun hasNext() = index < array.size
override fun nextBoolean() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index")
}
@JsName("byteArrayIterator")
internal fun byteArrayIterator(array: ByteArray) = object : ByteIterator() {
var index = 0
override fun hasNext() = index < array.size
override fun nextByte() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index")
}
@JsName("shortArrayIterator")
internal fun shortArrayIterator(array: ShortArray) = object : ShortIterator() {
var index = 0
override fun hasNext() = index < array.size
override fun nextShort() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index")
}
@JsName("charArrayIterator")
internal fun charArrayIterator(array: CharArray) = object : CharIterator() {
var index = 0
override fun hasNext() = index < array.size
override fun nextChar() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index")
}
@JsName("intArrayIterator")
internal fun intArrayIterator(array: IntArray) = object : IntIterator() {
var index = 0
override fun hasNext() = index < array.size
override fun nextInt() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index")
}
@JsName("floatArrayIterator")
internal fun floatArrayIterator(array: FloatArray) = object : FloatIterator() {
var index = 0
override fun hasNext() = index < array.size
override fun nextFloat() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index")
}
@JsName("doubleArrayIterator")
internal fun doubleArrayIterator(array: DoubleArray) = object : DoubleIterator() {
var index = 0
override fun hasNext() = index < array.size
override fun nextDouble() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index")
}
@JsName("longArrayIterator")
internal fun longArrayIterator(array: LongArray) = object : LongIterator() {
var index = 0
override fun hasNext() = index < array.size
override fun nextLong() = if (index < array.size) array[index++] else throw IndexOutOfBoundsException("$index")
}
@JsName("PropertyMetadata")
@@ -96,23 +155,71 @@ internal class BoxedChar(val c: Char) : Comparable<Char> {
}
}
/* For future binary compatibility with TypedArrays
* TODO: concat normal Array's and TypedArrays into an Array
internal inline fun <T> concat(args: Array<T>): T {
val typed = js("Array")(args.size)
for (i in 0..args.size - 1) {
val arr = args[i]
if (arr !is Array<*>) {
typed[i] = js("[]").slice.call(arr)
}
else {
typed[i] = arr
}
}
return js("[]").concat.apply(js("[]"), typed);
}
/** Concat regular Array's and TypedArray's into an Array.
*/
@PublishedApi
@JsName("arrayConcat")
internal fun <T> arrayConcat(a: T, b: T): T {
return a.asDynamic().concat.apply(js("[]"), js("arguments"));
return concat(js("arguments"))
}
/* For future binary compatibility with TypedArrays
* TODO: concat primitive arrays.
* For Byte-, Short-, Int-, Float-, and DoubleArray concat result into a TypedArray.
* For Boolean-, Char-, and LongArray return an Array with corresponding type property.
* Default to Array.prototype.concat for compatibility.
/** Concat primitive arrays. Main use: prepare vararg arguments.
* For compatibility with 1.1.0 the arguments may be a mixture of Array's and TypedArray's.
*
* If the first argument is TypedArray (Byte-, Short-, Char-, Int-, Float-, and DoubleArray) returns a TypedArray, otherwise an Array.
* If the first argument has the $type$ property (Boolean-, Char-, and LongArray) copy its value to result.$type$.
* If the first argument is a regular Array without the $type$ property default to arrayConcat.
*/
@PublishedApi
@JsName("primitiveArrayConcat")
internal fun <T> primitiveArrayConcat(a: T, b: T): T {
return a.asDynamic().concat.apply(js("[]"), js("arguments"));
val args: Array<T> = js("arguments")
if (a is Array<*> && a.asDynamic().`$type$` === undefined) {
return concat(args)
}
else {
var size = 0
for (i in 0..args.size - 1) {
size += args[i].asDynamic().length as Int
}
val result = js("new a.constructor(size)")
kotlin.copyArrayType(a, result)
size = 0
for (i in 0..args.size - 1) {
val arr = args[i].asDynamic()
for (j in 0..arr.length - 1) {
result[size++] = arr[j]
}
}
return result
}
}
@JsName("booleanArrayOf")
internal fun booleanArrayOf() = withType("BooleanArray", js("[].slice.call(arguments)"))
@JsName("charArrayOf") // The arguments have to be slice'd here because of Rhino (see KT-16974)
internal fun charArrayOf() = withType("CharArray", js("new Uint16Array([].slice.call(arguments))"))
@JsName("longArrayOf")
internal fun longArrayOf() = withType("LongArray", js("[].slice.call(arguments)"))
@JsName("withType")
internal inline fun withType(type: String, array: dynamic): dynamic {
array.`$type$` = type
return array
}
+1 -1
View File
@@ -49,7 +49,7 @@ public operator fun dynamic.iterator(): Iterator<dynamic> {
return when {
this["iterator"] != null ->
this["iterator"]()
js("Array").isArray(r) ->
js("Kotlin").isArrayish(r) ->
r.unsafeCast<Array<*>>().iterator()
else ->
+32 -37
View File
@@ -10,6 +10,7 @@ package kotlin.collections
import kotlin.js.*
import primitiveArrayConcat
import withType
import kotlin.comparisons.*
/**
@@ -13011,9 +13012,8 @@ public inline fun IntArray.copyOf(): IntArray {
/**
* Returns new array which is a copy of the original array.
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun LongArray.copyOf(): LongArray {
return this.asDynamic().slice()
public fun LongArray.copyOf(): LongArray {
return withType("LongArray", this.asDynamic().slice())
}
/**
@@ -13035,73 +13035,71 @@ public inline fun DoubleArray.copyOf(): DoubleArray {
/**
* Returns new array which is a copy of the original array.
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun BooleanArray.copyOf(): BooleanArray {
return this.asDynamic().slice()
public fun BooleanArray.copyOf(): BooleanArray {
return withType("BooleanArray", this.asDynamic().slice())
}
/**
* Returns new array which is a copy of the original array.
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun CharArray.copyOf(): CharArray {
return this.asDynamic().slice()
public fun CharArray.copyOf(): CharArray {
return withType("CharArray", this.asDynamic().slice())
}
/**
* Returns new array which is a copy of the original array, resized to the given [newSize].
*/
public fun ByteArray.copyOf(newSize: Int): ByteArray {
return arrayCopyResize(this, newSize, 0)
return fillFrom(this, ByteArray(newSize))
}
/**
* Returns new array which is a copy of the original array, resized to the given [newSize].
*/
public fun ShortArray.copyOf(newSize: Int): ShortArray {
return arrayCopyResize(this, newSize, 0)
return fillFrom(this, ShortArray(newSize))
}
/**
* Returns new array which is a copy of the original array, resized to the given [newSize].
*/
public fun IntArray.copyOf(newSize: Int): IntArray {
return arrayCopyResize(this, newSize, 0)
return fillFrom(this, IntArray(newSize))
}
/**
* Returns new array which is a copy of the original array, resized to the given [newSize].
*/
public fun LongArray.copyOf(newSize: Int): LongArray {
return arrayCopyResize(this, newSize, 0L)
return withType("LongArray", arrayCopyResize(this, newSize, 0L))
}
/**
* Returns new array which is a copy of the original array, resized to the given [newSize].
*/
public fun FloatArray.copyOf(newSize: Int): FloatArray {
return arrayCopyResize(this, newSize, 0.0f)
return fillFrom(this, FloatArray(newSize))
}
/**
* Returns new array which is a copy of the original array, resized to the given [newSize].
*/
public fun DoubleArray.copyOf(newSize: Int): DoubleArray {
return arrayCopyResize(this, newSize, 0.0)
return fillFrom(this, DoubleArray(newSize))
}
/**
* Returns new array which is a copy of the original array, resized to the given [newSize].
*/
public fun BooleanArray.copyOf(newSize: Int): BooleanArray {
return arrayCopyResize(this, newSize, false)
return withType("BooleanArray", arrayCopyResize(this, newSize, false))
}
/**
* Returns new array which is a copy of the original array, resized to the given [newSize].
*/
public fun CharArray.copyOf(newSize: Int): CharArray {
return arrayCopyResize(this, newSize, 0)
return withType("CharArray", fillFrom(this, CharArray(newSize)))
}
/**
@@ -13146,9 +13144,8 @@ public inline fun IntArray.copyOfRange(fromIndex: Int, toIndex: Int): IntArray {
/**
* Returns new array which is a copy of range of original array.
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun LongArray.copyOfRange(fromIndex: Int, toIndex: Int): LongArray {
return this.asDynamic().slice(fromIndex, toIndex)
public fun LongArray.copyOfRange(fromIndex: Int, toIndex: Int): LongArray {
return withType("LongArray", this.asDynamic().slice(fromIndex, toIndex))
}
/**
@@ -13170,17 +13167,15 @@ public inline fun DoubleArray.copyOfRange(fromIndex: Int, toIndex: Int): DoubleA
/**
* Returns new array which is a copy of range of original array.
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun BooleanArray.copyOfRange(fromIndex: Int, toIndex: Int): BooleanArray {
return this.asDynamic().slice(fromIndex, toIndex)
public fun BooleanArray.copyOfRange(fromIndex: Int, toIndex: Int): BooleanArray {
return withType("BooleanArray", this.asDynamic().slice(fromIndex, toIndex))
}
/**
* Returns new array which is a copy of range of original array.
*/
@Suppress("NOTHING_TO_INLINE")
public inline fun CharArray.copyOfRange(fromIndex: Int, toIndex: Int): CharArray {
return this.asDynamic().slice(fromIndex, toIndex)
public fun CharArray.copyOfRange(fromIndex: Int, toIndex: Int): CharArray {
return withType("CharArray", this.asDynamic().slice(fromIndex, toIndex))
}
/**
@@ -13266,21 +13261,21 @@ public operator fun <T> Array<out T>.plus(elements: Collection<T>): Array<T> {
* Returns an array containing all elements of the original array and then all elements of the given [elements] collection.
*/
public operator fun ByteArray.plus(elements: Collection<Byte>): ByteArray {
return arrayPlusCollection(this, elements)
return fillFromCollection(this.copyOf(size + elements.size), this.size, elements)
}
/**
* Returns an array containing all elements of the original array and then all elements of the given [elements] collection.
*/
public operator fun ShortArray.plus(elements: Collection<Short>): ShortArray {
return arrayPlusCollection(this, elements)
return fillFromCollection(this.copyOf(size + elements.size), this.size, elements)
}
/**
* Returns an array containing all elements of the original array and then all elements of the given [elements] collection.
*/
public operator fun IntArray.plus(elements: Collection<Int>): IntArray {
return arrayPlusCollection(this, elements)
return fillFromCollection(this.copyOf(size + elements.size), this.size, elements)
}
/**
@@ -13294,14 +13289,14 @@ public operator fun LongArray.plus(elements: Collection<Long>): LongArray {
* Returns an array containing all elements of the original array and then all elements of the given [elements] collection.
*/
public operator fun FloatArray.plus(elements: Collection<Float>): FloatArray {
return arrayPlusCollection(this, elements)
return fillFromCollection(this.copyOf(size + elements.size), this.size, elements)
}
/**
* Returns an array containing all elements of the original array and then all elements of the given [elements] collection.
*/
public operator fun DoubleArray.plus(elements: Collection<Double>): DoubleArray {
return arrayPlusCollection(this, elements)
return fillFromCollection(this.copyOf(size + elements.size), this.size, elements)
}
/**
@@ -13315,7 +13310,7 @@ public operator fun BooleanArray.plus(elements: Collection<Boolean>): BooleanArr
* Returns an array containing all elements of the original array and then all elements of the given [elements] collection.
*/
public operator fun CharArray.plus(elements: Collection<Char>): CharArray {
return arrayPlusCollection(this, elements)
return fillFromCollection(this.copyOf(size + elements.size), this.size, elements)
}
/**
@@ -13474,21 +13469,21 @@ public fun <T> Array<out T>.sortWith(comparator: Comparator<in T>): Unit {
* Returns a *typed* object array containing all of the elements of this primitive array.
*/
public fun ByteArray.toTypedArray(): Array<Byte> {
return copyOf().unsafeCast<Array<Byte>>()
return js("[]").slice.call(this)
}
/**
* Returns a *typed* object array containing all of the elements of this primitive array.
*/
public fun ShortArray.toTypedArray(): Array<Short> {
return copyOf().unsafeCast<Array<Short>>()
return js("[]").slice.call(this)
}
/**
* Returns a *typed* object array containing all of the elements of this primitive array.
*/
public fun IntArray.toTypedArray(): Array<Int> {
return copyOf().unsafeCast<Array<Int>>()
return js("[]").slice.call(this)
}
/**
@@ -13502,14 +13497,14 @@ public fun LongArray.toTypedArray(): Array<Long> {
* Returns a *typed* object array containing all of the elements of this primitive array.
*/
public fun FloatArray.toTypedArray(): Array<Float> {
return copyOf().unsafeCast<Array<Float>>()
return js("[]").slice.call(this)
}
/**
* Returns a *typed* object array containing all of the elements of this primitive array.
*/
public fun DoubleArray.toTypedArray(): Array<Double> {
return copyOf().unsafeCast<Array<Double>>()
return js("[]").slice.call(this)
}
/**
+23
View File
@@ -71,8 +71,18 @@ internal fun <T> arrayOfNulls(reference: Array<out T>, size: Int): Array<T> {
return arrayOfNulls<Any>(size).unsafeCast<Array<T>>()
}
internal fun fillFrom(src: dynamic, dst: dynamic): dynamic {
val srcLen: Int = src.length
val dstLen: Int = dst.length
var index: Int = 0
while (index < srcLen && index < dstLen) dst[index] = src[index++]
return dst
}
internal fun arrayCopyResize(source: dynamic, newSize: Int, defaultValue: Any?): dynamic {
val result = source.slice(0, newSize)
copyArrayType(source, result)
var index: Int = source.length
if (newSize > index) {
result.length = newSize
@@ -84,11 +94,24 @@ internal fun arrayCopyResize(source: dynamic, newSize: Int, defaultValue: Any?):
internal fun <T> arrayPlusCollection(array: dynamic, collection: Collection<T>): dynamic {
val result = array.slice()
result.length += collection.size
copyArrayType(array, result)
var index: Int = array.length
for (element in collection) result[index++] = element
return result
}
internal fun <T> fillFromCollection(dst: dynamic, startIndex: Int, collection: Collection<T>): dynamic {
var index = startIndex
for (element in collection) dst[index++] = element
return dst
}
internal inline fun copyArrayType(from: dynamic, to: dynamic) {
if (from.`$type$` !== undefined) {
to.`$type$` = from.`$type$`
}
}
// no singleton map implementation in js, return map as is
internal inline fun <K, V> Map<K, V>.toSingletonMapOrSelf(): Map<K, V> = this
+54 -9
View File
@@ -14,21 +14,65 @@
* limitations under the License.
*/
Kotlin.isBooleanArray = function (a) {
return (Array.isArray(a) || a instanceof Int8Array) && a.$type$ === "BooleanArray"
};
Kotlin.isByteArray = function (a) {
return a instanceof Int8Array && a.$type$ !== "BooleanArray"
};
Kotlin.isShortArray = function (a) {
return a instanceof Int16Array
};
Kotlin.isCharArray = function (a) {
return a instanceof Uint16Array && a.$type$ === "CharArray"
};
Kotlin.isIntArray = function (a) {
return a instanceof Int32Array
};
Kotlin.isFloatArray = function (a) {
return a instanceof Float32Array
};
Kotlin.isDoubleArray = function (a) {
return a instanceof Float64Array
};
Kotlin.isLongArray = function (a) {
return Array.isArray(a) && a.$type$ === "LongArray"
};
Kotlin.isArray = function (a) {
return Array.isArray(a) && !a.$type$;
};
Kotlin.isArrayish = function (a) {
return Array.isArray(a) || ArrayBuffer.isView(a)
};
Kotlin.arrayToString = function (a) {
return "[" + a.map(Kotlin.toString).join(", ") + "]";
};
Kotlin.arrayDeepToString = function (a, visited) {
visited = visited || [a];
return "[" + a.map(function(e) {
if (Array.isArray(e) && visited.indexOf(e) < 0) {
var toString = Kotlin.toString;
if (Kotlin.isCharArray(a)) {
toString = String.fromCharCode;
}
return "[" + a.map(function (e) {
if (Kotlin.isArrayish(e) && visited.indexOf(e) < 0) {
visited.push(e);
var result = Kotlin.arrayDeepToString(e, visited);
visited.pop();
return result;
}
else {
return Kotlin.toString(e);
return toString(e);
}
}).join(", ") + "]";
};
@@ -37,7 +81,7 @@ Kotlin.arrayEquals = function (a, b) {
if (a === b) {
return true;
}
if (!Array.isArray(b) || a.length !== b.length) {
if (!Kotlin.isArrayish(b) || a.length !== b.length) {
return false;
}
@@ -53,16 +97,17 @@ Kotlin.arrayDeepEquals = function (a, b) {
if (a === b) {
return true;
}
if (!Array.isArray(b) || a.length !== b.length) {
if (!Kotlin.isArrayish(b) || a.length !== b.length) {
return false;
}
for (var i = 0, n = a.length; i < n; i++) {
if (Array.isArray(a[i])) {
if (Kotlin.isArrayish(a[i])) {
if (!Kotlin.arrayDeepEquals(a[i], b[i])) {
return false;
}
} else if (!Kotlin.equals(a[i], b[i])) {
}
else if (!Kotlin.equals(a[i], b[i])) {
return false;
}
}
@@ -81,11 +126,11 @@ Kotlin.arrayDeepHashCode = function (arr) {
var result = 1;
for (var i = 0, n = arr.length; i < n; i++) {
var e = arr[i];
result = ((31 * result | 0) + (Array.isArray(e) ? Kotlin.arrayDeepHashCode(e) : Kotlin.hashCode(e))) | 0;
result = ((31 * result | 0) + (Kotlin.isArrayish(e) ? Kotlin.arrayDeepHashCode(e) : Kotlin.hashCode(e))) | 0;
}
return result;
};
Kotlin.primitiveArraySort = function(array) {
Kotlin.primitiveArraySort = function (array) {
array.sort(Kotlin.primitiveCompareTo)
};
+1 -1
View File
@@ -59,7 +59,7 @@ Kotlin.toString = function (o) {
if (o == null) {
return "null";
}
else if (Array.isArray(o)) {
else if (Kotlin.isArrayish(o)) {
return "[...]";
}
else {