JVM IR: Remove bridge construction from CallableReferenceLowering
This commit is contained in:
committed by
Georgy Bronnikov
parent
ebb59d51d2
commit
6aa8ecd745
@@ -141,7 +141,6 @@ private val jvmFilePhases =
|
||||
singletonReferencesPhase then
|
||||
|
||||
callableReferencePhase then
|
||||
functionNVarargInvokePhase then
|
||||
localDeclarationsPhase then
|
||||
|
||||
singleAbstractMethodPhase then
|
||||
@@ -172,6 +171,7 @@ private val jvmFilePhases =
|
||||
objectClassPhase then
|
||||
makeInitializersPhase(JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, true) then
|
||||
collectionStubMethodLowering then
|
||||
functionNVarargBridgePhase then
|
||||
bridgePhase then
|
||||
jvmStaticAnnotationPhase then
|
||||
staticDefaultFunctionPhase then
|
||||
|
||||
+18
-132
@@ -11,17 +11,13 @@ import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||
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.codegen.isInlineFunctionCall
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||
import org.jetbrains.kotlin.backend.jvm.localDeclarationsPhase
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||
import org.jetbrains.kotlin.codegen.PropertyReferenceCodegen
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
@@ -32,7 +28,6 @@ import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
|
||||
@@ -66,52 +61,26 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
|
||||
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid(this)
|
||||
|
||||
// Change calls to big arity invoke functions to vararg calls.
|
||||
// Mark function references appearing as inlined arguments to inline functions.
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||
markInlineFunctionReferences(expression)
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
if (expression.valueArgumentsCount < FunctionInvokeDescriptor.BIG_ARITY ||
|
||||
!expression.symbol.owner.parentAsClass.defaultType.isFunctionOrKFunction())
|
||||
return expression
|
||||
|
||||
return IrCallImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
expression.type, functionNInvokeFun.symbol, functionNInvokeFun.descriptor,
|
||||
1, expression.origin
|
||||
).apply {
|
||||
putTypeArgument(0, expression.type)
|
||||
dispatchReceiver = expression.dispatchReceiver
|
||||
extensionReceiver = expression.extensionReceiver
|
||||
val vararg = IrVarargImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
context.ir.symbols.array.typeWith(context.irBuiltIns.anyNType),
|
||||
context.irBuiltIns.anyNType,
|
||||
(0 until expression.valueArgumentsCount).map { expression.getValueArgument(it)!! }
|
||||
)
|
||||
putValueArgument(0, vararg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun markInlineFunctionReferences(expression: IrFunctionAccessExpression) {
|
||||
val function = expression.symbol.owner
|
||||
if (!function.isInlineFunctionCall(context))
|
||||
return
|
||||
if (function.isInlineFunctionCall(context)) {
|
||||
for (parameter in function.valueParameters) {
|
||||
if (!parameter.isInlineParameter())
|
||||
continue
|
||||
|
||||
for (parameter in function.valueParameters) {
|
||||
if (!parameter.isInlineParameter())
|
||||
continue
|
||||
val valueArgument = expression.getValueArgument(parameter.index) ?: continue
|
||||
if (!isInlineIrExpression(valueArgument))
|
||||
continue
|
||||
|
||||
val valueArgument = expression.getValueArgument(parameter.index) ?: continue
|
||||
if (!isInlineIrExpression(valueArgument))
|
||||
continue
|
||||
|
||||
if (valueArgument is IrFunctionReference) {
|
||||
ignoredFunctionReferences.add(valueArgument)
|
||||
} else if (valueArgument is IrBlock) {
|
||||
ignoredFunctionReferences.addIfNotNull(valueArgument.statements.filterIsInstance<IrFunctionReference>().singleOrNull())
|
||||
if (valueArgument is IrFunctionReference) {
|
||||
ignoredFunctionReferences.add(valueArgument)
|
||||
} else if (valueArgument is IrBlock) {
|
||||
ignoredFunctionReferences.addIfNotNull(valueArgument.statements.filterIsInstance<IrFunctionReference>().singleOrNull())
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitFunctionAccess(expression)
|
||||
}
|
||||
|
||||
// Ignore function references handled in SAM conversion
|
||||
@@ -161,10 +130,6 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
// The type of the reference is KFunction<in A1, ..., in An, out R>
|
||||
private val parameterTypes = (irFunctionReference.type as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
|
||||
private val argumentTypes = parameterTypes.dropLast(1)
|
||||
private val returnType = parameterTypes.last()
|
||||
|
||||
private val useVararg
|
||||
get() = argumentTypes.size >= FunctionInvokeDescriptor.BIG_ARITY
|
||||
|
||||
private val typeParameters = if (callee is IrConstructor)
|
||||
callee.parentAsClass.typeParameters + callee.typeParameters
|
||||
@@ -190,7 +155,10 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
fun build(): IrExpression {
|
||||
val constructor = createConstructor()
|
||||
val invokeMethod = createInvokeMethod()
|
||||
createBridge(invokeMethod)
|
||||
|
||||
val functionClass = context.ir.symbols.getJvmFunctionClass(argumentTypes.size)
|
||||
functionReferenceClass.superTypes += functionClass.typeWith(parameterTypes)
|
||||
invokeMethod.overriddenSymbols += functionClass.functions.single { it.owner.name.asString() == "invoke" }
|
||||
|
||||
if (!isLambda) {
|
||||
createGetSignatureMethod(functionGetSignature)
|
||||
@@ -339,80 +307,6 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
}
|
||||
}
|
||||
|
||||
// Build a bridge to the monomorphic invoke method. This is more elaborate than the usual BridgeLowering,
|
||||
// since we have special handling for functions with large arity (> 22 arguments). Large arity functions
|
||||
// are translated to functions with a single vararg argument, which checks the number of arguments and types
|
||||
// dynamically.
|
||||
private fun createBridge(invoke: IrSimpleFunction) {
|
||||
// Add supertypes
|
||||
val actualFunctionClass = if (useVararg)
|
||||
context.ir.symbols.functionN
|
||||
else
|
||||
context.ir.symbols.getJvmFunctionClass(argumentTypes.size)
|
||||
|
||||
functionReferenceClass.superTypes += actualFunctionClass.typeWith(if (useVararg) listOf(returnType) else parameterTypes)
|
||||
val superFunction = actualFunctionClass.owner.functions.find { it.name.asString() == "invoke" }!!
|
||||
|
||||
// Only add a bridge method when necessary
|
||||
if (context.state.typeMapper.mapAsmMethod(superFunction.descriptor) ==
|
||||
context.state.typeMapper.mapAsmMethod(invoke.descriptor)
|
||||
) {
|
||||
invoke.overriddenSymbols += superFunction.symbol
|
||||
return
|
||||
}
|
||||
|
||||
// Add the invoke bridge
|
||||
functionReferenceClass.addFunction {
|
||||
name = superFunction.name
|
||||
returnType = context.irBuiltIns.anyNType
|
||||
modality = Modality.FINAL
|
||||
visibility = Visibilities.PUBLIC
|
||||
origin = IrDeclarationOrigin.BRIDGE
|
||||
}.apply {
|
||||
overriddenSymbols += superFunction.symbol
|
||||
dispatchReceiverParameter = parentAsClass.thisReceiver!!.copyTo(this)
|
||||
if (useVararg)
|
||||
valueParameters += superFunction.valueParameters[0].copyTo(this)
|
||||
else
|
||||
superFunction.valueParameters.forEach { valueParameters += it.copyTo(this, type = context.irBuiltIns.anyNType) }
|
||||
|
||||
body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
|
||||
// Check the number of arguments for large arity functions
|
||||
if (useVararg) {
|
||||
+irIfThen(
|
||||
irNotEquals(
|
||||
irCall(arraySizeProperty.getter!!).apply {
|
||||
dispatchReceiver = irGet(valueParameters.single())
|
||||
},
|
||||
irInt(argumentTypes.size)
|
||||
),
|
||||
irCall(context.irBuiltIns.illegalArgumentExceptionSymbol).apply {
|
||||
putValueArgument(0, irString("Expected ${argumentTypes.size} arguments"))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
+irReturn(irCall(invoke).apply {
|
||||
dispatchReceiver = irGet(dispatchReceiverParameter!!)
|
||||
|
||||
for (parameter in invoke.valueParameters) {
|
||||
val index = parameter.index
|
||||
|
||||
val argument = if (useVararg) {
|
||||
val argArray = irGet(valueParameters.single())
|
||||
val argIndex = irInt(index)
|
||||
irCallOp(arrayGetFun.symbol, context.irBuiltIns.anyNType, argArray, argIndex)
|
||||
} else {
|
||||
irGet(valueParameters[index])
|
||||
}
|
||||
|
||||
putValueArgument(index, irImplicitCast(argument, argumentTypes[index]))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildOverride(superFunction: IrSimpleFunction, newReturnType: IrType = superFunction.returnType): IrSimpleFunction =
|
||||
functionReferenceClass.addFunction {
|
||||
setSourceRange(irFunctionReference)
|
||||
@@ -497,14 +391,6 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
|
||||
private fun IrClassSymbol.functionByName(name: String) = owner.functions.single { it.name.asString() == name }
|
||||
|
||||
private val arraySizeProperty by lazy {
|
||||
context.irBuiltIns.arrayClass.owner.properties.single { it.name.toString() == "size" }
|
||||
}
|
||||
|
||||
private val arrayGetFun by lazy {
|
||||
context.irBuiltIns.arrayClass.functionByName("get")
|
||||
}
|
||||
|
||||
private val functionReferenceReceiverField =
|
||||
context.ir.symbols.functionReference.owner.declarations.single { it is IrField && it.name.toString() == "receiver" } as IrField
|
||||
|
||||
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
internal val functionNVarargBridgePhase = makeIrFilePhase(
|
||||
::FunctionNVarargBridgeLowering,
|
||||
name = "FunctionBridgePhase",
|
||||
description = "Add bridges for invoke functions with a large number of arguments"
|
||||
)
|
||||
|
||||
// There are concrete function classes for functions with up to 22 arguments. Above that, we
|
||||
// inherit from the generic FunctionN class which has a vararg invoke method. This phase
|
||||
// adds a bridge method for such large arity functions, which checks the number of arguments
|
||||
// dynamically.
|
||||
private class FunctionNVarargBridgeLowering(val context: JvmBackendContext) :
|
||||
FileLoweringPass, IrElementTransformerVoidWithContext() {
|
||||
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid(this)
|
||||
|
||||
// Change calls to big arity invoke functions to vararg calls.
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||
if (expression.valueArgumentsCount < FunctionInvokeDescriptor.BIG_ARITY ||
|
||||
!expression.symbol.owner.parentAsClass.defaultType.isFunctionOrKFunction() ||
|
||||
expression.symbol.owner.name.asString() != "invoke")
|
||||
return super.visitFunctionAccess(expression)
|
||||
|
||||
return IrCallImpl(
|
||||
expression.startOffset, expression.endOffset,
|
||||
expression.type, functionNInvokeFun, functionNInvokeFun.descriptor,
|
||||
1, expression.origin
|
||||
).apply {
|
||||
putTypeArgument(0, expression.type)
|
||||
dispatchReceiver = expression.dispatchReceiver
|
||||
putValueArgument(0, IrVarargImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
context.ir.symbols.array.typeWith(context.irBuiltIns.anyNType),
|
||||
context.irBuiltIns.anyNType,
|
||||
(0 until expression.valueArgumentsCount).map { expression.getValueArgument(it)!! }
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitClassNew(declaration: IrClass): IrStatement {
|
||||
val bigArityFunctionSuperTypes = declaration.superTypes.filterIsInstance<IrSimpleType>().filter {
|
||||
it.isFunctionType && it.arguments.size > FunctionInvokeDescriptor.BIG_ARITY
|
||||
}
|
||||
|
||||
if (bigArityFunctionSuperTypes.isEmpty())
|
||||
return super.visitClassNew(declaration)
|
||||
declaration.transformChildrenVoid(this)
|
||||
|
||||
// Note that we allow classes with multiple function supertypes, so long as only one
|
||||
// of them has more than 22 arguments.
|
||||
// TODO: Add a proper diagnostic message in the frontend
|
||||
assert(bigArityFunctionSuperTypes.size == 1) {
|
||||
"Class has multiple big-arity function super types: ${bigArityFunctionSuperTypes.joinToString { it.render() }}"
|
||||
}
|
||||
|
||||
// Fix super class
|
||||
val superType = bigArityFunctionSuperTypes.single()
|
||||
declaration.superTypes.remove(superType)
|
||||
declaration.superTypes += context.ir.symbols.functionN.typeWith(
|
||||
(superType.arguments.last() as IrTypeProjection).type
|
||||
)
|
||||
|
||||
// Add vararg invoke bridge
|
||||
val invokeFunction = declaration.functions.single {
|
||||
it.name.asString() == "invoke" && it.valueParameters.size == superType.arguments.size - 1
|
||||
}
|
||||
invokeFunction.overriddenSymbols.clear()
|
||||
declaration.addBridge(invokeFunction, functionNInvokeFun.owner)
|
||||
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun IrClass.addBridge(invoke: IrSimpleFunction, superFunction: IrSimpleFunction) =
|
||||
addFunction {
|
||||
name = superFunction.name
|
||||
returnType = context.irBuiltIns.anyNType
|
||||
modality = Modality.FINAL
|
||||
visibility = Visibilities.PUBLIC
|
||||
origin = IrDeclarationOrigin.BRIDGE
|
||||
}.apply {
|
||||
overriddenSymbols += superFunction.symbol
|
||||
dispatchReceiverParameter = thisReceiver!!.copyTo(this)
|
||||
valueParameters += superFunction.valueParameters.single().copyTo(this)
|
||||
|
||||
body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
|
||||
// Check the number of arguments
|
||||
val argumentCount = invoke.valueParameters.size
|
||||
+irIfThen(
|
||||
irNotEquals(
|
||||
irCall(arraySizePropertyGetter).apply {
|
||||
dispatchReceiver = irGet(valueParameters.single())
|
||||
},
|
||||
irInt(argumentCount)
|
||||
),
|
||||
irCall(context.irBuiltIns.illegalArgumentExceptionSymbol).apply {
|
||||
putValueArgument(0, irString("Expected ${argumentCount} arguments"))
|
||||
}
|
||||
)
|
||||
|
||||
+irReturn(irCall(invoke).apply {
|
||||
dispatchReceiver = irGet(dispatchReceiverParameter!!)
|
||||
|
||||
for (parameter in invoke.valueParameters) {
|
||||
val index = parameter.index
|
||||
val argArray = irGet(valueParameters.single())
|
||||
val argument = irCallOp(arrayGetFun, context.irBuiltIns.anyNType, argArray, irInt(index))
|
||||
putValueArgument(index, irImplicitCast(argument, invoke.valueParameters[index].type))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a type is an instance of kotlin.Function*, kotlin.jvm.functions.Function*
|
||||
// or kotlin.reflect.KFunction*. Note that `IrType.isFunctionOrKFunction()` in IrTypeUtils
|
||||
// does not check for the jvm specific kotlin.jvm.functions package and can't be used here.
|
||||
private val IrType.isFunctionType: Boolean
|
||||
get() {
|
||||
val clazz = classOrNull?.owner ?: return false
|
||||
val name = clazz.name.asString()
|
||||
val fqName = clazz.parent.safeAs<IrPackageFragment>()?.fqName ?: return false
|
||||
return when {
|
||||
name.startsWith("Function") ->
|
||||
fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME || fqName == FUNCTIONS_PACKAGE_FQ_NAME
|
||||
name.startsWith("KFunction") ->
|
||||
fqName == REFLECT_PACKAGE_FQ_NAME
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private val functionNInvokeFun =
|
||||
context.ir.symbols.functionN.functions.single { it.owner.name.toString() == "invoke" }
|
||||
|
||||
private val arraySizePropertyGetter by lazy {
|
||||
context.irBuiltIns.arrayClass.owner.properties.single { it.name.toString() == "size" }.getter!!
|
||||
}
|
||||
|
||||
private val arrayGetFun by lazy {
|
||||
context.irBuiltIns.arrayClass.functions.single { it.owner.name.toString() == "get" }
|
||||
}
|
||||
|
||||
private val FUNCTIONS_PACKAGE_FQ_NAME =
|
||||
KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME
|
||||
.child(Name.identifier("jvm"))
|
||||
.child(Name.identifier("functions"))
|
||||
|
||||
private val REFLECT_PACKAGE_FQ_NAME =
|
||||
KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME
|
||||
.child(Name.identifier("reflect"))
|
||||
}
|
||||
-157
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||
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.builtins.functions.FunctionInvokeDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.typeWith
|
||||
import org.jetbrains.kotlin.ir.util.filterDeclarations
|
||||
import org.jetbrains.kotlin.ir.util.findDeclaration
|
||||
import org.jetbrains.kotlin.ir.util.isSubclassOf
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
internal val functionNVarargInvokePhase = makeIrFilePhase(
|
||||
::FunctionNVarargInvokeLowering,
|
||||
name = "FunctionNVarargInvoke",
|
||||
description = "Handle invoke functions with large number of arguments"
|
||||
)
|
||||
|
||||
private class FunctionNVarargInvokeLowering(var context: JvmBackendContext) : ClassLoweringPass {
|
||||
|
||||
override fun lower(irClass: IrClass) {
|
||||
val invokeFunctions = irClass.filterDeclarations<IrSimpleFunction> { it.name.toString() == "invoke" }
|
||||
if (invokeFunctions.isEmpty() ||
|
||||
invokeFunctions.any { it.valueParameters.size > 0 && it.valueParameters.last().varargElementType != null } ||
|
||||
invokeFunctions.all { it.valueParameters.size + (if (it.extensionReceiverParameter != null) 1 else 0) < FunctionInvokeDescriptor.Factory.BIG_ARITY }
|
||||
) {
|
||||
// No need to add a new vararg invoke method
|
||||
return
|
||||
}
|
||||
val functionInvokes = invokeFunctions.filter { irClass.isSubclassOf(context.ir.symbols.getFunction(it.valueParameters.size).owner) }
|
||||
if (functionInvokes.isEmpty()) return
|
||||
|
||||
irClass.declarations.add(generateVarargInvoke(irClass, functionInvokes))
|
||||
}
|
||||
|
||||
private fun generateVarargInvoke(irClass: IrClass, invokesToDelegateTo: List<IrSimpleFunction>): IrFunction {
|
||||
val backendContext = context
|
||||
val descriptor = WrappedSimpleFunctionDescriptor()
|
||||
return IrFunctionImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL,
|
||||
symbol = IrSimpleFunctionSymbolImpl(descriptor),
|
||||
name = Name.identifier("invoke"),
|
||||
visibility = Visibilities.PUBLIC,
|
||||
modality = Modality.OPEN,
|
||||
returnType = context.irBuiltIns.anyNType,
|
||||
isInline = false,
|
||||
isExternal = false,
|
||||
isTailrec = false,
|
||||
isSuspend = false
|
||||
).apply {
|
||||
descriptor.bind(this)
|
||||
parent = irClass
|
||||
dispatchReceiverParameter = irClass.thisReceiver?.copyTo(this)
|
||||
val varargParameterDescriptor = WrappedValueParameterDescriptor()
|
||||
val varargParam = IrValueParameterImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL,
|
||||
symbol = IrValueParameterSymbolImpl(varargParameterDescriptor),
|
||||
name = Name.identifier("args"),
|
||||
index = 0,
|
||||
type = context.irBuiltIns.arrayClass.typeWith(context.irBuiltIns.anyNType),
|
||||
varargElementType = context.irBuiltIns.anyNType,
|
||||
isCrossinline = false,
|
||||
isNoinline = false
|
||||
).apply {
|
||||
varargParameterDescriptor.bind(this)
|
||||
}
|
||||
varargParam.parent = this
|
||||
valueParameters.add(varargParam)
|
||||
val irBuilder = context.createIrBuilder(symbol, UNDEFINED_OFFSET, UNDEFINED_OFFSET)
|
||||
body = irBuilder.irBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
|
||||
val arrayGetFun =
|
||||
backendContext.irBuiltIns.arrayClass.owner.findDeclaration<IrSimpleFunction> { it.name.toString() == "get" }!!
|
||||
val arraySizeProperty = context.irBuiltIns.arrayClass.owner.findDeclaration<IrProperty> { it.name.toString() == "size" }!!
|
||||
val numberOfArguments = irTemporary(irCall(arraySizeProperty.getter!!).apply {
|
||||
dispatchReceiver = irGet(varargParam)
|
||||
})
|
||||
|
||||
for (target in invokesToDelegateTo) {
|
||||
+irIfThen(
|
||||
backendContext.irBuiltIns.unitType,
|
||||
irEquals(irGet(numberOfArguments), irInt(target.valueParameters.size)),
|
||||
irReturn(
|
||||
IrTypeOperatorCallImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
|
||||
backendContext.irBuiltIns.anyNType,
|
||||
IrTypeOperator.CAST,
|
||||
target.returnType,
|
||||
irCall(target).apply {
|
||||
dispatchReceiver = irGet(dispatchReceiverParameter!!)
|
||||
target.valueParameters.forEachIndexed { i, irValueParameter ->
|
||||
val type = irValueParameter.type
|
||||
putValueArgument(
|
||||
i,
|
||||
irBlock(resultType = type) {
|
||||
val argValue = irTemporary(
|
||||
irCallOp(
|
||||
arrayGetFun.symbol,
|
||||
context.irBuiltIns.anyNType,
|
||||
irGet(varargParam),
|
||||
irInt(i)
|
||||
)
|
||||
)
|
||||
+irIfThen(
|
||||
irNotIs(irGet(argValue), type),
|
||||
irCall(context.irBuiltIns.illegalArgumentExceptionSymbol).apply {
|
||||
putValueArgument(0, irString("Wrong type, expected $type"))
|
||||
}
|
||||
)
|
||||
+irGet(argValue)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val throwMessage = invokesToDelegateTo.map { it.valueParameters.size.toString() }.joinToString(
|
||||
prefix = "Expected ",
|
||||
separator = " or ",
|
||||
postfix = " arguments to invoke call"
|
||||
)
|
||||
+irCall(context.irBuiltIns.illegalArgumentExceptionSymbol).apply {
|
||||
putValueArgument(0, irString(throwMessage))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// !LANGUAGE: +FunctionTypesWithBigArity
|
||||
// WITH_RUNTIME
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// FILE: J.java
|
||||
|
||||
// import kotlin.jvm.functions.Arity;
|
||||
@@ -20,7 +20,7 @@ public class J {
|
||||
throw new AssertionError("Bad return value from function");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (i == 3 || i == 13 || i == 30 || i == 33) {
|
||||
if (i == 30) {
|
||||
throw new AssertionError(String.format("Call with %d arguments is expected to succeed", i), e);
|
||||
}
|
||||
// OK
|
||||
@@ -35,7 +35,7 @@ public class J {
|
||||
"Incorrect exception (IllegalArgumentException expected): " + e.getClass().getName() + ", i = " + i, e
|
||||
);
|
||||
}
|
||||
if (i != 3 && i != 13 && i != 30 && i != 33) {
|
||||
if (i != 30) {
|
||||
throw new AssertionError ("IllegalArgumentException expected, but nothing was thrown, i = " + i);
|
||||
}
|
||||
}
|
||||
@@ -47,9 +47,7 @@ public class J {
|
||||
class Fun : (Int, Int, Int) -> Int,
|
||||
(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Int,
|
||||
(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int,
|
||||
Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Int,
|
||||
(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int,
|
||||
Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Int {
|
||||
Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Int {
|
||||
override fun invoke(p00: Int, p01: Int, p02: Int): Int = 303
|
||||
|
||||
override fun invoke(
|
||||
@@ -62,13 +60,6 @@ class Fun : (Int, Int, Int) -> Int,
|
||||
p10: Int, p11: Int, p12: Int, p13: Int, p14: Int, p15: Int, p16: Int, p17: Int, p18: Int, p19: Int,
|
||||
p20: Int, p21: Int, p22: Int, p23: Int, p24: Int, p25: Int, p26: Int, p27: Int, p28: Int, p29: Int
|
||||
): Int = 330
|
||||
|
||||
override fun invoke(
|
||||
p00: Int, p01: Int, p02: Int, p03: Int, p04: Int, p05: Int, p06: Int, p07: Int, p08: Int, p09: Int,
|
||||
p10: Int, p11: Int, p12: Int, p13: Int, p14: Int, p15: Int, p16: Int, p17: Int, p18: Int, p19: Int,
|
||||
p20: Int, p21: Int, p22: Int, p23: Int, p24: Int, p25: Int, p26: Int, p27: Int, p28: Int, p29: Int,
|
||||
p30: Int, p31: Int, p32: Int
|
||||
): Int = 333
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun test() {
|
||||
1.(fun Int.() = 2)()
|
||||
}
|
||||
|
||||
+5
@@ -11345,6 +11345,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/functions/bigArity/callFromJava.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callFunViaVararg.kt")
|
||||
public void testCallFunViaVararg() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/functions/bigArity/callFunViaVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callWithIncorrectNumberOfArguments.kt")
|
||||
public void testCallWithIncorrectNumberOfArguments() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/functions/bigArity/callWithIncorrectNumberOfArguments.kt");
|
||||
|
||||
+5
@@ -11350,6 +11350,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/functions/bigArity/callFromJava.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callFunViaVararg.kt")
|
||||
public void testCallFunViaVararg() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/functions/bigArity/callFunViaVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callWithIncorrectNumberOfArguments.kt")
|
||||
public void testCallWithIncorrectNumberOfArguments() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/functions/bigArity/callWithIncorrectNumberOfArguments.kt");
|
||||
|
||||
Reference in New Issue
Block a user