JVM_IR: Handle annotation KClass fields in external declarations
This commit is contained in:
committed by
Alexander Udalov
parent
1ab0b76af3
commit
e7a5e5b4a0
@@ -19,6 +19,7 @@ public class AsmTypes {
|
||||
public static final Type JAVA_STRING_TYPE = getType(String.class);
|
||||
public static final Type JAVA_THROWABLE_TYPE = getType(Throwable.class);
|
||||
public static final Type JAVA_CLASS_TYPE = getType(Class.class);
|
||||
public static final Type JAVA_CLASS_ARRAY_TYPE = Type.getObjectType("[" + JAVA_CLASS_TYPE.getDescriptor());
|
||||
public static final Type ENUM_TYPE = getType(Enum.class);
|
||||
public static final Type NUMBER_TYPE = getType(Number.class);
|
||||
public static final Type BOOLEAN_WRAPPER_TYPE = getType(Boolean.class);
|
||||
|
||||
@@ -36,6 +36,13 @@ fun IrType.isKotlinResult(): Boolean = isNameInPackage("Result", kotlinPackageFq
|
||||
fun IrType.isContinuation(): Boolean = isNameInPackage("Continuation", kotlinCoroutinesPackageFqn)
|
||||
fun IrType.isNullableContinuation(): Boolean = isContinuation() && this is IrSimpleType && hasQuestionMark
|
||||
|
||||
fun IrType.isKClassArray(): Boolean {
|
||||
if (!isNonPrimitiveArray()) return false
|
||||
val argument = (this as? IrSimpleType)?.arguments?.singleOrNull() ?: return false
|
||||
val argumentType = (argument as? IrTypeProjection)?.type ?: return false
|
||||
return argumentType.isKClass()
|
||||
}
|
||||
|
||||
fun IrType.isNameInPackage(prefix: String, packageFqName: FqName): Boolean {
|
||||
val classifier = classifierOrNull ?: return false
|
||||
val name = classifier.descriptor.name.asString()
|
||||
|
||||
+19
-2
@@ -13,6 +13,9 @@ import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.ir.util.isKClass
|
||||
import org.jetbrains.kotlin.ir.util.isKClassArray
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -110,9 +113,23 @@ fun PromisedValue.coerce(target: Type, irTarget: IrType): PromisedValue {
|
||||
}
|
||||
}
|
||||
|
||||
return when (type) {
|
||||
return when {
|
||||
// All unsafe coercions between irTypes should use the UnsafeCoerce intrinsic
|
||||
target -> this
|
||||
type == target -> this
|
||||
type == AsmTypes.JAVA_CLASS_TYPE && target == AsmTypes.K_CLASS_TYPE && irType.isKClass() ->
|
||||
object : PromisedValue(codegen, target, irTarget) {
|
||||
override fun materialize() {
|
||||
this@coerce.materialize()
|
||||
AsmUtil.wrapJavaClassIntoKClass(mv)
|
||||
}
|
||||
}
|
||||
type == AsmTypes.JAVA_CLASS_ARRAY_TYPE && target == AsmTypes.K_CLASS_ARRAY_TYPE && irType.isKClassArray() ->
|
||||
object : PromisedValue(codegen, target, irTarget) {
|
||||
override fun materialize() {
|
||||
this@coerce.materialize()
|
||||
AsmUtil.wrapJavaClassesIntoKClasses(mv)
|
||||
}
|
||||
}
|
||||
else -> object : PromisedValue(codegen, target, irTarget) {
|
||||
override fun materialize() {
|
||||
val value = this@coerce.materialized
|
||||
|
||||
+14
-138
@@ -6,159 +6,35 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.KClassJavaProperty
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildField
|
||||
import org.jetbrains.kotlin.ir.builders.irCall
|
||||
import org.jetbrains.kotlin.ir.builders.irGet
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.util.isAnnotationClass
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
internal val annotationPhase = makeIrFilePhase(
|
||||
::AnnotationLowering,
|
||||
internal val annotationPhase = makeIrFilePhase<JvmBackendContext>(
|
||||
{ AnnotationLowering() },
|
||||
name = "Annotation",
|
||||
description = "Remove constructors and modify field types in annotation classes"
|
||||
description = "Remove constructors of annotation classes"
|
||||
)
|
||||
|
||||
/**
|
||||
* Remove the constructors from annotation classes and change the types of KClass
|
||||
* and Array<KClass> fields to use java.lang.Class instead. This phase also rewrites
|
||||
* the uses of annotation class fields appropriately.
|
||||
* Remove the constructors from annotation classes.
|
||||
*/
|
||||
private class AnnotationLowering(private val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
|
||||
private class AnnotationLowering : FileLoweringPass, IrElementTransformerVoid() {
|
||||
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid(this)
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(this)
|
||||
}
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
if (!declaration.isAnnotationClass) return super.visitClass(declaration)
|
||||
|
||||
override fun visitClass(irClass: IrClass): IrStatement {
|
||||
if (!irClass.isAnnotationClass) return super.visitClass(irClass)
|
||||
|
||||
irClass.declarations.removeIf {
|
||||
declaration.declarations.removeIf {
|
||||
it is IrConstructor
|
||||
}
|
||||
|
||||
for (declaration in irClass.declarations)
|
||||
if (declaration is IrSimpleFunction)
|
||||
lowerAnnotationField(declaration)
|
||||
|
||||
return irClass
|
||||
return declaration
|
||||
}
|
||||
|
||||
// Lower the types on annotation class fields (KClass -> Class, Array<KClass> -> Array<Class>)
|
||||
private fun lowerAnnotationField(declaration: IrSimpleFunction) {
|
||||
val property = declaration.correspondingPropertySymbol?.owner ?: return
|
||||
val field = property.backingField ?: return
|
||||
|
||||
val newType = when {
|
||||
field.type.isKClass() ->
|
||||
javaClassType((field.type as IrSimpleType).arguments)
|
||||
field.type.isKClassArray() -> {
|
||||
val projection = field.type.singleTypeProjectionOrNull as IrTypeProjection
|
||||
|
||||
javaClassArrayType(
|
||||
projection.variance,
|
||||
(projection.type as IrSimpleType).arguments
|
||||
)
|
||||
}
|
||||
else -> return
|
||||
}
|
||||
|
||||
val newField = buildField {
|
||||
updateFrom(field)
|
||||
name = field.name
|
||||
type = newType
|
||||
}
|
||||
|
||||
newField.correspondingPropertySymbol = property.symbol
|
||||
newField.initializer = field.initializer
|
||||
property.backingField = newField
|
||||
declaration.returnType = newType
|
||||
declaration.body = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap property accesses to annotation class fields if needed.
|
||||
* For example, assume that we have
|
||||
*
|
||||
* annotation class Ann(val c: KClass<*>, val ca: Array<KClass<*>>)
|
||||
*
|
||||
* and a variable `a: Ann`. Then we wrap a call of the form `a.c` in a call
|
||||
* to `getOrCreateKotlinClass` while `a.c.java` is reduced to `a.c`. Similarly,
|
||||
* a call of the form `a.ca` is wrapped with a call to `getOrCreateKotlinClasses`.
|
||||
*/
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
// Skip the KClass wrapper when it is only used to project out the Class instance
|
||||
var wrapIntoKClass = true
|
||||
var subject: IrExpression = expression
|
||||
if (expression.isGetJava()) {
|
||||
subject = expression.extensionReceiver!!
|
||||
wrapIntoKClass = false
|
||||
}
|
||||
|
||||
// Check for a property access on a KClass or Array<KClass> field of an
|
||||
// annotation class instance.
|
||||
val receiver = (subject as? IrCall)?.takeIf { it.symbol.owner.isGetter }
|
||||
?: return super.visitCall(expression)
|
||||
|
||||
val wrapIntoArray = receiver.type.isKClassArray()
|
||||
if (!wrapIntoArray && !receiver.type.isKClass())
|
||||
return super.visitCall(expression)
|
||||
|
||||
val function = (receiver.symbol.owner as? IrSimpleFunction)
|
||||
?.takeIf { (it.parent as? IrClass)?.isAnnotationClass ?: false }
|
||||
?: return super.visitCall(expression)
|
||||
|
||||
val field = function.correspondingPropertySymbol?.owner?.backingField
|
||||
?: return super.visitCall(expression)
|
||||
|
||||
// Wrap the property access with a call to getOrCreateKClass(es) and fix the type
|
||||
val irBuilder = context.createIrBuilder(function.symbol, expression.startOffset, expression.endOffset)
|
||||
val getField = irBuilder.irGet(field.type, receiver.dispatchReceiver!!, receiver.symbol)
|
||||
if (!wrapIntoKClass)
|
||||
return getField
|
||||
|
||||
return irBuilder.irCall(
|
||||
if (wrapIntoArray) context.ir.symbols.getOrCreateKotlinClasses else context.ir.symbols.getOrCreateKotlinClass
|
||||
).apply {
|
||||
putValueArgument(0, getField)
|
||||
}
|
||||
}
|
||||
|
||||
private fun javaClassType(typeArguments: List<IrTypeArgument>): IrType =
|
||||
context.ir.symbols.javaLangClass.createType(false, typeArguments)
|
||||
|
||||
private fun javaClassArrayType(variance: Variance, typeArguments: List<IrTypeArgument>): IrType {
|
||||
val argument = makeTypeProjection(javaClassType(typeArguments), variance)
|
||||
return context.irBuiltIns.arrayClass.createType(false, listOf(argument))
|
||||
}
|
||||
|
||||
private fun IrCall.isGetJava(): Boolean =
|
||||
context.irIntrinsics.getIntrinsic(symbol) is KClassJavaProperty
|
||||
}
|
||||
|
||||
private fun IrClassSymbol.getFunctionByName(name: String, numParams: Int): IrSimpleFunctionSymbol =
|
||||
functions
|
||||
.filter { it.owner.name.asString() == name }
|
||||
.single { it.owner.valueParameters.size == numParams }
|
||||
|
||||
private fun IrType.isKClassArray() =
|
||||
isNonPrimitiveArray() && singleTypeProjectionOrNull?.isKClass() ?: false
|
||||
|
||||
private val IrType.singleTypeProjectionOrNull: IrType?
|
||||
get() = (singleTypeArgumentOrNull as? IrTypeProjection)?.type
|
||||
|
||||
private val IrType.singleTypeArgumentOrNull: IrTypeArgument?
|
||||
get() = (this as? IrSimpleType)?.arguments?.singleOrNull()
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// FILE: JavaAnn.java
|
||||
|
||||
|
||||
compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/arrayClassParameterOnJavaClass.kt
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// FILE: JavaAnn.java
|
||||
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// FILE: JavaAnn.java
|
||||
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// FILE: JavaAnn.java
|
||||
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// FILE: JavaAnn.java
|
||||
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// FILE: JavaAnn.java
|
||||
|
||||
|
||||
Reference in New Issue
Block a user