Generate InnerClasses attribute for nested classes in annotation arguments

Otherwise we incorrectly try to load a nested class "A.B" as if it is a
top level class named "A$B" and fail.

 #KT-38853 Fixed
This commit is contained in:
Alexander Udalov
2020-05-13 19:04:42 +02:00
parent cdac6157a9
commit ed8efafa9b
7 changed files with 145 additions and 4 deletions
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.descriptors.annotations.*;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.AnnotationChecker;
@@ -432,8 +433,13 @@ public abstract class AnnotationCodegen {
@Override
public Void visitEnumValue(EnumValue value, Void data) {
String enumClassInternalName = AsmUtil.asmTypeByClassId(value.getEnumClassId()).getDescriptor();
ClassId enumClassId = value.getEnumClassId();
String enumClassInternalName = AsmUtil.asmTypeByClassId(enumClassId).getDescriptor();
String enumEntryName = value.getEnumEntryName().asString();
ClassDescriptor descriptor = FindClassInModuleKt.findClassAcrossModuleDependencies(state.getModule(), enumClassId);
if (descriptor != null) {
innerClassConsumer.addInnerClassInfoFromAnnotation(descriptor);
}
annotationVisitor.visitEnum(name, enumClassInternalName, enumEntryName);
return null;
}
@@ -450,8 +456,9 @@ public abstract class AnnotationCodegen {
@Override
public Void visitAnnotationValue(AnnotationValue value, Void data) {
String internalAnnName = typeMapper.mapType(value.getValue().getType()).getDescriptor();
AnnotationVisitor visitor = annotationVisitor.visitAnnotation(name, internalAnnName);
KotlinType classType = value.getValue().getType();
innerClassConsumer.addInnerClassInfoFromAnnotation(DescriptorUtils.getClassDescriptorForType(classType));
AnnotationVisitor visitor = annotationVisitor.visitAnnotation(name, typeMapper.mapType(classType).getDescriptor());
genAnnotationArguments(value.getValue(), visitor);
visitor.visitEnd();
return null;
@@ -459,7 +466,9 @@ public abstract class AnnotationCodegen {
@Override
public Void visitKClassValue(KClassValue value, Void data) {
annotationVisitor.visit(name, typeMapper.mapType(value.getArgumentType(module)));
KotlinType classType = value.getArgumentType(module);
innerClassConsumer.addInnerClassInfoFromAnnotation(DescriptorUtils.getClassDescriptorForType(classType));
annotationVisitor.visit(name, typeMapper.mapType(classType));
return null;
}
@@ -0,0 +1,44 @@
// IGNORE_BACKEND: JVM_IR
// FILE: annotations.kt
import kotlin.reflect.*
annotation class Anno(
val k: KClass<*>,
val e: C.NestedEnum,
val a: C.NestedAnno,
)
annotation class AnnoWithDefault(val k: KClass<*> = Nested0::class) {
class Nested0
}
class C {
class Nested1
enum class NestedEnum { E }
annotation class NestedAnno(val k: KClass<*>) {
class Nested2
}
}
// FILE: usage.kt
interface I {
@Anno(
C.Nested1::class,
C.NestedEnum.E,
C.NestedAnno(C.NestedAnno.Nested2::class),
)
@AnnoWithDefault
fun foo(): String = "OK"
}
// @I.class:
// 5 INNERCLASS
// 1 INNERCLASS C\$Nested1 C Nested1
// 1 INNERCLASS C\$NestedEnum C NestedEnum
// 1 INNERCLASS C\$NestedAnno C NestedAnno
// 1 INNERCLASS C\$NestedAnno\$Nested2 C\$NestedAnno Nested2
// 1 INNERCLASS I\$DefaultImpls I DefaultImpls
@@ -0,0 +1,42 @@
// IGNORE_BACKEND: JVM_IR
// FILE: 1.kt
import kotlin.reflect.*
annotation class Anno(
val k: KClass<*>,
val e: C.NestedEnum,
val a: C.NestedAnno,
)
annotation class AnnoWithDefault(val k: KClass<*> = Nested0::class) {
class Nested0
}
class C {
class Nested1
enum class NestedEnum { E }
annotation class NestedAnno(val k: KClass<*>) {
class Nested2
}
}
interface I {
@Anno(
C.Nested1::class,
C.NestedEnum.E,
C.NestedAnno(C.NestedAnno.Nested2::class),
)
@AnnoWithDefault
fun foo(): String = "OK"
}
// FILE: 2.kt
class D : I {
fun box(): String = foo()
}
fun box(): String = D().box()
@@ -3052,6 +3052,24 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/innerClasses")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InnerClasses extends AbstractBytecodeTextTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInInnerClasses() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/innerClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("nestedClassInAnnotationArgument.kt")
public void testNestedClassInAnnotationArgument() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/innerClasses/nestedClassInAnnotationArgument.kt");
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/interfaces")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -268,6 +268,11 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
runTest("compiler/testData/compileKotlinAgainstKotlin/nestedClass.kt");
}
@TestMetadata("nestedClassInAnnotationArgument.kt")
public void testNestedClassInAnnotationArgument() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/nestedClassInAnnotationArgument.kt");
}
@TestMetadata("nestedEnum.kt")
public void testNestedEnum() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/nestedEnum.kt");
@@ -3137,6 +3137,24 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/innerClasses")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InnerClasses extends AbstractIrBytecodeTextTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
public void testAllFilesPresentInInnerClasses() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/innerClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("nestedClassInAnnotationArgument.kt")
public void testNestedClassInAnnotationArgument() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/innerClasses/nestedClassInAnnotationArgument.kt");
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/interfaces")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -263,6 +263,11 @@ public class IrCompileKotlinAgainstKotlinTestGenerated extends AbstractIrCompile
runTest("compiler/testData/compileKotlinAgainstKotlin/nestedClass.kt");
}
@TestMetadata("nestedClassInAnnotationArgument.kt")
public void testNestedClassInAnnotationArgument() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/nestedClassInAnnotationArgument.kt");
}
@TestMetadata("nestedEnum.kt")
public void testNestedEnum() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/nestedEnum.kt");