backend/common: copy some sources from JVM BE
This commit is contained in:
committed by
SvyatoslavScherbina
parent
8071b4999f
commit
191c98ec60
+33
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.jvm
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.descriptors.JvmSharedVariablesManager
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.descriptors.SpecialDescriptorsFactory
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
|
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
|
||||||
|
|
||||||
|
class JvmBackendContext(
|
||||||
|
val state: GenerationState,
|
||||||
|
val psiSourceManager: PsiSourceManager,
|
||||||
|
val irBuiltIns: IrBuiltIns
|
||||||
|
) {
|
||||||
|
val builtIns = state.module.builtIns
|
||||||
|
val specialDescriptorsFactory = SpecialDescriptorsFactory(psiSourceManager, builtIns)
|
||||||
|
val sharedVariablesManager = JvmSharedVariablesManager(builtIns)
|
||||||
|
}
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.jvm
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.lower.*
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
|
|
||||||
|
class JvmLower(val context: JvmBackendContext) {
|
||||||
|
fun lower(irFile: IrFile) {
|
||||||
|
// TODO run lowering passes as callbacks in bottom-up visitor
|
||||||
|
FileClassLowering(context).lower(irFile)
|
||||||
|
ConstAndJvmFieldPropertiesLowering().lower(irFile)
|
||||||
|
PropertiesLowering().lower(irFile)
|
||||||
|
InterfaceLowering(context.state).runOnFilePostfix(irFile)
|
||||||
|
InterfaceDelegationLowering(context.state).runOnFilePostfix(irFile)
|
||||||
|
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
||||||
|
InnerClassesLowering(context).runOnFilePostfix(irFile)
|
||||||
|
InnerClassConstructorCallsLowering(context).runOnFilePostfix(irFile)
|
||||||
|
LocalFunctionsLowering(context).runOnFilePostfix(irFile)
|
||||||
|
EnumClassLowering(context).runOnFilePostfix(irFile)
|
||||||
|
ObjectClassLowering(context).runOnFilePostfix(irFile)
|
||||||
|
InitializersLowering(context).runOnFilePostfix(irFile)
|
||||||
|
SingletonReferencesLowering(context).runOnFilePostfix(irFile)
|
||||||
|
SyntheticAccessorLowering(context.state).lower(irFile)
|
||||||
|
BridgeLowering(context.state).runOnFilePostfix(irFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileLoweringPass {
|
||||||
|
fun lower(irFile: IrFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ClassLoweringPass {
|
||||||
|
fun lower(irClass: IrClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FunctionLoweringPass {
|
||||||
|
fun lower(irFunction: IrFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BodyLoweringPass {
|
||||||
|
fun lower(irBody: IrBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ClassLoweringPass.runOnFilePostfix(irFile: IrFile) {
|
||||||
|
irFile.acceptVoid(object : IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitClass(declaration: IrClass) {
|
||||||
|
declaration.acceptChildrenVoid(this)
|
||||||
|
lower(declaration)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BodyLoweringPass.runOnFilePostfix(irFile: IrFile) {
|
||||||
|
irFile.acceptVoid(object : IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitBody(body: IrBody) {
|
||||||
|
body.acceptChildrenVoid(this)
|
||||||
|
lower(body)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FunctionLoweringPass.runOnFilePostfix(irFile: IrFile) {
|
||||||
|
irFile.acceptVoid(object : IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
|
declaration.acceptChildrenVoid(this)
|
||||||
|
lower(declaration)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
+203
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.jvm.descriptors
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrSetVariable
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
|
import org.jetbrains.kotlin.types.*
|
||||||
|
|
||||||
|
interface SharedVariablesManager {
|
||||||
|
fun createSharedVariableDescriptor(variableDescriptor: VariableDescriptor): VariableDescriptor
|
||||||
|
fun defineSharedValue(sharedVariableDescriptor: VariableDescriptor, originalDeclaration: IrVariable): IrStatement
|
||||||
|
fun getSharedValue(sharedVariableDescriptor: VariableDescriptor, originalGet: IrGetValue): IrExpression
|
||||||
|
fun setSharedValue(sharedVariableDescriptor: VariableDescriptor, originalSet: IrSetVariable): IrExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
class JvmSharedVariablesManager(val builtIns: KotlinBuiltIns) : SharedVariablesManager {
|
||||||
|
private val kotlinJvmInternalPackage = KnownPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.jvm.internal"))
|
||||||
|
private val refNamespaceClass = KnownClassDescriptor.createClass(Name.identifier("Ref"), kotlinJvmInternalPackage, listOf(builtIns.anyType))
|
||||||
|
|
||||||
|
private class PrimitiveRefDescriptorsProvider(type: KotlinType, refClass: ClassDescriptor) {
|
||||||
|
val refType: KotlinType = refClass.defaultType
|
||||||
|
|
||||||
|
val refConstructor: ClassConstructorDescriptor =
|
||||||
|
ClassConstructorDescriptorImpl.create(refClass, Annotations.EMPTY, true, SourceElement.NO_SOURCE).apply {
|
||||||
|
initialize(emptyList(), Visibilities.PUBLIC, emptyList())
|
||||||
|
returnType = refType
|
||||||
|
}
|
||||||
|
|
||||||
|
val elementField: PropertyDescriptor =
|
||||||
|
PropertyDescriptorImpl.create(
|
||||||
|
refClass, Annotations.EMPTY, Modality.FINAL, Visibilities.PUBLIC, true,
|
||||||
|
Name.identifier("element"), CallableMemberDescriptor.Kind.DECLARATION, SourceElement.NO_SOURCE,
|
||||||
|
false, false, false, false
|
||||||
|
).initialize(type, dispatchReceiverParameter = refClass.thisAsReceiverParameter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val primitiveRefDescriptorProviders: Map<PrimitiveType, PrimitiveRefDescriptorsProvider> =
|
||||||
|
PrimitiveType.values().associate {
|
||||||
|
val type = builtIns.getPrimitiveKotlinType(it)
|
||||||
|
|
||||||
|
val refClassName = Name.identifier(it.typeName.asString() + "Ref")
|
||||||
|
val refClass = KnownClassDescriptor.createClass(refClassName, refNamespaceClass, listOf(builtIns.anyType))
|
||||||
|
|
||||||
|
it to PrimitiveRefDescriptorsProvider(type, refClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ObjectRefDescriptorsProvider {
|
||||||
|
val genericRefClass: ClassDescriptor =
|
||||||
|
KnownClassDescriptor.createClassWithTypeParameters(
|
||||||
|
Name.identifier("ObjectRef"), refNamespaceClass, listOf(builtIns.anyType), listOf(Name.identifier("T"))
|
||||||
|
)
|
||||||
|
|
||||||
|
val genericRefConstructor: ClassConstructorDescriptor =
|
||||||
|
ClassConstructorDescriptorImpl.create(genericRefClass, Annotations.EMPTY, true, SourceElement.NO_SOURCE).apply {
|
||||||
|
initialize(emptyList(), Visibilities.PUBLIC)
|
||||||
|
val typeParameter = typeParameters[0]
|
||||||
|
val typeParameterType = KotlinTypeFactory.simpleType(Annotations.EMPTY, typeParameter.typeConstructor, listOf(), false, MemberScope.Empty)
|
||||||
|
returnType = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, genericRefClass, listOf(TypeProjectionImpl(Variance.INVARIANT, typeParameterType)))
|
||||||
|
}
|
||||||
|
|
||||||
|
val constructorTypeParameter: TypeParameterDescriptor =
|
||||||
|
genericRefConstructor.typeParameters[0]
|
||||||
|
|
||||||
|
fun getSubstitutedRefConstructor(valueType: KotlinType): ClassConstructorDescriptor =
|
||||||
|
genericRefConstructor.substitute(TypeSubstitutor.create(
|
||||||
|
mapOf(constructorTypeParameter.typeConstructor to TypeProjectionImpl(Variance.INVARIANT, valueType))
|
||||||
|
))!!
|
||||||
|
|
||||||
|
val genericElementField: PropertyDescriptor =
|
||||||
|
PropertyDescriptorImpl.create(
|
||||||
|
genericRefClass, Annotations.EMPTY, Modality.FINAL, Visibilities.PUBLIC, true,
|
||||||
|
Name.identifier("element"), CallableMemberDescriptor.Kind.DECLARATION, SourceElement.NO_SOURCE,
|
||||||
|
false, false, false, false
|
||||||
|
).initialize(
|
||||||
|
type = builtIns.anyType,
|
||||||
|
dispatchReceiverParameter = genericRefClass.thisAsReceiverParameter
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getRefType(valueType: KotlinType) =
|
||||||
|
KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, genericRefClass, listOf(TypeProjectionImpl(Variance.INVARIANT, valueType)))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val objectRefDescriptorsProvider = ObjectRefDescriptorsProvider()
|
||||||
|
|
||||||
|
override fun createSharedVariableDescriptor(variableDescriptor: VariableDescriptor): VariableDescriptor =
|
||||||
|
LocalVariableDescriptor(
|
||||||
|
variableDescriptor.containingDeclaration, variableDescriptor.annotations, variableDescriptor.name,
|
||||||
|
getSharedVariableType(variableDescriptor.type),
|
||||||
|
false, false, variableDescriptor.source
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun defineSharedValue(sharedVariableDescriptor: VariableDescriptor, originalDeclaration: IrVariable): IrStatement {
|
||||||
|
val valueType = originalDeclaration.descriptor.type
|
||||||
|
val primitiveRefDescriptorsProvider = primitiveRefDescriptorProviders[getPrimitiveType(valueType)]
|
||||||
|
|
||||||
|
val refConstructor =
|
||||||
|
primitiveRefDescriptorsProvider?.refConstructor ?:
|
||||||
|
objectRefDescriptorsProvider.getSubstitutedRefConstructor(valueType)
|
||||||
|
|
||||||
|
val refConstructorTypeArguments =
|
||||||
|
if (primitiveRefDescriptorsProvider != null) null
|
||||||
|
else mapOf(objectRefDescriptorsProvider.constructorTypeParameter to valueType)
|
||||||
|
|
||||||
|
val elementPropertyDescriptor =
|
||||||
|
primitiveRefDescriptorsProvider?.elementField ?:
|
||||||
|
objectRefDescriptorsProvider.genericElementField
|
||||||
|
|
||||||
|
val refConstructorCall = IrCallImpl(
|
||||||
|
originalDeclaration.startOffset, originalDeclaration.endOffset,
|
||||||
|
refConstructor, refConstructorTypeArguments
|
||||||
|
)
|
||||||
|
val sharedVariableDeclaration = IrVariableImpl(
|
||||||
|
originalDeclaration.startOffset, originalDeclaration.endOffset, originalDeclaration.origin,
|
||||||
|
sharedVariableDescriptor, refConstructorCall
|
||||||
|
)
|
||||||
|
|
||||||
|
val initializer = originalDeclaration.initializer ?:
|
||||||
|
return sharedVariableDeclaration
|
||||||
|
|
||||||
|
val sharedVariableInitialization = IrSetFieldImpl(
|
||||||
|
initializer.startOffset, initializer.endOffset,
|
||||||
|
elementPropertyDescriptor,
|
||||||
|
IrGetValueImpl(initializer.startOffset, initializer.endOffset, sharedVariableDescriptor),
|
||||||
|
initializer
|
||||||
|
)
|
||||||
|
|
||||||
|
return IrCompositeImpl(
|
||||||
|
originalDeclaration.startOffset, originalDeclaration.endOffset, builtIns.unitType, null,
|
||||||
|
listOf(sharedVariableDeclaration, sharedVariableInitialization)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getElementFieldDescriptor(valueType: KotlinType): PropertyDescriptor {
|
||||||
|
val primitiveRefDescriptorsProvider = primitiveRefDescriptorProviders[getPrimitiveType(valueType)]
|
||||||
|
|
||||||
|
return primitiveRefDescriptorsProvider?.elementField ?:
|
||||||
|
objectRefDescriptorsProvider.genericElementField
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSharedValue(sharedVariableDescriptor: VariableDescriptor, originalGet: IrGetValue): IrExpression =
|
||||||
|
IrGetFieldImpl(
|
||||||
|
originalGet.startOffset, originalGet.endOffset,
|
||||||
|
getElementFieldDescriptor(originalGet.descriptor.type),
|
||||||
|
IrGetValueImpl(originalGet.startOffset, originalGet.endOffset, sharedVariableDescriptor),
|
||||||
|
originalGet.origin
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun setSharedValue(sharedVariableDescriptor: VariableDescriptor, originalSet: IrSetVariable): IrExpression =
|
||||||
|
IrSetFieldImpl(
|
||||||
|
originalSet.startOffset, originalSet.endOffset,
|
||||||
|
getElementFieldDescriptor(originalSet.descriptor.type),
|
||||||
|
IrGetValueImpl(originalSet.startOffset, originalSet.endOffset, sharedVariableDescriptor),
|
||||||
|
originalSet.value,
|
||||||
|
originalSet.origin
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getSharedVariableType(valueType: KotlinType): KotlinType =
|
||||||
|
primitiveRefDescriptorProviders[getPrimitiveType(valueType)]?.refType ?:
|
||||||
|
objectRefDescriptorsProvider.getRefType(valueType)
|
||||||
|
|
||||||
|
private fun getPrimitiveType(type: KotlinType): PrimitiveType? =
|
||||||
|
when {
|
||||||
|
KotlinBuiltIns.isBoolean(type) -> PrimitiveType.BOOLEAN
|
||||||
|
KotlinBuiltIns.isChar(type) -> PrimitiveType.CHAR
|
||||||
|
KotlinBuiltIns.isByte(type) -> PrimitiveType.BYTE
|
||||||
|
KotlinBuiltIns.isShort(type) -> PrimitiveType.SHORT
|
||||||
|
KotlinBuiltIns.isInt(type) -> PrimitiveType.INT
|
||||||
|
KotlinBuiltIns.isLong(type) -> PrimitiveType.LONG
|
||||||
|
KotlinBuiltIns.isFloat(type) -> PrimitiveType.FLOAT
|
||||||
|
KotlinBuiltIns.isDouble(type) -> PrimitiveType.DOUBLE
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+369
@@ -0,0 +1,369 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.jvm.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.AbstractClosureAnnotator
|
||||||
|
import org.jetbrains.kotlin.backend.common.Closure
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.ClassLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.parents
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class LocalFunctionsLowering(val context: JvmBackendContext): ClassLoweringPass {
|
||||||
|
override fun lower(irClass: IrClass) {
|
||||||
|
irClass.declarations.transformFlat { memberDeclaration ->
|
||||||
|
if (memberDeclaration is IrFunction)
|
||||||
|
LocalFunctionsTransformer(memberDeclaration).lowerLocalFunctions()
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LocalFunctionContext(val declaration: IrFunction) {
|
||||||
|
lateinit var closure: Closure
|
||||||
|
|
||||||
|
val closureParametersCount: Int get() = closure.capturedValues.size
|
||||||
|
|
||||||
|
lateinit var transformedDescriptor: FunctionDescriptor
|
||||||
|
|
||||||
|
val old2new: MutableMap<ValueDescriptor, ValueParameterDescriptor> = HashMap()
|
||||||
|
|
||||||
|
var index: Int = -1
|
||||||
|
|
||||||
|
override fun toString(): String =
|
||||||
|
"LocalFunctionContext for ${declaration.descriptor}"
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class LocalFunctionsTransformer(val memberFunction: IrFunction) {
|
||||||
|
val localFunctions: MutableMap<FunctionDescriptor, LocalFunctionContext> = LinkedHashMap()
|
||||||
|
val new2old: MutableMap<ValueParameterDescriptor, ValueDescriptor> = HashMap()
|
||||||
|
|
||||||
|
fun lowerLocalFunctions(): List<IrDeclaration>? {
|
||||||
|
collectLocalFunctions()
|
||||||
|
if (localFunctions.isEmpty()) return null
|
||||||
|
|
||||||
|
collectClosures()
|
||||||
|
|
||||||
|
transformDescriptors()
|
||||||
|
|
||||||
|
rewriteBodies()
|
||||||
|
|
||||||
|
return collectRewrittenDeclarations()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectRewrittenDeclarations(): ArrayList<IrDeclaration> =
|
||||||
|
ArrayList<IrDeclaration>(localFunctions.size + 1).apply {
|
||||||
|
add(memberFunction)
|
||||||
|
|
||||||
|
localFunctions.values.mapTo(this) {
|
||||||
|
val original = it.declaration
|
||||||
|
IrFunctionImpl(
|
||||||
|
original.startOffset, original.endOffset, original.origin,
|
||||||
|
it.transformedDescriptor,
|
||||||
|
original.body
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class FunctionBodiesRewriter(val localFunctionContext: LocalFunctionContext?) : IrElementTransformerVoid() {
|
||||||
|
|
||||||
|
override fun visitClass(declaration: IrClass): IrStatement {
|
||||||
|
// ignore local classes for now
|
||||||
|
return declaration
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunction(declaration: IrFunction): IrStatement {
|
||||||
|
// replace local function definition with an empty composite
|
||||||
|
return IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.builtIns.unitType)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||||
|
val remapped = localFunctionContext?.let { it.old2new[expression.descriptor] }
|
||||||
|
return if (remapped == null)
|
||||||
|
expression
|
||||||
|
else
|
||||||
|
IrGetValueImpl(expression.startOffset, expression.endOffset, remapped, expression.origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
val oldCallee = expression.descriptor.original
|
||||||
|
val localFunctionData = localFunctions[oldCallee] ?: return expression
|
||||||
|
|
||||||
|
val newCallee = localFunctionData.transformedDescriptor
|
||||||
|
|
||||||
|
val newCall = createNewCall(expression, newCallee).fillArguments(localFunctionData, expression)
|
||||||
|
|
||||||
|
return newCall
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T : IrMemberAccessExpression> T.fillArguments(calleeContext: LocalFunctionContext, oldExpression: IrMemberAccessExpression): T {
|
||||||
|
val closureParametersCount = calleeContext.closureParametersCount
|
||||||
|
|
||||||
|
mapValueParametersIndexed { index, newValueParameterDescriptor ->
|
||||||
|
val capturedValueDescriptor = new2old[newValueParameterDescriptor] ?:
|
||||||
|
throw AssertionError("Non-mapped parameter $newValueParameterDescriptor")
|
||||||
|
if (index >= closureParametersCount)
|
||||||
|
oldExpression.getValueArgument(capturedValueDescriptor as ValueParameterDescriptor)
|
||||||
|
else {
|
||||||
|
val remappedValueDescriptor = localFunctionContext?.let { it.old2new[capturedValueDescriptor] }
|
||||||
|
IrGetValueImpl(oldExpression.startOffset, oldExpression.endOffset,
|
||||||
|
remappedValueDescriptor ?: capturedValueDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchReceiver = oldExpression.dispatchReceiver
|
||||||
|
extensionReceiver = oldExpression.extensionReceiver
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCallableReference(expression: IrCallableReference): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
val oldCallee = expression.descriptor.original
|
||||||
|
val localFunctionData = localFunctions[oldCallee] ?: return expression
|
||||||
|
val newCallee = localFunctionData.transformedDescriptor
|
||||||
|
|
||||||
|
val newCallableReference = IrCallableReferenceImpl(
|
||||||
|
expression.startOffset, expression.endOffset,
|
||||||
|
expression.type, // TODO functional type for transformed descriptor
|
||||||
|
newCallee,
|
||||||
|
remapTypeArguments(expression, newCallee),
|
||||||
|
expression.origin
|
||||||
|
).fillArguments(localFunctionData, expression)
|
||||||
|
|
||||||
|
return newCallableReference
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
val oldReturnTarget = expression.returnTarget
|
||||||
|
val localFunctionData = localFunctions[oldReturnTarget] ?: return expression
|
||||||
|
val newReturnTarget = localFunctionData.transformedDescriptor
|
||||||
|
|
||||||
|
return IrReturnImpl(expression.startOffset, expression.endOffset, newReturnTarget, expression.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rewriteFunctionDeclaration(irFunction: IrFunction, localFunctionContext: LocalFunctionContext?) {
|
||||||
|
irFunction.transformChildrenVoid(FunctionBodiesRewriter(localFunctionContext))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rewriteBodies() {
|
||||||
|
localFunctions.values.forEach {
|
||||||
|
rewriteFunctionDeclaration(it.declaration, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
rewriteFunctionDeclaration(memberFunction, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNewCall(oldCall: IrCall, newCallee: FunctionDescriptor) =
|
||||||
|
if (oldCall is IrCallWithShallowCopy)
|
||||||
|
oldCall.shallowCopy(oldCall.origin, newCallee, oldCall.superQualifier)
|
||||||
|
else
|
||||||
|
IrCallImpl(
|
||||||
|
oldCall.startOffset, oldCall.endOffset,
|
||||||
|
newCallee,
|
||||||
|
remapTypeArguments(oldCall, newCallee),
|
||||||
|
oldCall.origin, oldCall.superQualifier
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun remapTypeArguments(oldExpression: IrMemberAccessExpression, newCallee: FunctionDescriptor): Map<TypeParameterDescriptor, KotlinType>? {
|
||||||
|
val oldCallee = oldExpression.descriptor
|
||||||
|
|
||||||
|
return if (oldCallee.typeParameters.isEmpty())
|
||||||
|
null
|
||||||
|
else oldCallee.typeParameters.associateBy(
|
||||||
|
{ newCallee.typeParameters[it.index] },
|
||||||
|
{ oldExpression.getTypeArgument(it)!! }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun transformDescriptors() {
|
||||||
|
localFunctions.values.forEach {
|
||||||
|
it.transformedDescriptor = createTransformedDescriptor(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun suggestLocalName(descriptor: DeclarationDescriptor): String {
|
||||||
|
val localFunctionContext = localFunctions[descriptor]
|
||||||
|
return if (localFunctionContext != null && localFunctionContext.index >= 0)
|
||||||
|
"lambda-${localFunctionContext.index}"
|
||||||
|
else
|
||||||
|
descriptor.name.asString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateNameForLiftedFunction(functionDescriptor: FunctionDescriptor): Name =
|
||||||
|
Name.identifier(
|
||||||
|
functionDescriptor.parentsWithSelf
|
||||||
|
.takeWhile { it is FunctionDescriptor }
|
||||||
|
.toList().reversed()
|
||||||
|
.map { suggestLocalName(it) }
|
||||||
|
.joinToString(separator = "$")
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun createTransformedDescriptor(localFunctionContext: LocalFunctionContext): FunctionDescriptor {
|
||||||
|
val oldDescriptor = localFunctionContext.declaration.descriptor
|
||||||
|
|
||||||
|
val memberOwner = memberFunction.descriptor.containingDeclaration
|
||||||
|
val newDescriptor = SimpleFunctionDescriptorImpl.create(
|
||||||
|
memberOwner,
|
||||||
|
oldDescriptor.annotations,
|
||||||
|
generateNameForLiftedFunction(oldDescriptor),
|
||||||
|
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||||
|
oldDescriptor.source
|
||||||
|
)
|
||||||
|
|
||||||
|
val closureParametersCount = localFunctionContext.closureParametersCount
|
||||||
|
val newValueParametersCount = closureParametersCount + oldDescriptor.valueParameters.size
|
||||||
|
|
||||||
|
val newDispatchReceiverParameter =
|
||||||
|
if (memberOwner is ClassDescriptor && oldDescriptor.dispatchReceiverParameter != null)
|
||||||
|
memberOwner.thisAsReceiverParameter
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
|
// Do not substitute type parameters for now.
|
||||||
|
val newTypeParameters = oldDescriptor.typeParameters
|
||||||
|
|
||||||
|
val newValueParameters = ArrayList<ValueParameterDescriptor>(newValueParametersCount).apply {
|
||||||
|
localFunctionContext.closure.capturedValues.mapIndexedTo(this) { i, capturedValueDescriptor ->
|
||||||
|
createUnsubstitutedCapturedValueParameter(newDescriptor, capturedValueDescriptor, i).apply {
|
||||||
|
localFunctionContext.recordRemapped(capturedValueDescriptor, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
oldDescriptor.valueParameters.mapIndexedTo(this) { i, oldValueParameterDescriptor ->
|
||||||
|
createUnsubstitutedParameter(newDescriptor, oldValueParameterDescriptor, closureParametersCount + i).apply {
|
||||||
|
localFunctionContext.recordRemapped(oldValueParameterDescriptor, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newDescriptor.initialize(
|
||||||
|
oldDescriptor.extensionReceiverParameter?.type,
|
||||||
|
newDispatchReceiverParameter,
|
||||||
|
newTypeParameters,
|
||||||
|
newValueParameters,
|
||||||
|
oldDescriptor.returnType,
|
||||||
|
Modality.FINAL,
|
||||||
|
Visibilities.PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
|
return newDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LocalFunctionContext.recordRemapped(oldDescriptor: ValueDescriptor, newDescriptor: ValueParameterDescriptor): ValueParameterDescriptor {
|
||||||
|
old2new[oldDescriptor] = newDescriptor
|
||||||
|
new2old[newDescriptor] = oldDescriptor
|
||||||
|
return newDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun suggestNameForCapturedValueParameter(valueDescriptor: ValueDescriptor): Name =
|
||||||
|
if (valueDescriptor.name.isSpecial) {
|
||||||
|
val oldNameStr = valueDescriptor.name.asString()
|
||||||
|
Name.identifier("$" + oldNameStr.substring(1, oldNameStr.length - 1))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
valueDescriptor.name
|
||||||
|
|
||||||
|
private fun createUnsubstitutedCapturedValueParameter(
|
||||||
|
newParameterOwner: CallableMemberDescriptor,
|
||||||
|
valueDescriptor: ValueDescriptor,
|
||||||
|
index: Int
|
||||||
|
): ValueParameterDescriptor =
|
||||||
|
ValueParameterDescriptorImpl(
|
||||||
|
newParameterOwner, null, index,
|
||||||
|
valueDescriptor.annotations,
|
||||||
|
suggestNameForCapturedValueParameter(valueDescriptor),
|
||||||
|
valueDescriptor.type,
|
||||||
|
false, false, false, false, null, valueDescriptor.source
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun createUnsubstitutedParameter(
|
||||||
|
newParameterOwner: CallableMemberDescriptor,
|
||||||
|
valueParameterDescriptor: ValueParameterDescriptor,
|
||||||
|
newIndex: Int
|
||||||
|
): ValueParameterDescriptor =
|
||||||
|
valueParameterDescriptor.copy(newParameterOwner, valueParameterDescriptor.name, newIndex)
|
||||||
|
|
||||||
|
|
||||||
|
private fun collectClosures() {
|
||||||
|
memberFunction.acceptChildrenVoid(object : AbstractClosureAnnotator() {
|
||||||
|
override fun visitClass(declaration: IrClass) {
|
||||||
|
// ignore local classes for now
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun recordFunctionClosure(functionDescriptor: FunctionDescriptor, closure: Closure) {
|
||||||
|
localFunctions[functionDescriptor]?.closure = closure
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun recordClassClosure(classDescriptor: ClassDescriptor, closure: Closure) {
|
||||||
|
// ignore local classes for now
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectLocalFunctions() {
|
||||||
|
memberFunction.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||||
|
var lambdasCount = 0
|
||||||
|
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
|
declaration.acceptChildrenVoid(this)
|
||||||
|
val localFunctionContext = LocalFunctionContext(declaration)
|
||||||
|
localFunctions[declaration.descriptor] = localFunctionContext
|
||||||
|
if (declaration.descriptor.name.isSpecial) {
|
||||||
|
localFunctionContext.index = lambdasCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitClass(declaration: IrClass) {
|
||||||
|
// ignore local classes for now
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+131
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.jvm.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.FunctionLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrSetVariable
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrValueAccessExpression
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class SharedVariablesLowering(val context: JvmBackendContext) : FunctionLoweringPass {
|
||||||
|
override fun lower(irFunction: IrFunction) {
|
||||||
|
SharedVariablesTransformer(irFunction).lowerSharedVariables()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class SharedVariablesTransformer(val irFunction: IrFunction) {
|
||||||
|
val sharedVariables = HashSet<ValueDescriptor>()
|
||||||
|
|
||||||
|
fun lowerSharedVariables() {
|
||||||
|
collectSharedVariables()
|
||||||
|
if (sharedVariables.isEmpty()) return
|
||||||
|
|
||||||
|
rewriteSharedVariables()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectSharedVariables() {
|
||||||
|
irFunction.acceptVoid(object : IrElementVisitorVoid {
|
||||||
|
val declarationsStack = ArrayDeque<IrDeclaration>()
|
||||||
|
val currentDeclaration: DeclarationDescriptor
|
||||||
|
get() = declarationsStack.peek().descriptor
|
||||||
|
|
||||||
|
val relevantVars = HashSet<VariableDescriptor>()
|
||||||
|
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
|
declarationsStack.push(declaration)
|
||||||
|
declaration.acceptChildrenVoid(this)
|
||||||
|
declarationsStack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitVariable(declaration: IrVariable) {
|
||||||
|
val variableDescriptor = declaration.descriptor
|
||||||
|
if (variableDescriptor.isVar) {
|
||||||
|
relevantVars.add(variableDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitVariableAccess(expression: IrValueAccessExpression) {
|
||||||
|
expression.acceptChildrenVoid(this)
|
||||||
|
|
||||||
|
val descriptor = expression.descriptor
|
||||||
|
if (descriptor in relevantVars && descriptor.containingDeclaration != currentDeclaration) {
|
||||||
|
sharedVariables.add(descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rewriteSharedVariables() {
|
||||||
|
val transformedDescriptors = HashMap<ValueDescriptor, VariableDescriptor>()
|
||||||
|
|
||||||
|
irFunction.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
|
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||||
|
declaration.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
val oldDescriptor = declaration.descriptor
|
||||||
|
if (oldDescriptor !in sharedVariables) return declaration
|
||||||
|
|
||||||
|
val newDescriptor = context.sharedVariablesManager.createSharedVariableDescriptor(oldDescriptor)
|
||||||
|
transformedDescriptors[oldDescriptor] = newDescriptor
|
||||||
|
|
||||||
|
return context.sharedVariablesManager.defineSharedValue(newDescriptor, declaration)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
irFunction.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
|
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
val newDescriptor = getTransformedDescriptor(expression.descriptor) ?: return expression
|
||||||
|
|
||||||
|
return context.sharedVariablesManager.getSharedValue(newDescriptor, expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitSetVariable(expression: IrSetVariable): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
val newDescriptor = getTransformedDescriptor(expression.descriptor) ?: return expression
|
||||||
|
|
||||||
|
return context.sharedVariablesManager.setSharedValue(newDescriptor, expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTransformedDescriptor(oldDescriptor: ValueDescriptor): VariableDescriptor? =
|
||||||
|
transformedDescriptors.getOrElse(oldDescriptor) {
|
||||||
|
assert(oldDescriptor !in sharedVariables) {
|
||||||
|
"Shared variable is not transformed: $oldDescriptor"
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user