[Injection] Introduce PlatformSpecificExtensions and PlatformExtensionsClashResolver

This commit introduces the ability to register a PlatformExtensionClashResolver
in a container. Each PlatformExtensionClashResolver has a corresponding
PlatformSpecificExtensions.

If, during container composition, several instances of
PlatformSpecificExtensions were registred, instead of throwing
InvalidCardinalityException, corresponding PlatformExtensionClashResolver
will be asked to resolve clash.

This allows to make injection more composable and less coupled across
different contributors of service, providing a basis for such motivating
cases as composing containers with both JS and JVM services (for analysis
of multiplatform modules).
Previously, that would be impossible:
a) JS would inject default instances for some services which would clash
with non-default JVM services (like SyntheticScopes)
b) Also, there are a very few services for which *both* platforms provide
non-default implementations, so they should be merged manually on
case-by-case basis (e.g., IdentifierChecker)
This commit is contained in:
Dmitry Savvinov
2019-03-13 17:29:50 +03:00
parent b631e89ea7
commit 72e28d37fc
16 changed files with 209 additions and 26 deletions
@@ -102,6 +102,11 @@ class StorageComponentContainer(
return this return this
} }
internal fun registerClashResolvers(resolvers: List<PlatformExtensionsClashResolver<*>>): StorageComponentContainer {
componentStorage.registerClashResolvers(resolvers)
return this
}
override fun <T> create(request: Class<T>): T { override fun <T> create(request: Class<T>): T {
val constructorBinding = request.bindToConstructor(unknownContext) val constructorBinding = request.bindToConstructor(unknownContext)
val args = constructorBinding.argumentDescriptors.map { it.getValue() }.toTypedArray() val args = constructorBinding.argumentDescriptors.map { it.getValue() }.toTypedArray()
@@ -50,6 +50,10 @@ fun StorageComponentContainer.useInstanceIfNotNull(instance: Any?) {
if (instance != null) registerInstance(instance) if (instance != null) registerInstance(instance)
} }
fun StorageComponentContainer.useClashResolver(clashResolver: PlatformExtensionsClashResolver<*>) {
registerClashResolvers(listOf(clashResolver))
}
inline operator fun <reified T : Any> ComponentProvider.getValue(thisRef: Any?, desc: KProperty<*>): T { inline operator fun <reified T : Any> ComponentProvider.getValue(thisRef: Any?, desc: KProperty<*>): T {
return getService(T::class.java) return getService(T::class.java)
} }
@@ -70,4 +70,20 @@ internal class ComponentRegistry {
} }
registrationMap += other.registrationMap registrationMap += other.registrationMap
} }
fun resolveClashesIfAny(container: ComponentContainer, clashResolvers: List<PlatformExtensionsClashResolver<*>>) {
/*
The idea is to create descriptor, which is very similar to other SingletonDescriptor, but instead of calling
constructor we call 'resolveExtensionsClash' with values of clashed components as arguments.
By mimicking the usual descriptors we get lazy evaluation and consistency checks for free.
*/
for (resolver in clashResolvers) {
val clashedComponents = registrationMap[resolver.applicableTo] as? Collection<ComponentDescriptor> ?: continue
if (clashedComponents.size <= 1) continue
val substituteDescriptor = ClashResolutionDescriptor(container, resolver, clashedComponents.toList())
registrationMap[resolver.applicableTo] = substituteDescriptor
}
}
} }
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.container package org.jetbrains.kotlin.container
import java.io.Closeable import java.io.Closeable
import java.lang.IllegalStateException
import java.lang.reflect.Type import java.lang.reflect.Type
import java.util.* import java.util.*
@@ -144,6 +145,29 @@ open class SingletonTypeComponentDescriptor(container: ComponentContainer, val k
override fun toString(): String = "Singleton: ${klass.simpleName}" override fun toString(): String = "Singleton: ${klass.simpleName}"
} }
internal class ClashResolutionDescriptor<E : PlatformSpecificExtension<E>>(
container: ComponentContainer,
private val resolver: PlatformExtensionsClashResolver<E>,
private val clashedComponents: List<ComponentDescriptor>
) : SingletonDescriptor(container) {
override fun createInstance(context: ValueResolveContext): Any {
state = ComponentState.Initializing
val extensions = computeArguments(clashedComponents) as List<E>
val resolution = resolver.resolveExtensionsClash(extensions)
state = ComponentState.Initialized
return resolution
}
override fun getRegistrations(): Iterable<Type> {
throw IllegalStateException("Shouldn't be called")
}
override fun getDependencies(context: ValueResolveContext): Collection<Type> {
throw IllegalStateException("Shouldn't be called")
}
}
class ImplicitSingletonTypeComponentDescriptor(container: ComponentContainer, klass: Class<*>) : SingletonTypeComponentDescriptor(container, klass) { class ImplicitSingletonTypeComponentDescriptor(container: ComponentContainer, klass: Class<*>) : SingletonTypeComponentDescriptor(container, klass) {
override fun toString(): String { override fun toString(): String {
return "Implicit: ${klass.simpleName}" return "Implicit: ${klass.simpleName}"
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.container
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import java.io.Closeable import java.io.Closeable
import java.io.PrintStream import java.io.PrintStream
import java.lang.IllegalStateException
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import java.lang.reflect.ParameterizedType import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type import java.lang.reflect.Type
@@ -37,13 +38,19 @@ internal class InvalidCardinalityException(message: String) : Exception(message)
class ComponentStorage(private val myId: String, parent: ComponentStorage?) : ValueResolver { class ComponentStorage(private val myId: String, parent: ComponentStorage?) : ValueResolver {
var state = ComponentStorageState.Initial var state = ComponentStorageState.Initial
private val registry = ComponentRegistry()
init {
parent?.let { registry.addAll(it.registry) }
}
private val descriptors = LinkedHashSet<ComponentDescriptor>() private val descriptors = LinkedHashSet<ComponentDescriptor>()
private val dependencies = MultiMap.createLinkedSet<ComponentDescriptor, Type>() private val dependencies = MultiMap.createLinkedSet<ComponentDescriptor, Type>()
private val clashResolvers = ArrayList<PlatformExtensionsClashResolver<*>>()
private val registry = ComponentRegistry()
init {
parent?.let {
registry.addAll(it.registry)
clashResolvers.addAll(it.clashResolvers)
}
}
override fun resolve(request: Type, context: ValueResolveContext): ValueDescriptor? { override fun resolve(request: Type, context: ValueResolveContext): ValueDescriptor? {
fun ComponentDescriptor.isDefaultComponent(): Boolean = fun ComponentDescriptor.isDefaultComponent(): Boolean =
@@ -107,6 +114,10 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
return registry.tryGetEntry(request) return registry.tryGetEntry(request)
} }
internal fun registerClashResolvers(resolvers: List<PlatformExtensionsClashResolver<*>>) {
clashResolvers.addAll(resolvers)
}
internal fun registerDescriptors(context: ComponentResolveContext, items: List<ComponentDescriptor>) { internal fun registerDescriptors(context: ComponentResolveContext, items: List<ComponentDescriptor>) {
if (state == ComponentStorageState.Disposed) { if (state == ComponentStorageState.Disposed) {
throw ContainerConsistencyException("Cannot register descriptors in $state state") throw ContainerConsistencyException("Cannot register descriptors in $state state")
@@ -135,6 +146,7 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va
val implicits = inspectDependenciesAndRegisterAdhoc(context, descriptors) val implicits = inspectDependenciesAndRegisterAdhoc(context, descriptors)
registry.resolveClashesIfAny(context.container, clashResolvers)
injectProperties(context, descriptors + implicits) injectProperties(context, descriptors + implicits)
} }
@@ -6,9 +6,11 @@
package org.jetbrains.kotlin.resolve.jvm.platform package org.jetbrains.kotlin.resolve.jvm.platform
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver
import org.jetbrains.kotlin.container.StorageComponentContainer import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useImpl import org.jetbrains.kotlin.container.useImpl
import org.jetbrains.kotlin.container.useInstance import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.load.java.components.SamConversionResolver
import org.jetbrains.kotlin.load.java.sam.JvmSamConversionTransformer import org.jetbrains.kotlin.load.java.sam.JvmSamConversionTransformer
import org.jetbrains.kotlin.load.java.sam.SamConversionResolverImpl import org.jetbrains.kotlin.load.java.sam.SamConversionResolverImpl
import org.jetbrains.kotlin.resolve.PlatformConfiguratorBase import org.jetbrains.kotlin.resolve.PlatformConfiguratorBase
@@ -18,7 +20,6 @@ import org.jetbrains.kotlin.resolve.jvm.*
import org.jetbrains.kotlin.resolve.jvm.checkers.* import org.jetbrains.kotlin.resolve.jvm.checkers.*
import org.jetbrains.kotlin.resolve.jvm.multiplatform.JavaActualAnnotationArgumentExtractor import org.jetbrains.kotlin.resolve.jvm.multiplatform.JavaActualAnnotationArgumentExtractor
import org.jetbrains.kotlin.synthetic.JavaSyntheticScopes import org.jetbrains.kotlin.synthetic.JavaSyntheticScopes
import org.jetbrains.kotlin.types.DynamicTypesSettings
import org.jetbrains.kotlin.types.expressions.FunctionWithBigAritySupport import org.jetbrains.kotlin.types.expressions.FunctionWithBigAritySupport
object JvmPlatformConfigurator : PlatformConfiguratorBase( object JvmPlatformConfigurator : PlatformConfiguratorBase(
@@ -73,6 +74,10 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase(
ExplicitMetadataChecker ExplicitMetadataChecker
), ),
additionalClashResolvers = listOf(
PlatformExtensionsClashResolver.FallbackToDefault(SamConversionResolver.Empty, SamConversionResolver::class.java)
),
identifierChecker = JvmSimpleNameBacktickChecker, identifierChecker = JvmSimpleNameBacktickChecker,
overloadFilter = JvmOverloadFilter, overloadFilter = JvmOverloadFilter,
@@ -98,6 +103,6 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase(
container.useImpl<JvmTypeSpecificityComparator>() container.useImpl<JvmTypeSpecificityComparator>()
container.useImpl<JvmDefaultSuperCallChecker>() container.useImpl<JvmDefaultSuperCallChecker>()
container.useImpl<JvmSamConversionTransformer>() container.useImpl<JvmSamConversionTransformer>()
container.useInstance(FunctionWithBigAritySupport.LANGUAGE_VERSION_DEPENDENT) container.useInstance(FunctionWithBigAritySupport.LanguageVersionDependent)
} }
} }
@@ -17,12 +17,14 @@
package org.jetbrains.kotlin.resolve package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.container.DefaultImplementation import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver
import org.jetbrains.kotlin.container.PlatformSpecificExtension
import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression
@DefaultImplementation(impl = IdentifierChecker.Default::class) @DefaultImplementation(impl = IdentifierChecker.Default::class)
interface IdentifierChecker { interface IdentifierChecker : PlatformSpecificExtension<IdentifierChecker> {
fun checkIdentifier(simpleNameExpression: KtSimpleNameExpression, diagnosticHolder: DiagnosticSink) fun checkIdentifier(simpleNameExpression: KtSimpleNameExpression, diagnosticHolder: DiagnosticSink)
fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink)
@@ -31,3 +33,18 @@ interface IdentifierChecker {
override fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) {} override fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) {}
} }
} }
class IdentifierCheckerClashesResolver : PlatformExtensionsClashResolver<IdentifierChecker>(IdentifierChecker::class.java) {
override fun resolveExtensionsClash(extensions: List<IdentifierChecker>): IdentifierChecker = CompositeIdentifierChecker(extensions)
}
// Launches every underlying checker
class CompositeIdentifierChecker(private val identifierCheckers: List<IdentifierChecker>) : IdentifierChecker {
override fun checkIdentifier(simpleNameExpression: KtSimpleNameExpression, diagnosticHolder: DiagnosticSink) {
identifierCheckers.forEach { it.checkIdentifier(simpleNameExpression, diagnosticHolder) }
}
override fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) {
identifierCheckers.forEach { it.checkDeclaration(declaration, diagnosticHolder) }
}
}
@@ -6,12 +6,9 @@
package org.jetbrains.kotlin.resolve package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMap import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMap
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.composeContainer
import org.jetbrains.kotlin.container.useImpl
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.resolve.calls.checkers.*
import org.jetbrains.kotlin.container.* import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.resolve.calls.checkers.*
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
import org.jetbrains.kotlin.resolve.checkers.* import org.jetbrains.kotlin.resolve.checkers.*
import org.jetbrains.kotlin.resolve.lazy.DelegationFilter import org.jetbrains.kotlin.resolve.lazy.DelegationFilter
import org.jetbrains.kotlin.types.DynamicTypesSettings import org.jetbrains.kotlin.types.DynamicTypesSettings
@@ -55,6 +52,20 @@ private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(
) )
private val DEFAULT_ANNOTATION_CHECKERS = listOf<AdditionalAnnotationChecker>() private val DEFAULT_ANNOTATION_CHECKERS = listOf<AdditionalAnnotationChecker>()
private val DEFAULT_CLASH_RESOLVERS = listOf<PlatformExtensionsClashResolver<*>>(
IdentifierCheckerClashesResolver(),
/**
* We should use NONE for clash resolution, because:
* - JvmTypeSpecificityComparator covers cases with flexible types and primitive types loaded from Java, and all this is irrelevant for
* non-JVM modules
* - JsTypeSpecificityComparator covers case with dynamics, which are not allowed in non-JS modules either
*/
PlatformExtensionsClashResolver.FallbackToDefault(TypeSpecificityComparator.NONE, TypeSpecificityComparator::class.java),
PlatformExtensionsClashResolver.FallbackToDefault(DynamicTypesSettings(), DynamicTypesSettings::class.java)
)
abstract class PlatformConfiguratorBase( abstract class PlatformConfiguratorBase(
private val dynamicTypesSettings: DynamicTypesSettings? = null, private val dynamicTypesSettings: DynamicTypesSettings? = null,
@@ -63,6 +74,7 @@ abstract class PlatformConfiguratorBase(
additionalTypeCheckers: List<AdditionalTypeChecker> = emptyList(), additionalTypeCheckers: List<AdditionalTypeChecker> = emptyList(),
additionalClassifierUsageCheckers: List<ClassifierUsageChecker> = emptyList(), additionalClassifierUsageCheckers: List<ClassifierUsageChecker> = emptyList(),
additionalAnnotationCheckers: List<AdditionalAnnotationChecker> = emptyList(), additionalAnnotationCheckers: List<AdditionalAnnotationChecker> = emptyList(),
additionalClashResolvers: List<PlatformExtensionsClashResolver<*>> = emptyList(),
private val identifierChecker: IdentifierChecker? = null, private val identifierChecker: IdentifierChecker? = null,
private val overloadFilter: OverloadFilter? = null, private val overloadFilter: OverloadFilter? = null,
private val platformToKotlinClassMap: PlatformToKotlinClassMap? = null, private val platformToKotlinClassMap: PlatformToKotlinClassMap? = null,
@@ -76,6 +88,7 @@ abstract class PlatformConfiguratorBase(
private val classifierUsageCheckers: List<ClassifierUsageChecker> = private val classifierUsageCheckers: List<ClassifierUsageChecker> =
DEFAULT_CLASSIFIER_USAGE_CHECKERS + additionalClassifierUsageCheckers DEFAULT_CLASSIFIER_USAGE_CHECKERS + additionalClassifierUsageCheckers
private val annotationCheckers: List<AdditionalAnnotationChecker> = DEFAULT_ANNOTATION_CHECKERS + additionalAnnotationCheckers private val annotationCheckers: List<AdditionalAnnotationChecker> = DEFAULT_ANNOTATION_CHECKERS + additionalAnnotationCheckers
private val clashResolvers: List<PlatformExtensionsClashResolver<*>> = DEFAULT_CLASH_RESOLVERS + additionalClashResolvers
override val platformSpecificContainer = composeContainer(this::class.java.simpleName) { override val platformSpecificContainer = composeContainer(this::class.java.simpleName) {
useInstanceIfNotNull(dynamicTypesSettings) useInstanceIfNotNull(dynamicTypesSettings)
@@ -84,6 +97,7 @@ abstract class PlatformConfiguratorBase(
typeCheckers.forEach { useInstance(it) } typeCheckers.forEach { useInstance(it) }
classifierUsageCheckers.forEach { useInstance(it) } classifierUsageCheckers.forEach { useInstance(it) }
annotationCheckers.forEach { useInstance(it) } annotationCheckers.forEach { useInstance(it) }
clashResolvers.forEach { useClashResolver(it) }
useInstanceIfNotNull(identifierChecker) useInstanceIfNotNull(identifierChecker)
useInstanceIfNotNull(overloadFilter) useInstanceIfNotNull(overloadFilter)
useInstanceIfNotNull(platformToKotlinClassMap) useInstanceIfNotNull(platformToKotlinClassMap)
@@ -17,11 +17,13 @@
package org.jetbrains.kotlin.resolve.checkers package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.container.DefaultImplementation import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver
import org.jetbrains.kotlin.container.PlatformSpecificExtension
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor
@DefaultImplementation(PlatformDiagnosticSuppressor.Default::class) @DefaultImplementation(PlatformDiagnosticSuppressor.Default::class)
interface PlatformDiagnosticSuppressor { interface PlatformDiagnosticSuppressor : PlatformSpecificExtension<PlatformDiagnosticSuppressor>{
fun shouldReportUnusedParameter(parameter: VariableDescriptor): Boolean fun shouldReportUnusedParameter(parameter: VariableDescriptor): Boolean
fun shouldReportNoBody(descriptor: CallableMemberDescriptor): Boolean fun shouldReportNoBody(descriptor: CallableMemberDescriptor): Boolean
@@ -32,3 +34,18 @@ interface PlatformDiagnosticSuppressor {
override fun shouldReportNoBody(descriptor: CallableMemberDescriptor): Boolean = true override fun shouldReportNoBody(descriptor: CallableMemberDescriptor): Boolean = true
} }
} }
class CompositePlatformDiagnosticSuppressor(private val suppressors: List<PlatformDiagnosticSuppressor>) : PlatformDiagnosticSuppressor {
override fun shouldReportUnusedParameter(parameter: VariableDescriptor): Boolean =
suppressors.all { it.shouldReportUnusedParameter(parameter) }
override fun shouldReportNoBody(descriptor: CallableMemberDescriptor): Boolean =
suppressors.all { it.shouldReportNoBody(descriptor) }
}
class PlatformDiagnosticSuppressorClashesResolver : PlatformExtensionsClashResolver<PlatformDiagnosticSuppressor>(
PlatformDiagnosticSuppressor::class.java
) {
override fun resolveExtensionsClash(extensions: List<PlatformDiagnosticSuppressor>): PlatformDiagnosticSuppressor =
CompositePlatformDiagnosticSuppressor(extensions)
}
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.resolve.deprecation
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.DefaultImplementation import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver
import org.jetbrains.kotlin.container.PlatformSpecificExtension
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
@@ -61,7 +63,7 @@ internal fun createDeprecationDiagnostic(
} }
@DefaultImplementation(CoroutineCompatibilitySupport::class) @DefaultImplementation(CoroutineCompatibilitySupport::class)
class CoroutineCompatibilitySupport private constructor(val enabled: Boolean) { class CoroutineCompatibilitySupport private constructor(val enabled: Boolean) : PlatformSpecificExtension<CoroutineCompatibilitySupport>{
@Suppress("unused") @Suppress("unused")
constructor() : this(true) constructor() : this(true)
@@ -11,6 +11,8 @@ import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.DefaultImplementation import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver
import org.jetbrains.kotlin.container.PlatformSpecificExtension
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
@@ -820,14 +822,19 @@ class DoubleColonExpressionResolver(
} }
} }
// By default, function types with big arity are supported. On platforms where they are not supported by default (e.g. JVM), /**
// LANGUAGE_VERSION_DEPENDENT should be used which makes the code check if the corresponding language feature is enabled. * By default, function types with big arity are supported. On platforms where they are not supported by default (e.g. JVM),
@DefaultImplementation(FunctionWithBigAritySupport::class) * [LanguageVersionDependent] should be used which makes the code check if the corresponding language feature is enabled.
class FunctionWithBigAritySupport private constructor(val shouldCheckLanguageVersionSettings: Boolean) { */
constructor() : this(false) @DefaultImplementation(FunctionWithBigAritySupport.Enabled::class)
interface FunctionWithBigAritySupport {
val shouldCheckLanguageVersionSettings: Boolean
companion object { object Enabled : FunctionWithBigAritySupport {
@JvmField override val shouldCheckLanguageVersionSettings: Boolean = false
val LANGUAGE_VERSION_DEPENDENT = FunctionWithBigAritySupport(true)
} }
}
object LanguageVersionDependent : FunctionWithBigAritySupport {
override val shouldCheckLanguageVersionSettings: Boolean = true
}
}
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.resolve.calls.results package org.jetbrains.kotlin.resolve.calls.results
import org.jetbrains.kotlin.container.DefaultImplementation import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver
import org.jetbrains.kotlin.container.PlatformSpecificExtension
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.MemberDescriptor import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
@@ -30,7 +32,7 @@ interface SpecificityComparisonCallbacks {
} }
@DefaultImplementation(impl = TypeSpecificityComparator.NONE::class) @DefaultImplementation(impl = TypeSpecificityComparator.NONE::class)
interface TypeSpecificityComparator { interface TypeSpecificityComparator : PlatformSpecificExtension<TypeSpecificityComparator> {
fun isDefinitelyLessSpecific(specific: KotlinTypeMarker, general: KotlinTypeMarker): Boolean fun isDefinitelyLessSpecific(specific: KotlinTypeMarker, general: KotlinTypeMarker): Boolean
object NONE : TypeSpecificityComparator { object NONE : TypeSpecificityComparator {
@@ -17,11 +17,13 @@
package org.jetbrains.kotlin.load.java.components package org.jetbrains.kotlin.load.java.components
import org.jetbrains.kotlin.container.DefaultImplementation import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver
import org.jetbrains.kotlin.container.PlatformSpecificExtension
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.SimpleType
@DefaultImplementation(impl = SamConversionResolver.Empty::class) @DefaultImplementation(impl = SamConversionResolver.Empty::class)
interface SamConversionResolver { interface SamConversionResolver : PlatformSpecificExtension<SamConversionResolver> {
object Empty : SamConversionResolver { object Empty : SamConversionResolver {
override fun resolveFunctionTypeIfSamInterface(classDescriptor: JavaClassDescriptor): SimpleType? = null override fun resolveFunctionTypeIfSamInterface(classDescriptor: JavaClassDescriptor): SimpleType? = null
} }
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.types
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.container.DefaultImplementation import org.jetbrains.kotlin.container.DefaultImplementation
import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver
import org.jetbrains.kotlin.container.PlatformSpecificExtension
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions import org.jetbrains.kotlin.renderer.DescriptorRendererOptions
@@ -25,7 +27,7 @@ import org.jetbrains.kotlin.types.model.DynamicTypeMarker
import org.jetbrains.kotlin.types.typeUtil.builtIns import org.jetbrains.kotlin.types.typeUtil.builtIns
@DefaultImplementation(impl = DynamicTypesSettings::class) @DefaultImplementation(impl = DynamicTypesSettings::class)
open class DynamicTypesSettings { open class DynamicTypesSettings : PlatformSpecificExtension<DynamicTypesSettings> {
open val dynamicTypesAllowed: Boolean open val dynamicTypesAllowed: Boolean
get() = false get() = false
} }
@@ -29,5 +29,7 @@ import kotlin.reflect.KClass
* *
* Such configurations may arise, for example, for multiplatform modules: consider analyzing JVM+JS module, where JS contributes * Such configurations may arise, for example, for multiplatform modules: consider analyzing JVM+JS module, where JS contributes
* default implementation of some particular service, and JVM contributes non-default. * default implementation of some particular service, and JVM contributes non-default.
*
* If you need more fine-grained control of clashes resolution, consider using [PlatformExtensionsClashResolver]
**/ **/
annotation class DefaultImplementation(val impl: KClass<*>) annotation class DefaultImplementation(val impl: KClass<*>)
@@ -0,0 +1,52 @@
/*
* 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.container
/**
* This is a marker-interface for components which are needed for common resolve
* facilities (like resolve, or deserialization), but are platform-specific.
*
* PlatformSpecificExtensions has to be present in the container in exactly one
* instance (hence common pattern with providing no-op DEFAULT/EMPTY implementation
* in the corresponding interface)
*
* In multiplatform modules such components require special treatment. Namely,
* if several components of the same type are provided, then it's not an illegal state;
* rather, we have to carefully resolve clash on case-by-case basis.
* See also [PlatformExtensionsClashResolver].
*/
interface PlatformSpecificExtension<S : PlatformSpecificExtension<S>>
/**
* Allows to specify which [PlatformSpecificExtension] should be used if there were two or more registrations
* for [applicableTo] class in the container.
*
* [PlatformExtensionsClashResolver] should be registred in the container via [useClashResolver]-extension.
*
* NB. YOU DON'T NEED this mechanism for the most popular case of "one or several default vs.
* zero or one non-default". Just use [DefaultImplementation], and default instances will be automatically
* discriminated (see respective KDoc).
* Use [PlatformExtensionsClashResolver] only for cases when you need more invloved logic.
*
* Example: [org.jetbrains.kotlin.resolve.IdentifierChecker]. It is used in platform-agnostic code,
* which resolves and checks identifiers for correctness. Each platform has it's own rules
* regarding identifier correctness. In MPP modules we can't choose only one IdentifierChecker;
* instead, we have to provide a "composite" IdentifierChecker which will launch checks of *each*
* platform.
*
*/
abstract class PlatformExtensionsClashResolver<E : PlatformSpecificExtension<E>>(val applicableTo: Class<E>) {
abstract fun resolveExtensionsClash(extensions: List<E>): E
class FallbackToDefault<E : PlatformSpecificExtension<E>>(
private val defaultValue: E,
applicableTo: Class<E>
) : PlatformExtensionsClashResolver<E>(applicableTo) {
override fun resolveExtensionsClash(extensions: List<E>): E = defaultValue
}
}