diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SpecialBridgeMethods.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SpecialBridgeMethods.kt new file mode 100644 index 00000000000..5cc615533b1 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SpecialBridgeMethods.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2010-2019 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.common.lower + +import org.jetbrains.kotlin.backend.common.CommonBackendContext +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.expressions.IrConstKind +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.ir.util.parentAsClass +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +class SpecialBridgeMethods(val context: CommonBackendContext) { + private data class SpecialMethodDescription(val kotlinFqClassName: FqName?, val name: Name, val arity: Int) + + private fun makeDescription(classFqName: String, funName: String, arity: Int) = + SpecialMethodDescription( + FqName(classFqName), + Name.identifier(funName), + arity + ) + + private fun IrSimpleFunction.toDescription() = SpecialMethodDescription( + parentAsClass.fqNameWhenAvailable, + name, + valueParameters.size + ) + + @Suppress("UNUSED_PARAMETER") + private fun constFalse(bridge: IrSimpleFunction) = + IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.booleanType, IrConstKind.Boolean, false) + + @Suppress("UNUSED_PARAMETER") + private fun constNull(bridge: IrSimpleFunction) = + IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.anyNType, IrConstKind.Null, null) + + @Suppress("UNUSED_PARAMETER") + private fun constMinusOne(bridge: IrSimpleFunction) = + IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.intType, IrConstKind.Int, -1) + + private fun getSecondArg(bridge: IrSimpleFunction) = + IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, bridge.valueParameters[1].symbol) + + private val SPECIAL_METHODS_WITH_DEFAULTS_MAP = mapOf IrExpression>( + makeDescription("kotlin.collections.Collection", "contains", 1) to ::constFalse, + makeDescription("kotlin.collections.MutableCollection", "remove", 1) to ::constFalse, + makeDescription("kotlin.collections.Map", "containsKey", 1) to ::constFalse, + makeDescription("kotlin.collections.Map", "containsValue", 1) to ::constFalse, + makeDescription("kotlin.collections.MutableMap", "remove", 2) to ::constFalse, + makeDescription("kotlin.collections.Map", "getOrDefault", 1) to ::getSecondArg, + makeDescription("kotlin.collections.Map", "get", 1) to ::constNull, + makeDescription("kotlin.collections.MutableMap", "remove", 1) to ::constNull, + makeDescription("kotlin.collections.List", "indexOf", 1) to ::constMinusOne, + makeDescription("kotlin.collections.List", "lastIndexOf", 1) to ::constMinusOne + ) + + fun findSpecialWithOverride(irFunction: IrSimpleFunction): Pair IrExpression>? { + irFunction.allOverridden().forEach { overridden -> + val description = overridden.toDescription() + SPECIAL_METHODS_WITH_DEFAULTS_MAP[description]?.let { + return Pair(overridden, it) + } + } + return null + } +} + +fun IrSimpleFunction.allOverridden(): Sequence { + val visited = mutableSetOf() + + fun IrSimpleFunction.search(): Sequence { + if (this in visited) return emptySequence() + return sequence { + yield(this@search) + visited.add(this@search) + overriddenSymbols.forEach { yieldAll(it.owner.search()) } + } + } + + return search().drop(1) // First element is `this` +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt index a05e16f888a..c1c47d77e3e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt @@ -7,14 +7,15 @@ package org.jetbrains.kotlin.ir.backend.js.lower import org.jetbrains.kotlin.backend.common.ClassLoweringPass import org.jetbrains.kotlin.backend.common.bridges.FunctionHandle -import org.jetbrains.kotlin.backend.common.bridges.findInterfaceImplementation import org.jetbrains.kotlin.backend.common.bridges.generateBridges import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny import org.jetbrains.kotlin.backend.common.ir.isSuspend +import org.jetbrains.kotlin.backend.common.lower.SpecialBridgeMethods import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlockBody +import org.jetbrains.kotlin.backend.common.lower.irNot import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin @@ -29,7 +30,6 @@ import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.resolve.DescriptorUtils // Constructs bridges for inherited generic functions // @@ -52,6 +52,8 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils // class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { + private val specialBridgeMethods = SpecialBridgeMethods(context) + override fun lower(irClass: IrClass) { irClass.declarations .asSequence() @@ -66,6 +68,11 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { if (function.isMethodOfAny()) return + val (specialOverride: IrSimpleFunction?, specialOverrideValueGenerator) = + specialBridgeMethods.findSpecialWithOverride(function) ?: Pair(null, null) + + val specialOverrideSignature = specialOverride?.let { FunctionAndSignature(it) } + val bridgesToGenerate = generateBridges( function = IrBasedFunctionHandle(function), signature = { FunctionAndSignature(it.function) } @@ -85,7 +92,16 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { continue } - irClass.declarations.add(createBridge(function, from.function, to.function)) + val bridge: IrDeclaration = when { + specialOverrideSignature == from -> + createBridge(function, from.function, to.function, specialOverrideValueGenerator) + + else -> + createBridge(function, from.function, to.function, null) + } + + + irClass.declarations.add(bridge) } } @@ -95,7 +111,8 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { private fun createBridge( function: IrSimpleFunction, bridge: IrSimpleFunction, - delegateTo: IrSimpleFunction + delegateTo: IrSimpleFunction, + defaultValueGenerator: ((IrSimpleFunction) -> IrExpression)? ): IrFunction { val origin = @@ -130,6 +147,16 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { } context.createIrBuilder(irFunction.symbol).irBlockBody(irFunction) { + if (defaultValueGenerator != null) { + irFunction.valueParameters.forEach { + +irIfThen( + context.irBuiltIns.unitType, + irNot(irIs(irGet(it), delegateTo.valueParameters[it.index].type)), + irReturn(defaultValueGenerator(irFunction)) + ) + } + } + val call = irCall(delegateTo.symbol) call.dispatchReceiver = irGet(irFunction.dispatchReceiverParameter!!) irFunction.extensionReceiverParameter?.let { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt index a09f7f6c551..b076ee903aa 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt @@ -7,8 +7,8 @@ package org.jetbrains.kotlin.backend.jvm.codegen import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.ir.ir2string +import org.jetbrains.kotlin.backend.common.lower.allOverridden import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.backend.jvm.lower.allOverridden import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.codegen.* diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index 81238180299..6edbf246dc6 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt @@ -6,6 +6,8 @@ package org.jetbrains.kotlin.backend.jvm.lower import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.common.lower.SpecialBridgeMethods +import org.jetbrains.kotlin.backend.common.lower.allOverridden import org.jetbrains.kotlin.backend.common.bridges.FunctionHandle import org.jetbrains.kotlin.backend.common.bridges.findAllReachableDeclarations import org.jetbrains.kotlin.backend.common.bridges.findConcreteSuperDeclaration @@ -25,12 +27,9 @@ import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl -import org.jetbrains.kotlin.ir.expressions.IrConstKind import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl @@ -45,7 +44,6 @@ import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.isInterface import org.jetbrains.kotlin.ir.util.parentAsClass -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.org.objectweb.asm.commons.Method @@ -62,6 +60,8 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass private val typeMapper = state.typeMapper + private val specialBridgeMethods = SpecialBridgeMethods(context) + override fun lower(irClass: IrClass) { // TODO: Bridges should be generated for @JvmDefaults, so the interface check is too optimistic. if (irClass.isInterface || irClass.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) { @@ -91,7 +91,7 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass val ourMethodName = ourSignature.name val (specialOverride, specialOverrideValueGenerator) = - findSpecialWithOverride(irFunction) ?: Pair(null, null) + specialBridgeMethods.findSpecialWithOverride(irFunction) ?: Pair(null, null) val specialOverrideSignature = specialOverride?.getJvmSignature() @@ -389,46 +389,7 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass } } - private data class SpecialMethodDescription(val kotlinFqClassName: FqName?, val name: Name, val arity: Int) - private fun makeDescription(classFqName: String, funName: String, arity: Int) = SpecialMethodDescription(FqName(classFqName), Name.identifier(funName), arity) - - private fun IrSimpleFunction.toDescription() = SpecialMethodDescription(parentAsClass.fqNameWhenAvailable, name, valueParameters.size) - - private fun constFalse(bridge: IrSimpleFunction) = - IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.booleanType, IrConstKind.Boolean, false) - - private fun constNull(bridge: IrSimpleFunction) = - IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.anyNType, IrConstKind.Null, null) - - private fun constMinusOne(bridge: IrSimpleFunction) = - IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.intType, IrConstKind.Int, -1) - - private fun getSecondArg(bridge: IrSimpleFunction) = - IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, bridge.valueParameters[1].symbol) - - private val SPECIAL_METHODS_WITH_DEFAULTS_MAP = mapOf IrExpression>( - makeDescription("kotlin.collections.Collection", "contains", 1) to ::constFalse, - makeDescription("kotlin.collections.MutableCollection", "remove", 1) to ::constFalse, - makeDescription("kotlin.collections.Map", "containsKey", 1) to ::constFalse, - makeDescription("kotlin.collections.Map", "containsValue", 1) to ::constFalse, - makeDescription("kotlin.collections.MutableMap", "remove", 2) to ::constFalse, - makeDescription("kotlin.collections.Map", "getOrDefault", 1) to ::getSecondArg, - makeDescription("kotlin.collections.Map", "get", 1) to ::constNull, - makeDescription("kotlin.collections.MutableMap", "remove", 1) to ::constNull, - makeDescription("kotlin.collections.List", "indexOf", 1) to ::constMinusOne, - makeDescription("kotlin.collections.List", "lastIndexOf", 1) to ::constMinusOne - ) - - private fun findSpecialWithOverride(irFunction: IrSimpleFunction): Pair IrExpression>? { - irFunction.allOverridden().forEach { overridden -> - val description = overridden.toDescription() - SPECIAL_METHODS_WITH_DEFAULTS_MAP[description]?.let { - return Pair(overridden, it) - } - } - return null - } private inner class FunctionHandleForIrFunction(val irFunction: IrSimpleFunction) : FunctionHandle { override val isDeclaration get() = irFunction.origin != IrDeclarationOrigin.FAKE_OVERRIDE @@ -464,20 +425,6 @@ private data class SignatureWithSource(val signature: Method, val source: IrSimp } } -fun IrSimpleFunction.allOverridden(): Sequence { - val visited = mutableSetOf() - - fun IrSimpleFunction.search(): Sequence { - if (this in visited) return emptySequence() - return sequence { - yield(this@search) - visited.add(this@search) - overriddenSymbols.forEach { yieldAll(it.owner.search()) } - } - } - - return search().drop(1) // First element is `this` -} fun IrSimpleFunction.overriddenInClasses(): Sequence = allOverridden().filter { !(it.parent.safeAs()?.isInterface ?: true) }