Skip to content

Commit 07c57f0

Browse files
committed
Allow stepping through disassembly
1 parent a4aa8d5 commit 07c57f0

6 files changed

Lines changed: 362 additions & 21 deletions

File tree

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));

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
import java.util.Enumeration;
1818

19+
import junit.framework.Test;
20+
import junit.framework.TestResult;
21+
import junit.framework.TestSuite;
1922
import org.eclipse.jdt.debug.tests.eval.ArrayAllocationTests;
2023
import org.eclipse.jdt.debug.tests.eval.ArrayAssignmentTests;
2124
import org.eclipse.jdt.debug.tests.eval.ArrayValueTests;
@@ -103,12 +106,10 @@
103106
import org.eclipse.jdt.debug.tests.eval.TypeHierarchy_68_3;
104107
import org.eclipse.jdt.debug.tests.eval.VariableDeclarationTests;
105108
import org.eclipse.jdt.debug.tests.eval.XfixOperatorsTests;
109+
import org.eclipse.jdt.debug.tests.sourcelookup.ClassFileEditorHighlightingTest;
110+
import org.eclipse.jdt.debug.tests.ui.DebugSelectionTests;
106111
import org.eclipse.swt.widgets.Display;
107112

108-
import junit.framework.Test;
109-
import junit.framework.TestResult;
110-
import junit.framework.TestSuite;
111-
112113
/**
113114
* Test all areas of the UI.
114115
*/
@@ -238,6 +239,9 @@ public EvalTestSuite() {
238239

239240
addTest(new TestSuite(DebugShellVariableTests.class));
240241

242+
addTest(new TestSuite(DebugSelectionTests.class));
243+
addTest(new TestSuite(ClassFileEditorHighlightingTest.class));
244+
241245
}
242246

243247
/**
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: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
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.TestUtil;
43+
import org.eclipse.jdt.debug.tests.ui.AbstractDebugUiTests;
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.junit.Assume;
51+
52+
public class ClassFileEditorHighlightingTest extends AbstractDebugUiTests {
53+
54+
private static final String CLASS_NAME = "OneToTen";
55+
private static final long TIMEOUT = 10_000;
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+
IJavaThread thread = null;
66+
try {
67+
thread = launchToBreakpoint(CLASS_NAME);
68+
69+
ClassFileEditor editor = (ClassFileEditor) EditorUtility.openInEditor(javaProject.getProject().getFile("bin/" + CLASS_NAME + ".class"));
70+
IClassFileEditorInput editorInput = (IClassFileEditorInput) editor.getEditorInput();
71+
IClassFile classFile = editorInput.getClassFile();
72+
thread.getTopStackFrame().getLaunch().setSourceLocator(stackFrame -> classFile);
73+
74+
openCurrentFrameAndExpectHighlightedText(thread, editor, " 0 getstatic java.lang.System.out : java.io.PrintStream [16]");
75+
76+
stepOver((IJavaStackFrame) thread.getTopStackFrame());
77+
openCurrentFrameAndExpectHighlightedText(thread, editor, " 8 getstatic java.lang.System.out : java.io.PrintStream [16]");
78+
79+
thread.resume();
80+
81+
} finally {
82+
terminateAndRemove(thread);
83+
removeAllBreakpoints();
84+
}
85+
}
86+
87+
public void testConstructorInPackage() throws Exception {
88+
IJavaProject javaProject = getProjectContext();
89+
createMethodBreakpoint("org.eclipse.debug.tests.targets", "ClassOne.java", "ClassOne", "<init>", "()V", true, false);
90+
91+
IJavaThread thread = null;
92+
try {
93+
thread = launchToBreakpoint("org.eclipse.debug.tests.targets.CallStack");
94+
95+
ClassFileEditor editor = (ClassFileEditor) EditorUtility.openInEditor(
96+
javaProject.getProject().getFile("bin/org/eclipse/debug/tests/targets/ClassOne.class")
97+
);
98+
IClassFileEditorInput editorInput = (IClassFileEditorInput) editor.getEditorInput();
99+
IClassFile classFile = editorInput.getClassFile();
100+
thread.getTopStackFrame().getLaunch().setSourceLocator(stackFrame -> classFile);
101+
102+
openCurrentFrameAndExpectHighlightedText(thread, editor, " 0 aload_0 [this]");
103+
104+
thread.resume();
105+
106+
} finally {
107+
terminateAndRemove(thread);
108+
removeAllBreakpoints();
109+
}
110+
}
111+
112+
public void testDisplaySourceWithClassFileEditorHighlightsLineInConstructor() throws Exception {
113+
IJavaProject javaProject = getProjectContext();
114+
createMethodBreakpoint("", "MethodCall.java", "MethodCall", "<init>", "()V", true, false);
115+
116+
IJavaThread thread = null;
117+
try {
118+
thread = launchToBreakpoint("MethodCall");
119+
120+
ClassFileEditor editor = (ClassFileEditor) EditorUtility.openInEditor(javaProject.getProject().getFile("bin/MethodCall.class"));
121+
IClassFileEditorInput editorInput = (IClassFileEditorInput) editor.getEditorInput();
122+
IClassFile classFile = editorInput.getClassFile();
123+
thread.getTopStackFrame().getLaunch().setSourceLocator(stackFrame -> classFile);
124+
125+
openCurrentFrameAndExpectHighlightedText(thread, editor, " 0 aload_0 [this]");
126+
127+
thread.resume();
128+
} finally {
129+
terminateAndRemove(thread);
130+
removeAllBreakpoints();
131+
}
132+
}
133+
134+
public void testClassFileWithoutDebuggingInformation() throws Exception {
135+
IJavaProject javaProject = getProjectContext();
136+
URI uri = ResourcesPlugin.getWorkspace().getRoot().getFile(javaProject.getOutputLocation()).getLocationURI();
137+
138+
compileWithJavac("NoSources.java", """
139+
class NoSources {
140+
private static int i;
141+
public static void main(String[] args) {
142+
i++;
143+
System.out.println(i);
144+
}
145+
}
146+
""", List.of("-g:none", "-d", new File(uri).getAbsolutePath(), "--release", "8"));
147+
String mainTypeName = "NoSources";
148+
ILaunchConfiguration config = createLaunchConfiguration(javaProject, mainTypeName);
149+
ILaunchConfigurationWorkingCopy workingCopy = config.getWorkingCopy();
150+
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_STOP_IN_MAIN, true);
151+
config = workingCopy.doSave();
152+
153+
IJavaThread thread = null;
154+
try {
155+
thread = launchToBreakpoint(config);
156+
157+
javaProject.getProject().refreshLocal(IResource.DEPTH_INFINITE, null);
158+
IFile classResource = javaProject.getProject().getFile("bin/" + mainTypeName + ".class");
159+
ClassFileEditor editor = (ClassFileEditor) EditorUtility.openInEditor(classResource, true);
160+
161+
IClassFileEditorInput editorInput = (IClassFileEditorInput) editor.getEditorInput();
162+
IClassFile classFile = editorInput.getClassFile();
163+
thread.getTopStackFrame().getLaunch().setSourceLocator(stackFrame -> classFile);
164+
165+
List<String> expectedHighlights = List.of(/* @formatter:off */
166+
" 0 getstatic NoSources.i : int [7]", " 3 iconst_1", " 4 iadd", " 5 putstatic NoSources.i : int [7]", " 8 getstatic java.lang.System.out : java.io.PrintStream [13]", " 11 getstatic NoSources.i : int [7]", " 14 invokevirtual java.io.PrintStream.println(int) : void [19]", " 17 return"/*
167+
* @formatter
168+
* :
169+
* on
170+
*/
171+
);
172+
for (int i = 0; i < expectedHighlights.size(); i++) {
173+
openCurrentFrameAndExpectHighlightedText(thread, editor, expectedHighlights.get(i));
174+
175+
if (i < expectedHighlights.size() - 1) {
176+
stepOver((IJavaStackFrame) thread.getTopStackFrame());
177+
}
178+
}
179+
thread.resume();
180+
} finally {
181+
terminateAndRemove(thread);
182+
removeAllBreakpoints();
183+
}
184+
}
185+
186+
private void openCurrentFrameAndExpectHighlightedText(IJavaThread thread, ClassFileEditor editor, String expectedHighlightedText) throws DebugException, InterruptedException {
187+
StyledText noSourceTextWidget = editor.getNoSourceTextWidget();
188+
IStackFrame topStackFrame = thread.getTopStackFrame();
189+
assertNotNull(topStackFrame);
190+
// sourceDisplay.displaySource(topStackFrame, PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), true);
191+
192+
long s = System.currentTimeMillis();
193+
while (System.currentTimeMillis() - s < TIMEOUT) {
194+
if (noSourceTextWidget.getStyleRanges().length != 0) {
195+
return;
196+
}
197+
TestUtil.runEventLoop();
198+
Thread.sleep(50L);
199+
}
200+
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(null, fileManager, collector, compilerOptions, null, List.of(fileObject));
215+
Boolean result = task.call();
216+
assertTrue(String.valueOf(collector.getDiagnostics()), result);
217+
}
218+
219+
private static class SourceJavaFileObject extends SimpleJavaFileObject {
220+
221+
private final String code;
222+
223+
protected SourceJavaFileObject(String name, String code) {
224+
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
225+
this.code = code;
226+
}
227+
228+
@Override
229+
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
230+
return code;
231+
}
232+
}
233+
}

0 commit comments

Comments
 (0)