diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt index 09ed33c365a..1f78768a5be 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil +import org.jetbrains.kotlin.ir.descriptors.IrPropertyDelegateDescriptor import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor @@ -137,7 +138,8 @@ class KotlinTypeMapper @JvmOverloads constructor( val container = descriptor.containingDeclaration return when (container) { is PackageFragmentDescriptor -> { - val packageMemberOwner = internalNameForPackageMemberOwner(descriptor as CallableMemberDescriptor, publicFacade) + val packageMemberOwner = + internalNameForPackageMemberOwner(descriptor as CallableMemberDescriptor, publicFacade, isIrBackend) Type.getObjectType(packageMemberOwner) } is ClassDescriptor -> mapClass((container as ClassDescriptor?)!!) @@ -1251,7 +1253,11 @@ class KotlinTypeMapper @JvmOverloads constructor( */ val LANGUAGE_VERSION_SETTINGS_DEFAULT: LanguageVersionSettings = LanguageVersionSettingsImpl.DEFAULT - private fun internalNameForPackageMemberOwner(callableDescriptor: CallableMemberDescriptor, publicFacade: Boolean): String { + private fun internalNameForPackageMemberOwner( + callableDescriptor: CallableMemberDescriptor, + publicFacade: Boolean, + isIrBackend: Boolean + ): String { val isAccessor: Boolean val descriptor = if (callableDescriptor is AccessorForCallableDescriptor<*>) { isAccessor = true @@ -1281,6 +1287,10 @@ class KotlinTypeMapper @JvmOverloads constructor( if (facadeFqName != null) return facadeFqName } + if (isIrBackend && directMember is IrPropertyDelegateDescriptor) { + return internalNameForPackageMemberOwner(directMember.correspondingProperty, publicFacade, isIrBackend) + } + // TODO: drop this usage and move IrBuiltinsPackageFragmentDescriptor to IR modules; it shouldn't be used here if (descriptor.containingDeclaration is IrBuiltinsPackageFragmentDescriptor) { return descriptor.containingDeclaration.name.asString() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt index 6e299b9e21c..444426c8ad6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -531,11 +531,12 @@ class DoubleColonExpressionResolver( checkReferenceIsToAllowedMember(descriptor, context.trace, expression) - val type = createKCallableTypeForReference(descriptor, lhs, reflectionTypes, context.scope.ownerDescriptor) ?: return null + val scope = context.scope.ownerDescriptor + val type = createKCallableTypeForReference(descriptor, lhs, reflectionTypes, scope) ?: return null when (descriptor) { is FunctionDescriptor -> bindFunctionReference(expression, type, context, descriptor) - is PropertyDescriptor -> bindPropertyReference(expression, type, context) + is PropertyDescriptor -> bindPropertyReference(expression, type, context, isMutablePropertyReference(descriptor, lhs, scope)) } return type @@ -608,11 +609,12 @@ class DoubleColonExpressionResolver( internal fun bindPropertyReference( expression: KtCallableReferenceExpression, referenceType: KotlinType, - context: ResolutionContext<*> + context: ResolutionContext<*>, + mutable: Boolean = true ) { val localVariable = LocalVariableDescriptor( context.scope.ownerDescriptor, Annotations.EMPTY, Name.special(""), referenceType, - expression.toSourceElement() + mutable, false, expression.toSourceElement() ) context.trace.record(BindingContext.VARIABLE, expression, localVariable) @@ -777,17 +779,26 @@ class DoubleColonExpressionResolver( } companion object { + private fun receiverTypeFor(descriptor: CallableDescriptor, lhs: DoubleColonLHS?): KotlinType? = + (descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter)?.let { (lhs as? DoubleColonLHS.Type)?.type } + + private fun isMutablePropertyReference( + descriptor: PropertyDescriptor, + lhs: DoubleColonLHS?, + scopeOwnerDescriptor: DeclarationDescriptor + ): Boolean { + val receiver = receiverTypeFor(descriptor, lhs)?.let(::TransientReceiver) + val setter = descriptor.setter + return descriptor.isVar && (setter == null || Visibilities.isVisible(receiver, setter, scopeOwnerDescriptor)) + } + fun createKCallableTypeForReference( descriptor: CallableDescriptor, lhs: DoubleColonLHS?, reflectionTypes: ReflectionTypes, scopeOwnerDescriptor: DeclarationDescriptor ): KotlinType? { - val receiverType = - if (descriptor.extensionReceiverParameter != null || descriptor.dispatchReceiverParameter != null) - (lhs as? DoubleColonLHS.Type)?.type - else null - + val receiverType = receiverTypeFor(descriptor, lhs) return when (descriptor) { is FunctionDescriptor -> { val returnType = descriptor.returnType ?: return null @@ -799,10 +810,7 @@ class DoubleColonExpressionResolver( ) } is PropertyDescriptor -> { - val mutable = descriptor.isVar && run { - val setter = descriptor.setter - setter == null || Visibilities.isVisible(receiverType?.let(::TransientReceiver), setter, scopeOwnerDescriptor) - } + val mutable = isMutablePropertyReference(descriptor, lhs, scopeOwnerDescriptor) reflectionTypes.getKPropertyType(Annotations.EMPTY, listOfNotNull(receiverType), descriptor.type, mutable) } is VariableDescriptor -> null diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt index 31498588eba..cf894503d85 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractClosureAnnotator.kt @@ -159,11 +159,6 @@ class ClosureAnnotator(declaration: IrDeclaration) { includeInParent(closureBuilder) } - override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty) { - // Getter and setter of local delegated properties are special generated functions and don't have closure. - declaration.delegate.initializer?.acceptVoid(this) - } - override fun visitVariableAccess(expression: IrValueAccessExpression) { closuresStack.peek()?.seeVariable(expression.symbol) super.visitVariableAccess(expression) @@ -199,7 +194,11 @@ class ClosureAnnotator(declaration: IrDeclaration) { processMemberAccess(expression.symbol.owner) } -// override fun visitPropertyReference(expression: IrPropertyReference) = processMemberAccess(expression.) + override fun visitPropertyReference(expression: IrPropertyReference) { + expression.acceptChildrenVoid(this) + expression.getter?.let { processMemberAccess(it.owner) } + expression.setter?.let { processMemberAccess(it.owner) } + } private fun processMemberAccess(declaration: IrDeclaration) { if (DescriptorUtils.isLocal(declaration.descriptor)) { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt index cb36a7bd9ea..1e71f82c1a1 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalDeclarationsLowering.kt @@ -230,6 +230,9 @@ class LocalDeclarationsLowering( } private inner class FunctionBodiesRewriter(val localContext: LocalContext?) : IrElementTransformerVoid() { + override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty) = + // Both accessors extracted as closures. + declaration.delegate.transform(this, null) override fun visitClass(declaration: IrClass) = if (declaration in localClasses) { // Replace local class definition with an empty composite. @@ -742,11 +745,6 @@ class LocalDeclarationsLowering( val localClassContext = LocalClassContext(declaration) localClasses[declaration] = localClassContext } - - override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty) { - // Getter and setter of local delegated properties are special generated functions and don't have closure. - declaration.delegate.initializer?.acceptVoid(this) - } }) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt index 005755ad44c..604db87c65f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt @@ -33,6 +33,7 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin { object JVM_OVERLOADS_WRAPPER : IrDeclarationOriginImpl("JVM_OVERLOADS_WRAPPER") object SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS : IrDeclarationOriginImpl("SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS", isSynthetic = true) + object GENERATED_PROPERTY_REFERENCE : IrDeclarationOriginImpl("GENERATED_PROPERTY_REFERENCE", isSynthetic = true) } interface JvmLoweredStatementOrigin : IrStatementOrigin { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index e1a2fabb7c0..429354b2d62 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -66,6 +66,7 @@ internal val jvmPhases = namedIrFilePhase( moveCompanionObjectFieldsPhase then constPhase then + propertyReferencePhase then propertiesToFieldsPhase then propertiesPhase then renameFieldsPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt new file mode 100644 index 00000000000..86b8f9c05fd --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt @@ -0,0 +1,329 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.jvm.lower + +import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext +import org.jetbrains.kotlin.backend.common.ir.copyTo +import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.builders.declarations.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.types.impl.* +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.* +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.Variance + +internal val propertyReferencePhase = makeIrFilePhase( + ::PropertyReferenceLowering, + name = "PropertyReference", + description = "Construct KProperty instances returned by expressions such as A::x and A()::x" +) + +internal class PropertyReferenceLowering(val context: JvmBackendContext) : ClassLoweringPass { + // TODO: join IrLocalDelegatedPropertyReference and IrPropertyReference via the class hierarchy? + private val IrMemberAccessExpression.getter: IrFunctionSymbol? + get() = (this as? IrPropertyReference)?.getter ?: (this as? IrLocalDelegatedPropertyReference)?.getter + + private val IrMemberAccessExpression.setter: IrFunctionSymbol? + get() = (this as? IrPropertyReference)?.setter ?: (this as? IrLocalDelegatedPropertyReference)?.setter + + private val IrMemberAccessExpression.field: IrFieldSymbol? + get() = (this as? IrPropertyReference)?.field + + private val IrMemberAccessExpression.signature: String + // Plain Java fields do not have a getter, but can be referenced nonetheless. + get() = getter?.owner?.let { context.state.typeMapper.mapSignatureSkipGeneric(it.descriptor).toString() } + ?: TODO("plain Java field signature") + + private val IrMemberAccessExpression.symbol: IrSymbol + get() = getter?.owner?.symbol ?: field!!.owner.symbol + + private val plainJavaClass = + context.getIrClass(FqName("java.lang.Class")).owner + + private val reflectionClass = + context.getIrClass(FqName("kotlin.jvm.internal.Reflection")).owner + + private val getOrCreateKotlinClass = + reflectionClass.functions.single { it.name.asString() == "getOrCreateKotlinClass" && it.valueParameters.size == 1 } + + private val getOrCreateKotlinPackage = + reflectionClass.functions.single { it.name.asString() == "getOrCreateKotlinPackage" && it.valueParameters.size == 2 } + + private val arrayItemGetter = + context.ir.symbols.array.owner.functions.single { it.name.asString() == "get" } + + private val kPropertyType = + context.getIrClass(FqName("kotlin.reflect.KProperty")).owner.defaultType + + private val kPropertiesFieldType = + context.ir.symbols.array.createType(false, listOf(makeTypeProjection(kPropertyType, Variance.OUT_VARIANCE))) + + // TODO: remove code duplication with CallableReferenceLowering + private val IrMemberAccessExpression.parentJavaClassReference + get() = IrClassReferenceImpl( + startOffset, endOffset, + plainJavaClass.defaultType, + plainJavaClass.symbol, + CrIrType(context.state.typeMapper.mapImplementationOwner(descriptor)) + ) + + private fun IrBuilderWithScope.buildReflectedContainerReference(expression: IrMemberAccessExpression): IrExpression { + val parent = expression.getter?.owner?.parent ?: expression.field?.owner?.parent + return when (parent) { + is IrPackageFragment -> irCall(getOrCreateKotlinPackage).apply { + putValueArgument(0, expression.parentJavaClassReference) + putValueArgument(1, irString(this@PropertyReferenceLowering.context.state.moduleName)) + } + is IrClass -> irCall(getOrCreateKotlinClass).apply { + putValueArgument(0, expression.parentJavaClassReference) + } + else -> irNull() + } + } + + private class PropertyReferenceKind( + val interfaceSymbol: IrClassSymbol, + val reflectedSymbol: IrClassSymbol, + val wrapper: IrFunction + ) + + private fun propertyReferenceKind(mutable: Boolean, i: Int) = PropertyReferenceKind( + context.getIrClass(FqName("kotlin.jvm.internal.${if (mutable) "Mutable" else ""}PropertyReference$i")), + context.getIrClass(FqName("kotlin.jvm.internal.${if (mutable) "Mutable" else ""}PropertyReference${i}Impl")), + reflectionClass.functions.single { it.name.asString() == (if (mutable) "mutableProperty$i" else "property$i") } + ) + + private fun propertyReferenceKindFor(expression: IrMemberAccessExpression): PropertyReferenceKind = + expression.getter?.owner?.let { + val boundReceivers = listOfNotNull(expression.dispatchReceiver, expression.extensionReceiver).size + val needReceivers = listOfNotNull(it.dispatchReceiverParameter, it.extensionReceiverParameter).size + // PropertyReference1 will swap the receivers if bound with the extension one, and PropertyReference0 + // has no way to bind two receivers at once. + if (boundReceivers == 2 || (expression.extensionReceiver != null && needReceivers == 2)) + TODO("property reference with 2 receivers") + propertyReferenceKind(expression.setter != null, needReceivers - boundReceivers) + } ?: expression.field?.owner?.let { + propertyReferenceKind(!it.isFinal, if (it.isStatic || expression.dispatchReceiver != null) 0 else 1) + } ?: throw AssertionError("property has no getter and no field: ${expression.dump()}") + + private data class PropertyCacheKey(val symbol: IrSymbol, val reflected: Boolean) + private data class PropertyClassCacheKey(val symbol: IrSymbol, val boundReceiver: Boolean) + private data class PropertyInstance(val initializer: IrExpression, val index: Int) + + override fun lower(irClass: IrClass) { + val kProperties = mutableMapOf() + val kPropertyClasses = mutableMapOf() + val kPropertiesField = buildField { + name = Name.identifier(JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME) + type = kPropertiesFieldType + origin = JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE + isFinal = true + isStatic = true + } + + irClass.transformChildrenVoid(object : IrElementTransformerVoidWithContext() { + override fun visitPropertyReference(expression: IrPropertyReference): IrExpression = + cachedKProperty(expression) + + override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression = + cachedKProperty(expression) + + private fun cachedKProperty(expression: IrMemberAccessExpression): IrExpression { + // Reflected implementation does not support partial application; also, cannot cache instances with arguments. + if (expression.dispatchReceiver != null || expression.extensionReceiver != null) + return createSpecializedKProperty(expression) + + // For delegated properties, the getter and setter contain a reference each as the second argument to getValue + // and setValue. Since it's highly unlikely that anyone will call get/set on these, optimize for space. + val useReflectedImpl = expression.origin == IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE + return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { + val (_, index) = kProperties.getOrPut(PropertyCacheKey(expression.symbol, useReflectedImpl)) { + val kProperty = if (useReflectedImpl) + createReflectedKProperty(expression) + else + createSpecializedKProperty(expression) + PropertyInstance(kProperty, kProperties.size) + } + irCall(arrayItemGetter).apply { + dispatchReceiver = irGetField(null, kPropertiesField) + putValueArgument(0, irInt(index)) + } + } + } + + // Create an instance of KProperty that uses Java reflection to locate the getter and the setter. This kind of reference + // does not support local variables or bound receivers (e.g. `Class()::field`) and is slower, but takes up less space. + private fun createReflectedKProperty(expression: IrMemberAccessExpression): IrExpression { + val referenceKind = propertyReferenceKindFor(expression) + return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { + irCall(referenceKind.wrapper).apply { + putValueArgument(0, irCall(referenceKind.reflectedSymbol.constructors.single()).apply { + putValueArgument(0, buildReflectedContainerReference(expression)) + putValueArgument(1, irString(expression.descriptor.name.asString())) + putValueArgument(2, irString(expression.signature)) + }) + } + } + } + + // Create an instance of KProperty that overrides the get() and set() method to directly call getX() and setX() on the object. + // This is (relatively) fast, but space-inefficient. Also, the instances can store bound receivers in their fields. + private fun createSpecializedKProperty(expression: IrMemberAccessExpression): IrExpression { + val bound = expression.dispatchReceiver != null || expression.extensionReceiver != null + val referenceClass = kPropertyClasses.getOrPut(PropertyClassCacheKey(expression.symbol, bound)) { + createKPropertySubclass(expression) + } + return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { + irCall(referenceClass.constructors.single()).apply { + var index = 0 + expression.dispatchReceiver?.let { putValueArgument(index++, it) } + expression.extensionReceiver?.let { putValueArgument(index++, it) } + } + } + } + + private fun createKPropertySubclass(expression: IrMemberAccessExpression): IrClass { + val superClass = propertyReferenceKindFor(expression).interfaceSymbol.owner + val referenceClass = buildClass { + setSourceRange(expression) + name = Name.identifier("${expression.descriptor.name}\$${kPropertyClasses.size}") + origin = JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE + }.apply { + parent = irClass + superTypes += IrSimpleTypeImpl(superClass.symbol, false, listOf(), listOf()) + createImplicitParameterDeclarationWithWrappedDescriptor() + } + val referenceThis = referenceClass.thisReceiver!! + + buildConstructor { + setSourceRange(expression) + returnType = referenceClass.defaultType + origin = referenceClass.origin + isPrimary = true + }.apply { + parent = referenceClass + referenceClass.declarations.add(this) + + val constructor = this + // See propertyReferenceKindFor -- only one of them could ever be present. + val parameter = (expression.dispatchReceiver ?: expression.extensionReceiver)?.let { + buildValueParameter { + setSourceRange(expression) + name = Name.identifier("receiver") + type = it.type + index = valueParameters.size + origin = referenceClass.origin + }.apply { + parent = constructor + valueParameters.add(this) + } + } + body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) { + val superArgs = if (parameter == null) 0 else 1 + +irDelegatingConstructorCall(superClass.constructors.single { it.valueParameters.size == superArgs }).apply { + parameter?.let { putValueArgument(0, irGet(parameter)) } + } + +IrInstanceInitializerCallImpl(startOffset, endOffset, referenceClass.symbol, context.irBuiltIns.unitType) + } + } + + fun buildOverride(method: IrSimpleFunction, build: IrBlockBodyBuilder.(List) -> IrExpression) = + buildFun { + setSourceRange(expression) + name = if (method.name.asString() == "") Name.identifier("getName") else method.name + returnType = method.returnType + visibility = method.visibility + origin = referenceClass.origin + }.apply { + parent = referenceClass + referenceClass.declarations.add(this) + + overriddenSymbols.add(method.symbol) + dispatchReceiverParameter = referenceThis.copyTo(this) + for (parameter in method.valueParameters) + valueParameters.add(parameter.copyTo(this)) + body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) { + +irReturn(build(valueParameters)) + } + } + + val receiverField = superClass.properties.single { it.name.asString() == "receiver" }.backingField!! + fun IrBuilderWithScope.setReceiversOn(call: IrCall, valueParameters: List) { + var index = 0 + call.dispatchReceiver = call.symbol.owner.dispatchReceiverParameter?.let { + if (expression.dispatchReceiver != null) + irGetField(irGet(referenceThis), receiverField) + else + irImplicitCast(irGet(valueParameters[index++]), it.type) + } + call.extensionReceiver = call.symbol.owner.extensionReceiverParameter?.let { + if (expression.extensionReceiver != null) + irGetField(irGet(referenceThis), receiverField) + else + irImplicitCast(irGet(valueParameters[index++]), it.type) + } + } + + buildOverride(superClass.properties.single { it.name.asString() == "name" }.getter!!) { + irString(expression.descriptor.name.asString()) + } + + buildOverride(superClass.functions.single { it.name.asString() == "getOwner" }) { + buildReflectedContainerReference(expression) + } + + buildOverride(superClass.functions.single { it.name.asString() == "getSignature" }) { + irString(expression.signature) + } + + expression.getter?.owner?.let { getter -> + buildOverride(superClass.functions.single { it.name.asString() == "get" }) { valueParameters -> + irGet(getter.returnType, null, getter.symbol).apply { setReceiversOn(this, valueParameters) } + } + } + + expression.setter?.owner?.let { setter -> + buildOverride(superClass.functions.single { it.name.asString() == "set" }) { valueParameters -> + val value = irGet(valueParameters.last()) + irSet(setter.returnType, null, setter.symbol, value).apply { setReceiversOn(this, valueParameters) } + } + } + + expression.field?.owner?.let { + TODO("plain Java property reference") // no accessors, should generate direct field read/write instead + } + return referenceClass + } + }) + + // Put new field at the beginning so that static delegated properties with initializers work correctly. + if (kProperties.isNotEmpty()) { + irClass.declarations.add(0, kPropertiesField.apply { + parent = irClass + initializer = context.createIrBuilder(irClass.symbol).run { + val initializers = kProperties.values.sortedBy { it.index }.map { it.initializer } + irExprBody(irCall(this@PropertyReferenceLowering.context.ir.symbols.arrayOf).apply { + putValueArgument(0, IrVarargImpl(startOffset, endOffset, kPropertiesFieldType, kPropertyType, initializers)) + }) + } + }) + } + irClass.declarations.addAll(0, kPropertyClasses.values) + } +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DelegatedPropertyGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DelegatedPropertyGenerator.kt index d02a2293f31..81c22b76b50 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DelegatedPropertyGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DelegatedPropertyGenerator.kt @@ -191,7 +191,7 @@ class DelegatedPropertyGenerator(declarationGenerator: DeclarationGenerator) : D statementGenerator: StatementGenerator ): IrCallableReference = ReflectionReferencesGenerator(statementGenerator).generateCallableReference( - ktElement.startOffsetSkippingComments, ktElement.endOffset, type, + ktElement, type, referencedDescriptor, null, IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE ) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt index ae1fe3997b9..d8f3454c93d 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ReflectionReferencesGenerator.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.ir.util.referenceClassifier import org.jetbrains.kotlin.ir.util.referenceFunction import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtClassLiteralExpression +import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments import org.jetbrains.kotlin.resolve.BindingContext @@ -62,9 +63,6 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St val descriptorImportedFromObject = resultingDescriptor as? ImportedFromObjectCallableDescriptor<*> val referencedDescriptor = descriptorImportedFromObject?.callableFromObject ?: resultingDescriptor - val startOffset = ktCallableReference.startOffsetSkippingComments - val endOffset = ktCallableReference.endOffset - return statementGenerator.generateCallReceiver( ktCallableReference, resultingDescriptor, @@ -72,7 +70,7 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St isSafe = false ).call { dispatchReceiverValue, extensionReceiverValue -> generateCallableReference( - startOffset, endOffset, + ktCallableReference, getInferredTypeWithImplicitCastsOrFail(ktCallableReference), referencedDescriptor, resolvedCall.typeArguments @@ -84,27 +82,27 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St } fun generateCallableReference( - startOffset: Int, - endOffset: Int, + ktElement: KtElement, type: KotlinType, callableDescriptor: CallableDescriptor, typeArguments: Map?, origin: IrStatementOrigin? = null - ): IrCallableReference = - when (callableDescriptor) { - is FunctionDescriptor -> - generateFunctionReference( - startOffset, endOffset, type, - context.symbolTable.referenceFunction(callableDescriptor.original), - callableDescriptor, - typeArguments, - origin - ) - is PropertyDescriptor -> - generatePropertyReference(startOffset, endOffset, type, callableDescriptor, typeArguments, origin) + ): IrCallableReference { + val startOffset = ktElement.startOffsetSkippingComments + val endOffset = ktElement.endOffset + return when (callableDescriptor) { + is FunctionDescriptor -> { + val symbol = context.symbolTable.referenceFunction(callableDescriptor.original) + generateFunctionReference(startOffset, endOffset, type, symbol, callableDescriptor, typeArguments, origin) + } + is PropertyDescriptor -> { + val mutable = get(BindingContext.VARIABLE, ktElement)?.isVar ?: true + generatePropertyReference(startOffset, endOffset, type, callableDescriptor, typeArguments, origin, mutable) + } else -> throw AssertionError("Unexpected callable reference: $callableDescriptor") } + } fun generateLocalDelegatedPropertyReference( startOffset: Int, @@ -135,10 +133,11 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St type: KotlinType, propertyDescriptor: PropertyDescriptor, typeArguments: Map?, - origin: IrStatementOrigin? + origin: IrStatementOrigin?, + mutable: Boolean ): IrPropertyReference { val getterDescriptor = propertyDescriptor.getter - val setterDescriptor = propertyDescriptor.setter + val setterDescriptor = if (mutable) propertyDescriptor.setter else null val fieldSymbol = if (getterDescriptor == null) context.symbolTable.referenceField(propertyDescriptor) else null val getterSymbol = getterDescriptor?.let { context.symbolTable.referenceSimpleFunction(it.original) } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt index b32ab3b4c9b..5b85b0ffa1c 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt @@ -199,7 +199,7 @@ fun IrBuilderWithScope.irNotEquals(arg1: IrExpression, arg2: IrExpression) = irEquals(arg1, arg2, origin = IrStatementOrigin.EXCLEQ) ) -fun IrBuilderWithScope.irGet(type: IrType, receiver: IrExpression, getterSymbol: IrFunctionSymbol): IrCall = +fun IrBuilderWithScope.irGet(type: IrType, receiver: IrExpression?, getterSymbol: IrFunctionSymbol): IrCall = IrGetterCallImpl( startOffset, endOffset, type, @@ -210,6 +210,18 @@ fun IrBuilderWithScope.irGet(type: IrType, receiver: IrExpression, getterSymbol: origin = IrStatementOrigin.GET_PROPERTY ) +fun IrBuilderWithScope.irSet(type: IrType, receiver: IrExpression?, getterSymbol: IrFunctionSymbol, value: IrExpression): IrCall = + IrSetterCallImpl( + startOffset, endOffset, + type, + getterSymbol, getterSymbol.descriptor, + typeArgumentsCount = 0, + dispatchReceiver = receiver, + extensionReceiver = null, + argument = value, + origin = IrStatementOrigin.EQ + ) + fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, type: IrType, typeArguments: List = emptyList()): IrCall = IrCallImpl(startOffset, endOffset, type, callee, callee.descriptor).apply { typeArguments.forEachIndexed { index, irType -> diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt index 3f0c07dcc1c..403af7ef769 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt @@ -46,6 +46,7 @@ class ReflectionTypes(module: ModuleDescriptor, private val notFoundClasses: Not fun getKSuspendFunction(n: Int): ClassDescriptor = find("$K_SUSPEND_FUNCTION_PREFIX$n", n + 1) val kClass: ClassDescriptor by ClassLookup(1) + val kProperty: ClassDescriptor by ClassLookup(1) val kProperty0: ClassDescriptor by ClassLookup(1) val kProperty1: ClassDescriptor by ClassLookup(2) val kProperty2: ClassDescriptor by ClassLookup(3)