diff options
author | Alexander Udalov <Alexander.Udalov@jetbrains.com> | 2018-06-19 15:55:46 +0200 |
---|---|---|
committer | Alexander Udalov <Alexander.Udalov@jetbrains.com> | 2018-06-21 14:29:25 +0200 |
commit | a3eb82380d3bc0b59315a6c8fc95e7f6582def4c (patch) | |
tree | e33a8601981debb831326b120dea092370510fc4 | |
parent | adabcc5c029dc14e2fa1915278e472c86c18118d (diff) | |
download | kotlin-a3eb82380d3bc0b59315a6c8fc95e7f6582def4c.tar.gz |
Fix serialization of type parameters inner generic class
The call to `createTopLevel` instead of `create` (which creates
serializers for outer classes properly, with correct type parameter
contexts) caused MetadataSerializer to write type parameter metadata
incorrectly. For example, in the following case:
class A<E> {
inner class B<T, E> { ... }
}
A's type parameter E would get id 0, and B's type parameters T and E
would get ids 0 and 1. This is a problem because ids are supposed to be
unique for each class including its outer classes, and deserializer,
decompiler and stub builder rely on this assumption.
JVM metadata is unaffected because `create` is called correctly there,
see MemberCodegen#generateKotlinClassMetadataAnnotation
#KT-24944 Fixed
10 files changed, 95 insertions, 34 deletions
diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializer.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializer.kt index e276c902b32..8ee354b560b 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializer.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/MetadataSerializer.kt @@ -158,7 +158,7 @@ open class MetadataSerializer(private val dependOnOldBuiltIns: Boolean) { } private fun serializeClass(classDescriptor: ClassDescriptor) { - val classProto = DescriptorSerializer.createTopLevel(extension).classProto(classDescriptor).build() + val classProto = DescriptorSerializer.create(classDescriptor, extension).classProto(classDescriptor).build() proto.addClass_(classProto) serializeClasses(classDescriptor.unsubstitutedInnerClassesScope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS)) diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt index d3339b2afaa..c117f9998ad 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt @@ -36,9 +36,9 @@ object KotlinStubVersions { // BuiltIn stub version should be increased if changes are made to builtIn stub building subsystem (org.jetbrains.kotlin.idea.decompiler.builtIns) // Increasing this version will lead to reindexing of all builtIn files (see KotlinBuiltInFileType). - const val BUILTIN_STUB_VERSION = BINARY_STUB_VERSION + 1 + const val BUILTIN_STUB_VERSION = BINARY_STUB_VERSION + 2 // JS stub version should be increased if changes are made to js stub building subsystem (org.jetbrains.kotlin.idea.decompiler.js) // Increasing this version will lead to reindexing of js binary files (see KotlinJavaScriptMetaFileType). - const val JS_STUB_VERSION = BINARY_STUB_VERSION + 1 + const val JS_STUB_VERSION = BINARY_STUB_VERSION + 2 } diff --git a/compiler/testData/multiplatform/innerGenericClass/common.kt b/compiler/testData/multiplatform/innerGenericClass/common.kt new file mode 100644 index 00000000000..e0eb22ced97 --- /dev/null +++ b/compiler/testData/multiplatform/innerGenericClass/common.kt @@ -0,0 +1,9 @@ +class A<E> { + inner class B<T, E> { + fun getAE() = this@A.getAE() + fun getBT(): T? = null + fun getBE(): E? = null + } + + fun getAE(): E? = null +} diff --git a/compiler/testData/multiplatform/innerGenericClass/common2.kt b/compiler/testData/multiplatform/innerGenericClass/common2.kt new file mode 100644 index 00000000000..721c47e019a --- /dev/null +++ b/compiler/testData/multiplatform/innerGenericClass/common2.kt @@ -0,0 +1,11 @@ +fun test(): String { + val b = A<String>().B<Int, Double>() + val x: String? = b.getAE() + val y: Int? = b.getBT() + val z: Double? = b.getBE() + + // This line is needed to ensure that B.getAE's return type is not an error type; if it was, this line would compile with no errors + b.getAE().unresolved() + + return "$x$y$z" +} diff --git a/compiler/testData/multiplatform/innerGenericClass/output.txt b/compiler/testData/multiplatform/innerGenericClass/output.txt new file mode 100644 index 00000000000..2c52162d1d6 --- /dev/null +++ b/compiler/testData/multiplatform/innerGenericClass/output.txt @@ -0,0 +1,10 @@ +-- Common -- +Exit code: OK +Output: + +-- Common (2) -- +Exit code: COMPILATION_ERROR +Output: +compiler/testData/multiplatform/innerGenericClass/common2.kt:8:15: error: unresolved reference: unresolved + b.getAE().unresolved() + ^ diff --git a/compiler/tests/org/jetbrains/kotlin/multiplatform/AbstractMultiPlatformIntegrationTest.kt b/compiler/tests/org/jetbrains/kotlin/multiplatform/AbstractMultiPlatformIntegrationTest.kt index 14e3123c51a..d7d67777feb 100644 --- a/compiler/tests/org/jetbrains/kotlin/multiplatform/AbstractMultiPlatformIntegrationTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/multiplatform/AbstractMultiPlatformIntegrationTest.kt @@ -33,44 +33,57 @@ import java.nio.file.Paths abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() { fun doTest(directoryPath: String) { val root = File(directoryPath).apply { assert(exists()) } - val commonSrc = File(root, "common.kt") - val jsSrc = File(root, "js.kt") - val jvmSrc = File(root, "jvm.kt") + val commonSrc = File(root, "common.kt").apply { assert(exists()) } + val jsSrc = File(root, "js.kt").takeIf(File::exists) + val jvmSrc = File(root, "jvm.kt").takeIf(File::exists) // TODO: consider inventing a more clever scheme - val jvm2Src = File(root, "jvm2.kt") + val common2Src = File(root, "common2.kt").takeIf(File::exists) + val jvm2Src = File(root, "jvm2.kt").takeIf(File::exists) val tmpdir = KotlinTestUtils.tmpDir(getTestName(true)) val optionalStdlibCommon = - if (InTextDirectivesUtils.isDirectiveDefined(commonSrc.readText(), "WITH_RUNTIME")) - arrayOf("-cp", findStdlibCommon().absolutePath) - else emptyArray() + 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 - val jvm2Dest = File(tmpdir, "jvm2").absolutePath + val jvmDest = File(tmpdir, "jvm").absolutePath.takeIf { jvmSrc != null } + val jsDest = File(File(tmpdir, "js"), "output.js").absolutePath.takeIf { jsSrc != null } + val common2Dest = File(tmpdir, "common2").absolutePath.takeIf { common2Src != null } + val jvm2Dest = File(tmpdir, "jvm2").absolutePath.takeIf { jvm2Src != null } val result = buildString { appendln("-- Common --") appendln(K2MetadataCompiler().compile(listOf(commonSrc), "-d", commonDest, *optionalStdlibCommon)) - if (jvmSrc.exists()) { + if (jvmSrc != null) { appendln() appendln("-- JVM --") - appendln(K2JVMCompiler().compileBothWays(commonSrc, jvmSrc, "-d", jvmDest)) + appendln(K2JVMCompiler().compileBothWays(commonSrc, jvmSrc, "-d", jvmDest!!)) } - if (jsSrc.exists()) { + if (jsSrc != null) { appendln() appendln("-- JS --") - appendln(K2JSCompiler().compileBothWays(commonSrc, jsSrc, "-output", jsDest)) + appendln(K2JSCompiler().compileBothWays(commonSrc, jsSrc, "-output", jsDest!!)) } - if (jvm2Src.exists()) { + if (common2Src != null) { + appendln() + appendln("-- Common (2) --") + appendln(K2MetadataCompiler().compile(listOf(common2Src), "-d", common2Dest!!, "-cp", commonDest, *optionalStdlibCommon)) + } + + if (jvm2Src != null) { appendln() appendln("-- JVM (2) --") - appendln(K2JVMCompiler().compile(listOf(jvm2Src), "-d", jvm2Dest, "-cp", listOf(commonDest, jvmDest).joinToString(File.pathSeparator))) + appendln( + K2JVMCompiler().compile( + listOf(jvm2Src), "-d", jvm2Dest!!, + "-cp", listOfNotNull(commonDest, common2Dest, jvmDest).joinToString(File.pathSeparator) + ) + ) } } @@ -89,17 +102,17 @@ abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() { private fun CLICompiler<*>.compileBothWays(commonSource: File, platformSource: File, vararg mainArguments: String): String { val configurations = listOf( - listOf(platformSource, commonSource), - listOf(commonSource, platformSource) + listOf(platformSource, commonSource), + listOf(commonSource, platformSource) ) val (platformFirst, commonFirst) = configurations.map { compile(it, *mainArguments) } if (platformFirst != commonFirst) { assertEquals( - "Compilation results are different when compiling [platform-specific, common] compared to when compiling [common, platform-specific]", - "// Compiling [platform-specific, common]\n\n$platformFirst", - "// Compiling [common, platform-specific]\n\n$commonFirst" + "Compilation results are different when compiling [platform-specific, common] compared to when compiling [common, platform-specific]", + "// Compiling [platform-specific, common]\n\n$platformFirst", + "// Compiling [common, platform-specific]\n\n$commonFirst" ) } return platformFirst @@ -107,8 +120,8 @@ abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() { private fun CLICompiler<*>.compile(sources: List<File>, vararg mainArguments: String): String = buildString { val (output, exitCode) = AbstractCliTest.executeCompilerGrabOutput( - this@compile, - sources.map(File::getAbsolutePath) + listOf("-Xmulti-platform") + mainArguments + loadExtraArguments(sources) + this@compile, + sources.map(File::getAbsolutePath) + listOf("-Xmulti-platform") + mainArguments + loadExtraArguments(sources) ) appendln("Exit code: $exitCode") appendln("Output:") diff --git a/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java index 8d9fe94b84a..369ca47b3e7 100644 --- a/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/multiplatform/MultiPlatformIntegrationTestGenerated.java @@ -84,6 +84,11 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform runTest("compiler/testData/multiplatform/incorrectImplInClass/"); } + @TestMetadata("innerGenericClass") + public void testInnerGenericClass() throws Exception { + runTest("compiler/testData/multiplatform/innerGenericClass/"); + } + @TestMetadata("jsNameClash") public void testJsNameClash() throws Exception { runTest("compiler/testData/multiplatform/jsNameClash/"); @@ -530,6 +535,19 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform } } + @TestMetadata("compiler/testData/multiplatform/innerGenericClass") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class InnerGenericClass extends AbstractMultiPlatformIntegrationTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInInnerGenericClass() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/multiplatform/innerGenericClass"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + } + @TestMetadata("compiler/testData/multiplatform/jsNameClash") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/KotlinJavascriptMetadataUtils.kt b/compiler/util/src/org/jetbrains/kotlin/utils/KotlinJavascriptMetadataUtils.kt index 4917c846b11..745ca4542a4 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/KotlinJavascriptMetadataUtils.kt +++ b/compiler/util/src/org/jetbrains/kotlin/utils/KotlinJavascriptMetadataUtils.kt @@ -32,7 +32,7 @@ class JsMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) { companion object { @JvmField - val INSTANCE = JsMetadataVersion(1, 2, 1) + val INSTANCE = JsMetadataVersion(1, 2, 2) @JvmField val INVALID_VERSION = JsMetadataVersion() diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsBinaryVersion.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsBinaryVersion.kt index c801bc487c4..bd8c9dc1a70 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsBinaryVersion.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsBinaryVersion.kt @@ -29,7 +29,7 @@ class BuiltInsBinaryVersion(vararg numbers: Int) : BinaryVersion(*numbers) { companion object { @JvmField - val INSTANCE = BuiltInsBinaryVersion(1, 0, 2) + val INSTANCE = BuiltInsBinaryVersion(1, 0, 3) @JvmField val INVALID_VERSION = BuiltInsBinaryVersion() diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt index 9511d51cb5c..6a3d48f04a9 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.serialization.js import org.jetbrains.kotlin.config.AnalysisFlag import org.jetbrains.kotlin.config.LanguageVersionSettings -import org.jetbrains.kotlin.config.isPreRelease import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.NoLookupLocation @@ -197,15 +196,16 @@ object KotlinJavascriptSerializationUtil { } val fileRegistry = KotlinFileRegistry() - val serializerExtension = KotlinJavascriptSerializerExtension(fileRegistry) - val serializer = DescriptorSerializer.createTopLevel(serializerExtension) + val extension = KotlinJavascriptSerializerExtension(fileRegistry) val classDescriptors = scope.filterIsInstance<ClassDescriptor>().sortedBy { it.fqNameSafe.asString() } fun serializeClasses(descriptors: Collection<DeclarationDescriptor>) { fun serializeClass(classDescriptor: ClassDescriptor) { if (skip(classDescriptor)) return - val classProto = serializer.classProto(classDescriptor).build() ?: error("Class not serialized: $classDescriptor") + val classProto = + DescriptorSerializer.create(classDescriptor, extension).classProto(classDescriptor).build() + ?: error("Class not serialized: $classDescriptor") builder.addClass_(classProto) serializeClasses(classDescriptor.unsubstitutedInnerClassesScope.getContributedDescriptors()) } @@ -219,10 +219,10 @@ object KotlinJavascriptSerializationUtil { serializeClasses(classDescriptors) - val stringTable = serializerExtension.stringTable + val stringTable = extension.stringTable val members = scope.filterNot(skip) - builder.`package` = serializer.packagePartProto(fqName, members).build() + builder.`package` = DescriptorSerializer.createTopLevel(extension).packagePartProto(fqName, members).build() builder.setExtension( JsProtoBuf.packageFragmentFiles, |