diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt index 9dd9c9e73c8..72a32c2a961 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/CallableReferenceLowering.kt @@ -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().forEach { reference -> - inlineLambdaReferences.add(reference) - } + inlineLambdaReferences += (it as IrBlock).statements.filterIsInstance() } } } @@ -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 private val isLambda = irFunctionReference.origin == IrStatementOrigin.LAMBDA @@ -196,13 +194,12 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL val functionReferenceClassSuperTypes: MutableList = 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 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 - } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargInvokeLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargInvokeLowering.kt index 7b1c285c7da..ba0ff7cd36a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargInvokeLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionNVarargInvokeLowering.kt @@ -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 { 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