From 776f4c4b7f29274ccaf2be3395ce68fc943e2d94 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 7 Mar 2017 16:43:24 +0300 Subject: [PATCH 1/6] Implemented callable references for properties --- .../jetbrains/kotlin/backend/konan/Context.kt | 61 ++++++++ .../backend/konan/lower/DelegationLowering.kt | 133 ++++++++++++++---- .../kotlin/konan/internal/KPropertyImpl.kt | 63 +++++++++ runtime/src/main/kotlin/kotlin/Function.kt | 1 + .../main/kotlin/kotlin/reflect/KProperty.kt | 52 ++++++- 5 files changed, 279 insertions(+), 31 deletions(-) create mode 100644 runtime/src/main/kotlin/konan/internal/KPropertyImpl.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 797e7573912..707be93221c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -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, + parameterNames: List?, + 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() fun getVtableBuilder(classDescriptor: ClassDescriptor) = vtableBuilders.getOrPut(classDescriptor) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt index 4dfe4a61bf3..99f0d540f23 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt @@ -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>() - 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(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() + 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(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") diff --git a/runtime/src/main/kotlin/konan/internal/KPropertyImpl.kt b/runtime/src/main/kotlin/konan/internal/KPropertyImpl.kt new file mode 100644 index 00000000000..de920a14299 --- /dev/null +++ b/runtime/src/main/kotlin/konan/internal/KPropertyImpl.kt @@ -0,0 +1,63 @@ +package konan.internal + +import kotlin.reflect.KProperty0 +import kotlin.reflect.KProperty1 +import kotlin.reflect.KProperty2 +import kotlin.reflect.KMutableProperty0 +import kotlin.reflect.KMutableProperty1 +import kotlin.reflect.KMutableProperty2 + +@FixmeReflection +open class KProperty0Impl(override val name: String, val getter: () -> R): KProperty0 { + override fun get(): R { + return getter() + } + override fun invoke(): R { + return getter() + } + +} + +@FixmeReflection +open class KProperty1Impl(override val name: String, val getter: (T) -> R): KProperty1 { + override fun get(receiver: T): R { + return getter(receiver) + } + override fun invoke(receiver: T): R { + return getter(receiver) + } +} + +@FixmeReflection +open class KProperty2Impl(override val name: String, val getter: (T1, T2) -> R): KProperty2 { + override fun get(receiver1: T1, receiver2: T2): R { + return getter(receiver1, receiver2) + } + override fun invoke(receiver1: T1, receiver2: T2): R { + return getter(receiver1, receiver2) + } +} + +@FixmeReflection +class KMutableProperty0Impl(name: String, getter: () -> R, val setter: (R) -> Unit) + : KProperty0Impl(name, getter), KMutableProperty0 { + override fun set(value: R): Unit { + setter(value) + } +} + +@FixmeReflection +public class KMutableProperty1Impl(name: String, getter: (T) -> R, val setter: (T, R) -> Unit) + : KProperty1Impl(name, getter), KMutableProperty1 { + override fun set(receiver: T, value: R): Unit { + setter(receiver, value) + } +} + +@FixmeReflection +public class KMutableProperty2Impl(name: String, getter: (T1, T2) -> R, val setter: (T1, T2, R) -> Unit) + : KProperty2Impl(name, getter), KMutableProperty2 { + override fun set(receiver1: T1, receiver2: T2, value: R): Unit { + setter(receiver1, receiver2, value) + } +} diff --git a/runtime/src/main/kotlin/kotlin/Function.kt b/runtime/src/main/kotlin/kotlin/Function.kt index 8c46cd9d69d..b640706adc2 100644 --- a/runtime/src/main/kotlin/kotlin/Function.kt +++ b/runtime/src/main/kotlin/kotlin/Function.kt @@ -5,4 +5,5 @@ package kotlin * * @param R return type of the function. */ +@FixmeReflection public interface Function diff --git a/runtime/src/main/kotlin/kotlin/reflect/KProperty.kt b/runtime/src/main/kotlin/kotlin/reflect/KProperty.kt index e4604877c36..97b734a5dfd 100644 --- a/runtime/src/main/kotlin/kotlin/reflect/KProperty.kt +++ b/runtime/src/main/kotlin/kotlin/reflect/KProperty.kt @@ -47,6 +47,46 @@ public interface KProperty : KCallable { // public interface Getter : Accessor, KFunction } +// +@FixmeReflection +public interface KProperty0 : kotlin.reflect.KProperty/* TODO , (T) -> R*/ { +// public abstract val getter: kotlin.reflect.KProperty1.Getter + + public abstract fun get(): R + public abstract operator fun invoke(): R + +// @kotlin.SinceKotlin public abstract fun getDelegate(receiver: T): kotlin.Any? +// +// public interface Getter : kotlin.reflect.KProperty.Getter, (T) -> R { +// } +} + +@FixmeReflection +public interface KProperty1 : kotlin.reflect.KProperty/* TODO , (T) -> R*/ { +// public abstract val getter: kotlin.reflect.KProperty1.Getter + + public abstract fun get(receiver: T): R + public abstract operator fun invoke(receiver: T): R + +// @kotlin.SinceKotlin public abstract fun getDelegate(receiver: T): kotlin.Any? +// +// public interface Getter : kotlin.reflect.KProperty.Getter, (T) -> R { +// } +} + +@FixmeReflection +public interface KProperty2 : kotlin.reflect.KProperty/* TODO , (T) -> R*/ { +// public abstract val getter: kotlin.reflect.KProperty1.Getter + + public abstract fun get(receiver1: T1, receiver2: T2): R + public abstract operator fun invoke(receiver1: T1, receiver2: T2): R + +// @kotlin.SinceKotlin public abstract fun getDelegate(receiver: T): kotlin.Any? +// +// public interface Getter : kotlin.reflect.KProperty.Getter, (T) -> R { +// } +} + /** * Represents a property declared as a `var`. */ @@ -62,6 +102,16 @@ public interface KMutableProperty : KProperty { } @FixmeReflection -public class KPropertyImpl(override val name: String) : KProperty { +public interface KMutableProperty0 : KProperty0, KMutableProperty { + public abstract fun set(value: R) +} +@FixmeReflection +public interface KMutableProperty1 : KProperty1, KMutableProperty { + public abstract fun set(receiver: T, value: R) +} + +@FixmeReflection +public interface KMutableProperty2 : KProperty2, KMutableProperty { + public abstract fun set(receiver1: T1, receiver2: T2, value: R) } \ No newline at end of file From 1a8ce13067d8ecd458c390f4bb818b0a294648df Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 7 Mar 2017 16:44:53 +0300 Subject: [PATCH 2/6] Added tests --- backend.native/tests/build.gradle | 30 +++++++++++++++++++ .../propertyCallableReference/valClass.kt | 9 ++++++ .../propertyCallableReference/valExtension.kt | 13 ++++++++ .../propertyCallableReference/valModule.kt | 6 ++++ .../propertyCallableReference/varClass.kt | 13 ++++++++ .../propertyCallableReference/varExtension.kt | 21 +++++++++++++ .../propertyCallableReference/varModule.kt | 8 +++++ 7 files changed, 100 insertions(+) create mode 100644 backend.native/tests/codegen/propertyCallableReference/valClass.kt create mode 100644 backend.native/tests/codegen/propertyCallableReference/valExtension.kt create mode 100644 backend.native/tests/codegen/propertyCallableReference/valModule.kt create mode 100644 backend.native/tests/codegen/propertyCallableReference/varClass.kt create mode 100644 backend.native/tests/codegen/propertyCallableReference/varExtension.kt create mode 100644 backend.native/tests/codegen/propertyCallableReference/varModule.kt diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index c7afcbf613c..fe9190f4f7e 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -651,6 +651,36 @@ task delegatedProperty_map(type: RunKonanTest) { source = "codegen/delegatedProperty/map.kt" } +task propertyCallableReference_valClass(type: RunKonanTest) { + goldValue = "42\n117\n" + source = "codegen/propertyCallableReference/valClass.kt" +} + +task propertyCallableReference_valModule(type: RunKonanTest) { + goldValue = "42\n" + source = "codegen/propertyCallableReference/valModule.kt" +} + +task propertyCallableReference_varClass(type: RunKonanTest) { + goldValue = "117\n117\n42\n42\n" + source = "codegen/propertyCallableReference/varClass.kt" +} + +task propertyCallableReference_varModule(type: RunKonanTest) { + goldValue = "117\n117\n" + source = "codegen/propertyCallableReference/varModule.kt" +} + +task propertyCallableReference_valExtension(type: RunKonanTest) { + goldValue = "42\n117\n" + source = "codegen/propertyCallableReference/valExtension.kt" +} + +task propertyCallableReference_varExtension(type: RunKonanTest) { + goldValue = "117\n117\n42\n42\n" + source = "codegen/propertyCallableReference/varExtension.kt" +} + task array0(type: RunKonanTest) { goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n" source = "runtime/collections/array0.kt" diff --git a/backend.native/tests/codegen/propertyCallableReference/valClass.kt b/backend.native/tests/codegen/propertyCallableReference/valClass.kt new file mode 100644 index 00000000000..9e3f29f56f5 --- /dev/null +++ b/backend.native/tests/codegen/propertyCallableReference/valClass.kt @@ -0,0 +1,9 @@ +class A(val x: Int) + +fun main(args: Array) { + val p1 = A::x + println(p1.get(A(42))) + val a = A(117) + val p2 = a::x + println(p2.get()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/propertyCallableReference/valExtension.kt b/backend.native/tests/codegen/propertyCallableReference/valExtension.kt new file mode 100644 index 00000000000..ef7a7dcc4ee --- /dev/null +++ b/backend.native/tests/codegen/propertyCallableReference/valExtension.kt @@ -0,0 +1,13 @@ +class A(y: Int) { + var x = y +} + +val A.z get() = this.x + +fun main(args: Array) { + val p1 = A::z + println(p1.get(A(42))) + val a = A(117) + val p2 = a::z + println(p2.get()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/propertyCallableReference/valModule.kt b/backend.native/tests/codegen/propertyCallableReference/valModule.kt new file mode 100644 index 00000000000..800051cebfa --- /dev/null +++ b/backend.native/tests/codegen/propertyCallableReference/valModule.kt @@ -0,0 +1,6 @@ +val x = 42 + +fun main(args: Array) { + val p = ::x + println(p.get()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/propertyCallableReference/varClass.kt b/backend.native/tests/codegen/propertyCallableReference/varClass.kt new file mode 100644 index 00000000000..a0b4e060a4e --- /dev/null +++ b/backend.native/tests/codegen/propertyCallableReference/varClass.kt @@ -0,0 +1,13 @@ +class A(var x: Int) + +fun main(args: Array) { + val p1 = A::x + val a = A(42) + p1.set(a, 117) + println(a.x) + println(p1.get(a)) + val p2 = a::x + p2.set(42) + println(a.x) + println(p2.get()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/propertyCallableReference/varExtension.kt b/backend.native/tests/codegen/propertyCallableReference/varExtension.kt new file mode 100644 index 00000000000..9f304fddd50 --- /dev/null +++ b/backend.native/tests/codegen/propertyCallableReference/varExtension.kt @@ -0,0 +1,21 @@ +class A(y: Int) { + var x = y +} + +var A.z: Int + get() = this.x + set(value: Int) { + this.x = value + } + +fun main(args: Array) { + val p1 = A::z + val a = A(42) + p1.set(a, 117) + println(a.x) + println(p1.get(a)) + val p2 = a::z + p2.set(42) + println(a.x) + println(p2.get()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/propertyCallableReference/varModule.kt b/backend.native/tests/codegen/propertyCallableReference/varModule.kt new file mode 100644 index 00000000000..627076ca8f7 --- /dev/null +++ b/backend.native/tests/codegen/propertyCallableReference/varModule.kt @@ -0,0 +1,8 @@ +var x = 42 + +fun main(args: Array) { + val p = ::x + p.set(117) + println(x) + println(p.get()) +} \ No newline at end of file From 532cab42a70eddac54b116fcec733b00de2847a7 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 7 Mar 2017 16:45:17 +0300 Subject: [PATCH 3/6] bug fix: Callable references as arguments of callable references. --- .../kotlin/backend/konan/lower/CallableReferenceLowering.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt index 4238398a03e..1ed929866e6 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CallableReferenceLowering.kt @@ -101,6 +101,7 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering, } override fun visitCallableReference(expression: IrCallableReference): IrExpression { + expression.transformChildrenVoid(this) if (!expression.type.isFunctionOrKFunctionType) { // Not a subject of this lowering. return expression From 6bb32743fd545b3b29a73dd2437e8db7c56eab08 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 7 Mar 2017 17:28:28 +0300 Subject: [PATCH 4/6] Removed usage of Kotlin.BuiltIns --- .../jetbrains/kotlin/backend/konan/Context.kt | 28 +++++++++++++++---- .../backend/konan/lower/DelegationLowering.kt | 8 ++---- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 707be93221c..3f0e21923bd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -9,8 +9,6 @@ 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 @@ -27,7 +25,11 @@ 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 org.jetbrains.kotlin.types.TypeProjection +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection +import org.jetbrains.kotlin.utils.addIfNotNull import java.lang.System.out +import java.util.* import kotlin.reflect.KProperty internal class SpecialDescriptorsFactory(val context: Context) { @@ -139,6 +141,22 @@ class ReflectionTypes(module: ModuleDescriptor) { } } + private fun getFunctionTypeArgumentProjections( + receiverType: KotlinType?, + parameterTypes: List, + returnType: KotlinType + ): List { + val arguments = ArrayList(parameterTypes.size + (if (receiverType != null) 1 else 0) + 1) + + arguments.addIfNotNull(receiverType?.asTypeProjection()) + + parameterTypes.mapTo(arguments, KotlinType::asTypeProjection) + + arguments.add(returnType.asTypeProjection()) + + return arguments + } + fun getKFunction(n: Int): ClassDescriptor = find(kotlinReflectScope, "KFunction$n") val kClass: ClassDescriptor by ClassLookup(kotlinReflectScope) @@ -156,11 +174,9 @@ class ReflectionTypes(module: ModuleDescriptor) { annotations: Annotations, receiverType: KotlinType?, parameterTypes: List, - parameterNames: List?, - returnType: KotlinType, - builtIns: KotlinBuiltIns + returnType: KotlinType ): KotlinType { - val arguments = getFunctionTypeArgumentProjections(receiverType, parameterTypes, parameterNames, returnType, builtIns) + val arguments = getFunctionTypeArgumentProjections(receiverType, parameterTypes, returnType) val classDescriptor = getKFunction(arguments.size - 1 /* return type */) return KotlinTypeFactory.simpleNotNullType(annotations, classDescriptor, arguments) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt index 99f0d540f23..3df4fd71a39 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt @@ -146,9 +146,7 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa annotations = Annotations.EMPTY, receiverType = receiverType, parameterTypes = listOf(), - parameterNames = null, - returnType = propertyDescriptor.type, - builtIns = context.builtIns) + returnType = propertyDescriptor.type) val getterCallableReference = IrCallableReferenceImpl(startOffset, endOffset, getterKFunctionType, getter, null).apply { dispatchReceiver = expression.dispatchReceiver extensionReceiver = expression.extensionReceiver @@ -161,9 +159,7 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa annotations = Annotations.EMPTY, receiverType = receiverType, parameterTypes = listOf(propertyDescriptor.type), - parameterNames = null, - returnType = context.builtIns.unitType, - builtIns = context.builtIns) + returnType = context.builtIns.unitType) IrCallableReferenceImpl(startOffset, endOffset, setterKFunctionType, it, null).apply { dispatchReceiver = expression.dispatchReceiver extensionReceiver = expression.extensionReceiver From bc0772eab1d22e4ccc34614c7b210d52c50c8abb Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 7 Mar 2017 20:02:20 +0300 Subject: [PATCH 5/6] Test on link stage. --- backend.native/tests/build.gradle | 6 ++++++ .../codegen/propertyCallableReference/linkTest_lib.kt | 3 +++ .../codegen/propertyCallableReference/linkTest_main.kt | 9 +++++++++ 3 files changed, 18 insertions(+) create mode 100644 backend.native/tests/codegen/propertyCallableReference/linkTest_lib.kt create mode 100644 backend.native/tests/codegen/propertyCallableReference/linkTest_main.kt diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index fe9190f4f7e..582a42f8a14 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -681,6 +681,12 @@ task propertyCallableReference_varExtension(type: RunKonanTest) { source = "codegen/propertyCallableReference/varExtension.kt" } +task propertyCallableReference_linkTest(type: LinkKonanTest) { + goldValue = "42\n117\n" + source = "codegen/propertyCallableReference/linkTest_main.kt" + lib = "codegen/propertyCallableReference/linkTest_lib.kt" +} + task array0(type: RunKonanTest) { goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n" source = "runtime/collections/array0.kt" diff --git a/backend.native/tests/codegen/propertyCallableReference/linkTest_lib.kt b/backend.native/tests/codegen/propertyCallableReference/linkTest_lib.kt new file mode 100644 index 00000000000..7595b1acde4 --- /dev/null +++ b/backend.native/tests/codegen/propertyCallableReference/linkTest_lib.kt @@ -0,0 +1,3 @@ +package a + +class A(val x: Int) \ No newline at end of file diff --git a/backend.native/tests/codegen/propertyCallableReference/linkTest_main.kt b/backend.native/tests/codegen/propertyCallableReference/linkTest_main.kt new file mode 100644 index 00000000000..5af7b724a75 --- /dev/null +++ b/backend.native/tests/codegen/propertyCallableReference/linkTest_main.kt @@ -0,0 +1,9 @@ +import a.A + +fun main(args: Array) { + val p1 = A::x + println(p1.get(A(42))) + val a = A(117) + val p2 = a::x + println(p2.get()) +} \ No newline at end of file From 1a5434bada437422d72067f9dcf461eebe9c4b04 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 7 Mar 2017 20:23:33 +0300 Subject: [PATCH 6/6] Forbade caching of KProperty for local delegated properties. --- .../backend/konan/lower/DelegationLowering.kt | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt index 3df4fd71a39..b8eef19c073 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt @@ -99,20 +99,19 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa val propertyDescriptor = expression.descriptor as? VariableDescriptorWithAccessors if (propertyDescriptor == null) return expression 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)) - } + if (receiversCount == 1 || propertyDescriptor !is PropertyDescriptor) // Has receiver or is local delegated. + return createKProperty(expression, propertyDescriptor) + else if (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)) } - 1 -> return createKProperty(expression, propertyDescriptor) - else -> throw AssertionError("Callable reference to properties with two receivers is not allowed: $propertyDescriptor") } + else throw AssertionError("Callable reference to properties with two receivers is not allowed: $propertyDescriptor") } })