From 09f36927a5c66e8a64fc6e2a456ba8c60fcacbdc Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 19 Sep 2017 17:24:47 +0300 Subject: [PATCH] Do not try to generate 'expect' declarations in multifile classes Similarly to PackageCodegenImpl#generateFile #KT-15754 Fixed #KT-17478 Fixed --- .../kotlin/backend/common/CodegenUtil.kt | 5 +++++ .../kotlin/codegen/ExpressionCodegen.java | 10 ++++------ .../codegen/FunctionGenerationStrategy.java | 6 +++++- .../kotlin/codegen/MultifileClassCodegen.kt | 5 +++-- .../codegen/MultifileClassPartCodegen.kt | 5 +++-- .../kotlin/codegen/PackageCodegenImpl.java | 6 ++---- .../kotlin/codegen/PackagePartCodegen.java | 8 +++----- .../SuspendFunctionGenerationStrategy.kt | 3 ++- .../multiplatform/jvmMultifileClass/common.kt | 6 ++++++ .../multiplatform/jvmMultifileClass/jvm.kt | 5 +++++ .../jvmMultifileClass/output.txt | 7 +++++++ .../AbstractMultiPlatformIntegrationTest.kt | 20 ++++++++++++++++++- ...MultiPlatformIntegrationTestGenerated.java | 6 ++++++ 13 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 compiler/testData/multiplatform/jvmMultifileClass/common.kt create mode 100644 compiler/testData/multiplatform/jvmMultifileClass/jvm.kt create mode 100644 compiler/testData/multiplatform/jvmMultifileClass/output.txt diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt index 576c587717f..f2a99c572b7 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression @@ -161,4 +162,8 @@ object CodegenUtil { (1..arity).joinToString(prefix = "callableReferenceFakeCall(", separator = ", ", postfix = ")") { "p$it" } return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall) as KtCallExpression } + + @JvmStatic + fun getActualDeclarations(file: KtFile): List = + file.declarations.filterNot(KtDeclaration::hasExpectModifier) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 840793fe7cb..17ba8ed6cf0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -103,9 +103,7 @@ import java.util.*; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt; import static org.jetbrains.kotlin.codegen.AsmUtil.*; -import static org.jetbrains.kotlin.codegen.CodegenUtilKt.extractReificationArgument; -import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isPossiblyUninitializedSingleton; -import static org.jetbrains.kotlin.codegen.CodegenUtilKt.unwrapInitialSignatureDescriptor; +import static org.jetbrains.kotlin.codegen.CodegenUtilKt.*; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.*; @@ -1611,7 +1609,7 @@ public class ExpressionCodegen extends KtVisitor impleme return null; } - public void returnExpression(KtExpression expr) { + public void returnExpression(@NotNull KtExpression expr) { boolean isBlockedNamedFunction = expr instanceof KtBlockExpression && expr.getParent() instanceof KtNamedFunction; FunctionDescriptor originalSuspendLambdaDescriptor = getOriginalSuspendLambdaDescriptorFromContext(context); @@ -1644,7 +1642,7 @@ public class ExpressionCodegen extends KtVisitor impleme } } - private static boolean endsWithReturn(KtElement bodyExpression) { + private static boolean endsWithReturn(@NotNull KtElement bodyExpression) { if (bodyExpression instanceof KtBlockExpression) { List statements = ((KtBlockExpression) bodyExpression).getStatements(); return statements.size() > 0 && statements.get(statements.size() - 1) instanceof KtReturnExpression; @@ -1653,7 +1651,7 @@ public class ExpressionCodegen extends KtVisitor impleme return bodyExpression instanceof KtReturnExpression; } - private static boolean isLambdaVoidBody(KtElement bodyExpression, Type returnType) { + private static boolean isLambdaVoidBody(@NotNull KtElement bodyExpression, @NotNull Type returnType) { if (bodyExpression instanceof KtBlockExpression) { PsiElement parent = bodyExpression.getParent(); if (parent instanceof KtFunctionLiteral) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionGenerationStrategy.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionGenerationStrategy.java index 70e647d1220..d11e209f615 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionGenerationStrategy.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionGenerationStrategy.java @@ -20,6 +20,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.codegen.context.MethodContext; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.psi.KtDeclarationWithBody; +import org.jetbrains.kotlin.psi.KtExpression; +import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; import org.jetbrains.org.objectweb.asm.MethodVisitor; @@ -49,7 +51,9 @@ public abstract class FunctionGenerationStrategy { @Override public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) { - codegen.returnExpression(declaration.getBodyExpression()); + KtExpression bodyExpression = declaration.getBodyExpression(); + assert bodyExpression != null : "Function has no body: " + PsiUtilsKt.getElementTextWithContext(declaration); + codegen.returnExpression(bodyExpression); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt index 3b0474ebd89..0b3c6a0b45b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt @@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.util.ArrayUtil import com.intellij.util.SmartList +import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.codegen.context.FieldOwnerContext import org.jetbrains.kotlin.codegen.context.MethodContext import org.jetbrains.kotlin.codegen.state.GenerationState @@ -241,7 +242,7 @@ class MultifileClassCodegenImpl( private fun addDelegateGenerationTasksForDeclarationsInFile(file: KtFile, packageFragment: PackageFragmentDescriptor, partType: Type) { val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType) val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext) - for (declaration in file.declarations) { + for (declaration in CodegenUtil.getActualDeclarations(file)) { if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) { val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) if (descriptor !is MemberDescriptor) { @@ -367,7 +368,7 @@ class MultifileClassCodegenImpl( } private fun KtFile.hasDeclarationsForPartClass() = - declarations.any { it is KtProperty || it is KtFunction } + CodegenUtil.getActualDeclarations(this).any { it is KtProperty || it is KtFunction } private fun getCompiledPackageFragment( facadeFqName: FqName, state: GenerationState diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassPartCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassPartCodegen.kt index 17ced65e860..bad8b7e4975 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassPartCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassPartCodegen.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen import com.intellij.util.ArrayUtil +import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.codegen.context.MultifileClassPartContext import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension import org.jetbrains.kotlin.codegen.state.GenerationState @@ -135,7 +136,7 @@ class MultifileClassPartCodegen( } override fun generateBody() { - for (declaration in element.declarations) { + for (declaration in CodegenUtil.getActualDeclarations(element)) { if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) { genSimpleMember(declaration) } @@ -162,7 +163,7 @@ class MultifileClassPartCodegen( override fun generateKotlinMetadataAnnotation() { val members = ArrayList() - for (declaration in element.declarations) { + for (declaration in CodegenUtil.getActualDeclarations(element)) { when (declaration) { is KtNamedFunction -> { val functionDescriptor = bindingContext.get(BindingContext.FUNCTION, declaration) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java index 7a635d9a4ec..4245c86fa24 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java @@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile; 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.PackageContext; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; @@ -31,7 +32,6 @@ import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus; import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt; import org.jetbrains.org.objectweb.asm.Type; @@ -98,9 +98,7 @@ public class PackageCodegenImpl implements PackageCodegen { List classOrObjects = new ArrayList<>(); - for (KtDeclaration declaration : file.getDeclarations()) { - if (PsiUtilsKt.hasExpectModifier(declaration)) continue; - + for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(file)) { if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) { generatePackagePart = true; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackagePartCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackagePartCodegen.java index 67057075f71..c9d8fd6ea34 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackagePartCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackagePartCodegen.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen; import com.intellij.util.ArrayUtil; import kotlin.Unit; import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.backend.common.CodegenUtil; import org.jetbrains.kotlin.codegen.annotation.AnnotatedSimple; import org.jetbrains.kotlin.codegen.context.FieldOwnerContext; import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension; @@ -32,7 +33,6 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl; import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader; import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.serialization.DescriptorSerializer; import org.jetbrains.kotlin.serialization.ProtoBuf; @@ -88,9 +88,7 @@ public class PackagePartCodegen extends MemberCodegen { @Override protected void generateBody() { - for (KtDeclaration declaration : element.getDeclarations()) { - if (PsiUtilsKt.hasExpectModifier(declaration)) continue; - + for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(element)) { if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty || declaration instanceof KtTypeAlias) { genSimpleMember(declaration); } @@ -104,7 +102,7 @@ public class PackagePartCodegen extends MemberCodegen { @Override protected void generateKotlinMetadataAnnotation() { List members = new ArrayList<>(); - for (KtDeclaration declaration : element.getDeclarations()) { + for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(element)) { if (declaration instanceof KtNamedFunction) { SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, declaration); members.add(functionDescriptor); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt index 83c5786ac46..6cad1404b95 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -70,6 +71,6 @@ class SuspendFunctionGenerationStrategy( override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { this.codegen = codegen - codegen.returnExpression(declaration.bodyExpression) + codegen.returnExpression(declaration.bodyExpression ?: error("Function has no body: " + declaration.getElementTextWithContext())) } } diff --git a/compiler/testData/multiplatform/jvmMultifileClass/common.kt b/compiler/testData/multiplatform/jvmMultifileClass/common.kt new file mode 100644 index 00000000000..b3fd69adec3 --- /dev/null +++ b/compiler/testData/multiplatform/jvmMultifileClass/common.kt @@ -0,0 +1,6 @@ +// WITH_RUNTIME + +@file:kotlin.jvm.JvmMultifileClass +@file:kotlin.jvm.JvmName("CommonKt") + +expect fun foo() diff --git a/compiler/testData/multiplatform/jvmMultifileClass/jvm.kt b/compiler/testData/multiplatform/jvmMultifileClass/jvm.kt new file mode 100644 index 00000000000..05007b3dc69 --- /dev/null +++ b/compiler/testData/multiplatform/jvmMultifileClass/jvm.kt @@ -0,0 +1,5 @@ +actual fun foo() {} + +fun test() { + foo() +} diff --git a/compiler/testData/multiplatform/jvmMultifileClass/output.txt b/compiler/testData/multiplatform/jvmMultifileClass/output.txt new file mode 100644 index 00000000000..b08950d503d --- /dev/null +++ b/compiler/testData/multiplatform/jvmMultifileClass/output.txt @@ -0,0 +1,7 @@ +-- Common -- +Exit code: OK +Output: + +-- JVM -- +Exit code: OK +Output: diff --git a/compiler/tests/org/jetbrains/kotlin/multiplatform/AbstractMultiPlatformIntegrationTest.kt b/compiler/tests/org/jetbrains/kotlin/multiplatform/AbstractMultiPlatformIntegrationTest.kt index 1d28d758442..14e3123c51a 100644 --- a/compiler/tests/org/jetbrains/kotlin/multiplatform/AbstractMultiPlatformIntegrationTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/multiplatform/AbstractMultiPlatformIntegrationTest.kt @@ -26,6 +26,9 @@ import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndAddNewlineAtEOF import java.io.File +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() { fun doTest(directoryPath: String) { @@ -38,6 +41,11 @@ abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() { val tmpdir = KotlinTestUtils.tmpDir(getTestName(true)) + val optionalStdlibCommon = + if (InTextDirectivesUtils.isDirectiveDefined(commonSrc.readText(), "WITH_RUNTIME")) + arrayOf("-cp", findStdlibCommon().absolutePath) + else emptyArray() + val commonDest = File(tmpdir, "common").absolutePath val jvmDest = File(tmpdir, "jvm").absolutePath val jsDest = File(File(tmpdir, "js"), "output.js").absolutePath @@ -45,7 +53,7 @@ abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() { val result = buildString { appendln("-- Common --") - appendln(K2MetadataCompiler().compile(listOf(commonSrc), "-d", commonDest)) + appendln(K2MetadataCompiler().compile(listOf(commonSrc), "-d", commonDest, *optionalStdlibCommon)) if (jvmSrc.exists()) { appendln() @@ -69,6 +77,16 @@ abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() { KotlinTestUtils.assertEqualsToFile(File(root, "output.txt"), result.replace('\\', '/')) } + private fun findStdlibCommon(): File { + // Take kotlin-stdlib-common.jar from dist/ when it's there + val stdlibCommonLibsDir = "libraries/stdlib/common/build/libs" + val commonLibs = Files.newDirectoryStream(Paths.get(stdlibCommonLibsDir)).use(Iterable::toList) + return commonLibs.sorted().findLast { + val name = it.toFile().name + !name.endsWith("-javadoc.jar") && !name.endsWith("-sources.jar") + }?.toFile() ?: error("kotlin-stdlib-common is not found in $stdlibCommonLibsDir") + } + private fun CLICompiler<*>.compileBothWays(commonSource: File, platformSource: File, vararg mainArguments: String): String { val configurations = listOf( listOf(platformSource, commonSource), diff --git a/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java index a7d2f06b80f..25207b58b49 100644 --- a/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java @@ -102,6 +102,12 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform doTest(fileName); } + @TestMetadata("jvmMultifileClass") + public void testJvmMultifileClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/jvmMultifileClass/"); + doTest(fileName); + } + @TestMetadata("missingOverload") public void testMissingOverload() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/missingOverload/");