Support sealed class inheritors in the same file

#KT-11573 Fixed
This commit is contained in:
Stanislav Erokhin
2016-05-17 19:23:32 +03:00
parent 26be254fbe
commit 7332032bb6
22 changed files with 343 additions and 72 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,9 @@ import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
import org.jetbrains.org.objectweb.asm.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class PackageCodegen {
private final GenerationState state;
@@ -76,6 +78,12 @@ public class PackageCodegen {
}
}
private void generateClassesAndObjectsInFile(@NotNull List<KtClassOrObject> classOrObjects, @NotNull PackageContext packagePartContext) {
for (KtClassOrObject classOrObject : CodegenUtilKt.sortTopLevelClassesAndPrepareContextForSealedClasses(classOrObjects, packagePartContext, state)) {
generateClassOrObject(classOrObject, packagePartContext);
}
}
private void generateFile(@NotNull KtFile file) {
JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file);
@@ -88,6 +96,8 @@ public class PackageCodegen {
boolean generatePackagePart = false;
List<KtClassOrObject> classOrObjects = new ArrayList<KtClassOrObject>();
for (KtDeclaration declaration : file.getDeclarations()) {
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction) {
generatePackagePart = true;
@@ -95,7 +105,7 @@ public class PackageCodegen {
else if (declaration instanceof KtClassOrObject) {
KtClassOrObject classOrObject = (KtClassOrObject) declaration;
if (state.getGenerateDeclaredClassFilter().shouldGenerateClass(classOrObject)) {
generateClassOrObject(classOrObject, packagePartContext);
classOrObjects.add(classOrObject);
}
}
else if (declaration instanceof KtScript) {
@@ -106,6 +116,7 @@ public class PackageCodegen {
}
}
}
generateClassesAndObjectsInFile(classOrObjects, packagePartContext);
if (!generatePackagePart || !state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return;
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,21 +18,27 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.context.PackageContext
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import java.util.*
fun generateIsCheck(
v: InstructionAdapter,
@@ -125,4 +131,48 @@ fun populateCompanionBackingFieldNamesToOuterContextIfNeeded(companion: KtObject
}
}
}
// Top level subclasses of a sealed class should be generated before that sealed class,
// so that we'd generate the necessary accessor for its constructor afterwards
fun sortTopLevelClassesAndPrepareContextForSealedClasses(
classOrObjects: List<KtClassOrObject>,
packagePartContext: PackageContext,
state: GenerationState
): List<KtClassOrObject> {
fun prepareContextIfNeeded(descriptor: ClassDescriptor?) {
if (DescriptorUtils.isSealedClass(descriptor)) {
// save context for sealed class
packagePartContext.intoClass(descriptor!!, OwnerKind.IMPLEMENTATION, state)
}
}
// optimization
when (classOrObjects.size) {
0 -> return emptyList()
1 -> {
prepareContextIfNeeded(state.bindingContext.get(BindingContext.CLASS, classOrObjects.first()))
return classOrObjects
}
}
val result = ArrayList<KtClassOrObject>(classOrObjects.size)
val descriptorToPsi = LinkedHashMap<ClassDescriptor, KtClassOrObject>()
for (classOrObject in classOrObjects) {
val descriptor = state.bindingContext.get(BindingContext.CLASS, classOrObject)
if (descriptor == null) {
result.add(classOrObject)
}
else {
prepareContextIfNeeded(descriptor)
descriptorToPsi[descriptor] = classOrObject
}
}
// topologicalOrder(listOf(1, 2, 3)) { emptyList() } = listOf(3, 2, 1). Because of this used keys.reversed().
val sortedDescriptors = DFS.topologicalOrder(descriptorToPsi.keys.reversed()) {
it.typeConstructor.supertypes.map { it.constructor.declarationDescriptor as? ClassDescriptor }.filter { it in descriptorToPsi.keys }
}
sortedDescriptors.mapTo(result) { descriptorToPsi[it]!! }
return result
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -274,15 +274,16 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
}
@NotNull
public ClassContext intoClass(ClassDescriptor descriptor, OwnerKind kind, GenerationState state) {
if (descriptor.isCompanionObject()) {
CodegenContext companionContext = this.findChildContext(descriptor);
if (companionContext != null) {
assert companionContext.getContextKind() == kind : "Kinds should be same, but: " +
companionContext.getContextKind() + "!= " + kind;
return (ClassContext) companionContext;
public ClassContext intoClass(@NotNull ClassDescriptor descriptor, @NotNull OwnerKind kind, @NotNull GenerationState state) {
if (shouldAddChild(descriptor)) {
CodegenContext savedContext = this.findChildContext(descriptor);
if (savedContext != null) {
assert savedContext.getContextKind() == kind : "Kinds should be same, but: " +
savedContext.getContextKind() + "!= " + kind;
return (ClassContext) savedContext;
}
}
ClassContext classContext = new ClassContext(state.getTypeMapper(), descriptor, kind, this, null);
if (descriptor.getCompanionObjectDescriptor() != null) {
@@ -571,6 +572,17 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
descriptorContext = ExpressionCodegen.getParentContextSubclassOf((ClassDescriptor) enclosed, this);
}
if (descriptorContext == null && descriptor instanceof ConstructorDescriptor) {
ClassDescriptor classDescriptor = ((ConstructorDescriptor) descriptor).getContainingDeclaration();
if (DescriptorUtils.isSealedClass(classDescriptor)) {
CodegenContext parentContextForClass = findParentContextWithDescriptor(classDescriptor.getContainingDeclaration());
if (parentContextForClass != null) {
//generate super constructor calls for top-level sealed classes from top level child
descriptorContext = parentContextForClass.findChildContext(classDescriptor);
}
}
}
if (descriptorContext == null) {
return descriptor;
}
@@ -630,7 +642,7 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
}
private void addChild(@NotNull CodegenContext child) {
if (shouldAddChild(child)) {
if (shouldAddChild(child.contextDescriptor)) {
if (childContexts == null) {
childContexts = new HashMap<DeclarationDescriptor, CodegenContext>();
}
@@ -639,8 +651,8 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
}
}
protected boolean shouldAddChild(@NotNull CodegenContext child) {
return DescriptorUtils.isCompanionObject(child.contextDescriptor);
private static boolean shouldAddChild(@NotNull DeclarationDescriptor childContextDescriptor) {
return DescriptorUtils.isCompanionObject(childContextDescriptor) || DescriptorUtils.isSealedClass(childContextDescriptor);
}
@Nullable