diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index a2e58cc8174..44e9564615f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.resolve.constants.ArrayValue; import org.jetbrains.kotlin.resolve.constants.ConstantValue; import org.jetbrains.kotlin.resolve.constants.KClassValue; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; +import org.jetbrains.kotlin.resolve.inline.InlineOnlyKt; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.kotlin.resolve.jvm.RuntimeAssertionInfo; @@ -76,6 +77,7 @@ 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.isEffectivelyInlineOnly; @@ -636,11 +638,19 @@ public class FunctionCodegen { frameMap.enter(continuationValueDescriptor, typeMapper.mapType(continuationValueDescriptor)); } if (context.isInlineMethodContext()) { - functionFakeIndex = newFakeTempIndex(mv, frameMap); + functionFakeIndex = newFakeTempIndex(mv, frameMap, -1); } if (context instanceof InlineLambdaContext) { - lambdaFakeIndex = newFakeTempIndex(mv, frameMap); + 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); } methodEntry = new Label(); @@ -704,8 +714,27 @@ public class FunctionCodegen { } } - private static int newFakeTempIndex(@NotNull MethodVisitor mv, FrameMap frameMap) { + private static boolean isLambdaPassedToInlineOnly(KtFunction lambda, BindingContext bindingContext) { + ValueParameterDescriptor parameterDescriptor = InlineUtil.getInlineArgumentDescriptor(lambda, bindingContext); + if (parameterDescriptor == null) { + return false; + } + + CallableDescriptor containingCallable = parameterDescriptor.getContainingDeclaration(); + if (containingCallable instanceof FunctionDescriptor) { + return InlineOnlyKt.isInlineOnly((MemberDescriptor) containingCallable); + } + + return false; + } + + private static int newFakeTempIndex(@NotNull MethodVisitor mv, @NotNull FrameMap frameMap, int lineNumber) { 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/SMAP.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt index a2cfd08bdc0..17fee0b9de7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAP.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.codegen.inline.SMAP.Companion.END import org.jetbrains.kotlin.codegen.inline.SMAP.Companion.FILE_SECTION import org.jetbrains.kotlin.codegen.inline.SMAP.Companion.LINE_SECTION import org.jetbrains.kotlin.codegen.inline.SMAP.Companion.STRATA_SECTION +import org.jetbrains.kotlin.load.java.JvmAbi import java.util.* import kotlin.math.max @@ -92,6 +93,10 @@ open class NestedSourceMapper( private var lastVisitedRange: RangeMapping? = null override fun mapLineNumber(lineNumber: Int): Int { + if (lineNumber in JvmAbi.SYNTHETIC_MARKER_LINE_NUMBERS) { + return lineNumber + } + val mappedLineNumber = visitedLines.get(lineNumber) return if (mappedLineNumber > 0) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPAndMethodNode.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPAndMethodNode.kt index 55834a9131d..51afe04635a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPAndMethodNode.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPAndMethodNode.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen.inline import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence +import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.org.objectweb.asm.tree.LineNumberNode import org.jetbrains.org.objectweb.asm.tree.MethodNode import java.util.* @@ -30,7 +31,13 @@ class SMAPAndMethodNode(val node: MethodNode, val classSMAP: SMAP) { private fun createLineNumberSequence(node: MethodNode, classSMAP: SMAP): Sequence { return InsnSequence(node.instructions.first, null).filterIsInstance().map { lineNumber -> - val index = classSMAP.intervals.binarySearch(RangeMapping(lineNumber.line, lineNumber.line, 1), Comparator { value, key -> + val mapping = RangeMapping(lineNumber.line, lineNumber.line, 1) + + if (lineNumber.line in JvmAbi.SYNTHETIC_MARKER_LINE_NUMBERS) { + return@map mapping + } + + val index = classSMAP.intervals.binarySearch(mapping, Comparator { value, key -> if (key.dest in value) 0 else RangeMapping.Comparator.compare(value, key) }) if (index < 0) { diff --git a/compiler/testData/lineNumber/custom/noParametersArgumentCallInExpression.kt b/compiler/testData/lineNumber/custom/noParametersArgumentCallInExpression.kt index 46a34bcae2d..efb008da3ba 100644 --- a/compiler/testData/lineNumber/custom/noParametersArgumentCallInExpression.kt +++ b/compiler/testData/lineNumber/custom/noParametersArgumentCallInExpression.kt @@ -8,5 +8,5 @@ inline fun lookAtMe(f: () -> Int) { val a = 21 a + f() } - +// IGNORE_BACKEND: JVM_IR // 2 13 14 3 15 5 8 9 10 \ No newline at end of file diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.java b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.java index f4605974be5..fead436aeb6 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.java +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.java @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.load.java; +import kotlin.collections.CollectionsKt; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.builtins.CompanionObjectMapping; import org.jetbrains.kotlin.descriptors.*; @@ -13,6 +14,8 @@ import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.util.capitalizeDecapitalize.CapitalizeDecapitalizeKt; +import java.util.List; + import static org.jetbrains.kotlin.resolve.DescriptorUtils.isClassOrEnumClass; import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject; @@ -45,6 +48,20 @@ public final class JvmAbi { public static final ClassId REFLECTION_FACTORY_IMPL = ClassId.topLevel(new FqName("kotlin.reflect.jvm.internal.ReflectionFactoryImpl")); + /* + Line number is used when inlining function arguments to @InlineOnly functions. + All line information is stripped from the inlined implementation of such functions, + so if a lambda is on the same line with the call, the debugger can't stop on both 'line' and 'lambda' breakpoints. + + Example: + > require(foo) { "foo is false" } + */ + public static final int LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER = 65100; + + public static final List SYNTHETIC_MARKER_LINE_NUMBERS = CollectionsKt.listOf( + LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER + ); + public static final String LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT = "$i$a$"; public static final String LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION = "$i$f$";