Add mechanism for type coercion in JS

Use it for char boxing/unboxing and unit materialization.
Possible to use for other purposes, for example, to add type checks
to dynamics.

See KT-18793, KT-17915, KT-19081, KT-18216, KT-12970, KT-17014,
KT-13932, KT-13930
This commit is contained in:
Alexey Andreev
2017-07-18 16:42:30 +03:00
parent ae509d5980
commit 37fa45dc34
63 changed files with 959 additions and 264 deletions
+11 -1
View File
@@ -9,5 +9,15 @@ fun box(): String {
assertEquals(false, 'A'== 'B')
assertEquals(false, ('A' as Any) == (65 as Any))
assertTrue(bar('Q'))
assertFalse(bar('W'))
assertTrue(baz('Q'))
assertFalse(baz('W'))
return "OK"
}
}
fun bar(x: Char) = x.equals('Q')
fun baz(x: Any) = x.equals('Q')
+18
View File
@@ -0,0 +1,18 @@
// EXPECTED_REACHABLE_NODES: 1004
class A {
var log = ""
fun foo() {
log += "foo()"
}
val bar: Any = foo()
}
fun box(): String {
val a = A()
if (a.bar != Unit) return "fail1: ${a.bar}"
if (a.log != "foo()") return "fail2: ${a.log}"
return "OK"
}
@@ -0,0 +1,17 @@
// EXPECTED_REACHABLE_NODES: 1006
abstract class A<out T> {
abstract fun foo(): T
}
class B() : A<Char>() {
override fun foo() = 'Q'
}
private fun typeOf(x: dynamic) = js("typeof x")
fun box(): String {
val a: A<Any> = B()
if (typeOf(a.foo()) != "object") return "fail1"
if (typeOf(B().foo()) != "number") return "fail2"
return "OK"
}
@@ -0,0 +1,20 @@
// EXPECTED_REACHABLE_NODES: 1012
var log = ""
abstract class A<out T> {
abstract fun foo(): T
}
class B() : A<Unit>() {
override fun foo() {
log += "B.foo()"
}
}
fun box(): String {
val a: A<Any> = B()
if (a.foo() != Unit) return "fail1"
if (log != "B.foo()") return "fail2"
return "OK"
}
@@ -0,0 +1,20 @@
// EXPECTED_REACHABLE_NODES: 1006
var log = ""
class A {
operator fun component1() {
log += "A.component1()"
}
operator fun component2() = 23
}
fun box(): String {
val (x: Any, y) = A()
if (x != Unit) return "fail1: $x"
if (y != 23) return "fail2: $y"
if (log != "A.component1()") return "fail3: $log"
return "OK"
}
@@ -0,0 +1,14 @@
// EXPECTED_REACHABLE_NODES: 996
fun box(): String {
val a = 'Q'.foo()
if (a != "number") return "fail1: $a"
val b = 'W'.bar()
if (b != "object") return "fail2: $b"
return "OK"
}
fun Char.foo() = jsTypeOf(this.asDynamic())
fun Any.bar() = jsTypeOf(this.asDynamic())
+19
View File
@@ -0,0 +1,19 @@
// EXPECTED_REACHABLE_NODES: 1005
var log = ""
fun foo() {
log += "foo()"
}
fun test(x: Int) = if (x < 10) foo() else 55
fun box(): String {
val a = test(20)
if (a !is Int) return "fail1: $a"
val b = test(5)
if (b !is Unit) return "fail2: $b"
if (log != "foo()") return "fail3: $log"
return "OK"
}
@@ -0,0 +1,12 @@
// EXPECTED_REACHABLE_NODES: 1000
inline fun foo(i : Int) = if (i % 2 == 0) {} else i
fun box(): String {
val a = foo(1)
if (a != 1) return "fail1: $a"
val b = foo(2)
if (b != Unit) return "fail2: $b"
return "OK"
}
@@ -0,0 +1,22 @@
// EXPECTED_REACHABLE_NODES: 997
// 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
fun <T> bar(x: T, y: (T) -> Boolean): Boolean = y(x) && jsTypeOf(x.asDynamic()) != "number"
fun typeOf(x: dynamic) = js("typeof x")
fun box(): String {
val f = { x: Char ->
val a: Char = x
val b: Any = x
typeOf(a) == "number" && typeOf(b) == "object"
}
if (!f('Q')) return "fail1"
if (!bar('W', f)) return "fail2"
return "OK"
}
+25
View File
@@ -0,0 +1,25 @@
// EXPECTED_REACHABLE_NODES: 1009
class A {
operator fun iterator() = B()
}
class B() {
private var count = 0
operator fun next() {
count++
}
operator fun hasNext() = count < 5
}
fun box(): String {
var i = 0
for (x: Any in A()) {
if (x != Unit) return "fail1: $x"
i++
}
if (i != 5) return "fail2: $i"
return "OK"
}
@@ -0,0 +1,32 @@
// EXPECTED_REACHABLE_NODES: 997
fun foo(x: Any): String {
return when (x) {
is Char -> "char: ${x.toInt()}"
else -> "other: $x"
}
}
fun bar(x: Any): String {
return when (x) {
is Char -> "char: ${x.baz()}"
else -> "other: $x"
}
}
fun Char.baz(): Boolean = jsTypeOf(asDynamic()) == "number"
fun box(): String {
val a = foo('0')
if (a != "char: 48") return "fail1: $a"
val b = foo(23)
if (b != "other: 23") return "fail2: $b"
val c = bar('0')
if (c != "char: true") return "fail3: $c"
val d = bar(23)
if (d != "other: 23") return "fail4: $d"
return "OK"
}
@@ -0,0 +1,19 @@
// EXPECTED_REACHABLE_NODES: 1004
var log = ""
fun test(param: Any?) {
param?.let {
log += "test($param);"
} ?: run {
log += "test-null;"
}
}
fun box(): String {
test(null)
test(23)
if (log != "test-null;test(23);") return "fail: $log"
return "OK"
}
@@ -0,0 +1,15 @@
// EXPECTED_REACHABLE_NODES: 1003
var log = ""
fun foo() {
log += "foo()"
}
val bar: Any = foo()
fun box(): String {
if (bar != Unit) return "fail1: $bar"
if (log != "foo()") return "fail2: $log"
return "OK"
}
@@ -0,0 +1,19 @@
// EXPECTED_REACHABLE_NODES: 1004
fun test(x: Int): Any {
return try {
if (x % 2 == 0) throw RuntimeException()
x
}
catch (e: RuntimeException) {
}
}
fun box(): String {
val a = test(1)
if (a != 1) return "fail1: $a"
val b = test(2)
if (b != Unit) return "fail2: $b"
return "OK"
}
@@ -0,0 +1,17 @@
// EXPECTED_REACHABLE_NODES: 1004
var log = ""
fun foo() {
log += "foo"
}
fun Unit.bar() = jsTypeOf(this.asDynamic()) == "undefined"
fun Any.baz() = jsTypeOf(this.asDynamic()) == "object"
fun box(): String {
if (!foo().bar()) return "fail1"
if (!foo().baz()) return "fail2"
return "OK"
}
+14
View File
@@ -0,0 +1,14 @@
// EXPECTED_REACHABLE_NODES: 1002
var log = ""
fun foo(): Unit {
log += "foo();"
}
fun box(): String {
if (foo() !is Any) return "fail1"
if (foo() as Any != Unit) return "fail2"
if (log != "foo();foo();") return "fail3: $log"
return "OK"
}
+14
View File
@@ -0,0 +1,14 @@
// EXPECTED_REACHABLE_NODES: 1004
class C {
fun foo() {}
}
fun box(): String {
val a: C? = C()
val b: C? = null
if (a?.foo() != Unit) return "fail1: ${a?.foo()}"
if (b?.foo() != null) return "fail2: ${b?.foo()}"
return "OK"
}
+22
View File
@@ -0,0 +1,22 @@
// EXPECTED_REACHABLE_NODES: 1005
var log = ""
fun foo() {
log += "foo()"
}
fun test(x: Int) = when (x) {
in 0..10 -> foo()
else -> 55
}
fun box(): String {
val a = test(20)
if (a !is Int) return "fail1: $a"
val b = test(5)
if (b !is Unit) return "fail2: $b"
if (log != "foo()") return "fail3: $log"
return "OK"
}
@@ -3,7 +3,7 @@
// FILE: lib.kt
package lib
external fun bar()
external fun bar(): Int
val bar = 32
@@ -0,0 +1,26 @@
// EXPECTED_REACHABLE_NODES: 997
fun box(): String {
val a = CharArray(1)
val aType = jsTypeOf(a.asDynamic()[0])
if (aType != "number") return "fail1: $aType"
a[0] = 'Q'
val aType2 = jsTypeOf(a.asDynamic()[0])
if (aType2 != "number") return "fail2: $aType2"
val aType3 = jsTypeOf(a[0].asDynamic())
if (aType3 != "number") return "fail3: $aType3"
val b = Array<Char>(1) { 'Q' }
val bType = jsTypeOf(b.asDynamic()[0])
if (bType != "object") return "fail4: $bType"
b[0] = 'W'
val bType2 = jsTypeOf(b.asDynamic()[0])
if (bType2 != "object") return "fail5: $bType2"
val bType3 = jsTypeOf(b[0].asDynamic())
if (bType3 != "number") return "fail6: $bType3"
return "OK"
}
-20
View File
@@ -1,20 +0,0 @@
var log = ""
inline fun String.foo(a: String): Int {
log += "foo1"
return asDynamic().indexOf(a)
}
inline fun String.foo(a: Char): Int {
log += "foo2"
return indexOf(a.toString())
}
fun bar(a: String, b: Char, x: Int) {
log += if (x > 0)
23
else
a.foo(b)
}
// LINES: 6 4 4 5 5 8 8 8 8 8 8 8 11 9 9 10 10 8 8 8 8 18 14 14 14 14 14 15 17 17 9 9 14 10 17 10 14 14 * 1 * 1
+1 -1
View File
@@ -27,4 +27,4 @@ fun bar(f: (Pair<String, String>) -> Unit) {
}
// LINES: 22 16 16 17 18 20 20 21 21 23 9 9 9 9 4 9 9 9 5 5 6 7 11 11 12 12 15 15 27 26 26 * 1 * 1
// LINES: 22 16 16 17 18 20 20 21 21 22 22 23 9 9 9 9 4 9 9 9 5 5 6 7 11 11 12 12 15 15 27 26 26 * 1 * 1
@@ -32,4 +32,4 @@ inline operator fun P.component1() = a
inline operator fun P.component2() = b
// LINES: 22 17 17 31 18 18 33 20 20 21 21 23 9 9 9 9 4 9 9 9 6 6 31 7 7 33 11 11 12 12 15 15 27 26 26 29 29 29 * 31 31 31 33 33 33 * 1 * 1
// LINES: 22 17 17 31 18 18 33 20 20 21 21 22 22 23 9 9 9 9 4 9 9 9 6 6 31 7 7 33 11 11 12 12 15 15 27 26 26 29 29 29 * 31 31 31 33 33 33 * 1 * 1
+1 -1
View File
@@ -4,4 +4,4 @@ fun foo(x: Int): () -> Unit = {
fun bar() = 23
// LINES: 1 1 1 2 2 1 1 1 5 5 5
// LINES: 1 1 1 2 2 3 3 1 1 1 5 5 5