JVM_IR: make CallableReferenceLowering a bit shorter

This commit is contained in:
pyos
2019-06-12 10:25:00 +02:00
committed by Georgy Bronnikov
parent 6d63214d1b
commit b45e8c7021
2 changed files with 88 additions and 223 deletions
@@ -31,21 +31,15 @@ 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.lower.inlineclasses.isPrimaryInlineClassConstructor
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.codegen.PropertyReferenceCodegen
import org.jetbrains.kotlin.descriptors.ClassKind
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.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
@@ -88,13 +82,11 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
val callee = expression.symbol.owner
if (callee.isInlineFunctionCall(context)) {
//TODO: more wise filtering
callee.valueParameters.forEach { valueParameter ->
for (valueParameter in callee.valueParameters) {
if (valueParameter.isInlineParameter()) {
expression.getValueArgument(valueParameter.index)?.let {
if (isInlineIrExpression(it)) {
(it as IrBlock).statements.filterIsInstance<IrFunctionReference>().forEach { reference ->
inlineLambdaReferences.add(reference)
}
inlineLambdaReferences += (it as IrBlock).statements.filterIsInstance<IrFunctionReference>()
}
}
}
@@ -103,7 +95,7 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
val argumentsCount = expression.valueArgumentsCount
// Change calls to FunctionN with large N to varargs calls.
val newCall = if (argumentsCount > MAX_ARGCOUNT_WITHOUT_VARARG &&
val newCall = if (argumentsCount >= FunctionInvokeDescriptor.Factory.BIG_ARITY &&
callee.parentAsClass.defaultType.isFunctionOrKFunction()
) {
val vararg = IrVarargImpl(
@@ -145,8 +137,7 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
val currentDeclarationParent = allScopes.map { it.irElement }.last { it is IrDeclarationParent } as IrDeclarationParent
val loweredFunctionReference = FunctionReferenceBuilder(currentDeclarationParent, expression).build()
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
return irBuilder.irBlock(expression) {
return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).irBlock(expression) {
+loweredFunctionReference.functionReferenceClass
+irCall(loweredFunctionReference.functionReferenceConstructor.symbol).apply {
expression.getArguments().forEachIndexed { index, argument ->
@@ -158,6 +149,14 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
})
}
private val arrayGetFun by lazy {
context.irBuiltIns.arrayClass.owner.functions.find { it.name.asString() == "get" }!!
}
private val arraySizeProperty by lazy {
context.irBuiltIns.arrayClass.owner.properties.find { it.name.toString() == "size" }!!
}
private class BuiltFunctionReference(
val functionReferenceClass: IrClass,
val functionReferenceConstructor: IrConstructor
@@ -182,7 +181,6 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
}
private lateinit var functionReferenceClass: IrClass
private lateinit var functionReferenceThis: IrValueParameterSymbol
private lateinit var argumentToFieldMap: Map<IrValueParameter, IrField>
private val isLambda = irFunctionReference.origin == IrStatementOrigin.LAMBDA
@@ -196,13 +194,12 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
val functionReferenceClassSuperTypes: MutableList<IrType> = mutableListOf(functionReferenceOrLambda.owner.defaultType)
val numberOfParameters = unboundCalleeParameters.size
useVararg = (numberOfParameters > MAX_ARGCOUNT_WITHOUT_VARARG)
useVararg = (numberOfParameters >= FunctionInvokeDescriptor.Factory.BIG_ARITY)
val functionClassSymbol = if (useVararg)
context.ir.symbols.functionN
else
context.ir.symbols.getJvmFunctionClass(numberOfParameters)
val functionClass = functionClassSymbol.owner
val functionParameterTypes = unboundCalleeParameters.map { it.type }
val functionClassTypeParameters = if (useVararg)
listOf(returnType)
@@ -236,88 +233,53 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
setSourceRange(irFunctionReference)
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
name = "${callee.name.safeName()}\$${functionReferenceCount++}".synthesizedName
kind = ClassKind.CLASS
visibility = Visibilities.PUBLIC
modality = Modality.FINAL
}.apply {
parent = referenceParent
superTypes.addAll(functionReferenceClassSuperTypes)
createImplicitParameterDeclarationWithWrappedDescriptor()
}
functionReferenceThis = functionReferenceClass.thisReceiver!!.symbol
argumentToFieldMap = boundCalleeParameters.associate {
it to buildField(it.name.safeName(), it.type)
}
val constructor = createConstructor()
functionReferenceClass.declarations.add(constructor)
val superInvokeFunction = functionClass.functions.find { it.name.asString() == "invoke" }!!
val invokeMethod = createInvokeMethod(superInvokeFunction)
functionReferenceClass.declarations.add(invokeMethod)
createInvokeMethod(functionClassSymbol.owner.functions.find { it.name.asString() == "invoke" }!!)
if (!isLambda) {
val getSignatureMethod =
createGetSignatureMethod(functionReferenceOrLambda.owner.functions.find { it.name.asString() == "getSignature"}!!)
val getNameMethod =
createGetNameMethod(functionReferenceOrLambda.owner.functions.find { it.name.asString() == "getName" }!!)
val getOwnerMethod =
createGetOwnerMethod(functionReferenceOrLambda.owner.functions.find { it.name.asString() == "getOwner" }!!)
val suspendInvokeMethod =
if (suspendFunctionClass != null) {
val suspendInvokeFunction =
suspendFunctionClass.functions.find { it.name.asString() == "invoke" }!!
createInvokeMethod(suspendInvokeFunction)
} else null
functionReferenceClass.declarations.add(getSignatureMethod)
functionReferenceClass.declarations.add(getNameMethod)
functionReferenceClass.declarations.add(getOwnerMethod)
suspendInvokeMethod?.let { functionReferenceClass.declarations.add(it) }
createGetSignatureMethod(functionReferenceOrLambda.owner.functions.find { it.name.asString() == "getSignature"}!!)
createGetNameMethod(functionReferenceOrLambda.owner.functions.find { it.name.asString() == "getName" }!!)
createGetOwnerMethod(functionReferenceOrLambda.owner.functions.find { it.name.asString() == "getOwner" }!!)
if (suspendFunctionClass != null) {
createInvokeMethod(suspendFunctionClass.functions.find { it.name.asString() == "invoke" }!!)
}
}
return BuiltFunctionReference(functionReferenceClass, constructor)
}
private fun createConstructor(): IrConstructor =
buildConstructor {
functionReferenceClass.addConstructor {
setSourceRange(irFunctionReference)
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
visibility = Visibilities.PUBLIC
returnType = functionReferenceClass.defaultType
isPrimary = true
}.apply {
val constructor = this
parent = functionReferenceClass
val boundArgsSet = boundCalleeParameters.toSet()
for (param in callee.explicitParameters) {
if (param in boundArgsSet) {
val newParam = param.copyTo(
constructor,
index = valueParameters.size,
type = param.type.substitute(typeArgumentsMap)
)
valueParameters.add(newParam)
}
for (param in boundCalleeParameters) {
valueParameters += param.copyTo(
this,
index = valueParameters.size,
type = param.type.substitute(typeArgumentsMap)
)
}
val kFunctionRefConstructorSymbol =
functionReferenceOrLambda.constructors.filter { it.owner.valueParameters.size == if (isLambda) 1 else 2 }.single()
val irBuilder = context.createIrBuilder(this.symbol, startOffset, endOffset)
body = irBuilder.irBlockBody {
+IrDelegatingConstructorCallImpl(
startOffset, endOffset, context.irBuiltIns.unitType,
kFunctionRefConstructorSymbol, kFunctionRefConstructorSymbol.descriptor
).apply {
val const =
IrConstImpl.int(startOffset, endOffset, context.irBuiltIns.intType, unboundCalleeParameters.size)
putValueArgument(0, const)
body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
+irDelegatingConstructorCall(kFunctionRefConstructorSymbol.owner).apply {
putValueArgument(0, irInt(unboundCalleeParameters.size))
if (!isLambda) {
val irReceiver = valueParameters.firstOrNull()
val receiver = boundCalleeParameters.singleOrNull()
@@ -333,7 +295,7 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
//TODO don't write receiver again: use it from base class
boundCalleeParameters.forEachIndexed { index, it ->
+irSetField(
irGet(functionReferenceThis.owner),
irGet(functionReferenceClass.thisReceiver!!),
argumentToFieldMap[it]!!,
irGet(valueParameters[index])
)
@@ -342,61 +304,27 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
}
}
private fun IrSimpleFunction.invokeInlineClassConstructor(irConstructor: IrConstructor): IrBody {
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
val param = irConstructor.valueParameters[0].copyTo(this)
valueParameters.add(param)
val from = irConstructor.valueParameters[0].type
val to = irConstructor.returnType
val call = irBuilder.irCall(context.ir.symbols.unsafeCoerceIntrinsicSymbol, to, listOf(from, to)).apply {
putValueArgument(0, irBuilder.irGet(param))
}
return irBuilder.irExprBody(call)
}
private fun createInvokeMethod(superFunction: IrSimpleFunction): IrSimpleFunction =
buildFun {
setSourceRange(irFunctionReference)
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
name = Name.identifier("invoke")
visibility = Visibilities.PUBLIC
returnType = callee.returnType
isSuspend = superFunction.isSuspend
}.apply {
val function = this
parent = functionReferenceClass
overriddenSymbols.add(superFunction.symbol)
buildOverride(superFunction, callee.returnType).apply {
annotations.addAll(callee.annotations)
dispatchReceiverParameter = functionReferenceClass.thisReceiver?.copyTo(function)
if (callee.isPrimaryInlineClassConstructor) {
body = invokeInlineClassConstructor(callee as IrConstructor)
return this
}
val unboundArgsSet = unboundCalleeParameters.toSet()
if (useVararg) {
valueParameters.add(superFunction.valueParameters[0].copyTo(function))
valueParameters.add(superFunction.valueParameters[0].copyTo(this))
} else {
for (param in callee.explicitParameters) {
if (param in unboundArgsSet) {
val newParam = param.copyTo(
function,
index = valueParameters.size,
type = param.type.substitute(typeArgumentsMap)
)
valueParameters.add(newParam)
}
for (param in unboundCalleeParameters) {
valueParameters += param.copyTo(
this,
index = valueParameters.size,
type = param.type.substitute(typeArgumentsMap)
)
}
}
val irBuilder = context.createIrBuilder(function.symbol, startOffset, endOffset)
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
body = irBuilder.irBlockBody(startOffset, endOffset) {
val arrayGetFun = context.irBuiltIns.arrayClass.owner.functions.find { it.name.asString() == "get" }!!
if (useVararg) {
val varargParam = valueParameters.single()
val arraySizeProperty = context.irBuiltIns.arrayClass.owner.properties.find { it.name.toString() == "size" }!!
+irIfThen(
irNotEquals(
irCall(arraySizeProperty.getter!!).apply {
@@ -422,11 +350,12 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
!unboundArgsSet.contains(parameter) ->
// Bound parameter - read from field.
irGetField(irGet(dispatchReceiverParameter!!), argumentToFieldMap[parameter]!!)
function.isSuspend && unboundIndex == valueParameters.size ->
isSuspend && unboundIndex == valueParameters.size ->
// For suspend functions the last argument is continuation and it is implicit.
// irCall(getContinuationSymbol, listOf(ourSymbol.descriptor.returnType!!))
TODO()
// irCall(getContinuationSymbol,
// listOf(ourSymbol.descriptor.returnType!!))
useVararg -> {
val type = parameter.type
val varargParam = valueParameters.single()
@@ -446,14 +375,12 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
+irGet(argValue)
}
}
else -> {
else ->
irGet(valueParameters[unboundIndex++])
}
}
putArgument(callee, parameter, argument)
}
if (!useVararg) assert(unboundIndex == valueParameters.size) { "Not all arguments of <invoke> are used" }
}
)
}
@@ -461,113 +388,56 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
}
private fun buildField(fieldName: Name, fieldType: IrType): IrField =
buildField {
functionReferenceClass.addField {
setSourceRange(irFunctionReference)
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
name = fieldName
type = fieldType
visibility = JavaVisibilities.PACKAGE_VISIBILITY
isFinal = true
}.also {
it.parent = functionReferenceClass
functionReferenceClass.declarations.add(it)
}
private fun createGetSignatureMethod(superFunction: IrSimpleFunction): IrSimpleFunction =
buildFun {
private fun buildOverride(superFunction: IrSimpleFunction, newReturnType: IrType = superFunction.returnType): IrSimpleFunction =
functionReferenceClass.addFunction {
setSourceRange(irFunctionReference)
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
name = Name.identifier("getSignature")
returnType = superFunction.returnType
origin = functionReferenceClass.origin
name = superFunction.name
returnType = newReturnType
visibility = superFunction.visibility
modality = superFunction.modality
isSuspend = superFunction.isSuspend
}.apply {
val function = this
parent = functionReferenceClass
overriddenSymbols.add(superFunction.symbol)
dispatchReceiverParameter = functionReferenceClass.thisReceiver!!.copyTo(function)
val irBuilder = context.createIrBuilder(function.symbol, startOffset, endOffset)
body = irBuilder.irBlockBody(startOffset, endOffset) {
+irReturn(
IrConstImpl.string(
-1, -1, context.irBuiltIns.stringType,
PropertyReferenceCodegen.getSignatureString(
irFunctionReference.symbol.descriptor, this@CallableReferenceLowering.context.state
)
)
)
}
overriddenSymbols += superFunction.symbol
dispatchReceiverParameter = functionReferenceClass.thisReceiver?.copyTo(this)
}
private val IrFunction.originalName: Name
get() = (metadata as? MetadataSource.Function)?.descriptor?.name ?: name
private fun createGetNameMethod(superFunction: IrSimpleFunction): IrSimpleFunction =
buildFun {
setSourceRange(irFunctionReference)
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
name = Name.identifier("getName")
returnType = superFunction.returnType
visibility = superFunction.visibility
modality = superFunction.modality
}.apply {
val function = this
parent = functionReferenceClass
overriddenSymbols.add(superFunction.symbol)
dispatchReceiverParameter = functionReferenceClass.thisReceiver?.copyTo(function)
val irBuilder = context.createIrBuilder(function.symbol, startOffset, endOffset)
body = irBuilder.irBlockBody(startOffset, endOffset) {
+irReturn(
IrConstImpl.string(-1, -1, context.irBuiltIns.stringType, callee.originalName.asString())
)
}
private fun createGetSignatureMethod(superFunction: IrSimpleFunction): IrSimpleFunction = buildOverride(superFunction).apply {
val state = context.state
body = context.createIrBuilder(symbol, startOffset, endOffset).run {
// TODO do not use descriptors
irExprBody(irString(PropertyReferenceCodegen.getSignatureString(irFunctionReference.symbol.descriptor, state)))
}
}
private fun createGetOwnerMethod(superFunction: IrSimpleFunction): IrSimpleFunction =
buildFun {
setSourceRange(functionReferenceClass)
origin = JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
name = Name.identifier("getOwner")
returnType = superFunction.returnType
visibility = superFunction.visibility
modality = superFunction.modality
}.apply {
val function = this
parent = functionReferenceClass
overriddenSymbols.add(superFunction.symbol)
dispatchReceiverParameter = functionReferenceClass.thisReceiver?.copyTo(function)
val irBuilder = context.createIrBuilder(function.symbol, startOffset, endOffset)
body = irBuilder.irBlockBody(startOffset, endOffset) {
+irReturn(
generateCallableReferenceDeclarationContainer()
)
}
private fun createGetNameMethod(superFunction: IrSimpleFunction): IrSimpleFunction = buildOverride(superFunction).apply {
body = context.createIrBuilder(symbol, startOffset, endOffset).run {
irExprBody(irString(callee.originalName.asString()))
}
}
fun IrBuilderWithScope.generateCallableReferenceDeclarationContainer(): IrExpression {
val globalContext = this@CallableReferenceLowering.context
private fun createGetOwnerMethod(superFunction: IrSimpleFunction): IrSimpleFunction = buildOverride(superFunction).apply {
val globalContext = context
val state = globalContext.state
val irContainer = callee.parent
val isContainerPackage =
((irContainer as? IrClass)?.origin == IrDeclarationOrigin.FILE_CLASS) || irContainer is IrPackageFragment
val type = when {
irContainer is IrClass ->
// TODO: getDefaultType() here is wrong and won't work for arrays
state.typeMapper.mapType(irContainer.defaultType.toKotlinType())
// // TODO: this code is only needed for property references, which are not yet supported.
// descriptor is VariableDescriptorWithAccessors -> {
// assert(false) { "VariableDescriptorWithAccessors" }
// globalContext.state.bindingContext.get(
// CodegenBinding.DELEGATED_PROPERTY_METADATA_OWNER, descriptor
// )!!
// }
val type = when (irContainer) {
// TODO: getDefaultType() here is wrong and won't work for arrays
is IrClass -> state.typeMapper.mapType(irContainer.defaultType.toKotlinType())
else -> state.typeMapper.mapOwner(callee.descriptor)
}
@@ -580,22 +450,20 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
CrIrType(type)
)
return if (isContainerPackage) {
// Note that this name is not used in reflection. There should be the name of the referenced declaration's module instead,
// but there's no nice API to obtain that name here yet
// TODO: write the referenced declaration's module name and use it in reflection
val module = IrConstImpl.string(
-1, -1, globalContext.irBuiltIns.stringType,
state.moduleName
)
irCall(globalContext.ir.symbols.getOrCreateKotlinPackage).apply {
putValueArgument(0, clazzRef)
putValueArgument(1, module)
}
} else {
irCall(globalContext.ir.symbols.getOrCreateKotlinClass).apply {
putValueArgument(0, clazzRef)
}
body = context.createIrBuilder(symbol, startOffset, endOffset).run {
irExprBody(if (isContainerPackage) {
irCall(globalContext.ir.symbols.getOrCreateKotlinPackage).apply {
putValueArgument(0, clazzRef)
// Note that this name is not used in reflection. There should be the name of the referenced declaration's
// module instead, but there's no nice API to obtain that name here yet
// TODO: write the referenced declaration's module name and use it in reflection
putValueArgument(1, irString(state.moduleName))
}
} else {
irCall(globalContext.ir.symbols.getOrCreateKotlinClass).apply {
putValueArgument(0, clazzRef)
}
})
}
}
}
@@ -607,8 +475,4 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
Name.identifier("$${name.substring(1, name.length - 1)}")
} else this
}
companion object {
const val MAX_ARGCOUNT_WITHOUT_VARARG = 22
}
}
@@ -14,6 +14,7 @@ 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
@@ -44,7 +45,7 @@ private class FunctionNVarargInvokeLowering(var context: JvmBackendContext) : Cl
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) <= CallableReferenceLowering.MAX_ARGCOUNT_WITHOUT_VARARG }
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