[JS IR BE] Support bridges for special methods
Mostly reused from JVM IR lowering. Shared logic moved to common backend module.
This commit is contained in:
+88
@@ -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<SpecialMethodDescription, (IrSimpleFunction) -> 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<IrSimpleFunction, (IrSimpleFunction) -> 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<IrSimpleFunction> {
|
||||
val visited = mutableSetOf<IrSimpleFunction>()
|
||||
|
||||
fun IrSimpleFunction.search(): Sequence<IrSimpleFunction> {
|
||||
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`
|
||||
}
|
||||
+31
-4
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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.*
|
||||
|
||||
+5
-58
@@ -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<SpecialMethodDescription, (IrSimpleFunction) -> 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<IrSimpleFunction, (IrSimpleFunction) -> 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<IrSimpleFunction> {
|
||||
val visited = mutableSetOf<IrSimpleFunction>()
|
||||
|
||||
fun IrSimpleFunction.search(): Sequence<IrSimpleFunction> {
|
||||
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<IrSimpleFunction> =
|
||||
allOverridden().filter { !(it.parent.safeAs<IrClass>()?.isInterface ?: true) }
|
||||
|
||||
Reference in New Issue
Block a user