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.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.requests.RequestManagerImpl; import com.intellij.debugger.engine.requests.RequestManagerImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.impl.DebuggerUtilsImpl;
import com.intellij.debugger.impl.PositionUtil; import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.jdi.ClassesByNameProvider; import com.intellij.debugger.jdi.ClassesByNameProvider;
import com.intellij.debugger.jdi.MethodBytecodeUtil; import com.intellij.debugger.jdi.MethodBytecodeUtil;
@@ -22,7 +21,6 @@ import com.intellij.debugger.ui.breakpoints.*;
import com.intellij.icons.AllIcons; import com.intellij.icons.AllIcons;
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Document;
import com.intellij.openapi.progress.*; import com.intellij.openapi.progress.*;
@@ -54,7 +52,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.java.debugger.breakpoints.properties.JavaMethodBreakpointProperties; import org.jetbrains.java.debugger.breakpoints.properties.JavaMethodBreakpointProperties;
import org.jetbrains.kotlin.asJava.LightClassUtilsKt; 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.idea.debugger.KotlinDebuggerCoreBundle;
import org.jetbrains.kotlin.psi.KtClass; import org.jetbrains.kotlin.psi.KtClass;
import org.jetbrains.kotlin.psi.KtClassOrObject; import org.jetbrains.kotlin.psi.KtClassOrObject;
@@ -543,20 +544,22 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
MethodDescriptor descriptor = DumbService.getInstance(project).runReadActionInSmartMode(() -> { MethodDescriptor descriptor = DumbService.getInstance(project).runReadActionInSmartMode(() -> {
// MODIFICATION: Start Kotlin implementation // MODIFICATION: Start Kotlin implementation
PsiMethod method = resolveJvmMethodFromKotlinDeclaration(project, sourcePosition); PsiMethod method = resolveJvmMethodFromKotlinDeclaration(project, sourcePosition);
// MODIFICATION: End Kotlin implementation
if (method == null) { if (method == null) {
return null; return null;
} }
int methodOffset = method.getTextOffset();
if (!DocumentUtil.isValidOffset(methodOffset, document) || document.getLineNumber(methodOffset) < sourcePosition.getLine()) { KtDeclaration kotlinOrigin = getSourceOrigin(method);
return null;
if (kotlinOrigin != null) {
int offset = kotlinOrigin.getTextOffset();
if (!DocumentUtil.isValidOffset(offset, document) || document.getLineNumber(offset) < sourcePosition.getLine()) {
return null;
}
} }
PsiIdentifier identifier = method.getNameIdentifier(); MethodDescriptor res = new MethodDescriptor();
int methodNameOffset = identifier != null? identifier.getTextOffset() : methodOffset;
MethodDescriptor res =
new MethodDescriptor();
res.methodName = JVMNameUtil.getJVMMethodName(method); res.methodName = JVMNameUtil.getJVMMethodName(method);
try { try {
res.methodSignature = JVMNameUtil.getJVMSignature(method); res.methodSignature = JVMNameUtil.getJVMSignature(method);
res.isStatic = method.hasModifierProperty(PsiModifier.STATIC); res.isStatic = method.hasModifierProperty(PsiModifier.STATIC);
@@ -564,8 +567,9 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
catch (IndexNotReadyException ignored) { catch (IndexNotReadyException ignored) {
return null; return null;
} }
res.methodLine = document.getLineNumber(methodNameOffset);
return res; return res;
// MODIFICATION: End Kotlin implementation
}); });
if (descriptor == null || descriptor.methodName == null || descriptor.methodSignature == null) { if (descriptor == null || descriptor.methodName == null || descriptor.methodSignature == null) {
return null; return null;
@@ -574,6 +578,30 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
} }
// MODIFICATION: Start Kotlin implementation // 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 @Nullable
private static PsiMethod resolveJvmMethodFromKotlinDeclaration(@NotNull Project project, @NotNull SourcePosition sourcePosition) { private static PsiMethod resolveJvmMethodFromKotlinDeclaration(@NotNull Project project, @NotNull SourcePosition sourcePosition) {
KtDeclaration declaration = PositionUtil.getPsiElementAt(project, KtDeclaration.class, sourcePosition); KtDeclaration declaration = PositionUtil.getPsiElementAt(project, KtDeclaration.class, sourcePosition);
@@ -583,7 +611,7 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
if (constructor != null) { if (constructor != null) {
declaration = constructor; declaration = constructor;
} else { } else {
KtLightClass lightClass = LightClassUtilsKt.toLightClass((KtClassOrObject) declaration); PsiClass lightClass = LightClassUtilsKt.toLightClass((KtClassOrObject) declaration);
if (lightClass != null) { if (lightClass != null) {
PsiMethod[] constructors = lightClass.getConstructors(); PsiMethod[] constructors = lightClass.getConstructors();
if (constructors.length > 0) { if (constructors.length > 0) {
@@ -599,7 +627,8 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
return null; return null;
} }
for (PsiElement element : LightClassUtilsKt.toLightElements(declaration)) { KtDeclaration originalDeclaration = SourceNavigationHelper.INSTANCE.getOriginalElement(declaration);
for (PsiElement element : LightClassUtilsKt.toLightElements(originalDeclaration)) {
if (element instanceof PsiMethod) { if (element instanceof PsiMethod) {
// TODO handle all light methods // TODO handle all light methods
return (PsiMethod) element; return (PsiMethod) element;
@@ -672,7 +701,6 @@ public class KotlinFunctionBreakpoint extends BreakpointWithHighlighter<JavaMeth
String methodName; String methodName;
JVMName methodSignature; JVMName methodSignature;
boolean isStatic; boolean isStatic;
int methodLine;
} }
private static void processPreparedSubTypes(ReferenceType classType, 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"); 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") @TestMetadata("functionBreakpoints.kt")
public void testFunctionBreakpoints() throws Exception { public void testFunctionBreakpoints() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/custom/functionBreakpoints.kt"); 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