diff --git a/api.debugger.jpda/apichanges.xml b/api.debugger.jpda/apichanges.xml
--- a/api.debugger.jpda/apichanges.xml
+++ b/api.debugger.jpda/apichanges.xml
@@ -922,6 +922,22 @@
+
+
+
+ Enhanced smart-stepping.
+
+
+
+
+
+ SmartSteppingCallback.stopAt()
method added to be able
+ to override the default smart-stepping logic and provide specific
+ steps that should be performed at given locations.
+
+
+
+
diff --git a/api.debugger.jpda/manifest.mf b/api.debugger.jpda/manifest.mf
--- a/api.debugger.jpda/manifest.mf
+++ b/api.debugger.jpda/manifest.mf
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.api.debugger.jpda/2
OpenIDE-Module-Localizing-Bundle: org/netbeans/api/debugger/jpda/Bundle.properties
-OpenIDE-Module-Specification-Version: 3.4
+OpenIDE-Module-Specification-Version: 3.5
OpenIDE-Module-Package-Dependencies: com.sun.jdi[VirtualMachineManager]
diff --git a/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/SmartSteppingCallback.java b/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/SmartSteppingCallback.java
--- a/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/SmartSteppingCallback.java
+++ b/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/SmartSteppingCallback.java
@@ -49,6 +49,9 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
+import java.util.Objects;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.JPDAStep;
import org.netbeans.api.debugger.jpda.JPDAThread;
import org.netbeans.api.debugger.jpda.SmartSteppingFilter;
import org.netbeans.modules.debugger.jpda.apiregistry.DebuggerProcessor;
@@ -85,6 +88,153 @@
*/
public abstract boolean stopHere (ContextProvider lookupProvider, JPDAThread thread, SmartSteppingFilter f);
+ /**
+ * This is an enhanced version of former {@link #stopHere(org.netbeans.spi.debugger.ContextProvider, org.netbeans.api.debugger.jpda.JPDAThread, org.netbeans.api.debugger.jpda.SmartSteppingFilter)}
+ * method, which is called during stepping through debugged application.
+ * The frame
argument allows this method to be called for specific stack frames,
+ * to check if the execution could be suspended there. This is valuable e.g. for step out.
+ * When a top frame is provided, this method can suggest a specific step to continue with,
+ * in case it's not possible to stop at the given location.
+ *
+ * The default implementation calls {@link #stopHere(org.netbeans.spi.debugger.ContextProvider, org.netbeans.api.debugger.jpda.JPDAThread, org.netbeans.api.debugger.jpda.SmartSteppingFilter)}
+ * when called with a top frame and throws an {@link UnsupportedOperationException} otherwise.
+ *
+ * @param lookupProvider The debugger services lookup
+ * @param frame The frame in question
+ * @param f a filter
+ * @return whether the debugger can stop at this location, or whether it should continue with a step.
+ * @since 3.5
+ */
+ public StopOrStep stopAt(ContextProvider lookupProvider, CallStackFrame frame, SmartSteppingFilter f) {
+ int depth = frame.getFrameDepth();
+ if (depth > 0) {
+ throw new UnsupportedOperationException("Not supporting frames with depth > 0");
+ }
+ boolean stop = stopHere(lookupProvider, frame.getThread(), f);
+ return stop ? StopOrStep.stop() : StopOrStep.skip();
+ }
+
+ /**
+ * Information about a possibility to stop at the given location,
+ * or suggestion to perform a step.
+ * Used by {@link #stopAt(org.netbeans.spi.debugger.ContextProvider, org.netbeans.api.debugger.jpda.CallStackFrame, org.netbeans.api.debugger.jpda.SmartSteppingFilter)}
+ * @since 3.5
+ */
+ public static final class StopOrStep {
+
+ private static final StopOrStep STOP = new StopOrStep(true, 0, 0);
+ private static final StopOrStep SKIP = new StopOrStep(false, 0, 0);
+
+ private final boolean stop;
+ private final int stepSize;
+ private final int stepDepth;
+
+ /**
+ * Express the possibility to stop.
+ * @return information about a possibility to stop at the given location.
+ */
+ public static StopOrStep stop() {
+ return STOP;
+ }
+
+ /**
+ * Express the necessity to skip the given location,
+ * using whatever the default debugger action is at the moment.
+ * @return information about the necessity to skip the given location.
+ */
+ public static StopOrStep skip() {
+ return SKIP;
+ }
+
+ /**
+ * Express the necessity to perform a step at the given location.
+ * @param stepSize the step size,
+ * one of {@link #JPDAStep.STEP_LINE} or {@link #JPDAStep.STEP_MIN},
+ * or 0
for the default size.
+ * @param stepDepth the step depth,
+ * one of {@link #JPDAStep.STEP_INTO}, {@link #JPDAStep.STEP_OVER},
+ * {@link #JPDAStep.STEP_OUT}, or 0
for the default depth.
+ * @return the step information instance.
+ * throws {@link IllegalArgumentException} when the size or depth is wrong.
+ */
+ public static StopOrStep step(int stepSize, int stepDepth) {
+ return new StopOrStep(false, stepSize, stepDepth);
+ }
+
+ private StopOrStep(boolean stop, int stepSize, int stepDepth) {
+ this.stop = stop;
+ switch (stepSize) {
+ case 0:
+ case JPDAStep.STEP_MIN:
+ case JPDAStep.STEP_LINE:
+ // O.K.
+ break;
+ default:
+ throw new IllegalArgumentException("Wrong step size: "+stepSize);
+ }
+ switch (stepDepth) {
+ case 0:
+ case JPDAStep.STEP_INTO:
+ case JPDAStep.STEP_OVER:
+ case JPDAStep.STEP_OUT:
+ // O.K.
+ break;
+ default:
+ throw new IllegalArgumentException("Wrong step depth: "+stepDepth);
+ }
+ this.stepSize = stepSize;
+ this.stepDepth = stepDepth;
+ }
+
+ /**
+ * Whether this is a possibility to stop.
+ * If yes, values of step size and step depth are irrelevant.
+ * @return true
if it's possible to stop, false
otherwise.
+ */
+ public boolean isStop() {
+ return stop;
+ }
+
+ /**
+ * Get the step size.
+ * @return One of {@link #JPDAStep.STEP_LINE} or {@link #JPDAStep.STEP_MIN},
+ * or 0
for the default size.
+ */
+ public int getStepSize() {
+ return stepSize;
+ }
+
+ /**
+ * Get the step depth.
+ * @return One of {@link #JPDAStep.STEP_INTO}, {@link #JPDAStep.STEP_OVER},
+ * {@link #JPDAStep.STEP_OUT}, or 0
for the default depth.
+ */
+ public int getStepDepth() {
+ return stepDepth;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof StopOrStep)) {
+ return false;
+ }
+ StopOrStep ss = (StopOrStep) obj;
+ return stop == ss.stop &&
+ stepSize == ss.stepSize &&
+ stepDepth == ss.stepDepth;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(stop, stepSize, stepDepth);
+ }
+
+ @Override
+ public String toString() {
+ return "StopOrStep["+stop+","+stepSize+","+stepDepth+"]";
+ }
+
+ }
/**
* Declarative registration of a SmartSteppingCallback implementation.