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:
+2
-1
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.DescriptorsToIrRemapper
|
|||||||
import org.jetbrains.kotlin.backend.common.ir.*
|
import org.jetbrains.kotlin.backend.common.ir.*
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper
|
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.builtins.CompanionObjectMapping.isMappedIntrinsicCompanionObject
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
@@ -135,7 +136,7 @@ class JvmDeclarationFactory(
|
|||||||
|
|
||||||
fun getDefaultImplsFunction(interfaceFun: IrSimpleFunction): IrSimpleFunction {
|
fun getDefaultImplsFunction(interfaceFun: IrSimpleFunction): IrSimpleFunction {
|
||||||
val parent = interfaceFun.parentAsClass
|
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) {
|
return defaultImplsMethods.getOrPut(interfaceFun) {
|
||||||
val defaultImpls = getDefaultImplsClass(interfaceFun.parentAsClass)
|
val defaultImpls = getDefaultImplsClass(interfaceFun.parentAsClass)
|
||||||
|
|
||||||
|
|||||||
+44
-25
@@ -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.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
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.backend.jvm.ir.hasJvmDefault
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
@@ -41,11 +43,28 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
|
|||||||
private val removedFunctions = hashMapOf<IrFunctionSymbol, IrFunctionSymbol>()
|
private val removedFunctions = hashMapOf<IrFunctionSymbol, IrFunctionSymbol>()
|
||||||
|
|
||||||
override fun lower(irClass: IrClass) {
|
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 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:
|
// There are 6 cases for functions on interfaces:
|
||||||
loop@ for (function in irClass.functions) {
|
loop@ for (function in irClass.functions) {
|
||||||
when {
|
when {
|
||||||
@@ -78,7 +97,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
|
|||||||
&& !implementation.isMethodOfAny()
|
&& !implementation.isMethodOfAny()
|
||||||
&& (!implementation.hasJvmDefault() || context.state.jvmDefaultMode.isCompatibility)
|
&& (!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)
|
Visibilities.isPrivate(function.visibility)
|
||||||
|| (function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER && !function.hasJvmDefault())
|
|| (function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER && !function.hasJvmDefault())
|
||||||
|| function.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS -> {
|
|| function.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS -> {
|
||||||
val defaultImpl = createDefaultImpl(function, members)
|
removedFunctions[function.symbol] = createDefaultImpl(function).symbol
|
||||||
removedFunctions[function.symbol] = defaultImpl.symbol
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,7 +116,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
|
|||||||
* an abstract stub is left.
|
* an abstract stub is left.
|
||||||
*/
|
*/
|
||||||
!function.hasJvmDefault() -> {
|
!function.hasJvmDefault() -> {
|
||||||
createDefaultImpl(function, members)
|
createDefaultImpl(function)
|
||||||
function.body = null
|
function.body = null
|
||||||
//TODO reset modality to abstract
|
//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, ...
|
* 5) _With_ @JvmDefault, we move and bridge if in compatibility mode, ...
|
||||||
*/
|
*/
|
||||||
context.state.jvmDefaultMode.isCompatibility -> {
|
context.state.jvmDefaultMode.isCompatibility -> {
|
||||||
val defaultImpl = createDefaultImpl(function, members)
|
val defaultImpl = createDefaultImpl(function)
|
||||||
function.body = IrExpressionBodyImpl(createDelegatingCall(defaultImpl, function))
|
function.body = IrExpressionBodyImpl(createDelegatingCall(defaultImpl, function))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,9 +133,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
irClass.declarations.removeAll {
|
val defaultImplsIrClass = context.declarationFactory.getDefaultImplsClass(irClass)
|
||||||
it is IrFunction && removedFunctions.containsKey(it.symbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move metadata for local delegated properties from the interface to DefaultImpls, since this is where kotlin-reflect looks for it.
|
// 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]
|
val localDelegatedProperties = context.localDelegatedProperties[irClass.attributeOwnerId as IrClass]
|
||||||
@@ -135,17 +151,24 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
|
|||||||
delegatedPropertyArray.parent = defaultImplsIrClass
|
delegatedPropertyArray.parent = defaultImplsIrClass
|
||||||
delegatedPropertyArray.initializer?.patchDeclarationParents(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 =
|
private fun handleAnnotationClass(irClass: IrClass) {
|
||||||
context.declarationFactory.getDefaultImplsFunction(function).also {
|
// We produce $DefaultImpls for annotation classes only to move $annotations methods (for property annotations) there.
|
||||||
it.body = function.body?.patchDeclarationParents(it)
|
val annotationsMethods =
|
||||||
copyBodyToStatic(function, it)
|
irClass.functions.filter { it.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS }
|
||||||
members.add(it)
|
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 {
|
private fun createDelegatingCall(defaultImpls: IrFunction, interfaceMethod: IrFunction): IrCall {
|
||||||
@@ -168,15 +191,11 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun delegateInheritedDefaultImplementationToDefaultImpls(
|
private fun delegateInheritedDefaultImplementationToDefaultImpls(fakeOverride: IrSimpleFunction, implementation: IrSimpleFunction) {
|
||||||
fakeOverride: IrSimpleFunction,
|
|
||||||
implementation: IrSimpleFunction,
|
|
||||||
defaultImpls: IrClass
|
|
||||||
) {
|
|
||||||
val defaultImplFun = context.declarationFactory.getDefaultImplsFunction(implementation)
|
val defaultImplFun = context.declarationFactory.getDefaultImplsFunction(implementation)
|
||||||
val irFunction = context.declarationFactory.getDefaultImplsFunction(fakeOverride)
|
val irFunction = context.declarationFactory.getDefaultImplsFunction(fakeOverride)
|
||||||
|
|
||||||
defaultImpls.declarations.add(irFunction)
|
irFunction.parentAsClass.declarations.add(irFunction)
|
||||||
context.createIrBuilder(irFunction.symbol, UNDEFINED_OFFSET, UNDEFINED_OFFSET).apply {
|
context.createIrBuilder(irFunction.symbol, UNDEFINED_OFFSET, UNDEFINED_OFFSET).apply {
|
||||||
irFunction.body = irBlockBody {
|
irFunction.body = irBlockBody {
|
||||||
+irReturn(createDelegatingCall(defaultImplFun, irFunction))
|
+irReturn(createDelegatingCall(defaultImplFun, irFunction))
|
||||||
|
|||||||
@@ -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"
|
||||||
+5
@@ -56,6 +56,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/annotations/annotatedObjectLiteral.kt");
|
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")
|
@TestMetadata("annotationWithKotlinProperty.kt")
|
||||||
public void testAnnotationWithKotlinProperty() throws Exception {
|
public void testAnnotationWithKotlinProperty() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/annotations/annotationWithKotlinProperty.kt");
|
runTest("compiler/testData/codegen/box/annotations/annotationWithKotlinProperty.kt");
|
||||||
|
|||||||
+5
@@ -61,6 +61,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/annotations/annotatedObjectLiteral.kt");
|
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")
|
@TestMetadata("annotationWithKotlinProperty.kt")
|
||||||
public void testAnnotationWithKotlinProperty() throws Exception {
|
public void testAnnotationWithKotlinProperty() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/annotations/annotationWithKotlinProperty.kt");
|
runTest("compiler/testData/codegen/box/annotations/annotationWithKotlinProperty.kt");
|
||||||
|
|||||||
+5
@@ -56,6 +56,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/annotations/annotatedObjectLiteral.kt");
|
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")
|
@TestMetadata("annotationWithKotlinProperty.kt")
|
||||||
public void testAnnotationWithKotlinProperty() throws Exception {
|
public void testAnnotationWithKotlinProperty() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/annotations/annotationWithKotlinProperty.kt");
|
runTest("compiler/testData/codegen/box/annotations/annotationWithKotlinProperty.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user