Skip to content

Commit 65ad692

Browse files
committed
Allow stepping through disassembly
1 parent ab4aa4d commit 65ad692

File tree

7 files changed

+453
-100
lines changed

7 files changed

+453
-100
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
import org.eclipse.jdt.debug.tests.refactoring.RenamePublicTypeUnitTests;
136136
import org.eclipse.jdt.debug.tests.sourcelookup.ArchiveSourceLookupTests;
137137
import org.eclipse.jdt.debug.tests.sourcelookup.Bug565462Tests;
138+
import org.eclipse.jdt.debug.tests.sourcelookup.ClassFileEditorHighlightingTest;
138139
import org.eclipse.jdt.debug.tests.sourcelookup.DefaultSourceContainerTests;
139140
import org.eclipse.jdt.debug.tests.sourcelookup.DirectorySourceContainerTests;
140141
import org.eclipse.jdt.debug.tests.sourcelookup.DirectorySourceLookupTests;
@@ -229,6 +230,7 @@ public AutomatedSuite() {
229230
addTest(new TestSuite(TypeResolutionTests.class));
230231
addTest(new TestSuite(JarSourceLookupTests.class));
231232
addTest(new TestSuite(Bug565462Tests.class));
233+
addTest(new UIThreadTestSuite(ClassFileEditorHighlightingTest.class));
232234

233235
// Variable tests
234236
addTest(new TestSuite(InstanceVariableTests.class));
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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;
15+
16+
import junit.framework.TestResult;
17+
import junit.framework.TestSuite;
18+
import org.eclipse.debug.internal.ui.DebugUIPlugin;
19+
20+
/**
21+
* Allows running tests in the UI thread.
22+
*/
23+
public class UIThreadTestSuite extends TestSuite {
24+
25+
public UIThreadTestSuite(Class<?> clazz) {
26+
super(clazz);
27+
}
28+
@Override
29+
public void run(TestResult result) {
30+
DebugUIPlugin.getStandardDisplay().syncExec(() -> super.run(result));
31+
}
32+
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
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 java.io.File;
17+
import java.io.IOException;
18+
import java.net.URI;
19+
import java.nio.charset.StandardCharsets;
20+
import java.util.List;
21+
import java.util.Locale;
22+
23+
import javax.tools.DiagnosticCollector;
24+
import javax.tools.JavaCompiler;
25+
import javax.tools.JavaCompiler.CompilationTask;
26+
import javax.tools.JavaFileObject;
27+
import javax.tools.SimpleJavaFileObject;
28+
import javax.tools.StandardJavaFileManager;
29+
import javax.tools.ToolProvider;
30+
31+
import org.eclipse.core.resources.IFile;
32+
import org.eclipse.core.resources.IResource;
33+
import org.eclipse.core.resources.ResourcesPlugin;
34+
import org.eclipse.debug.core.DebugException;
35+
import org.eclipse.debug.core.ILaunchConfiguration;
36+
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
37+
import org.eclipse.debug.core.model.IStackFrame;
38+
import org.eclipse.jdt.core.IClassFile;
39+
import org.eclipse.jdt.core.IJavaProject;
40+
import org.eclipse.jdt.debug.core.IJavaStackFrame;
41+
import org.eclipse.jdt.debug.core.IJavaThread;
42+
import org.eclipse.jdt.debug.tests.AbstractDebugTest;
43+
import org.eclipse.jdt.internal.debug.ui.sourcelookup.JavaStackFrameSourceDisplayAdapter;
44+
import org.eclipse.jdt.internal.ui.javaeditor.ClassFileEditor;
45+
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
46+
import org.eclipse.jdt.internal.ui.javaeditor.IClassFileEditorInput;
47+
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
48+
import org.eclipse.swt.custom.StyleRange;
49+
import org.eclipse.swt.custom.StyledText;
50+
import org.eclipse.ui.PlatformUI;
51+
import org.junit.Assume;
52+
53+
public class ClassFileEditorHighlightingTest extends AbstractDebugTest {
54+
55+
private static final String CLASS_NAME = "OneToTen";
56+
57+
public ClassFileEditorHighlightingTest(String name) {
58+
super(name);
59+
}
60+
61+
public void testDisplaySourceWithClassFileEditorHighlightsLine() throws Exception {
62+
IJavaProject javaProject = getProjectContext();
63+
createLineBreakpoint(21, CLASS_NAME);
64+
65+
JavaStackFrameSourceDisplayAdapter display = new JavaStackFrameSourceDisplayAdapter();
66+
67+
IJavaThread thread = null;
68+
try {
69+
thread = launchToBreakpoint(CLASS_NAME);
70+
71+
ClassFileEditor editor = (ClassFileEditor) EditorUtility.openInEditor(javaProject.getProject().getFile("bin/" + CLASS_NAME + ".class"));
72+
IClassFileEditorInput editorInput = (IClassFileEditorInput) editor.getEditorInput();
73+
IClassFile classFile = editorInput.getClassFile();
74+
thread.getTopStackFrame().getLaunch().setSourceLocator(stackFrame -> classFile);
75+
76+
openCurrentFrameAndExpectHighlightedText(display, thread, editor, " 0 getstatic java.lang.System.out : java.io.PrintStream [16]");
77+
78+
stepOver((IJavaStackFrame) thread.getTopStackFrame());
79+
openCurrentFrameAndExpectHighlightedText(display, thread, editor, " 8 getstatic java.lang.System.out : java.io.PrintStream [16]");
80+
81+
thread.resume();
82+
83+
} finally {
84+
terminateAndRemove(thread);
85+
removeAllBreakpoints();
86+
}
87+
}
88+
89+
public void testConstructorInPackage() throws Exception {
90+
IJavaProject javaProject = getProjectContext();
91+
createMethodBreakpoint("org.eclipse.debug.tests.targets", "ClassOne.java", "ClassOne", "<init>", "()V", true, false);
92+
93+
JavaStackFrameSourceDisplayAdapter display = new JavaStackFrameSourceDisplayAdapter();
94+
95+
IJavaThread thread = null;
96+
try {
97+
thread = launchToBreakpoint("org.eclipse.debug.tests.targets.CallStack");
98+
99+
ClassFileEditor editor = (ClassFileEditor) EditorUtility.openInEditor(
100+
javaProject.getProject().getFile("bin/org/eclipse/debug/tests/targets/ClassOne.class")
101+
);
102+
IClassFileEditorInput editorInput = (IClassFileEditorInput) editor.getEditorInput();
103+
IClassFile classFile = editorInput.getClassFile();
104+
thread.getTopStackFrame().getLaunch().setSourceLocator(stackFrame -> classFile);
105+
106+
openCurrentFrameAndExpectHighlightedText(display, thread, editor, " 0 aload_0 [this]");
107+
108+
thread.resume();
109+
110+
} finally {
111+
terminateAndRemove(thread);
112+
removeAllBreakpoints();
113+
}
114+
}
115+
116+
public void testDisplaySourceWithClassFileEditorHighlightsLineInConstructor() throws Exception {
117+
IJavaProject javaProject = getProjectContext();
118+
createMethodBreakpoint("", "MethodCall.java", "MethodCall", "<init>", "()V", true, false);
119+
120+
JavaStackFrameSourceDisplayAdapter display = new JavaStackFrameSourceDisplayAdapter();
121+
122+
IJavaThread thread = null;
123+
try {
124+
thread = launchToBreakpoint("MethodCall");
125+
126+
ClassFileEditor editor = (ClassFileEditor) EditorUtility.openInEditor(javaProject.getProject().getFile("bin/MethodCall.class"));
127+
IClassFileEditorInput editorInput = (IClassFileEditorInput) editor.getEditorInput();
128+
IClassFile classFile = editorInput.getClassFile();
129+
thread.getTopStackFrame().getLaunch().setSourceLocator(stackFrame -> classFile);
130+
131+
openCurrentFrameAndExpectHighlightedText(display, thread, editor, " 0 aload_0 [this]");
132+
133+
thread.resume();
134+
} finally {
135+
terminateAndRemove(thread);
136+
removeAllBreakpoints();
137+
}
138+
}
139+
140+
public void testClassFileWithoutDebuggingInformation() throws Exception {
141+
IJavaProject javaProject = getProjectContext();
142+
URI uri = ResourcesPlugin.getWorkspace().getRoot().getFile(javaProject.getOutputLocation()).getLocationURI();
143+
144+
compileWithJavac("NoSources.java", """
145+
class NoSources {
146+
private static int i;
147+
public static void main(String[] args) {
148+
i++;
149+
System.out.println(i);
150+
}
151+
}
152+
""", List.of("-g:none", "-d", new File(uri).getAbsolutePath(), "--release", "8"));
153+
String mainTypeName = "NoSources";
154+
ILaunchConfiguration config = createLaunchConfiguration(javaProject, mainTypeName);
155+
ILaunchConfigurationWorkingCopy workingCopy = config.getWorkingCopy();
156+
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_STOP_IN_MAIN, true);
157+
config = workingCopy.doSave();
158+
159+
JavaStackFrameSourceDisplayAdapter sourceDisplay = new JavaStackFrameSourceDisplayAdapter();
160+
161+
IJavaThread thread = null;
162+
try {
163+
thread = launchToBreakpoint(config);
164+
165+
javaProject.getProject().refreshLocal(IResource.DEPTH_INFINITE, null);
166+
IFile classResource = javaProject.getProject().getFile("bin/" + mainTypeName + ".class");
167+
ClassFileEditor editor = (ClassFileEditor) EditorUtility.openInEditor(classResource, true);
168+
169+
IClassFileEditorInput editorInput = (IClassFileEditorInput) editor.getEditorInput();
170+
IClassFile classFile = editorInput.getClassFile();
171+
thread.getTopStackFrame().getLaunch().setSourceLocator(stackFrame -> classFile);
172+
173+
List<String> expectedHighlights = List.of(/* @formatter:off */
174+
" 0 getstatic NoSources.i : int [7]",
175+
" 3 iconst_1", " 4 iadd",
176+
" 5 putstatic NoSources.i : int [7]",
177+
" 8 getstatic java.lang.System.out : java.io.PrintStream [13]",
178+
" 11 getstatic NoSources.i : int [7]",
179+
" 14 invokevirtual java.io.PrintStream.println(int) : void [19]",
180+
" 17 return"/* @formatter:on */
181+
);
182+
for (int i = 0; i < expectedHighlights.size(); i++) {
183+
openCurrentFrameAndExpectHighlightedText(sourceDisplay, thread, editor, expectedHighlights.get(i));
184+
185+
if (i < expectedHighlights.size() - 1) {
186+
stepOver((IJavaStackFrame) thread.getTopStackFrame());
187+
}
188+
}
189+
thread.resume();
190+
} finally {
191+
terminateAndRemove(thread);
192+
removeAllBreakpoints();
193+
}
194+
}
195+
196+
private void openCurrentFrameAndExpectHighlightedText(JavaStackFrameSourceDisplayAdapter sourceDisplay, IJavaThread thread, ClassFileEditor editor, String expectedHighlightedText) throws DebugException {
197+
StyledText noSourceTextWidget = editor.getNoSourceTextWidget();
198+
IStackFrame topStackFrame = thread.getTopStackFrame();
199+
assertNotNull(topStackFrame);
200+
sourceDisplay.displaySource(topStackFrame, PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), true);
201+
StyleRange[] styleRanges = noSourceTextWidget.getStyleRanges();
202+
assertEquals(1, styleRanges.length);
203+
String highlightedText = noSourceTextWidget.getContent().getTextRange(styleRanges[0].start, styleRanges[0].length);
204+
assertEquals(expectedHighlightedText, highlightedText);
205+
}
206+
207+
private void compileWithJavac(String className, String source, List<String> compilerOptions) {
208+
JavaFileObject fileObject = new SourceJavaFileObject(className, source);
209+
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
210+
Assume.assumeNotNull(compiler);
211+
212+
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, Locale.ROOT, StandardCharsets.UTF_8);
213+
DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
214+
CompilationTask task = compiler.getTask(
215+
null, fileManager, collector, compilerOptions, null, List.of(fileObject)
216+
);
217+
Boolean result = task.call();
218+
assertTrue(String.valueOf(collector.getDiagnostics()), result);
219+
}
220+
221+
private static class SourceJavaFileObject extends SimpleJavaFileObject {
222+
223+
private final String code;
224+
225+
protected SourceJavaFileObject(String name, String code) {
226+
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
227+
this.code = code;
228+
}
229+
230+
@Override
231+
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
232+
return code;
233+
}
234+
}
235+
}

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
}

0 commit comments

Comments
 (0)