Generate classes in MultifileClassCodegen exactly the same as in PackageCodegen

Two known issues with generateNonPartClassDeclarations that was here
before were the fact that we didn't sort sealed classes and its
subclasses which led to NoSuchMethodError (KT-27097), and the fact that
we didn't skip expect classes which led to incorrect duplicate JVM class
name diagnostic (KT-30843)

 #KT-27097 Fixed
 #KT-30843 Fixed
This commit is contained in:
Alexander Udalov
2019-04-05 18:40:28 +02:00
parent c8c630cecb
commit 2f003ef545
11 changed files with 109 additions and 68 deletions
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.psi.KtTypeAlias
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.MemberComparator
@@ -199,7 +198,7 @@ class MultifileClassCodegenImpl(
val partType = Type.getObjectType(JvmFileClassUtil.getFileClassInternalName(file))
val partContext = state.rootContext.intoMultifileClassPart(packageFragment, facadeClassType, partType, file)
generateNonPartClassDeclarations(file, partContext)
PackageCodegenImpl.generateClassesAndObjectsInFile(file, partContext, state)
if (!state.generateDeclaredClassFilter.shouldGeneratePackagePart(file) || !file.hasDeclarationsForPartClass()) return
@@ -214,21 +213,6 @@ class MultifileClassCodegenImpl(
addDelegateGenerationTasksForDeclarationsInFile(file, packageFragment, partType)
}
private fun generateNonPartClassDeclarations(file: KtFile, partContext: FieldOwnerContext<PackageFragmentDescriptor>) {
for (declaration in file.declarations) {
when (declaration) {
is KtClassOrObject ->
if (state.generateDeclaredClassFilter.shouldGenerateClass(declaration)) {
generateClassOrObject(declaration, partContext)
}
is KtScript ->
if (state.generateDeclaredClassFilter.shouldGenerateScript(declaration)) {
ScriptCodegen.createScriptCodegen(declaration, state, partContext).generate()
}
}
}
}
private fun addDelegateGenerationTasksForDeclarationsInFile(file: KtFile, packageFragment: PackageFragmentDescriptor, partType: Type) {
val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType)
val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext)
@@ -17,17 +17,10 @@
package org.jetbrains.kotlin.codegen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.codegen.context.PackageContext;
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.psi.KtFile;
import java.util.Collection;
public interface PackageCodegen {
void generate(@NotNull CompilationErrorHandler errorHandler);
void generateClassOrObject(@NotNull KtClassOrObject classOrObject, @NotNull PackageContext packagePartContext);
PackageFragmentDescriptor getPackageFragment();
}
@@ -23,6 +23,7 @@ import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.codegen.context.CodegenContext;
import org.jetbrains.kotlin.codegen.context.PackageContext;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
@@ -82,9 +83,39 @@ public class PackageCodegenImpl implements PackageCodegen {
}
}
private void generateClassesAndObjectsInFile(@NotNull List<KtClassOrObject> classOrObjects, @NotNull PackageContext packagePartContext) {
for (KtClassOrObject classOrObject : CodegenUtilKt.sortTopLevelClassesAndPrepareContextForSealedClasses(classOrObjects, packagePartContext, state)) {
generateClassOrObject(classOrObject, packagePartContext);
public static void generateClassesAndObjectsInFile(
@NotNull KtFile file,
@NotNull CodegenContext<?> context,
@NotNull GenerationState state
) {
List<KtClassOrObject> classOrObjects = new ArrayList<>();
for (KtDeclaration declaration : file.getDeclarations()) {
if (declaration instanceof KtClassOrObject) {
ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, declaration);
if (PsiUtilsKt.hasExpectModifier(declaration) &&
(descriptor == null || !ExpectedActualDeclarationChecker.shouldGenerateExpectClass(descriptor))) {
continue;
}
KtClassOrObject classOrObject = (KtClassOrObject) declaration;
if (state.getGenerateDeclaredClassFilter().shouldGenerateClass(classOrObject)) {
classOrObjects.add(classOrObject);
}
}
else if (declaration instanceof KtScript) {
KtScript script = (KtScript) declaration;
if (state.getGenerateDeclaredClassFilter().shouldGenerateScript(script)) {
ScriptCodegen.createScriptCodegen(script, state, context).generate();
}
}
}
List<KtClassOrObject> sortedClasses =
CodegenUtilKt.sortTopLevelClassesAndPrepareContextForSealedClasses(classOrObjects, context, state);
for (KtClassOrObject classOrObject : sortedClasses) {
MemberCodegen.genClassOrObject(context, classOrObject, state, null);
}
}
@@ -103,31 +134,7 @@ public class PackageCodegenImpl implements PackageCodegen {
CodeFragmentCodegen.createCodegen((KtCodeFragment) file, state, packagePartContext).generate();
}
} else {
List<KtClassOrObject> classOrObjects = new ArrayList<>();
for (KtDeclaration declaration : file.getDeclarations()) {
if (declaration instanceof KtClassOrObject) {
ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, declaration);
if (PsiUtilsKt.hasExpectModifier(declaration) &&
(descriptor == null || !ExpectedActualDeclarationChecker.shouldGenerateExpectClass(descriptor))) {
continue;
}
KtClassOrObject classOrObject = (KtClassOrObject) declaration;
if (state.getGenerateDeclaredClassFilter().shouldGenerateClass(classOrObject)) {
classOrObjects.add(classOrObject);
}
}
else if (declaration instanceof KtScript) {
KtScript script = (KtScript) declaration;
if (state.getGenerateDeclaredClassFilter().shouldGenerateScript(script)) {
ScriptCodegen.createScriptCodegen(script, state, packagePartContext).generate();
}
}
}
generateClassesAndObjectsInFile(classOrObjects, packagePartContext);
generateClassesAndObjectsInFile(file, packagePartContext, state);
}
if (!state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return;
@@ -166,11 +173,6 @@ public class PackageCodegenImpl implements PackageCodegen {
return fragments.get(0);
}
@Override
public void generateClassOrObject(@NotNull KtClassOrObject classOrObject, @NotNull PackageContext packagePartContext) {
MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null);
}
@Override
public PackageFragmentDescriptor getPackageFragment() {
return packageFragment;
@@ -8,12 +8,11 @@ package org.jetbrains.kotlin.codegen
import com.intellij.psi.PsiElement
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.context.CodegenContext
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.context.MultifileClassFacadeContext
import org.jetbrains.kotlin.codegen.context.PackageContext
import org.jetbrains.kotlin.codegen.coroutines.continuationAsmType
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
import org.jetbrains.kotlin.codegen.inline.NUMBERED_FUNCTION_PREFIX
@@ -169,13 +168,13 @@ fun populateCompanionBackingFieldNamesToOuterContextIfNeeded(
// so that we'd generate the necessary accessor for its constructor afterwards
fun sortTopLevelClassesAndPrepareContextForSealedClasses(
classOrObjects: List<KtClassOrObject>,
packagePartContext: PackageContext,
context: CodegenContext<*>,
state: GenerationState
): List<KtClassOrObject> {
fun prepareContextIfNeeded(descriptor: ClassDescriptor?) {
if (DescriptorUtils.isSealedClass(descriptor)) {
// save context for sealed class
packagePartContext.intoClass(descriptor!!, OwnerKind.IMPLEMENTATION, state)
context.intoClass(descriptor!!, OwnerKind.IMPLEMENTATION, state)
}
}
@@ -18,11 +18,9 @@ package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.context.PackageContext
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
@@ -45,10 +43,6 @@ class JvmIrCodegenFactory(private val phaseConfig: PhaseConfig) : CodegenFactory
JvmBackendFacade.doGenerateFiles(files, state, errorHandler, phaseConfig)
}
override fun generateClassOrObject(classOrObject: KtClassOrObject, packagePartContext: PackageContext) {
TODO()
}
override fun getPackageFragment(): PackageFragmentDescriptor {
return impl.packageFragment
}
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
import org.jetbrains.kotlin.codegen.MemberCodegen
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.name.FqName
@@ -48,7 +49,7 @@ class LightClassDataProviderForClassOrObject(
val packageCodegen = state.factory.forPackage(packageFqName, files)
val packagePartType = Type.getObjectType(JvmFileClassUtil.getFileClassInternalName(file))
val context = state.rootContext.intoPackagePart(packageCodegen.packageFragment, packagePartType, file)
packageCodegen.generateClassOrObject(getOutermostClassOrObject(classOrObject), context)
MemberCodegen.genClassOrObject(context, getOutermostClassOrObject(classOrObject), state, null)
state.factory.done()
}
}
@@ -0,0 +1,13 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
@file:JvmMultifileClass
@file:JvmName("Test")
package test
sealed class Foo(val value: String)
class Bar : Foo("OK")
fun box(): String = Bar().value
@@ -0,0 +1,25 @@
// !LANGUAGE: +MultiPlatformProjects
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: common.kt
@file:JvmMultifileClass
@file:JvmName("Test")
package test
expect class Foo {
val value: String
}
// FILE: jvm.kt
@file:JvmMultifileClass
@file:JvmName("Test")
package test
actual class Foo(actual val value: String)
fun box(): String {
return Foo("OK").value
}
@@ -15822,6 +15822,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/multifileClasses/samePartNameDifferentFacades.kt");
}
@TestMetadata("sealedClassHierarchy.kt")
public void testSealedClassHierarchy() throws Exception {
runTest("compiler/testData/codegen/box/multifileClasses/sealedClassHierarchy.kt");
}
@TestMetadata("compiler/testData/codegen/box/multifileClasses/optimized")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -15878,6 +15883,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/multiplatform"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("expectClassInJvmMultifileFacade.kt")
public void testExpectClassInJvmMultifileFacade() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/expectClassInJvmMultifileFacade.kt");
}
@TestMetadata("noArgActualConstructor.kt")
public void testNoArgActualConstructor() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt");
@@ -15822,6 +15822,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/multifileClasses/samePartNameDifferentFacades.kt");
}
@TestMetadata("sealedClassHierarchy.kt")
public void testSealedClassHierarchy() throws Exception {
runTest("compiler/testData/codegen/box/multifileClasses/sealedClassHierarchy.kt");
}
@TestMetadata("compiler/testData/codegen/box/multifileClasses/optimized")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -15878,6 +15883,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/multiplatform"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("expectClassInJvmMultifileFacade.kt")
public void testExpectClassInJvmMultifileFacade() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/expectClassInJvmMultifileFacade.kt");
}
@TestMetadata("noArgActualConstructor.kt")
public void testNoArgActualConstructor() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt");
@@ -15827,6 +15827,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/multifileClasses/samePartNameDifferentFacades.kt");
}
@TestMetadata("sealedClassHierarchy.kt")
public void testSealedClassHierarchy() throws Exception {
runTest("compiler/testData/codegen/box/multifileClasses/sealedClassHierarchy.kt");
}
@TestMetadata("compiler/testData/codegen/box/multifileClasses/optimized")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -15883,6 +15888,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/multiplatform"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
}
@TestMetadata("expectClassInJvmMultifileFacade.kt")
public void testExpectClassInJvmMultifileFacade() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/expectClassInJvmMultifileFacade.kt");
}
@TestMetadata("noArgActualConstructor.kt")
public void testNoArgActualConstructor() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt");