JVM_IR KT-50073 inline callable reference adapter into 'invoke'
This commit is contained in:
+3
-1
@@ -57,6 +57,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
|||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.lang.RuntimeException
|
||||||
|
|
||||||
class ClassCodegen private constructor(
|
class ClassCodegen private constructor(
|
||||||
val irClass: IrClass,
|
val irClass: IrClass,
|
||||||
@@ -141,7 +142,8 @@ class ClassCodegen private constructor(
|
|||||||
for (method in irClass.declarations.filterIsInstance<IrFunction>()) {
|
for (method in irClass.declarations.filterIsInstance<IrFunction>()) {
|
||||||
if (method.name.asString() != "<clinit>" &&
|
if (method.name.asString() != "<clinit>" &&
|
||||||
method.origin != JvmLoweredDeclarationOrigin.INLINE_LAMBDA &&
|
method.origin != JvmLoweredDeclarationOrigin.INLINE_LAMBDA &&
|
||||||
method.origin != IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR
|
method.origin != IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR &&
|
||||||
|
!(method.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE && method.body == null)
|
||||||
) {
|
) {
|
||||||
generateMethod(method, smap)
|
generateMethod(method, smap)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -481,7 +481,9 @@ class ExpressionCodegen(
|
|||||||
fun handleValueParameter(i: Int, irParameter: IrValueParameter) {
|
fun handleValueParameter(i: Int, irParameter: IrValueParameter) {
|
||||||
val arg = expression.getValueArgument(i)
|
val arg = expression.getValueArgument(i)
|
||||||
val parameterType = callable.valueParameterTypes[i]
|
val parameterType = callable.valueParameterTypes[i]
|
||||||
require(arg != null) { "Null argument in ExpressionCodegen for parameter ${irParameter.render()}" }
|
require(arg != null) {
|
||||||
|
"No argument for parameter ${irParameter.render()}:\n${expression.dump()}"
|
||||||
|
}
|
||||||
callGenerator.genValueAndPut(irParameter, arg, parameterType, this, data)
|
callGenerator.genValueAndPut(irParameter, arg, parameterType, this, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+107
-14
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
|||||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||||
import org.jetbrains.kotlin.backend.common.ir.*
|
import org.jetbrains.kotlin.backend.common.ir.*
|
||||||
import org.jetbrains.kotlin.backend.common.lower.SamEqualsHashCodeMethodsGenerator
|
import org.jetbrains.kotlin.backend.common.lower.SamEqualsHashCodeMethodsGenerator
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.VariableRemapper
|
||||||
import org.jetbrains.kotlin.backend.common.lower.parents
|
import org.jetbrains.kotlin.backend.common.lower.parents
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
@@ -573,16 +574,16 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val adaptedReferenceOriginalTarget: IrFunction? = adapteeCall?.symbol?.owner
|
private val adaptedReferenceOriginalTarget: IrFunction? = adapteeCall?.symbol?.owner
|
||||||
private val isAdaptedFunInterfaceConstructorReference =
|
private val isFunInterfaceConstructorReference =
|
||||||
callee.origin == IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR
|
callee.origin == IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR
|
||||||
private val constructedFunInterfaceSymbol: IrClassSymbol? =
|
private val constructedFunInterfaceSymbol: IrClassSymbol? =
|
||||||
if (isAdaptedFunInterfaceConstructorReference)
|
if (isFunInterfaceConstructorReference)
|
||||||
callee.returnType.classOrNull
|
callee.returnType.classOrNull
|
||||||
?: throw AssertionError("Fun interface type expected: ${callee.returnType.render()}")
|
?: throw AssertionError("Fun interface type expected: ${callee.returnType.render()}")
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
private val isAdaptedReference =
|
private val isAdaptedReference =
|
||||||
isAdaptedFunInterfaceConstructorReference || adaptedReferenceOriginalTarget != null
|
isFunInterfaceConstructorReference || adaptedReferenceOriginalTarget != null
|
||||||
|
|
||||||
private val samInterface = samSuperType?.getClass()
|
private val samInterface = samSuperType?.getClass()
|
||||||
private val isKotlinFunInterface = samInterface != null && !samInterface.isFromJava()
|
private val isKotlinFunInterface = samInterface != null && !samInterface.isFromJava()
|
||||||
@@ -594,7 +595,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
samSuperType
|
samSuperType
|
||||||
?: when {
|
?: when {
|
||||||
isLambda -> context.ir.symbols.lambdaClass
|
isLambda -> context.ir.symbols.lambdaClass
|
||||||
isAdaptedFunInterfaceConstructorReference -> context.ir.symbols.funInterfaceConstructorReferenceClass
|
isFunInterfaceConstructorReference -> context.ir.symbols.funInterfaceConstructorReferenceClass
|
||||||
useOptimizedSuperClass -> when {
|
useOptimizedSuperClass -> when {
|
||||||
isAdaptedReference -> context.ir.symbols.adaptedFunctionReference
|
isAdaptedReference -> context.ir.symbols.adaptedFunctionReference
|
||||||
else -> context.ir.symbols.functionReferenceImpl
|
else -> context.ir.symbols.functionReferenceImpl
|
||||||
@@ -755,7 +756,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
// arity, [receiver]
|
// arity, [receiver]
|
||||||
val constructor =
|
val constructor =
|
||||||
when {
|
when {
|
||||||
isAdaptedFunInterfaceConstructorReference ->
|
isFunInterfaceConstructorReference ->
|
||||||
context.ir.symbols.funInterfaceConstructorReferenceClass.owner.constructors.single()
|
context.ir.symbols.funInterfaceConstructorReferenceClass.owner.constructors.single()
|
||||||
samSuperType != null ->
|
samSuperType != null ->
|
||||||
context.irBuiltIns.anyClass.owner.constructors.single()
|
context.irBuiltIns.anyClass.owner.constructors.single()
|
||||||
@@ -785,7 +786,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
call: IrFunctionAccessExpression,
|
call: IrFunctionAccessExpression,
|
||||||
generateBoundReceiver: IrBuilder.() -> IrExpression
|
generateBoundReceiver: IrBuilder.() -> IrExpression
|
||||||
) {
|
) {
|
||||||
if (isAdaptedFunInterfaceConstructorReference) {
|
if (isFunInterfaceConstructorReference) {
|
||||||
val funInterfaceKClassRef = kClassReference(constructedFunInterfaceSymbol!!.owner.defaultType)
|
val funInterfaceKClassRef = kClassReference(constructedFunInterfaceSymbol!!.owner.defaultType)
|
||||||
val funInterfaceJavaClassRef = kClassToJavaClass(funInterfaceKClassRef)
|
val funInterfaceJavaClassRef = kClassToJavaClass(funInterfaceKClassRef)
|
||||||
call.putValueArgument(0, funInterfaceJavaClassRef)
|
call.putValueArgument(0, funInterfaceJavaClassRef)
|
||||||
@@ -850,12 +851,15 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
IrDeclarationOrigin.INSTANCE_RECEIVER,
|
IrDeclarationOrigin.INSTANCE_RECEIVER,
|
||||||
functionReferenceClass.symbol.defaultType
|
functionReferenceClass.symbol.defaultType
|
||||||
)
|
)
|
||||||
if (isLambda)
|
|
||||||
createLambdaInvokeMethod()
|
when {
|
||||||
else if (isAdaptedFunInterfaceConstructorReference)
|
isLambda ->
|
||||||
createFunInterfaceConstructorInvokeMethod()
|
createLambdaInvokeMethod()
|
||||||
else
|
isFunInterfaceConstructorReference ->
|
||||||
createFunctionReferenceInvokeMethod(receiverVar)
|
createFunInterfaceConstructorInvokeMethod()
|
||||||
|
else ->
|
||||||
|
createFunctionReferenceInvokeMethod(receiverVar)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inline the body of an anonymous function into the generated lambda subclass.
|
// Inline the body of an anonymous function into the generated lambda subclass.
|
||||||
@@ -866,7 +870,6 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
}
|
}
|
||||||
valueParameters += valueParameterMap.values
|
valueParameters += valueParameterMap.values
|
||||||
body = callee.moveBodyTo(this, valueParameterMap)
|
body = callee.moveBodyTo(this, valueParameterMap)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrSimpleFunction.createFunInterfaceConstructorInvokeMethod() {
|
private fun IrSimpleFunction.createFunInterfaceConstructorInvokeMethod() {
|
||||||
@@ -921,10 +924,100 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
}?.let { putArgument(callee, parameter, it) }
|
}?.let { putArgument(callee, parameter, it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
irExprBody(call)
|
irExprBody(
|
||||||
|
inlineAdapterCallIfPossible(call, this@createFunctionReferenceInvokeMethod)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun inlineAdapterCallIfPossible(
|
||||||
|
expression: IrFunctionAccessExpression,
|
||||||
|
invokeMethod: IrSimpleFunction
|
||||||
|
): IrExpression {
|
||||||
|
val irCall = expression as? IrCall
|
||||||
|
?: return expression
|
||||||
|
val callee = irCall.symbol.owner
|
||||||
|
if (callee.origin != IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE)
|
||||||
|
return expression
|
||||||
|
|
||||||
|
// TODO fix testSuspendUnitConversion
|
||||||
|
if (callee.isSuspend) return expression
|
||||||
|
|
||||||
|
// Callable reference adapter is a simple function that delegates to callable reference target,
|
||||||
|
// adapting its signature for required functional type.
|
||||||
|
// Usually it simply forwards arguments to target function.
|
||||||
|
// It also passes 'receiver' field for bound references, with downcast to the actual receiver type.
|
||||||
|
// In any case, adapter itself is synthetic and is not necessarily debuggable, so we can reuse variables freely.
|
||||||
|
// Inlining adapter into 'invoke' saves us two methods (adapter & synthetic accessor).
|
||||||
|
val adapterBody = callee.body as? IrBlockBody
|
||||||
|
if (adapterBody == null || adapterBody.statements.size != 1)
|
||||||
|
throw AssertionError("Unexpected adapter body: ${callee.dump()}")
|
||||||
|
val resultStatement = adapterBody.statements[0]
|
||||||
|
val resultExpression: IrExpression =
|
||||||
|
when {
|
||||||
|
resultStatement is IrReturn ->
|
||||||
|
resultStatement.value
|
||||||
|
resultStatement is IrTypeOperatorCall && resultStatement.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT ->
|
||||||
|
resultStatement
|
||||||
|
resultStatement is IrCall ->
|
||||||
|
resultStatement
|
||||||
|
resultStatement is IrConstructorCall ->
|
||||||
|
resultStatement
|
||||||
|
else ->
|
||||||
|
throw AssertionError("Unexpected adapter body: ${callee.dump()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val startOffset = irCall.startOffset
|
||||||
|
val endOffset = irCall.endOffset
|
||||||
|
|
||||||
|
val callArguments = LinkedHashMap<IrValueParameter, IrValueDeclaration>()
|
||||||
|
val inlinedAdapterBlock = IrBlockImpl(startOffset, endOffset, irCall.type, origin = null)
|
||||||
|
var tmpVarIndex = 0
|
||||||
|
|
||||||
|
fun wrapIntoTemporaryVariableIfNecessary(expression: IrExpression): IrValueDeclaration {
|
||||||
|
if (expression is IrGetValue)
|
||||||
|
return expression.symbol.owner
|
||||||
|
if (expression !is IrTypeOperatorCall || expression.argument !is IrGetField)
|
||||||
|
throw AssertionError("Unexpected adapter argument:\n${expression.dump()}")
|
||||||
|
val temporaryVar = IrVariableImpl(
|
||||||
|
startOffset, endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
|
||||||
|
IrVariableSymbolImpl(),
|
||||||
|
Name.identifier("tmp_${tmpVarIndex++}"),
|
||||||
|
expression.type,
|
||||||
|
isVar = false, isConst = false, isLateinit = false
|
||||||
|
)
|
||||||
|
temporaryVar.parent = invokeMethod
|
||||||
|
temporaryVar.initializer = expression
|
||||||
|
inlinedAdapterBlock.statements.add(temporaryVar)
|
||||||
|
return temporaryVar
|
||||||
|
}
|
||||||
|
|
||||||
|
callee.dispatchReceiverParameter?.let {
|
||||||
|
callArguments[it] = wrapIntoTemporaryVariableIfNecessary(
|
||||||
|
irCall.dispatchReceiver
|
||||||
|
?: throw AssertionError("No dispatch receiver in adapter call: ${irCall.dump()}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
callee.extensionReceiverParameter?.let {
|
||||||
|
callArguments[it] = wrapIntoTemporaryVariableIfNecessary(
|
||||||
|
irCall.extensionReceiver
|
||||||
|
?: throw AssertionError("No extension receiver in adapter call: ${irCall.dump()}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (valueParameter in callee.valueParameters) {
|
||||||
|
callArguments[valueParameter] = wrapIntoTemporaryVariableIfNecessary(
|
||||||
|
irCall.getValueArgument(valueParameter.index)
|
||||||
|
?: throw AssertionError("No value argument #${valueParameter.index} in adapter call: ${irCall.dump()}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val inlinedAdapterResult = resultExpression.transform(VariableRemapper(callArguments), null)
|
||||||
|
inlinedAdapterBlock.statements.add(inlinedAdapterResult)
|
||||||
|
|
||||||
|
callee.body = null
|
||||||
|
return inlinedAdapterBlock
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildOverride(superFunction: IrSimpleFunction, newReturnType: IrType = superFunction.returnType): IrSimpleFunction =
|
private fun buildOverride(superFunction: IrSimpleFunction, newReturnType: IrType = superFunction.returnType): IrSimpleFunction =
|
||||||
functionReferenceClass.addFunction {
|
functionReferenceClass.addFunction {
|
||||||
setSourceRange(irFunctionReference)
|
setSourceRange(irFunctionReference)
|
||||||
|
|||||||
-2
@@ -26,13 +26,11 @@ public final class A {
|
|||||||
inner (anonymous) class A$testDefaultArguments$1
|
inner (anonymous) class A$testDefaultArguments$1
|
||||||
inner (anonymous) class A$testDefaultArguments$2
|
inner (anonymous) class A$testDefaultArguments$2
|
||||||
public method <init>(): void
|
public method <init>(): void
|
||||||
public synthetic final static method access$testDefaultArguments$defaultArgs(p0: A): java.lang.String
|
|
||||||
public synthetic final static method access$testDefaultArguments$defaultArgs-0(p0: A, p1: kotlin.coroutines.Continuation): java.lang.Object
|
public synthetic final static method access$testDefaultArguments$defaultArgs-0(p0: A, p1: kotlin.coroutines.Continuation): java.lang.Object
|
||||||
synthetic static method defaultArgs$default(p0: A, p1: int, p2: java.lang.String, p3: int, p4: java.lang.Object): java.lang.String
|
synthetic static method defaultArgs$default(p0: A, p1: int, p2: java.lang.String, p3: int, p4: java.lang.Object): java.lang.String
|
||||||
private final method defaultArgs(p0: int, p1: java.lang.String): java.lang.String
|
private final method defaultArgs(p0: int, p1: java.lang.String): java.lang.String
|
||||||
private final method myApply(p0: kotlin.jvm.functions.Function0): void
|
private final method myApply(p0: kotlin.jvm.functions.Function0): void
|
||||||
private final method myApplySuspend(p0: kotlin.jvm.functions.Function1): void
|
private final method myApplySuspend(p0: kotlin.jvm.functions.Function1): void
|
||||||
private synthetic final static method testDefaultArguments$defaultArgs(p0: A): java.lang.String
|
|
||||||
private synthetic final static method testDefaultArguments$defaultArgs-0(p0: A, p1: kotlin.coroutines.Continuation): java.lang.Object
|
private synthetic final static method testDefaultArguments$defaultArgs-0(p0: A, p1: kotlin.coroutines.Continuation): java.lang.Object
|
||||||
public final method testDefaultArguments(): void
|
public final method testDefaultArguments(): void
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user