Skip to content

Commit 5935fe2

Browse files
committed
Allow stepping through disassembly
1 parent 2e3d5c8 commit 5935fe2

File tree

6 files changed

+244
-86
lines changed

6 files changed

+244
-86
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2025 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
@@ -134,6 +134,7 @@
134134
import org.eclipse.jdt.debug.tests.refactoring.RenamePublicTypeUnitTests;
135135
import org.eclipse.jdt.debug.tests.sourcelookup.ArchiveSourceLookupTests;
136136
import org.eclipse.jdt.debug.tests.sourcelookup.Bug565462Tests;
137+
import org.eclipse.jdt.debug.tests.sourcelookup.ClassFileEditorHighlightingTest;
137138
import org.eclipse.jdt.debug.tests.sourcelookup.DefaultSourceContainerTests;
138139
import org.eclipse.jdt.debug.tests.sourcelookup.DirectorySourceContainerTests;
139140
import org.eclipse.jdt.debug.tests.sourcelookup.DirectorySourceLookupTests;
@@ -228,6 +229,7 @@ public AutomatedSuite() {
228229
addTest(new TestSuite(TypeResolutionTests.class));
229230
addTest(new TestSuite(JarSourceLookupTests.class));
230231
addTest(new TestSuite(Bug565462Tests.class));
232+
addTest(new TestSuite(ClassFileEditorHighlightingTest.class));
231233

232234
// Variable tests
233235
addTest(new TestSuite(InstanceVariableTests.class));
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026, Daniel Schmid and others.
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+
* Daniel Schmid - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.jdt.debug.tests.sourcelookup;
15+
16+
import org.eclipse.debug.core.model.IStackFrame;
17+
import org.eclipse.jdt.core.IClassFile;
18+
import org.eclipse.jdt.core.IJavaProject;
19+
import org.eclipse.jdt.debug.core.IJavaStackFrame;
20+
import org.eclipse.jdt.debug.core.IJavaThread;
21+
import org.eclipse.jdt.debug.tests.AbstractDebugTest;
22+
import org.eclipse.jdt.internal.debug.ui.sourcelookup.JavaStackFrameSourceDisplayAdapter;
23+
import org.eclipse.jdt.internal.ui.javaeditor.ClassFileEditor;
24+
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
25+
import org.eclipse.jdt.internal.ui.javaeditor.IClassFileEditorInput;
26+
import org.eclipse.swt.custom.StyleRange;
27+
import org.eclipse.swt.custom.StyledText;
28+
import org.eclipse.ui.PlatformUI;
29+
30+
public class ClassFileEditorHighlightingTest extends AbstractDebugTest {
31+
32+
private static final String CLASS_NAME = "OneToTen";
33+
34+
public ClassFileEditorHighlightingTest(String name) {
35+
super(name);
36+
}
37+
38+
public void test123() throws Exception {
39+
IJavaProject javaProject = getProjectContext();
40+
createLineBreakpoint(21, CLASS_NAME);
41+
42+
JavaStackFrameSourceDisplayAdapter sourceDisplay = new JavaStackFrameSourceDisplayAdapter();
43+
44+
IJavaThread thread = null;
45+
try {
46+
thread = launchToBreakpoint(CLASS_NAME);
47+
48+
ClassFileEditor editor = (ClassFileEditor) EditorUtility.openInEditor(
49+
javaProject.getProject().getFile("bin/" + CLASS_NAME + ".class")
50+
);
51+
IClassFileEditorInput editorInput = (IClassFileEditorInput) editor.getEditorInput();
52+
IClassFile classFile = editorInput.getClassFile();
53+
thread.getTopStackFrame().getLaunch().setSourceLocator(stackFrame -> classFile);
54+
55+
sourceDisplay.displaySource(thread.getTopStackFrame(), PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), true);
56+
StyledText noSourceTextWidget = editor.getNoSourceTextWidget();
57+
StyleRange[] styleRanges = noSourceTextWidget.getStyleRanges();
58+
assertEquals(1, styleRanges.length);
59+
String highlightedText = noSourceTextWidget.getContent().getTextRange(styleRanges[0].start, styleRanges[0].length);
60+
assertEquals(" 0 getstatic java.lang.System.out : java.io.PrintStream [16]", highlightedText);
61+
62+
stepOver((IJavaStackFrame) thread.getTopStackFrame());
63+
IStackFrame topStackFrame = thread.getTopStackFrame();
64+
sourceDisplay.displaySource(topStackFrame, PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), true);
65+
styleRanges = noSourceTextWidget.getStyleRanges();
66+
assertEquals(1, styleRanges.length);
67+
highlightedText = noSourceTextWidget.getContent().getTextRange(styleRanges[0].start, styleRanges[0].length);
68+
assertEquals(" 8 getstatic java.lang.System.out : java.io.PrintStream [16]", highlightedText);
69+
70+
thread.resume();
71+
} finally {
72+
terminateAndRemove(thread);
73+
removeAllBreakpoints();
74+
}
75+
}
76+
}

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/JavaDebugShowInAdapterFactory.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2006, 2025 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
@@ -14,8 +14,6 @@
1414
package org.eclipse.jdt.internal.debug.ui.sourcelookup;
1515

1616
import org.eclipse.core.runtime.IAdapterFactory;
17-
import org.eclipse.debug.core.DebugException;
18-
import org.eclipse.debug.internal.ui.DebugUIPlugin;
1917
import org.eclipse.debug.internal.ui.sourcelookup.SourceLookupFacility;
2018
import org.eclipse.debug.ui.sourcelookup.ISourceDisplay;
2119
import org.eclipse.jdt.debug.core.IJavaStackFrame;
@@ -29,6 +27,8 @@
2927
*/
3028
public class JavaDebugShowInAdapterFactory implements IAdapterFactory {
3129

30+
private JavaStackFrameSourceDisplayAdapter javaStackFrameSourceDisplayAdapter = new JavaStackFrameSourceDisplayAdapter();
31+
3232
/* (non-Javadoc)
3333
* @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, java.lang.Class)
3434
*/
@@ -54,13 +54,8 @@ public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
5454
SourceLookupFacility.getDefault().displaySource(frame, page, forceSourceLookup);
5555
};
5656
}
57-
try {
58-
if (adaptableObject instanceof JDIStackFrame jdiFrame
59-
&& org.eclipse.jdt.internal.debug.core.model.LambdaUtils.isLambdaFrame(jdiFrame)) {
60-
return (T) new LambdaStackFrameSourceDisplayAdapter();
61-
}
62-
} catch (DebugException e) {
63-
DebugUIPlugin.log(e);
57+
if (adaptableObject instanceof JDIStackFrame) {
58+
return (T) javaStackFrameSourceDisplayAdapter;// ensure the same instance is reused
6459
}
6560
}
6661
return null;
@@ -73,5 +68,4 @@ public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
7368
public Class<?>[] getAdapterList() {
7469
return new Class[] { IShowInSource.class, IShowInTargetList.class, ISourceDisplay.class };
7570
}
76-
7771
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025, 2026 IBM Corporation and others.
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.sourcelookup;
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.internal.ui.DebugUIPlugin;
21+
import org.eclipse.debug.internal.ui.sourcelookup.SourceLookupFacility;
22+
import org.eclipse.debug.internal.ui.sourcelookup.SourceLookupResult;
23+
import org.eclipse.debug.ui.DebugUITools;
24+
import org.eclipse.debug.ui.contexts.DebugContextEvent;
25+
import org.eclipse.debug.ui.contexts.IDebugContextListener;
26+
import org.eclipse.debug.ui.contexts.IDebugContextService;
27+
import org.eclipse.debug.ui.sourcelookup.ISourceDisplay;
28+
import org.eclipse.jdt.core.IJavaElement;
29+
import org.eclipse.jdt.core.JavaModelException;
30+
import org.eclipse.jdt.core.dom.IMethodBinding;
31+
import org.eclipse.jdt.core.dom.LambdaExpression;
32+
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
33+
import org.eclipse.jdt.internal.debug.core.model.LambdaUtils;
34+
import org.eclipse.jdt.internal.debug.ui.actions.ToggleBreakpointAdapter;
35+
import org.eclipse.jdt.internal.ui.javaeditor.ClassFileEditor;
36+
import org.eclipse.jdt.internal.ui.javaeditor.IClassFileEditorInput;
37+
import org.eclipse.jdt.ui.JavaUI;
38+
import org.eclipse.jface.text.BadLocationException;
39+
import org.eclipse.jface.text.IDocument;
40+
import org.eclipse.jface.text.IRegion;
41+
import org.eclipse.jface.viewers.IStructuredSelection;
42+
import org.eclipse.ui.IEditorInput;
43+
import org.eclipse.ui.IEditorPart;
44+
import org.eclipse.ui.IWorkbenchPage;
45+
import org.eclipse.ui.PartInitException;
46+
import org.eclipse.ui.texteditor.IDocumentProvider;
47+
import org.eclipse.ui.texteditor.ITextEditor;
48+
49+
/**
50+
* @since 3.2
51+
*/
52+
public class JavaStackFrameSourceDisplayAdapter implements ISourceDisplay {
53+
54+
private class ClassFileUnhighlightingListener implements IDebugContextListener {
55+
56+
private final IDebugContextService service;
57+
58+
public ClassFileUnhighlightingListener(IDebugContextService service) {
59+
this.service = service;
60+
}
61+
@Override
62+
public void debugContextChanged(DebugContextEvent event) {
63+
if ((event.getFlags() & DebugContextEvent.STATE) > 0) {
64+
if (event.getContext() instanceof IStructuredSelection selection && selection.getFirstElement() instanceof JDIStackFrame frame
65+
&& frame.equals(currentClassFileFrame)) {
66+
return;
67+
}
68+
69+
if (currentClassFileEditor != null) {
70+
currentClassFileEditor.unhighlight();
71+
currentClassFileEditor = null;
72+
currentClassFileFrame = null;
73+
service.removeDebugContextListener(this);
74+
classFileUnhighlighterRegistered = false;
75+
}
76+
}
77+
}
78+
}
79+
80+
private boolean classFileUnhighlighterRegistered = false;
81+
private JDIStackFrame currentClassFileFrame;
82+
private ClassFileEditor currentClassFileEditor;
83+
84+
@Override
85+
public void displaySource(Object element, IWorkbenchPage page, boolean forceSourceLookup) {
86+
JDIStackFrame jdiFrame = (JDIStackFrame) element;
87+
try {
88+
SourceLookupResult sourceRes = SourceLookupFacility.getDefault().lookup(element, jdiFrame.getLaunch().getSourceLocator(), forceSourceLookup);
89+
90+
if (sourceRes.getEditorInput() instanceof IClassFileEditorInput input) {
91+
if (handleClassFile(page, jdiFrame, input)) {
92+
ensureListenerRegistered(page);
93+
return;
94+
}
95+
}
96+
97+
if (LambdaUtils.isLambdaFrame(jdiFrame)) {
98+
if (handleLambda(jdiFrame, sourceRes)) {
99+
return;
100+
}
101+
}
102+
} catch (CoreException | BadLocationException e) {
103+
DebugUIPlugin.log(e);
104+
}
105+
SourceLookupFacility.getDefault().displaySource(jdiFrame, page, forceSourceLookup);
106+
}
107+
108+
private boolean handleLambda(JDIStackFrame jdiFrame, SourceLookupResult sourceRes) throws CoreException, BadLocationException {
109+
IDocumentProvider provider = JavaUI.getDocumentProvider();
110+
IEditorInput editorInput = sourceRes.getEditorInput();
111+
provider.connect(editorInput);
112+
IDocument document = provider.getDocument(editorInput);
113+
IRegion region = document.getLineInformation(jdiFrame.getLineNumber() - 1);
114+
IJavaElement je = JavaUI.getEditorInputJavaElement(editorInput);
115+
if (je != null) {
116+
IEditorPart part = JavaUI.openInEditor(je);
117+
if (part instanceof ITextEditor textEditor) {
118+
List<LambdaExpression> inLineLambdas = ToggleBreakpointAdapter.findLambdaExpressions(textEditor, region);
119+
for (LambdaExpression exp : inLineLambdas) {
120+
IMethodBinding methodBinding = exp.resolveMethodBinding();
121+
String key = methodBinding.getKey();
122+
if (key.contains(jdiFrame.getName())) {
123+
textEditor.selectAndReveal(exp.getStartPosition(), exp.getLength());
124+
return true;
125+
}
126+
}
127+
}
128+
}
129+
return false;
130+
}
131+
132+
private boolean handleClassFile(IWorkbenchPage page, JDIStackFrame jdiFrame, IClassFileEditorInput input) throws JavaModelException, PartInitException, DebugException {
133+
IJavaElement je = JavaUI.getEditorInputJavaElement(input);
134+
if (je != null) {
135+
IEditorPart part = JavaUI.openInEditor(je);
136+
if (part instanceof ClassFileEditor editor && editor.getDocumentProvider().getDocument(editor.getEditorInput()).getLength() == 0) {
137+
editor.highlightInstruction(jdiFrame.getMethodName(), jdiFrame.getSignature(), jdiFrame.getCodeIndex(), "currentIPColor"); //$NON-NLS-1$
138+
ensureListenerRegistered(page);
139+
currentClassFileEditor = editor;
140+
currentClassFileFrame = jdiFrame;
141+
return true;
142+
}
143+
}
144+
return false;
145+
}
146+
147+
private void ensureListenerRegistered(IWorkbenchPage page) {
148+
if (classFileUnhighlighterRegistered) {
149+
return;
150+
}
151+
IDebugContextService service = DebugUITools.getDebugContextManager().getContextService(page.getWorkbenchWindow());
152+
service.addDebugContextListener(new ClassFileUnhighlightingListener(service));
153+
classFileUnhighlighterRegistered = true;
154+
}
155+
}

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/sourcelookup/LambdaStackFrameSourceDisplayAdapter.java

Lines changed: 0 additions & 73 deletions
This file was deleted.

org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIStackFrame.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2022 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
@@ -1744,4 +1744,8 @@ public synchronized Category getCategory() {
17441744
public synchronized void resetCategory() {
17451745
fCategory = null;
17461746
}
1747+
1748+
public long getCodeIndex() {
1749+
return fLocation.codeIndex();
1750+
}
17471751
}

0 commit comments

Comments
 (0)