diff options
author | Gayan Perera | 2021-07-08 17:40:19 +0000 |
---|---|---|
committer | Sarika Sinha | 2021-08-04 13:56:34 +0000 |
commit | 1f63b07950714f66b0d7f72abd1b5493cebc56e2 (patch) | |
tree | 51b26cde12afd73720c7796bacad92e7891e276e | |
parent | fa6774a8f07b7c40adce5fb42e083ec9ad862b67 (diff) | |
download | eclipse.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>
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; + } + }; + + } + } |