You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by sk...@apache.org on 2022/05/05 08:59:27 UTC

[netbeans] branch delivery updated: Smart stepping implemented for Groovy. SmartSteppingFilterWrapper introduced as a step-specific filter.

This is an automated email from the ASF dual-hosted git repository.

skygo pushed a commit to branch delivery
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/delivery by this push:
     new d1de9d57a2 Smart stepping implemented for Groovy. SmartSteppingFilterWrapper introduced as a step-specific filter.
     new 35437b17bc Merge pull request #4073 from ebarboni/deliverymissingpr2
d1de9d57a2 is described below

commit d1de9d57a28323cf8747adb5d9fb99ce98120de0
Author: Martin Entlicher <ma...@oracle.com>
AuthorDate: Wed May 4 18:36:01 2022 +0200

    Smart stepping implemented for Groovy. SmartSteppingFilterWrapper introduced as a step-specific filter.
---
 .../groovy/support/debug/GroovySmartStepping.java  | 105 +++++++++++++++++++++
 .../spi/debugger/jpda/SmartSteppingCallback.java   |  13 ++-
 .../debugger/jpda/ui/SmartSteppingImpl.java        |  12 +--
 .../ui/actions/JPDAMethodChooserFactoryUIImpl.java |   6 ++
 .../modules/debugger/jpda/JPDADebuggerImpl.java    |  11 +--
 .../modules/debugger/jpda/JPDAStepImpl.java        |  36 ++-----
 .../jpda/actions/SmartSteppingFilterImpl.java      |  47 +--------
 .../jpda/actions/SmartSteppingFilterWrapper.java   |  87 +++++++++++++++++
 .../debugger/jpda/actions/StepActionProvider.java  |  49 +++++++---
 .../debugger/jpda/actions/StepIntoNextMethod.java  |  41 ++++----
 10 files changed, 287 insertions(+), 120 deletions(-)

diff --git a/groovy/groovy.support/src/org/netbeans/modules/groovy/support/debug/GroovySmartStepping.java b/groovy/groovy.support/src/org/netbeans/modules/groovy/support/debug/GroovySmartStepping.java
new file mode 100644
index 0000000000..946a74f786
--- /dev/null
+++ b/groovy/groovy.support/src/org/netbeans/modules/groovy/support/debug/GroovySmartStepping.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.groovy.support.debug;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+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.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.jpda.SmartSteppingCallback;
+
+/**
+ * Stepping in Groovy, steps through the language implementation.
+ */
+@SmartSteppingCallback.Registration(path="netbeans-JPDASession")
+public class GroovySmartStepping extends SmartSteppingCallback {
+
+    private static final String[] GROOVY_PACKAGES = { "org.codehaus.groovy.", "org.apache.groovy.", "groovy.", "groovyjar" };   // NOI18N
+    private static final Set<String> PATTERNS_SKIP_IN_GROOVY = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("java.", "jdk.internal.", "sun.", "com.sun."))); // NOI18N
+    private static final Set<String> FILTERS_SKIP_IN_GROOVY = PATTERNS_SKIP_IN_GROOVY.stream().map(pattern -> pattern + '*').collect(Collectors.toSet());
+
+    private static final Logger logger = Logger.getLogger(GroovySmartStepping.class.getName());
+
+    private final Map<SmartSteppingFilter, Boolean> steppingInGroovy = Collections.synchronizedMap(new WeakHashMap<>());
+
+    @Override
+    public void initFilter (SmartSteppingFilter filter) {
+    }
+
+    @Override
+    public boolean stopHere (ContextProvider lookupProvider, JPDAThread thread, SmartSteppingFilter filter) {
+        return true;
+    }
+
+    @Override
+    public StopOrStep stopAt(ContextProvider lookupProvider, CallStackFrame frame, SmartSteppingFilter filter) {
+        String className = frame.getClassName();
+        if (logger.isLoggable(Level.FINE)) {
+            logger.fine("GroovySmartStepping.stopAt("+className+")");
+        }
+        JPDAThread thread = frame.getThread();
+        boolean inGroovy = false;
+        for (String gp : GROOVY_PACKAGES) {
+            if (className.startsWith(gp)) {
+                inGroovy = true;
+                steppingInGroovy.put(filter, true);
+                break;
+            }
+        }
+        if (inGroovy) {
+            // We need to step through the Groovy packages
+            logger.fine(" => In Groovy: Step In");
+            return StopOrStep.step(0, JPDAStep.STEP_INTO);
+        }
+        if (Boolean.TRUE.equals(steppingInGroovy.get(filter))) {
+            if (isPackageToSkip(className)) {
+                logger.fine(" => Was In Groovy: Step In");
+                filter.addExclusionPatterns(FILTERS_SKIP_IN_GROOVY);
+                return StopOrStep.step(0, JPDAStep.STEP_INTO);
+            } else {
+                logger.fine(" => FINISHED, stop");
+                return StopOrStep.stop();
+            }
+        } else {
+            logger.fine(" => FINISHED (no Groovy), stop");
+            return StopOrStep.stop();
+        }
+    }
+
+    private static boolean isPackageToSkip(String className) {
+        for (String pattern : PATTERNS_SKIP_IN_GROOVY) {
+            if (className.startsWith(pattern)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/java/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/SmartSteppingCallback.java b/java/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/SmartSteppingCallback.java
index b9f831e9ec..e905d02b27 100644
--- a/java/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/SmartSteppingCallback.java
+++ b/java/api.debugger.jpda/src/org/netbeans/spi/debugger/jpda/SmartSteppingCallback.java
@@ -26,6 +26,7 @@ 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.JPDADebugger;
 import org.netbeans.api.debugger.jpda.JPDAStep;
 import org.netbeans.api.debugger.jpda.JPDAThread;
 import org.netbeans.api.debugger.jpda.SmartSteppingFilter;
@@ -55,10 +56,14 @@ public abstract class SmartSteppingCallback {
      * This method is called during stepping through debugged application.
      * The execution is stopped when all registered <code>SmartSteppingCallback</code>s
      * returns true.
+     * <p>
+     * The {@link SmartSteppingFilter} instance is unique per step. Any changes to it applies
+     * to one ongoing step only. Use {@link JPDADebugger#getSmartSteppingFilter()} to make
+     * persistent changes.
      *
      * @param thread contains all available information about current position
      *        in debugged application
-     * @param f a filter
+     * @param f a filter, unique to the ongoing step
      * @return true if execution should be stopped on the current position
      */
     public abstract boolean stopHere (ContextProvider lookupProvider, JPDAThread thread, SmartSteppingFilter f);
@@ -70,13 +75,17 @@ public abstract class SmartSteppingCallback {
      * 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.
+     * <p>
+     * The {@link SmartSteppingFilter} instance is unique per step. Any changes to it applies
+     * to one ongoing step only. Register a {@link SmartSteppingFilter} implementation
+     * to make persistent changes.
      * 
      * 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
+     * @param f a filter, unique to the ongoing step
      * @return whether the debugger can stop at this location, or whether it should continue with a step.
      * @since 3.5
      */
diff --git a/java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/SmartSteppingImpl.java b/java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/SmartSteppingImpl.java
index e25fcb12ae..8c0f6f7e96 100644
--- a/java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/SmartSteppingImpl.java
+++ b/java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/SmartSteppingImpl.java
@@ -153,15 +153,15 @@ PropertyChangeListener {
             
         HashSet s = new HashSet ();
         s.add (name.replace ('/', '.') + ".*");
-        addExclusionPatterns (s);
+        addExclusionPatterns (s, f);
         return false;
     }
     
-    private void addExclusionPatterns (
-        Set ep
-    ) {
-        smartSteppingFilter.addExclusionPatterns (ep);
-        exclusionPatterns.addAll (ep);
+    private void addExclusionPatterns(Set ep, SmartSteppingFilter f) {
+        f.addExclusionPatterns (ep);
+        if (f == smartSteppingFilter) {
+            exclusionPatterns.addAll (ep);
+        }
     }
     
     private void removeExclusionPatterns () {
diff --git a/java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/actions/JPDAMethodChooserFactoryUIImpl.java b/java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/actions/JPDAMethodChooserFactoryUIImpl.java
index d2ebe98798..451c6836ab 100644
--- a/java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/actions/JPDAMethodChooserFactoryUIImpl.java
+++ b/java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/actions/JPDAMethodChooserFactoryUIImpl.java
@@ -20,6 +20,8 @@
 package org.netbeans.modules.debugger.jpda.ui.actions;
 
 import com.sun.jdi.ReferenceType;
+import java.awt.GraphicsEnvironment;
+import javax.swing.SwingUtilities;
 import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
 import org.netbeans.modules.debugger.jpda.actions.JPDAMethodChooserFactory;
 import org.netbeans.modules.debugger.jpda.actions.StepIntoActionProvider;
@@ -38,6 +40,10 @@ public class JPDAMethodChooserFactoryUIImpl implements JPDAMethodChooserFactory
 
     @Override
     public boolean initChooserUI(JPDADebuggerImpl debugger, String url, ReferenceType clazz, int methodLine) {
+        if (GraphicsEnvironment.isHeadless()) {
+            // Not supported in headless environment
+            return false;
+        }
         final MethodChooserSupport cSupport = new MethodChooserSupport(debugger, url, clazz, methodLine);
         boolean continuedDirectly = cSupport.init();
         if (cSupport.getSegmentsCount() == 0) {
diff --git a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java
index b3c3073977..16db46b46d 100644
--- a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java
+++ b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java
@@ -615,7 +615,7 @@ public class JPDADebuggerImpl extends JPDADebugger {
     /**
      * Test whether we should stop here according to the smart-stepping rules.
      */
-    StopOrStep stopHere(JPDAThread t) {
+    StopOrStep stopHere(JPDAThread t, SmartSteppingFilter filter) {
         CallStackFrame topFrame = null;
         try {
             CallStackFrame[] topFrameArr = t.getCallStack(0, 1);
@@ -624,12 +624,11 @@ public class JPDADebuggerImpl extends JPDADebugger {
             }
         } catch (AbsentInformationException aiex) {}
         if (topFrame != null) {
-            return getCompoundSmartSteppingListener().stopAt
-                        (lookupProvider, topFrame, getSmartSteppingFilter());
+            return getCompoundSmartSteppingListener().stopAt(lookupProvider, topFrame, filter);
         } else {
-            return getCompoundSmartSteppingListener().stopHere
-                        (lookupProvider, t, getSmartSteppingFilter()) ?
-                    StopOrStep.stop() : StopOrStep.skip();
+            return getCompoundSmartSteppingListener().stopHere(lookupProvider, t, filter) ?
+                    StopOrStep.stop() :
+                    StopOrStep.skip();
         }
     }
 
diff --git a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDAStepImpl.java b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDAStepImpl.java
index 2a3706fd16..e4bc7f25b0 100644
--- a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDAStepImpl.java
+++ b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDAStepImpl.java
@@ -55,12 +55,11 @@ import org.netbeans.api.debugger.jpda.JPDADebugger;
 import org.netbeans.api.debugger.jpda.JPDAStep;
 import org.netbeans.api.debugger.jpda.JPDAThread;
 import org.netbeans.api.debugger.jpda.MethodBreakpoint;
-import org.netbeans.api.debugger.jpda.SmartSteppingFilter;
 import org.netbeans.api.debugger.jpda.Variable;
 import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
 import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener;
-import org.netbeans.modules.debugger.jpda.actions.CompoundSmartSteppingListener;
-import org.netbeans.modules.debugger.jpda.actions.SmartSteppingFilterImpl;
+import org.netbeans.modules.debugger.jpda.actions.SmartSteppingFilterWrapper;
+import org.netbeans.modules.debugger.jpda.actions.StepActionProvider;
 import org.netbeans.modules.debugger.jpda.actions.StepIntoActionProvider;
 import org.netbeans.modules.debugger.jpda.impl.StepUtils;
 import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
@@ -119,12 +118,14 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
     private boolean wasInBoxingUnboxingLocation;
     private StopHereCheck stopHereCheck;
     private final Properties p;
+    private final SmartSteppingFilterWrapper smartSteppingFilter;
     
     private final Session session;
     
     public JPDAStepImpl(JPDADebugger debugger, Session session, int size, int depth) {
         super(debugger, size, depth);
         this.session = session;
+        this.smartSteppingFilter = new SmartSteppingFilterWrapper(debugger.getSmartSteppingFilter());
         p = Properties.getDefault().getProperties("debugger.options.JPDA"); // NOI18N
     }
     
@@ -134,7 +135,7 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
         if (ignoreStepFilters || steppingFromFilteredLocation) {
             exclusionPatterns = stepFilters;
         } else {
-            exclusionPatterns = debugger.getSmartSteppingFilter().getExclusionPatterns();
+            exclusionPatterns = smartSteppingFilter.getExclusionPatterns();
             if (stepFilters != null) {
                 int epl = exclusionPatterns.length;
                 exclusionPatterns = Arrays.copyOf(exclusionPatterns, epl + stepFilters.length);
@@ -153,7 +154,7 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
     }
     
     private String[] getCurrentExclusionPatterns() {
-        String[] exclusionPatterns = debugger.getSmartSteppingFilter().getExclusionPatterns();
+        String[] exclusionPatterns = smartSteppingFilter.getExclusionPatterns();
         String[] stepFilters = getSteppingFilters();
         if (stepFilters != null) {
             int epl = exclusionPatterns.length;
@@ -200,8 +201,8 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
                 //SingleThreadedStepWatch.stepRequestDeleted(stepRequest);
                 debuggerImpl.getOperator().unregister(stepRequest);
             }
-            steppingFromFilteredLocation = !((SmartSteppingFilterImpl) debuggerImpl.getSmartSteppingFilter()).stopHere(tr.getClassName());
-            steppingFromCompoundFilteredLocation = !debuggerImpl.stopHere(tr).isStop();
+            steppingFromFilteredLocation = !StepActionProvider.stopInClass(tr.getClassName(), smartSteppingFilter);
+            steppingFromCompoundFilteredLocation = !debuggerImpl.stopHere(tr, smartSteppingFilter).isStop();
             int size = getSize();
             boolean stepAdded = false;
             if (logger.isLoggable(Level.FINE)) {
@@ -903,7 +904,7 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
                         if (steppingFromCompoundFilteredLocation) {
                             stop = StopOrStep.stop();
                         } else {
-                            stop = debuggerImpl.stopHere(t);
+                            stop = debuggerImpl.stopHere(t, smartSteppingFilter);
                         }
                         if (stop.isStop() && !steppingFromFilteredLocation) {
                             String[] exclusionPatterns = getCurrentExclusionPatterns();
@@ -976,7 +977,7 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
                 }
 
                 // Not synthetic
-                StopOrStep stop = debuggerImpl.stopHere(t);
+                StopOrStep stop = debuggerImpl.stopHere(t, smartSteppingFilter);
                 if (stop.isStop()) {
                     //S ystem.out.println("/nStepAction.exec end - do not resume");
                     return false; // do not resume
@@ -1124,23 +1125,6 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
         return topFrame;
     }
     
-    private SmartSteppingFilterImpl smartSteppingFilter;
-
-    private SmartSteppingFilterImpl getSmartSteppingFilterImpl () {
-        if (smartSteppingFilter == null) {
-            smartSteppingFilter = (SmartSteppingFilterImpl) session.lookupFirst(null, SmartSteppingFilter.class);
-        }
-        return smartSteppingFilter;
-    }
-
-    private CompoundSmartSteppingListener compoundSmartSteppingListener;
-
-    private CompoundSmartSteppingListener getCompoundSmartSteppingListener () {
-        if (compoundSmartSteppingListener == null)
-            compoundSmartSteppingListener = session.lookupFirst(null, CompoundSmartSteppingListener.class);
-        return compoundSmartSteppingListener;
-    }
-
     public void setIgnoreStepFilters(boolean ignoreStepFilters) {
         this.ignoreStepFilters = ignoreStepFilters;
     }
diff --git a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/SmartSteppingFilterImpl.java b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/SmartSteppingFilterImpl.java
index 276f483423..cf2b4078a9 100644
--- a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/SmartSteppingFilterImpl.java
+++ b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/SmartSteppingFilterImpl.java
@@ -21,10 +21,8 @@ package org.netbeans.modules.debugger.jpda.actions;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.Set;
 import org.netbeans.api.debugger.Properties;
 import org.netbeans.api.debugger.jpda.SmartSteppingFilter;
@@ -40,9 +38,6 @@ import org.openide.util.WeakListeners;
 public class SmartSteppingFilterImpl implements SmartSteppingFilter {
 
     private final HashSet<String> filter = new HashSet<String>();
-    private final ArrayList<String> exact = new ArrayList<String>();
-    private final ArrayList<String> start = new ArrayList<String>();
-    private final ArrayList<String> end = new ArrayList<String>();
     private final PropertyChangeSupport pcs;
     {pcs = new PropertyChangeSupport (this);}
     private final Properties options = Properties.getDefault().
@@ -76,11 +71,7 @@ public class SmartSteppingFilterImpl implements SmartSteppingFilter {
         }
         synchronized (filter) {
             filter.clear();
-            exact.clear();
-            start.clear();
-            end.clear();
             filter.addAll (patterns);
-            refreshFilters (patterns);
         }
         pcs.firePropertyChange (PROP_EXCLUSION_PATTERNS, null, patterns);
     }
@@ -102,7 +93,6 @@ public class SmartSteppingFilterImpl implements SmartSteppingFilter {
 
         synchronized (filter) {
             filter.addAll (reallyNew);
-            refreshFilters (reallyNew);
         }
 
         pcs.firePropertyChange (PROP_EXCLUSION_PATTERNS, null, reallyNew);
@@ -117,10 +107,6 @@ public class SmartSteppingFilterImpl implements SmartSteppingFilter {
     public void removeExclusionPatterns (Set<String> patterns) {
         synchronized (filter) {
             filter.removeAll (patterns);
-            exact.clear();
-            start.clear();
-            end.clear();
-            refreshFilters (filter);
         }
 
         pcs.firePropertyChange (PROP_EXCLUSION_PATTERNS, patterns, null);
@@ -138,21 +124,8 @@ public class SmartSteppingFilterImpl implements SmartSteppingFilter {
     }
 
     public boolean stopHere (String className) {
-        synchronized (filter) {
-            int i, k = exact.size ();
-            for (i = 0; i < k; i++) {
-                if (exact.get (i).equals (className)) return false;
-            }
-            k = start.size ();
-            for (i = 0; i < k; i++) {
-                if (className.startsWith (start.get (i))) return false;
-            }
-            k = end.size ();
-            for (i = 0; i < k; i++) {
-                if (className.endsWith (end.get (i))) return false;
-            }
-        }
-        return true;
+        // Retained for compatibility
+        return StepActionProvider.stopInClass(className, this);
     }
     
     /**
@@ -177,20 +150,4 @@ public class SmartSteppingFilterImpl implements SmartSteppingFilter {
         pcs.removePropertyChangeListener (l);
     }
 
-    /**
-     * Updates exact, start and end filter lists.
-     */
-    private void refreshFilters (Set<String> newFilters) {
-        Iterator<String> i = newFilters.iterator ();
-        while (i.hasNext ()) {
-            String p = i.next ();
-            if (p.startsWith ("*"))
-                end.add (p.substring (1));
-            else
-            if (p.endsWith ("*"))
-                start.add (p.substring (0, p.length () - 1));
-            else
-                exact.add (p);
-        }
-    }
 }
diff --git a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/SmartSteppingFilterWrapper.java b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/SmartSteppingFilterWrapper.java
new file mode 100644
index 0000000000..0cd704d28f
--- /dev/null
+++ b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/SmartSteppingFilterWrapper.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.actions;
+
+import java.beans.PropertyChangeListener;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.SmartSteppingFilter;
+
+/**
+ * A wrapper over {@link JPDADebugger#getSmartSteppingFilter()}. A new instance
+ * of this class is created for every step to store step's specific patterns.
+ */
+public final class SmartSteppingFilterWrapper implements SmartSteppingFilter {
+
+    private final SmartSteppingFilter delegate;
+    private Set<String> addedPatterns;
+    private Set<String> removedPatterns;
+
+    public SmartSteppingFilterWrapper(SmartSteppingFilter delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public synchronized void addExclusionPatterns(Set<String> patterns) {
+        if (addedPatterns == null) {
+            addedPatterns = new LinkedHashSet<>();
+        }
+        addedPatterns.addAll(patterns);
+    }
+
+    @Override
+    public synchronized void removeExclusionPatterns(Set<String> patterns) {
+        if (removedPatterns == null) {
+            removedPatterns = new LinkedHashSet<>();
+        }
+        removedPatterns.addAll(patterns);
+    }
+    
+    @Override
+    public String[] getExclusionPatterns() {
+        String[] patterns = delegate.getExclusionPatterns();
+        synchronized (this) {
+            if (addedPatterns == null && removedPatterns == null) {
+                return patterns;
+            }
+            Set<String> newPatterns = new LinkedHashSet<>(Arrays.asList(patterns));
+            if (removedPatterns != null) {
+                newPatterns.removeAll(removedPatterns);
+            }
+            if (addedPatterns != null) {
+                newPatterns.addAll(addedPatterns);
+            }
+            return newPatterns.toArray(new String[newPatterns.size()]);
+        }
+    }
+    
+    @Override
+    public void addPropertyChangeListener(PropertyChangeListener l) {
+        // No listening on the temporary wrapper
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removePropertyChangeListener(PropertyChangeListener l) {
+        // No listening on the temporary wrapper
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepActionProvider.java b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepActionProvider.java
index 6cff450a5e..05602aff90 100644
--- a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepActionProvider.java
+++ b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepActionProvider.java
@@ -168,7 +168,32 @@ implements Executor {
             }
         });
     }
-    
+
+    /**
+     * Returns whether we can stop in the class based on the smart stepping filter.
+     */
+    public static boolean stopInClass(String className, SmartSteppingFilter filter) {
+        for (String pattern : filter.getExclusionPatterns()) {
+            if (pattern.startsWith ("*")) {
+                String end = pattern.substring(1);
+                if (className.endsWith(end)) {
+                    return false;
+                }
+            } else if (pattern.endsWith ("*")) {
+                String start = pattern.substring(0, pattern.length() - 1);
+                if (className.startsWith(start)) {
+                    return false;
+                }
+            } else {
+                // exact match
+                if (className.equals(pattern)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
     public void runAction(final Object action) {
         runAction(getJDIAction(action), StepRequest.STEP_LINE, true, null, null, null);
     }
@@ -209,6 +234,7 @@ implements Executor {
                     }
                 }
             }
+            smartSteppingFilter = new SmartSteppingFilterWrapper(debugger.getSmartSteppingFilter());
             resumeThread.waitUntilMethodInvokeDone();
             ThreadReference tr = resumeThread.getThreadReference ();
             removeStepRequests (tr);
@@ -258,17 +284,17 @@ implements Executor {
             if (isSteppingFromFilteredLocation != null) {
                 steppingFromFilteredLocation = isSteppingFromFilteredLocation;
             } else {
-                steppingFromFilteredLocation = !getSmartSteppingFilterImpl ().stopHere(className);
+                steppingFromFilteredLocation = !stopInClass(className, smartSteppingFilter);
             }
             if (isSteppingFromCompoundFilteredLocation != null) {
                 steppingFromCompoundFilteredLocation = isSteppingFromCompoundFilteredLocation;
             } else {
                 if (topFrame != null) {
                     steppingFromCompoundFilteredLocation = !getCompoundSmartSteppingListener ().stopAt
-                             (lookupProvider, topFrame, getSmartSteppingFilterImpl()).isStop();
+                             (lookupProvider, topFrame, smartSteppingFilter).isStop();
                 } else {
                     steppingFromCompoundFilteredLocation = !getCompoundSmartSteppingListener ().stopHere
-                             (lookupProvider, resumeThread, getSmartSteppingFilterImpl());
+                             (lookupProvider, resumeThread, smartSteppingFilter);
                 }
             }
             if (logger.isLoggable(Level.FINE)) {
@@ -449,7 +475,7 @@ implements Executor {
             if (steppingFromFilteredLocation) {
                 fsh = StopOrStep.stop();
             } else {
-                fsh = getSmartSteppingFilterImpl().stopHere(className) ? StopOrStep.stop() : StopOrStep.skip();
+                fsh = stopInClass(className, smartSteppingFilter) ? StopOrStep.stop() : StopOrStep.skip();
             }
             if (loggerStep.isLoggable(Level.FINE))
                 loggerStep.fine("SS  SmartSteppingFilter.stopHere (" + className + ") ? " + fsh);
@@ -461,10 +487,10 @@ implements Executor {
                     CallStackFrame topFrame = getTopFrame(t);
                     if (topFrame != null) {
                         fsh = getCompoundSmartSteppingListener ().stopAt
-                             (lookupProvider, topFrame, getSmartSteppingFilterImpl());
+                             (lookupProvider, topFrame, smartSteppingFilter);
                     } else {
                         fsh = getCompoundSmartSteppingListener ().stopHere
-                             (lookupProvider, t, getSmartSteppingFilterImpl()) ? StopOrStep.stop() : StopOrStep.skip();
+                             (lookupProvider, t, smartSteppingFilter) ? StopOrStep.stop() : StopOrStep.skip();
                     }
                 }
                 if (fsh.isStop()) {
@@ -741,15 +767,8 @@ implements Executor {
         return stepIntoActionProvider;
     }
 
-    private SmartSteppingFilterImpl smartSteppingFilterImpl;
+    private SmartSteppingFilterWrapper smartSteppingFilter;
     
-    private SmartSteppingFilterImpl getSmartSteppingFilterImpl () {
-        if (smartSteppingFilterImpl == null)
-            smartSteppingFilterImpl = (SmartSteppingFilterImpl) lookupProvider.
-                lookupFirst (null, SmartSteppingFilter.class);
-        return smartSteppingFilterImpl;
-    }
-
     private CompoundSmartSteppingListener compoundSmartSteppingListener;
     
     private CompoundSmartSteppingListener getCompoundSmartSteppingListener () {
diff --git a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepIntoNextMethod.java b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepIntoNextMethod.java
index 4f7ed77f2c..4bdcf8c3cf 100644
--- a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepIntoNextMethod.java
+++ b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepIntoNextMethod.java
@@ -86,20 +86,22 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
     private int depth;
     private final JPDADebuggerImpl debugger;
     private final ContextProvider contextProvider;
-    private boolean smartSteppingStepOut;
+    private final boolean smartSteppingStepOut;
     private boolean steppingFromFilteredLocation;
     private boolean steppingFromCompoundFilteredLocation;
+    private SmartSteppingFilterWrapper smartSteppingFilter;
+    private boolean didStepThrough;
     private final Properties p;
 
     public StepIntoNextMethod(ContextProvider contextProvider) {
         this.debugger = (JPDADebuggerImpl) contextProvider.lookupFirst(null, JPDADebugger.class);
         this.contextProvider = contextProvider;
-        getSmartSteppingFilterImpl ().addPropertyChangeListener (this);
+        debugger.getSmartSteppingFilter().addPropertyChangeListener (this);
         SourcePath ec = contextProvider.lookupFirst(null, SourcePath.class);
         ec.addPropertyChangeListener (this);
         Map properties = contextProvider.lookupFirst(null, Map.class);
-        if (properties != null)
-            smartSteppingStepOut = properties.containsKey (StepIntoActionProvider.SS_STEP_OUT);
+        smartSteppingStepOut = (properties != null) ?
+                properties.containsKey (StepIntoActionProvider.SS_STEP_OUT) : false;
         p = Properties.getDefault().getProperties("debugger.options.JPDA"); // NOI18N
     }
 
@@ -125,6 +127,8 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
             smartLogger.finer("Can not step into next method! No current thread!");
             return ;
         }
+        smartSteppingFilter = new SmartSteppingFilterWrapper(debugger.getSmartSteppingFilter());
+        didStepThrough = false;
         boolean locked = lock == null;
         try {
             if (lock == null) {
@@ -152,7 +156,7 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
             if (isSteppingFromFilteredLocation != null) {
                 steppingFromFilteredLocation = isSteppingFromFilteredLocation.booleanValue();
             } else {
-                steppingFromFilteredLocation = !getSmartSteppingFilterImpl ().stopHere(t.getClassName());
+                steppingFromFilteredLocation = !StepActionProvider.stopInClass(t.getClassName(), smartSteppingFilter);
             }
             if (isSteppingFromCompoundFilteredLocation != null) {
                 steppingFromCompoundFilteredLocation = isSteppingFromCompoundFilteredLocation.booleanValue();
@@ -160,10 +164,10 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
                 CallStackFrame topFrame = getTopFrame(t);
                 if (topFrame != null) {
                     steppingFromCompoundFilteredLocation = !getCompoundSmartSteppingListener ().stopAt
-                             (contextProvider, topFrame, getSmartSteppingFilterImpl()).isStop();
+                             (contextProvider, topFrame, smartSteppingFilter).isStop();
                 } else {
                     steppingFromCompoundFilteredLocation = !getCompoundSmartSteppingListener ().stopHere
-                                       (contextProvider, t, getSmartSteppingFilterImpl ());
+                                       (contextProvider, t, smartSteppingFilter);
                 }
             }
 
@@ -299,7 +303,7 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
             boolean useStepFilters = p.getBoolean("UseStepFilters", true);
             boolean stepThrough = useStepFilters && p.getBoolean("StepThroughFilters", false)
                                   && !smartSteppingStepOut;
-            if (!stepThrough && isFilteredClassOnStack(tr, depth)) {
+            if (!stepThrough && !didStepThrough && isFilteredClassOnStack(tr, depth)) {
                 // There's a class that was skipped by step on the stack.
                 // If step through is false, we should step out from here.
                 smartLogger.finer(" stoped with a filtered class on stack and step through is false.");
@@ -348,10 +352,10 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
                 CallStackFrame topFrame = getTopFrame(t);
                 if (topFrame != null) {
                     stop = getCompoundSmartSteppingListener().stopAt
-                                       (contextProvider, topFrame, getSmartSteppingFilterImpl());
+                                       (contextProvider, topFrame, smartSteppingFilter);
                 } else {
                     stop = getCompoundSmartSteppingListener().stopHere
-                                       (contextProvider, t, getSmartSteppingFilterImpl()) ? StopOrStep.stop() : StopOrStep.skip();
+                                       (contextProvider, t, smartSteppingFilter) ? StopOrStep.stop() : StopOrStep.skip();
                 }
             }
             if (stop.isStop()) {
@@ -379,11 +383,14 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
                     stepSize = StepRequest.STEP_LINE;
                 }
                 if (stepDepth == 0) {
-                    if (!stepThrough) {
+                    if (!stepThrough || smartSteppingStepOut) {
                         stepDepth = StepRequest.STEP_OUT;
                     } else {
                         stepDepth = StepRequest.STEP_INTO;
                     }
+                } else if (stepDepth == StepRequest.STEP_INTO) {
+                    // We're stepping through in this case
+                    didStepThrough = true;
                 }
                 StepRequest newSR = setStepRequest(stepDepth);
                 if (newSR == null) {
@@ -457,6 +464,7 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
             synchronized (this) {
                 if (stepIntoRequest != null) {
                     try {
+                        addPatternsToRequest(smartSteppingFilter.getExclusionPatterns(), stepIntoRequest);
                         try {
                             EventRequestWrapper.enable (stepIntoRequest);
                             enabledStepRequest = stepIntoRequest;
@@ -527,7 +535,7 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
             try {
                 if (!steppingFromFilteredLocation) {
                     addPatternsToRequest (
-                        getSmartSteppingFilterImpl ().getExclusionPatterns (),
+                        smartSteppingFilter.getExclusionPatterns (),
                         stepRequest
                     );
                 }
@@ -562,13 +570,6 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
         return stepRequest;
     }
 
-    private SmartSteppingFilterImpl smartSteppingFilter;
-
-    private SmartSteppingFilterImpl getSmartSteppingFilterImpl () {
-        if (smartSteppingFilter == null)
-            smartSteppingFilter = (SmartSteppingFilterImpl) contextProvider.lookupFirst(null, SmartSteppingFilter.class);
-        return smartSteppingFilter;
-    }
 
     private CompoundSmartSteppingListener compoundSmartSteppingListener;
 
@@ -596,7 +597,7 @@ public class StepIntoNextMethod implements Executor, PropertyChangeListener {
         if (steppingFromFilteredLocation) {
             return false;
         }
-        String[] patterns = getSmartSteppingFilterImpl ().getExclusionPatterns();
+        String[] patterns = smartSteppingFilter.getExclusionPatterns();
         if (patterns.length == 0) return false;
         try {
             int n = ThreadReferenceWrapper.frameCount(tr);


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists