Support inner enums from binaries

#KT-2771 Fixed
This commit is contained in:
Alexander Udalov
2012-09-19 21:11:19 +04:00
parent 83fb970066
commit bce5cb9034
8 changed files with 179 additions and 13 deletions
@@ -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<ClassDescriptor> resolveInnerClasses(DeclarationDescriptor owner, PsiClass psiClass, boolean staticMembers) {
if (staticMembers) {
return new ArrayList<ClassDescriptor>(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<ClassDescriptor> resolveInnerClassesOfClassObject(DeclarationDescriptor owner, PsiClass psiClass) {
if (!DescriptorUtils.isClassObject(owner)) {
return new ArrayList<ClassDescriptor>(0);
}
List<ClassDescriptor> r = new ArrayList<ClassDescriptor>(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<PsiAnnotation> result = new ArrayList<PsiAnnotation>();
@@ -0,0 +1,7 @@
package test
class A {
enum class E {
ENTRY
}
}
@@ -0,0 +1,19 @@
namespace test
internal final class test.A : jet.Any {
public final /*constructor*/ fun <init>(): test.A
internal final class object test.A.<class-object-for-A> {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-A>
internal final enum class test.A.<class-object-for-A>.E : jet.Enum<test.A.<class-object-for-A>.E> {
private final /*constructor*/ fun <init>(/*0*/ p0: jet.String?, /*1*/ p1: jet.Int): test.A.<class-object-for-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.<class-object-for-A>.E.<class-object-for-E> {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-A>.E.<class-object-for-E>
public final val ENTRY: test.A.<class-object-for-A>.E
public final fun valueOf(/*0*/ value: jet.String): test.A.<class-object-for-A>.E
public final fun values(): jet.Array<test.A.<class-object-for-A>.E>
}
}
}
}
@@ -0,0 +1,8 @@
package test
class A {
class object { }
enum class E {
ENTRY
}
}
@@ -0,0 +1,19 @@
namespace test
internal final class test.A : jet.Any {
public final /*constructor*/ fun <init>(): test.A
internal final class object test.A.<class-object-for-A> {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-A>
internal final enum class test.A.<class-object-for-A>.E : jet.Enum<test.A.<class-object-for-A>.E> {
private final /*constructor*/ fun <init>(/*0*/ p0: jet.String?, /*1*/ p1: jet.Int): test.A.<class-object-for-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.<class-object-for-A>.E.<class-object-for-E> {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-A>.E.<class-object-for-E>
public final val ENTRY: test.A.<class-object-for-A>.E
public final fun valueOf(/*0*/ value: jet.String): test.A.<class-object-for-A>.E
public final fun values(): jet.Array<test.A.<class-object-for-A>.E>
}
}
}
}
@@ -0,0 +1,24 @@
namespace test
internal final class test.A : jet.Any {
public final /*constructor*/ fun <init>(): test.A
internal final class object test.A.<class-object-for-A> : jet.Any {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-A>
internal final enum class test.A.<class-object-for-A>.E : jet.Enum<test.A.<class-object-for-A>.E> {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-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.<class-object-for-A>.E.<class-object-for-E> {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-A>.E.<class-object-for-E>
internal final val ENTRY: test.A.<class-object-for-A>.E.<class-object-for-E>.ENTRY
internal final enum entry test.A.<class-object-for-A>.E.<class-object-for-E>.ENTRY : test.A.<class-object-for-A>.E {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-A>.E.<class-object-for-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.<class-object-for-A>.E
public final fun values(): jet.Array<test.A.<class-object-for-A>.E>
}
}
}
}
@@ -0,0 +1,24 @@
namespace test
internal final class test.A : jet.Any {
public final /*constructor*/ fun <init>(): test.A
public final class object test.A.<class-object-for-A> {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-A>
internal final enum class test.A.<class-object-for-A>.E : jet.Enum<test.A.<class-object-for-A>.E> {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-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.<class-object-for-A>.E.<class-object-for-E> {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-A>.E.<class-object-for-E>
internal final val ENTRY: test.A.<class-object-for-A>.E.<class-object-for-E>.ENTRY
internal final enum entry test.A.<class-object-for-A>.E.<class-object-for-E>.ENTRY : test.A.<class-object-for-A>.E {
private final /*constructor*/ fun <init>(): test.A.<class-object-for-A>.E.<class-object-for-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.<class-object-for-A>.E
public final fun values(): jet.Array<test.A.<class-object-for-A>.E>
}
}
}
}
@@ -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);
}
}