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_STRING_TYPE = getType(String.class);
|
||||||
public static final Type JAVA_THROWABLE_TYPE = getType(Throwable.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_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 ENUM_TYPE = getType(Enum.class);
|
||||||
public static final Type NUMBER_TYPE = getType(Number.class);
|
public static final Type NUMBER_TYPE = getType(Number.class);
|
||||||
public static final Type BOOLEAN_WRAPPER_TYPE = getType(Boolean.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.isContinuation(): Boolean = isNameInPackage("Continuation", kotlinCoroutinesPackageFqn)
|
||||||
fun IrType.isNullableContinuation(): Boolean = isContinuation() && this is IrSimpleType && hasQuestionMark
|
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 {
|
fun IrType.isNameInPackage(prefix: String, packageFqName: FqName): Boolean {
|
||||||
val classifier = classifierOrNull ?: return false
|
val classifier = classifierOrNull ?: return false
|
||||||
val name = classifier.descriptor.name.asString()
|
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.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
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.kotlin.types.KotlinType
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
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
|
// 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) {
|
else -> object : PromisedValue(codegen, target, irTarget) {
|
||||||
override fun materialize() {
|
override fun materialize() {
|
||||||
val value = this@coerce.materialized
|
val value = this@coerce.materialized
|
||||||
|
|||||||
+14
-138
@@ -6,159 +6,35 @@
|
|||||||
package org.jetbrains.kotlin.backend.jvm.lower
|
package org.jetbrains.kotlin.backend.jvm.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
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.common.phaser.makeIrFilePhase
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
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.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildField
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.builders.irCall
|
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||||
import org.jetbrains.kotlin.ir.builders.irGet
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.util.isAnnotationClass
|
||||||
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.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
import org.jetbrains.kotlin.types.Variance
|
|
||||||
|
|
||||||
internal val annotationPhase = makeIrFilePhase(
|
internal val annotationPhase = makeIrFilePhase<JvmBackendContext>(
|
||||||
::AnnotationLowering,
|
{ AnnotationLowering() },
|
||||||
name = "Annotation",
|
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
|
* Remove the constructors from annotation classes.
|
||||||
* and Array<KClass> fields to use java.lang.Class instead. This phase also rewrites
|
|
||||||
* the uses of annotation class fields appropriately.
|
|
||||||
*/
|
*/
|
||||||
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) {
|
override fun visitClass(declaration: IrClass): IrStatement {
|
||||||
irFile.transformChildrenVoid(this)
|
if (!declaration.isAnnotationClass) return super.visitClass(declaration)
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitClass(irClass: IrClass): IrStatement {
|
declaration.declarations.removeIf {
|
||||||
if (!irClass.isAnnotationClass) return super.visitClass(irClass)
|
|
||||||
|
|
||||||
irClass.declarations.removeIf {
|
|
||||||
it is IrConstructor
|
it is IrConstructor
|
||||||
}
|
}
|
||||||
|
|
||||||
for (declaration in irClass.declarations)
|
return declaration
|
||||||
if (declaration is IrSimpleFunction)
|
|
||||||
lowerAnnotationField(declaration)
|
|
||||||
|
|
||||||
return irClass
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// WITH_RUNTIME
|
||||||
// FILE: JavaAnn.java
|
// FILE: JavaAnn.java
|
||||||
|
|
||||||
|
|||||||
compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/arrayClassParameterOnJavaClass.kt
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// FILE: JavaAnn.java
|
// FILE: JavaAnn.java
|
||||||
|
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// FILE: JavaAnn.java
|
// FILE: JavaAnn.java
|
||||||
|
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// FILE: JavaAnn.java
|
// FILE: JavaAnn.java
|
||||||
|
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// FILE: JavaAnn.java
|
// FILE: JavaAnn.java
|
||||||
|
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// FILE: JavaAnn.java
|
// FILE: JavaAnn.java
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user