diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index a255d267e56..992387234b4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -33,9 +33,9 @@ abstract class InlineCodegen( protected val invocationParamBuilder = ParametersBuilder.newBuilder() protected val expressionMap = linkedMapOf() - protected val maskValues = ArrayList() - protected var maskStartIndex = -1 - protected var methodHandleInDefaultMethodIndex = -1 + private val maskValues = ArrayList() + private var maskStartIndex = -1 + private var methodHandleInDefaultMethodIndex = -1 protected fun generateStub(text: String, codegen: BaseExpressionCodegen) { leaveTemps() @@ -71,11 +71,16 @@ abstract class InlineCodegen( private fun inlineCall(nodeAndSmap: SMAPAndMethodNode, isInlineOnly: Boolean): InlineResult { val node = nodeAndSmap.node if (maskStartIndex != -1) { - for (lambda in extractDefaultLambdas(node)) { - invocationParamBuilder.buildParameters().getParameterByDeclarationSlot(lambda.offset).functionalArgument = lambda - val prev = expressionMap.put(lambda.offset, lambda) - assert(prev == null) { "Lambda with offset ${lambda.offset} already exists: $prev" } - if (lambda.needReification) { + val infos = expandMaskConditionsAndUpdateVariableNodes( + node, maskStartIndex, maskValues, methodHandleInDefaultMethodIndex, computeDefaultLambdaOffsets() + ) + val parameters = invocationParamBuilder.buildParameters() + for (info in infos) { + val lambda = DefaultLambda(info, sourceCompiler) + parameters.getParameterByDeclarationSlot(info.offset).functionalArgument = lambda + val prev = expressionMap.put(info.offset, lambda) + assert(prev == null) { "Lambda with offset ${info.offset} already exists: $prev" } + if (info.needReification) { lambda.reifiedTypeParametersUsages.mergeAll(reifiedTypeInliner.reifyInstructions(lambda.node.node)) } rememberCapturedForDefaultLambda(lambda) @@ -136,15 +141,7 @@ abstract class InlineCodegen( return result } - abstract fun extractDefaultLambdas(node: MethodNode): List - - protected inline fun extractDefaultLambdas( - node: MethodNode, parameters: Map, block: ExtractedDefaultLambda.(T) -> DefaultLambda - ): List = expandMaskConditionsAndUpdateVariableNodes( - node, maskStartIndex, maskValues, methodHandleInDefaultMethodIndex, parameters.keys - ).map { - it.block(parameters[it.offset]!!) - } + abstract fun computeDefaultLambdaOffsets(): Set private fun generateAndInsertFinallyBlocks( intoNode: MethodNode, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt index 25a293f38fe..a41e9a3efab 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt @@ -6,12 +6,12 @@ package org.jetbrains.kotlin.codegen.inline import org.jetbrains.kotlin.codegen.AsmUtil +import org.jetbrains.kotlin.codegen.coroutines.isCoroutineSuperClass import org.jetbrains.kotlin.resolve.jvm.AsmTypes.* import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.org.objectweb.asm.ClassReader import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Label -import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode @@ -73,73 +73,70 @@ abstract class ExpressionLambda : LambdaInfo() { } } -abstract class DefaultLambda( - final override val lambdaClassType: Type, - capturedArgs: Array, - val offset: Int, - val needReification: Boolean, - sourceCompiler: SourceCompilerForInline -) : LambdaInfo() { - final override val isSuspend - get() = false // TODO: it should probably be true sometimes, but it never was - final override val isBoundCallableReference: Boolean - final override val capturedVars: List - final override val invokeMethod: Method +class DefaultLambda(info: ExtractedDefaultLambda, sourceCompiler: SourceCompilerForInline) : LambdaInfo() { + val needReification = info.needReification // TODO: remove this + + override val lambdaClassType: Type = info.type + override val isSuspend: Boolean get() = false // TODO: it should probably be true sometimes, but it never was + override val isBoundCallableReference: Boolean + override val capturedVars: List + + override val invokeMethod: Method get() = Method(node.node.name, node.node.desc) + private val nullableAnyType = sourceCompiler.state.module.builtIns.nullableAnyType + + override val invokeMethodParameters: List + get() = List(invokeMethod.argumentTypes.size) { nullableAnyType } + + override val invokeMethodReturnType: KotlinType + get() = nullableAnyType + val originalBoundReceiverType: Type? - protected val isPropertyReference: Boolean - protected val isFunctionReference: Boolean - init { - val classBytes = loadClass(sourceCompiler) + val classBytes = + sourceCompiler.state.inlineCache.classBytes.getOrPut(lambdaClassType.internalName) { + loadClassBytesByInternalName(sourceCompiler.state, lambdaClassType.internalName) + } val superName = ClassReader(classBytes).superName - isPropertyReference = superName in PROPERTY_REFERENCE_SUPER_CLASSES - isFunctionReference = superName == FUNCTION_REFERENCE.internalName || superName == FUNCTION_REFERENCE_IMPL.internalName + // TODO: suspend lambdas are their own continuations, so the body is pre-inlined into `invokeSuspend` + // and thus can't be detangled from the state machine. To make them inlinable, this needs to be redesigned. + // See `SuspendLambdaLowering`. + require(!sourceCompiler.state.languageVersionSettings.isCoroutineSuperClass(superName)) { + "suspend default lambda ${lambdaClassType.internalName} cannot be inlined; use a function reference instead" + } - val constructorMethod = Method("", Type.VOID_TYPE, capturedArgs) + val constructorMethod = Method("", Type.VOID_TYPE, info.capturedArgs) val constructor = getMethodNode(classBytes, lambdaClassType, constructorMethod)?.node - assert(constructor != null || capturedArgs.isEmpty()) { + assert(constructor != null || info.capturedArgs.isEmpty()) { "can't find constructor '$constructorMethod' for default lambda '${lambdaClassType.internalName}'" } + + val isPropertyReference = superName in PROPERTY_REFERENCE_SUPER_CLASSES + val isReference = isPropertyReference || + superName == FUNCTION_REFERENCE.internalName || superName == FUNCTION_REFERENCE_IMPL.internalName // This only works for primitives but not inline classes, since information about the Kotlin type of the bound // receiver is not present anywhere. This is why with JVM_IR the constructor argument of bound references // is already `Object`, and this field is never used. originalBoundReceiverType = - capturedArgs.singleOrNull()?.takeIf { (isFunctionReference || isPropertyReference) && AsmUtil.isPrimitive(it) } + info.capturedArgs.singleOrNull()?.takeIf { isReference && AsmUtil.isPrimitive(it) } capturedVars = - if (isFunctionReference || isPropertyReference) - capturedArgs.singleOrNull()?.let { - listOf(capturedParamDesc(AsmUtil.RECEIVER_PARAMETER_NAME, AsmUtil.boxType(it), isSuspend = false)) + if (isReference) + info.capturedArgs.singleOrNull()?.let { + // See `InlinedLambdaRemapper` + listOf(capturedParamDesc(AsmUtil.RECEIVER_PARAMETER_NAME, OBJECT_TYPE, isSuspend = false)) } ?: emptyList() else constructor?.findCapturedFieldAssignmentInstructions()?.map { fieldNode -> capturedParamDesc(fieldNode.name, Type.getType(fieldNode.desc), isSuspend = false) }?.toList() ?: emptyList() - isBoundCallableReference = (isFunctionReference || isPropertyReference) && capturedVars.isNotEmpty() - } - - private fun loadClass(sourceCompiler: SourceCompilerForInline): ByteArray = - sourceCompiler.state.inlineCache.classBytes.getOrPut(lambdaClassType.internalName) { - loadClassBytesByInternalName(sourceCompiler.state, lambdaClassType.internalName) - } - - // Returns whether the loaded invoke is erased, i.e. the name equals the fallback and all types are `Object`. - protected fun loadInvoke(sourceCompiler: SourceCompilerForInline, erasedName: String, actualMethod: Method): Boolean { - node = getMethodNodeImprecise(loadClass(sourceCompiler), lambdaClassType, actualMethod, erasedName) - ?: error("Can't find method '$actualMethod' in '${lambdaClassType.internalName}'") - return invokeMethod.run { name == erasedName && returnType == OBJECT_TYPE && argumentTypes.all { it == OBJECT_TYPE } } + isBoundCallableReference = isReference && capturedVars.isNotEmpty() + node = loadDefaultLambdaBody(classBytes, lambdaClassType, isPropertyReference) } private companion object { - fun getMethodNodeImprecise(classBytes: ByteArray, classType: Type, method: Method, erasedName: String) = - getMethodNode(classBytes, classType) { it, access -> - (it.name == method.name || it.name == erasedName) && - it.argumentTypes.size == method.argumentTypes.size && access.and(Opcodes.ACC_SYNTHETIC) == 0 - } - val PROPERTY_REFERENCE_SUPER_CLASSES = listOf( PROPERTY_REFERENCE0, PROPERTY_REFERENCE1, PROPERTY_REFERENCE2, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt index 896bd08873f..0db35255204 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt @@ -5,16 +5,13 @@ package org.jetbrains.kotlin.codegen.inline -import org.jetbrains.kotlin.builtins.isSuspendFunctionType import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype import org.jetbrains.kotlin.codegen.* -import org.jetbrains.kotlin.codegen.DescriptorAsmUtil.getMethodAsmFlags import org.jetbrains.kotlin.codegen.binding.CalculatedClosure import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType @@ -28,12 +25,9 @@ import org.jetbrains.kotlin.resolve.inline.isInlineOnly import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.org.objectweb.asm.Label -import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.Method -import org.jetbrains.org.objectweb.asm.tree.MethodNode class PsiInlineCodegen( codegen: ExpressionCodegen, @@ -95,8 +89,7 @@ class PsiInlineCodegen( private val hiddenParameters = mutableListOf>() override fun processHiddenParameters() { - val contextKind = (sourceCompiler as PsiSourceCompilerForInline).context.contextKind - if (getMethodAsmFlags(functionDescriptor, contextKind, state) and Opcodes.ACC_STATIC == 0) { + if (!DescriptorAsmUtil.isStaticMethod((sourceCompiler as PsiSourceCompilerForInline).context.contextKind, functionDescriptor)) { hiddenParameters += invocationParamBuilder.addNextParameter(methodOwner, false, actualDispatchReceiver) to codegen.frameMap.enterTemp(methodOwner) } @@ -199,10 +192,14 @@ class PsiInlineCodegen( override fun reorderArgumentsIfNeeded(actualArgsWithDeclIndex: List, valueParameterTypes: List) = Unit - override fun extractDefaultLambdas(node: MethodNode): List = - extractDefaultLambdas(node, extractDefaultLambdaOffsetAndDescriptor(jvmSignature, functionDescriptor)) { parameter -> - PsiDefaultLambda(type, capturedArgs, parameter, offset, needReification, sourceCompiler) - } + override fun computeDefaultLambdaOffsets(): Set { + val contextKind = (sourceCompiler as PsiSourceCompilerForInline).context.contextKind + return parameterOffsets(DescriptorAsmUtil.isStaticMethod(contextKind, functionDescriptor), jvmSignature.valueParameters) + .drop(jvmSignature.valueParameters.indexOfFirst { it.kind == JvmMethodParameterKind.VALUE }) + .filterIndexedTo(mutableSetOf()) { index, _ -> + functionDescriptor.valueParameters[index].let { InlineUtil.isInlineParameter(it) && it.declaresDefaultValue() } + } + } } private val FunctionDescriptor.explicitParameters @@ -301,32 +298,3 @@ class PsiExpressionLambda( val isPropertyReference: Boolean get() = propertyReferenceInfo != null } - -class PsiDefaultLambda( - lambdaClassType: Type, - capturedArgs: Array, - parameterDescriptor: ValueParameterDescriptor, - offset: Int, - needReification: Boolean, - sourceCompiler: SourceCompilerForInline -) : DefaultLambda(lambdaClassType, capturedArgs, offset, needReification, sourceCompiler) { - private val invokeMethodDescriptor: FunctionDescriptor - - override val invokeMethodParameters: List - get() = invokeMethodDescriptor.explicitParameters.map { it.returnType } - - override val invokeMethodReturnType: KotlinType? - get() = invokeMethodDescriptor.returnType - - init { - val name = if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE - val descriptor = parameterDescriptor.type.memberScope - .getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND) - .single() - .let { if (parameterDescriptor.type.isSuspendFunctionType) getOrCreateJvmSuspendFunctionView(it, sourceCompiler.state) else it } - // This is technically wrong as it always uses `invoke`, but `loadInvoke` will fall back to `get` which is never mangled... - val asmMethod = sourceCompiler.state.typeMapper.mapAsmMethod(descriptor) - val invokeIsErased = loadInvoke(sourceCompiler, name.asString(), asmMethod) - invokeMethodDescriptor = if (invokeIsErased) descriptor.original else descriptor - } -} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/defaultMethodUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/defaultMethodUtil.kt index 9aa051cf06d..a081a599c94 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/defaultMethodUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/defaultMethodUtil.kt @@ -16,20 +16,16 @@ package org.jetbrains.kotlin.codegen.inline -import org.jetbrains.kotlin.codegen.DescriptorAsmUtil -import org.jetbrains.kotlin.codegen.OwnerKind import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.isNeedClassReificationMarker import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence import org.jetbrains.kotlin.codegen.optimization.common.asSequence -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.inline.InlineUtil -import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind -import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.tree.* +import kotlin.math.max private data class Condition( val mask: Int, val constant: Int, @@ -41,25 +37,6 @@ private data class Condition( val varIndex = varInsNode?.`var` ?: 0 } -fun extractDefaultLambdaOffsetAndDescriptor( - jvmSignature: JvmMethodSignature, - functionDescriptor: FunctionDescriptor -): Map { - val valueParameters = jvmSignature.valueParameters - val containingDeclaration = functionDescriptor.containingDeclaration - val kind = - if (DescriptorUtils.isInterface(containingDeclaration)) OwnerKind.DEFAULT_IMPLS - else OwnerKind.getMemberOwnerKind(containingDeclaration) - val parameterOffsets = parameterOffsets(DescriptorAsmUtil.isStaticMethod(kind, functionDescriptor), valueParameters) - val valueParameterOffset = valueParameters.takeWhile { it.kind != JvmMethodParameterKind.VALUE }.size - - return functionDescriptor.valueParameters.filter { - InlineUtil.isInlineParameter(it) && it.declaresDefaultValue() - }.associateBy { - parameterOffsets[valueParameterOffset + it.index] - } -} - class ExtractedDefaultLambda(val type: Type, val capturedArgs: Array, val offset: Int, val needReification: Boolean) fun expandMaskConditionsAndUpdateVariableNodes( @@ -202,3 +179,68 @@ private fun defaultLambdaFakeCallStub(args: Array, lambdaOffset: Int): Met false ) } + +fun loadDefaultLambdaBody(classBytes: ByteArray, classType: Type, isPropertyReference: Boolean): SMAPAndMethodNode { + // In general we can't know what the correct unboxed `invoke` is, and what Kotlin types its arguments have, + // as the type of this object may be any subtype of the parameter's type. All we know is that Function + // has to have a `invoke` that takes `Object`s and returns an `Object`; everything else needs to be figured + // out from its contents. TODO: for > 22 arguments, the only argument is an array. `MethodInliner` can't do that. + val invokeName = if (isPropertyReference) OperatorNameConventions.GET.asString() else OperatorNameConventions.INVOKE.asString() + val invokeNode = getMethodNode(classBytes, classType) { + it.name == invokeName && it.returnType == AsmTypes.OBJECT_TYPE && it.argumentTypes.all { arg -> arg == AsmTypes.OBJECT_TYPE } + } ?: error("can't find erased invoke '$invokeName(Object...): Object' in default lambda '${classType.internalName}'") + return if (invokeNode.node.access.and(Opcodes.ACC_BRIDGE) == 0) + invokeNode + else + invokeNode.node.inlineBridge(classBytes, classType) +} + +private fun MethodNode.inlineBridge(classBytes: ByteArray, classType: Type): SMAPAndMethodNode { + // If the erased invoke is a bridge, we need to locate the unboxed invoke and inline it. As mentioned above, + // we don't know what the Kotlin types of its arguments/returned value are, so we can't generate our own + // boxing/unboxing code; luckily, the bridge already has that. + val invokeInsn = instructions.singleOrNull { it is MethodInsnNode && it.owner == classType.internalName } as MethodInsnNode? + ?: error("no single invoke of method on this in '${name}${desc}' of default lambda '${classType.internalName}'") + val targetMethod = Method(invokeInsn.name, invokeInsn.desc) + val target = getMethodNode(classBytes, classType, targetMethod) + ?: error("can't find non-bridge invoke '$targetMethod' in default lambda '${classType.internalName}") + + // Store unboxed/casted arguments in the correct variable slots + val targetArgs = targetMethod.argumentTypes + val targetArgsSize = targetArgs.sumOf { it.size } + if (target.node.access.and(Opcodes.ACC_STATIC) == 0) 1 else 0 + var offset = targetArgsSize + for (type in targetArgs.reversed()) { + offset -= type.size + instructions.insertBefore(invokeInsn, VarInsnNode(type.getOpcode(Opcodes.ISTORE), offset)) + } + if (target.node.access.and(Opcodes.ACC_STATIC) == 0) { + instructions.insertBefore(invokeInsn, InsnNode(Opcodes.POP)) // this + } + + // Remap returns and ranges for arguments' LVT entries + val invokeLabel = LabelNode() + val returnLabel = LabelNode() + instructions.insertBefore(invokeInsn, invokeLabel) + instructions.insert(invokeInsn, returnLabel) + for (insn in target.node.instructions) { + if (insn.opcode in Opcodes.IRETURN..Opcodes.RETURN) { + target.node.instructions.set(insn, JumpInsnNode(Opcodes.GOTO, returnLabel)) + } + } + for (local in target.node.localVariables) { + if (local.index < targetArgsSize) { + local.start = invokeLabel + local.end = returnLabel + } + } + + // Insert contents of the method into the bridge + instructions.filterIsInstance().forEach { instructions.remove(it) } // those are not meaningful + instructions.insertBefore(invokeInsn, target.node.instructions) + instructions.remove(invokeInsn) + localVariables = target.node.localVariables + tryCatchBlocks = target.node.tryCatchBlocks + maxLocals = max(maxLocals, target.node.maxLocals) + maxStack = max(maxStack, target.node.maxStack) + return SMAPAndMethodNode(this, target.classSMAP) +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt index 88492dfd0c5..07094cf1a1a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt @@ -85,7 +85,7 @@ private const val INLINE_MARKER_AFTER_INLINE_SUSPEND_ID = 7 private const val INLINE_MARKER_BEFORE_UNBOX_INLINE_CLASS = 8 private const val INLINE_MARKER_AFTER_UNBOX_INLINE_CLASS = 9 -internal inline fun getMethodNode(classData: ByteArray, classType: Type, crossinline match: (Method, Int) -> Boolean): SMAPAndMethodNode? { +internal inline fun getMethodNode(classData: ByteArray, classType: Type, crossinline match: (Method) -> Boolean): SMAPAndMethodNode? { var node: MethodNode? = null var sourceFile: String? = null var sourceMap: String? = null @@ -96,7 +96,7 @@ internal inline fun getMethodNode(classData: ByteArray, classType: Type, crossin } override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? { - if (!match(Method(name, desc), access)) return null + if (!match(Method(name, desc))) return null node?.let { existing -> throw AssertionError("Can't find proper '$name' method for inline: ambiguity between '${existing.name + existing.desc}' and '${name + desc}'") } @@ -112,7 +112,7 @@ internal inline fun getMethodNode(classData: ByteArray, classType: Type, crossin } internal fun getMethodNode(classData: ByteArray, classType: Type, method: Method): SMAPAndMethodNode? = - getMethodNode(classData, classType) { it, _ -> it == method } + getMethodNode(classData, classType) { it == method } internal fun Collection.lineNumberRange(): Pair { var minLine = Int.MAX_VALUE diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxInlineCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxInlineCodegenTestGenerated.java index c63524e4df7..b567d70d628 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxInlineCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxInlineCodegenTestGenerated.java @@ -1948,6 +1948,12 @@ public class FirBlackBoxInlineCodegenTestGenerated extends AbstractFirBlackBoxIn runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature2.kt"); } + @Test + @TestMetadata("differentInvokeSignature3.kt") + public void testDifferentInvokeSignature3() throws Exception { + runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt"); + } + @Test @TestMetadata("functionImportedFromObject.kt") public void testFunctionImportedFromObject() throws Exception { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt index 75ed99ba084..87822f1e738 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt @@ -6,9 +6,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen import org.jetbrains.kotlin.backend.jvm.JvmBackendContext -import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter -import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi import org.jetbrains.kotlin.codegen.IrExpressionLambda import org.jetbrains.kotlin.codegen.JvmKotlinType import org.jetbrains.kotlin.codegen.StackValue @@ -21,18 +19,13 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrValueParameter import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.IrTypeProjection -import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.Method -import org.jetbrains.org.objectweb.asm.tree.MethodNode class IrInlineCodegen( codegen: ExpressionCodegen, @@ -123,10 +116,12 @@ class IrInlineCodegen( generateStub(text, codegen) } - override fun extractDefaultLambdas(node: MethodNode): List = - extractDefaultLambdas(node, extractDefaultLambdaOffsetAndDescriptor(jvmSignature, function)) { parameter -> - IrDefaultLambda(type, capturedArgs, parameter, offset, needReification, sourceCompiler as IrSourceCompilerForInline) - } + override fun computeDefaultLambdaOffsets(): Set = + parameterOffsets(function.isStatic, jvmSignature.valueParameters) + .drop(if (function.extensionReceiverParameter != null) 1 else 0) + .filterIndexedTo(mutableSetOf()) { index, _ -> + function.valueParameters[index].let { it.defaultValue != null && it.isInlineParameter() } + } } class IrExpressionLambdaImpl( @@ -187,58 +182,6 @@ class IrExpressionLambdaImpl( } } -class IrDefaultLambda( - lambdaClassType: Type, - capturedArgs: Array, - irValueParameter: IrValueParameter, - offset: Int, - needReification: Boolean, - sourceCompiler: IrSourceCompilerForInline -) : DefaultLambda(lambdaClassType, capturedArgs, offset, needReification, sourceCompiler) { - - private val typeArguments: MutableList - - override val invokeMethodParameters: List - get() = typeArguments.dropLast(1).map { it.toIrBasedKotlinType() } - - override val invokeMethodReturnType: KotlinType - get() = typeArguments.last().toIrBasedKotlinType() - - init { - val context = sourceCompiler.codegen.context - - typeArguments = - (irValueParameter.type as IrSimpleType).arguments - .mapTo(mutableListOf()) { - when (it) { - is IrTypeProjection -> it.type - else -> context.irBuiltIns.anyNType - } - } - .apply { - // Suspend function references: `suspend (A) -> B` => `invoke(A, Continuation): Any?` - // TODO: default suspend lambdas are currently uninlinable due to having a state machine - if (irValueParameter.type.isSuspendFunction()) { - set(size - 1, context.ir.symbols.continuationClass.typeWith(get(size - 1))) - add(context.irBuiltIns.anyNType) - } - } - - val base = if (isPropertyReference) OperatorNameConventions.GET.asString() else OperatorNameConventions.INVOKE.asString() - val name = InlineClassAbi.hashSuffix( - context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures, - typeArguments.dropLast(1), - typeArguments.last().takeIf { it.isInlineClassType() } - )?.let { "$base-$it" } ?: base - // TODO: while technically only the number of arguments here matters right now (see `loadInvoke`), - // it would be better to map to a non-erased signature if not a property reference. - if (loadInvoke(sourceCompiler, base, Method(name, AsmTypes.OBJECT_TYPE, Array(typeArguments.size - 1) { AsmTypes.OBJECT_TYPE }))) { - // If the loaded method is `invoke(Object, ...) -> Object`, then it expects boxed parameters and returns a boxed value. - typeArguments.replaceAll { context.irBuiltIns.anyNType } - } - } -} - fun IrExpression.isInlineIrExpression() = when (this) { is IrBlock -> origin.isInlineIrExpression() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/defaultMethodUtilIr.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/defaultMethodUtilIr.kt deleted file mode 100644 index 160e292aae9..00000000000 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/defaultMethodUtilIr.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.codegen - -import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter -import org.jetbrains.kotlin.codegen.inline.parameterOffsets -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrValueParameter -import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature - -fun extractDefaultLambdaOffsetAndDescriptor( - jvmSignature: JvmMethodSignature, - irFunction: IrFunction -): Map { - val valueParameters = jvmSignature.valueParameters - val parameterOffsets = parameterOffsets(irFunction.isStatic, valueParameters) - - val valueParameterOffset = if (irFunction.extensionReceiverParameter != null) 1 else 0 - - return irFunction.valueParameters.filter { it.defaultValue != null && it.isInlineParameter() }.associateBy { - parameterOffsets[valueParameterOffset + it.index] - } -} diff --git a/compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt b/compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt new file mode 100644 index 00000000000..2a3990e7cde --- /dev/null +++ b/compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt @@ -0,0 +1,14 @@ +// SKIP_INLINE_CHECK_IN: inlineFun$default +// FILE: 1.kt +package test + +inline class C(val value: Any?) + +fun foo(x: Any?): C = x as C + +inline fun inlineFun(s: (C) -> Any? = ::foo): Any? = s(C("OK")) + +// FILE: 2.kt +import test.* + +fun box(): String = (inlineFun() as C).value as String diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxInlineCodegenTestGenerated.java index bdc9a054f18..d8d9a1fe3f6 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxInlineCodegenTestGenerated.java @@ -1948,6 +1948,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature2.kt"); } + @Test + @TestMetadata("differentInvokeSignature3.kt") + public void testDifferentInvokeSignature3() throws Exception { + runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt"); + } + @Test @TestMetadata("functionImportedFromObject.kt") public void testFunctionImportedFromObject() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java index c1f4f7fce41..f12831ecaed 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -1948,6 +1948,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature2.kt"); } + @Test + @TestMetadata("differentInvokeSignature3.kt") + public void testDifferentInvokeSignature3() throws Exception { + runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt"); + } + @Test @TestMetadata("functionImportedFromObject.kt") public void testFunctionImportedFromObject() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxInlineCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxInlineCodegenTestGenerated.java index e53208ff3d9..bed351acab3 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxInlineCodegenTestGenerated.java @@ -1948,6 +1948,12 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature2.kt"); } + @Test + @TestMetadata("differentInvokeSignature3.kt") + public void testDifferentInvokeSignature3() throws Exception { + runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt"); + } + @Test @TestMetadata("functionImportedFromObject.kt") public void testFunctionImportedFromObject() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrCompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrCompileKotlinAgainstInlineKotlinTestGenerated.java index dafddadcfd3..1cd27d2c6f3 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrCompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrCompileKotlinAgainstInlineKotlinTestGenerated.java @@ -1948,6 +1948,12 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature2.kt"); } + @Test + @TestMetadata("differentInvokeSignature3.kt") + public void testDifferentInvokeSignature3() throws Exception { + runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt"); + } + @Test @TestMetadata("functionImportedFromObject.kt") public void testFunctionImportedFromObject() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmIrAgainstOldBoxInlineTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmIrAgainstOldBoxInlineTestGenerated.java index 9a7fd74cba4..d75f4e6903e 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmIrAgainstOldBoxInlineTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmIrAgainstOldBoxInlineTestGenerated.java @@ -1948,6 +1948,12 @@ public class JvmIrAgainstOldBoxInlineTestGenerated extends AbstractJvmIrAgainstO runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature2.kt"); } + @Test + @TestMetadata("differentInvokeSignature3.kt") + public void testDifferentInvokeSignature3() throws Exception { + runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt"); + } + @Test @TestMetadata("functionImportedFromObject.kt") public void testFunctionImportedFromObject() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmOldAgainstIrBoxInlineTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmOldAgainstIrBoxInlineTestGenerated.java index 6f8fc4b31be..bc5dc17bfa1 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmOldAgainstIrBoxInlineTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/JvmOldAgainstIrBoxInlineTestGenerated.java @@ -1948,6 +1948,12 @@ public class JvmOldAgainstIrBoxInlineTestGenerated extends AbstractJvmOldAgainst runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature2.kt"); } + @Test + @TestMetadata("differentInvokeSignature3.kt") + public void testDifferentInvokeSignature3() throws Exception { + runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt"); + } + @Test @TestMetadata("functionImportedFromObject.kt") public void testFunctionImportedFromObject() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenInlineES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenInlineES6TestGenerated.java index 5b6afe29c2a..7d941344e53 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenInlineES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenInlineES6TestGenerated.java @@ -1540,6 +1540,11 @@ public class IrJsCodegenInlineES6TestGenerated extends AbstractIrJsCodegenInline runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature2.kt"); } + @TestMetadata("differentInvokeSignature3.kt") + public void testDifferentInvokeSignature3() throws Exception { + runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt"); + } + @TestMetadata("functionImportedFromObject.kt") public void testFunctionImportedFromObject() throws Exception { runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/functionImportedFromObject.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenInlineTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenInlineTestGenerated.java index a29c5d7e35a..944d1474d02 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenInlineTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenInlineTestGenerated.java @@ -1540,6 +1540,11 @@ public class IrJsCodegenInlineTestGenerated extends AbstractIrJsCodegenInlineTes runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature2.kt"); } + @TestMetadata("differentInvokeSignature3.kt") + public void testDifferentInvokeSignature3() throws Exception { + runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt"); + } + @TestMetadata("functionImportedFromObject.kt") public void testFunctionImportedFromObject() throws Exception { runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/functionImportedFromObject.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenInlineTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenInlineTestGenerated.java index b8f1a1cf431..f2773db60dc 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenInlineTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenInlineTestGenerated.java @@ -1540,6 +1540,11 @@ public class JsCodegenInlineTestGenerated extends AbstractJsCodegenInlineTest { runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature2.kt"); } + @TestMetadata("differentInvokeSignature3.kt") + public void testDifferentInvokeSignature3() throws Exception { + runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/differentInvokeSignature3.kt"); + } + @TestMetadata("functionImportedFromObject.kt") public void testFunctionImportedFromObject() throws Exception { runTest("compiler/testData/codegen/boxInline/defaultValues/lambdaInlining/callableReferences/functionImportedFromObject.kt");