Generate enum values with bodies properly (KT-15803)
This commit is contained in:
+78
-24
@@ -155,22 +155,15 @@ class ClassFileToSourceStubConverter(
|
||||
val isNested = (descriptor as? ClassDescriptor)?.isNested ?: false
|
||||
val isInner = isNested && (descriptor as? ClassDescriptor)?.isInner ?: false
|
||||
|
||||
val flags = when {
|
||||
(descriptor.containingDeclaration as? ClassDescriptor)?.kind == ClassKind.INTERFACE -> {
|
||||
// Classes inside interfaces should always be public and static.
|
||||
// See com.sun.tools.javac.comp.Enter.visitClassDef for more information.
|
||||
(clazz.access or Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC) and
|
||||
Opcodes.ACC_PRIVATE.inv() and Opcodes.ACC_PROTECTED.inv() // Remove private and protected modifiers
|
||||
}
|
||||
!isInner && isNested -> clazz.access or Opcodes.ACC_STATIC
|
||||
else -> clazz.access
|
||||
}
|
||||
|
||||
val modifiers = convertModifiers(flags, ElementKind.CLASS, packageFqName, clazz.visibleAnnotations, clazz.invisibleAnnotations)
|
||||
val flags = getClassAccessFlags(clazz, descriptor, isInner, isNested)
|
||||
|
||||
val isEnum = clazz.isEnum()
|
||||
val isAnnotation = clazz.isAnnotation()
|
||||
|
||||
val modifiers = convertModifiers(flags,
|
||||
if (isEnum) ElementKind.ENUM else ElementKind.CLASS,
|
||||
packageFqName, clazz.visibleAnnotations, clazz.invisibleAnnotations)
|
||||
|
||||
val isDefaultImpls = clazz.name.endsWith("${descriptor.name.asString()}/DefaultImpls")
|
||||
&& isPublic(clazz.access) && isFinal(clazz.access)
|
||||
&& descriptor is ClassDescriptor
|
||||
@@ -181,14 +174,7 @@ class ClassFileToSourceStubConverter(
|
||||
return null
|
||||
}
|
||||
|
||||
val simpleName = when (descriptor) {
|
||||
is PackageFragmentDescriptor -> {
|
||||
val className = if (packageFqName.isEmpty()) clazz.name else clazz.name.drop(packageFqName.length + 1)
|
||||
if (className.isEmpty()) throw IllegalStateException("Invalid package facade class name: ${clazz.name}")
|
||||
className
|
||||
}
|
||||
else -> if (isDefaultImpls) "DefaultImpls" else descriptor.name.asString()
|
||||
}
|
||||
val simpleName = getClassName(clazz, descriptor, isDefaultImpls, packageFqName)
|
||||
|
||||
val interfaces = mapJList(clazz.interfaces) {
|
||||
if (isAnnotation && it == "java/lang/annotation/Annotation") return@mapJList null
|
||||
@@ -200,7 +186,49 @@ class ClassFileToSourceStubConverter(
|
||||
val hasSuperClass = clazz.superName != "java/lang/Object" && !isEnum
|
||||
val genericType = signatureParser.parseClassSignature(clazz.signature, superClass, interfaces)
|
||||
|
||||
val fields = mapJList<FieldNode, JCTree>(clazz.fields) { convertField(it, packageFqName) }
|
||||
class EnumValueData(val field: FieldNode, val innerClass: InnerClassNode?, val correspondingClass: ClassNode?)
|
||||
|
||||
val enumValuesData = clazz.fields.filter { it.isEnumValue() }.map { field ->
|
||||
var foundInnerClass: InnerClassNode? = null
|
||||
var correspondingClass: ClassNode? = null
|
||||
|
||||
for (innerClass in clazz.innerClasses) {
|
||||
// Class should have the same name as enum value
|
||||
if (innerClass.innerName != field.name) continue
|
||||
val classNode = kaptContext.compiledClasses.firstOrNull { it.name == innerClass.name } ?: continue
|
||||
|
||||
// Super class name of the class should be our enum class
|
||||
if (classNode.superName != clazz.name) continue
|
||||
|
||||
correspondingClass = classNode
|
||||
foundInnerClass = innerClass
|
||||
break
|
||||
}
|
||||
|
||||
EnumValueData(field, foundInnerClass, correspondingClass)
|
||||
}
|
||||
|
||||
val enumValues: JavacList<JCTree> = mapJList(enumValuesData) { data ->
|
||||
val constructorArguments = Type.getArgumentTypes(clazz.methods.firstOrNull {
|
||||
it.name == "<init>" && Type.getArgumentsAndReturnSizes(it.desc).shr(2) >= 2
|
||||
}?.desc ?: "()Z")
|
||||
|
||||
val args = mapJList(constructorArguments.drop(2)) { convertLiteralExpression(getDefaultValue(it)) }
|
||||
|
||||
val def = data.correspondingClass?.let { convertClass(it, packageFqName, false) }
|
||||
|
||||
convertField(data.field, packageFqName, treeMaker.NewClass(
|
||||
/* enclosing = */ null,
|
||||
/* typeArgs = */ JavacList.nil(),
|
||||
/* clazz = */ treeMaker.Ident(treeMaker.name(data.field.name)),
|
||||
/* args = */ args,
|
||||
/* def = */ def))
|
||||
}
|
||||
|
||||
val fields = mapJList<FieldNode, JCTree>(clazz.fields) {
|
||||
if (it.isEnumValue()) null else convertField(it, packageFqName)
|
||||
}
|
||||
|
||||
val methods = mapJList<MethodNode, JCTree>(clazz.methods) {
|
||||
if (isEnum) {
|
||||
if (it.name == "values" && it.desc == "()[L${clazz.name};") return@mapJList null
|
||||
@@ -209,7 +237,9 @@ class ClassFileToSourceStubConverter(
|
||||
|
||||
convertMethod(it, clazz, packageFqName)
|
||||
}
|
||||
|
||||
val nestedClasses = mapJList<InnerClassNode, JCTree>(clazz.innerClasses) { innerClass ->
|
||||
if (enumValuesData.any { it.innerClass == innerClass }) return@mapJList null
|
||||
if (innerClass.outerName != clazz.name) return@mapJList null
|
||||
val innerClassNode = kaptContext.compiledClasses.firstOrNull { it.name == innerClass.name } ?: return@mapJList null
|
||||
convertClass(innerClassNode, packageFqName, false)
|
||||
@@ -221,10 +251,32 @@ class ClassFileToSourceStubConverter(
|
||||
genericType.typeParameters,
|
||||
if (hasSuperClass) genericType.superClass else null,
|
||||
genericType.interfaces,
|
||||
fields + methods + nestedClasses)
|
||||
enumValues + fields + methods + nestedClasses)
|
||||
}
|
||||
|
||||
private fun convertField(field: FieldNode, packageFqName: String): JCVariableDecl? {
|
||||
private fun getClassAccessFlags(clazz: ClassNode, descriptor: DeclarationDescriptor, isInner: Boolean, isNested: Boolean) = when {
|
||||
(descriptor.containingDeclaration as? ClassDescriptor)?.kind == ClassKind.INTERFACE -> {
|
||||
// Classes inside interfaces should always be public and static.
|
||||
// See com.sun.tools.javac.comp.Enter.visitClassDef for more information.
|
||||
(clazz.access or Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC) and
|
||||
Opcodes.ACC_PRIVATE.inv() and Opcodes.ACC_PROTECTED.inv() // Remove private and protected modifiers
|
||||
}
|
||||
!isInner && isNested -> clazz.access or Opcodes.ACC_STATIC
|
||||
else -> clazz.access
|
||||
}
|
||||
|
||||
private fun getClassName(clazz: ClassNode, descriptor: DeclarationDescriptor, isDefaultImpls: Boolean, packageFqName: String): String {
|
||||
return when (descriptor) {
|
||||
is PackageFragmentDescriptor -> {
|
||||
val className = if (packageFqName.isEmpty()) clazz.name else clazz.name.drop(packageFqName.length + 1)
|
||||
if (className.isEmpty()) throw IllegalStateException("Invalid package facade class name: ${clazz.name}")
|
||||
className
|
||||
}
|
||||
else -> if (isDefaultImpls) "DefaultImpls" else descriptor.name.asString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertField(field: FieldNode, packageFqName: String, explicitInitializer: JCExpression? = null): JCVariableDecl? {
|
||||
if (isSynthetic(field.access)) return null
|
||||
val descriptor = kaptContext.origins[field]?.descriptor
|
||||
|
||||
@@ -244,7 +296,8 @@ class ClassFileToSourceStubConverter(
|
||||
|
||||
val value = field.value
|
||||
|
||||
val initializer = convertValueOfPrimitiveTypeOrString(value)
|
||||
val initializer = explicitInitializer
|
||||
?: convertValueOfPrimitiveTypeOrString(value)
|
||||
?: if (isFinal(field.access)) convertLiteralExpression(getDefaultValue(type)) else null
|
||||
|
||||
return treeMaker.VarDef(modifiers, name, typeExpression, initializer)
|
||||
@@ -460,6 +513,7 @@ class ClassFileToSourceStubConverter(
|
||||
} ?: annotations
|
||||
|
||||
val flags = when (kind) {
|
||||
ElementKind.ENUM -> access and CLASS_MODIFIERS and Opcodes.ACC_ABSTRACT.inv().toLong()
|
||||
ElementKind.CLASS -> access and CLASS_MODIFIERS
|
||||
ElementKind.METHOD -> access and METHOD_MODIFIERS
|
||||
ElementKind.FIELD -> access and FIELD_MODIFIERS
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AnnotationNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.FieldNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
internal fun isEnum(access: Int) = (access and Opcodes.ACC_ENUM) != 0
|
||||
@@ -33,6 +34,8 @@ internal fun ClassNode.isEnum() = (access and Opcodes.ACC_ENUM) != 0
|
||||
internal fun ClassNode.isAnnotation() = (access and Opcodes.ACC_ANNOTATION) != 0
|
||||
internal fun MethodNode.isVarargs() = (access and Opcodes.ACC_VARARGS) != 0
|
||||
|
||||
internal fun FieldNode.isEnumValue() = (access and Opcodes.ACC_ENUM) != 0
|
||||
|
||||
internal fun <T> List<T>?.isNullOrEmpty() = this == null || this.isEmpty()
|
||||
|
||||
internal fun MethodNode.isJvmOverloadsGenerated(): Boolean {
|
||||
|
||||
+6
@@ -32,6 +32,12 @@ import java.util.regex.Pattern;
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFileToSourceStubConverterTest {
|
||||
@TestMetadata("abstractEnum.kt")
|
||||
public void testAbstractEnum() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/abstractEnum.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("abstractMethods.kt")
|
||||
public void testAbstractMethods() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/abstractMethods.kt");
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
enum class E {
|
||||
X {
|
||||
override fun a() {}
|
||||
},
|
||||
Y {
|
||||
override fun a() {}
|
||||
};
|
||||
|
||||
abstract fun a()
|
||||
|
||||
fun b() {}
|
||||
|
||||
object Obj
|
||||
class NestedClass
|
||||
}
|
||||
|
||||
enum class E2 {
|
||||
X("") {
|
||||
override fun a() {}
|
||||
},
|
||||
Y(5) {
|
||||
override fun a() {}
|
||||
};
|
||||
|
||||
constructor(n: Int) {}
|
||||
constructor(s: String) {}
|
||||
|
||||
abstract fun a()
|
||||
}
|
||||
|
||||
enum class E3(val a: String) {
|
||||
X(""), Y("")
|
||||
}
|
||||
|
||||
enum class E4(val a: String, val b: Int, val c: Long, val d: Boolean) {
|
||||
X("", 4, 2L, true)
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
public enum E {
|
||||
/*public static final*/ X /* = new X(){
|
||||
|
||||
@java.lang.Override()
|
||||
public void a() {
|
||||
}
|
||||
|
||||
X() {
|
||||
super();
|
||||
}
|
||||
} */,
|
||||
/*public static final*/ Y /* = new Y(){
|
||||
|
||||
@java.lang.Override()
|
||||
public void a() {
|
||||
}
|
||||
|
||||
Y() {
|
||||
super();
|
||||
}
|
||||
} */;
|
||||
|
||||
public abstract void a();
|
||||
|
||||
public final void b() {
|
||||
}
|
||||
|
||||
E() {
|
||||
}
|
||||
|
||||
public static final class Obj {
|
||||
public static final E.Obj INSTANCE = null;
|
||||
|
||||
private Obj() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class NestedClass {
|
||||
|
||||
public NestedClass() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public enum E2 {
|
||||
/*public static final*/ X /* = new X(0){
|
||||
|
||||
@java.lang.Override()
|
||||
public void a() {
|
||||
}
|
||||
|
||||
X() {
|
||||
super(0);
|
||||
}
|
||||
} */,
|
||||
/*public static final*/ Y /* = new Y(0){
|
||||
|
||||
@java.lang.Override()
|
||||
public void a() {
|
||||
}
|
||||
|
||||
Y() {
|
||||
super(0);
|
||||
}
|
||||
} */;
|
||||
|
||||
public abstract void a();
|
||||
|
||||
E2(int n) {
|
||||
}
|
||||
|
||||
E2(java.lang.String s) {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public enum E3 {
|
||||
/*public static final*/ X /* = new X(null) */,
|
||||
/*public static final*/ Y /* = new Y(null) */;
|
||||
private final java.lang.String a = null;
|
||||
|
||||
public final java.lang.String getA() {
|
||||
return null;
|
||||
}
|
||||
|
||||
E3(java.lang.String a) {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public enum E4 {
|
||||
/*public static final*/ X /* = new X(null, 0, 0L, false) */;
|
||||
private final java.lang.String a = null;
|
||||
private final int b = 0;
|
||||
private final long c = 0L;
|
||||
private final boolean d = false;
|
||||
|
||||
public final java.lang.String getA() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final int getB() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final long getC() {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
public final boolean getD() {
|
||||
return false;
|
||||
}
|
||||
|
||||
E4(java.lang.String a, int b, long c, boolean d) {
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -37,8 +37,8 @@ public abstract @interface Anno3 {
|
||||
|
||||
|
||||
public enum Colors {
|
||||
/*public static final*/ WHITE /* = null */,
|
||||
/*public static final*/ BLACK /* = null */;
|
||||
/*public static final*/ WHITE /* = new WHITE() */,
|
||||
/*public static final*/ BLACK /* = new BLACK() */;
|
||||
|
||||
Colors() {
|
||||
}
|
||||
|
||||
+2
-2
@@ -37,8 +37,8 @@ package test;
|
||||
|
||||
@Anno(value = "enum")
|
||||
public enum Enum {
|
||||
/*public static final*/ WHITE /* = null */,
|
||||
/*public static final*/ BLACK /* = null */;
|
||||
/*public static final*/ WHITE /* = new WHITE(0) */,
|
||||
/*public static final*/ BLACK /* = new BLACK(0) */;
|
||||
private final int x = 0;
|
||||
|
||||
public final int getX() {
|
||||
|
||||
+4
-4
@@ -7,8 +7,8 @@ public abstract @interface Anno1 {
|
||||
|
||||
|
||||
public enum Enum1 {
|
||||
/*public static final*/ BLACK /* = null */,
|
||||
/*public static final*/ WHITE /* = null */;
|
||||
/*public static final*/ BLACK /* = new BLACK() */,
|
||||
/*public static final*/ WHITE /* = new WHITE() */;
|
||||
|
||||
Enum1() {
|
||||
}
|
||||
@@ -18,8 +18,8 @@ public enum Enum1 {
|
||||
|
||||
|
||||
public enum Enum2 {
|
||||
/*public static final*/ RED /* = null */,
|
||||
/*public static final*/ WHITE /* = null */;
|
||||
/*public static final*/ RED /* = new RED(null, 0) */,
|
||||
/*public static final*/ WHITE /* = new WHITE(null, 0) */;
|
||||
private final java.lang.String col = null;
|
||||
private final int col2 = 0;
|
||||
|
||||
|
||||
+2
-2
@@ -32,8 +32,8 @@ public final class Inheritor extends BaseClass {
|
||||
|
||||
|
||||
public enum Result {
|
||||
/*public static final*/ SUCCESS /* = null */,
|
||||
/*public static final*/ ERROR /* = null */;
|
||||
/*public static final*/ SUCCESS /* = new SUCCESS() */,
|
||||
/*public static final*/ ERROR /* = new ERROR() */;
|
||||
|
||||
Result() {
|
||||
}
|
||||
|
||||
+2
-2
@@ -37,8 +37,8 @@ public final class Test {
|
||||
}
|
||||
|
||||
public static enum NestedEnum {
|
||||
/*public static final*/ BLACK /* = null */,
|
||||
/*public static final*/ WHITE /* = null */;
|
||||
/*public static final*/ BLACK /* = new BLACK() */,
|
||||
/*public static final*/ WHITE /* = new WHITE() */;
|
||||
|
||||
NestedEnum() {
|
||||
}
|
||||
|
||||
+14
-14
@@ -1,8 +1,20 @@
|
||||
package test;
|
||||
|
||||
public enum EnumClass {
|
||||
/*public static final*/ BLACK /* = new BLACK() */,
|
||||
/*public static final*/ WHITE /* = new WHITE() */;
|
||||
|
||||
EnumClass() {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
package test;
|
||||
|
||||
public enum EnumClass2 {
|
||||
/*public static final*/ WHITE /* = null */,
|
||||
/*public static final*/ RED /* = null */;
|
||||
/*public static final*/ WHITE /* = new WHITE(null) */,
|
||||
/*public static final*/ RED /* = new RED(null) */;
|
||||
private final java.lang.String blah = null;
|
||||
|
||||
EnumClass2(java.lang.String blah) {
|
||||
@@ -18,18 +30,6 @@ public abstract @interface MyAnnotation {
|
||||
|
||||
////////////////////
|
||||
|
||||
package test;
|
||||
|
||||
public enum EnumClass {
|
||||
/*public static final*/ BLACK /* = null */,
|
||||
/*public static final*/ WHITE /* = null */;
|
||||
|
||||
EnumClass() {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
package error;
|
||||
|
||||
public final class NonExistentClass {
|
||||
|
||||
+9
@@ -24,4 +24,13 @@ enum EnumClass2 {
|
||||
EnumClass2(String blah) {
|
||||
this.blah = blah;
|
||||
}
|
||||
}
|
||||
|
||||
enum EnumClass3 {
|
||||
A {
|
||||
@Override
|
||||
void a() {}
|
||||
};
|
||||
|
||||
abstract void a();
|
||||
}
|
||||
Reference in New Issue
Block a user