Skip to content

Commit 1a630b5

Browse files
igoriakovlevSpace Team
authored and
Space Team
committed
[WasmJs] Add support for external class reflection
Fix #KT-64890
1 parent 0872420 commit 1a630b5

File tree

24 files changed

+332
-81
lines changed

24 files changed

+332
-81
lines changed

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt

-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
281281
override val getKClassFromExpression = getInternalWithoutPackage("getKClassFromExpression")
282282
override val primitiveClassesObject = context.getIrClass(FqName("kotlin.reflect.js.internal.PrimitiveClasses"))
283283
override val kTypeClass: IrClassSymbol = context.getIrClass(FqName("kotlin.reflect.KType"))
284-
override val getClassData: IrSimpleFunctionSymbol get() = jsClass
285284
}
286285

287286
internal val reflectionSymbols: JsReflectionSymbols = JsReflectionSymbols()

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -758,8 +758,8 @@ private val jsClassUsageInReflectionPhase = makeBodyLoweringPhase(
758758
)
759759

760760
private val classReferenceLoweringPhase = makeBodyLoweringPhase(
761-
::ClassReferenceLowering,
762-
name = "ClassReferenceLowering",
761+
::JsClassReferenceLowering,
762+
name = "JsClassReferenceLowering",
763763
description = "Handle class references",
764764
prerequisite = setOf(jsClassUsageInReflectionPhase)
765765
)

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt

+3
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,7 @@ class JsMapping : DefaultMapping() {
5757

5858
val wasmExternalClassToInstanceCheck =
5959
DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
60+
61+
val wasmGetJsClass =
62+
DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
6063
}

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ReflectionSymbols.kt

-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@ package org.jetbrains.kotlin.ir.backend.js
77

88
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
99
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
10-
import org.jetbrains.kotlin.ir.types.IrType
1110

1211
interface ReflectionSymbols {
1312
val getKClassFromExpression: IrSimpleFunctionSymbol
1413
val getKClass: IrSimpleFunctionSymbol
15-
val getClassData: IrSimpleFunctionSymbol
1614
val createKType: IrSimpleFunctionSymbol?
1715
val createDynamicKType: IrSimpleFunctionSymbol?
1816
val createKTypeParameter: IrSimpleFunctionSymbol?

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ClassReferenceLowering.kt

+33-25
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,36 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
2525
import org.jetbrains.kotlin.name.Name
2626
import org.jetbrains.kotlin.types.*
2727

28-
class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
28+
class JsClassReferenceLowering(context: JsIrBackendContext) : ClassReferenceLowering(context) {
29+
private val getClassData = context.intrinsics.jsClass
2930

30-
private val reflectionSymbols get() = context.reflectionSymbols
31+
override fun callGetKClass(
32+
returnType: IrType,
33+
typeArgument: IrType
34+
): IrCall {
35+
val primitiveKClass =
36+
getFinalPrimitiveKClass(returnType, typeArgument) ?: getOpenPrimitiveKClass(returnType, typeArgument)
37+
38+
if (primitiveKClass != null)
39+
return primitiveKClass
40+
41+
return JsIrBuilder.buildCall(reflectionSymbols.getKClass, returnType, listOf(typeArgument))
42+
.apply {
43+
putValueArgument(0, callGetClassByType(typeArgument))
44+
}
45+
}
46+
47+
private fun callGetClassByType(type: IrType) =
48+
JsIrBuilder.buildCall(
49+
getClassData,
50+
typeArguments = listOf(type),
51+
origin = JsStatementOrigins.CLASS_REFERENCE
52+
)
53+
}
54+
55+
abstract class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
56+
57+
protected val reflectionSymbols get() = context.reflectionSymbols
3158

3259
private val primitiveClassProperties by lazy(LazyThreadSafetyMode.NONE) {
3360
reflectionSymbols.primitiveClassesObject.owner.declarations.filterIsInstance<IrProperty>()
@@ -96,7 +123,7 @@ class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLowering
96123
)
97124
}
98125

99-
private fun getFinalPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
126+
protected fun getFinalPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
100127
for ((typePredicate, v) in finalPrimitiveClasses) {
101128
if (typePredicate(typeArgument))
102129
return getPrimitiveClass(v, returnType)
@@ -106,7 +133,7 @@ class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLowering
106133
}
107134

108135

109-
private fun getOpenPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
136+
protected fun getOpenPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
110137
for ((typePredicate, v) in openPrimitiveClasses) {
111138
if (typePredicate(typeArgument))
112139
return getPrimitiveClass(v, returnType)
@@ -123,28 +150,10 @@ class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLowering
123150
return null
124151
}
125152

126-
private fun callGetKClass(
153+
abstract fun callGetKClass(
127154
returnType: IrType = reflectionSymbols.getKClass.owner.returnType,
128155
typeArgument: IrType
129-
): IrCall {
130-
val primitiveKClass =
131-
getFinalPrimitiveKClass(returnType, typeArgument) ?: getOpenPrimitiveKClass(returnType, typeArgument)
132-
133-
if (primitiveKClass != null)
134-
return primitiveKClass
135-
136-
return JsIrBuilder.buildCall(reflectionSymbols.getKClass, returnType, listOf(typeArgument))
137-
.apply {
138-
putValueArgument(0, callGetClassByType(typeArgument))
139-
}
140-
}
141-
142-
private fun callGetClassByType(type: IrType) =
143-
JsIrBuilder.buildCall(
144-
reflectionSymbols.getClassData,
145-
typeArguments = listOf(type),
146-
origin = JsStatementOrigins.CLASS_REFERENCE
147-
)
156+
): IrCall
148157

149158
private fun buildCall(name: IrSimpleFunctionSymbol, vararg args: IrExpression): IrExpression =
150159
JsIrBuilder.buildCall(name).apply {
@@ -154,7 +163,6 @@ class ClassReferenceLowering(val context: JsCommonBackendContext) : BodyLowering
154163
}
155164

156165
private fun createKType(type: IrType, visitedTypeParams: MutableSet<IrTypeParameter>): IrExpression {
157-
158166
if (type is IrSimpleType)
159167
return createSimpleKType(type, visitedTypeParams)
160168
if (type is IrDynamicType)

compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,8 @@ private val staticMembersLoweringPhase = makeWasmModulePhase(
489489
)
490490

491491
private val classReferenceLoweringPhase = makeWasmModulePhase(
492-
::ClassReferenceLowering,
493-
name = "ClassReferenceLowering",
492+
::WasmClassReferenceLowering,
493+
name = "WasmClassReferenceLowering",
494494
description = "Handle class references"
495495
)
496496

@@ -681,6 +681,8 @@ val wasmPhases = SameTypeNamedCompilerPhase(
681681

682682
wasmStringSwitchOptimizerLowering then
683683

684+
associatedObjectsLowering then
685+
684686
complexExternalDeclarationsToTopLevelFunctionsLowering then
685687
complexExternalDeclarationsUsagesLowering then
686688

@@ -727,8 +729,6 @@ val wasmPhases = SameTypeNamedCompilerPhase(
727729
eraseVirtualDispatchReceiverParametersTypes then
728730
bridgesConstructionPhase then
729731

730-
associatedObjectsLowering then
731-
732732
objectDeclarationLoweringPhase then
733733
genericReturnTypeLowering then
734734
unitToVoidLowering then

compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,9 @@ class WasmSymbols(
5252

5353
internal inner class WasmReflectionSymbols : ReflectionSymbols {
5454
override val createKType: IrSimpleFunctionSymbol = getInternalFunction("createKType")
55-
override val getClassData: IrSimpleFunctionSymbol = getInternalFunction("wasmGetTypeInfoData")
5655
override val getKClass: IrSimpleFunctionSymbol = getInternalFunction("getKClass")
5756
override val getKClassFromExpression: IrSimpleFunctionSymbol = getInternalFunction("getKClassFromExpression")
58-
override val createDynamicKType: IrSimpleFunctionSymbol get() = error("Dynamic type is not supported by WASM")
57+
override val createDynamicKType: IrSimpleFunctionSymbol get() = error("Dynamic type is not supported by Wasm")
5958
override val createKTypeParameter: IrSimpleFunctionSymbol = getInternalFunction("createKTypeParameter")
6059
override val getStarKTypeProjection = getInternalFunction("getStarKTypeProjection")
6160
override val createCovariantKTypeProjection = getInternalFunction("createCovariantKTypeProjection")
@@ -67,6 +66,7 @@ class WasmSymbols(
6766

6867
val getTypeInfoTypeDataByPtr: IrSimpleFunctionSymbol = getInternalFunction("getTypeInfoTypeDataByPtr")
6968
val wasmTypeInfoData: IrClassSymbol = getInternalClass("TypeInfoData")
69+
val kClassImpl: IrClassSymbol = getInternalClass("KClassImpl")
7070
}
7171

7272
internal val reflectionSymbols: WasmReflectionSymbols = WasmReflectionSymbols()
@@ -359,6 +359,8 @@ class WasmSymbols(
359359

360360
internal val throwAsJsException: IrSimpleFunctionSymbol =
361361
getInternalFunction("throwAsJsException")
362+
363+
val kExternalClassImpl: IrClassSymbol = getInternalClass("KExternalClassImpl")
362364
}
363365

364366
private val wasmExportClass = getIrClass(FqName("kotlin.wasm.WasmExport"))

compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/AssociatedObjectsLowering.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class AssociatedObjectsLowering(val context: WasmBackendContext) : FileLoweringP
6060
for (klassAnnotation in declaration.annotations) {
6161
val annotationClass = klassAnnotation.symbol.owner.parentClassOrNull ?: continue
6262
if (klassAnnotation.valueArgumentsCount != 1) continue
63+
if (declaration.isEffectivelyExternal()) continue
6364
val associatedObject = klassAnnotation.associatedObject() ?: continue
6465

6566
val builder = cachedBuilder ?: context.createIrBuilder(context.wasmSymbols.initAssociatedObjects)
@@ -104,7 +105,7 @@ private fun IrBuilderWithScope.createAssociatedObjectAdd(
104105
)
105106
addCall.putValueArgument(
106107
3,
107-
irGetObjectValue(irBuiltIns.anyType, associatedObject)
108+
irGetObjectValue(associatedObject.defaultType, associatedObject)
108109
)
109110
}
110111
}

compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/BuiltInsLowering.kt

+61-22
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,19 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
1212
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
1313
import org.jetbrains.kotlin.config.AnalysisFlags
1414
import org.jetbrains.kotlin.config.languageVersionSettings
15+
import org.jetbrains.kotlin.descriptors.ClassKind
16+
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
1517
import org.jetbrains.kotlin.ir.backend.js.lower.calls.EnumIntrinsicsUtils
1618
import org.jetbrains.kotlin.ir.backend.js.utils.erasedUpperBound
1719
import org.jetbrains.kotlin.ir.backend.js.utils.isEqualsInheritedFromAny
1820
import org.jetbrains.kotlin.ir.builders.*
21+
import org.jetbrains.kotlin.ir.declarations.IrConstructor
1922
import org.jetbrains.kotlin.ir.declarations.IrFile
2023
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
2124
import org.jetbrains.kotlin.ir.expressions.IrCall
2225
import org.jetbrains.kotlin.ir.expressions.IrExpression
26+
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
27+
import org.jetbrains.kotlin.ir.expressions.putClassTypeArgument
2328
import org.jetbrains.kotlin.ir.util.toIrConst
2429
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
2530
import org.jetbrains.kotlin.ir.types.*
@@ -157,35 +162,35 @@ class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
157162
val newSymbol = irBuiltins.suspendFunctionN(arity).getSimpleFunction("invoke")!!
158163
return irCall(call, newSymbol, argumentsAsReceivers = true)
159164
}
160-
symbols.reflectionSymbols.getClassData -> {
165+
context.reflectionSymbols.getKClass -> {
161166
val type = call.getTypeArgument(0)!!
162167
val klass = type.classOrNull?.owner ?: error("Invalid type")
163168

164-
val typeId = builder.irCall(symbols.wasmTypeId).also {
165-
it.putTypeArgument(0, type)
169+
val constructorArgument: IrExpression
170+
val kclassConstructor: IrConstructor
171+
if (klass.isEffectivelyExternal()) {
172+
check(context.configuration.get(JSConfigurationKeys.WASM_TARGET, WasmTarget.JS) == WasmTarget.JS) { "External classes reflection in WASI mode are not supported" }
173+
kclassConstructor = symbols.jsRelatedSymbols.kExternalClassImpl.owner.constructors.first()
174+
constructorArgument = getExternalKClassCtorArgument(type, builder)
175+
} else {
176+
kclassConstructor = symbols.reflectionSymbols.kClassImpl.owner.constructors.first()
177+
constructorArgument = getKClassCtorArgument(type, builder)
166178
}
167179

168-
if (!klass.isInterface) {
169-
return builder.irCall(context.wasmSymbols.reflectionSymbols.getTypeInfoTypeDataByPtr).also {
170-
it.putValueArgument(0, typeId)
171-
}
172-
} else {
173-
val infoDataCtor = symbols.reflectionSymbols.wasmTypeInfoData.constructors.first()
174-
val fqName = type.classFqName!!
175-
val fqnShouldBeEmitted =
176-
context.configuration.languageVersionSettings.getFlag(AnalysisFlags.allowFullyQualifiedNameInKClass)
177-
val packageName = if (fqnShouldBeEmitted) fqName.parentOrNull()?.asString() ?: "" else ""
178-
val typeName = fqName.shortName().asString()
179-
180-
return with(builder) {
181-
irCallConstructor(infoDataCtor, emptyList()).also {
182-
it.putValueArgument(0, typeId)
183-
it.putValueArgument(1, packageName.toIrConst(context.irBuiltIns.stringType))
184-
it.putValueArgument(2, typeName.toIrConst(context.irBuiltIns.stringType))
185-
}
186-
}
180+
return IrConstructorCallImpl(
181+
startOffset = UNDEFINED_OFFSET,
182+
endOffset = UNDEFINED_OFFSET,
183+
type = kclassConstructor.returnType,
184+
symbol = kclassConstructor.symbol,
185+
typeArgumentsCount = 1,
186+
valueArgumentsCount = 1,
187+
constructorTypeArgumentsCount = 0
188+
).also {
189+
it.putClassTypeArgument(0, type)
190+
it.putValueArgument(0, constructorArgument)
187191
}
188192
}
193+
189194
symbols.enumValueOfIntrinsic ->
190195
return EnumIntrinsicsUtils.transformEnumValueOfIntrinsic(call)
191196
symbols.enumValuesIntrinsic ->
@@ -197,6 +202,40 @@ class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
197202
return call
198203
}
199204

205+
private fun getKClassCtorArgument(type: IrType, builder: DeclarationIrBuilder): IrExpression {
206+
val klass = type.classOrNull?.owner ?: error("Invalid type")
207+
208+
val typeId = builder.irCall(symbols.wasmTypeId).also {
209+
it.putTypeArgument(0, type)
210+
}
211+
212+
if (!klass.isInterface) {
213+
return builder.irCall(context.wasmSymbols.reflectionSymbols.getTypeInfoTypeDataByPtr).also {
214+
it.putValueArgument(0, typeId)
215+
}
216+
} else {
217+
val fqName = type.classFqName!!
218+
val fqnShouldBeEmitted =
219+
context.configuration.languageVersionSettings.getFlag(AnalysisFlags.allowFullyQualifiedNameInKClass)
220+
val packageName = if (fqnShouldBeEmitted) fqName.parentOrNull()?.asString() ?: "" else ""
221+
val typeName = fqName.shortName().asString()
222+
223+
return builder.irCallConstructor(symbols.reflectionSymbols.wasmTypeInfoData.constructors.first(), emptyList()).also {
224+
it.putValueArgument(0, typeId)
225+
it.putValueArgument(1, packageName.toIrConst(context.irBuiltIns.stringType))
226+
it.putValueArgument(2, typeName.toIrConst(context.irBuiltIns.stringType))
227+
}
228+
}
229+
}
230+
231+
private fun getExternalKClassCtorArgument(type: IrType, builder: DeclarationIrBuilder): IrExpression {
232+
val klass = type.classOrNull?.owner ?: error("Invalid type")
233+
check(klass.kind != ClassKind.INTERFACE) { "External interface must not be a class literal" }
234+
val classGetClassFunction = context.mapping.wasmGetJsClass[klass]!!
235+
val wrappedGetClassIfAny = context.mapping.wasmJsInteropFunctionToWrapper[classGetClassFunction] ?: classGetClassFunction
236+
return builder.irCall(wrappedGetClassIfAny)
237+
}
238+
200239
override fun lower(irFile: IrFile) {
201240
val builder = context.createIrBuilder(irFile.symbol)
202241
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {

compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt

+17-5
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ import org.jetbrains.kotlin.backend.wasm.utils.getWasmImportDescriptor
1515
import org.jetbrains.kotlin.descriptors.ClassKind
1616
import org.jetbrains.kotlin.ir.IrElement
1717
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
18-
import org.jetbrains.kotlin.ir.backend.js.utils.getJsModule
19-
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
20-
import org.jetbrains.kotlin.ir.backend.js.utils.getJsQualifier
21-
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
18+
import org.jetbrains.kotlin.ir.backend.js.utils.*
2219
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
2320
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
2421
import org.jetbrains.kotlin.ir.builders.irCallConstructor
@@ -28,6 +25,7 @@ import org.jetbrains.kotlin.ir.expressions.*
2825
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
2926
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
3027
import org.jetbrains.kotlin.ir.types.IrType
28+
import org.jetbrains.kotlin.ir.types.makeNullable
3129
import org.jetbrains.kotlin.ir.util.*
3230
import org.jetbrains.kotlin.ir.visitors.*
3331
import org.jetbrains.kotlin.name.Name
@@ -96,8 +94,10 @@ class ComplexExternalDeclarationsToTopLevelFunctionsLowering(val context: WasmBa
9694
if (klass.kind == ClassKind.OBJECT)
9795
generateExternalObjectInstanceGetter(klass)
9896

99-
if (klass.kind != ClassKind.INTERFACE)
97+
if (klass.kind != ClassKind.INTERFACE) {
10098
generateInstanceCheckForExternalClass(klass)
99+
generateGetClassForExternalClass(klass)
100+
}
101101
}
102102

103103
fun processExternalProperty(property: IrProperty) {
@@ -374,6 +374,18 @@ class ComplexExternalDeclarationsToTopLevelFunctionsLowering(val context: WasmBa
374374
}
375375
}
376376

377+
fun generateGetClassForExternalClass(klass: IrClass) {
378+
context.mapping.wasmGetJsClass[klass] = createExternalJsFunction(
379+
klass.name,
380+
"_\$external_class_get",
381+
resultType = context.wasmSymbols.jsRelatedSymbols.jsAnyType.makeNullable(),
382+
jsCode = buildString {
383+
append("() => ")
384+
appendExternalClassReference(klass)
385+
}
386+
)
387+
}
388+
377389
private fun createExternalJsFunction(
378390
originalName: Name,
379391
suffix: String,

0 commit comments

Comments
 (0)