Generate values()/valueOf() even for empty enums
This commit is contained in:
@@ -904,41 +904,39 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generateEnumMethodsAndConstInitializers() {
|
private void generateEnumMethodsAndConstInitializers() {
|
||||||
if (!myEnumConstants.isEmpty()) {
|
if (isEnumClass(descriptor)) {
|
||||||
generateEnumMethods();
|
generateEnumValuesMethod();
|
||||||
|
generateEnumValueOfMethod();
|
||||||
if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
|
initializeEnumConstants();
|
||||||
initializeEnumConstants(createOrGetClInitCodegen());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateEnumMethods() {
|
private void generateEnumValuesMethod() {
|
||||||
if (myEnumConstants.size() > 0) {
|
Type type = typeMapper.mapType(KotlinBuiltIns.getInstance().getArrayType(descriptor.getDefaultType()));
|
||||||
{
|
|
||||||
Type type = typeMapper.mapType(KotlinBuiltIns.getInstance().getArrayType(descriptor.getDefaultType()));
|
|
||||||
|
|
||||||
MethodVisitor mv = v.newMethod(myClass, ACC_PUBLIC | ACC_STATIC, "values", "()" + type.getDescriptor(), null, null);
|
MethodVisitor mv = v.newMethod(myClass, ACC_PUBLIC | ACC_STATIC, "values", "()" + type.getDescriptor(), null, null);
|
||||||
mv.visitCode();
|
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
|
||||||
mv.visitFieldInsn(GETSTATIC, classAsmType.getInternalName(), VALUES, type.getDescriptor());
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, type.getInternalName(), "clone", "()Ljava/lang/Object;");
|
mv.visitCode();
|
||||||
mv.visitTypeInsn(CHECKCAST, type.getInternalName());
|
mv.visitFieldInsn(GETSTATIC, classAsmType.getInternalName(), VALUES, type.getDescriptor());
|
||||||
mv.visitInsn(ARETURN);
|
mv.visitMethodInsn(INVOKEVIRTUAL, type.getInternalName(), "clone", "()Ljava/lang/Object;");
|
||||||
FunctionCodegen.endVisit(mv, "values()", myClass);
|
mv.visitTypeInsn(CHECKCAST, type.getInternalName());
|
||||||
}
|
mv.visitInsn(ARETURN);
|
||||||
{
|
FunctionCodegen.endVisit(mv, "values()", myClass);
|
||||||
MethodVisitor mv =
|
}
|
||||||
v.newMethod(myClass, ACC_PUBLIC | ACC_STATIC, "valueOf", "(Ljava/lang/String;)" + classAsmType.getDescriptor(), null,
|
|
||||||
null);
|
private void generateEnumValueOfMethod() {
|
||||||
mv.visitCode();
|
MethodVisitor mv =
|
||||||
mv.visitLdcInsn(classAsmType);
|
v.newMethod(myClass, ACC_PUBLIC | ACC_STATIC, "valueOf", "(Ljava/lang/String;)" + classAsmType.getDescriptor(), null, null);
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;");
|
|
||||||
mv.visitTypeInsn(CHECKCAST, classAsmType.getInternalName());
|
mv.visitCode();
|
||||||
mv.visitInsn(ARETURN);
|
mv.visitLdcInsn(classAsmType);
|
||||||
FunctionCodegen.endVisit(mv, "values()", myClass);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
}
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;");
|
||||||
}
|
mv.visitTypeInsn(CHECKCAST, classAsmType.getInternalName());
|
||||||
|
mv.visitInsn(ARETURN);
|
||||||
|
FunctionCodegen.endVisit(mv, "valueOf()", myClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void generateSyntheticAccessors() {
|
protected void generateSyntheticAccessors() {
|
||||||
@@ -1609,63 +1607,71 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
|
|
||||||
private final List<JetEnumEntry> myEnumConstants = new ArrayList<JetEnumEntry>();
|
private final List<JetEnumEntry> myEnumConstants = new ArrayList<JetEnumEntry>();
|
||||||
|
|
||||||
private void initializeEnumConstants(ExpressionCodegen codegen) {
|
private void initializeEnumConstants() {
|
||||||
InstructionAdapter iv = codegen.v;
|
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
|
||||||
int ordinal = -1;
|
|
||||||
|
ExpressionCodegen codegen = createOrGetClInitCodegen();
|
||||||
|
InstructionAdapter iv = codegen.v;
|
||||||
|
|
||||||
assert myEnumConstants.size() > 0;
|
|
||||||
Type arrayAsmType = typeMapper.mapType(KotlinBuiltIns.getInstance().getArrayType(descriptor.getDefaultType()));
|
Type arrayAsmType = typeMapper.mapType(KotlinBuiltIns.getInstance().getArrayType(descriptor.getDefaultType()));
|
||||||
v.newField(myClass, ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, VALUES, arrayAsmType.getDescriptor(), null, null);
|
v.newField(myClass, ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, VALUES, arrayAsmType.getDescriptor(), null, null);
|
||||||
|
|
||||||
iv.iconst(myEnumConstants.size());
|
iv.iconst(myEnumConstants.size());
|
||||||
iv.newarray(classAsmType);
|
iv.newarray(classAsmType);
|
||||||
|
|
||||||
|
if (!myEnumConstants.isEmpty()) {
|
||||||
|
iv.dup();
|
||||||
|
for (int ordinal = 0, size = myEnumConstants.size(); ordinal < size; ordinal++) {
|
||||||
|
initializeEnumConstant(codegen, ordinal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iv.putstatic(classAsmType.getInternalName(), VALUES, arrayAsmType.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeEnumConstant(@NotNull ExpressionCodegen codegen, int ordinal) {
|
||||||
|
InstructionAdapter iv = codegen.v;
|
||||||
|
JetEnumEntry enumConstant = myEnumConstants.get(ordinal);
|
||||||
|
|
||||||
|
iv.dup();
|
||||||
|
iv.iconst(ordinal);
|
||||||
|
|
||||||
|
ClassDescriptor classDescriptor = bindingContext.get(BindingContext.CLASS, enumConstant);
|
||||||
|
assert classDescriptor != null;
|
||||||
|
Type implClass = typeMapper.mapClass(classDescriptor);
|
||||||
|
|
||||||
|
List<JetDelegationSpecifier> delegationSpecifiers = enumConstant.getDelegationSpecifiers();
|
||||||
|
if (delegationSpecifiers.size() > 1) {
|
||||||
|
throw new UnsupportedOperationException("multiple delegation specifiers for enum constant not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
iv.anew(implClass);
|
||||||
iv.dup();
|
iv.dup();
|
||||||
|
|
||||||
for (JetEnumEntry enumConstant : myEnumConstants) {
|
iv.aconst(enumConstant.getName());
|
||||||
ordinal++;
|
iv.iconst(ordinal);
|
||||||
|
|
||||||
iv.dup();
|
if (delegationSpecifiers.size() == 1 && !enumEntryNeedSubclass(state.getBindingContext(), enumConstant)) {
|
||||||
iv.iconst(ordinal);
|
JetDelegationSpecifier specifier = delegationSpecifiers.get(0);
|
||||||
|
if (!(specifier instanceof JetDelegatorToSuperCall)) {
|
||||||
ClassDescriptor classDescriptor = bindingContext.get(BindingContext.CLASS, enumConstant);
|
throw new UnsupportedOperationException("unsupported type of enum constant initializer: " + specifier);
|
||||||
assert classDescriptor != null;
|
|
||||||
Type implClass = typeMapper.mapClass(classDescriptor);
|
|
||||||
|
|
||||||
List<JetDelegationSpecifier> delegationSpecifiers = enumConstant.getDelegationSpecifiers();
|
|
||||||
if (delegationSpecifiers.size() > 1) {
|
|
||||||
throw new UnsupportedOperationException("multiple delegation specifiers for enum constant not supported");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iv.anew(implClass);
|
ResolvedCall<? extends CallableDescriptor> resolvedCall =
|
||||||
iv.dup();
|
bindingContext.get(BindingContext.RESOLVED_CALL, ((JetDelegatorToSuperCall) specifier).getCalleeExpression());
|
||||||
|
assert resolvedCall != null : "Enum entry delegation specifier is unresolved: " + specifier.getText();
|
||||||
|
|
||||||
iv.aconst(enumConstant.getName());
|
CallableMethod method = typeMapper.mapToCallableMethod((ConstructorDescriptor) resolvedCall.getResultingDescriptor());
|
||||||
iv.iconst(ordinal);
|
|
||||||
|
|
||||||
if (delegationSpecifiers.size() == 1 && !enumEntryNeedSubclass(state.getBindingContext(), enumConstant)) {
|
codegen.invokeMethodWithArguments(method, resolvedCall, null, StackValue.none());
|
||||||
JetDelegationSpecifier specifier = delegationSpecifiers.get(0);
|
|
||||||
if (specifier instanceof JetDelegatorToSuperCall) {
|
|
||||||
ResolvedCall<? extends CallableDescriptor> resolvedCall =
|
|
||||||
bindingContext.get(BindingContext.RESOLVED_CALL, ((JetDelegatorToSuperCall) specifier).getCalleeExpression());
|
|
||||||
assert resolvedCall != null : "Enum entry delegation specifier is unresolved: " + specifier.getText();
|
|
||||||
|
|
||||||
ConstructorDescriptor constructorDescriptor = (ConstructorDescriptor) resolvedCall.getResultingDescriptor();
|
|
||||||
CallableMethod method = typeMapper.mapToCallableMethod(constructorDescriptor);
|
|
||||||
|
|
||||||
codegen.invokeMethodWithArguments(method, resolvedCall, null, StackValue.none());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new UnsupportedOperationException("unsupported type of enum constant initializer: " + specifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
iv.invokespecial(implClass.getInternalName(), "<init>", "(Ljava/lang/String;I)V");
|
|
||||||
}
|
|
||||||
iv.dup();
|
|
||||||
iv.putstatic(classAsmType.getInternalName(), enumConstant.getName(), classAsmType.getDescriptor());
|
|
||||||
iv.astore(OBJECT_TYPE);
|
|
||||||
}
|
}
|
||||||
iv.putstatic(classAsmType.getInternalName(), VALUES, arrayAsmType.getDescriptor());
|
else {
|
||||||
|
iv.invokespecial(implClass.getInternalName(), "<init>", "(Ljava/lang/String;I)V");
|
||||||
|
}
|
||||||
|
|
||||||
|
iv.dup();
|
||||||
|
iv.putstatic(classAsmType.getInternalName(), enumConstant.getName(), classAsmType.getDescriptor());
|
||||||
|
iv.astore(OBJECT_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void generateInitializers(
|
public static void generateInitializers(
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
enum class Empty
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
if (Empty.values().size != 0) return "Fail: ${Empty.values()}"
|
||||||
|
|
||||||
|
try {
|
||||||
|
val found = Empty.valueOf("nonExistentEntry")
|
||||||
|
return "Fail: $found"
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1958,6 +1958,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest("compiler/testData/codegen/box/enum/asReturnExpression.kt");
|
doTest("compiler/testData/codegen/box/enum/asReturnExpression.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("emptyEnumValuesValueOf.kt")
|
||||||
|
public void testEmptyEnumValuesValueOf() throws Exception {
|
||||||
|
doTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("entrywithinner.kt")
|
@TestMetadata("entrywithinner.kt")
|
||||||
public void testEntrywithinner() throws Exception {
|
public void testEntrywithinner() throws Exception {
|
||||||
doTest("compiler/testData/codegen/box/enum/entrywithinner.kt");
|
doTest("compiler/testData/codegen/box/enum/entrywithinner.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user