[Platform API] Introduce fundamental abstraction of Platform
This is a large commit, which introduces general API for working with
abstraction of Platform.
- Add new abstraction to 'core' - SimplePlatform - which represents
exactly one platform
- Clients are strongly prohibited to create instances of SimplePlatform
by hand, instead, corresponding *Platforms abstraction should be used
(e.g. JvmPlatforms, JsPlatforms, KonanPlatforms)
- Move TargetPlatform to 'core', it represents now a collection of
SimplePlatforms
- Clients are strongly encouraged to use TargetPlatform
(not SimplePlatform) in API, to enforce checks for multiplatform
- Provide a helper-extensions to work with TargetPlatform
(in particular, for getting a specific component platform)
- Remove MultiTargetPlatform in favour of TargetPlatform
- Notably, this commit leaves another widely used duplicated abstraction,
namely, IdePlatform. For the sake sanity, removal of IdePlatform is
extracted in the separate commit.
This commit is contained in:
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.descriptors
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
|
||||
interface ModuleDescriptor : DeclarationDescriptor {
|
||||
override fun getContainingDeclaration(): DeclarationDescriptor? = null
|
||||
@@ -30,6 +31,10 @@ interface ModuleDescriptor : DeclarationDescriptor {
|
||||
*/
|
||||
val stableName: Name?
|
||||
|
||||
// NB: this field should actually be non-null, but making it so implies a LOT of work, so we postpone it for a moment
|
||||
// TODO: make it non-null
|
||||
val platform: TargetPlatform?
|
||||
|
||||
fun shouldSeeInternalsOf(targetModule: ModuleDescriptor): Boolean
|
||||
|
||||
override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R {
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.MultiTargetPlatform
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import java.lang.IllegalArgumentException
|
||||
@@ -34,8 +34,8 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
|
||||
private val storageManager: StorageManager,
|
||||
override val builtIns: KotlinBuiltIns,
|
||||
// May be null in compiler context, should be not-null in IDE context
|
||||
multiTargetPlatform: MultiTargetPlatform? = null,
|
||||
capabilities: Map<ModuleDescriptor.Capability<*>, Any?> = emptyMap(),
|
||||
override val platform: TargetPlatform? = null,
|
||||
private val capabilities: Map<ModuleDescriptor.Capability<*>, Any?> = emptyMap(),
|
||||
override val stableName: Name? = null
|
||||
) : DeclarationDescriptorImpl(Annotations.EMPTY, moduleName), ModuleDescriptor {
|
||||
init {
|
||||
@@ -44,8 +44,6 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private val capabilities = capabilities + (multiTargetPlatform?.let { mapOf(MultiTargetPlatform.CAPABILITY to it) } ?: emptyMap())
|
||||
|
||||
private var dependencies: ModuleDependencies? = null
|
||||
private var packageFragmentProviderForModuleContent: PackageFragmentProvider? = null
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.platform
|
||||
|
||||
/**
|
||||
* Core abstraction of Platform API, represents a collection of platforms.
|
||||
*
|
||||
* This is the primarily abstraction intended to use in the most part of API, as, usually,
|
||||
* pretty much anything that may have a platform, may have a several platforms as well in the
|
||||
* context of multiplatform projects.
|
||||
*
|
||||
* Please, use it over the [SimplePlatform] unless you're absolutely sure what you're doing.
|
||||
*
|
||||
* NB. Even in cases, where some part of logic makes sense only for a particular platform (e.g., JVM),
|
||||
* it still can be applicable for [TargetPlatform]s with [componentPlatforms] > 1, e.g. when it consists
|
||||
* of two version of JDK, JDK and Android, several versions of Android API, etc.
|
||||
*/
|
||||
data class TargetPlatform(val componentPlatforms: Set<SimplePlatform>) : Collection<SimplePlatform> by componentPlatforms {
|
||||
init {
|
||||
if (componentPlatforms.isEmpty()) throw IllegalArgumentException("Don't instantiate TargetPlatform with empty set of platforms")
|
||||
}
|
||||
|
||||
override fun toString(): String = presentableDescription
|
||||
}
|
||||
|
||||
/**
|
||||
* Core abstraction of Platform API, represents exactly one platform.
|
||||
*
|
||||
* API guarantees:
|
||||
*
|
||||
* - direct inheritors are well-known and represent three major platforms supported at the moment (JVM, JS, Native)
|
||||
*
|
||||
* - exact enumeration of all inheritors isn't available at the compile time, see [CommonPlatforms]
|
||||
*
|
||||
* - each implementation should support equality in a broad sense of "absolutely the same platform"
|
||||
*
|
||||
* - it is _prohibited_ to create instances of [SimplePlatform] in the client's code, use respective factory instance (e.g., [JvmPlatforms])
|
||||
* to get instances of platforms
|
||||
*
|
||||
* Ideally, each specific subtype should be either a data class or singleton.
|
||||
*/
|
||||
abstract class SimplePlatform(val platformName: String) {
|
||||
override fun toString(): String = platformName
|
||||
|
||||
/** See KDoc for [TargetPlatform.oldFashionedDescription] */
|
||||
abstract val oldFashionedDescription: String
|
||||
|
||||
// FIXME(dsavvinov): hack to allow injection inject JvmTarget into container.
|
||||
// Proper fix would be to rewrite clients to get JdkPlatform from container, and pull JvmTarget from it
|
||||
// (this will also remove need in TargetPlatformVersion as the whole, and, in particular, ugly passing
|
||||
// of TargetPlatformVersion.NoVersion in non-JVM code)
|
||||
open val targetPlatformVersion: TargetPlatformVersion = TargetPlatformVersion.NoVersion
|
||||
}
|
||||
|
||||
interface TargetPlatformVersion {
|
||||
val description: String
|
||||
|
||||
object NoVersion : TargetPlatformVersion {
|
||||
override val description = ""
|
||||
}
|
||||
}
|
||||
|
||||
fun TargetPlatform?.isCommon(): Boolean = this != null && this.size > 1
|
||||
|
||||
fun SimplePlatform.toTargetPlatform(): TargetPlatform = TargetPlatform(setOf(this))
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.platform
|
||||
|
||||
inline fun <reified T : SimplePlatform> TargetPlatform.subplatformOfType(): T? = componentPlatforms.filterIsInstance<T>().singleOrNull()
|
||||
fun <T> TargetPlatform.subplatformOfType(klass: Class<T>): T? = componentPlatforms.filterIsInstance(klass).singleOrNull()
|
||||
|
||||
inline fun <reified T : SimplePlatform> TargetPlatform?.has(): Boolean = this != null && subplatformOfType<T>() != null
|
||||
fun <T> TargetPlatform?.has(klass: Class<T>): Boolean = this != null && subplatformOfType(klass) != null
|
||||
|
||||
|
||||
/**
|
||||
* Returns human-readable description, mapping multiplatform to 'Common (experimental)',
|
||||
* as well as maintaining some quirks of the previous representation, like trailing whitespaces
|
||||
*
|
||||
* It is needed mainly for backwards compatibility, because some subsystem actually
|
||||
* managed to rely on the format of that string. In particular, 'facetSerialization.kt' uses
|
||||
* those string as keys in serialized `.iml`-file, and changing format of that string (including
|
||||
* trimming pointless whitespaces) leads to incorrectly deserialized facets.
|
||||
*
|
||||
* New clients are encouraged to use [presentableDescription] description instead, as it
|
||||
* also provides better description for multiplatforms.
|
||||
*/
|
||||
val TargetPlatform.oldFashionedDescription: String
|
||||
get() = if (this.isCommon()) "Common (experimental) " else this.single().oldFashionedDescription
|
||||
|
||||
|
||||
/**
|
||||
* Renders multiplatform in form
|
||||
* '$PLATFORM_1 / $PLATFORM_2 / ...'
|
||||
* e.g.
|
||||
* 'JVM (1.8) / JS / Native'
|
||||
*/
|
||||
val TargetPlatform.presentableDescription: String
|
||||
get() = componentPlatforms.joinToString(separator = "/")
|
||||
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils.getContainingClass
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||
import org.jetbrains.kotlin.resolve.constants.EnumValue
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
@@ -57,6 +58,9 @@ val DeclarationDescriptor.isExtension: Boolean
|
||||
val DeclarationDescriptor.module: ModuleDescriptor
|
||||
get() = DescriptorUtils.getContainingModule(this)
|
||||
|
||||
val DeclarationDescriptor.platform: TargetPlatform?
|
||||
get() = module.platform
|
||||
|
||||
fun ModuleDescriptor.resolveTopLevelClass(topLevelClassFqName: FqName, location: LookupLocation): ClassDescriptor? {
|
||||
assert(!topLevelClassFqName.isRoot)
|
||||
return getPackage(topLevelClassFqName.parent()).memberScope.getContributedClassifier(
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.MemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
|
||||
sealed class MultiTargetPlatform : Comparable<MultiTargetPlatform> {
|
||||
object Common : MultiTargetPlatform() {
|
||||
override fun compareTo(other: MultiTargetPlatform): Int =
|
||||
if (other is Common) 0 else -1
|
||||
}
|
||||
|
||||
data class Specific(val platform: String) : MultiTargetPlatform() {
|
||||
override fun compareTo(other: MultiTargetPlatform): Int =
|
||||
when (other) {
|
||||
is Common -> 1
|
||||
is Specific -> platform.compareTo(other.platform)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CAPABILITY = ModuleDescriptor.Capability<MultiTargetPlatform>("MULTI_TARGET_PLATFORM")
|
||||
}
|
||||
}
|
||||
|
||||
fun ModuleDescriptor.getMultiTargetPlatform(): MultiTargetPlatform? =
|
||||
module.getCapability(MultiTargetPlatform.CAPABILITY)
|
||||
|
||||
fun MemberDescriptor.getMultiTargetPlatform(): String? =
|
||||
(module.getMultiTargetPlatform() as? MultiTargetPlatform.Specific)?.platform
|
||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl;
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
@@ -80,6 +81,12 @@ public class ErrorUtils {
|
||||
return Name.special("<ERROR MODULE>");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TargetPlatform getPlatform() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PackageViewDescriptor getPackage(@NotNull FqName fqName) {
|
||||
|
||||
Reference in New Issue
Block a user