Implemented callable references for properties

This commit is contained in:
Igor Chevdar
2017-03-07 16:43:24 +03:00
parent 484fba5b55
commit 776f4c4b7f
5 changed files with 279 additions and 31 deletions
@@ -9,17 +9,26 @@ import org.jetbrains.kotlin.backend.konan.llvm.Llvm
import org.jetbrains.kotlin.backend.konan.llvm.LlvmDeclarations
import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.verifyModule
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.getFunctionTypeArgumentProjections
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import java.lang.System.out
import kotlin.reflect.KProperty
internal class SpecialDescriptorsFactory(val context: Context) {
private val enumSpecialDescriptorsFactory by lazy { EnumSpecialDescriptorsFactory(context) }
@@ -106,11 +115,63 @@ internal class SpecialDescriptorsFactory(val context: Context) {
}
}
class ReflectionTypes(module: ModuleDescriptor) {
val KOTLIN_REFLECT_FQ_NAME = FqName("kotlin.reflect")
val KONAN_INTERNAL_FQ_NAME = FqName("konan.internal")
private val kotlinReflectScope: MemberScope by lazy(LazyThreadSafetyMode.PUBLICATION) {
module.getPackage(KOTLIN_REFLECT_FQ_NAME).memberScope
}
private val konanInternalScope: MemberScope by lazy(LazyThreadSafetyMode.PUBLICATION) {
module.getPackage(KONAN_INTERNAL_FQ_NAME).memberScope
}
private fun find(memberScope: MemberScope, className: String): ClassDescriptor {
val name = Name.identifier(className)
return memberScope.getContributedClassifier(name, NoLookupLocation.FROM_REFLECTION) as ClassDescriptor
}
private class ClassLookup(val memberScope: MemberScope) {
operator fun getValue(types: ReflectionTypes, property: KProperty<*>): ClassDescriptor {
return types.find(memberScope, property.name.capitalize())
}
}
fun getKFunction(n: Int): ClassDescriptor = find(kotlinReflectScope, "KFunction$n")
val kClass: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kProperty0: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kProperty1: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kProperty2: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kProperty0Impl: ClassDescriptor by ClassLookup(konanInternalScope)
val kProperty1Impl: ClassDescriptor by ClassLookup(konanInternalScope)
val kMutableProperty0Impl: ClassDescriptor by ClassLookup(konanInternalScope)
val kMutableProperty1Impl: ClassDescriptor by ClassLookup(konanInternalScope)
val kMutableProperty0: ClassDescriptor by ClassLookup(kotlinReflectScope)
val kMutableProperty1: ClassDescriptor by ClassLookup(kotlinReflectScope)
fun getKFunctionType(
annotations: Annotations,
receiverType: KotlinType?,
parameterTypes: List<KotlinType>,
parameterNames: List<Name>?,
returnType: KotlinType,
builtIns: KotlinBuiltIns
): KotlinType {
val arguments = getFunctionTypeArgumentProjections(receiverType, parameterTypes, parameterNames, returnType, builtIns)
val classDescriptor = getKFunction(arguments.size - 1 /* return type */)
return KotlinTypeFactory.simpleNotNullType(annotations, classDescriptor, arguments)
}
}
internal class Context(val config: KonanConfig) : KonanBackendContext() {
var moduleDescriptor: ModuleDescriptor? = null
val specialDescriptorsFactory = SpecialDescriptorsFactory(this)
val reflectionTypes: ReflectionTypes by lazy { ReflectionTypes(moduleDescriptor!!) }
private val vtableBuilders = mutableMapOf<ClassDescriptor, ClassVtablesBuilder>()
fun getVtableBuilder(classDescriptor: ClassDescriptor) = vtableBuilders.getOrPut(classDescriptor) {
@@ -1,26 +1,26 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.jvm.descriptors.initialize
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.OverriddenFunctionDescriptor
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.konan.ir.createArrayOfExpression
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrLocalDelegatedProperty
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrCallableReference
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression
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.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName
@@ -28,31 +28,48 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.*
internal class PropertyDelegationLowering(val context: Context) : FileLoweringPass {
private val kotlinReflectPackage = context.irModule!!.descriptor.getPackage(FqName.fromSegments(listOf("kotlin", "reflect")))
private val genericKPropertyImplType = kotlinReflectPackage.memberScope.getContributedClassifier(Name.identifier("KPropertyImpl"),
NoLookupLocation.FROM_BACKEND) as ClassDescriptor
private val genericKProperty0ImplType = context.reflectionTypes.kProperty0Impl
private val genericKProperty1ImplType = context.reflectionTypes.kProperty1Impl
private val genericKMutableProperty0ImplType = context.reflectionTypes.kMutableProperty0Impl
private val genericKMutableProperty1ImplType = context.reflectionTypes.kMutableProperty1Impl
private val kotlinPackage = context.irModule!!.descriptor.getPackage(FqName("kotlin"))
private val genericArrayType = kotlinPackage.memberScope.getContributedClassifier(Name.identifier("Array"), NoLookupLocation.FROM_BACKEND) as ClassDescriptor
fun getKPropertyImplConstructorDescriptorWithProjection(type: KotlinType): ClassConstructorDescriptor {
val typeParameterT = genericKPropertyImplType.declaredTypeParameters[0]
val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(type)))
private fun getKPropertyImplConstructorDescriptor(returnType: KotlinType, isMutable: Boolean): ClassConstructorDescriptor {
val genericKPropertyImplType = if (isMutable) genericKMutableProperty0ImplType else genericKProperty0ImplType
val typeParameterR = genericKPropertyImplType.declaredTypeParameters[0]
val typeSubstitutor = TypeSubstitutor.create(mapOf(
typeParameterR.typeConstructor to TypeProjectionImpl(returnType)))
return genericKPropertyImplType.unsubstitutedPrimaryConstructor!!.substitute(typeSubstitutor)!!
}
fun ClassDescriptor.replace(vararg type: KotlinType): SimpleType {
private fun getKPropertyImplConstructorDescriptor(receiverType: KotlinType?, returnType: KotlinType, isMutable: Boolean)
: ClassConstructorDescriptor {
if (receiverType == null)
return getKPropertyImplConstructorDescriptor(returnType, isMutable)
val genericKPropertyImplType = if (isMutable) genericKMutableProperty1ImplType else genericKProperty1ImplType
val typeParameterT = genericKPropertyImplType.declaredTypeParameters[0]
val typeParameterR = genericKPropertyImplType.declaredTypeParameters[1]
val typeSubstitutor = TypeSubstitutor.create(mapOf(
typeParameterT.typeConstructor to TypeProjectionImpl(receiverType),
typeParameterR.typeConstructor to TypeProjectionImpl(returnType)))
return genericKPropertyImplType.unsubstitutedPrimaryConstructor!!.substitute(typeSubstitutor)!!
}
private fun ClassDescriptor.replace(vararg type: KotlinType): SimpleType {
return this.defaultType.replace(type.map(::TypeProjectionImpl))
}
override fun lower(irFile: IrFile) {
val kProperties = mutableMapOf<VariableDescriptorWithAccessors, Pair<IrExpression, Int>>()
val getter = genericArrayType.unsubstitutedMemberScope.getContributedFunctions(Name.identifier("get"), NoLookupLocation.FROM_BACKEND).single()
val arrayItemGetter = genericArrayType.unsubstitutedMemberScope.getContributedFunctions(Name.identifier("get"),
NoLookupLocation.FROM_BACKEND).single()
val typeParameterT = genericArrayType.declaredTypeParameters[0]
val kPropertyImplType = genericKPropertyImplType.replace(context.builtIns.anyType)
val kPropertyImplType = genericKProperty1ImplType.replace(context.builtIns.anyType, context.builtIns.anyType)
val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(kPropertyImplType)))
val substitutedGetter = getter.substitute(typeSubstitutor)!!
val substitutedArrayItemGetter = arrayItemGetter.substitute(typeSubstitutor)!!
val kPropertiesField = createKPropertiesFieldDescriptor(irFile.packageFragmentDescriptor, genericArrayType.replace(kPropertyImplType))
@@ -81,26 +98,27 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
expression.transformChildrenVoid(this)
val propertyDescriptor = expression.descriptor as? VariableDescriptorWithAccessors
if (propertyDescriptor == null) return expression
val field = kProperties.getOrPut(propertyDescriptor) {
val initializer = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET,
getKPropertyImplConstructorDescriptorWithProjection(propertyDescriptor.type)).apply {
putValueArgument(0, IrConstImpl<String>(UNDEFINED_OFFSET, UNDEFINED_OFFSET,
context.builtIns.stringType, IrConstKind.String, propertyDescriptor.name.asString()))
val receiversCount = listOf(expression.dispatchReceiver, expression.extensionReceiver).count { it != null }
when (receiversCount) {
0 -> { // Cache KProperties with no arguments.
val field = kProperties.getOrPut(propertyDescriptor) {
createKProperty(expression, propertyDescriptor) to kProperties.size
}
return IrCallImpl(expression.startOffset, expression.endOffset, substitutedArrayItemGetter).apply {
dispatchReceiver = IrGetFieldImpl(expression.startOffset, expression.endOffset, kPropertiesField)
putValueArgument(0, IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, field.second))
}
}
val index = kProperties.size
initializer to index
}
return IrCallImpl(expression.startOffset, expression.endOffset, substitutedGetter).apply {
dispatchReceiver = IrGetFieldImpl(expression.startOffset, expression.endOffset, kPropertiesField)
putValueArgument(0, IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, field.second))
1 -> return createKProperty(expression, propertyDescriptor)
else -> throw AssertionError("Callable reference to properties with two receivers is not allowed: $propertyDescriptor")
}
}
})
if (kProperties.isNotEmpty()) {
val initializers = kProperties.values.sortedBy { it.second }.map { it.first }
// TODO: move to object for lazy initialization.
irFile.declarations.add(0, IrFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET,
DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION,
kPropertiesField,
@@ -109,6 +127,61 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
}
}
private fun createKProperty(expression: IrCallableReference, propertyDescriptor: VariableDescriptorWithAccessors): IrCallImpl {
val getter = propertyDescriptor.getter!!
val receiverTypes = mutableListOf<KotlinType>()
getter.dispatchReceiverParameter.let {
if (it != null && expression.dispatchReceiver == null)
receiverTypes.add(it.type)
}
getter.extensionReceiverParameter.let {
if (it != null && expression.extensionReceiver == null)
receiverTypes.add(it.type)
}
val receiverType = receiverTypes.singleOrNull()
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val getterKFunctionType = context.reflectionTypes.getKFunctionType(
annotations = Annotations.EMPTY,
receiverType = receiverType,
parameterTypes = listOf(),
parameterNames = null,
returnType = propertyDescriptor.type,
builtIns = context.builtIns)
val getterCallableReference = IrCallableReferenceImpl(startOffset, endOffset, getterKFunctionType, getter, null).apply {
dispatchReceiver = expression.dispatchReceiver
extensionReceiver = expression.extensionReceiver
}
val setterCallableReference = propertyDescriptor.setter.let {
if (it == null) null
else {
val setterKFunctionType = context.reflectionTypes.getKFunctionType(
annotations = Annotations.EMPTY,
receiverType = receiverType,
parameterTypes = listOf(propertyDescriptor.type),
parameterNames = null,
returnType = context.builtIns.unitType,
builtIns = context.builtIns)
IrCallableReferenceImpl(startOffset, endOffset, setterKFunctionType, it, null).apply {
dispatchReceiver = expression.dispatchReceiver
extensionReceiver = expression.extensionReceiver
}
}
}
val descriptor = getKPropertyImplConstructorDescriptor(receiverType, propertyDescriptor.type, setterCallableReference != null)
val initializer = IrCallImpl(startOffset, endOffset, descriptor).apply {
putValueArgument(0, IrConstImpl<String>(startOffset, endOffset,
context.builtIns.stringType, IrConstKind.String, propertyDescriptor.name.asString()))
putValueArgument(1, getterCallableReference)
if (setterCallableReference != null)
putValueArgument(2, setterCallableReference)
}
return initializer
}
private object DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION :
IrDeclarationOriginImpl("KPROPERTIES_FOR_DELEGATION")