JVM IR: avoid quadratic complexity in InheritedDefaultMethodsOnClassesLowering

Visiting all children and not stopping on classes made
`ClassLoweringPass.runOnFilePostfix` perform with the complexity of the
square of the amount of class nesting levels.
This commit is contained in:
Alexander Udalov
2020-10-12 19:10:26 +02:00
parent 5ca4c2ce1a
commit 1859d4d340
2 changed files with 41 additions and 37 deletions
@@ -339,6 +339,7 @@ private val jvmFilePhases = listOf(
interfacePhase,
inheritedDefaultMethodsOnClassesPhase,
replaceDefaultImplsOverriddenSymbolsPhase,
interfaceSuperCallsPhase,
interfaceDefaultCallsPhase,
interfaceObjectCallsPhase,
@@ -34,10 +34,7 @@ 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.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal val inheritedDefaultMethodsOnClassesPhase = makeIrFilePhase(
@@ -46,39 +43,17 @@ internal val inheritedDefaultMethodsOnClassesPhase = makeIrFilePhase(
description = "Add bridge-implementations in classes that inherit default implementations from interfaces"
)
private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendContext) : IrElementVisitorVoid, ClassLoweringPass {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) {
if (!irClass.isJvmInterface)
generateInterfaceMethods(irClass)
super.visitClass(irClass)
}
private fun generateInterfaceMethods(irClass: IrClass) {
irClass.declarations.transformInPlace { declaration ->
(declaration as? IrSimpleFunction)?.findInterfaceImplementation(context.state.jvmDefaultMode)?.let { implementation ->
generateDelegationToDefaultImpl(implementation, declaration)
} ?: declaration
if (!irClass.isJvmInterface) {
irClass.declarations.transformInPlace { declaration ->
(declaration as? IrSimpleFunction)?.findInterfaceImplementation(context.state.jvmDefaultMode)?.let { implementation ->
generateDelegationToDefaultImpl(implementation, declaration)
} ?: declaration
}
}
}
// Functions introduced by this lowering may be inherited lower in the hierarchy.
// Here we use the same logic as the delegation itself (`getTargetForRedirection`) to determine
// if the overriden symbol has been, or will be, replaced and patch it accordingly.
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
declaration.overriddenSymbols = declaration.overriddenSymbols.map { symbol ->
if (symbol.owner.findInterfaceImplementation(context.state.jvmDefaultMode) != null)
context.cachedDeclarations.getDefaultImplsRedirection(symbol.owner).symbol
else symbol
}
super.visitSimpleFunction(declaration)
}
private fun generateDelegationToDefaultImpl(
interfaceImplementation: IrSimpleFunction,
classOverride: IrSimpleFunction
@@ -110,6 +85,34 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo
}
}
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) : FileLoweringPass, IrElementVisitorVoid {
override fun lower(irFile: IrFile) {
irFile.acceptVoid(this)
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
// 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.
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
declaration.overriddenSymbols = declaration.overriddenSymbols.map { symbol ->
if (symbol.owner.findInterfaceImplementation(context.state.jvmDefaultMode) != null)
context.cachedDeclarations.getDefaultImplsRedirection(symbol.owner).symbol
else symbol
}
super.visitSimpleFunction(declaration)
}
}
internal val interfaceSuperCallsPhase = makeIrFilePhase(
lowering = ::InterfaceSuperCallsLowering,
name = "InterfaceSuperCalls",
@@ -228,10 +231,10 @@ private class InterfaceObjectCallsLowering(val context: JvmBackendContext) : IrE
* override of any interface implementation or such method was already generated into the superclass or is a method from Any.
*/
private fun isDefaultImplsBridge(f: IrSimpleFunction) =
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE ||
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY ||
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_TO_SYNTHETIC ||
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY_SYNTHETIC
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE ||
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY ||
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_TO_SYNTHETIC ||
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY_SYNTHETIC
internal fun IrSimpleFunction.findInterfaceImplementation(jvmDefaultMode: JvmDefaultMode): IrSimpleFunction? {
if (!isFakeOverride) return null