[WasmJs] Add support for external class reflection
Fix #KT-64890
This commit is contained in:
@@ -305,7 +305,6 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
||||
override val getKClassFromExpression = getInternalWithoutPackage("getKClassFromExpression")
|
||||
override val primitiveClassesObject = context.getIrClass(FqName("kotlin.reflect.js.internal.PrimitiveClasses"))
|
||||
override val kTypeClass: IrClassSymbol = context.getIrClass(FqName("kotlin.reflect.KType"))
|
||||
override val getClassData: IrSimpleFunctionSymbol get() = jsClass
|
||||
}
|
||||
|
||||
internal val reflectionSymbols: JsReflectionSymbols = JsReflectionSymbols()
|
||||
|
||||
@@ -670,8 +670,8 @@ private val jsClassUsageInReflectionPhase = makeIrModulePhase(
|
||||
)
|
||||
|
||||
private val classReferenceLoweringPhase = makeIrModulePhase(
|
||||
::ClassReferenceLowering,
|
||||
name = "ClassReferenceLowering",
|
||||
::JsClassReferenceLowering,
|
||||
name = "JsClassReferenceLowering",
|
||||
description = "Handle class references",
|
||||
prerequisite = setOf(jsClassUsageInReflectionPhase)
|
||||
)
|
||||
|
||||
@@ -56,4 +56,7 @@ class JsMapping : DefaultMapping() {
|
||||
|
||||
val wasmExternalClassToInstanceCheck =
|
||||
DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
|
||||
val wasmGetJsClass =
|
||||
DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
}
|
||||
|
||||
@@ -7,12 +7,10 @@ package org.jetbrains.kotlin.ir.backend.js
|
||||
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
|
||||
interface ReflectionSymbols {
|
||||
val getKClassFromExpression: IrSimpleFunctionSymbol
|
||||
val getKClass: IrSimpleFunctionSymbol
|
||||
val getClassData: IrSimpleFunctionSymbol
|
||||
val createKType: IrSimpleFunctionSymbol?
|
||||
val createDynamicKType: IrSimpleFunctionSymbol?
|
||||
val createKTypeParameter: IrSimpleFunctionSymbol?
|
||||
|
||||
+33
-25
@@ -25,9 +25,36 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.*
|
||||
|
||||
class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
|
||||
class JsClassReferenceLowering(context: JsIrBackendContext) : ClassReferenceLowering(context) {
|
||||
private val getClassData = context.intrinsics.jsClass
|
||||
|
||||
private val reflectionSymbols get() = context.reflectionSymbols
|
||||
override fun callGetKClass(
|
||||
returnType: IrType,
|
||||
typeArgument: IrType
|
||||
): IrCall {
|
||||
val primitiveKClass =
|
||||
getFinalPrimitiveKClass(returnType, typeArgument) ?: getOpenPrimitiveKClass(returnType, typeArgument)
|
||||
|
||||
if (primitiveKClass != null)
|
||||
return primitiveKClass
|
||||
|
||||
return JsIrBuilder.buildCall(reflectionSymbols.getKClass, returnType, listOf(typeArgument))
|
||||
.apply {
|
||||
putValueArgument(0, callGetClassByType(typeArgument))
|
||||
}
|
||||
}
|
||||
|
||||
private fun callGetClassByType(type: IrType) =
|
||||
JsIrBuilder.buildCall(
|
||||
getClassData,
|
||||
typeArguments = listOf(type),
|
||||
origin = JsStatementOrigins.CLASS_REFERENCE
|
||||
)
|
||||
}
|
||||
|
||||
abstract class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
|
||||
|
||||
protected val reflectionSymbols get() = context.reflectionSymbols
|
||||
|
||||
private val primitiveClassProperties by lazy(LazyThreadSafetyMode.NONE) {
|
||||
reflectionSymbols.primitiveClassesObject.owner.declarations.filterIsInstance<IrProperty>()
|
||||
@@ -96,7 +123,7 @@ class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLowering
|
||||
)
|
||||
}
|
||||
|
||||
private fun getFinalPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
|
||||
protected fun getFinalPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
|
||||
for ((typePredicate, v) in finalPrimitiveClasses) {
|
||||
if (typePredicate(typeArgument))
|
||||
return getPrimitiveClass(v, returnType)
|
||||
@@ -106,7 +133,7 @@ class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLowering
|
||||
}
|
||||
|
||||
|
||||
private fun getOpenPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
|
||||
protected fun getOpenPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
|
||||
for ((typePredicate, v) in openPrimitiveClasses) {
|
||||
if (typePredicate(typeArgument))
|
||||
return getPrimitiveClass(v, returnType)
|
||||
@@ -123,28 +150,10 @@ class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLowering
|
||||
return null
|
||||
}
|
||||
|
||||
private fun callGetKClass(
|
||||
abstract fun callGetKClass(
|
||||
returnType: IrType = reflectionSymbols.getKClass.owner.returnType,
|
||||
typeArgument: IrType
|
||||
): IrCall {
|
||||
val primitiveKClass =
|
||||
getFinalPrimitiveKClass(returnType, typeArgument) ?: getOpenPrimitiveKClass(returnType, typeArgument)
|
||||
|
||||
if (primitiveKClass != null)
|
||||
return primitiveKClass
|
||||
|
||||
return JsIrBuilder.buildCall(reflectionSymbols.getKClass, returnType, listOf(typeArgument))
|
||||
.apply {
|
||||
putValueArgument(0, callGetClassByType(typeArgument))
|
||||
}
|
||||
}
|
||||
|
||||
private fun callGetClassByType(type: IrType) =
|
||||
JsIrBuilder.buildCall(
|
||||
reflectionSymbols.getClassData,
|
||||
typeArguments = listOf(type),
|
||||
origin = JsStatementOrigins.CLASS_REFERENCE
|
||||
)
|
||||
): IrCall
|
||||
|
||||
private fun buildCall(name: IrSimpleFunctionSymbol, vararg args: IrExpression): IrExpression =
|
||||
JsIrBuilder.buildCall(name).apply {
|
||||
@@ -154,7 +163,6 @@ class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLowering
|
||||
}
|
||||
|
||||
private fun createKType(type: IrType, visitedTypeParams: MutableSet<IrTypeParameter>): IrExpression {
|
||||
|
||||
if (type is IrSimpleType)
|
||||
return createSimpleKType(type, visitedTypeParams)
|
||||
if (type is IrDynamicType)
|
||||
|
||||
+2
-2
@@ -470,8 +470,8 @@ private val staticMembersLoweringPhase = makeIrModulePhase(
|
||||
)
|
||||
|
||||
private val classReferenceLoweringPhase = makeIrModulePhase(
|
||||
::ClassReferenceLowering,
|
||||
name = "ClassReferenceLowering",
|
||||
::WasmClassReferenceLowering,
|
||||
name = "WasmClassReferenceLowering",
|
||||
description = "Handle class references"
|
||||
)
|
||||
|
||||
|
||||
@@ -52,10 +52,9 @@ class WasmSymbols(
|
||||
|
||||
internal inner class WasmReflectionSymbols : ReflectionSymbols {
|
||||
override val createKType: IrSimpleFunctionSymbol = getInternalFunction("createKType")
|
||||
override val getClassData: IrSimpleFunctionSymbol = getInternalFunction("wasmGetTypeInfoData")
|
||||
override val getKClass: IrSimpleFunctionSymbol = getInternalFunction("getKClass")
|
||||
override val getKClassFromExpression: IrSimpleFunctionSymbol = getInternalFunction("getKClassFromExpression")
|
||||
override val createDynamicKType: IrSimpleFunctionSymbol get() = error("Dynamic type is not supported by WASM")
|
||||
override val createDynamicKType: IrSimpleFunctionSymbol get() = error("Dynamic type is not supported by Wasm")
|
||||
override val createKTypeParameter: IrSimpleFunctionSymbol = getInternalFunction("createKTypeParameter")
|
||||
override val getStarKTypeProjection = getInternalFunction("getStarKTypeProjection")
|
||||
override val createCovariantKTypeProjection = getInternalFunction("createCovariantKTypeProjection")
|
||||
@@ -67,6 +66,7 @@ class WasmSymbols(
|
||||
|
||||
val getTypeInfoTypeDataByPtr: IrSimpleFunctionSymbol = getInternalFunction("getTypeInfoTypeDataByPtr")
|
||||
val wasmTypeInfoData: IrClassSymbol = getInternalClass("TypeInfoData")
|
||||
val kClassImpl: IrClassSymbol = getInternalClass("KClassImpl")
|
||||
}
|
||||
|
||||
internal val reflectionSymbols: WasmReflectionSymbols = WasmReflectionSymbols()
|
||||
@@ -408,6 +408,8 @@ class WasmSymbols(
|
||||
|
||||
internal val throwAsJsException: IrSimpleFunctionSymbol =
|
||||
getInternalFunction("throwAsJsException")
|
||||
|
||||
val kExternalClassImpl: IrClassSymbol = getInternalClass("KExternalClassImpl")
|
||||
}
|
||||
|
||||
private val wasmExportClass = getIrClass(FqName("kotlin.wasm.WasmExport"))
|
||||
|
||||
+2
-1
@@ -63,6 +63,7 @@ class AssociatedObjectsLowering(val context: WasmBackendContext) : FileLoweringP
|
||||
for (klassAnnotation in declaration.annotations) {
|
||||
val annotationClass = klassAnnotation.symbol.owner.parentClassOrNull ?: continue
|
||||
if (klassAnnotation.valueArgumentsCount != 1) continue
|
||||
if (declaration.isEffectivelyExternal()) continue
|
||||
val associatedObject = klassAnnotation.associatedObject() ?: continue
|
||||
|
||||
val builder = cachedBuilder ?: context.createIrBuilder(context.wasmSymbols.tryGetAssociatedObject)
|
||||
@@ -102,7 +103,7 @@ private fun IrBuilderWithScope.createAssociatedObjectSelector(
|
||||
condition = irEquals(classIdParam, classId),
|
||||
thenPart = irIfThen(
|
||||
condition = irEquals(keyIdParam, keyId),
|
||||
thenPart = irReturn(irGetObjectValue(irBuiltIns.anyType, associatedObject))
|
||||
thenPart = irReturn(irGetObjectValue(associatedObject.defaultType, associatedObject))
|
||||
)
|
||||
)
|
||||
}
|
||||
+61
-24
@@ -12,21 +12,24 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.config.AnalysisFlags
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.calls.EnumIntrinsicsUtils
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.erasedUpperBound
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.isEqualsInheritedFromAny
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.putClassTypeArgument
|
||||
import org.jetbrains.kotlin.ir.util.toIrConst
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.config.WasmTarget
|
||||
import org.jetbrains.kotlin.name.parentOrNull
|
||||
|
||||
class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
|
||||
@@ -160,35 +163,35 @@ class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
|
||||
val newSymbol = irBuiltins.suspendFunctionN(arity).getSimpleFunction("invoke")!!
|
||||
return irCall(call, newSymbol, argumentsAsReceivers = true)
|
||||
}
|
||||
symbols.reflectionSymbols.getClassData -> {
|
||||
context.reflectionSymbols.getKClass -> {
|
||||
val type = call.getTypeArgument(0)!!
|
||||
val klass = type.classOrNull?.owner ?: error("Invalid type")
|
||||
|
||||
val typeId = builder.irCall(symbols.wasmTypeId).also {
|
||||
it.putTypeArgument(0, type)
|
||||
val constructorArgument: IrExpression
|
||||
val kclassConstructor: IrConstructor
|
||||
if (klass.isEffectivelyExternal()) {
|
||||
check(context.isWasmJsTarget) { "External classes reflection in WASI mode are not supported" }
|
||||
kclassConstructor = symbols.jsRelatedSymbols.kExternalClassImpl.owner.constructors.first()
|
||||
constructorArgument = getExternalKClassCtorArgument(type, builder)
|
||||
} else {
|
||||
kclassConstructor = symbols.reflectionSymbols.kClassImpl.owner.constructors.first()
|
||||
constructorArgument = getKClassCtorArgument(type, builder)
|
||||
}
|
||||
|
||||
if (!klass.isInterface) {
|
||||
return builder.irCall(context.wasmSymbols.reflectionSymbols.getTypeInfoTypeDataByPtr).also {
|
||||
it.putValueArgument(0, typeId)
|
||||
}
|
||||
} else {
|
||||
val infoDataCtor = symbols.reflectionSymbols.wasmTypeInfoData.constructors.first()
|
||||
val fqName = type.classFqName!!
|
||||
val fqnShouldBeEmitted =
|
||||
context.configuration.languageVersionSettings.getFlag(AnalysisFlags.allowFullyQualifiedNameInKClass)
|
||||
val packageName = if (fqnShouldBeEmitted) fqName.parentOrNull()?.asString() ?: "" else ""
|
||||
val typeName = fqName.shortName().asString()
|
||||
|
||||
return with(builder) {
|
||||
irCallConstructor(infoDataCtor, emptyList()).also {
|
||||
it.putValueArgument(0, typeId)
|
||||
it.putValueArgument(1, packageName.toIrConst(context.irBuiltIns.stringType))
|
||||
it.putValueArgument(2, typeName.toIrConst(context.irBuiltIns.stringType))
|
||||
}
|
||||
}
|
||||
return IrConstructorCallImpl(
|
||||
startOffset = UNDEFINED_OFFSET,
|
||||
endOffset = UNDEFINED_OFFSET,
|
||||
type = kclassConstructor.returnType,
|
||||
symbol = kclassConstructor.symbol,
|
||||
typeArgumentsCount = 1,
|
||||
valueArgumentsCount = 1,
|
||||
constructorTypeArgumentsCount = 0
|
||||
).also {
|
||||
it.putClassTypeArgument(0, type)
|
||||
it.putValueArgument(0, constructorArgument)
|
||||
}
|
||||
}
|
||||
|
||||
symbols.enumValueOfIntrinsic ->
|
||||
return EnumIntrinsicsUtils.transformEnumValueOfIntrinsic(call)
|
||||
symbols.enumValuesIntrinsic ->
|
||||
@@ -200,6 +203,40 @@ class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
|
||||
return call
|
||||
}
|
||||
|
||||
private fun getKClassCtorArgument(type: IrType, builder: DeclarationIrBuilder): IrExpression {
|
||||
val klass = type.classOrNull?.owner ?: error("Invalid type")
|
||||
|
||||
val typeId = builder.irCall(symbols.wasmTypeId).also {
|
||||
it.putTypeArgument(0, type)
|
||||
}
|
||||
|
||||
if (!klass.isInterface) {
|
||||
return builder.irCall(context.wasmSymbols.reflectionSymbols.getTypeInfoTypeDataByPtr).also {
|
||||
it.putValueArgument(0, typeId)
|
||||
}
|
||||
} else {
|
||||
val fqName = type.classFqName!!
|
||||
val fqnShouldBeEmitted =
|
||||
context.configuration.languageVersionSettings.getFlag(AnalysisFlags.allowFullyQualifiedNameInKClass)
|
||||
val packageName = if (fqnShouldBeEmitted) fqName.parentOrNull()?.asString() ?: "" else ""
|
||||
val typeName = fqName.shortName().asString()
|
||||
|
||||
return builder.irCallConstructor(symbols.reflectionSymbols.wasmTypeInfoData.constructors.first(), emptyList()).also {
|
||||
it.putValueArgument(0, typeId)
|
||||
it.putValueArgument(1, packageName.toIrConst(context.irBuiltIns.stringType))
|
||||
it.putValueArgument(2, typeName.toIrConst(context.irBuiltIns.stringType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getExternalKClassCtorArgument(type: IrType, builder: DeclarationIrBuilder): IrExpression {
|
||||
val klass = type.classOrNull?.owner ?: error("Invalid type")
|
||||
check(klass.kind != ClassKind.INTERFACE) { "External interface must not be a class literal" }
|
||||
val classGetClassFunction = context.mapping.wasmGetJsClass[klass]!!
|
||||
val wrappedGetClassIfAny = context.mapping.wasmJsInteropFunctionToWrapper[classGetClassFunction] ?: classGetClassFunction
|
||||
return builder.irCall(wrappedGetClassIfAny)
|
||||
}
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
val builder = context.createIrBuilder(irFile.symbol)
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
|
||||
|
||||
+17
-5
@@ -15,10 +15,7 @@ import org.jetbrains.kotlin.backend.wasm.utils.getWasmImportDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsQualifier
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.builders.irCallConstructor
|
||||
@@ -28,6 +25,7 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -96,8 +94,10 @@ class ComplexExternalDeclarationsToTopLevelFunctionsLowering(val context: WasmBa
|
||||
if (klass.kind == ClassKind.OBJECT)
|
||||
generateExternalObjectInstanceGetter(klass)
|
||||
|
||||
if (klass.kind != ClassKind.INTERFACE)
|
||||
if (klass.kind != ClassKind.INTERFACE) {
|
||||
generateInstanceCheckForExternalClass(klass)
|
||||
generateGetClassForExternalClass(klass)
|
||||
}
|
||||
}
|
||||
|
||||
fun processExternalProperty(property: IrProperty) {
|
||||
@@ -374,6 +374,18 @@ class ComplexExternalDeclarationsToTopLevelFunctionsLowering(val context: WasmBa
|
||||
}
|
||||
}
|
||||
|
||||
fun generateGetClassForExternalClass(klass: IrClass) {
|
||||
context.mapping.wasmGetJsClass[klass] = createExternalJsFunction(
|
||||
klass.name,
|
||||
"_\$external_class_get",
|
||||
resultType = context.wasmSymbols.jsRelatedSymbols.jsAnyType.makeNullable(),
|
||||
jsCode = buildString {
|
||||
append("() => ")
|
||||
appendExternalClassReference(klass)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun createExternalJsFunction(
|
||||
originalName: Name,
|
||||
suffix: String,
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.backend.wasm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.ClassReferenceLowering
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
|
||||
class WasmClassReferenceLowering(context: WasmBackendContext) : ClassReferenceLowering(context) {
|
||||
override fun callGetKClass(
|
||||
returnType: IrType,
|
||||
typeArgument: IrType
|
||||
): IrCall {
|
||||
val primitiveKClass =
|
||||
getFinalPrimitiveKClass(returnType, typeArgument) ?: getOpenPrimitiveKClass(returnType, typeArgument)
|
||||
|
||||
if (primitiveKClass != null)
|
||||
return primitiveKClass
|
||||
|
||||
return JsIrBuilder.buildCall(reflectionSymbols.getKClass, returnType).also {
|
||||
it.putTypeArgument(0, typeArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// TARGET_BACKEND: WASM
|
||||
|
||||
// FILE: findAssociatedExternalObject.js
|
||||
|
||||
const JS_OBJECT = {}
|
||||
|
||||
// FILE: findAssociatedExternalObject.kt
|
||||
|
||||
import kotlin.reflect.*
|
||||
|
||||
@OptIn(ExperimentalAssociatedObjects::class)
|
||||
@AssociatedObjectKey
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Associated(val kClass: KClass<*>)
|
||||
|
||||
external object JS_OBJECT
|
||||
|
||||
@Associated(JS_OBJECT::class)
|
||||
class ObjectKey
|
||||
|
||||
@OptIn(ExperimentalAssociatedObjects::class)
|
||||
fun box(): String {
|
||||
if (ObjectKey::class.findAssociatedObject<Associated>() !== JS_OBJECT) return "FAIL2"
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// TARGET_BACKEND: WASM
|
||||
|
||||
// FILE: reflectionOnExternals.js
|
||||
class Outer { }
|
||||
|
||||
Outer.Inner = Promise;
|
||||
|
||||
// FILE: reflectionOnExternals.kt
|
||||
import kotlin.js.Promise
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
@JsName("Promise")
|
||||
external class PromiseAlias : JsAny
|
||||
|
||||
external class Outer : JsAny {
|
||||
class Inner : JsAny
|
||||
@JsName("Inner")
|
||||
class InnerAlias : JsAny
|
||||
}
|
||||
|
||||
fun checkPromise(kClass: KClass<*>, promiseObject: Any, nonPromiseObject: Any): String? {
|
||||
if (kClass.simpleName != "Promise") return "FAIL1"
|
||||
if (kClass != Promise::class) return "FAIL2"
|
||||
if (Promise::class != kClass) return "FAIL3"
|
||||
if (!kClass.isInstance(promiseObject)) return "FAIL4"
|
||||
if (kClass.isInstance(nonPromiseObject)) return "FAIL5"
|
||||
return null
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val promiseObj: Any = Promise<JsAny> { resolve, reject -> Unit }
|
||||
val someObject: Any = Any()
|
||||
checkPromise(Promise::class, promiseObj, someObject)?.let { return "1_" + it }
|
||||
checkPromise(PromiseAlias::class, promiseObj, someObject)?.let { return "2_" + it }
|
||||
checkPromise(promiseObj::class, promiseObj, someObject)?.let { return "3_" + it }
|
||||
checkPromise(Outer.Inner::class, promiseObj, someObject)?.let { return "4_" + it }
|
||||
checkPromise(Outer.InnerAlias::class, promiseObj, someObject)?.let { return "5_" + it }
|
||||
|
||||
val typeOfPromiseClassifier = typeOf<Promise<*>>().classifier
|
||||
if (typeOfPromiseClassifier !is KClass<*>) return "FAIL6"
|
||||
checkPromise(typeOfPromiseClassifier, promiseObj, someObject)?.let { return "7_" + it }
|
||||
|
||||
val typeOfPromiseWithArguments = typeOf<Promise<Promise<*>>>()
|
||||
if (typeOfPromiseWithArguments.arguments.size != 1) return "FAIL8"
|
||||
val typeOfPromiseArgumentClassifier = typeOfPromiseWithArguments.arguments[0].type?.classifier
|
||||
if (typeOfPromiseArgumentClassifier !is KClass<*>) return "FAIL9"
|
||||
checkPromise(typeOfPromiseArgumentClassifier, promiseObj, someObject)?.let { return "10_" + it }
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: WASM
|
||||
// EXPECTED_REACHABLE_NODES: 1414
|
||||
// FILE: main.kt
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ internal fun <T> wasmIsInterface(obj: Any): Boolean =
|
||||
internal fun <T> wasmTypeId(): Int =
|
||||
implementedAsIntrinsic
|
||||
|
||||
//TODO("Remove after bootstrap KT-65322")
|
||||
@ExcludedFromCodegen
|
||||
internal fun <T> wasmGetTypeInfoData(): TypeInfoData =
|
||||
implementedAsIntrinsic
|
||||
implementedAsIntrinsic
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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 kotlin.wasm.internal
|
||||
|
||||
import kotlin.reflect.*
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun getJsClassName(jsKlass: JsAny): String? =
|
||||
js("jsKlass.name")
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun instanceOf(ref: JsAny, jsKlass: JsAny): Boolean =
|
||||
js("ref instanceof jsKlass")
|
||||
|
||||
internal class KExternalClassImpl<T : Any> @WasmPrimitiveConstructor constructor(private val jsConstructor: JsAny) : KClass<T> {
|
||||
override val simpleName: String? get() = getJsClassName(jsConstructor)
|
||||
override val qualifiedName: String? get() = null
|
||||
|
||||
override fun isInstance(value: Any?): Boolean =
|
||||
value is JsExternalBox && instanceOf(value.ref, jsConstructor)
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is KExternalClassImpl<*> && jsConstructor == other.jsConstructor
|
||||
|
||||
override fun hashCode(): Int = simpleName.hashCode()
|
||||
|
||||
override fun toString(): String = "class $simpleName"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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 kotlin.wasm.internal
|
||||
|
||||
import kotlin.reflect.*
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun getConstructor(obj: JsAny): JsAny? =
|
||||
js("obj.constructor")
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal actual fun <T : Any> getKClassForObject(obj: Any): KClass<T> {
|
||||
if (obj !is JsExternalBox) return KClassImpl(getTypeInfoTypeDataByPtr(obj.typeInfo))
|
||||
val jsConstructor = getConstructor(obj.ref) ?: error("JavaScript constructor is not defined")
|
||||
return KExternalClassImpl(jsConstructor)
|
||||
}
|
||||
@@ -2,13 +2,9 @@
|
||||
* Copyright 2010-2021 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 kotlin.reflect.wasm.internal
|
||||
package kotlin.wasm.internal
|
||||
|
||||
import kotlin.reflect.*
|
||||
import kotlin.wasm.internal.TypeInfoData
|
||||
import kotlin.wasm.internal.getSuperTypeId
|
||||
import kotlin.wasm.internal.isInterfaceById
|
||||
import kotlin.wasm.internal.isInterfaceType
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal object NothingKClassImpl : KClass<Nothing> {
|
||||
override val simpleName: String = "Nothing"
|
||||
@@ -30,10 +26,10 @@ internal object ErrorKClass : KClass<Nothing> {
|
||||
override fun hashCode(): Int = super.hashCode() // KT-24971
|
||||
}
|
||||
|
||||
internal class KClassImpl<T : Any>(internal val typeData: TypeInfoData) : KClass<T> {
|
||||
internal class KClassImpl<T : Any> @WasmPrimitiveConstructor constructor(internal val typeData: TypeInfoData) : KClass<T> {
|
||||
override val simpleName: String get() = typeData.typeName
|
||||
override val qualifiedName: String =
|
||||
if (typeData.packageName.isEmpty()) typeData.typeName else "${typeData.packageName}.${typeData.typeName}"
|
||||
override val qualifiedName: String
|
||||
get() = if (typeData.packageName.isEmpty()) typeData.typeName else "${typeData.packageName}.${typeData.typeName}"
|
||||
|
||||
private fun checkSuperTypeInstance(obj: Any): Boolean {
|
||||
var typeId = obj.typeInfo
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package kotlin.wasm.internal
|
||||
|
||||
import kotlin.reflect.wasm.internal.KClassImpl
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@PublishedApi
|
||||
|
||||
@@ -7,7 +7,6 @@ package kotlin.wasm.internal
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.wasm.internal.*
|
||||
|
||||
internal object PrimitiveClasses {
|
||||
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
// a package is omitted to get declarations directly under the module
|
||||
package kotlin.wasm.internal
|
||||
|
||||
import kotlin.reflect.*
|
||||
import kotlin.reflect.wasm.internal.*
|
||||
|
||||
internal expect fun <T : Any> getKClassForObject(obj: Any): KClass<T>
|
||||
|
||||
//TODO(Replace getKClass to intrinsic argument-less implementation after bootstrap KT-65322")
|
||||
//@ExcludedFromCodegen
|
||||
//internal fun <T : Any> getKClass(): KClass<T> =
|
||||
// implementedAsIntrinsic
|
||||
internal fun <T : Any> getKClass(typeInfoData: TypeInfoData): KClass<T> =
|
||||
KClassImpl(typeInfoData)
|
||||
|
||||
@@ -34,12 +38,12 @@ internal fun <T : Any> getKClassFromExpression(e: T): KClass<T> =
|
||||
is DoubleArray -> PrimitiveClasses.doubleArrayClass
|
||||
is KClass<*> -> KClass::class
|
||||
is Array<*> -> PrimitiveClasses.arrayClass
|
||||
else -> getKClass(getTypeInfoTypeDataByPtr(e.typeInfo))
|
||||
else -> getKClassForObject(e)
|
||||
} as KClass<T>
|
||||
|
||||
@Suppress("REIFIED_TYPE_PARAMETER_NO_INLINE")
|
||||
internal inline fun <reified T : Any> wasmGetKClass(): KClass<T> =
|
||||
KClassImpl(wasmGetTypeInfoData<T>())
|
||||
KClassImpl(getTypeInfoTypeDataByPtr(wasmTypeId<T>()))
|
||||
|
||||
internal fun createKType(classifier: KClassifier, arguments: Array<KTypeProjection>, isMarkedNullable: Boolean): KType =
|
||||
KTypeImpl(classifier, arguments.asList(), isMarkedNullable)
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.
|
||||
*/
|
||||
|
||||
// a package is omitted to get declarations directly under the module
|
||||
package kotlin.wasm.internal
|
||||
|
||||
import kotlin.reflect.*
|
||||
|
||||
internal actual fun <T : Any> getKClassForObject(obj: Any): KClass<T> =
|
||||
KClassImpl(getTypeInfoTypeDataByPtr(obj.typeInfo))
|
||||
Generated
+12
@@ -24,6 +24,12 @@ public class FirWasmJsCodegenInteropTestGenerated extends AbstractFirWasmJsCodeg
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.WASM, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("associatedExternalObject.kt")
|
||||
public void testAssociatedExternalObject() {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/associatedExternalObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("callingWasmDirectly.kt")
|
||||
public void testCallingWasmDirectly() {
|
||||
@@ -162,6 +168,12 @@ public class FirWasmJsCodegenInteropTestGenerated extends AbstractFirWasmJsCodeg
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/nullableExternRefs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reflectionOnExternals.kt")
|
||||
public void testReflectionOnExternals() {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/reflectionOnExternals.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("types.kt")
|
||||
public void testTypes() {
|
||||
|
||||
+12
@@ -24,6 +24,12 @@ public class K1WasmCodegenWasmJsInteropTestGenerated extends AbstractK1WasmCodeg
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.WASM, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("associatedExternalObject.kt")
|
||||
public void testAssociatedExternalObject() {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/associatedExternalObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("callingWasmDirectly.kt")
|
||||
public void testCallingWasmDirectly() {
|
||||
@@ -162,6 +168,12 @@ public class K1WasmCodegenWasmJsInteropTestGenerated extends AbstractK1WasmCodeg
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/nullableExternRefs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reflectionOnExternals.kt")
|
||||
public void testReflectionOnExternals() {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/reflectionOnExternals.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("types.kt")
|
||||
public void testTypes() {
|
||||
|
||||
Reference in New Issue
Block a user