From fee6a752e082a849e724ac9cc414306d159e92f6 Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Mon, 6 Apr 2020 22:13:47 +0700 Subject: [PATCH] HMPP: Fix serialization of TargetPlatform in Kotlin facet --- .../kotlin/platform/konan/NativePlatform.kt | 20 ++++--- .../kotlin/platform/TargetPlatform.kt | 11 +++- .../jetbrains/kotlin/platform/platformUtil.kt | 10 ++-- .../kotlin/config/facetSerialization.kt | 55 ++++++++++++++----- 4 files changed, 68 insertions(+), 28 deletions(-) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/platform/konan/NativePlatform.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/platform/konan/NativePlatform.kt index 468c783e7aee0..87460aefdc74e 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/platform/konan/NativePlatform.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/platform/konan/NativePlatform.kt @@ -10,20 +10,21 @@ import org.jetbrains.kotlin.platform.SimplePlatform import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.toTargetPlatform -sealed class NativePlatform : SimplePlatform("Native") +sealed class NativePlatform : SimplePlatform("Native") { + override val oldFashionedDescription: String + get() = toString() + " " +} object NativePlatformUnspecifiedTarget : NativePlatform() { - override fun toString() = "$platformName (general)" - - override val oldFashionedDescription: String - get() = "Native (general) " + override val targetName: String + get() = "general" } data class NativePlatformWithTarget(val target: KonanTarget) : NativePlatform() { - override fun toString() = "$platformName ($target)" + override fun toString() = super.toString() // override the method generated for data class - override val oldFashionedDescription: String - get() = "Native ($target) " + override val targetName: String + get() = target.visibleName } @Suppress("DEPRECATION_ERROR") @@ -64,3 +65,6 @@ object NativePlatforms { } fun TargetPlatform?.isNative(): Boolean = this?.isNotEmpty() == true && all { it is NativePlatform } + +private val legacyNativePlatformUnspecifiedTargetSerializedRepresentation = "${NativePlatformUnspecifiedTarget.platformName} []" +fun NativePlatformUnspecifiedTarget.legacySerializeToString(): String = legacyNativePlatformUnspecifiedTargetSerializedRepresentation diff --git a/core/descriptors/src/org/jetbrains/kotlin/platform/TargetPlatform.kt b/core/descriptors/src/org/jetbrains/kotlin/platform/TargetPlatform.kt index b2452f7e30052..01c33692dc6aa 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/platform/TargetPlatform.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/platform/TargetPlatform.kt @@ -57,7 +57,14 @@ open class TargetPlatform(val componentPlatforms: Set) : Collect * Ideally, each specific subtype should be either a data class or singleton. */ abstract class SimplePlatform(val platformName: String) { - override fun toString(): String = platformName + override fun toString(): String { + val targetName = targetName + return if (targetName.isNotEmpty()) "$platformName ($targetName)" else platformName + } + + // description of TargetPlatformVersion or name of custom platform-specific target; used in serialization + open val targetName: String + get() = targetPlatformVersion.description /** See KDoc for [TargetPlatform.oldFashionedDescription] */ abstract val oldFashionedDescription: String @@ -87,4 +94,4 @@ fun TargetPlatform?.isCommon(): Boolean = this != null && size > 1 && iterator() fun SimplePlatform.toTargetPlatform(): TargetPlatform = TargetPlatform(setOf(this)) -fun SimplePlatform.serializeToString() = "${this.platformName} [${this.targetPlatformVersion.description}]" +fun SimplePlatform.serializeToString(): String = "$platformName [$targetName]" diff --git a/core/descriptors/src/org/jetbrains/kotlin/platform/platformUtil.kt b/core/descriptors/src/org/jetbrains/kotlin/platform/platformUtil.kt index 8ae06fb045a27..b9ad7f34f0f02 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/platform/platformUtil.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/platform/platformUtil.kt @@ -25,16 +25,16 @@ fun TargetPlatform?.has(klass: Class): Boolean = this != null && subplatf * also provides better description for multiplatforms. */ val TargetPlatform.oldFashionedDescription: String - // the invocation if isCommon is not preferable as it could be governed by separate flag and may - // to appear independently on count of target platforms + // this method mistakenly detects "common native" platform as "Common (experimental)" + // though this does not seem to have any significant effect get() = this.singleOrNull()?.oldFashionedDescription ?: "Common (experimental) " /** * Renders multiplatform in form - * '$PLATFORM_1 / $PLATFORM_2 / ...' + * '$PLATFORM_1/$PLATFORM_2/...' * e.g. - * 'JVM (1.8) / JS / Native' + * 'JVM (1.8)/JS/Native (ios_x64)' */ val TargetPlatform.presentableDescription: String - get() = componentPlatforms.joinToString(separator = "/") \ No newline at end of file + get() = componentPlatforms.joinToString(separator = "/") diff --git a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/facetSerialization.kt b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/facetSerialization.kt index 5d751b901eb88..8489487d4b62c 100644 --- a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/facetSerialization.kt +++ b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/facetSerialization.kt @@ -21,6 +21,9 @@ import org.jetbrains.kotlin.platform.js.JsPlatform import org.jetbrains.kotlin.platform.jvm.JdkPlatform import org.jetbrains.kotlin.platform.jvm.JvmPlatform import org.jetbrains.kotlin.platform.konan.NativePlatform +import org.jetbrains.kotlin.platform.konan.NativePlatformUnspecifiedTarget +import org.jetbrains.kotlin.platform.konan.NativePlatforms +import org.jetbrains.kotlin.platform.konan.legacySerializeToString import java.lang.reflect.Modifier import kotlin.reflect.KClass import kotlin.reflect.full.superclasses @@ -104,19 +107,40 @@ private fun readV1Config(element: Element): KotlinFacetSettings { } } +// TODO: Introduce new version of facet serialization. See https://youtrack.jetbrains.com/issue/KT-38235 +// This is necessary to avoid having too much custom logic for platform serialization. fun Element.getFacetPlatformByConfigurationElement(): TargetPlatform { val platformNames = getAttributeValue("allPlatforms")?.split('/')?.toSet() - if (platformNames != null) { - return TargetPlatform(CommonPlatforms.allSimplePlatforms - .flatMap { it.componentPlatforms } - .filter { platformNames.contains(it.serializeToString()) } - .toSet()) + if (platformNames != null && platformNames.isNotEmpty()) { + val knownSimplePlatforms = HashMap() // serialization presentation to simple platform + + // first, collect serialization presentations for every known simple platform + CommonPlatforms.allSimplePlatforms + .flatMap { it.componentPlatforms } + .forEach { knownSimplePlatforms[it.serializeToString()] = it } + + // next, add legacy aliases for some of the simple platforms; ex: unspecifiedNativePlatform + NativePlatformUnspecifiedTarget.let { knownSimplePlatforms[it.legacySerializeToString()] = it } + + val simplePlatforms = platformNames.mapNotNull(knownSimplePlatforms::get) + if (simplePlatforms.isNotEmpty()) return TargetPlatform(simplePlatforms.toSet()) + + // empty set of simple platforms is not allowed, in such case fallback to legacy algorithm } - // failed to read list of all platforms. Fallback to legacy algorythm - val platformName = getAttributeValue("platform") - // this code could be simplified using union after fixing the equals method in SimplePlatform - val allPlatforms = ArrayList(CommonPlatforms.allSimplePlatforms).also { it.add(CommonPlatforms.defaultCommonPlatform) } - return allPlatforms.firstOrNull { it.oldFashionedDescription == platformName }.orDefault() + + // failed to read list of all platforms. Fallback to legacy algorithm + val platformName = getAttributeValue("platform") as String + + return CommonPlatforms.allSimplePlatforms.firstOrNull { + // first, look for exact match through all simple platforms + it.oldFashionedDescription == platformName + } ?: CommonPlatforms.defaultCommonPlatform.takeIf { + // then, check exact match for the default common platform + it.oldFashionedDescription == platformName + } ?: NativePlatforms.unspecifiedNativePlatform.takeIf { + // if none of the above succeeded, check if it's an old-style record about native platform (without suffix with target name) + it.oldFashionedDescription.startsWith(platformName) + }.orDefault() // finally, fallback to the default platform } private fun readV2AndLaterConfig(element: Element): KotlinFacetSettings { @@ -298,9 +322,14 @@ private fun buildChildElement(element: Element, tag: String, bean: Any, filter: private fun KotlinFacetSettings.writeLatestConfig(element: Element) { val filter = SkipDefaultsSerializationFilter() - targetPlatform?.let { - element.setAttribute("platform", it.oldFashionedDescription) - element.setAttribute("allPlatforms", it.componentPlatforms.map { it.serializeToString() }.sorted().joinToString(separator = "/")) + // TODO: Introduce new version of facet serialization. See https://youtrack.jetbrains.com/issue/KT-38235 + // This is necessary to avoid having too much custom logic for platform serialization. + targetPlatform?.let { targetPlatform -> + element.setAttribute("platform", targetPlatform.oldFashionedDescription) + element.setAttribute( + "allPlatforms", + targetPlatform.componentPlatforms.map { it.serializeToString() }.sorted().joinToString(separator = "/") + ) } if (!useProjectSettings) { element.setAttribute("useProjectSettings", useProjectSettings.toString())