Skip to content

Commit e717a57

Browse files
committed
Honor Java Expression's context when creating Watches
When selecting a field under an object, both the Watch command and Drag & Drop previously created a non-contextual expression using only the field name, which caused evaluation failures. This fix ensures that both Watch and Drag & Drop now generate proper context-aware expressions such as myobj.myfield instead of just the field name. Platform changes : eclipse-platform/eclipse.platform#2311 Fixes : https://bugs.eclipse.org/bugs/show_bug.cgi?id=321289
1 parent 80f607a commit e717a57

File tree

5 files changed

+182
-4
lines changed

5 files changed

+182
-4
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 IBM Corporation.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* IBM Corporation - initial API and implementation
13+
*******************************************************************************/
14+
15+
/**
16+
* WatchItemContext
17+
*/
18+
public class WatchItemContext {
19+
20+
private class X {
21+
private int x = 0;
22+
}
23+
24+
private class Y {
25+
private X x = new X();
26+
private int y = 0;
27+
}
28+
29+
private class Z {
30+
private Y y = new Y();
31+
private int z = 0;
32+
}
33+
34+
public static void main(String[] args) {
35+
new WatchItemContext().foo();
36+
}
37+
38+
public void foo() {
39+
X x = new X();
40+
Y y = new Y();
41+
Z z = new Z();
42+
System.out.println(x + " " + y + " " + z);
43+
}
44+
}

org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ public abstract class AbstractDebugTest extends TestCase implements IEvaluation
219219
"OutSync", "OutSync2", "ConsoleOutputUmlaut", "ErrorRecurrence", "ModelPresentationTests", "Bug565982",
220220
"SuspendVMConditionalBreakpointsTestSnippet", "FileConditionSnippet2", "compare.CompareObjectsStringTest", "compare.CompareListObjects",
221221
"compare.CompareMapObjects", "compare.CompareSetObjects", "compare.CompareNormalObjects", "compare.CompareArrayObjects",
222-
"StatementStep", "StatementStepArgument", "StatementStepNested", "StatementStepWithOperations" };
222+
"StatementStep", "StatementStepArgument", "StatementStepNested", "StatementStepWithOperations", "WatchItemContext" };
223223

224224
/**
225225
* the default timeout

org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/WatchExpressionTests.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2015 IBM Corporation and others.
2+
* Copyright (c) 2000, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -13,10 +13,13 @@
1313
*******************************************************************************/
1414
package org.eclipse.jdt.debug.tests.core;
1515

16+
import java.util.List;
17+
1618
import org.eclipse.debug.core.DebugEvent;
1719
import org.eclipse.debug.core.DebugPlugin;
1820
import org.eclipse.debug.core.IExpressionManager;
1921
import org.eclipse.debug.core.model.IValue;
22+
import org.eclipse.debug.core.model.IVariable;
2023
import org.eclipse.debug.core.model.IWatchExpression;
2124
import org.eclipse.debug.internal.ui.DebugUIPlugin;
2225
import org.eclipse.debug.ui.IDebugUIConstants;
@@ -27,6 +30,7 @@
2730
import org.eclipse.jdt.debug.testplugin.DebugElementEventWaiter;
2831
import org.eclipse.jdt.debug.testplugin.ExpressionWaiter;
2932
import org.eclipse.jdt.debug.tests.AbstractDebugTest;
33+
import org.eclipse.jdt.internal.debug.ui.heapwalking.JavaNestedWatchExpressionFilter;
3034
import org.eclipse.swt.widgets.Display;
3135
import org.eclipse.ui.IWorkbench;
3236
import org.eclipse.ui.IWorkbenchPage;
@@ -165,6 +169,32 @@ public void DisabledtestStepping() throws Exception {
165169
}
166170
}
167171

172+
public void testExpressionContext() throws Exception {
173+
String typeName = "WatchItemContext";
174+
createLineBreakpoint(42, typeName);
175+
IJavaThread thread = null;
176+
try {
177+
thread = launchToBreakpoint(typeName);
178+
assertNotNull("Breakpoint not hit within timeout period", thread);
179+
IJavaStackFrame stackFrame = (IJavaStackFrame) thread.getTopStackFrame();
180+
IVariable v1 = stackFrame.findVariable("y");
181+
IVariable v2 = v1.getValue().getVariables()[2];
182+
JavaNestedWatchExpressionFilter javaFilter = new JavaNestedWatchExpressionFilter();
183+
assertTrue(javaFilter.canCreateWatchExpression(List.of(v1, v2)));
184+
String exp = javaFilter.createWatchExpression(List.of(v1, v2));
185+
assertEquals("Invalid Expression", "y.y", exp);
186+
IWatchExpression expression = getExpressionManager().newWatchExpression(exp);
187+
DebugElementEventWaiter waiter = new ExpressionWaiter(DebugEvent.CHANGE, expression);
188+
getExpressionManager().addExpression(expression);
189+
waiter.waitForEvent();
190+
assertEquals("Wrong Value", "0", expression.getValue().toString());
191+
} finally {
192+
terminateAndRemove(thread);
193+
removeAllBreakpoints();
194+
removeAllExpressions();
195+
}
196+
}
197+
168198
/**
169199
* Dumps any error messages to the console.
170200
*/
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 IBM Corporation.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* IBM Corporation - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.jdt.internal.debug.ui.heapwalking;
15+
16+
import java.util.List;
17+
18+
import org.eclipse.core.runtime.CoreException;
19+
import org.eclipse.debug.core.DebugException;
20+
import org.eclipse.debug.core.model.IVariable;
21+
import org.eclipse.debug.internal.ui.views.variables.IndexedVariablePartition;
22+
import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter2;
23+
import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapterExtension;
24+
import org.eclipse.jdt.internal.debug.core.logicalstructures.JDIPlaceholderVariable;
25+
import org.eclipse.jdt.internal.debug.core.model.JDIArrayEntryVariable;
26+
import org.eclipse.jdt.internal.debug.core.model.JDIPlaceholderValue;
27+
import org.eclipse.jdt.internal.debug.core.model.JDIReferenceListEntryVariable;
28+
import org.eclipse.jdt.internal.debug.core.model.JDIReferenceListVariable;
29+
30+
/**
31+
* Uses the {@link IWatchExpressionFactoryAdapterExtension} to filter when the watch expression action is available based on the variable selected.
32+
*
33+
* Currently removes the action from {@link JDIPlaceholderVariable}s and {@link JDIReferenceListVariable}s.
34+
*/
35+
public class JavaNestedWatchExpressionFilter implements IWatchExpressionFactoryAdapter2 {
36+
37+
@Override
38+
public String createWatchExpression(Object element) throws CoreException {
39+
if (element instanceof List<?> expVariablesList) {
40+
StringBuilder expresion = new StringBuilder();
41+
for (Object ob : expVariablesList) {
42+
if (ob instanceof IVariable variable) {
43+
String current = variable.getName();
44+
expresion.append(current);
45+
expresion.append('.');
46+
}
47+
}
48+
expresion.deleteCharAt(expresion.length() - 1);
49+
return expresion.toString();
50+
}
51+
return null;
52+
}
53+
54+
@Override
55+
public boolean canCreateWatchExpression(Object variable) {
56+
if (variable instanceof List<?> expVariablesList) {
57+
for (Object ob : expVariablesList) {
58+
if (ob instanceof IVariable variable2) {
59+
return checkForValidVariable(variable2);
60+
}
61+
}
62+
}
63+
return false;
64+
}
65+
66+
/**
67+
* Checks whether the given {@link IVariable} is suitable for creating a watch expression.
68+
* <p>
69+
* Filters out variables that represent internal or synthetic structures (such as reference lists, array entries, indexed partitions) or
70+
* placeholder values that do not have a concrete value.
71+
* </p>
72+
*
73+
* @param variable
74+
* the variable to check
75+
* @return {@code true} if the variable can be used to create a watch expression, {@code false} otherwise
76+
*/
77+
private boolean checkForValidVariable(IVariable variable) {
78+
if (variable instanceof JDIReferenceListVariable || variable instanceof JDIReferenceListEntryVariable
79+
|| variable instanceof JDIArrayEntryVariable || variable instanceof IndexedVariablePartition) {
80+
return false;
81+
}
82+
try {
83+
if (variable.getValue() instanceof JDIPlaceholderValue) {
84+
return false;
85+
}
86+
} catch (DebugException e) {
87+
}
88+
return true;
89+
}
90+
91+
}

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaDebugElementAdapterFactory.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2006, 2015 IBM Corporation and others.
2+
* Copyright (c) 2006, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -19,10 +19,12 @@
1919
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
2020
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider;
2121
import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter;
22+
import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter2;
2223
import org.eclipse.jdt.debug.core.IJavaStackFrame;
2324
import org.eclipse.jdt.debug.core.IJavaValue;
2425
import org.eclipse.jdt.debug.core.IJavaVariable;
2526
import org.eclipse.jdt.internal.debug.ui.display.JavaInspectExpression;
27+
import org.eclipse.jdt.internal.debug.ui.heapwalking.JavaNestedWatchExpressionFilter;
2628
import org.eclipse.jdt.internal.debug.ui.heapwalking.JavaWatchExpressionFilter;
2729

2830
/**
@@ -33,6 +35,7 @@
3335
* @see JavaVariableContentProvider
3436
* @see ExpressionLabelProvider
3537
* @see JavaExpressionContentProvider
38+
* @see JavaNestedWatchExpressionFilter
3639
* @see JavaWatchExpressionFilter
3740
* @see JavaStackFrameMementoProvider
3841
* @since 3.3
@@ -44,6 +47,7 @@ public class JavaDebugElementAdapterFactory implements IAdapterFactory {
4447
private static final IElementLabelProvider fgLPExpression = new ExpressionLabelProvider();
4548
private static final IElementContentProvider fgCPExpression = new JavaExpressionContentProvider();
4649
private static final IWatchExpressionFactoryAdapter fgWEVariable = new JavaWatchExpressionFilter();
50+
private static final IWatchExpressionFactoryAdapter2 NestedWatchExpressionsAdapter = new JavaNestedWatchExpressionFilter();
4751
private static final IElementMementoProvider fgMPStackFrame = new JavaStackFrameMementoProvider();
4852
private static final IElementLabelProvider fgLPFrame = new JavaStackFrameLabelProvider();
4953

@@ -83,6 +87,14 @@ public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
8387
return (T) fgWEVariable;
8488
}
8589
}
90+
if (IWatchExpressionFactoryAdapter2.class.equals(adapterType)) {
91+
if (adaptableObject instanceof IJavaVariable) {
92+
return (T) NestedWatchExpressionsAdapter;
93+
}
94+
if (adaptableObject instanceof JavaInspectExpression) {
95+
return (T) NestedWatchExpressionsAdapter;
96+
}
97+
}
8698
if (IElementMementoProvider.class.equals(adapterType)) {
8799
if (adaptableObject instanceof IJavaStackFrame) {
88100
return (T) fgMPStackFrame;
@@ -96,6 +108,7 @@ public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
96108
*/
97109
@Override
98110
public Class<?>[] getAdapterList() {
99-
return new Class[] {IElementLabelProvider.class, IElementContentProvider.class, IWatchExpressionFactoryAdapter.class, IElementMementoProvider.class};
111+
return new Class[] { IElementLabelProvider.class, IElementContentProvider.class, IWatchExpressionFactoryAdapter.class,
112+
IWatchExpressionFactoryAdapter2.class, IElementMementoProvider.class };
100113
}
101114
}

0 commit comments

Comments
 (0)