JVM_IR: Handle annotation KClass fields in external declarations

This commit is contained in:
Steven Schäfer
2019-06-25 11:05:25 +02:00
committed by Alexander Udalov
parent 1ab0b76af3
commit e7a5e5b4a0
10 changed files with 41 additions and 146 deletions
@@ -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()
@@ -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
@@ -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,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// FILE: JavaAnn.java // FILE: JavaAnn.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// FILE: JavaAnn.java // FILE: JavaAnn.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// FILE: JavaAnn.java // FILE: JavaAnn.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// FILE: JavaAnn.java // FILE: JavaAnn.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// FILE: JavaAnn.java // FILE: JavaAnn.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// FILE: JavaAnn.java // FILE: JavaAnn.java