Skip to content

Commit 2e2ce2b

Browse files
SougandhSiloveeclipse
authored andcommitted
Add support for statement-level stepping
Introduce a debug preference to enable statement-level stepping. This allows the debugger to skip intermediate bytecode instructions in multi-line statements and suspend only at the next executable source statement. This avoids unnecessary stops at intermediate load instructions during stepping. Fixes : #854 Also-by: Andrey Loskutov <loskutov@gmx.de>
1 parent 33daa6b commit 2e2ce2b

16 files changed

Lines changed: 440 additions & 10 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
public class StatementStep {
15+
16+
public static void main(String[] args) {
17+
String s = ";;";
18+
String s2 = ";;";
19+
String s3 = ";;";
20+
String s4 = ";;";
21+
String s5 = ";;";
22+
tet(
23+
1.0f,
24+
s2,
25+
s3,
26+
tet2(s4,s4),
27+
s5);
28+
s2 = s + s2;
29+
s2 = s + s2;
30+
}
31+
32+
public static String tet(float s, String s2,String s3,String s4,String s5) {
33+
return "sou";
34+
}
35+
public static String tet2(String s, String s2) {
36+
return "sous";
37+
}
38+
39+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
public class StatementStepArgument {
15+
16+
public static void method1(String firstName, String lastName)
17+
{
18+
System.out.println("Method1 " +
19+
" : first name: " + firstName +
20+
" : last name: " + lastName);
21+
}
22+
23+
public static void main(String[] args) {
24+
25+
String firstName = "John";
26+
String lastName = "Smith";
27+
28+
lastName = "Smith";
29+
30+
method1(firstName,
31+
lastName
32+
);
33+
34+
System.out.println("End call method1");
35+
36+
} /* main( args ) */
37+
38+
} /* TestApp */
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
public class StatementStepNested {
15+
16+
public static void main(String[] args) {
17+
String s1 = "A";
18+
String s2 = "B";
19+
String s3 = "C";
20+
String s4 = "D";
21+
22+
test(
23+
s1,
24+
helper1(
25+
s2,
26+
helper1(s3,
27+
helper1(s2,
28+
s3)
29+
)
30+
),
31+
s4
32+
);
33+
34+
System.out.println("Sou");
35+
}
36+
37+
static String helper1(String a, String b) {
38+
return a + b;
39+
}
40+
41+
static String helper2(String a) {
42+
return a;
43+
}
44+
45+
static void test(String a, String b, String c) {
46+
}
47+
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
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.debug.test.stepping;
15+
16+
import org.eclipse.core.runtime.ISafeRunnable;
17+
import org.eclipse.debug.core.DebugEvent;
18+
import org.eclipse.debug.core.model.ILineBreakpoint;
19+
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
20+
import org.eclipse.jdt.debug.core.IJavaStackFrame;
21+
import org.eclipse.jdt.debug.core.IJavaThread;
22+
import org.eclipse.jdt.debug.testplugin.DebugElementKindEventWaiter;
23+
import org.eclipse.jdt.debug.testplugin.DebugEventWaiter;
24+
import org.eclipse.jdt.debug.tests.AbstractDebugTest;
25+
import org.eclipse.jdt.debug.tests.TestUtil;
26+
import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants;
27+
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
28+
import org.eclipse.jface.preference.IPreferenceStore;
29+
30+
public class StatementSteppingTests extends AbstractDebugTest {
31+
32+
private boolean originalPreferenceValue;
33+
34+
public StatementSteppingTests(String name) {
35+
super(name);
36+
}
37+
38+
@Override
39+
protected void setUp() throws Exception {
40+
super.setUp();
41+
originalPreferenceValue = getPrefStore().getBoolean(IJDIPreferencesConstants.PREF_STATEMENT_LEVEL_STEPPING);
42+
if (!originalPreferenceValue) {
43+
getPrefStore().setValue(IJDIPreferencesConstants.PREF_STATEMENT_LEVEL_STEPPING, true);
44+
}
45+
}
46+
47+
@Override
48+
protected void tearDown() throws Exception {
49+
if (originalPreferenceValue != getPrefStore().getBoolean(IJDIPreferencesConstants.PREF_STATEMENT_LEVEL_STEPPING)) {
50+
getPrefStore().setValue(IJDIPreferencesConstants.PREF_STATEMENT_LEVEL_STEPPING, originalPreferenceValue);
51+
}
52+
super.tearDown();
53+
}
54+
55+
/**
56+
* Tests a step over with lots of arguments
57+
*/
58+
public void testLotsOfMultilineArguments() throws Exception {
59+
60+
String typeName = "StatementStep";
61+
ILineBreakpoint bp = createLineBreakpoint(21, typeName);
62+
IJavaDebugTarget javaDebugTarget = null;
63+
IJavaThread t = null;
64+
boolean originalStepFilter = false;
65+
try {
66+
IJavaThread thread = launchToLineBreakpoint(typeName, bp, true);
67+
t = thread;
68+
IJavaStackFrame stackFrame = (IJavaStackFrame) thread.getTopStackFrame();
69+
javaDebugTarget = (IJavaDebugTarget) stackFrame.getDebugTarget();
70+
originalStepFilter = javaDebugTarget.isStepFiltersEnabled();
71+
72+
javaDebugTarget.setStepFiltersEnabled(true);
73+
74+
runAndWaitForSuspendEvent(() -> thread.stepOver());
75+
assertEquals("Suspended at wrong line", 26, stackFrame.getLineNumber());
76+
77+
runAndWaitForSuspendEvent(() -> thread.stepInto());
78+
79+
stackFrame = (IJavaStackFrame) thread.getTopStackFrame();
80+
assertEquals("Suspended at wrong line", 36, stackFrame.getLineNumber());
81+
82+
runAndWaitForSuspendEvent(() -> thread.stepReturn());
83+
84+
stackFrame = (IJavaStackFrame) thread.getTopStackFrame();
85+
assertEquals("Suspended at wrong line", 22, stackFrame.getLineNumber());
86+
87+
thread.stepOver();
88+
runAndWaitForSuspendEvent(() -> thread.stepOver());
89+
90+
assertEquals("Suspended at wrong line", 28, stackFrame.getLineNumber());
91+
} finally {
92+
terminateAndRemove(t);
93+
removeAllBreakpoints();
94+
javaDebugTarget.setStepFiltersEnabled(originalStepFilter);
95+
}
96+
}
97+
98+
public void testStepOverWithSingleMultilineArgument() throws Exception {
99+
100+
String typeName = "StatementStepArgument";
101+
ILineBreakpoint bp = createLineBreakpoint(28, typeName);
102+
IJavaDebugTarget javaDebugTarget = null;
103+
IJavaThread t = null;
104+
boolean originalStepFilter = false;
105+
try {
106+
IJavaThread thread = launchToLineBreakpoint(typeName, bp, true);
107+
t = thread;
108+
IJavaStackFrame stackFrame = (IJavaStackFrame) thread.getTopStackFrame();
109+
javaDebugTarget = (IJavaDebugTarget) stackFrame.getDebugTarget();
110+
originalStepFilter = javaDebugTarget.isStepFiltersEnabled();
111+
112+
javaDebugTarget.setStepFiltersEnabled(true);
113+
runAndWaitForSuspendEvent(() -> thread.stepOver());
114+
assertEquals("Suspended at wrong line", 30, stackFrame.getLineNumber());
115+
116+
runAndWaitForSuspendEvent(() -> thread.stepOver());
117+
assertEquals("Suspended at wrong line", 34, stackFrame.getLineNumber());
118+
119+
} finally {
120+
terminateAndRemove(t);
121+
removeAllBreakpoints();
122+
javaDebugTarget.setStepFiltersEnabled(originalStepFilter);
123+
}
124+
}
125+
126+
public void testStepOverWithNestedMultiLineArgs() throws Exception {
127+
128+
String typeName = "StatementStepNested";
129+
ILineBreakpoint bp = createLineBreakpoint(20, typeName);
130+
IJavaDebugTarget javaDebugTarget = null;
131+
IJavaThread t = null;
132+
boolean originalStepFilter = false;
133+
try {
134+
IJavaThread thread = launchToLineBreakpoint(typeName, bp, true);
135+
t = thread;
136+
IJavaStackFrame stackFrame = (IJavaStackFrame) thread.getTopStackFrame();
137+
javaDebugTarget = (IJavaDebugTarget) stackFrame.getDebugTarget();
138+
originalStepFilter = javaDebugTarget.isStepFiltersEnabled();
139+
140+
javaDebugTarget.setStepFiltersEnabled(true);
141+
runAndWaitForSuspendEvent(() -> thread.stepOver());
142+
assertEquals("Suspended at wrong line", 27, stackFrame.getLineNumber());
143+
144+
runAndWaitForSuspendEvent(() -> thread.stepInto());
145+
stackFrame = (IJavaStackFrame) thread.getTopStackFrame();
146+
assertEquals("Suspended at wrong line", 38, stackFrame.getLineNumber());
147+
148+
runAndWaitForSuspendEvent(() -> thread.stepReturn());
149+
stackFrame = (IJavaStackFrame) thread.getTopStackFrame();
150+
assertEquals("Suspended at wrong line", 26, stackFrame.getLineNumber());
151+
152+
runAndWaitForSuspendEvent(() -> thread.stepOver());
153+
assertEquals("Suspended at wrong line", 24, stackFrame.getLineNumber());
154+
155+
runAndWaitForSuspendEvent(() -> thread.stepOver());
156+
assertEquals("Suspended at wrong line", 22, stackFrame.getLineNumber());
157+
158+
runAndWaitForSuspendEvent(() -> thread.stepOver());
159+
assertEquals("Suspended at wrong line", 34, stackFrame.getLineNumber());
160+
161+
} finally {
162+
terminateAndRemove(t);
163+
removeAllBreakpoints();
164+
javaDebugTarget.setStepFiltersEnabled(originalStepFilter);
165+
}
166+
}
167+
168+
/**
169+
* Returns the <code>JDIDebugUIPlugin</code> preference store
170+
*/
171+
protected IPreferenceStore getPrefStore() {
172+
return JDIDebugUIPlugin.getDefault().getPreferenceStore();
173+
}
174+
175+
private void runAndWaitForSuspendEvent(ISafeRunnable runnable) throws Exception {
176+
DebugEventWaiter waiter = new DebugElementKindEventWaiter(DebugEvent.SUSPEND, IJavaThread.class);
177+
runnable.run();
178+
waiter.waitForEvent();
179+
TestUtil.waitForJobs(getName(), 100, DEFAULT_TIMEOUT);
180+
}
181+
182+
@Override
183+
protected boolean enableUIEventLoopProcessingInWaiter() {
184+
return true;
185+
}
186+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,8 @@ public abstract class AbstractDebugTest extends TestCase implements IEvaluation
217217
"Bug534319earlyStart", "Bug534319lateStart", "Bug534319singleThread", "Bug534319startBetwen", "MethodCall", "Bug538303", "Bug540243",
218218
"OutSync", "OutSync2", "ConsoleOutputUmlaut", "ErrorRecurrence", "ModelPresentationTests", "Bug565982",
219219
"SuspendVMConditionalBreakpointsTestSnippet", "FileConditionSnippet2", "compare.CompareObjectsStringTest", "compare.CompareListObjects",
220-
"compare.CompareMapObjects", "compare.CompareSetObjects", "compare.CompareNormalObjects", "compare.CompareArrayObjects" };
220+
"compare.CompareMapObjects", "compare.CompareSetObjects", "compare.CompareNormalObjects", "compare.CompareArrayObjects",
221+
"StatementStep", "StatementStepArgument", "StatementStepNested" };
221222

222223
/**
223224
* the default timeout

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
@@ -17,6 +17,7 @@
1717

1818
import org.eclipse.core.runtime.Platform;
1919
import org.eclipse.jdt.debug.test.stepping.ForceReturnTests;
20+
import org.eclipse.jdt.debug.test.stepping.StatementSteppingTests;
2021
import org.eclipse.jdt.debug.test.stepping.StepFilterTests;
2122
import org.eclipse.jdt.debug.test.stepping.StepIntoSelectionTests;
2223
import org.eclipse.jdt.debug.test.stepping.StepIntoSelectionWithGenerics;
@@ -251,6 +252,7 @@ public AutomatedSuite() {
251252
if (JavaProjectHelper.isJava6Compatible()) {
252253
addTest(new TestSuite(ForceReturnTests.class));
253254
}
255+
addTest(new TestSuite(StatementSteppingTests.class));
254256

255257
//Classpath tests
256258
addTest(new TestSuite(JavaLibraryPathTests.class));

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2024 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
@@ -77,6 +77,8 @@ public class DebugUIMessages extends NLS {
7777
public static String JavaDebugPreferencePage_ShowStepResult_local;
7878
public static String JavaDebugPreferencePage_ShowStepResult_remote;
7979
public static String JavaDebugPreferencePage_ShowStepTimeout_ms_1;
80+
81+
public static String JavaDebugPreferencePage_StatementLevelStepping;
8082
public static String JavaDebugPreferencePage_Communication_1;
8183
public static String JavaDebugPreferencePage_Debugger__timeout__2;
8284
public static String JavaDebugPreferencePage__Launch_timeout__ms___1;

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ JavaDebugPreferencePage_ShowStepResult_1=Show method result after a step operati
3737
JavaDebugPreferencePage_ShowStepResult_local=Enable for local launc&h types
3838
JavaDebugPreferencePage_ShowStepResult_remote=Enable for remote connections (may be e&ven slower)
3939
JavaDebugPreferencePage_ShowStepTimeout_ms_1=Don't show &if step operation takes longer than (ms):
40+
JavaDebugPreferencePage_StatementLevelStepping=Filter intermediate instructions while stepping
4041
JavaDebugPreferencePage_Communication_1=Communication
4142
JavaDebugPreferencePage_Debugger__timeout__2=Debugger &timeout (ms):
4243
JavaDebugPreferencePage__Launch_timeout__ms___1=&Launch timeout (ms):

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/IJDIPreferencesConstants.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2011 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
@@ -269,4 +269,9 @@ public interface IJDIPreferencesConstants {
269269
*/
270270
public static final String PREF_PROMPT_DELETE_CONDITIONAL_BREAKPOINT= IJavaDebugUIConstants.PLUGIN_ID + ".prompt_delete_conditional_breakpoint"; //$NON-NLS-1$
271271

272+
/**
273+
* Boolean preference controlling whether debugger should suspend only for statement-level instructions.
274+
*/
275+
public static final String PREF_STATEMENT_LEVEL_STEPPING = IJavaDebugUIConstants.PLUGIN_ID + ".statement_only_stepping"; //$NON-NLS-1$
276+
272277
}

0 commit comments

Comments
 (0)