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:
@@ -339,6 +339,7 @@ private val jvmFilePhases = listOf(
|
|||||||
|
|
||||||
interfacePhase,
|
interfacePhase,
|
||||||
inheritedDefaultMethodsOnClassesPhase,
|
inheritedDefaultMethodsOnClassesPhase,
|
||||||
|
replaceDefaultImplsOverriddenSymbolsPhase,
|
||||||
interfaceSuperCallsPhase,
|
interfaceSuperCallsPhase,
|
||||||
interfaceDefaultCallsPhase,
|
interfaceDefaultCallsPhase,
|
||||||
interfaceObjectCallsPhase,
|
interfaceObjectCallsPhase,
|
||||||
|
|||||||
+40
-37
@@ -34,10 +34,7 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
|
|||||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.*
|
||||||
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.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
internal val inheritedDefaultMethodsOnClassesPhase = makeIrFilePhase(
|
internal val inheritedDefaultMethodsOnClassesPhase = makeIrFilePhase(
|
||||||
@@ -46,39 +43,17 @@ internal val inheritedDefaultMethodsOnClassesPhase = makeIrFilePhase(
|
|||||||
description = "Add bridge-implementations in classes that inherit default implementations from interfaces"
|
description = "Add bridge-implementations in classes that inherit default implementations from interfaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendContext) : IrElementVisitorVoid, ClassLoweringPass {
|
private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||||
|
|
||||||
override fun visitElement(element: IrElement) {
|
|
||||||
element.acceptChildrenVoid(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun lower(irClass: IrClass) {
|
override fun lower(irClass: IrClass) {
|
||||||
if (!irClass.isJvmInterface)
|
if (!irClass.isJvmInterface) {
|
||||||
generateInterfaceMethods(irClass)
|
irClass.declarations.transformInPlace { declaration ->
|
||||||
|
(declaration as? IrSimpleFunction)?.findInterfaceImplementation(context.state.jvmDefaultMode)?.let { implementation ->
|
||||||
super.visitClass(irClass)
|
generateDelegationToDefaultImpl(implementation, declaration)
|
||||||
}
|
} ?: declaration
|
||||||
|
}
|
||||||
private fun generateInterfaceMethods(irClass: IrClass) {
|
|
||||||
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(
|
private fun generateDelegationToDefaultImpl(
|
||||||
interfaceImplementation: IrSimpleFunction,
|
interfaceImplementation: IrSimpleFunction,
|
||||||
classOverride: 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(
|
internal val interfaceSuperCallsPhase = makeIrFilePhase(
|
||||||
lowering = ::InterfaceSuperCallsLowering,
|
lowering = ::InterfaceSuperCallsLowering,
|
||||||
name = "InterfaceSuperCalls",
|
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.
|
* 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) =
|
private fun isDefaultImplsBridge(f: IrSimpleFunction) =
|
||||||
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE ||
|
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE ||
|
||||||
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY ||
|
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY ||
|
||||||
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_TO_SYNTHETIC ||
|
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_TO_SYNTHETIC ||
|
||||||
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY_SYNTHETIC
|
f.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY_SYNTHETIC
|
||||||
|
|
||||||
internal fun IrSimpleFunction.findInterfaceImplementation(jvmDefaultMode: JvmDefaultMode): IrSimpleFunction? {
|
internal fun IrSimpleFunction.findInterfaceImplementation(jvmDefaultMode: JvmDefaultMode): IrSimpleFunction? {
|
||||||
if (!isFakeOverride) return null
|
if (!isFakeOverride) return null
|
||||||
|
|||||||
Reference in New Issue
Block a user