From 9b34e21619364f89dd5ad4a9bc22ef9bb1d52e61 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Tue, 31 Jan 2017 17:16:50 +0300 Subject: [PATCH] JS: fixed Array.iterator methods; added -Xtypedarray compiler key The Array.iterator used to lack next() 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). --- .../arguments/K2JSCompilerArguments.java | 4 + .../jetbrains/kotlin/cli/js/K2JSCompiler.java | 4 + compiler/testData/cli/js/jsExtraHelp.out | 1 + .../codegen/box/arrays/arrayInstanceOf.kt | 3 +- .../box/arrays/iteratorByteArrayNextByte.kt | 3 - .../box/arrays/iteratorLongArrayNextLong.kt | 3 - .../testData/codegen/box/arrays/kt2997.kt | 4 +- .../testData/codegen/box/arrays/kt7288.kt | 2 + .../codegen/box/arrays/primitiveArrays.kt | 196 ++++++ .../box/ranges/forInIndices/kt13241_Array.kt | 1 + .../light-analysis/arrays/primitiveArrays.txt | 22 + .../ir/IrBlackBoxCodegenTestGenerated.java | 6 + .../codegen/BlackBoxCodegenTestGenerated.java | 6 + .../LightAnalysisModeTestGenerated.java | 6 + .../kotlin/builtins/KotlinBuiltIns.java | 14 + .../kotlin/generators/tests/GenerateTests.kt | 4 + .../kotlin/js/config/JSConfigurationKeys.java | 4 + js/js.libraries/src/builtins/arrayUtils.kt | 62 +- js/js.libraries/src/core/builtins.kt | 151 ++++- js/js.libraries/src/core/dynamic.kt | 2 +- .../src/core/generated/_ArraysJs.kt | 69 +-- js/js.libraries/src/core/kotlin.kt | 23 + js/js.libraries/src/js/arrayUtils.js | 63 +- js/js.libraries/src/js/core.js | 2 +- .../jetbrains/kotlin/js/test/BasicBoxTest.kt | 7 +- .../kotlin/js/test/rhino/RhinoUtils.java | 1 + .../semantics/JsCodegenBoxTestGenerated.java | 82 +-- .../JsTypedArraysBoxTestGenerated.java | 557 ++++++++++++++++++ .../abstractClassesForGeneratedTests.kt | 6 + .../kotlin/js/translate/context/Namer.java | 11 + .../js/translate/expression/LoopTranslator.kt | 4 +- .../expression/PatternTranslator.java | 22 +- .../functions/factories/ArrayFIF.java | 139 ----- .../intrinsic/functions/factories/ArrayFIF.kt | 162 +++++ .../functions/factories/CompositeFIF.java | 4 - .../functions/factories/TopLevelFIF.java | 15 +- .../reference/CallArgumentTranslator.kt | 153 ++--- .../kotlin/js/translate/utils/JsAstUtils.java | 15 +- js/js.translator/testData/rhino-polyfills.js | 52 ++ js/js.translator/testData/test.html | 4 +- .../stdlib/test/collections/ArraysTest.kt | 9 +- .../kotlin/gradle/dsl/KotlinJsOptions.kt | 8 +- .../kotlin/gradle/dsl/KotlinJsOptionsBase.kt | 9 +- .../src/generators/GenerateStandardLib.kt | 1 + .../src/templates/SpecialJVM.kt | 225 ++++--- 45 files changed, 1668 insertions(+), 473 deletions(-) create mode 100644 compiler/testData/codegen/box/arrays/primitiveArrays.kt create mode 100644 compiler/testData/codegen/light-analysis/arrays/primitiveArrays.txt create mode 100644 js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsTypedArraysBoxTestGenerated.java delete mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.java create mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.kt create mode 100644 js/js.translator/testData/rhino-polyfills.js diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java index 7162817cfba..00cbf9a3666 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java @@ -71,6 +71,10 @@ public class K2JSCompilerArguments extends CommonCompilerArguments { @ValueDescription("") public String outputPostfix; + @GradleOption(DefaultValues.BooleanFalseDefault.class) + @Argument(value = "Xtypedarrays", description = "Translate primitive arrays to JS typed arrays") + public boolean typedArrays; + @NotNull public static K2JSCompilerArguments createDefaultInstance() { K2JSCompilerArguments arguments = new K2JSCompilerArguments(); diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java b/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java index 60947e3b19c..5b616e5579b 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java @@ -286,6 +286,10 @@ public class K2JSCompiler extends CLICompiler { ContainerUtil.addAllNotNull(libraries, arguments.libraries.split(File.pathSeparator)); } + if (arguments.typedArrays) { + configuration.put(JSConfigurationKeys.TYPED_ARRAYS_ENABLED, true); + } + configuration.put(JSConfigurationKeys.LIBRARIES, libraries); String moduleKindName = arguments.moduleKind; diff --git a/compiler/testData/cli/js/jsExtraHelp.out b/compiler/testData/cli/js/jsExtraHelp.out index b6a25f8c5e0..88b73eace14 100644 --- a/compiler/testData/cli/js/jsExtraHelp.out +++ b/compiler/testData/cli/js/jsExtraHelp.out @@ -1,5 +1,6 @@ Usage: kotlinc-js where advanced options include: + -Xtypedarrays Translate primitive arrays to JS typed arrays -Xno-inline Disable method inlining -Xrepeat Repeat compilation (for performance analysis) -Xskip-metadata-version-check Load classes with bad metadata version anyway (incl. pre-release classes) diff --git a/compiler/testData/codegen/box/arrays/arrayInstanceOf.kt b/compiler/testData/codegen/box/arrays/arrayInstanceOf.kt index d5323fbbdbb..384bd293741 100644 --- a/compiler/testData/codegen/box/arrays/arrayInstanceOf.kt +++ b/compiler/testData/codegen/box/arrays/arrayInstanceOf.kt @@ -1,5 +1,4 @@ -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS +// IGNORE_BACKEND_WITHOUT_CHECK: JS //test [], get and iterator calls fun test(createIntNotLong: Boolean): String { diff --git a/compiler/testData/codegen/box/arrays/iteratorByteArrayNextByte.kt b/compiler/testData/codegen/box/arrays/iteratorByteArrayNextByte.kt index 19bf8463026..61c458433a7 100644 --- a/compiler/testData/codegen/box/arrays/iteratorByteArrayNextByte.kt +++ b/compiler/testData/codegen/box/arrays/iteratorByteArrayNextByte.kt @@ -1,6 +1,3 @@ -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - fun box(): String { val a = ByteArray(5) val x = a.iterator() diff --git a/compiler/testData/codegen/box/arrays/iteratorLongArrayNextLong.kt b/compiler/testData/codegen/box/arrays/iteratorLongArrayNextLong.kt index 74bf8c9f4a4..cc9cec5d570 100644 --- a/compiler/testData/codegen/box/arrays/iteratorLongArrayNextLong.kt +++ b/compiler/testData/codegen/box/arrays/iteratorLongArrayNextLong.kt @@ -1,6 +1,3 @@ -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - fun box(): String { val a = LongArray(5) val x = a.iterator() diff --git a/compiler/testData/codegen/box/arrays/kt2997.kt b/compiler/testData/codegen/box/arrays/kt2997.kt index 6ccce13d01d..01fb0d954b2 100644 --- a/compiler/testData/codegen/box/arrays/kt2997.kt +++ b/compiler/testData/codegen/box/arrays/kt2997.kt @@ -1,6 +1,6 @@ //KT-2997 Automatically cast error (Array) -// IGNORE_BACKEND: JS -// Unmute when JS implements primitive arrays via TypeArray +// IGNORE_BACKEND_WITHOUT_CHECK: JS +// Unmute when JS implements primitive arrays via TypedArray fun foo(a: Any): Int { if (a is IntArray) { diff --git a/compiler/testData/codegen/box/arrays/kt7288.kt b/compiler/testData/codegen/box/arrays/kt7288.kt index 12cd2e6c591..81671432759 100644 --- a/compiler/testData/codegen/box/arrays/kt7288.kt +++ b/compiler/testData/codegen/box/arrays/kt7288.kt @@ -1,3 +1,5 @@ +// IGNORE_BACKEND_WITHOUT_CHECK: JS + fun test(b: Boolean): String { val a = if (b) IntArray(5) else LongArray(5) if (a is IntArray) { diff --git a/compiler/testData/codegen/box/arrays/primitiveArrays.kt b/compiler/testData/codegen/box/arrays/primitiveArrays.kt new file mode 100644 index 00000000000..769610cb168 --- /dev/null +++ b/compiler/testData/codegen/box/arrays/primitiveArrays.kt @@ -0,0 +1,196 @@ +// WITH_RUNTIME + +import kotlin.test.assertTrue +import kotlin.test.assertFalse +import kotlin.test.assertEquals +import kotlin.test.assertFails + +fun box(): String { + assertTrue(eqBoolean(booleanArrayOf(false), BooleanArray(1))) + assertTrue(eqBoolean(booleanArrayOf(false, true, false), BooleanArray(3) { it % 2 != 0 })) + assertTrue(eqBoolean(booleanArrayOf(true), booleanArrayOf(true).copyOf())) + assertTrue(eqBoolean(booleanArrayOf(true, false), booleanArrayOf(true).copyOf(2))) + assertTrue(eqBoolean(booleanArrayOf(true), booleanArrayOf(true, true).copyOf(1))) + assertTrue(eqBoolean(booleanArrayOf(false, true), booleanArrayOf(false) + true)) + assertTrue(eqBoolean(booleanArrayOf(false, true, false), booleanArrayOf(false) + listOf(true, false))) + assertTrue(eqBoolean(booleanArrayOf(true, false), booleanArrayOf(false, true, false, true).copyOfRange(1, 3))) + assertTrue(eqBoolean(booleanArrayOf(false, true, false, true), customBooleanArrayOf(false, *booleanArrayOf(true, false), true))) + assertTrue(booleanArrayOf(true).iterator() is BooleanIterator) + assertEquals(true, booleanArrayOf(true).iterator().nextBoolean()) + assertEquals(true, booleanArrayOf(true).iterator().next()) + assertFalse(booleanArrayOf().iterator().hasNext()) + assertTrue(assertFails { booleanArrayOf().iterator().next() } is IndexOutOfBoundsException) + + assertTrue(eqByte(byteArrayOf(0), ByteArray(1))) + assertTrue(eqByte(byteArrayOf(1, 2, 3), ByteArray(3) { (it + 1).toByte() })) + assertTrue(eqByte(byteArrayOf(1), byteArrayOf(1).copyOf())) + assertTrue(eqByte(byteArrayOf(1, 0), byteArrayOf(1).copyOf(2))) + assertTrue(eqByte(byteArrayOf(1), byteArrayOf(1, 2).copyOf(1))) + assertTrue(eqByte(byteArrayOf(1, 2), byteArrayOf(1) + 2)) + assertTrue(eqByte(byteArrayOf(1, 2, 3), byteArrayOf(1) + listOf(2.toByte(), 3.toByte()))) + assertTrue(eqByte(byteArrayOf(2, 3), byteArrayOf(1, 2, 3, 4).copyOfRange(1, 3))) + assertTrue(eqByte(byteArrayOf(1, 2, 3, 4), customByteArrayOf(1.toByte(), *byteArrayOf(2, 3), 4.toByte()))) + assertTrue(byteArrayOf(1).iterator() is ByteIterator) + assertEquals(1, byteArrayOf(1).iterator().nextByte()) + assertEquals(1, byteArrayOf(1).iterator().next()) + assertFalse(byteArrayOf().iterator().hasNext()) + assertTrue(assertFails { byteArrayOf().iterator().next() } is IndexOutOfBoundsException) + + assertTrue(eqShort(shortArrayOf(0), ShortArray(1))) + assertTrue(eqShort(shortArrayOf(1, 2, 3), ShortArray(3) { (it + 1).toShort() })) + assertTrue(eqShort(shortArrayOf(1), shortArrayOf(1).copyOf())) + assertTrue(eqShort(shortArrayOf(1, 0), shortArrayOf(1).copyOf(2))) + assertTrue(eqShort(shortArrayOf(1), shortArrayOf(1, 2).copyOf(1))) + assertTrue(eqShort(shortArrayOf(1, 2), shortArrayOf(1) + 2)) + assertTrue(eqShort(shortArrayOf(1, 2, 3), shortArrayOf(1) + listOf(2.toShort(), 3.toShort()))) + assertTrue(eqShort(shortArrayOf(2, 3), shortArrayOf(1, 2, 3, 4).copyOfRange(1, 3))) + assertTrue(eqShort(shortArrayOf(1, 2, 3, 4), customShortArrayOf(1.toShort(), *shortArrayOf(2, 3), 4.toShort()))) + assertTrue(shortArrayOf(1).iterator() is ShortIterator) + assertEquals(1, shortArrayOf(1).iterator().nextShort()) + assertEquals(1, shortArrayOf(1).iterator().next()) + assertFalse(shortArrayOf().iterator().hasNext()) + assertTrue(assertFails { shortArrayOf().iterator().next() } is IndexOutOfBoundsException) + + assertTrue(eqChar(charArrayOf(0.toChar()), CharArray(1))) + assertTrue(eqChar(charArrayOf('a', 'b', 'c'), CharArray(3) { 'a' + it })) + assertTrue(eqChar(charArrayOf('a'), charArrayOf('a').copyOf())) + assertTrue(eqChar(charArrayOf('a', 0.toChar()), charArrayOf('a').copyOf(2))) + assertTrue(eqChar(charArrayOf('a'), charArrayOf('a', 'b').copyOf(1))) + assertTrue(eqChar(charArrayOf('a', 'b'), charArrayOf('a') + 'b')) + assertTrue(eqChar(charArrayOf('a', 'b', 'c'), charArrayOf('a') + listOf('b', 'c'))) + assertTrue(eqChar(charArrayOf('b', 'c'), charArrayOf('a', 'b', 'c', 'd').copyOfRange(1, 3))) + assertTrue(eqChar(charArrayOf('a', 'b', 'c', 'd'), customCharArrayOf('a', *charArrayOf('b', 'c'), 'd'))) + assertTrue(charArrayOf('a').iterator() is CharIterator) + assertEquals('a', charArrayOf('a').iterator().nextChar()) + assertEquals('a', charArrayOf('a').iterator().next()) + assertFalse(charArrayOf().iterator().hasNext()) + assertTrue(assertFails { charArrayOf().iterator().next() } is IndexOutOfBoundsException) + + assertTrue(eqInt(intArrayOf(0), IntArray(1))) + assertTrue(eqInt(intArrayOf(1, 2, 3), IntArray(3) { it + 1 })) + assertTrue(eqInt(intArrayOf(1), intArrayOf(1).copyOf())) + assertTrue(eqInt(intArrayOf(1, 0), intArrayOf(1).copyOf(2))) + assertTrue(eqInt(intArrayOf(1), intArrayOf(1, 2).copyOf(1))) + assertTrue(eqInt(intArrayOf(1, 2), intArrayOf(1) + 2)) + assertTrue(eqInt(intArrayOf(1, 2, 3), intArrayOf(1) + listOf(2, 3))) + assertTrue(eqInt(intArrayOf(2, 3), intArrayOf(1, 2, 3, 4).copyOfRange(1, 3))) + assertTrue(eqInt(intArrayOf(1, 2, 3, 4), customIntArrayOf(1, *intArrayOf(2, 3), 4))) + assertTrue(intArrayOf(1).iterator() is IntIterator) + assertEquals(1, intArrayOf(1).iterator().nextInt()) + assertEquals(1, intArrayOf(1).iterator().next()) + assertFalse(intArrayOf().iterator().hasNext()) + assertTrue(assertFails { intArrayOf().iterator().next() } is IndexOutOfBoundsException) + + assertTrue(eqFloat(floatArrayOf(0f), FloatArray(1))) + assertTrue(eqFloat(floatArrayOf(1f, 2f, 3f), FloatArray(3) { (it + 1).toFloat() })) + assertTrue(eqFloat(floatArrayOf(1f), floatArrayOf(1f).copyOf())) + assertTrue(eqFloat(floatArrayOf(1f, 0f), floatArrayOf(1f).copyOf(2))) + assertTrue(eqFloat(floatArrayOf(1f), floatArrayOf(1f, 2f).copyOf(1))) + assertTrue(eqFloat(floatArrayOf(1f, 2f), floatArrayOf(1f) + 2f)) + assertTrue(eqFloat(floatArrayOf(1f, 2f, 3f), floatArrayOf(1f) + listOf(2f, 3f))) + assertTrue(eqFloat(floatArrayOf(2f, 3f), floatArrayOf(1f, 2f, 3f, 4f).copyOfRange(1, 3))) + assertTrue(eqFloat(floatArrayOf(1f, 2f, 3f, 4f), customFloatArrayOf(1f, *floatArrayOf(2f, 3f), 4f))) + assertTrue(floatArrayOf(1f).iterator() is FloatIterator) + assertEquals(1f, floatArrayOf(1f).iterator().nextFloat()) + assertEquals(1f, floatArrayOf(1f).iterator().next()) + assertFalse(floatArrayOf().iterator().hasNext()) + assertTrue(assertFails { floatArrayOf().iterator().next() } is IndexOutOfBoundsException) + + assertTrue(eqDouble(doubleArrayOf(0.0), DoubleArray(1))) + assertTrue(eqDouble(doubleArrayOf(1.0, 2.0, 3.0), DoubleArray(3) { (it + 1).toDouble() })) + assertTrue(eqDouble(doubleArrayOf(1.0), doubleArrayOf(1.0).copyOf())) + assertTrue(eqDouble(doubleArrayOf(1.0, 0.0), doubleArrayOf(1.0).copyOf(2))) + assertTrue(eqDouble(doubleArrayOf(1.0), doubleArrayOf(1.0, 2.0).copyOf(1))) + assertTrue(eqDouble(doubleArrayOf(1.0, 2.0), doubleArrayOf(1.0) + 2.0)) + assertTrue(eqDouble(doubleArrayOf(1.0, 2.0, 3.0), doubleArrayOf(1.0) + listOf(2.0, 3.0))) + assertTrue(eqDouble(doubleArrayOf(2.0, 3.0), doubleArrayOf(1.0, 2.0, 3.0, 4.0).copyOfRange(1, 3))) + assertTrue(eqDouble(doubleArrayOf(1.0, 2.0, 3.0, 4.0), customDoubleArrayOf(1.0, *doubleArrayOf(2.0, 3.0), 4.0))) + assertTrue(doubleArrayOf(1.0).iterator() is DoubleIterator) + assertEquals(1.0, doubleArrayOf(1.0).iterator().nextDouble()) + assertEquals(1.0, doubleArrayOf(1.0).iterator().next()) + assertFalse(doubleArrayOf().iterator().hasNext()) + assertTrue(assertFails { doubleArrayOf().iterator().next() } is IndexOutOfBoundsException) + + assertTrue(eqLong(longArrayOf(0), LongArray(1))) + assertTrue(eqLong(longArrayOf(1, 2, 3), LongArray(3) { it + 1L })) + assertTrue(eqLong(longArrayOf(1), longArrayOf(1).copyOf())) + assertTrue(eqLong(longArrayOf(1, 0), longArrayOf(1).copyOf(2))) + assertTrue(eqLong(longArrayOf(1), longArrayOf(1, 2).copyOf(1))) + assertTrue(eqLong(longArrayOf(1, 2), longArrayOf(1) + 2)) + assertTrue(eqLong(longArrayOf(1, 2, 3), longArrayOf(1) + listOf(2L, 3L))) + assertTrue(eqLong(longArrayOf(2, 3), longArrayOf(1, 2, 3, 4).copyOfRange(1, 3))) + assertTrue(eqLong(longArrayOf(1, 2, 3, 4), customLongArrayOf(1L, *longArrayOf(2, 3), 4L))) + assertTrue(longArrayOf(1).iterator() is LongIterator) + assertEquals(1L, longArrayOf(1).iterator().nextLong()) + assertEquals(1L, longArrayOf(1).iterator().next()) + assertFalse(longArrayOf().iterator().hasNext()) + assertTrue(assertFails { longArrayOf().iterator().next() } is IndexOutOfBoundsException) + + // If `is` checks work... + if (intArrayOf() is IntArray) { + assertTrue(booleanArrayOf(false) is BooleanArray) + assertTrue(byteArrayOf(0) is ByteArray) + assertTrue(shortArrayOf(0) is ShortArray) + assertTrue(charArrayOf('a') is CharArray) + assertTrue(intArrayOf(0) is IntArray) + assertTrue(floatArrayOf(0f) is FloatArray) + assertTrue(doubleArrayOf(0.0) is DoubleArray) + assertTrue(longArrayOf(0) is LongArray) + } + + // Rhino `instanceof` fails to distinguish TypedArray's + if (intArrayOf() is IntArray && (byteArrayOf() as Any) !is IntArray) { + assertTrue(checkExactArrayType(booleanArrayOf(false), booleanArray = true)) + assertTrue(checkExactArrayType(byteArrayOf(0), byteArray = true)) + assertTrue(checkExactArrayType(shortArrayOf(0), shortArray = true)) + assertTrue(checkExactArrayType(charArrayOf('a'), charArray = true)) + assertTrue(checkExactArrayType(intArrayOf(0), intArray = true)) + assertTrue(checkExactArrayType(floatArrayOf(0f), floatArray = true)) + assertTrue(checkExactArrayType(doubleArrayOf(0.0), doubleArray = true)) + assertTrue(checkExactArrayType(longArrayOf(0), longArray = true)) + assertTrue(checkExactArrayType(arrayOf(), array = true)) + } + + return "OK" +} + +fun eqBoolean(expected: BooleanArray, actual: BooleanArray): Boolean = actual.size == expected.size && actual.foldIndexed(true) { i, r, v -> r && expected[i] == v } +fun eqByte(expected: ByteArray, actual: ByteArray): Boolean = actual.size == expected.size && actual.foldIndexed(true) { i, r, v -> r && expected[i] == v } +fun eqShort(expected: ShortArray, actual: ShortArray): Boolean = actual.size == expected.size && actual.foldIndexed(true) { i, r, v -> r && expected[i] == v } +fun eqChar(expected: CharArray, actual: CharArray): Boolean = actual.size == expected.size && actual.foldIndexed(true) { i, r, v -> r && expected[i] == v } +fun eqInt(expected: IntArray, actual: IntArray): Boolean = actual.size == expected.size && actual.foldIndexed(true) { i, r, v -> r && expected[i] == v } +fun eqLong(expected: LongArray, actual: LongArray): Boolean = actual.size == expected.size && actual.foldIndexed(true) { i, r, v -> r && expected[i] == v } +fun eqFloat(expected: FloatArray, actual: FloatArray): Boolean = actual.size == expected.size && actual.foldIndexed(true) { i, r, v -> r && expected[i] == v } +fun eqDouble(expected: DoubleArray, actual: DoubleArray): Boolean = actual.size == expected.size && actual.foldIndexed(true) { i, r, v -> r && expected[i] == v } + +fun customBooleanArrayOf(vararg arr: Boolean): BooleanArray = arr +fun customByteArrayOf(vararg arr: Byte): ByteArray = arr +fun customShortArrayOf(vararg arr: Short): ShortArray = arr +fun customCharArrayOf(vararg arr: Char): CharArray = arr +fun customIntArrayOf(vararg arr: Int): IntArray = arr +fun customFloatArrayOf(vararg arr: Float): FloatArray = arr +fun customDoubleArrayOf(vararg arr: Double): DoubleArray = arr +fun customLongArrayOf(vararg arr: Long): LongArray = arr + +fun checkExactArrayType( + a: Any?, + booleanArray: Boolean = false, + byteArray: Boolean = false, + shortArray: Boolean = false, + charArray: Boolean = false, + intArray: Boolean = false, + longArray: Boolean = false, + floatArray: Boolean = false, + doubleArray: Boolean = false, + array: Boolean = false +): Boolean { + return a is BooleanArray == booleanArray && + a is ByteArray == byteArray && + a is ShortArray == shortArray && + a is CharArray == charArray && + a is IntArray == intArray && + a is LongArray == longArray && + a is FloatArray == floatArray && + a is DoubleArray == doubleArray && + a is Array<*> == array +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ranges/forInIndices/kt13241_Array.kt b/compiler/testData/codegen/box/ranges/forInIndices/kt13241_Array.kt index fdabe3cc1e6..8991023a287 100644 --- a/compiler/testData/codegen/box/ranges/forInIndices/kt13241_Array.kt +++ b/compiler/testData/codegen/box/ranges/forInIndices/kt13241_Array.kt @@ -1,3 +1,4 @@ +// IGNORE_BACKEND_WITHOUT_CHECK: JS // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/light-analysis/arrays/primitiveArrays.txt b/compiler/testData/codegen/light-analysis/arrays/primitiveArrays.txt new file mode 100644 index 00000000000..3e6364254bb --- /dev/null +++ b/compiler/testData/codegen/light-analysis/arrays/primitiveArrays.txt @@ -0,0 +1,22 @@ +@kotlin.Metadata +public final class PrimitiveArraysKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public synthetic static method checkExactArrayType$default(p0: java.lang.Object, p1: boolean, p2: boolean, p3: boolean, p4: boolean, p5: boolean, p6: boolean, p7: boolean, p8: boolean, p9: boolean, p10: int, p11: java.lang.Object): boolean + public final static method checkExactArrayType(@org.jetbrains.annotations.Nullable p0: java.lang.Object, p1: boolean, p2: boolean, p3: boolean, p4: boolean, p5: boolean, p6: boolean, p7: boolean, p8: boolean, p9: boolean): boolean + public final static @org.jetbrains.annotations.NotNull method customBooleanArrayOf(@org.jetbrains.annotations.NotNull p0: boolean[]): boolean[] + public final static @org.jetbrains.annotations.NotNull method customByteArrayOf(@org.jetbrains.annotations.NotNull p0: byte[]): byte[] + public final static @org.jetbrains.annotations.NotNull method customCharArrayOf(@org.jetbrains.annotations.NotNull p0: char[]): char[] + public final static @org.jetbrains.annotations.NotNull method customDoubleArrayOf(@org.jetbrains.annotations.NotNull p0: double[]): double[] + public final static @org.jetbrains.annotations.NotNull method customFloatArrayOf(@org.jetbrains.annotations.NotNull p0: float[]): float[] + public final static @org.jetbrains.annotations.NotNull method customIntArrayOf(@org.jetbrains.annotations.NotNull p0: int[]): int[] + public final static @org.jetbrains.annotations.NotNull method customLongArrayOf(@org.jetbrains.annotations.NotNull p0: long[]): long[] + public final static @org.jetbrains.annotations.NotNull method customShortArrayOf(@org.jetbrains.annotations.NotNull p0: short[]): short[] + public final static method eqBoolean(@org.jetbrains.annotations.NotNull p0: boolean[], @org.jetbrains.annotations.NotNull p1: boolean[]): boolean + public final static method eqByte(@org.jetbrains.annotations.NotNull p0: byte[], @org.jetbrains.annotations.NotNull p1: byte[]): boolean + public final static method eqChar(@org.jetbrains.annotations.NotNull p0: char[], @org.jetbrains.annotations.NotNull p1: char[]): boolean + public final static method eqDouble(@org.jetbrains.annotations.NotNull p0: double[], @org.jetbrains.annotations.NotNull p1: double[]): boolean + public final static method eqFloat(@org.jetbrains.annotations.NotNull p0: float[], @org.jetbrains.annotations.NotNull p1: float[]): boolean + public final static method eqInt(@org.jetbrains.annotations.NotNull p0: int[], @org.jetbrains.annotations.NotNull p1: int[]): boolean + public final static method eqLong(@org.jetbrains.annotations.NotNull p0: long[], @org.jetbrains.annotations.NotNull p1: long[]): boolean + public final static method eqShort(@org.jetbrains.annotations.NotNull p0: short[], @org.jetbrains.annotations.NotNull p1: short[]): boolean +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index bdc08706d99..f9c09810a0b 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -611,6 +611,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("primitiveArrays.kt") + public void testPrimitiveArrays() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/primitiveArrays.kt"); + doTest(fileName); + } + @TestMetadata("stdlib.kt") public void testStdlib() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/stdlib.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index b9a15a57185..90afc1bf5c3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -611,6 +611,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("primitiveArrays.kt") + public void testPrimitiveArrays() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/primitiveArrays.kt"); + doTest(fileName); + } + @TestMetadata("stdlib.kt") public void testStdlib() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/stdlib.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 46ebcd40ece..3caf092b431 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -611,6 +611,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("primitiveArrays.kt") + public void testPrimitiveArrays() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/primitiveArrays.kt"); + doTest(fileName); + } + @TestMetadata("stdlib.kt") public void testStdlib() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/stdlib.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 95269485f39..5ce96259240 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -877,6 +877,20 @@ public abstract class KotlinBuiltIns { return descriptor != null && getPrimitiveTypeByArrayClassFqName(getFqName(descriptor)) != null; } + @Nullable + public static PrimitiveType getPrimitiveArrayElementType(@NotNull KotlinType type) { + ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); + if (descriptor == null) return null; + return getPrimitiveTypeByArrayClassFqName(getFqName(descriptor)); + } + + @Nullable + public static PrimitiveType getPrimitiveType(@NotNull KotlinType type) { + ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); + if (type.isMarkedNullable() || !(descriptor instanceof ClassDescriptor)) return null; + return getPrimitiveTypeByFqName(getFqName(descriptor)); + } + public static boolean isPrimitiveType(@NotNull KotlinType type) { return !type.isMarkedNullable() && isPrimitiveTypeOrNullablePrimitiveType(type); } diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 8a1f3b97d38..706d0dca4b0 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -1277,6 +1277,10 @@ fun main(args: Array) { testClass { model("codegen/boxInline/enum/", targetBackend = TargetBackend.JS) } + + testClass { + model("codegen/box/arrays", targetBackend = TargetBackend.JS) + } } } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java index d1ca4990a30..c890a1af470 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java @@ -27,6 +27,7 @@ public class JSConfigurationKeys { public static final CompilerConfigurationKey SOURCE_MAP = CompilerConfigurationKey.create("generate source map"); + public static final CompilerConfigurationKey META_INFO = CompilerConfigurationKey.create("generate .meta.js and .kjsm files"); @@ -38,4 +39,7 @@ public class JSConfigurationKeys { public static final CompilerConfigurationKey MODULE_KIND = CompilerConfigurationKey.create("module kind"); + + public static final CompilerConfigurationKey TYPED_ARRAYS_ENABLED = + CompilerConfigurationKey.create("TypedArrays enabled"); } diff --git a/js/js.libraries/src/builtins/arrayUtils.kt b/js/js.libraries/src/builtins/arrayUtils.kt index 06092712e0a..4042b4bdcfa 100644 --- a/js/js.libraries/src/builtins/arrayUtils.kt +++ b/js/js.libraries/src/builtins/arrayUtils.kt @@ -19,22 +19,54 @@ external private fun Array(size: Int): Array @JsName("newArray") -fun newArray(size: Int, initValue: T): Array { - return fillArray(Array(size), initValue) -} - -private fun fillArray(array: Array, value: T): Array { - for (i in 0..array.size - 1) { - array[i] = value - } - return array; -} +fun newArray(size: Int, initValue: T) = fillArrayVal(Array(size), initValue) @JsName("newArrayF") -fun arrayWithFun(size: Int, init: (Int) -> T): Array { - var result = Array(size) - for (i in 0..size - 1) { - result[i] = init(i) +fun arrayWithFun(size: Int, init: (Int) -> T) = fillArrayFun(Array(size), init) + +@JsName("fillArray") +fun fillArrayFun(array: Array, init: (Int) -> T): Array { + for (i in 0..array.size - 1) { + array[i] = init(i) } - return result + return array } + +@JsName("booleanArray") +fun booleanArray(size: Int, init: dynamic): Array { + val result: dynamic = Array(size) + result.`$type$` = "BooleanArray" + return when (init) { + null, true -> fillArrayVal(result, false) + false -> result + else -> fillArrayFun(result, init) + } +} + +@JsName("charArray") +fun charArray(size: Int, init: dynamic): Array { + val result = js("new Uint16Array(size)") + result.`$type$` = "CharArray" + return when (init) { + null, true, false -> result // For consistency + else -> fillArrayFun(result, init) + } +} + +@JsName("longArray") +fun longArray(size: Int, init: dynamic): Array { + val result: dynamic = Array(size) + result.`$type$` = "LongArray" + return when (init) { + null, true -> fillArrayVal(result, 0L) + false -> result + else -> fillArrayFun(result, init) + } +} + +private fun fillArrayVal(array: Array, initValue: T): Array { + for (i in 0..array.size - 1) { + array[i] = initValue + } + return array +} \ No newline at end of file diff --git a/js/js.libraries/src/core/builtins.kt b/js/js.libraries/src/core/builtins.kt index b907133596d..fee6909283c 100644 --- a/js/js.libraries/src/core/builtins.kt +++ b/js/js.libraries/src/core/builtins.kt @@ -15,21 +15,80 @@ */ @JsName("arrayIterator") -internal fun arrayIterator(array: dynamic): MutableIterator { - return object : MutableIterator { - 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 = array + object : Iterator { + 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 { } } -/* For future binary compatibility with TypedArrays - * TODO: concat normal Array's and TypedArrays into an Array +internal inline fun concat(args: Array): 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 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 primitiveArrayConcat(a: T, b: T): T { - return a.asDynamic().concat.apply(js("[]"), js("arguments")); + val args: Array = 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 } \ No newline at end of file diff --git a/js/js.libraries/src/core/dynamic.kt b/js/js.libraries/src/core/dynamic.kt index 8ea21cea14f..07817f142a2 100644 --- a/js/js.libraries/src/core/dynamic.kt +++ b/js/js.libraries/src/core/dynamic.kt @@ -49,7 +49,7 @@ public operator fun dynamic.iterator(): Iterator { return when { this["iterator"] != null -> this["iterator"]() - js("Array").isArray(r) -> + js("Kotlin").isArrayish(r) -> r.unsafeCast>().iterator() else -> diff --git a/js/js.libraries/src/core/generated/_ArraysJs.kt b/js/js.libraries/src/core/generated/_ArraysJs.kt index 0ec724bd14b..9b1a62c720d 100644 --- a/js/js.libraries/src/core/generated/_ArraysJs.kt +++ b/js/js.libraries/src/core/generated/_ArraysJs.kt @@ -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 Array.plus(elements: Collection): Array { * 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): 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): 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): 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): 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): 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): 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): 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): CharArray { - return arrayPlusCollection(this, elements) + return fillFromCollection(this.copyOf(size + elements.size), this.size, elements) } /** @@ -13474,21 +13469,21 @@ public fun Array.sortWith(comparator: Comparator): Unit { * Returns a *typed* object array containing all of the elements of this primitive array. */ public fun ByteArray.toTypedArray(): Array { - return copyOf().unsafeCast>() + return js("[]").slice.call(this) } /** * Returns a *typed* object array containing all of the elements of this primitive array. */ public fun ShortArray.toTypedArray(): Array { - return copyOf().unsafeCast>() + return js("[]").slice.call(this) } /** * Returns a *typed* object array containing all of the elements of this primitive array. */ public fun IntArray.toTypedArray(): Array { - return copyOf().unsafeCast>() + return js("[]").slice.call(this) } /** @@ -13502,14 +13497,14 @@ public fun LongArray.toTypedArray(): Array { * Returns a *typed* object array containing all of the elements of this primitive array. */ public fun FloatArray.toTypedArray(): Array { - return copyOf().unsafeCast>() + return js("[]").slice.call(this) } /** * Returns a *typed* object array containing all of the elements of this primitive array. */ public fun DoubleArray.toTypedArray(): Array { - return copyOf().unsafeCast>() + return js("[]").slice.call(this) } /** diff --git a/js/js.libraries/src/core/kotlin.kt b/js/js.libraries/src/core/kotlin.kt index 2055ee4393b..9b51020519d 100644 --- a/js/js.libraries/src/core/kotlin.kt +++ b/js/js.libraries/src/core/kotlin.kt @@ -71,8 +71,18 @@ internal fun arrayOfNulls(reference: Array, size: Int): Array { return arrayOfNulls(size).unsafeCast>() } +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 arrayPlusCollection(array: dynamic, collection: Collection): 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 fillFromCollection(dst: dynamic, startIndex: Int, collection: Collection): 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 Map.toSingletonMapOrSelf(): Map = this diff --git a/js/js.libraries/src/js/arrayUtils.js b/js/js.libraries/src/js/arrayUtils.js index e1563877040..c9e24be32ce 100644 --- a/js/js.libraries/src/js/arrayUtils.js +++ b/js/js.libraries/src/js/arrayUtils.js @@ -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) }; diff --git a/js/js.libraries/src/js/core.js b/js/js.libraries/src/js/core.js index 6c51f8d6bf5..515b7947c19 100644 --- a/js/js.libraries/src/js/core.js +++ b/js/js.libraries/src/js/core.js @@ -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 { diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index cfde5c9faa3..8a2579fcaa5 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -60,7 +60,8 @@ import java.util.regex.Pattern abstract class BasicBoxTest( private val pathToTestDir: String, - private val pathToOutputDir: String + private val pathToOutputDir: String, + private val typedArraysEnabled: Boolean = false ) : KotlinTestWithEnvironment() { private val COMMON_FILES_NAME = "_common" private val COMMON_FILES_DIR = "_commonFiles/" @@ -268,6 +269,10 @@ abstract class BasicBoxTest( //configuration.put(JSConfigurationKeys.SOURCE_MAP, shouldGenerateSourceMap()) configuration.put(JSConfigurationKeys.META_INFO, multiModule) + if (typedArraysEnabled) { + configuration.put(JSConfigurationKeys.TYPED_ARRAYS_ENABLED, true) + } + return JsConfig(project, configuration) } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/rhino/RhinoUtils.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/rhino/RhinoUtils.java index 06aa5dfec46..1bc43ed205a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/rhino/RhinoUtils.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/rhino/RhinoUtils.java @@ -191,6 +191,7 @@ public final class RhinoUtils { private static ScriptableObject initScope(@NotNull EcmaVersion version, @NotNull Context context, @NotNull List jsLibraries) { ScriptableObject scope = context.initStandardObjects(); try { + runFileWithRhino(DIST_DIR_JS_PATH + "../../js/js.translator/testData/rhino-polyfills.js", context, scope); runFileWithRhino(DIST_DIR_JS_PATH + "kotlin.js", context, scope); runFileWithRhino(DIST_DIR_JS_PATH + "../classes/kotlin-test-js/kotlin-test.js", context, scope); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 6132640642f..63f2c304f59 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -391,6 +391,24 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class Arrays extends AbstractJsCodegenBoxTest { + @TestMetadata("arrayInstanceOf.kt") + public void ignoreArrayInstanceOf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/arrayInstanceOf.kt"); + doTest(fileName); + } + + @TestMetadata("kt2997.kt") + public void ignoreKt2997() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt2997.kt"); + doTest(fileName); + } + + @TestMetadata("kt7288.kt") + public void ignoreKt7288() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt7288.kt"); + doTest(fileName); + } + public void testAllFilesPresentInArrays() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } @@ -413,18 +431,6 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } - @TestMetadata("arrayInstanceOf.kt") - public void testArrayInstanceOf() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/arrayInstanceOf.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); - } - @TestMetadata("arrayPlusAssign.kt") public void testArrayPlusAssign() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/arrayPlusAssign.kt"); @@ -590,13 +596,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("iteratorByteArrayNextByte.kt") public void testIteratorByteArrayNextByte() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorByteArrayNextByte.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("iteratorCharArray.kt") @@ -632,13 +632,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("iteratorLongArrayNextLong.kt") public void testIteratorLongArrayNextLong() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorLongArrayNextLong.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("iteratorShortArray.kt") @@ -659,18 +653,6 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } - @TestMetadata("kt2997.kt") - public void testKt2997() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt2997.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); - } - @TestMetadata("kt33.kt") public void testKt33() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt33.kt"); @@ -737,12 +719,6 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } - @TestMetadata("kt7288.kt") - public void testKt7288() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt7288.kt"); - doTest(fileName); - } - @TestMetadata("kt7338.kt") public void testKt7338() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt7338.kt"); @@ -803,6 +779,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("primitiveArrays.kt") + public void testPrimitiveArrays() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/primitiveArrays.kt"); + doTest(fileName); + } + @TestMetadata("stdlib.kt") public void testStdlib() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/stdlib.kt"); @@ -14894,6 +14876,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class ForInIndices extends AbstractJsCodegenBoxTest { + @TestMetadata("kt13241_Array.kt") + public void ignoreKt13241_Array() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInIndices/kt13241_Array.kt"); + doTest(fileName); + } + public void testAllFilesPresentInForInIndices() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInIndices"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } @@ -14970,12 +14958,6 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } - @TestMetadata("kt13241_Array.kt") - public void testKt13241_Array() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInIndices/kt13241_Array.kt"); - doTest(fileName); - } - @TestMetadata("kt13241_CharSequence.kt") public void testKt13241_CharSequence() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInIndices/kt13241_CharSequence.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsTypedArraysBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsTypedArraysBoxTestGenerated.java new file mode 100644 index 00000000000..9c56404731d --- /dev/null +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsTypedArraysBoxTestGenerated.java @@ -0,0 +1,557 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.test.semantics; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/codegen/box/arrays") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class JsTypedArraysBoxTestGenerated extends AbstractJsTypedArraysBoxTest { + @TestMetadata("arrayInstanceOf.kt") + public void ignoreArrayInstanceOf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/arrayInstanceOf.kt"); + doTest(fileName); + } + + @TestMetadata("kt2997.kt") + public void ignoreKt2997() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt2997.kt"); + doTest(fileName); + } + + @TestMetadata("kt7288.kt") + public void ignoreKt7288() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt7288.kt"); + doTest(fileName); + } + + public void testAllFilesPresentInArrays() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("arrayConstructorsSimple.kt") + public void testArrayConstructorsSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/arrayConstructorsSimple.kt"); + doTest(fileName); + } + + @TestMetadata("arrayGetAssignMultiIndex.kt") + public void testArrayGetAssignMultiIndex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/arrayGetAssignMultiIndex.kt"); + doTest(fileName); + } + + @TestMetadata("arrayGetMultiIndex.kt") + public void testArrayGetMultiIndex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/arrayGetMultiIndex.kt"); + doTest(fileName); + } + + @TestMetadata("arrayPlusAssign.kt") + public void testArrayPlusAssign() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/arrayPlusAssign.kt"); + doTest(fileName); + } + + @TestMetadata("arraysAreCloneable.kt") + public void testArraysAreCloneable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/arraysAreCloneable.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("cloneArray.kt") + public void testCloneArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/cloneArray.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("clonePrimitiveArrays.kt") + public void testClonePrimitiveArrays() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/clonePrimitiveArrays.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("collectionAssignGetMultiIndex.kt") + public void testCollectionAssignGetMultiIndex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/collectionAssignGetMultiIndex.kt"); + doTest(fileName); + } + + @TestMetadata("collectionGetMultiIndex.kt") + public void testCollectionGetMultiIndex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/collectionGetMultiIndex.kt"); + doTest(fileName); + } + + @TestMetadata("forEachBooleanArray.kt") + public void testForEachBooleanArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/forEachBooleanArray.kt"); + doTest(fileName); + } + + @TestMetadata("forEachByteArray.kt") + public void testForEachByteArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/forEachByteArray.kt"); + doTest(fileName); + } + + @TestMetadata("forEachCharArray.kt") + public void testForEachCharArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/forEachCharArray.kt"); + doTest(fileName); + } + + @TestMetadata("forEachDoubleArray.kt") + public void testForEachDoubleArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/forEachDoubleArray.kt"); + doTest(fileName); + } + + @TestMetadata("forEachFloatArray.kt") + public void testForEachFloatArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/forEachFloatArray.kt"); + doTest(fileName); + } + + @TestMetadata("forEachIntArray.kt") + public void testForEachIntArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/forEachIntArray.kt"); + doTest(fileName); + } + + @TestMetadata("forEachLongArray.kt") + public void testForEachLongArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/forEachLongArray.kt"); + doTest(fileName); + } + + @TestMetadata("forEachShortArray.kt") + public void testForEachShortArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/forEachShortArray.kt"); + doTest(fileName); + } + + @TestMetadata("genericArrayInObjectLiteralConstructor.kt") + public void testGenericArrayInObjectLiteralConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/genericArrayInObjectLiteralConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("hashMap.kt") + public void testHashMap() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/hashMap.kt"); + doTest(fileName); + } + + @TestMetadata("inProjectionAsParameter.kt") + public void testInProjectionAsParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/inProjectionAsParameter.kt"); + doTest(fileName); + } + + @TestMetadata("inProjectionOfArray.kt") + public void testInProjectionOfArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/inProjectionOfArray.kt"); + doTest(fileName); + } + + @TestMetadata("inProjectionOfList.kt") + public void testInProjectionOfList() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/inProjectionOfList.kt"); + doTest(fileName); + } + + @TestMetadata("indices.kt") + public void testIndices() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/indices.kt"); + doTest(fileName); + } + + @TestMetadata("indicesChar.kt") + public void testIndicesChar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/indicesChar.kt"); + doTest(fileName); + } + + @TestMetadata("iterator.kt") + public void testIterator() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iterator.kt"); + doTest(fileName); + } + + @TestMetadata("iteratorBooleanArray.kt") + public void testIteratorBooleanArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorBooleanArray.kt"); + doTest(fileName); + } + + @TestMetadata("iteratorByteArray.kt") + public void testIteratorByteArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorByteArray.kt"); + doTest(fileName); + } + + @TestMetadata("iteratorByteArrayNextByte.kt") + public void testIteratorByteArrayNextByte() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorByteArrayNextByte.kt"); + doTest(fileName); + } + + @TestMetadata("iteratorCharArray.kt") + public void testIteratorCharArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorCharArray.kt"); + doTest(fileName); + } + + @TestMetadata("iteratorDoubleArray.kt") + public void testIteratorDoubleArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorDoubleArray.kt"); + doTest(fileName); + } + + @TestMetadata("iteratorFloatArray.kt") + public void testIteratorFloatArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorFloatArray.kt"); + doTest(fileName); + } + + @TestMetadata("iteratorIntArray.kt") + public void testIteratorIntArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorIntArray.kt"); + doTest(fileName); + } + + @TestMetadata("iteratorLongArray.kt") + public void testIteratorLongArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorLongArray.kt"); + doTest(fileName); + } + + @TestMetadata("iteratorLongArrayNextLong.kt") + public void testIteratorLongArrayNextLong() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorLongArrayNextLong.kt"); + doTest(fileName); + } + + @TestMetadata("iteratorShortArray.kt") + public void testIteratorShortArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/iteratorShortArray.kt"); + doTest(fileName); + } + + @TestMetadata("kt1291.kt") + public void testKt1291() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt1291.kt"); + doTest(fileName); + } + + @TestMetadata("kt238.kt") + public void testKt238() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt238.kt"); + doTest(fileName); + } + + @TestMetadata("kt33.kt") + public void testKt33() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt33.kt"); + doTest(fileName); + } + + @TestMetadata("kt3771.kt") + public void testKt3771() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt3771.kt"); + doTest(fileName); + } + + @TestMetadata("kt4118.kt") + public void testKt4118() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt4118.kt"); + doTest(fileName); + } + + @TestMetadata("kt4348.kt") + public void testKt4348() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt4348.kt"); + doTest(fileName); + } + + @TestMetadata("kt4357.kt") + public void testKt4357() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt4357.kt"); + doTest(fileName); + } + + @TestMetadata("kt503.kt") + public void testKt503() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt503.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("kt594.kt") + public void testKt594() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt594.kt"); + doTest(fileName); + } + + @TestMetadata("kt602.kt") + public void testKt602() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt602.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("kt7009.kt") + public void testKt7009() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt7009.kt"); + doTest(fileName); + } + + @TestMetadata("kt7338.kt") + public void testKt7338() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt7338.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("kt779.kt") + public void testKt779() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt779.kt"); + doTest(fileName); + } + + @TestMetadata("kt945.kt") + public void testKt945() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt945.kt"); + doTest(fileName); + } + + @TestMetadata("kt950.kt") + public void testKt950() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/kt950.kt"); + doTest(fileName); + } + + @TestMetadata("longAsIndex.kt") + public void testLongAsIndex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/longAsIndex.kt"); + doTest(fileName); + } + + @TestMetadata("multiArrayConstructors.kt") + public void testMultiArrayConstructors() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiArrayConstructors.kt"); + doTest(fileName); + } + + @TestMetadata("nonLocalReturnArrayConstructor.kt") + public void testNonLocalReturnArrayConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/nonLocalReturnArrayConstructor.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("nonNullArray.kt") + public void testNonNullArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/nonNullArray.kt"); + doTest(fileName); + } + + @TestMetadata("primitiveArrays.kt") + public void testPrimitiveArrays() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/primitiveArrays.kt"); + doTest(fileName); + } + + @TestMetadata("stdlib.kt") + public void testStdlib() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/stdlib.kt"); + doTest(fileName); + } + + @TestMetadata("compiler/testData/codegen/box/arrays/multiDecl") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MultiDecl extends AbstractJsTypedArraysBoxTest { + public void testAllFilesPresentInMultiDecl() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays/multiDecl"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("kt15560.kt") + public void testKt15560() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/kt15560.kt"); + doTest(fileName); + } + + @TestMetadata("kt15568.kt") + public void testKt15568() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/kt15568.kt"); + doTest(fileName); + } + + @TestMetadata("kt15575.kt") + public void testKt15575() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/kt15575.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclFor.kt") + public void testMultiDeclFor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/MultiDeclFor.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclForComponentExtensions.kt") + public void testMultiDeclForComponentExtensions() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/MultiDeclForComponentExtensions.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclForComponentMemberExtensions.kt") + public void testMultiDeclForComponentMemberExtensions() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/MultiDeclForComponentMemberExtensions.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclForComponentMemberExtensionsInExtensionFunction.kt") + public void testMultiDeclForComponentMemberExtensionsInExtensionFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/MultiDeclForComponentMemberExtensionsInExtensionFunction.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclForValCaptured.kt") + public void testMultiDeclForValCaptured() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/MultiDeclForValCaptured.kt"); + doTest(fileName); + } + + @TestMetadata("compiler/testData/codegen/box/arrays/multiDecl/int") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Int extends AbstractJsTypedArraysBoxTest { + public void testAllFilesPresentInInt() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays/multiDecl/int"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("MultiDeclForComponentExtensions.kt") + public void testMultiDeclForComponentExtensions() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/int/MultiDeclForComponentExtensions.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclForComponentExtensionsValCaptured.kt") + public void testMultiDeclForComponentExtensionsValCaptured() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/int/MultiDeclForComponentExtensionsValCaptured.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclForComponentMemberExtensions.kt") + public void testMultiDeclForComponentMemberExtensions() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/int/MultiDeclForComponentMemberExtensions.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclForComponentMemberExtensionsInExtensionFunction.kt") + public void testMultiDeclForComponentMemberExtensionsInExtensionFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/int/MultiDeclForComponentMemberExtensionsInExtensionFunction.kt"); + doTest(fileName); + } + } + + @TestMetadata("compiler/testData/codegen/box/arrays/multiDecl/long") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Long extends AbstractJsTypedArraysBoxTest { + public void testAllFilesPresentInLong() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/arrays/multiDecl/long"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("MultiDeclForComponentExtensions.kt") + public void testMultiDeclForComponentExtensions() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/long/MultiDeclForComponentExtensions.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclForComponentExtensionsValCaptured.kt") + public void testMultiDeclForComponentExtensionsValCaptured() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/long/MultiDeclForComponentExtensionsValCaptured.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclForComponentMemberExtensions.kt") + public void testMultiDeclForComponentMemberExtensions() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/long/MultiDeclForComponentMemberExtensions.kt"); + doTest(fileName); + } + + @TestMetadata("MultiDeclForComponentMemberExtensionsInExtensionFunction.kt") + public void testMultiDeclForComponentMemberExtensionsInExtensionFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/multiDecl/long/MultiDeclForComponentMemberExtensionsInExtensionFunction.kt"); + doTest(fileName); + } + } + } +} diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt index c90b0367553..f804f10aaec 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt @@ -46,3 +46,9 @@ abstract class AbstractJsCodegenBoxTest : BasicBoxTest( "compiler/testData/codegen/box/", BasicBoxTest.TEST_DATA_DIR_PATH + "out/codegen/box/" ) + +abstract class AbstractJsTypedArraysBoxTest : BasicBoxTest( + "compiler/testData/codegen/box/arrays/", + BasicBoxTest.TEST_DATA_DIR_PATH + "out/codegen/box/arrays-typedarrays/", + typedArraysEnabled = true +) \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java index ed99be264fc..a0d27f30661 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.translate.context; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.builtins.PrimitiveType; import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor; @@ -283,6 +284,16 @@ public final class Namer { return kotlin(IS_CHAR_SEQUENCE); } + @NotNull + public JsExpression isArray() { + return kotlin("isArray"); + } + + @NotNull + public JsExpression isPrimitiveArray(@NotNull PrimitiveType type) { + return kotlin("is" + type.getArrayTypeName().asString()); + } + @NotNull private JsExpression invokeFunctionAndSetTypeCheckMetadata( @NotNull String functionName, diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt index a1ffa57a1f7..9450842e7da 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.general.Translation -import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.CompositeFIF +import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.ArrayFIF import org.jetbrains.kotlin.js.translate.utils.BindingUtils.* import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.* import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopRange @@ -165,7 +165,7 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont fun translateForOverArray(): JsStatement { val rangeExpression = context.defineTemporary(Translation.translateAsExpression(loopRange, context)) - val length = CompositeFIF.LENGTH_PROPERTY_INTRINSIC.apply(rangeExpression, listOf(), context) + val length = ArrayFIF.LENGTH_PROPERTY_INTRINSIC.apply(rangeExpression, listOf(), context) val end = context.defineTemporary(length) val index = context.declareTemporary(context.program().getNumberLiteral(0)) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java index 523dd8edaf8..08811f17b5d 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.js.backend.ast.JsConditional; import org.jetbrains.kotlin.js.backend.ast.JsExpression; import org.jetbrains.kotlin.js.backend.ast.JsInvocation; import org.jetbrains.kotlin.js.backend.ast.JsLiteral; +import org.jetbrains.kotlin.js.config.JSConfigurationKeys; import org.jetbrains.kotlin.js.patterns.NamePredicate; import org.jetbrains.kotlin.js.patterns.typePredicates.TypePredicatesKt; import org.jetbrains.kotlin.js.translate.context.Namer; @@ -34,9 +35,10 @@ import org.jetbrains.kotlin.js.translate.context.TemporaryVariable; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.general.Translation; +import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.ArrayFIF; import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF; -import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator; +import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; import org.jetbrains.kotlin.js.translate.utils.BindingUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; @@ -210,7 +212,15 @@ public final class PatternTranslator extends AbstractTranslator { return namer().isTypeOf(program().getStringLiteral("function")); } - if (isArray(type)) return Namer.IS_ARRAY_FUN_REF; + if (isArray(type)) { + if (ArrayFIF.typedArraysEnabled(context())) { + return namer().isArray(); + } + else { + return Namer.IS_ARRAY_FUN_REF; + } + + } if (TypePredicatesKt.getCHAR_SEQUENCE().apply(type)) return namer().isCharSequence(); @@ -247,6 +257,14 @@ public final class PatternTranslator extends AbstractTranslator { return namer().isTypeOf(program().getStringLiteral("number")); } + if (ArrayFIF.typedArraysEnabled(context())) { + if (KotlinBuiltIns.isPrimitiveArray(type)) { + PrimitiveType arrayType = KotlinBuiltIns.getPrimitiveArrayElementType(type); + assert arrayType != null; + return namer().isPrimitiveArray(arrayType); + } + } + return null; } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.java deleted file mode 100644 index c223af55507..00000000000 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories; - -import com.google.common.collect.Lists; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.builtins.KotlinBuiltIns; -import org.jetbrains.kotlin.builtins.PrimitiveType; -import org.jetbrains.kotlin.js.backend.ast.*; -import org.jetbrains.kotlin.js.patterns.DescriptorPredicate; -import org.jetbrains.kotlin.js.patterns.NamePredicate; -import org.jetbrains.kotlin.js.translate.context.Namer; -import org.jetbrains.kotlin.js.translate.context.TranslationContext; -import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic; -import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed; -import org.jetbrains.kotlin.name.Name; - -import java.util.List; - -import static com.intellij.openapi.util.text.StringUtil.decapitalize; -import static org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern; -import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.assignment; - -public final class ArrayFIF extends CompositeFIF { - private static final NamePredicate NUMBER_ARRAY; - private static final NamePredicate CHAR_ARRAY; - private static final NamePredicate BOOLEAN_ARRAY; - private static final NamePredicate LONG_ARRAY; - private static final NamePredicate ARRAYS; - private static final DescriptorPredicate ARRAY_FACTORY_METHODS; - - static { - List arrayTypeNames = Lists.newArrayList(); - List arrayFactoryMethodNames = Lists.newArrayList(Name.identifier("arrayOf")); - for (PrimitiveType type : PrimitiveType.values()) { - Name arrayTypeName = type.getArrayTypeName(); - if (type != PrimitiveType.CHAR && type != PrimitiveType.BOOLEAN && type != PrimitiveType.LONG) { - arrayTypeNames.add(arrayTypeName); - } - arrayFactoryMethodNames.add(Name.identifier(decapitalize(arrayTypeName.asString() + "Of"))); - } - - Name arrayName = KotlinBuiltIns.FQ_NAMES.array.shortName(); - Name booleanArrayName = PrimitiveType.BOOLEAN.getArrayTypeName(); - Name charArrayName = PrimitiveType.CHAR.getArrayTypeName(); - Name longArrayName = PrimitiveType.LONG.getArrayTypeName(); - - NUMBER_ARRAY = new NamePredicate(arrayTypeNames); - CHAR_ARRAY = new NamePredicate(charArrayName); - BOOLEAN_ARRAY = new NamePredicate(booleanArrayName); - LONG_ARRAY = new NamePredicate(longArrayName); - - arrayTypeNames.add(charArrayName); - arrayTypeNames.add(booleanArrayName); - arrayTypeNames.add(longArrayName); - arrayTypeNames.add(arrayName); - ARRAYS = new NamePredicate(arrayTypeNames); - ARRAY_FACTORY_METHODS = pattern(Namer.KOTLIN_LOWER_NAME, new NamePredicate(arrayFactoryMethodNames)); - } - - private static final FunctionIntrinsic ARRAY_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() { - @NotNull - @Override - public JsExpression apply( - @Nullable JsExpression receiver, - @NotNull List arguments, - @NotNull TranslationContext context - ) { - assert arguments.size() == 1; - return arguments.get(0); - } - }; - - @NotNull - public static final FunctionIntrinsic GET_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() { - @NotNull - @Override - public JsExpression apply(@Nullable JsExpression receiver, - @NotNull List arguments, - @NotNull TranslationContext context) { - assert receiver != null; - assert arguments.size() == 1 : "Array get expression must have one argument."; - JsExpression indexExpression = arguments.get(0); - return new JsArrayAccess(receiver, indexExpression); - } - }; - - @NotNull - public static final FunctionIntrinsic SET_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() { - @NotNull - @Override - public JsExpression apply(@Nullable JsExpression receiver, - @NotNull List arguments, - @NotNull TranslationContext context) { - assert receiver != null; - assert arguments.size() == 2 : "Array set expression must have two arguments."; - JsExpression indexExpression = arguments.get(0); - JsExpression value = arguments.get(1); - JsArrayAccess arrayAccess = new JsArrayAccess(receiver, indexExpression); - return assignment(arrayAccess, value); - } - }; - - @NotNull - public static final FunctionIntrinsicFactory INSTANCE = new ArrayFIF(); - - private ArrayFIF() { - add(pattern(ARRAYS, "get"), GET_INTRINSIC); - add(pattern(ARRAYS, "set"), SET_INTRINSIC); - add(pattern(ARRAYS, ""), LENGTH_PROPERTY_INTRINSIC); - add(pattern(ARRAYS, "iterator"), new KotlinFunctionIntrinsic("arrayIterator")); - - add(pattern(NUMBER_ARRAY, "(Int)"), new KotlinFunctionIntrinsic("newArray", JsNumberLiteral.ZERO)); - add(pattern(CHAR_ARRAY, "(Int)"), new KotlinFunctionIntrinsic("newArray", JsNumberLiteral.ZERO)); - add(pattern(BOOLEAN_ARRAY, "(Int)"), new KotlinFunctionIntrinsic("newArray", JsLiteral.FALSE)); - add(pattern(LONG_ARRAY, "(Int)"), new KotlinFunctionIntrinsic("newArray", new JsNameRef(Namer.LONG_ZERO, Namer.kotlinLong()))); - - add(pattern(ARRAYS, "(Int,Function1)"), new KotlinFunctionIntrinsic("newArrayF")); - - add(pattern("kotlin", "arrayOfNulls"), new KotlinFunctionIntrinsic("newArray", JsLiteral.NULL)); - - add(ARRAY_FACTORY_METHODS, ARRAY_INTRINSIC); - } -} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.kt new file mode 100644 index 00000000000..1347ce05388 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.kt @@ -0,0 +1,162 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories + +import com.intellij.openapi.util.text.StringUtil.decapitalize +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.builtins.PrimitiveType.* +import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind +import org.jetbrains.kotlin.js.backend.ast.metadata.sideEffects +import org.jetbrains.kotlin.js.config.JSConfigurationKeys +import org.jetbrains.kotlin.js.patterns.NamePredicate +import org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern +import org.jetbrains.kotlin.js.translate.context.Namer +import org.jetbrains.kotlin.js.translate.context.TranslationContext +import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.BuiltInPropertyIntrinsic +import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.name.Name +import java.util.* + +object ArrayFIF : CompositeFIF() { + @JvmField + val GET_INTRINSIC = intrinsify { receiver, arguments, _ -> + assert(arguments.size == 1) { "Array get expression must have one argument." } + val (indexExpression) = arguments + JsArrayAccess(receiver!!, indexExpression) + } + + @JvmField + val SET_INTRINSIC = intrinsify { receiver, arguments, _ -> + assert(arguments.size == 2) { "Array set expression must have two arguments." } + val (indexExpression, value) = arguments + val arrayAccess = JsArrayAccess(receiver!!, indexExpression) + JsAstUtils.assignment(arrayAccess, value) + } + + @JvmField + val LENGTH_PROPERTY_INTRINSIC = BuiltInPropertyIntrinsic("length") + + @JvmStatic + fun typedArraysEnabled(ctx: TranslationContext) = ctx.config.configuration.getBoolean(JSConfigurationKeys.TYPED_ARRAYS_ENABLED) + + fun castOrCreatePrimitiveArray(ctx: TranslationContext, type: PrimitiveType?, arg: JsArrayLiteral): JsExpression { + if (type == null || !typedArraysEnabled(ctx)) return arg + + if (type in TYPED_ARRAY_MAP) { + return createTypedArray(type, arg) + } + else { + return JsAstUtils.invokeKotlinFunction(type.lowerCaseName + "ArrayOf", *arg.expressions.toTypedArray()) + } + } + + private val TYPED_ARRAY_MAP = EnumMap(mapOf(BYTE to "Int8", + SHORT to "Int16", + INT to "Int32", + FLOAT to "Float32", + DOUBLE to "Float64")) + + private fun createTypedArray(type: PrimitiveType, arg: JsExpression): JsExpression { + assert(type in TYPED_ARRAY_MAP) + return JsNew(JsNameRef(TYPED_ARRAY_MAP[type] + "Array"), listOf(arg)) + } + + private val PrimitiveType.lowerCaseName + get() = typeName.asString().toLowerCase() + + init { + val arrayName = KotlinBuiltIns.FQ_NAMES.array.shortName() + + val arrayTypeNames = mutableListOf(arrayName) + PrimitiveType.values().mapTo(arrayTypeNames) { it.arrayTypeName } + + val arrays = NamePredicate(arrayTypeNames) + add(pattern(arrays, "get"), GET_INTRINSIC) + add(pattern(arrays, "set"), SET_INTRINSIC) + add(pattern(arrays, ""), LENGTH_PROPERTY_INTRINSIC) + + for (type in PrimitiveType.values()) { + add(pattern(NamePredicate(type.arrayTypeName), "(Int)"), intrinsify { _, arguments, context -> + assert(arguments.size == 1) { "Array (Int) expression must have one argument." } + val (size) = arguments + + if (typedArraysEnabled(context)) { + if (type in TYPED_ARRAY_MAP) { + createTypedArray(type, size) + } + else { + JsAstUtils.invokeKotlinFunction("${type.lowerCaseName}Array", size) + + } + } + else { + val initValue = when (type) { + BOOLEAN -> JsLiteral.FALSE + LONG -> JsNameRef(Namer.LONG_ZERO, Namer.kotlinLong()) + else -> JsNumberLiteral.ZERO + } + JsAstUtils.invokeKotlinFunction("newArray", size, initValue) + } + }) + + add(pattern(NamePredicate(type.arrayTypeName), "(Int,Function1)"), intrinsify { _, arguments, context -> + assert(arguments.size == 2) { "Array (Int,Function1) expression must have two arguments." } + val (size, fn) = arguments + if (typedArraysEnabled(context)) { + if (type in TYPED_ARRAY_MAP) { + JsAstUtils.invokeKotlinFunction("fillArray", createTypedArray(type, size), fn) + } + else { + JsAstUtils.invokeKotlinFunction("${type.lowerCaseName}Array", size, fn) + } + } + else { + JsAstUtils.invokeKotlinFunction("newArrayF", size, fn) + } + }) + + add(pattern(NamePredicate(type.arrayTypeName), "iterator"), intrinsify { receiver, _, context -> + if (typedArraysEnabled(context)) { + JsAstUtils.invokeKotlinFunction("${type.lowerCaseName}ArrayIterator", receiver!!) + } + else { + JsAstUtils.invokeKotlinFunction("arrayIterator", receiver!!, + context.program().getStringLiteral(type.arrayTypeName.asString())) + } + }) + } + + add(pattern(NamePredicate(arrayName), "(Int,Function1)"), KotlinFunctionIntrinsic("newArrayF")) + add(pattern(NamePredicate(arrayName), "iterator"), KotlinFunctionIntrinsic("arrayIterator")) + + add(pattern(Namer.KOTLIN_LOWER_NAME, "arrayOfNulls"), KotlinFunctionIntrinsic("newArray", JsLiteral.NULL)) + + val arrayFactoryMethodNames = arrayTypeNames.map { Name.identifier(decapitalize(it.asString() + "Of")) } + val arrayFactoryMethods = pattern(Namer.KOTLIN_LOWER_NAME, NamePredicate(arrayFactoryMethodNames)) + add(arrayFactoryMethods, intrinsify { _, arguments, _ -> arguments[0] }) + } + + private fun intrinsify(f: (receiver: JsExpression?, arguments: List, context: TranslationContext) -> JsExpression) + = object : FunctionIntrinsicWithReceiverComputed() { + override fun apply(receiver: JsExpression?, arguments: List, context: TranslationContext): JsExpression { + return f(receiver, arguments, context) + } + } +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/CompositeFIF.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/CompositeFIF.java index eb5afbdaeae..97e418c2fb2 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/CompositeFIF.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/CompositeFIF.java @@ -22,15 +22,11 @@ import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; -import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.BuiltInPropertyIntrinsic; import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic; import java.util.List; public abstract class CompositeFIF implements FunctionIntrinsicFactory { - @NotNull - public static final BuiltInPropertyIntrinsic LENGTH_PROPERTY_INTRINSIC = new BuiltInPropertyIntrinsic("length"); - @NotNull private final List, FunctionIntrinsic>> patternsAndIntrinsics = Lists.newArrayList(); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java index 39ef6f9f498..cc74aa22ec9 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java @@ -16,12 +16,14 @@ package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.descriptors.CallableDescriptor; +import org.jetbrains.kotlin.descriptors.FunctionDescriptor; +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; import org.jetbrains.kotlin.js.backend.ast.JsExpression; import org.jetbrains.kotlin.js.backend.ast.JsInvocation; import org.jetbrains.kotlin.js.backend.ast.JsNameRef; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.js.patterns.DescriptorPredicate; import org.jetbrains.kotlin.js.patterns.NamePredicate; import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo; @@ -164,13 +166,14 @@ public final class TopLevelFIF extends CompositeFIF { public static final KotlinFunctionIntrinsic TO_STRING = new KotlinFunctionIntrinsic("toString"); @NotNull - public static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsic() { + public static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsicWithReceiverComputed() { @NotNull @Override public JsExpression apply( - @NotNull CallInfo callInfo, @NotNull List arguments, @NotNull TranslationContext context + @Nullable JsExpression receiver, @NotNull List arguments, @NotNull TranslationContext context ) { - return JsAstUtils.charToString(callInfo.getDispatchReceiver()); + assert receiver != null; + return JsAstUtils.charToString(receiver); } }; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt index 4dee0a535e6..04fce5a4d4d 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.js.translate.reference import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.js.backend.ast.* @@ -28,6 +29,7 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.expression.PatternTranslator import org.jetbrains.kotlin.js.translate.general.AbstractTranslator import org.jetbrains.kotlin.js.translate.general.Translation +import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.ArrayFIF import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils @@ -83,7 +85,7 @@ class CallArgumentTranslator private constructor( var argsBeforeVararg: List? = null var concatArguments: MutableList? = null val argsToJsExpr = translateUnresolvedArguments(context(), resolvedCall) - var isVarargTypePrimitive: Boolean? = null + var varargPrimitiveType: PrimitiveType? = null for (parameterDescriptor in valueParameters) { val actualArgument = valueArgumentsByIndex[parameterDescriptor.index] @@ -96,19 +98,21 @@ class CallArgumentTranslator private constructor( hasSpreadOperator = arguments.any { it.getSpreadElement() != null } } - isVarargTypePrimitive = KotlinBuiltIns.isPrimitiveType(parameterDescriptor.original.varargElementType!!) + varargPrimitiveType = KotlinBuiltIns.getPrimitiveType(parameterDescriptor.original.varargElementType!!) if (hasSpreadOperator) { if (isNativeFunctionCall) { argsBeforeVararg = result result = mutableListOf() - concatArguments = prepareConcatArguments(arguments, translateResolvedArgument(actualArgument, argsToJsExpr)) + concatArguments = prepareConcatArguments(arguments, + translateResolvedArgument(actualArgument, argsToJsExpr), + null) } else { result.addAll(translateVarargArgument(actualArgument, argsToJsExpr, actualArgument.arguments.size > 1, - isVarargTypePrimitive)) + varargPrimitiveType)) } } else { @@ -116,7 +120,7 @@ class CallArgumentTranslator private constructor( result.addAll(translateResolvedArgument(actualArgument, argsToJsExpr)) } else { - result.addAll(translateVarargArgument(actualArgument, argsToJsExpr, true, isVarargTypePrimitive)) + result.addAll(translateVarargArgument(actualArgument, argsToJsExpr, true, varargPrimitiveType)) } } } @@ -130,14 +134,14 @@ class CallArgumentTranslator private constructor( assert(concatArguments != null) { "concatArguments should not be null" } if (!result.isEmpty()) { - concatArguments!!.add(JsArrayLiteral(result).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE }) + concatArguments!!.add(toArray(null, result)) } if (!argsBeforeVararg!!.isEmpty()) { - concatArguments!!.add(0, JsArrayLiteral(argsBeforeVararg).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE }) + concatArguments!!.add(0, toArray(null, argsBeforeVararg)) } - result = mutableListOf(concatArgumentsIfNeeded(concatArguments!!, isVarargTypePrimitive!!, true)) + result = mutableListOf(concatArgumentsIfNeeded(concatArguments!!, varargPrimitiveType, true)) if (receiver != null) { cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver) @@ -210,6 +214,74 @@ class CallArgumentTranslator private constructor( return result } + private fun translateVarargArgument( + resolvedArgument: ResolvedValueArgument, + translatedArgs: Map, + shouldWrapVarargInArray: Boolean, + varargPrimitiveType: PrimitiveType? + ): List { + val arguments = resolvedArgument.arguments + if (arguments.isEmpty()) { + return if (shouldWrapVarargInArray) { + return listOf(toArray(varargPrimitiveType, listOf())) + } + else { + listOf() + } + } + + val list = translateResolvedArgument(resolvedArgument, translatedArgs) + + return if (shouldWrapVarargInArray) { + val concatArguments = prepareConcatArguments(arguments, list, varargPrimitiveType) + val concatExpression = concatArgumentsIfNeeded(concatArguments, varargPrimitiveType, false) + listOf(concatExpression) + } + else { + listOf(JsAstUtils.invokeMethod(list[0], "slice")) + } + } + + private fun toArray(varargPrimitiveType: PrimitiveType?, elements: List): JsExpression { + return ArrayFIF.castOrCreatePrimitiveArray(context(), + varargPrimitiveType, + JsArrayLiteral(elements).apply { sideEffects = SideEffectKind.PURE }) + } + + private fun prepareConcatArguments( + arguments: List, + list: List, + varargPrimitiveType: PrimitiveType? + ): MutableList { + assert(arguments.isNotEmpty()) { "arguments.size should not be 0" } + assert(arguments.size == list.size) { "arguments.size: " + arguments.size + " != list.size: " + list.size } + + val concatArguments = mutableListOf() + var lastArrayContent = mutableListOf() + + val size = arguments.size + for (index in 0..size - 1) { + val valueArgument = arguments[index] + val expressionArgument = list[index] + + if (valueArgument.getSpreadElement() != null) { + if (lastArrayContent.size > 0) { + concatArguments.add(toArray(varargPrimitiveType, lastArrayContent)) + lastArrayContent = mutableListOf() + } + concatArguments.add(expressionArgument) + } + else { + lastArrayContent.add(expressionArgument) + } + } + if (lastArrayContent.size > 0) { + concatArguments.add(toArray(varargPrimitiveType, lastArrayContent)) + } + + return concatArguments + } + companion object { @JvmStatic fun translate(resolvedCall: ResolvedCall<*>, receiver: JsExpression?, context: TranslationContext): ArgumentsInfo { @@ -240,43 +312,15 @@ class CallArgumentTranslator private constructor( return resolvedArgument.arguments.map { translatedArgs[it]!! } } - private fun translateVarargArgument( - resolvedArgument: ResolvedValueArgument, - translatedArgs: Map, - shouldWrapVarargInArray: Boolean, - isVarargTypePrimitive: Boolean - ): List { - val arguments = resolvedArgument.arguments - if (arguments.isEmpty()) { - return if (shouldWrapVarargInArray) { - return listOf(JsArrayLiteral(listOf()).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE }) - } - else { - listOf() - } - } - - val list = translateResolvedArgument(resolvedArgument, translatedArgs) - - return if (shouldWrapVarargInArray) { - val concatArguments = prepareConcatArguments(arguments, list) - val concatExpression = concatArgumentsIfNeeded(concatArguments, isVarargTypePrimitive, false) - listOf(concatExpression) - } - else { - listOf(JsAstUtils.invokeMethod(list[0], "slice")) - } - } - private fun concatArgumentsIfNeeded( concatArguments: List, - isVarargTypePrimitive: Boolean, + varargPrimitiveType: PrimitiveType?, isMixed: Boolean ): JsExpression { assert(concatArguments.isNotEmpty()) { "concatArguments.size should not be 0" } if (concatArguments.size > 1) { - if (isVarargTypePrimitive) { + if (varargPrimitiveType != null) { val method = if (isMixed) "arrayConcat" else "primitiveArrayConcat" return JsAstUtils.invokeKotlinFunction(method, concatArguments[0], *concatArguments.subList(1, concatArguments.size).toTypedArray()) @@ -289,39 +333,6 @@ class CallArgumentTranslator private constructor( return concatArguments[0] } } - - private fun prepareConcatArguments(arguments: List, list: List): MutableList { - assert(arguments.isNotEmpty()) { "arguments.size should not be 0" } - assert(arguments.size == list.size) { "arguments.size: " + arguments.size + " != list.size: " + list.size } - - val concatArguments = mutableListOf() - var lastArrayContent = mutableListOf() - - val size = arguments.size - for (index in 0..size - 1) { - val valueArgument = arguments[index] - val expressionArgument = list[index] - - if (valueArgument.getSpreadElement() != null) { - if (lastArrayContent.size > 0) { - concatArguments.add(JsArrayLiteral(lastArrayContent).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE }) - concatArguments.add(expressionArgument) - lastArrayContent = mutableListOf() - } - else { - concatArguments.add(expressionArgument) - } - } - else { - lastArrayContent.add(expressionArgument) - } - } - if (lastArrayContent.size > 0) { - concatArguments.add(JsArrayLiteral(lastArrayContent).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE }) - } - - return concatArguments - } } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java index a8638e18a95..905b1a4a668 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsAstUtils.java @@ -16,13 +16,13 @@ package org.jetbrains.kotlin.js.translate.utils; -import org.jetbrains.kotlin.js.backend.ast.*; -import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; -import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind; import com.intellij.util.SmartList; import kotlin.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.js.backend.ast.*; +import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; +import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind; import org.jetbrains.kotlin.js.translate.context.Namer; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.types.expressions.OperatorConventions; @@ -575,4 +575,13 @@ public final class JsAstUtils { MetadataProperties.setCoroutineReceiver(result, true); return result; } + + @NotNull + public static JsExpression comma(JsExpression first, JsExpression... tail) { + JsExpression result = first; + for (JsExpression e : tail) { + result = new JsBinaryOperation(JsBinaryOperator.COMMA, result, e); + } + return result; + } } diff --git a/js/js.translator/testData/rhino-polyfills.js b/js/js.translator/testData/rhino-polyfills.js new file mode 100644 index 00000000000..a495434b014 --- /dev/null +++ b/js/js.translator/testData/rhino-polyfills.js @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function() { + function normalizeOffset(offset, length) { + if (offset < 0) return Math.max(0, offset + length); + return Math.min(offset, length); + } + function typedArraySlice(begin, end) { + if (typeof end === "undefined") { + end = this.length; + } + begin = normalizeOffset(begin || 0, this.length); + end = Math.max(begin, normalizeOffset(end, this.length)); + return new this.constructor(this.subarray(begin, end)); + } + + var arrays = [Int8Array, Int16Array, Uint16Array, Int32Array, Float32Array, Float64Array]; + for (var i = 0; i < arrays.length; ++i) { + var TypedArray = arrays[i]; + if (typeof TypedArray.prototype.slice === "undefined") { + Object.defineProperty(TypedArray.prototype, 'slice', { + value: typedArraySlice + }); + } + } + + // Patch apply to work with TypedArrays if needed. + try { + (function() {}).apply(null, new Int32Array(0)) + } catch (e) { + var apply = Function.prototype.apply; + Object.defineProperty(Function.prototype, 'apply', { + value: function(self, array) { + return apply.call(this, self, [].slice.call(array)); + } + }); + } +})(); \ No newline at end of file diff --git a/js/js.translator/testData/test.html b/js/js.translator/testData/test.html index b8b40ad3d0c..f3888d957fe 100644 --- a/js/js.translator/testData/test.html +++ b/js/js.translator/testData/test.html @@ -3,10 +3,10 @@ - + diff --git a/libraries/stdlib/test/collections/ArraysTest.kt b/libraries/stdlib/test/collections/ArraysTest.kt index 9c37319fc68..be0729f8927 100644 --- a/libraries/stdlib/test/collections/ArraysTest.kt +++ b/libraries/stdlib/test/collections/ArraysTest.kt @@ -228,11 +228,10 @@ class ArraysTest { assertEquals(arr.asList().toString(), arr.contentToString()) } -// @Ignore("KT-16056") -// @Test fun contentDeepToString() { -// val arr = arrayOf("aa", 1, null, charArrayOf('d')) -// assertEquals("[aa, 1, null, [d]]", arr.contentDeepToString()) -// } + @Test fun contentDeepToString() { + val arr = arrayOf("aa", 1, null, charArrayOf('d')) + assertEquals("[aa, 1, null, [d]]", arr.contentDeepToString()) + } @Test fun contentDeepToStringNoRecursion() { // a[b[a, b]] diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptions.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptions.kt index ee46dd8a4a5..e752eb9daa3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptions.kt @@ -48,4 +48,10 @@ interface KotlinJsOptions : org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions * Default value: "v5" */ var target: kotlin.String -} + + /** + * Translate primitive arrays to JS typed arrays + * Default value: false + */ + var typedArrays: kotlin.Boolean +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptionsBase.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptionsBase.kt index 37dda0ca9bd..68c9fd7dcb5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptionsBase.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptionsBase.kt @@ -59,6 +59,11 @@ internal abstract class KotlinJsOptionsBase : org.jetbrains.kotlin.gradle.dsl.Ko get() = targetField ?: "v5" set(value) { targetField = value } + private var typedArraysField: kotlin.Boolean? = null + override var typedArrays: kotlin.Boolean + get() = typedArraysField ?: false + set(value) { typedArraysField = value } + internal open fun updateArguments(args: org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments) { apiVersionField?.let { args.apiVersion = it } languageVersionField?.let { args.languageVersion = it } @@ -71,6 +76,7 @@ internal abstract class KotlinJsOptionsBase : org.jetbrains.kotlin.gradle.dsl.Ko outputFileField?.let { args.outputFile = it } sourceMapField?.let { args.sourceMap = it } targetField?.let { args.target = it } + typedArraysField?.let { args.typedArrays = it } } } @@ -86,4 +92,5 @@ internal fun org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments.fil outputFile = null sourceMap = false target = "v5" -} + typedArrays = false +} \ No newline at end of file diff --git a/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateStandardLib.kt b/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateStandardLib.kt index f6d6b604963..852529e6f99 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateStandardLib.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateStandardLib.kt @@ -103,6 +103,7 @@ private fun List.writeTo(file: File, sourceFile: SourceFile, p writer.appendln("import kotlin.js.*") if (sourceFile == SourceFile.Arrays) { writer.appendln("import primitiveArrayConcat") + writer.appendln("import withType") } } writer.append("import kotlin.comparisons.*\n\n") diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJVM.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJVM.kt index 820487289c4..44e34be8ab9 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJVM.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/SpecialJVM.kt @@ -211,33 +211,43 @@ object CommonArrays { } } - fun f_plusCollection() = f("plus(elements: Collection)") { - operator(true) + fun f_plusCollection() = + (listOf(InvariantArraysOfObjects to null) + PrimitiveType.defaultPrimitives.map { ArraysOfPrimitives to it }).map { + val (family, primitive) = it + f("plus(elements: Collection)") { + operator(true) - // TODO: inline arrayPlusCollection when @PublishedAPI is available -// inline(Platform.JS, Inline.Yes) -// annotations(Platform.JS, """@Suppress("NOTHING_TO_INLINE")""") + only(family) + if (family == InvariantArraysOfObjects) { + only(Platform.JS, ArraysOfObjects) + } - only(InvariantArraysOfObjects, ArraysOfPrimitives) - only(Platform.JS, ArraysOfObjects, ArraysOfPrimitives) + // TODO: inline arrayPlusCollection when @PublishedAPI is available +// inline(Platform.JS, Inline.Yes) +// annotations(Platform.JS, """@Suppress("NOTHING_TO_INLINE")""") - returns("SELF") - returns(Platform.JS, ArraysOfObjects) { "Array" } - doc { "Returns an array containing all elements of the original array and then all elements of the given [elements] collection." } - body(Platform.JVM) { - """ - var index = size - val result = java.util.Arrays.copyOf(this, index + elements.size) - for (element in elements) result[index++] = element - return result - """ - } - body(Platform.JS) { - """ - return arrayPlusCollection(this, elements) - """ - } - } + returns("SELF") + returns(Platform.JS, ArraysOfObjects) { "Array" } + doc { "Returns an array containing all elements of the original array and then all elements of the given [elements] collection." } + body(Platform.JVM) { + """ + var index = size + val result = java.util.Arrays.copyOf(this, index + elements.size) + for (element in elements) result[index++] = element + return result + """ + } + if (primitive != null) { + only(primitive) + } + when (primitive) { + null, PrimitiveType.Boolean, PrimitiveType.Long -> + body(Platform.JS) { "return arrayPlusCollection(this, elements)" } + else -> + body(Platform.JS) { "return fillFromCollection(this.copyOf(size + elements.size), this.size, elements)" } + } + } + } fun f_plusArray() = f("plus(elements: SELF)") { operator(true) @@ -275,81 +285,110 @@ object CommonArrays { } - fun f_copyOfRange() = f("copyOfRange(fromIndex: Int, toIndex: Int)") { - inline(Platform.JVM, Inline.Only) - inline(Platform.JS, Inline.Yes) - annotations(Platform.JS, """@Suppress("NOTHING_TO_INLINE")""") + fun f_copyOfRange() = (listOf(InvariantArraysOfObjects to null) + PrimitiveType.defaultPrimitives.map { ArraysOfPrimitives to it }).map { + val (family, primitive) = it + f("copyOfRange(fromIndex: Int, toIndex: Int)") { + only(family) + if (family == InvariantArraysOfObjects) { + only(Platform.JS, ArraysOfObjects) + } - only(InvariantArraysOfObjects, ArraysOfPrimitives) - only(Platform.JS, ArraysOfObjects, ArraysOfPrimitives) + inline(Platform.JVM, Inline.Only) - doc { "Returns new array which is a copy of range of original array." } - returns("SELF") - returns(Platform.JS, ArraysOfObjects) { "Array" } - body(Platform.JVM) { - "return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)" - } - body(Platform.JS) { - // TODO: Arguments checking as in java? - "return this.asDynamic().slice(fromIndex, toIndex)" + doc { "Returns new array which is a copy of range of original array." } + returns("SELF") + returns(Platform.JS, ArraysOfObjects) { "Array" } + body(Platform.JVM) { + "return java.util.Arrays.copyOfRange(this, fromIndex, toIndex)" + } + + if (primitive != null) { + only(primitive) + } + when (primitive) { + PrimitiveType.Char, PrimitiveType.Boolean, PrimitiveType.Long -> + body(Platform.JS) { "return withType(\"${primitive}Array\", this.asDynamic().slice(fromIndex, toIndex))" } + else -> { + annotations(Platform.JS, """@Suppress("NOTHING_TO_INLINE")""") + inline(Platform.JS, Inline.Yes) + body(Platform.JS) { "return this.asDynamic().slice(fromIndex, toIndex)" } + } + } } } - fun f_copyOf() = f("copyOf()") { - inline(Platform.JVM, Inline.Only) - inline(Platform.JS, Inline.Yes) - annotations(Platform.JS, """@Suppress("NOTHING_TO_INLINE")""") + fun f_copyOf() = (listOf(InvariantArraysOfObjects to null) + PrimitiveType.defaultPrimitives.map { ArraysOfPrimitives to it }).map { + val (family, primitive) = it + f("copyOf()") { + only(family) + if (family == InvariantArraysOfObjects) { + only(Platform.JS, ArraysOfObjects) + } - only(InvariantArraysOfObjects, ArraysOfPrimitives) - only(Platform.JS, ArraysOfObjects, ArraysOfPrimitives) + inline(Platform.JVM, Inline.Only) - only(InvariantArraysOfObjects, ArraysOfPrimitives) - doc { "Returns new array which is a copy of the original array." } - returns("SELF") - returns(Platform.JS, ArraysOfObjects) { "Array" } + doc { "Returns new array which is a copy of the original array." } + returns("SELF") + returns(Platform.JS, ArraysOfObjects) { "Array" } - body(Platform.JVM) { - "return java.util.Arrays.copyOf(this, size)" - } - body(Platform.JS) { - "return this.asDynamic().slice()" + body(Platform.JVM) { + "return java.util.Arrays.copyOf(this, size)" + } + body(Platform.JS) { + "return this.asDynamic().slice()" + } + + if (primitive != null) { + only(primitive) + } + when (primitive) { + PrimitiveType.Char, PrimitiveType.Boolean, PrimitiveType.Long -> + body(Platform.JS) { "return withType(\"${primitive}Array\", this.asDynamic().slice())" } + else -> { + annotations(Platform.JS, """@Suppress("NOTHING_TO_INLINE")""") + inline(Platform.JS, Inline.Yes) + body(Platform.JS) { "return this.asDynamic().slice()" } + } + } } } fun f_copyOfResized() = - (PrimitiveType.defaultPrimitives.map { ArraysOfPrimitives to it } + (InvariantArraysOfObjects to null)).map { - val (family, primitive) = it - f("copyOf(newSize: Int)") { - only(family) - if (family == InvariantArraysOfObjects) { - only(Platform.JS, ArraysOfObjects) - } - - inline(Platform.JVM, Inline.Only) - doc { "Returns new array which is a copy of the original array, resized to the given [newSize]." } - val defaultValue: String - if (primitive != null) { - only(primitive) - returns("SELF") - defaultValue = when (primitive) { - PrimitiveType.Boolean -> false.toString() - PrimitiveType.Char -> "0" - else -> "ZERO" + (PrimitiveType.defaultPrimitives.map { ArraysOfPrimitives to it } + (InvariantArraysOfObjects to null)).map { + val (family, primitive) = it + f("copyOf(newSize: Int)") { + only(family) + if (family == InvariantArraysOfObjects) { + only(Platform.JS, ArraysOfObjects) + } + + inline(Platform.JVM, Inline.Only) + doc { "Returns new array which is a copy of the original array, resized to the given [newSize]." } + + if (primitive != null) { + only(primitive) + returns("SELF") + when (primitive) { + PrimitiveType.Boolean -> + body(Platform.JS) { "return withType(\"BooleanArray\", arrayCopyResize(this, newSize, false))" } + PrimitiveType.Char -> + body(Platform.JS) { "return withType(\"CharArray\", fillFrom(this, ${primitive}Array(newSize)))" } + PrimitiveType.Long -> + body(Platform.JS) { "return withType(\"LongArray\", arrayCopyResize(this, newSize, ZERO))" } + else -> + body(Platform.JS) { "return fillFrom(this, ${primitive}Array(newSize))" } + } + } + else { + returns { "Array" } + body(Platform.JS) { "return arrayCopyResize(this, newSize, null)" } + } + + body(Platform.JVM) { + "return java.util.Arrays.copyOf(this, newSize)" } - } else { - returns { "Array" } - defaultValue = "null" - } - body(Platform.JVM) { - "return java.util.Arrays.copyOf(this, newSize)" - } - body(Platform.JS) { - """ - return arrayCopyResize(this, newSize, $defaultValue) - """ } } - } fun f_sortPrimitives() = (PrimitiveType.numericPrimitives + PrimitiveType.Char).map { primitive -> @@ -481,12 +520,13 @@ object CommonArrays { return result as Array """ } - - if (primitive == PrimitiveType.Char) { - body(Platform.JS) { "return Array(size, { i -> this[i] })" } - } - else { - body(Platform.JS) { "return copyOf().unsafeCast>()" } + when (primitive) { + PrimitiveType.Char -> + body(Platform.JS) { "return Array(size, { i -> this[i] })" } + PrimitiveType.Boolean, PrimitiveType.Long -> + body(Platform.JS) { "return copyOf().unsafeCast>()" } + else -> + body(Platform.JS) { "return js(\"[]\").slice.call(this)" } } } } @@ -496,7 +536,10 @@ object CommonArrays { fun templates() = listOf(f_plusElement()) + f_plusElementOperator() + - listOf(f_plusCollection(), f_plusArray(), f_copyOf(), f_copyOfRange()) + + f_plusCollection() + + listOf(f_plusArray()) + + f_copyOf() + + f_copyOfRange() + f_copyOfResized() + f_sortPrimitives() + listOf(f_sort(), f_sortWith(), f_asList()) +