Deserialize repeated annotations inside implicit container
#KT-49651 Fixed
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
// FULL_JDK
|
||||
|
||||
package test
|
||||
|
||||
@java.lang.annotation.Repeatable(Anno.Container::class)
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS)
|
||||
annotation class Anno(val code: Int) {
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS)
|
||||
annotation class Container(val value: Array<Anno>)
|
||||
}
|
||||
|
||||
@Anno(1)
|
||||
@Anno(2)
|
||||
class Z
|
||||
|
||||
@Anno(3)
|
||||
@Anno(4)
|
||||
fun f() {}
|
||||
|
||||
@Anno(5)
|
||||
@Anno(6)
|
||||
typealias S = String
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package test
|
||||
|
||||
@test.Anno(code = 3) @test.Anno(code = 4) public fun f(): kotlin.Unit
|
||||
|
||||
@java.lang.annotation.Repeatable(value = test.Anno.Container::class) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS}) public final annotation class Anno : kotlin.Annotation {
|
||||
/*primary*/ public constructor Anno(/*0*/ code: kotlin.Int)
|
||||
public final val code: kotlin.Int
|
||||
public final fun <get-code>(): kotlin.Int
|
||||
|
||||
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS}) public final annotation class Container : kotlin.Annotation {
|
||||
/*primary*/ public constructor Container(/*0*/ value: kotlin.Array<test.Anno>)
|
||||
public final val value: kotlin.Array<test.Anno>
|
||||
public final fun <get-value>(): kotlin.Array<test.Anno>
|
||||
}
|
||||
}
|
||||
|
||||
@test.Anno(code = 1) @test.Anno(code = 2) public final class Z {
|
||||
/*primary*/ public constructor Z()
|
||||
}
|
||||
@test.Anno(code = 5) @test.Anno(code = 6) public typealias S = kotlin.String
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package test
|
||||
|
||||
@Repeatable
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS)
|
||||
annotation class Anno(val code: Int)
|
||||
|
||||
@Anno(1)
|
||||
@Anno(2)
|
||||
class Z
|
||||
|
||||
@Anno(3)
|
||||
@Anno(4)
|
||||
fun f() {}
|
||||
|
||||
@Anno(5)
|
||||
@Anno(6)
|
||||
typealias S = String
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package test
|
||||
|
||||
@test.Anno(code = 3) @test.Anno(code = 4) public fun f(): kotlin.Unit
|
||||
|
||||
@kotlin.annotation.Repeatable @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS}) public final annotation class Anno : kotlin.Annotation {
|
||||
/*primary*/ public constructor Anno(/*0*/ code: kotlin.Int)
|
||||
public final val code: kotlin.Int
|
||||
public final fun <get-code>(): kotlin.Int
|
||||
}
|
||||
|
||||
@test.Anno(code = 1) @test.Anno(code = 2) public final class Z {
|
||||
/*primary*/ public constructor Z()
|
||||
}
|
||||
@test.Anno(code = 5) @test.Anno(code = 6) public typealias S = kotlin.String
|
||||
+1
-1
@@ -132,7 +132,7 @@ public abstract class AbstractLoadJavaTest extends TestCaseWithTmpdir {
|
||||
File ktFile = new File(ktFileName);
|
||||
File txtFile = getTxtFileFromKtFile(ktFileName);
|
||||
|
||||
CompilerConfiguration configuration = newConfiguration(configurationKind, TestJdkKind.MOCK_JDK, getClasspath(), Collections.emptyList());
|
||||
CompilerConfiguration configuration = newConfiguration(configurationKind, getJdkKind(), getClasspath(), Collections.emptyList());
|
||||
updateConfiguration(configuration);
|
||||
if (useTypeTableInSerializer) {
|
||||
configuration.put(JVMConfigurationKeys.USE_TYPE_TABLE, true);
|
||||
|
||||
@@ -30,6 +30,7 @@ fun main(args: Array<String>) {
|
||||
testGroup("compiler/tests-java8/tests", "compiler/testData") {
|
||||
testClass<AbstractLoadJava8Test> {
|
||||
model("loadJava8/compiledJava", extension = "java", testMethod = "doTestCompiledJava")
|
||||
model("loadJava8/compiledKotlinWithStdlib", testMethod = "doTestCompiledKotlinWithStdlib")
|
||||
model("loadJava8/sourceJava", extension = "java", testMethod = "doTestSourceJava")
|
||||
}
|
||||
|
||||
|
||||
+36
@@ -123,6 +123,42 @@ public class LoadJava8TestGenerated extends AbstractLoadJava8Test {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/loadJava8/compiledKotlinWithStdlib")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CompiledKotlinWithStdlib extends AbstractLoadJava8Test {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTestCompiledKotlinWithStdlib, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInCompiledKotlinWithStdlib() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/loadJava8/compiledKotlinWithStdlib"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/loadJava8/compiledKotlinWithStdlib/annotations")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Annotations extends AbstractLoadJava8Test {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTestCompiledKotlinWithStdlib, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInAnnotations() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/loadJava8/compiledKotlinWithStdlib/annotations"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("RepeatableAnnotationWithExplicitContainer.kt")
|
||||
public void testRepeatableAnnotationWithExplicitContainer() throws Exception {
|
||||
runTest("compiler/testData/loadJava8/compiledKotlinWithStdlib/annotations/RepeatableAnnotationWithExplicitContainer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("RepeatableAnnotationWithImplicitContainer.kt")
|
||||
public void testRepeatableAnnotationWithImplicitContainer() throws Exception {
|
||||
runTest("compiler/testData/loadJava8/compiledKotlinWithStdlib/annotations/RepeatableAnnotationWithImplicitContainer.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/loadJava8/sourceJava")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+4
-1
@@ -401,7 +401,10 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader<A : Any, C : Any>(
|
||||
|
||||
val containerKClassValue = arguments[Name.identifier("value")] as? KClassValue ?: return false
|
||||
val normalClass = containerKClassValue.value as? KClassValue.Value.NormalClass ?: return false
|
||||
val classId = normalClass.classId
|
||||
return isImplicitRepeatableContainer(normalClass.classId)
|
||||
}
|
||||
|
||||
protected fun isImplicitRepeatableContainer(classId: ClassId): Boolean {
|
||||
if (classId.outerClassId == null ||
|
||||
classId.shortClassName.asString() != JvmAbi.REPEATABLE_ANNOTATION_CONTAINER_NAME
|
||||
) return false
|
||||
|
||||
+13
-3
@@ -114,6 +114,12 @@ class BinaryClassAnnotationAndConstantLoaderImpl(
|
||||
val parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass)
|
||||
if (parameter != null) {
|
||||
arguments[name] = ConstantValueFactory.createArrayValue(elements.compact(), parameter.type)
|
||||
} else if (isImplicitRepeatableContainer(annotationClassId) && name.asString() == "value") {
|
||||
// In case this is an implicit repeatable annotation container, its class descriptor can't be resolved by the
|
||||
// frontend, so we'd like to flatten its value and add repeated annotations to the list.
|
||||
// E.g. if we see `@Foo.Container(@Foo(1), @Foo(2))` in the bytecode on some declaration where `Foo` is some
|
||||
// Kotlin-repeatable annotation, we want to read annotations on that declaration as a list `[@Foo(1), @Foo(2)]`.
|
||||
elements.filterIsInstance<AnnotationValue>().mapTo(result, AnnotationValue::value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,9 +140,13 @@ class BinaryClassAnnotationAndConstantLoaderImpl(
|
||||
// Do not load the @java.lang.annotation.Repeatable annotation instance generated automatically by the compiler for
|
||||
// Kotlin-repeatable annotation classes. Otherwise the reference to the implicit nested "Container" class cannot be
|
||||
// resolved, since that class is only generated in the backend, and is not visible to the frontend.
|
||||
if (!isRepeatableWithImplicitContainer(annotationClassId, arguments)) {
|
||||
result.add(AnnotationDescriptorImpl(annotationClass.defaultType, arguments, source))
|
||||
}
|
||||
if (isRepeatableWithImplicitContainer(annotationClassId, arguments)) return
|
||||
|
||||
// Do not load the implicit repeatable annotation container entry. The contents of its "value" argument have been flattened
|
||||
// and added to the result already, see `visitArray`.
|
||||
if (isImplicitRepeatableContainer(annotationClassId)) return
|
||||
|
||||
result.add(AnnotationDescriptorImpl(annotationClass.defaultType, arguments, source))
|
||||
}
|
||||
|
||||
private fun createConstant(name: Name?, value: Any?): ConstantValue<*> {
|
||||
|
||||
Reference in New Issue
Block a user