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
This commit is contained in:
+36
-23
@@ -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:")
|
||||
|
||||
Generated
+18
@@ -89,6 +89,11 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform
|
||||
runTest("compiler/testData/multiplatform/inlineClasses/");
|
||||
}
|
||||
|
||||
@TestMetadata("innerGenericClass")
|
||||
public void testInnerGenericClass() throws Exception {
|
||||
runTest("compiler/testData/multiplatform/innerGenericClass/");
|
||||
}
|
||||
|
||||
@TestMetadata("jsNameClash")
|
||||
public void testJsNameClash() throws Exception {
|
||||
runTest("compiler/testData/multiplatform/jsNameClash/");
|
||||
@@ -558,6 +563,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)
|
||||
|
||||
Reference in New Issue
Block a user