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()
+ }
+ }
+}