diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 2f52b2efde2..5c56391675c 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5946,6 +5946,11 @@ + + + + + @@ -7849,6 +7854,11 @@ + + + + + diff --git a/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts index a96f7e240b5..4bbeed9ab90 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } object BackwardsCompatibilityTestConfiguration { - const val minimalBackwardsCompatibleVersion = "1.7.0-dev-1924" + const val minimalBackwardsCompatibleVersion = "1.7.0-dev-1987" } kotlin.sourceSets.configureEach { @@ -18,6 +18,9 @@ dependencies { testImplementation(gradleKotlinDsl()) testImplementation(project(":kotlin-gradle-plugin")) testImplementation(project(":kotlin-test:kotlin-test-junit")) + testImplementation("org.reflections:reflections:0.10.2") { + because("Tests on the object graph are performed. This library will find implementations of interfaces at runtime") + } } publish() diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer.kt index f2456b38854..f4e738308b5 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer.kt +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer.kt @@ -38,6 +38,9 @@ sealed class KotlinExternalModelContainer : Serializable { override val ids: Set> = emptySet() override fun contains(key: KotlinExternalModelKey): Boolean = false override fun get(key: KotlinExternalModelKey): T? = null + + + private const val serialVersionUID = 0L } override fun toString(): String { diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinVariant.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinVariant.kt index 22797b52dd4..777a81edacd 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinVariant.kt +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinVariant.kt @@ -18,6 +18,8 @@ data class IdeaKotlinVariantImpl( override val variantAttributes: Map, override val compilationOutputs: IdeaKotlinCompilationOutput, ) : IdeaKotlinVariant, IdeaKotlinFragment by fragment { + + @InternalKotlinGradlePluginApi companion object { private const val serialVersionUID = 0L } diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/BackwardsCompatibilityDeserializationTest.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/BackwardsCompatibilityDeserializationTest.kt index 87f9ed028d2..a9cc01fa354 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/BackwardsCompatibilityDeserializationTest.kt +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/BackwardsCompatibilityDeserializationTest.kt @@ -26,7 +26,6 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosX64Variant import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinLinuxX64Variant import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.jvm -import org.junit.Test import serialize import unwrapProxyInstance import java.io.File diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinProjectModelObjectGraphTest.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinProjectModelObjectGraphTest.kt new file mode 100644 index 00000000000..d2be6355b0f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinProjectModelObjectGraphTest.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2010-2022 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.gradle.kpm.idea + +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.reflections.Reflections +import java.lang.reflect.Field +import java.lang.reflect.Modifier +import kotlin.reflect.KClass +import kotlin.reflect.full.memberProperties +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +@OptIn(kotlin.ExperimentalStdlibApi::class) +@RunWith(Parameterized::class) +class IdeaKotlinProjectModelObjectGraphTest(private val node: KClass<*>, @Suppress("unused_parameter") clazzName: String) { + + @Test + fun `test - node implements Serializable`() { + assertTrue( + java.io.Serializable::class.java.isAssignableFrom(node.java), + "Expected node ${node.simpleName} to implement `Serializable`" + ) + } + + @Test + fun `test - node implementations contain serialVersionUID`() { + val reflections = Reflections("org.jetbrains.kotlin") + reflections.getSubTypesOf(node.java).forEach { subtype -> + if (!subtype.isInterface && !Modifier.isAbstract(subtype.modifiers)) { + assertNodeImplementationDefinesSerialVersionUID(subtype) + } + } + } + + private fun assertNodeImplementationDefinesSerialVersionUID(implementationClass: Class<*>) { + val serialVersionUID = assertNotNull( + implementationClass.getDeclaredFieldOrNull("serialVersionUID"), + "Expected $implementationClass to declare 'serialVersionUID' field" + ) + + assertTrue( + Modifier.isStatic(serialVersionUID.modifiers), + "Expected $implementationClass to declare 'serialVersionUID' statically" + ) + + assertTrue( + serialVersionUID.type.isPrimitive, + "Expected $implementationClass to declare primitive 'serialVersionUID'" + ) + + assertEquals( + serialVersionUID.type, Long::class.javaPrimitiveType, + "Expected $implementationClass to declare 'serialVersionUID' of type Long" + ) + } + + private fun Class<*>.getDeclaredFieldOrNull(name: String): Field? { + return try { + getDeclaredField(name) + } catch (t: NoSuchFieldException) { + return null + } + } + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{1}") + fun findNodes(): List> { + val classes = mutableSetOf>(IdeaKotlinProjectModel::class) + val resolveQueue = ArrayDeque(classes) + + while (resolveQueue.isNotEmpty()) { + val children = resolveQueue.removeFirst().resolveReachableClasses() + children.forEach { child -> + if (classes.add(child)) { + resolveQueue.add(child) + } + } + } + + return classes.map { clazz -> arrayOf(clazz, checkNotNull(clazz.simpleName)) } + } + + private fun KClass<*>.resolveReachableClasses(): Set> { + return this.memberProperties + .map { member -> member.returnType } + .flatMap { type -> setOf(type) + type.arguments.mapNotNull { it.type } } + .mapNotNull { type -> type.classifier as? KClass<*> } + .filter { clazz -> clazz.java.name.startsWith("org.jetbrains") } + .toSet() + } + } +}