[WasmJs] Add support for external class reflection
Fix #KT-64890
This commit is contained in:
+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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user