Fix initialization order of enum entries and companion

#KT-5761 Fixed
This commit is contained in:
Alexander Udalov
2015-05-29 16:38:07 +03:00
parent 7ba0dda29c
commit df4d5a3ae2
3 changed files with 38 additions and 44 deletions
@@ -81,6 +81,7 @@ import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall; import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall;
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getNotNull;
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration; import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getResolvedCall; import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getResolvedCall;
@@ -225,6 +226,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
AnnotationCodegen.forClass(v.getVisitor(), typeMapper).genAnnotations(descriptor, null); AnnotationCodegen.forClass(v.getVisitor(), typeMapper).genAnnotations(descriptor, null);
generateReflectionObjectFieldIfNeeded(); generateReflectionObjectFieldIfNeeded();
generateEnumEntries();
} }
@Override @Override
@@ -360,7 +363,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
generateSyntheticAccessors(); generateSyntheticAccessors();
generateEnumMethodsAndConstInitializers(); generateEnumMethods();
generateFunctionsForDataClasses(); generateFunctionsForDataClasses();
@@ -764,11 +767,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
return constructor; return constructor;
} }
private void generateEnumMethodsAndConstInitializers() { private void generateEnumMethods() {
if (isEnumClass(descriptor)) { if (isEnumClass(descriptor)) {
generateEnumValuesMethod(); generateEnumValuesMethod();
generateEnumValueOfMethod(); generateEnumValueOfMethod();
initializeEnumConstants();
} }
} }
@@ -1615,24 +1617,22 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
} }
} }
@Override private void generateEnumEntries() {
protected void generateDeclaration(JetDeclaration declaration) { if (descriptor.getKind() != ClassKind.ENUM_CLASS) return;
if (declaration instanceof JetEnumEntry) {
String name = declaration.getName(); List<JetEnumEntry> enumEntries = KotlinPackage.filterIsInstance(element.getDeclarations(), JetEnumEntry.class);
assert name != null : "Enum entry has no name: " + declaration.getText();
ClassDescriptor entryDescriptor = bindingContext.get(BindingContext.CLASS, declaration); for (JetEnumEntry enumEntry : enumEntries) {
FieldVisitor fv = v.newField(OtherOrigin(declaration, entryDescriptor), ACC_PUBLIC | ACC_ENUM | ACC_STATIC | ACC_FINAL, ClassDescriptor descriptor = getNotNull(bindingContext, BindingContext.CLASS, enumEntry);
name, classAsmType.getDescriptor(), null, null); FieldVisitor fv = v.newField(OtherOrigin(enumEntry, descriptor), ACC_PUBLIC | ACC_ENUM | ACC_STATIC | ACC_FINAL,
AnnotationCodegen.forField(fv, typeMapper).genAnnotations(entryDescriptor, null); descriptor.getName().asString(), classAsmType.getDescriptor(), null, null);
myEnumConstants.add((JetEnumEntry) declaration); AnnotationCodegen.forField(fv, typeMapper).genAnnotations(descriptor, null);
} }
super.generateDeclaration(declaration); initializeEnumConstants(enumEntries);
} }
private final List<JetEnumEntry> myEnumConstants = new ArrayList<JetEnumEntry>(); private void initializeEnumConstants(@NotNull List<JetEnumEntry> enumEntries) {
private void initializeEnumConstants() {
if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return; if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
ExpressionCodegen codegen = createOrGetClInitCodegen(); ExpressionCodegen codegen = createOrGetClInitCodegen();
@@ -1642,48 +1642,39 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
v.newField(OtherOrigin(myClass), ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, ENUM_VALUES_FIELD_NAME, v.newField(OtherOrigin(myClass), ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, ENUM_VALUES_FIELD_NAME,
arrayAsmType.getDescriptor(), null, null); arrayAsmType.getDescriptor(), null, null);
iv.iconst(myEnumConstants.size()); iv.iconst(enumEntries.size());
iv.newarray(classAsmType); iv.newarray(classAsmType);
if (!myEnumConstants.isEmpty()) { if (!enumEntries.isEmpty()) {
iv.dup(); iv.dup();
for (int ordinal = 0, size = myEnumConstants.size(); ordinal < size; ordinal++) { for (int ordinal = 0, size = enumEntries.size(); ordinal < size; ordinal++) {
initializeEnumConstant(codegen, ordinal); initializeEnumConstant(enumEntries, ordinal);
} }
} }
iv.putstatic(classAsmType.getInternalName(), ENUM_VALUES_FIELD_NAME, arrayAsmType.getDescriptor()); iv.putstatic(classAsmType.getInternalName(), ENUM_VALUES_FIELD_NAME, arrayAsmType.getDescriptor());
} }
private void initializeEnumConstant(@NotNull ExpressionCodegen codegen, int ordinal) { private void initializeEnumConstant(@NotNull List<JetEnumEntry> enumEntries, int ordinal) {
ExpressionCodegen codegen = createOrGetClInitCodegen();
InstructionAdapter iv = codegen.v; InstructionAdapter iv = codegen.v;
JetEnumEntry enumConstant = myEnumConstants.get(ordinal); JetEnumEntry enumEntry = enumEntries.get(ordinal);
iv.dup(); iv.dup();
iv.iconst(ordinal); iv.iconst(ordinal);
ClassDescriptor classDescriptor = bindingContext.get(BindingContext.CLASS, enumConstant); ClassDescriptor classDescriptor = getNotNull(bindingContext, BindingContext.CLASS, enumEntry);
assert classDescriptor != null;
Type implClass = typeMapper.mapClass(classDescriptor); 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.anew(implClass);
iv.dup(); iv.dup();
iv.aconst(enumConstant.getName()); iv.aconst(enumEntry.getName());
iv.iconst(ordinal); iv.iconst(ordinal);
if (delegationSpecifiers.size() == 1 && !enumEntryNeedSubclass(bindingContext, enumConstant)) { List<JetDelegationSpecifier> delegationSpecifiers = enumEntry.getDelegationSpecifiers();
JetDelegationSpecifier specifier = delegationSpecifiers.get(0); if (delegationSpecifiers.size() == 1 && !enumEntryNeedSubclass(bindingContext, enumEntry)) {
if (!(specifier instanceof JetDelegatorToSuperCall)) { ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCallWithAssert(delegationSpecifiers.get(0), bindingContext);
throw new UnsupportedOperationException("unsupported type of enum constant initializer: " + specifier);
}
ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCallWithAssert(specifier, bindingContext);
CallableMethod method = typeMapper.mapToCallableMethod((ConstructorDescriptor) resolvedCall.getResultingDescriptor()); CallableMethod method = typeMapper.mapToCallableMethod((ConstructorDescriptor) resolvedCall.getResultingDescriptor());
@@ -1694,7 +1685,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
} }
iv.dup(); iv.dup();
iv.putstatic(classAsmType.getInternalName(), enumConstant.getName(), classAsmType.getDescriptor()); iv.putstatic(classAsmType.getInternalName(), enumEntry.getName(), classAsmType.getDescriptor());
iv.astore(OBJECT_TYPE); iv.astore(OBJECT_TYPE);
} }
@@ -6,14 +6,17 @@ enum class Game {
companion object { companion object {
fun foo() = ROCK fun foo() = ROCK
val bar = PAPER val bar = PAPER
val values2 = values()
val scissors = valueOf("SCISSORS")
} }
} }
fun box(): String { fun box(): String {
if (Game.foo() != Game.ROCK) return "Fail 1" if (Game.foo() != Game.ROCK) return "Fail 1"
// TODO: fix initialization order and uncomment (KT-5761) if (Game.bar != Game.PAPER) return "Fail 2: ${Game.bar}"
// if (Game.bar != Game.PAPER) return "Fail 2: ${Game.bar}"
if (Game.values().size() != 3) return "Fail 3" if (Game.values().size() != 3) return "Fail 3"
if (Game.valueOf("SCISSORS") != Game.SCISSORS) return "Fail 4" if (Game.valueOf("SCISSORS") != Game.SCISSORS) return "Fail 4"
if (Game.values2.size() != 3) return "Fail 5"
if (Game.scissors != Game.SCISSORS) return "Fail 6"
return "OK" return "OK"
} }
@@ -3124,9 +3124,9 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("classObjectInEnum.kt") @TestMetadata("companionObjectInEnum.kt")
public void testClassObjectInEnum() throws Exception { public void testCompanionObjectInEnum() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/classObjectInEnum.kt"); String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/companionObjectInEnum.kt");
doTest(fileName); doTest(fileName);
} }