Debugger: Disallow breakpoints for @InlineOnly function bodies (KT-32687, KT-24408)

Kotlin compiler strips all debug information for @InlineOnly functions, making them non-debuggable.
This commit disables breakpoints inside @InlineOnly functions to prevent false expectations.
This commit is contained in:
Yan Zhulanow
2019-07-16 18:20:37 +09:00
parent 25fb77e7ad
commit 780c0518e5
7 changed files with 97 additions and 4 deletions
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
private val INLINE_ONLY_ANNOTATION_FQ_NAME = FqName("kotlin.internal.InlineOnly")
val INLINE_ONLY_ANNOTATION_FQ_NAME = FqName("kotlin.internal.InlineOnly")
/**
* @return true if it's impossible to observe a call instruction referencing this member in the bytecode.
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.resolve.inline.INLINE_ONLY_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
@@ -315,8 +316,6 @@ private tailrec fun isInlineOrContainedInInline(declaration: IrDeclaration?): Bo
/* Borrowed from inlineOnly.kt */
private val INLINE_ONLY_ANNOTATION_FQ_NAME = FqName("kotlin.internal.InlineOnly")
fun IrDeclarationWithVisibility.isInlineOnlyOrReifiable(): Boolean =
this is IrFunction && (isReifiable() || isInlineOnly())
@@ -153,7 +153,11 @@ public class KotlinFunctionBreakpointType
if (element instanceof KtFunction) {
KtFunction function = (KtFunction) element;
return ApplicabilityResult.maybe(function.hasBody() && !KtPsiUtil.isLocal(function));
return ApplicabilityResult.maybe(
function.hasBody()
&& !KtPsiUtil.isLocal(function)
&& !BreakpointTypeUtilsKt.isInlineOnly(function)
);
}
if (element instanceof KtPropertyAccessor) {
@@ -135,6 +135,14 @@ public class KotlinLineBreakpointType extends JavaLineBreakpointType implements
return ApplicabilityResult.MAYBE_YES;
}
PsiElement containingMethod = getContainingMethod(element);
if (containingMethod instanceof KtCallableDeclaration) {
KtCallableDeclaration callable = (KtCallableDeclaration) containingMethod;
if (BreakpointTypeUtilsKt.isInlineOnly(callable)) {
return ApplicabilityResult.DEFINITELY_NO;
}
}
if (isClosingBraceInMethod(element)) {
return ApplicabilityResult.MAYBE_YES;
}
@@ -31,11 +31,17 @@ import com.intellij.xdebugger.XDebuggerUtil
import com.intellij.xdebugger.XSourcePosition
import com.intellij.xdebugger.impl.XSourcePositionImpl
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
import org.jetbrains.kotlin.idea.core.util.getLineNumber
import org.jetbrains.kotlin.idea.debugger.findElementAtLine
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.inline.INLINE_ONLY_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import java.util.*
@@ -55,6 +61,9 @@ class ApplicabilityResult(val isApplicable: Boolean, val shouldStop: Boolean) {
@JvmField
val DEFINITELY_YES = ApplicabilityResult(isApplicable = true, shouldStop = true)
@JvmField
val DEFINITELY_NO = ApplicabilityResult(isApplicable = false, shouldStop = true)
@JvmField
val MAYBE_YES = ApplicabilityResult(isApplicable = true, shouldStop = false)
}
@@ -181,3 +190,18 @@ fun getLambdasAtLineIfAny(file: KtFile, line: Int): List<KtFunction> {
}
}
internal fun KtCallableDeclaration.isInlineOnly(): Boolean {
if (!hasModifier(KtTokens.INLINE_KEYWORD)) {
return false
}
val inlineOnlyAnnotation = annotationEntries
.firstOrNull { it.shortName == INLINE_ONLY_ANNOTATION_FQ_NAME.shortName() }
?: return false
return runReadAction f@{
val bindingContext = inlineOnlyAnnotation.analyze(BodyResolveMode.PARTIAL)
val annotationDescriptor = bindingContext[BindingContext.ANNOTATION, inlineOnlyAnnotation] ?: return@f false
return@f annotationDescriptor.fqName == INLINE_ONLY_ANNOTATION_FQ_NAME
}
}
@@ -0,0 +1,53 @@
fun foo() {
require(true) { "foo" } /// *, L, λ
require(true) { val a = 5 } /// *, L, λ
require(true) { /// L
val a = 5 /// L
} /// L
block { val a = 5 } /// *, L, λ
block { /// L
val a = 5 /// L
} /// L
inlineBlock { val a = 5} /// *, L, λ
inlineBlock { /// L
val a = 5 /// L
} /// L
inlineOnlyBlock { val a = 5 } /// *, L, λ
inlineOnlyBlock { /// L
val a = 5 /// L
} /// L
inlineOnlyBlock2 { val a = 5 } /// *, L, λ
inlineOnlyBlock2 { /// L
val a = 5 /// L
} /// L
} /// L
private fun block(block: () -> Unit) { /// M
block() /// L
} /// L
private inline fun inlineBlock(block: () -> Unit) { /// M
block() /// L
} /// L
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.InlineOnly
private inline fun inlineOnlyBlock(block: () -> Unit) {
block()
}
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.InlineOnly
private inline fun inlineOnlyBlock2(noinline block: () -> Unit) {
block()
}
@@ -39,6 +39,11 @@ public class BreakpointApplicabilityTestGenerated extends AbstractBreakpointAppl
runTest("idea/testData/debugger/breakpointApplicability/functions.kt");
}
@TestMetadata("inlineOnly.kt")
public void testInlineOnly() throws Exception {
runTest("idea/testData/debugger/breakpointApplicability/inlineOnly.kt");
}
@TestMetadata("locals.kt")
public void testLocals() throws Exception {
runTest("idea/testData/debugger/breakpointApplicability/locals.kt");