Support inner enums from binaries
#KT-2771 Fixed
This commit is contained in:
+69
-12
@@ -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 PsiClass psiClass,
|
||||||
@Nullable FqName fqName,
|
@Nullable FqName fqName,
|
||||||
@NotNull ClassDescriptorFromJvmBytecode descriptor
|
@NotNull ClassDescriptorFromJvmBytecode descriptor
|
||||||
@@ -545,8 +545,9 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
false);
|
false);
|
||||||
String context = "constructor of class " + psiClass.getQualifiedName();
|
String context = "constructor of class " + psiClass.getQualifiedName();
|
||||||
ValueParameterDescriptors valueParameterDescriptors = resolveParameterDescriptors(constructorDescriptor,
|
ValueParameterDescriptors valueParameterDescriptors = resolveParameterDescriptors(constructorDescriptor,
|
||||||
constructor.getParameters(),
|
constructor.getParameters(),
|
||||||
TypeVariableResolvers.classTypeVariableResolver(classData.classDescriptor, context));
|
TypeVariableResolvers.classTypeVariableResolver(
|
||||||
|
classData.classDescriptor, context));
|
||||||
if (valueParameterDescriptors.receiverType != null) {
|
if (valueParameterDescriptors.receiverType != null) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
@@ -585,6 +586,13 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
return createClassObjectDescriptorForEnum(containing, psiClass);
|
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);
|
PsiClass classObjectPsiClass = getInnerClassClassObject(psiClass);
|
||||||
if (classObjectPsiClass == null) {
|
if (classObjectPsiClass == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -602,20 +610,35 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
return classObjectDescriptor;
|
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
|
@NotNull
|
||||||
private MutableClassDescriptorLite createClassObjectDescriptorForEnum(@NotNull ClassDescriptor containing, @NotNull PsiClass psiClass) {
|
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();
|
String psiClassQualifiedName = psiClass.getQualifiedName();
|
||||||
assert psiClassQualifiedName != null : "Reading java class with no qualified name";
|
assert psiClassQualifiedName != null : "Reading java class with no qualified name";
|
||||||
FqNameUnsafe fqName = new FqNameUnsafe(psiClassQualifiedName + "." + getClassObjectName(psiClass.getName()).getName());
|
FqNameUnsafe fqName = new FqNameUnsafe(psiClassQualifiedName + "." + getClassObjectName(psiClass.getName()).getName());
|
||||||
ClassDescriptorFromJvmBytecode classObjectDescriptor = new ClassDescriptorFromJvmBytecode(
|
ClassDescriptorFromJvmBytecode classObjectDescriptor = new ClassDescriptorFromJvmBytecode(
|
||||||
containing, ClassKind.CLASS_OBJECT, psiClass, null, this);
|
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()));
|
setUpClassObjectDescriptor(containing, fqName, data, getClassObjectName(containing.getName().getName()));
|
||||||
|
|
||||||
classObjectDescriptor.getBuilder().addFunctionDescriptor(createEnumClassObjectValuesMethod(classObjectDescriptor, trace));
|
|
||||||
classObjectDescriptor.getBuilder().addFunctionDescriptor(createEnumClassObjectValueOfMethod(classObjectDescriptor, trace));
|
|
||||||
|
|
||||||
return classObjectDescriptor;
|
return classObjectDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,6 +679,13 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
if (clazz == null) {
|
if (clazz == null) {
|
||||||
throw new IllegalStateException("PsiClass not found by name " + containerFqName + ", required to be container declaration of " + fqName);
|
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;
|
return clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1887,7 +1917,7 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
|
|
||||||
public List<ClassDescriptor> resolveInnerClasses(DeclarationDescriptor owner, PsiClass psiClass, boolean staticMembers) {
|
public List<ClassDescriptor> resolveInnerClasses(DeclarationDescriptor owner, PsiClass psiClass, boolean staticMembers) {
|
||||||
if (staticMembers) {
|
if (staticMembers) {
|
||||||
return new ArrayList<ClassDescriptor>(0);
|
return resolveInnerClassesOfClassObject(owner, psiClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
PsiClass[] innerPsiClasses = psiClass.getInnerClasses();
|
PsiClass[] innerPsiClasses = psiClass.getInnerClasses();
|
||||||
@@ -1900,14 +1930,41 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
if (innerPsiClass.getName().equals(JvmAbi.CLASS_OBJECT_CLASS_NAME)) {
|
if (innerPsiClass.getName().equals(JvmAbi.CLASS_OBJECT_CLASS_NAME)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ClassDescriptor classDescriptor = resolveClass(new FqName(innerPsiClass.getQualifiedName()),
|
if (isInnerEnum(innerPsiClass, owner)) {
|
||||||
DescriptorSearchRule.IGNORE_IF_FOUND_IN_KOTLIN);
|
// Inner enums will be put later into our class object
|
||||||
assert classDescriptor != null: "couldn't resolve class " + innerPsiClass.getQualifiedName();
|
continue;
|
||||||
|
}
|
||||||
|
ClassDescriptor classDescriptor = resolveInnerClass(innerPsiClass);
|
||||||
r.add(classDescriptor);
|
r.add(classDescriptor);
|
||||||
}
|
}
|
||||||
return r;
|
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
|
@NotNull
|
||||||
public static PsiAnnotation[] getAllAnnotations(@NotNull PsiModifierListOwner owner) {
|
public static PsiAnnotation[] getAllAnnotations(@NotNull PsiModifierListOwner owner) {
|
||||||
List<PsiAnnotation> result = new ArrayList<PsiAnnotation>();
|
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 {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user