[Wasm] Restrict types allowed in JS interop
- Prohibit Any, Array and other unsupported non-external types in JS interop context - Add K1 diagnostic - Update BE testdata ^KT-57136 Fixed
This commit is contained in:
committed by
Space Team
parent
bb05c8528f
commit
1e91fe155b
@@ -53,7 +53,7 @@ external class C {
|
|||||||
val x1: Int
|
val x1: Int
|
||||||
val x2: Int
|
val x2: Int
|
||||||
fun foo(x3: Int = definedExternally, x4: Int = definedExternally): String
|
fun foo(x3: Int = definedExternally, x4: Int = definedExternally): String
|
||||||
fun bar(x5: C = definedExternally, x6: Any = definedExternally) : String
|
fun bar(x5: C = definedExternally, x6: C = definedExternally) : String
|
||||||
}
|
}
|
||||||
|
|
||||||
open external class Writable: WritableStream {
|
open external class Writable: WritableStream {
|
||||||
|
|||||||
+15
-13
@@ -88,7 +88,7 @@ function createJsLambda() {
|
|||||||
|
|
||||||
// FILE: externals.kt
|
// FILE: externals.kt
|
||||||
|
|
||||||
external fun createJsLambda(): (Boolean, Byte, Short, Char, Int, Long, Float, Double, String, EI, DC, (DC) -> Int) -> Int
|
external fun createJsLambda(): (Boolean, Byte, Short, Char, Int, Long, Float, Double, String, EI, JSDC, (JSDC) -> Int) -> Int
|
||||||
|
|
||||||
external fun apply7(f: (String) -> ((String) -> ((String) -> ((String) -> ((String) -> ((String) -> ((String) -> String))))))): String
|
external fun apply7(f: (String) -> ((String) -> ((String) -> ((String) -> ((String) -> ((String) -> ((String) -> String))))))): String
|
||||||
|
|
||||||
@@ -98,10 +98,11 @@ external fun is123Array(x: EI): Boolean
|
|||||||
external fun create123Array(): EI
|
external fun create123Array(): EI
|
||||||
|
|
||||||
data class DC(val x: Int, val y: Int)
|
data class DC(val x: Int, val y: Int)
|
||||||
|
typealias JSDC = JsHandle<DC>
|
||||||
|
|
||||||
external fun extenalWithLambda(
|
external fun extenalWithLambda(
|
||||||
x: (Boolean, Byte, Short, Char, Int, Long, Float, Double, String, EI, DC) -> Unit,
|
x: (Boolean, Byte, Short, Char, Int, Long, Float, Double, String, EI, JSDC) -> Unit,
|
||||||
dc: DC,
|
dc: JSDC,
|
||||||
)
|
)
|
||||||
|
|
||||||
external fun externalWithLambdas2(
|
external fun externalWithLambdas2(
|
||||||
@@ -115,8 +116,8 @@ external fun externalWithLambdas2(
|
|||||||
double: () -> Double,
|
double: () -> Double,
|
||||||
string: () -> String,
|
string: () -> String,
|
||||||
ei: () -> EI,
|
ei: () -> EI,
|
||||||
dc: () -> DC,
|
dc: () -> JSDC,
|
||||||
dcGetY: (DC) -> Int,
|
dcGetY: (JSDC) -> Int,
|
||||||
): Int
|
): Int
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
@@ -165,7 +166,8 @@ fun box(): String {
|
|||||||
double: Double,
|
double: Double,
|
||||||
string: String,
|
string: String,
|
||||||
ei: EI,
|
ei: EI,
|
||||||
dc: DC ->
|
jsdc: JSDC ->
|
||||||
|
val dc = jsdc.get()
|
||||||
test(bool == true)
|
test(bool == true)
|
||||||
test(byte == 1.toByte())
|
test(byte == 1.toByte())
|
||||||
test(short == 2.toShort())
|
test(short == 2.toShort())
|
||||||
@@ -177,7 +179,7 @@ fun box(): String {
|
|||||||
test(string == "S")
|
test(string == "S")
|
||||||
test(is123Array(ei))
|
test(is123Array(ei))
|
||||||
test(dc.x == 100 && dc.y == 200)
|
test(dc.x == 100 && dc.y == 200)
|
||||||
}, DC(100, 200))
|
}, DC(100, 200).toJsHandle())
|
||||||
|
|
||||||
|
|
||||||
if (extenalWithLambdasCount != 11) return "Fail 1"
|
if (extenalWithLambdasCount != 11) return "Fail 1"
|
||||||
@@ -193,8 +195,8 @@ fun box(): String {
|
|||||||
double = { 600.5 },
|
double = { 600.5 },
|
||||||
string = { "700" },
|
string = { "700" },
|
||||||
ei = { create123Array() },
|
ei = { create123Array() },
|
||||||
dc = { DC(800, 800) },
|
dc = { DC(800, 800).toJsHandle() },
|
||||||
dcGetY = { it.y }
|
dcGetY = { it.get().y }
|
||||||
)
|
)
|
||||||
if (externalWithLambdas2Count != 11) return "Fail externalWithLambdas2"
|
if (externalWithLambdas2Count != 11) return "Fail externalWithLambdas2"
|
||||||
|
|
||||||
@@ -210,8 +212,8 @@ fun box(): String {
|
|||||||
{ 600.5 },
|
{ 600.5 },
|
||||||
{ "700" },
|
{ "700" },
|
||||||
{ create123Array() },
|
{ create123Array() },
|
||||||
{ DC(800, 800) },
|
{ DC(800, 800).toJsHandle() },
|
||||||
{ it.y }
|
{ it.get().y }
|
||||||
)
|
)
|
||||||
if (externalWithLambdas2RefCount != 11) return "Fail externalWithLambdas2"
|
if (externalWithLambdas2RefCount != 11) return "Fail externalWithLambdas2"
|
||||||
|
|
||||||
@@ -228,8 +230,8 @@ fun box(): String {
|
|||||||
600.5,
|
600.5,
|
||||||
"700",
|
"700",
|
||||||
create123Array(),
|
create123Array(),
|
||||||
DC(800, 800),
|
DC(800, 800).toJsHandle(),
|
||||||
{ it.y }
|
{ it.get().y }
|
||||||
)
|
)
|
||||||
if (jsLambdaCount != 11)
|
if (jsLambdaCount != 11)
|
||||||
return "Fail 3"
|
return "Fail 3"
|
||||||
|
|||||||
+5
-4
@@ -1,13 +1,14 @@
|
|||||||
|
// TARGET_BACKEND: WASM
|
||||||
// MODULE: main
|
// MODULE: main
|
||||||
// FILE: externals.kt
|
// FILE: externals.kt
|
||||||
|
|
||||||
class C(val x: Int)
|
class C(val x: Int)
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
fun makeC(x: Int): C = C(x)
|
fun makeC(x: Int): JsHandle<C> = C(x).toJsHandle()
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
fun getX(c: C): Int = c.x
|
fun getX(c: JsHandle<C>): Int = c.get().x
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
fun getString(s: String): String = "Test string $s";
|
fun getString(s: String): String = "Test string $s";
|
||||||
@@ -18,10 +19,10 @@ fun isEven(x: Int): Boolean = x % 2 == 0
|
|||||||
external interface EI
|
external interface EI
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
fun eiAsAny(ei: EI): Any = ei
|
fun eiAsAny(ei: EI): JsHandle<Any> = ei.toJsHandle()
|
||||||
|
|
||||||
@JsExport
|
@JsExport
|
||||||
fun anyAsEI(any: Any): EI = any as EI
|
fun anyAsEI(any: JsHandle<Any>): EI = any.get() as EI
|
||||||
|
|
||||||
fun box(): String = "OK"
|
fun box(): String = "OK"
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ fun testExterRef() {
|
|||||||
check(null2ExternRef() == null)
|
check(null2ExternRef() == null)
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataRef
|
class DataRefImpl
|
||||||
|
typealias DataRef = JsHandle<DataRefImpl>
|
||||||
|
|
||||||
fun notNullDataRef(x: DataRef): DataRef = js("x")
|
fun notNullDataRef(x: DataRef): DataRef = js("x")
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ fun nullDataRef(x: DataRef): DataRef? = js("x")
|
|||||||
fun null2DataRef(x: DataRef): DataRef? = js("null")
|
fun null2DataRef(x: DataRef): DataRef? = js("null")
|
||||||
|
|
||||||
fun testDataRef() {
|
fun testDataRef() {
|
||||||
val dataRef = DataRef()
|
val dataRef = DataRefImpl().toJsHandle()
|
||||||
check(notNullDataRef(dataRef) == dataRef)
|
check(notNullDataRef(dataRef) == dataRef)
|
||||||
checkNPE { notNull2DataRef(dataRef) }
|
checkNPE { notNull2DataRef(dataRef) }
|
||||||
check (nullDataRef(dataRef) == dataRef)
|
check (nullDataRef(dataRef) == dataRef)
|
||||||
@@ -121,22 +122,6 @@ fun testFloat() {
|
|||||||
check(null2Float() == null)
|
check(null2Float() == null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun notNullNumber(): Number = js("123.5")
|
|
||||||
|
|
||||||
fun notNull2Number(): Number = js("null")
|
|
||||||
|
|
||||||
fun nullNumber(): Number? = js("123.5")
|
|
||||||
|
|
||||||
fun null2Number(): Number? = js("null")
|
|
||||||
|
|
||||||
fun testNumber() {
|
|
||||||
check(notNullNumber() == 123.5)
|
|
||||||
check(notNull2Number() == 0.0)
|
|
||||||
check(nullNumber() == 123.5)
|
|
||||||
check(null2Number() == null)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
testString()
|
testString()
|
||||||
testExterRef()
|
testExterRef()
|
||||||
@@ -145,6 +130,5 @@ fun box(): String {
|
|||||||
testBoolean()
|
testBoolean()
|
||||||
testShort()
|
testShort()
|
||||||
testFloat()
|
testFloat()
|
||||||
testNumber()
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,27 +40,6 @@ fun testExterRef() {
|
|||||||
null2ExternRef(null)
|
null2ExternRef(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataRef
|
|
||||||
|
|
||||||
fun notNullDataRef(x: DataRef) {
|
|
||||||
js("if (x === null) throw 'error'")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun nullDataRef(x: DataRef?) {
|
|
||||||
js("if (x === null) throw 'error'")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun null2DataRef(x: DataRef?) {
|
|
||||||
js("if (x !== null) throw 'error'")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testDataRef() {
|
|
||||||
val dataRef = DataRef()
|
|
||||||
notNullDataRef(dataRef)
|
|
||||||
nullDataRef(dataRef)
|
|
||||||
null2DataRef(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun notNullInt(x: Int) {
|
fun notNullInt(x: Int) {
|
||||||
js("if (x !== 123) throw 'error'")
|
js("if (x !== 123) throw 'error'")
|
||||||
}
|
}
|
||||||
@@ -133,47 +112,12 @@ fun testFloat() {
|
|||||||
null2Float(null)
|
null2Float(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notNullNumber(x: Number) {
|
|
||||||
js("if (x !== 123.5) throw 'error'")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun nullNumber(x: Number?) {
|
|
||||||
js("if (x !== 123.5) throw 'error'")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun null2Number(x: Number?) {
|
|
||||||
js("if (x !== null) throw 'error'")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun byte2Number(x: Number) {
|
|
||||||
js("if (x !== 123) throw 'error'")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun notNullByte2Number(x: Number?) {
|
|
||||||
js("if (x !== 123) throw 'error'")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun nullByte2Number(x: Number?) {
|
|
||||||
js("if (x !== null) throw 'error'")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testNumber() {
|
|
||||||
notNullNumber(123.5)
|
|
||||||
nullNumber(123.5)
|
|
||||||
null2Number(null)
|
|
||||||
byte2Number(123)
|
|
||||||
notNullByte2Number(123)
|
|
||||||
nullByte2Number(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
testString()
|
testString()
|
||||||
testExterRef()
|
testExterRef()
|
||||||
testDataRef()
|
|
||||||
testInt()
|
testInt()
|
||||||
testBoolean()
|
testBoolean()
|
||||||
testShort()
|
testShort()
|
||||||
testFloat()
|
testFloat()
|
||||||
testNumber()
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
@@ -72,10 +72,8 @@ external fun getFalseBoolean(): Boolean
|
|||||||
|
|
||||||
external interface EI
|
external interface EI
|
||||||
|
|
||||||
external fun createJsObjectAsAny(): Any
|
|
||||||
external fun createJsObjectAsExternalInterface(): EI
|
external fun createJsObjectAsExternalInterface(): EI
|
||||||
external fun getObjectValueEI(x: EI): String
|
external fun getObjectValueEI(x: EI): String
|
||||||
external fun getObjectValueAny(x: Any): String
|
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
// Strings
|
// Strings
|
||||||
@@ -98,15 +96,6 @@ fun box(): String {
|
|||||||
val objAsEI: EI = createJsObjectAsExternalInterface()
|
val objAsEI: EI = createJsObjectAsExternalInterface()
|
||||||
if (getObjectValueEI(objAsEI) != "object created by createJsObjectAsExternalInterface")
|
if (getObjectValueEI(objAsEI) != "object created by createJsObjectAsExternalInterface")
|
||||||
return "Fail createJsObjectAsExternalInterface + getObjectValueEI"
|
return "Fail createJsObjectAsExternalInterface + getObjectValueEI"
|
||||||
if (getObjectValueAny(objAsEI) != "object created by createJsObjectAsExternalInterface")
|
|
||||||
return "Fail createJsObjectAsExternalInterface + getObjectValueAny"
|
|
||||||
|
|
||||||
// Any
|
|
||||||
val objAsAny: Any = createJsObjectAsAny()
|
|
||||||
if (getObjectValueAny(objAsAny) != "object created by createJsObjectAsAny")
|
|
||||||
return "Fail createJsObjectAsAny + getObjectValueAny"
|
|
||||||
if (getObjectValueEI(objAsAny as EI) != "object created by createJsObjectAsAny")
|
|
||||||
return "Fail createJsObjectAsAny + getObjectValueEI"
|
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ fun funBlockBody(x: Int): Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <!IMPLICIT_NOTHING_RETURN_TYPE!>returnTypeNotSepcified<!>() = js("1")
|
fun <!IMPLICIT_NOTHING_RETURN_TYPE!>returnTypeNotSepcified<!>() = js("1")
|
||||||
val <!IMPLICIT_NOTHING_PROPERTY_TYPE!>valTypeNotSepcified<!> = js("1")
|
<!WRONG_JS_INTEROP_TYPE!>val <!IMPLICIT_NOTHING_PROPERTY_TYPE!>valTypeNotSepcified<!><!> = js("1")
|
||||||
|
|
||||||
val a = "1"
|
val a = "1"
|
||||||
fun nonConst(): String = "1"
|
fun nonConst(): String = "1"
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// !OPT_IN: kotlin.js.ExperimentalJsExport
|
||||||
|
|
||||||
|
external interface EI
|
||||||
|
external open class EC
|
||||||
|
external object EO
|
||||||
|
|
||||||
|
external fun supportedTypes(
|
||||||
|
boolean: Boolean,
|
||||||
|
|
||||||
|
byte: Byte,
|
||||||
|
short: Short,
|
||||||
|
int: Int,
|
||||||
|
long: Long,
|
||||||
|
|
||||||
|
float: Float,
|
||||||
|
double: Double,
|
||||||
|
|
||||||
|
char: Char,
|
||||||
|
string: String,
|
||||||
|
|
||||||
|
ei: EI,
|
||||||
|
ec: EC,
|
||||||
|
eo: EO,
|
||||||
|
|
||||||
|
f1: (Boolean, Byte, Short, Int, Long, Float, Double, Char, String, EI, EC, EO) -> Unit,
|
||||||
|
|
||||||
|
f2: (Boolean) -> ((Int) -> ((Float) -> ((String) -> EI))),
|
||||||
|
|
||||||
|
f3: ((((Boolean) -> Int) -> Float) -> String) -> EI,
|
||||||
|
): Unit
|
||||||
|
|
||||||
|
external fun supportedNullableTypes(
|
||||||
|
boolean: Boolean?,
|
||||||
|
|
||||||
|
byte: Byte?,
|
||||||
|
short: Short?,
|
||||||
|
int: Int?,
|
||||||
|
long: Long?,
|
||||||
|
|
||||||
|
float: Float?,
|
||||||
|
double: Double?,
|
||||||
|
|
||||||
|
char: Char?,
|
||||||
|
string: String?,
|
||||||
|
|
||||||
|
ei: EI?,
|
||||||
|
ec: EC?,
|
||||||
|
eo: EO?,
|
||||||
|
|
||||||
|
f1: ((Boolean?, Byte?, Short?, Int?, Long?, Float?, Double?, Char?, String?, EI?, EC?, EO?) -> Unit)?,
|
||||||
|
|
||||||
|
f2: ((Boolean?) -> ((Int?) -> ((Float?) -> ((String?) -> EI)?)?)?)?,
|
||||||
|
|
||||||
|
f3: ((((((((Boolean?) -> Int?)?) -> Float?)?) -> String?)?) -> EI?)?,
|
||||||
|
): Unit
|
||||||
|
|
||||||
|
external fun supportedReturnTypeUnit(): Unit
|
||||||
|
external fun supportedReturnTypeNothing(): Nothing
|
||||||
|
external fun supportedReturnTypeBoolean(): Boolean
|
||||||
|
external fun supportedReturnTypeNullableInt(): Int?
|
||||||
|
external fun supportedReturnTypeEI(): EI
|
||||||
|
external fun supportedReturnTypeNullableEC(): EC?
|
||||||
|
|
||||||
|
external fun <
|
||||||
|
T1 : EI,
|
||||||
|
T2 : EC?,
|
||||||
|
T3 : T1?
|
||||||
|
> supportedTypeParamtersUpperBounds(p1: T1, p2: T2): T3
|
||||||
|
|
||||||
|
|
||||||
|
external fun wrongExternalTypes(
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>any: Any<!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>nany: Any?<!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>unit: Unit<!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>nunit: Unit?<!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>nothing: Nothing<!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>nnothing: Nothing?<!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>charSequence: CharSequence<!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>list: List<Int><!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>array: Array<Int><!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>intArray: IntArray<!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>pair: Pair<Int, Int><!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>number: Number<!>,
|
||||||
|
)
|
||||||
|
|
||||||
|
external fun <<!WRONG_JS_INTEROP_TYPE!>T<!>> supportedTypeParamtersUpperBounds(p: T): T where T : EI, T : Any
|
||||||
|
|
||||||
|
external fun <
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>T1<!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>T2 : Number<!>,
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>T3: List<Int><!>,
|
||||||
|
> supportedTypeParamtersUpperBounds(
|
||||||
|
p1: T1,
|
||||||
|
p2: T2
|
||||||
|
): T3
|
||||||
|
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>fun jsCode1(<!WRONG_JS_INTEROP_TYPE!>x: Any<!>): Any<!> = js("x")
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>fun jsCode2(<!WRONG_JS_INTEROP_TYPE!>x: Any<!>): Any<!> {
|
||||||
|
js("return x;")
|
||||||
|
}
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>val jsProp: Any<!> = js("1")
|
||||||
|
|
||||||
|
<!WRONG_JS_INTEROP_TYPE!>@JsExport
|
||||||
|
fun exported(<!WRONG_JS_INTEROP_TYPE!>x: Any<!>): Any<!> = x
|
||||||
Generated
-6
@@ -55,12 +55,6 @@ public class FirJsCodegenWasmJsInteropTestGenerated extends AbstractFirJsCodegen
|
|||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/jsCode.kt");
|
runTest("compiler/testData/codegen/boxWasmJsInterop/jsCode.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestMetadata("jsExport.kt")
|
|
||||||
public void testJsExport() throws Exception {
|
|
||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("jsModule.kt")
|
@TestMetadata("jsModule.kt")
|
||||||
public void testJsModule() throws Exception {
|
public void testJsModule() throws Exception {
|
||||||
|
|||||||
Generated
-6
@@ -55,12 +55,6 @@ public class IrCodegenWasmJsInteropJsTestGenerated extends AbstractIrCodegenWasm
|
|||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/jsCode.kt");
|
runTest("compiler/testData/codegen/boxWasmJsInterop/jsCode.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestMetadata("jsExport.kt")
|
|
||||||
public void testJsExport() throws Exception {
|
|
||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("jsModule.kt")
|
@TestMetadata("jsModule.kt")
|
||||||
public void testJsModule() throws Exception {
|
public void testJsModule() throws Exception {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ object WasmPlatformConfigurator : PlatformConfiguratorBase(
|
|||||||
WasmExternalDeclarationChecker,
|
WasmExternalDeclarationChecker,
|
||||||
WasmImportAnnotationChecker,
|
WasmImportAnnotationChecker,
|
||||||
WasmJsFunAnnotationChecker,
|
WasmJsFunAnnotationChecker,
|
||||||
|
WasmJsInteropTypesChecker,
|
||||||
),
|
),
|
||||||
additionalCallCheckers = listOf(
|
additionalCallCheckers = listOf(
|
||||||
JsModuleCallChecker,
|
JsModuleCallChecker,
|
||||||
|
|||||||
+7
@@ -18,6 +18,13 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
|
|||||||
Renderers.RENDER_TYPE
|
Renderers.RENDER_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
put(
|
||||||
|
ErrorsWasm.WRONG_JS_INTEROP_TYPE, "Type {1} cannot be used in {0}. " +
|
||||||
|
"Only external, primitive, string and function types are supported in Kotlin/Wasm JS interop.",
|
||||||
|
CommonRenderers.STRING,
|
||||||
|
Renderers.RENDER_TYPE
|
||||||
|
)
|
||||||
|
|
||||||
put(ErrorsWasm.NESTED_WASM_IMPORT, "Only top-level functions can be imported with @WasmImport")
|
put(ErrorsWasm.NESTED_WASM_IMPORT, "Only top-level functions can be imported with @WasmImport")
|
||||||
put(ErrorsWasm.WASM_IMPORT_ON_NON_EXTERNAL_DECLARATION, "Functions annotated with @WasmImport must be external")
|
put(ErrorsWasm.WASM_IMPORT_ON_NON_EXTERNAL_DECLARATION, "Functions annotated with @WasmImport must be external")
|
||||||
put(ErrorsWasm.WASM_IMPORT_PARAMETER_DEFAULT_VALUE, "Default parameter values are not supported with @WasmImport")
|
put(ErrorsWasm.WASM_IMPORT_PARAMETER_DEFAULT_VALUE, "Default parameter values are not supported with @WasmImport")
|
||||||
|
|||||||
+4
-4
@@ -6,10 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.wasm.resolve.diagnostics;
|
package org.jetbrains.kotlin.wasm.resolve.diagnostics;
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
|
import org.jetbrains.kotlin.diagnostics.*;
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
|
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
|
||||||
import org.jetbrains.kotlin.diagnostics.PositioningStrategies;
|
|
||||||
import org.jetbrains.kotlin.psi.KtElement;
|
import org.jetbrains.kotlin.psi.KtElement;
|
||||||
import org.jetbrains.kotlin.types.KotlinType;
|
import org.jetbrains.kotlin.types.KotlinType;
|
||||||
|
|
||||||
@@ -19,6 +16,9 @@ public interface ErrorsWasm {
|
|||||||
DiagnosticFactory1<KtElement, KotlinType> NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE =
|
DiagnosticFactory1<KtElement, KotlinType> NON_EXTERNAL_TYPE_EXTENDS_EXTERNAL_TYPE =
|
||||||
DiagnosticFactory1.create(ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
DiagnosticFactory1.create(ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
|
|
||||||
|
DiagnosticFactory2<PsiElement, String, KotlinType>
|
||||||
|
WRONG_JS_INTEROP_TYPE = DiagnosticFactory2.create(ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||||
|
|
||||||
DiagnosticFactory0<PsiElement> NESTED_WASM_IMPORT = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> NESTED_WASM_IMPORT = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<PsiElement> WASM_IMPORT_ON_NON_EXTERNAL_DECLARATION = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> WASM_IMPORT_ON_NON_EXTERNAL_DECLARATION = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<PsiElement> WASM_IMPORT_PARAMETER_DEFAULT_VALUE = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> WASM_IMPORT_PARAMETER_DEFAULT_VALUE = DiagnosticFactory0.create(ERROR);
|
||||||
|
|||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.wasm.resolve.diagnostics
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||||
|
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
|
import org.jetbrains.kotlin.wasm.util.hasValidJsCodeBody
|
||||||
|
|
||||||
|
// TODO: Implement in K2: KT-56849
|
||||||
|
object WasmJsInteropTypesChecker : DeclarationChecker {
|
||||||
|
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||||
|
if (descriptor !is MemberDescriptor)
|
||||||
|
return
|
||||||
|
|
||||||
|
val trace = context.trace
|
||||||
|
val bindingContext = trace.bindingContext
|
||||||
|
|
||||||
|
fun isExternalJsInteropDeclaration() =
|
||||||
|
descriptor.isEffectivelyExternal() &&
|
||||||
|
!descriptor.annotations.hasAnnotation(FqName("kotlin.wasm.WasmImport"))
|
||||||
|
|
||||||
|
fun isJsCodeDeclaration() =
|
||||||
|
(descriptor is FunctionDescriptor && descriptor.hasValidJsCodeBody(bindingContext) ||
|
||||||
|
descriptor is PropertyDescriptor && descriptor.hasValidJsCodeBody(bindingContext))
|
||||||
|
|
||||||
|
fun isJsExportDeclaration() =
|
||||||
|
AnnotationsUtils.isExportedObject(descriptor, bindingContext)
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isExternalJsInteropDeclaration() &&
|
||||||
|
!isJsCodeDeclaration() &&
|
||||||
|
!isJsExportDeclaration()
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KotlinType.checkJsInteropType(
|
||||||
|
typePositionDescription: String,
|
||||||
|
reportOn: PsiElement,
|
||||||
|
isInFunctionReturnPosition: Boolean = false,
|
||||||
|
) {
|
||||||
|
if (!isTypeSupportedInJsInterop(this, isInFunctionReturnPosition)) {
|
||||||
|
trace.report(ErrorsWasm.WRONG_JS_INTEROP_TYPE.on(reportOn, typePositionDescription, this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TypeParameterDescriptor.checkJsInteropTypeParameter() {
|
||||||
|
for (upperBound in this.upperBounds) {
|
||||||
|
if (!isTypeSupportedInJsInterop(upperBound, isInFunctionReturnPosition = false)) {
|
||||||
|
val reportOn = this.findPsi() ?: declaration
|
||||||
|
trace.report(ErrorsWasm.WRONG_JS_INTEROP_TYPE.on(reportOn, "type parameter upper bound", upperBound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when (descriptor) {
|
||||||
|
is ClassDescriptor -> {
|
||||||
|
for (typeParameter in descriptor.declaredTypeParameters) {
|
||||||
|
typeParameter.checkJsInteropTypeParameter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PropertyDescriptor -> {
|
||||||
|
for (typeParameter in descriptor.typeParameters) {
|
||||||
|
typeParameter.checkJsInteropTypeParameter()
|
||||||
|
}
|
||||||
|
descriptor.type.checkJsInteropType("external property", declaration)
|
||||||
|
}
|
||||||
|
is FunctionDescriptor -> {
|
||||||
|
for (typeParameter in descriptor.typeParameters) {
|
||||||
|
typeParameter.checkJsInteropTypeParameter()
|
||||||
|
}
|
||||||
|
for (parameter in descriptor.valueParameters) {
|
||||||
|
val typeToCheck = parameter.varargElementType ?: parameter.type
|
||||||
|
typeToCheck.checkJsInteropType(
|
||||||
|
"external function parameter",
|
||||||
|
reportOn = parameter.findPsi() ?: declaration
|
||||||
|
)
|
||||||
|
}
|
||||||
|
descriptor.returnType?.checkJsInteropType(
|
||||||
|
"external function return",
|
||||||
|
reportOn = declaration,
|
||||||
|
isInFunctionReturnPosition = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isTypeSupportedInJsInterop(
|
||||||
|
type: KotlinType,
|
||||||
|
isInFunctionReturnPosition: Boolean,
|
||||||
|
): Boolean {
|
||||||
|
if (type.isUnit() || type.isNothing()) {
|
||||||
|
return isInFunctionReturnPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
val nonNullable = type.makeNotNullable()
|
||||||
|
if (
|
||||||
|
KotlinBuiltIns.isPrimitiveType(nonNullable) ||
|
||||||
|
KotlinBuiltIns.isString(nonNullable)
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interop type parameters upper bounds should are checked
|
||||||
|
// on declaration site separately
|
||||||
|
if (nonNullable.isTypeParameter()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val classifierDescriptor = nonNullable.constructor.declarationDescriptor
|
||||||
|
if (classifierDescriptor is MemberDescriptor && classifierDescriptor.isEffectivelyExternal()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.isFunctionType) {
|
||||||
|
val arguments = type.arguments
|
||||||
|
for (i in 0 until arguments.lastIndex) {
|
||||||
|
val isArgumentSupported = isTypeSupportedInJsInterop(
|
||||||
|
arguments[i].type,
|
||||||
|
isInFunctionReturnPosition = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!isArgumentSupported) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isTypeSupportedInJsInterop(
|
||||||
|
arguments.last().type, // Function type result type
|
||||||
|
isInFunctionReturnPosition = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
+6
@@ -68,6 +68,12 @@ public class DiagnosticsWasmTestGenerated extends AbstractDiagnosticsWasmTest {
|
|||||||
public void testJsFun() throws Exception {
|
public void testJsFun() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/wasmTests/jsInterop/jsFun.kt");
|
runTest("compiler/testData/diagnostics/wasmTests/jsInterop/jsFun.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("types.kt")
|
||||||
|
public void testTypes() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/wasmTests/jsInterop/types.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
|||||||
Reference in New Issue
Block a user