Multiple minor fixes.
This commit is contained in:
committed by
Dmitry Petrov
parent
da93f57afb
commit
ea83407dce
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.psi2ir
|
package org.jetbrains.kotlin.psi2ir
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
@@ -25,7 +24,6 @@ import org.jetbrains.kotlin.psi.KtElement
|
|||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||||
import org.jetbrains.kotlin.psi.KtSecondaryConstructor
|
import org.jetbrains.kotlin.psi.KtSecondaryConstructor
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.getResolvedCall
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||||
@@ -35,6 +33,7 @@ import org.jetbrains.kotlin.types.KotlinType
|
|||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||||
import org.jetbrains.kotlin.types.upperIfFlexible
|
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
fun KotlinType.containsNull() =
|
fun KotlinType.containsNull() =
|
||||||
KotlinTypeChecker.DEFAULT.isSubtypeOf(builtIns.nullableNothingType, this.upperIfFlexible())
|
KotlinTypeChecker.DEFAULT.isSubtypeOf(builtIns.nullableNothingType, this.upperIfFlexible())
|
||||||
@@ -42,19 +41,12 @@ fun KotlinType.containsNull() =
|
|||||||
fun KtElement.deparenthesize(): KtElement =
|
fun KtElement.deparenthesize(): KtElement =
|
||||||
if (this is KtExpression) KtPsiUtil.safeDeparenthesize(this) else this
|
if (this is KtExpression) KtPsiUtil.safeDeparenthesize(this) else this
|
||||||
|
|
||||||
val CallableDescriptor.explicitReceiverType: KotlinType?
|
|
||||||
get() {
|
|
||||||
extensionReceiverParameter?.let { return it.type }
|
|
||||||
dispatchReceiverParameter?.let { return it.type }
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ResolvedCall<*>.isValueArgumentReorderingRequired(): Boolean {
|
fun ResolvedCall<*>.isValueArgumentReorderingRequired(): Boolean {
|
||||||
var lastValueParameterIndex = -1
|
var lastValueParameterIndex = -1
|
||||||
for (valueArgument in call.valueArguments) {
|
for (valueArgument in call.valueArguments) {
|
||||||
val argumentMapping = getArgumentMapping(valueArgument)
|
val argumentMapping = getArgumentMapping(valueArgument)
|
||||||
if (argumentMapping !is ArgumentMatch || argumentMapping.isError()) {
|
if (argumentMapping !is ArgumentMatch || argumentMapping.isError()) {
|
||||||
throw AssertionError("Value argument in function call is mapped with error")
|
throw Exception("Value argument in function call is mapped with error")
|
||||||
}
|
}
|
||||||
val argumentIndex = argumentMapping.valueParameter.index
|
val argumentIndex = argumentMapping.valueParameter.index
|
||||||
if (argumentIndex < lastValueParameterIndex) {
|
if (argumentIndex < lastValueParameterIndex) {
|
||||||
|
|||||||
@@ -86,8 +86,13 @@ class BodyGenerator(val scopeOwner: DeclarationDescriptor, override val context:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun StatementGenerator.generateReturnExpression(ktExpression: KtExpression, irBlockBody: IrBlockBodyImpl) {
|
private fun StatementGenerator.generateReturnExpression(ktExpression: KtExpression, irBlockBody: IrBlockBodyImpl) {
|
||||||
val irExpression = generateExpression(ktExpression)
|
val irReturnExpression = generateStatement(ktExpression)
|
||||||
irBlockBody.addStatement(irExpression.wrapWithReturn())
|
if (irReturnExpression is IrExpression) {
|
||||||
|
irBlockBody.addStatement(irReturnExpression.wrapWithReturn())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
irBlockBody.addStatement(irReturnExpression)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrExpression.wrapWithReturn() =
|
private fun IrExpression.wrapWithReturn() =
|
||||||
|
|||||||
@@ -56,9 +56,9 @@ class CallGenerator(statementGenerator: StatementGenerator): StatementGeneratorE
|
|||||||
if (descriptor !is ConstructorDescriptor) throw AssertionError("Constructor expected: $descriptor")
|
if (descriptor !is ConstructorDescriptor) throw AssertionError("Constructor expected: $descriptor")
|
||||||
|
|
||||||
return call.callReceiver.call { dispatchReceiver, extensionReceiver ->
|
return call.callReceiver.call { dispatchReceiver, extensionReceiver ->
|
||||||
if (dispatchReceiver != null) throw AssertionError("Dispatch receiver should be null: $dispatchReceiver")
|
|
||||||
if (extensionReceiver != null) throw AssertionError("Extension receiver should be null: $extensionReceiver")
|
|
||||||
val irCall = IrDelegatingConstructorCallImpl(startOffset, endOffset, descriptor)
|
val irCall = IrDelegatingConstructorCallImpl(startOffset, endOffset, descriptor)
|
||||||
|
irCall.dispatchReceiver = dispatchReceiver?.load()
|
||||||
|
irCall.extensionReceiver = extensionReceiver?.load()
|
||||||
addParametersToCall(startOffset, endOffset, call, irCall, descriptor.returnType)
|
addParametersToCall(startOffset, endOffset, call, irCall, descriptor.returnType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-5
@@ -19,13 +19,11 @@ package org.jetbrains.kotlin.psi2ir.generators
|
|||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.backend.common.DataClassMethodGenerator
|
import org.jetbrains.kotlin.backend.common.DataClassMethodGenerator
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClassImpl
|
import org.jetbrains.kotlin.ir.declarations.IrClassImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.mapValueParameters
|
import org.jetbrains.kotlin.ir.expressions.mapValueParameters
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.psi.KtParameter
|
import org.jetbrains.kotlin.psi.KtParameter
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
@@ -91,9 +89,17 @@ class DataClassMembersGenerator(
|
|||||||
private val IMUL = INT.findFirstFunction("times") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, INT_TYPE) }
|
private val IMUL = INT.findFirstFunction("times") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, INT_TYPE) }
|
||||||
private val IADD = INT.findFirstFunction("plus") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, INT_TYPE) }
|
private val IADD = INT.findFirstFunction("plus") { KotlinTypeChecker.DEFAULT.equalTypes(it.valueParameters[0].type, INT_TYPE) }
|
||||||
|
|
||||||
private fun getHashCodeFunction(type: KotlinType): CallableDescriptor =
|
private fun getHashCodeFunction(type: KotlinType): CallableDescriptor {
|
||||||
(type.constructor.declarationDescriptor as? ClassDescriptor)?.findFirstFunction("hashCode") { it.valueParameters.isEmpty() } ?:
|
val typeConstructorDescriptor = type.constructor.declarationDescriptor
|
||||||
throw AssertionError("Unexpected type: $type")
|
when (typeConstructorDescriptor) {
|
||||||
|
is ClassDescriptor ->
|
||||||
|
return typeConstructorDescriptor.findFirstFunction("hashCode") { it.valueParameters.isEmpty() }
|
||||||
|
is TypeParameterDescriptor ->
|
||||||
|
return getHashCodeFunction(context.builtIns.anyType) // TODO
|
||||||
|
else ->
|
||||||
|
throw AssertionError("Unexpected type: $type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
|
override fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
|
||||||
buildMember(function) {
|
buildMember(function) {
|
||||||
|
|||||||
+3
-2
@@ -60,8 +60,9 @@ class ErrorExpressionGenerator(statementGenerator: StatementGenerator) : Stateme
|
|||||||
val type = getErrorExpressionType(ktName)
|
val type = getErrorExpressionType(ktName)
|
||||||
|
|
||||||
val irErrorCall = IrErrorCallExpressionImpl(ktName.startOffset, ktName.endOffset, type, "") // TODO problem description?
|
val irErrorCall = IrErrorCallExpressionImpl(ktName.startOffset, ktName.endOffset, type, "") // TODO problem description?
|
||||||
irErrorCall.explicitReceiver = (ktName.parent as? KtDotQualifiedExpression)?.let {
|
irErrorCall.explicitReceiver = (ktName.parent as? KtDotQualifiedExpression)?.let { ktParent ->
|
||||||
statementGenerator.generateExpression(it.receiverExpression)
|
if (ktParent.receiverExpression == ktName) null
|
||||||
|
else statementGenerator.generateExpression(ktParent.receiverExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
return irErrorCall
|
return irErrorCall
|
||||||
|
|||||||
+1
-4
@@ -22,16 +22,13 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
|||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
|
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
|
||||||
import org.jetbrains.kotlin.storage.StorageManager
|
|
||||||
|
|
||||||
class GeneratorContext(
|
class GeneratorContext(
|
||||||
val moduleDescriptor: ModuleDescriptor,
|
val moduleDescriptor: ModuleDescriptor,
|
||||||
val bindingContext: BindingContext
|
val bindingContext: BindingContext
|
||||||
) {
|
) {
|
||||||
val storageManager: StorageManager = LockBasedStorageManager.NO_LOCKS
|
|
||||||
val sourceManager = PsiSourceManager()
|
val sourceManager = PsiSourceManager()
|
||||||
val syntheticDescriptorsFactory by lazy { SyntheticDescriptorsFactory(storageManager) }
|
val syntheticDescriptorsFactory = SyntheticDescriptorsFactory()
|
||||||
|
|
||||||
val reflectionTypes = ReflectionTypes(moduleDescriptor)
|
val reflectionTypes = ReflectionTypes(moduleDescriptor)
|
||||||
val builtIns: KotlinBuiltIns get() = moduleDescriptor.builtIns
|
val builtIns: KotlinBuiltIns get() = moduleDescriptor.builtIns
|
||||||
|
|||||||
+20
-10
@@ -20,29 +20,36 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.IrSyntheticPropertyAccessorDescriptor.Kind.MEMBER_PROPERTY
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrSyntheticPropertyAccessorDescriptor.Kind.STATIC_PROPERTY
|
import org.jetbrains.kotlin.ir.descriptors.IrSyntheticPropertyAccessorDescriptor.Kind.STATIC_PROPERTY
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrSyntheticPropertyGetterDescriptorImpl
|
import org.jetbrains.kotlin.ir.descriptors.IrSyntheticPropertyGetterDescriptorImpl
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrSyntheticPropertySetterDescriptorImpl
|
import org.jetbrains.kotlin.ir.descriptors.IrSyntheticPropertySetterDescriptorImpl
|
||||||
import org.jetbrains.kotlin.storage.StorageManager
|
|
||||||
import java.lang.AssertionError
|
import java.lang.AssertionError
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class SyntheticDescriptorsFactory(storageManager: StorageManager) {
|
class SyntheticDescriptorsFactory {
|
||||||
private val propertyGetters = storageManager.createMemoizedFunction<PropertyDescriptor, PropertyGetterDescriptor> {
|
private val propertyGetters = HashMap<PropertyDescriptor, PropertyGetterDescriptor>()
|
||||||
property ->
|
|
||||||
when {
|
private fun generateGetter(property: PropertyDescriptor): PropertyGetterDescriptor {
|
||||||
|
return when {
|
||||||
isStaticPropertyInClass(property) ->
|
isStaticPropertyInClass(property) ->
|
||||||
IrSyntheticPropertyGetterDescriptorImpl(property, STATIC_PROPERTY)
|
IrSyntheticPropertyGetterDescriptorImpl(property, STATIC_PROPERTY)
|
||||||
|
isPropertyInClass(property) ->
|
||||||
|
IrSyntheticPropertyGetterDescriptorImpl(property, MEMBER_PROPERTY)
|
||||||
else ->
|
else ->
|
||||||
throw AssertionError("Don't know how to create synthetic getter for $property")
|
throw AssertionError("Don't know how to create synthetic getter for $property")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val propertySetters = storageManager.createMemoizedFunction<PropertyDescriptor, PropertySetterDescriptor> {
|
private val propertySetters = HashMap<PropertyDescriptor, PropertySetterDescriptor>()
|
||||||
property ->
|
|
||||||
when {
|
private fun generateSetter(property: PropertyDescriptor): PropertySetterDescriptor {
|
||||||
|
return when {
|
||||||
isStaticPropertyInClass(property) ->
|
isStaticPropertyInClass(property) ->
|
||||||
IrSyntheticPropertySetterDescriptorImpl(property, STATIC_PROPERTY)
|
IrSyntheticPropertySetterDescriptorImpl(property, STATIC_PROPERTY)
|
||||||
|
isPropertyInClass(property) ->
|
||||||
|
IrSyntheticPropertySetterDescriptorImpl(property, MEMBER_PROPERTY)
|
||||||
else ->
|
else ->
|
||||||
throw AssertionError("Don't know how to create synthetic setter for $property")
|
throw AssertionError("Don't know how to create synthetic setter for $property")
|
||||||
}
|
}
|
||||||
@@ -53,9 +60,12 @@ class SyntheticDescriptorsFactory(storageManager: StorageManager) {
|
|||||||
propertyDescriptor.dispatchReceiverParameter == null &&
|
propertyDescriptor.dispatchReceiverParameter == null &&
|
||||||
propertyDescriptor.extensionReceiverParameter == null
|
propertyDescriptor.extensionReceiverParameter == null
|
||||||
|
|
||||||
|
private fun isPropertyInClass(propertyDescriptor: PropertyDescriptor): Boolean =
|
||||||
|
propertyDescriptor.containingDeclaration is ClassDescriptor
|
||||||
|
|
||||||
fun getOrCreatePropertyGetter(propertyDescriptor: PropertyDescriptor): PropertyGetterDescriptor =
|
fun getOrCreatePropertyGetter(propertyDescriptor: PropertyDescriptor): PropertyGetterDescriptor =
|
||||||
propertyGetters(propertyDescriptor)
|
propertyGetters.getOrPut(propertyDescriptor) { generateGetter(propertyDescriptor) }
|
||||||
|
|
||||||
fun getOrCreatePropertySetter(propertyDescriptor: PropertyDescriptor): PropertySetterDescriptor =
|
fun getOrCreatePropertySetter(propertyDescriptor: PropertyDescriptor): PropertySetterDescriptor =
|
||||||
propertySetters(propertyDescriptor)
|
propertySetters.getOrPut(propertyDescriptor) { generateSetter(propertyDescriptor) }
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -21,6 +21,6 @@ import java.io.File
|
|||||||
abstract class AbstractPsi2IrAcceptanceTestCase : AbstractIrGeneratorTestCase() {
|
abstract class AbstractPsi2IrAcceptanceTestCase : AbstractIrGeneratorTestCase() {
|
||||||
override fun doTest(wholeFile: File, testFiles: List<TestFile>) {
|
override fun doTest(wholeFile: File, testFiles: List<TestFile>) {
|
||||||
// Just make sure that nothing interesting happens - e.g., no exceptions thrown
|
// Just make sure that nothing interesting happens - e.g., no exceptions thrown
|
||||||
generateIrFilesAsSingleModule(testFiles)
|
generateIrFilesAsSingleModule(testFiles, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir
|
package org.jetbrains.kotlin.ir
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.ir.util.render
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
|
||||||
interface IrElement {
|
interface IrElement {
|
||||||
@@ -73,7 +74,7 @@ fun IrElement.assertDetached() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun IrElement.throwNoSuchSlot(slot: Int): Nothing =
|
fun IrElement.throwNoSuchSlot(slot: Int): Nothing =
|
||||||
throw AssertionError("$this: no such slot $slot")
|
throw AssertionError("${this.render()}: no such slot $slot")
|
||||||
|
|
||||||
inline fun <reified T : IrElement> IrElement.assertCast(): T =
|
inline fun <reified T : IrElement> IrElement.assertCast(): T =
|
||||||
if (this is T) this else throw AssertionError("Expected ${T::class.simpleName}: $this")
|
if (this is T) this else throw AssertionError("Expected ${T::class.simpleName}: $this")
|
||||||
+2
-1
@@ -27,7 +27,8 @@ import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
|
|||||||
|
|
||||||
interface IrSyntheticPropertyAccessorDescriptor : PropertyAccessorDescriptor {
|
interface IrSyntheticPropertyAccessorDescriptor : PropertyAccessorDescriptor {
|
||||||
enum class Kind {
|
enum class Kind {
|
||||||
STATIC_PROPERTY
|
STATIC_PROPERTY,
|
||||||
|
MEMBER_PROPERTY
|
||||||
}
|
}
|
||||||
|
|
||||||
val kind: Kind
|
val kind: Kind
|
||||||
|
|||||||
+14
-3
@@ -18,9 +18,7 @@ package org.jetbrains.kotlin.ir.expressions
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.ir.SETTER_ARGUMENT_INDEX
|
import org.jetbrains.kotlin.ir.*
|
||||||
import org.jetbrains.kotlin.ir.assertDetached
|
|
||||||
import org.jetbrains.kotlin.ir.detach
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
|
||||||
abstract class IrPropertyAccessorCallBase(
|
abstract class IrPropertyAccessorCallBase(
|
||||||
@@ -106,4 +104,17 @@ class IrSetterCallImpl(
|
|||||||
argumentImpl?.detach()
|
argumentImpl?.detach()
|
||||||
argumentImpl = null
|
argumentImpl = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getChild(slot: Int): IrElement? =
|
||||||
|
when (slot) {
|
||||||
|
SETTER_ARGUMENT_INDEX -> argumentImpl
|
||||||
|
else -> super.getChild(slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun replaceChild(slot: Int, newChild: IrElement) {
|
||||||
|
when (slot) {
|
||||||
|
SETTER_ARGUMENT_INDEX -> putArgument(slot, newChild.assertCast())
|
||||||
|
else -> super.replaceChild(slot, newChild)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user