diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java index 76135ba1dee..e35e8655b1b 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java @@ -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); diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt index 87467212ecd..4268f2d15f6 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt @@ -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() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt index 1568857c307..a6aa4ff8353 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/PromisedValue.kt @@ -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 diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AnnotationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AnnotationLowering.kt index ffc9c799f3d..1c0db360a5c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AnnotationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AnnotationLowering.kt @@ -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( + { 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 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 -> Array) - 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>) - * - * 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 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): IrType = - context.ir.symbols.javaLangClass.createType(false, typeArguments) - - private fun javaClassArrayType(variance: Variance, typeArguments: List): 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() diff --git a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/arrayClassParameter.kt b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/arrayClassParameter.kt index 3da004bc51a..dbecbc2f49e 100644 --- a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/arrayClassParameter.kt +++ b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/arrayClassParameter.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // FILE: JavaAnn.java diff --git a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/arrayClassParameterOnJavaClass.kt b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/arrayClassParameterOnJavaClass.kt index cfb05a3a7c0..91840ba4fa5 100644 --- a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/arrayClassParameterOnJavaClass.kt +++ b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/arrayClassParameterOnJavaClass.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // FILE: JavaAnn.java diff --git a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/classParameter.kt b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/classParameter.kt index bf488788663..2531baa93a1 100644 --- a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/classParameter.kt +++ b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/classParameter.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // FILE: JavaAnn.java diff --git a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/classParameterOnJavaClass.kt b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/classParameterOnJavaClass.kt index 17b1b8382b0..78dfd9e4ef9 100644 --- a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/classParameterOnJavaClass.kt +++ b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/classParameterOnJavaClass.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // FILE: JavaAnn.java diff --git a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/varargClassParameter.kt b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/varargClassParameter.kt index b64f9b7fb7e..52b64832711 100644 --- a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/varargClassParameter.kt +++ b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/varargClassParameter.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // FILE: JavaAnn.java diff --git a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/varargClassParameterOnJavaClass.kt b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/varargClassParameterOnJavaClass.kt index d081871118a..1e3d9a9e5c0 100644 --- a/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/varargClassParameterOnJavaClass.kt +++ b/compiler/testData/codegen/boxAgainstJava/annotations/kClassMapping/varargClassParameterOnJavaClass.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // FILE: JavaAnn.java