org.jetbrains.kotlin.backend.jvm.lower.InheritedDefaultMethodsOnClassesLowering.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* 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.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
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.backend.jvm.ir.*
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.ir.IrElement
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.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.expressions.putArgument
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
internal val inheritedDefaultMethodsOnClassesPhase = makeIrFilePhase(
::InheritedDefaultMethodsOnClassesLowering,
name = "InheritedDefaultMethodsOnClasses",
description = "Add bridge-implementations in classes that inherit default implementations from interfaces"
)
private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) {
if (!irClass.isJvmInterface) {
irClass.declarations.transformInPlace {
transformMemberDeclaration(it)
}
}
}
private fun transformMemberDeclaration(declaration: IrDeclaration): IrDeclaration {
if (declaration !is IrSimpleFunction) return declaration
if (declaration.isFakeOverride && declaration.name.asString() == "clone") {
val overriddenFunctions = declaration.allOverridden(false)
val cloneFun = overriddenFunctions.find { it.parentAsClass.hasEqualFqName(StandardNames.FqNames.cloneable.toSafe()) }
if (cloneFun != null && overriddenFunctions.all { it.isFakeOverride || it == cloneFun }) {
return generateCloneImplementation(declaration, cloneFun)
}
}
val implementation = declaration.findInterfaceImplementation(context.config.jvmDefaultMode)
?: return declaration
return generateDelegationToDefaultImpl(implementation, declaration)
}
private fun generateCloneImplementation(fakeOverride: IrSimpleFunction, cloneFun: IrSimpleFunction): IrSimpleFunction {
assert(fakeOverride.isFakeOverride)
val irFunction = context.cachedDeclarations.getDefaultImplsRedirection(fakeOverride)
val irClass = fakeOverride.parentAsClass
val classStartOffset = irClass.startOffset
context.createJvmIrBuilder(irFunction.symbol, classStartOffset, classStartOffset).apply {
irFunction.body = irBlockBody {
+irReturn(
irCall(cloneFun, origin = null, superQualifierSymbol = cloneFun.parentAsClass.symbol).apply {
dispatchReceiver = irGet(irFunction.dispatchReceiverParameter!!)
}
)
}
}
return irFunction
}
private fun generateDelegationToDefaultImpl(
interfaceImplementation: IrSimpleFunction,
classOverride: IrSimpleFunction
): IrSimpleFunction {
val irFunction = context.cachedDeclarations.getDefaultImplsRedirection(classOverride)
val superMethod = firstSuperMethodFromKotlin(irFunction, interfaceImplementation).owner
val superClassType = superMethod.parentAsClass.defaultType
val defaultImplFun = context.cachedDeclarations.getDefaultImplsFunction(superMethod)
val classStartOffset = classOverride.parentAsClass.startOffset
val backendContext = context
context.createIrBuilder(irFunction.symbol, classStartOffset, classStartOffset).apply {
irFunction.body = irExprBody(irBlock {
val parameter2arguments = backendContext.multiFieldValueClassReplacements
.mapFunctionMfvcStructures(this, defaultImplFun, irFunction) { sourceParameter, _ ->
irGet(sourceParameter).let {
if (sourceParameter != irFunction.dispatchReceiverParameter) it
else it.reinterpretAsDispatchReceiverOfType(superClassType)
}
}
+irCall(defaultImplFun.symbol, irFunction.returnType).apply {
for (index in superMethod.parentAsClass.typeParameters.indices) {
putTypeArgument(index, createPlaceholderAnyNType(context.irBuiltIns))
}
passTypeArgumentsFrom(irFunction, offset = superMethod.parentAsClass.typeParameters.size)
for ((parameter, argument) in parameter2arguments) {
if (argument != null) {
putArgument(parameter, argument)
}
}
}
})
}
return irFunction
}
}
internal val replaceDefaultImplsOverriddenSymbolsPhase = makeIrFilePhase(
::ReplaceDefaultImplsOverriddenSymbols,
name = "ReplaceDefaultImplsOverriddenSymbols",
description = "Replace overridden symbols for methods inherited from interfaces to classes"
)
private class ReplaceDefaultImplsOverriddenSymbols(private val context: JvmBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) {
for (declaration in irClass.declarations) {
if (declaration is IrSimpleFunction) {
visitSimpleFunction(declaration)
}
}
}
// Functions introduced by InheritedDefaultMethodsOnClassesLowering may be inherited lower in the hierarchy.
// Here we use the same logic as the delegation itself (`getTargetForRedirection`) to determine
// if the overridden symbol has been, or will be, replaced and patch it accordingly.
fun visitSimpleFunction(declaration: IrSimpleFunction) {
declaration.overriddenSymbols = declaration.overriddenSymbols.map { symbol ->
if (symbol.owner.findInterfaceImplementation(context.config.jvmDefaultMode) != null)
context.cachedDeclarations.getDefaultImplsRedirection(symbol.owner).symbol
else symbol
}
}
}
internal val interfaceSuperCallsPhase = makeIrFilePhase(
lowering = ::InterfaceSuperCallsLowering,
name = "InterfaceSuperCalls",
description = "Redirect super interface calls to DefaultImpls"
)
private class InterfaceSuperCallsLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun visitCall(expression: IrCall): IrExpression {
val superQualifierClass = expression.superQualifierSymbol?.owner
if (superQualifierClass == null || !superQualifierClass.isInterface || expression.isSuperToAny()) {
return super.visitCall(expression)
}
val superCallee = expression.symbol.owner
if (superCallee.isDefinitelyNotDefaultImplsMethod(context.config.jvmDefaultMode, superCallee.resolveFakeOverride()))
return super.visitCall(expression)
val redirectTarget = context.cachedDeclarations.getDefaultImplsFunction(superCallee)
val newCall = createDelegatingCallWithPlaceholderTypeArguments(expression, redirectTarget, context.irBuiltIns)
postprocessMovedThis(newCall)
return super.visitCall(newCall)
}
private fun postprocessMovedThis(irCall: IrCall) {
val movedThisParameter = irCall.symbol.owner.valueParameters
.find { it.origin == IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER }
?: return
val movedThisParameterIndex = movedThisParameter.index
irCall.putValueArgument(
movedThisParameterIndex,
irCall.getValueArgument(movedThisParameterIndex)?.reinterpretAsDispatchReceiverOfType(movedThisParameter.type)
)
}
}
// Given a dispatch receiver expression, wrap it in REINTERPRET_CAST to the given type,
// unless it's a value of inline class (which could be boxed at this point).
// Avoids a CHECKCAST on a moved dispatch receiver argument.
internal fun IrExpression.reinterpretAsDispatchReceiverOfType(irType: IrType): IrExpression =
if (this.type.isInlineClassType())
this
else
IrTypeOperatorCallImpl(
this.startOffset, this.endOffset,
irType, IrTypeOperator.REINTERPRET_CAST, irType,
this
)
internal val interfaceDefaultCallsPhase = makeIrFilePhase(
lowering = ::InterfaceDefaultCallsLowering,
name = "InterfaceDefaultCalls",
description = "Redirect interface calls with default arguments to DefaultImpls (except method compiled to JVM defaults)"
)
private class InterfaceDefaultCallsLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {
// TODO If there are no default _implementations_ we can avoid generating defaultImpls class entirely by moving default arg dispatchers to the interface class
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun visitCall(expression: IrCall): IrExpression {
val callee = expression.symbol.owner
if (!callee.hasInterfaceParent() ||
callee.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
callee.isSimpleFunctionCompiledToJvmDefault(context.config.jvmDefaultMode)
) {
return super.visitCall(expression)
}
val redirectTarget = context.cachedDeclarations.getDefaultImplsFunction(callee)
// InterfaceLowering bridges from DefaultImpls in compatibility mode -- if that's the case,
// this phase will inadvertently cause a recursive loop as the bridge on the DefaultImpls
// gets redirected to call itself.
if (redirectTarget == currentFunction?.irElement) return super.visitCall(expression)
val newCall = createDelegatingCallWithPlaceholderTypeArguments(expression, redirectTarget, context.irBuiltIns)
return super.visitCall(newCall)
}
}
internal fun IrSimpleFunction.isDefinitelyNotDefaultImplsMethod(
jvmDefaultMode: JvmDefaultMode,
implementation: IrSimpleFunction?,
): Boolean =
implementation == null ||
implementation.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB ||
implementation.isCompiledToJvmDefault(jvmDefaultMode) ||
origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
hasAnnotation(PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME) ||
isCloneableClone()
private fun IrSimpleFunction.isCloneableClone(): Boolean =
name.asString() == "clone" &&
(parent as? IrClass)?.fqNameWhenAvailable?.asString() == "kotlin.Cloneable" &&
valueParameters.isEmpty()
internal val interfaceObjectCallsPhase = makeIrFilePhase(
lowering = ::InterfaceObjectCallsLowering,
name = "InterfaceObjectCalls",
description = "Resolve calls to Object methods on interface types to virtual methods"
)
private class InterfaceObjectCallsLowering(val context: JvmBackendContext) : IrElementVisitorVoid, FileLoweringPass {
override fun lower(irFile: IrFile) = irFile.acceptChildren(this, null)
override fun visitElement(element: IrElement) {
element.acceptChildren(this, null)
}
override fun visitCall(expression: IrCall) {
expression.acceptChildren(this, null)
if (expression.superQualifierSymbol != null && !expression.isSuperToAny()) return
val callee = expression.symbol.owner
if (!callee.isMethodOfAny()) return
if (!callee.hasInterfaceParent() && expression.dispatchReceiver?.run { type.erasedUpperBound.isJvmInterface } != true) return
val resolved = callee.resolveFakeOverride() ?: return
expression.symbol = resolved.symbol
if (expression.superQualifierSymbol != null) {
expression.superQualifierSymbol = context.irBuiltIns.anyClass
}
}
}
/**
* Given a fake override in a class, returns an overridden declaration with implementation in interface, such that a method delegating to that
* interface implementation should be generated into the class containing the fake override; or null if the given function is not a fake
* override of any interface implementation or such method was already generated into the superclass or is a method from Any.
*/
internal fun IrSimpleFunction.findInterfaceImplementation(jvmDefaultMode: JvmDefaultMode): IrSimpleFunction? {
if (!isFakeOverride) return null
val parent = parent
if (parent is IrClass && (parent.isJvmInterface || parent.isFromJava())) return null
val implementation = resolveFakeOverride(toSkip = ::isDefaultImplsBridge) ?: return null
if (!implementation.hasInterfaceParent()
|| DescriptorVisibilities.isPrivate(implementation.visibility)
|| implementation.isDefinitelyNotDefaultImplsMethod(jvmDefaultMode, implementation)
|| implementation.isMethodOfAny()
) {
return null
}
// Only generate interface delegation for functions immediately inherited from an interface.
// (Otherwise, delegation will be present in the parent class)
if (overriddenSymbols.any {
!it.owner.parentAsClass.isInterface &&
it.owner.modality != Modality.ABSTRACT &&
it.owner.resolveFakeOverride(toSkip = ::isDefaultImplsBridge) == implementation
}) {
return null
}
return implementation
}
private fun isDefaultImplsBridge(f: IrSimpleFunction) =
f.origin == JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE