JVM_IR indy-SAM on functional expression
This commit is contained in:
+34
-4
@@ -13,6 +13,10 @@ import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.indy.LambdaMetafactoryArguments
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.indy.LambdaMetafactoryArgumentsBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.indy.SamDelegatingLambdaBlock
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.indy.SamDelegatingLambdaBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
|
||||
import org.jetbrains.kotlin.config.JvmClosureGenerationScheme
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
@@ -142,6 +146,14 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
} else if (invokable is IrBlock && invokable.origin.isLambda && invokable.statements.last() is IrFunctionReference) {
|
||||
invokable.statements.dropLast(1).forEach { it.transform(this, null) }
|
||||
invokable.statements.last() as IrFunctionReference
|
||||
} else if (shouldGenerateIndySamConversions && canGenerateIndySamConversionOnFunctionalExpression(samSuperType, invokable)) {
|
||||
val lambdaBlock = SamDelegatingLambdaBuilder(context)
|
||||
.build(invokable, samSuperType, currentScope!!.scope.scopeOwnerSymbol)
|
||||
val lambdaMetafactoryArguments = LambdaMetafactoryArgumentsBuilder(context, crossinlineLambdas)
|
||||
.getLambdaMetafactoryArgumentsOrNull(lambdaBlock.ref, samSuperType, false)
|
||||
?: return super.visitTypeOperator(expression)
|
||||
invokable.transformChildrenVoid()
|
||||
return wrapSamDelegatingLambdaWithIndySamConversion(samSuperType, lambdaBlock, lambdaMetafactoryArguments)
|
||||
} else {
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
@@ -159,18 +171,36 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
return FunctionReferenceBuilder(reference, samSuperType).build()
|
||||
}
|
||||
|
||||
private fun canGenerateIndySamConversionOnFunctionalExpression(samSuperType: IrType, expression: IrExpression): Boolean {
|
||||
val samClass = samSuperType.classOrNull
|
||||
?: throw AssertionError("Class type expected: ${samSuperType.render()}")
|
||||
if (!samClass.owner.isFromJava())
|
||||
return false
|
||||
if (expression is IrBlock && expression.origin == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
private fun wrapSamDelegatingLambdaWithIndySamConversion(
|
||||
samSuperType: IrType,
|
||||
lambdaBlock: SamDelegatingLambdaBlock,
|
||||
lambdaMetafactoryArguments: LambdaMetafactoryArguments
|
||||
): IrExpression {
|
||||
val indySamConversion = wrapWithIndySamConversion(samSuperType, lambdaMetafactoryArguments)
|
||||
lambdaBlock.replaceRefWith(indySamConversion)
|
||||
return lambdaBlock.block
|
||||
}
|
||||
|
||||
private fun wrapSamConversionArgumentWithIndySamConversion(
|
||||
expression: IrTypeOperatorCall,
|
||||
lambdaMetafactoryArguments: LambdaMetafactoryArguments
|
||||
): IrExpression {
|
||||
val samType = expression.typeOperand
|
||||
return when (val argument = expression.argument) {
|
||||
is IrFunctionReference -> {
|
||||
is IrFunctionReference ->
|
||||
wrapWithIndySamConversion(samType, lambdaMetafactoryArguments)
|
||||
}
|
||||
is IrBlock -> {
|
||||
is IrBlock ->
|
||||
wrapFunctionReferenceInsideBlockWithIndySamConversion(samType, lambdaMetafactoryArguments, argument)
|
||||
}
|
||||
else -> throw AssertionError("Block or function reference expected: ${expression.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -3,7 +3,7 @@
|
||||
* 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.jvm.lower
|
||||
package org.jetbrains.kotlin.backend.jvm.lower.indy
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.backend.common.lower.VariableRemapper
|
||||
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getSingleAbstractMethod
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isCompiledToJvmDefault
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.findInterfaceImplementation
|
||||
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
|
||||
@@ -30,14 +31,14 @@ import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class LambdaMetafactoryArguments(
|
||||
internal class LambdaMetafactoryArguments(
|
||||
val samMethod: IrSimpleFunction,
|
||||
val fakeInstanceMethod: IrSimpleFunction,
|
||||
val implMethodReference: IrFunctionReference,
|
||||
val extraOverriddenMethods: List<IrSimpleFunction>
|
||||
)
|
||||
|
||||
class LambdaMetafactoryArgumentsBuilder(
|
||||
internal class LambdaMetafactoryArgumentsBuilder(
|
||||
private val context: JvmBackendContext,
|
||||
private val crossinlineLambdas: Set<IrSimpleFunction>
|
||||
) {
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.jvm.lower.indy
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.JvmIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getSingleAbstractMethod
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrContainerExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.functions
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
internal sealed class SamDelegatingLambdaBlock {
|
||||
abstract val block: IrContainerExpression
|
||||
abstract val ref: IrFunctionReference
|
||||
abstract fun replaceRefWith(expression: IrExpression)
|
||||
}
|
||||
|
||||
|
||||
internal class RegularDelegatingLambdaBlock(
|
||||
override val block: IrContainerExpression,
|
||||
override val ref: IrFunctionReference
|
||||
) : SamDelegatingLambdaBlock() {
|
||||
|
||||
override fun replaceRefWith(expression: IrExpression) {
|
||||
block.statements[block.statements.size - 1] = expression
|
||||
block.type = expression.type
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class NullableDelegatingLambdaBlock(
|
||||
override val block: IrContainerExpression,
|
||||
override val ref: IrFunctionReference,
|
||||
private val ifExpr: IrExpression,
|
||||
private val ifNotNullBlock: IrContainerExpression
|
||||
) : SamDelegatingLambdaBlock() {
|
||||
|
||||
override fun replaceRefWith(expression: IrExpression) {
|
||||
ifNotNullBlock.statements[ifNotNullBlock.statements.size - 1] = expression
|
||||
ifNotNullBlock.type = expression.type
|
||||
ifExpr.type = expression.type
|
||||
block.type = expression.type
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class SamDelegatingLambdaBuilder(private val jvmContext: JvmBackendContext) {
|
||||
fun build(expression: IrExpression, superType: IrType, scopeSymbol: IrSymbol): SamDelegatingLambdaBlock {
|
||||
return if (superType.isNullable() && expression.type.isNullable())
|
||||
buildNullableDelegatingLambda(expression, superType, scopeSymbol)
|
||||
else
|
||||
buildRegularDelegatingLambda(expression, superType, scopeSymbol)
|
||||
}
|
||||
|
||||
private fun buildRegularDelegatingLambda(
|
||||
expression: IrExpression,
|
||||
superType: IrType,
|
||||
scopeSymbol: IrSymbol
|
||||
): SamDelegatingLambdaBlock {
|
||||
lateinit var ref: IrFunctionReference
|
||||
val block = jvmContext.createJvmIrBuilder(scopeSymbol, expression.startOffset, expression.endOffset).run {
|
||||
// {
|
||||
// val tmp = <expression>
|
||||
// fun `<anonymous>`(p1: T1, ..., pN: TN): R =
|
||||
// tmp.invoke(p1, ..., pN)
|
||||
// ::`<anonymous>`
|
||||
// }
|
||||
|
||||
irBlock(origin = IrStatementOrigin.LAMBDA) {
|
||||
val tmp = irTemporary(expression)
|
||||
val lambda = createDelegatingLambda(scopeSymbol, expression, superType, tmp)
|
||||
.also { +it }
|
||||
ref = createDelegatingLambdaReference(expression, lambda)
|
||||
.also { +it }
|
||||
}
|
||||
}
|
||||
block.type = expression.type
|
||||
return RegularDelegatingLambdaBlock(block, ref)
|
||||
}
|
||||
|
||||
private fun buildNullableDelegatingLambda(
|
||||
expression: IrExpression,
|
||||
superType: IrType,
|
||||
scopeSymbol: IrSymbol
|
||||
): SamDelegatingLambdaBlock {
|
||||
lateinit var ref: IrFunctionReference
|
||||
lateinit var ifExpr: IrExpression
|
||||
lateinit var ifNotNullBlock: IrContainerExpression
|
||||
val block = jvmContext.createJvmIrBuilder(scopeSymbol, expression.startOffset, expression.endOffset).run {
|
||||
// {
|
||||
// val tmp = <expression>
|
||||
// if (tmp == null)
|
||||
// null
|
||||
// else {
|
||||
// fun `<anonymous>`(p1: T1, ..., pN: TN): R =
|
||||
// tmp.invoke(p1, ..., pN)
|
||||
// ::`<anonymous>`
|
||||
// }
|
||||
// }
|
||||
|
||||
irBlock(origin = IrStatementOrigin.LAMBDA) {
|
||||
val tmp = irTemporary(expression)
|
||||
ifNotNullBlock = irBlock {
|
||||
val lambda = createDelegatingLambda(scopeSymbol, expression, superType, tmp)
|
||||
.also { +it }
|
||||
ref = createDelegatingLambdaReference(expression, lambda)
|
||||
.also { +it }
|
||||
}
|
||||
ifExpr = irIfNull(expression.type, irGet(tmp), irNull(), ifNotNullBlock)
|
||||
.also { +it }
|
||||
}
|
||||
}
|
||||
block.type = expression.type
|
||||
return NullableDelegatingLambdaBlock(block, ref, ifExpr, ifNotNullBlock)
|
||||
}
|
||||
|
||||
private fun createDelegatingLambda(
|
||||
scopeSymbol: IrSymbol,
|
||||
expression: IrExpression,
|
||||
superType: IrType,
|
||||
tmp: IrVariable
|
||||
): IrSimpleFunction {
|
||||
val superMethod = superType.getSingleAbstractMethod()
|
||||
?: throw AssertionError("SAM type expected: ${superType.render()}")
|
||||
val effectiveValueParametersCount = superMethod.valueParameters.size +
|
||||
if (superMethod.extensionReceiverParameter == null) 0 else 1
|
||||
val invocableFunctionClass =
|
||||
if (superMethod.isSuspend)
|
||||
jvmContext.ir.symbols.suspendFunctionN(effectiveValueParametersCount).owner
|
||||
else
|
||||
jvmContext.ir.symbols.functionN(effectiveValueParametersCount).owner
|
||||
val invokeFunction = invocableFunctionClass.functions.single { it.name == OperatorNameConventions.INVOKE }
|
||||
val typeSubstitutor = createTypeSubstitutor(superType)
|
||||
|
||||
return jvmContext.irFactory.buildFun {
|
||||
name = Name.special("<anonymous>")
|
||||
returnType = typeSubstitutor.substitute(superMethod.returnType)
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
modality = Modality.FINAL
|
||||
origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
|
||||
isSuspend = superMethod.isSuspend
|
||||
}.also { lambda ->
|
||||
lambda.dispatchReceiverParameter = null
|
||||
lambda.extensionReceiverParameter = null
|
||||
lambda.valueParameters = createLambdaValueParameters(superMethod, lambda, typeSubstitutor)
|
||||
lambda.body = jvmContext.createJvmIrBuilder(lambda.symbol, expression.startOffset, expression.endOffset)
|
||||
.irBlockBody {
|
||||
+irReturn(
|
||||
irCall(invokeFunction).also { invokeCall ->
|
||||
invokeCall.dispatchReceiver = irGet(tmp)
|
||||
var parameterIndex = 0
|
||||
invokeFunction.extensionReceiverParameter?.let {
|
||||
invokeCall.extensionReceiver = irGet(lambda.valueParameters[parameterIndex++])
|
||||
}
|
||||
for (argumentIndex in invokeFunction.valueParameters.indices) {
|
||||
invokeCall.putValueArgument(argumentIndex, irGet(lambda.valueParameters[parameterIndex++]))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
lambda.parent = scopeSymbol.owner as IrDeclarationParent
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLambdaValueParameters(
|
||||
superMethod: IrSimpleFunction,
|
||||
lambda: IrSimpleFunction,
|
||||
typeSubstitutor: IrTypeSubstitutor
|
||||
): List<IrValueParameter> {
|
||||
val lambdaParameters = ArrayList<IrValueParameter>()
|
||||
var index = 0
|
||||
superMethod.extensionReceiverParameter?.let { superExtensionReceiver ->
|
||||
lambdaParameters.add(superExtensionReceiver.copySubstituted(lambda, typeSubstitutor, index++, Name.identifier("\$receiver")))
|
||||
}
|
||||
superMethod.valueParameters.mapTo(lambdaParameters) { superValueParameter ->
|
||||
superValueParameter.copySubstituted(lambda, typeSubstitutor, index++)
|
||||
}
|
||||
return lambdaParameters
|
||||
}
|
||||
|
||||
private fun IrValueParameter.copySubstituted(
|
||||
function: IrSimpleFunction,
|
||||
substitutor: IrTypeSubstitutor,
|
||||
newIndex: Int,
|
||||
newName: Name = name
|
||||
) =
|
||||
buildValueParameter(function) {
|
||||
name = newName
|
||||
index = newIndex
|
||||
type = substitutor.substitute(this@copySubstituted.type)
|
||||
}
|
||||
|
||||
private fun JvmIrBuilder.createDelegatingLambdaReference(expression: IrExpression, lambda: IrSimpleFunction): IrFunctionReference {
|
||||
return IrFunctionReferenceImpl(
|
||||
startOffset, endOffset,
|
||||
expression.type,
|
||||
lambda.symbol,
|
||||
typeArgumentsCount = 0,
|
||||
valueArgumentsCount = lambda.valueParameters.size,
|
||||
reflectionTarget = null,
|
||||
origin = IrStatementOrigin.LAMBDA
|
||||
)
|
||||
}
|
||||
|
||||
private fun createTypeSubstitutor(irType: IrType): IrTypeSubstitutor {
|
||||
if (irType !is IrSimpleType)
|
||||
throw AssertionError("Simple type expected: ${irType.render()}")
|
||||
val irClassSymbol = irType.classOrNull
|
||||
?: throw AssertionError("Class type expected: ${irType.render()}")
|
||||
return IrTypeSubstitutor(
|
||||
irClassSymbol.owner.typeParameters.map { it.symbol },
|
||||
irType.arguments,
|
||||
jvmContext.irBuiltIns
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user