Fix initialization order of enum entries and companion
#KT-5761 Fixed
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -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"
|
||||||
}
|
}
|
||||||
+3
-3
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user