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 c6e615b909d..d4026b969e7 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 @@ -100,10 +100,11 @@ private val lateinitUsageLoweringPhase = makeIrFilePhase( description = "Insert checks for lateinit field references" ) -private val propertiesPhase = makeIrFilePhase( +internal val propertiesPhase = makeIrFilePhase( ::JvmPropertiesLowering, name = "Properties", - description = "Move fields and accessors for properties to their classes, and create synthetic methods for property annotations", + description = "Move fields and accessors for properties to their classes, replace calls to default property accessors " + + "with field accesses, remove unused accessors and create synthetic methods for property annotations", stickyPostconditions = setOf((PropertiesLowering)::checkNoProperties) ) @@ -294,9 +295,8 @@ private val jvmFilePhases = inlineCallableReferenceToLambdaPhase then propertyReferencePhase then constPhase then - propertiesToFieldsPhase then - remapObjectFieldAccesses then propertiesPhase then + remapObjectFieldAccesses then anonymousObjectSuperConstructorPhase then tailrecPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmPropertiesLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmPropertiesLowering.kt index 08efc095a53..2639484ca40 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmPropertiesLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmPropertiesLowering.kt @@ -6,76 +6,137 @@ package org.jetbrains.kotlin.backend.jvm.lower import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext import org.jetbrains.kotlin.backend.common.ir.copyTo +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrDeclaration -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl -import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl -import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.types.classifierOrFail +import org.jetbrains.kotlin.ir.types.makeNotNull import org.jetbrains.kotlin.ir.types.typeWith +import org.jetbrains.kotlin.ir.util.coerceToUnit +import org.jetbrains.kotlin.ir.util.hasAnnotation +import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* -class JvmPropertiesLowering(private val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass { +class JvmPropertiesLowering(private val backendContext: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass { override fun lower(irFile: IrFile) { irFile.accept(this, null) } - override fun visitClass(declaration: IrClass): IrStatement { + override fun visitClassNew(declaration: IrClass): IrStatement { declaration.transformChildrenVoid(this) - declaration.transformDeclarationsFlat { lowerProperty(it, declaration.kind) } + declaration.transformDeclarationsFlat { if (it is IrProperty) lowerProperty(it, declaration.kind) else null } return declaration } - private fun lowerProperty(declaration: IrDeclaration, kind: ClassKind): List? = - if (declaration is IrProperty) - ArrayList(4).apply { - val field = declaration.backingField + override fun visitCall(expression: IrCall): IrExpression { + val simpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression) + val property = simpleFunction.correspondingPropertySymbol?.owner ?: return super.visitCall(expression) + expression.transformChildrenVoid() - // JvmFields in a companion object refer to companion's owners and should not be generated within companion. - if ((kind != ClassKind.ANNOTATION_CLASS || field?.isStatic == true) && field?.parent == declaration.parent) { - addIfNotNull(field) - } - addIfNotNull(declaration.getter) - addIfNotNull(declaration.setter) - - if (!declaration.isFakeOverride && declaration.annotations.isNotEmpty()) { - add(createSyntheticMethodForAnnotations(declaration)) + if (shouldSubstituteAccessorWithField(property, simpleFunction)) { + backendContext.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).apply { + return when (simpleFunction) { + property.getter -> substituteGetter(property, expression) + property.setter -> substituteSetter(property, expression) + else -> error("Orphaned property getter/setter: ${simpleFunction.render()}") } } - else - null + } - private fun createSyntheticMethodForAnnotations(declaration: IrProperty): IrFunctionImpl { - val descriptor = WrappedSimpleFunctionDescriptor(declaration.descriptor.annotations) - val symbol = IrSimpleFunctionSymbolImpl(descriptor) - val name = computeSyntheticMethodName(declaration) - return IrFunctionImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS, symbol, - Name.identifier(name), declaration.visibility, Modality.OPEN, context.irBuiltIns.unitType, - isInline = false, isExternal = false, isTailrec = false, isSuspend = false, isExpect = false, isFakeOverride = false, - isOperator = false - ).apply { - descriptor.bind(this) + return expression + } - val extensionReceiver = declaration.getter?.extensionReceiverParameter - if (extensionReceiver != null) { + private fun IrBuilderWithScope.substituteSetter(irProperty: IrProperty, expression: IrCall): IrExpression = + patchReceiver(irSetField(expression.dispatchReceiver, irProperty.backingField!!, expression.getValueArgument(0)!!)) + + private fun IrBuilderWithScope.substituteGetter(irProperty: IrProperty, expression: IrCall): IrExpression { + val backingField = irProperty.backingField!! + val value = irGetField(expression.dispatchReceiver, backingField) + return if (irProperty.isLateinit) { + irBlock { + val tmpVal = irTemporary(value) + +irIfNull( + expression.type.makeNotNull(), + irGet(tmpVal), + backendContext.throwUninitializedPropertyAccessException(this, backingField.name.asString()), + irGet(tmpVal) + ) + } + } else { + value + } + } + + private fun IrBuilderWithScope.patchReceiver(expression: IrFieldAccessExpression): IrExpression = + if (expression.symbol.owner.isStatic && expression.receiver != null) { + irBlock { + +expression.receiver!!.coerceToUnit(context.irBuiltIns) + expression.receiver = null + +expression + } + } else { + expression + } + + private fun lowerProperty(declaration: IrProperty, kind: ClassKind): List? = + ArrayList(4).apply { + val field = declaration.backingField + + // JvmFields in a companion object refer to companion's owners and should not be generated within companion. + if ((kind != ClassKind.ANNOTATION_CLASS || field?.isStatic == true) && field?.parent == declaration.parent) { + addIfNotNull(field) + } + + if (!declaration.isConst) { + declaration.getter?.takeIf { !shouldSubstituteAccessorWithField(declaration, it) }?.let { add(it) } + declaration.setter?.takeIf { !shouldSubstituteAccessorWithField(declaration, it) }?.let { add(it) } + } + + if (!declaration.isFakeOverride && declaration.annotations.isNotEmpty()) { + add(createSyntheticMethodForAnnotations(declaration)) + } + } + + private fun shouldSubstituteAccessorWithField(property: IrProperty, accessor: IrSimpleFunction?): Boolean { + if (accessor == null) return false + + if ((property.parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS) return false + + if (property.backingField?.hasAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME) == true) return true + + return accessor.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR && Visibilities.isPrivate(accessor.visibility) + } + + private fun createSyntheticMethodForAnnotations(declaration: IrProperty): IrFunctionImpl = + buildFun { + origin = JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS + name = Name.identifier(computeSyntheticMethodName(declaration)) + visibility = declaration.visibility + modality = Modality.OPEN + returnType = backendContext.irBuiltIns.unitType + }.apply { + declaration.getter?.extensionReceiverParameter?.let { extensionReceiver -> // Use raw type of extension receiver to avoid generic signature, which would be useless for this method. extensionReceiverParameter = extensionReceiver.copyTo(this, type = extensionReceiver.type.classifierOrFail.typeWith()) } @@ -86,13 +147,12 @@ class JvmPropertiesLowering(private val context: JvmBackendContext) : IrElementT annotations = declaration.annotations metadata = declaration.metadata } - } private fun computeSyntheticMethodName(property: IrProperty): String { val baseName = - if (context.state.languageVersionSettings.supportsFeature(LanguageFeature.UseGetterNameForPropertyAnnotationsMethodOnJvm)) { + if (backendContext.state.languageVersionSettings.supportsFeature(LanguageFeature.UseGetterNameForPropertyAnnotationsMethodOnJvm)) { property.getter?.let { getter -> - context.methodSignatureMapper.mapFunctionName(getter) + backendContext.methodSignatureMapper.mapFunctionName(getter) } ?: JvmAbi.getterName(property.name.asString()) } else { property.name.asString() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/MoveCompanionObjectFieldsLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/MoveCompanionObjectFieldsLowering.kt index f8aaa8a8bf4..79564e3f849 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/MoveCompanionObjectFieldsLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/MoveCompanionObjectFieldsLowering.kt @@ -10,8 +10,8 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference +import org.jetbrains.kotlin.backend.jvm.propertiesPhase import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.ir.builders.declarations.addField import org.jetbrains.kotlin.ir.builders.declarations.addProperty import org.jetbrains.kotlin.ir.builders.declarations.buildField import org.jetbrains.kotlin.ir.declarations.* @@ -21,7 +21,9 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl -import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.util.isObject +import org.jetbrains.kotlin.ir.util.parentAsClass +import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid internal val moveOrCopyCompanionObjectFieldsPhase = makeIrFilePhase( @@ -34,7 +36,7 @@ internal val remapObjectFieldAccesses = makeIrFilePhase( ::RemapObjectFieldAccesses, name = "RemapObjectFieldAccesses", description = "Make IrGetField/IrSetField to objects' fields point to the static versions", - prerequisite = setOf(propertiesToFieldsPhase) + prerequisite = setOf(propertiesPhase) ) private class MoveOrCopyCompanionObjectFieldsLowering(val context: JvmBackendContext) : ClassLoweringPass { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt deleted file mode 100644 index c1417840ef3..00000000000 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. - * 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.CommonBackendContext -import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.common.lower.createIrBuilder -import org.jetbrains.kotlin.backend.common.lower.irBlock -import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression -import org.jetbrains.kotlin.ir.expressions.IrTypeOperator -import org.jetbrains.kotlin.ir.expressions.impl.* -import org.jetbrains.kotlin.ir.types.makeNotNull -import org.jetbrains.kotlin.ir.util.coerceToUnit -import org.jetbrains.kotlin.ir.util.hasAnnotation -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid -import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME - -internal val propertiesToFieldsPhase = makeIrFilePhase( - ::PropertiesToFieldsLowering, - name = "PropertiesToFields", - description = "Replace calls to default property accessors with field access and remove those accessors" -) - -class PropertiesToFieldsLowering(val context: CommonBackendContext) : IrElementTransformerVoid(), FileLoweringPass { - override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(this) - } - - override fun visitProperty(declaration: IrProperty): IrStatement { - if (declaration.isConst || shouldSubstituteAccessorWithField(declaration, declaration.getter)) { - declaration.getter = null - } - if (declaration.isConst || shouldSubstituteAccessorWithField(declaration, declaration.setter)) { - declaration.setter = null - } - return super.visitProperty(declaration) - } - - override fun visitCall(expression: IrCall): IrExpression { - val simpleFunction = (expression.symbol.owner as? IrSimpleFunction) ?: return super.visitCall(expression) - val property = simpleFunction.correspondingPropertySymbol?.owner ?: return super.visitCall(expression) - - if (shouldSubstituteAccessorWithField(property, simpleFunction)) { - // property.getter & property.setter might be erased by the above function. - when (simpleFunction.valueParameters.size) { - 0 -> return substituteGetter(property, expression) - 1 -> return substituteSetter(property, expression) - } - } - - return super.visitCall(expression) - } - - private fun shouldSubstituteAccessorWithField(property: IrProperty, accessor: IrSimpleFunction?): Boolean { - if (accessor == null) return false - - if ((property.parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS) return false - - if (property.backingField?.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) == true) return true - - return accessor.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR && Visibilities.isPrivate(accessor.visibility) - } - - private fun substituteSetter(irProperty: IrProperty, expression: IrCall): IrExpression { - val backingField = irProperty.backingField!! - val receiver = expression.dispatchReceiver?.transform(this, null) - val setExpr = IrSetFieldImpl( - expression.startOffset, - expression.endOffset, - backingField.symbol, - receiver, - expression.getValueArgument(expression.valueArgumentsCount - 1)!!.transform(this, null), - expression.type, - expression.origin, - expression.superQualifierSymbol - ) - return buildSubstitution(backingField.isStatic, setExpr, receiver) - } - - private fun substituteGetter(irProperty: IrProperty, expression: IrCall): IrExpression { - val backingField = irProperty.backingField!! - val receiver = expression.dispatchReceiver?.transform(this, null) - val getExpr = IrGetFieldImpl( - expression.startOffset, - expression.endOffset, - backingField.symbol, - backingField.type, - receiver, - expression.origin, - expression.superQualifierSymbol - ) - val substitution = buildSubstitution(backingField.isStatic, getExpr, receiver) - return if (irProperty.isLateinit) - insertLateinitCheck(substitution, backingField) - else - substitution - } - - private fun insertLateinitCheck(expression: IrExpression, field: IrField): IrExpression { - val backendContext = context - val startOffset = expression.startOffset - val endOffset = expression.endOffset - val irBuilder = context.createIrBuilder(field.symbol, startOffset, endOffset) - irBuilder.run { - return irBlock(expression) { - val tmpVar = irTemporaryVar(expression) - +irIfThenElse( - expression.type.makeNotNull(), - irEqualsNull(irGet(tmpVar)), - backendContext.throwUninitializedPropertyAccessException(this, field.name.asString()), - irGet(expression.type.makeNotNull(), tmpVar.symbol) - ) - } - } - } - - private fun buildSubstitution(needBlock: Boolean, setOrGetExpr: IrFieldAccessExpression, receiver: IrExpression?): IrExpression { - if (receiver != null && needBlock) { - // Evaluate `dispatchReceiver` for the sake of its side effects, then return `setOrGetExpr`. - return context.createIrBuilder(setOrGetExpr.symbol, setOrGetExpr.startOffset, setOrGetExpr.endOffset).irBlock(setOrGetExpr) { - +receiver.coerceToUnit(context.irBuiltIns) - setOrGetExpr.receiver = null - +setOrGetExpr - } - } else { - // Just `setOrGetExpr` (`dispatchReceiver` is evaluated as a subexpression thereof) - return setOrGetExpr - } - } -}