From 4f4f749c0837e8bfc17b9db4e69276e94807db8d Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Wed, 18 May 2022 14:03:46 +0200 Subject: [PATCH] [kpm] Replace KotlinExternalModelContainer with new Extras implementation This newly introduced `Extras` shall present a more generic mechanism of attaching data of a given type to any entity. --- ...e_Plugin___idea___binary_compatibility.xml | 25 -- ...e_Plugin___idea___binary_compatibility.xml | 25 -- .../kotlin-gradle-plugin-idea.txt | 60 +--- .../kotlin-tooling-core.txt | 95 ++++++ .../plugin/mpp/pm20/KotlinGradleFragment.kt | 3 + .../build.gradle.kts | 2 + .../kpm/KotlinExternalModelContainer.kt | 54 ---- .../kpm/KotlinExternalModelContainerImpl.kt | 103 ------- .../gradle/kpm/KotlinExternalModelId.kt | 45 --- .../gradle/kpm/KotlinExternalModelKey.kt | 38 --- .../kpm/KotlinExternalModelSerializer.kt | 40 --- .../gradle/kpm/KotlinExternalModelType.kt | 43 --- .../gradle/kpm/idea/IdeaKotlinDependency.kt | 11 +- .../gradle/kpm/idea/IdeaKotlinFragment.kt | 7 +- .../kpm/KotlinExternalModelContainerTest.kt | 127 -------- ...ckwardsCompatibilityDeserializationTest.kt | 34 +-- .../IdeaKotlinProjectModelObjectGraphTest.kt | 24 +- .../kotlin/gradle/android/AndroidDsl.kt | 9 +- .../android/IdeaDependencyResolution.kt | 7 +- .../android/KotlinAndroidVariantFactory.kt | 4 +- .../gradle/kpm/external/ExternalVariantApi.kt | 4 - .../kpm/idea/IdeaKotlinFragmentBuilder.kt | 6 +- .../mpp/pm20/KotlinGradleFragmentInternal.kt | 8 +- .../plugin/mpp/pm20/LegacyMappedVariant.kt | 4 + .../jetbrains/kotlin/tooling/core/Extras.kt | 152 +++++++++ .../kotlin/tooling/core/ExtrasImpl.kt | 149 +++++++++ .../kotlin/tooling/core/ExtrasUtils.kt | 49 +++ .../tooling/core/ReifiedTypeSignature.kt | 85 ++++++ .../kotlin/tooling/core/UnsafeApi.kt | 9 + .../tooling/core/ExtrasIdFromStringTest.kt | 41 +++ .../tooling/core/ExtrasSerializableTest.kt | 55 ++++ .../kotlin/tooling/core/ExtrasTest.kt | 289 ++++++++++++++++++ .../tooling/core/ReifiedTypeSignatureTest.kt | 105 +++++++ 33 files changed, 1098 insertions(+), 614 deletions(-) delete mode 100644 .idea/runConfigurations/Test__Kotlin_Gradle_Plugin___idea___binary_compatibility.xml delete mode 100644 .idea/runConfigurations/Update__Kotlin_Gradle_Plugin___idea___binary_compatibility.xml delete mode 100644 libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerImpl.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelId.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelSerializer.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelType.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerTest.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/Extras.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasImpl.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasUtils.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ReifiedTypeSignature.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/UnsafeApi.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasIdFromStringTest.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasSerializableTest.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasTest.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ReifiedTypeSignatureTest.kt diff --git a/.idea/runConfigurations/Test__Kotlin_Gradle_Plugin___idea___binary_compatibility.xml b/.idea/runConfigurations/Test__Kotlin_Gradle_Plugin___idea___binary_compatibility.xml deleted file mode 100644 index 6341d660eaf..00000000000 --- a/.idea/runConfigurations/Test__Kotlin_Gradle_Plugin___idea___binary_compatibility.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - false - true - false - - - \ No newline at end of file diff --git a/.idea/runConfigurations/Update__Kotlin_Gradle_Plugin___idea___binary_compatibility.xml b/.idea/runConfigurations/Update__Kotlin_Gradle_Plugin___idea___binary_compatibility.xml deleted file mode 100644 index 805a22b7c4f..00000000000 --- a/.idea/runConfigurations/Update__Kotlin_Gradle_Plugin___idea___binary_compatibility.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - false - true - false - - - \ No newline at end of file diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-gradle-plugin-idea.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-gradle-plugin-idea.txt index 86d190f2c0e..1402dfa0e9e 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-gradle-plugin-idea.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-gradle-plugin-idea.txt @@ -1,59 +1,3 @@ -public abstract class org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer : java/io/Serializable { - public static final field Companion Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer$Companion; - public abstract fun contains (Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey;)Z - public fun equals (Ljava/lang/Object;)Z - public abstract fun get (Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey;)Ljava/lang/Object; - public abstract fun getIds ()Ljava/util/Set; - public final fun getOrThrow (Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey;)Ljava/lang/Object; - public fun hashCode ()I - public final fun isEmpty ()Z - public final fun isNotEmpty ()Z - public fun toString ()Ljava/lang/String; -} - -public final class org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer$Empty : org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer { - public static final field INSTANCE Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer$Empty; - public fun contains (Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey;)Z - public fun get (Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey;)Ljava/lang/Object; - public fun getIds ()Ljava/util/Set; -} - -public final class org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelId : java/io/Serializable { - public static final field Companion Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelId$Companion; - public fun (Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelType;Ljava/lang/String;)V - public fun equals (Ljava/lang/Object;)Z - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey { - public fun (Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelId;Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelSerializer;)V - public fun equals (Ljava/lang/Object;)Z - public fun hashCode ()I -} - -public abstract interface class org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelSerializer { - public static final field Companion Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelSerializer$Companion; - public abstract fun deserialize ([B)Ljava/lang/Object; - public abstract fun serialize (Ljava/lang/Object;)[B -} - -public final class org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelType : java/io/Serializable { - public static final field Companion Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelType$Companion; - public fun (Ljava/lang/String;)V - public fun equals (Ljava/lang/Object;)Z - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelTypeKt { - public static final fun externalModelTypeSignature (Lkotlin/reflect/KType;)Ljava/lang/String; -} - -public abstract class org/jetbrains/kotlin/gradle/kpm/KotlinMutableExternalModelContainer : org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer { - public abstract fun set (Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey;Ljava/lang/Object;)V -} - public abstract interface class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinBinaryCoordinates : org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinDependencyCoordinates { public abstract fun getGroup ()Ljava/lang/String; public abstract fun getKotlinFragmentName ()Ljava/lang/String; @@ -80,7 +24,7 @@ public abstract interface class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinD public static final field DOCUMENTATION_BINARY_TYPE Ljava/lang/String; public static final field SOURCES_BINARY_TYPE Ljava/lang/String; public abstract fun getCoordinates ()Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinDependencyCoordinates; - public abstract fun getExternal ()Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer; + public abstract fun getExtras ()Lorg/jetbrains/kotlin/tooling/core/Extras; } public final class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinDependency$Companion { @@ -101,7 +45,7 @@ public final class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinDependencyKt { public abstract interface class org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragment : java/io/Serializable { public abstract fun getCoordinates ()Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragmentCoordinates; public abstract fun getDependencies ()Ljava/util/List; - public abstract fun getExternal ()Lorg/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer; + public abstract fun getExtras ()Lorg/jetbrains/kotlin/tooling/core/Extras; public abstract fun getLanguageSettings ()Lorg/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinLanguageSettings; public abstract fun getPlatforms ()Ljava/util/Set; public abstract fun getResourceDirectories ()Ljava/util/List; diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-tooling-core.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-tooling-core.txt index 8582e796e37..9ef0d034faa 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-tooling-core.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-tooling-core.txt @@ -1,3 +1,28 @@ +public abstract class org/jetbrains/kotlin/tooling/core/AbstractExtras : org/jetbrains/kotlin/tooling/core/Extras { + public fun ()V + public synthetic fun add (Ljava/lang/Object;)Z + public fun add (Lorg/jetbrains/kotlin/tooling/core/Extras$Entry;)Z + public fun addAll (Ljava/util/Collection;)Z + public fun clear ()V + public final fun contains (Ljava/lang/Object;)Z + public fun contains (Lorg/jetbrains/kotlin/tooling/core/Extras$Entry;)Z + public fun contains (Lorg/jetbrains/kotlin/tooling/core/Extras$Key;)Z + public fun containsAll (Ljava/util/Collection;)Z + public fun equals (Ljava/lang/Object;)Z + public fun getSize ()I + public fun hashCode ()I + public fun isEmpty ()Z + public fun iterator ()Ljava/util/Iterator; + public fun remove (Ljava/lang/Object;)Z + public fun removeAll (Ljava/util/Collection;)Z + public fun removeIf (Ljava/util/function/Predicate;)Z + public fun retainAll (Ljava/util/Collection;)Z + public final fun size ()I + public fun toArray ()[Ljava/lang/Object; + public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object; + public fun toString ()Ljava/lang/String; +} + public final class org/jetbrains/kotlin/tooling/core/ClosureKt { public static final fun closureTo (Ljava/util/Set;Ljava/util/Set;Ljava/util/List;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/util/Set; public static final fun createResultSet (I)Ljava/util/Set; @@ -5,6 +30,52 @@ public final class org/jetbrains/kotlin/tooling/core/ClosureKt { public static final fun createResultSet (Ljava/lang/Object;I)Ljava/util/Set; } +public abstract interface class org/jetbrains/kotlin/tooling/core/Extras : java/util/Collection, kotlin/jvm/internal/markers/KMappedMarker { + public abstract fun contains (Lorg/jetbrains/kotlin/tooling/core/Extras$Key;)Z + public abstract fun get (Lorg/jetbrains/kotlin/tooling/core/Extras$Key;)Ljava/lang/Object; + public abstract fun getEntries ()Ljava/util/Set; + public abstract fun getKeys ()Ljava/util/Set; + public abstract fun iterator ()Ljava/util/Iterator; +} + +public final class org/jetbrains/kotlin/tooling/core/Extras$Entry : java/io/Serializable { + public fun (Lorg/jetbrains/kotlin/tooling/core/Extras$Key;Ljava/lang/Object;)V + public final fun component1 ()Lorg/jetbrains/kotlin/tooling/core/Extras$Key; + public final fun component2 ()Ljava/lang/Object; + public fun equals (Ljava/lang/Object;)Z + public final fun getKey ()Lorg/jetbrains/kotlin/tooling/core/Extras$Key; + public final fun getValue ()Ljava/lang/Object; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/jetbrains/kotlin/tooling/core/Extras$Key : java/io/Serializable { + public static final field Companion Lorg/jetbrains/kotlin/tooling/core/Extras$Key$Companion; + public fun (Lorg/jetbrains/kotlin/tooling/core/ReifiedTypeSignature;Ljava/lang/String;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getName ()Ljava/lang/String; + public final fun getStableString ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/jetbrains/kotlin/tooling/core/Extras$Key$Companion { + public final fun fromString (Ljava/lang/String;)Lorg/jetbrains/kotlin/tooling/core/Extras$Key; +} + +public final class org/jetbrains/kotlin/tooling/core/ExtrasUtilsKt { + public static final fun emptyExtras ()Lorg/jetbrains/kotlin/tooling/core/Extras; + public static final fun extrasOf ()Lorg/jetbrains/kotlin/tooling/core/Extras; + public static final fun extrasOf ([Lorg/jetbrains/kotlin/tooling/core/Extras$Entry;)Lorg/jetbrains/kotlin/tooling/core/Extras; + public static final fun mutableExtrasOf ()Lorg/jetbrains/kotlin/tooling/core/MutableExtras; + public static final fun mutableExtrasOf ([Lorg/jetbrains/kotlin/tooling/core/Extras$Entry;)Lorg/jetbrains/kotlin/tooling/core/MutableExtras; + public static final fun plus (Lorg/jetbrains/kotlin/tooling/core/Extras;Ljava/lang/Iterable;)Lorg/jetbrains/kotlin/tooling/core/Extras; + public static final fun plus (Lorg/jetbrains/kotlin/tooling/core/Extras;Lorg/jetbrains/kotlin/tooling/core/Extras$Entry;)Lorg/jetbrains/kotlin/tooling/core/Extras; + public static final fun toExtras (Ljava/lang/Iterable;)Lorg/jetbrains/kotlin/tooling/core/Extras; + public static final fun toMutableExtras (Ljava/lang/Iterable;)Lorg/jetbrains/kotlin/tooling/core/MutableExtras; + public static final fun withValue (Lorg/jetbrains/kotlin/tooling/core/Extras$Key;Ljava/lang/Object;)Lorg/jetbrains/kotlin/tooling/core/Extras$Entry; +} + public final class org/jetbrains/kotlin/tooling/core/KotlinToolingVersion : java/io/Serializable, java/lang/Comparable { public fun (IIILjava/lang/String;)V public synthetic fun compareTo (Ljava/lang/Object;)I @@ -53,3 +124,27 @@ public final class org/jetbrains/kotlin/tooling/core/KotlinToolingVersionKt { public static final fun toKotlinVersion (Lorg/jetbrains/kotlin/tooling/core/KotlinToolingVersion;)Lkotlin/KotlinVersion; } +public abstract interface class org/jetbrains/kotlin/tooling/core/MutableExtras : org/jetbrains/kotlin/tooling/core/Extras { + public abstract fun clear ()V + public abstract fun put (Lorg/jetbrains/kotlin/tooling/core/Extras$Entry;)Ljava/lang/Object; + public abstract fun putAll (Ljava/lang/Iterable;)V + public abstract fun remove (Lorg/jetbrains/kotlin/tooling/core/Extras$Key;)Ljava/lang/Object; + public abstract fun set (Lorg/jetbrains/kotlin/tooling/core/Extras$Key;Ljava/lang/Object;)Ljava/lang/Object; +} + +public final class org/jetbrains/kotlin/tooling/core/ReifiedTypeSignature : java/io/Serializable { + public fun (Ljava/lang/String;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getSignature ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/jetbrains/kotlin/tooling/core/ReifiedTypeSignatureKt { + public static final fun renderReifiedTypeSignatureString (Lkotlin/reflect/KType;)Ljava/lang/String; +} + +public abstract interface annotation class org/jetbrains/kotlin/tooling/core/UnsafeApi : java/lang/annotation/Annotation { + public abstract fun message ()Ljava/lang/String; +} + diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragment.kt b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragment.kt index c4a577c5642..2cfec87e851 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragment.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragment.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder import org.jetbrains.kotlin.project.model.KotlinModuleFragment import org.jetbrains.kotlin.project.model.utils.variantsContainingFragment +import org.jetbrains.kotlin.tooling.core.MutableExtras import org.jetbrains.kotlin.tooling.core.closure import org.jetbrains.kotlin.tooling.core.withClosure @@ -29,6 +30,8 @@ interface KotlinGradleFragment : KotlinModuleFragment, HasKotlinDependencies, Ko val project: Project get() = containingModule.project + val extras: MutableExtras + fun refines(other: KotlinGradleFragment) fun refines(other: NamedDomainObjectProvider) diff --git a/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts index 2a671a63fc0..df3ed957d1c 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts @@ -11,6 +11,7 @@ kotlin.sourceSets.configureEach { } dependencies { + api(project(":kotlin-tooling-core")) implementation(kotlinStdlib()) testImplementation(gradleApi()) testImplementation(gradleKotlinDsl()) @@ -24,6 +25,7 @@ dependencies { testFixturesImplementation(gradleApi()) testFixturesImplementation(gradleKotlinDsl()) + testFixturesImplementation(project(":kotlin-tooling-core")) testFixturesImplementation(project(":kotlin-test:kotlin-test-junit")) } 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 deleted file mode 100644 index 9de6deb4f4e..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 - -import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi -import java.io.Serializable - -sealed class KotlinExternalModelContainer : Serializable { - abstract val ids: Set> - abstract operator fun contains(key: KotlinExternalModelKey): Boolean - abstract operator fun get(key: KotlinExternalModelKey): T? - fun getOrThrow(key: KotlinExternalModelKey): T = get(key) - ?: throw NoSuchElementException("Missing external model for ${key.id}") - - fun isEmpty(): Boolean = ids.isEmpty() - fun isNotEmpty(): Boolean = ids.isNotEmpty() - - override fun equals(other: Any?): Boolean { - if (other !is KotlinExternalModelContainer) return false - if (this.isEmpty() && other.isEmpty()) return true - return super.equals(other) - } - - override fun hashCode(): Int { - if (this.isEmpty()) return 0 - return super.hashCode() - } - - @InternalKotlinGradlePluginApi - companion object { - fun mutable(): KotlinMutableExternalModelContainer = KotlinMutableExternalModelContainerImpl() - } - - object Empty : KotlinExternalModelContainer() { - override val ids: Set> = emptySet() - override fun contains(key: KotlinExternalModelKey): Boolean = false - override fun get(key: KotlinExternalModelKey): T? = null - - /* Necessary for stable serialization */ - @Suppress("unused") - private const val serialVersionUID = 0L - } - - override fun toString(): String { - return "KotlinExternalModelContainer(${ids.joinToString("; ", "[", "]")}" - } -} - -sealed class KotlinMutableExternalModelContainer : KotlinExternalModelContainer() { - abstract operator fun set(key: KotlinExternalModelKey, value: T) -} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerImpl.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerImpl.kt deleted file mode 100644 index 711f24be37f..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerImpl.kt +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 - -import org.jetbrains.kotlin.gradle.kpm.idea.WriteReplacedModel -import java.io.Serializable - -@WriteReplacedModel(SerializedKotlinExternalModelContainerCarrier::class) -internal class KotlinMutableExternalModelContainerImpl private constructor( - private val values: MutableMap, Any> -) : KotlinMutableExternalModelContainer() { - - constructor() : this(mutableMapOf()) - - override val ids: Set> - @Synchronized get() = values.keys.map { it.id }.toSet() - - @Synchronized - override fun set(key: KotlinExternalModelKey, value: T) { - values[key] = value - } - - @Synchronized - override fun contains(key: KotlinExternalModelKey): Boolean { - return key in values - } - - @Synchronized - @Suppress("unchecked_cast") - override fun get(key: KotlinExternalModelKey): T? { - return values[key]?.let { it as T } - } - - @Synchronized - private fun writeReplace(): Any { - return SerializedKotlinExternalModelContainerCarrier(serialize(values)) - } - - companion object { - private const val serialVersionUID = 0L - } -} - -@WriteReplacedModel(SerializedKotlinExternalModelContainerCarrier::class) -private class SerializedKotlinExternalModelContainer( - private val serializedValues: MutableMap, ByteArray> -) : KotlinExternalModelContainer(), Serializable { - - private val deserializedValues = mutableMapOf, Any>() - - override val ids: Set> - @Synchronized get() = serializedValues.keys + deserializedValues.keys.map { it.id } - - @Synchronized - override fun contains(key: KotlinExternalModelKey): Boolean { - return key.id in serializedValues || key in deserializedValues - } - - @Synchronized - @Suppress("unchecked_cast") - override fun get(key: KotlinExternalModelKey): T? { - deserializedValues[key]?.let { return it as T } - val serializedValue = serializedValues[key.id] ?: return null - val deserializedValue = key.serializer?.deserialize(serializedValue) ?: return null - deserializedValues[key] = deserializedValue - serializedValues.remove(key.id) - return deserializedValue - } - - @Synchronized - private fun writeReplace(): Any { - return SerializedKotlinExternalModelContainerCarrier(serializedValues + serialize(deserializedValues)) - } - - companion object { - private const val serialVersionUID = 0L - } -} - -private class SerializedKotlinExternalModelContainerCarrier( - private val serializedValues: Map, ByteArray> -) : Serializable { - - private fun readResolve(): Any { - return SerializedKotlinExternalModelContainer(serializedValues.toMutableMap()) - } - - companion object { - private const val serialVersionUID = 0L - } -} - -private fun serialize(values: Map, Any>): Map, ByteArray> { - return values.filterKeys { it.serializer != null } - .mapValues { (key, value) -> - @Suppress("unchecked_cast") - val serializer = checkNotNull(key.serializer) as KotlinExternalModelSerializer - serializer.serialize(value) - }.mapKeys { (key, _) -> key.id } -} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelId.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelId.kt deleted file mode 100644 index 3883eba07f3..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelId.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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. - */ - -@file:Suppress("FunctionName") - -package org.jetbrains.kotlin.gradle.kpm - -import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi -import java.io.Serializable - -inline fun KotlinExternalModelId(disambiguationName: String? = null): KotlinExternalModelId { - return KotlinExternalModelId(KotlinExternalModelType(), disambiguationName) -} - -class KotlinExternalModelId @PublishedApi internal constructor( - private val type: KotlinExternalModelType, - private val disambiguationName: String? = null -) : Serializable { - override fun toString(): String { - return if (disambiguationName == null) type.toString() - else "$disambiguationName:$type" - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - other as KotlinExternalModelId<*> - if (type != other.type) return false - if (disambiguationName != other.disambiguationName) return false - return true - } - - override fun hashCode(): Int { - var result = type.hashCode() - result = 31 * result + (disambiguationName?.hashCode() ?: 0) - return result - } - - @InternalKotlinGradlePluginApi - companion object { - private const val serialVersionUID = 0L - } -} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey.kt deleted file mode 100644 index 1e95bde24cb..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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. - */ - -@file:Suppress("FunctionName") - -package org.jetbrains.kotlin.gradle.kpm - -inline fun KotlinExternalModelKey( - disambiguationName: String? = null, serializer: KotlinExternalModelSerializer? = null -): KotlinExternalModelKey = KotlinExternalModelKey(KotlinExternalModelId(disambiguationName), serializer) - -inline fun KotlinExternalModelKey(serializer: KotlinExternalModelSerializer): KotlinExternalModelKey = - KotlinExternalModelKey(KotlinExternalModelId(), serializer) - -class KotlinExternalModelKey @PublishedApi internal constructor( - internal val id: KotlinExternalModelId, - internal val serializer: KotlinExternalModelSerializer? = null, -) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as KotlinExternalModelKey<*> - - if (id != other.id) return false - if (serializer != other.serializer) return false - - return true - } - - override fun hashCode(): Int { - var result = id.hashCode() - result = 31 * result + (serializer?.hashCode() ?: 0) - return result - } -} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelSerializer.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelSerializer.kt deleted file mode 100644 index 34ab8a70888..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelSerializer.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 - -import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi -import java.io.* - -interface KotlinExternalModelSerializer { - fun serialize(value: T): ByteArray - fun deserialize(data: ByteArray): T - - @InternalKotlinGradlePluginApi - companion object { - inline fun serializable(): KotlinExternalModelSerializer = - serializable(T::class.java) - - fun serializable(clazz: Class): KotlinExternalModelSerializer = - KotlinSerializableExternalModelSerializer(clazz) - } -} - -private class KotlinSerializableExternalModelSerializer( - private val clazz: Class -) : KotlinExternalModelSerializer { - override fun serialize(value: T): ByteArray { - return ByteArrayOutputStream().run { - ObjectOutputStream(this).use { stream -> stream.writeObject(value) } - toByteArray() - } - } - - override fun deserialize(data: ByteArray): T { - return ObjectInputStream(ByteArrayInputStream(data)).use { stream -> - clazz.cast(stream.readObject()) - } - } -} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelType.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelType.kt deleted file mode 100644 index 37ed10e39bf..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelType.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 - -import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi -import java.io.Serializable -import kotlin.reflect.KClass -import kotlin.reflect.KType -import kotlin.reflect.typeOf - -@Suppress("FunctionName") -inline fun KotlinExternalModelType(): KotlinExternalModelType { - return KotlinExternalModelType(externalModelTypeSignature(typeOf())) -} - -class KotlinExternalModelType -@PublishedApi internal constructor(private val signature: String) : Serializable { - override fun equals(other: Any?): Boolean { - if (other !is KotlinExternalModelType<*>) return false - if (other.signature != this.signature) return false - return true - } - - override fun hashCode(): Int = 31 * signature.hashCode() - override fun toString(): String = "ExternalModelType($signature)" - - @InternalKotlinGradlePluginApi - companion object { - private const val serialVersionUID = 0L - } -} - -@PublishedApi -internal fun externalModelTypeSignature(type: KType): String { - require(!type.isMarkedNullable) { "Unexpected nullable type. Found $type" } - require(type.arguments.isEmpty()) { "Parameterized types are not supported. Found $type" } - val classifier = requireNotNull(type.classifier) { "Expected classifier. Found $type" } - val clazz = (classifier as? KClass<*>) ?: throw IllegalArgumentException("Expected KClass classifier. Found $classifier") - return clazz.java.name ?: throw IllegalArgumentException("Missing qualifiedName in $type") -} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinDependency.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinDependency.kt index a04ffbb7a60..65b8775147b 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinDependency.kt +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinDependency.kt @@ -7,17 +7,18 @@ package org.jetbrains.kotlin.gradle.kpm.idea -import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelContainer import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.CLASSPATH_BINARY_TYPE import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.DOCUMENTATION_BINARY_TYPE import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.SOURCES_BINARY_TYPE +import org.jetbrains.kotlin.tooling.core.Extras +import org.jetbrains.kotlin.tooling.core.emptyExtras import java.io.File import java.io.Serializable import java.util.* sealed interface IdeaKotlinDependency : Serializable { val coordinates: IdeaKotlinDependencyCoordinates? - val external: KotlinExternalModelContainer + val extras: Extras companion object { const val CLASSPATH_BINARY_TYPE = "org.jetbrains.binary.type.classpath" @@ -61,7 +62,7 @@ val IdeaKotlinResolvedBinaryDependency.isClasspathType get() = binaryType == CLA data class IdeaKotlinFragmentDependencyImpl( override val type: IdeaKotlinFragmentDependency.Type, override val coordinates: IdeaKotlinFragmentCoordinates, - override val external: KotlinExternalModelContainer = KotlinExternalModelContainer.Empty, + override val extras: Extras = emptyExtras() ) : IdeaKotlinFragmentDependency { override fun toString(): String { @@ -80,7 +81,7 @@ data class IdeaKotlinResolvedBinaryDependencyImpl( override val coordinates: IdeaKotlinBinaryCoordinates?, override val binaryType: String, override val binaryFile: File, - override val external: KotlinExternalModelContainer = KotlinExternalModelContainer.Empty + override val extras: Extras = emptyExtras() ) : IdeaKotlinResolvedBinaryDependency { override fun toString(): String { @@ -97,7 +98,7 @@ data class IdeaKotlinResolvedBinaryDependencyImpl( data class IdeaKotlinUnresolvedBinaryDependencyImpl( override val cause: String?, override val coordinates: IdeaKotlinBinaryCoordinates?, - override val external: KotlinExternalModelContainer = KotlinExternalModelContainer.Empty + override val extras: Extras = emptyExtras() ) : IdeaKotlinUnresolvedBinaryDependency { @InternalKotlinGradlePluginApi diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragment.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragment.kt index dc624576aa1..e024168c059 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragment.kt +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragment.kt @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.gradle.kpm.idea -import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelContainer +import org.jetbrains.kotlin.tooling.core.Extras import java.io.Serializable sealed interface IdeaKotlinFragment : Serializable { @@ -15,7 +15,7 @@ sealed interface IdeaKotlinFragment : Serializable { val dependencies: List val sourceDirectories: List val resourceDirectories: List - val external: KotlinExternalModelContainer + val extras: Extras } val IdeaKotlinFragment.name get() = coordinates.fragmentName @@ -28,7 +28,7 @@ data class IdeaKotlinFragmentImpl( override val dependencies: List, override val sourceDirectories: List, override val resourceDirectories: List, - override val external: KotlinExternalModelContainer + override val extras: Extras ) : IdeaKotlinFragment { @InternalKotlinGradlePluginApi @@ -36,4 +36,3 @@ data class IdeaKotlinFragmentImpl( private const val serialVersionUID = 0L } } - diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerTest.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerTest.kt deleted file mode 100644 index 9fc5a6d0348..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerTest.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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 - -import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.deserialize -import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.serialize -import java.io.Serializable -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotSame -import kotlin.test.assertNull - -class KotlinExternalModelContainerTest { - - private data class RetainedModel(val value: Int) : Serializable - private data class UnretainedModel(val value: Int) - - private val retainedModelKey = KotlinExternalModelKey(KotlinExternalModelSerializer.serializable()) - private val retainedModelKeyFoo = KotlinExternalModelKey("foo", KotlinExternalModelSerializer.serializable()) - private val retainedModelKeyBar = KotlinExternalModelKey("bar", KotlinExternalModelSerializer.serializable()) - - private val unretainedModelKey = KotlinExternalModelKey() - private val unretainedModelKeyFoo = KotlinExternalModelKey("foo") - private val unretainedModelKeyBar = KotlinExternalModelKey("bar") - - @Test - fun `test - attach simple values`() { - val container = KotlinExternalModelContainer.mutable() - container[retainedModelKey] = RetainedModel(1) - container[retainedModelKeyFoo] = RetainedModel(2) - container[retainedModelKeyBar] = RetainedModel(3) - container[unretainedModelKey] = UnretainedModel(4) - container[unretainedModelKeyFoo] = UnretainedModel(5) - container[unretainedModelKeyBar] = UnretainedModel(6) - - assertEquals(RetainedModel(1), container[retainedModelKey]) - assertEquals(RetainedModel(2), container[retainedModelKeyFoo]) - assertEquals(RetainedModel(3), container[retainedModelKeyBar]) - assertEquals(UnretainedModel(4), container[unretainedModelKey]) - assertEquals(UnretainedModel(5), container[unretainedModelKeyFoo]) - assertEquals(UnretainedModel(6), container[unretainedModelKeyBar]) - } - - @Test - fun `test - accessing missing value`() { - val container = KotlinExternalModelContainer.mutable() - assertNull(container[retainedModelKey]) - assertNull(container[unretainedModelKey]) - } - - @Test - fun `test - serializing container`() { - val container = KotlinExternalModelContainer.mutable() - container[retainedModelKey] = RetainedModel(1) - container[retainedModelKeyFoo] = RetainedModel(2) - container[retainedModelKeyBar] = RetainedModel(3) - container[unretainedModelKey] = UnretainedModel(4) - container[unretainedModelKeyFoo] = UnretainedModel(5) - container[unretainedModelKeyBar] = UnretainedModel(6) - - val deserializedContainer = container.serialize().deserialize() - - assertEquals(RetainedModel(1), deserializedContainer[retainedModelKey]) - assertEquals(RetainedModel(2), deserializedContainer[retainedModelKeyFoo]) - assertEquals(RetainedModel(3), deserializedContainer[retainedModelKeyBar]) - assertNull(deserializedContainer[unretainedModelKey]) - assertNull(deserializedContainer[unretainedModelKeyFoo]) - assertNull(deserializedContainer[unretainedModelKeyBar]) - } - - @Test - fun `test - serializing container twice`() { - val container = KotlinExternalModelContainer.mutable() - container[retainedModelKey] = RetainedModel(1) - container[unretainedModelKey] = UnretainedModel(4) - - val deserializedContainer = container.serialize().deserialize() - assertEquals(RetainedModel(1), deserializedContainer[retainedModelKey]) - assertNull(deserializedContainer[unretainedModelKey]) - - val twiceDeserializedContainer = deserializedContainer.serialize().deserialize() - assertEquals(RetainedModel(1), twiceDeserializedContainer[retainedModelKey]) - assertNull(twiceDeserializedContainer[unretainedModelKey]) - } - - @Test - fun `test - accessing deserialized container without serializer`() { - val container = KotlinExternalModelContainer.mutable() - container[retainedModelKey] = RetainedModel(1) - container[retainedModelKeyFoo] = RetainedModel(2) - - val deserializedContainer = container.serialize().deserialize() - assertEquals(RetainedModel(1), deserializedContainer[retainedModelKey]) - - /* Accessing something already deserialized without deserializer: Returning null, because the keys do not match */ - assertNull(deserializedContainer[KotlinExternalModelKey(retainedModelKey.id)]) - - /* Accessing something not deserialized without deserializer: Cannot deserialize: Null to be lenient */ - assertNull(deserializedContainer[KotlinExternalModelKey(retainedModelKeyFoo.id)]) - - /* Accessing something previously requested but now with deserializer */ - assertEquals(RetainedModel(2), deserializedContainer[retainedModelKeyFoo]) - - /* Now accessing retainedModelFoo without serializer should behave like above */ - assertNull(deserializedContainer[KotlinExternalModelKey(retainedModelKeyFoo.id)]) - } - - @Test - fun `test - empty containers are equal`() { - val container1 = KotlinExternalModelContainer.mutable() - val container2 = KotlinExternalModelContainer.mutable() - - assertNotSame(container1, container2) - assertEquals(container1, container2) - assertEquals(container2, container1) - assertEquals(KotlinExternalModelContainer.Empty, container1) - assertEquals(container1, KotlinExternalModelContainer.Empty) - assertEquals(KotlinExternalModelContainer.Empty, container2) - assertEquals(container2, KotlinExternalModelContainer.Empty) - - assertEquals(container1.hashCode(), container2.hashCode()) - assertEquals(container1.hashCode(), KotlinExternalModelContainer.Empty.hashCode()) - } -} 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 c9a3497fdd9..77319fcfa1b 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 @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -@file:OptIn(InternalKotlinGradlePluginApi::class) +@file:OptIn(InternalKotlinGradlePluginApi::class, ExternalVariantApi::class) package org.jetbrains.kotlin.gradle.kpm.idea @@ -13,10 +13,7 @@ import createProxyInstance import org.gradle.api.internal.project.ProjectInternal import org.gradle.kotlin.dsl.create import org.gradle.testfixtures.ProjectBuilder -import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelKey -import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelSerializer.Companion.serializable import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi -import org.jetbrains.kotlin.gradle.kpm.external.external import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.deserialize import org.jetbrains.kotlin.gradle.kpm.idea.testFixtures.serialize import org.jetbrains.kotlin.gradle.plugin.KotlinPm20PluginWrapper @@ -24,6 +21,7 @@ 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.jetbrains.kotlin.tooling.core.extrasKeyOf import unwrapProxyInstance import java.io.File import java.io.Serializable @@ -42,6 +40,9 @@ import kotlin.test.* */ class BackwardsCompatibilityDeserializationTest { + data class RetainedModel(val id: Int) : Serializable + data class UnretainedModel(val id: Int) + @Test fun `test - simple project`() { val (project, kotlinExtension) = createKpmProject() @@ -79,22 +80,19 @@ class BackwardsCompatibilityDeserializationTest { } } - @OptIn(ExternalVariantApi::class) + @Ignore("No serialisation for extras implemented, yet") @Test - fun `test - attaching serializable models`() { - data class RetainedModel(val id: Int) : Serializable - data class UnretainedModel(val id: Int) - - val retainedModelKey = KotlinExternalModelKey(serializable()) - val unretainedModelKey = KotlinExternalModelKey() + fun `test - attaching serializable extras`() { + val retainedModelKey = extrasKeyOf() + val unretainedModelKey = extrasKeyOf() val project = ProjectBuilder.builder().build() as ProjectInternal project.plugins.apply(KotlinPm20PluginWrapper::class.java) /* Setup example project */ val kotlinExtension = project.extensions.getByType(KotlinPm20ProjectExtension::class.java) - kotlinExtension.main.common.external[retainedModelKey] = RetainedModel(2411) - kotlinExtension.main.common.external[unretainedModelKey] = UnretainedModel(510) + kotlinExtension.main.common.extras[retainedModelKey] = RetainedModel(2411) + kotlinExtension.main.common.extras[unretainedModelKey] = UnretainedModel(510) val model = project.buildIdeaKotlinProjectModel() val deserializedModel = deserializeModelWithBackwardsCompatibleClasses(model) @@ -108,10 +106,10 @@ class BackwardsCompatibilityDeserializationTest { run { val deserializedCommonFragment = unwrapProxyInstance(deserializedCommonFragmentProxy) - val external = deserializedCommonFragment.serialize().deserialize().external - assertEquals(1, external.ids.size) - assertEquals(RetainedModel(2411), external[retainedModelKey]) - assertNull(external[unretainedModelKey]) + val extras = deserializedCommonFragment.serialize().deserialize().extras + assertEquals(1, extras.keys.size) + assertEquals(RetainedModel(2411), extras[retainedModelKey]) + assertNull(extras[unretainedModelKey]) } } } @@ -127,7 +125,7 @@ private fun getClasspathForBackwardsCompatibilityTest(): List { return compatibilityTestClasspath.split(";").map { path -> File(path) } .onEach { file -> if (!file.exists()) println("[WARNING] Missing $file") } - .flatMap { file -> if(file.isDirectory) file.listFiles().orEmpty().toList() else listOf(file) } + .flatMap { file -> if (file.isDirectory) file.listFiles().orEmpty().toList() else listOf(file) } } private fun deserializeModelWithBackwardsCompatibleClasses(model: IdeaKotlinProjectModel): Any { 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 index ab706c71421..e21029a25b0 100644 --- 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 @@ -5,6 +5,9 @@ package org.jetbrains.kotlin.gradle.kpm.idea +import org.jetbrains.kotlin.tooling.core.AbstractExtras +import org.jetbrains.kotlin.tooling.core.Extras +import org.jetbrains.kotlin.tooling.core.MutableExtras import org.junit.runner.RunWith import org.junit.runners.Parameterized import org.reflections.Reflections @@ -14,10 +17,7 @@ import kotlin.reflect.KClass import kotlin.reflect.KVisibility.PUBLIC import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.memberProperties -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertTrue +import kotlin.test.* @OptIn(ExperimentalStdlibApi::class) @RunWith(Parameterized::class) @@ -86,6 +86,13 @@ class IdeaKotlinProjectModelObjectGraphTest(private val node: KClass<*>, @Suppre companion object { private val reflections = Reflections("org.jetbrains.kotlin") + private val ignoredNodes = setOf( + /* + Extras interface and AbstractExtras are okay for now: + Let's check known implementations for correctness + */ + Extras::class, MutableExtras::class, AbstractExtras::class + ) @JvmStatic @Parameterized.Parameters(name = "{1}") @@ -114,7 +121,14 @@ class IdeaKotlinProjectModelObjectGraphTest(private val node: KClass<*>, @Suppre } } - return classes.map { clazz -> arrayOf(clazz, checkNotNull(clazz.simpleName)) } + fun KClass<*>.displayName() = java.name + .removePrefix("org.jetbrains.kotlin") + .removePrefix(".gradle.kpm") + .removePrefix(".") + + return classes + .filter { it !in ignoredNodes } + .map { clazz -> arrayOf(clazz, checkNotNull(clazz.displayName())) } } private fun KClass<*>.resolveReachableClasses(): Set> { diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidDsl.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidDsl.kt index a0ddba9c48c..57c7e65c235 100644 --- a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidDsl.kt +++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidDsl.kt @@ -3,17 +3,16 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -@file:OptIn(InternalKotlinGradlePluginApi::class) package org.jetbrains.kotlin.gradle.android -import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelKey -import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelSerializer -import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment +import org.jetbrains.kotlin.tooling.core.extrasKeyOf import java.io.File import java.io.Serializable -val androidDslKey = KotlinExternalModelKey(KotlinExternalModelSerializer.serializable()) +internal val androidDslKey = extrasKeyOf() +val KotlinGradleFragment.androidDsl: AndroidDsl? get() = this.extras[androidDslKey] class AndroidDsl : Serializable { var compileSdk = 0 diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/IdeaDependencyResolution.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/IdeaDependencyResolution.kt index e9e0e2295c8..ad6724e689e 100644 --- a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/IdeaDependencyResolution.kt +++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/IdeaDependencyResolution.kt @@ -8,7 +8,6 @@ package org.jetbrains.kotlin.gradle.android import com.android.build.gradle.internal.publishing.AndroidArtifacts import org.gradle.api.attributes.Usage import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi -import org.jetbrains.kotlin.gradle.kpm.external.external import org.jetbrains.kotlin.gradle.kpm.external.project import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinDependency.Companion.CLASSPATH_BINARY_TYPE import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinProjectModelBuilder.FragmentConstraint @@ -19,16 +18,14 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.containingVariants -@OptIn(ExternalVariantApi::class) val isAndroidFragment = FragmentConstraint { fragment -> - fragment.containingVariants.all { variant -> androidDslKey in variant.external } + fragment.containingVariants.all { variant -> variant.androidDsl != null } } -@OptIn(ExternalVariantApi::class) val isAndroidAndJvmSharedFragment = FragmentConstraint constraint@{ fragment -> val variants = fragment.containingVariants if (variants.any { it.platformType != KotlinPlatformType.jvm }) return@constraint false - variants.any { androidDslKey in it.external } && variants.any { androidDslKey !in it.external } + variants.any { it.androidDsl != null } && variants.any { it.androidDsl == null } } @OptIn(ExternalVariantApi::class, InternalKotlinGradlePluginApi::class) diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt index 4125ecd7c18..9b0459b8e1f 100644 --- a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt @@ -17,7 +17,6 @@ import org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMEN import org.gradle.kotlin.dsl.named import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi import org.jetbrains.kotlin.gradle.kpm.external.createExternalJvmVariant -import org.jetbrains.kotlin.gradle.kpm.external.external import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* @@ -88,6 +87,5 @@ fun KotlinGradleModule.createKotlinAndroidVariant(androidVariant: BaseVariant) { val androidDsl = AndroidDsl() androidDsl.androidManifest = project.file("AndroidManifest.xml") androidDsl.compileSdk = 23 - androidCommon.external[androidDslKey] = androidDsl - kotlinVariant.external[androidDslKey] = androidDsl + kotlinVariant.extras[androidDslKey] = androidDsl } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/external/ExternalVariantApi.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/external/ExternalVariantApi.kt index 12bf564b5fe..81404a3de8d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/external/ExternalVariantApi.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/external/ExternalVariantApi.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.gradle.kpm.external import org.gradle.api.Project import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension -import org.jetbrains.kotlin.gradle.kpm.KotlinMutableExternalModelContainer import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKotlinProjectModelBuilder import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* @@ -36,6 +35,3 @@ fun KotlinGradleModule.createExternalJvmVariant( val KotlinGradleVariantInternal.compilationData get() = this.compilationData -@ExternalVariantApi -val KotlinGradleFragment.external: KotlinMutableExternalModelContainer - get() = (this as KotlinGradleFragmentInternal).external diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragmentBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragmentBuilder.kt index 351771dc38d..3da89fee990 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragmentBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragmentBuilder.kt @@ -5,8 +5,8 @@ package org.jetbrains.kotlin.gradle.kpm.idea -import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelContainer import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* +import org.jetbrains.kotlin.tooling.core.emptyExtras internal fun IdeaKotlinProjectModelBuildingContext.IdeaKotlinFragment(fragment: KotlinGradleFragment): IdeaKotlinFragment { return if (fragment is KotlinGradleVariant) buildIdeaKotlinVariant(fragment) @@ -20,8 +20,8 @@ private fun IdeaKotlinProjectModelBuildingContext.buildIdeaKotlinFragment(fragme languageSettings = IdeaKotlinLanguageSettings(fragment.languageSettings), dependencies = dependencyResolver.resolve(fragment).toList(), sourceDirectories = fragment.kotlinSourceRoots.sourceDirectories.files.toList().map { file -> IdeaKotlinSourceDirectoryImpl(file) }, - resourceDirectories = emptyList(), - external = (fragment as? KotlinGradleFragmentInternal)?.external ?: KotlinExternalModelContainer.Empty + resourceDirectories = emptyList(), // TODO + extras = emptyExtras() // TODO: Requires more sophisticated serialization ) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragmentInternal.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragmentInternal.kt index 591a0fe5e3a..3a045d778b2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragmentInternal.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragmentInternal.kt @@ -11,8 +11,6 @@ import org.gradle.api.Project import org.gradle.api.file.SourceDirectorySet import org.gradle.api.provider.Provider import org.gradle.util.ConfigureUtil -import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelContainer -import org.jetbrains.kotlin.gradle.kpm.KotlinMutableExternalModelContainer import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinDependencyHandler @@ -23,6 +21,8 @@ import org.jetbrains.kotlin.gradle.plugin.sources.FragmentConsistencyChecks import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheckWhenEvaluated import org.jetbrains.kotlin.project.model.KotlinModuleDependency +import org.jetbrains.kotlin.tooling.core.MutableExtras +import org.jetbrains.kotlin.tooling.core.mutableExtrasOf import javax.inject.Inject open class KotlinGradleFragmentInternal @Inject constructor( @@ -37,13 +37,13 @@ open class KotlinGradleFragmentInternal @Inject constructor( final override val project: Project // overriding with final to avoid warnings get() = super.project + override val extras: MutableExtras = mutableExtrasOf() + // TODO pull up to KotlinModuleFragment // FIXME apply to compilation // FIXME check for consistency override val languageSettings: LanguageSettingsBuilder = DefaultLanguageSettingsBuilder() - internal val external: KotlinMutableExternalModelContainer = KotlinExternalModelContainer.mutable() - override fun refines(other: KotlinGradleFragment) { checkCanRefine(other) refines(containingModule.fragments.named(other.name)) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/LegacyMappedVariant.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/LegacyMappedVariant.kt index afa52640970..183af0fdfb2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/LegacyMappedVariant.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/LegacyMappedVariant.kt @@ -27,6 +27,8 @@ import org.jetbrains.kotlin.gradle.utils.filesProvider import org.jetbrains.kotlin.project.model.KotlinAttributeKey import org.jetbrains.kotlin.project.model.KotlinModuleDependency import org.jetbrains.kotlin.project.model.KotlinModuleFragment +import org.jetbrains.kotlin.tooling.core.MutableExtras +import org.jetbrains.kotlin.tooling.core.mutableExtrasOf internal open class LegacyMappedVariant( internal val compilation: KotlinCompilation<*>, @@ -86,6 +88,8 @@ internal open class LegacyMappedVariant( override val languageSettings: LanguageSettingsBuilder get() = compilation.defaultSourceSet.languageSettings + override val extras: MutableExtras = mutableExtrasOf() + override fun refines(other: KotlinGradleFragment) { fragmentForDefaultSourceSet.refines(other) } diff --git a/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/Extras.kt b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/Extras.kt new file mode 100644 index 00000000000..dd777de84a7 --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/Extras.kt @@ -0,0 +1,152 @@ +/* + * 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.tooling.core + +import org.jetbrains.kotlin.tooling.core.Extras.Entry +import org.jetbrains.kotlin.tooling.core.Extras.Key +import java.io.Serializable + +/** + * A generic container holding typed and scoped values. + * ### Attaching and getting simple typed values: + * ```kotlin + * val extras = mutableExtrasOf() + * extras[extrasKeyOf()] = 42 // Attach arbitrary Int value + * extras[extrasKeyOf()] = "Hello" // Attach arbitrary String value + * + * extras[extrasKeyOf()] // -> returns 42 + * extras[extrasKeyOf] // -> returns "Hello" + * ``` + * + * ### Attaching multiple values with the same type by naming the keys + * ```kotlin + * val extras = mutableExtrasOf() + * extras[extrasKeyOf("a")] = 1 // Attach Int with name 'a' + * extras[extrasKeyOf("b")] = 2 // Attach Int with name 'b' + * + * extras[extrasKeyOf("a")] // -> returns 1 + * extras[extrasKeyOf("b")] // -> returns 2 + * ``` + * + * ### Creating immutable extras + * ```kotlin + * val extras = extrasOf( + * extrasKeyOf() withValue 1, + * extrasKeyOf() withValue "Hello" + * ) + * ``` + * + * ### Converting to immutable extras + * ```kotlin + * val extras = mutableExtrasOf( + * extrasKeyOf() withValue 0 + * ) + * + * // Captures the content, similar to `.toList()` or `.toSet()` + * val immutableExtras = extras.toExtras() + * ``` + * + * ### Use case example: Filtering Extras + * ```kotlin + * val extras = extrasOf( + * extrasKeyOf() withValue 0, + * extrasKeyOf("keep") withValue 1, + * extrasKeyOf() withValue "Hello" + * ) + * + * val filteredExtras = extras + * .filter { (key, value) -> key.id.name == "keep" || value is String } + * .toExtras() + * ``` + */ +interface Extras : Collection> { + /* Not implemented as data class to ensure more controllable binary compatibility */ + class Key @PublishedApi internal constructor( + internal val type: ReifiedTypeSignature, + val name: String? = null, + ) : Serializable { + + val stableString: String + get() { + return if (name == null) type.signature + else "${type.signature};$name" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Key<*>) return false + if (name != other.name) return false + if (type != other.type) return false + return true + } + + override fun hashCode(): Int { + var result = name?.hashCode() ?: 0 + result = 31 * result + type.hashCode() + return result + } + + override fun toString(): String = stableString + + companion object { + fun fromString(stableString: String): Key<*> { + @OptIn(UnsafeApi::class) return if (stableString.contains(';')) { + val split = stableString.split(';', limit = 2) + Key(ReifiedTypeSignature(split[0]), split[1]) + } else Key(ReifiedTypeSignature(stableString)) + } + + private const val serialVersionUID = 0L + } + } + + /* Not implemented as data class to ensure more controllable binary compatibility */ + class Entry(val key: Key, val value: T) : Serializable { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Entry<*>) return false + if (other.key != key) return false + if (other.value != value) return false + return true + } + + override fun hashCode(): Int { + var result = key.hashCode() + result = 31 * result + value.hashCode() + return result + } + + override fun toString(): String = "$key=$value" + + operator fun component1() = key + operator fun component2() = value + + internal companion object { + private const val serialVersionUID = 0L + } + } + + val keys: Set> + val entries: Set> + operator fun get(key: Key): T? + operator fun contains(key: Key<*>): Boolean + override fun iterator(): Iterator> +} + +interface MutableExtras : Extras { + /** + * @return The previous value or null if no previous value was set + */ + operator fun set(key: Key, value: T): T? + + fun put(entry: Entry): T? + + fun putAll(from: Iterable>) + + fun remove(key: Key): T? + + fun clear() +} diff --git a/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasImpl.kt b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasImpl.kt new file mode 100644 index 00000000000..7b0a683ef82 --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasImpl.kt @@ -0,0 +1,149 @@ +/* + * 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.tooling.core + +import org.jetbrains.kotlin.tooling.core.Extras.Entry +import org.jetbrains.kotlin.tooling.core.Extras.Key +import java.io.Serializable + +@Suppress("unchecked_cast") +internal class MutableExtrasImpl( + initialEntries: Iterable> = emptyList() +) : MutableExtras, AbstractExtras(), Serializable { + + private val extras: MutableMap, Entry<*>> = + initialEntries.associateByTo(mutableMapOf()) { it.key } + + override val keys: Set> + get() = extras.keys.toSet() + + override val entries: Set> + get() = extras.values.toSet() + + override val size: Int + get() = extras.size + + override fun isEmpty(): Boolean = extras.isEmpty() + + override fun set(key: Key, value: T): T? { + return put(Entry(key, value)) + } + + override fun put(entry: Entry): T? { + return extras.put(entry.key, entry)?.let { it.value as T } + } + + override fun putAll(from: Iterable>) { + this.extras.putAll(from.associateBy { it.key }) + } + + override fun get(key: Key): T? { + return extras[key]?.let { it.value as T } + } + + override fun remove(key: Key): T? { + return extras.remove(key)?.let { it.value as T } + } + + override fun clear() { + extras.clear() + } + + internal companion object { + private const val serialVersionUID = 0L + } +} + +@Suppress("unchecked_cast") +internal class ImmutableExtrasImpl private constructor( + private val extras: Map, Entry<*>> +) : AbstractExtras(), Serializable { + constructor(extras: Iterable>) : this(extras.associateBy { it.key }) + + constructor(extras: Array>) : this(extras.associateBy { it.key }) + + override val keys: Set> = extras.keys + + override fun isEmpty(): Boolean = extras.isEmpty() + + override val size: Int = extras.size + + override val entries: Set> = extras.values.toSet() + + override fun get(key: Key): T? { + return extras[key]?.let { it.value as T } + } + + internal companion object { + private const val serialVersionUID = 0L + } + + /* Replace during serialization */ + private fun writeReplace(): Any = Surrogate(entries) + + private class Surrogate(private val entries: Set>) : Serializable { + fun readResolve(): Any = ImmutableExtrasImpl(entries) + + private companion object { + private const val serialVersionUID = 0L + } + } +} + +abstract class AbstractExtras : Extras { + + override val size: Int get() = keys.size + + override fun isEmpty(): Boolean = keys.isEmpty() + + override fun contains(key: Key<*>): Boolean = key in keys + + override fun contains(element: Entry<*>): Boolean = + entries.contains(element) + + override fun containsAll(elements: Collection>): Boolean = + entries.containsAll(elements) + + override fun iterator(): Iterator> = entries.iterator() + + override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other !is Extras) return false + if (other.entries != this.entries) return false + return true + } + + override fun hashCode(): Int { + return 31 * entries.hashCode() + } + + override fun toString(): String { + return "Extras($entries)" + } +} + +internal object EmptyExtras : AbstractExtras(), Serializable { + + override val size: Int = 0 + + override val keys: Set> = emptySet() + + override val entries: Set> = emptySet() + + override fun isEmpty(): Boolean = true + + override fun get(key: Key): T? = null + + override fun contains(key: Key<*>): Boolean = false + + override fun contains(element: Entry): Boolean = false + + @Suppress("unused") // Necessary for java.io.Serializable stability + private const val serialVersionUID = 0L + + /* Ensure single instance, even after deserialization */ + private fun readResolve(): Any = EmptyExtras +} diff --git a/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasUtils.kt b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasUtils.kt new file mode 100644 index 00000000000..6324691ac1a --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasUtils.kt @@ -0,0 +1,49 @@ +/* + * 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.tooling.core + +/** + * Creates a value based key for accessing any [Extras] container + * + * @param T The type of data that is stored in the extras container + * ```kotlin + * extrasKeyOf() == extrasKeyOf() + * extrasKeyOf() != extrasKeyOf() + * extrasKeyOf>() == extrasKeyOf>() + * extrasKeyOf>() != extrasKeyOf>() + * ``` + * + * @param name This typed keys can also be distinguished with an additional name. In this case + * ```kotlin + * extrasKeyOf() != extrasKeyOf("a") + * extrasKeyOf("a") == extrasKeyOf("a") + * extrasKeyOf("b") != extrasKeyOf("a") + * extrasKeyOf("a") != extrasKeyOf("a") + * ``` + */ +inline fun extrasKeyOf(name: String? = null): Extras.Key = + Extras.Key(ReifiedTypeSignature(), name) + +fun emptyExtras(): Extras = EmptyExtras + +fun extrasOf() = emptyExtras() + +fun extrasOf(vararg entries: Extras.Entry<*>): Extras = if (entries.isEmpty()) EmptyExtras else ImmutableExtrasImpl(entries) + +fun mutableExtrasOf(): MutableExtras = MutableExtrasImpl() + +fun mutableExtrasOf(vararg entries: Extras.Entry<*>): MutableExtras = MutableExtrasImpl(entries.toList()) + +fun Iterable>.toExtras(): Extras = ImmutableExtrasImpl(this) + +fun Iterable>.toMutableExtras(): MutableExtras = MutableExtrasImpl(this) + +infix fun Extras.Key.withValue(value: T): Extras.Entry = Extras.Entry(this, value) + +operator fun Extras.plus(entry: Extras.Entry<*>): Extras = ImmutableExtrasImpl(this.entries + entry) + +operator fun Extras.plus(entries: Iterable>): Extras = ImmutableExtrasImpl(this.entries + entries) + diff --git a/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ReifiedTypeSignature.kt b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ReifiedTypeSignature.kt new file mode 100644 index 00000000000..f676996f341 --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ReifiedTypeSignature.kt @@ -0,0 +1,85 @@ +/* + * 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. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.tooling.core + +import java.io.Serializable +import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.KVariance +import kotlin.reflect.typeOf + +@PublishedApi +internal inline fun ReifiedTypeSignature(): ReifiedTypeSignature { + @OptIn(UnsafeApi::class, ExperimentalStdlibApi::class) + return ReifiedTypeSignature(renderReifiedTypeSignatureString(typeOf())) +} + +@PublishedApi +internal class ReifiedTypeSignature +@UnsafeApi("Use 'reifiedTypeSignatureOf' instead") +@PublishedApi internal constructor(val signature: String) : Serializable { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ReifiedTypeSignature<*>) return false + if (other.signature != this.signature) return false + return true + } + + override fun hashCode(): Int { + return 31 * signature.hashCode() + } + + override fun toString(): String = signature + + internal companion object { + private const val serialVersionUID = 0L + } +} + +@PublishedApi +@UnsafeApi("Use 'ReifiedTypeSignature' instead") +internal fun renderReifiedTypeSignatureString(type: KType): String { + val classifier = type.classifier ?: throw IllegalArgumentException("Expected denotable type, found $type") + val classifierClass = classifier as? KClass<*> ?: throw IllegalArgumentException("Expected class type, found $type") + val classifierName = classifierClass.qualifiedName ?: throw IllegalArgumentException( + "Expected non-anonymous, non-local type, found $type" + ) + + /* Fast path: Just a non-nullable class without arguments */ + if (type.arguments.isEmpty() && !type.isMarkedNullable) { + return classifierName + } + + return buildString { + append(classifierName) + if (type.arguments.isNotEmpty()) { + append("<") + type.arguments.forEachIndexed forEach@{ index, argument -> + if (argument.type == null || argument.variance == null) { + append("*") + return@forEach + } + when (argument.variance) { + KVariance.IN -> append("in ") + KVariance.OUT -> append("out ") + else -> Unit + } + + append(renderReifiedTypeSignatureString(argument.type ?: return@forEach)) + if (index != type.arguments.lastIndex) { + append(", ") + } + } + append(">") + } + if (type.isMarkedNullable) { + append("?") + } + } +} diff --git a/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/UnsafeApi.kt b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/UnsafeApi.kt new file mode 100644 index 00000000000..1626cc602a8 --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/UnsafeApi.kt @@ -0,0 +1,9 @@ +/* + * 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.tooling.core + +@RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "Use safe API counterpart") +annotation class UnsafeApi(val message: String = "") diff --git a/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasIdFromStringTest.kt b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasIdFromStringTest.kt new file mode 100644 index 00000000000..e13049a23e4 --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasIdFromStringTest.kt @@ -0,0 +1,41 @@ +/* + * 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.tooling.core + +import kotlin.test.Test +import kotlin.test.assertEquals + +class ExtrasKeyStableStringTest { + @Test + fun `test - sample 0`() { + assertEquals("kotlin.String", extrasKeyOf().stableString) + } + + @Test + fun `test - sample 1`() { + assertEquals("kotlin.String;withName", extrasKeyOf("withName").stableString) + } + + @Test + fun `test - sample 2`() { + assertEquals(extrasKeyOf(), Extras.Key.fromString(extrasKeyOf().stableString)) + } + + @Test + fun `test - sample 3`() { + assertEquals(extrasKeyOf("withName"), Extras.Key.fromString(extrasKeyOf("withName").stableString)) + } + + @Test + fun `test - sample 4`() { + assertEquals( + extrasKeyOf>>("withName"), + Extras.Key.fromString( + extrasKeyOf>>("withName").stableString + ) + ) + } +} diff --git a/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasSerializableTest.kt b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasSerializableTest.kt new file mode 100644 index 00000000000..e7d1e0ce0ed --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasSerializableTest.kt @@ -0,0 +1,55 @@ +/* + * 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.tooling.core + +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.ObjectInputStream +import java.io.ObjectOutputStream +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotSame +import kotlin.test.assertSame + +class ExtrasSerializableTest { + + @Test + fun `test - serialize deserialize - MutableExtras`() { + val extras = mutableExtrasOf(extrasKeyOf() withValue "hello") + val deserializedExtras = extras.serialize().deserialize() + assertNotSame(extras, deserializedExtras) + assertEquals(extras, deserializedExtras) + } + + @Test + fun `test - serialize deserialize - EmptyExtras`() { + assertSame(emptyExtras(), emptyExtras()) + assertSame(emptyExtras(), emptyExtras().serialize().deserialize()) + } + + @Test + fun `test - serialize deserialize - ImmutableExtras`() { + val extras = extrasOf(extrasKeyOf() withValue "hello") + val deserializedExtras = extras.serialize().deserialize() + assertNotSame(extras, deserializedExtras) + assertEquals(extras, deserializedExtras) + assertSame(extras.javaClass, deserializedExtras.javaClass) + } + + private fun Any.serialize(): ByteArray { + return ByteArrayOutputStream().use { byteArrayOutputStream -> + ObjectOutputStream(byteArrayOutputStream).use { objectOutputStream -> objectOutputStream.writeObject(this) } + byteArrayOutputStream.toByteArray() + } + } + + private inline fun ByteArray.deserialize(): T { + return ObjectInputStream(ByteArrayInputStream(this)).use { stream -> + stream.readObject() as T + } + } +} + diff --git a/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasTest.kt b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasTest.kt new file mode 100644 index 00000000000..1e8e0eca05a --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasTest.kt @@ -0,0 +1,289 @@ +@file:Suppress("RemoveExplicitTypeArguments") + +package org.jetbrains.kotlin.tooling.core + +import kotlin.test.* + +class ExtrasTest { + + @Test + fun `test - isEmpty`() { + assertTrue(mutableExtrasOf().isEmpty()) + assertTrue(extrasOf().isEmpty()) + assertTrue(emptyExtras().isEmpty()) + + assertFalse(mutableExtrasOf().isNotEmpty()) + assertFalse(extrasOf().isNotEmpty()) + assertFalse(emptyExtras().isNotEmpty()) + } + + @Test + fun `test - add and get`() { + val extras = mutableExtrasOf() + assertNull(extras[extrasKeyOf()]) + assertNull(extras[extrasKeyOf("a")]) + assertNull(extras[extrasKeyOf("b")]) + + extras[extrasKeyOf()] = "22222" + assertEquals("22222", extras[extrasKeyOf()]) + assertNull(extras[extrasKeyOf("a")]) + assertNull(extras[extrasKeyOf("b")]) + + extras[extrasKeyOf("a")] = "value a" + assertEquals("22222", extras[extrasKeyOf()]) + assertEquals("value a", extras[extrasKeyOf("a")]) + assertNull(extras[extrasKeyOf("b")]) + + extras[extrasKeyOf("b")] = "value b" + assertEquals("22222", extras[extrasKeyOf()]) + assertEquals("value a", extras[extrasKeyOf("a")]) + assertEquals("value b", extras[extrasKeyOf("b")]) + + assertNull(extras[extrasKeyOf("c")]) + } + + @Test + fun `test - ids`() { + val stringKey = extrasKeyOf() + val stringKeyA = extrasKeyOf("a") + val intKey = extrasKeyOf() + val intKeyA = extrasKeyOf("a") + + val keys = setOf(stringKey, stringKeyA, intKey, intKeyA) + + val extras = extrasOf( + stringKey withValue "string", + stringKeyA withValue "stringA", + intKey withValue 1, + intKeyA withValue 2 + ) + + val mutableExtras = mutableExtrasOf( + stringKey withValue "string", + stringKeyA withValue "stringA", + intKey withValue 1, + intKeyA withValue 2 + ) + + assertEquals(keys, extras.keys) + assertEquals(keys, mutableExtras.keys) + } + + @Test + fun `test - keys`() { + val stringKey = extrasKeyOf() + val stringKeyA = extrasKeyOf("a") + val intKey = extrasKeyOf() + val intKeyA = extrasKeyOf("a") + + val keys = listOf(stringKey, stringKeyA, intKey, intKeyA) + + val extras = extrasOf( + stringKey withValue "string", + stringKeyA withValue "stringA", + intKey withValue 1, + intKeyA withValue 2 + ) + + val mutableExtras = mutableExtrasOf( + stringKey withValue "string", + stringKeyA withValue "stringA", + intKey withValue 1, + intKeyA withValue 2 + ) + + assertEquals(keys, extras.entries.map { it.key }) + assertEquals(keys, mutableExtras.entries.map { it.key }) + } + + @Test + fun `test - equality`() { + val stringKey = extrasKeyOf() + val stringKeyA = extrasKeyOf("a") + val intKey = extrasKeyOf() + val intKeyA = extrasKeyOf("a") + + val extras = extrasOf( + stringKey withValue "string", + stringKeyA withValue "stringA", + intKey withValue 1, + intKeyA withValue 2 + ) + + val mutableExtras = mutableExtrasOf( + stringKey withValue "string", + stringKeyA withValue "stringA", + intKey withValue 1, + intKeyA withValue 2 + ) + + assertEquals(extras, mutableExtras) + assertEquals(extras, mutableExtras.toExtras()) + assertEquals(extras.toExtras(), mutableExtras.toExtras()) + assertEquals(extras.toMutableExtras(), mutableExtras.toExtras()) + assertEquals(extras.toMutableExtras(), mutableExtras) + assertEquals(mutableExtras, extras) + + assertNotEquals(extras, extras + extrasKeyOf("b").withValue(2)) + assertNotEquals(mutableExtras, mutableExtras + extrasKeyOf("b").withValue(2)) + } + + @Test + fun `test - equality - empty`() { + val extras0 = extrasOf() + val extras1 = mutableExtrasOf() + val extras2 = mutableExtrasOf() + + assertNotSame(extras0, extras1) + assertNotSame(extras1, extras2) + assertEquals(extras0, extras1) + assertEquals(extras1, extras2) + assertEquals(extras2, extras1) + assertEquals(emptyExtras(), extras0) + assertEquals(emptyExtras(), extras1) + assertEquals(extras0, emptyExtras()) + assertEquals(extras1, emptyExtras()) + assertEquals(emptyExtras(), extras2) + assertEquals(extras2, emptyExtras()) + + assertEquals(extras0.hashCode(), extras1.hashCode()) + assertEquals(extras1.hashCode(), extras2.hashCode()) + assertEquals(extras1.hashCode(), emptyExtras().hashCode()) + } + + @Test + fun `test - overwrite - mutable`() { + val key = extrasKeyOf() + val extras = mutableExtrasOf() + assertNull(extras.set(key, 1)) + assertEquals(1, extras[key]) + assertEquals(1, extras.set(key, 2)) + assertEquals(2, extras[key]) + } + + @Test + fun `test - overwrite - immutable`() { + val key = extrasKeyOf() + val extras0 = extrasOf() + val extras1 = extras0 + (key withValue 1) + assertNull(extras0[key]) + assertEquals(1, extras1[key]) + + val extras2 = extras1 + (key withValue 2) + assertNull(extras0[key]) + assertEquals(1, extras1[key]) + assertEquals(2, extras2[key]) + } + + @Test + fun `test - key equality`() { + assertEquals(extrasKeyOf(), extrasKeyOf()) + assertEquals(extrasKeyOf>(), extrasKeyOf>()) + assertEquals(extrasKeyOf("a"), extrasKeyOf("a")) + assertNotEquals>(extrasKeyOf(), extrasKeyOf()) + assertNotEquals>(extrasKeyOf("a"), extrasKeyOf()) + assertNotEquals>(extrasKeyOf("a"), extrasKeyOf("b")) + } + + @Test + fun `test - add two extras`() { + val keyA = extrasKeyOf("a") + val keyB = extrasKeyOf("b") + val keyC = extrasKeyOf("c") + val keyD = extrasKeyOf() + val keyE = extrasKeyOf() + + val extras1 = extrasOf( + keyA withValue 0, + keyB withValue 1, + keyC withValue 2, + keyD withValue 3 + ) + + val extras2 = extrasOf( + keyC withValue 4, + keyE withValue 5 + ) + + val combinedExtras = extras1 + extras2 + + assertEquals( + extrasOf( + keyA withValue 0, + keyB withValue 1, + keyC withValue 4, + keyE withValue 5 + ), + combinedExtras + ) + } + + @Test + fun `test - mutable extras - remove`() { + val extras = mutableExtrasOf( + extrasKeyOf() withValue "2", + extrasKeyOf("other") withValue "4", + extrasKeyOf() withValue 1, + extrasKeyOf("cash") withValue 1 + ) + + extras.remove(extrasKeyOf("sunny")) + assertEquals(1, extras[extrasKeyOf("cash")]) + + assertEquals(1, extras.remove(extrasKeyOf("cash"))) + assertNull(extras[extrasKeyOf("cash")]) + + assertEquals(1, extras.remove(extrasKeyOf())) + assertNull(extras[extrasKeyOf()]) + + assertEquals("4", extras.remove(extrasKeyOf("other"))) + + assertNull(extras[extrasKeyOf("other")]) + + assertEquals( + extrasOf(extrasKeyOf() withValue "2"), + extras.toExtras() + ) + } + + @Test + fun `test - mutable extras - clear`() { + val extras = mutableExtrasOf( + extrasKeyOf() withValue "2", + extrasKeyOf("other") withValue "4", + extrasKeyOf() withValue 1, + extrasKeyOf("cash") withValue 1 + ) + + assertFalse(extras.isEmpty(), "Expected non-empty extras") + extras.clear() + assertTrue(extras.isEmpty(), "Expected extras to be empty") + + assertEquals(emptyExtras(), extras) + assertEquals(emptyExtras(), extras.toExtras()) + } + + @Test + fun `test mutable extras - putAll`() { + val extras = mutableExtrasOf( + extrasKeyOf() withValue "1", + extrasKeyOf("overwrite") withValue "2" + ) + + extras.putAll( + extrasOf( + extrasKeyOf("overwrite") withValue "3", + extrasKeyOf() withValue 1 + ) + ) + + assertEquals( + extrasOf( + extrasKeyOf() withValue "1", + extrasKeyOf("overwrite") withValue "3", + extrasKeyOf() withValue 1 + ), + extras + ) + } +} diff --git a/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ReifiedTypeSignatureTest.kt b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ReifiedTypeSignatureTest.kt new file mode 100644 index 00000000000..86a8f202520 --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ReifiedTypeSignatureTest.kt @@ -0,0 +1,105 @@ +/* + * 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.tooling.core + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +class ReifiedTypeSignatureTest { + + class Box + + class Outer { + inner class Inner + } + + open class Recursive> + + class AnyRecursive : Recursive() + + @Test + fun `test - sample 0`() { + assertEquals( + "kotlin.collections.List", ReifiedTypeSignature>().signature + ) + } + + @Test + fun `test - sample 1`() { + assertEquals( + "kotlin.collections.List<*>", ReifiedTypeSignature>().signature + ) + } + + @Test + fun `test - sample 2`() { + assertEquals( + "kotlin.String", ReifiedTypeSignature().signature + ) + } + + @Test + fun `test - sample 3`() { + assertEquals( + "kotlin.collections.Map>>", + ReifiedTypeSignature>>>().signature + ) + } + + + @Test + fun `test - sample 5 - inner class`() { + assertEquals( + "org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.Outer.Inner", + ReifiedTypeSignature().signature + ) + } + + @Test + fun `test - sample 6 - recursive types`() { + assertEquals( + "org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.Recursive<*>", + ReifiedTypeSignature>().signature + ) + + assertEquals( + "org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.AnyRecursive", + ReifiedTypeSignature().signature + ) + + assertEquals( + "org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest" + + ".Recursive", + ReifiedTypeSignature>().signature + ) + } + + @Test + fun `test sample 7 - nullable`() { + assertEquals("kotlin.collections.List<*>?", ReifiedTypeSignature?>().signature) + assertEquals("kotlin.collections.List", ReifiedTypeSignature>().signature) + } + + @Test + fun `test sample 8 - variance`() { + assertEquals( + "org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.Box", + ReifiedTypeSignature>().signature + ) + assertEquals( + "org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.Box", + ReifiedTypeSignature>().signature + ) + } + + @Suppress("RemoveExplicitTypeArguments", "TYPE_INFERENCE_ONLY_INPUT_TYPES_WARNING") + @Test + fun `test - equals`() { + assertEquals(ReifiedTypeSignature>(), ReifiedTypeSignature>()) + assertNotEquals(ReifiedTypeSignature>(), ReifiedTypeSignature>()) + } +}