From 67dfe7bed1a43cb5240fb84dee867268273e5544 Mon Sep 17 00:00:00 2001 From: Sergej Jaskiewicz Date: Wed, 1 Dec 2021 16:22:09 +0300 Subject: [PATCH] [JS IR] Make Char a value class (again) #KT-35100 Fixed --- .../kotlin/js/test/BoxJsTestGenerated.java | 6 + .../js/test/ir/IrBoxJsTestGenerated.java | 6 + .../testData/box/char/charInExternalDecl.js | 69 ++++++++++ .../testData/box/char/charInExternalDecl.kt | 121 ++++++++++++++++++ .../testData/box/char/topLevelCallables.kt | 9 +- .../testData/box/coercion/bridgeChar.kt | 3 +- .../testData/box/coercion/charValParameter.kt | 4 +- .../testData/box/coercion/defaultAccessors.kt | 3 +- .../coercion/derivedFunctionReturningChar.kt | 2 - .../box/coercion/extensionReceiver.kt | 2 - .../testData/box/coercion/lambdaParameters.kt | 10 +- .../box/coercion/propertyBridgeChar.kt | 4 +- .../box/coercion/receiverSmartCast.kt | 2 - .../standardClasses/arrayFactoryMethods.kt | 2 - .../box/standardClasses/charArrayGetSet.kt | 3 +- libraries/stdlib/api/js-v1/kotlin.kt | 2 +- libraries/stdlib/api/js/kotlin.kt | 2 +- libraries/stdlib/js-ir/builtins/Char.kt | 24 +--- libraries/stdlib/js-ir/runtime/arrays.kt | 3 +- 19 files changed, 226 insertions(+), 51 deletions(-) create mode 100644 js/js.translator/testData/box/char/charInExternalDecl.js create mode 100644 js/js.translator/testData/box/char/charInExternalDecl.kt diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java index c88a47eebac..ef1e503d1f4 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java @@ -216,6 +216,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/char/charEquals.kt"); } + @Test + @TestMetadata("charInExternalDecl.kt") + public void testCharInExternalDecl() throws Exception { + runTest("js/js.translator/testData/box/char/charInExternalDecl.kt"); + } + @Test @TestMetadata("charInStringTemplate.kt") public void testCharInStringTemplate() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index 4c0f3c9d4b0..4dbc063a873 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -216,6 +216,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/char/charEquals.kt"); } + @Test + @TestMetadata("charInExternalDecl.kt") + public void testCharInExternalDecl() throws Exception { + runTest("js/js.translator/testData/box/char/charInExternalDecl.kt"); + } + @Test @TestMetadata("charInStringTemplate.kt") public void testCharInStringTemplate() throws Exception { diff --git a/js/js.translator/testData/box/char/charInExternalDecl.js b/js/js.translator/testData/box/char/charInExternalDecl.js new file mode 100644 index 00000000000..afa40fa30de --- /dev/null +++ b/js/js.translator/testData/box/char/charInExternalDecl.js @@ -0,0 +1,69 @@ +function actualTypeOfChar(x) { + return typeof x; +} + +function actualTypeOfMaybeChar(x) { + return actualTypeOfChar(x); +} + +function getSomeChar() { + return 'a'.charCodeAt(0); +} + +function getMaybeChar() { + return getSomeChar(); +} + +function getCharNull() { + return null; +} + +var charVal = getSomeChar(); +var maybeCharVal = getMaybeChar(); +var charNullVal = getCharNull(); + +var charVar = getSomeChar(); +var maybeCharVar = null; + +function A(c) { + this.c = c; + this._baz = 'q'.charCodeAt(0); + this._nullableBaz = null; +} + +A.prototype.foo = function () { + return this.c; +}; + +A.prototype.maybeFoo = A.prototype.foo; +A.prototype.fooNull = getCharNull; + +A.prototype.actualTypeOfChar = actualTypeOfChar +A.prototype.actualTypeOfMaybeChar = actualTypeOfMaybeChar + +Object.defineProperty(A.prototype, 'bar', { + configurable: true, + get: A.prototype.foo +}); + +Object.defineProperty(A.prototype, 'maybeBar', { + configurable: true, + get: A.prototype.maybeFoo +}); + +Object.defineProperty(A.prototype, 'barNull', { + configurable: true, + get: A.prototype.fooNull +}); + +Object.defineProperty(A.prototype, 'baz', { + configurable: true, + get: function () { return this._baz; }, + set: function (v) { this._baz = v; } +}); + +Object.defineProperty(A.prototype, 'maybeBaz', { + configurable: true, + get: function () { return this._nullableBaz; }, + set: function (v) { this._nullableBaz = v; } +}); diff --git a/js/js.translator/testData/box/char/charInExternalDecl.kt b/js/js.translator/testData/box/char/charInExternalDecl.kt new file mode 100644 index 00000000000..63da389a47d --- /dev/null +++ b/js/js.translator/testData/box/char/charInExternalDecl.kt @@ -0,0 +1,121 @@ +// See KT-43224 + +external fun actualTypeOfChar(x: Char): String +external fun actualTypeOfMaybeChar(x: Char?): String + +external fun getSomeChar(): Char +external fun getMaybeChar(): Char? +external fun getCharNull(): Char? + +external val charVal: Char +external val maybeCharVal: Char? +external val charNullVal: Char? + +external var charVar: Char +external var maybeCharVar: Char? + +external interface I { + fun foo(): Char + fun maybeFoo(): Char? + fun fooNull(): Char? + fun actualTypeOfChar(x: Char): String + fun actualTypeOfMaybeChar(x: Char?): String + + val bar: Char + val maybeBar: Char? + val barNull: Char? + + var baz: Char + var maybeBaz: Char? +} + +external class A(c: Char): I { + override fun foo(): Char + override fun maybeFoo(): Char? + override fun fooNull(): Char? + override fun actualTypeOfChar(x: Char): String + override fun actualTypeOfMaybeChar(x: Char?): String + + override val bar: Char + override val maybeBar: Char? + override val barNull: Char? + + override var baz: Char + override var maybeBaz: Char? +} + +val expectedCharRepresentationInProperty = if (testUtils.isLegacyBackend()) "object" else "number" + +fun box(): String { + + if (actualTypeOfChar('a') != "number") return "Fail (actualTypeOfChar)" + if (actualTypeOfMaybeChar('a') != "number") return "Fail (actualTypeOfMaybeChar with nonnull arg)" + if (actualTypeOfMaybeChar(null) != "object") return "Fail (actualTypeOfMaybeChar with null arg)" + + if (getSomeChar() != 'a') return "Fail (getSomeChar)" + if (getMaybeChar() != 'a') return "Fail (getMaybeChar)" + if (getCharNull() != null) return "Fail (getCharNull)" + + if (charVal != 'a') return "Fail (charVal)" + if (maybeCharVal != 'a') return "Fail (maybeCharVal)" + if (charNullVal != null) return "Fail (charNullVal)" + + if (charVar != 'a') return "Fail (charVar initial value)" + charVar = 'b' + if (js("typeof charVar") != "number") return "Fail (typeof charVar after modification)" + if (charVar != 'b') return "Fail (charVar after modification)" + + if (maybeCharVar != null) return "Fail (maybeCharVar initial value)" + maybeCharVar = 'a' + if (js("typeof maybeCharVar") != "number") return "Fail (typeof maybeCharVar after 1st modification)" + if (maybeCharVar != 'a') return "Fail (maybeCharVar after 1st modification)" + maybeCharVar = null + if (js("typeof maybeCharVar") != "object") return "Fail (typeof maybeCharVar after 2nd modification)" + if (maybeCharVar != null) return "Fail (maybeCharVar after 2nd modification)" + + val a = A('b') + if (a.foo() != 'b') return "Fail (a.foo())" + if (a.maybeFoo() != 'b') return "Fail (a.maybeFoo())" + if (a.fooNull() != null) return "Fail (a.fooNull())" + if (a.actualTypeOfChar('a') != "number") return "Fail (a.actualTypeOfChar())" + if (a.actualTypeOfMaybeChar('a') != "number") return "Fail (a.actualTypeOfMaybeChar() with nonnull arg)" + if (a.actualTypeOfMaybeChar(null) != "object") return "Fail (a.actualTypeOfMaybeChar() with null arg)" + if (a.bar != 'b') return "Fail (a.bar)" + if (a.maybeBar != 'b') return "Fail (a.maybeBar)" + if (a.barNull != null) return "Fail (a.barNull)" + if (a.baz != 'q') return "Fail (a.baz initial value)" + a.baz = 'r' + if (js("typeof a._baz") != expectedCharRepresentationInProperty) return "Fail (typeof a._baz after modification)" + if (a.baz != 'r') return "Fail (a.baz after modification)" + if (a.maybeBaz != null) return "Fail (a.maybeBaz initial value)" + a.maybeBaz = 's' + if (js("typeof a._nullableBaz") != expectedCharRepresentationInProperty) return "Fail (typeof a._nullableBaz after 1st modification)" + if (a.maybeBaz != 's') return "Fail (a.maybeBaz after 1st modification)" + a.maybeBaz = null + if (js("typeof a._nullableBaz") != "object") return "Fail (typeof a._nullableBaz after 2nd modification)" + if (a.maybeBaz != null) return "Fail (a.maybeBaz after 2nd modification)" + + val b: I = A('e') + if (b.foo() != 'e') return "Fail (b.foo())" + if (b.maybeFoo() != 'e') return "Fail (b.maybeFoo())" + if (b.fooNull() != null) return "Fail (b.fooNull())" + if (b.actualTypeOfChar('a') != "number") return "Fail (b.actualTypeOfChar())" + if (b.actualTypeOfMaybeChar('a') != "number") return "Fail (b.actualTypeOfMaybeChar() with nonnull arg)" + if (b.actualTypeOfMaybeChar(null) != "object") return "Fail (b.actualTypeOfMaybeChar() with null arg)" + if (b.bar != 'e') return "Fail (b.bar)" + if (b.maybeBar != 'e') return "Fail (b.maybeBar)" + if (b.barNull != null) return "Fail (b.barNull)" + if (b.baz != 'q') return "Fail (b.baz initial value)" + b.baz = 'r' + if (js("typeof b._baz") != expectedCharRepresentationInProperty) return "Fail (typeof b._baz after modification)" + if (b.baz != 'r') return "Fail (b.baz after modification)" + if (b.maybeBaz != null) return "Fail (b.maybeBaz initial value)" + b.maybeBaz = 's' + if (js("typeof b._nullableBaz") != expectedCharRepresentationInProperty) return "Fail (typeof b._nullableBaz after 1st modification)" + if (b.maybeBaz != 's') return "Fail (b.maybeBaz after 1st modification)" + b.maybeBaz = null + if (js("typeof b._nullableBaz") != "object") return "Fail (typeof b._nullableBaz after 2nd modification)" + if (b.maybeBaz != null) return "Fail (b.maybeBaz after 2nd modification)" + + return "OK" +} diff --git a/js/js.translator/testData/box/char/topLevelCallables.kt b/js/js.translator/testData/box/char/topLevelCallables.kt index 536301f9809..b6b3079186a 100644 --- a/js/js.translator/testData/box/char/topLevelCallables.kt +++ b/js/js.translator/testData/box/char/topLevelCallables.kt @@ -1,16 +1,15 @@ // SKIP_MINIFICATION -// Exported declaration uses non-exportable return type: Char -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 - -@JsName("foo") +@JsExport fun foo(): Char = '1' +@JsExport val p1: Char = '2' +@JsExport var p2: Char = '3' +@JsExport var p3: Char = '4' get() = field + 1 set(value) { diff --git a/js/js.translator/testData/box/coercion/bridgeChar.kt b/js/js.translator/testData/box/coercion/bridgeChar.kt index a183662fe77..9caa6a50260 100644 --- a/js/js.translator/testData/box/coercion/bridgeChar.kt +++ b/js/js.translator/testData/box/coercion/bridgeChar.kt @@ -1,6 +1,5 @@ // EXPECTED_REACHABLE_NODES: 1293 -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 + open class A { fun foo(): Char = 'X' } diff --git a/js/js.translator/testData/box/coercion/charValParameter.kt b/js/js.translator/testData/box/coercion/charValParameter.kt index 8e118678174..7f66e3816f8 100644 --- a/js/js.translator/testData/box/coercion/charValParameter.kt +++ b/js/js.translator/testData/box/coercion/charValParameter.kt @@ -1,8 +1,6 @@ // EXPECTED_REACHABLE_NODES: 1276 -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -class A(val x: Char) +class A(@JsName("x") val x: Char) fun typeOf(x: dynamic): String = js("typeof x") diff --git a/js/js.translator/testData/box/coercion/defaultAccessors.kt b/js/js.translator/testData/box/coercion/defaultAccessors.kt index 077af238d95..92dbe4f166e 100644 --- a/js/js.translator/testData/box/coercion/defaultAccessors.kt +++ b/js/js.translator/testData/box/coercion/defaultAccessors.kt @@ -1,6 +1,5 @@ // EXPECTED_REACHABLE_NODES: 1288 -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 + @JsExport interface I { val a: Char diff --git a/js/js.translator/testData/box/coercion/derivedFunctionReturningChar.kt b/js/js.translator/testData/box/coercion/derivedFunctionReturningChar.kt index 368888ee9ab..87f84604411 100644 --- a/js/js.translator/testData/box/coercion/derivedFunctionReturningChar.kt +++ b/js/js.translator/testData/box/coercion/derivedFunctionReturningChar.kt @@ -1,6 +1,4 @@ // EXPECTED_REACHABLE_NODES: 1292 -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 abstract class A { abstract fun foo(): T } diff --git a/js/js.translator/testData/box/coercion/extensionReceiver.kt b/js/js.translator/testData/box/coercion/extensionReceiver.kt index 3e595c19d15..1b247053d73 100644 --- a/js/js.translator/testData/box/coercion/extensionReceiver.kt +++ b/js/js.translator/testData/box/coercion/extensionReceiver.kt @@ -1,6 +1,4 @@ // EXPECTED_REACHABLE_NODES: 1282 -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 fun box(): String { val a = 'Q'.foo() if (a != "number") return "fail1: $a" diff --git a/js/js.translator/testData/box/coercion/lambdaParameters.kt b/js/js.translator/testData/box/coercion/lambdaParameters.kt index 1e1c22a9435..a4b756dc823 100644 --- a/js/js.translator/testData/box/coercion/lambdaParameters.kt +++ b/js/js.translator/testData/box/coercion/lambdaParameters.kt @@ -1,10 +1,8 @@ // EXPECTED_REACHABLE_NODES: 1283 -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 -// CHECK_NOT_CALLED_IN_SCOPE: function=toBoxedChar scope=box$lambda -// CHECK_CALLED_IN_SCOPE: function=unboxChar scope=box$lambda -// CHECK_CALLED_IN_SCOPE: function=toBoxedChar scope=box -// CHECK_NOT_CALLED_IN_SCOPE: function=unboxChar scope=box +// CHECK_NOT_CALLED_IN_SCOPE: function=toBoxedChar scope=box$lambda TARGET_BACKENDS=JS +// CHECK_CALLED_IN_SCOPE: function=unboxChar scope=box$lambda TARGET_BACKENDS=JS +// CHECK_CALLED_IN_SCOPE: function=toBoxedChar scope=box TARGET_BACKENDS=JS +// CHECK_NOT_CALLED_IN_SCOPE: function=unboxChar scope=box TARGET_BACKENDS=JS fun bar(x: T, y: (T) -> Boolean): Boolean = y(x) && jsTypeOf(x.asDynamic()) != "number" diff --git a/js/js.translator/testData/box/coercion/propertyBridgeChar.kt b/js/js.translator/testData/box/coercion/propertyBridgeChar.kt index b365f31c881..5fa6f4df973 100644 --- a/js/js.translator/testData/box/coercion/propertyBridgeChar.kt +++ b/js/js.translator/testData/box/coercion/propertyBridgeChar.kt @@ -1,7 +1,6 @@ // EXPECTED_REACHABLE_NODES: 1289 -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 +@JsExport open class A { val foo: Char get() = 'X' @@ -31,6 +30,7 @@ interface I { val mutable: Any } +@JsExport class B : A(), I fun typeOf(x: dynamic): String = js("typeof x") diff --git a/js/js.translator/testData/box/coercion/receiverSmartCast.kt b/js/js.translator/testData/box/coercion/receiverSmartCast.kt index 17a3a5e577a..0ba720655bc 100644 --- a/js/js.translator/testData/box/coercion/receiverSmartCast.kt +++ b/js/js.translator/testData/box/coercion/receiverSmartCast.kt @@ -1,6 +1,4 @@ // EXPECTED_REACHABLE_NODES: 1283 -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 fun foo(x: Any): String { return when (x) { is Char -> "char: ${x.toInt()}" diff --git a/js/js.translator/testData/box/standardClasses/arrayFactoryMethods.kt b/js/js.translator/testData/box/standardClasses/arrayFactoryMethods.kt index 12ea9515021..595386fee49 100644 --- a/js/js.translator/testData/box/standardClasses/arrayFactoryMethods.kt +++ b/js/js.translator/testData/box/standardClasses/arrayFactoryMethods.kt @@ -1,6 +1,4 @@ // EXPECTED_REACHABLE_NODES: 1293 -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 package foo diff --git a/js/js.translator/testData/box/standardClasses/charArrayGetSet.kt b/js/js.translator/testData/box/standardClasses/charArrayGetSet.kt index fd44bf1ab91..86acb5ea2ab 100644 --- a/js/js.translator/testData/box/standardClasses/charArrayGetSet.kt +++ b/js/js.translator/testData/box/standardClasses/charArrayGetSet.kt @@ -1,6 +1,5 @@ // EXPECTED_REACHABLE_NODES: 1281 -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 + fun box(): String { val a = CharArray(1) val aType = jsTypeOf(a.asDynamic()[0]) diff --git a/libraries/stdlib/api/js-v1/kotlin.kt b/libraries/stdlib/api/js-v1/kotlin.kt index 20ea08bca11..8f626ccc2cf 100644 --- a/libraries/stdlib/api/js-v1/kotlin.kt +++ b/libraries/stdlib/api/js-v1/kotlin.kt @@ -1064,7 +1064,7 @@ public final class ByteArray { public final operator fun set(index: kotlin.Int, value: kotlin.Byte): kotlin.Unit } -public final class Char : kotlin.Comparable { +/*∆*/ public final class Char : kotlin.Comparable { public open override operator fun compareTo(other: kotlin.Char): kotlin.Int public final operator fun dec(): kotlin.Char diff --git a/libraries/stdlib/api/js/kotlin.kt b/libraries/stdlib/api/js/kotlin.kt index 2e629db9e7a..d3c22e911d2 100644 --- a/libraries/stdlib/api/js/kotlin.kt +++ b/libraries/stdlib/api/js/kotlin.kt @@ -1032,7 +1032,7 @@ public final class ByteArray { public final operator fun set(index: kotlin.Int, value: kotlin.Byte): kotlin.Unit } -public final class Char : kotlin.Comparable { +/*∆*/ public final inline class Char : kotlin.Comparable { /*∆*/ @kotlin.SinceKotlin(version = "1.5") /*∆*/ @kotlin.WasExperimental(markerClass = {kotlin.ExperimentalStdlibApi::class}) /*∆*/ public constructor Char(code: kotlin.UShort) diff --git a/libraries/stdlib/js-ir/builtins/Char.kt b/libraries/stdlib/js-ir/builtins/Char.kt index 600ff0c5bcd..8b6534810fe 100644 --- a/libraries/stdlib/js-ir/builtins/Char.kt +++ b/libraries/stdlib/js-ir/builtins/Char.kt @@ -9,13 +9,13 @@ package kotlin * Represents a 16-bit Unicode character. * On the JVM, non-nullable values of this type are represented as values of the primitive type `char`. */ -// TODO: KT-35100 -//public inline class Char internal constructor (val value: Int) : Comparable { -public class Char -@SinceKotlin("1.5") -@WasExperimental(ExperimentalStdlibApi::class) -constructor(code: UShort) : Comparable { - private val value: Int = code.toInt() +public value class Char +@kotlin.internal.LowPriorityInOverloadResolution +internal constructor(private val value: Int) : Comparable { + + @SinceKotlin("1.5") + @WasExperimental(ExperimentalStdlibApi::class) + public constructor(code: UShort) : this(code.toInt()) /** * Compares this value with the specified value for order. @@ -76,16 +76,6 @@ constructor(code: UShort) : Comparable { @DeprecatedSinceKotlin(warningSince = "1.5") public fun toDouble(): Double = value.toDouble() - override fun equals(other: Any?): Boolean { - @Suppress("IMPLICIT_BOXING_IN_IDENTITY_EQUALS") - if (other === this) return true - if (other !is Char) return false - - return this.value == other.value - } - - override fun hashCode(): Int = value - // TODO implicit usages of toString and valueOf must be covered in DCE @Suppress("JS_NAME_PROHIBITED_FOR_OVERRIDE") @JsName("toString") diff --git a/libraries/stdlib/js-ir/runtime/arrays.kt b/libraries/stdlib/js-ir/runtime/arrays.kt index 11f2ac129e4..a4aad8d4898 100644 --- a/libraries/stdlib/js-ir/runtime/arrays.kt +++ b/libraries/stdlib/js-ir/runtime/arrays.kt @@ -34,8 +34,7 @@ internal fun booleanArray(size: Int): BooleanArray = withType("BooleanArray", fi internal fun booleanArrayOf(arr: Array): BooleanArray = withType("BooleanArray", arr.asDynamic().slice()).unsafeCast() -//internal fun charArray(size: Int): CharArray = withType("CharArray", fillArrayVal(Array(size), 0)).unsafeCast() -internal fun charArray(size: Int): CharArray = withType("CharArray", fillArrayVal(Array(size), Char(0))).unsafeCast() +internal fun charArray(size: Int): CharArray = withType("CharArray", fillArrayVal(Array(size), 0)).unsafeCast() internal fun charArrayOf(arr: Array): CharArray = withType("CharArray", arr.asDynamic().slice()).unsafeCast()