[Commonizer] Include dependencies in CirCommonClassifierId
^KT-48288
This commit is contained in:
committed by
Space
parent
ef6f84b151
commit
c1f118e502
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.commonizer.cir
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvided
|
||||||
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiers
|
||||||
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
|
|
||||||
|
internal fun CirProvidedClassifiers.toCirClassOrTypeAliasTypeOrNull(type: CirProvided.ClassOrTypeAliasType): CirClassOrTypeAliasType? {
|
||||||
|
return when (type) {
|
||||||
|
is CirProvided.ClassType -> type.toCirClassTypeOrNull()
|
||||||
|
is CirProvided.TypeAliasType -> toCirTypeAliasTypeOrNull(type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun CirProvidedClassifiers.toCirTypeAliasTypeOrNull(type: CirProvided.TypeAliasType): CirTypeAliasType? {
|
||||||
|
val typeAlias = this.classifier(type.classifierId) as? CirProvided.TypeAlias ?: return null
|
||||||
|
return CirTypeAliasType.createInterned(
|
||||||
|
typeAliasId = type.classifierId,
|
||||||
|
isMarkedNullable = type.isMarkedNullable,
|
||||||
|
arguments = type.arguments.map { it.toCirTypeProjection() ?: return null },
|
||||||
|
underlyingType = toCirClassOrTypeAliasTypeOrNull(typeAlias.underlyingType) ?: return null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun CirProvided.ClassType.toCirClassTypeOrNull(): CirClassType? {
|
||||||
|
return CirClassType.createInterned(
|
||||||
|
classId = this.classifierId,
|
||||||
|
outerType = this.outerType?.let { it.toCirClassTypeOrNull() ?: return null },
|
||||||
|
isMarkedNullable = this.isMarkedNullable,
|
||||||
|
arguments = this.arguments.map { it.toCirTypeProjection() ?: return null },
|
||||||
|
visibility = Visibilities.Public,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun CirProvided.TypeProjection.toCirTypeProjection(): CirTypeProjection? {
|
||||||
|
return when (this) {
|
||||||
|
is CirProvided.StarTypeProjection -> CirStarTypeProjection
|
||||||
|
is CirProvided.RegularTypeProjection -> CirRegularTypeProjection(
|
||||||
|
projectionKind = variance,
|
||||||
|
type = (type as? CirProvided.ClassType)?.toCirClassTypeOrNull() ?: return null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -94,15 +94,16 @@ class CirEntityId private constructor(val packageName: CirPackageName, val relat
|
|||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other !is CirEntityId) return false
|
if (other !is CirEntityId) return false
|
||||||
|
if (other._hashCode != this._hashCode) return false
|
||||||
if (other.packageName != this.packageName) return false
|
if (other.packageName != this.packageName) return false
|
||||||
if (!other.relativeNameSegments.contentEquals(relativeNameSegments)) return false
|
if (!other.relativeNameSegments.contentEquals(relativeNameSegments)) return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _hashCode: Int = 0
|
private var _hashCode: Int = hashCode(packageName)
|
||||||
|
.appendHashCode(relativeNameSegments).also { hashCode -> _hashCode = hashCode }
|
||||||
|
|
||||||
override fun hashCode(): Int = if (_hashCode != 0) _hashCode else
|
override fun hashCode(): Int = _hashCode
|
||||||
hashCode(packageName).appendHashCode(relativeNameSegments).also { hashCode -> _hashCode = hashCode }
|
|
||||||
|
|
||||||
override fun toString(): String = buildString {
|
override fun toString(): String = buildString {
|
||||||
packageName.segments.joinTo(this, "/")
|
packageName.segments.joinTo(this, "/")
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex
|
|||||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvided
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvided
|
||||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiers
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiers
|
||||||
import org.jetbrains.kotlin.commonizer.mergedtree.findClass
|
import org.jetbrains.kotlin.commonizer.mergedtree.findClass
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
|
||||||
|
|
||||||
internal interface CirSupertypesResolver {
|
internal interface CirSupertypesResolver {
|
||||||
/**
|
/**
|
||||||
@@ -63,42 +62,3 @@ internal class SimpleCirSupertypesResolver(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun CirProvidedClassifiers.toCirClassOrTypeAliasTypeOrNull(type: CirProvided.Type): CirClassOrTypeAliasType? {
|
|
||||||
return when (type) {
|
|
||||||
is CirProvided.ClassType -> type.toCirClassTypeOrNull()
|
|
||||||
is CirProvided.TypeParameterType -> null
|
|
||||||
is CirProvided.TypeAliasType -> TODO()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun CirProvidedClassifiers.toCirTypeAliasTypeOrNull(type: CirProvided.TypeAliasType): CirTypeAliasType? {
|
|
||||||
val typeAlias = this.classifier(type.typeAliasId) as? CirProvided.TypeAlias ?: return null
|
|
||||||
return CirTypeAliasType.createInterned(
|
|
||||||
typeAliasId = type.typeAliasId,
|
|
||||||
isMarkedNullable = type.isMarkedNullable,
|
|
||||||
arguments = type.arguments.map { it.toCirTypeProjection() ?: return null },
|
|
||||||
underlyingType = toCirClassOrTypeAliasTypeOrNull(typeAlias.underlyingType) ?: return null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO NOW move
|
|
||||||
internal fun CirProvided.ClassType.toCirClassTypeOrNull(): CirClassType? {
|
|
||||||
return CirClassType.createInterned(
|
|
||||||
classId = this.classId,
|
|
||||||
outerType = this.outerType?.let { it.toCirClassTypeOrNull() ?: return null },
|
|
||||||
isMarkedNullable = this.isMarkedNullable,
|
|
||||||
arguments = this.arguments.map { it.toCirTypeProjection() ?: return null },
|
|
||||||
visibility = Visibilities.Public,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun CirProvided.TypeProjection.toCirTypeProjection(): CirTypeProjection? {
|
|
||||||
return when (this) {
|
|
||||||
is CirProvided.StarTypeProjection -> CirStarTypeProjection
|
|
||||||
is CirProvided.RegularTypeProjection -> CirRegularTypeProjection(
|
|
||||||
projectionKind = variance,
|
|
||||||
type = (type as? CirProvided.ClassType)?.toCirClassTypeOrNull() ?: return null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -265,28 +265,22 @@ private class CirTypeAliasTypeInternedImpl(
|
|||||||
override val isMarkedNullable: Boolean
|
override val isMarkedNullable: Boolean
|
||||||
) : CirTypeAliasType() {
|
) : CirTypeAliasType() {
|
||||||
// See also org.jetbrains.kotlin.types.KotlinType.cachedHashCode
|
// See also org.jetbrains.kotlin.types.KotlinType.cachedHashCode
|
||||||
private var cachedHashCode = 0
|
private val hashCode = hashCode(classifierId)
|
||||||
|
|
||||||
private fun computeHashCode() = hashCode(classifierId)
|
|
||||||
.appendHashCode(underlyingType)
|
.appendHashCode(underlyingType)
|
||||||
.appendHashCode(arguments)
|
.appendHashCode(arguments)
|
||||||
.appendHashCode(isMarkedNullable)
|
.appendHashCode(isMarkedNullable)
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var currentHashCode = cachedHashCode
|
|
||||||
if (currentHashCode != 0) return currentHashCode
|
|
||||||
|
|
||||||
currentHashCode = computeHashCode()
|
override fun hashCode(): Int = hashCode
|
||||||
cachedHashCode = currentHashCode
|
|
||||||
return currentHashCode
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean = when {
|
override fun equals(other: Any?): Boolean = when {
|
||||||
other === this -> true
|
other === this -> true
|
||||||
other is CirTypeAliasType -> classifierId == other.classifierId
|
other is CirTypeAliasType ->
|
||||||
&& underlyingType == other.underlyingType
|
hashCode == other.hashCode() &&
|
||||||
&& isMarkedNullable == other.isMarkedNullable
|
classifierId == other.classifierId
|
||||||
&& arguments == other.arguments
|
&& underlyingType == other.underlyingType
|
||||||
|
&& isMarkedNullable == other.isMarkedNullable
|
||||||
|
&& arguments == other.arguments
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -68,7 +68,6 @@ internal class ClassOrTypeAliasTypeCommonizer(
|
|||||||
val commonizedClassifier = classifiers.commonizedNodes.classNode(classifierId)?.commonDeclaration?.invoke()
|
val commonizedClassifier = classifiers.commonizedNodes.classNode(classifierId)?.commonDeclaration?.invoke()
|
||||||
?: classifiers.commonizedNodes.typeAliasNode(classifierId)?.commonDeclaration?.invoke()
|
?: classifiers.commonizedNodes.typeAliasNode(classifierId)?.commonDeclaration?.invoke()
|
||||||
|
|
||||||
|
|
||||||
when (commonizedClassifier) {
|
when (commonizedClassifier) {
|
||||||
is CirClass -> return CirClassType.createInterned(
|
is CirClass -> return CirClassType.createInterned(
|
||||||
classId = classifierId,
|
classId = classifierId,
|
||||||
|
|||||||
+14
-2
@@ -7,8 +7,20 @@ package org.jetbrains.kotlin.commonizer.mergedtree
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
||||||
|
|
||||||
@JvmInline
|
class CirCommonClassifierId(val aliases: List<CirEntityId>) {
|
||||||
value class CirCommonClassifierId(val aliases: LinkedHashSet<CirEntityId>) {
|
|
||||||
|
private val _hashCode = aliases.hashCode()
|
||||||
|
|
||||||
|
override fun hashCode(): Int = _hashCode
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other === this) return true
|
||||||
|
if (other !is CirCommonClassifierId) return false
|
||||||
|
if (other._hashCode != this._hashCode) return false
|
||||||
|
if (other.aliases != this.aliases) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return aliases.joinToString(prefix = "(", postfix = ")")
|
return aliases.joinToString(prefix = "(", postfix = ")")
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-11
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.commonizer.mergedtree
|
package org.jetbrains.kotlin.commonizer.mergedtree
|
||||||
|
|
||||||
|
import gnu.trove.THashMap
|
||||||
|
import gnu.trove.THashSet
|
||||||
import org.jetbrains.kotlin.commonizer.TargetDependent
|
import org.jetbrains.kotlin.commonizer.TargetDependent
|
||||||
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
||||||
import org.jetbrains.kotlin.commonizer.cir.CirTypeAlias
|
import org.jetbrains.kotlin.commonizer.cir.CirTypeAlias
|
||||||
@@ -27,8 +29,8 @@ private class CirCommonClassifierIdResolverImpl(
|
|||||||
private val dependencies: CirProvidedClassifiers
|
private val dependencies: CirProvidedClassifiers
|
||||||
) : CirCommonClassifierIdResolver {
|
) : CirCommonClassifierIdResolver {
|
||||||
|
|
||||||
private val cachedResults = HashMap<CirEntityId, CirCommonClassifierId>()
|
private val cachedResults = THashMap<CirEntityId, CirCommonClassifierId>()
|
||||||
private val cachedNullResults = HashSet<CirEntityId>()
|
private val cachedNullResults = THashSet<CirEntityId>()
|
||||||
|
|
||||||
override fun findCommonId(id: CirEntityId): CirCommonClassifierId? {
|
override fun findCommonId(id: CirEntityId): CirCommonClassifierId? {
|
||||||
cachedResults[id]?.let { return it }
|
cachedResults[id]?.let { return it }
|
||||||
@@ -37,10 +39,10 @@ private class CirCommonClassifierIdResolverImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun doFindCommonId(id: CirEntityId): CirCommonClassifierId? {
|
private fun doFindCommonId(id: CirEntityId): CirCommonClassifierId? {
|
||||||
val results = LinkedHashSet<CirEntityId>()
|
val results = ArrayList<CirEntityId>()
|
||||||
|
|
||||||
/* Set of every classifier id that once was enqueued already */
|
/* Set of every classifier id that once was enqueued already */
|
||||||
val visited = mutableSetOf<CirEntityId>()
|
val visited = THashSet<CirEntityId>()
|
||||||
|
|
||||||
/* Actual, current queue of classifiers to resolve */
|
/* Actual, current queue of classifiers to resolve */
|
||||||
val queue = ArrayDeque<CirEntityId>()
|
val queue = ArrayDeque<CirEntityId>()
|
||||||
@@ -50,6 +52,8 @@ private class CirCommonClassifierIdResolverImpl(
|
|||||||
|
|
||||||
while (queue.isNotEmpty()) {
|
while (queue.isNotEmpty()) {
|
||||||
val nextClassifierId = queue.removeFirst()
|
val nextClassifierId = queue.removeFirst()
|
||||||
|
|
||||||
|
/* Either CirClassifier or CirProvided.Classifier or null */
|
||||||
val foundClassifiers = classifierIndices.associateWith { index ->
|
val foundClassifiers = classifierIndices.associateWith { index ->
|
||||||
index.findClassifier(nextClassifierId) ?: dependencies.classifier(nextClassifierId)
|
index.findClassifier(nextClassifierId) ?: dependencies.classifier(nextClassifierId)
|
||||||
}
|
}
|
||||||
@@ -69,6 +73,12 @@ private class CirCommonClassifierIdResolverImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies.findTypeAliasesWithUnderlyingType(nextClassifierId).forEach { aliasId ->
|
||||||
|
if (visited.add(aliasId)) {
|
||||||
|
queue.add(aliasId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Propagate to the right (towards expansion)
|
// Propagate to the right (towards expansion)
|
||||||
if (classifier is CirTypeAlias) {
|
if (classifier is CirTypeAlias) {
|
||||||
if (visited.add(classifier.underlyingType.classifierId)) {
|
if (visited.add(classifier.underlyingType.classifierId)) {
|
||||||
@@ -77,13 +87,8 @@ private class CirCommonClassifierIdResolverImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (classifier is CirProvided.TypeAlias) {
|
if (classifier is CirProvided.TypeAlias) {
|
||||||
val underlyingTypeId = when (val underlyingType = classifier.underlyingType) {
|
if (visited.add(classifier.underlyingType.classifierId)) {
|
||||||
is CirProvided.ClassType -> underlyingType.classId
|
queue.add(classifier.underlyingType.classifierId)
|
||||||
is CirProvided.TypeAliasType -> underlyingType.typeAliasId
|
|
||||||
is CirProvided.TypeParameterType -> null
|
|
||||||
}
|
|
||||||
if (underlyingTypeId != null && visited.add(underlyingTypeId)) {
|
|
||||||
queue.add(underlyingTypeId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -30,6 +30,7 @@ object CirFictitiousFunctionClassifiers : CirProvidedClassifiers {
|
|||||||
|
|
||||||
override fun hasClassifier(classifierId: CirEntityId) = classifierId in classifiers
|
override fun hasClassifier(classifierId: CirEntityId) = classifierId in classifiers
|
||||||
override fun classifier(classifierId: CirEntityId): CirProvided.RegularClass? = classifiers[classifierId]
|
override fun classifier(classifierId: CirEntityId): CirProvided.RegularClass? = classifiers[classifierId]
|
||||||
|
override fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirEntityId> = emptyList()
|
||||||
|
|
||||||
private inline fun buildFictitiousFunctionClass(prefix: String, arity: Int, consumer: (CirEntityId, CirProvided.RegularClass) -> Unit) {
|
private inline fun buildFictitiousFunctionClass(prefix: String, arity: Int, consumer: (CirEntityId, CirProvided.RegularClass) -> Unit) {
|
||||||
val typeParameters = List(arity + 1) { index ->
|
val typeParameters = List(arity + 1) { index ->
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ class CirKnownClassifiers(
|
|||||||
val targetDependencies: TargetDependent<CirProvidedClassifiers>,
|
val targetDependencies: TargetDependent<CirProvidedClassifiers>,
|
||||||
val commonizedNodes: CirCommonizedClassifierNodes,
|
val commonizedNodes: CirCommonizedClassifierNodes,
|
||||||
val commonDependencies: CirProvidedClassifiers,
|
val commonDependencies: CirProvidedClassifiers,
|
||||||
val commonClassifierIdResolver: CirCommonClassifierIdResolver = CirCommonClassifierIdResolver(classifierIndices),
|
val commonClassifierIdResolver: CirCommonClassifierIdResolver = CirCommonClassifierIdResolver(classifierIndices, commonDependencies),
|
||||||
)
|
)
|
||||||
|
|
||||||
/** A set of all CIR nodes built for commonized classes and type aliases. */
|
/** A set of all CIR nodes built for commonized classes and type aliases. */
|
||||||
|
|||||||
+23
-10
@@ -22,9 +22,13 @@ interface CirProvidedClassifiers {
|
|||||||
fun hasClassifier(classifierId: CirEntityId): Boolean
|
fun hasClassifier(classifierId: CirEntityId): Boolean
|
||||||
fun classifier(classifierId: CirEntityId): CirProvided.Classifier?
|
fun classifier(classifierId: CirEntityId): CirProvided.Classifier?
|
||||||
|
|
||||||
|
// TODO NOW: Test
|
||||||
|
fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirEntityId>
|
||||||
|
|
||||||
object EMPTY : CirProvidedClassifiers {
|
object EMPTY : CirProvidedClassifiers {
|
||||||
override fun hasClassifier(classifierId: CirEntityId) = false
|
override fun hasClassifier(classifierId: CirEntityId) = false
|
||||||
override fun classifier(classifierId: CirEntityId): CirProvided.Classifier? = null
|
override fun classifier(classifierId: CirEntityId): CirProvided.Classifier? = null
|
||||||
|
override fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId) = emptyList<CirEntityId>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CompositeClassifiers(val delegates: List<CirProvidedClassifiers>) : CirProvidedClassifiers {
|
private class CompositeClassifiers(val delegates: List<CirProvidedClassifiers>) : CirProvidedClassifiers {
|
||||||
@@ -39,6 +43,10 @@ interface CirProvidedClassifiers {
|
|||||||
}
|
}
|
||||||
return fallbackReturn
|
return fallbackReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirEntityId> {
|
||||||
|
return delegates.flatMap { it.findTypeAliasesWithUnderlyingType(underlyingClassifier) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -97,7 +105,7 @@ object CirProvided {
|
|||||||
|
|
||||||
data class TypeAlias(
|
data class TypeAlias(
|
||||||
override val typeParameters: List<TypeParameter>,
|
override val typeParameters: List<TypeParameter>,
|
||||||
val underlyingType: Type
|
val underlyingType: ClassOrTypeAliasType
|
||||||
) : Classifier
|
) : Classifier
|
||||||
|
|
||||||
/* Type parameter */
|
/* Type parameter */
|
||||||
@@ -108,23 +116,28 @@ object CirProvided {
|
|||||||
val isMarkedNullable: Boolean
|
val isMarkedNullable: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed interface ClassOrTypeAliasType : Type {
|
||||||
|
val classifierId: CirEntityId
|
||||||
|
val arguments: List<TypeProjection>
|
||||||
|
}
|
||||||
|
|
||||||
data class TypeParameterType(
|
data class TypeParameterType(
|
||||||
val index: Int,
|
val index: Int,
|
||||||
override val isMarkedNullable: Boolean
|
override val isMarkedNullable: Boolean
|
||||||
) : Type
|
) : Type
|
||||||
|
|
||||||
data class ClassType(
|
data class ClassType(
|
||||||
val classId: CirEntityId,
|
override val classifierId: CirEntityId,
|
||||||
val outerType: ClassType?,
|
override val arguments: List<TypeProjection>,
|
||||||
val arguments: List<TypeProjection>,
|
override val isMarkedNullable: Boolean,
|
||||||
override val isMarkedNullable: Boolean
|
val outerType: ClassType?
|
||||||
) : Type
|
) : ClassOrTypeAliasType
|
||||||
|
|
||||||
data class TypeAliasType(
|
data class TypeAliasType(
|
||||||
val typeAliasId: CirEntityId,
|
override val classifierId: CirEntityId,
|
||||||
val arguments: List<TypeProjection>,
|
override val arguments: List<TypeProjection>,
|
||||||
override val isMarkedNullable: Boolean
|
override val isMarkedNullable: Boolean
|
||||||
) : Type
|
) : ClassOrTypeAliasType
|
||||||
|
|
||||||
/* Type projections */
|
/* Type projections */
|
||||||
sealed interface TypeProjection
|
sealed interface TypeProjection
|
||||||
@@ -139,7 +152,7 @@ object CirProvided {
|
|||||||
private object ArtificialSupertypes {
|
private object ArtificialSupertypes {
|
||||||
private fun createType(classId: String): CirProvided.ClassType {
|
private fun createType(classId: String): CirProvided.ClassType {
|
||||||
return CirProvided.ClassType(
|
return CirProvided.ClassType(
|
||||||
classId = CirEntityId.create(classId),
|
classifierId = CirEntityId.create(classId),
|
||||||
outerType = null, arguments = emptyList(), isMarkedNullable = false
|
outerType = null, arguments = emptyList(), isMarkedNullable = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-4
@@ -25,6 +25,18 @@ internal class CirProvidedClassifiersByModules internal constructor(
|
|||||||
private val hasForwardDeclarations: Boolean,
|
private val hasForwardDeclarations: Boolean,
|
||||||
private val classifiers: Map<CirEntityId, CirProvided.Classifier>,
|
private val classifiers: Map<CirEntityId, CirProvided.Classifier>,
|
||||||
) : CirProvidedClassifiers {
|
) : CirProvidedClassifiers {
|
||||||
|
|
||||||
|
private val typeAliasesByUnderlyingTypes = run {
|
||||||
|
THashMap<CirEntityId, MutableList<CirEntityId>>().also { map ->
|
||||||
|
classifiers.forEach { (id, classifier) ->
|
||||||
|
if (classifier is CirProvided.TypeAlias) {
|
||||||
|
val set = map.computeIfAbsent(classifier.underlyingType.classifierId) { ArrayList() }
|
||||||
|
set.add(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun hasClassifier(classifierId: CirEntityId) =
|
override fun hasClassifier(classifierId: CirEntityId) =
|
||||||
if (classifierId.packageName.isUnderKotlinNativeSyntheticPackages) {
|
if (classifierId.packageName.isUnderKotlinNativeSyntheticPackages) {
|
||||||
hasForwardDeclarations
|
hasForwardDeclarations
|
||||||
@@ -32,6 +44,10 @@ internal class CirProvidedClassifiersByModules internal constructor(
|
|||||||
classifierId in classifiers
|
classifierId in classifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirEntityId> {
|
||||||
|
return typeAliasesByUnderlyingTypes[underlyingClassifier].orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
override fun classifier(classifierId: CirEntityId) =
|
override fun classifier(classifierId: CirEntityId) =
|
||||||
classifiers[classifierId] ?: if (hasForwardDeclarations && classifierId.packageName.isUnderKotlinNativeSyntheticPackages)
|
classifiers[classifierId] ?: if (hasForwardDeclarations && classifierId.packageName.isUnderKotlinNativeSyntheticPackages)
|
||||||
FALLBACK_FORWARD_DECLARATION_CLASS else null
|
FALLBACK_FORWARD_DECLARATION_CLASS else null
|
||||||
@@ -167,7 +183,7 @@ private fun readClass(
|
|||||||
|
|
||||||
val supertypes = (classProto.supertypeList.map { readType(it, typeReadContext) } +
|
val supertypes = (classProto.supertypeList.map { readType(it, typeReadContext) } +
|
||||||
classProto.supertypeIdList.map { readType(classProto.typeTable.getType(it), typeReadContext) })
|
classProto.supertypeIdList.map { readType(classProto.typeTable.getType(it), typeReadContext) })
|
||||||
.filterNot { type -> type is CirProvided.ClassType && type.classId == ANY_CLASS_ID }
|
.filterNot { type -> type is CirProvided.ClassType && type.classifierId == ANY_CLASS_ID }
|
||||||
|
|
||||||
|
|
||||||
val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(classProto.flags))
|
val visibility = ProtoEnumFlags.visibility(Flags.VISIBILITY.get(classProto.flags))
|
||||||
@@ -198,7 +214,7 @@ private inline fun readTypeAlias(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val underlyingType = readType(typeAliasProto.underlyingType(types), TypeReadContext(strings, types, typeParameterNameToIndex))
|
val underlyingType = readType(typeAliasProto.underlyingType(types), TypeReadContext(strings, types, typeParameterNameToIndex))
|
||||||
val typeAlias = CirProvided.TypeAlias(typeParameters, underlyingType)
|
val typeAlias = CirProvided.TypeAlias(typeParameters, underlyingType as CirProvided.ClassOrTypeAliasType)
|
||||||
|
|
||||||
consumer(typeAliasId, typeAlias)
|
consumer(typeAliasId, typeAlias)
|
||||||
}
|
}
|
||||||
@@ -243,14 +259,14 @@ private fun readType(typeProto: ProtoBuf.Type, context: TypeReadContext): CirPro
|
|||||||
}
|
}
|
||||||
|
|
||||||
CirProvided.ClassType(
|
CirProvided.ClassType(
|
||||||
classId = classId,
|
classifierId = classId,
|
||||||
outerType = outerType,
|
outerType = outerType,
|
||||||
arguments = readTypeArguments(argumentList, context),
|
arguments = readTypeArguments(argumentList, context),
|
||||||
isMarkedNullable = nullable
|
isMarkedNullable = nullable
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
hasTypeAliasName() -> CirProvided.TypeAliasType(
|
hasTypeAliasName() -> CirProvided.TypeAliasType(
|
||||||
typeAliasId = CirEntityId.create(context.strings.getQualifiedClassName(typeAliasName)),
|
classifierId = CirEntityId.create(context.strings.getQualifiedClassName(typeAliasName)),
|
||||||
arguments = readTypeArguments(argumentList, context),
|
arguments = readTypeArguments(argumentList, context),
|
||||||
isMarkedNullable = nullable
|
isMarkedNullable = nullable
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-3
@@ -40,7 +40,6 @@ class CirTypeSignature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typealias ObjCFunctionApproximation = Int
|
typealias ObjCFunctionApproximation = Int
|
||||||
|
|
||||||
data class PropertyApproximationKey(
|
data class PropertyApproximationKey(
|
||||||
@@ -188,8 +187,7 @@ internal fun CirTypeSignature.appendTypeApproximationSignature(context: Signatur
|
|||||||
internal fun CirTypeSignature.appendClassOrTypeAliasTypeApproximationSignature(
|
internal fun CirTypeSignature.appendClassOrTypeAliasTypeApproximationSignature(
|
||||||
context: SignatureBuildingContext, type: CirClassOrTypeAliasType
|
context: SignatureBuildingContext, type: CirClassOrTypeAliasType
|
||||||
) {
|
) {
|
||||||
val classifierId = context.commonClassifierIdResolver.findCommonId(type.classifierId)?.aliases ?: type.classifierId
|
add(context.commonClassifierIdResolver.findCommonId(type.classifierId) ?: type.classifierId)
|
||||||
add(classifierId)
|
|
||||||
if (type.arguments.isNotEmpty()) {
|
if (type.arguments.isNotEmpty()) {
|
||||||
add(TypeSignatureElements.ArgumentsStartToken)
|
add(TypeSignatureElements.ArgumentsStartToken)
|
||||||
type.arguments.forEachIndexed { index, argument ->
|
type.arguments.forEachIndexed { index, argument ->
|
||||||
|
|||||||
+6
-6
@@ -110,8 +110,8 @@ object CirTypeAliasExpander {
|
|||||||
expansion: CirTypeAliasExpansion,
|
expansion: CirTypeAliasExpansion,
|
||||||
type: CirProvided.TypeAliasType
|
type: CirProvided.TypeAliasType
|
||||||
): CirTypeAliasType {
|
): CirTypeAliasType {
|
||||||
val typeAlias: CirProvided.TypeAlias = expansion.typeResolver.resolveClassifier(type.typeAliasId)
|
val typeAlias: CirProvided.TypeAlias = expansion.typeResolver.resolveClassifier(type.classifierId)
|
||||||
checkArgumentsCount(typeAlias, type.typeAliasId, type.arguments)
|
checkArgumentsCount(typeAlias, type.classifierId, type.arguments)
|
||||||
|
|
||||||
val expandedArguments = type.arguments.compactMapIndexed { index, argument ->
|
val expandedArguments = type.arguments.compactMapIndexed { index, argument ->
|
||||||
val projection = expandTypeProjection(expansion, argument, typeAlias.typeParameters[index].variance)
|
val projection = expandTypeProjection(expansion, argument, typeAlias.typeParameters[index].variance)
|
||||||
@@ -122,7 +122,7 @@ object CirTypeAliasExpander {
|
|||||||
val nestedExpandedType = expand(nestedExpansion)
|
val nestedExpandedType = expand(nestedExpansion)
|
||||||
|
|
||||||
return CirTypeAliasType.createInterned(
|
return CirTypeAliasType.createInterned(
|
||||||
typeAliasId = type.typeAliasId,
|
typeAliasId = type.classifierId,
|
||||||
underlyingType = nestedExpandedType,
|
underlyingType = nestedExpandedType,
|
||||||
arguments = expandedArguments,
|
arguments = expandedArguments,
|
||||||
isMarkedNullable = type.isMarkedNullable
|
isMarkedNullable = type.isMarkedNullable
|
||||||
@@ -133,8 +133,8 @@ object CirTypeAliasExpander {
|
|||||||
expansion: CirTypeAliasExpansion,
|
expansion: CirTypeAliasExpansion,
|
||||||
type: CirProvided.ClassType
|
type: CirProvided.ClassType
|
||||||
): CirClassType {
|
): CirClassType {
|
||||||
val clazz: CirProvided.Class = expansion.typeResolver.resolveClassifier(type.classId)
|
val clazz: CirProvided.Class = expansion.typeResolver.resolveClassifier(type.classifierId)
|
||||||
checkArgumentsCount(clazz, type.classId, type.arguments)
|
checkArgumentsCount(clazz, type.classifierId, type.arguments)
|
||||||
|
|
||||||
val expandedArguments = type.arguments.compactMapIndexed { index, argument ->
|
val expandedArguments = type.arguments.compactMapIndexed { index, argument ->
|
||||||
val projection = expandTypeProjection(expansion, argument, clazz.typeParameters[index].variance)
|
val projection = expandTypeProjection(expansion, argument, clazz.typeParameters[index].variance)
|
||||||
@@ -142,7 +142,7 @@ object CirTypeAliasExpander {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return CirClassType.createInterned(
|
return CirClassType.createInterned(
|
||||||
classId = type.classId,
|
classId = type.classifierId,
|
||||||
outerType = type.outerType?.let { expandClassType(expansion, it) },
|
outerType = type.outerType?.let { expandClassType(expansion, it) },
|
||||||
visibility = clazz.visibility,
|
visibility = clazz.visibility,
|
||||||
arguments = expandedArguments,
|
arguments = expandedArguments,
|
||||||
|
|||||||
+55
-6
@@ -8,7 +8,10 @@ package org.jetbrains.kotlin.commonizer
|
|||||||
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
import org.jetbrains.kotlin.commonizer.cir.CirEntityId
|
||||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirClassifierIndex
|
||||||
import org.jetbrains.kotlin.commonizer.mergedtree.CirCommonClassifierIdResolver
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirCommonClassifierIdResolver
|
||||||
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiers
|
||||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
|
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
|
||||||
|
import org.jetbrains.kotlin.commonizer.utils.createCirProvidedClassifiers
|
||||||
|
import org.jetbrains.kotlin.commonizer.utils.createCirTreeRoot
|
||||||
import org.jetbrains.kotlin.commonizer.utils.createCirTreeRootFromSourceCode
|
import org.jetbrains.kotlin.commonizer.utils.createCirTreeRootFromSourceCode
|
||||||
|
|
||||||
class CirCommonClassifierIdResolverTest : AbstractInlineSourcesCommonizationTest() {
|
class CirCommonClassifierIdResolverTest : AbstractInlineSourcesCommonizationTest() {
|
||||||
@@ -64,11 +67,11 @@ class CirCommonClassifierIdResolverTest : AbstractInlineSourcesCommonizationTest
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
//assertEquals(setOf("C"), resolver.findCommonId("A"))
|
assertEquals(setOf("C"), resolver.findCommonId("A"))
|
||||||
//assertEquals(setOf("C"), resolver.findCommonId("B"))
|
assertEquals(setOf("C"), resolver.findCommonId("B"))
|
||||||
//assertEquals(setOf("C"), resolver.findCommonId("C"))
|
assertEquals(setOf("C"), resolver.findCommonId("C"))
|
||||||
assertEquals(setOf("C"), resolver.findCommonId("D"))
|
assertEquals(setOf("C"), resolver.findCommonId("D"))
|
||||||
//assertEquals(setOf("C"), resolver.findCommonId("E"))
|
assertEquals(setOf("C"), resolver.findCommonId("E"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -223,12 +226,58 @@ class CirCommonClassifierIdResolverTest : AbstractInlineSourcesCommonizationTest
|
|||||||
assertEquals(setOf("A", "B", "C", "X", "Y", "Z"), resolver.findCommonId("Y"))
|
assertEquals(setOf("A", "B", "C", "X", "Y", "Z"), resolver.findCommonId("Y"))
|
||||||
assertEquals(setOf("A", "B", "C", "X", "Y", "Z"), resolver.findCommonId("Z"))
|
assertEquals(setOf("A", "B", "C", "X", "Y", "Z"), resolver.findCommonId("Z"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun `test sample 7 - with dependencies`() {
|
||||||
|
val dependenciesModule = createModule {
|
||||||
|
source(
|
||||||
|
"""
|
||||||
|
class D_X
|
||||||
|
class D_TA = D_X
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val rootA = createCirTreeRoot {
|
||||||
|
dependency(dependenciesModule)
|
||||||
|
source(
|
||||||
|
"""
|
||||||
|
typealias A = D_TA
|
||||||
|
typealias B = D_X
|
||||||
|
typealias C = B
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val rootB = createCirTreeRoot {
|
||||||
|
dependency(dependenciesModule)
|
||||||
|
source(
|
||||||
|
"""
|
||||||
|
typealias A = D_TA
|
||||||
|
typealias C = D_TA
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val resolver = createCommonClassifierIdResolver(
|
||||||
|
rootA, rootB, dependencies = createCirProvidedClassifiers(dependenciesModule)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(setOf("D_X", "D_TA", "A", "C"), resolver.findCommonId("D_X"))
|
||||||
|
assertEquals(setOf("D_X", "D_TA", "A", "C"), resolver.findCommonId("D_TA"))
|
||||||
|
assertEquals(setOf("D_X", "D_TA", "A", "C"), resolver.findCommonId("A"))
|
||||||
|
assertEquals(setOf("D_X", "D_TA", "A", "C"), resolver.findCommonId("B"))
|
||||||
|
assertEquals(setOf("D_X", "D_TA", "A", "C"), resolver.findCommonId("C"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createCommonClassifierIdResolver(vararg root: CirTreeRoot): CirCommonClassifierIdResolver {
|
private fun createCommonClassifierIdResolver(
|
||||||
|
vararg root: CirTreeRoot,
|
||||||
|
dependencies: CirProvidedClassifiers = CirProvidedClassifiers.EMPTY
|
||||||
|
): CirCommonClassifierIdResolver {
|
||||||
return CirCommonClassifierIdResolver(
|
return CirCommonClassifierIdResolver(
|
||||||
TargetDependent(root.withIndex().associate { (index, root) -> LeafCommonizerTarget(index.toString()) to root })
|
TargetDependent(root.withIndex().associate { (index, root) -> LeafCommonizerTarget(index.toString()) to root })
|
||||||
.mapValue(::CirClassifierIndex)
|
.mapValue(::CirClassifierIndex),
|
||||||
|
dependencies = dependencies
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType?>() {
|
|||||||
commonDependencies = object : CirProvidedClassifiers {
|
commonDependencies = object : CirProvidedClassifiers {
|
||||||
override fun hasClassifier(classifierId: CirEntityId) = classifierId.packageName.isUnderStandardKotlinPackages
|
override fun hasClassifier(classifierId: CirEntityId) = classifierId.packageName.isUnderStandardKotlinPackages
|
||||||
override fun classifier(classifierId: CirEntityId) = error("This method should not be called")
|
override fun classifier(classifierId: CirEntityId) = error("This method should not be called")
|
||||||
|
override fun findTypeAliasesWithUnderlyingType(underlyingClassifier: CirEntityId): List<CirEntityId> = emptyList()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -183,8 +183,7 @@ class HierarchicalPropertyCommonizationTest : AbstractInlineSourcesCommonization
|
|||||||
"(a, b)", """
|
"(a, b)", """
|
||||||
expect class AB expect constructor()
|
expect class AB expect constructor()
|
||||||
typealias TA_AB = AB
|
typealias TA_AB = AB
|
||||||
// https://youtrack.jetbrains.com/issue/KT-47100
|
expect val x: TA_AB
|
||||||
expect val x: AB
|
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -214,8 +213,7 @@ class HierarchicalPropertyCommonizationTest : AbstractInlineSourcesCommonization
|
|||||||
"(a, b)", """
|
"(a, b)", """
|
||||||
expect class AB expect constructor()
|
expect class AB expect constructor()
|
||||||
typealias TA_AB = AB
|
typealias TA_AB = AB
|
||||||
// https://youtrack.jetbrains.com/issue/KT-47100
|
expect val x: TA_AB
|
||||||
expect val x: AB
|
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -84,7 +84,7 @@ class InlineTypeAliasCirNodeTransformerTest : KtInlineSourceCommonizerTestCase()
|
|||||||
visibility = Visibilities.Public,
|
visibility = Visibilities.Public,
|
||||||
supertypes = listOf(
|
supertypes = listOf(
|
||||||
CirProvided.ClassType(
|
CirProvided.ClassType(
|
||||||
classId = CirEntityId.create("dep/ClassA"),
|
classifierId = CirEntityId.create("dep/ClassA"),
|
||||||
outerType = null,
|
outerType = null,
|
||||||
arguments = emptyList(),
|
arguments = emptyList(),
|
||||||
isMarkedNullable = false
|
isMarkedNullable = false
|
||||||
@@ -151,7 +151,7 @@ class InlineTypeAliasCirNodeTransformerTest : KtInlineSourceCommonizerTestCase()
|
|||||||
visibility = Visibilities.Public,
|
visibility = Visibilities.Public,
|
||||||
supertypes = listOf(
|
supertypes = listOf(
|
||||||
CirProvided.ClassType(
|
CirProvided.ClassType(
|
||||||
classId = CirEntityId.create("dep/ClassA"),
|
classifierId = CirEntityId.create("dep/ClassA"),
|
||||||
outerType = null,
|
outerType = null,
|
||||||
arguments = emptyList(),
|
arguments = emptyList(),
|
||||||
isMarkedNullable = false
|
isMarkedNullable = false
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ package org.jetbrains.kotlin.commonizer.utils
|
|||||||
|
|
||||||
import org.intellij.lang.annotations.Language
|
import org.intellij.lang.annotations.Language
|
||||||
import org.jetbrains.kotlin.commonizer.AbstractInlineSourcesCommonizationTest.InlineSourcesCommonizationTestDsl
|
import org.jetbrains.kotlin.commonizer.AbstractInlineSourcesCommonizationTest.InlineSourcesCommonizationTestDsl
|
||||||
|
import org.jetbrains.kotlin.commonizer.ModulesProvider
|
||||||
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiers
|
||||||
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirProvidedClassifiersByModules
|
||||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeModule
|
import org.jetbrains.kotlin.commonizer.tree.CirTreeModule
|
||||||
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
|
import org.jetbrains.kotlin.commonizer.tree.CirTreeRoot
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.library.SerializedMetadata
|
import org.jetbrains.kotlin.library.SerializedMetadata
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
interface InlineSourceBuilder {
|
interface InlineSourceBuilder {
|
||||||
annotation class ModuleBuilderDsl
|
annotation class ModuleBuilderDsl
|
||||||
@@ -94,3 +98,19 @@ fun InlineSourceBuilder.createCirTreeRoot(builder: InlineSourceBuilder.ModuleBui
|
|||||||
fun InlineSourceBuilder.createCirTreeRootFromSourceCode(@Language("kotlin") sourceCode: String): CirTreeRoot {
|
fun InlineSourceBuilder.createCirTreeRootFromSourceCode(@Language("kotlin") sourceCode: String): CirTreeRoot {
|
||||||
return CirTreeRoot(listOf(createCirTreeFromSourceCode(sourceCode)))
|
return CirTreeRoot(listOf(createCirTreeFromSourceCode(sourceCode)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InlineSourceBuilder.ModuleBuilderDsl
|
||||||
|
fun InlineSourceBuilder.createCirProvidedClassifiers(module: InlineSourceBuilder.Module): CirProvidedClassifiers {
|
||||||
|
val modulesProvider = object : ModulesProvider {
|
||||||
|
override val moduleInfos: Collection<ModulesProvider.ModuleInfo> = listOf(
|
||||||
|
ModulesProvider.ModuleInfo(name = "CirProvidedForTest", originalLocation = File("."), cInteropAttributes = null)
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun loadModuleMetadata(name: String): SerializedMetadata {
|
||||||
|
if (name == moduleInfos.single().name) return createMetadata(module)
|
||||||
|
else error("Unknown Module $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CirProvidedClassifiersByModules.load(modulesProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user