From bce5cb9034e84a21c863fad490d795a57cab8cfa Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 19 Sep 2012 21:11:19 +0400 Subject: [PATCH] Support inner enums from binaries #KT-2771 Fixed --- .../resolve/java/JavaDescriptorResolver.java | 81 ++++++++++++++++--- .../loadKotlinCustom/enum/innerEnum.kt | 7 ++ .../loadKotlinCustom/enum/innerEnumBinary.txt | 19 +++++ .../enum/innerEnumExistingClassObject.kt | 8 ++ .../innerEnumExistingClassObjectBinary.txt | 19 +++++ .../innerEnumExistingClassObjectSource.txt | 24 ++++++ .../loadKotlinCustom/enum/innerEnumSource.txt | 24 ++++++ .../jvm/compiler/LoadKotlinCustomTest.java | 10 ++- 8 files changed, 179 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/loadKotlinCustom/enum/innerEnum.kt create mode 100644 compiler/testData/loadKotlinCustom/enum/innerEnumBinary.txt create mode 100644 compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObject.kt create mode 100644 compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObjectBinary.txt create mode 100644 compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObjectSource.txt create mode 100644 compiler/testData/loadKotlinCustom/enum/innerEnumSource.txt diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java index 66130db9c49..c7e59bd451c 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java @@ -214,9 +214,9 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes } - static class ResolverEnumClassObjectClassData extends ResolverClassData { + static class ResolverSyntheticClassObjectClassData extends ResolverClassData { - protected ResolverEnumClassObjectClassData( + protected ResolverSyntheticClassObjectClassData( @Nullable PsiClass psiClass, @Nullable FqName fqName, @NotNull ClassDescriptorFromJvmBytecode descriptor @@ -545,8 +545,9 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes false); String context = "constructor of class " + psiClass.getQualifiedName(); ValueParameterDescriptors valueParameterDescriptors = resolveParameterDescriptors(constructorDescriptor, - constructor.getParameters(), - TypeVariableResolvers.classTypeVariableResolver(classData.classDescriptor, context)); + constructor.getParameters(), + TypeVariableResolvers.classTypeVariableResolver( + classData.classDescriptor, context)); if (valueParameterDescriptors.receiverType != null) { throw new IllegalStateException(); } @@ -585,6 +586,13 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes return createClassObjectDescriptorForEnum(containing, psiClass); } + // If there's at least one inner enum, we need to create a class object (to put this enum into) + for (PsiClass innerClass : psiClass.getInnerClasses()) { + if (isInnerEnum(innerClass, containing)) { + return createSyntheticClassObject(containing, psiClass); + } + } + PsiClass classObjectPsiClass = getInnerClassClassObject(psiClass); if (classObjectPsiClass == null) { return null; @@ -602,20 +610,35 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes return classObjectDescriptor; } + private static boolean isInnerEnum(@NotNull PsiClass innerClass, DeclarationDescriptor owner) { + if (!innerClass.isEnum()) return false; + if (!(owner instanceof ClassDescriptor)) return false; + + ClassKind kind = ((ClassDescriptor) owner).getKind(); + return kind == ClassKind.CLASS || kind == ClassKind.TRAIT || kind == ClassKind.ENUM_CLASS; + } + @NotNull private MutableClassDescriptorLite createClassObjectDescriptorForEnum(@NotNull ClassDescriptor containing, @NotNull PsiClass psiClass) { + MutableClassDescriptorLite classObjectDescriptor = createSyntheticClassObject(containing, psiClass); + + classObjectDescriptor.getBuilder().addFunctionDescriptor(createEnumClassObjectValuesMethod(classObjectDescriptor, trace)); + classObjectDescriptor.getBuilder().addFunctionDescriptor(createEnumClassObjectValueOfMethod(classObjectDescriptor, trace)); + + return classObjectDescriptor; + } + + @NotNull + private MutableClassDescriptorLite createSyntheticClassObject(@NotNull ClassDescriptor containing, @NotNull PsiClass psiClass) { String psiClassQualifiedName = psiClass.getQualifiedName(); assert psiClassQualifiedName != null : "Reading java class with no qualified name"; FqNameUnsafe fqName = new FqNameUnsafe(psiClassQualifiedName + "." + getClassObjectName(psiClass.getName()).getName()); ClassDescriptorFromJvmBytecode classObjectDescriptor = new ClassDescriptorFromJvmBytecode( containing, ClassKind.CLASS_OBJECT, psiClass, null, this); - ResolverEnumClassObjectClassData data = new ResolverEnumClassObjectClassData(psiClass, null, classObjectDescriptor); + ResolverSyntheticClassObjectClassData data = new ResolverSyntheticClassObjectClassData(psiClass, null, classObjectDescriptor); setUpClassObjectDescriptor(containing, fqName, data, getClassObjectName(containing.getName().getName())); - classObjectDescriptor.getBuilder().addFunctionDescriptor(createEnumClassObjectValuesMethod(classObjectDescriptor, trace)); - classObjectDescriptor.getBuilder().addFunctionDescriptor(createEnumClassObjectValueOfMethod(classObjectDescriptor, trace)); - return classObjectDescriptor; } @@ -656,6 +679,13 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes if (clazz == null) { throw new IllegalStateException("PsiClass not found by name " + containerFqName + ", required to be container declaration of " + fqName); } + if (isInnerEnum(psiClass, clazz)) { + ClassDescriptor classObjectDescriptor = clazz.getClassObjectDescriptor(); + if (classObjectDescriptor == null) { + throw new IllegalStateException("Class object for a class with inner enum should've been created earlier: " + clazz); + } + return classObjectDescriptor; + } return clazz; } @@ -1887,7 +1917,7 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes public List resolveInnerClasses(DeclarationDescriptor owner, PsiClass psiClass, boolean staticMembers) { if (staticMembers) { - return new ArrayList(0); + return resolveInnerClassesOfClassObject(owner, psiClass); } PsiClass[] innerPsiClasses = psiClass.getInnerClasses(); @@ -1900,14 +1930,41 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes if (innerPsiClass.getName().equals(JvmAbi.CLASS_OBJECT_CLASS_NAME)) { continue; } - ClassDescriptor classDescriptor = resolveClass(new FqName(innerPsiClass.getQualifiedName()), - DescriptorSearchRule.IGNORE_IF_FOUND_IN_KOTLIN); - assert classDescriptor != null: "couldn't resolve class " + innerPsiClass.getQualifiedName(); + if (isInnerEnum(innerPsiClass, owner)) { + // Inner enums will be put later into our class object + continue; + } + ClassDescriptor classDescriptor = resolveInnerClass(innerPsiClass); r.add(classDescriptor); } return r; } + private List resolveInnerClassesOfClassObject(DeclarationDescriptor owner, PsiClass psiClass) { + if (!DescriptorUtils.isClassObject(owner)) { + return new ArrayList(0); + } + + List r = new ArrayList(0); + // If we're a class object, inner enums of our parent need to be put into us + DeclarationDescriptor containingDeclaration = owner.getContainingDeclaration(); + for (PsiClass innerPsiClass : psiClass.getInnerClasses()) { + if (isInnerEnum(innerPsiClass, containingDeclaration)) { + ClassDescriptor classDescriptor = resolveInnerClass(innerPsiClass); + r.add(classDescriptor); + } + } + return r; + } + + private ClassDescriptor resolveInnerClass(@NotNull PsiClass innerPsiClass) { + String name = innerPsiClass.getQualifiedName(); + assert name != null : "Inner class has no qualified name"; + ClassDescriptor classDescriptor = resolveClass(new FqName(name), DescriptorSearchRule.IGNORE_IF_FOUND_IN_KOTLIN); + assert classDescriptor != null : "Couldn't resolve class " + name; + return classDescriptor; + } + @NotNull public static PsiAnnotation[] getAllAnnotations(@NotNull PsiModifierListOwner owner) { List result = new ArrayList(); diff --git a/compiler/testData/loadKotlinCustom/enum/innerEnum.kt b/compiler/testData/loadKotlinCustom/enum/innerEnum.kt new file mode 100644 index 00000000000..c70ba41c988 --- /dev/null +++ b/compiler/testData/loadKotlinCustom/enum/innerEnum.kt @@ -0,0 +1,7 @@ +package test + +class A { + enum class E { + ENTRY + } +} diff --git a/compiler/testData/loadKotlinCustom/enum/innerEnumBinary.txt b/compiler/testData/loadKotlinCustom/enum/innerEnumBinary.txt new file mode 100644 index 00000000000..ced1db8fa84 --- /dev/null +++ b/compiler/testData/loadKotlinCustom/enum/innerEnumBinary.txt @@ -0,0 +1,19 @@ +namespace test + +internal final class test.A : jet.Any { + public final /*constructor*/ fun (): test.A + internal final class object test.A. { + private final /*constructor*/ fun (): test.A. + internal final enum class test.A..E : jet.Enum.E> { + private final /*constructor*/ fun (/*0*/ p0: jet.String?, /*1*/ p1: jet.Int): test.A..E + public final override /*1*/ /*fake_override*/ fun name(): jet.String + public final override /*1*/ /*fake_override*/ fun ordinal(): jet.Int + internal final class object test.A..E. { + private final /*constructor*/ fun (): test.A..E. + public final val ENTRY: test.A..E + public final fun valueOf(/*0*/ value: jet.String): test.A..E + public final fun values(): jet.Array.E> + } + } + } +} diff --git a/compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObject.kt b/compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObject.kt new file mode 100644 index 00000000000..e7ace469ff6 --- /dev/null +++ b/compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObject.kt @@ -0,0 +1,8 @@ +package test + +class A { + class object { } + enum class E { + ENTRY + } +} diff --git a/compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObjectBinary.txt b/compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObjectBinary.txt new file mode 100644 index 00000000000..ced1db8fa84 --- /dev/null +++ b/compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObjectBinary.txt @@ -0,0 +1,19 @@ +namespace test + +internal final class test.A : jet.Any { + public final /*constructor*/ fun (): test.A + internal final class object test.A. { + private final /*constructor*/ fun (): test.A. + internal final enum class test.A..E : jet.Enum.E> { + private final /*constructor*/ fun (/*0*/ p0: jet.String?, /*1*/ p1: jet.Int): test.A..E + public final override /*1*/ /*fake_override*/ fun name(): jet.String + public final override /*1*/ /*fake_override*/ fun ordinal(): jet.Int + internal final class object test.A..E. { + private final /*constructor*/ fun (): test.A..E. + public final val ENTRY: test.A..E + public final fun valueOf(/*0*/ value: jet.String): test.A..E + public final fun values(): jet.Array.E> + } + } + } +} diff --git a/compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObjectSource.txt b/compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObjectSource.txt new file mode 100644 index 00000000000..5b02b0508f5 --- /dev/null +++ b/compiler/testData/loadKotlinCustom/enum/innerEnumExistingClassObjectSource.txt @@ -0,0 +1,24 @@ +namespace test + +internal final class test.A : jet.Any { + public final /*constructor*/ fun (): test.A + internal final class object test.A. : jet.Any { + private final /*constructor*/ fun (): test.A. + internal final enum class test.A..E : jet.Enum.E> { + private final /*constructor*/ fun (): test.A..E + public final override /*1*/ /*fake_override*/ fun name(): jet.String + public final override /*1*/ /*fake_override*/ fun ordinal(): jet.Int + internal final class object test.A..E. { + private final /*constructor*/ fun (): test.A..E. + internal final val ENTRY: test.A..E..ENTRY + internal final enum entry test.A..E..ENTRY : test.A..E { + private final /*constructor*/ fun (): test.A..E..ENTRY + public final override /*1*/ /*fake_override*/ fun name(): jet.String + public final override /*1*/ /*fake_override*/ fun ordinal(): jet.Int + } + public final fun valueOf(/*0*/ value: jet.String): test.A..E + public final fun values(): jet.Array.E> + } + } + } +} diff --git a/compiler/testData/loadKotlinCustom/enum/innerEnumSource.txt b/compiler/testData/loadKotlinCustom/enum/innerEnumSource.txt new file mode 100644 index 00000000000..13bb16d5e0b --- /dev/null +++ b/compiler/testData/loadKotlinCustom/enum/innerEnumSource.txt @@ -0,0 +1,24 @@ +namespace test + +internal final class test.A : jet.Any { + public final /*constructor*/ fun (): test.A + public final class object test.A. { + private final /*constructor*/ fun (): test.A. + internal final enum class test.A..E : jet.Enum.E> { + private final /*constructor*/ fun (): test.A..E + public final override /*1*/ /*fake_override*/ fun name(): jet.String + public final override /*1*/ /*fake_override*/ fun ordinal(): jet.Int + internal final class object test.A..E. { + private final /*constructor*/ fun (): test.A..E. + internal final val ENTRY: test.A..E..ENTRY + internal final enum entry test.A..E..ENTRY : test.A..E { + private final /*constructor*/ fun (): test.A..E..ENTRY + public final override /*1*/ /*fake_override*/ fun name(): jet.String + public final override /*1*/ /*fake_override*/ fun ordinal(): jet.Int + } + public final fun valueOf(/*0*/ value: jet.String): test.A..E + public final fun values(): jet.Array.E> + } + } + } +} diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadKotlinCustomTest.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadKotlinCustomTest.java index 83b02e13956..d94393bd4fa 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadKotlinCustomTest.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadKotlinCustomTest.java @@ -82,6 +82,14 @@ public final class LoadKotlinCustomTest extends TestCaseWithTmpdir { } public void testEnumVisibility() throws Exception { - doTest(PATH + "/enum"); + doTest(ENUM_DIR); + } + + public void testInnerEnum() throws Exception { + doTest(ENUM_DIR); + } + + public void testInnerEnumExistingClassObject() throws Exception { + doTest(ENUM_DIR); } }