Load default values for annotation members from classfiles

so that defaults are available to synthetic implementations.

#KT-48181 Fixed

Implementation is for JVM IR; other backends & FIR need to be supported
separately.
This commit is contained in:
Leonid Startsev
2021-11-19 15:32:28 +03:00
committed by Space
parent e8868a2eff
commit 767c570af4
18 changed files with 339 additions and 109 deletions
@@ -467,6 +467,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/annotations/instances/multifileEqHc.kt");
}
@Test
@TestMetadata("multimoduleCreation.kt")
public void testMultimoduleCreation() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/multimoduleCreation.kt");
}
@Test
@TestMetadata("multimoduleInlining.kt")
public void testMultimoduleInlining() throws Exception {
@@ -80,17 +80,20 @@ internal class AnnotationsLoader(private val session: FirSession, private val ko
}
}
override fun visitClassLiteral(name: Name, value: ClassLiteralValue) {
override fun visitClassLiteral(name: Name?, value: ClassLiteralValue) {
if (name == null) return
argumentMap[name] = buildGetClassCall {
argumentList = buildUnaryArgumentList(value.toFirClassReferenceExpression())
}
}
override fun visitEnum(name: Name, enumClassId: ClassId, enumEntryName: Name) {
override fun visitEnum(name: Name?, enumClassId: ClassId, enumEntryName: Name) {
if (name == null) return
argumentMap[name] = enumClassId.toEnumEntryReferenceExpression(enumEntryName)
}
override fun visitArray(name: Name): KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor {
override fun visitArray(name: Name?): KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor? {
if (name == null) return null
return object : KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor {
private val elements = mutableListOf<FirExpression>()
@@ -131,7 +134,8 @@ internal class AnnotationsLoader(private val session: FirSession, private val ko
}
}
override fun visitAnnotation(name: Name, classId: ClassId): KotlinJvmBinaryClass.AnnotationArgumentVisitor {
override fun visitAnnotation(name: Name?, classId: ClassId): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
if (name == null) return null
val list = mutableListOf<FirAnnotation>()
val visitor = loadAnnotation(classId, list)
return object : KotlinJvmBinaryClass.AnnotationArgumentVisitor by visitor {
@@ -322,6 +322,11 @@ private fun FirSession.loadMemberAnnotations(
}
return annotationsLoader.loadAnnotationIfNotSpecial(classId, result)
}
override fun visitAnnotationMemberDefaultValue(): KotlinJvmBinaryClass.AnnotationArgumentVisitor? {
// TODO: load annotation default values to properly support annotation instantiation feature
return null
}
}
open inner class MemberAnnotationVisitor(protected val signature: MemberSignature) : KotlinJvmBinaryClass.AnnotationVisitor {
@@ -173,17 +173,18 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
return new org.jetbrains.org.objectweb.asm.AnnotationVisitor(API_VERSION) {
@Override
public void visit(String name, @NotNull Object value) {
Name identifier = name == null ? null : Name.identifier(name);
if (value instanceof Type) {
v.visitClassLiteral(Name.identifier(name), resolveKotlinNameByType((Type) value, innerClasses));
v.visitClassLiteral(identifier, resolveKotlinNameByType((Type) value, innerClasses));
}
else {
v.visit(name == null ? null : Name.identifier(name), value);
v.visit(identifier, value);
}
}
@Override
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitArray(String name) {
AnnotationArrayArgumentVisitor arv = v.visitArray(Name.identifier(name));
AnnotationArrayArgumentVisitor arv = v.visitArray(name == null ? null : Name.identifier(name));
return arv == null ? null : new org.jetbrains.org.objectweb.asm.AnnotationVisitor(API_VERSION) {
@Override
public void visit(String name, @NotNull Object value) {
@@ -215,13 +216,14 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
@Override
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotation(String name, @NotNull String desc) {
AnnotationArgumentVisitor arv = v.visitAnnotation(Name.identifier(name), resolveNameByDesc(desc, innerClasses));
AnnotationArgumentVisitor arv =
v.visitAnnotation(name == null ? null : Name.identifier(name), resolveNameByDesc(desc, innerClasses));
return arv == null ? null : convertAnnotationVisitor(arv, innerClasses);
}
@Override
public void visitEnum(String name, @NotNull String desc, @NotNull String value) {
v.visitEnum(Name.identifier(name), resolveNameByDesc(desc, innerClasses), Name.identifier(value));
v.visitEnum(name == null ? null : Name.identifier(name), resolveNameByDesc(desc, innerClasses), Name.identifier(value));
}
@Override
@@ -269,6 +271,12 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
return convertAnnotationVisitor(v, desc, innerClasses);
}
@Override
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitAnnotationDefault() {
AnnotationArgumentVisitor av = v.visitAnnotationMemberDefaultValue();
return av == null ? null : convertAnnotationVisitor(av, innerClasses);
}
@Override
public org.jetbrains.org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(int parameter, @NotNull String desc, boolean visible) {
int parameterIndex = parameter + methodParamCount - (visible ? visibleAnnotableParameterCount : invisibleAnnotableParameterCount);
@@ -15,12 +15,12 @@ import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope
import org.jetbrains.kotlin.backend.jvm.ir.javaClassReference
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
@@ -136,11 +136,16 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext,
}.also { it.parent = implClass }
val parameter = generatedConstructor.addValueParameter(propName.asString(), propType)
// VALUE_FROM_PARAMETER
val originalParameter = ((property.backingField?.initializer?.expression as? IrGetValue)?.symbol?.owner as? IrValueParameter)
if (originalParameter?.defaultValue != null) {
parameter.defaultValue = originalParameter.defaultValue!!.deepCopyWithVariables().also { it.transformChildrenVoid() }
}
val defaultExpression = property.backingField?.initializer?.expression
val newDefaultValue: IrExpressionBody? =
if (defaultExpression is IrGetValue && defaultExpression.symbol.owner is IrValueParameter) {
// INITIALIZE_PROPERTY_FROM_PARAMETER
(defaultExpression.symbol.owner as IrValueParameter).defaultValue
} else if (defaultExpression != null) {
property.backingField!!.initializer
} else null
parameter.defaultValue = newDefaultValue?.deepCopyWithVariables()?.also { it.transformChildrenVoid() }
ctorBody.statements += IrSetFieldImpl(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, field.symbol,
@@ -0,0 +1,66 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// IGNORE_DEXING
// WITH_RUNTIME
// !LANGUAGE: +InstantiationOfAnnotationClasses
// MODULE: lib
// FILE: lib.kt
package a
import kotlin.reflect.KClass
annotation class A(val kClass: KClass<*> = Int::class)
annotation class OtherArrays(
val doublesArray: DoubleArray = [],
val enumArray: Array<kotlin.text.RegexOption> = [],
val annotationsArray: Array<JvmStatic> = [],
val namesArray: Array<JvmName> = [JvmName("foo")]
)
// Uncomment when KT-49998 is resolved
//annotation class UnsignedValue(
// val uint: UInt = 2147483657U // Int.MAX_VALUE + 10
//)
// MODULE: app(lib)
// FILE: app.kt
// kotlin.Metadata: IntArray, Array<String>
// kotlin.Deprecated: Nested annotation, enum instance
// a.A: KClass
// a.OtherArrays: Arrays of enums and other annotations
package test
import a.*
import kotlin.test.*
class C {
fun one(): A = A()
fun two(): Metadata = Metadata()
fun three(): Deprecated = Deprecated("foo")
fun four(): OtherArrays = OtherArrays()
// fun five(): UnsignedValue = UnsignedValue()
}
fun box(): String {
val a = C().one()
assertEquals(Int::class, a.kClass)
assertEquals(
"""@kotlin.Metadata(bytecodeVersion=[1, 0, 3], data1=[], data2=[], extraInt=0, extraString=, kind=1, metadataVersion=[], packageName=)""",
C().two().toString()
)
assertEquals(
"""@kotlin.Deprecated(level=WARNING, message=foo, replaceWith=@kotlin.ReplaceWith(expression=, imports=[]))""",
C().three().toString()
)
assertEquals(
"""@a.OtherArrays(annotationsArray=[], doublesArray=[], enumArray=[], namesArray=[@kotlin.jvm.JvmName(name=foo)])""",
C().four().toString()
)
// assertEquals(Int.MAX_VALUE.toUInt() + 10.toUInt(), C().five().uint)
return "OK"
}
@@ -1,4 +1,6 @@
// ALLOW_AST_ACCESS
// NO_CHECK_SOURCE_VS_BINARY
//^ While compiling source, we do not store annotation default values, but we load them when reading compiled files
package test
annotation class Anno(val value: String = "0", val x: Int = 0)
@@ -2,9 +2,9 @@ package test
public final annotation class Anno : kotlin.Annotation {
/*primary*/ public constructor Anno(/*0*/ value: kotlin.String = ..., /*1*/ x: kotlin.Int = ...)
public final val value: kotlin.String
public final val value: kotlin.String = "0"
public final fun <get-value>(): kotlin.String
public final val x: kotlin.Int
public final val x: kotlin.Int = 0
public final fun <get-x>(): kotlin.Int
}
@@ -1,4 +1,6 @@
//ALLOW_AST_ACCESS
// NO_CHECK_SOURCE_VS_BINARY
//^ While compiling source, we do not store annotation default values, but we load them when reading compiled files
package test
@Target(AnnotationTarget.TYPEALIAS)
@@ -2,7 +2,7 @@ package test
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPEALIAS}) public final annotation class Ann : kotlin.Annotation {
/*primary*/ public constructor Ann(/*0*/ value: kotlin.String = ...)
public final val value: kotlin.String
public final val value: kotlin.String = ""
public final fun <get-value>(): kotlin.String
}
@test.Ann public typealias A1 = kotlin.String
@@ -467,6 +467,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/annotations/instances/multifileEqHc.kt");
}
@Test
@TestMetadata("multimoduleCreation.kt")
public void testMultimoduleCreation() throws Exception {
runTest("compiler/testData/codegen/box/annotations/instances/multimoduleCreation.kt");
}
@Test
@TestMetadata("multimoduleInlining.kt")
public void testMultimoduleInlining() throws Exception {