[Commonizer] Implement CirNodeRelationship over CommonizerCondition
This commit is contained in:
committed by
Space
parent
8272564ca0
commit
0cf1618960
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("FunctionName")
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.commonizer.mergedtree
|
||||||
|
|
||||||
|
internal sealed class CirNodeRelationship {
|
||||||
|
|
||||||
|
class PreferredNode(val node: CirNode<*, *>) : CirNodeRelationship()
|
||||||
|
|
||||||
|
class ParentNode(val node: CirNode<*, *>) : CirNodeRelationship()
|
||||||
|
|
||||||
|
class Composite private constructor(val relationships: List<CirNodeRelationship>) : CirNodeRelationship() {
|
||||||
|
companion object {
|
||||||
|
operator fun CirNodeRelationship.plus(other: CirNodeRelationship): Composite {
|
||||||
|
if (this is Composite) {
|
||||||
|
return if (other is Composite) {
|
||||||
|
Composite(this.relationships + other.relationships)
|
||||||
|
} else Composite(this.relationships + other)
|
||||||
|
} else if (other is Composite) {
|
||||||
|
return Composite(listOf(this) + other.relationships)
|
||||||
|
}
|
||||||
|
return Composite(listOf(this, other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun ParentNode(node: CirNode<*, *>?): ParentNode? = if (node != null) CirNodeRelationship.ParentNode(node) else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
-37
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.mergedtree
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.commonizer.cir.CirDeclaration
|
|
||||||
import org.jetbrains.kotlin.storage.NullableLazyValue
|
|
||||||
|
|
||||||
|
|
||||||
internal fun interface CommonizerCondition {
|
|
||||||
|
|
||||||
fun allowCommonization(): Boolean
|
|
||||||
|
|
||||||
infix fun and(other: CommonizerCondition) = CommonizerCondition { allowCommonization() && other.allowCommonization() }
|
|
||||||
|
|
||||||
companion object Factory {
|
|
||||||
fun none(): CommonizerCondition = NoneCommonizerCondition
|
|
||||||
|
|
||||||
fun nodeIsNotCommonized(node: CirNode<*, *>) = CommonizerCondition { node.commonDeclaration() == null }
|
|
||||||
|
|
||||||
fun parent(parent: CirNode<*, *>): ParentNodeCommonizerCondition = ParentNodeCommonizerCondition(parent.commonDeclaration)
|
|
||||||
|
|
||||||
fun parent(parent: CirNode<*, *>?): CommonizerCondition = if (parent == null) none() else parent(parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private object NoneCommonizerCondition : CommonizerCondition {
|
|
||||||
override fun allowCommonization(): Boolean = true
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class ParentNodeCommonizerCondition(
|
|
||||||
private val parentNodeCommonDeclaration: NullableLazyValue<CirDeclaration>
|
|
||||||
) : CommonizerCondition {
|
|
||||||
override fun allowCommonization(): Boolean = parentNodeCommonDeclaration() != null
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,7 @@ internal fun buildRootNode(
|
|||||||
): CirRootNode = buildNode(
|
): CirRootNode = buildNode(
|
||||||
storageManager = storageManager,
|
storageManager = storageManager,
|
||||||
size = size,
|
size = size,
|
||||||
commonizerCondition = CommonizerCondition.none(),
|
nodeRelationship = null,
|
||||||
commonizerProducer = ::RootCommonizer,
|
commonizerProducer = ::RootCommonizer,
|
||||||
nodeProducer = ::CirRootNode
|
nodeProducer = ::CirRootNode
|
||||||
)
|
)
|
||||||
@@ -31,7 +31,7 @@ internal fun buildModuleNode(
|
|||||||
): CirModuleNode = buildNode(
|
): CirModuleNode = buildNode(
|
||||||
storageManager = storageManager,
|
storageManager = storageManager,
|
||||||
size = size,
|
size = size,
|
||||||
commonizerCondition = CommonizerCondition.none(),
|
nodeRelationship = null,
|
||||||
commonizerProducer = ::ModuleCommonizer,
|
commonizerProducer = ::ModuleCommonizer,
|
||||||
nodeProducer = ::CirModuleNode
|
nodeProducer = ::CirModuleNode
|
||||||
)
|
)
|
||||||
@@ -42,7 +42,7 @@ internal fun buildPackageNode(
|
|||||||
): CirPackageNode = buildNode(
|
): CirPackageNode = buildNode(
|
||||||
storageManager = storageManager,
|
storageManager = storageManager,
|
||||||
size = size,
|
size = size,
|
||||||
commonizerCondition = CommonizerCondition.none(),
|
nodeRelationship = null,
|
||||||
commonizerProducer = ::PackageCommonizer,
|
commonizerProducer = ::PackageCommonizer,
|
||||||
nodeProducer = ::CirPackageNode
|
nodeProducer = ::CirPackageNode
|
||||||
)
|
)
|
||||||
@@ -51,11 +51,11 @@ internal fun buildPropertyNode(
|
|||||||
storageManager: StorageManager,
|
storageManager: StorageManager,
|
||||||
size: Int,
|
size: Int,
|
||||||
classifiers: CirKnownClassifiers,
|
classifiers: CirKnownClassifiers,
|
||||||
condition: CommonizerCondition = CommonizerCondition.none(),
|
nodeRelationship: CirNodeRelationship? = null,
|
||||||
): CirPropertyNode = buildNode(
|
): CirPropertyNode = buildNode(
|
||||||
storageManager = storageManager,
|
storageManager = storageManager,
|
||||||
size = size,
|
size = size,
|
||||||
commonizerCondition = condition,
|
nodeRelationship = nodeRelationship,
|
||||||
commonizerProducer = { PropertyCommonizer(classifiers) },
|
commonizerProducer = { PropertyCommonizer(classifiers) },
|
||||||
nodeProducer = ::CirPropertyNode
|
nodeProducer = ::CirPropertyNode
|
||||||
)
|
)
|
||||||
@@ -64,11 +64,11 @@ internal fun buildFunctionNode(
|
|||||||
storageManager: StorageManager,
|
storageManager: StorageManager,
|
||||||
size: Int,
|
size: Int,
|
||||||
classifiers: CirKnownClassifiers,
|
classifiers: CirKnownClassifiers,
|
||||||
condition: CommonizerCondition = CommonizerCondition.none(),
|
nodeRelationship: CirNodeRelationship?,
|
||||||
): CirFunctionNode = buildNode(
|
): CirFunctionNode = buildNode(
|
||||||
storageManager = storageManager,
|
storageManager = storageManager,
|
||||||
size = size,
|
size = size,
|
||||||
commonizerCondition = condition,
|
nodeRelationship = nodeRelationship,
|
||||||
commonizerProducer = { FunctionCommonizer(classifiers) },
|
commonizerProducer = { FunctionCommonizer(classifiers) },
|
||||||
nodeProducer = ::CirFunctionNode
|
nodeProducer = ::CirFunctionNode
|
||||||
)
|
)
|
||||||
@@ -77,12 +77,12 @@ internal fun buildClassNode(
|
|||||||
storageManager: StorageManager,
|
storageManager: StorageManager,
|
||||||
size: Int,
|
size: Int,
|
||||||
classifiers: CirKnownClassifiers,
|
classifiers: CirKnownClassifiers,
|
||||||
condition: CommonizerCondition = CommonizerCondition.none(),
|
nodeRelationship: CirNodeRelationship?,
|
||||||
classId: CirEntityId
|
classId: CirEntityId
|
||||||
): CirClassNode = buildNode(
|
): CirClassNode = buildNode(
|
||||||
storageManager = storageManager,
|
storageManager = storageManager,
|
||||||
size = size,
|
size = size,
|
||||||
commonizerCondition = condition,
|
nodeRelationship = nodeRelationship,
|
||||||
commonizerProducer = { ClassCommonizer(classifiers) },
|
commonizerProducer = { ClassCommonizer(classifiers) },
|
||||||
recursionMarker = CirClassRecursionMarker,
|
recursionMarker = CirClassRecursionMarker,
|
||||||
nodeProducer = { targetDeclarations, commonDeclaration ->
|
nodeProducer = { targetDeclarations, commonDeclaration ->
|
||||||
@@ -97,11 +97,11 @@ internal fun buildClassConstructorNode(
|
|||||||
storageManager: StorageManager,
|
storageManager: StorageManager,
|
||||||
size: Int,
|
size: Int,
|
||||||
classifiers: CirKnownClassifiers,
|
classifiers: CirKnownClassifiers,
|
||||||
condition: ParentNodeCommonizerCondition,
|
nodeRelationship: CirNodeRelationship?,
|
||||||
): CirClassConstructorNode = buildNode(
|
): CirClassConstructorNode = buildNode(
|
||||||
storageManager = storageManager,
|
storageManager = storageManager,
|
||||||
size = size,
|
size = size,
|
||||||
commonizerCondition = condition,
|
nodeRelationship = nodeRelationship,
|
||||||
commonizerProducer = { ClassConstructorCommonizer(classifiers) },
|
commonizerProducer = { ClassConstructorCommonizer(classifiers) },
|
||||||
nodeProducer = ::CirClassConstructorNode
|
nodeProducer = ::CirClassConstructorNode
|
||||||
)
|
)
|
||||||
@@ -114,7 +114,7 @@ internal fun buildTypeAliasNode(
|
|||||||
): CirTypeAliasNode = buildNode(
|
): CirTypeAliasNode = buildNode(
|
||||||
storageManager = storageManager,
|
storageManager = storageManager,
|
||||||
size = size,
|
size = size,
|
||||||
commonizerCondition = CommonizerCondition.none(),
|
nodeRelationship = null,
|
||||||
commonizerProducer = { TypeAliasCommonizer(classifiers) },
|
commonizerProducer = { TypeAliasCommonizer(classifiers) },
|
||||||
recursionMarker = CirClassifierRecursionMarker,
|
recursionMarker = CirClassifierRecursionMarker,
|
||||||
nodeProducer = { targetDeclarations, commonDeclaration ->
|
nodeProducer = { targetDeclarations, commonDeclaration ->
|
||||||
@@ -127,14 +127,14 @@ internal fun buildTypeAliasNode(
|
|||||||
private fun <T : CirDeclaration, R : CirDeclaration, N : CirNode<T, R>> buildNode(
|
private fun <T : CirDeclaration, R : CirDeclaration, N : CirNode<T, R>> buildNode(
|
||||||
storageManager: StorageManager,
|
storageManager: StorageManager,
|
||||||
size: Int,
|
size: Int,
|
||||||
commonizerCondition: CommonizerCondition,
|
nodeRelationship: CirNodeRelationship?,
|
||||||
commonizerProducer: () -> Commonizer<T, R>,
|
commonizerProducer: () -> Commonizer<T, R>,
|
||||||
recursionMarker: R? = null,
|
recursionMarker: R? = null,
|
||||||
nodeProducer: (CommonizedGroup<T>, NullableLazyValue<R>) -> N
|
nodeProducer: (CommonizedGroup<T>, NullableLazyValue<R>) -> N
|
||||||
): N {
|
): N {
|
||||||
val targetDeclarations = CommonizedGroup<T>(size)
|
val targetDeclarations = CommonizedGroup<T>(size)
|
||||||
|
|
||||||
val commonComputable = { commonize(commonizerCondition, targetDeclarations, commonizerProducer()) }
|
val commonComputable = { commonize(nodeRelationship, targetDeclarations, commonizerProducer()) }
|
||||||
|
|
||||||
val commonLazyValue = if (recursionMarker != null)
|
val commonLazyValue = if (recursionMarker != null)
|
||||||
storageManager.createRecursionTolerantNullableLazyValue(commonComputable, recursionMarker)
|
storageManager.createRecursionTolerantNullableLazyValue(commonComputable, recursionMarker)
|
||||||
@@ -158,13 +158,22 @@ internal fun <T : Any, R> commonize(
|
|||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun <T : Any, R> commonize(
|
private inline fun <T : Any, R> commonize(
|
||||||
commonizerCondition: CommonizerCondition,
|
nodeRelationship: CirNodeRelationship?,
|
||||||
targetDeclarations: CommonizedGroup<T>,
|
targetDeclarations: CommonizedGroup<T>,
|
||||||
commonizer: Commonizer<T, R>
|
commonizer: Commonizer<T, R>
|
||||||
): R? {
|
): R? {
|
||||||
if (commonizerCondition.allowCommonization()) {
|
if (nodeRelationship.shouldCommonize()) {
|
||||||
return commonize(targetDeclarations, commonizer)
|
return commonize(targetDeclarations, commonizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun CirNodeRelationship?.shouldCommonize(): Boolean {
|
||||||
|
return when (this) {
|
||||||
|
null -> true
|
||||||
|
is CirNodeRelationship.ParentNode -> node.commonDeclaration() != null
|
||||||
|
is CirNodeRelationship.PreferredNode -> node.commonDeclaration() == null
|
||||||
|
is CirNodeRelationship.Composite -> relationships.all { it.shouldCommonize() }
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-4
@@ -7,6 +7,9 @@ package org.jetbrains.kotlin.commonizer.transformer
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.commonizer.cir.*
|
import org.jetbrains.kotlin.commonizer.cir.*
|
||||||
import org.jetbrains.kotlin.commonizer.mergedtree.*
|
import org.jetbrains.kotlin.commonizer.mergedtree.*
|
||||||
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.Composite.Companion.plus
|
||||||
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.ParentNode
|
||||||
|
import org.jetbrains.kotlin.commonizer.mergedtree.CirNodeRelationship.PreferredNode
|
||||||
import org.jetbrains.kotlin.commonizer.transformer.CirNodeTransformer.Context
|
import org.jetbrains.kotlin.commonizer.transformer.CirNodeTransformer.Context
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
@@ -84,21 +87,21 @@ internal class InlineTypeAliasCirNodeTransformer(
|
|||||||
fromAliasedClassNode.constructors.forEach { (key, aliasedConstructorNode) ->
|
fromAliasedClassNode.constructors.forEach { (key, aliasedConstructorNode) ->
|
||||||
val aliasedConstructor = aliasedConstructorNode.targetDeclarations[targetIndex] ?: return@forEach
|
val aliasedConstructor = aliasedConstructorNode.targetDeclarations[targetIndex] ?: return@forEach
|
||||||
intoClassNode.constructors.getOrPut(key) {
|
intoClassNode.constructors.getOrPut(key) {
|
||||||
buildClassConstructorNode(storageManager, targetSize, classifiers, CommonizerCondition.parent(intoClassNode))
|
buildClassConstructorNode(storageManager, targetSize, classifiers, ParentNode(intoClassNode))
|
||||||
}.targetDeclarations[targetIndex] = aliasedConstructor.withContainingClass(intoClass).markedArtificial()
|
}.targetDeclarations[targetIndex] = aliasedConstructor.withContainingClass(intoClass).markedArtificial()
|
||||||
}
|
}
|
||||||
|
|
||||||
fromAliasedClassNode.functions.forEach { (key, aliasedFunctionNode) ->
|
fromAliasedClassNode.functions.forEach { (key, aliasedFunctionNode) ->
|
||||||
val aliasedFunction = aliasedFunctionNode.targetDeclarations[targetIndex] ?: return@forEach
|
val aliasedFunction = aliasedFunctionNode.targetDeclarations[targetIndex] ?: return@forEach
|
||||||
intoClassNode.functions.getOrPut(key) {
|
intoClassNode.functions.getOrPut(key) {
|
||||||
buildFunctionNode(storageManager, targetSize, classifiers, CommonizerCondition.parent(intoClassNode))
|
buildFunctionNode(storageManager, targetSize, classifiers, ParentNode(intoClassNode))
|
||||||
}.targetDeclarations[targetIndex] = aliasedFunction.withContainingClass(intoClass).markedArtificial()
|
}.targetDeclarations[targetIndex] = aliasedFunction.withContainingClass(intoClass).markedArtificial()
|
||||||
}
|
}
|
||||||
|
|
||||||
fromAliasedClassNode.properties.forEach { (key, aliasedPropertyNode) ->
|
fromAliasedClassNode.properties.forEach { (key, aliasedPropertyNode) ->
|
||||||
val aliasedProperty = aliasedPropertyNode.targetDeclarations[targetIndex] ?: return@forEach
|
val aliasedProperty = aliasedPropertyNode.targetDeclarations[targetIndex] ?: return@forEach
|
||||||
intoClassNode.properties.getOrPut(key) {
|
intoClassNode.properties.getOrPut(key) {
|
||||||
buildPropertyNode(storageManager, targetSize, classifiers, CommonizerCondition.parent(intoClassNode))
|
buildPropertyNode(storageManager, targetSize, classifiers, ParentNode(intoClassNode))
|
||||||
}.targetDeclarations[targetIndex] = aliasedProperty.withContainingClass(intoClass).markedArtificial()
|
}.targetDeclarations[targetIndex] = aliasedProperty.withContainingClass(intoClass).markedArtificial()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +115,7 @@ internal class InlineTypeAliasCirNodeTransformer(
|
|||||||
// and if the original typeAliasNode cannot be commonized.
|
// and if the original typeAliasNode cannot be commonized.
|
||||||
// Therefore, this artificial class node acts as a fallback with the original type-alias being still the preferred
|
// Therefore, this artificial class node acts as a fallback with the original type-alias being still the preferred
|
||||||
// option for commonization
|
// option for commonization
|
||||||
condition = CommonizerCondition.parent(this) and CommonizerCondition.nodeIsNotCommonized(typeAliasNode),
|
nodeRelationship = ParentNode(this) + PreferredNode(typeAliasNode),
|
||||||
classId = typeAliasNode.id
|
classId = typeAliasNode.id
|
||||||
)
|
)
|
||||||
this.classes[typeAliasNode.classifierName] = classNode
|
this.classes[typeAliasNode.classifierName] = classNode
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ internal fun CirNodeWithMembers<*, *>.buildClass(
|
|||||||
context: TargetBuildingContext, treeClass: CirTreeClass, parent: CirNode<*, *>? = null
|
context: TargetBuildingContext, treeClass: CirTreeClass, parent: CirNode<*, *>? = null
|
||||||
) {
|
) {
|
||||||
val classNode = classes.getOrPut(treeClass.clazz.name) {
|
val classNode = classes.getOrPut(treeClass.clazz.name) {
|
||||||
buildClassNode(context.storageManager, context.targets, context.classifiers, CommonizerCondition.parent(parent), treeClass.id)
|
buildClassNode(context.storageManager, context.targets, context.classifiers, CirNodeRelationship.ParentNode(parent), treeClass.id)
|
||||||
}
|
}
|
||||||
classNode.targetDeclarations[context.targetIndex] = treeClass.clazz
|
classNode.targetDeclarations[context.targetIndex] = treeClass.clazz
|
||||||
treeClass.functions.forEach { function -> classNode.buildFunction(context, function, classNode) }
|
treeClass.functions.forEach { function -> classNode.buildFunction(context, function, classNode) }
|
||||||
@@ -65,7 +65,7 @@ internal fun CirNodeWithMembers<*, *>.buildFunction(
|
|||||||
context: TargetBuildingContext, treeFunction: CirTreeFunction, parent: CirNode<*, *>? = null
|
context: TargetBuildingContext, treeFunction: CirTreeFunction, parent: CirNode<*, *>? = null
|
||||||
) {
|
) {
|
||||||
val functionNode = functions.getOrPut(treeFunction.approximationKey) {
|
val functionNode = functions.getOrPut(treeFunction.approximationKey) {
|
||||||
buildFunctionNode(context.storageManager, context.targets, context.classifiers, CommonizerCondition.parent(parent))
|
buildFunctionNode(context.storageManager, context.targets, context.classifiers, CirNodeRelationship.ParentNode(parent))
|
||||||
}
|
}
|
||||||
functionNode.targetDeclarations[context.targetIndex] = treeFunction.function
|
functionNode.targetDeclarations[context.targetIndex] = treeFunction.function
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ internal fun CirNodeWithMembers<*, *>.buildProperty(
|
|||||||
context: TargetBuildingContext, treeProperty: CirTreeProperty, parent: CirNode<*, *>? = null
|
context: TargetBuildingContext, treeProperty: CirTreeProperty, parent: CirNode<*, *>? = null
|
||||||
) {
|
) {
|
||||||
val propertyNode = properties.getOrPut(treeProperty.approximationKey) {
|
val propertyNode = properties.getOrPut(treeProperty.approximationKey) {
|
||||||
buildPropertyNode(context.storageManager, context.targets, context.classifiers, CommonizerCondition.parent(parent))
|
buildPropertyNode(context.storageManager, context.targets, context.classifiers, CirNodeRelationship.ParentNode(parent))
|
||||||
}
|
}
|
||||||
propertyNode.targetDeclarations[context.targetIndex] = treeProperty.property
|
propertyNode.targetDeclarations[context.targetIndex] = treeProperty.property
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ internal fun CirClassNode.buildConstructor(
|
|||||||
context: TargetBuildingContext, treeConstructor: CirTreeClassConstructor, parent: CirNode<*, *>
|
context: TargetBuildingContext, treeConstructor: CirTreeClassConstructor, parent: CirNode<*, *>
|
||||||
) {
|
) {
|
||||||
val constructorNode = constructors.getOrPut(treeConstructor.approximationKey) {
|
val constructorNode = constructors.getOrPut(treeConstructor.approximationKey) {
|
||||||
buildClassConstructorNode(context.storageManager, context.targets, context.classifiers, CommonizerCondition.parent(parent))
|
buildClassConstructorNode(context.storageManager, context.targets, context.classifiers, CirNodeRelationship.ParentNode(parent))
|
||||||
}
|
}
|
||||||
constructorNode.targetDeclarations[context.targetIndex] = treeConstructor.constructor
|
constructorNode.targetDeclarations[context.targetIndex] = treeConstructor.constructor
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -425,7 +425,7 @@ class TypeCommonizerTest : AbstractCommonizerTest<CirType, CirType>() {
|
|||||||
storageManager = LockBasedStorageManager.NO_LOCKS,
|
storageManager = LockBasedStorageManager.NO_LOCKS,
|
||||||
size = variants.size,
|
size = variants.size,
|
||||||
classifiers = classifiers,
|
classifiers = classifiers,
|
||||||
condition = CommonizerCondition.none(),
|
nodeRelationship = null,
|
||||||
classId = type.classifierId
|
classId = type.classifierId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user