Bug 317045 - Debug variables hover does not show value of fields of
outer class
- updated AST to latest level
- fixed inherited fields not found
- fixed fields from local types not found
- added example with nested inner types including local types
Change-Id: I90d84d9a93edbe02978ea35bd7524b9b861f8a58
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
diff --git a/org.eclipse.jdt.debug.tests/java8/Bug317045.java b/org.eclipse.jdt.debug.tests/java8/Bug317045.java
new file mode 100644
index 0000000..7daf684
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/java8/Bug317045.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Andrey Loskutov 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:
+ * Andrey Loskutov <loskutov@gmx.de> - initial API and implementation
+ *******************************************************************************/
+public class Bug317045 {
+
+ private String var0 = "0";
+ private String var1 = "1";
+ private String var2 = "2";
+ private String var3 = "3";
+
+ public static void main(String[] args) throws Exception {
+ new Bug317045().run();
+ }
+
+ public void run() {
+ class InnerClass1 extends Class0 {
+ private String var1 = "11";
+ public void run1() {
+ System.out.println(var0);
+ System.out.println(var1);
+ System.out.println(var2);
+ System.out.println(var3);
+ new InnerClass1() {
+ private String var2 = "21";
+ public void run11() {
+ System.out.println(var0);
+ System.out.println(var1);
+ System.out.println(var2);
+ System.out.println(var3); // bp 2
+ System.out.println(InnerClass1.this.var0);
+ System.out.println(InnerClass1.this.var1);
+ System.out.println(Bug317045.this.var0); // x
+ System.out.println(Bug317045.this.var1); // x
+ System.out.println(Bug317045.this.var2);
+ System.out.println(Bug317045.this.var3);
+ }
+ }.run11();
+ }
+ }
+ new Class0().run0();
+ new InnerClass1().run1();
+ new Class2().run2();
+ }
+
+ class Class0 {
+ String var0 = "00";
+ public void run0() {
+ System.out.println(var0);
+ System.out.println(var1);
+ System.out.println(var2);
+ System.out.println(var3); // bp 1
+ }
+ }
+
+ class Class2 extends Class0 {
+ String var2 = "22";
+ public void run2() {
+ System.out.println(var0);
+ System.out.println(var1);
+ System.out.println(var2);
+ System.out.println(var3); // bp 3
+ System.out.println(Bug317045.this.var0); // x
+ System.out.println(Bug317045.this.var1);
+ System.out.println(Bug317045.this.var2);
+ System.out.println(Bug317045.this.var3);
+ }
+ }
+}
\ 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 3086de1..57e9c4c 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
@@ -472,6 +472,7 @@
cfgs.add(createLaunchConfiguration(jp, "EvalTestIntf18"));
cfgs.add(createLaunchConfiguration(jp, "EvalIntfSuperDefault"));
cfgs.add(createLaunchConfiguration(jp, "DebugHoverTest18"));
+ cfgs.add(createLaunchConfiguration(jp, "Bug317045"));
cfgs.add(createLaunchConfiguration(jp, "Bug549394"));
cfgs.add(createLaunchConfiguration(jp, "Bug541110"));
cfgs.add(createLaunchConfiguration(jp, "ClosureVariableTest_Bug542989"));
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 1abb501..264c3a1 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
@@ -181,6 +181,107 @@
}
}
+ public void testResolveInInner() throws Exception {
+ final String typeName = "Bug317045";
+ final String typeName1 = typeName + "$Class0";
+ final String typeName2 = typeName;
+ final String typeName3 = typeName + "$Class2";
+ final String expectedMethod1 = "run0";
+ final String expectedMethod2 = "run11";
+ final String expectedMethod3 = "run2";
+ final int framesNumber1 = 3;
+ final int framesNumber2 = 4;
+ final int framesNumber3 = 3;
+ final int bpLine1 = 61;
+ final int bpLine2 = 39;
+ final int bpLine3 = 71;
+
+ IJavaBreakpoint bp1 = createLineBreakpoint(bpLine1, "", typeName + ".java", typeName1);
+ IJavaBreakpoint bp2 = createLineBreakpoint(bpLine2, "", typeName + ".java", typeName2);
+ IJavaBreakpoint bp3 = createLineBreakpoint(bpLine3, "", typeName + ".java", typeName3);
+ bp1.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ bp2.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ bp3.setSuspendPolicy(IJavaBreakpoint.SUSPEND_THREAD);
+ IFile file = (IFile) bp1.getMarker().getResource();
+ assertEquals(typeName + ".java", file.getName());
+
+ IJavaThread thread = null;
+ try {
+ thread = launchToBreakpoint(typeName);
+ CompilationUnitEditor part = openEditorAndValidateStack(expectedMethod1, framesNumber1, file, thread);
+
+ JavaDebugHover hover = new JavaDebugHover();
+ hover.setEditor(part);
+
+ Map<String, Region> offsets = new LinkedHashMap<>();
+ offsets.put("var3", new Region(1832, "var3".length()));
+ offsets.put("var2", new Region(1803, "var2".length()));
+ offsets.put("var1", new Region(1774, "var1".length()));
+ offsets.put("var0", new Region(1745, "var0".length()));
+ String[] values = { "3", "2", "1", "00" };
+ Set<Entry<String, Region>> entrySet = offsets.entrySet();
+ int startLine = bpLine1;
+ int valueIndex = 0;
+ for (Entry<String, Region> varData : entrySet) {
+ // select variables and validate the hover, going backwards from the breakpoint
+ validateLine(startLine--, part, hover, varData, values[valueIndex++]);
+ }
+
+ resumeToLineBreakpoint(thread, (ILineBreakpoint) bp2);
+ part = openEditorAndValidateStack(expectedMethod2, framesNumber2, file, thread);
+
+ offsets = new LinkedHashMap<>();
+ offsets.put("var3", new Region(1242, "var3".length()));
+ offsets.put("var2", new Region(1210, "var2".length()));
+ offsets.put("var1", new Region(1178, "var1".length()));
+ offsets.put("var0", new Region(1146, "var0".length()));
+ values = new String[] { "3", "21", "11", "00" };
+
+ entrySet = offsets.entrySet();
+ startLine = bpLine2;
+ valueIndex = 0;
+ for (Entry<String, Region> varData : entrySet) {
+ // select variables and validate the hover, going backwards from the breakpoint
+ validateLine(startLine--, part, hover, varData, values[valueIndex++]);
+ }
+
+ offsets = new LinkedHashMap<>();
+ offsets.put("var3", new Region(1030, "var3".length()));
+ offsets.put("var2", new Region(1000, "var2".length()));
+ offsets.put("var1", new Region(970, "var1".length()));
+ offsets.put("var0", new Region(940, "var0".length()));
+ values = new String[] { "3", "2", "11", "00" };
+
+ entrySet = offsets.entrySet();
+ startLine = 32;
+ valueIndex = 0;
+ for (Entry<String, Region> varData : entrySet) {
+ // select variables and validate the hover, going backwards from the breakpoint
+ validateLine(startLine--, part, hover, varData, values[valueIndex++]);
+ }
+
+ resumeToLineBreakpoint(thread, (ILineBreakpoint) bp3);
+ part = openEditorAndValidateStack(expectedMethod3, framesNumber3, file, thread);
+
+ offsets = new LinkedHashMap<>();
+ offsets.put("var3", new Region(2040, "var3".length()));
+ offsets.put("var2", new Region(2011, "var2".length()));
+ offsets.put("var1", new Region(1982, "var1".length()));
+ offsets.put("var0", new Region(1953, "var0".length()));
+ values = new String[] { "3", "22", "1", "00" };
+ entrySet = offsets.entrySet();
+ startLine = bpLine3;
+ valueIndex = 0;
+ for (Entry<String, Region> varData : entrySet) {
+ // select variables and validate the hover, going backwards from the breakpoint
+ validateLine(startLine--, part, hover, varData, values[valueIndex++]);
+ }
+ } 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));
@@ -218,6 +319,24 @@
assertEquals("v" + valueIndex, object.value());
}
+ private void validateLine(final int line, CompilationUnitEditor part, JavaDebugHover hover, Entry<String, Region> varData, String varValue) throws Exception, DebugException {
+ String debugVarName = varData.getKey();
+ String variableName = debugVarName;
+ IRegion region = varData.getValue();
+ String text = selectAndReveal(part, line, region);
+ assertEquals(variableName, text);
+ Object args = sync(() -> hover.getHoverInfo2(part.getViewer(), region));
+
+ assertNotNull(args);
+ JDIModificationVariable var = (JDIModificationVariable) args;
+ assertEquals(debugVarName, var.getName());
+ JDIValue value = (JDIValue) var.getValue();
+ assertEquals(JDIObjectValue.class, value.getClass());
+ JDIObjectValue valueObj = (JDIObjectValue) var.getValue();
+ StringReferenceImpl object = (StringReferenceImpl) valueObj.getUnderlyingObject();
+ assertEquals(varValue, object.value());
+ }
+
String selectAndReveal(CompilationUnitEditor editor, int line, IRegion region) throws Exception {
ITextSelection selection = sync(() -> {
getActivePage().activate(editor);
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 a6144d9..7d0ada1 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
@@ -352,7 +352,7 @@
ITypeRoot typeRoot = (ITypeRoot) codeAssist;
ASTNode root = SharedASTProviderCore.getAST(typeRoot, SharedASTProviderCore.WAIT_NO, null);
if (root == null) {
- ASTParser parser = ASTParser.newParser(AST.JLS4);
+ ASTParser parser = ASTParser.newParser(AST.JLS12);
parser.setSource(typeRoot);
parser.setFocalPosition(hoverRegion.getOffset());
root = parser.createAST(null);
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIObjectValue.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIObjectValue.java
index c830ffb..0d477fd 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIObjectValue.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIObjectValue.java
@@ -258,33 +258,62 @@
return null;
}
- /*
- * (non-Javadoc)
- *
- * @see org.eclipse.jdt.debug.core.IJavaObject#getField(java.lang.String,
- * java.lang.String)
- */
@Override
- public IJavaFieldVariable getField(String name,
- String declaringTypeSignature) throws DebugException {
+ public IJavaFieldVariable getField(final String name, final String declaringTypeSignature) throws DebugException {
ReferenceType ref = getUnderlyingReferenceType();
try {
Field field = null;
Field fieldTmp = null;
+ List<Field> synteticFields = new ArrayList<>();
Iterator<Field> fields = ref.allFields().iterator();
- while (fields.hasNext()) {
+ List<ReferenceType> superTypes = null;
+ main: while (fields.hasNext()) {
fieldTmp = fields.next();
- if (name.equals(fieldTmp.name())
- && declaringTypeSignature.equals(fieldTmp
- .declaringType().signature())) {
- field = fieldTmp;
- break;
+ if (name.equals(fieldTmp.name())) {
+ ReferenceType declaringType = fieldTmp.declaringType();
+ String signature = declaringType.signature();
+ if (declaringTypeSignature.equals(signature)) {
+ field = fieldTmp;
+ break;
+ }
+ // check if we are inside local type - Signature.createTypeSignature
+ // can't create proper type name out of source field in JavaDebugHover
+ // we get LDebugHoverTest$InnerClass2; instead of LDebugHoverTest$1InnerClass2;
+ signature = signature.replaceFirst("\\$\\d+", "\\$"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (declaringTypeSignature.equals(signature)) {
+ field = fieldTmp;
+ break;
+ }
+ if (superTypes == null) {
+ superTypes = superTypes(ref);
+ }
+ for (ReferenceType st : superTypes) {
+ if (st.signature().equals(signature)) {
+ field = fieldTmp;
+ break main;
+ }
+ }
+ }
+ if (fieldTmp.isSynthetic()) {
+ synteticFields.add(fieldTmp);
}
}
+ JDIDebugTarget debugTarget = (JDIDebugTarget) getDebugTarget();
if (field != null) {
- return new JDIFieldVariable((JDIDebugTarget) getDebugTarget(),
- field, getUnderlyingObject(), fLogicalParent);
+ return new JDIFieldVariable(debugTarget, field, getUnderlyingObject(), fLogicalParent);
}
+
+ // Check possible references of variables defined in outer class
+ for (Field outer : synteticFields) {
+ // retrieve the reference to the "outer" object
+ JDIFieldVariable syntVariable = new JDIFieldVariable(debugTarget, outer, getUnderlyingObject(), fLogicalParent);
+ JDIObjectValue outerObject = (JDIObjectValue) syntVariable.getValue();
+ if (outerObject != null) {
+ // ask "outer" object about field probably declared within
+ return outerObject.getField(name, outer.signature());
+ }
+ }
+
} catch (RuntimeException e) {
targetRequestFailed(
MessageFormat.format(
@@ -295,6 +324,20 @@
return null;
}
+ static List<ReferenceType> superTypes(ReferenceType type) {
+ List<ReferenceType> superTypes = new ArrayList<>();
+ ReferenceType t = type;
+ while (t instanceof ClassType) {
+ ClassType ct = (ClassType) t;
+ t = ct.superclass();
+ if (t == null || "java.lang.Object".equals(t.name())) { //$NON-NLS-1$
+ break;
+ }
+ superTypes.add(t);
+ }
+ return superTypes;
+ }
+
/**
* Returns a variable representing the field in this object with the given
* name, or <code>null</code> if there is no field with the given name, or