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_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()
@@ -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
@@ -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,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// FILE: JavaAnn.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// FILE: JavaAnn.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// FILE: JavaAnn.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// FILE: JavaAnn.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// FILE: JavaAnn.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// FILE: JavaAnn.java