You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by en...@apache.org on 2020/10/09 22:28:28 UTC

[netbeans] branch master updated: Improved stability of Truffle debugger and implemented step from scripts to Java.

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

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


The following commit(s) were added to refs/heads/master by this push:
     new f282d78  Improved stability of Truffle debugger and implemented step from scripts to Java.
     new dfb0edb  Merge pull request #2436 from entlicher/TruffleDebugStabilityAndStep2Java
f282d78 is described below

commit f282d78f09c4bf6296cbf062f12808c628287fdb
Author: Martin Entlicher <ma...@oracle.com>
AuthorDate: Fri Oct 9 15:09:14 2020 +0200

    Improved stability of Truffle debugger and implemented step from scripts to Java.
---
 java/api.debugger.jpda/apichanges.xml              |  16 ++
 java/api.debugger.jpda/manifest.mf                 |   2 +-
 .../netbeans/api/debugger/jpda/JPDADebugger.java   |  15 +-
 .../org/netbeans/api/debugger/jpda/JPDAStep.java   |  60 +++++++
 .../org/netbeans/api/debugger/jpda/JPDAThread.java |   2 -
 java/debugger.jpda.truffle/nbproject/project.xml   |   9 +-
 .../debugger/jpda/truffle/DebugManagerHandler.java |   2 +-
 .../debugger/jpda/truffle/PersistentValues.java    | 129 +++++++++++++++
 .../debugger/jpda/truffle/RemoteServices.java      | 175 +++++++++++++--------
 .../debugger/jpda/truffle/TruffleDebugManager.java | 121 ++++----------
 .../jpda/truffle/access/CurrentPCInfo.java         |   2 +-
 .../jpda/truffle/access/TruffleAccess.java         |   4 +-
 .../actions/PauseInGraalScriptActionProvider.java  |  85 +++++-----
 .../truffle/actions/RunToCursorActionProvider.java |  15 +-
 .../jpda/truffle/actions/StepActionProvider.java   |  49 +++++-
 .../truffle/breakpoints/TruffleLineBreakpoint.java |  64 ++++++++
 .../{ => impl}/TruffleBreakpointReader.java        |  16 +-
 .../{ => impl}/TruffleBreakpointsHandler.java      |  57 +++++--
 .../jpda/truffle/frames/TruffleStackFrame.java     |   2 +-
 .../debugger/jpda/truffle/source/Source.java       |   2 +
 .../truffle/source/SourceBinaryTranslator.java     | 126 +++++++++++++++
 .../jpda/truffle/vars/TruffleVariable.java         |   8 +
 .../truffle/vars/{ => impl}/TruffleEvaluator.java  |   2 +-
 .../truffle/vars/{ => impl}/TruffleExpression.java |   2 +-
 .../jpda/truffle/vars/{ => impl}/TruffleScope.java |   3 +-
 .../vars/{ => impl}/TruffleStackVariable.java      |   8 +-
 .../vars/{ => impl}/TruffleVariableImpl.java       |   3 +-
 .../models/TruffleLocalVariablesTreeModel.java     |   2 +-
 .../vars/models/TruffleVariablesNodeModel.java     |   2 +-
 .../vars/models/TruffleVariablesTableModel.java    |   4 +-
 .../vars/models/TruffleVariablesTreeModel.java     |   4 +-
 .../truffle/vars/tooltip/ToolTipAnnotation.java    |   2 +-
 .../jpda/backend/truffle/AgentClassLoader.java}    |  31 ++--
 .../jpda/backend/truffle/JPDATruffleAccessor.java  |   5 +-
 .../modules/debugger/jpda/JPDAStepImpl.java        | 127 +++++++--------
 .../jpda/breakpoints/LineBreakpointImpl.java       |  15 +-
 36 files changed, 834 insertions(+), 337 deletions(-)

diff --git a/java/api.debugger.jpda/apichanges.xml b/java/api.debugger.jpda/apichanges.xml
index 67a5177..69574ef 100644
--- a/java/api.debugger.jpda/apichanges.xml
+++ b/java/api.debugger.jpda/apichanges.xml
@@ -959,6 +959,22 @@ These are mainly class and thread filters and hit counts.
         </description>
         <class package="org.netbeans.api.debugger.jpda" name="ExceptionBreakpoint" />
     </change>
+
+    <change>
+        <api name="JPDADebuggerAPI"/>
+        <summary>JPDAStep enhanced with stepping filters and JPDADebugger.getSession() added.</summary>
+        <version major="3" minor="19"/>
+        <date day="9" month="10" year="2020"/>
+        <author login="mentlicher"/>
+        <compatibility addition="yes" source="compatible" binary="compatible"/>
+        <description>
+            <code>JPDAStep</code> enhanced with stepping filters for better control.
+            <code>JPDADebugger.getSession()</code> added to be able to easily retrieve the session
+            this JPDA debugger belongs into.
+        </description>
+        <class package="org.netbeans.api.debugger.jpda" name="JPDAStep" />
+        <class package="org.netbeans.api.debugger.jpda" name="JPDADebugger" />
+    </change>
 </changes>
 
   <!-- Now the surrounding HTML text and document structure: -->
diff --git a/java/api.debugger.jpda/manifest.mf b/java/api.debugger.jpda/manifest.mf
index e545029..49338d3 100644
--- a/java/api.debugger.jpda/manifest.mf
+++ b/java/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.18
+OpenIDE-Module-Specification-Version: 3.19
 OpenIDE-Module-Package-Dependencies: com.sun.jdi[VirtualMachineManager]
 
diff --git a/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java b/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java
index 26d3e6a..d073f22 100644
--- a/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java
+++ b/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java
@@ -19,7 +19,6 @@
 
 package org.netbeans.api.debugger.jpda;
 
-import com.sun.jdi.VirtualMachine;
 import com.sun.jdi.connect.Connector.Argument;
 import com.sun.jdi.connect.ListeningConnector;
 import com.sun.jdi.request.EventRequest;
@@ -38,6 +37,7 @@ import java.util.Map;
 import org.netbeans.api.debugger.DebuggerEngine;
 import org.netbeans.api.debugger.DebuggerInfo;
 import org.netbeans.api.debugger.DebuggerManager;
+import org.netbeans.api.debugger.Session;
 import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
 import org.netbeans.api.java.classpath.ClassPath;
 
@@ -552,7 +552,7 @@ public abstract class JPDADebugger {
     public boolean canGetInstanceInfo() {
         return false;
     }
-    
+
     /**
      * Get the list of all classes in the debuggee.
      * @return The list of all classes.
@@ -614,7 +614,15 @@ public abstract class JPDADebugger {
     public ThreadsCollector getThreadsCollector() {
         return null;
     }
-    
+
+    /**
+     * Get the session associated with this debugger.
+     * @since 3.19
+     */
+    public Session getSession() {
+        throw new AbstractMethodError();
+    }
+
     /**
      * Creates a deadlock detector.
      * @return deadlock detector with automatic detection of deadlock among suspended threads
@@ -746,7 +754,6 @@ public abstract class JPDADebugger {
             String serviceName = (String) attrs.get(DebuggerProcessor.SERVICE_NAME);
             return new ContextAware(serviceName);
         }
-
     }
 
 }
diff --git a/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAStep.java b/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAStep.java
index 9b20316..1b895b7 100644
--- a/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAStep.java
+++ b/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAStep.java
@@ -20,9 +20,12 @@ package org.netbeans.api.debugger.jpda;
 
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
+import java.util.Arrays;
 
 import com.sun.jdi.request.StepRequest;
 
+import org.netbeans.api.debugger.Properties;
+
 /**
  * Represents one JPDA step.
  *
@@ -32,6 +35,8 @@ public abstract class JPDAStep {
     private int size;
     private int depth;
     private boolean hidden;
+    private String[] classFilters;
+    private boolean stepThroughFilters;
     /** Associated JPDA debugger */
     protected JPDADebugger debugger;
     private PropertyChangeSupport pcs;
@@ -64,6 +69,9 @@ public abstract class JPDAStep {
         this.depth = depth;
         this.debugger = debugger;
         this.hidden = false;
+        Properties p = Properties.getDefault().getProperties("debugger.options.JPDA"); // NOI18N
+        boolean useStepFilters = p.getBoolean("UseStepFilters", true);
+        this.stepThroughFilters = useStepFilters && p.getBoolean("StepThroughFilters", false);
         pcs = new PropertyChangeSupport(this);
     }
    
@@ -113,6 +121,58 @@ public abstract class JPDAStep {
         return depth;
     }
     
+    /**
+     * Add additional class exclusion filters to this step.
+     * The provided list of class filters is combined with the current
+     * {@link SmartSteppingFilter#getExclusionPatterns()} just for this step.
+     *
+     * @param classFilters A list of class filters
+     * @since 3.19
+     */
+    public void addSteppingFilters(String... classFilters) {
+        if (this.classFilters == null) {
+            this.classFilters = classFilters;
+        } else {
+            int cfl = this.classFilters.length;
+            this.classFilters = Arrays.copyOf(this.classFilters, cfl + classFilters.length);
+            System.arraycopy(classFilters, 0, this.classFilters, cfl, classFilters.length);
+        }
+    }
+    
+    /**
+     * Get the additional exclusion filters of this step.
+     *
+     * @return A list of exclusion patterns, or <code>null</code> when no additional
+     * filters are provided
+     * @since 3.19
+     */
+    public String[] getSteppingFilters() {
+        return classFilters;
+    }
+    
+    /**
+     * Set whether this step is stepping through exclusion filters, or not.
+     * The default value is taken from the option property.
+     *
+     * @param stepThroughFilters <code>true</code> to step through the filters,
+     *        <code>false</code> otherwise
+     * @since 3.19
+     */
+    public void setStepThroughFilters(boolean stepThroughFilters) {
+        this.stepThroughFilters = stepThroughFilters;
+    }
+    
+    /**
+     * Test whether this step is stepping through the exclusion filters.
+     *
+     * @return <code>true</code> to step through the filters,
+     *         <code>false</code> otherwise
+     * @since 3.19
+     */
+    public boolean isStepThroughFilters() {
+        return stepThroughFilters;
+    }
+    
     /** Adds the step request to the associated
      *  {@link org.netbeans.api.debugger.jpda.JPDADebugger}.
      *  Method is not synchronized.
diff --git a/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAThread.java b/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAThread.java
index 8ec5ef0..e5e7e49 100644
--- a/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAThread.java
+++ b/java/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAThread.java
@@ -21,10 +21,8 @@ package org.netbeans.api.debugger.jpda;
 
 import com.sun.jdi.AbsentInformationException;
 import com.sun.jdi.ThreadReference;
-import java.beans.PropertyChangeListener;
 import java.util.List;
 import java.util.concurrent.locks.Lock;
-import org.netbeans.spi.debugger.jpda.EditorContext;
 import org.netbeans.spi.debugger.jpda.EditorContext.Operation;
 
 
diff --git a/java/debugger.jpda.truffle/nbproject/project.xml b/java/debugger.jpda.truffle/nbproject/project.xml
index 60a04ac..aff8c0d 100644
--- a/java/debugger.jpda.truffle/nbproject/project.xml
+++ b/java/debugger.jpda.truffle/nbproject/project.xml
@@ -40,7 +40,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>2</release-version>
-                        <specification-version>3.5</specification-version>
+                        <specification-version>3.19</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
@@ -269,7 +269,12 @@
                     </test-dependency>
                 </test-type>
             </test-dependencies>
-            <public-packages/>
+            <public-packages>
+                <package>org.netbeans.modules.debugger.jpda.truffle.breakpoints</package>
+                <package>org.netbeans.modules.debugger.jpda.truffle.frames</package>
+                <package>org.netbeans.modules.debugger.jpda.truffle.source</package>
+                <package>org.netbeans.modules.debugger.jpda.truffle.vars</package>
+            </public-packages>
             <extra-compilation-unit>
                 <package-root>truffle-backend</package-root>
                 <classpath>${module.classpath}:${truffle-backend.cp.extra}</classpath>
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/DebugManagerHandler.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/DebugManagerHandler.java
index 385b411..f2b03f6 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/DebugManagerHandler.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/DebugManagerHandler.java
@@ -65,7 +65,7 @@ import org.netbeans.modules.debugger.jpda.jdi.VirtualMachineWrapper;
 import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
 import static org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess.BASIC_CLASS_NAME;
-import org.netbeans.modules.debugger.jpda.truffle.breakpoints.TruffleBreakpointsHandler;
+import org.netbeans.modules.debugger.jpda.truffle.breakpoints.impl.TruffleBreakpointsHandler;
 import org.netbeans.modules.debugger.jpda.truffle.options.TruffleOptions;
 import org.netbeans.modules.javascript2.debug.breakpoints.JSLineBreakpoint;
 import org.openide.util.Exceptions;
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/PersistentValues.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/PersistentValues.java
new file mode 100644
index 0000000..1b0a7cc
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/PersistentValues.java
@@ -0,0 +1,129 @@
+/*
+ * 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.truffle;
+
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.StringReference;
+import com.sun.jdi.VirtualMachine;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectReferenceWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.UnsupportedOperationExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VirtualMachineWrapper;
+
+/**
+ * Create values with disabled collection, which can be released later on.
+ * This is necessary e.g. for temporary variables so that they are not collected
+ * prematurely.
+ */
+public final class PersistentValues {
+
+    private final VirtualMachine vm;
+    private final List<ObjectReference> references = new ArrayList<>(5);
+
+    public PersistentValues(VirtualMachine vm) {
+        this.vm = vm;
+    }
+
+    public StringReference mirrorOf(String string) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, UnsupportedOperationExceptionWrapper {
+        StringReference stringMirror = null;
+        while (stringMirror == null) {
+            stringMirror = VirtualMachineWrapper.mirrorOf(vm, string);
+            try {
+                ObjectReferenceWrapper.disableCollection(stringMirror);
+            } catch (ObjectCollectedExceptionWrapper ce) {
+                stringMirror = null;
+            }
+        }
+        references.add(stringMirror);
+        return stringMirror;
+    }
+
+    /**
+     * @return the reference or <code>null</code> in case of JDI exceptions.
+     */
+    public StringReference mirrorOf0(String string) {
+        try {
+            return mirrorOf(string);
+        } catch (InternalExceptionWrapper | UnsupportedOperationExceptionWrapper | VMDisconnectedExceptionWrapper e) {
+            return null;
+        }
+    }
+
+    public <T extends ObjectReference> T valueOf(ValueSupplier<T> valueSupplier) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, UnsupportedOperationExceptionWrapper {
+        T value = null;
+        while (value == null) {
+            value = valueSupplier.get();
+            try {
+                ObjectReferenceWrapper.disableCollection(value);
+            } catch (ObjectCollectedExceptionWrapper ce) {
+                value = null;
+            }
+        }
+        references.add(value);
+        return value;
+    }
+
+    public <T extends ObjectReference> T invokeOf(InvokeSupplier<T> valueSupplier) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, UnsupportedOperationExceptionWrapper, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException, InvocationException, ObjectCollectedExceptionWrapper {
+        T value = null;
+        while (value == null) {
+            value = valueSupplier.get();
+            try {
+                ObjectReferenceWrapper.disableCollection(value);
+            } catch (ObjectCollectedExceptionWrapper ce) {
+                value = null;
+            }
+        }
+        references.add(value);
+        return value;
+    }
+
+    public void collect() {
+        for (ObjectReference reference : references) {
+            try {
+                ObjectReferenceWrapper.enableCollection(reference);
+            } catch (InternalExceptionWrapper | ObjectCollectedExceptionWrapper | UnsupportedOperationExceptionWrapper ex) {
+                // continue
+            } catch (VMDisconnectedExceptionWrapper ex) {
+                break;
+            }
+        }
+        references.clear();
+    }
+
+    @FunctionalInterface
+    public interface ValueSupplier<T> {
+
+        public T get() throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, UnsupportedOperationExceptionWrapper;
+    }
+
+    @FunctionalInterface
+    public interface InvokeSupplier<T> {
+
+        public T get() throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, UnsupportedOperationExceptionWrapper, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException, InvocationException, ObjectCollectedExceptionWrapper;
+    }
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/RemoteServices.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/RemoteServices.java
index 5ad1d57..b566a93 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/RemoteServices.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/RemoteServices.java
@@ -23,6 +23,7 @@ import com.sun.jdi.ArrayReference;
 import com.sun.jdi.ArrayType;
 import com.sun.jdi.BooleanValue;
 import com.sun.jdi.ByteValue;
+import com.sun.jdi.ClassLoaderReference;
 import com.sun.jdi.ClassNotLoadedException;
 import com.sun.jdi.ClassObjectReference;
 import com.sun.jdi.ClassType;
@@ -43,6 +44,7 @@ import java.beans.PropertyChangeListener;
 import java.beans.PropertyVetoException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Constructor;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -94,6 +96,11 @@ public final class RemoteServices {
     private static final Logger logger = Logger.getLogger(RemoteServices.class.getName());
     
     static final String REMOTE_CLASSES_ZIPFILE = "/org/netbeans/modules/debugger/jpda/truffle/resources/JPDATruffleBackend.jar";
+
+    private static final String TRUFFLE_CLASS = "com.oracle.truffle.api.Truffle";
+    private static final String EXPORT_TRUFFLE_CLASS = "com.oracle.truffle.polyglot.LanguageCache$Loader";
+    private static final String EXPORT_TRUFFLE_METHOD = "exportTruffle";
+    private static final String EXPORT_TRUFFLE_SIGNAT = "(Ljava/lang/ClassLoader;)V";
     
     private static final Map<JPDADebugger, ClassObjectReference> remoteServiceClasses = new WeakHashMap<>();
     private static final Map<JPDADebugger, ThreadReference> remoteServiceAccess = new WeakHashMap<>();
@@ -125,7 +132,7 @@ public final class RemoteServices {
         /* Use:
            com.oracle.truffle.api.impl.TruffleLocator.class.getClassLoader()
         */
-        ClassType truffleLocatorClass = getClass(vm, "com.oracle.truffle.api.impl.TruffleLocator");
+        ClassType truffleLocatorClass = getClass(vm, TRUFFLE_CLASS);
         return truffleLocatorClass.classLoader();
     }
     
@@ -167,6 +174,15 @@ public final class RemoteServices {
         return cl;
     }
     
+    private static int getTargetMajorVersion(VirtualMachine vm) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper {
+        String version = VirtualMachineWrapper.version(vm);
+        int dot = version.indexOf(".");
+        if (dot < 0) {
+            dot = version.length();
+        }
+        return Integer.parseInt(version.substring(0, dot));
+    }
+
     public static ClassObjectReference uploadBasicClasses(JPDAThreadImpl t, String basicClassName) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException, IOException, PropertyVetoException, InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper, UnsupportedOperationExceptionWrapper, ClassNotPreparedExceptionWrapper {
         ThreadReference tawt = t.getThreadReference();
         VirtualMachine vm = tawt.virtualMachine();
@@ -189,59 +205,7 @@ public final class RemoteServices {
                 }
                 // Suppose that when there's the basic class loaded, there are all.
                 if (basicClass == null) {  // Load the classes only if there's not the basic one.
-                    ObjectReference cl;
-                    cl = getTruffleClassLoader(tawt, vm);
-                    if (cl == null) {
-                        cl = getBootstrapClassLoader(tawt, vm);
-                    }
-                    if (cl == null) {
-                        cl = getContextClassLoader(tawt, vm);
-                    }
-                    ClassType classLoaderClass = (ClassType) ObjectReferenceWrapper.referenceType(cl);
-
-                    ByteValue[] mirrorBytesCache = new ByteValue[256];
-                    for (RemoteClass rc : remoteClasses) {
-                        String className = rc.name;
-                        ClassObjectReference theUploadedClass;
-                        ArrayReference byteArray = createTargetBytes(vm, rc.bytes, mirrorBytesCache);
-                        StringReference nameMirror = null;
-                        try {
-                            Method defineClass = ClassTypeWrapper.concreteMethodByName(classLoaderClass, "defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
-                            boolean uploaded = false;
-                            while (!uploaded) {
-                                nameMirror = VirtualMachineWrapper.mirrorOf(vm, className);
-                                try {
-                                    ObjectReferenceWrapper.disableCollection(nameMirror);
-                                    uploaded = true;
-                                } catch (ObjectCollectedExceptionWrapper ocex) {
-                                    // Just collected, try again...
-                                }
-                            }
-                            uploaded = false;
-                            while (!uploaded) {
-                                theUploadedClass = (ClassObjectReference) ObjectReferenceWrapper.invokeMethod(cl, tawt, defineClass, Arrays.asList(nameMirror, byteArray, vm.mirrorOf(0), vm.mirrorOf(rc.bytes.length)), ObjectReference.INVOKE_SINGLE_THREADED);
-                                if (basicClass == null && rc.name.indexOf('$') < 0 && rc.name.endsWith("Accessor")) {
-                                    try {
-                                        // Disable collection only of the basic class
-                                        ObjectReferenceWrapper.disableCollection(theUploadedClass);
-                                        basicClass = theUploadedClass;
-                                        uploaded = true;
-                                    } catch (ObjectCollectedExceptionWrapper ocex) {
-                                        // Just collected, try again...
-                                    }
-                                } else {
-                                    uploaded = true;
-                                }
-                            }
-                        } finally {
-                            ObjectReferenceWrapper.enableCollection(byteArray); // We can dispose it now
-                            if (nameMirror != null) {
-                                ObjectReferenceWrapper.enableCollection(nameMirror);
-                            }
-                        }
-                        //Method resolveClass = classLoaderClass.concreteMethodByName("resolveClass", "(Ljava/lang/Class;)V");
-                        //systemClassLoader.invokeMethod(tawt, resolveClass, Arrays.asList(theUploadedClass), ObjectReference.INVOKE_SINGLE_THREADED);
-                    }
+                    basicClass = doUpload(vm, tawt, remoteClasses);
                 }
                 if (basicClass != null) {
                     // Initialize the class:
@@ -266,6 +230,92 @@ public final class RemoteServices {
         }
     }
     
+    private static ClassObjectReference doUpload(VirtualMachine vm, ThreadReference tawt, List<RemoteClass> remoteClasses) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException, IOException, PropertyVetoException, InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper, UnsupportedOperationExceptionWrapper, ClassNotPreparedExceptionWrapper {
+        String agentClassLoaderName = "AgentClassLoader";
+        PersistentValues values = new PersistentValues(vm);
+        ByteValue[] mirrorBytesCache = new ByteValue[256];
+        ClassLoaderReference classLoader;
+        try {
+            if (getTargetMajorVersion(vm) > 8) { // Module system is in place
+                RemoteClass agent = null;
+                for (RemoteClass rc : remoteClasses) {
+                    if (rc.name.endsWith(agentClassLoaderName)) {
+                        agent = rc;
+                        break;
+                    }
+                }
+                if (agent == null) {
+                    throw new IllegalStateException("The " + agentClassLoaderName + " class is missing.");
+                }
+                // Upload the class loader first, using the Truffle's class loader:
+                ClassType truffleLocatorClass = getClass(vm, TRUFFLE_CLASS);
+                ClassLoaderReference truffleClassLoader = truffleLocatorClass.classLoader();
+                ClassType classLoaderClass = (ClassType) ObjectReferenceWrapper.referenceType(truffleClassLoader);
+                // Define the class loader's code:
+                Method defineClass = ClassTypeWrapper.concreteMethodByName(classLoaderClass, "defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
+                ArrayReference byteArray = createTargetBytes(vm, agent.bytes, mirrorBytesCache, values);
+                StringReference nameMirror = values.mirrorOf(agent.name);
+                ClassObjectReference theUploadedClassLoader = (ClassObjectReference) ObjectReferenceWrapper.invokeMethod(truffleClassLoader, tawt, defineClass, Arrays.asList(nameMirror, byteArray, vm.mirrorOf(0), vm.mirrorOf(agent.bytes.length)), ObjectReference.INVOKE_SINGLE_THREADED);
+                // We have the class loader's class. Create it's instance now.
+                // Find the constructor and call newInstance on it:
+                ClassType theClass = getClass(vm, Class.class.getName());
+                Method getDeclaredConstructors = ClassTypeWrapper.concreteMethodByName(theClass, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;");
+                ArrayReference constructors = (ArrayReference) ObjectReferenceWrapper.invokeMethod(theUploadedClassLoader, tawt, getDeclaredConstructors, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
+                ObjectReference constructor = (ObjectReference) constructors.getValue(0);
+                ClassType constructorClass = getClass(vm, Constructor.class.getName());
+                Method newInstance = ClassTypeWrapper.concreteMethodByName(constructorClass, "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;");
+                ClassLoaderReference newInstanceOfClassLoader = values.invokeOf(() -> (ClassLoaderReference) constructor.invokeMethod(tawt, newInstance, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED));
+                classLoader = (ClassLoaderReference) newInstanceOfClassLoader;
+
+                // We have an agent class loader that we'll use to define Truffle backend debugging classes.
+                // We need to export the agent class loader to Truffle so that we can upload classes that access Truffle APIs.
+                ClassType languageLoader = getClass(vm, EXPORT_TRUFFLE_CLASS);
+                if (languageLoader == null) {
+                    Exceptions.printStackTrace(new IllegalStateException("Class " + EXPORT_TRUFFLE_CLASS + " not found in the debuggee."));
+                    return null;
+                }
+                Method exportTruffle = ClassTypeWrapper.concreteMethodByName(languageLoader, EXPORT_TRUFFLE_METHOD, EXPORT_TRUFFLE_SIGNAT);
+                if (exportTruffle == null) {
+                    Exceptions.printStackTrace(new IllegalStateException("Method " + EXPORT_TRUFFLE_METHOD + " was not found in " + EXPORT_TRUFFLE_CLASS +" in the debuggee."));
+                    return null;
+                }
+                ClassTypeWrapper.invokeMethod(languageLoader, tawt, exportTruffle, Collections.singletonList(classLoader), ObjectReference.INVOKE_SINGLE_THREADED);
+            } else {
+                ObjectReference cl;
+                cl = getTruffleClassLoader(tawt, vm);
+                if (cl == null) {
+                    cl = getBootstrapClassLoader(tawt, vm);
+                }
+                if (cl == null) {
+                    cl = getContextClassLoader(tawt, vm);
+                }
+                classLoader = (ClassLoaderReference) cl;
+            }
+
+            ClassType classLoaderClass = (ClassType) ObjectReferenceWrapper.referenceType(classLoader);
+            ClassObjectReference basicClass = null;
+            for (RemoteClass rc : remoteClasses) {
+                String className = rc.name;
+                if (className.endsWith(agentClassLoaderName)) {
+                    continue;
+                }
+                ClassObjectReference theUploadedClass;
+                ArrayReference byteArray = createTargetBytes(vm, rc.bytes, mirrorBytesCache, values);
+                Method defineClass = ClassTypeWrapper.concreteMethodByName(classLoaderClass, "defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
+                StringReference nameMirror = values.mirrorOf(className);
+                theUploadedClass = values.invokeOf(() -> (ClassObjectReference) ObjectReferenceWrapper.invokeMethod(classLoader, tawt, defineClass, Arrays.asList(nameMirror, byteArray, vm.mirrorOf(0), vm.mirrorOf(rc.bytes.length)), ObjectReference.INVOKE_SINGLE_THREADED));
+                if (basicClass == null && rc.name.indexOf('$') < 0 && rc.name.endsWith("Accessor")) {
+                    // Disable collection only of the basic class
+                    ObjectReferenceWrapper.disableCollection(theUploadedClass);
+                    basicClass = theUploadedClass;
+                }
+            }
+            return basicClass;
+        } finally {
+            values.collect();
+        }
+    }
+
     private static void runOnBreakpoint(final JPDAThread awtThread, String bpClass, String bpMethod, final Runnable runnable, final CountDownLatch latch) {
         final MethodBreakpoint mb = MethodBreakpoint.create(bpClass, bpMethod);
         final JPDADebugger dbg = ((JPDAThreadImpl)awtThread).getDebugger();
@@ -616,25 +666,16 @@ public final class RemoteServices {
     }
     
     private static ArrayReference createTargetBytes(VirtualMachine vm, byte[] bytes,
-                                                    ByteValue[] mirrorBytesCache) throws InvalidTypeException,
+                                                    ByteValue[] mirrorBytesCache,
+                                                    PersistentValues persistValues) throws InvalidTypeException,
                                                                                          ClassNotLoadedException,
                                                                                          InternalExceptionWrapper,
                                                                                          VMDisconnectedExceptionWrapper,
                                                                                          ObjectCollectedExceptionWrapper,
                                                                                          UnsupportedOperationExceptionWrapper {
         ArrayType bytesArrayClass = getArrayClass(vm, "byte[]");
-        ArrayReference array = null;
-        boolean disabledCollection = false;
-        while (!disabledCollection) {
-            array = ArrayTypeWrapper.newInstance(bytesArrayClass, bytes.length);
-            try {
-                ObjectReferenceWrapper.disableCollection(array);
-                disabledCollection = true;
-            } catch (ObjectCollectedExceptionWrapper ocex) {
-                // Collected too soon, try again...
-            }
-        }
-        List<Value> values = new ArrayList<Value>(bytes.length);
+        ArrayReference array = persistValues.valueOf(() -> ArrayTypeWrapper.newInstance(bytesArrayClass, bytes.length));
+        List<Value> values = new ArrayList<>(bytes.length);
         for (int i = 0; i < bytes.length; i++) {
             byte b = bytes[i];
             ByteValue mb = mirrorBytesCache[128 + b];
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/TruffleDebugManager.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/TruffleDebugManager.java
index 6f9b233..b37c0e9 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/TruffleDebugManager.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/TruffleDebugManager.java
@@ -20,18 +20,8 @@
 package org.netbeans.modules.debugger.jpda.truffle;
 
 import com.sun.jdi.ClassType;
-import com.sun.jdi.IncompatibleThreadStateException;
-import com.sun.jdi.Location;
-import com.sun.jdi.Method;
 import com.sun.jdi.ObjectReference;
-import com.sun.jdi.ReferenceType;
-import com.sun.jdi.StackFrame;
-import com.sun.jdi.ThreadReference;
 import com.sun.jdi.Value;
-import com.sun.jdi.event.Event;
-import com.sun.jdi.event.LocatableEvent;
-import com.sun.jdi.request.BreakpointRequest;
-import com.sun.jdi.request.EventRequest;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.util.ArrayList;
@@ -47,39 +37,22 @@ import org.netbeans.api.debugger.DebuggerManager;
 import org.netbeans.api.debugger.DebuggerManagerAdapter;
 import org.netbeans.api.debugger.LazyDebuggerManagerListener;
 import org.netbeans.api.debugger.Session;
-import org.netbeans.api.debugger.jpda.ClassLoadUnloadBreakpoint;
 import org.netbeans.api.debugger.jpda.JPDABreakpoint;
 import org.netbeans.api.debugger.jpda.JPDAClassType;
 import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
 import org.netbeans.api.debugger.jpda.MethodBreakpoint;
 import org.netbeans.api.debugger.jpda.ObjectVariable;
 import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
 import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener;
 import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
 import org.netbeans.modules.debugger.jpda.expr.JDIVariable;
-import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.InvalidRequestStateExceptionWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.MethodWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.ReferenceTypeWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.StackFrameWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.ThreadReferenceWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.event.LocatableEventWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestManagerWrapper;
-import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestWrapper;
-import org.netbeans.modules.debugger.jpda.models.JPDAClassTypeImpl;
 import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
 import org.netbeans.modules.debugger.jpda.truffle.actions.PauseInGraalScriptActionProvider;
-import org.netbeans.modules.debugger.jpda.util.Executor;
 import org.netbeans.modules.javascript2.debug.breakpoints.JSLineBreakpoint;
 import org.netbeans.spi.debugger.ActionsProvider;
 import org.netbeans.spi.debugger.DebuggerServiceRegistration;
-import org.openide.util.Exceptions;
 
 /**
  * Initiates guest language debugging, detects Engine in the JVM.
@@ -89,7 +62,8 @@ public class TruffleDebugManager extends DebuggerManagerAdapter {
     
     private static final Logger LOG = Logger.getLogger(TruffleDebugManager.class.getName());
     
-    private static final String SESSION_CREATION_BP_CLASS = "org.graalvm.polyglot.Engine";
+    private static final String ENGINE_CLASS = "org.graalvm.polyglot.Engine";
+    private static final String ENGINE_BUILDER_CLASS = "org.graalvm.polyglot.Engine$Builder";
     // Breakpoint on this class triggers search of existing engines
     private static final String EXISTING_ENGINES_TRIGGER = "com.oracle.truffle.api.frame.FrameSlot";
     
@@ -115,7 +89,8 @@ public class TruffleDebugManager extends DebuggerManagerAdapter {
         debugManagerLoadBP = MethodBreakpoint.create(SESSION_CREATION_BP_CLASS, SESSION_CREATION_BP_METHOD);
         ((MethodBreakpoint) debugManagerLoadBP).setBreakpointType(MethodBreakpoint.TYPE_METHOD_EXIT);
         */
-        debugManagerLoadBP = ClassLoadUnloadBreakpoint.create(SESSION_CREATION_BP_CLASS, false, ClassLoadUnloadBreakpoint.TYPE_CLASS_LOADED);
+        debugManagerLoadBP = MethodBreakpoint.create(ENGINE_BUILDER_CLASS, "build");
+        ((MethodBreakpoint) debugManagerLoadBP).setBreakpointType(MethodBreakpoint.TYPE_METHOD_ENTRY);
         debugManagerLoadBP.setHidden(true);
         
         LOG.log(Level.FINE, "TruffleDebugManager.initBreakpoints(): submitted BP {0}", debugManagerLoadBP);
@@ -171,7 +146,7 @@ public class TruffleDebugManager extends DebuggerManagerAdapter {
             @Override
             public void breakpointReached(JPDABreakpointEvent event) {
                 try {
-                    submitPECreationBP(debugger, event.getReferenceType());
+                    handleEngineBuilder(debugger, event);
                 } finally {
                     event.resume();
                 }
@@ -180,16 +155,7 @@ public class TruffleDebugManager extends DebuggerManagerAdapter {
         debugManagerLoadBP.addJPDABreakpointListener(bpl);
         // Submit creation BPs for existing engine classes:
         Runnable submitEngineCreation = () -> {
-            List<JPDAClassType> polyglotEngines = new ArrayList<>();
-            //polyglotEngines.addAll(debugger.getClassesByName(SESSION_CREATION_BP_CLASS[0]));
-            List<JPDAClassType> enginePe = debugger.getClassesByName(SESSION_CREATION_BP_CLASS);
-            polyglotEngines.addAll(enginePe);
-            for (JPDAClassType pe : polyglotEngines) {
-                submitPECreationBP(debugger, ((JPDAClassTypeImpl) pe).getType());
-                // TODO: Find possible existing instances of the engine
-                // List<ObjectVariable> engines = pe.getInstances(0);
-                // We have no suspended thread... :-(
-            }
+            List<JPDAClassType> enginePe = debugger.getClassesByName(ENGINE_CLASS);
             // Find possible existing instances of the engine
             if (!enginePe.isEmpty() && debugger.canGetInstanceInfo()) {
                 long engineInstances = 0;
@@ -217,60 +183,27 @@ public class TruffleDebugManager extends DebuggerManagerAdapter {
         return bpl;
     }
 
-    private void submitPECreationBP(final JPDADebugger debugger, ReferenceType type) {
-        try {
-            List<Method> constructors = ReferenceTypeWrapper.methodsByName(type, "<init>");
-            for (Method c : constructors) {
-                if (!c.argumentTypeNames().isEmpty()) {
-                    Location lastLocation = null;
-                    Location l;
-                    int i = 0;
-                    // Search for the last (return) statement:
-                    while ((l = MethodWrapper.locationOfCodeIndex(c, i)) != null) {
-                        lastLocation = l;
-                        i++;
-                    }
-                    BreakpointRequest bp = EventRequestManagerWrapper.createBreakpointRequest(lastLocation.virtualMachine().eventRequestManager(), lastLocation);
-                    EventRequestWrapper.setSuspendPolicy(bp, EventRequest.SUSPEND_EVENT_THREAD);
-                    ((JPDADebuggerImpl) debugger).getOperator().register(bp, new Executor() {
-                        @Override
-                        public boolean exec(Event event) {
-                            try {
-                                ThreadReference threadReference = LocatableEventWrapper.thread((LocatableEvent) event);
-                                JPDAThreadImpl thread = ((JPDADebuggerImpl) debugger).getThread(threadReference);
-                                StackFrame topFrame = ThreadReferenceWrapper.frame(threadReference, 0);
-                                List<Value> argumentValues = topFrame.getArgumentValues();
-                                if (argumentValues.get(0) == null) {
-                                    // An empty constructor used for the builder only.
-                                    return true;
-                                }
-                                ObjectReference engine = StackFrameWrapper.thisObject(topFrame);
-                                haveNewPE(debugger, thread, engine);
-                            } catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper |
-                                     ObjectCollectedExceptionWrapper ex) {
-                            } catch (IllegalThreadStateExceptionWrapper |
-                                     IncompatibleThreadStateException |
-                                     InvalidStackFrameExceptionWrapper ex) {
-                                Exceptions.printStackTrace(ex);
-                            }
-                            return true;
-                        }
-
-                        @Override
-                        public void removed(EventRequest eventRequest) {
-                        }
-
-                    });
-                    try {
-                        EventRequestWrapper.enable(bp);
-                    } catch (InvalidRequestStateExceptionWrapper irsx) {
-                        Exceptions.printStackTrace(irsx);
-                    }
-                }
+    /**
+     * Called from a method entry breakpoint on Engine$Builder.build().
+     * We need to submit a temporary method-exit breakpoint on the build method.
+     * We must not keep the method exit breakpoint active as it causes a significant performance degradation.
+     */
+    private void handleEngineBuilder(final JPDADebugger debugger, JPDABreakpointEvent entryEvent) {
+        MethodBreakpoint builderExitBreakpoint = MethodBreakpoint.create(ENGINE_BUILDER_CLASS, "build");
+        builderExitBreakpoint.setBreakpointType(MethodBreakpoint.TYPE_METHOD_EXIT);
+        builderExitBreakpoint.setThreadFilters(debugger, new JPDAThread[]{entryEvent.getThread()});
+        builderExitBreakpoint.setSuspend(JPDABreakpoint.SUSPEND_EVENT_THREAD);
+        builderExitBreakpoint.setHidden(true);
+        builderExitBreakpoint.addJPDABreakpointListener(exitEvent -> {
+            try {
+                builderExitBreakpoint.disable();
+                DebuggerManager.getDebuggerManager().removeBreakpoint(builderExitBreakpoint);
+                haveNewPE(debugger, (JPDAThreadImpl) exitEvent.getThread(), (ObjectReference) ((JDIVariable) exitEvent.getVariable()).getJDIValue());
+            } finally {
+                exitEvent.resume();
             }
-        } catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper |
-                 ObjectCollectedExceptionWrapper | ClassNotPreparedExceptionWrapper ex) {
-        }
+        });
+        DebuggerManager.getDebuggerManager().addBreakpoint(builderExitBreakpoint);
     }
 
     private void submitExistingEnginesProbe(final JPDADebugger debugger, List<JPDAClassType> enginePe) {
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/CurrentPCInfo.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/CurrentPCInfo.java
index 6814e35..c1dcc60 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/CurrentPCInfo.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/CurrentPCInfo.java
@@ -31,7 +31,7 @@ import org.netbeans.modules.debugger.jpda.truffle.ast.TruffleNode;
 import org.netbeans.modules.debugger.jpda.truffle.frames.TruffleStackFrame;
 import org.netbeans.modules.debugger.jpda.truffle.frames.TruffleStackInfo;
 import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleScope;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleScope;
 
 /**
  * Container of information about the current program counter.
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleAccess.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleAccess.java
index 7450372..de1ebc8 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleAccess.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleAccess.java
@@ -66,8 +66,8 @@ import org.netbeans.modules.debugger.jpda.truffle.frames.TruffleStackFrame;
 import org.netbeans.modules.debugger.jpda.truffle.frames.TruffleStackInfo;
 import org.netbeans.modules.debugger.jpda.truffle.source.Source;
 import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleScope;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleStackVariable;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleScope;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleStackVariable;
 import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariable;
 import org.netbeans.modules.debugger.jpda.util.WeakHashMapActive;
 import org.openide.util.Exceptions;
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/PauseInGraalScriptActionProvider.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/PauseInGraalScriptActionProvider.java
index af7fffa..4dfc2f6 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/PauseInGraalScriptActionProvider.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/PauseInGraalScriptActionProvider.java
@@ -27,6 +27,7 @@ import com.sun.jdi.InvocationException;
 import com.sun.jdi.Method;
 import com.sun.jdi.ObjectReference;
 import com.sun.jdi.ThreadReference;
+import java.awt.GraphicsEnvironment;
 import java.lang.ref.WeakReference;
 import java.util.Collections;
 import java.util.Map;
@@ -62,13 +63,13 @@ import org.openide.util.Lookup;
 public class PauseInGraalScriptActionProvider extends JPDADebuggerActionProvider {
 
     private static final Logger LOG = Logger.getLogger(PauseInGraalScriptActionProvider.class.getName());
-    
+
     public static final String NAME = "pauseInGraalScript";  // NOI18N
     private static final String ACCESSOR_SUSPEND_NEXT_EXECUTION = "suspendNextExecution";   // NOI18N
     private static final String ACCESSOR_SUSPEND_NEXT_EXECUTION_SIGNAT = "()V";
-    
+
     private static WeakReference<Action> actionReference = new WeakReference<>(null);
-    private static boolean actionState = false;
+    private static final ThreadLocal<Boolean> AVOID_REENTRANT = new ThreadLocal<>();
     private boolean suspendState = false;
 
     public PauseInGraalScriptActionProvider (ContextProvider lookupProvider) {
@@ -77,48 +78,42 @@ public class PauseInGraalScriptActionProvider extends JPDADebuggerActionProvider
 
     @Override
     protected void checkEnabled(int debuggerState) {
-        Action action = actionReference.get();
-        if (action != null) {
-            ClassObjectReference serviceClass = RemoteServices.getServiceClass(getDebuggerImpl());
-            boolean hasServiceClass = serviceClass != null;
-            setEnabled(NAME, hasServiceClass);
-            if (hasServiceClass) {
-                JPDAThread currentThread = debugger.getCurrentThread();
-                if (currentThread != null && TruffleAccess.getCurrentPCInfo(currentThread) != null) {
-                    suspendState = false;
-                }
-                if (actionState != suspendState) {
-                    SwingUtilities.invokeLater(() -> {
-                        actionState = suspendState;
-                        action.putValue(Action.SELECTED_KEY, actionState);
-                    });
-                }
-            } else {
-                if (actionState) {
-                    SwingUtilities.invokeLater(() -> {
-                        action.putValue(Action.SELECTED_KEY, false);
-                        actionState = false;
-                    });
-                }
+        if (AVOID_REENTRANT.get() != null) {
+            return;
+        }
+        try {
+            AVOID_REENTRANT.set(true);
+            checkEnabledImpl(debuggerState);
+        } finally {
+            AVOID_REENTRANT.set(null);
+        }
+    }
+
+    private void checkEnabledImpl(int debuggerState) {
+        ClassObjectReference serviceClass = RemoteServices.getServiceClass(getDebuggerImpl());
+        boolean hasServiceClass = serviceClass != null;
+        setEnabled(NAME, hasServiceClass);
+        if (hasServiceClass) {
+            JPDAThread currentThread = debugger.getCurrentThread();
+            if (currentThread != null && TruffleAccess.getCurrentPCInfo(currentThread) != null) {
+                suspendState = false;
             }
+            updateActionState(true, suspendState);
+        } else {
+            updateActionState(false, false);
         }
     }
 
     @Override
     public void doAction(Object actionName) {
-        Action action = actionReference.get();
-        if (action != null) {
-            suspendState = !suspendState;
-            actionState = suspendState;
-            SwingUtilities.invokeLater(() -> {
-                action.putValue(Action.SELECTED_KEY, actionState);
-            });
-            if (suspendState) {
-                scheduleSuspend();
-            } else {
-                cancelSuspend();
-            }
+        assert NAME.equals(actionName);
+        suspendState = !suspendState;
+        if (suspendState) {
+            scheduleSuspend();
+        } else {
+            cancelSuspend();
         }
+        updateActionState(true, suspendState);
     }
 
     @Override
@@ -176,11 +171,23 @@ public class PauseInGraalScriptActionProvider extends JPDADebuggerActionProvider
             java.lang.reflect.Method createActionMethod = debuggerActionClass.getDeclaredMethod("createAction", Map.class);
             Action action = (Action) createActionMethod.invoke(null, params);
             action.putValue(Actions.ACTION_VALUE_TOGGLE, Boolean.TRUE);
-            action.putValue(Action.SELECTED_KEY, actionState);
+            action.putValue(Action.SELECTED_KEY, false);
             actionReference = new WeakReference<>(action);
             return action;
         } catch (Exception ex) {
             throw new RuntimeException(ex);
         }
     }
+
+    private static void updateActionState(boolean active, boolean suspendState) {
+        if (GraphicsEnvironment.isHeadless()) {
+            return;
+        }
+        Action action = actionReference.get();
+        if (action != null) {
+            SwingUtilities.invokeLater(() -> {
+                action.putValue(Action.SELECTED_KEY, active & suspendState);
+            });
+        }
+    }
 }
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/RunToCursorActionProvider.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/RunToCursorActionProvider.java
index 0506b2f..b85a459 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/RunToCursorActionProvider.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/RunToCursorActionProvider.java
@@ -53,12 +53,14 @@ import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.ClassTypeWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.UnsupportedOperationExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
+import org.netbeans.modules.debugger.jpda.truffle.PersistentValues;
 import org.netbeans.modules.debugger.jpda.truffle.TruffleDebugManager;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
-import org.netbeans.modules.debugger.jpda.truffle.breakpoints.TruffleBreakpointsHandler;
+import org.netbeans.modules.debugger.jpda.truffle.breakpoints.impl.TruffleBreakpointsHandler;
 import org.netbeans.modules.debugger.jpda.truffle.source.Source;
 import org.netbeans.spi.debugger.ActionsProvider;
 import org.netbeans.spi.debugger.ActionsProviderSupport;
@@ -163,10 +165,11 @@ public class RunToCursorActionProvider extends ActionsProviderSupport {
                 @Override
                 public void callMethods(JPDAThread thread) {
                     ThreadReference tr = ((JPDAThreadImpl) thread).getThreadReference();
-                    StringReference pathRef = tr.virtualMachine().mirrorOf(uri);
-                    IntegerValue lineRef = tr.virtualMachine().mirrorOf(line);
-                    List<? extends Value> args = Arrays.asList(new Value[] { pathRef, lineRef });
+                    PersistentValues persistents = new PersistentValues(tr.virtualMachine());
                     try {
+                        StringReference pathRef = persistents.mirrorOf(uri);
+                        IntegerValue lineRef = tr.virtualMachine().mirrorOf(line);
+                        List<? extends Value> args = Arrays.asList(new Value[] { pathRef, lineRef });
                         ArrayReference ret = (ArrayReference) ClassTypeWrapper.invokeMethod(
                                 debugAccessor,
                                 tr,
@@ -178,10 +181,12 @@ public class RunToCursorActionProvider extends ActionsProviderSupport {
                         Throwable ex = new InvocationExceptionTranslated(iex, (JPDADebuggerImpl) debugger).preload((JPDAThreadImpl) thread);
                         Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Setting one shot breakpoint to "+uri+":"+line));
                     } catch (InvalidTypeException | ClassNotLoadedException |
-                             IncompatibleThreadStateException |
+                             IncompatibleThreadStateException | UnsupportedOperationExceptionWrapper |
                              InternalExceptionWrapper | VMDisconnectedExceptionWrapper |
                              ObjectCollectedExceptionWrapper ex) {
                         Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Setting one shot breakpoint to "+uri+":"+line));
+                    } finally {
+                        persistents.collect();
                     }
                 }
             });
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/StepActionProvider.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/StepActionProvider.java
index d1f18ae..2ca211a 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/StepActionProvider.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/StepActionProvider.java
@@ -23,6 +23,8 @@ import com.sun.jdi.VirtualMachine;
 import com.sun.jdi.request.EventRequestManager;
 import com.sun.jdi.request.StepRequest;
 
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 import java.io.InvalidObjectException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -34,14 +36,20 @@ import java.util.Set;
 import java.util.logging.Logger;
 
 import org.netbeans.api.debugger.ActionsManager;
+import org.netbeans.api.debugger.DebuggerManager;
 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.event.JPDABreakpointEvent;
+import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener;
 import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
 import org.netbeans.modules.debugger.jpda.actions.JPDADebuggerActionProvider;
 import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.InvalidRequestStateExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestManagerWrapper;
+import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
 import org.netbeans.modules.debugger.jpda.truffle.access.CurrentPCInfo;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
@@ -65,6 +73,9 @@ public class StepActionProvider extends JPDADebuggerActionProvider {
             ActionsManager.ACTION_CONTINUE
     })));
 
+    private static final String STEP2JAVA_CLASS = "com.oracle.truffle.polyglot.HostMethodDesc$SingleMethod$MHBase";
+    private static final String STEP2JAVA_METHOD = "invokeHandle";
+
     public StepActionProvider (ContextProvider lookupProvider) {
         super (
             (JPDADebuggerImpl) lookupProvider.lookupFirst 
@@ -108,6 +119,9 @@ public class StepActionProvider extends JPDADebuggerActionProvider {
             Exceptions.printStackTrace(ex);
         }
         killJavaStep(debugger);
+        if (ActionsManager.ACTION_STEP_INTO.equals(action)) {
+            setBreakpoint2Java(currentThread);
+        }
         if (stepCmd > 0) {
             debugger.resumeCurrentThread();
         } else {
@@ -154,5 +168,38 @@ public class StepActionProvider extends JPDADebuggerActionProvider {
     public Set getActions() {
         return ACTIONS;
     }
-    
+
+    private void setBreakpoint2Java(JPDAThread currentThread) {
+        MethodBreakpoint stepIntoJavaBreakpoint = MethodBreakpoint.create(STEP2JAVA_CLASS, STEP2JAVA_METHOD);
+        stepIntoJavaBreakpoint.setBreakpointType(MethodBreakpoint.TYPE_METHOD_ENTRY);
+        stepIntoJavaBreakpoint.setThreadFilters(debugger, new JPDAThread[]{currentThread});
+        stepIntoJavaBreakpoint.setHidden(true);
+        stepIntoJavaBreakpoint.addJPDABreakpointListener(new JPDABreakpointListener() {
+            @Override
+            public void breakpointReached(JPDABreakpointEvent event) {
+                stepIntoJavaBreakpoint.removeJPDABreakpointListener(this);
+                JPDAStep step2Java = debugger.createJPDAStep(JPDAStep.STEP_LINE, JPDAStep.STEP_INTO);
+                // Step through the reflection invocation onto a user Java code
+                // Need to add the standard exclusion patterns as we're stepping from an excluded location
+                step2Java.addSteppingFilters(debugger.getSmartSteppingFilter().getExclusionPatterns());
+                // Additional invocation-specific patterns:
+                step2Java.addSteppingFilters("java.lang.invoke.*", "sun.invoke.*", Class.class.getName(), System.class.getName());
+                step2Java.setStepThroughFilters(true);
+                step2Java.addStep(currentThread);
+                event.resume();
+            }
+        });
+        ((JPDAThreadImpl) currentThread).addPropertyChangeListener(JPDAThread.PROP_SUSPENDED, new PropertyChangeListener() {
+            @Override
+            public void propertyChange(PropertyChangeEvent evt) {
+                if ((Boolean) evt.getNewValue()) {
+                    // Remove the step breakpoint on any suspend
+                    DebuggerManager.getDebuggerManager().removeBreakpoint(stepIntoJavaBreakpoint);
+                    ((JPDAThreadImpl) evt.getSource()).removePropertyChangeListener(JPDAThread.PROP_SUSPENDED, this);
+                }
+            }
+        });
+        DebuggerManager.getDebuggerManager().addBreakpoint(stepIntoJavaBreakpoint);
+    }
+
 }
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleLineBreakpoint.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleLineBreakpoint.java
index 2a083a7..b74c53c 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleLineBreakpoint.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleLineBreakpoint.java
@@ -19,12 +19,76 @@
 
 package org.netbeans.modules.debugger.jpda.truffle.breakpoints;
 
+import java.beans.PropertyChangeListener;
+import java.net.URL;
+
 import org.netbeans.modules.javascript2.debug.EditorLineHandler;
+import org.netbeans.modules.javascript2.debug.EditorLineHandlerFactory;
 import org.netbeans.modules.javascript2.debug.breakpoints.JSLineBreakpoint;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Lookup;
 
 public class TruffleLineBreakpoint extends JSLineBreakpoint {
     
     public TruffleLineBreakpoint(EditorLineHandler lineHandler) {
         super(lineHandler);
     }
+
+    public TruffleLineBreakpoint(URL url, int lineNumber) {
+        this(getEditorLineHandler(url, lineNumber));
+    }
+
+    private static EditorLineHandler getEditorLineHandler(URL url, int lineNumber) {
+        EditorLineHandler handler;
+        if (Lookup.getDefault().lookup(EditorLineHandlerFactory.class) != null) {
+            handler = EditorLineHandlerFactory.getHandler(url, lineNumber);
+        } else {
+            handler = new FixedLineHandler(url, lineNumber);
+        }
+        return handler;
+    }
+
+    private static final class FixedLineHandler implements EditorLineHandler {
+
+        private final URL url;
+        private int lineNumber;
+
+        FixedLineHandler(URL url, int lineNumber) {
+            this.url = url;
+            this.lineNumber = lineNumber;
+        }
+
+        @Override
+        public FileObject getFileObject() {
+            return URLMapper.findFileObject(url);
+        }
+
+        @Override
+        public URL getURL() {
+            return url;
+        }
+
+        @Override
+        public int getLineNumber() {
+            return lineNumber;
+        }
+
+        @Override
+        public void setLineNumber(int lineNumber) {
+            this.lineNumber = lineNumber;
+        }
+
+        @Override
+        public void dispose() {
+        }
+
+        @Override
+        public void addPropertyChangeListener(PropertyChangeListener pchl) {
+        }
+
+        @Override
+        public void removePropertyChangeListener(PropertyChangeListener pchl) {
+        }
+    }
 }
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleBreakpointReader.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/impl/TruffleBreakpointReader.java
similarity index 88%
rename from java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleBreakpointReader.java
rename to java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/impl/TruffleBreakpointReader.java
index 8710469..34efe6c 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleBreakpointReader.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/impl/TruffleBreakpointReader.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.netbeans.modules.debugger.jpda.truffle.breakpoints;
+package org.netbeans.modules.debugger.jpda.truffle.breakpoints.impl;
 
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -25,12 +25,14 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.netbeans.api.debugger.Breakpoint;
 import org.netbeans.api.debugger.Properties;
+import org.netbeans.modules.debugger.jpda.truffle.breakpoints.TruffleLineBreakpoint;
 import org.netbeans.modules.debugger.jpda.truffle.source.Source;
 import org.netbeans.modules.javascript2.debug.EditorLineHandler;
 import org.netbeans.modules.javascript2.debug.EditorLineHandlerFactory;
 import org.netbeans.spi.debugger.DebuggerServiceRegistration;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.URLMapper;
+import org.openide.util.Lookup;
 
 /**
  * Breakpoints storage.
@@ -64,11 +66,15 @@ public class TruffleBreakpointReader implements Properties.Reader {
                         return null;
                     }
                 } else {
-                    EditorLineHandler line = EditorLineHandlerFactory.getHandler(fo, lineNumber);
-                    if (line != null) {
-                        b = new TruffleLineBreakpoint(line);
+                    if (Lookup.getDefault().lookup(EditorLineHandlerFactory.class) != null) {
+                        EditorLineHandler line = EditorLineHandlerFactory.getHandler(fo, lineNumber);
+                        if (line != null) {
+                            b = new TruffleLineBreakpoint(line);
+                        } else {
+                            return null;
+                        }
                     } else {
-                        return null;
+                        b = new TruffleLineBreakpoint(url, lineNumber);
                     }
                 }
             } catch (MalformedURLException ex) {
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleBreakpointsHandler.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/impl/TruffleBreakpointsHandler.java
similarity index 89%
rename from java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleBreakpointsHandler.java
rename to java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/impl/TruffleBreakpointsHandler.java
index f658577..6cdb45c 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleBreakpointsHandler.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/impl/TruffleBreakpointsHandler.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.netbeans.modules.debugger.jpda.truffle.breakpoints;
+package org.netbeans.modules.debugger.jpda.truffle.breakpoints.impl;
 
 import com.sun.jdi.ArrayReference;
 import com.sun.jdi.ClassNotLoadedException;
@@ -35,7 +35,9 @@ import com.sun.jdi.VirtualMachine;
 
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
+import java.io.IOException;
 import java.net.URI;
+import java.net.URL;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -50,18 +52,30 @@ import org.netbeans.api.debugger.Breakpoint;
 import org.netbeans.api.debugger.DebuggerManager;
 import org.netbeans.api.debugger.jpda.JPDADebugger;
 import org.netbeans.api.debugger.jpda.JPDAThread;
+import org.netbeans.api.java.queries.BinaryForSourceQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.project.Sources;
 import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
 import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.ClassTypeWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.ObjectReferenceWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.UnsupportedOperationExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
 import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
+import org.netbeans.modules.debugger.jpda.truffle.PersistentValues;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
 import org.netbeans.modules.debugger.jpda.truffle.source.Source;
+import org.netbeans.modules.debugger.jpda.truffle.source.SourceBinaryTranslator;
 import org.netbeans.modules.javascript2.debug.breakpoints.JSLineBreakpoint;
+import org.netbeans.spi.java.queries.BinaryForSourceQueryImplementation;
 import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
 import org.openide.util.Exceptions;
 import org.openide.util.Mutex;
 
@@ -122,7 +136,7 @@ public class TruffleBreakpointsHandler {
             }
             URI uri = Source.getTruffleInternalURI(fileObject);
             if (uri == null) {
-                uri = fileObject.toURI();
+                uri = SourceBinaryTranslator.source2Binary(fileObject);
             }
             ObjectReference bpImpl;
             if (bp.isEnabled()) {
@@ -145,7 +159,7 @@ public class TruffleBreakpointsHandler {
             }
         }
     }
-    
+
     private static int getIgnoreCount(JSLineBreakpoint bp) {
         int ignoreCount = 0;
         if (Breakpoint.HIT_COUNT_FILTERING_STYLE.GREATER.equals(bp.getHitCountFilteringStyle())) {
@@ -160,6 +174,7 @@ public class TruffleBreakpointsHandler {
         assert t.isMethodInvoking();
         ThreadReference tr = t.getThreadReference();
         VirtualMachine vm = tr.virtualMachine();
+        PersistentValues persistents = new PersistentValues(vm);
         try {
             Method setLineBreakpointMethod = ClassTypeWrapper.concreteMethodByName(
                     accessorClass,
@@ -168,10 +183,10 @@ public class TruffleBreakpointsHandler {
             if (setLineBreakpointMethod == null) {
                 throw new IllegalStateException("Method "+ACCESSOR_SET_LINE_BREAKPOINT+" with signature:\n"+ACCESSOR_SET_LINE_BREAKPOINT_MGR_SIGNAT+"\nis not present in accessor class "+accessorClass);
             }
-            Value uriRef = vm.mirrorOf(uri.toString());
+            Value uriRef = persistents.mirrorOf(uri.toString());
             IntegerValue lineRef = vm.mirrorOf(line);
             IntegerValue icRef = vm.mirrorOf(ignoreCount);
-            StringReference conditionRef = (condition != null) ? vm.mirrorOf(condition) : null;
+            StringReference conditionRef = (condition != null) ? persistents.mirrorOf(condition) : null;
             List<? extends Value> args = Arrays.asList(new Value[] { debugManager, uriRef, lineRef, icRef, conditionRef });
             ObjectReference ret = (ObjectReference) ClassTypeWrapper.invokeMethod(
                     accessorClass,
@@ -183,9 +198,11 @@ public class TruffleBreakpointsHandler {
         } catch (VMDisconnectedExceptionWrapper | InternalExceptionWrapper |
                  ClassNotLoadedException | ClassNotPreparedExceptionWrapper |
                  IncompatibleThreadStateException | InvalidTypeException |
-                 ObjectCollectedExceptionWrapper ex) {
+                 UnsupportedOperationExceptionWrapper | ObjectCollectedExceptionWrapper ex) {
             Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Setting breakpoint to "+uri+":"+line));
             return null;
+        } finally {
+            persistents.collect();
         }
     }
     
@@ -199,7 +216,7 @@ public class TruffleBreakpointsHandler {
         if (tiuri != null) {
             uri = tiuri;
         } else {
-            uri = fileObject.toURI();
+            uri = SourceBinaryTranslator.source2Binary(fileObject);
         }
         final int line = bp.getLineNumber();
         final int ignoreCount = getIgnoreCount(bp);
@@ -219,24 +236,28 @@ public class TruffleBreakpointsHandler {
                     public void callMethods(JPDAThread thread) throws InvocationException {
                         ThreadReference tr = ((JPDAThreadImpl) thread).getThreadReference();
                         VirtualMachine vm = tr.virtualMachine();
-                        StringReference uriRef = vm.mirrorOf(uri.toString());
-                        IntegerValue lineRef = vm.mirrorOf(line);
-                        IntegerValue icRef = vm.mirrorOf(ignoreCount);
-                        StringReference conditionRef = (condition != null) ? vm.mirrorOf(condition) : null;
-                        List<? extends Value> args = Arrays.asList(new Value[] { uriRef, lineRef, icRef, conditionRef });
+                        PersistentValues persistents = new PersistentValues(vm);
                         try {
+                            StringReference uriRef = persistents.mirrorOf(uri.toString());
+                            IntegerValue lineRef = vm.mirrorOf(line);
+                            IntegerValue icRef = vm.mirrorOf(ignoreCount);
+                            StringReference conditionRef = (condition != null) ? persistents.mirrorOf(condition) : null;
+                            List<? extends Value> args = Arrays.asList(new Value[] { uriRef, lineRef, icRef, conditionRef });
                             ArrayReference ret = (ArrayReference) ClassTypeWrapper.invokeMethod(
                                     accessorClass,
                                     tr,
                                     setLineBreakpointMethod,
                                     args,
                                     ObjectReference.INVOKE_SINGLE_THREADED);
+                            ret.disableCollection();
                             bpRef[0] = ret;
                         } catch (InvalidTypeException | ClassNotLoadedException |
-                                 IncompatibleThreadStateException |
+                                 IncompatibleThreadStateException | UnsupportedOperationExceptionWrapper |
                                  InternalExceptionWrapper | VMDisconnectedExceptionWrapper |
                                  ObjectCollectedExceptionWrapper ex) {
                             Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Setting breakpoint to "+uri+":"+line));
+                        } finally {
+                            persistents.collect();
                         }
                     }
                 });
@@ -254,6 +275,7 @@ public class TruffleBreakpointsHandler {
                     breakpoints.add((ObjectReference) v);
                 }
             }
+            bpArray.enableCollection();
         }
         if (!breakpoints.isEmpty()) {
             synchronized (breakpointsMap) {
@@ -418,6 +440,7 @@ public class TruffleBreakpointsHandler {
             if (vm == null) {
                 return ;
             }
+            PersistentValues persistents = new PersistentValues(vm);
             switch (propertyName) {
                 case JSLineBreakpoint.PROP_ENABLED:
                     method = TruffleBPMethods.setEnabled;
@@ -426,7 +449,7 @@ public class TruffleBreakpointsHandler {
                 case JSLineBreakpoint.PROP_CONDITION:
                     method = TruffleBPMethods.setCondition;
                     String condition = jsbp.getCondition();
-                    StringReference conditionRef = (condition != null) ? vm.mirrorOf(condition) : null;
+                    StringReference conditionRef = (condition != null) ? persistents.mirrorOf0(condition) : null;
                     args = Collections.singletonList(conditionRef);
                     break;
                 case Breakpoint.PROP_HIT_COUNT_FILTER:
@@ -439,7 +462,11 @@ public class TruffleBreakpointsHandler {
             ((JPDADebuggerImpl) debugger).getRequestProcessor().post(new Runnable() {
                 @Override
                 public void run() {
-                    setBreakpointProperty(jsbp, method, args);
+                    try {
+                        setBreakpointProperty(jsbp, method, args);
+                    } finally {
+                        persistents.collect();
+                    }
                 }
             });
         }
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackFrame.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackFrame.java
index 6ef5d69..ac74193 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackFrame.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackFrame.java
@@ -35,7 +35,7 @@ import org.netbeans.modules.debugger.jpda.truffle.access.CurrentPCInfo;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
 import org.netbeans.modules.debugger.jpda.truffle.source.Source;
 import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleScope;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleScope;
 import org.openide.util.Exceptions;
 
 /**
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/Source.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/Source.java
index 74c59ed..426642f 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/Source.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/Source.java
@@ -64,6 +64,8 @@ public final class Source {
             } catch (IOException ex) {
                 Exceptions.printStackTrace(ex);
             }
+        } else {
+            uri = SourceBinaryTranslator.binary2Source(uri);
         }
         if (url == null) {
             try {
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceBinaryTranslator.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceBinaryTranslator.java
new file mode 100644
index 0000000..1ab8656
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceBinaryTranslator.java
@@ -0,0 +1,126 @@
+/*
+ * 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.truffle.source;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.java.queries.BinaryForSourceQuery;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.project.Sources;
+import org.netbeans.spi.java.queries.BinaryForSourceQueryImplementation;
+import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+
+public final class SourceBinaryTranslator {
+
+    private static final String[] SOURCE_IDS = new String[] {JavaProjectConstants.SOURCES_TYPE_RESOURCES, JavaProjectConstants.SOURCES_TYPE_JAVA, JavaProjectConstants.SOURCES_TYPE_MODULES};
+
+    /**
+     * Returns a file from binary location, that corresponds to the provided one.
+     * If none is found, the provided file's URI is returned.
+     */
+    public static URI source2Binary(FileObject fileObject) {
+        Project project = FileOwnerQuery.getOwner(fileObject);
+        if (project != null) {
+            BinaryForSourceQueryImplementation binaryForSource = project.getLookup().lookup(BinaryForSourceQueryImplementation.class);
+            if (binaryForSource != null) {
+                Sources sources = ProjectUtils.getSources(project);
+                for (String sourceId : SOURCE_IDS) {
+                    SourceGroup[] sourceGroups = sources.getSourceGroups(sourceId);
+                    for (SourceGroup sourceGroup : sourceGroups) {
+                        FileObject sourceRoot = sourceGroup.getRootFolder();
+                        String relativePath = FileUtil.getRelativePath(sourceRoot, fileObject);
+                        if (relativePath != null) {
+                            BinaryForSourceQuery.Result binaryRoots = binaryForSource.findBinaryRoots(sourceRoot.toURL());
+                            if (binaryRoots != null) {
+                                URL[] roots = binaryRoots.getRoots();
+                                for (URL root : roots) {
+                                    FileObject rootFo = URLMapper.findFileObject(root);
+                                    if (rootFo != null) {
+                                        FileObject binaryFo = rootFo.getFileObject(relativePath);
+                                        if (binaryFo != null && binaryFo.getSize() == fileObject.getSize()) {
+                                            return binaryFo.toURI();
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return fileObject.toURI();
+    }
+
+
+    /**
+     * Returns a file from source location, that corresponds to the provided one.
+     * If none is found, the provided file's URI is returned.
+     */
+    public static URI binary2Source(URI uri) {
+        FileObject fileObject;
+        try {
+            fileObject = URLMapper.findFileObject(uri.toURL());
+        } catch (MalformedURLException ex) {
+            return uri;
+        }
+        if (fileObject != null) {
+            Project project = FileOwnerQuery.getOwner(fileObject);
+            if (project != null) {
+                BinaryForSourceQueryImplementation binaryForSource = project.getLookup().lookup(BinaryForSourceQueryImplementation.class);
+                if (binaryForSource != null) {
+                    Sources sources = ProjectUtils.getSources(project);
+                    for (String sourceId : SOURCE_IDS) {
+                        SourceGroup[] sourceGroups = sources.getSourceGroups(sourceId);
+                        for (SourceGroup sourceGroup : sourceGroups) {
+                            FileObject sourceRoot = sourceGroup.getRootFolder();
+                            BinaryForSourceQuery.Result binaryRoots = binaryForSource.findBinaryRoots(sourceRoot.toURL());
+                            if (binaryRoots != null) {
+                                URL[] roots = binaryRoots.getRoots();
+                                for (URL root : roots) {
+                                    FileObject rootFo = URLMapper.findFileObject(root);
+                                    if (rootFo != null) {
+                                        String relativePath = FileUtil.getRelativePath(rootFo, fileObject);
+                                        if (relativePath != null) {
+                                            FileObject sourceFo = sourceRoot.getFileObject(relativePath);
+                                            if (sourceFo != null && sourceFo.getSize() == fileObject.getSize()) {
+                                                return sourceFo.toURI();
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return uri;
+    }
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleVariable.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleVariable.java
index 3674c42..e096eb4 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleVariable.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleVariable.java
@@ -21,8 +21,10 @@ package org.netbeans.modules.debugger.jpda.truffle.vars;
 
 import org.netbeans.api.debugger.jpda.JPDADebugger;
 import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
 import org.netbeans.modules.debugger.jpda.truffle.LanguageName;
 import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleVariableImpl;
 
 /**
  * Representation of <code>DebugValue</code>.
@@ -43,6 +45,8 @@ public interface TruffleVariable {
     
     Object getValue();
     
+    String getDisplayValue();
+    
     boolean hasValueSource();
     
     SourcePosition getValueSource();
@@ -56,4 +60,8 @@ public interface TruffleVariable {
     Object[] getChildren();
 
     ObjectVariable setValue(JPDADebugger debugger, String newExpression);
+
+    public static TruffleVariable get(Variable var) {
+        return TruffleVariableImpl.get(var);
+    }
 }
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleEvaluator.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleEvaluator.java
similarity index 97%
rename from java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleEvaluator.java
rename to java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleEvaluator.java
index f62ddc8..0775584 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleEvaluator.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleEvaluator.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.netbeans.modules.debugger.jpda.truffle.vars;
+package org.netbeans.modules.debugger.jpda.truffle.vars.impl;
 
 import org.netbeans.api.debugger.jpda.CallStackFrame;
 import org.netbeans.api.debugger.jpda.InvalidExpressionException;
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleExpression.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleExpression.java
similarity index 94%
copy from java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleExpression.java
copy to java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleExpression.java
index 62e9c8a..2af97d8 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleExpression.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleExpression.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.netbeans.modules.debugger.jpda.truffle.vars;
+package org.netbeans.modules.debugger.jpda.truffle.vars.impl;
 
 public final class TruffleExpression {
     
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleScope.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleScope.java
similarity index 95%
rename from java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleScope.java
rename to java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleScope.java
index 42370ce..d787c66 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleScope.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleScope.java
@@ -17,11 +17,12 @@
  * under the License.
  */
 
-package org.netbeans.modules.debugger.jpda.truffle.vars;
+package org.netbeans.modules.debugger.jpda.truffle.vars.impl;
 
 import org.netbeans.api.debugger.jpda.JPDADebugger;
 import org.netbeans.api.debugger.jpda.ObjectVariable;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
+import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariable;
 
 /**
  * Representation of DebugScope.
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleStackVariable.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleStackVariable.java
similarity index 95%
rename from java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleStackVariable.java
rename to java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleStackVariable.java
index 62903ba..910720b 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleStackVariable.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleStackVariable.java
@@ -17,13 +17,14 @@
  * under the License.
  */
 
-package org.netbeans.modules.debugger.jpda.truffle.vars;
+package org.netbeans.modules.debugger.jpda.truffle.vars.impl;
 
 import java.util.function.Supplier;
 import org.netbeans.api.debugger.jpda.JPDADebugger;
 import org.netbeans.api.debugger.jpda.ObjectVariable;
 import org.netbeans.modules.debugger.jpda.truffle.LanguageName;
 import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
+import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariable;
 
 public class TruffleStackVariable implements TruffleVariable {
     
@@ -101,6 +102,11 @@ public class TruffleStackVariable implements TruffleVariable {
     }
 
     @Override
+    public String getDisplayValue() {
+        return valueStr;
+    }
+
+    @Override
     public ObjectVariable setValue(JPDADebugger debugger, String newExpression) {
         if (this.valueStr.equals(newExpression)) {
             return null;
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleVariableImpl.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleVariableImpl.java
similarity index 98%
rename from java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleVariableImpl.java
rename to java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleVariableImpl.java
index 3ec8ad4..163a3bd 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleVariableImpl.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/impl/TruffleVariableImpl.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.netbeans.modules.debugger.jpda.truffle.vars;
+package org.netbeans.modules.debugger.jpda.truffle.vars.impl;
 
 import java.io.InvalidObjectException;
 import org.netbeans.api.debugger.jpda.Field;
@@ -30,6 +30,7 @@ import org.netbeans.modules.debugger.jpda.truffle.LanguageName;
 import org.netbeans.modules.debugger.jpda.truffle.access.CurrentPCInfo;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
 import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
+import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariable;
 import org.openide.util.Exceptions;
 
 public class TruffleVariableImpl implements TruffleVariable {
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleLocalVariablesTreeModel.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleLocalVariablesTreeModel.java
index aa7d518..81c304d 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleLocalVariablesTreeModel.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleLocalVariablesTreeModel.java
@@ -26,7 +26,7 @@ import org.netbeans.modules.debugger.jpda.truffle.access.CurrentPCInfo;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
 import org.netbeans.modules.debugger.jpda.truffle.frames.TruffleStackFrame;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleScope;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleScope;
 import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariable;
 import org.netbeans.spi.debugger.ContextProvider;
 import org.netbeans.spi.debugger.DebuggerServiceRegistration;
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesNodeModel.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesNodeModel.java
index a0d7266..1731ba1 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesNodeModel.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesNodeModel.java
@@ -31,7 +31,7 @@ import org.netbeans.api.debugger.jpda.Super;
 import org.netbeans.api.debugger.jpda.This;
 import org.netbeans.api.debugger.jpda.Variable;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleScope;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleScope;
 import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariable;
 import org.netbeans.spi.debugger.DebuggerServiceRegistration;
 import org.netbeans.spi.debugger.DebuggerServiceRegistrations;
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesTableModel.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesTableModel.java
index edb8f61..d776869 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesTableModel.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesTableModel.java
@@ -32,9 +32,9 @@ import org.netbeans.modules.debugger.jpda.truffle.access.CurrentPCInfo;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
 import org.netbeans.modules.debugger.jpda.truffle.frames.TruffleStackFrame;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleScope;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleScope;
 import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariable;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariableImpl;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleVariableImpl;
 import org.netbeans.spi.debugger.ContextProvider;
 import org.netbeans.spi.debugger.DebuggerServiceRegistration;
 import org.netbeans.spi.debugger.DebuggerServiceRegistrations;
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesTreeModel.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesTreeModel.java
index 66c135c..a8ef2b2 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesTreeModel.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesTreeModel.java
@@ -25,9 +25,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
 import org.netbeans.api.debugger.jpda.JPDADebugger;
 import org.netbeans.api.debugger.jpda.Variable;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleScope;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleScope;
 import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariable;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariableImpl;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleVariableImpl;
 import org.netbeans.spi.debugger.ContextProvider;
 import org.netbeans.spi.debugger.DebuggerServiceRegistration;
 import org.netbeans.spi.debugger.DebuggerServiceRegistrations;
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/tooltip/ToolTipAnnotation.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/tooltip/ToolTipAnnotation.java
index 8a89c1c..314f3d6 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/tooltip/ToolTipAnnotation.java
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/tooltip/ToolTipAnnotation.java
@@ -38,7 +38,7 @@ import org.netbeans.api.debugger.jpda.ObjectVariable;
 import org.netbeans.api.debugger.jpda.Variable;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleEval;
 import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
-import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariableImpl;
+import org.netbeans.modules.debugger.jpda.truffle.vars.impl.TruffleVariableImpl;
 import org.netbeans.spi.debugger.ui.EditorContextDispatcher;
 import org.openide.cookies.EditorCookie;
 import org.openide.loaders.DataObject;
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleExpression.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/AgentClassLoader.java
similarity index 52%
rename from java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleExpression.java
rename to java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/AgentClassLoader.java
index 62e9c8a..c9a4a13 100644
--- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleExpression.java
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/AgentClassLoader.java
@@ -16,22 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.netbeans.modules.debugger.jpda.backend.truffle;
 
-package org.netbeans.modules.debugger.jpda.truffle.vars;
+/**
+ * The classes of Truffle debugging backend are loaded in this class loader,
+ * to be isolated from the guest application.
+ * <p>
+ * This class loader is exported to Truffle
+ * (by com.oracle.truffle.polyglot.LanguageCache$Loader.exportTruffle())
+ * to be able to access Truffle module code on JDK 9+.
+ */
+public final class AgentClassLoader extends ClassLoader {
 
-public final class TruffleExpression {
-    
-    private final String expr;
-    
-    public static TruffleExpression parse (String expr) {
-        return new TruffleExpression(expr);
-    }
-    
-    private TruffleExpression(String expr) {
-        this.expr = expr;
+    public AgentClassLoader() throws ClassNotFoundException {
+        super(getTruffleClassLoader());
     }
-    
-    public String getExpression() {
-        return expr;
+
+    private static ClassLoader getTruffleClassLoader() throws ClassNotFoundException {
+        Class truffleClass = Class.forName("com.oracle.truffle.api.Truffle");
+        return truffleClass.getClassLoader();
     }
+
 }
diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor.java
index af6d364..d30c94a 100644
--- a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor.java
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor.java
@@ -78,7 +78,10 @@ public class JPDATruffleAccessor extends Object {
      */
     //private static int stepCmd = 0;
 
-    public JPDATruffleAccessor() {}
+    public JPDATruffleAccessor() {
+        // JDI needs to know about String class in this class loader.
+        new String("Initialize String class");
+    }
     
     static Thread startAccessLoop() {
         if (!accessLoopRunning) {
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 d5de421..e762f6f 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
@@ -127,6 +127,41 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
         p = Properties.getDefault().getProperties("debugger.options.JPDA"); // NOI18N
     }
     
+    private String[] applyExclusionPatterns(StepRequest stepRequest) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper {
+        String[] exclusionPatterns;
+        String[] stepFilters = getSteppingFilters();
+        if (ignoreStepFilters || steppingFromFilteredLocation) {
+            exclusionPatterns = stepFilters;
+        } else {
+            exclusionPatterns = debugger.getSmartSteppingFilter().getExclusionPatterns();
+            if (stepFilters != null) {
+                int epl = exclusionPatterns.length;
+                exclusionPatterns = Arrays.copyOf(exclusionPatterns, epl + stepFilters.length);
+                System.arraycopy(stepFilters, 0, exclusionPatterns, epl, stepFilters.length);
+            }
+        }
+        if (exclusionPatterns != null) {
+            for (int i = 0; i < exclusionPatterns.length; i++) {
+                StepRequestWrapper.addClassExclusionFilter(stepRequest, exclusionPatterns [i]);
+                logger.finer("   add pattern: "+exclusionPatterns[i]);
+            }
+        } else {
+            exclusionPatterns = new String[]{};
+        }
+        return exclusionPatterns;
+    }
+    
+    private String[] getCurrentExclusionPatterns() {
+        String[] exclusionPatterns = debugger.getSmartSteppingFilter().getExclusionPatterns();
+        String[] stepFilters = getSteppingFilters();
+        if (stepFilters != null) {
+            int epl = exclusionPatterns.length;
+            exclusionPatterns = Arrays.copyOf(exclusionPatterns, epl + stepFilters.length);
+            System.arraycopy(stepFilters, 0, exclusionPatterns, epl, stepFilters.length);
+        }
+        return exclusionPatterns;
+    }
+    
     @Override
     public void addStep(JPDAThread tr) {
         JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger;
@@ -191,28 +226,11 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
                     getDepth()
                 );
                 //stepRequest.addCountFilter(1); - works bad with exclusion filters!
-                String[] exclusionPatterns;
-                if (ignoreStepFilters || steppingFromFilteredLocation) {
-                    exclusionPatterns = null;
-                } else {
-                    exclusionPatterns = debuggerImpl.getSmartSteppingFilter().getExclusionPatterns();
-                    for (int i = 0; i < exclusionPatterns.length; i++) {
-                        StepRequestWrapper.addClassExclusionFilter(stepRequest, exclusionPatterns [i]);
-                        logger.finer("   add pattern: "+exclusionPatterns[i]);
-                    }
-                }
+                String[] exclusionPatterns = applyExclusionPatterns(stepRequest);
                 debuggerImpl.getOperator().register(stepRequest, this);
                 EventRequestWrapper.setSuspendPolicy(stepRequest, debugger.getSuspend());
-                boolean useStepFilters = p.getBoolean("UseStepFilters", true);
-                boolean stepThrough = useStepFilters && p.getBoolean("StepThroughFilters", false);
-                if (!stepThrough && exclusionPatterns != null && exclusionPatterns.length > 0) {
-                    StepPatternDepth spd = new StepPatternDepth();
-                    spd.exclusionPatterns = exclusionPatterns;
-                    spd.stackDepth = tr.getStackDepth();
-                    stepPatternDepth = spd;
-                } else {
-                    stepPatternDepth = null;
-                }
+                boolean stepThrough = isStepThroughFilters();
+                stepPatternDepth = new StepPatternDepth(tr.getStackDepth(), exclusionPatterns, stepThrough);
                 logger.fine("Set stepPatternDepth to "+stepPatternDepth);
 
                 try {
@@ -583,13 +601,12 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
                 tr.addLastOperation(lastOperation);
             }
             logger.fine("Have stepPatternDepth : "+stepPatternDepth);
-            int stepDepthDiff = 0;
-            if (stepPatternDepth != null) {
+            int sd = tr.getStackDepth();
+            int stepDepthDiff = (stepPatternDepth != null) ? sd - stepPatternDepth.stackDepth : 0;
+            if (stepPatternDepth != null && !stepPatternDepth.isStepThrough && stepPatternDepth.exclusionPatterns != null && stepPatternDepth.exclusionPatterns.length >= 0) {
                 StepPatternDepth newStepPatternDepth = null;
                 try {
-                    int sd = tr.getStackDepth();
                     logger.fine("Current stack depth = "+sd);
-                    stepDepthDiff = sd - stepPatternDepth.stackDepth;
                     if (stepDepthDiff > 1) {
                         // There are some (possibly filtered) stack frames in between.
                         // StepThroughFilters is false, therefore we should step out if we can not stop here:
@@ -616,16 +633,14 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
                                 StepRequest.STEP_OUT
                             );
                             EventRequestWrapper.addCountFilter(stepRequest, 1);
-                            String[] exclusionPatterns = debuggerImpl.getSmartSteppingFilter().getExclusionPatterns();
+                            String[] exclusionPatterns = getCurrentExclusionPatterns();
                             // JDI is inconsistent!!! Step into steps *through* filters, but step out does *NOT*
                             //for (int i = 0; i < exclusionPatterns.length; i++) {
                                 //StepRequestWrapper.addClassExclusionFilter(stepRequest, exclusionPatterns [i]);
                             //}
                             if (sd > (stepPatternDepth.stackDepth + 2)) {
                                 // There's still something perhaps filterable in beteen
-                                newStepPatternDepth = new StepPatternDepth();
-                                newStepPatternDepth.exclusionPatterns = exclusionPatterns;
-                                newStepPatternDepth.stackDepth = stepPatternDepth.stackDepth;
+                                newStepPatternDepth = new StepPatternDepth(stepPatternDepth.stackDepth, exclusionPatterns, stepPatternDepth.isStepThrough);
                             }
                             
                             debuggerImpl.getOperator ().register (stepRequest, this);
@@ -832,7 +847,7 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
         JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger;
         // 2) init info about current state
         boolean useStepFilters = p.getBoolean("UseStepFilters", true);
-        boolean stepThrough = useStepFilters && p.getBoolean("StepThroughFilters", false);
+        boolean stepThrough = isStepThroughFilters();
         try {
             ThreadReference tr = LocatableEventWrapper.thread (event);
             JPDAThreadImpl t = debuggerImpl.getThread (tr);
@@ -887,7 +902,7 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
                             stop = debuggerImpl.stopHere(t);
                         }
                         if (stop.isStop() && !steppingFromFilteredLocation) {
-                            String[] exclusionPatterns = debuggerImpl.getSmartSteppingFilter().getExclusionPatterns();
+                            String[] exclusionPatterns = getCurrentExclusionPatterns();
                             String className = ReferenceTypeWrapper.name(LocationWrapper.declaringType(loc));
                             for (String pattern : exclusionPatterns) {
                                 if (match(className, pattern)) {
@@ -932,27 +947,10 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
                             doStepDepth
                         );
                         //EventRequestWrapper.addCountFilter(stepRequest, 1);
-                        String[] exclusionPatterns;
-                        if (ignoreStepFilters || steppingFromFilteredLocation) {
-                            exclusionPatterns = null;
-                        } else {
-                            exclusionPatterns = debuggerImpl.getSmartSteppingFilter().getExclusionPatterns();
-                            if (doStepDepth != StepRequest.STEP_OUT) {
-                                for (int i = 0; i < exclusionPatterns.length; i++) {
-                                    StepRequestWrapper.addClassExclusionFilter(stepRequest, exclusionPatterns [i]);
-                                }
-                            }
-                        }
+                        String[] exclusionPatterns = applyExclusionPatterns(stepRequest);
                         debuggerImpl.getOperator ().register (stepRequest, this);
                         EventRequestWrapper.setSuspendPolicy (stepRequest, debugger.getSuspend ());
-                        if (!stepThrough && exclusionPatterns != null && exclusionPatterns.length > 0) {
-                            StepPatternDepth spd = new StepPatternDepth();
-                            spd.exclusionPatterns = exclusionPatterns;
-                            spd.stackDepth = t.getStackDepth();
-                            stepPatternDepth = spd;
-                        } else {
-                            stepPatternDepth = null;
-                        }
+                        stepPatternDepth = new StepPatternDepth(t.getStackDepth(), exclusionPatterns, stepThrough);
                         logger.fine("Set stepPatternDepth to "+stepPatternDepth);
                         try {
                             EventRequestWrapper.enable (stepRequest);
@@ -1019,24 +1017,8 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
                         logger.throwing(getClass().getName(), "shouldNotStopHere", ex);
                     }
                 }
-                String[] exclusionPatterns;
-                if (steppingFromFilteredLocation) {
-                    exclusionPatterns = null;
-                } else {
-                    exclusionPatterns = debuggerImpl.getSmartSteppingFilter().getExclusionPatterns();
-                    for (int i = 0; i < exclusionPatterns.length; i++) {
-                        StepRequestWrapper.addClassExclusionFilter(stepRequest, exclusionPatterns [i]);
-                        logger.finer("   add pattern: "+exclusionPatterns[i]);
-                    }
-                }
-                if (!stepThrough && exclusionPatterns != null && exclusionPatterns.length > 0) {
-                    StepPatternDepth spd = new StepPatternDepth();
-                    spd.exclusionPatterns = exclusionPatterns;
-                    spd.stackDepth = t.getStackDepth();
-                    stepPatternDepth = spd;
-                } else {
-                    stepPatternDepth = null;
-                }
+                String[] exclusionPatterns = applyExclusionPatterns(stepRequest);
+                stepPatternDepth = new StepPatternDepth(t.getStackDepth(), exclusionPatterns, stepThrough);
                 logger.fine("Set stepPatternDepth to "+stepPatternDepth);
 
                 debuggerImpl.getOperator ().register (stepRequest, this);
@@ -1232,8 +1214,15 @@ public class JPDAStepImpl extends JPDAStep implements Executor {
     
     private static final class StepPatternDepth {
         
-        String[] exclusionPatterns;
-        int stackDepth;
+        final String[] exclusionPatterns;
+        final int stackDepth;
+        final boolean isStepThrough;
+
+        StepPatternDepth(int stackDepth, String[] exclusionPatterns, boolean isStepThrough) {
+            this.stackDepth = stackDepth;
+            this.exclusionPatterns = exclusionPatterns;
+            this.isStepThrough = isStepThrough;
+        }
 
         private boolean isFiltered(String className) {
             for (int i = 0; i < exclusionPatterns.length; i++) {
diff --git a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/LineBreakpointImpl.java b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/LineBreakpointImpl.java
index 98ca2da..0a92290 100644
--- a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/LineBreakpointImpl.java
+++ b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/breakpoints/LineBreakpointImpl.java
@@ -44,6 +44,8 @@ import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -242,7 +244,7 @@ public class LineBreakpointImpl extends ClassBasedBreakpoint {
         if (!isInSources) {
             String relativePath = EditorContextBridge.getRelativePath(className);
             String classURL = getDebugger().getEngineContext().getURL(relativePath, true);
-            if (classURL != null && !classURL.equals(breakpoint.getURL())) {
+            if (classURL != null && !areURLEqual(classURL, breakpoint.getURL())) {
                 // Silently ignore breakpoints from other sources that resolve to a different URL.
                 logger.log(Level.FINE,
                        "LineBreakpoint {0} NOT submitted, because it's URL ''{1}'' differes from class ''{2}'' URL ''{3}''.",
@@ -293,6 +295,17 @@ public class LineBreakpointImpl extends ClassBasedBreakpoint {
         }
     }
 
+    private static boolean areURLEqual(String url1, String url2) {
+        if (url1.equals(url2)) {
+            return true;
+        }
+        try {
+            return new URI(url1).equals(new URI(url2));
+        } catch (URISyntaxException ex) {
+            return false;
+        }
+    }
+
     private void setInvalid(String reason) {
         logger.warning(
                 "Unable to submit line breakpoint to "+getBreakpoint().getURL()+


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