[WasmJs] Add support for external class reflection

Fix #KT-64890
This commit is contained in:
Igor Yakovlev
2024-01-26 21:16:29 +01:00
parent e302420197
commit 6930fc8fed
24 changed files with 330 additions and 81 deletions
@@ -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?
@@ -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)
@@ -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"))
@@ -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))
)
)
}
@@ -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() {
@@ -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,
@@ -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))
@@ -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() {
@@ -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() {