Bug 573385 - Add support debug hovers on chained local variables objects
Extend solution provided in Bug572629 to support local variables.
Change-Id: Ib61b90b317849d6752ecfa70d159043312ae8738
Signed-off-by: Gayan Perera <gayanper@gmail.com>
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.debug/+/180255
Tested-by: JDT Bot <jdt-bot@eclipse.org>
Reviewed-by: Sarika Sinha <sarika.sinha@in.ibm.com>
diff --git a/org.eclipse.jdt.debug.tests/java8/Bug572629.java b/org.eclipse.jdt.debug.tests/java8/Bug572629.java
index 7b97fa9..b83892b 100644
--- a/org.eclipse.jdt.debug.tests/java8/Bug572629.java
+++ b/org.eclipse.jdt.debug.tests/java8/Bug572629.java
@@ -1,3 +1,5 @@
+import java.util.stream.Stream;
+
/*******************************************************************************
* Copyright (c) 2021 Gayan Perera and others.
*
@@ -33,7 +35,19 @@
return this.payload == other.payload && this.payloads.length == other.payloads.length;
}
+ public void hoverOverLocal(String[] names) {
+ char[] name = new char[] {'n', 'a', 'm', 'e'};
+ Bug572629 object = new Bug572629("p");
+
+ System.out.println(name.length);
+ System.out.println(object.payload);
+ System.out.println(names.length);
+ Stream.of(name).forEach(a -> {
+ System.out.println(a.length);
+ });
+ }
public static void main(String[] args) {
new Bug572629("p").equals(new Bug572629("r"));
+ new Bug572629("p").hoverOverLocal(new String[] {"name"});
}
}
\ No newline at end of file
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 5f0b7e3..3f2105a 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
@@ -290,7 +290,7 @@
final String typeName = "Bug572629";
final String expectedMethod = "equals";
final int frameNumber = 2;
- final int bpLine = 33;
+ final int bpLine = 35;
IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
@@ -327,7 +327,7 @@
final String typeName = "Bug572629";
final String expectedMethod = "equals";
final int frameNumber = 2;
- final int bpLine = 33;
+ final int bpLine = 35;
IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
@@ -364,7 +364,7 @@
final String typeName = "Bug572629";
final String expectedMethod = "equals";
final int frameNumber = 2;
- final int bpLine = 33;
+ final int bpLine = 35;
IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
@@ -401,7 +401,7 @@
final String typeName = "Bug572629";
final String expectedMethod = "equals";
final int frameNumber = 2;
- final int bpLine = 33;
+ final int bpLine = 35;
IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
@@ -437,7 +437,7 @@
final String typeName = "Bug572629";
final String expectedMethod = "equals";
final int frameNumber = 2;
- final int bpLine = 33;
+ final int bpLine = 35;
IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
@@ -474,7 +474,7 @@
final String typeName = "Bug572629";
final String expectedMethod = "equals";
final int frameNumber = 2;
- final int bpLine = 32;
+ final int bpLine = 34;
IJavaBreakpoint bp = createLineBreakpoint(bpLine, "", typeName + ".java", typeName);
bp.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
@@ -505,6 +505,154 @@
}
}
+ public void testBug572629_LocalVariableHover_ArrayLength_ExpectValue() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+
+ final String typeName = "Bug572629";
+ final String expectedMethod = "hoverOverLocal";
+ final int frameNumber = 2;
+ final int bpLine = 42;
+
+ 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().indexOf("name.length") + "name.".length();
+ IRegion region = new Region(offset, "length".length());
+ String text = selectAndReveal(part, bpLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("name.length", info.getName());
+ assertEquals("4", info.getValue().getValueString());
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug572629_ParameterVariableHover_ArrayLength_ExpectValue() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+
+ final String typeName = "Bug572629";
+ final String expectedMethod = "hoverOverLocal";
+ final int frameNumber = 2;
+ final int bpLine = 44;
+
+ 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().indexOf("names.length") + "names.".length();
+ IRegion region = new Region(offset, "length".length());
+ String text = selectAndReveal(part, bpLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("names.length", info.getName());
+ assertEquals("1", info.getValue().getValueString());
+ } finally {
+ terminateAndRemove(thread);
+ removeAllBreakpoints();
+ }
+ }
+
+ public void testBug572629_ChainedLocalVariableHover_ExpectValue() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+
+ final String typeName = "Bug572629";
+ final String expectedMethod = "hoverOverLocal";
+ final int frameNumber = 2;
+ final int bpLine = 43;
+
+ 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("object.payload") + "object.".length();
+ IRegion region = new Region(offset, "payload".length());
+ String text = selectAndReveal(part, bpLine, 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 testBug572629_LocalArrayVariableLengthHoverInSideLambda_ExpectValue() throws Exception {
+ sync(() -> TestUtil.waitForJobs(getName(), 1000, 10000, ProcessConsole.class));
+
+ final String typeName = "Bug572629";
+ final String expectedMethod = "lambda$0";
+ final int frameNumber = 6;
+ final int bpLine = 46;
+
+ 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().indexOf("a.length") + "a.".length();
+ IRegion region = new Region(offset, "length".length());
+ String text = selectAndReveal(part, bpLine, region);
+ assertEquals(variableName, text);
+ IVariable info = (IVariable) sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(info);
+ assertEquals("a.length", info.getName());
+ assertEquals("4", info.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));
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 d5dcaa9..3c076f5 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
@@ -298,12 +298,12 @@
IJavaElement[] resolve = resolveElement(hoverRegion.getOffset(), codeAssist);
try {
- boolean onArrayLegnth = false;
+ boolean onArrayLength = false;
if (resolve.length == 0 && isOverNameLength(hoverRegion, document)) {
// lets check if this is part of an array variable by jumping 2 chars backward from offset.
resolve = resolveElement(hoverRegion.getOffset() - 2, codeAssist);
- onArrayLegnth = (resolve.length == 1) && (resolve[0] instanceof IField)
- && ((IField) resolve[0]).getTypeSignature().startsWith("["); //$NON-NLS-1$
+ onArrayLength = (resolve.length == 1) && isLocalOrMemberVariable(resolve[0])
+ && isArrayTypeVariable(resolve[0]);
}
for (int i = 0; i < resolve.length; i++) {
@@ -312,7 +312,7 @@
IField field = (IField) javaElement;
IJavaVariable variable = null;
IJavaDebugTarget debugTarget = (IJavaDebugTarget) frame.getDebugTarget();
- if (Flags.isStatic(field.getFlags()) && !onArrayLegnth) {
+ if (Flags.isStatic(field.getFlags()) && !onArrayLength) {
IJavaType[] javaTypes = debugTarget.getJavaTypes(field.getDeclaringType().getFullyQualifiedName());
if (javaTypes != null) {
for (int j = 0; j < javaTypes.length; j++) {
@@ -361,19 +361,12 @@
} else {
if (!frame.isStatic() && !frame.isNative()) {
// ensure that we only resolve a field access on 'this':
- if (!(codeAssist instanceof ITypeRoot)) {
+ if (!(codeAssist instanceof ITypeRoot)) {
return null;
}
- ITypeRoot typeRoot = (ITypeRoot) codeAssist;
- ASTNode root = SharedASTProviderCore.getAST(typeRoot, SharedASTProviderCore.WAIT_NO, null);
- if (root == null) {
- ASTParser parser = ASTParser.newParser(AST.JLS15);
- parser.setSource(typeRoot);
- parser.setFocalPosition(hoverRegion.getOffset());
- root = parser.createAST(null);
- }
- ASTNode node = NodeFinder.perform(root, hoverRegion.getOffset(), hoverRegion.getLength());
- if (node == null) {
+ ITypeRoot typeRoot = (ITypeRoot) codeAssist;
+ ASTNode node = findNodeAtRegion(typeRoot, hoverRegion);
+ if (node == null) {
return null;
}
StructuralPropertyDescriptor locationInParent = node.getLocationInParent();
@@ -381,7 +374,7 @@
FieldAccess fieldAccess = (FieldAccess) node.getParent();
if (fieldAccess.getExpression() instanceof ThisExpression) {
variable = evaluateField(frame, field);
- } else if(onArrayLegnth) {
+ } else if (onArrayLength) {
variable = evaluateQualifiedNode(fieldAccess, frame, typeRoot.getJavaProject());
}
} else if (locationInParent == QualifiedName.NAME_PROPERTY) {
@@ -397,6 +390,19 @@
break;
}
if (javaElement instanceof ILocalVariable) {
+ // if we are on a array, regardless where we are send it to evaluation engine
+ if (onArrayLength) {
+ if (!(codeAssist instanceof ITypeRoot)) {
+ return null;
+ }
+ ITypeRoot typeRoot = (ITypeRoot) codeAssist;
+ ASTNode node = findNodeAtRegion(typeRoot, hoverRegion);
+ if (node == null) {
+ return null;
+ }
+ return evaluateQualifiedNode(node.getParent(), frame, typeRoot.getJavaProject());
+ }
+
ILocalVariable var = (ILocalVariable)javaElement;
IJavaElement parent = var.getParent();
while (!(parent instanceof IMethod) && !(parent instanceof IInitializer) && parent != null) {
@@ -468,6 +474,33 @@
return null;
}
+ private ASTNode findNodeAtRegion(ITypeRoot typeRoot, IRegion hoverRegion) {
+ ASTNode root = SharedASTProviderCore.getAST(typeRoot, SharedASTProviderCore.WAIT_NO, null);
+ if (root == null) {
+ ASTParser parser = ASTParser.newParser(AST.JLS15);
+ parser.setSource(typeRoot);
+ parser.setFocalPosition(hoverRegion.getOffset());
+ root = parser.createAST(null);
+ }
+ return NodeFinder.perform(root, hoverRegion.getOffset(), hoverRegion.getLength());
+ }
+
+ private boolean isArrayTypeVariable(IJavaElement element) throws JavaModelException {
+ String signature;
+ if (element instanceof IField) {
+ signature = ((IField) element).getTypeSignature();
+ } else if (element instanceof ILocalVariable) {
+ signature = ((ILocalVariable) element).getTypeSignature();
+ } else {
+ signature = ""; //$NON-NLS-1$
+ }
+ return signature.startsWith("["); //$NON-NLS-1$
+ }
+
+ private boolean isLocalOrMemberVariable(IJavaElement element) {
+ return (element instanceof IField) || (element instanceof ILocalVariable);
+ }
+
private boolean isOverNameLength(IRegion hoverRegion, IDocument document) {
try {
return "length".equals(document.get(hoverRegion.getOffset(), hoverRegion.getLength())); //$NON-NLS-1$