Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGayan Perera2021-07-08 17:40:19 +0000
committerSarika Sinha2021-08-04 13:56:34 +0000
commit1f63b07950714f66b0d7f72abd1b5493cebc56e2 (patch)
tree51b26cde12afd73720c7796bacad92e7891e276e
parentfa6774a8f07b7c40adce5fb42e083ec9ad862b67 (diff)
downloadeclipse.jdt.debug-1f63b07950714f66b0d7f72abd1b5493cebc56e2.tar.gz
eclipse.jdt.debug-1f63b07950714f66b0d7f72abd1b5493cebc56e2.tar.xz
eclipse.jdt.debug-1f63b07950714f66b0d7f72abd1b5493cebc56e2.zip
Bug 573547 - Add support to hover on outer scope var-chains in lambdaI20210805-1800I20210804-1800
The fix tries to find the first frame which contains the current hovering variable and feed that frame for all different mechanisms of variable evaluations base on where we are hovering. Change-Id: I7f158ab71d0236f32de5eb686aa6078f58ea233c Signed-off-by: Gayan Perera <gayanper@gmail.com> Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.debug/+/182911 Tested-by: JDT Bot <jdt-bot@eclipse.org> Tested-by: Sarika Sinha <sarika.sinha@in.ibm.com> Reviewed-by: Sarika Sinha <sarika.sinha@in.ibm.com>
-rw-r--r--org.eclipse.jdt.debug.tests/java8/Bug573547.java63
-rw-r--r--org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java1
-rw-r--r--org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DebugHoverTests.java449
-rw-r--r--org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugHover.java91
4 files changed, 591 insertions, 13 deletions
diff --git a/org.eclipse.jdt.debug.tests/java8/Bug573547.java b/org.eclipse.jdt.debug.tests/java8/Bug573547.java
new file mode 100644
index 000000000..806dc9a3f
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/java8/Bug573547.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Gayan Perera and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Gayan Perera - initial API and implementation
+ *******************************************************************************/
+
+import java.util.stream.Stream;
+
+public class Bug573547 {
+ private String payload;
+
+ private String[] payloads;
+
+ private static String[] PAYLOADS = new String[] {"1"};
+
+ public Bug573547(String payload) {
+ this.payload = payload;
+ this.payloads = new String[]{payload};
+ }
+
+ public void hoverOverLocal(String[] names) {
+ char[] name = new char[] {'n', 'a', 'm', 'e'};
+ Bug573547 object = new Bug573547("p");
+
+ System.out.println(name.length);
+ System.out.println(object.payload);
+ System.out.println(names.length);
+ /*Root*/System.out.println(object.payloads.length);
+ System.out.println(this.payloads.length);
+ System.out.println(payloads.length);
+
+ Stream.of(name).forEach(a -> {
+ System.out.println(a.length);
+ });
+ nestedHover();
+ }
+
+ public void nestedHover() {
+ String object = "1234";
+ /*Nested1*/System.out.println(object);
+ (new Nest()).nestedHover();
+ }
+ public static void main(String[] args) {
+ new Bug573547("p").hoverOverLocal(new String[] {"name"});
+ }
+
+ private class Nest {
+ /*Nested2*/private String payload = "np";
+
+ public void nestedHover() {
+ String object = "1234n";
+ /*Nested2*/System.out.println(object);
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
index 685087aad..b84734ccf 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
@@ -498,6 +498,7 @@ public abstract class AbstractDebugTest extends TestCase implements IEvaluation
cfgs.add(createLaunchConfiguration(jp, "Bug573589"));
cfgs.add(createLaunchConfiguration(jp, "Bug574395"));
cfgs.add(createLaunchConfiguration(jp, "Bug571310"));
+ cfgs.add(createLaunchConfiguration(jp, "Bug573547"));
loaded18 = true;
waitForBuild();
}
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DebugHoverTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DebugHoverTests.java
index f21e89cc3..50f724854 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DebugHoverTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DebugHoverTests.java
@@ -25,6 +25,8 @@ import org.eclipse.debug.core.model.ILineBreakpoint;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.internal.ui.views.console.ProcessConsole;
+import org.eclipse.debug.internal.ui.views.launch.LaunchView;
+import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jdi.internal.StringReferenceImpl;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.debug.core.IJavaArray;
@@ -43,6 +45,8 @@ import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Region;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.test.OrderedTestSuite;
import junit.framework.Test;
@@ -954,6 +958,434 @@ public class DebugHoverTests extends AbstractDebugUiTests {
}
}
+ public void testBug573547_insideLambda_onOuterScopeLocalVariableChain() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+
+ final String typeName = "Bug573547";
+ final String expectedMethod = "lambda$0";
+ final int frameNumber = 6;
+ final int bpLine = 41;
+ final int hoverLine = 34;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ String variableName = "payload";
+ int offset = part.getViewer().getDocument().get().indexOf("System.out.println(object.payload")
+ + "System.out.println(object.".length();
+ IRegion region = new Region(offset, "payload".length());
+ String text = selectAndReveal(part, hoverLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("object.payload", info.getName());
+ assertEquals("p", info.getValue().getValueString());
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug573547_insideLambda_onOuterScopeMemberVariable() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+ final String typeName = "Bug573547";
+ final String expectedMethod = "lambda$0";
+ final int frameNumber = 6;
+ final int bpLine = 41;
+ final int hoverLine = 38;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ String variableName = "payloads";
+ int offset = part.getViewer().getDocument().get().lastIndexOf("System.out.println(payloads") + "System.out.println(".length();
+ IRegion region = new Region(offset, "payloads".length());
+ String text = selectAndReveal(part, hoverLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("payloads", info.getName());
+ assertTrue("Not an array variable", info.getValue() instanceof IJavaArray);
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug573547_insideLambda_onOuterScopeMemberVariable_withThis() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+ final String typeName = "Bug573547";
+ final String expectedMethod = "lambda$0";
+ final int frameNumber = 6;
+ final int bpLine = 41;
+ final int hoverLine = 37;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ String variableName = "payloads";
+ int offset = part.getViewer().getDocument().get().lastIndexOf("System.out.println(this.payloads") + "System.out.println(this.".length();
+ IRegion region = new Region(offset, "payloads".length());
+ String text = selectAndReveal(part, hoverLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("payloads", info.getName());
+ assertTrue("Not an array variable", info.getValue() instanceof IJavaArray);
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug573547_insideLambda_onOuterScopeStaticVariable() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+ final String typeName = "Bug573547";
+ final String expectedMethod = "lambda$0";
+ final int frameNumber = 6;
+ final int bpLine = 41;
+ final int hoverLine = 22;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ String variableName = "PAYLOADS";
+ int offset = part.getViewer().getDocument().get().lastIndexOf("private static String[] PAYLOADS") + "private static String[] ".length();
+ IRegion region = new Region(offset, "payloads".length());
+ String text = selectAndReveal(part, hoverLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("PAYLOADS", info.getName());
+ assertTrue("Not an array variable", info.getValue() instanceof IJavaArray);
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug573547_insideLambda_onOuterScopeMemberVariable_onLength() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+ final String typeName = "Bug573547";
+ final String expectedMethod = "lambda$0";
+ final int frameNumber = 6;
+ final int bpLine = 41;
+ final int hoverLine = 38;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ String variableName = "length";
+ int offset = part.getViewer().getDocument().get().lastIndexOf("System.out.println(payloads.length")
+ + "System.out.println(payloads.".length();
+ IRegion region = new Region(offset, "length".length());
+ String text = selectAndReveal(part, hoverLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("payloads.length", info.getName());
+ assertEquals("1", info.getValue().getValueString());
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug573547_insideLambda_onOuterScopeMemberVariable_withThis_onLength() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+ final String typeName = "Bug573547";
+ final String expectedMethod = "lambda$0";
+ final int frameNumber = 6;
+ final int bpLine = 41;
+ final int hoverLine = 37;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ String variableName = "length";
+ int offset = part.getViewer().getDocument().get().lastIndexOf("System.out.println(this.payloads.length")
+ + "System.out.println(this.payloads.".length();
+ IRegion region = new Region(offset, "length".length());
+ String text = selectAndReveal(part, hoverLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("this.payloads.length", info.getName());
+ assertEquals("1", info.getValue().getValueString());
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug573547_insideLambda_onOuterScopeLocalVariable_onLength() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+ final String typeName = "Bug573547";
+ final String expectedMethod = "lambda$0";
+ final int frameNumber = 6;
+ final int bpLine = 41;
+ final int hoverLine = 36;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ String variableName = "length";
+ int offset = part.getViewer().getDocument().get().lastIndexOf("System.out.println(object.payloads.length")
+ + "System.out.println(object.payloads.".length();
+ IRegion region = new Region(offset, "length".length());
+ String text = selectAndReveal(part, hoverLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("object.payloads.length", info.getName());
+ assertEquals("1", info.getValue().getValueString());
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug573547_insideLambda_onOuterScopeVariable_whileOnPreviousFrame() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+ final String typeName = "Bug573547";
+ final String expectedMethod = "lambda$0";
+ final int frameNumber = 6;
+ final int bpLine = 41;
+ final int hoverLine = 36;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+
+ selectFrame(thread.getStackFrames()[4]);
+
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ String variableName = "object";
+ int offset = part.getViewer().getDocument().get().lastIndexOf("/*Root*/System.out.println(object")
+ + "/*Root*/System.out.println(".length();
+ IRegion region = new Region(offset, "object".length());
+ String text = selectAndReveal(part, hoverLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("object", info.getName());
+ assertEquals("Bug573547", info.getValue().getReferenceTypeName());
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug573547_inNestedMethodInvocation_useCorrectFrameForSelectedVariable() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+ final String typeName = "Bug573547";
+ final String expectedMethod = "nestedHover";
+ final int frameNumber = 3;
+ final int bpLine = 48;
+ final int hoverLine = 48;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ String variableName = "object";
+ int offset = part.getViewer().getDocument().get().lastIndexOf("/*Nested1*/System.out.println(object")
+ + "/*Nested1*/System.out.println(".length();
+ IRegion region = new Region(offset, "object".length());
+ String text = selectAndReveal(part, hoverLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("object", info.getName());
+ assertEquals("1234", info.getValue().getValueString());
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug573547_onVariableOutOfExecutionStack_expectNoHoverInfo() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+ final String typeName = "Bug573547";
+ final String expectedMethod = "nestedHover";
+ final int frameNumber = 3;
+ final int bpLine = 48;
+ final int hoverLine = 60;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ String variableName = "object";
+ int offset = part.getViewer().getDocument().get().lastIndexOf("/*Nested2*/System.out.println(object")
+ + "/*Nested2*/System.out.println(".length();
+ IRegion region = new Region(offset, "object".length());
+ String text = selectAndReveal(part, hoverLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNull(info);
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug573547_inNestedMethodInvocation_inNestedClasses_useCorrectFrameForSelectedVariables() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+ final String typeName = "Bug573547";
+ final String expectedMethod = "nestedHover";
+ final int frameNumber = 4;
+ final int bpLine = 60;
+ final int hoverLineVar = 60;
+ final int hoverLineField = 56;
+
+ IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
+ bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod, frameNumber, file, thread);
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ // local variable
+ String variableNameVar = "object";
+ int offsetVar = part.getViewer().getDocument().get().lastIndexOf("/*Nested2*/System.out.println(object")
+ + "/*Nested1*/System.out.println(".length();
+ IRegion regionVar = new Region(offsetVar, "object".length());
+ String textVar = selectAndReveal(part, hoverLineVar, regionVar);
+ assertEquals(variableNameVar, textVar);
+ IVariable infoVar = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), regionVar));
+
+ assertNotNull(infoVar);
+ assertEquals("object", infoVar.getName());
+ assertEquals("1234n", infoVar.getValue().getValueString());
+
+ // field
+ String variableNameField = "payload";
+ int offsetField = part.getViewer().getDocument().get().lastIndexOf("/*Nested2*/private String payload")
+ + "/*Nested2*/private String ".length();
+ IRegion regionField = new Region(offsetField, "payload".length());
+ String textField = selectAndReveal(part, hoverLineField, regionField);
+ assertEquals(variableNameField, textField);
+ IVariable infoField = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), regionField));
+
+ assertNotNull(infoField);
+ assertEquals("payload", infoField.getName());
+ assertEquals("np", infoField.getValue().getValueString());
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
private CompilationUnitEditor openEditorAndValidateStack(final String expectedMethod, final int expectedFramesNumber, IFile file, IJavaThread thread) throws Exception, DebugException {
// Let now all pending jobs proceed, ignore console jobs
sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
@@ -1021,6 +1453,23 @@ public class DebugHoverTests extends AbstractDebugUiTests {
return sync(() -> selection.getText());
}
+ private void selectFrame(IStackFrame frame) throws Exception {
+ LaunchView debugView = sync(() -> (LaunchView) getActivePage().findView(IDebugUIConstants.ID_DEBUG_VIEW));
+ assertNotNull("expected Debug View to be open", debugView);
+
+ TreeSelection selection = sync(() -> (TreeSelection) debugView.getViewer().getSelection());
+ TreePath path = selection.getPaths()[0];
+ TreePath newPath = path.getParentPath().createChildPath(frame);
+ TreeSelection newSelection = new TreeSelection(newPath);
+ sync(() -> debugView.getViewer().setSelection(newSelection, true));
+ processUiEvents(100);
+ }
+
+ @Override
+ protected boolean enableUIEventLoopProcessingInWaiter() {
+ return true;
+ }
+
public void testResolveIn2Lambdas() throws Exception {
sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugHover.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugHover.java
index 32ccf1613..3a1b53032 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugHover.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaDebugHover.java
@@ -19,11 +19,13 @@ import java.util.Optional;
import java.util.StringJoiner;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
+import java.util.function.Predicate;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
@@ -63,6 +65,7 @@ import org.eclipse.jdt.debug.eval.IEvaluationListener;
import org.eclipse.jdt.debug.eval.IEvaluationResult;
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
import org.eclipse.jdt.internal.debug.core.logicalstructures.JDIPlaceholderVariable;
+import org.eclipse.jdt.internal.debug.core.model.JDIThisVariable;
import org.eclipse.jdt.internal.debug.eval.ast.engine.ASTEvaluationEngine;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.text.java.hover.IJavaEditorTextHover;
@@ -80,7 +83,8 @@ import org.eclipse.ui.IEditorPart;
public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension, ITextHoverExtension2 {
- private IEditorPart fEditor;
+ private static final String THIS = "this"; //$NON-NLS-1$
+ private IEditorPart fEditor;
/* (non-Javadoc)
* @see org.eclipse.jdt.ui.text.java.hover.IJavaEditorTextHover#setEditor(org.eclipse.ui.IEditorPart)
@@ -267,7 +271,7 @@ public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension
if (document != null) {
try {
String variableName= document.get(hoverRegion.getOffset(), hoverRegion.getLength());
- if (variableName.equals("this")) { //$NON-NLS-1$
+ if (variableName.equals(THIS)) {
try {
IJavaVariable variable = frame.findVariable(variableName);
if (variable != null) {
@@ -373,15 +377,15 @@ public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension
StructuralPropertyDescriptor locationInParent = node.getLocationInParent();
if (locationInParent == FieldAccess.NAME_PROPERTY) {
FieldAccess fieldAccess = (FieldAccess) node.getParent();
- if (fieldAccess.getExpression() instanceof ThisExpression) {
- variable = evaluateField(frame, field);
+ if (fieldAccess.getExpression() instanceof ThisExpression && !onArrayLength) {
+ variable = evaluateField(findFirstFrameForVariable(frame, forField(field)), field);
} else {
- variable = evaluateQualifiedNode(fieldAccess, frame, typeRoot.getJavaProject());
+ variable = evaluateQualifiedNode(fieldAccess, frame, typeRoot.getJavaProject(), forField(field));
}
} else if (locationInParent == QualifiedName.NAME_PROPERTY) {
- variable = evaluateQualifiedNode(node.getParent(), frame, typeRoot.getJavaProject());
+ variable = evaluateQualifiedNode(node.getParent(), frame, typeRoot.getJavaProject(), forField(field));
} else {
- variable = evaluateField(frame, field);
+ variable = evaluateField(findFirstFrameForVariable(frame, forField(field)), field);
}
}
}
@@ -391,6 +395,7 @@ public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension
break;
}
if (javaElement instanceof ILocalVariable) {
+ ILocalVariable var = (ILocalVariable) javaElement;
// if we are on a array, regardless where we are send it to evaluation engine
if (onArrayLength) {
if (!(codeAssist instanceof ITypeRoot)) {
@@ -401,10 +406,9 @@ public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension
if (node == null) {
return null;
}
- return evaluateQualifiedNode(node.getParent(), frame, typeRoot.getJavaProject());
+ return evaluateQualifiedNode(node.getParent(), frame, typeRoot.getJavaProject(), forLocalVariable(var));
}
- ILocalVariable var = (ILocalVariable)javaElement;
IJavaElement parent = var.getParent();
while (!(parent instanceof IMethod) && !(parent instanceof IInitializer) && parent != null) {
parent = parent.getParent();
@@ -462,7 +466,7 @@ public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension
}
// find variable if equal or method is a Lambda Method
if (equal || method.isLambdaMethod()) {
- return findLocalVariable(frame, var.getElementName());
+ return findLocalVariable(findFirstFrameForVariable(frame, forLocalVariable(var)), var.getElementName());
}
}
break;
@@ -530,7 +534,7 @@ public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension
return null;
}
- private IJavaVariable evaluateQualifiedNode(ASTNode node, IJavaStackFrame frame, IJavaProject project) {
+ private IJavaVariable evaluateQualifiedNode(ASTNode node, IJavaStackFrame frame, IJavaProject project, Predicate<IJavaStackFrame> framePredicate) {
StringBuilder snippetBuilder = new StringBuilder();
if (node instanceof QualifiedName) {
snippetBuilder.append(((QualifiedName) node).getFullyQualifiedName());
@@ -545,7 +549,7 @@ public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension
@Override
public boolean visit(ThisExpression node) {
- segments.add("this"); //$NON-NLS-1$
+ segments.add(THIS);
return true;
}
@@ -556,6 +560,7 @@ public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension
}
final String snippet = snippetBuilder.toString();
+
class Evaluator implements IEvaluationListener {
private CompletableFuture<IEvaluationResult> result = new CompletableFuture<>();
@@ -566,7 +571,7 @@ public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension
public void run() throws DebugException {
IAstEvaluationEngine engine = JDIDebugPlugin.getDefault().getEvaluationEngine(project, (IJavaDebugTarget) frame.getDebugTarget());
- engine.evaluate(snippet, frame, this, DebugEvent.EVALUATION_IMPLICIT, false);
+ engine.evaluate(snippet, findFirstFrameForVariable(frame, framePredicate), this, DebugEvent.EVALUATION_IMPLICIT, false);
}
public Optional<IEvaluationResult> getResult() {
@@ -593,4 +598,64 @@ public class JavaDebugHover implements IJavaEditorTextHover, ITextHoverExtension
public IInformationControlCreator getInformationPresenterControlCreator() {
return new ExpressionInformationControlCreator();
}
+
+ private static IJavaStackFrame findFirstFrameForVariable(IJavaStackFrame currentFrame, Predicate<IJavaStackFrame> framePredicate) throws DebugException {
+ // check the current frame first
+ if (framePredicate.test(currentFrame)) {
+ return currentFrame;
+ }
+
+ for (IStackFrame stackFrame : currentFrame.getThread().getStackFrames()) {
+ IJavaStackFrame javaStackFrame = (IJavaStackFrame) stackFrame;
+ if (currentFrame != javaStackFrame && framePredicate.test(javaStackFrame)) {
+ return javaStackFrame;
+ }
+ }
+
+ // we couldn't find a frame, so return the current frame, this is highly unlikely we endup here.
+ return currentFrame;
+ }
+
+ private static boolean containsVariable(IStackFrame frame, String variableName) throws DebugException {
+ for (IVariable variable : frame.getVariables()) {
+ if (variable instanceof JDIThisVariable) {
+ for (IVariable fieldVar : variable.getValue().getVariables()) {
+ if (variableName.equals(fieldVar.getName())) {
+ return true;
+ }
+ }
+ } else if (variableName.equals(variable.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // the following two predicates will make sure to find correct frame according the java element's enclosing parent.
+ private static Predicate<IJavaStackFrame> forLocalVariable(ILocalVariable variable) {
+ return frame -> {
+ try {
+ return variable.getDeclaringMember() != null && variable.getDeclaringMember().getElementName().equals(frame.getMethodName())
+ && containsVariable(frame, variable.getElementName());
+ } catch (DebugException e) {
+ JDIDebugUIPlugin.log(e);
+ return false;
+ }
+ };
+
+ }
+
+ private static Predicate<IJavaStackFrame> forField(IField field) {
+ return frame -> {
+ try {
+ return frame.getThis() != null && frame.getThis().getJavaType().getName().equals(field.getDeclaringType().getFullyQualifiedName())
+ && containsVariable(frame, field.getElementName());
+ } catch (DebugException e) {
+ JDIDebugUIPlugin.log(e);
+ return false;
+ }
+ };
+
+ }
+
}

Back to the top