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
|
singletonReferencesPhase then
|
||||||
|
|
||||||
callableReferencePhase then
|
callableReferencePhase then
|
||||||
functionNVarargInvokePhase then
|
|
||||||
localDeclarationsPhase then
|
localDeclarationsPhase then
|
||||||
|
|
||||||
singleAbstractMethodPhase then
|
singleAbstractMethodPhase then
|
||||||
@@ -172,6 +171,7 @@ private val jvmFilePhases =
|
|||||||
objectClassPhase then
|
objectClassPhase then
|
||||||
makeInitializersPhase(JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, true) then
|
makeInitializersPhase(JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, true) then
|
||||||
collectionStubMethodLowering then
|
collectionStubMethodLowering then
|
||||||
|
functionNVarargBridgePhase then
|
||||||
bridgePhase then
|
bridgePhase then
|
||||||
jvmStaticAnnotationPhase then
|
jvmStaticAnnotationPhase then
|
||||||
staticDefaultFunctionPhase 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.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
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.common.phaser.makeIrFilePhase
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineFunctionCall
|
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineFunctionCall
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
|
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
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.codegen.PropertyReferenceCodegen
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
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.builders.declarations.buildClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
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.IrClassReferenceImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
|
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)
|
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 {
|
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
|
val function = expression.symbol.owner
|
||||||
if (!function.isInlineFunctionCall(context))
|
if (function.isInlineFunctionCall(context)) {
|
||||||
return
|
for (parameter in function.valueParameters) {
|
||||||
|
if (!parameter.isInlineParameter())
|
||||||
|
continue
|
||||||
|
|
||||||
for (parameter in function.valueParameters) {
|
val valueArgument = expression.getValueArgument(parameter.index) ?: continue
|
||||||
if (!parameter.isInlineParameter())
|
if (!isInlineIrExpression(valueArgument))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
val valueArgument = expression.getValueArgument(parameter.index) ?: continue
|
if (valueArgument is IrFunctionReference) {
|
||||||
if (!isInlineIrExpression(valueArgument))
|
ignoredFunctionReferences.add(valueArgument)
|
||||||
continue
|
} 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
|
// 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>
|
// 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 parameterTypes = (irFunctionReference.type as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
|
||||||
private val argumentTypes = parameterTypes.dropLast(1)
|
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)
|
private val typeParameters = if (callee is IrConstructor)
|
||||||
callee.parentAsClass.typeParameters + callee.typeParameters
|
callee.parentAsClass.typeParameters + callee.typeParameters
|
||||||
@@ -190,7 +155,10 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
|||||||
fun build(): IrExpression {
|
fun build(): IrExpression {
|
||||||
val constructor = createConstructor()
|
val constructor = createConstructor()
|
||||||
val invokeMethod = createInvokeMethod()
|
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) {
|
if (!isLambda) {
|
||||||
createGetSignatureMethod(functionGetSignature)
|
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 =
|
private fun buildOverride(superFunction: IrSimpleFunction, newReturnType: IrType = superFunction.returnType): IrSimpleFunction =
|
||||||
functionReferenceClass.addFunction {
|
functionReferenceClass.addFunction {
|
||||||
setSourceRange(irFunctionReference)
|
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 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 =
|
private val functionReferenceReceiverField =
|
||||||
context.ir.symbols.functionReference.owner.declarations.single { it is IrField && it.name.toString() == "receiver" } as IrField
|
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_RUNTIME
|
||||||
// WITH_COROUTINES
|
// WITH_COROUTINES
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// !LANGUAGE: +FunctionTypesWithBigArity
|
// !LANGUAGE: +FunctionTypesWithBigArity
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM
|
||||||
// FILE: J.java
|
// FILE: J.java
|
||||||
|
|
||||||
// import kotlin.jvm.functions.Arity;
|
// import kotlin.jvm.functions.Arity;
|
||||||
@@ -20,7 +20,7 @@ public class J {
|
|||||||
throw new AssertionError("Bad return value from function");
|
throw new AssertionError("Bad return value from function");
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} 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);
|
throw new AssertionError(String.format("Call with %d arguments is expected to succeed", i), e);
|
||||||
}
|
}
|
||||||
// OK
|
// OK
|
||||||
@@ -35,7 +35,7 @@ public class J {
|
|||||||
"Incorrect exception (IllegalArgumentException expected): " + e.getClass().getName() + ", i = " + i, e
|
"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);
|
throw new AssertionError ("IllegalArgumentException expected, but nothing was thrown, i = " + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,9 +47,7 @@ public class J {
|
|||||||
class Fun : (Int, Int, Int) -> Int,
|
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, 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(p00: Int, p01: Int, p02: Int): Int = 303
|
||||||
|
|
||||||
override fun invoke(
|
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,
|
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
|
p20: Int, p21: Int, p22: Int, p23: Int, p24: Int, p25: Int, p26: Int, p27: Int, p28: Int, p29: Int
|
||||||
): Int = 330
|
): 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 {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
fun test() {
|
fun test() {
|
||||||
1.(fun Int.() = 2)()
|
1.(fun Int.() = 2)()
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -11345,6 +11345,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/functions/bigArity/callFromJava.kt");
|
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")
|
@TestMetadata("callWithIncorrectNumberOfArguments.kt")
|
||||||
public void testCallWithIncorrectNumberOfArguments() throws Exception {
|
public void testCallWithIncorrectNumberOfArguments() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/functions/bigArity/callWithIncorrectNumberOfArguments.kt");
|
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");
|
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")
|
@TestMetadata("callWithIncorrectNumberOfArguments.kt")
|
||||||
public void testCallWithIncorrectNumberOfArguments() throws Exception {
|
public void testCallWithIncorrectNumberOfArguments() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/functions/bigArity/callWithIncorrectNumberOfArguments.kt");
|
runTest("compiler/testData/codegen/box/functions/bigArity/callWithIncorrectNumberOfArguments.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user