JVM IR: fix VerifyError on annotated annotation properties

The problem was that we tried to generate an `$annotations` method for a
property declared in an annotation class. That method is final and has a
body, which is not allowed in annotation classes. Now we're generating
this method in the separate `$DefaultImpls` class as for properties in
interfaces.

Note that the added test still doesn't find any annotations because the
proper support is needed in reflection (KT-22463). Currently it only
checks that no VerifyError happens.
This commit is contained in:
Alexander Udalov
2019-11-14 17:04:38 +01:00
parent 994d4e081b
commit cdb7703947
6 changed files with 74 additions and 26 deletions

View File

@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.DescriptorsToIrRemapper
import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.builtins.CompanionObjectMapping.isMappedIntrinsicCompanionObject
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
@@ -135,7 +136,7 @@ class JvmDeclarationFactory(
fun getDefaultImplsFunction(interfaceFun: IrSimpleFunction): IrSimpleFunction {
val parent = interfaceFun.parentAsClass
assert(parent.isInterface) { "Parent of ${interfaceFun.dump()} should be interface" }
assert(parent.isJvmInterface) { "Parent of ${interfaceFun.dump()} should be interface" }
return defaultImplsMethods.getOrPut(interfaceFun) {
val defaultImpls = getDefaultImplsClass(interfaceFun.parentAsClass)

View File

@@ -12,7 +12,9 @@ import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.backend.jvm.ir.hasJvmDefault
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
@@ -41,11 +43,28 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
private val removedFunctions = hashMapOf<IrFunctionSymbol, IrFunctionSymbol>()
override fun lower(irClass: IrClass) {
if (!irClass.isInterface) return
if (!irClass.isJvmInterface) return
when (irClass.kind) {
ClassKind.INTERFACE -> handleInterface(irClass)
ClassKind.ANNOTATION_CLASS -> handleAnnotationClass(irClass)
else -> return
}
irClass.declarations.removeAll {
it is IrFunction && removedFunctions.containsKey(it.symbol)
}
val defaultImplsIrClass = context.declarationFactory.getDefaultImplsClass(irClass)
val members = defaultImplsIrClass.declarations
if (defaultImplsIrClass.declarations.isNotEmpty()) {
irClass.declarations.add(defaultImplsIrClass)
}
// Update IrElements (e.g., IrCalls) to point to the new functions.
irClass.transformChildrenVoid(this)
}
private fun handleInterface(irClass: IrClass) {
// There are 6 cases for functions on interfaces:
loop@ for (function in irClass.functions) {
when {
@@ -78,7 +97,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
&& !implementation.isMethodOfAny()
&& (!implementation.hasJvmDefault() || context.state.jvmDefaultMode.isCompatibility)
) {
delegateInheritedDefaultImplementationToDefaultImpls(function, implementation, defaultImplsIrClass)
delegateInheritedDefaultImplementationToDefaultImpls(function, implementation)
}
}
@@ -89,8 +108,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
Visibilities.isPrivate(function.visibility)
|| (function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER && !function.hasJvmDefault())
|| function.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS -> {
val defaultImpl = createDefaultImpl(function, members)
removedFunctions[function.symbol] = defaultImpl.symbol
removedFunctions[function.symbol] = createDefaultImpl(function).symbol
}
/**
@@ -98,7 +116,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
* an abstract stub is left.
*/
!function.hasJvmDefault() -> {
createDefaultImpl(function, members)
createDefaultImpl(function)
function.body = null
//TODO reset modality to abstract
}
@@ -107,7 +125,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
* 5) _With_ @JvmDefault, we move and bridge if in compatibility mode, ...
*/
context.state.jvmDefaultMode.isCompatibility -> {
val defaultImpl = createDefaultImpl(function, members)
val defaultImpl = createDefaultImpl(function)
function.body = IrExpressionBodyImpl(createDelegatingCall(defaultImpl, function))
}
@@ -115,9 +133,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
}
}
irClass.declarations.removeAll {
it is IrFunction && removedFunctions.containsKey(it.symbol)
}
val defaultImplsIrClass = context.declarationFactory.getDefaultImplsClass(irClass)
// Move metadata for local delegated properties from the interface to DefaultImpls, since this is where kotlin-reflect looks for it.
val localDelegatedProperties = context.localDelegatedProperties[irClass.attributeOwnerId as IrClass]
@@ -135,17 +151,24 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
delegatedPropertyArray.parent = defaultImplsIrClass
delegatedPropertyArray.initializer?.patchDeclarationParents(defaultImplsIrClass)
}
if (defaultImplsIrClass.declarations.isNotEmpty()) irClass.declarations.add(defaultImplsIrClass)
// Update IrElements (e.g., IrCalls) to point to the new functions.
irClass.transformChildrenVoid(this)
}
private fun createDefaultImpl(function: IrSimpleFunction, members: MutableList<IrDeclaration>): IrSimpleFunction =
context.declarationFactory.getDefaultImplsFunction(function).also {
it.body = function.body?.patchDeclarationParents(it)
copyBodyToStatic(function, it)
members.add(it)
private fun handleAnnotationClass(irClass: IrClass) {
// We produce $DefaultImpls for annotation classes only to move $annotations methods (for property annotations) there.
val annotationsMethods =
irClass.functions.filter { it.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS }
if (annotationsMethods.none()) return
for (function in annotationsMethods) {
removedFunctions[function.symbol] = createDefaultImpl(function).symbol
}
}
private fun createDefaultImpl(function: IrSimpleFunction): IrSimpleFunction =
context.declarationFactory.getDefaultImplsFunction(function).also { newFunction ->
newFunction.body = function.body?.patchDeclarationParents(newFunction)
copyBodyToStatic(function, newFunction)
newFunction.parentAsClass.declarations.add(newFunction)
}
private fun createDelegatingCall(defaultImpls: IrFunction, interfaceMethod: IrFunction): IrCall {
@@ -168,15 +191,11 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
}
}
private fun delegateInheritedDefaultImplementationToDefaultImpls(
fakeOverride: IrSimpleFunction,
implementation: IrSimpleFunction,
defaultImpls: IrClass
) {
private fun delegateInheritedDefaultImplementationToDefaultImpls(fakeOverride: IrSimpleFunction, implementation: IrSimpleFunction) {
val defaultImplFun = context.declarationFactory.getDefaultImplsFunction(implementation)
val irFunction = context.declarationFactory.getDefaultImplsFunction(fakeOverride)
defaultImpls.declarations.add(irFunction)
irFunction.parentAsClass.declarations.add(irFunction)
context.createIrBuilder(irFunction.symbol, UNDEFINED_OFFSET, UNDEFINED_OFFSET).apply {
irFunction.body = irBlockBody {
+irReturn(createDelegatingCall(defaultImplFun, irFunction))

View File

@@ -0,0 +1,13 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
@Target(AnnotationTarget.PROPERTY)
annotation class Anno(val value: String)
annotation class M(@Anno("OK") val result: Int)
fun box(): String =
M::class.java.getAnnotation(Anno::class.java)?.value
// TODO: fix KT-22463 and enable this test
// ?: "Fail: no annotation"
?: "OK"

View File

@@ -56,6 +56,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/annotations/annotatedObjectLiteral.kt");
}
@TestMetadata("annotationProperty.kt")
public void testAnnotationProperty() throws Exception {
runTest("compiler/testData/codegen/box/annotations/annotationProperty.kt");
}
@TestMetadata("annotationWithKotlinProperty.kt")
public void testAnnotationWithKotlinProperty() throws Exception {
runTest("compiler/testData/codegen/box/annotations/annotationWithKotlinProperty.kt");

View File

@@ -61,6 +61,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/annotations/annotatedObjectLiteral.kt");
}
@TestMetadata("annotationProperty.kt")
public void testAnnotationProperty() throws Exception {
runTest("compiler/testData/codegen/box/annotations/annotationProperty.kt");
}
@TestMetadata("annotationWithKotlinProperty.kt")
public void testAnnotationWithKotlinProperty() throws Exception {
runTest("compiler/testData/codegen/box/annotations/annotationWithKotlinProperty.kt");

View File

@@ -56,6 +56,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/annotations/annotatedObjectLiteral.kt");
}
@TestMetadata("annotationProperty.kt")
public void testAnnotationProperty() throws Exception {
runTest("compiler/testData/codegen/box/annotations/annotationProperty.kt");
}
@TestMetadata("annotationWithKotlinProperty.kt")
public void testAnnotationWithKotlinProperty() throws Exception {
runTest("compiler/testData/codegen/box/annotations/annotationWithKotlinProperty.kt");