diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index d58ff679268..aca1e37cb43 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -78,7 +78,6 @@ import static org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.isAccessor; import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION; import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION; import static org.jetbrains.kotlin.descriptors.ModalityKt.isOverridable; -import static org.jetbrains.kotlin.load.java.JvmAbi.LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER; import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor; import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.inline.InlineOnlyKt.isInlineOnlyPrivateInBytecode; @@ -592,7 +591,6 @@ public class FunctionCodegen { Label methodEnd; int functionFakeIndex = -1; - int lambdaFakeIndex = -1; if (context.getParentContext() instanceof MultifileClassFacadeContext) { generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext()); @@ -623,20 +621,8 @@ public class FunctionCodegen { ValueParameterDescriptor continuationValueDescriptor = view.getValueParameters().get(view.getValueParameters().size() - 1); frameMap.enter(continuationValueDescriptor, typeMapper.mapType(continuationValueDescriptor)); } - if (context.isInlineMethodContext()) { - functionFakeIndex = newFakeTempIndex(mv, frameMap, -1); - } - - if (context instanceof InlineLambdaContext) { - int lineNumber = -1; - if (strategy instanceof ClosureGenerationStrategy) { - KtDeclarationWithBody declaration = ((ClosureGenerationStrategy) strategy).getDeclaration(); - BindingContext bindingContext = typeMapper.getBindingContext(); - if (declaration instanceof KtFunctionLiteral && isLambdaPassedToInlineOnly((KtFunction) declaration, bindingContext)) { - lineNumber = LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER; - } - } - lambdaFakeIndex = newFakeTempIndex(mv, frameMap, lineNumber); + if (context.isInlineMethodContext() || context instanceof InlineLambdaContext) { + functionFakeIndex = newFakeTempIndex(mv, frameMap); } methodEntry = new Label(); @@ -665,7 +651,7 @@ public class FunctionCodegen { generateLocalVariableTable( mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind(), parentCodegen.state, - (functionFakeIndex >= 0 ? 1 : 0) + (lambdaFakeIndex >= 0 ? 1 : 0) + (functionFakeIndex >= 0 ? 1 : 0) ); //TODO: it's best to move all below logic to 'generateLocalVariableTable' method @@ -676,9 +662,7 @@ public class FunctionCodegen { Type.INT_TYPE.getDescriptor(), null, methodEntry, methodEnd, functionFakeIndex); - } - - if (context instanceof InlineLambdaContext && thisType != null && lambdaFakeIndex != -1) { + } else if (context instanceof InlineLambdaContext && thisType != null && functionFakeIndex != -1) { String internalName = thisType.getInternalName(); String lambdaLocalName = StringsKt.substringAfterLast(internalName, '/', internalName); @@ -696,7 +680,7 @@ public class FunctionCodegen { JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + "-" + functionName + "-" + lambdaLocalName, Type.INT_TYPE.getDescriptor(), null, methodEntry, methodEnd, - lambdaFakeIndex); + functionFakeIndex); } } @@ -714,13 +698,8 @@ public class FunctionCodegen { return false; } - private static int newFakeTempIndex(@NotNull MethodVisitor mv, @NotNull FrameMap frameMap, int lineNumber) { + private static int newFakeTempIndex(@NotNull MethodVisitor mv, @NotNull FrameMap frameMap) { int fakeIndex = frameMap.enterTemp(Type.INT_TYPE); - if (lineNumber >= 0) { - Label label = new Label(); - mv.visitLabel(label); - mv.visitLineNumber(lineNumber, label); - } mv.visitLdcInsn(0); mv.visitVarInsn(ISTORE, fakeIndex); return fakeIndex; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt index 4fb05b10762..6895a3a0c8a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -270,6 +270,7 @@ class MethodInliner( visitInsn(Opcodes.NOP) } + inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info) addInlineMarker(this, true) val lambdaParameters = info.addAllParameters(nodeRemapper) @@ -306,7 +307,7 @@ class MethodInliner( .put(OBJECT_TYPE, erasedInvokeFunction.returnType, this) setLambdaInlining(false) addInlineMarker(this, false) - inlineOnlySmapSkipper?.markCallSiteLineNumber(remappingMethodAdapter) + inlineOnlySmapSkipper?.onInlineLambdaEnd(remappingMethodAdapter) } else if (isAnonymousConstructorCall(owner, name)) { //TODO add method //TODO add proper message var info = transformationInfo as? AnonymousObjectTransformationInfo ?: throw AssertionError( 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 cc085e68f15..d86f0e085fc 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt @@ -536,10 +536,24 @@ fun isFakeLocalVariableForInline(name: String): Boolean { internal fun isThis0(name: String): Boolean = AsmUtil.CAPTURED_THIS_FIELD == name class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) { - private val callLineNumber = codegen.lastLineNumber - fun markCallSiteLineNumber(mv: MethodVisitor) { + fun onInlineLambdaStart(mv: MethodVisitor, info: LambdaInfo) { + val firstLine = info.node.node.instructions.asSequence().mapNotNull { it as? LineNumberNode }.firstOrNull()?.line ?: -1 + if (callLineNumber >= 0 && firstLine == callLineNumber) { + // We want the debugger to be able to break both on the inline call itself, plus on each + // invocation of the inline lambda passed to it. For that to happen there needs to be at least + // one different line number in between those breakpoints for the VM to emit a locatable event. + // @InlineOnly functions, however, contain no line numbers, so if the lambda is single-line, + // the entire call will "meld" into a single region. To break it up, we insert a different line + // number that is remapped by the SMAP to a line that does not exist. + val label = Label() + mv.visitLabel(label) + mv.visitLineNumber(JvmAbi.LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER, label) + } + } + + fun onInlineLambdaEnd(mv: MethodVisitor) { if (callLineNumber >= 0) { val label = Label() mv.visitLabel(label) diff --git a/compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnly.kt b/compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnly.kt new file mode 100644 index 00000000000..6084e5aaf54 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnly.kt @@ -0,0 +1,15 @@ +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@kotlin.internal.InlineOnly +inline fun T.myLet(block: (T) -> R) = block(this) + +fun box(): String { + val k = "".myLet { + it + "K" + } + return "O".myLet(fun (it: String): String { + return it + k + }) +} + +// See KT-23064 for the problem and InlineOnlySmapSkipper for an explanation. +// 0 LINENUMBER 65100 diff --git a/compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnlyOneLine.kt b/compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnlyOneLine.kt new file mode 100644 index 00000000000..35358c61461 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnlyOneLine.kt @@ -0,0 +1,11 @@ +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@kotlin.internal.InlineOnly +inline fun T.myLet(block: (T) -> R) = block(this) + +fun box(): String { + val k = "".myLet { it + "K" } + return "O".myLet(fun (it: String): String { return it + k }) +} + +// See KT-23064 for the problem and InlineOnlySmapSkipper for an explanation. +// 2 LINENUMBER 65100 diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 50bc0f19cd3..a95d33f7126 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -3386,6 +3386,16 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt"); } + @TestMetadata("stdlibInlineOnly.kt") + public void testStdlibInlineOnly() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnly.kt"); + } + + @TestMetadata("stdlibInlineOnlyOneLine.kt") + public void testStdlibInlineOnlyOneLine() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnlyOneLine.kt"); + } + @TestMetadata("tryCatch.kt") public void testTryCatch() throws Exception { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index a45082c8037..46ebd2b4c94 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -3431,6 +3431,16 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt"); } + @TestMetadata("stdlibInlineOnly.kt") + public void testStdlibInlineOnly() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnly.kt"); + } + + @TestMetadata("stdlibInlineOnlyOneLine.kt") + public void testStdlibInlineOnlyOneLine() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnlyOneLine.kt"); + } + @TestMetadata("tryCatch.kt") public void testTryCatch() throws Exception { runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");