Debugger: Fix function breakpoints for libraries (KT-36403)

This commit is contained in:
Yan Zhulanow
2020-02-06 21:52:08 +09:00
parent 18070303d5
commit 85a5e5241f
4 changed files with 67 additions and 15 deletions
@@ -13,7 +13,6 @@ import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.requests.RequestManagerImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.impl.DebuggerUtilsImpl;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.jdi.ClassesByNameProvider;
import com.intellij.debugger.jdi.MethodBytecodeUtil;
@@ -22,7 +21,6 @@ import com.intellij.debugger.ui.breakpoints.*;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.progress.*;
@@ -54,7 +52,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.java.debugger.breakpoints.properties.JavaMethodBreakpointProperties;
import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
import org.jetbrains.kotlin.asJava.classes.KtLightClass;
import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin;
import org.jetbrains.kotlin.asJava.elements.KtLightMethod;
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile;
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper;
import org.jetbrains.kotlin.idea.debugger.KotlinDebuggerCoreBundle;
import org.jetbrains.kotlin.psi.KtClass;
import org.jetbrains.kotlin.psi.KtClassOrObject;
@@ -543,20 +544,22 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
MethodDescriptor descriptor = DumbService.getInstance(project).runReadActionInSmartMode(() -> {
// MODIFICATION: Start Kotlin implementation
PsiMethod method = resolveJvmMethodFromKotlinDeclaration(project, sourcePosition);
// MODIFICATION: End Kotlin implementation
if (method == null) {
return null;
}
int methodOffset = method.getTextOffset();
if (!DocumentUtil.isValidOffset(methodOffset, document) || document.getLineNumber(methodOffset) < sourcePosition.getLine()) {
return null;
KtDeclaration kotlinOrigin = getSourceOrigin(method);
if (kotlinOrigin != null) {
int offset = kotlinOrigin.getTextOffset();
if (!DocumentUtil.isValidOffset(offset, document) || document.getLineNumber(offset) < sourcePosition.getLine()) {
return null;
}
}
PsiIdentifier identifier = method.getNameIdentifier();
int methodNameOffset = identifier != null? identifier.getTextOffset() : methodOffset;
MethodDescriptor res =
new MethodDescriptor();
MethodDescriptor res = new MethodDescriptor();
res.methodName = JVMNameUtil.getJVMMethodName(method);
try {
res.methodSignature = JVMNameUtil.getJVMSignature(method);
res.isStatic = method.hasModifierProperty(PsiModifier.STATIC);
@@ -564,8 +567,9 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
catch (IndexNotReadyException ignored) {
return null;
}
res.methodLine = document.getLineNumber(methodNameOffset);
return res;
// MODIFICATION: End Kotlin implementation
});
if (descriptor == null || descriptor.methodName == null || descriptor.methodSignature == null) {
return null;
@@ -574,6 +578,30 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
}
// MODIFICATION: Start Kotlin implementation
@Nullable
private static KtDeclaration getSourceOrigin(PsiMethod method) {
if (method instanceof KtLightMethod) {
KtLightMethod lightMethod = (KtLightMethod) method;
LightMemberOrigin lightMemberOrigin = lightMethod.getLightMemberOrigin();
if (lightMemberOrigin != null) {
KtDeclaration originalElement = lightMemberOrigin.getAuxiliaryOriginalElement();
if (originalElement == null) {
originalElement = lightMemberOrigin.getOriginalElement();
}
if (originalElement != null) {
KtDeclaration sourceOrigin = SourceNavigationHelper.INSTANCE.getNavigationElement(originalElement);
if (sourceOrigin.getContainingFile() instanceof KtClsFile) {
return null;
}
return sourceOrigin;
}
}
}
return null;
}
@Nullable
private static PsiMethod resolveJvmMethodFromKotlinDeclaration(@NotNull Project project, @NotNull SourcePosition sourcePosition) {
KtDeclaration declaration = PositionUtil.getPsiElementAt(project, KtDeclaration.class, sourcePosition);
@@ -583,7 +611,7 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
if (constructor != null) {
declaration = constructor;
} else {
KtLightClass lightClass = LightClassUtilsKt.toLightClass((KtClassOrObject) declaration);
PsiClass lightClass = LightClassUtilsKt.toLightClass((KtClassOrObject) declaration);
if (lightClass != null) {
PsiMethod[] constructors = lightClass.getConstructors();
if (constructors.length > 0) {
@@ -599,7 +627,8 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
return null;
}
for (PsiElement element : LightClassUtilsKt.toLightElements(declaration)) {
KtDeclaration originalDeclaration = SourceNavigationHelper.INSTANCE.getOriginalElement(declaration);
for (PsiElement element : LightClassUtilsKt.toLightElements(originalDeclaration)) {
if (element instanceof PsiMethod) {
// TODO handle all light methods
return (PsiMethod) element;
@@ -672,7 +701,6 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
String methodName;
JVMName methodSignature;
boolean isStatic;
int methodLine;
}
private static void processPreparedSubTypes(ReferenceType classType,
@@ -1053,6 +1053,11 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/custom/funLiteral.kt");
}
@TestMetadata("functionBreakpointInStdlib.kt")
public void testFunctionBreakpointInStdlib() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/custom/functionBreakpointInStdlib.kt");
}
@TestMetadata("functionBreakpoints.kt")
public void testFunctionBreakpoints() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/custom/functionBreakpoints.kt");
@@ -0,0 +1,11 @@
package test
import java.io.StringReader
fun main() {
val reader = StringReader("test")
reader.readText()
}
// ADDITIONAL_BREAKPOINT: ReadWrite.kt / public fun Reader.readText() / fun
// STEP_OVER: 1
@@ -0,0 +1,8 @@
FunctionBreakpoint created at ReadWrite.kt:105
Run Java
Connected to the target VM
ReadWrite.!EXT!
ReadWrite.!EXT!
Disconnected from the target VM
Process finished with exit code 0