[JS IR BE] support SAM conversions in DCE-driven mode

This commit is contained in:
Anton Bannykh
2020-03-05 18:17:47 +03:00
committed by Anton Bannykh
parent 8fcd73e3cb
commit abc6ecaa1c
19 changed files with 48 additions and 30 deletions
@@ -21,11 +21,9 @@ import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.isNullable import org.jetbrains.kotlin.ir.types.isNullable
@@ -58,9 +56,9 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) :
// Coming from the frontend, every SAM interface is associated with exactly one function type // Coming from the frontend, every SAM interface is associated with exactly one function type
// (see SamType.getKotlinFunctionType). This is why we can cache implementations just based on // (see SamType.getKotlinFunctionType). This is why we can cache implementations just based on
// the superType. // the superType.
private val cachedImplementations = mutableMapOf<IrType, IrClass>() protected val cachedImplementations = mutableMapOf<IrType, IrClass>()
private val inlineCachedImplementations = mutableMapOf<IrType, IrClass>() protected val inlineCachedImplementations = mutableMapOf<IrType, IrClass>()
private var enclosingContainer: IrDeclarationContainer? = null protected var enclosingContainer: IrDeclarationContainer? = null
abstract fun getWrapperVisibility(expression: IrTypeOperatorCall, scopes: List<ScopeWithIr>): Visibility abstract fun getWrapperVisibility(expression: IrTypeOperatorCall, scopes: List<ScopeWithIr>): Visibility
@@ -81,6 +79,10 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) :
} }
} }
protected open fun currentScopeSymbol(): IrSymbol? {
return currentScope?.scope?.scopeOwnerSymbol
}
override fun visitClassNew(declaration: IrClass): IrStatement { override fun visitClassNew(declaration: IrClass): IrStatement {
val prevContainer = enclosingContainer val prevContainer = enclosingContainer
if (prevContainer == null || prevContainer is IrFile) if (prevContainer == null || prevContainer is IrFile)
@@ -99,7 +101,7 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) :
val erasedSuperType = getSuperTypeForWrapper(expression.typeOperand) val erasedSuperType = getSuperTypeForWrapper(expression.typeOperand)
val superType = if (expression.typeOperand.isNullable()) erasedSuperType.makeNullable() else erasedSuperType val superType = if (expression.typeOperand.isNullable()) erasedSuperType.makeNullable() else erasedSuperType
val invokable = expression.argument.transform(this, null) val invokable = expression.argument.transform(this, null)
context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).apply { context.createIrBuilder(currentScopeSymbol()!!).apply {
// Do not generate a wrapper class for null, it has no invoke() anyway. // Do not generate a wrapper class for null, it has no invoke() anyway.
if (invokable.isNullConst()) if (invokable.isNullConst())
return invokable return invokable
@@ -521,11 +521,11 @@ private val bridgesConstructionPhase = makeDeclarationTransformerPhase(
prerequisite = setOf(suspendFunctionsLoweringPhase) prerequisite = setOf(suspendFunctionsLoweringPhase)
) )
private val singleAbstractMethodPhase = makeJsModulePhase( private val singleAbstractMethodPhase = makeBodyLoweringPhase(
::JsSingleAbstractMethodLowering, ::JsSingleAbstractMethodLowering,
name = "SingleAbstractMethod", name = "SingleAbstractMethod",
description = "Replace SAM conversions with instances of interface-implementing classes" description = "Replace SAM conversions with instances of interface-implementing classes"
).toModuleLowering() )
private val typeOperatorLoweringPhase = makeBodyLoweringPhase( private val typeOperatorLoweringPhase = makeBodyLoweringPhase(
::TypeOperatorLowering, ::TypeOperatorLowering,
@@ -5,22 +5,55 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.ScopeWithIr import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.lower.SingleAbstractMethodLowering import org.jetbrains.kotlin.backend.common.lower.SingleAbstractMethodLowering
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.parentClassOrNull
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.render
class JsSingleAbstractMethodLowering(context: JsIrBackendContext) : SingleAbstractMethodLowering(context) { class JsSingleAbstractMethodLowering(context: JsIrBackendContext) : SingleAbstractMethodLowering(context), BodyLoweringPass {
override fun getWrapperVisibility(expression: IrTypeOperatorCall, scopes: List<ScopeWithIr>): Visibility { override fun getWrapperVisibility(expression: IrTypeOperatorCall, scopes: List<ScopeWithIr>): Visibility {
return Visibilities.PRIVATE return Visibilities.PRIVATE
} }
private var enclosingBodyContainer: IrDeclaration? = null
override fun lower(irFile: IrFile) {
super<SingleAbstractMethodLowering>.lower(irFile)
}
override fun lower(irBody: IrBody, container: IrDeclaration) {
cachedImplementations.clear()
inlineCachedImplementations.clear()
enclosingContainer = container.parentClassOrNull ?: container.file
enclosingBodyContainer = container
irBody.transformChildrenVoid()
for (wrapper in cachedImplementations.values + inlineCachedImplementations.values) {
val parentClass = wrapper.parent as IrDeclarationContainer
stageController.unrestrictDeclarationListsAccess {
parentClass.declarations += wrapper
}
}
}
override fun currentScopeSymbol(): IrSymbol? {
return super.currentScopeSymbol() ?: (enclosingBodyContainer as? IrSymbolOwner)?.symbol
}
override fun getSuperTypeForWrapper(typeOperand: IrType): IrType { override fun getSuperTypeForWrapper(typeOperand: IrType): IrType {
// FE doesn't allow type parameters for now. // FE doesn't allow type parameters for now.
// And since there is a to-do in common SingleAbstractMethodLowering (at function visitTypeOperator), // And since there is a to-do in common SingleAbstractMethodLowering (at function visitTypeOperator),
@@ -1,5 +1,4 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// SKIP_DCE_DRIVEN
fun interface Foo { fun interface Foo {
fun invoke(): String fun invoke(): String
@@ -1,5 +1,4 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// SKIP_DCE_DRIVEN
// FILE: lib.kt // FILE: lib.kt
@@ -1,5 +1,4 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// SKIP_DCE_DRIVEN
fun interface KRunnable { fun interface KRunnable {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
fun interface FunInterface { fun interface FunInterface {
fun invoke() fun invoke()
@@ -1,6 +1,5 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// SKIP_DCE_DRIVEN
fun interface MyRunnable { fun interface MyRunnable {
fun run() fun run()
@@ -1,5 +1,4 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// SKIP_DCE_DRIVEN
fun interface Base { fun interface Base {
fun doStuff(): String fun doStuff(): String
@@ -1,6 +1,5 @@
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JS_IR_ES6 // IGNORE_BACKEND: JS_IR_ES6
// SKIP_DCE_DRIVEN
fun interface FunWithReceiver { fun interface FunWithReceiver {
fun String.foo(): String fun String.foo(): String
@@ -1,6 +1,5 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// WITH_RUNTIME // WITH_RUNTIME
// SKIP_DCE_DRIVEN
fun interface MyRunnable { fun interface MyRunnable {
fun invoke() fun invoke()
@@ -1,5 +1,4 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// SKIP_DCE_DRIVEN
// MODULE: m1 // MODULE: m1
// FILE: m1.kt // FILE: m1.kt
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
interface I { interface I {
fun inherited(s: String): String = privateInherited(s) fun inherited(s: String): String = privateInherited(s)
@@ -1,6 +1,5 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// WITH_RUNTIME // WITH_RUNTIME
// SKIP_DCE_DRIVEN
fun interface KRunnable { fun interface KRunnable {
fun invoke() fun invoke()
@@ -1,6 +1,5 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// WITH_RUNTIME // WITH_RUNTIME
// SKIP_DCE_DRIVEN
fun interface Fn<T, R> { fun interface Fn<T, R> {
fun run(s: String, i: Int, t: T): R fun run(s: String, i: Int, t: T): R
@@ -1,7 +1,5 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// SKIP_DCE_DRIVEN
// This test should check argument coercion between the SAM and the lambda. // This test should check argument coercion between the SAM and the lambda.
// For now it checks that Char is boxed in JS // For now it checks that Char is boxed in JS
@@ -1,6 +1,5 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// WITH_RUNTIME // WITH_RUNTIME
// SKIP_DCE_DRIVEN
fun interface KRunnable { fun interface KRunnable {
fun invoke() fun invoke()
@@ -1,5 +1,4 @@
// !LANGUAGE: +FunctionalInterfaceConversion // !LANGUAGE: +FunctionalInterfaceConversion
// SKIP_DCE_DRIVEN
fun interface S { fun interface S {
fun invoke(): String fun invoke(): String
@@ -1,7 +1,6 @@
// !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions // !LANGUAGE: +NewInference +FunctionalInterfaceConversion +SamConversionPerArgument +SamConversionForKotlinFunctions
// WITH_COROUTINES // WITH_COROUTINES
// WITH_RUNTIME // WITH_RUNTIME
// SKIP_DCE_DRIVEN
import helpers.* import helpers.*
import kotlin.coroutines.* import kotlin.coroutines.*