Generate values()/valueOf() even for empty enums

This commit is contained in:
Alexander Udalov
2013-10-16 20:13:12 +04:00
parent b4ecb36445
commit 5a1daaa4b6
3 changed files with 98 additions and 74 deletions
@@ -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");