From dc1a1c5821eafff5da9b857c3c41d38fee53ecdd Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Thu, 3 Dec 2020 17:59:14 +0300 Subject: [PATCH] Support cross-module usages of @JvmRecord classes The problem is that JvmRecord has SOURCE retention Probably, increasing its retention might be a more reliable solution (or in some other way serializing that the class is a record) Just checking supertypes seems like a reasonable approximation: only records kotlin are allowed to extend j.l.Record. But the relevant diagnostic has been added only since 1.4.30, so potentially there could have been exist a non-record class with such supertype compiled by 1.4.20, but this case seems to be ill-formed and marginal anyway. For Java classes, it's irrelevant since they don't have member properties (only synthetic extensions) ^KT-43677 In Progress --- .../kotlin/codegen/state/KotlinTypeMapper.kt | 7 ++-- .../jvm/codegen/MethodSignatureMapper.kt | 7 ++-- .../org/jetbrains/kotlin/ir/types/irTypes.kt | 5 +++ .../jvmRecordBinary.kt | 16 +++++++++ ...ractCompileKotlinAgainstKotlinJdk15Test.kt | 27 ++++++++++++++ ...bstractCompileKotlinAgainstKotlinTest.java | 6 ++-- ...ctIrCompileKotlinAgainstKotlinJdk15Test.kt | 18 ++++++++++ ...KotlinAgainstKotlinJdk15TestGenerated.java | 35 +++++++++++++++++++ ...KotlinAgainstKotlinJdk15TestGenerated.java | 35 +++++++++++++++++++ .../generators/tests/GenerateCompilerTests.kt | 8 +++++ 10 files changed, 156 insertions(+), 8 deletions(-) create mode 100644 compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt create mode 100644 compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCompileKotlinAgainstKotlinJdk15Test.kt create mode 100644 compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractIrCompileKotlinAgainstKotlinJdk15Test.kt create mode 100644 compiler/tests-gen/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinJdk15TestGenerated.java create mode 100644 compiler/tests-gen/org/jetbrains/kotlin/codegen/IrCompileKotlinAgainstKotlinJdk15TestGenerated.java diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt index b6c994a9b89..9df67a870c3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt @@ -61,9 +61,9 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isPublishedApi import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.jvm.AsmTypes.DEFAULT_CONSTRUCTOR_MARKER import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE +import org.jetbrains.kotlin.resolve.jvm.JAVA_LANG_RECORD_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.resolve.jvm.annotations.isCompiledToJvmDefault -import org.jetbrains.kotlin.resolve.jvm.annotations.isJvmRecord import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature @@ -592,7 +592,7 @@ class KotlinTypeMapper @JvmOverloads constructor( return property.name.asString() } - if ((containingDeclaration as? ClassDescriptor)?.isJvmRecord() == true) return property.name.asString() + if ((containingDeclaration as? ClassDescriptor)?.hasJavaLangRecordSupertype() == true) return property.name.asString() val isAccessor = property is AccessorForPropertyDescriptor val propertyName = if (isAccessor) @@ -628,6 +628,9 @@ class KotlinTypeMapper @JvmOverloads constructor( } } + private fun ClassDescriptor.hasJavaLangRecordSupertype() = + typeConstructor.supertypes.any { KotlinBuiltIns.isConstructedFromGivenClass(it, JAVA_LANG_RECORD_FQ_NAME) } + private val shouldMangleByReturnType = languageVersionSettings.supportsFeature(LanguageFeature.MangleClassMembersReturningInlineClasses) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt index 1e9427642f5..ee529cefb01 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt @@ -41,7 +41,7 @@ import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.name.NameUtils -import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_RECORD_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.resolve.jvm.JAVA_LANG_RECORD_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature @@ -84,8 +84,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) { if (property != null) { val propertyName = property.name.asString() val propertyParent = property.parentAsClass - if (propertyParent.isAnnotationClass || propertyParent.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME)) - return propertyName + if (propertyParent.isAnnotationClass || propertyParent.superTypes.any { it.isJavaLangRecord() }) return propertyName // The enum property getters and have special names which also // apply to their fake overrides. Unfortunately, getJvmMethodNameIfSpecial does not handle @@ -102,6 +101,8 @@ class MethodSignatureMapper(private val context: JvmBackendContext) { return mangleMemberNameIfRequired(function.name.asString(), function) } + private fun IrType.isJavaLangRecord() = getClass()!!.hasEqualFqName(JAVA_LANG_RECORD_FQ_NAME) + private fun mangleMemberNameIfRequired(name: String, function: IrSimpleFunction): String { val newName = JvmCodegenUtil.sanitizeNameIfNeeded(name, context.state.languageVersionSettings) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt index 148e8ad430b..00c2732e0cf 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt @@ -17,7 +17,9 @@ import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.impl.* import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.isPropertyAccessor +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.makeNullable @@ -74,6 +76,9 @@ val IrType.classifierOrNull: IrClassifierSymbol? val IrType.classOrNull: IrClassSymbol? get() = classifierOrNull as? IrClassSymbol +val IrType.classFqName: FqName? + get() = classOrNull?.owner?.fqNameWhenAvailable + val IrTypeArgument.typeOrNull: IrType? get() = (this as? IrTypeProjection)?.type fun IrType.makeNotNull() = diff --git a/compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt b/compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt new file mode 100644 index 00000000000..1a03e42f875 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt @@ -0,0 +1,16 @@ +// !API_VERSION: 1.5 +// !LANGUAGE: +JvmRecordSupport +// JVM_TARGET: 15_PREVIEW +// FILE: A.kt +@JvmRecord +data class MyRecord(val foo: String, val bar: String) + +// FILE: B.kt + +fun main() { + val myRecord = MyRecord("O", "K") + val s = myRecord.foo + myRecord.bar + if (s != "OK") { + throw AssertionError("fail: $s") + } +} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCompileKotlinAgainstKotlinJdk15Test.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCompileKotlinAgainstKotlinJdk15Test.kt new file mode 100644 index 00000000000..fbad556ee4b --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCompileKotlinAgainstKotlinJdk15Test.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen + +import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TestJdkKind + +abstract class AbstractCompileKotlinAgainstKotlinJdk15Test : AbstractCompileKotlinAgainstKotlinTest() { + override fun invokeBox(className: String) { + runJvmInstance( + KotlinTestUtils.getJdk15Home(), + additionalArgs = listOf("--enable-preview"), + classPath = listOfNotNull( + aDir, bDir, ForTestCompileRuntime.runtimeJarForTests(), + ), + className + ) + } + + override fun getTestJdkKind(files: List): TestJdkKind { + return TestJdkKind.FULL_JDK_15 + } +} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCompileKotlinAgainstKotlinTest.java b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCompileKotlinAgainstKotlinTest.java index 1079b2c6760..9085cf3b184 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCompileKotlinAgainstKotlinTest.java +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCompileKotlinAgainstKotlinTest.java @@ -35,8 +35,8 @@ import java.util.stream.Collectors; public abstract class AbstractCompileKotlinAgainstKotlinTest extends CodegenTestCase { private File tmpdir; - private File aDir; - private File bDir; + protected File aDir; + protected File bDir; @Override protected void setUp() throws Exception { @@ -80,7 +80,7 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends CodegenTest return new Pair<>(factoryA, factoryB); } - private void invokeBox(@NotNull String className) throws Exception { + protected void invokeBox(@NotNull String className) throws Exception { callBoxMethodAndCheckResult(createGeneratedClassLoader(), className); } diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractIrCompileKotlinAgainstKotlinJdk15Test.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractIrCompileKotlinAgainstKotlinJdk15Test.kt new file mode 100644 index 00000000000..f7c555620b6 --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractIrCompileKotlinAgainstKotlinJdk15Test.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen + +import org.jetbrains.kotlin.test.TargetBackend + +abstract class AbstractIrCompileKotlinAgainstKotlinJdk15Test : AbstractCompileKotlinAgainstKotlinJdk15Test() { + override fun getBackendA(): TargetBackend { + return TargetBackend.JVM_IR + } + + override fun getBackendB(): TargetBackend { + return TargetBackend.JVM_IR + } +} diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinJdk15TestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinJdk15TestGenerated.java new file mode 100644 index 00000000000..bdaf5dfa80c --- /dev/null +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinJdk15TestGenerated.java @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/compileKotlinAgainstKotlinJdk15") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class CompileKotlinAgainstKotlinJdk15TestGenerated extends AbstractCompileKotlinAgainstKotlinJdk15Test { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInCompileKotlinAgainstKotlinJdk15() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/compileKotlinAgainstKotlinJdk15"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @TestMetadata("jvmRecordBinary.kt") + public void testJvmRecordBinary() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt"); + } +} diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/IrCompileKotlinAgainstKotlinJdk15TestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/IrCompileKotlinAgainstKotlinJdk15TestGenerated.java new file mode 100644 index 00000000000..f4ff67670fb --- /dev/null +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/IrCompileKotlinAgainstKotlinJdk15TestGenerated.java @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/compileKotlinAgainstKotlinJdk15") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class IrCompileKotlinAgainstKotlinJdk15TestGenerated extends AbstractIrCompileKotlinAgainstKotlinJdk15Test { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInCompileKotlinAgainstKotlinJdk15() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/compileKotlinAgainstKotlinJdk15"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @TestMetadata("jvmRecordBinary.kt") + public void testJvmRecordBinary() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt"); + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt b/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt index 9d4dd0e0e52..6310e05612c 100644 --- a/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt +++ b/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt @@ -368,6 +368,14 @@ fun main(args: Array) { model("compileKotlinAgainstKotlin") } + testClass { + model("compileKotlinAgainstKotlinJdk15") + } + + testClass { + model("compileKotlinAgainstKotlinJdk15") + } + testClass { model("renderer") }